blob: fde0528c6c05303fa8fe82e8ef8d703163407d0a [file] [log] [blame] [edit]
/*
* Amlogic A113 PCI Express Root-Complex driver
*
* Copyright (C) 2017 Yue Wang <yue.wang@amlogic.com>
*
* Based on Linux kernel driver:
* pcie-amlogic.c:
*
*/
#include <common.h>
#include <pci.h>
#include <asm/arch/clock.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <linux/sizes.h>
#include <errno.h>
#include "pcie_amlogic.h"
#include <asm/arch-axg/pci.h>
static t_pl_region_regs *pcieA_pl_region_regs =
(t_pl_region_regs *)(PCIE_A_PORT_LOGIC_BASE_ADDR);
static t_pl_region_regs *pcieB_pl_region_regs =
(t_pl_region_regs *)(PCIE_B_PORT_LOGIC_BASE_ADDR);
static t_pcie_user_cfg *pcieA_user_cfg =
(t_pcie_user_cfg *)(PCIE_A_USER_CFG_BASE_ADDR);
static t_pcie_user_cfg *pcieB_user_cfg =
(t_pcie_user_cfg *)(PCIE_B_USER_CFG_BASE_ADDR);
static t_pcie_user_status *pcieA_user_status =
(t_pcie_user_status *)(PCIE_A_USER_STATUS_BASE_ADDR);
static t_pcie_user_status *pcieB_user_status =
(t_pcie_user_status *)(PCIE_B_USER_STATUS_BASE_ADDR);
static t_pcie_cap_regs *pcieA_pcie_cap_regs =
(t_pcie_cap_regs *)(PCIE_A_PCIE_CAP_BASE_ADDR);
static t_pcie_cap_regs *pcieB_pcie_cap_regs =
(t_pcie_cap_regs *)(PCIE_B_PCIE_CAP_BASE_ADDR);
static t_type1_hdr_regs *pcieA_type1_hdr_regs =
(t_type1_hdr_regs *)(PCIE_A_HDR_BASE_ADDR);
static t_type1_hdr_regs *pcieB_type1_hdr_regs =
(t_type1_hdr_regs *)(PCIE_B_HDR_BASE_ADDR);
static u8 dw_pcie_iatu_unroll_enabled(e_pcieDev pcie_dev)
{
u32 val;
u64 amlogic_dbi_addr;
if (pcie_dev == PCIE_A)
amlogic_dbi_addr = AMLOGIC_PCIE_A_DBI_ADDR;
else
amlogic_dbi_addr = AMLOGIC_PCIE_B_DBI_ADDR;
val = readl(amlogic_dbi_addr+PCIE_ATU_VIEWPORT);
if (val == 0xffffffff)
return 1;
return 0;
}
static void dw_pcie_writel_unroll(e_pcieDev pcie_dev, u32 index, u32 reg,
u32 val)
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
u64 amlogic_dbi_addr;
if (pcie_dev == PCIE_A)
amlogic_dbi_addr = AMLOGIC_PCIE_A_DBI_ADDR;
else
amlogic_dbi_addr = AMLOGIC_PCIE_B_DBI_ADDR;
writel(val, amlogic_dbi_addr + offset + reg);
}
static void dw_pcie_prog_outbound_atu(e_pcieDev pcie_dev, int index,
int type, u64 cpu_addr, u64 pci_addr, u32 size)
{
u64 amlogic_dbi_addr;
if (pcie_dev == PCIE_A)
amlogic_dbi_addr = AMLOGIC_PCIE_A_DBI_ADDR;
else
amlogic_dbi_addr = AMLOGIC_PCIE_B_DBI_ADDR;
if (iatu_unroll_enabled) {
dw_pcie_writel_unroll(pcie_dev, index, PCIE_ATU_UNR_LOWER_BASE,
lower_32_bits(cpu_addr));
dw_pcie_writel_unroll(pcie_dev, index, PCIE_ATU_UNR_UPPER_BASE,
upper_32_bits(cpu_addr));
dw_pcie_writel_unroll(pcie_dev, index, PCIE_ATU_UNR_LIMIT,
lower_32_bits(size - 1));
dw_pcie_writel_unroll(pcie_dev, index, PCIE_ATU_UNR_LOWER_TARGET,
lower_32_bits(pci_addr));
dw_pcie_writel_unroll(pcie_dev, index, PCIE_ATU_UNR_UPPER_TARGET,
upper_32_bits(pci_addr));
dw_pcie_writel_unroll(pcie_dev, index, PCIE_ATU_UNR_REGION_CTRL1,
type);
dw_pcie_writel_unroll(pcie_dev, index, PCIE_ATU_UNR_REGION_CTRL2,
PCIE_ATU_ENABLE);
} else {
writel(PCIE_ATU_REGION_OUTBOUND | index,
amlogic_dbi_addr + PCIE_ATU_VIEWPORT);
writel(lower_32_bits(cpu_addr),
amlogic_dbi_addr + PCIE_ATU_LOWER_BASE);
writel(upper_32_bits(cpu_addr),
amlogic_dbi_addr + PCIE_ATU_UPPER_BASE);
writel(lower_32_bits(cpu_addr + size - 1),
amlogic_dbi_addr + PCIE_ATU_LIMIT);
writel(lower_32_bits(pci_addr),
amlogic_dbi_addr + PCIE_ATU_LOWER_TARGET);
writel(upper_32_bits(pci_addr),
amlogic_dbi_addr + PCIE_ATU_UPPER_TARGET);
writel(type, amlogic_dbi_addr + PCIE_ATU_CR1);
writel(PCIE_ATU_ENABLE, amlogic_dbi_addr + PCIE_ATU_CR2);
}
}
/*
* iATU region setup
*/
static int amlogic_pcie_regions_setup(e_pcieDev pcie_dev)
{
u64 amlogic_dbi_addr;
u32 high_2m_base_addr;
u32 high_2m_limit;
u64 target_base_addr;
int type;
if (pcie_dev == PCIE_A) {
amlogic_dbi_addr = AMLOGIC_PCIE_A_DBI_ADDR;
high_2m_base_addr = EP_A_BASE_ADDR + 0x200000;
high_2m_limit = high_2m_base_addr + 0x1fffff;
} else {
amlogic_dbi_addr = AMLOGIC_PCIE_B_DBI_ADDR;
high_2m_base_addr = EP_B_BASE_ADDR + 0x200000;
high_2m_limit = high_2m_base_addr + 0x1fffff;
}
/* CMD reg:I/O space, MEM space, and Bus Master Enable */
/* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */
setbits_le32(amlogic_dbi_addr + PCI_CLASS_REVISION,
PCI_CLASS_BRIDGE_PCI << 16);
iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pcie_dev);
target_base_addr = high_2m_base_addr & 0x3fffff;
type = PCIE_ATU_TYPE_MEM;
if (type == PCIE_ATU_TYPE_CFG0 || type == PCIE_ATU_TYPE_CFG1) {
target_base_addr = (0 & 0xff) << 24 |
(0 & 0x1F) << 19 |
(0 & 0x7) << 16;
} else if (type == PCIE_ATU_TYPE_MEM)
target_base_addr &= (~0xfff);
dw_pcie_prog_outbound_atu(pcie_dev, PCIE_ATU_REGION_INDEX1,
PCIE_ATU_TYPE_MEM, high_2m_base_addr,
target_base_addr, high_2m_limit);
return 0;
}
static int amlogic_pcie_read_config
(struct pci_controller *hose, pci_dev_t d, int where, u32 *val)
{
int type;
e_pcieDev pcie_dev = *(int *)hose->priv_data;
u64 ep_type0_hdr_regs;
u32 ep_cfg_base_addr;
u32 ep_cfg_addr_limit;
u64 target_base_addr;
if (pcie_dev == PCIE_A) {
ep_type0_hdr_regs = EP_A_BASE_ADDR;
ep_cfg_base_addr = EP_A_BASE_ADDR;
ep_cfg_addr_limit = EP_A_BASE_ADDR + 0x1fffff;
} else {
ep_type0_hdr_regs = EP_B_BASE_ADDR;
ep_cfg_base_addr = EP_B_BASE_ADDR;
ep_cfg_addr_limit = EP_B_BASE_ADDR + 0x1fffff;
}
target_base_addr = ep_cfg_base_addr & 0x3fffff;
type = TYPE_CFG0;
if (type == TYPE_CFG0 || type == TYPE_CFG1) {
target_base_addr = (0 & 0xff) << 24 |
(0 & 0x1F) << 19 |
(0 & 0x7) << 16;
} else if (type == TYPE_MEM)
target_base_addr &= (~0xfff);
dw_pcie_prog_outbound_atu(pcie_dev, PCIE_ATU_REGION_INDEX0,
type, ep_cfg_base_addr,
target_base_addr, ep_cfg_addr_limit);
*val = readl(ep_type0_hdr_regs + where);
return 0;
}
static int amlogic_pcie_write_config(struct pci_controller *hose, pci_dev_t d,
int where, u32 val)
{
int type;
e_pcieDev pcie_dev = *(int *)hose->priv_data;
u64 ep_type0_hdr_regs;
u32 ep_cfg_base_addr;
u32 ep_cfg_addr_limit;
u64 target_base_addr;
if (pcie_dev == PCIE_A) {
ep_type0_hdr_regs = EP_A_BASE_ADDR;
ep_cfg_base_addr = EP_A_BASE_ADDR;
ep_cfg_addr_limit = EP_A_BASE_ADDR + 0x1fffff;
} else {
ep_type0_hdr_regs = EP_B_BASE_ADDR;
ep_cfg_base_addr = EP_B_BASE_ADDR;
ep_cfg_addr_limit = EP_B_BASE_ADDR + 0x1fffff;
}
target_base_addr = ep_cfg_base_addr & 0x3fffff;
type = TYPE_CFG0;
if (type == TYPE_CFG0 || type == TYPE_CFG1) {
target_base_addr = (0 & 0xff) << 24 |
(0 & 0x1F) << 19 |
(0 & 0x7) << 16;
} else if (type == TYPE_MEM)
target_base_addr &= (~0xfff);
dw_pcie_prog_outbound_atu(pcie_dev, PCIE_ATU_REGION_INDEX0,
type, ep_cfg_base_addr,
target_base_addr, ep_cfg_addr_limit);
writel(val, ep_type0_hdr_regs + where);
return 0;
}
static void amlogic_pcie_init_PLL(e_pcieDev pcie_dev)
{
if (pcie_dev == PCIE_A) {
*PCIE_PHY_BASE = 0x1d;
mdelay(1);
*PCIE_PHY_BASE = 0x1c;
mdelay(1);
*P_RESET0_LEVEL &= ~((0x3<<6) | (0x3<<1));
mdelay(1);
*PCIE_CTRL_6 &= ~(0X3 << 3);
*MIPI_CTRL &= ~((0x1 << 26) | (0x1 << 29));
mdelay(1);
*PCIE_CTRL_0 = 0x400106c8;
*PCIE_CTRL_1 = 0x0084a2aa;
*PCIE_CTRL_2 = 0xb75020be;
*PCIE_CTRL_3 = 0x0a47488e;
*PCIE_CTRL_4 = 0xc000004d;
*PCIE_CTRL_5 = 0x00078000;
*PCIE_CTRL_6 = 0x002323c6;
mdelay(1);
writel(readl(PCIE_CTRL_0) | 1<<29, PCIE_CTRL_0);
mdelay(1);
writel(readl(PCIE_CTRL_0) & ~(1<<29), PCIE_CTRL_0);
*P_RESET0_LEVEL |= (0x3<<6);
mdelay(1);
*CLK81_HIGH |= (0x1 << 26);
}
mdelay(1);
if (pcie_dev == PCIE_A)
*CLK81_LOW |= (0x1 << 16);
else
*CLK81_LOW |= (0x1 << 17);
mdelay(1);
if (pcie_dev == PCIE_A)
*P_RESET0_LEVEL |= (0x1 << 1);
else
*P_RESET0_LEVEL |= (0x1 << 2);
mdelay(1);
if (pcie_dev == PCIE_A)
*MIPI_CTRL |= ((0x1 << 26) | (0x1 << 29));
mdelay(1);
if (pcie_dev == PCIE_A)
*PCIE_CTRL_6 |= (0x1 << 4);
else
*PCIE_CTRL_6 |= (0x1 << 3);
mdelay(1);
}
static int amlogic_pcie_init_dw(e_pcieDev pcie_dev)
{
t_pl_region_regs *pl_region_regs;
t_type1_hdr_regs *type1_hdr_regs;
if (pcie_dev == PCIE_A) {
pl_region_regs = pcieA_pl_region_regs;
type1_hdr_regs = pcieA_type1_hdr_regs;
pcieA_user_cfg->cfg0 |= (1<<7);
} else {
pl_region_regs = pcieB_pl_region_regs;
type1_hdr_regs = pcieB_type1_hdr_regs;
pcieB_user_cfg->cfg0 |= (1<<7);
}
pl_region_regs->port_link_ctrl_off_reg |= 1<<7;
pl_region_regs->port_link_ctrl_off_reg &= ~(0x3f<<16);
pl_region_regs->port_link_ctrl_off_reg |= 0x1<<16;
pl_region_regs->gen2_ctrl_off_reg &= ~(0x1f<<8);
pl_region_regs->gen2_ctrl_off_reg |= 0x1<<8;
pl_region_regs->gen2_ctrl_off_reg |= (1<<17);
type1_hdr_regs->base_addr0_reg = 0x0;
type1_hdr_regs->base_addr1_reg = 0x0;
return 0;
}
void amlogic_wait_linkup(e_pcieDev pcie_dev)
{
t_pcie_user_status *user_status;
int smlh_up = 0;
int rdlh_up = 0;
int ltssm_up = 0;
int speed_okay = 0;
int current_data_rate;
int cnt = 0;
if (pcie_dev == PCIE_A)
user_status = pcieA_user_status;
else
user_status = pcieB_user_status;
while (smlh_up == 0 || rdlh_up == 0 ||
ltssm_up == 0 || speed_okay == 0) {
smlh_up = (user_status->status12 >> 6) & 0x1;
rdlh_up = (user_status->status12 >> 16) & 0x1;
ltssm_up = ((user_status->status12 >> 10)
& 0x1f) == 0x11 ? 1 : 0;
current_data_rate = (user_status->status17>> 7) & 0x1;
if (current_data_rate == PCIE_GEN2 ||
current_data_rate == PCIE_GEN1)
speed_okay = 1;
cnt++;
if (cnt >= WAIT_LINKUP_TIMEOUT) {
if (pcie_dev == PCIE_A)
printf("Error:PCIE A Wait linkup timeout.\n");
else
printf("Error:PCIE B Wait linkup timeout.\n");
return;
}
udelay(20);
}
if (pcie_dev == PCIE_A)
printf("PCIE A linkup success\n");
else
printf("PCIE B linkup success\n");
}
void amlogic_set_max_payload(e_pcieDev pcie_dev, int size)
{
int max_payload_size = 1;
switch (size) {
case 128:
max_payload_size = 0;
break;
case 256:
max_payload_size = 1;
break;
case 512:
max_payload_size = 2;
break;
case 1024:
max_payload_size = 3;
break;
case 2048:
max_payload_size = 4;
break;
case 4096:
max_payload_size = 5;
break;
}
if (pcie_dev == PCIE_A) {
pcieA_pcie_cap_regs->dev_ctrl_dev_stus_reg &= ~(0x7<<5);
pcieA_pcie_cap_regs->dev_ctrl_dev_stus_reg |= (max_payload_size<<5);
} else {
pcieB_pcie_cap_regs->dev_ctrl_dev_stus_reg &= ~(0x7<<5);
pcieB_pcie_cap_regs->dev_ctrl_dev_stus_reg |= (max_payload_size<<5);
}
}
void amlogic_set_max_rd_req_size(e_pcieDev pcie_dev, int size)
{
int max_rd_req_size = 1;
switch (size) {
case 128:
max_rd_req_size = 0;
break;
case 256:
max_rd_req_size = 1;
break;
case 512:
max_rd_req_size = 2;
break;
case 1024:
max_rd_req_size = 3;
break;
case 2048:
max_rd_req_size = 4;
break;
case 4096:
max_rd_req_size = 5;
break;
}
if (pcie_dev == PCIE_A) {
pcieA_pcie_cap_regs->dev_ctrl_dev_stus_reg &= ~(0x7<<12);
pcieA_pcie_cap_regs->dev_ctrl_dev_stus_reg |= (max_rd_req_size<<12);
} else {
pcieB_pcie_cap_regs->dev_ctrl_dev_stus_reg &= ~(0x7<<12);
pcieB_pcie_cap_regs->dev_ctrl_dev_stus_reg |= (max_rd_req_size<<12);
}
}
void amlogic_configure_dsp_memory_map(e_pcieDev pcie_dev)
{
t_type1_hdr_regs *type1_hdr_regs;
u32 io_base_addr;
u32 io_limit;
u32 mem_base_addr;
u32 mem_limit;
u32 pref_mem_base_addr;
u32 pref_mem_limit;
if (pcie_dev == PCIE_A) {
type1_hdr_regs = pcieA_type1_hdr_regs;
io_base_addr = PCIE_A_IO_BASE_ADDR;
io_limit = PCIE_A_IO_LIMIT;
mem_base_addr = PCIE_A_MEM_BASE_ADDR;
mem_limit = PCIE_A_MEM_LIMIT;
pref_mem_base_addr = PCIE_A_PREF_MEM_BASE_ADDR;
pref_mem_limit = PCIE_A_PREF_MEM_LIMIT;
} else {
type1_hdr_regs = pcieB_type1_hdr_regs;
io_base_addr = PCIE_B_IO_BASE_ADDR;
io_limit = PCIE_B_IO_LIMIT;
mem_base_addr = PCIE_B_MEM_BASE_ADDR;
mem_limit = PCIE_B_MEM_LIMIT;
pref_mem_base_addr = PCIE_B_PREF_MEM_BASE_ADDR;
pref_mem_limit = PCIE_B_PREF_MEM_LIMIT;
}
type1_hdr_regs->io_limit_base_reg = (((io_base_addr >> 12) & 0xF) << 4)
| (1 << 0) | (1 << 8) |
(((io_limit >> 12) & 0XF) << 12);
type1_hdr_regs->io_limit_base_upper_reg = ((io_base_addr >> 16) & 0xFFFF)
<< 0 | ((io_limit >> 16) & 0XFFFF) << 16;
type1_hdr_regs->pref_mem_limit_base_reg = 0x00010001;
type1_hdr_regs->pref_base_upper_reg = 0;
type1_hdr_regs->pref_limit_upper_reg = 0;
type1_hdr_regs->pref_mem_limit_base_reg =
((pref_mem_base_addr >> 20) & 0xFFF) << 4 |
((pref_mem_limit >> 20) & 0xFFF) << 20;
type1_hdr_regs->mem_limit_base_reg =
((mem_base_addr >> 20) & 0xFFF) << 4 |
((mem_limit >> 20) & 0xFFF) << 20;
}
void amlogic_enable_memory_space(e_pcieDev pcie_dev)
{
t_type1_hdr_regs *type1_hdr_regs;
if (pcie_dev == PCIE_A)
type1_hdr_regs = pcieA_type1_hdr_regs;
else
type1_hdr_regs = pcieB_type1_hdr_regs;
type1_hdr_regs->stus_cmd_reg = 7;
}
static int amlogic_pcie_link_up(e_pcieDev pcie_dev)
{
amlogic_pcie_init_dw(pcie_dev);
amlogic_set_max_payload(pcie_dev, 256);
amlogic_set_max_rd_req_size(pcie_dev, 256);
amlogic_pcie_regions_setup(pcie_dev);
amlogic_configure_dsp_memory_map(pcie_dev);
amlogic_enable_memory_space(pcie_dev);
amlogic_pcie_init_reset_pin(pcie_dev);
amlogic_wait_linkup(pcie_dev);
return 0;
}
void amlogic_pcie_PLL_disable(void)
{
amlogic_pcie_disable();
*PCIE_PHY_BASE = 0x1d;
mdelay(1);
*P_RESET0_LEVEL &= ~((0x3<<6) | (0x3<<1));
mdelay(1);
*PCIE_CTRL_0 = 0x80000000;
*PCIE_CTRL_1 = 0x0;
*PCIE_CTRL_2 = 0x0;
*PCIE_CTRL_3 = 0x0;
*PCIE_CTRL_4 = 0x0;
*PCIE_CTRL_5 = 0x0;
*PCIE_CTRL_6 = 0x0;
*CLK81_LOW &= ~(0x1 << 16);
*CLK81_LOW &= ~(0x1 << 17);
*MIPI_CTRL &= ~((0x1 << 26) | (0x1 << 29));
*PCIE_CTRL_6 &= ~(0X3 << 3);
}
void amlogic_pcie_init(e_pcieDev pcie_dev)
{
/* Static instance of the controller. */
static struct pci_controller pcc;
struct pci_controller *hose = &pcc;
int ret;
amlogic_pcie_init_PLL(pcie_dev);
memset(&pcc, 0, sizeof(pcc));
if (pcie_dev == PCIE_A) {
/* PCI I/O space */
pci_set_region(&hose->regions[0],
PCIE_A_IO_BASE_ADDR, PCIE_A_IO_BASE_ADDR,
0xff, PCI_REGION_IO);
/* PCI memory space */
pci_set_region(&hose->regions[1],
PCIE_A_MEM_BASE_ADDR, PCIE_A_MEM_BASE_ADDR,
0x6fffffff, PCI_REGION_MEM);
/* System memory space */
pci_set_region(&hose->regions[2],
EP_A_PREF_MEM_BASE_ADDR, EP_A_PREF_MEM_BASE_ADDR,
0x7fffff, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
} else {
/* PCI I/O space */
pci_set_region(&hose->regions[0],
PCIE_B_IO_BASE_ADDR, PCIE_B_IO_BASE_ADDR,
0xff, PCI_REGION_IO);
/* PCI memory space */
pci_set_region(&hose->regions[1],
PCIE_B_MEM_BASE_ADDR, PCIE_B_MEM_BASE_ADDR,
0x6fffffff, PCI_REGION_MEM);
/* System memory space */
pci_set_region(&hose->regions[2],
EP_B_PREF_MEM_BASE_ADDR, EP_B_PREF_MEM_BASE_ADDR,
0x7fffff, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
}
hose->region_count = 3;
pcie_type = pcie_dev;
hose->priv_data = &pcie_type;
pci_set_ops(hose,
pci_hose_read_config_byte_via_dword,
pci_hose_read_config_word_via_dword,
amlogic_pcie_read_config,
pci_hose_write_config_byte_via_dword,
pci_hose_write_config_word_via_dword,
amlogic_pcie_write_config);
/* Start the controller. */
ret = amlogic_pcie_link_up(pcie_dev);
if (!ret) {
pci_register_hose(hose);
hose->last_busno = pci_hose_scan(hose);
}
if (pcie_dev == PCIE_B)
amlogic_pcie_PLL_disable();
}
/* Probe function. */
void pci_init_board(void)
{
amlogic_pcie_init(PCIE_A);
amlogic_pcie_init(PCIE_B);
}