Subject: [PATCH v2 4/4] phy: cadence-torrent: Add support to output received reference clock

Add support to output received reference clock. Model the received
reference clock as an alternate parent of reference clock driver
clock. When received refclk is selected to output on cmn_refclk_p/m,
this is the internal reference clock driven on the pma_cmn_refclk_int.

Signed-off-by: Swapnil Jakhade <[email protected]>
---
drivers/phy/cadence/phy-cadence-torrent.c | 159 ++++++++++++++++++++--
1 file changed, 148 insertions(+), 11 deletions(-)

diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c
index 615aca6bd52b..5786166133d3 100644
--- a/drivers/phy/cadence/phy-cadence-torrent.c
+++ b/drivers/phy/cadence/phy-cadence-torrent.c
@@ -235,11 +235,12 @@
#define PHY_PMA_CMN_CTRL2 0x0001U
#define PHY_PMA_PLL_RAW_CTRL 0x0003U

-#define CDNS_TORRENT_OUTPUT_CLOCKS 2
+#define CDNS_TORRENT_OUTPUT_CLOCKS 3

static const char * const clk_names[] = {
[CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver",
[CDNS_TORRENT_DERIVED_REFCLK] = "refclk-der",
+ [CDNS_TORRENT_RECEIVED_REFCLK] = "refclk-rec",
};

static const struct reg_field phy_pll_cfg =
@@ -281,6 +282,13 @@ static const struct reg_field refclk_out_cmn_cfg[] = {
[CMN_CDIAG_REFCLK_DRV0_CTRL_6] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 6, 6),
};

+static const int refclk_driver_parent_index[] = {
+ CDNS_TORRENT_DERIVED_REFCLK,
+ CDNS_TORRENT_RECEIVED_REFCLK
+};
+
+static u32 cdns_torrent_refclk_driver_mux_table[] = { 1, 0 };
+
enum cdns_torrent_phy_type {
TYPE_NONE,
TYPE_DP,
@@ -368,6 +376,16 @@ struct cdns_torrent_derived_refclk {
#define to_cdns_torrent_derived_refclk(_hw) \
container_of(_hw, struct cdns_torrent_derived_refclk, hw)

+struct cdns_torrent_received_refclk {
+ struct clk_hw hw;
+ struct regmap_field *phy_pipe_cmn_ctrl1_0;
+ struct regmap_field *cmn_cdiag_refclk_ovrd_4;
+ struct clk_init_data clk_data;
+};
+
+#define to_cdns_torrent_received_refclk(_hw) \
+ container_of(_hw, struct cdns_torrent_received_refclk, hw)
+
struct cdns_reg_pairs {
u32 val;
u32 off;
@@ -1711,12 +1729,94 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph
return 0;
}

+static int cdns_torrent_received_refclk_enable(struct clk_hw *hw)
+{
+ struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw);
+
+ regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 1);
+
+ return 0;
+}
+
+static void cdns_torrent_received_refclk_disable(struct clk_hw *hw)
+{
+ struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw);
+
+ regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 0);
+}
+
+static int cdns_torrent_received_refclk_is_enabled(struct clk_hw *hw)
+{
+ struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw);
+ int val, cmn_val;
+
+ regmap_field_read(received_refclk->phy_pipe_cmn_ctrl1_0, &val);
+ regmap_field_read(received_refclk->cmn_cdiag_refclk_ovrd_4, &cmn_val);
+
+ return val && !cmn_val;
+}
+
+static const struct clk_ops cdns_torrent_received_refclk_ops = {
+ .enable = cdns_torrent_received_refclk_enable,
+ .disable = cdns_torrent_received_refclk_disable,
+ .is_enabled = cdns_torrent_received_refclk_is_enabled,
+};
+
+static int cdns_torrent_received_refclk_register(struct cdns_torrent_phy *cdns_phy)
+{
+ struct cdns_torrent_received_refclk *received_refclk;
+ struct device *dev = cdns_phy->dev;
+ struct clk_init_data *init;
+ const char *parent_name;
+ char clk_name[100];
+ struct clk_hw *hw;
+ struct clk *clk;
+ int ret;
+
+ received_refclk = devm_kzalloc(dev, sizeof(*received_refclk), GFP_KERNEL);
+ if (!received_refclk)
+ return -ENOMEM;
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
+ clk_names[CDNS_TORRENT_RECEIVED_REFCLK]);
+
+ clk = devm_clk_get_optional(dev, "phy_en_refclk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "No parent clock for received_refclk\n");
+ return PTR_ERR(clk);
+ }
+
+ init = &received_refclk->clk_data;
+
+ if (clk) {
+ parent_name = __clk_get_name(clk);
+ init->parent_names = &parent_name;
+ init->num_parents = 1;
+ }
+ init->ops = &cdns_torrent_received_refclk_ops;
+ init->flags = 0;
+ init->name = clk_name;
+
+ received_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0;
+ received_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4;
+
+ received_refclk->hw.init = init;
+
+ hw = &received_refclk->hw;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ret;
+
+ cdns_phy->clk_hw_data->hws[CDNS_TORRENT_RECEIVED_REFCLK] = hw;
+
+ return 0;
+}
+
static int cdns_torrent_refclk_driver_enable(struct clk_hw *hw)
{
struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);

regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0);
- regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1);
regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1);
regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0);

@@ -1740,10 +1840,30 @@ static int cdns_torrent_refclk_driver_is_enabled(struct clk_hw *hw)
return !val;
}

+static u8 cdns_torrent_refclk_driver_get_parent(struct clk_hw *hw)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);
+ unsigned int val;
+
+ regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], &val);
+ return clk_mux_val_to_index(hw, cdns_torrent_refclk_driver_mux_table, 0, val);
+}
+
+static int cdns_torrent_refclk_driver_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);
+ unsigned int val;
+
+ val = cdns_torrent_refclk_driver_mux_table[index];
+ return regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], val);
+}
+
static const struct clk_ops cdns_torrent_refclk_driver_ops = {
.enable = cdns_torrent_refclk_driver_enable,
.disable = cdns_torrent_refclk_driver_disable,
.is_enabled = cdns_torrent_refclk_driver_is_enabled,
+ .set_parent = cdns_torrent_refclk_driver_set_parent,
+ .get_parent = cdns_torrent_refclk_driver_get_parent,
};

static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy)
@@ -1752,7 +1872,8 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy
struct device *dev = cdns_phy->dev;
struct regmap_field *field;
struct clk_init_data *init;
- const char *parent_name;
+ const char **parent_names;
+ unsigned int num_parents;
struct regmap *regmap;
char clk_name[100];
struct clk_hw *hw;
@@ -1762,12 +1883,19 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy
if (!refclk_driver)
return -ENOMEM;

- hw = cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK];
- if (IS_ERR_OR_NULL(hw)) {
- dev_err(dev, "No parent clock for refclk driver clock\n");
- return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT;
+ num_parents = ARRAY_SIZE(refclk_driver_parent_index);
+ parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL);
+ if (!parent_names)
+ return -ENOMEM;
+
+ for (i = 0; i < num_parents; i++) {
+ hw = cdns_phy->clk_hw_data->hws[refclk_driver_parent_index[i]];
+ if (IS_ERR_OR_NULL(hw)) {
+ dev_err(dev, "No parent clock for refclk driver clock\n");
+ return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT;
+ }
+ parent_names[i] = clk_hw_get_name(hw);
}
- parent_name = clk_hw_get_name(hw);

snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
clk_names[CDNS_TORRENT_REFCLK_DRIVER]);
@@ -1775,9 +1903,9 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy
init = &refclk_driver->clk_data;

init->ops = &cdns_torrent_refclk_driver_ops;
- init->flags = 0;
- init->parent_names = &parent_name;
- init->num_parents = 1;
+ init->flags = CLK_SET_RATE_NO_REPARENT;
+ init->parent_names = parent_names;
+ init->num_parents = num_parents;
init->name = clk_name;

regmap = cdns_phy->regmap_common_cdb;
@@ -1791,6 +1919,9 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy
refclk_driver->cmn_fields[i] = field;
}

+ /* Enable Derived reference clock as default */
+ regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1);
+
refclk_driver->hw.init = init;

hw = &refclk_driver->hw;
@@ -2308,6 +2439,12 @@ static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy)
return ret;
}

+ ret = cdns_torrent_received_refclk_register(cdns_phy);
+ if (ret) {
+ dev_err(dev, "failed to register received refclk\n");
+ return ret;
+ }
+
ret = cdns_torrent_refclk_driver_register(cdns_phy);
if (ret) {
dev_err(dev, "failed to register refclk driver\n");
--
2.26.1