Hi Kishon, Thanks for the comments! On 01/21/2015 11:11 AM, Kishon Vijay Abraham I wrote: > Hi, > > On Friday 12 December 2014 10:43 PM, Stanimir Varbanov wrote: >> Add a PCIe PHY driver used by PCIe host controller driver >> on Qualcomm SoCs like Snapdragon 805. >> >> Signed-off-by: Stanimir Varbanov <svarbanov@xxxxxxxxxx> >> --- >> drivers/phy/Kconfig | 7 + >> drivers/phy/Makefile | 1 + >> drivers/phy/phy-qcom-pcie.c | 311 +++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 319 insertions(+), 0 deletions(-) >> create mode 100644 drivers/phy/phy-qcom-pcie.c >> >> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig >> index 2a436e6..135bdcc 100644 >> --- a/drivers/phy/Kconfig >> +++ b/drivers/phy/Kconfig >> @@ -218,6 +218,13 @@ config PHY_QCOM_IPQ806X_SATA >> depends on OF >> select GENERIC_PHY >> >> +config PHY_QCOM_PCIE >> + tristate "Qualcomm PCIe SerDes/PHY driver" >> + depends on ARCH_QCOM >> + depends on HAS_IOMEM >> + depends on OF >> + select GENERIC_PHY > > Please add a small description about the driver here. Sure I will. <snip> >> +static const struct phy_regs pcie_phy_regs[] = { >> + { PCIE_PHY_POWER_DOWN_CONTROL, 0x03 }, >> + { QSERDES_COM_SYSCLK_EN_SEL, 0x08 }, >> + { QSERDES_COM_DEC_START1, 0x82 }, >> + { QSERDES_COM_DEC_START2, 0x03 }, >> + { QSERDES_COM_DIV_FRAC_START1, 0xd5 }, >> + { QSERDES_COM_DIV_FRAC_START2, 0xaa }, >> + { QSERDES_COM_DIV_FRAC_START3, 0x13 }, >> + { QSERDES_COM_PLLLOCK_CMP_EN, 0x01 }, >> + { QSERDES_COM_PLLLOCK_CMP1, 0x2b }, >> + { QSERDES_COM_PLLLOCK_CMP2, 0x68 }, >> + { QSERDES_COM_PLL_CRCTRL, 0xff }, >> + { QSERDES_COM_PLL_CP_SETI, 0x3f }, >> + { QSERDES_COM_PLL_IP_SETP, 0x07 }, >> + { QSERDES_COM_PLL_CP_SETP, 0x03 }, >> + { QSERDES_RX_CDR_CONTROL, 0xf3 }, >> + { QSERDES_RX_CDR_CONTROL2, 0x6b }, >> + { QSERDES_COM_RESETSM_CNTRL, 0x10 }, >> + { QSERDES_RX_RX_TERM_HIGHZ_CM_AC_COUPLE, 0x87 }, >> + { QSERDES_RX_RX_EQ_GAIN12, 0x54 }, >> + { PCIE_PHY_POWER_STATE_CONFIG1, 0xa3 }, >> + { PCIE_PHY_POWER_STATE_CONFIG2, 0xcb }, >> + { QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10 }, >> + { PCIE_PHY_ENDPOINT_REFCLK_DRIVE, 0x10 }, >> + { PCIE_PHY_SW_RESET, 0x00 }, >> + { PCIE_PHY_START, 0x03 }, > > No magic values for register writes. Unfortunately these register values are taken as they are in CAF downstream kernel and there are no bit/fields description for them. >> +}; >> + >> +static void qcom_pcie_phy_init(struct qcom_pcie_phy *pcie) >> +{ >> + const struct phy_regs *regs = pcie_phy_regs; >> + int i; >> + >> + for (i = 0; i < ARRAY_SIZE(pcie_phy_regs); i++) >> + writel(regs[i].val, pcie->base + regs[i].reg_offset); >> +} >> + >> +static bool qcom_pcie_phy_is_ready(struct qcom_pcie_phy *pcie) >> +{ >> + u32 val = readl(pcie->base + PCIE_PHY_PCS_STATUS); >> + >> + return val & BIT(6) ? false : true; >> +} >> + >> +static int qcom_pcie_phy_power_on(struct phy *phy) >> +{ >> + struct qcom_pcie_phy *pcie = phy_get_drvdata(phy); >> + struct device *dev = pcie->dev; >> + int ret, retries; >> + >> + ret = regulator_enable(pcie->vdda_pll); >> + if (ret) { >> + dev_err(dev, "cannot enable vdda_pll regulator\n"); >> + return ret; >> + } >> + >> + ret = regulator_enable(pcie->vdda); >> + if (ret) { >> + dev_err(dev, "cannot enable vdda regulator\n"); >> + goto fail_vdda_pll; >> + } >> + >> + ret = reset_control_deassert(pcie->res_phy); >> + if (ret) { >> + dev_err(dev, "cannot deassert phy reset\n"); >> + goto fail_vdda; >> + } >> + >> + qcom_pcie_phy_init(pcie); >> + >> + usleep_range(PHY_DELAY_MIN_US, PHY_DELAY_MAX_US); > > add a comment on why this delay is required. Actually this delay is not required anymore and I will remove it in next version. The delay which is important here is the delay between clk_set_rate and clk_prepare_enable. >> + >> + ret = clk_set_rate(pcie->clk, ~0); > > What is the actual clock rate? According to clk freq table in clock driver it could be 125Mhz or 250Mhz. >> + if (ret) { >> + dev_err(dev, "cannot set pipe clk rate\n"); >> + goto fail_res; >> + } >> + >> + /* >> + * setting pipe rate takes time, try arbitrary delay before enabling >> + * the clock >> + */ >> + retries = PIPE_CLK_RETRIES_COUNT; >> + do { >> + usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US); >> + >> + ret = clk_prepare_enable(pcie->clk); >> + if (!ret) >> + break; >> + } while (retries--); >> + >> + if (retries < 0) { >> + dev_err(dev, "cannot enable phy clock\n"); >> + goto fail_res; >> + } >> + >> + retries = PHY_RETRIES_COUNT; >> + do { >> + ret = qcom_pcie_phy_is_ready(pcie); >> + if (ret) >> + break; >> + usleep_range(PHY_DELAY_MIN_US, PHY_DELAY_MAX_US); >> + } while (retries--); >> + >> + if (retries < 0) { >> + dev_err(dev, "phy failed to come up\n"); >> + ret = -ETIMEDOUT; >> + goto fail; >> + } >> + >> + return 0; >> + >> +fail: >> + clk_disable_unprepare(pcie->clk); >> +fail_res: >> + reset_control_assert(pcie->res_phy); >> +fail_vdda: >> + regulator_disable(pcie->vdda); >> +fail_vdda_pll: >> + regulator_disable(pcie->vdda_pll); >> + >> + return ret; >> +} >> + <snip> >> + >> + phy = devm_phy_create(dev, NULL, &qcom_pcie_phy_ops, NULL); > > Please rebase it to the latest kernel. Already done. -- regards, Stan -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html