phy: cadence-torrent: Add support to output received reference clock
authorSwapnil Jakhade <sjakhade@cadence.com>
Wed, 22 Sep 2021 12:37:35 +0000 (14:37 +0200)
committerVinod Koul <vkoul@kernel.org>
Tue, 26 Oct 2021 11:06:23 +0000 (16:36 +0530)
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 <sjakhade@cadence.com>
Link: https://lore.kernel.org/r/20210922123735.21927-5-sjakhade@cadence.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/phy/cadence/phy-cadence-torrent.c

index 615aca6..5786166 100644 (file)
 #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");