aboutsummaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2018-01-28 13:56:19 -0500
committerTom Rini <trini@konsulko.com>2018-01-28 13:56:19 -0500
commitf2ee91501892d3280a4c58093ad3a18dccd0b562 (patch)
tree46bba8f1e2aed3dcb868ce9e4fbd010c2ef12b13 /drivers/clk
parent4f6c7b12ed425095e635c32f184e8f8002da3823 (diff)
parent0289e291a5140b9e97ff3b1a12819ba0d5015887 (diff)
downloadu-boot-f2ee91501892d3280a4c58093ad3a18dccd0b562.zip
u-boot-f2ee91501892d3280a4c58093ad3a18dccd0b562.tar.gz
u-boot-f2ee91501892d3280a4c58093ad3a18dccd0b562.tar.bz2
Merge git://git.denx.de/u-boot-rockchip
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/clk-uclass.c140
-rw-r--r--drivers/clk/rockchip/clk_rk322x.c107
-rw-r--r--drivers/clk/rockchip/clk_rk3288.c108
-rw-r--r--drivers/clk/rockchip/clk_rk3328.c178
-rw-r--r--drivers/clk/rockchip/clk_rk3368.c93
-rw-r--r--drivers/clk/rockchip/clk_rk3399.c94
6 files changed, 701 insertions, 19 deletions
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index fbea720..ad76379 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -2,6 +2,7 @@
* Copyright (C) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
* Copyright (c) 2016, NVIDIA CORPORATION.
+ * Copyright (c) 2018, Theobroma Systems Design und Consulting GmbH
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -10,6 +11,7 @@
#include <clk.h>
#include <clk-uclass.h>
#include <dm.h>
+#include <dm/read.h>
#include <dt-structs.h>
#include <errno.h>
@@ -53,7 +55,8 @@ static int clk_of_xlate_default(struct clk *clk,
return 0;
}
-int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
+static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name,
+ int index, struct clk *clk)
{
int ret;
struct ofnode_phandle_args args;
@@ -65,7 +68,7 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
assert(clk);
clk->dev = NULL;
- ret = dev_read_phandle_with_args(dev, "clocks", "#clock-cells", 0,
+ ret = dev_read_phandle_with_args(dev, prop_name, "#clock-cells", 0,
index, &args);
if (ret) {
debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n",
@@ -95,6 +98,127 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
return clk_request(dev_clk, clk);
}
+
+int clk_get_by_index(struct udevice *dev, int index, struct clk *clk)
+{
+ return clk_get_by_indexed_prop(dev, "clocks", index, clk);
+}
+
+static int clk_set_default_parents(struct udevice *dev)
+{
+ struct clk clk, parent_clk;
+ int index;
+ int num_parents;
+ int ret;
+
+ num_parents = dev_count_phandle_with_args(dev, "assigned-clock-parents",
+ "#clock-cells");
+ if (num_parents < 0) {
+ debug("%s: could not read assigned-clock-parents for %p\n",
+ __func__, dev);
+ return 0;
+ }
+
+ for (index = 0; index < num_parents; index++) {
+ ret = clk_get_by_indexed_prop(dev, "assigned-clock-parents",
+ index, &parent_clk);
+ if (ret) {
+ debug("%s: could not get parent clock %d for %s\n",
+ __func__, index, dev_read_name(dev));
+ return ret;
+ }
+
+ ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
+ index, &clk);
+ if (ret) {
+ debug("%s: could not get assigned clock %d for %s\n",
+ __func__, index, dev_read_name(dev));
+ return ret;
+ }
+
+ ret = clk_set_parent(&clk, &parent_clk);
+
+ /*
+ * Not all drivers may support clock-reparenting (as of now).
+ * Ignore errors due to this.
+ */
+ if (ret == -ENOSYS)
+ continue;
+
+ if (ret) {
+ debug("%s: failed to reparent clock %d for %s\n",
+ __func__, index, dev_read_name(dev));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int clk_set_default_rates(struct udevice *dev)
+{
+ struct clk clk;
+ int index;
+ int num_rates;
+ int size;
+ int ret = 0;
+ u32 *rates = NULL;
+
+ size = dev_read_size(dev, "assigned-clock-rates");
+ if (size < 0)
+ return 0;
+
+ num_rates = size / sizeof(u32);
+ rates = calloc(num_rates, sizeof(u32));
+ if (!rates)
+ return -ENOMEM;
+
+ ret = dev_read_u32_array(dev, "assigned-clock-rates", rates, num_rates);
+ if (ret)
+ goto fail;
+
+ for (index = 0; index < num_rates; index++) {
+ ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
+ index, &clk);
+ if (ret) {
+ debug("%s: could not get assigned clock %d for %s\n",
+ __func__, index, dev_read_name(dev));
+ continue;
+ }
+
+ ret = clk_set_rate(&clk, rates[index]);
+ if (ret < 0) {
+ debug("%s: failed to set rate on clock %d for %s\n",
+ __func__, index, dev_read_name(dev));
+ break;
+ }
+ }
+
+fail:
+ free(rates);
+ return ret;
+}
+
+int clk_set_defaults(struct udevice *dev)
+{
+ int ret;
+
+ /* If this is running pre-reloc state, don't take any action. */
+ if (!(gd->flags & GD_FLG_RELOC))
+ return 0;
+
+ debug("%s(%s)\n", __func__, dev_read_name(dev));
+
+ ret = clk_set_default_parents(dev);
+ if (ret)
+ return ret;
+
+ ret = clk_set_default_rates(dev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
# endif /* OF_PLATDATA */
int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk)
@@ -188,6 +312,18 @@ ulong clk_set_rate(struct clk *clk, ulong rate)
return ops->set_rate(clk, rate);
}
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ const struct clk_ops *ops = clk_dev_ops(clk->dev);
+
+ debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent);
+
+ if (!ops->set_parent)
+ return -ENOSYS;
+
+ return ops->set_parent(clk, parent);
+}
+
int clk_enable(struct clk *clk)
{
const struct clk_ops *ops = clk_dev_ops(clk->dev);
diff --git a/drivers/clk/rockchip/clk_rk322x.c b/drivers/clk/rockchip/clk_rk322x.c
index c8a2413..4e6d2f0 100644
--- a/drivers/clk/rockchip/clk_rk322x.c
+++ b/drivers/clk/rockchip/clk_rk322x.c
@@ -239,6 +239,41 @@ static ulong rockchip_mmc_get_clk(struct rk322x_cru *cru, uint clk_general_rate,
return DIV_TO_RATE(src_rate, div) / 2;
}
+static ulong rk322x_mac_set_clk(struct rk322x_cru *cru, uint freq)
+{
+ ulong ret;
+
+ /*
+ * The gmac clock can be derived either from an external clock
+ * or can be generated from internally by a divider from SCLK_MAC.
+ */
+ if (readl(&cru->cru_clksel_con[5]) & BIT(5)) {
+ /* An external clock will always generate the right rate... */
+ ret = freq;
+ } else {
+ u32 con = readl(&cru->cru_clksel_con[5]);
+ ulong pll_rate;
+ u8 div;
+
+ if ((con >> MAC_PLL_SEL_SHIFT) & MAC_PLL_SEL_MASK)
+ pll_rate = GPLL_HZ;
+ else
+ /* CPLL is not set */
+ return -EPERM;
+
+ div = DIV_ROUND_UP(pll_rate, freq) - 1;
+ if (div <= 0x1f)
+ rk_clrsetreg(&cru->cru_clksel_con[5], CLK_MAC_DIV_MASK,
+ div << CLK_MAC_DIV_SHIFT);
+ else
+ debug("Unsupported div for gmac:%d\n", div);
+
+ return DIV_TO_RATE(pll_rate, div);
+ }
+
+ return ret;
+}
+
static ulong rockchip_mmc_set_clk(struct rk322x_cru *cru, uint clk_general_rate,
int periph, uint freq)
{
@@ -352,6 +387,11 @@ static ulong rk322x_clk_set_rate(struct clk *clk, ulong rate)
case CLK_DDR:
new_rate = rk322x_ddr_set_clk(priv->cru, rate);
break;
+ case SCLK_MAC:
+ new_rate = rk322x_mac_set_clk(priv->cru, rate);
+ break;
+ case PLL_GPLL:
+ return 0;
default:
return -ENOENT;
}
@@ -359,9 +399,76 @@ static ulong rk322x_clk_set_rate(struct clk *clk, ulong rate)
return new_rate;
}
+static int rk322x_gmac_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct rk322x_clk_priv *priv = dev_get_priv(clk->dev);
+ struct rk322x_cru *cru = priv->cru;
+
+ /*
+ * If the requested parent is in the same clock-controller and the id
+ * is SCLK_MAC_SRC ("sclk_gmac_src"), switch to the internal clock.
+ */
+ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC_SRC)) {
+ debug("%s: switching RGMII to SCLK_MAC_SRC\n", __func__);
+ rk_clrsetreg(&cru->cru_clksel_con[5], BIT(5), 0);
+ return 0;
+ }
+
+ /*
+ * If the requested parent is in the same clock-controller and the id
+ * is SCLK_MAC_EXTCLK (sclk_mac_extclk), switch to the external clock.
+ */
+ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC_EXTCLK)) {
+ debug("%s: switching RGMII to SCLK_MAC_EXTCLK\n", __func__);
+ rk_clrsetreg(&cru->cru_clksel_con[5], BIT(5), BIT(5));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int rk322x_gmac_extclk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct rk322x_clk_priv *priv = dev_get_priv(clk->dev);
+ const char *clock_output_name;
+ struct rk322x_cru *cru = priv->cru;
+ int ret;
+
+ ret = dev_read_string_index(parent->dev, "clock-output-names",
+ parent->id, &clock_output_name);
+ if (ret < 0)
+ return -ENODATA;
+
+ if (!strcmp(clock_output_name, "ext_gmac")) {
+ debug("%s: switching gmac extclk to ext_gmac\n", __func__);
+ rk_clrsetreg(&cru->cru_clksel_con[29], BIT(10), 0);
+ return 0;
+ } else if (!strcmp(clock_output_name, "phy_50m_out")) {
+ debug("%s: switching gmac extclk to phy_50m_out\n", __func__);
+ rk_clrsetreg(&cru->cru_clksel_con[29], BIT(10), BIT(10));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int rk322x_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ switch (clk->id) {
+ case SCLK_MAC:
+ return rk322x_gmac_set_parent(clk, parent);
+ case SCLK_MAC_EXTCLK:
+ return rk322x_gmac_extclk_set_parent(clk, parent);
+ }
+
+ debug("%s: unsupported clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+}
+
static struct clk_ops rk322x_clk_ops = {
.get_rate = rk322x_clk_get_rate,
.set_rate = rk322x_clk_set_rate,
+ .set_parent = rk322x_clk_set_parent,
};
static int rk322x_clk_ofdata_to_platdata(struct udevice *dev)
diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c
index b64c107..552a71a 100644
--- a/drivers/clk/rockchip/clk_rk3288.c
+++ b/drivers/clk/rockchip/clk_rk3288.c
@@ -295,15 +295,42 @@ static int pll_para_config(ulong freq_hz, struct pll_div *div, uint *ext_div)
return 0;
}
-static int rockchip_mac_set_clk(struct rk3288_cru *cru,
- int periph, uint freq)
+static int rockchip_mac_set_clk(struct rk3288_cru *cru, uint freq)
{
- /* Assuming mac_clk is fed by an external clock */
- rk_clrsetreg(&cru->cru_clksel_con[21],
- RMII_EXTCLK_MASK,
- RMII_EXTCLK_SELECT_EXT_CLK << RMII_EXTCLK_SHIFT);
+ ulong ret;
+
+ /*
+ * The gmac clock can be derived either from an external clock
+ * or can be generated from internally by a divider from SCLK_MAC.
+ */
+ if (readl(&cru->cru_clksel_con[21]) & RMII_EXTCLK_MASK) {
+ /* An external clock will always generate the right rate... */
+ ret = freq;
+ } else {
+ u32 con = readl(&cru->cru_clksel_con[21]);
+ ulong pll_rate;
+ u8 div;
+
+ if (((con >> EMAC_PLL_SHIFT) & EMAC_PLL_MASK) ==
+ EMAC_PLL_SELECT_GENERAL)
+ pll_rate = GPLL_HZ;
+ else if (((con >> EMAC_PLL_SHIFT) & EMAC_PLL_MASK) ==
+ EMAC_PLL_SELECT_CODEC)
+ pll_rate = CPLL_HZ;
+ else
+ pll_rate = NPLL_HZ;
+
+ div = DIV_ROUND_UP(pll_rate, freq) - 1;
+ if (div <= 0x1f)
+ rk_clrsetreg(&cru->cru_clksel_con[21], MAC_DIV_CON_MASK,
+ div << MAC_DIV_CON_SHIFT);
+ else
+ debug("Unsupported div for gmac:%d\n", div);
+
+ return DIV_TO_RATE(pll_rate, div);
+ }
- return 0;
+ return ret;
}
static int rockchip_vop_set_clk(struct rk3288_cru *cru, struct rk3288_grf *grf,
@@ -744,7 +771,7 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate)
break;
#ifndef CONFIG_SPL_BUILD
case SCLK_MAC:
- new_rate = rockchip_mac_set_clk(priv->cru, clk->id, rate);
+ new_rate = rockchip_mac_set_clk(priv->cru, rate);
break;
case DCLK_VOP0:
case DCLK_VOP1:
@@ -797,6 +824,17 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate)
case SCLK_SARADC:
new_rate = rockchip_saradc_set_clk(priv->cru, rate);
break;
+ case PLL_GPLL:
+ case PLL_CPLL:
+ case PLL_NPLL:
+ case ACLK_CPU:
+ case HCLK_CPU:
+ case PCLK_CPU:
+ case ACLK_PERI:
+ case HCLK_PERI:
+ case PCLK_PERI:
+ case SCLK_UART0:
+ return 0;
default:
return -ENOENT;
}
@@ -804,9 +842,63 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate)
return new_rate;
}
+static int __maybe_unused rk3288_gmac_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
+ struct rk3288_cru *cru = priv->cru;
+ const char *clock_output_name;
+ int ret;
+
+ /*
+ * If the requested parent is in the same clock-controller and
+ * the id is SCLK_MAC_PLL ("mac_pll_src"), switch to the internal
+ * clock.
+ */
+ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC_PLL)) {
+ debug("%s: switching GAMC to SCLK_MAC_PLL\n", __func__);
+ rk_clrsetreg(&cru->cru_clksel_con[21], RMII_EXTCLK_MASK, 0);
+ return 0;
+ }
+
+ /*
+ * Otherwise, we need to check the clock-output-names of the
+ * requested parent to see if the requested id is "ext_gmac".
+ */
+ ret = dev_read_string_index(parent->dev, "clock-output-names",
+ parent->id, &clock_output_name);
+ if (ret < 0)
+ return -ENODATA;
+
+ /* If this is "ext_gmac", switch to the external clock input */
+ if (!strcmp(clock_output_name, "ext_gmac")) {
+ debug("%s: switching GMAC to external clock\n", __func__);
+ rk_clrsetreg(&cru->cru_clksel_con[21], RMII_EXTCLK_MASK,
+ RMII_EXTCLK_SELECT_EXT_CLK << RMII_EXTCLK_SHIFT);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int __maybe_unused rk3288_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ switch (clk->id) {
+ case SCLK_MAC:
+ return rk3288_gmac_set_parent(clk, parent);
+ case SCLK_USBPHY480M_SRC:
+ return 0;
+ }
+
+ debug("%s: unsupported clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+}
+
static struct clk_ops rk3288_clk_ops = {
.get_rate = rk3288_clk_get_rate,
.set_rate = rk3288_clk_set_rate,
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
+ .set_parent = rk3288_clk_set_parent,
+#endif
};
static int rk3288_clk_ofdata_to_platdata(struct udevice *dev)
diff --git a/drivers/clk/rockchip/clk_rk3328.c b/drivers/clk/rockchip/clk_rk3328.c
index fa0c777..2ccc798 100644
--- a/drivers/clk/rockchip/clk_rk3328.c
+++ b/drivers/clk/rockchip/clk_rk3328.c
@@ -13,6 +13,7 @@
#include <asm/arch/clock.h>
#include <asm/arch/cru_rk3328.h>
#include <asm/arch/hardware.h>
+#include <asm/arch/grf_rk3328.h>
#include <asm/io.h>
#include <dm/lists.h>
#include <dt-bindings/clock/rk3328-cru.h>
@@ -94,6 +95,14 @@ enum {
PCLK_DBG_DIV_SHIFT = 0,
PCLK_DBG_DIV_MASK = 0xF << PCLK_DBG_DIV_SHIFT,
+ /* CLKSEL_CON27 */
+ GMAC2IO_PLL_SEL_SHIFT = 7,
+ GMAC2IO_PLL_SEL_MASK = 1 << GMAC2IO_PLL_SEL_SHIFT,
+ GMAC2IO_PLL_SEL_CPLL = 0,
+ GMAC2IO_PLL_SEL_GPLL = 1,
+ GMAC2IO_CLK_DIV_MASK = 0x1f,
+ GMAC2IO_CLK_DIV_SHIFT = 0,
+
/* CLKSEL_CON28 */
ACLK_PERIHP_PLL_SEL_CPLL = 0,
ACLK_PERIHP_PLL_SEL_GPLL,
@@ -393,6 +402,44 @@ static ulong rk3328_i2c_set_clk(struct rk3328_cru *cru, ulong clk_id, uint hz)
return DIV_TO_RATE(GPLL_HZ, src_clk_div);
}
+static ulong rk3328_gmac2io_set_clk(struct rk3328_cru *cru, ulong rate)
+{
+ struct rk3328_grf_regs *grf;
+ ulong ret;
+
+ grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+
+ /*
+ * The RGMII CLK can be derived either from an external "clkin"
+ * or can be generated from internally by a divider from SCLK_MAC.
+ */
+ if (readl(&grf->mac_con[1]) & BIT(10) &&
+ readl(&grf->soc_con[4]) & BIT(14)) {
+ /* An external clock will always generate the right rate... */
+ ret = rate;
+ } else {
+ u32 con = readl(&cru->clksel_con[27]);
+ ulong pll_rate;
+ u8 div;
+
+ if ((con >> GMAC2IO_PLL_SEL_SHIFT) & GMAC2IO_PLL_SEL_GPLL)
+ pll_rate = GPLL_HZ;
+ else
+ pll_rate = CPLL_HZ;
+
+ div = DIV_ROUND_UP(pll_rate, rate) - 1;
+ if (div <= 0x1f)
+ rk_clrsetreg(&cru->clksel_con[27], GMAC2IO_CLK_DIV_MASK,
+ div << GMAC2IO_CLK_DIV_SHIFT);
+ else
+ debug("Unsupported div for gmac:%d\n", div);
+
+ return DIV_TO_RATE(pll_rate, div);
+ }
+
+ return ret;
+}
+
static ulong rk3328_mmc_get_clk(struct rk3328_cru *cru, uint clk_id)
{
u32 div, con, con_id;
@@ -558,12 +605,48 @@ static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate)
case SCLK_I2C3:
ret = rk3328_i2c_set_clk(priv->cru, clk->id, rate);
break;
+ case SCLK_MAC2IO:
+ ret = rk3328_gmac2io_set_clk(priv->cru, rate);
+ break;
case SCLK_PWM:
ret = rk3328_pwm_set_clk(priv->cru, rate);
break;
case SCLK_SARADC:
ret = rk3328_saradc_set_clk(priv->cru, rate);
break;
+ case DCLK_LCDC:
+ case SCLK_PDM:
+ case SCLK_RTC32K:
+ case SCLK_UART0:
+ case SCLK_UART1:
+ case SCLK_UART2:
+ case SCLK_SDIO:
+ case SCLK_TSP:
+ case SCLK_WIFI:
+ case ACLK_BUS_PRE:
+ case HCLK_BUS_PRE:
+ case PCLK_BUS_PRE:
+ case ACLK_PERI_PRE:
+ case HCLK_PERI:
+ case PCLK_PERI:
+ case ACLK_VIO_PRE:
+ case HCLK_VIO_PRE:
+ case ACLK_RGA_PRE:
+ case SCLK_RGA:
+ case ACLK_VOP_PRE:
+ case ACLK_RKVDEC_PRE:
+ case ACLK_RKVENC:
+ case ACLK_VPU_PRE:
+ case SCLK_VDEC_CABAC:
+ case SCLK_VDEC_CORE:
+ case SCLK_VENC_CORE:
+ case SCLK_VENC_DSP:
+ case SCLK_EFUSE:
+ case PCLK_DDR:
+ case ACLK_GMAC:
+ case PCLK_GMAC:
+ case SCLK_USB3OTG_SUSPEND:
+ return 0;
default:
return -ENOENT;
}
@@ -571,9 +654,104 @@ static ulong rk3328_clk_set_rate(struct clk *clk, ulong rate)
return ret;
}
+static int rk3328_gmac2io_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct rk3328_grf_regs *grf;
+ const char *clock_output_name;
+ int ret;
+
+ grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+
+ /*
+ * If the requested parent is in the same clock-controller and the id
+ * is SCLK_MAC2IO_SRC ("clk_mac2io_src"), switch to the internal clock.
+ */
+ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC2IO_SRC)) {
+ debug("%s: switching RGMII to SCLK_MAC2IO_SRC\n", __func__);
+ rk_clrreg(&grf->mac_con[1], BIT(10));
+ return 0;
+ }
+
+ /*
+ * Otherwise, we need to check the clock-output-names of the
+ * requested parent to see if the requested id is "gmac_clkin".
+ */
+ ret = dev_read_string_index(parent->dev, "clock-output-names",
+ parent->id, &clock_output_name);
+ if (ret < 0)
+ return -ENODATA;
+
+ /* If this is "gmac_clkin", switch to the external clock input */
+ if (!strcmp(clock_output_name, "gmac_clkin")) {
+ debug("%s: switching RGMII to CLKIN\n", __func__);
+ rk_setreg(&grf->mac_con[1], BIT(10));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int rk3328_gmac2io_ext_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct rk3328_grf_regs *grf;
+ const char *clock_output_name;
+ int ret;
+
+ grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+
+ /*
+ * If the requested parent is in the same clock-controller and the id
+ * is SCLK_MAC2IO ("clk_mac2io"), switch to the internal clock.
+ */
+ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC2IO)) {
+ debug("%s: switching RGMII to SCLK_MAC2IO\n", __func__);
+ rk_clrreg(&grf->soc_con[4], BIT(14));
+ return 0;
+ }
+
+ /*
+ * Otherwise, we need to check the clock-output-names of the
+ * requested parent to see if the requested id is "gmac_clkin".
+ */
+ ret = dev_read_string_index(parent->dev, "clock-output-names",
+ parent->id, &clock_output_name);
+ if (ret < 0)
+ return -ENODATA;
+
+ /* If this is "gmac_clkin", switch to the external clock input */
+ if (!strcmp(clock_output_name, "gmac_clkin")) {
+ debug("%s: switching RGMII to CLKIN\n", __func__);
+ rk_setreg(&grf->soc_con[4], BIT(14));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int rk3328_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ switch (clk->id) {
+ case SCLK_MAC2IO:
+ return rk3328_gmac2io_set_parent(clk, parent);
+ case SCLK_MAC2IO_EXT:
+ return rk3328_gmac2io_ext_set_parent(clk, parent);
+ case DCLK_LCDC:
+ case SCLK_PDM:
+ case SCLK_RTC32K:
+ case SCLK_UART0:
+ case SCLK_UART1:
+ case SCLK_UART2:
+ return 0;
+ }
+
+ debug("%s: unsupported clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+}
+
static struct clk_ops rk3328_clk_ops = {
.get_rate = rk3328_clk_get_rate,
.set_rate = rk3328_clk_set_rate,
+ .set_parent = rk3328_clk_set_parent,
};
static int rk3328_clk_probe(struct udevice *dev)
diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c
index a831991..3ac9add 100644
--- a/drivers/clk/rockchip/clk_rk3368.c
+++ b/drivers/clk/rockchip/clk_rk3368.c
@@ -311,15 +311,43 @@ static ulong rk3368_ddr_set_clk(struct rk3368_cru *cru, ulong set_rate)
#endif
#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP)
-static ulong rk3368_gmac_set_clk(struct rk3368_cru *cru,
- ulong clk_id, ulong set_rate)
+static ulong rk3368_gmac_set_clk(struct rk3368_cru *cru, ulong set_rate)
{
+ ulong ret;
+
/*
- * This models the 'assigned-clock-parents = <&ext_gmac>' from
- * the DTS and switches to the 'ext_gmac' clock parent.
+ * The gmac clock can be derived either from an external clock
+ * or can be generated from internally by a divider from SCLK_MAC.
*/
- rk_setreg(&cru->clksel_con[43], GMAC_MUX_SEL_EXTCLK);
- return set_rate;
+ if (readl(&cru->clksel_con[43]) & GMAC_MUX_SEL_EXTCLK) {
+ /* An external clock will always generate the right rate... */
+ ret = set_rate;
+ } else {
+ u32 con = readl(&cru->clksel_con[43]);
+ ulong pll_rate;
+ u8 div;
+
+ if (((con >> GMAC_PLL_SHIFT) & GMAC_PLL_MASK) ==
+ GMAC_PLL_SELECT_GENERAL)
+ pll_rate = GPLL_HZ;
+ else if (((con >> GMAC_PLL_SHIFT) & GMAC_PLL_MASK) ==
+ GMAC_PLL_SELECT_CODEC)
+ pll_rate = CPLL_HZ;
+ else
+ /* CPLL is not set */
+ return -EPERM;
+
+ div = DIV_ROUND_UP(pll_rate, set_rate) - 1;
+ if (div <= 0x1f)
+ rk_clrsetreg(&cru->clksel_con[43], GMAC_DIV_CON_MASK,
+ div << GMAC_DIV_CON_SHIFT);
+ else
+ debug("Unsupported div for gmac:%d\n", div);
+
+ return DIV_TO_RATE(pll_rate, div);
+ }
+
+ return ret;
}
#endif
@@ -479,7 +507,7 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate)
#if CONFIG_IS_ENABLED(GMAC_ROCKCHIP)
case SCLK_MAC:
/* select the external clock */
- ret = rk3368_gmac_set_clk(priv->cru, clk->id, rate);
+ ret = rk3368_gmac_set_clk(priv->cru, rate);
break;
#endif
case SCLK_SARADC:
@@ -492,9 +520,60 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate)
return ret;
}
+static int __maybe_unused rk3368_gmac_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct rk3368_clk_priv *priv = dev_get_priv(clk->dev);
+ struct rk3368_cru *cru = priv->cru;
+ const char *clock_output_name;
+ int ret;
+
+ /*
+ * If the requested parent is in the same clock-controller and
+ * the id is SCLK_MAC ("sclk_mac"), switch to the internal
+ * clock.
+ */
+ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC)) {
+ debug("%s: switching GAMC to SCLK_MAC\n", __func__);
+ rk_clrreg(&cru->clksel_con[43], GMAC_MUX_SEL_EXTCLK);
+ return 0;
+ }
+
+ /*
+ * Otherwise, we need to check the clock-output-names of the
+ * requested parent to see if the requested id is "ext_gmac".
+ */
+ ret = dev_read_string_index(parent->dev, "clock-output-names",
+ parent->id, &clock_output_name);
+ if (ret < 0)
+ return -ENODATA;
+
+ /* If this is "ext_gmac", switch to the external clock input */
+ if (!strcmp(clock_output_name, "ext_gmac")) {
+ debug("%s: switching GMAC to external clock\n", __func__);
+ rk_setreg(&cru->clksel_con[43], GMAC_MUX_SEL_EXTCLK);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int __maybe_unused rk3368_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ switch (clk->id) {
+ case SCLK_MAC:
+ return rk3368_gmac_set_parent(clk, parent);
+ }
+
+ debug("%s: unsupported clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+}
+
static struct clk_ops rk3368_clk_ops = {
.get_rate = rk3368_clk_get_rate,
.set_rate = rk3368_clk_set_rate,
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
+ .set_parent = rk3368_clk_set_parent,
+#endif
};
static int rk3368_clk_probe(struct udevice *dev)
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index 2f4c4e3..42926ba 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -742,6 +742,30 @@ static ulong rk3399_mmc_set_clk(struct rk3399_cru *cru,
return rk3399_mmc_get_clk(cru, clk_id);
}
+static ulong rk3399_gmac_set_clk(struct rk3399_cru *cru, ulong rate)
+{
+ ulong ret;
+
+ /*
+ * The RGMII CLK can be derived either from an external "clkin"
+ * or can be generated from internally by a divider from SCLK_MAC.
+ */
+ if (readl(&cru->clksel_con[19]) & BIT(4)) {
+ /* An external clock will always generate the right rate... */
+ ret = rate;
+ } else {
+ /*
+ * No platform uses an internal clock to date.
+ * Implement this once it becomes necessary and print an error
+ * if someone tries to use it (while it remains unimplemented).
+ */
+ pr_err("%s: internal clock is UNIMPLEMENTED\n", __func__);
+ ret = 0;
+ }
+
+ return ret;
+}
+
#define PMUSGRF_DDR_RGN_CON16 0xff330040
static ulong rk3399_ddr_set_clk(struct rk3399_cru *cru,
ulong set_rate)
@@ -859,14 +883,31 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate)
switch (clk->id) {
case 0 ... 63:
return 0;
+
+ case ACLK_PERIHP:
+ case HCLK_PERIHP:
+ case PCLK_PERIHP:
+ return 0;
+
+ case ACLK_PERILP0:
+ case HCLK_PERILP0:
+ case PCLK_PERILP0:
+ return 0;
+
+ case ACLK_CCI:
+ return 0;
+
+ case HCLK_PERILP1:
+ case PCLK_PERILP1:
+ return 0;
+
case HCLK_SDMMC:
case SCLK_SDMMC:
case SCLK_EMMC:
ret = rk3399_mmc_set_clk(priv->cru, clk->id, rate);
break;
case SCLK_MAC:
- /* nothing to do, as this is an external clock */
- ret = rate;
+ ret = rk3399_gmac_set_clk(priv->cru, rate);
break;
case SCLK_I2C1:
case SCLK_I2C2:
@@ -902,6 +943,52 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate)
return ret;
}
+static int __maybe_unused rk3399_gmac_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
+ const char *clock_output_name;
+ int ret;
+
+ /*
+ * If the requested parent is in the same clock-controller and
+ * the id is SCLK_MAC ("clk_gmac"), switch to the internal clock.
+ */
+ if ((parent->dev == clk->dev) && (parent->id == SCLK_MAC)) {
+ debug("%s: switching RGMII to SCLK_MAC\n", __func__);
+ rk_clrreg(&priv->cru->clksel_con[19], BIT(4));
+ return 0;
+ }
+
+ /*
+ * Otherwise, we need to check the clock-output-names of the
+ * requested parent to see if the requested id is "clkin_gmac".
+ */
+ ret = dev_read_string_index(parent->dev, "clock-output-names",
+ parent->id, &clock_output_name);
+ if (ret < 0)
+ return -ENODATA;
+
+ /* If this is "clkin_gmac", switch to the external clock input */
+ if (!strcmp(clock_output_name, "clkin_gmac")) {
+ debug("%s: switching RGMII to CLKIN\n", __func__);
+ rk_setreg(&priv->cru->clksel_con[19], BIT(4));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int __maybe_unused rk3399_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ switch (clk->id) {
+ case SCLK_RMII_SRC:
+ return rk3399_gmac_set_parent(clk, parent);
+ }
+
+ debug("%s: unsupported clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+}
+
static int rk3399_clk_enable(struct clk *clk)
{
switch (clk->id) {
@@ -919,6 +1006,9 @@ static int rk3399_clk_enable(struct clk *clk)
static struct clk_ops rk3399_clk_ops = {
.get_rate = rk3399_clk_get_rate,
.set_rate = rk3399_clk_set_rate,
+#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
+ .set_parent = rk3399_clk_set_parent,
+#endif
.enable = rk3399_clk_enable,
};