[PATCH 5/5] phy: cadence-torrent: Model reference clock driver as a gate and mux clock

From: Swapnil Jakhade
Date: Wed Sep 08 2021 - 14:27:44 EST


When reference clock driver is enabled, either derived or received refclk
is output on cmn_refclk_p/m. Model reference clock driver as a "clock"
with gate and mux clock operations.

Signed-off-by: Swapnil Jakhade <sjakhade@xxxxxxxxxxx>
---
drivers/phy/cadence/phy-cadence-torrent.c | 160 ++++++++++++++++++++++
1 file changed, 160 insertions(+)

diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c
index 2c42a6690632..5786166133d3 100644
--- a/drivers/phy/cadence/phy-cadence-torrent.c
+++ b/drivers/phy/cadence/phy-cadence-torrent.c
@@ -266,6 +266,29 @@ static const struct reg_field phy_pipe_cmn_ctrl1_0 = REG_FIELD(PHY_PIPE_CMN_CTRL
static const struct reg_field cmn_cdiag_refclk_ovrd_4 =
REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4);

+#define REFCLK_OUT_NUM_CMN_CONFIG 4
+
+enum cdns_torrent_refclk_out_cmn {
+ CMN_CDIAG_REFCLK_DRV0_CTRL_1,
+ CMN_CDIAG_REFCLK_DRV0_CTRL_4,
+ CMN_CDIAG_REFCLK_DRV0_CTRL_5,
+ CMN_CDIAG_REFCLK_DRV0_CTRL_6,
+};
+
+static const struct reg_field refclk_out_cmn_cfg[] = {
+ [CMN_CDIAG_REFCLK_DRV0_CTRL_1] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 1, 1),
+ [CMN_CDIAG_REFCLK_DRV0_CTRL_4] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 4, 4),
+ [CMN_CDIAG_REFCLK_DRV0_CTRL_5] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 5, 5),
+ [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,
@@ -334,6 +357,15 @@ enum phy_powerstate {
POWERSTATE_A3 = 3,
};

+struct cdns_torrent_refclk_driver {
+ struct clk_hw hw;
+ struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG];
+ struct clk_init_data clk_data;
+};
+
+#define to_cdns_torrent_refclk_driver(_hw) \
+ container_of(_hw, struct cdns_torrent_refclk_driver, hw)
+
struct cdns_torrent_derived_refclk {
struct clk_hw hw;
struct regmap_field *phy_pipe_cmn_ctrl1_0;
@@ -1780,6 +1812,128 @@ static int cdns_torrent_received_refclk_register(struct cdns_torrent_phy *cdns_p
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_5], 1);
+ regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0);
+
+ return 0;
+}
+
+static void cdns_torrent_refclk_driver_disable(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_1], 1);
+}
+
+static int cdns_torrent_refclk_driver_is_enabled(struct clk_hw *hw)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw);
+ int val;
+
+ regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], &val);
+
+ 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)
+{
+ struct cdns_torrent_refclk_driver *refclk_driver;
+ struct device *dev = cdns_phy->dev;
+ struct regmap_field *field;
+ struct clk_init_data *init;
+ const char **parent_names;
+ unsigned int num_parents;
+ struct regmap *regmap;
+ char clk_name[100];
+ struct clk_hw *hw;
+ int i, ret;
+
+ refclk_driver = devm_kzalloc(dev, sizeof(*refclk_driver), GFP_KERNEL);
+ if (!refclk_driver)
+ return -ENOMEM;
+
+ 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);
+ }
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
+ clk_names[CDNS_TORRENT_REFCLK_DRIVER]);
+
+ init = &refclk_driver->clk_data;
+
+ init->ops = &cdns_torrent_refclk_driver_ops;
+ 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;
+
+ for (i = 0; i < REFCLK_OUT_NUM_CMN_CONFIG; i++) {
+ field = devm_regmap_field_alloc(dev, regmap, refclk_out_cmn_cfg[i]);
+ if (IS_ERR(field)) {
+ dev_err(dev, "Refclk driver CMN reg field init failed\n");
+ return PTR_ERR(field);
+ }
+ 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;
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret)
+ return ret;
+
+ cdns_phy->clk_hw_data->hws[CDNS_TORRENT_REFCLK_DRIVER] = hw;
+
+ return 0;
+}
+
static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base,
u32 block_offset,
u8 reg_offset_shift,
@@ -2291,6 +2445,12 @@ static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy)
return ret;
}

+ ret = cdns_torrent_refclk_driver_register(cdns_phy);
+ if (ret) {
+ dev_err(dev, "failed to register refclk driver\n");
+ return ret;
+ }
+
ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data);
if (ret) {
dev_err(dev, "Failed to add clock provider: %s\n", node->name);
--
2.26.1