mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-10 23:29:46 +00:00
Merge branch 'pci/controller/broadcom'
- Add DT property "brcm,clkreq-mode" and driver support for different CLKREQ# modes (Jim Quinlan) * pci/controller/broadcom: PCI: brcmstb: Configure HW CLKREQ# mode appropriate for downstream device dt-bindings: PCI: brcmstb: Add property "brcm,clkreq-mode"
This commit is contained in:
commit
6f77f0ac5e
@ -64,6 +64,24 @@ properties:
|
||||
|
||||
aspm-no-l0s: true
|
||||
|
||||
brcm,clkreq-mode:
|
||||
description: A string that determines the operating
|
||||
clkreq mode of the PCIe RC HW with respect to controlling the refclk
|
||||
signal. There are three different modes -- "safe", which drives the
|
||||
refclk signal unconditionally and will work for all devices but does
|
||||
not provide any power savings; "no-l1ss" -- which provides Clock
|
||||
Power Management, L0s, and L1, but cannot provide L1 substate (L1SS)
|
||||
power savings. If the downstream device connected to the RC is L1SS
|
||||
capable AND the OS enables L1SS, all PCIe traffic may abruptly halt,
|
||||
potentially hanging the system; "default" -- which provides L0s, L1,
|
||||
and L1SS, but not compliant to provide Clock Power Management;
|
||||
specifically, may not be able to meet the T_CLRon max timing of 400ns
|
||||
as specified in "Dynamic Clock Control", section 3.2.5.2.2 PCI
|
||||
Express Mini CEM 2.1 specification. This situation is atypical and
|
||||
should happen only with older devices.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [ safe, no-l1ss, default ]
|
||||
|
||||
brcm,scb-sizes:
|
||||
description: u64 giving the 64bit PCIe memory
|
||||
viewport size of a memory controller. There may be up to
|
||||
|
@ -48,6 +48,9 @@
|
||||
#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc
|
||||
#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00
|
||||
|
||||
#define PCIE_RC_CFG_PRIV1_ROOT_CAP 0x4f8
|
||||
#define PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK 0xf8
|
||||
|
||||
#define PCIE_RC_DL_MDIO_ADDR 0x1100
|
||||
#define PCIE_RC_DL_MDIO_WR_DATA 0x1104
|
||||
#define PCIE_RC_DL_MDIO_RD_DATA 0x1108
|
||||
@ -121,9 +124,12 @@
|
||||
|
||||
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204
|
||||
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2
|
||||
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK 0x200000
|
||||
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000
|
||||
#define PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x00800000
|
||||
|
||||
#define PCIE_CLKREQ_MASK \
|
||||
(PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK | \
|
||||
PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK)
|
||||
|
||||
#define PCIE_INTR2_CPU_BASE 0x4300
|
||||
#define PCIE_MSI_INTR2_BASE 0x4500
|
||||
@ -1028,13 +1034,89 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This extends the timeout period for an access to an internal bus. This
|
||||
* access timeout may occur during L1SS sleep periods, even without the
|
||||
* presence of a PCIe access.
|
||||
*/
|
||||
static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie)
|
||||
{
|
||||
/* TIMEOUT register is two registers before RGR1_SW_INIT_1 */
|
||||
const unsigned int REG_OFFSET = PCIE_RGR1_SW_INIT_1(pcie) - 8;
|
||||
u32 timeout_us = 4000000; /* 4 seconds, our setting for L1SS */
|
||||
|
||||
/* Each unit in timeout register is 1/216,000,000 seconds */
|
||||
writel(216 * timeout_us, pcie->base + REG_OFFSET);
|
||||
}
|
||||
|
||||
static void brcm_config_clkreq(struct brcm_pcie *pcie)
|
||||
{
|
||||
static const char err_msg[] = "invalid 'brcm,clkreq-mode' DT string\n";
|
||||
const char *mode = "default";
|
||||
u32 clkreq_cntl;
|
||||
int ret, tmp;
|
||||
|
||||
ret = of_property_read_string(pcie->np, "brcm,clkreq-mode", &mode);
|
||||
if (ret && ret != -EINVAL) {
|
||||
dev_err(pcie->dev, err_msg);
|
||||
mode = "safe";
|
||||
}
|
||||
|
||||
/* Start out assuming safe mode (both mode bits cleared) */
|
||||
clkreq_cntl = readl(pcie->base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
|
||||
clkreq_cntl &= ~PCIE_CLKREQ_MASK;
|
||||
|
||||
if (strcmp(mode, "no-l1ss") == 0) {
|
||||
/*
|
||||
* "no-l1ss" -- Provides Clock Power Management, L0s, and
|
||||
* L1, but cannot provide L1 substate (L1SS) power
|
||||
* savings. If the downstream device connected to the RC is
|
||||
* L1SS capable AND the OS enables L1SS, all PCIe traffic
|
||||
* may abruptly halt, potentially hanging the system.
|
||||
*/
|
||||
clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
|
||||
/*
|
||||
* We want to un-advertise L1 substates because if the OS
|
||||
* tries to configure the controller into using L1 substate
|
||||
* power savings it may fail or hang when the RC HW is in
|
||||
* "no-l1ss" mode.
|
||||
*/
|
||||
tmp = readl(pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP);
|
||||
u32p_replace_bits(&tmp, 2, PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK);
|
||||
writel(tmp, pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP);
|
||||
|
||||
} else if (strcmp(mode, "default") == 0) {
|
||||
/*
|
||||
* "default" -- Provides L0s, L1, and L1SS, but not
|
||||
* compliant to provide Clock Power Management;
|
||||
* specifically, may not be able to meet the Tclron max
|
||||
* timing of 400ns as specified in "Dynamic Clock Control",
|
||||
* section 3.2.5.2.2 of the PCIe spec. This situation is
|
||||
* atypical and should happen only with older devices.
|
||||
*/
|
||||
clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK;
|
||||
brcm_extend_rbus_timeout(pcie);
|
||||
|
||||
} else {
|
||||
/*
|
||||
* "safe" -- No power savings; refclk is driven by RC
|
||||
* unconditionally.
|
||||
*/
|
||||
if (strcmp(mode, "safe") != 0)
|
||||
dev_err(pcie->dev, err_msg);
|
||||
mode = "safe";
|
||||
}
|
||||
writel(clkreq_cntl, pcie->base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
|
||||
|
||||
dev_info(pcie->dev, "clkreq-mode set to %s\n", mode);
|
||||
}
|
||||
|
||||
static int brcm_pcie_start_link(struct brcm_pcie *pcie)
|
||||
{
|
||||
struct device *dev = pcie->dev;
|
||||
void __iomem *base = pcie->base;
|
||||
u16 nlw, cls, lnksta;
|
||||
bool ssc_good = false;
|
||||
u32 tmp;
|
||||
int ret, i;
|
||||
|
||||
/* Unassert the fundamental reset */
|
||||
@ -1059,6 +1141,8 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
brcm_config_clkreq(pcie);
|
||||
|
||||
if (pcie->gen)
|
||||
brcm_pcie_set_gen(pcie, pcie->gen);
|
||||
|
||||
@ -1077,14 +1161,6 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie)
|
||||
pci_speed_string(pcie_link_speed[cls]), nlw,
|
||||
ssc_good ? "(SSC)" : "(!SSC)");
|
||||
|
||||
/*
|
||||
* Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1
|
||||
* is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1.
|
||||
*/
|
||||
tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
|
||||
tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
|
||||
writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user