[4/9] net: mdio: ipq4019: configure CMN PLL clock for ipq5332
Commit Message
The reference clock of CMN PLL block is selectable, the internal
48MHZ is used by default.
The output clock of CMN PLL block is for providing the clock
source of ethernet device(such as qca8084), there are 1 X 25MHZ
and 3 x 50MHZ output clocks available.
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
---
drivers/net/mdio/mdio-ipq4019.c | 81 ++++++++++++++++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)
Comments
> +static void ipq_cmn_clock_config(struct mii_bus *bus)
> +{
> + u32 reg_val;
> + const char *cmn_ref_clk;
> + struct ipq4019_mdio_data *priv = bus->priv;
Reverse christmass tree place.
> +
> + if (priv && priv->cmn_membase) {
Can priv be NULL? Can cmn_membase be NULL?
> + reg_val = readl(priv->cmn_membase + CMN_PLL_REFERENCE_CLOCK);
> + reg_val &= ~(CMN_PLL_REFCLK_EXTERNAL | CMN_PLL_REFCLK_INDEX);
> +
> + /* Select reference clock source */
> + cmn_ref_clk = of_get_property(bus->parent->of_node, "cmn_ref_clk", NULL);
> + if (!cmn_ref_clk) {
> + /* Internal 48MHZ selected by default */
> + reg_val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
> + } else {
> + if (!strcmp(cmn_ref_clk, "external_25MHz"))
Not strings, please use u32 values. You can then list the valid values
in the yaml file, and get te tools to verify the DT.
> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 3));
> + else if (!strcmp(cmn_ref_clk, "external_31250KHz"))
> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 4));
> + else if (!strcmp(cmn_ref_clk, "external_40MHz"))
> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 6));
> + else if (!strcmp(cmn_ref_clk, "external_48MHz"))
> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7));
> + else if (!strcmp(cmn_ref_clk, "external_50MHz"))
> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 8));
> + else
> + reg_val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
If the value is not valid, return -EINVAL.
Andrew
On 11/15/2023 11:19 PM, Andrew Lunn wrote:
>> +static void ipq_cmn_clock_config(struct mii_bus *bus)
>> +{
>> + u32 reg_val;
>> + const char *cmn_ref_clk;
>> + struct ipq4019_mdio_data *priv = bus->priv;
>
> Reverse christmass tree place.
will fix it in the next patch set.
>
>> +
>> + if (priv && priv->cmn_membase) {
>
> Can priv be NULL? Can cmn_membase be NULL?
priv can't be NULL, cmn_membase is optional, the legacy chip does not
provide the cmn_membase in device node.
will remove the priv check here.
>
>> + reg_val = readl(priv->cmn_membase + CMN_PLL_REFERENCE_CLOCK);
>> + reg_val &= ~(CMN_PLL_REFCLK_EXTERNAL | CMN_PLL_REFCLK_INDEX);
>> +
>> + /* Select reference clock source */
>> + cmn_ref_clk = of_get_property(bus->parent->of_node, "cmn_ref_clk", NULL);
>> + if (!cmn_ref_clk) {
>> + /* Internal 48MHZ selected by default */
>> + reg_val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
>> + } else {
>> + if (!strcmp(cmn_ref_clk, "external_25MHz"))
>
> Not strings, please use u32 values. You can then list the valid values
> in the yaml file, and get te tools to verify the DT.
will update this in the next patch.
>
>> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
>> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 3));
>> + else if (!strcmp(cmn_ref_clk, "external_31250KHz"))
>> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
>> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 4));
>> + else if (!strcmp(cmn_ref_clk, "external_40MHz"))
>> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
>> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 6));
>> + else if (!strcmp(cmn_ref_clk, "external_48MHz"))
>> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
>> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7));
>> + else if (!strcmp(cmn_ref_clk, "external_50MHz"))
>> + reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
>> + FIELD_PREP(CMN_PLL_REFCLK_INDEX, 8));
>> + else
>> + reg_val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
>
> If the value is not valid, return -EINVAL.
will add it in the next patch set.
>
> Andrew
On 11/15/23 04:25, Luo Jie wrote:
> The reference clock of CMN PLL block is selectable, the internal
> 48MHZ is used by default.
>
> The output clock of CMN PLL block is for providing the clock
> source of ethernet device(such as qca8084), there are 1 X 25MHZ
> and 3 x 50MHZ output clocks available.
>
> Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
> ---
> drivers/net/mdio/mdio-ipq4019.c | 81 ++++++++++++++++++++++++++++++++-
> 1 file changed, 80 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/mdio/mdio-ipq4019.c b/drivers/net/mdio/mdio-ipq4019.c
> index 93ae4684de31..ca9cda98d1f8 100644
> --- a/drivers/net/mdio/mdio-ipq4019.c
> +++ b/drivers/net/mdio/mdio-ipq4019.c
> @@ -43,6 +43,13 @@
> /* Maximum SOC PCS(uniphy) number on IPQ platform */
> #define ETH_LDO_RDY_CNT 3
>
> +#define CMN_PLL_REFERENCE_CLOCK 0x784
> +#define CMN_PLL_REFCLK_INDEX GENMASK(3, 0)
> +#define CMN_PLL_REFCLK_EXTERNAL BIT(9)
> +
> +#define CMN_PLL_POWER_ON_AND_RESET 0x780
> +#define CMN_ANA_EN_SW_RSTN BIT(6)
> +
> enum mdio_clk_id {
> MDIO_CLK_MDIO_AHB,
> MDIO_CLK_UNIPHY0_AHB,
> @@ -54,6 +61,7 @@ enum mdio_clk_id {
>
> struct ipq4019_mdio_data {
> void __iomem *membase;
> + void __iomem *cmn_membase;
> void __iomem *eth_ldo_rdy[ETH_LDO_RDY_CNT];
> struct clk *clk[MDIO_CLK_CNT];
> struct gpio_descs *reset_gpios;
> @@ -227,12 +235,73 @@ static int ipq4019_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,
> return 0;
> }
>
> +/* For the CMN PLL block, the reference clock can be configured according to
> + * the device tree property "cmn_ref_clk", the internal 48MHZ is used by default
> + * on the ipq533 platform.
> + *
> + * The output clock of CMN PLL block is provided to the MDIO slave devices,
> + * threre are 4 CMN PLL output clocks (1x25MHZ + 3x50MHZ) enabled by default.
> + *
> + * such as the output 50M clock for the qca8084 PHY.
> + */
> +static void ipq_cmn_clock_config(struct mii_bus *bus)
> +{
> + u32 reg_val;
> + const char *cmn_ref_clk;
> + struct ipq4019_mdio_data *priv = bus->priv;
> +
> + if (priv && priv->cmn_membase) {
> + reg_val = readl(priv->cmn_membase + CMN_PLL_REFERENCE_CLOCK);
> + reg_val &= ~(CMN_PLL_REFCLK_EXTERNAL | CMN_PLL_REFCLK_INDEX);
> +
> + /* Select reference clock source */
> + cmn_ref_clk = of_get_property(bus->parent->of_node, "cmn_ref_clk", NULL);
> + if (!cmn_ref_clk) {
> + /* Internal 48MHZ selected by default */
> + reg_val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
> + } else {
> + if (!strcmp(cmn_ref_clk, "external_25MHz"))
As pointed out by others, such string properties won't go through
Konrad
@@ -43,6 +43,13 @@
/* Maximum SOC PCS(uniphy) number on IPQ platform */
#define ETH_LDO_RDY_CNT 3
+#define CMN_PLL_REFERENCE_CLOCK 0x784
+#define CMN_PLL_REFCLK_INDEX GENMASK(3, 0)
+#define CMN_PLL_REFCLK_EXTERNAL BIT(9)
+
+#define CMN_PLL_POWER_ON_AND_RESET 0x780
+#define CMN_ANA_EN_SW_RSTN BIT(6)
+
enum mdio_clk_id {
MDIO_CLK_MDIO_AHB,
MDIO_CLK_UNIPHY0_AHB,
@@ -54,6 +61,7 @@ enum mdio_clk_id {
struct ipq4019_mdio_data {
void __iomem *membase;
+ void __iomem *cmn_membase;
void __iomem *eth_ldo_rdy[ETH_LDO_RDY_CNT];
struct clk *clk[MDIO_CLK_CNT];
struct gpio_descs *reset_gpios;
@@ -227,12 +235,73 @@ static int ipq4019_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,
return 0;
}
+/* For the CMN PLL block, the reference clock can be configured according to
+ * the device tree property "cmn_ref_clk", the internal 48MHZ is used by default
+ * on the ipq533 platform.
+ *
+ * The output clock of CMN PLL block is provided to the MDIO slave devices,
+ * threre are 4 CMN PLL output clocks (1x25MHZ + 3x50MHZ) enabled by default.
+ *
+ * such as the output 50M clock for the qca8084 PHY.
+ */
+static void ipq_cmn_clock_config(struct mii_bus *bus)
+{
+ u32 reg_val;
+ const char *cmn_ref_clk;
+ struct ipq4019_mdio_data *priv = bus->priv;
+
+ if (priv && priv->cmn_membase) {
+ reg_val = readl(priv->cmn_membase + CMN_PLL_REFERENCE_CLOCK);
+ reg_val &= ~(CMN_PLL_REFCLK_EXTERNAL | CMN_PLL_REFCLK_INDEX);
+
+ /* Select reference clock source */
+ cmn_ref_clk = of_get_property(bus->parent->of_node, "cmn_ref_clk", NULL);
+ if (!cmn_ref_clk) {
+ /* Internal 48MHZ selected by default */
+ reg_val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
+ } else {
+ if (!strcmp(cmn_ref_clk, "external_25MHz"))
+ reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
+ FIELD_PREP(CMN_PLL_REFCLK_INDEX, 3));
+ else if (!strcmp(cmn_ref_clk, "external_31250KHz"))
+ reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
+ FIELD_PREP(CMN_PLL_REFCLK_INDEX, 4));
+ else if (!strcmp(cmn_ref_clk, "external_40MHz"))
+ reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
+ FIELD_PREP(CMN_PLL_REFCLK_INDEX, 6));
+ else if (!strcmp(cmn_ref_clk, "external_48MHz"))
+ reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
+ FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7));
+ else if (!strcmp(cmn_ref_clk, "external_50MHz"))
+ reg_val |= (CMN_PLL_REFCLK_EXTERNAL |
+ FIELD_PREP(CMN_PLL_REFCLK_INDEX, 8));
+ else
+ reg_val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
+ }
+
+ writel(reg_val, priv->cmn_membase + CMN_PLL_REFERENCE_CLOCK);
+
+ /* assert CMN PLL */
+ reg_val = readl(priv->cmn_membase + CMN_PLL_POWER_ON_AND_RESET);
+ reg_val &= ~CMN_ANA_EN_SW_RSTN;
+ writel(reg_val, priv->cmn_membase);
+ fsleep(IPQ_PHY_SET_DELAY_US);
+
+ /* deassert CMN PLL */
+ reg_val |= CMN_ANA_EN_SW_RSTN;
+ writel(reg_val, priv->cmn_membase + CMN_PLL_POWER_ON_AND_RESET);
+ fsleep(IPQ_PHY_SET_DELAY_US);
+ }
+}
+
static int ipq_mdio_reset(struct mii_bus *bus)
{
struct ipq4019_mdio_data *priv = bus->priv;
u32 val;
int ret;
+ ipq_cmn_clock_config(bus);
+
/* For the platform ipq5332, there are two uniphy available to connect the
* ethernet devices, the uniphy gcc clock should be enabled for resetting
* the connected device such as qca8386 switch or qca8081 PHY effectively.
@@ -328,11 +397,21 @@ static int ipq4019_mdio_probe(struct platform_device *pdev)
/* This resource is optional */
for (ret = 0; ret < ETH_LDO_RDY_CNT; ret++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, ret + 1);
- if (res)
+ if (res && strcmp(res->name, "cmn_blk"))
priv->eth_ldo_rdy[ret] = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
}
+ /* The CMN block resource is for providing clock source of ethernet, which can
+ * be optionally configured on the platform ipq9574 and ipq5332.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cmn_blk");
+ if (res) {
+ priv->cmn_membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->cmn_membase))
+ return PTR_ERR(priv->cmn_membase);
+ }
+
for (ret = 0; ret < MDIO_CLK_CNT; ret++) {
priv->clk[ret] = devm_clk_get_optional(&pdev->dev, mdio_clk_name[ret]);
if (IS_ERR(priv->clk[ret]))