drivers: pcie: add brcmstb pcie controller driver
Add PCIe controller driver for brcmstb, required by Raspberry Pi 5. Signed-off-by: Junho Lee <junho@tsnlab.com>
This commit is contained in:
parent
c85c8d33d3
commit
5edfd02691
7 changed files with 673 additions and 0 deletions
|
|
@ -1,2 +1,3 @@
|
||||||
add_subdirectory_ifdef(CONFIG_PCIE host)
|
add_subdirectory_ifdef(CONFIG_PCIE host)
|
||||||
add_subdirectory_ifdef(CONFIG_PCIE_ENDPOINT endpoint)
|
add_subdirectory_ifdef(CONFIG_PCIE_ENDPOINT endpoint)
|
||||||
|
add_subdirectory_ifdef(CONFIG_PCIE_CONTROLLER controller)
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,4 @@
|
||||||
|
|
||||||
source "drivers/pcie/host/Kconfig"
|
source "drivers/pcie/host/Kconfig"
|
||||||
source "drivers/pcie/endpoint/Kconfig"
|
source "drivers/pcie/endpoint/Kconfig"
|
||||||
|
source "drivers/pcie/controller/Kconfig"
|
||||||
|
|
|
||||||
5
drivers/pcie/controller/CMakeLists.txt
Normal file
5
drivers/pcie/controller/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_PCIE_BRCMSTB pcie_brcmstb.c)
|
||||||
10
drivers/pcie/controller/Kconfig
Normal file
10
drivers/pcie/controller/Kconfig
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# PCIe controller configuration options
|
||||||
|
|
||||||
|
# Copyright 2024 TSN Lab, Inc.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
if PCIE_CONTROLLER
|
||||||
|
|
||||||
|
source "drivers/pcie/controller/Kconfig.brcmstb"
|
||||||
|
|
||||||
|
endif # PCIE_CONTROLLER
|
||||||
9
drivers/pcie/controller/Kconfig.brcmstb
Normal file
9
drivers/pcie/controller/Kconfig.brcmstb
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Copyright (c) 2024 Junho Lee <junho@tsnlab.com>
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config PCIE_BRCMSTB
|
||||||
|
bool "Broadcom Set-top box SoC PCIe Driver"
|
||||||
|
default y
|
||||||
|
depends on DT_HAS_BRCM_BRCMSTB_PCIE_ENABLED
|
||||||
|
help
|
||||||
|
Enable Driver for Broadcom Set-top box SoC PCIe controllers.
|
||||||
635
drivers/pcie/controller/pcie_brcmstb.c
Normal file
635
drivers/pcie/controller/pcie_brcmstb.c
Normal file
|
|
@ -0,0 +1,635 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Junho Lee <junho@tsnlab.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(pcie_brcmstb, LOG_LEVEL_ERR);
|
||||||
|
|
||||||
|
#include <zephyr/arch/common/sys_bitops.h>
|
||||||
|
#include <zephyr/arch/cpu.h>
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/math/ilog2.h>
|
||||||
|
#include <zephyr/sys/device_mmio.h>
|
||||||
|
|
||||||
|
#include <zephyr/drivers/pcie/pcie.h>
|
||||||
|
#include <zephyr/drivers/pcie/controller.h>
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT brcm_brcmstb_pcie
|
||||||
|
|
||||||
|
#define PCIE_RC_PL_PHY_CTL_15 0x184c
|
||||||
|
#define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK 0xff
|
||||||
|
|
||||||
|
#define PCIE_MISC_MISC_CTRL 0x4008
|
||||||
|
#define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000
|
||||||
|
#define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000
|
||||||
|
#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000
|
||||||
|
#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_LSB 20
|
||||||
|
#define PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK 0xf8000000
|
||||||
|
#define PCIE_MISC_MISC_CTRL_SCB0_SIZE_LSB 27
|
||||||
|
|
||||||
|
#define PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK 0x1f
|
||||||
|
|
||||||
|
#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c
|
||||||
|
#define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f
|
||||||
|
|
||||||
|
#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034
|
||||||
|
#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f
|
||||||
|
#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_LSB 0
|
||||||
|
#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038
|
||||||
|
|
||||||
|
#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
|
||||||
|
#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
|
||||||
|
|
||||||
|
#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4
|
||||||
|
#define PCIE_MISC_RC_BAR4_CONFIG_HI 0x40d8
|
||||||
|
|
||||||
|
#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE 0x1
|
||||||
|
#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK 0xfffff000
|
||||||
|
#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK 0xff
|
||||||
|
|
||||||
|
#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4
|
||||||
|
#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK 0x1
|
||||||
|
|
||||||
|
#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO 0x410c
|
||||||
|
#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI 0x4110
|
||||||
|
|
||||||
|
#define PCIE_MISC_UBUS_CTRL 0x40a4
|
||||||
|
#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK 0x2000
|
||||||
|
#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK 0x80000
|
||||||
|
|
||||||
|
#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170
|
||||||
|
#define PCIE_MISC_UBUS_TIMEOUT 0x40a8
|
||||||
|
#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c
|
||||||
|
|
||||||
|
#define PCIE_MISC_PCIE_CTRL 0x4064
|
||||||
|
#define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK 0x4
|
||||||
|
|
||||||
|
#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c
|
||||||
|
#define PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff
|
||||||
|
|
||||||
|
#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1 0x0188
|
||||||
|
#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc
|
||||||
|
#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_LSB 2
|
||||||
|
#define PCIE_RC_CFG_VENDOR_SPECIFIC_REG1_LITTLE_ENDIAN 0x0
|
||||||
|
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010
|
||||||
|
#define PCIE_MEM_WIN0_LO(win) (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + (win) * 8)
|
||||||
|
#define PCIE_MEM_WIN0_HI(win) (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + (win) * 8)
|
||||||
|
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_LSB 20
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK 0xfff0
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_LSB 4
|
||||||
|
|
||||||
|
#define PCIE_MEM_WIN0_BASE_LIMIT(win) (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + (win) * 4)
|
||||||
|
|
||||||
|
/* Hamming weight of PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK */
|
||||||
|
#define HIGH_ADDR_SHIFT 12
|
||||||
|
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI 0x4080
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK 0xff
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_LSB 0
|
||||||
|
|
||||||
|
#define PCIE_MEM_WIN0_BASE_HI(win) (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + (win) * 8)
|
||||||
|
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI 0x4084
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK 0xff
|
||||||
|
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_LSB 0
|
||||||
|
|
||||||
|
#define PCIE_MEM_WIN0_LIMIT_HI(win) (PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + (win) * 8)
|
||||||
|
|
||||||
|
#define PCIE_EXT_CFG_DATA 0x8000
|
||||||
|
#define PCIE_EXT_CFG_INDEX 0x9000
|
||||||
|
|
||||||
|
#define PCI_BASE_ADDRESS_0 0x10
|
||||||
|
|
||||||
|
#define PCI_COMMAND 0x0004
|
||||||
|
#define PCI_COMMAND_MEMORY 0x2
|
||||||
|
#define PCI_COMMAND_MASTER 0x4
|
||||||
|
|
||||||
|
#define PCI_EXP_LNKCAP 0x0c
|
||||||
|
#define PCI_EXP_LNKCAP_SLS 0xf
|
||||||
|
#define PCI_EXP_LNKCTL2 0x30
|
||||||
|
|
||||||
|
#define BRCM_PCIE_CAP_REGS 0x00ac
|
||||||
|
|
||||||
|
#define BCM2712_RC_BAR2_SIZE 0x400000
|
||||||
|
#define BCM2712_RC_BAR2_OFFSET 0x0
|
||||||
|
#define BCM2712_RC_BAR4_CPU 0x0
|
||||||
|
#define BCM2712_RC_BAR4_SIZE 0x0
|
||||||
|
#define BCM2712_RC_BAR4_PCI 0x0
|
||||||
|
#define BCM2712_SCB0_SIZE 0x400000
|
||||||
|
|
||||||
|
#define BCM2712_BURST_SIZE 0x1
|
||||||
|
|
||||||
|
#define BCM2712_CLOCK_RATE 750000000ULL /* 750Mhz */
|
||||||
|
|
||||||
|
#define BCM2712_UBUS_TIMEOUT_NS 250000000ULL /* 250ms */
|
||||||
|
#define BCM2712_UBUS_TIMEOUT_TICKS (BCM2712_UBUS_TIMEOUT_NS * BCM2712_CLOCK_RATE / 1000000000ULL)
|
||||||
|
|
||||||
|
#define BCM2712_RC_CONFIG_RETRY_TIMEOUT_NS 240000000ULL /* 240ms */
|
||||||
|
#define BCM2712_RC_CONFIG_RETRY_TIMEOUT_TICKS \
|
||||||
|
(BCM2712_RC_CONFIG_RETRY_TIMEOUT_NS * BCM2712_CLOCK_RATE / 1000000000ULL)
|
||||||
|
|
||||||
|
#define BCM2712_PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE 0x060400
|
||||||
|
|
||||||
|
#define MDIO_DATA_DONE_MASK 0x80000000
|
||||||
|
#define MDIO_CMD_WRITE 0x0
|
||||||
|
#define MDIO_PORT0 0x0
|
||||||
|
|
||||||
|
#define PCIE_RC_DL_MDIO_ADDR 0x1100
|
||||||
|
#define PCIE_RC_DL_MDIO_WR_DATA 0x1104
|
||||||
|
#define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD 0x12 /* 18.52ns as ticks */
|
||||||
|
|
||||||
|
#define SET_ADDR_OFFSET 0x1f
|
||||||
|
|
||||||
|
#define DMA_RANGES_IDX 2
|
||||||
|
|
||||||
|
#define PCIE_ECAM_BDF_SHIFT 12
|
||||||
|
|
||||||
|
#define BAR_MAX 8
|
||||||
|
|
||||||
|
#define SZ_1M 0x100000
|
||||||
|
|
||||||
|
struct pcie_brcmstb_config {
|
||||||
|
const struct pcie_ctrl_config *common;
|
||||||
|
size_t regs_count;
|
||||||
|
struct {
|
||||||
|
uintptr_t addr;
|
||||||
|
size_t size;
|
||||||
|
} regs[BAR_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum pcie_region_type {
|
||||||
|
PCIE_REGION_IO = 0,
|
||||||
|
PCIE_REGION_MEM,
|
||||||
|
PCIE_REGION_MEM64,
|
||||||
|
PCIE_REGION_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pcie_brcmstb_data {
|
||||||
|
uintptr_t cfg_phys_addr;
|
||||||
|
mm_reg_t cfg_addr;
|
||||||
|
size_t cfg_size;
|
||||||
|
struct {
|
||||||
|
uintptr_t phys_start;
|
||||||
|
uintptr_t bus_start;
|
||||||
|
size_t size;
|
||||||
|
size_t allocation_offset;
|
||||||
|
} regions[PCIE_REGION_MAX];
|
||||||
|
size_t bar_cnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint32_t lower_32_bits(uint64_t val)
|
||||||
|
{
|
||||||
|
return val & 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t upper_32_bits(uint64_t val)
|
||||||
|
{
|
||||||
|
return (val >> 32) & 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t encode_ibar_size(uint64_t size)
|
||||||
|
{
|
||||||
|
uint32_t tmp;
|
||||||
|
uint32_t size_upper = (uint32_t)(size >> 32);
|
||||||
|
|
||||||
|
if (size_upper > 0) {
|
||||||
|
tmp = ilog2(size_upper) + 32;
|
||||||
|
} else {
|
||||||
|
tmp = ilog2(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp >= 12 && tmp <= 15) {
|
||||||
|
return (tmp - 12) + 0x1c;
|
||||||
|
} else if (tmp >= 16 && tmp <= 36) {
|
||||||
|
return tmp - 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mm_reg_t pcie_brcmstb_map_bus(const struct device *dev, pcie_bdf_t bdf, unsigned int reg)
|
||||||
|
{
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
|
||||||
|
sys_write32(bdf << PCIE_ECAM_BDF_SHIFT, data->cfg_addr + PCIE_EXT_CFG_INDEX);
|
||||||
|
return data->cfg_addr + PCIE_EXT_CFG_DATA + reg * sizeof(uint32_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t pcie_brcmstb_conf_read(const struct device *dev, pcie_bdf_t bdf, unsigned int reg)
|
||||||
|
{
|
||||||
|
mm_reg_t conf_addr = pcie_brcmstb_map_bus(dev, bdf, reg);
|
||||||
|
|
||||||
|
if (!conf_addr) {
|
||||||
|
return 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sys_read32(conf_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcie_brcmstb_conf_write(const struct device *dev, pcie_bdf_t bdf, unsigned int reg,
|
||||||
|
uint32_t data)
|
||||||
|
{
|
||||||
|
mm_reg_t conf_addr = pcie_brcmstb_map_bus(dev, bdf, reg);
|
||||||
|
|
||||||
|
if (!conf_addr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_write32(data, conf_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum pcie_region_type pcie_brcmstb_determine_region_type(const struct device *dev,
|
||||||
|
bool mem, bool mem64)
|
||||||
|
{
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
|
||||||
|
if (!mem) {
|
||||||
|
return PCIE_REGION_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data->regions[PCIE_REGION_MEM64].size > 0) &&
|
||||||
|
(mem64 || data->regions[PCIE_REGION_MEM].size == 0)) {
|
||||||
|
return PCIE_REGION_MEM64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PCIE_REGION_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pcie_brcmstb_region_allocate_type(const struct device *dev, pcie_bdf_t bdf,
|
||||||
|
size_t bar_size, uintptr_t *bar_bus_addr,
|
||||||
|
enum pcie_region_type type)
|
||||||
|
{
|
||||||
|
const struct pcie_brcmstb_config *config = dev->config;
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
uintptr_t addr;
|
||||||
|
|
||||||
|
addr = (((data->regions[type].bus_start + config->regs[PCIE_BDF_TO_BUS(bdf) + 1].addr +
|
||||||
|
data->regions[type].allocation_offset) -
|
||||||
|
1) |
|
||||||
|
((bar_size)-1)) +
|
||||||
|
1;
|
||||||
|
|
||||||
|
if (addr + bar_size > data->regions[type].bus_start + data->regions[type].size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*bar_bus_addr = addr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pcie_brcmstb_region_allocate(const struct device *dev, pcie_bdf_t bdf, bool mem,
|
||||||
|
bool mem64, size_t bar_size, uintptr_t *bar_bus_addr)
|
||||||
|
{
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
enum pcie_region_type type;
|
||||||
|
|
||||||
|
if (!mem && mem64) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mem && data->regions[PCIE_REGION_MEM64].size == 0 &&
|
||||||
|
data->regions[PCIE_REGION_MEM].size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mem && data->regions[PCIE_REGION_IO].size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = pcie_brcmstb_determine_region_type(dev, mem, mem64);
|
||||||
|
|
||||||
|
return pcie_brcmstb_region_allocate_type(dev, bdf, bar_size, bar_bus_addr, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pcie_brcmstb_region_get_allocate_base(const struct device *dev, pcie_bdf_t bdf,
|
||||||
|
bool mem, bool mem64, size_t align,
|
||||||
|
uintptr_t *bar_base_addr)
|
||||||
|
{
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
enum pcie_region_type type;
|
||||||
|
|
||||||
|
if (!mem && mem64) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mem && data->regions[PCIE_REGION_MEM64].size == 0 &&
|
||||||
|
data->regions[PCIE_REGION_MEM].size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mem && data->regions[PCIE_REGION_IO].size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = pcie_brcmstb_determine_region_type(dev, mem, mem64);
|
||||||
|
|
||||||
|
*bar_base_addr =
|
||||||
|
(((data->regions[type].bus_start + data->regions[type].allocation_offset) - 1) |
|
||||||
|
((align)-1)) +
|
||||||
|
1;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pcie_brcmstb_region_translate(const struct device *dev, pcie_bdf_t bdf, bool mem,
|
||||||
|
bool mem64, uintptr_t bar_bus_addr, uintptr_t *bar_addr)
|
||||||
|
{
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
enum pcie_region_type type;
|
||||||
|
|
||||||
|
type = pcie_brcmstb_determine_region_type(dev, mem, mem64);
|
||||||
|
|
||||||
|
*bar_addr = data->regions[type].phys_start + (bar_bus_addr - data->regions[type].bus_start);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEVICE_API(pcie_ctrl, pcie_brcmstb_api) = {
|
||||||
|
.conf_read = pcie_brcmstb_conf_read,
|
||||||
|
.conf_write = pcie_brcmstb_conf_write,
|
||||||
|
.region_allocate = pcie_brcmstb_region_allocate,
|
||||||
|
.region_get_allocate_base = pcie_brcmstb_region_get_allocate_base,
|
||||||
|
.region_translate = pcie_brcmstb_region_translate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pcie_brcmstb_parse_regions(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct pcie_brcmstb_config *config = dev->config;
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
enum pcie_region_type type;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < DMA_RANGES_IDX; i++) {
|
||||||
|
switch ((config->common->ranges[i].flags >> 24) & 0x03) {
|
||||||
|
case 0x01:
|
||||||
|
type = PCIE_REGION_IO;
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
type = PCIE_REGION_MEM;
|
||||||
|
break;
|
||||||
|
case 0x03:
|
||||||
|
type = PCIE_REGION_MEM64;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data->regions[type].bus_start = config->common->ranges[i].pcie_bus_addr;
|
||||||
|
data->regions[type].phys_start = config->common->ranges[i].host_map_addr;
|
||||||
|
data->regions[type].size = config->common->ranges[i].map_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data->regions[PCIE_REGION_IO].size && !data->regions[PCIE_REGION_MEM].size &&
|
||||||
|
!data->regions[PCIE_REGION_MEM64].size) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static mm_reg_t pcie_brcmstb_mdio_from_pkt(int port, int regad, int cmd)
|
||||||
|
{
|
||||||
|
return (mm_reg_t)cmd << 20 | port << 16 | regad;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcie_brcmstb_mdio_write(mm_reg_t base, uint8_t port, uint8_t regad, uint16_t wrdata)
|
||||||
|
{
|
||||||
|
sys_write32(pcie_brcmstb_mdio_from_pkt(port, regad, MDIO_CMD_WRITE),
|
||||||
|
base + PCIE_RC_DL_MDIO_ADDR);
|
||||||
|
sys_write32(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcie_brcmstb_munge_pll(const struct device *dev)
|
||||||
|
{
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
uint8_t regs[] = {0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e};
|
||||||
|
uint16_t vals[] = {0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007};
|
||||||
|
|
||||||
|
pcie_brcmstb_mdio_write(data->cfg_addr, MDIO_PORT0, SET_ADDR_OFFSET, 0x1600);
|
||||||
|
for (i = 0; i < 7; i++) {
|
||||||
|
k_busy_wait(300);
|
||||||
|
pcie_brcmstb_mdio_write(data->cfg_addr, MDIO_PORT0, regs[i], vals[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcie_brcmstb_set_outbound_win(const struct device *dev, uint8_t win, uintptr_t cpu_addr,
|
||||||
|
uintptr_t pcie_addr, size_t size)
|
||||||
|
{
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
uint32_t cpu_addr_mb_high, limit_addr_mb_high;
|
||||||
|
uintptr_t cpu_addr_mb, limit_addr_mb;
|
||||||
|
uint32_t tmp, tmp2;
|
||||||
|
|
||||||
|
sys_write32(lower_32_bits(pcie_addr), data->cfg_addr + PCIE_MEM_WIN0_LO(win));
|
||||||
|
sys_write32(upper_32_bits(pcie_addr), data->cfg_addr + PCIE_MEM_WIN0_HI(win));
|
||||||
|
|
||||||
|
cpu_addr_mb = cpu_addr / SZ_1M;
|
||||||
|
limit_addr_mb = (cpu_addr + size - 1) / SZ_1M;
|
||||||
|
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MEM_WIN0_BASE_LIMIT(win));
|
||||||
|
tmp &= ~PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK;
|
||||||
|
tmp &= ~PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK;
|
||||||
|
tmp2 = (cpu_addr_mb << PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_LSB);
|
||||||
|
tmp2 &= PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK;
|
||||||
|
tmp |= tmp2;
|
||||||
|
tmp2 = (limit_addr_mb << PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_LSB);
|
||||||
|
tmp2 &= PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK;
|
||||||
|
tmp |= tmp2;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MEM_WIN0_BASE_LIMIT(win));
|
||||||
|
|
||||||
|
cpu_addr_mb_high = cpu_addr_mb >> HIGH_ADDR_SHIFT;
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MEM_WIN0_BASE_HI(win));
|
||||||
|
tmp &= ~PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK;
|
||||||
|
tmp |= (cpu_addr_mb_high << PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_LSB);
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MEM_WIN0_BASE_HI(win));
|
||||||
|
|
||||||
|
limit_addr_mb_high = limit_addr_mb >> HIGH_ADDR_SHIFT;
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MEM_WIN0_LIMIT_HI(win));
|
||||||
|
tmp &= ~PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK;
|
||||||
|
tmp |= (cpu_addr_mb_high << PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_LSB);
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MEM_WIN0_LIMIT_HI(win));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcie_brcmstb_setup(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct pcie_brcmstb_config *config = dev->config;
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
uint32_t tmp;
|
||||||
|
uint16_t tmp16;
|
||||||
|
|
||||||
|
/* This block is for BCM2712 only */
|
||||||
|
pcie_brcmstb_munge_pll(dev);
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_RC_PL_PHY_CTL_15);
|
||||||
|
tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK;
|
||||||
|
tmp |= PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_RC_PL_PHY_CTL_15);
|
||||||
|
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MISC_MISC_CTRL);
|
||||||
|
tmp |= PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK;
|
||||||
|
tmp |= PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK;
|
||||||
|
tmp &= ~PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK;
|
||||||
|
tmp |= (BCM2712_BURST_SIZE << PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_LSB);
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MISC_MISC_CTRL);
|
||||||
|
|
||||||
|
uint64_t rc_bar2_offset = config->common->ranges[DMA_RANGES_IDX].host_map_addr -
|
||||||
|
config->common->ranges[DMA_RANGES_IDX].pcie_bus_addr;
|
||||||
|
uint64_t rc_bar2_size = config->common->ranges[DMA_RANGES_IDX].map_length;
|
||||||
|
|
||||||
|
tmp = lower_32_bits(rc_bar2_offset);
|
||||||
|
tmp &= ~PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK;
|
||||||
|
tmp |= encode_ibar_size(rc_bar2_size) << PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_LSB;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MISC_RC_BAR2_CONFIG_LO);
|
||||||
|
sys_write32(upper_32_bits(rc_bar2_offset), data->cfg_addr + PCIE_MISC_RC_BAR2_CONFIG_HI);
|
||||||
|
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
|
||||||
|
tmp |= PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
|
||||||
|
|
||||||
|
/* Set SCB Size */
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MISC_MISC_CTRL);
|
||||||
|
tmp &= ~PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK;
|
||||||
|
tmp |= (ilog2(config->common->ranges[DMA_RANGES_IDX].map_length) - 15)
|
||||||
|
<< PCIE_MISC_MISC_CTRL_SCB0_SIZE_LSB;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MISC_MISC_CTRL);
|
||||||
|
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MISC_UBUS_CTRL);
|
||||||
|
tmp |= PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK;
|
||||||
|
tmp |= PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MISC_UBUS_CTRL);
|
||||||
|
sys_write32(0xffffffff, data->cfg_addr + PCIE_MISC_AXI_READ_ERROR_DATA);
|
||||||
|
|
||||||
|
/* Set timeouts */
|
||||||
|
sys_write32(BCM2712_UBUS_TIMEOUT_TICKS, data->cfg_addr + PCIE_MISC_UBUS_TIMEOUT);
|
||||||
|
sys_write32(BCM2712_RC_CONFIG_RETRY_TIMEOUT_TICKS,
|
||||||
|
data->cfg_addr + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT);
|
||||||
|
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MISC_RC_BAR1_CONFIG_LO);
|
||||||
|
tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MISC_RC_BAR1_CONFIG_LO);
|
||||||
|
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MISC_RC_BAR3_CONFIG_LO);
|
||||||
|
tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MISC_RC_BAR3_CONFIG_LO);
|
||||||
|
|
||||||
|
/* Set gen to 2 */
|
||||||
|
tmp16 = sys_read16(data->cfg_addr + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
|
||||||
|
tmp = sys_read32(data->cfg_addr + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
|
||||||
|
tmp &= ~PCI_EXP_LNKCAP_SLS;
|
||||||
|
tmp |= 0x2;
|
||||||
|
sys_write32(tmp, data->cfg_addr + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
|
||||||
|
tmp16 &= ~0xf;
|
||||||
|
tmp16 |= 0x2;
|
||||||
|
sys_write16(tmp16, data->cfg_addr + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
|
||||||
|
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_RC_CFG_PRIV1_ID_VAL3);
|
||||||
|
tmp &= ~PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK;
|
||||||
|
tmp |= BCM2712_PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_RC_CFG_PRIV1_ID_VAL3);
|
||||||
|
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
|
||||||
|
tmp &= ~PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK;
|
||||||
|
tmp |= PCIE_RC_CFG_VENDOR_SPECIFIC_REG1_LITTLE_ENDIAN
|
||||||
|
<< PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_LSB;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcie_brcmstb_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct pcie_brcmstb_config *config = dev->config;
|
||||||
|
struct pcie_brcmstb_data *data = dev->data;
|
||||||
|
uint32_t tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (config->common->ranges_count < DMA_RANGES_IDX) {
|
||||||
|
/* Workaround since macros for `dma-ranges` property is not available */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pcie_brcmstb_parse_regions(dev);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->cfg_phys_addr = config->common->cfg_addr;
|
||||||
|
data->cfg_size = config->common->cfg_size;
|
||||||
|
|
||||||
|
device_map(&data->cfg_addr, data->cfg_phys_addr, data->cfg_size, K_MEM_CACHE_NONE);
|
||||||
|
|
||||||
|
/* PCIe Setup */
|
||||||
|
pcie_brcmstb_setup(dev);
|
||||||
|
|
||||||
|
/* Assert PERST# */
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_MISC_PCIE_CTRL);
|
||||||
|
tmp |= PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_MISC_PCIE_CTRL);
|
||||||
|
|
||||||
|
k_busy_wait(500000);
|
||||||
|
|
||||||
|
/* Enable resources and bus-mastering */
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCI_COMMAND);
|
||||||
|
tmp |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCI_COMMAND);
|
||||||
|
|
||||||
|
for (int i = 0; i < DMA_RANGES_IDX; i++) {
|
||||||
|
pcie_brcmstb_set_outbound_win(dev, i, config->common->ranges[i].host_map_addr,
|
||||||
|
config->common->ranges[i].pcie_bus_addr,
|
||||||
|
config->common->ranges[i].map_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assign BARs */
|
||||||
|
/* TODO: It might be possible to do this without extra <regs> property */
|
||||||
|
for (int i = 1; i < config->regs_count; i++) {
|
||||||
|
sys_write32(config->regs[i].addr, data->cfg_addr + PCIE_EXT_CFG_DATA +
|
||||||
|
PCI_BASE_ADDRESS_0 + 0x4 * (i - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable resources */
|
||||||
|
tmp = sys_read32(data->cfg_addr + PCIE_EXT_CFG_DATA + PCI_COMMAND);
|
||||||
|
tmp |= PCI_COMMAND_MEMORY;
|
||||||
|
sys_write32(tmp, data->cfg_addr + PCIE_EXT_CFG_DATA + PCI_COMMAND);
|
||||||
|
k_busy_wait(500000);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PCIE_BRCMSTB_INIT(n) \
|
||||||
|
static struct pcie_brcmstb_data pcie_brcmstb_data_##n = {}; \
|
||||||
|
\
|
||||||
|
static const struct pcie_ctrl_config pcie_ctrl_cfg_##n = { \
|
||||||
|
.cfg_addr = DT_INST_REG_ADDR(n), \
|
||||||
|
.cfg_size = DT_INST_REG_SIZE(n), \
|
||||||
|
.ranges_count = DT_NUM_RANGES(DT_DRV_INST(n)), \
|
||||||
|
.ranges = {DT_FOREACH_RANGE(DT_DRV_INST(n), PCIE_RANGE_FORMAT)}, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
static const struct pcie_brcmstb_config pcie_brcmstb_cfg_##n = { \
|
||||||
|
.common = &pcie_ctrl_cfg_##n, \
|
||||||
|
.regs_count = DT_NUM_REGS(DT_DRV_INST(n)), \
|
||||||
|
.regs = \
|
||||||
|
{ \
|
||||||
|
{DT_REG_ADDR_BY_IDX(DT_DRV_INST(n), 0), \
|
||||||
|
DT_REG_SIZE_BY_IDX(DT_DRV_INST(n), 0)}, \
|
||||||
|
{DT_REG_ADDR_BY_IDX(DT_DRV_INST(n), 1), \
|
||||||
|
DT_REG_SIZE_BY_IDX(DT_DRV_INST(n), 1)}, \
|
||||||
|
{DT_REG_ADDR_BY_IDX(DT_DRV_INST(n), 2), \
|
||||||
|
DT_REG_SIZE_BY_IDX(DT_DRV_INST(n), 2)}, \
|
||||||
|
}, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(n, pcie_brcmstb_init, NULL, &pcie_brcmstb_data_##n, \
|
||||||
|
&pcie_brcmstb_cfg_##n, PRE_KERNEL_1, CONFIG_PCIE_INIT_PRIORITY, \
|
||||||
|
&pcie_brcmstb_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(PCIE_BRCMSTB_INIT)
|
||||||
12
dts/bindings/pcie/controller/brcm,brcmstb-pcie.yaml
Normal file
12
dts/bindings/pcie/controller/brcm,brcmstb-pcie.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Copyright (c) 2024 Junho Lee <junho@tsnlab.com>
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: BRCMSTB PCIe controller
|
||||||
|
|
||||||
|
compatible: "brcm,brcmstb-pcie"
|
||||||
|
|
||||||
|
include: [base.yaml, pcie-controller.yaml]
|
||||||
|
|
||||||
|
properties:
|
||||||
|
reg:
|
||||||
|
required: true
|
||||||
Loading…
Reference in a new issue