2021-03-04 14:48:08

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 00/13] PHY: Add support in Sierra to use external clock

Patch series adds support in Sierra driver to use external clock.

v1 of the patch series can be found @ [1]
v2 of the patch series can be found @ [2]
v3 of the patch series can be found @ [3]

Changes from v3:
1) Instead of adding separate subnodes for each clock, just add
#clock-cells in Sierra SERDES nodes and model the clocks. This is
in alignment with Rob's comment for a different series [4]
2) Removed device tree changes from the series.

Changes from v2:
1) Add depends on COMMON_CLK in Sierra
2) Add modelling PLL_CMNLC and PLL_CMNLC1 as clocks into a separate
patch
3) Disable clocks in Sierra driver remove

Changes from v1:
1) Remove the part that prevents configuration if the SERDES is already
configured and focus only on using external clock and the associated
cleanups
2) Change patch ordering
3) Use exclusive reset control APIs
4) Fix error handling code
5) Include DT patches in this series (I can send this separately to DT
MAINTAINER once the driver patches are merged)

[1] -> http://lore.kernel.org/r/[email protected]
[2] -> http://lore.kernel.org/r/[email protected]
[3] -> http://lore.kernel.org/r/[email protected]
[4] -> http://lore.kernel.org/r/[email protected]

Kishon Vijay Abraham I (13):
phy: cadence: Sierra: Fix PHY power_on sequence
phy: ti: j721e-wiz: Invoke wiz_init() before
of_platform_device_create()
phy: cadence: cadence-sierra: Create PHY only for "phy" or "link"
sub-nodes
phy: ti: j721e-wiz: Get PHY properties only for "phy" or "link"
subnode
phy: cadence: cadence-sierra: Move all clk_get_*() to a separate
function
phy: cadence: cadence-sierra: Move all reset_control_get*() to a
separate function
phy: cadence: cadence-sierra: Explicitly request exclusive reset
control
phy: cadence-torrent: Use a common header file for Cadence SERDES
phy: cadence: cadence-sierra: Add array of input clocks in "struct
cdns_sierra_phy"
phy: cadence: cadence-sierra: Add missing clk_disable_unprepare() in
.remove callback
dt-bindings: phy: phy-cadence-sierra: Add binding to model Sierra as
clock provider
phy: cadence: phy-cadence-sierra: Model PLL_CMNLC and PLL_CMNLC1 as
clocks (mux clocks)
phy: cadence: phy-cadence-sierra: Enable pll_cmnlc and pll_cmnlc1
clocks

.../bindings/phy/phy-cadence-sierra.yaml | 17 +-
drivers/phy/cadence/Kconfig | 1 +
drivers/phy/cadence/phy-cadence-sierra.c | 419 ++++++++++++++++--
drivers/phy/cadence/phy-cadence-torrent.c | 2 +-
drivers/phy/ti/phy-j721e-wiz.c | 21 +-
include/dt-bindings/phy/phy-cadence-torrent.h | 15 -
include/dt-bindings/phy/phy-cadence.h | 20 +
7 files changed, 428 insertions(+), 67 deletions(-)
delete mode 100644 include/dt-bindings/phy/phy-cadence-torrent.h
create mode 100644 include/dt-bindings/phy/phy-cadence.h

--
2.17.1


2021-03-04 14:48:09

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 06/13] phy: cadence: cadence-sierra: Move all reset_control_get*() to a separate function

No functional change. Group devm_reset_control_get() and
devm_reset_control_get_optional() to a separate function.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/phy/cadence/phy-cadence-sierra.c | 36 ++++++++++++++++--------
1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index 7bf1b4c7774a..935f165404e4 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -509,6 +509,28 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
return 0;
}

+static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp,
+ struct device *dev)
+{
+ struct reset_control *rst;
+
+ rst = devm_reset_control_get(dev, "sierra_reset");
+ if (IS_ERR(rst)) {
+ dev_err(dev, "failed to get reset\n");
+ return PTR_ERR(rst);
+ }
+ sp->phy_rst = rst;
+
+ rst = devm_reset_control_get_optional(dev, "sierra_apb");
+ if (IS_ERR(rst)) {
+ dev_err(dev, "failed to get apb reset\n");
+ return PTR_ERR(rst);
+ }
+ sp->apb_rst = rst;
+
+ return 0;
+}
+
static int cdns_sierra_phy_probe(struct platform_device *pdev)
{
struct cdns_sierra_phy *sp;
@@ -559,17 +581,9 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
if (ret)
return ret;

- sp->phy_rst = devm_reset_control_get(dev, "sierra_reset");
- if (IS_ERR(sp->phy_rst)) {
- dev_err(dev, "failed to get reset\n");
- return PTR_ERR(sp->phy_rst);
- }
-
- sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb");
- if (IS_ERR(sp->apb_rst)) {
- dev_err(dev, "failed to get apb reset\n");
- return PTR_ERR(sp->apb_rst);
- }
+ ret = cdns_sierra_phy_get_resets(sp, dev);
+ if (ret)
+ return ret;

ret = clk_prepare_enable(sp->clk);
if (ret)
--
2.17.1

2021-03-04 14:48:25

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 13/13] phy: cadence: phy-cadence-sierra: Enable pll_cmnlc and pll_cmnlc1 clocks

Get pll_cmnlc and pll_cmnlc1 optional clocks and enable them.
This will enable REFRCV/1 in case the pll_cmnlc/1 takes input
from REFRCV/1 respectively.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/phy/cadence/phy-cadence-sierra.c | 40 ++++++++++++++++++++++--
1 file changed, 37 insertions(+), 3 deletions(-)

diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index be2c91be4205..68d81f953f4f 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -768,6 +768,40 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
return 0;
}

+static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp)
+{
+ int ret;
+
+ ret = clk_prepare_enable(sp->input_clks[PLL0_REFCLK]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
+ if (ret)
+ goto err_pll_cmnlc;
+
+ ret = clk_prepare_enable(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]);
+ if (ret)
+ goto err_pll_cmnlc1;
+
+ return 0;
+
+err_pll_cmnlc:
+ clk_disable_unprepare(sp->input_clks[PHY_CLK]);
+
+err_pll_cmnlc1:
+ clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
+
+ return 0;
+}
+
+static void cdns_sierra_phy_disable_clocks(struct cdns_sierra_phy *sp)
+{
+ clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC1]);
+ clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
+ clk_disable_unprepare(sp->input_clks[PHY_CLK]);
+}
+
static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp,
struct device *dev)
{
@@ -848,7 +882,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
if (ret)
goto unregister_clk;

- ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
+ ret = cdns_sierra_phy_enable_clocks(sp);
if (ret)
goto unregister_clk;

@@ -925,7 +959,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
reset_control_put(sp->phys[i].lnk_rst);
of_node_put(child);
clk_disable:
- clk_disable_unprepare(sp->input_clks[PHY_CLK]);
+ cdns_sierra_phy_disable_clocks(sp);
reset_control_assert(sp->apb_rst);
unregister_clk:
cdns_sierra_clk_register(sp);
@@ -941,6 +975,7 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
reset_control_assert(phy->apb_rst);
pm_runtime_disable(&pdev->dev);

+ cdns_sierra_phy_disable_clocks(phy);
/*
* The device level resets will be put automatically.
* Need to put the subnode resets here though.
@@ -950,7 +985,6 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
reset_control_put(phy->phys[i].lnk_rst);
}

- clk_disable_unprepare(phy->input_clks[PHY_CLK]);
cdns_sierra_clk_unregister(phy);

return 0;
--
2.17.1

2021-03-04 14:48:32

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 02/13] phy: ti: j721e-wiz: Invoke wiz_init() before of_platform_device_create()

Invoke wiz_init() before configuring anything else in Sierra/Torrent
(invoked as part of of_platform_device_create()). wiz_init() resets the
SERDES device and any configuration done in the probe() of
Sierra/Torrent will be lost. In order to prevent SERDES configuration
from getting reset, invoke wiz_init() immediately before invoking
of_platform_device_create().

Fixes: 091876cc355d ("phy: ti: j721e-wiz: Add support for WIZ module present in TI J721E SoC")
Signed-off-by: Kishon Vijay Abraham I <[email protected]>
Cc: <[email protected]> # v5.10
---
drivers/phy/ti/phy-j721e-wiz.c | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c
index 995c7dbec77b..1bb73822f44a 100644
--- a/drivers/phy/ti/phy-j721e-wiz.c
+++ b/drivers/phy/ti/phy-j721e-wiz.c
@@ -1262,27 +1262,24 @@ static int wiz_probe(struct platform_device *pdev)
goto err_get_sync;
}

+ ret = wiz_init(wiz);
+ if (ret) {
+ dev_err(dev, "WIZ initialization failed\n");
+ goto err_wiz_init;
+ }
+
serdes_pdev = of_platform_device_create(child_node, NULL, dev);
if (!serdes_pdev) {
dev_WARN(dev, "Unable to create SERDES platform device\n");
ret = -ENOMEM;
- goto err_pdev_create;
- }
- wiz->serdes_pdev = serdes_pdev;
-
- ret = wiz_init(wiz);
- if (ret) {
- dev_err(dev, "WIZ initialization failed\n");
goto err_wiz_init;
}
+ wiz->serdes_pdev = serdes_pdev;

of_node_put(child_node);
return 0;

err_wiz_init:
- of_platform_device_destroy(&serdes_pdev->dev, NULL);
-
-err_pdev_create:
wiz_clock_cleanup(wiz, node);

err_get_sync:
--
2.17.1

2021-03-04 14:48:36

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 04/13] phy: ti: j721e-wiz: Get PHY properties only for "phy" or "link" subnode

"serdes" node (child node of WIZ) can have sub-nodes for representing links
or it can have sub-nodes for representing the various clocks within the
serdes. Instead of trying to read "reg" from every child node used for
assigning "lane_phy_type", read only if the child node's name is "phy"
or "link" subnode.
Ideally all PHY dt nodes should have node name as "phy", however
existing devicetree used "link" as subnode. So in order to maintain old
DT compatibility get PHY properties for "phy" or "link" subnode.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/phy/ti/phy-j721e-wiz.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c
index 1bb73822f44a..659597645201 100644
--- a/drivers/phy/ti/phy-j721e-wiz.c
+++ b/drivers/phy/ti/phy-j721e-wiz.c
@@ -1102,6 +1102,10 @@ static int wiz_get_lane_phy_types(struct device *dev, struct wiz *wiz)
u32 reg, num_lanes = 1, phy_type = PHY_NONE;
int ret, i;

+ if (!(of_node_name_eq(subnode, "phy") ||
+ of_node_name_eq(subnode, "link")))
+ continue;
+
ret = of_property_read_u32(subnode, "reg", &reg);
if (ret) {
dev_err(dev,
--
2.17.1

2021-03-04 14:51:23

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 12/13] phy: cadence: phy-cadence-sierra: Model PLL_CMNLC and PLL_CMNLC1 as clocks (mux clocks)

Sierra has two PLLs, PLL_CMNLC and PLL_CMNLC1 and each of these PLLs has
two inputs, plllc_refclk (input from pll0_refclk) and refrcv (input from
pll1_refclk). Model PLL_CMNLC and PLL_CMNLC1 as clocks so that it's
possible to select one of these two inputs from device tree.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/phy/cadence/Kconfig | 1 +
drivers/phy/cadence/phy-cadence-sierra.c | 267 ++++++++++++++++++++++-
2 files changed, 265 insertions(+), 3 deletions(-)

diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
index 432832bdbd16..23d5382c34ed 100644
--- a/drivers/phy/cadence/Kconfig
+++ b/drivers/phy/cadence/Kconfig
@@ -24,6 +24,7 @@ config PHY_CADENCE_DPHY
config PHY_CADENCE_SIERRA
tristate "Cadence Sierra PHY Driver"
depends on OF && HAS_IOMEM && RESET_CONTROLLER
+ depends on COMMON_CLK
select GENERIC_PHY
help
Enable this to support the Cadence Sierra PHY driver
diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index ac32b7b0289f..be2c91be4205 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -7,6 +7,7 @@
*
*/
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -20,10 +21,12 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <dt-bindings/phy/phy.h>
+#include <dt-bindings/phy/phy-cadence.h>

/* PHY register offsets */
#define SIERRA_COMMON_CDB_OFFSET 0x0
#define SIERRA_MACRO_ID_REG 0x0
+#define SIERRA_CMN_PLLLC_GEN_PREG 0x42
#define SIERRA_CMN_PLLLC_MODE_PREG 0x48
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49
#define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A
@@ -31,6 +34,9 @@
#define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F
#define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50
#define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62
+#define SIERRA_CMN_REFRCV_PREG 0x98
+#define SIERRA_CMN_REFRCV1_PREG 0xB8
+#define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2

#define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
((0x4000 << (block_offset)) + \
@@ -144,13 +150,19 @@
#define SIERRA_MAX_LANES 16
#define PLL_LOCK_TIME 100000

-#define CDNS_SIERRA_INPUT_CLOCKS 3
+#define CDNS_SIERRA_OUTPUT_CLOCKS 2
+#define CDNS_SIERRA_INPUT_CLOCKS 5
enum cdns_sierra_clock_input {
PHY_CLK,
CMN_REFCLK_DIG_DIV,
CMN_REFCLK1_DIG_DIV,
+ PLL0_REFCLK,
+ PLL1_REFCLK,
};

+#define SIERRA_NUM_CMN_PLLC 2
+#define SIERRA_NUM_CMN_PLLC_PARENTS 2
+
static const struct reg_field macro_id_type =
REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
static const struct reg_field phy_pll_cfg_1 =
@@ -158,6 +170,53 @@ static const struct reg_field phy_pll_cfg_1 =
static const struct reg_field pllctrl_lock =
REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0);

+static const char * const clk_names[] = {
+ [CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc",
+ [CDNS_SIERRA_PLL_CMNLC1] = "pll_cmnlc1",
+};
+
+enum cdns_sierra_cmn_plllc {
+ CMN_PLLLC,
+ CMN_PLLLC1,
+};
+
+struct cdns_sierra_pll_mux_reg_fields {
+ struct reg_field pfdclk_sel_preg;
+ struct reg_field plllc1en_field;
+ struct reg_field termen_field;
+};
+
+static const struct cdns_sierra_pll_mux_reg_fields cmn_plllc_pfdclk1_sel_preg[] = {
+ [CMN_PLLLC] = {
+ .pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC_GEN_PREG, 1, 1),
+ .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 8, 8),
+ .termen_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 0, 0),
+ },
+ [CMN_PLLLC1] = {
+ .pfdclk_sel_preg = REG_FIELD(SIERRA_CMN_PLLLC1_GEN_PREG, 1, 1),
+ .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 8, 8),
+ .termen_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 0, 0),
+ },
+};
+
+struct cdns_sierra_pll_mux {
+ struct clk_hw hw;
+ struct regmap_field *pfdclk_sel_preg;
+ struct regmap_field *plllc1en_field;
+ struct regmap_field *termen_field;
+ struct clk_init_data clk_data;
+};
+
+#define to_cdns_sierra_pll_mux(_hw) \
+ container_of(_hw, struct cdns_sierra_pll_mux, hw)
+
+static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS] = {
+ [CMN_PLLLC] = { PLL0_REFCLK, PLL1_REFCLK },
+ [CMN_PLLLC1] = { PLL1_REFCLK, PLL0_REFCLK },
+};
+
+static u32 cdns_sierra_pll_mux_table[] = { 0, 1 };
+
struct cdns_sierra_inst {
struct phy *phy;
u32 phy_type;
@@ -204,10 +263,15 @@ struct cdns_sierra_phy {
struct regmap_field *macro_id_type;
struct regmap_field *phy_pll_cfg_1;
struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
+ struct regmap_field *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC];
+ struct regmap_field *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC];
+ struct regmap_field *cmn_plllc_pfdclk1_sel_preg[SIERRA_NUM_CMN_PLLC];
struct clk *input_clks[CDNS_SIERRA_INPUT_CLOCKS];
int nsubnodes;
u32 num_lanes;
bool autoconf;
+ struct clk_onecell_data clk_data;
+ struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS];
};

static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val)
@@ -369,6 +433,153 @@ static const struct phy_ops ops = {
.owner = THIS_MODULE,
};

+static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw)
+{
+ struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
+ struct regmap_field *field = mux->pfdclk_sel_preg;
+ unsigned int val;
+
+ regmap_field_read(field, &val);
+ return clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table, 0, val);
+}
+
+static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
+ struct regmap_field *plllc1en_field = mux->plllc1en_field;
+ struct regmap_field *termen_field = mux->termen_field;
+ struct regmap_field *field = mux->pfdclk_sel_preg;
+ int val, ret;
+
+ ret = regmap_field_write(plllc1en_field, 0);
+ ret |= regmap_field_write(termen_field, 0);
+ if (index == 1) {
+ ret |= regmap_field_write(plllc1en_field, 1);
+ ret |= regmap_field_write(termen_field, 1);
+ }
+
+ val = cdns_sierra_pll_mux_table[index];
+ ret |= regmap_field_write(field, val);
+
+ return ret;
+}
+
+static const struct clk_ops cdns_sierra_pll_mux_ops = {
+ .set_parent = cdns_sierra_pll_mux_set_parent,
+ .get_parent = cdns_sierra_pll_mux_get_parent,
+};
+
+static int cdns_sierra_pll_mux_register(struct cdns_sierra_phy *sp,
+ struct regmap_field *pfdclk1_sel_field,
+ struct regmap_field *plllc1en_field,
+ struct regmap_field *termen_field,
+ int clk_index)
+{
+ struct cdns_sierra_pll_mux *mux;
+ struct device *dev = sp->dev;
+ struct clk_init_data *init;
+ const char **parent_names;
+ unsigned int num_parents;
+ char clk_name[100];
+ struct clk *clk;
+ int i;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ num_parents = SIERRA_NUM_CMN_PLLC_PARENTS;
+ parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL);
+ if (!parent_names)
+ return -ENOMEM;
+
+ for (i = 0; i < num_parents; i++) {
+ clk = sp->input_clks[pll_mux_parent_index[clk_index][i]];
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "No parent clock for derived_refclk\n");
+ return PTR_ERR(clk);
+ }
+ parent_names[i] = __clk_get_name(clk);
+ }
+
+ snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), clk_names[clk_index]);
+
+ init = &mux->clk_data;
+
+ init->ops = &cdns_sierra_pll_mux_ops;
+ init->flags = CLK_SET_RATE_NO_REPARENT;
+ init->parent_names = parent_names;
+ init->num_parents = num_parents;
+ init->name = clk_name;
+
+ mux->pfdclk_sel_preg = pfdclk1_sel_field;
+ mux->plllc1en_field = plllc1en_field;
+ mux->termen_field = termen_field;
+ mux->hw.init = init;
+
+ clk = devm_clk_register(dev, &mux->hw);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ sp->output_clks[clk_index] = clk;
+
+ return 0;
+}
+
+static int cdns_sierra_phy_register_pll_mux(struct cdns_sierra_phy *sp)
+{
+ struct regmap_field *pfdclk1_sel_field;
+ struct regmap_field *plllc1en_field;
+ struct regmap_field *termen_field;
+ struct device *dev = sp->dev;
+ int ret = 0, i, clk_index;
+
+ clk_index = CDNS_SIERRA_PLL_CMNLC;
+ for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++, clk_index++) {
+ pfdclk1_sel_field = sp->cmn_plllc_pfdclk1_sel_preg[i];
+ plllc1en_field = sp->cmn_refrcv_refclk_plllc1en_preg[i];
+ termen_field = sp->cmn_refrcv_refclk_termen_preg[i];
+
+ ret = cdns_sierra_pll_mux_register(sp, pfdclk1_sel_field, plllc1en_field,
+ termen_field, clk_index);
+ if (ret) {
+ dev_err(dev, "Fail to register cmn plllc mux\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void cdns_sierra_clk_unregister(struct cdns_sierra_phy *sp)
+{
+ struct device *dev = sp->dev;
+ struct device_node *node = dev->of_node;
+
+ of_clk_del_provider(node);
+}
+
+static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp)
+{
+ struct device *dev = sp->dev;
+ struct device_node *node = dev->of_node;
+ int ret;
+
+ ret = cdns_sierra_phy_register_pll_mux(sp);
+ if (ret) {
+ dev_err(dev, "Failed to pll mux clocks\n");
+ return ret;
+ }
+
+ sp->clk_data.clks = sp->output_clks;
+ sp->clk_data.clk_num = CDNS_SIERRA_OUTPUT_CLOCKS;
+ ret = of_clk_add_provider(node, of_clk_src_onecell_get, &sp->clk_data);
+ if (ret)
+ dev_err(dev, "Failed to add clock provider: %s\n", node->name);
+
+ return ret;
+}
+
static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
struct device_node *child)
{
@@ -407,6 +618,7 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp)
{
struct device *dev = sp->dev;
struct regmap_field *field;
+ struct reg_field reg_field;
struct regmap *regmap;
int i;

@@ -418,6 +630,32 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp)
}
sp->macro_id_type = field;

+ for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++) {
+ reg_field = cmn_plllc_pfdclk1_sel_preg[i].pfdclk_sel_preg;
+ field = devm_regmap_field_alloc(dev, regmap, reg_field);
+ if (IS_ERR(field)) {
+ dev_err(dev, "PLLLC%d_PFDCLK1_SEL failed\n", i);
+ return PTR_ERR(field);
+ }
+ sp->cmn_plllc_pfdclk1_sel_preg[i] = field;
+
+ reg_field = cmn_plllc_pfdclk1_sel_preg[i].plllc1en_field;
+ field = devm_regmap_field_alloc(dev, regmap, reg_field);
+ if (IS_ERR(field)) {
+ dev_err(dev, "REFRCV%d_REFCLK_PLLLC1EN failed\n", i);
+ return PTR_ERR(field);
+ }
+ sp->cmn_refrcv_refclk_plllc1en_preg[i] = field;
+
+ reg_field = cmn_plllc_pfdclk1_sel_preg[i].termen_field;
+ field = devm_regmap_field_alloc(dev, regmap, reg_field);
+ if (IS_ERR(field)) {
+ dev_err(dev, "REFRCV%d_REFCLK_TERMEN failed\n", i);
+ return PTR_ERR(field);
+ }
+ sp->cmn_refrcv_refclk_termen_preg[i] = field;
+ }
+
regmap = sp->regmap_phy_config_ctrl;
field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1);
if (IS_ERR(field)) {
@@ -511,6 +749,22 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
}
sp->input_clks[CMN_REFCLK1_DIG_DIV] = clk;

+ clk = devm_clk_get_optional(dev, "pll0_refclk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "pll0_refclk clock not found\n");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ sp->input_clks[PLL0_REFCLK] = clk;
+
+ clk = devm_clk_get_optional(dev, "pll1_refclk");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "pll1_refclk clock not found\n");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ sp->input_clks[PLL1_REFCLK] = clk;
+
return 0;
}

@@ -586,13 +840,17 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
if (ret)
return ret;

- ret = cdns_sierra_phy_get_resets(sp, dev);
+ ret = cdns_sierra_clk_register(sp);
if (ret)
return ret;

+ ret = cdns_sierra_phy_get_resets(sp, dev);
+ if (ret)
+ goto unregister_clk;
+
ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
if (ret)
- return ret;
+ goto unregister_clk;

/* Enable APB */
reset_control_deassert(sp->apb_rst);
@@ -669,6 +927,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
clk_disable:
clk_disable_unprepare(sp->input_clks[PHY_CLK]);
reset_control_assert(sp->apb_rst);
+unregister_clk:
+ cdns_sierra_clk_register(sp);
return ret;
}

@@ -691,6 +951,7 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
}

clk_disable_unprepare(phy->input_clks[PHY_CLK]);
+ cdns_sierra_clk_unregister(phy);

return 0;
}
--
2.17.1

2021-03-04 14:51:26

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 11/13] dt-bindings: phy: phy-cadence-sierra: Add binding to model Sierra as clock provider

Add #clock-cells binding to model Sierra as clock provider and include
clock IDs for PLL_CMNLC and PLL_CMNLC1.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
.../bindings/phy/phy-cadence-sierra.yaml | 17 ++++++++++++++++-
include/dt-bindings/phy/phy-cadence.h | 4 ++++
2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml
index d210843863df..84383e2e0b34 100644
--- a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml
+++ b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml
@@ -26,6 +26,9 @@ properties:
'#size-cells':
const: 0

+ '#clock-cells':
+ const: 1
+
resets:
minItems: 1
maxItems: 2
@@ -49,12 +52,24 @@ properties:
const: serdes

clocks:
- maxItems: 2
+ minItems: 2
+ maxItems: 4

clock-names:
+ minItems: 2
items:
- const: cmn_refclk_dig_div
- const: cmn_refclk1_dig_div
+ - const: pll0_refclk
+ - const: pll1_refclk
+
+ assigned-clocks:
+ minItems: 1
+ maxItems: 2
+
+ assigned-clock-parents:
+ minItems: 1
+ maxItems: 2

cdns,autoconf:
type: boolean
diff --git a/include/dt-bindings/phy/phy-cadence.h b/include/dt-bindings/phy/phy-cadence.h
index 4a5ea52a856f..4652bcb86265 100644
--- a/include/dt-bindings/phy/phy-cadence.h
+++ b/include/dt-bindings/phy/phy-cadence.h
@@ -13,4 +13,8 @@

#define CDNS_TORRENT_REFCLK_DRIVER 0

+/* Sierra */
+#define CDNS_SIERRA_PLL_CMNLC 0
+#define CDNS_SIERRA_PLL_CMNLC1 1
+
#endif /* _DT_BINDINGS_CADENCE_SERDES_H */
--
2.17.1

2021-03-05 00:02:02

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 07/13] phy: cadence: cadence-sierra: Explicitly request exclusive reset control

No functional change. Since the reset controls obtained in
Sierra is exclusively used by the Sierra device, use
exclusive reset control request API calls.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/phy/cadence/phy-cadence-sierra.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index 935f165404e4..44c52a0842dc 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -514,14 +514,14 @@ static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp,
{
struct reset_control *rst;

- rst = devm_reset_control_get(dev, "sierra_reset");
+ rst = devm_reset_control_get_exclusive(dev, "sierra_reset");
if (IS_ERR(rst)) {
dev_err(dev, "failed to get reset\n");
return PTR_ERR(rst);
}
sp->phy_rst = rst;

- rst = devm_reset_control_get_optional(dev, "sierra_apb");
+ rst = devm_reset_control_get_optional_exclusive(dev, "sierra_apb");
if (IS_ERR(rst)) {
dev_err(dev, "failed to get apb reset\n");
return PTR_ERR(rst);
--
2.17.1

2021-03-05 00:02:02

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 09/13] phy: cadence: cadence-sierra: Add array of input clocks in "struct cdns_sierra_phy"

Instead of having separate structure members for each input clock, add
an array for the input clocks within "struct cdns_sierra_phy". This is
in preparation for adding more input clocks required for supporting
additional clock combination.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/phy/cadence/phy-cadence-sierra.c | 25 ++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index 44c52a0842dc..a45278c30948 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -144,6 +144,13 @@
#define SIERRA_MAX_LANES 16
#define PLL_LOCK_TIME 100000

+#define CDNS_SIERRA_INPUT_CLOCKS 3
+enum cdns_sierra_clock_input {
+ PHY_CLK,
+ CMN_REFCLK_DIG_DIV,
+ CMN_REFCLK1_DIG_DIV,
+};
+
static const struct reg_field macro_id_type =
REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
static const struct reg_field phy_pll_cfg_1 =
@@ -197,9 +204,7 @@ struct cdns_sierra_phy {
struct regmap_field *macro_id_type;
struct regmap_field *phy_pll_cfg_1;
struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
- struct clk *clk;
- struct clk *cmn_refclk_dig_div;
- struct clk *cmn_refclk1_dig_div;
+ struct clk *input_clks[CDNS_SIERRA_INPUT_CLOCKS];
int nsubnodes;
u32 num_lanes;
bool autoconf;
@@ -281,8 +286,8 @@ static int cdns_sierra_phy_init(struct phy *gphy)
if (phy->autoconf)
return 0;

- clk_set_rate(phy->cmn_refclk_dig_div, 25000000);
- clk_set_rate(phy->cmn_refclk1_dig_div, 25000000);
+ clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000);
+ clk_set_rate(phy->input_clks[CMN_REFCLK1_DIG_DIV], 25000000);
if (ins->phy_type == PHY_TYPE_PCIE) {
num_cmn_regs = phy->init_data->pcie_cmn_regs;
num_ln_regs = phy->init_data->pcie_ln_regs;
@@ -488,7 +493,7 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
dev_err(dev, "failed to get clock phy_clk\n");
return PTR_ERR(clk);
}
- sp->clk = clk;
+ sp->input_clks[PHY_CLK] = clk;

clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
if (IS_ERR(clk)) {
@@ -496,7 +501,7 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
ret = PTR_ERR(clk);
return ret;
}
- sp->cmn_refclk_dig_div = clk;
+ sp->input_clks[CMN_REFCLK_DIG_DIV] = clk;

clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div");
if (IS_ERR(clk)) {
@@ -504,7 +509,7 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
ret = PTR_ERR(clk);
return ret;
}
- sp->cmn_refclk1_dig_div = clk;
+ sp->input_clks[CMN_REFCLK1_DIG_DIV] = clk;

return 0;
}
@@ -585,7 +590,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
if (ret)
return ret;

- ret = clk_prepare_enable(sp->clk);
+ ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
if (ret)
return ret;

@@ -662,7 +667,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
reset_control_put(sp->phys[i].lnk_rst);
of_node_put(child);
clk_disable:
- clk_disable_unprepare(sp->clk);
+ clk_disable_unprepare(sp->input_clks[PHY_CLK]);
reset_control_assert(sp->apb_rst);
return ret;
}
--
2.17.1

2021-03-05 00:02:21

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 01/13] phy: cadence: Sierra: Fix PHY power_on sequence

Commit 44d30d622821d ("phy: cadence: Add driver for Sierra PHY")
de-asserts PHY_RESET even before the configurations are loaded in
phy_init(). However PHY_RESET should be de-asserted only after
all the configurations has been initialized, instead of de-asserting
in probe. Fix it here.

Fixes: 44d30d622821d ("phy: cadence: Add driver for Sierra PHY")
Signed-off-by: Kishon Vijay Abraham I <[email protected]>
Cc: <[email protected]> # v5.4+
---
drivers/phy/cadence/phy-cadence-sierra.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index 26a0badabe38..19f32ae877b9 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -319,6 +319,12 @@ static int cdns_sierra_phy_on(struct phy *gphy)
u32 val;
int ret;

+ ret = reset_control_deassert(sp->phy_rst);
+ if (ret) {
+ dev_err(dev, "Failed to take the PHY out of reset\n");
+ return ret;
+ }
+
/* Take the PHY lane group out of reset */
ret = reset_control_deassert(ins->lnk_rst);
if (ret) {
@@ -616,7 +622,6 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)

pm_runtime_enable(dev);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
- reset_control_deassert(sp->phy_rst);
return PTR_ERR_OR_ZERO(phy_provider);

put_child:
--
2.17.1

2021-03-05 00:02:54

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 03/13] phy: cadence: cadence-sierra: Create PHY only for "phy" or "link" sub-nodes

Cadence Sierra PHY driver registers PHY using devm_phy_create()
for all sub-nodes of Sierra device tree node. However Sierra device
tree node can have sub-nodes for the various clocks in addtion to the
PHY. Use devm_phy_create() only for nodes with name "phy" (or "link"
for old device tree) which represent the actual PHY.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/phy/cadence/phy-cadence-sierra.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
index 19f32ae877b9..f7ba0ed416bc 100644
--- a/drivers/phy/cadence/phy-cadence-sierra.c
+++ b/drivers/phy/cadence/phy-cadence-sierra.c
@@ -577,6 +577,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
for_each_available_child_of_node(dn, child) {
struct phy *gphy;

+ if (!(of_node_name_eq(child, "phy") ||
+ of_node_name_eq(child, "link")))
+ continue;
+
sp->phys[node].lnk_rst =
of_reset_control_array_get_exclusive(child);

--
2.17.1

2021-03-05 00:02:57

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [PATCH v4 08/13] phy: cadence-torrent: Use a common header file for Cadence SERDES

No functional change. In order to have a single header file for all
Cadence SERDES move phy-cadence-torrent.h to phy-cadence.h. This is
in preparation for adding Cadence Sierra SERDES specific macros.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/phy/cadence/phy-cadence-torrent.c | 2 +-
.../phy/{phy-cadence-torrent.h => phy-cadence.h} | 9 +++++----
2 files changed, 6 insertions(+), 5 deletions(-)
rename include/dt-bindings/phy/{phy-cadence-torrent.h => phy-cadence.h} (51%)

diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c
index b371795e66a2..56083fd0c69f 100644
--- a/drivers/phy/cadence/phy-cadence-torrent.c
+++ b/drivers/phy/cadence/phy-cadence-torrent.c
@@ -7,7 +7,7 @@
*/

#include <dt-bindings/phy/phy.h>
-#include <dt-bindings/phy/phy-cadence-torrent.h>
+#include <dt-bindings/phy/phy-cadence.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
diff --git a/include/dt-bindings/phy/phy-cadence-torrent.h b/include/dt-bindings/phy/phy-cadence.h
similarity index 51%
rename from include/dt-bindings/phy/phy-cadence-torrent.h
rename to include/dt-bindings/phy/phy-cadence.h
index 3c92c6192493..4a5ea52a856f 100644
--- a/include/dt-bindings/phy/phy-cadence-torrent.h
+++ b/include/dt-bindings/phy/phy-cadence.h
@@ -1,15 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * This header provides constants for Cadence Torrent SERDES.
+ * This header provides constants for Cadence SERDES.
*/

-#ifndef _DT_BINDINGS_TORRENT_SERDES_H
-#define _DT_BINDINGS_TORRENT_SERDES_H
+#ifndef _DT_BINDINGS_CADENCE_SERDES_H
+#define _DT_BINDINGS_CADENCE_SERDES_H

+/* Torrent */
#define TORRENT_SERDES_NO_SSC 0
#define TORRENT_SERDES_EXTERNAL_SSC 1
#define TORRENT_SERDES_INTERNAL_SSC 2

#define CDNS_TORRENT_REFCLK_DRIVER 0

-#endif /* _DT_BINDINGS_TORRENT_SERDES_H */
+#endif /* _DT_BINDINGS_CADENCE_SERDES_H */
--
2.17.1

2021-03-05 00:11:49

by Philipp Zabel

[permalink] [raw]
Subject: Re: [PATCH v4 06/13] phy: cadence: cadence-sierra: Move all reset_control_get*() to a separate function

On Thu, 2021-03-04 at 10:11 +0530, Kishon Vijay Abraham I wrote:
> No functional change. Group devm_reset_control_get() and
> devm_reset_control_get_optional() to a separate function.
>
> Signed-off-by: Kishon Vijay Abraham I <[email protected]>
> ---
> drivers/phy/cadence/phy-cadence-sierra.c | 36 ++++++++++++++++--------
> 1 file changed, 25 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c
> index 7bf1b4c7774a..935f165404e4 100644
> --- a/drivers/phy/cadence/phy-cadence-sierra.c
> +++ b/drivers/phy/cadence/phy-cadence-sierra.c
> @@ -509,6 +509,28 @@ static int cdns_sierra_phy_get_clocks(struct cdns_sierra_phy *sp,
> return 0;
> }
>
> +static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp,
> + struct device *dev)
> +{
> + struct reset_control *rst;
> +
> + rst = devm_reset_control_get(dev, "sierra_reset");

Please use
rst = devm_reset_control_get_exclusive(dev, "sierra_reset");
here ...

> + if (IS_ERR(rst)) {
> + dev_err(dev, "failed to get reset\n");
> + return PTR_ERR(rst);
> + }
> + sp->phy_rst = rst;
> +
> + rst = devm_reset_control_get_optional(dev, "sierra_apb");

... and
rst = devm_reset_control_get_optional_exclusive(dev, "sierra_apb");
here (no functional change).

With that,

Reviewed-by: Philipp Zabel <[email protected]>

regards
Philipp

Subject: RE: [PATCH v4 12/13] phy: cadence: phy-cadence-sierra: Model PLL_CMNLC and PLL_CMNLC1 as clocks (mux clocks)

Hi Kishon,

> -----Original Message-----
> From: Kishon Vijay Abraham I <[email protected]>
> Sent: Thursday, March 4, 2021 10:11 AM
> To: Kishon Vijay Abraham I <[email protected]>; Vinod Koul
> <[email protected]>; Rob Herring <[email protected]>; Philipp Zabel
> <[email protected]>; Swapnil Kashinath Jakhade
> <[email protected]>
> Cc: [email protected]; [email protected]; Lokesh Vutla
> <[email protected]>
> Subject: [PATCH v4 12/13] phy: cadence: phy-cadence-sierra: Model
> PLL_CMNLC and PLL_CMNLC1 as clocks (mux clocks)
>
> EXTERNAL MAIL
>
>
> Sierra has two PLLs, PLL_CMNLC and PLL_CMNLC1 and each of these PLLs has
> two inputs, plllc_refclk (input from pll0_refclk) and refrcv (input from
> pll1_refclk). Model PLL_CMNLC and PLL_CMNLC1 as clocks so that it's
> possible to select one of these two inputs from device tree.
>
> Signed-off-by: Kishon Vijay Abraham I <[email protected]>
> ---
> drivers/phy/cadence/Kconfig | 1 +
> drivers/phy/cadence/phy-cadence-sierra.c | 267 ++++++++++++++++++++++-
> 2 files changed, 265 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
> index 432832bdbd16..23d5382c34ed 100644
> --- a/drivers/phy/cadence/Kconfig
> +++ b/drivers/phy/cadence/Kconfig
> @@ -24,6 +24,7 @@ config PHY_CADENCE_DPHY
> config PHY_CADENCE_SIERRA
> tristate "Cadence Sierra PHY Driver"
> depends on OF && HAS_IOMEM && RESET_CONTROLLER
> + depends on COMMON_CLK
> select GENERIC_PHY
> help
> Enable this to support the Cadence Sierra PHY driver
> diff --git a/drivers/phy/cadence/phy-cadence-sierra.c
> b/drivers/phy/cadence/phy-cadence-sierra.c
> index ac32b7b0289f..be2c91be4205 100644
> --- a/drivers/phy/cadence/phy-cadence-sierra.c
> +++ b/drivers/phy/cadence/phy-cadence-sierra.c
> @@ -7,6 +7,7 @@
> *
> */
> #include <linux/clk.h>
> +#include <linux/clk-provider.h>
> #include <linux/delay.h>
> #include <linux/err.h>
> #include <linux/io.h>
> @@ -20,10 +21,12 @@
> #include <linux/of.h>
> #include <linux/of_platform.h>
> #include <dt-bindings/phy/phy.h>
> +#include <dt-bindings/phy/phy-cadence.h>
>
> /* PHY register offsets */
> #define SIERRA_COMMON_CDB_OFFSET 0x0
> #define SIERRA_MACRO_ID_REG 0x0
> +#define SIERRA_CMN_PLLLC_GEN_PREG 0x42
> #define SIERRA_CMN_PLLLC_MODE_PREG 0x48
> #define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49
> #define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A
> @@ -31,6 +34,9 @@
> #define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F
> #define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50
> #define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62
> +#define SIERRA_CMN_REFRCV_PREG 0x98
> +#define SIERRA_CMN_REFRCV1_PREG 0xB8
> +#define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2
>
> #define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
> ((0x4000 << (block_offset)) + \
> @@ -144,13 +150,19 @@
> #define SIERRA_MAX_LANES 16
> #define PLL_LOCK_TIME 100000
>
> -#define CDNS_SIERRA_INPUT_CLOCKS 3
> +#define CDNS_SIERRA_OUTPUT_CLOCKS 2
> +#define CDNS_SIERRA_INPUT_CLOCKS 5
> enum cdns_sierra_clock_input {
> PHY_CLK,
> CMN_REFCLK_DIG_DIV,
> CMN_REFCLK1_DIG_DIV,
> + PLL0_REFCLK,
> + PLL1_REFCLK,
> };
>
> +#define SIERRA_NUM_CMN_PLLC 2
> +#define SIERRA_NUM_CMN_PLLC_PARENTS 2
> +
> static const struct reg_field macro_id_type =
> REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
> static const struct reg_field phy_pll_cfg_1 =
> @@ -158,6 +170,53 @@ static const struct reg_field phy_pll_cfg_1 =
> static const struct reg_field pllctrl_lock =
> REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0,
> 0);
>
> +static const char * const clk_names[] = {
> + [CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc",
> + [CDNS_SIERRA_PLL_CMNLC1] = "pll_cmnlc1",
> +};
> +
> +enum cdns_sierra_cmn_plllc {
> + CMN_PLLLC,
> + CMN_PLLLC1,
> +};
> +
> +struct cdns_sierra_pll_mux_reg_fields {
> + struct reg_field pfdclk_sel_preg;
> + struct reg_field plllc1en_field;
> + struct reg_field termen_field;
> +};
> +
> +static const struct cdns_sierra_pll_mux_reg_fields
> cmn_plllc_pfdclk1_sel_preg[] = {
> + [CMN_PLLLC] = {
> + .pfdclk_sel_preg =
> REG_FIELD(SIERRA_CMN_PLLLC_GEN_PREG, 1, 1),
> + .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 8,
> 8),
> + .termen_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 0,
> 0),
> + },
> + [CMN_PLLLC1] = {
> + .pfdclk_sel_preg =
> REG_FIELD(SIERRA_CMN_PLLLC1_GEN_PREG, 1, 1),
> + .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 8,
> 8),
> + .termen_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 0, 0),
> + },
> +};
> +
> +struct cdns_sierra_pll_mux {
> + struct clk_hw hw;
> + struct regmap_field *pfdclk_sel_preg;
> + struct regmap_field *plllc1en_field;
> + struct regmap_field *termen_field;
> + struct clk_init_data clk_data;
> +};
> +
> +#define to_cdns_sierra_pll_mux(_hw) \
> + container_of(_hw, struct cdns_sierra_pll_mux, hw)
> +
> +static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS]
> = {
> + [CMN_PLLLC] = { PLL0_REFCLK, PLL1_REFCLK },
> + [CMN_PLLLC1] = { PLL1_REFCLK, PLL0_REFCLK },
> +};
> +
> +static u32 cdns_sierra_pll_mux_table[] = { 0, 1 };
> +
> struct cdns_sierra_inst {
> struct phy *phy;
> u32 phy_type;
> @@ -204,10 +263,15 @@ struct cdns_sierra_phy {
> struct regmap_field *macro_id_type;
> struct regmap_field *phy_pll_cfg_1;
> struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
> + struct regmap_field
> *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC];
> + struct regmap_field
> *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC];
> + struct regmap_field
> *cmn_plllc_pfdclk1_sel_preg[SIERRA_NUM_CMN_PLLC];
> struct clk *input_clks[CDNS_SIERRA_INPUT_CLOCKS];
> int nsubnodes;
> u32 num_lanes;
> bool autoconf;
> + struct clk_onecell_data clk_data;
> + struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS];
> };
>
> static int cdns_regmap_write(void *context, unsigned int reg, unsigned int
> val)
> @@ -369,6 +433,153 @@ static const struct phy_ops ops = {
> .owner = THIS_MODULE,
> };
>
> +static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw)
> +{
> + struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
> + struct regmap_field *field = mux->pfdclk_sel_preg;
> + unsigned int val;
> +
> + regmap_field_read(field, &val);
> + return clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table, 0, val);
> +}
> +
> +static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index)
> +{
> + struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
> + struct regmap_field *plllc1en_field = mux->plllc1en_field;
> + struct regmap_field *termen_field = mux->termen_field;
> + struct regmap_field *field = mux->pfdclk_sel_preg;
> + int val, ret;
> +
> + ret = regmap_field_write(plllc1en_field, 0);
> + ret |= regmap_field_write(termen_field, 0);
> + if (index == 1) {
> + ret |= regmap_field_write(plllc1en_field, 1);
> + ret |= regmap_field_write(termen_field, 1);
> + }
> +
> + val = cdns_sierra_pll_mux_table[index];
> + ret |= regmap_field_write(field, val);
> +
> + return ret;
> +}
> +
> +static const struct clk_ops cdns_sierra_pll_mux_ops = {
> + .set_parent = cdns_sierra_pll_mux_set_parent,
> + .get_parent = cdns_sierra_pll_mux_get_parent,
> +};
> +
> +static int cdns_sierra_pll_mux_register(struct cdns_sierra_phy *sp,
> + struct regmap_field
> *pfdclk1_sel_field,
> + struct regmap_field *plllc1en_field,
> + struct regmap_field *termen_field,
> + int clk_index)
> +{
> + struct cdns_sierra_pll_mux *mux;
> + struct device *dev = sp->dev;
> + struct clk_init_data *init;
> + const char **parent_names;
> + unsigned int num_parents;
> + char clk_name[100];
> + struct clk *clk;
> + int i;
> +
> + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
> + if (!mux)
> + return -ENOMEM;
> +
> + num_parents = SIERRA_NUM_CMN_PLLC_PARENTS;
> + parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents),
> GFP_KERNEL);
> + if (!parent_names)
> + return -ENOMEM;
> +
> + for (i = 0; i < num_parents; i++) {
> + clk = sp->input_clks[pll_mux_parent_index[clk_index][i]];
> + if (IS_ERR_OR_NULL(clk)) {
> + dev_err(dev, "No parent clock for derived_refclk\n");
> + return PTR_ERR(clk);
> + }
> + parent_names[i] = __clk_get_name(clk);
> + }
> +
> + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
> clk_names[clk_index]);
> +
> + init = &mux->clk_data;
> +
> + init->ops = &cdns_sierra_pll_mux_ops;
> + init->flags = CLK_SET_RATE_NO_REPARENT;
> + init->parent_names = parent_names;
> + init->num_parents = num_parents;
> + init->name = clk_name;
> +
> + mux->pfdclk_sel_preg = pfdclk1_sel_field;
> + mux->plllc1en_field = plllc1en_field;
> + mux->termen_field = termen_field;
> + mux->hw.init = init;
> +
> + clk = devm_clk_register(dev, &mux->hw);
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + sp->output_clks[clk_index] = clk;
> +
> + return 0;
> +}
> +
> +static int cdns_sierra_phy_register_pll_mux(struct cdns_sierra_phy *sp)
> +{
> + struct regmap_field *pfdclk1_sel_field;
> + struct regmap_field *plllc1en_field;
> + struct regmap_field *termen_field;
> + struct device *dev = sp->dev;
> + int ret = 0, i, clk_index;
> +
> + clk_index = CDNS_SIERRA_PLL_CMNLC;
> + for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++, clk_index++) {
> + pfdclk1_sel_field = sp->cmn_plllc_pfdclk1_sel_preg[i];
> + plllc1en_field = sp->cmn_refrcv_refclk_plllc1en_preg[i];
> + termen_field = sp->cmn_refrcv_refclk_termen_preg[i];
> +
> + ret = cdns_sierra_pll_mux_register(sp, pfdclk1_sel_field,
> plllc1en_field,
> + termen_field, clk_index);
> + if (ret) {
> + dev_err(dev, "Fail to register cmn plllc mux\n");
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void cdns_sierra_clk_unregister(struct cdns_sierra_phy *sp)
> +{
> + struct device *dev = sp->dev;
> + struct device_node *node = dev->of_node;
> +
> + of_clk_del_provider(node);
> +}
> +
> +static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp)
> +{
> + struct device *dev = sp->dev;
> + struct device_node *node = dev->of_node;
> + int ret;
> +
> + ret = cdns_sierra_phy_register_pll_mux(sp);
> + if (ret) {
> + dev_err(dev, "Failed to pll mux clocks\n");
> + return ret;
> + }
> +
> + sp->clk_data.clks = sp->output_clks;
> + sp->clk_data.clk_num = CDNS_SIERRA_OUTPUT_CLOCKS;
> + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &sp-
> >clk_data);
> + if (ret)
> + dev_err(dev, "Failed to add clock provider: %s\n", node-
> >name);
> +
> + return ret;
> +}
> +
> static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
> struct device_node *child)
> {
> @@ -407,6 +618,7 @@ static int cdns_regfield_init(struct cdns_sierra_phy
> *sp)
> {
> struct device *dev = sp->dev;
> struct regmap_field *field;
> + struct reg_field reg_field;
> struct regmap *regmap;
> int i;
>
> @@ -418,6 +630,32 @@ static int cdns_regfield_init(struct cdns_sierra_phy
> *sp)
> }
> sp->macro_id_type = field;
>
> + for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++) {
> + reg_field = cmn_plllc_pfdclk1_sel_preg[i].pfdclk_sel_preg;
> + field = devm_regmap_field_alloc(dev, regmap, reg_field);
> + if (IS_ERR(field)) {
> + dev_err(dev, "PLLLC%d_PFDCLK1_SEL failed\n", i);
> + return PTR_ERR(field);
> + }
> + sp->cmn_plllc_pfdclk1_sel_preg[i] = field;
> +
> + reg_field = cmn_plllc_pfdclk1_sel_preg[i].plllc1en_field;
> + field = devm_regmap_field_alloc(dev, regmap, reg_field);
> + if (IS_ERR(field)) {
> + dev_err(dev, "REFRCV%d_REFCLK_PLLLC1EN failed\n",
> i);
> + return PTR_ERR(field);
> + }
> + sp->cmn_refrcv_refclk_plllc1en_preg[i] = field;
> +
> + reg_field = cmn_plllc_pfdclk1_sel_preg[i].termen_field;
> + field = devm_regmap_field_alloc(dev, regmap, reg_field);
> + if (IS_ERR(field)) {
> + dev_err(dev, "REFRCV%d_REFCLK_TERMEN failed\n",
> i);
> + return PTR_ERR(field);
> + }
> + sp->cmn_refrcv_refclk_termen_preg[i] = field;
> + }
> +
> regmap = sp->regmap_phy_config_ctrl;
> field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1);
> if (IS_ERR(field)) {
> @@ -511,6 +749,22 @@ static int cdns_sierra_phy_get_clocks(struct
> cdns_sierra_phy *sp,
> }
> sp->input_clks[CMN_REFCLK1_DIG_DIV] = clk;
>
> + clk = devm_clk_get_optional(dev, "pll0_refclk");
> + if (IS_ERR(clk)) {
> + dev_err(dev, "pll0_refclk clock not found\n");
> + ret = PTR_ERR(clk);
> + return ret;
> + }
> + sp->input_clks[PLL0_REFCLK] = clk;
> +
> + clk = devm_clk_get_optional(dev, "pll1_refclk");
> + if (IS_ERR(clk)) {
> + dev_err(dev, "pll1_refclk clock not found\n");
> + ret = PTR_ERR(clk);
> + return ret;
> + }
> + sp->input_clks[PLL1_REFCLK] = clk;
> +
> return 0;
> }
>
> @@ -586,13 +840,17 @@ static int cdns_sierra_phy_probe(struct
> platform_device *pdev)
> if (ret)
> return ret;
>
> - ret = cdns_sierra_phy_get_resets(sp, dev);
> + ret = cdns_sierra_clk_register(sp);
> if (ret)
> return ret;
>
> + ret = cdns_sierra_phy_get_resets(sp, dev);
> + if (ret)
> + goto unregister_clk;
> +
> ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
> if (ret)
> - return ret;
> + goto unregister_clk;
>
> /* Enable APB */
> reset_control_deassert(sp->apb_rst);
> @@ -669,6 +927,8 @@ static int cdns_sierra_phy_probe(struct
> platform_device *pdev)
> clk_disable:
> clk_disable_unprepare(sp->input_clks[PHY_CLK]);
> reset_control_assert(sp->apb_rst);
> +unregister_clk:
> + cdns_sierra_clk_register(sp);

Should be cdns_sierra_clk_unregister(sp) ?

> return ret;
> }
>
> @@ -691,6 +951,7 @@ static int cdns_sierra_phy_remove(struct
> platform_device *pdev)
> }
>
> clk_disable_unprepare(phy->input_clks[PHY_CLK]);
> + cdns_sierra_clk_unregister(phy);
>
> return 0;
> }
> --
> 2.17.1

Thanks & regards,
Swapnil

Subject: RE: [PATCH v4 13/13] phy: cadence: phy-cadence-sierra: Enable pll_cmnlc and pll_cmnlc1 clocks

Hi Kishon,

> -----Original Message-----
> From: Kishon Vijay Abraham I <[email protected]>
> Sent: Thursday, March 4, 2021 10:11 AM
> To: Kishon Vijay Abraham I <[email protected]>; Vinod Koul
> <[email protected]>; Rob Herring <[email protected]>; Philipp Zabel
> <[email protected]>; Swapnil Kashinath Jakhade
> <[email protected]>
> Cc: [email protected]; [email protected]; Lokesh Vutla
> <[email protected]>
> Subject: [PATCH v4 13/13] phy: cadence: phy-cadence-sierra: Enable
> pll_cmnlc and pll_cmnlc1 clocks
>
> EXTERNAL MAIL
>
>
> Get pll_cmnlc and pll_cmnlc1 optional clocks and enable them.
> This will enable REFRCV/1 in case the pll_cmnlc/1 takes input
> from REFRCV/1 respectively.
>
> Signed-off-by: Kishon Vijay Abraham I <[email protected]>
> ---
> drivers/phy/cadence/phy-cadence-sierra.c | 40 ++++++++++++++++++++++--
> 1 file changed, 37 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/phy/cadence/phy-cadence-sierra.c
> b/drivers/phy/cadence/phy-cadence-sierra.c
> index be2c91be4205..68d81f953f4f 100644
> --- a/drivers/phy/cadence/phy-cadence-sierra.c
> +++ b/drivers/phy/cadence/phy-cadence-sierra.c
> @@ -768,6 +768,40 @@ static int cdns_sierra_phy_get_clocks(struct
> cdns_sierra_phy *sp,
> return 0;
> }
>
> +static int cdns_sierra_phy_enable_clocks(struct cdns_sierra_phy *sp)
> +{
> + int ret;
> +
> + ret = clk_prepare_enable(sp->input_clks[PLL0_REFCLK]);

Should be PHY_CLK instead of PLL0_REFCLK ?

> + if (ret)
> + return ret;
> +
> + ret = clk_prepare_enable(sp-
> >output_clks[CDNS_SIERRA_PLL_CMNLC]);
> + if (ret)
> + goto err_pll_cmnlc;
> +
> + ret = clk_prepare_enable(sp-
> >output_clks[CDNS_SIERRA_PLL_CMNLC1]);
> + if (ret)
> + goto err_pll_cmnlc1;
> +
> + return 0;
> +
> +err_pll_cmnlc:
> + clk_disable_unprepare(sp->input_clks[PHY_CLK]);
> +
> +err_pll_cmnlc1:
> + clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
> +

Error handling above looks incorrect.
Also should this error path return ret instead of 0 ?

> + return 0;
> +}
> +
> +static void cdns_sierra_phy_disable_clocks(struct cdns_sierra_phy *sp)
> +{
> + clk_disable_unprepare(sp-
> >output_clks[CDNS_SIERRA_PLL_CMNLC1]);
> + clk_disable_unprepare(sp->output_clks[CDNS_SIERRA_PLL_CMNLC]);
> + clk_disable_unprepare(sp->input_clks[PHY_CLK]);
> +}
> +
> static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp,
> struct device *dev)
> {
> @@ -848,7 +882,7 @@ static int cdns_sierra_phy_probe(struct
> platform_device *pdev)
> if (ret)
> goto unregister_clk;
>
> - ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
> + ret = cdns_sierra_phy_enable_clocks(sp);
> if (ret)
> goto unregister_clk;
>
> @@ -925,7 +959,7 @@ static int cdns_sierra_phy_probe(struct
> platform_device *pdev)
> reset_control_put(sp->phys[i].lnk_rst);
> of_node_put(child);
> clk_disable:
> - clk_disable_unprepare(sp->input_clks[PHY_CLK]);
> + cdns_sierra_phy_disable_clocks(sp);
> reset_control_assert(sp->apb_rst);
> unregister_clk:
> cdns_sierra_clk_register(sp);
> @@ -941,6 +975,7 @@ static int cdns_sierra_phy_remove(struct
> platform_device *pdev)
> reset_control_assert(phy->apb_rst);
> pm_runtime_disable(&pdev->dev);
>
> + cdns_sierra_phy_disable_clocks(phy);
> /*
> * The device level resets will be put automatically.
> * Need to put the subnode resets here though.
> @@ -950,7 +985,6 @@ static int cdns_sierra_phy_remove(struct
> platform_device *pdev)
> reset_control_put(phy->phys[i].lnk_rst);
> }
>
> - clk_disable_unprepare(phy->input_clks[PHY_CLK]);
> cdns_sierra_clk_unregister(phy);
>
> return 0;
> --
> 2.17.1

Thanks & regards,
Swapnil

2021-03-08 08:41:47

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v4 12/13] phy: cadence: phy-cadence-sierra: Model PLL_CMNLC and PLL_CMNLC1 as clocks (mux clocks)

Hi Swapnil,

On 06/03/21 1:17 am, Swapnil Kashinath Jakhade wrote:
> Hi Kishon,
>
>> -----Original Message-----
>> From: Kishon Vijay Abraham I <[email protected]>
>> Sent: Thursday, March 4, 2021 10:11 AM
>> To: Kishon Vijay Abraham I <[email protected]>; Vinod Koul
>> <[email protected]>; Rob Herring <[email protected]>; Philipp Zabel
>> <[email protected]>; Swapnil Kashinath Jakhade
>> <[email protected]>
>> Cc: [email protected]; [email protected]; Lokesh Vutla
>> <[email protected]>
>> Subject: [PATCH v4 12/13] phy: cadence: phy-cadence-sierra: Model
>> PLL_CMNLC and PLL_CMNLC1 as clocks (mux clocks)
>>
>> EXTERNAL MAIL
>>
>>
>> Sierra has two PLLs, PLL_CMNLC and PLL_CMNLC1 and each of these PLLs has
>> two inputs, plllc_refclk (input from pll0_refclk) and refrcv (input from
>> pll1_refclk). Model PLL_CMNLC and PLL_CMNLC1 as clocks so that it's
>> possible to select one of these two inputs from device tree.
>>
>> Signed-off-by: Kishon Vijay Abraham I <[email protected]>
>> ---
>> drivers/phy/cadence/Kconfig | 1 +
>> drivers/phy/cadence/phy-cadence-sierra.c | 267 ++++++++++++++++++++++-
>> 2 files changed, 265 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
>> index 432832bdbd16..23d5382c34ed 100644
>> --- a/drivers/phy/cadence/Kconfig
>> +++ b/drivers/phy/cadence/Kconfig
>> @@ -24,6 +24,7 @@ config PHY_CADENCE_DPHY
>> config PHY_CADENCE_SIERRA
>> tristate "Cadence Sierra PHY Driver"
>> depends on OF && HAS_IOMEM && RESET_CONTROLLER
>> + depends on COMMON_CLK
>> select GENERIC_PHY
>> help
>> Enable this to support the Cadence Sierra PHY driver
>> diff --git a/drivers/phy/cadence/phy-cadence-sierra.c
>> b/drivers/phy/cadence/phy-cadence-sierra.c
>> index ac32b7b0289f..be2c91be4205 100644
>> --- a/drivers/phy/cadence/phy-cadence-sierra.c
>> +++ b/drivers/phy/cadence/phy-cadence-sierra.c
>> @@ -7,6 +7,7 @@
>> *
>> */
>> #include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> #include <linux/delay.h>
>> #include <linux/err.h>
>> #include <linux/io.h>
>> @@ -20,10 +21,12 @@
>> #include <linux/of.h>
>> #include <linux/of_platform.h>
>> #include <dt-bindings/phy/phy.h>
>> +#include <dt-bindings/phy/phy-cadence.h>
>>
>> /* PHY register offsets */
>> #define SIERRA_COMMON_CDB_OFFSET 0x0
>> #define SIERRA_MACRO_ID_REG 0x0
>> +#define SIERRA_CMN_PLLLC_GEN_PREG 0x42
>> #define SIERRA_CMN_PLLLC_MODE_PREG 0x48
>> #define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49
>> #define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A
>> @@ -31,6 +34,9 @@
>> #define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F
>> #define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50
>> #define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62
>> +#define SIERRA_CMN_REFRCV_PREG 0x98
>> +#define SIERRA_CMN_REFRCV1_PREG 0xB8
>> +#define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2
>>
>> #define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
>> ((0x4000 << (block_offset)) + \
>> @@ -144,13 +150,19 @@
>> #define SIERRA_MAX_LANES 16
>> #define PLL_LOCK_TIME 100000
>>
>> -#define CDNS_SIERRA_INPUT_CLOCKS 3
>> +#define CDNS_SIERRA_OUTPUT_CLOCKS 2
>> +#define CDNS_SIERRA_INPUT_CLOCKS 5
>> enum cdns_sierra_clock_input {
>> PHY_CLK,
>> CMN_REFCLK_DIG_DIV,
>> CMN_REFCLK1_DIG_DIV,
>> + PLL0_REFCLK,
>> + PLL1_REFCLK,
>> };
>>
>> +#define SIERRA_NUM_CMN_PLLC 2
>> +#define SIERRA_NUM_CMN_PLLC_PARENTS 2
>> +
>> static const struct reg_field macro_id_type =
>> REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
>> static const struct reg_field phy_pll_cfg_1 =
>> @@ -158,6 +170,53 @@ static const struct reg_field phy_pll_cfg_1 =
>> static const struct reg_field pllctrl_lock =
>> REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0,
>> 0);
>>
>> +static const char * const clk_names[] = {
>> + [CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc",
>> + [CDNS_SIERRA_PLL_CMNLC1] = "pll_cmnlc1",
>> +};
>> +
>> +enum cdns_sierra_cmn_plllc {
>> + CMN_PLLLC,
>> + CMN_PLLLC1,
>> +};
>> +
>> +struct cdns_sierra_pll_mux_reg_fields {
>> + struct reg_field pfdclk_sel_preg;
>> + struct reg_field plllc1en_field;
>> + struct reg_field termen_field;
>> +};
>> +
>> +static const struct cdns_sierra_pll_mux_reg_fields
>> cmn_plllc_pfdclk1_sel_preg[] = {
>> + [CMN_PLLLC] = {
>> + .pfdclk_sel_preg =
>> REG_FIELD(SIERRA_CMN_PLLLC_GEN_PREG, 1, 1),
>> + .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 8,
>> 8),
>> + .termen_field = REG_FIELD(SIERRA_CMN_REFRCV1_PREG, 0,
>> 0),
>> + },
>> + [CMN_PLLLC1] = {
>> + .pfdclk_sel_preg =
>> REG_FIELD(SIERRA_CMN_PLLLC1_GEN_PREG, 1, 1),
>> + .plllc1en_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 8,
>> 8),
>> + .termen_field = REG_FIELD(SIERRA_CMN_REFRCV_PREG, 0, 0),
>> + },
>> +};
>> +
>> +struct cdns_sierra_pll_mux {
>> + struct clk_hw hw;
>> + struct regmap_field *pfdclk_sel_preg;
>> + struct regmap_field *plllc1en_field;
>> + struct regmap_field *termen_field;
>> + struct clk_init_data clk_data;
>> +};
>> +
>> +#define to_cdns_sierra_pll_mux(_hw) \
>> + container_of(_hw, struct cdns_sierra_pll_mux, hw)
>> +
>> +static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS]
>> = {
>> + [CMN_PLLLC] = { PLL0_REFCLK, PLL1_REFCLK },
>> + [CMN_PLLLC1] = { PLL1_REFCLK, PLL0_REFCLK },
>> +};
>> +
>> +static u32 cdns_sierra_pll_mux_table[] = { 0, 1 };
>> +
>> struct cdns_sierra_inst {
>> struct phy *phy;
>> u32 phy_type;
>> @@ -204,10 +263,15 @@ struct cdns_sierra_phy {
>> struct regmap_field *macro_id_type;
>> struct regmap_field *phy_pll_cfg_1;
>> struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
>> + struct regmap_field
>> *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC];
>> + struct regmap_field
>> *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC];
>> + struct regmap_field
>> *cmn_plllc_pfdclk1_sel_preg[SIERRA_NUM_CMN_PLLC];
>> struct clk *input_clks[CDNS_SIERRA_INPUT_CLOCKS];
>> int nsubnodes;
>> u32 num_lanes;
>> bool autoconf;
>> + struct clk_onecell_data clk_data;
>> + struct clk *output_clks[CDNS_SIERRA_OUTPUT_CLOCKS];
>> };
>>
>> static int cdns_regmap_write(void *context, unsigned int reg, unsigned int
>> val)
>> @@ -369,6 +433,153 @@ static const struct phy_ops ops = {
>> .owner = THIS_MODULE,
>> };
>>
>> +static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw)
>> +{
>> + struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
>> + struct regmap_field *field = mux->pfdclk_sel_preg;
>> + unsigned int val;
>> +
>> + regmap_field_read(field, &val);
>> + return clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table, 0, val);
>> +}
>> +
>> +static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index)
>> +{
>> + struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw);
>> + struct regmap_field *plllc1en_field = mux->plllc1en_field;
>> + struct regmap_field *termen_field = mux->termen_field;
>> + struct regmap_field *field = mux->pfdclk_sel_preg;
>> + int val, ret;
>> +
>> + ret = regmap_field_write(plllc1en_field, 0);
>> + ret |= regmap_field_write(termen_field, 0);
>> + if (index == 1) {
>> + ret |= regmap_field_write(plllc1en_field, 1);
>> + ret |= regmap_field_write(termen_field, 1);
>> + }
>> +
>> + val = cdns_sierra_pll_mux_table[index];
>> + ret |= regmap_field_write(field, val);
>> +
>> + return ret;
>> +}
>> +
>> +static const struct clk_ops cdns_sierra_pll_mux_ops = {
>> + .set_parent = cdns_sierra_pll_mux_set_parent,
>> + .get_parent = cdns_sierra_pll_mux_get_parent,
>> +};
>> +
>> +static int cdns_sierra_pll_mux_register(struct cdns_sierra_phy *sp,
>> + struct regmap_field
>> *pfdclk1_sel_field,
>> + struct regmap_field *plllc1en_field,
>> + struct regmap_field *termen_field,
>> + int clk_index)
>> +{
>> + struct cdns_sierra_pll_mux *mux;
>> + struct device *dev = sp->dev;
>> + struct clk_init_data *init;
>> + const char **parent_names;
>> + unsigned int num_parents;
>> + char clk_name[100];
>> + struct clk *clk;
>> + int i;
>> +
>> + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
>> + if (!mux)
>> + return -ENOMEM;
>> +
>> + num_parents = SIERRA_NUM_CMN_PLLC_PARENTS;
>> + parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents),
>> GFP_KERNEL);
>> + if (!parent_names)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < num_parents; i++) {
>> + clk = sp->input_clks[pll_mux_parent_index[clk_index][i]];
>> + if (IS_ERR_OR_NULL(clk)) {
>> + dev_err(dev, "No parent clock for derived_refclk\n");
>> + return PTR_ERR(clk);
>> + }
>> + parent_names[i] = __clk_get_name(clk);
>> + }
>> +
>> + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
>> clk_names[clk_index]);
>> +
>> + init = &mux->clk_data;
>> +
>> + init->ops = &cdns_sierra_pll_mux_ops;
>> + init->flags = CLK_SET_RATE_NO_REPARENT;
>> + init->parent_names = parent_names;
>> + init->num_parents = num_parents;
>> + init->name = clk_name;
>> +
>> + mux->pfdclk_sel_preg = pfdclk1_sel_field;
>> + mux->plllc1en_field = plllc1en_field;
>> + mux->termen_field = termen_field;
>> + mux->hw.init = init;
>> +
>> + clk = devm_clk_register(dev, &mux->hw);
>> + if (IS_ERR(clk))
>> + return PTR_ERR(clk);
>> +
>> + sp->output_clks[clk_index] = clk;
>> +
>> + return 0;
>> +}
>> +
>> +static int cdns_sierra_phy_register_pll_mux(struct cdns_sierra_phy *sp)
>> +{
>> + struct regmap_field *pfdclk1_sel_field;
>> + struct regmap_field *plllc1en_field;
>> + struct regmap_field *termen_field;
>> + struct device *dev = sp->dev;
>> + int ret = 0, i, clk_index;
>> +
>> + clk_index = CDNS_SIERRA_PLL_CMNLC;
>> + for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++, clk_index++) {
>> + pfdclk1_sel_field = sp->cmn_plllc_pfdclk1_sel_preg[i];
>> + plllc1en_field = sp->cmn_refrcv_refclk_plllc1en_preg[i];
>> + termen_field = sp->cmn_refrcv_refclk_termen_preg[i];
>> +
>> + ret = cdns_sierra_pll_mux_register(sp, pfdclk1_sel_field,
>> plllc1en_field,
>> + termen_field, clk_index);
>> + if (ret) {
>> + dev_err(dev, "Fail to register cmn plllc mux\n");
>> + return ret;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void cdns_sierra_clk_unregister(struct cdns_sierra_phy *sp)
>> +{
>> + struct device *dev = sp->dev;
>> + struct device_node *node = dev->of_node;
>> +
>> + of_clk_del_provider(node);
>> +}
>> +
>> +static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp)
>> +{
>> + struct device *dev = sp->dev;
>> + struct device_node *node = dev->of_node;
>> + int ret;
>> +
>> + ret = cdns_sierra_phy_register_pll_mux(sp);
>> + if (ret) {
>> + dev_err(dev, "Failed to pll mux clocks\n");
>> + return ret;
>> + }
>> +
>> + sp->clk_data.clks = sp->output_clks;
>> + sp->clk_data.clk_num = CDNS_SIERRA_OUTPUT_CLOCKS;
>> + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &sp-
>>> clk_data);
>> + if (ret)
>> + dev_err(dev, "Failed to add clock provider: %s\n", node-
>>> name);
>> +
>> + return ret;
>> +}
>> +
>> static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
>> struct device_node *child)
>> {
>> @@ -407,6 +618,7 @@ static int cdns_regfield_init(struct cdns_sierra_phy
>> *sp)
>> {
>> struct device *dev = sp->dev;
>> struct regmap_field *field;
>> + struct reg_field reg_field;
>> struct regmap *regmap;
>> int i;
>>
>> @@ -418,6 +630,32 @@ static int cdns_regfield_init(struct cdns_sierra_phy
>> *sp)
>> }
>> sp->macro_id_type = field;
>>
>> + for (i = 0; i < SIERRA_NUM_CMN_PLLC; i++) {
>> + reg_field = cmn_plllc_pfdclk1_sel_preg[i].pfdclk_sel_preg;
>> + field = devm_regmap_field_alloc(dev, regmap, reg_field);
>> + if (IS_ERR(field)) {
>> + dev_err(dev, "PLLLC%d_PFDCLK1_SEL failed\n", i);
>> + return PTR_ERR(field);
>> + }
>> + sp->cmn_plllc_pfdclk1_sel_preg[i] = field;
>> +
>> + reg_field = cmn_plllc_pfdclk1_sel_preg[i].plllc1en_field;
>> + field = devm_regmap_field_alloc(dev, regmap, reg_field);
>> + if (IS_ERR(field)) {
>> + dev_err(dev, "REFRCV%d_REFCLK_PLLLC1EN failed\n",
>> i);
>> + return PTR_ERR(field);
>> + }
>> + sp->cmn_refrcv_refclk_plllc1en_preg[i] = field;
>> +
>> + reg_field = cmn_plllc_pfdclk1_sel_preg[i].termen_field;
>> + field = devm_regmap_field_alloc(dev, regmap, reg_field);
>> + if (IS_ERR(field)) {
>> + dev_err(dev, "REFRCV%d_REFCLK_TERMEN failed\n",
>> i);
>> + return PTR_ERR(field);
>> + }
>> + sp->cmn_refrcv_refclk_termen_preg[i] = field;
>> + }
>> +
>> regmap = sp->regmap_phy_config_ctrl;
>> field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1);
>> if (IS_ERR(field)) {
>> @@ -511,6 +749,22 @@ static int cdns_sierra_phy_get_clocks(struct
>> cdns_sierra_phy *sp,
>> }
>> sp->input_clks[CMN_REFCLK1_DIG_DIV] = clk;
>>
>> + clk = devm_clk_get_optional(dev, "pll0_refclk");
>> + if (IS_ERR(clk)) {
>> + dev_err(dev, "pll0_refclk clock not found\n");
>> + ret = PTR_ERR(clk);
>> + return ret;
>> + }
>> + sp->input_clks[PLL0_REFCLK] = clk;
>> +
>> + clk = devm_clk_get_optional(dev, "pll1_refclk");
>> + if (IS_ERR(clk)) {
>> + dev_err(dev, "pll1_refclk clock not found\n");
>> + ret = PTR_ERR(clk);
>> + return ret;
>> + }
>> + sp->input_clks[PLL1_REFCLK] = clk;
>> +
>> return 0;
>> }
>>
>> @@ -586,13 +840,17 @@ static int cdns_sierra_phy_probe(struct
>> platform_device *pdev)
>> if (ret)
>> return ret;
>>
>> - ret = cdns_sierra_phy_get_resets(sp, dev);
>> + ret = cdns_sierra_clk_register(sp);
>> if (ret)
>> return ret;
>>
>> + ret = cdns_sierra_phy_get_resets(sp, dev);
>> + if (ret)
>> + goto unregister_clk;
>> +
>> ret = clk_prepare_enable(sp->input_clks[PHY_CLK]);
>> if (ret)
>> - return ret;
>> + goto unregister_clk;
>>
>> /* Enable APB */
>> reset_control_deassert(sp->apb_rst);
>> @@ -669,6 +927,8 @@ static int cdns_sierra_phy_probe(struct
>> platform_device *pdev)
>> clk_disable:
>> clk_disable_unprepare(sp->input_clks[PHY_CLK]);
>> reset_control_assert(sp->apb_rst);
>> +unregister_clk:
>> + cdns_sierra_clk_register(sp);
>
> Should be cdns_sierra_clk_unregister(sp) ?
>
>> return ret;
>> }
>>
>> @@ -691,6 +951,7 @@ static int cdns_sierra_phy_remove(struct
>> platform_device *pdev)
>> }
>>
>> clk_disable_unprepare(phy->input_clks[PHY_CLK]);
>> + cdns_sierra_clk_unregister(phy);

Thank you for reviewing and spotting this. Will both the patches and resend.

Thanks
Kishon

>>
>> return 0;
>> }
>> --
>> 2.17.1
>
> Thanks & regards,
> Swapnil
>