671 lines
19 KiB
C
671 lines
19 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* SpacemiT K1 PCIe and PCIe/USB 3 combo PHY driver
|
|
*
|
|
* Copyright (C) 2025 by RISCstar Solutions Corporation. All rights reserved.
|
|
*/
|
|
|
|
#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>
|
|
|
|
/*
|
|
* Three PCIe ports are supported in the SpacemiT K1 SoC, and this driver
|
|
* supports their PHYs.
|
|
*
|
|
* The PHY for PCIe port A is different from the PHYs for ports B and C:
|
|
* - It has one lane, while ports B and C have two
|
|
* - It is a combo PHY can be used for PCIe or USB 3
|
|
* - It can automatically calibrate PCIe TX and RX termination settings
|
|
*
|
|
* The PHY functionality for PCIe ports B and C is identical:
|
|
* - They have two PCIe lanes (but can be restricted to 1 via device tree)
|
|
* - They are used for PCIe only
|
|
* - They are configured using TX and RX values computed for port A
|
|
*
|
|
* A given board is designed to use the combo PHY for either PCIe or USB 3.
|
|
* Whether the combo PHY is configured for PCIe or USB 3 is specified in
|
|
* device tree using a phandle plus an argument. The argument indicates
|
|
* the type (either PHY_TYPE_PCIE or PHY_TYPE_USB3).
|
|
*
|
|
* Each PHY has a reset that it gets and deasserts during initialization.
|
|
* Each depends also on other clocks and resets provided by the controller
|
|
* hardware (PCIe or USB) it is associated with. The controller drivers
|
|
* are required to enable any clocks and de-assert any resets that affect
|
|
* PHY operation. In addition each PHY implements an internal PLL, driven
|
|
* by an external (24 MHz) oscillator.
|
|
*
|
|
* PCIe PHYs must be programmed with RX and TX calibration values. The
|
|
* combo PHY is the only one that can determine these values. They are
|
|
* determined by temporarily enabling the combo PHY in PCIe mode at probe
|
|
* time (if necessary). This calibration only needs to be done once, and
|
|
* when it has completed the TX and RX values are saved.
|
|
*
|
|
* To allow the combo PHY to be enabled for calibration, the resets and
|
|
* clocks it uses in PCIe mode must be supplied.
|
|
*/
|
|
|
|
struct k1_pcie_phy {
|
|
struct device *dev; /* PHY provider device */
|
|
struct phy *phy;
|
|
void __iomem *regs;
|
|
u32 pcie_lanes; /* Max (1 or 2) unless limited by DT */
|
|
struct clk *pll;
|
|
struct clk_hw pll_hw; /* Private PLL clock */
|
|
|
|
/* The remaining fields are only used for the combo PHY */
|
|
u32 type; /* PHY_TYPE_PCIE or PHY_TYPE_USB3 */
|
|
struct regmap *pmu; /* MMIO regmap (no errors) */
|
|
};
|
|
|
|
#define CALIBRATION_TIMEOUT 500000 /* For combo PHY (usec) */
|
|
#define PLL_TIMEOUT 500000 /* For PHY PLL lock (usec) */
|
|
#define POLL_DELAY 500 /* Time between polls (usec) */
|
|
|
|
/* Selecting the combo PHY operating mode requires APMU regmap access */
|
|
#define SYSCON_APMU "spacemit,apmu"
|
|
|
|
/* PMU space, for selecting between PCIe and USB 3 mode (combo PHY only) */
|
|
|
|
#define PMUA_USB_PHY_CTRL0 0x0110
|
|
#define COMBO_PHY_SEL BIT(3) /* 0: PCIe; 1: USB 3 */
|
|
|
|
#define PCIE_CLK_RES_CTRL 0x03cc
|
|
#define PCIE_APP_HOLD_PHY_RST BIT(30)
|
|
|
|
/* PHY register space */
|
|
|
|
/* Offset between lane 0 and lane 1 registers when there are two */
|
|
#define PHY_LANE_OFFSET 0x0400
|
|
|
|
/* PHY PLL configuration */
|
|
#define PCIE_PU_ADDR_CLK_CFG 0x0008
|
|
#define PLL_READY BIT(0) /* read-only */
|
|
#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) /* We set after PLL config */
|
|
|
|
#define PCIE_RC_DONE_STATUS 0x0018
|
|
#define CFG_FORCE_RCV_RETRY BIT(10) /* Used for PCIe */
|
|
|
|
/* PCIe PHY lane calibration; assumes 24MHz input clock */
|
|
#define PCIE_RC_CAL_REG2 0x0020
|
|
#define RC_CAL_TOGGLE BIT(22)
|
|
#define CLKSEL GENMASK(31, 29)
|
|
#define CLKSEL_24M 0x3
|
|
|
|
/* Additional PHY PLL configuration (USB 3 and PCIe) */
|
|
#define PCIE_PU_PLL_1 0x0048
|
|
#define REF_100_WSSC BIT(12) /* 1: input is 100MHz, SSC */
|
|
#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
|
|
|
|
/* PCIe PHY configuration */
|
|
#define PCIE_PU_PLL_2 0x004c
|
|
#define GEN_REF100 BIT(4) /* 1: generate 100MHz clk */
|
|
|
|
#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) /* 0: use AFE_RTERM_REG value */
|
|
|
|
#define PCIE_LTSSM_DIS_ENTRY 0x005c
|
|
#define CFG_REFCLK_MODE GENMASK(9, 8)
|
|
#define RFCLK_MODE_DRIVER 0x1
|
|
#define OVRD_REFCLK_MODE BIT(10) /* 1: use CFG_RFCLK_MODE */
|
|
|
|
#define PCIE_TX_REG1 0x0064
|
|
#define TX_RTERM_REG GENMASK(15, 12)
|
|
#define TX_RTERM_SEL BIT(25) /* 1: use TX_RTERM_REG */
|
|
|
|
/* Zeroed for the combo PHY operating in USB mode */
|
|
#define USB3_TEST_CTRL 0x0068
|
|
|
|
/* PHY calibration values, determined by the combo PHY at probe time */
|
|
#define PCIE_RCAL_RESULT 0x0084 /* Port A PHY only */
|
|
#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; /* Invalid initial value */
|
|
|
|
/* Save the RX and TX receiver termination values */
|
|
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)
|
|
{
|
|
/* Valid if no bits outside those we care about are set */
|
|
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);
|
|
}
|
|
|
|
/* Only the combo PHY has a PMU pointer defined */
|
|
static bool k1_phy_port_a(struct k1_pcie_phy *k1_phy)
|
|
{
|
|
return !!k1_phy->pmu;
|
|
}
|
|
|
|
/* The PLL clocks are driven by the external oscillator */
|
|
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);
|
|
}
|
|
|
|
/* USB mode only works on the combo PHY, which has only one lane */
|
|
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);
|
|
}
|
|
|
|
/* Perform PCIe-specific register updates before starting the PLL clock */
|
|
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; /* Next lane */
|
|
}
|
|
|
|
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; /* Enable 100 MHz PLL output clock */
|
|
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);
|
|
|
|
/*
|
|
* Disable 100 MHz input reference with spread-spectrum
|
|
* clocking and select the 24 MHz clock input frequency
|
|
*/
|
|
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);
|
|
|
|
/* Mark PLL configuration done on all lanes */
|
|
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; /* Next lane */
|
|
}
|
|
|
|
/*
|
|
* Wait for indication the PHY PLL is locked. Lanes for ports
|
|
* B and C share a PLL, so it's enough to sample just lane 0.
|
|
*/
|
|
return readl_poll_timeout(k1_phy->regs + PCIE_PU_ADDR_CLK_CFG,
|
|
val, val & PLL_READY,
|
|
POLL_DELAY, PLL_TIMEOUT);
|
|
}
|
|
|
|
/* Prepare implies enable, and once enabled, it's always on */
|
|
static const struct clk_ops k1_pcie_phy_pll_ops = {
|
|
.prepare = k1_pcie_phy_pll_prepare,
|
|
};
|
|
|
|
/* We represent the PHY PLL as a private clock */
|
|
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); /* __clk_register() duplicates the name we provide */
|
|
|
|
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;
|
|
}
|
|
|
|
/* Select PCIe or USB 3 mode for the combo PHY. */
|
|
static void k1_combo_phy_sel(struct k1_pcie_phy *k1_phy, bool usb)
|
|
{
|
|
struct regmap *pmu = k1_phy->pmu;
|
|
|
|
/* Only change it if it's not already in the desired state */
|
|
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;
|
|
|
|
/* For the combo PHY, set PHY to PCIe mode */
|
|
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);
|
|
|
|
/* Set RX analog front-end receiver termination value */
|
|
val &= ~AFE_RTERM_REG;
|
|
val |= FIELD_PREP(AFE_RTERM_REG, rx_rterm);
|
|
|
|
/* And enable refclock receiver termination */
|
|
val |= EN_RTERM;
|
|
writel(val, regs + PCIE_RX_REG1);
|
|
|
|
val = readl(regs + PCIE_RX_REG2);
|
|
/* Use PCIE_RX_REG1 AFE_RTERM_REG value */
|
|
val &= ~RX_RTERM_SEL;
|
|
writel(val, regs + PCIE_RX_REG2);
|
|
|
|
val = readl(regs + PCIE_TX_REG1);
|
|
|
|
/* Set TX driver termination value */
|
|
val &= ~TX_RTERM_REG;
|
|
val |= FIELD_PREP(TX_RTERM_REG, tx_rterm);
|
|
|
|
/* Use PCIE_TX_REG1 TX_RTERM_REG value */
|
|
val |= TX_RTERM_SEL;
|
|
writel(val, regs + PCIE_TX_REG1);
|
|
|
|
/* Set the input clock to 24 MHz, and clear RC_CAL_TOGGLE */
|
|
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);
|
|
|
|
/* Now trigger recalibration by setting RC_CAL_TOGGLE again */
|
|
val |= RC_CAL_TOGGLE;
|
|
writel(val, regs + PCIE_RC_CAL_REG2);
|
|
|
|
val = readl(regs + PCIE_LTSSM_DIS_ENTRY);
|
|
/* Override the reference clock; set to refclk driver mode */
|
|
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; /* Next lane */
|
|
}
|
|
}
|
|
|
|
/* Only called for combo PHY */
|
|
static void k1_pcie_phy_init_usb(struct k1_pcie_phy *k1_phy)
|
|
{
|
|
k1_combo_phy_sel(k1_phy, true);
|
|
|
|
/* We're not doing any testing */
|
|
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);
|
|
|
|
/* Note: port type is only valid for port A (both checks needed) */
|
|
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,
|
|
};
|
|
|
|
/*
|
|
* Get values needed for calibrating PHYs operating in PCIe mode. Only
|
|
* the combo PHY is able to do this, and its calibration values are used
|
|
* for configuring all PCIe PHYs.
|
|
*
|
|
* We always need to de-assert the "global" reset on the combo PHY,
|
|
* because the USB driver depends on it. If used for PCIe, that driver
|
|
* will (also) de-assert this, but by leaving it de-asserted for the
|
|
* combo PHY, the USB driver doesn't have to do this. Note: although
|
|
* SpacemiT refers to this as the global reset, we name the "phy" reset.
|
|
*
|
|
* In addition, we guarantee the APP_HOLD_PHY_RESET bit is clear for the
|
|
* combo PHY, so the USB driver doesn't have to manage that either. The
|
|
* PCIe driver is free to change this bit for normal operation.
|
|
*
|
|
* Calibration only needs to be done once. It's possible calibration has
|
|
* already completed (e.g., it might have happened in the boot loader, or
|
|
* -EPROBE_DEFER might result in this function being called again). So we
|
|
* check that early too, to avoid doing it more than once.
|
|
*
|
|
* Otherwise we temporarily power up the PHY using the PCIe app clocks
|
|
* and resets, wait for the hardware to indicate calibration is done,
|
|
* grab the value, then shut the PHY down again.
|
|
*/
|
|
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;
|
|
|
|
/* Nothing to do if we already set the receiver termination value */
|
|
if (k1_phy_rterm_valid())
|
|
return 0;
|
|
|
|
/*
|
|
* We also guarantee the APP_HOLD_PHY_RESET bit is clear. We can
|
|
* leave this bit clear even if an error happens below.
|
|
*/
|
|
regmap_assign_bits(k1_phy->pmu, PCIE_CLK_RES_CTRL,
|
|
PCIE_APP_HOLD_PHY_RST, false);
|
|
|
|
/* If the calibration already completed (e.g. by U-Boot), we're done */
|
|
val = readl(k1_phy->regs + PCIE_RCAL_RESULT);
|
|
if (val & R_TUNE_DONE)
|
|
goto out_tune_done;
|
|
|
|
/* Put the PHY into PCIe mode */
|
|
k1_combo_phy_sel(k1_phy, false);
|
|
|
|
/* Get and enable the PCIe app clocks */
|
|
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;
|
|
|
|
/* Get the PCIe application resets (not the PHY reset) */
|
|
ret = reset_control_bulk_get_shared(dev, ARRAY_SIZE(resets), resets);
|
|
if (ret)
|
|
goto out_disable_clocks;
|
|
|
|
/* De-assert the PCIe application resets */
|
|
ret = reset_control_bulk_deassert(ARRAY_SIZE(resets), resets);
|
|
if (ret)
|
|
goto out_put_resets;
|
|
|
|
/*
|
|
* This is the core activity here. Wait for the hardware to
|
|
* signal that it has completed calibration/tuning. Once it
|
|
* has, the register value will contain the values we'll
|
|
* use to configure PCIe PHYs.
|
|
*/
|
|
ret = readl_poll_timeout(k1_phy->regs + PCIE_RCAL_RESULT,
|
|
val, val & R_TUNE_DONE,
|
|
POLL_DELAY, CALIBRATION_TIMEOUT);
|
|
|
|
/* Clean up. We're done with the resets and clocks */
|
|
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 we got the value without timing out, set k1_phy_rterm */
|
|
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;
|
|
|
|
/* The argument specifying the PHY mode is required */
|
|
if (args->args_count != 1)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
/* We only support PCIe and USB 3 mode */
|
|
type = args->args[0];
|
|
if (type != PHY_TYPE_PCIE && type != PHY_TYPE_USB3)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
/* This PHY can only be used once */
|
|
if (k1_phy->type != PHY_NONE)
|
|
return ERR_PTR(-EBUSY);
|
|
|
|
k1_phy->type = type;
|
|
|
|
return k1_phy->phy;
|
|
}
|
|
|
|
/* Use the maximum number of PCIe lanes unless limited by device tree */
|
|
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;
|
|
|
|
/* Setting the PHY mode requires access to the PMU regmap */
|
|
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");
|
|
|
|
/* Needed by k1_pcie_combo_phy_xlate(), which also sets k1_phy->type */
|
|
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;
|
|
|
|
/* Only the combo PHY can calibrate, so it must probe first */
|
|
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");
|
|
|
|
/* De-assert the PHY (global) reset and leave it that way */
|
|
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");
|