From f7d1046da18fd03a047b5f4d290a8ab8550ebf73 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Mon, 8 Jan 2018 11:15:08 +0100 Subject: clk: add clk_set_parent() Clocks may support multiple parents: this change introduces an optional operation on the clk-uclass to set a clock's parent. Signed-off-by: Philipp Tomsich Tested-by: David Wu Series-changes: 2 - Fixed David's email address. --- drivers/clk/clk-uclass.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index fbea720..20ee0c5 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -188,6 +188,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); -- cgit v1.1 From 95f9a7e5957093612b1e8447ac5460a6adcea3ba Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Mon, 8 Jan 2018 11:18:18 +0100 Subject: clk: refactor clk_get_by_index() into clk_get_by_indexed_prop() The logic in clk_get_by_index() may be useful for other properties than 'clocks': e.g. 'assigned-clocks' and 'assigned-clock-parents' follows the same model. This commit refactors clk_get_by_index() by introducing an internal function clk_get_by_indexed_prop() that allows to specify the name of the property to process. The original clk_get_by_index() call is simply directed through this helper function with the property name fixed to "clocks". Signed-off-by: Philipp Tomsich Tested-by: David Wu Series-changes: 2 - Fixed David's email address. --- drivers/clk/clk-uclass.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 20ee0c5..7fdf16d 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -53,7 +53,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 +66,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 +96,11 @@ 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); +} # endif /* OF_PLATDATA */ int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk) -- cgit v1.1 From a45f17e8b9f91628936349ef40a06d10dc9c08ae Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Mon, 8 Jan 2018 13:11:01 +0100 Subject: rockchip: clk: rk3399: implement set_parent() operation This implements the (newly added) set_parent() operation for the RK3399 with a focus on allowing the RGMII clock parent to be configured via the assigned-clock-parents property of the GMAC node. This implementation supports only the GMAC (in fact only the RGMII clock parent) and allows to set this clock's parent either to the internal SCLK_GMAC or to an external clock input (identifiable by it providing a 'clock-output-name' of "gmac_clkin"). Signed-off-by: Philipp Tomsich Tested-by: David Wu Series-changes: 2 - Fixed David's email address. --- drivers/clk/rockchip/clk_rk3399.c | 74 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c index 2f4c4e3..e791936 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) @@ -865,8 +889,7 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate) 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 +925,52 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate) return ret; } +static int 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 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 +988,7 @@ 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, + .set_parent = rk3399_clk_set_parent, .enable = rk3399_clk_enable, }; -- cgit v1.1 From f4fcba5c5baaaa9d477d753f97124efdb8e45893 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Mon, 8 Jan 2018 13:59:18 +0100 Subject: clk: implement clk_set_defaults() Linux uses the properties 'assigned-clocks', 'assigned-clock-parents' and 'assigned-clock-rates' to configure the clock subsystem for use with various peripheral nodes. This implements clk_set_defaults() and hooks it up with the general device probibin in drivers/core/device.c: when a new device is probed, clk_set_defaults() will be called for it and will process the properties mentioned above. Note that this functionality is designed to fail gracefully (i.e. if a clock-driver does not implement set_parent(), we simply accept this and ignore the error) as not to break existing board-support. Signed-off-by: Philipp Tomsich Tested-by: David Wu Series-changes: 2 - Fixed David's email address. Series-version: 2 Cover-letter: clk: support assigned-clock, assigned-clock-parents, assigned-clock-rates For various peripherals on Rockchip SoCs (e.g. for the Ethernet GMAC), the parent-clock needs to be set via the DTS. This adds the required plumbing and implements the GMAC case for the RK3399. END --- drivers/clk/clk-uclass.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 7fdf16d..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 * 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 #include #include +#include #include #include @@ -101,6 +103,122 @@ 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) -- cgit v1.1 From d2f1f1abafbedd3580334f2564bfea918e49522d Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Mon, 8 Jan 2018 14:00:27 +0100 Subject: rockchip: clk: rk3399: accept all assigned-clocks from the 'cru'-node The RK3399 CRU-node assigns rates to a number of clocks that are not implemented in the RK3399 clock-driver (but which have been sufficiently initialised from rkclk_init()): for these clocks, we simply ignore the set_rate() operation and return 0 to signal success. Signed-off-by: Philipp Tomsich Tested-by: David Wu Series-changes: 2 - Fixed David's email address. --- drivers/clk/rockchip/clk_rk3399.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/clk') diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c index e791936..e431ec8 100644 --- a/drivers/clk/rockchip/clk_rk3399.c +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -883,6 +883,24 @@ 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: -- cgit v1.1 From 7cd4ebab2bc425b027dc91872b1feb766e9bc9ff Mon Sep 17 00:00:00 2001 From: David Wu Date: Sat, 13 Jan 2018 14:02:36 +0800 Subject: clk: rockchip: Add rk3328 gamc clock support The rk3328 soc has two gmac controllers, one is gmac2io, the other is gmac2phy. We use the gmac2io rgmii interface for 1000M phy here. Signed-off-by: David Wu Acked-by: Philipp Tomsich Reviewed-by: Philipp Tomsich --- drivers/clk/rockchip/clk_rk3328.c | 178 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) (limited to 'drivers/clk') 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 #include #include +#include #include #include #include @@ -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) -- cgit v1.1 From 5bb616c6e2ba3ff4098b882bb0bcd7e0000376dd Mon Sep 17 00:00:00 2001 From: David Wu Date: Sat, 13 Jan 2018 14:05:12 +0800 Subject: clk: rockchip: Add rk322x gamc clock support Assuming mac_clk is fed by an external clock, set clk_rmii_src clock select control register from IO for rgmii interface. Signed-off-by: David Wu Acked-by: Philipp Tomsich Reviewed-by: Philipp Tomsich --- drivers/clk/rockchip/clk_rk322x.c | 107 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) (limited to 'drivers/clk') 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) -- cgit v1.1 From 01c60eafbbf613f8b29fb317f7c52be932d303d1 Mon Sep 17 00:00:00 2001 From: David Wu Date: Sat, 13 Jan 2018 14:06:33 +0800 Subject: clk: rockchip: clk_rk3288: Implement "assign-clock-parent" and "assign-clock-rate" The RK3288 CRU-node assigns rates to a number of clocks that are not implemented in the RK3288 clock-driver (but which have been sufficiently initialised from rkclk_init()): for these clocks, we implement the gmac clock set parent, but simply ignore the others' set_rate() operation and return 0 to signal success. Signed-off-by: David Wu Acked-by: Philipp Tomsich Reviewed-by: Philipp Tomsich --- drivers/clk/rockchip/clk_rk3288.c | 106 +++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 8 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index b64c107..baa8122 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; - return 0; + /* + * 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 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,61 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) return new_rate; } +static int 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 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, + .set_parent = rk3288_clk_set_parent, }; static int rk3288_clk_ofdata_to_platdata(struct udevice *dev) -- cgit v1.1 From 64a12202ed113c4696c0f4b6c3d6a35d97383851 Mon Sep 17 00:00:00 2001 From: David Wu Date: Sat, 13 Jan 2018 14:07:04 +0800 Subject: clk: rockchip: clk_rk3368: Implement "assign-clock-parent" Implement the setting parent for gmac clock, and add internal pll div set for mac clk. Signed-off-by: David Wu Acked-by: Philipp Tomsich Reviewed-by: Philipp Tomsich --- drivers/clk/rockchip/clk_rk3368.c | 91 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 7 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/rockchip/clk_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index a831991..3364e6a 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,58 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) return ret; } +static int 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 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, + .set_parent = rk3368_clk_set_parent, }; static int rk3368_clk_probe(struct udevice *dev) -- cgit v1.1 From 75b381aae8dd5fbc3c6b3f453dc3ca9ce3c94ab4 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Thu, 25 Jan 2018 15:27:10 +0100 Subject: rockchip: clk: guard set_parent implementations against OF_PLATDATA The set_parent implementations do not make sense when OF_PLATDATA is enabled. We guard these against OF_PLATDATA and don't populate the set_parent-op when this is the case. Signed-off-by: Philipp Tomsich --- drivers/clk/rockchip/clk_rk3288.c | 6 ++++-- drivers/clk/rockchip/clk_rk3368.c | 6 ++++-- drivers/clk/rockchip/clk_rk3399.c | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index baa8122..552a71a 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -842,7 +842,7 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) return new_rate; } -static int rk3288_gmac_set_parent(struct clk *clk, struct clk *parent) +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; @@ -880,7 +880,7 @@ static int rk3288_gmac_set_parent(struct clk *clk, struct clk *parent) return -EINVAL; } -static int rk3288_clk_set_parent(struct clk *clk, struct clk *parent) +static int __maybe_unused rk3288_clk_set_parent(struct clk *clk, struct clk *parent) { switch (clk->id) { case SCLK_MAC: @@ -896,7 +896,9 @@ static int rk3288_clk_set_parent(struct clk *clk, struct clk *parent) 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_rk3368.c b/drivers/clk/rockchip/clk_rk3368.c index 3364e6a..3ac9add 100644 --- a/drivers/clk/rockchip/clk_rk3368.c +++ b/drivers/clk/rockchip/clk_rk3368.c @@ -520,7 +520,7 @@ static ulong rk3368_clk_set_rate(struct clk *clk, ulong rate) return ret; } -static int rk3368_gmac_set_parent(struct clk *clk, struct clk *parent) +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; @@ -557,7 +557,7 @@ static int rk3368_gmac_set_parent(struct clk *clk, struct clk *parent) return -EINVAL; } -static int rk3368_clk_set_parent(struct clk *clk, struct clk *parent) +static int __maybe_unused rk3368_clk_set_parent(struct clk *clk, struct clk *parent) { switch (clk->id) { case SCLK_MAC: @@ -571,7 +571,9 @@ static int rk3368_clk_set_parent(struct clk *clk, struct clk *parent) 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 e431ec8..42926ba 100644 --- a/drivers/clk/rockchip/clk_rk3399.c +++ b/drivers/clk/rockchip/clk_rk3399.c @@ -943,7 +943,7 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate) return ret; } -static int rk3399_gmac_set_parent(struct clk *clk, struct clk *parent) +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; @@ -978,7 +978,7 @@ static int rk3399_gmac_set_parent(struct clk *clk, struct clk *parent) return -EINVAL; } -static int rk3399_clk_set_parent(struct clk *clk, struct clk *parent) +static int __maybe_unused rk3399_clk_set_parent(struct clk *clk, struct clk *parent) { switch (clk->id) { case SCLK_RMII_SRC: @@ -1006,7 +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, }; -- cgit v1.1