#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <dt-bindings/phy/phy.h>
struct k1_pcie_phy {
struct device *dev;
struct phy *phy;
void __iomem *regs;
u32 pcie_lanes;
struct clk *pll;
struct clk_hw pll_hw;
u32 type;
struct regmap *pmu;
};
#define CALIBRATION_TIMEOUT 500000
#define PLL_TIMEOUT 500000
#define POLL_DELAY 500
#define SYSCON_APMU "spacemit,apmu"
#define PMUA_USB_PHY_CTRL0 0x0110
#define COMBO_PHY_SEL BIT(3)
#define PCIE_CLK_RES_CTRL 0x03cc
#define PCIE_APP_HOLD_PHY_RST BIT(30)
#define PHY_LANE_OFFSET 0x0400
#define PCIE_PU_ADDR_CLK_CFG 0x0008
#define PLL_READY BIT(0)
#define CFG_INTERNAL_TIMER_ADJ GENMASK(10, 7)
#define TIMER_ADJ_USB 0x2
#define TIMER_ADJ_PCIE 0x6
#define CFG_SW_PHY_INIT_DONE BIT(11)
#define PCIE_RC_DONE_STATUS 0x0018
#define CFG_FORCE_RCV_RETRY BIT(10)
#define PCIE_RC_CAL_REG2 0x0020
#define RC_CAL_TOGGLE BIT(22)
#define CLKSEL GENMASK(31, 29)
#define CLKSEL_24M 0x3
#define PCIE_PU_PLL_1 0x0048
#define REF_100_WSSC BIT(12)
#define FREF_SEL GENMASK(15, 13)
#define FREF_24M 0x1
#define SSC_DEP_SEL GENMASK(19, 16)
#define SSC_DEP_NONE 0x0
#define SSC_DEP_5000PPM 0xa
#define PCIE_PU_PLL_2 0x004c
#define GEN_REF100 BIT(4)
#define PCIE_RX_REG1 0x0050
#define EN_RTERM BIT(3)
#define AFE_RTERM_REG GENMASK(11, 8)
#define PCIE_RX_REG2 0x0054
#define RX_RTERM_SEL BIT(5)
#define PCIE_LTSSM_DIS_ENTRY 0x005c
#define CFG_REFCLK_MODE GENMASK(9, 8)
#define RFCLK_MODE_DRIVER 0x1
#define OVRD_REFCLK_MODE BIT(10)
#define PCIE_TX_REG1 0x0064
#define TX_RTERM_REG GENMASK(15, 12)
#define TX_RTERM_SEL BIT(25)
#define USB3_TEST_CTRL 0x0068
#define PCIE_RCAL_RESULT 0x0084
#define RTERM_VALUE_RX GENMASK(3, 0)
#define RTERM_VALUE_TX GENMASK(7, 4)
#define R_TUNE_DONE BIT(10)
static u32 k1_phy_rterm = ~0;
static void k1_phy_rterm_set(u32 val)
{
k1_phy_rterm = val & (RTERM_VALUE_RX | RTERM_VALUE_TX);
}
static bool k1_phy_rterm_valid(void)
{
return !(k1_phy_rterm & ~(RTERM_VALUE_RX | RTERM_VALUE_TX));
}
static u32 k1_phy_rterm_rx(void)
{
return FIELD_GET(RTERM_VALUE_RX, k1_phy_rterm);
}
static u32 k1_phy_rterm_tx(void)
{
return FIELD_GET(RTERM_VALUE_TX, k1_phy_rterm);
}
static bool k1_phy_port_a(struct k1_pcie_phy *k1_phy)
{
return !!k1_phy->pmu;
}
static const struct clk_parent_data k1_pcie_phy_data[] = {
{ .fw_name = "refclk", },
};
static struct k1_pcie_phy *clk_hw_to_k1_phy(struct clk_hw *clk_hw)
{
return container_of(clk_hw, struct k1_pcie_phy, pll_hw);
}
static void k1_pcie_phy_pll_prepare_usb(struct k1_pcie_phy *k1_phy)
{
void __iomem *regs = k1_phy->regs;
u32 val;
val = readl(regs + PCIE_PU_ADDR_CLK_CFG);
val &= ~CFG_INTERNAL_TIMER_ADJ;
val |= FIELD_PREP(CFG_INTERNAL_TIMER_ADJ, TIMER_ADJ_USB);
writel(val, regs + PCIE_PU_ADDR_CLK_CFG);
val = readl(regs + PCIE_PU_PLL_1);
val &= ~SSC_DEP_SEL;
val |= FIELD_PREP(SSC_DEP_SEL, SSC_DEP_5000PPM);
writel(val, regs + PCIE_PU_PLL_1);
}
static void k1_pcie_phy_pll_prepare_pcie(struct k1_pcie_phy *k1_phy)
{
void __iomem *regs = k1_phy->regs;
u32 val;
u32 i;
for (i = 0; i < k1_phy->pcie_lanes; i++) {
val = readl(regs + PCIE_PU_ADDR_CLK_CFG);
val &= ~CFG_INTERNAL_TIMER_ADJ;
val |= FIELD_PREP(CFG_INTERNAL_TIMER_ADJ, TIMER_ADJ_PCIE);
writel(val, regs + PCIE_PU_ADDR_CLK_CFG);
regs += PHY_LANE_OFFSET;
}
regs = k1_phy->regs;
val = readl(regs + PCIE_RC_DONE_STATUS);
val |= CFG_FORCE_RCV_RETRY;
writel(val, regs + PCIE_RC_DONE_STATUS);
val = readl(regs + PCIE_PU_PLL_1);
val &= ~SSC_DEP_SEL;
val |= FIELD_PREP(SSC_DEP_SEL, SSC_DEP_NONE);
writel(val, regs + PCIE_PU_PLL_1);
val = readl(regs + PCIE_PU_PLL_2);
val |= GEN_REF100;
writel(val, regs + PCIE_PU_PLL_2);
}
static int k1_pcie_phy_pll_prepare(struct clk_hw *clk_hw)
{
struct k1_pcie_phy *k1_phy = clk_hw_to_k1_phy(clk_hw);
void __iomem *regs = k1_phy->regs;
u32 val;
u32 i;
if (k1_phy_port_a(k1_phy) && k1_phy->type == PHY_TYPE_USB3)
k1_pcie_phy_pll_prepare_usb(k1_phy);
else
k1_pcie_phy_pll_prepare_pcie(k1_phy);
val = readl(regs + PCIE_PU_PLL_1);
val &= ~REF_100_WSSC;
val &= ~FREF_SEL;
val |= FIELD_PREP(FREF_SEL, FREF_24M);
writel(val, regs + PCIE_PU_PLL_1);
for (i = 0; i < k1_phy->pcie_lanes; i++) {
val = readl(regs + PCIE_PU_ADDR_CLK_CFG);
val |= CFG_SW_PHY_INIT_DONE;
writel(val, regs + PCIE_PU_ADDR_CLK_CFG);
regs += PHY_LANE_OFFSET;
}
return readl_poll_timeout(k1_phy->regs + PCIE_PU_ADDR_CLK_CFG,
val, val & PLL_READY,
POLL_DELAY, PLL_TIMEOUT);
}
static const struct clk_ops k1_pcie_phy_pll_ops = {
.prepare = k1_pcie_phy_pll_prepare,
};
static int k1_pcie_phy_pll_setup(struct k1_pcie_phy *k1_phy)
{
struct clk_hw *hw = &k1_phy->pll_hw;
struct device *dev = k1_phy->dev;
struct clk_init_data init = { };
char *name;
int ret;
name = kasprintf(GFP_KERNEL, "pcie%u_phy_pll", k1_phy->phy->id);
if (!name)
return -ENOMEM;
init.name = name;
init.ops = &k1_pcie_phy_pll_ops;
init.parent_data = k1_pcie_phy_data;
init.num_parents = ARRAY_SIZE(k1_pcie_phy_data);
hw->init = &init;
ret = devm_clk_hw_register(dev, hw);
kfree(name);
if (ret)
return ret;
k1_phy->pll = devm_clk_hw_get_clk(dev, hw, "pll");
if (IS_ERR(k1_phy->pll))
return PTR_ERR(k1_phy->pll);
return 0;
}
static void k1_combo_phy_sel(struct k1_pcie_phy *k1_phy, bool usb)
{
struct regmap *pmu = k1_phy->pmu;
if (!regmap_test_bits(pmu, PMUA_USB_PHY_CTRL0, COMBO_PHY_SEL) == usb)
regmap_assign_bits(pmu, PMUA_USB_PHY_CTRL0, COMBO_PHY_SEL, usb);
}
static void k1_pcie_phy_init_pcie(struct k1_pcie_phy *k1_phy)
{
u32 rx_rterm = k1_phy_rterm_rx();
u32 tx_rterm = k1_phy_rterm_tx();
void __iomem *regs;
u32 val;
int i;
if (k1_phy_port_a(k1_phy))
k1_combo_phy_sel(k1_phy, false);
regs = k1_phy->regs;
for (i = 0; i < k1_phy->pcie_lanes; i++) {
val = readl(regs + PCIE_RX_REG1);
val &= ~AFE_RTERM_REG;
val |= FIELD_PREP(AFE_RTERM_REG, rx_rterm);
val |= EN_RTERM;
writel(val, regs + PCIE_RX_REG1);
val = readl(regs + PCIE_RX_REG2);
val &= ~RX_RTERM_SEL;
writel(val, regs + PCIE_RX_REG2);
val = readl(regs + PCIE_TX_REG1);
val &= ~TX_RTERM_REG;
val |= FIELD_PREP(TX_RTERM_REG, tx_rterm);
val |= TX_RTERM_SEL;
writel(val, regs + PCIE_TX_REG1);
val = readl(regs + PCIE_RC_CAL_REG2);
val &= CLKSEL;
val |= FIELD_PREP(CLKSEL, CLKSEL_24M);
val &= ~RC_CAL_TOGGLE;
writel(val, regs + PCIE_RC_CAL_REG2);
val |= RC_CAL_TOGGLE;
writel(val, regs + PCIE_RC_CAL_REG2);
val = readl(regs + PCIE_LTSSM_DIS_ENTRY);
val |= OVRD_REFCLK_MODE;
val &= ~CFG_REFCLK_MODE;
val |= FIELD_PREP(CFG_REFCLK_MODE, RFCLK_MODE_DRIVER);
writel(val, regs + PCIE_LTSSM_DIS_ENTRY);
regs += PHY_LANE_OFFSET;
}
}
static void k1_pcie_phy_init_usb(struct k1_pcie_phy *k1_phy)
{
k1_combo_phy_sel(k1_phy, true);
writel(0, k1_phy->regs + USB3_TEST_CTRL);
}
static int k1_pcie_phy_init(struct phy *phy)
{
struct k1_pcie_phy *k1_phy = phy_get_drvdata(phy);
if (k1_phy_port_a(k1_phy) && k1_phy->type == PHY_TYPE_USB3)
k1_pcie_phy_init_usb(k1_phy);
else
k1_pcie_phy_init_pcie(k1_phy);
return clk_prepare_enable(k1_phy->pll);
}
static int k1_pcie_phy_exit(struct phy *phy)
{
struct k1_pcie_phy *k1_phy = phy_get_drvdata(phy);
clk_disable_unprepare(k1_phy->pll);
return 0;
}
static const struct phy_ops k1_pcie_phy_ops = {
.init = k1_pcie_phy_init,
.exit = k1_pcie_phy_exit,
.owner = THIS_MODULE,
};
static int k1_pcie_combo_phy_calibrate(struct k1_pcie_phy *k1_phy)
{
struct reset_control_bulk_data resets[] = {
{ .id = "dbi", },
{ .id = "mstr", },
{ .id = "slv", },
};
struct clk_bulk_data clocks[] = {
{ .id = "dbi", },
{ .id = "mstr", },
{ .id = "slv", },
};
struct device *dev = k1_phy->dev;
int ret = 0;
int val;
if (k1_phy_rterm_valid())
return 0;
regmap_assign_bits(k1_phy->pmu, PCIE_CLK_RES_CTRL,
PCIE_APP_HOLD_PHY_RST, false);
val = readl(k1_phy->regs + PCIE_RCAL_RESULT);
if (val & R_TUNE_DONE)
goto out_tune_done;
k1_combo_phy_sel(k1_phy, false);
ret = clk_bulk_get(dev, ARRAY_SIZE(clocks), clocks);
if (ret < 0)
goto out_tune_done;
ret = clk_bulk_prepare_enable(ARRAY_SIZE(clocks), clocks);
if (ret)
goto out_put_clocks;
ret = reset_control_bulk_get_shared(dev, ARRAY_SIZE(resets), resets);
if (ret)
goto out_disable_clocks;
ret = reset_control_bulk_deassert(ARRAY_SIZE(resets), resets);
if (ret)
goto out_put_resets;
ret = readl_poll_timeout(k1_phy->regs + PCIE_RCAL_RESULT,
val, val & R_TUNE_DONE,
POLL_DELAY, CALIBRATION_TIMEOUT);
reset_control_bulk_assert(ARRAY_SIZE(resets), resets);
out_put_resets:
reset_control_bulk_put(ARRAY_SIZE(resets), resets);
out_disable_clocks:
clk_bulk_disable_unprepare(ARRAY_SIZE(clocks), clocks);
out_put_clocks:
clk_bulk_put(ARRAY_SIZE(clocks), clocks);
out_tune_done:
if (!ret)
k1_phy_rterm_set(val);
return ret;
}
static struct phy *
k1_pcie_combo_phy_xlate(struct device *dev, const struct of_phandle_args *args)
{
struct k1_pcie_phy *k1_phy = dev_get_drvdata(dev);
u32 type;
if (args->args_count != 1)
return ERR_PTR(-EINVAL);
type = args->args[0];
if (type != PHY_TYPE_PCIE && type != PHY_TYPE_USB3)
return ERR_PTR(-EINVAL);
if (k1_phy->type != PHY_NONE)
return ERR_PTR(-EBUSY);
k1_phy->type = type;
return k1_phy->phy;
}
static u32 k1_pcie_num_lanes(struct k1_pcie_phy *k1_phy, bool port_a)
{
struct device *dev = k1_phy->dev;
u32 count = 0;
u32 max;
int ret;
ret = of_property_read_u32(dev_of_node(dev), "num-lanes", &count);
if (count == 1)
return 1;
if (count == 2 && !port_a)
return 2;
max = port_a ? 1 : 2;
if (ret != -EINVAL)
dev_warn(dev, "bad lane count %u for port; using %u\n",
count, max);
return max;
}
static int k1_pcie_combo_phy_probe(struct k1_pcie_phy *k1_phy)
{
struct device *dev = k1_phy->dev;
struct regmap *regmap;
int ret;
regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev), SYSCON_APMU);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "failed to get PMU\n");
k1_phy->pmu = regmap;
ret = k1_pcie_combo_phy_calibrate(k1_phy);
if (ret)
return dev_err_probe(dev, ret, "calibration failed\n");
dev_set_drvdata(dev, k1_phy);
return 0;
}
static int k1_pcie_phy_probe(struct platform_device *pdev)
{
struct phy *(*xlate)(struct device *dev,
const struct of_phandle_args *args);
struct device *dev = &pdev->dev;
struct reset_control *phy_reset;
struct phy_provider *provider;
struct k1_pcie_phy *k1_phy;
bool probing_port_a;
int ret;
xlate = of_device_get_match_data(dev);
probing_port_a = xlate == k1_pcie_combo_phy_xlate;
if (!k1_phy_rterm_valid() && !probing_port_a)
return -EPROBE_DEFER;
k1_phy = devm_kzalloc(dev, sizeof(*k1_phy), GFP_KERNEL);
if (!k1_phy)
return -ENOMEM;
k1_phy->dev = dev;
k1_phy->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(k1_phy->regs))
return dev_err_probe(dev, PTR_ERR(k1_phy->regs),
"error mapping registers\n");
phy_reset = devm_reset_control_get_exclusive_deasserted(dev, "phy");
if (IS_ERR(phy_reset))
return PTR_ERR(phy_reset);
if (probing_port_a) {
ret = k1_pcie_combo_phy_probe(k1_phy);
if (ret)
return dev_err_probe(dev, ret,
"error probing combo phy\n");
}
k1_phy->pcie_lanes = k1_pcie_num_lanes(k1_phy, probing_port_a);
k1_phy->phy = devm_phy_create(dev, NULL, &k1_pcie_phy_ops);
if (IS_ERR(k1_phy->phy))
return dev_err_probe(dev, PTR_ERR(k1_phy->phy),
"error creating phy\n");
phy_set_drvdata(k1_phy->phy, k1_phy);
ret = k1_pcie_phy_pll_setup(k1_phy);
if (ret)
return dev_err_probe(dev, ret, "error initializing clock\n");
provider = devm_of_phy_provider_register(dev, xlate);
if (IS_ERR(provider))
return dev_err_probe(dev, PTR_ERR(provider),
"error registering provider\n");
return 0;
}
static const struct of_device_id k1_pcie_phy_of_match[] = {
{ .compatible = "spacemit,k1-combo-phy", k1_pcie_combo_phy_xlate, },
{ .compatible = "spacemit,k1-pcie-phy", of_phy_simple_xlate, },
{ },
};
MODULE_DEVICE_TABLE(of, k1_pcie_phy_of_match);
static struct platform_driver k1_pcie_phy_driver = {
.probe = k1_pcie_phy_probe,
.driver = {
.of_match_table = k1_pcie_phy_of_match,
.name = "spacemit-k1-pcie-phy",
}
};
module_platform_driver(k1_pcie_phy_driver);
MODULE_DESCRIPTION("SpacemiT K1 PCIe and USB 3 PHY driver");
MODULE_LICENSE("GPL");