From patchwork Fri Nov 20 11:23:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tero Kristo X-Patchwork-Id: 1403610 X-Patchwork-Delegate: lokeshvutla@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=ti.com header.i=@ti.com header.a=rsa-sha256 header.s=ti-com-17Q1 header.b=CzMCQwK2; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4CcvQX2PLZz9sVC for ; Fri, 20 Nov 2020 22:27:00 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id DF43F825FC; Fri, 20 Nov 2020 12:24:51 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; unprotected) header.d=ti.com header.i=@ti.com header.b="CzMCQwK2"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 56351825D0; Fri, 20 Nov 2020 12:24:17 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from fllv0015.ext.ti.com (fllv0015.ext.ti.com [198.47.19.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 1AF7782366 for ; Fri, 20 Nov 2020 12:24:06 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=t-kristo@ti.com Received: from lelv0266.itg.ti.com ([10.180.67.225]) by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id 0AKBO3Mq033606; Fri, 20 Nov 2020 05:24:03 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1605871443; bh=USQbTnhXOly01HzD/pDNtd0ZeePIqR3UiQL5iCTsYn4=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=CzMCQwK2gYdm1P3Wh56HNru0Xvsf3gE1F5ruERDgCFsm0vjx3Ogghfs8ogpnkLzA1 q8JxePDt8WcZEpACdBWkw9W7g3AiQb4UXZNvBRDGGBPYZCCQOTPJxcn1ncsyzQgRY4 L1Bhs5Gwy17dJhqmo7ibCzlTjdXiEnnjTWms54Uk= Received: from DLEE111.ent.ti.com (dlee111.ent.ti.com [157.170.170.22]) by lelv0266.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 0AKBO3TV022769 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 20 Nov 2020 05:24:03 -0600 Received: from DLEE103.ent.ti.com (157.170.170.33) by DLEE111.ent.ti.com (157.170.170.22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1979.3; Fri, 20 Nov 2020 05:24:03 -0600 Received: from fllv0039.itg.ti.com (10.64.41.19) by DLEE103.ent.ti.com (157.170.170.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1979.3 via Frontend Transport; Fri, 20 Nov 2020 05:24:02 -0600 Received: from sokoban.bb.dnainternet.fi (ileax41-snat.itg.ti.com [10.172.224.153]) by fllv0039.itg.ti.com (8.15.2/8.15.2) with ESMTP id 0AKBNZmv076324; Fri, 20 Nov 2020 05:24:01 -0600 From: Tero Kristo To: , CC: , , , Subject: [PATCHv2 14/25] clk: add support for TI K3 SoC clocks Date: Fri, 20 Nov 2020 13:23:15 +0200 Message-ID: <20201120112326.10350-15-t-kristo@ti.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20201120112326.10350-1-t-kristo@ti.com> References: <20201120112326.10350-1-t-kristo@ti.com> MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.3 at phobos.denx.de X-Virus-Status: Clean Add driver to support TI K3 generation SoC clocks. This driver registers the clocks provided via platform data, and adds support for controlling the clocks via DT handles. Signed-off-by: Tero Kristo --- drivers/clk/Kconfig | 10 ++ drivers/clk/Makefile | 1 + drivers/clk/clk-k3.c | 340 +++++++++++++++++++++++++++++++++++++++++++ include/k3-clk.h | 162 +++++++++++++++++++++ 4 files changed, 513 insertions(+) create mode 100644 drivers/clk/clk-k3.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index b2e9458f85..003a28f7ac 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -106,6 +106,16 @@ config CLK_TI_SCI available on some new TI's SoCs. If you wish to use clock resources managed by the TI System Controller, say Y here. Otherwise, say N. +config CLK_K3 + bool "Clock support for K3 SoC family of devices" + depends on CLK + help + Enables the clock translation layer from DT to device clocks. + +config SPL_CLK_K3 + bool "Clock support for K3 SoC family of devices" + depends on CLK && SPL + config CLK_K3_PLL bool "PLL clock support for K3 SoC family of devices" depends on CLK && LIB_RATIONAL diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 6009eab800..2f0cb80cb9 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -49,5 +49,6 @@ obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o obj-$(CONFIG_STM32H7) += clk_stm32h7.o obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o obj-$(CONFIG_$(SPL_TPL_)CLK_K3_PLL) += clk-k3-pll.o +obj-$(CONFIG_$(SPL_TPL_)CLK_K3) += clk-k3.o obj-$(CONFIG_CLK_VERSAL) += clk_versal.o obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o diff --git a/drivers/clk/clk-k3.c b/drivers/clk/clk-k3.c new file mode 100644 index 0000000000..f0392aad5d --- /dev/null +++ b/drivers/clk/clk-k3.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments K3 clock driver + * + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + * Tero Kristo + */ + +#include +#include +#include +#include +#include +#include "k3-clk.h" + +#define PLL_MIN_FREQ 800000000 +#define PLL_MAX_FREQ 3200000000UL + +/** + * struct clk_map - mapping from dev/clk id tuples towards physical clocks + * @dev_id: device ID for the clock + * @clk_id: clock ID for the clock + * @clk: pointer to the registered clock entry for the mapping + */ +struct clk_map { + u16 dev_id; + u32 clk_id; + struct clk *clk; +}; + +/** + * struct ti_clk_data - clock controller information structure + * @map: mapping from dev/clk id tuples to physical clock entries + * @size: number of entries in the map + */ +struct ti_clk_data { + struct clk_map *map; + int size; +}; + +static ulong osc_freq; + +static void clk_add_map(struct ti_clk_data *data, struct clk *clk, + u32 dev_id, u32 clk_id) +{ + struct clk_map *map; + + debug("%s: added clk=%p, data=%p, dev=%d, clk=%d\n", __func__, + clk, data, dev_id, clk_id); + if (!clk) + return; + + map = data->map + data->size++; + + map->dev_id = dev_id; + map->clk_id = clk_id; + map->clk = clk; +} + +static const struct soc_attr ti_k3_soc_clk_data[] = { +#ifdef CONFIG_SOC_K3_J721E + { + .family = "J721E", + .data = &j721e_clk_platdata, + }, + { + .family = "J7200", + .data = &j7200_clk_platdata, + }, +#endif + { /* sentinel */ } +}; + +static int ti_clk_probe(struct udevice *dev) +{ + struct ti_clk_data *data = dev_get_priv(dev); + struct clk *clk; + const char *name; + const struct clk_data *ti_clk_data; + int i, j; + const struct soc_attr *soc_match_data; + const struct ti_k3_clk_platdata *pdata; + + debug("%s(dev=%p)\n", __func__, dev); + + soc_match_data = soc_device_match(ti_k3_soc_clk_data); + if (!soc_match_data) + return -ENODEV; + + pdata = (const struct ti_k3_clk_platdata *)soc_match_data->data; + + data->map = kcalloc(pdata->soc_dev_clk_data_cnt, sizeof(*data->map), + GFP_KERNEL); + data->size = 0; + + for (i = 0; i < pdata->clk_list_cnt; i++) { + ti_clk_data = &pdata->clk_list[i]; + + switch (ti_clk_data->type) { + case CLK_TYPE_FIXED_RATE: + name = ti_clk_data->clk.fixed_rate.name; + clk = clk_register_fixed_rate(NULL, + name, + ti_clk_data->clk.fixed_rate.rate); + break; + case CLK_TYPE_DIV: + name = ti_clk_data->clk.div.name; + clk = clk_register_divider(NULL, name, + ti_clk_data->clk.div.parent, + ti_clk_data->clk.div.flags, + map_physmem(ti_clk_data->clk.div.reg, 0, MAP_NOCACHE), + ti_clk_data->clk.div.shift, + ti_clk_data->clk.div.width, + 0); + break; + case CLK_TYPE_MUX: + name = ti_clk_data->clk.mux.name; + clk = clk_register_mux(NULL, name, + ti_clk_data->clk.mux.parents, + ti_clk_data->clk.mux.num_parents, + ti_clk_data->clk.mux.flags, + map_physmem(ti_clk_data->clk.mux.reg, 0, MAP_NOCACHE), + ti_clk_data->clk.mux.shift, + ti_clk_data->clk.mux.width, + 0); + break; + case CLK_TYPE_PLL: + name = ti_clk_data->clk.pll.name; + clk = clk_register_ti_pll(name, + ti_clk_data->clk.pll.parent, + map_physmem(ti_clk_data->clk.pll.reg, 0, MAP_NOCACHE)); + + if (!osc_freq) + osc_freq = clk_get_rate(clk_get_parent(clk)); + break; + default: + name = NULL; + clk = NULL; + printf("WARNING: %s has encountered unknown clk type %d\n", + __func__, ti_clk_data->type); + } + + if (clk && ti_clk_data->default_freq) + clk_set_rate(clk, ti_clk_data->default_freq); + + if (clk && name) { + for (j = 0; j < pdata->soc_dev_clk_data_cnt; j++) { + if (!strcmp(name, pdata->soc_dev_clk_data[j].clk_name)) { + clk_add_map(data, clk, pdata->soc_dev_clk_data[j].dev_id, + pdata->soc_dev_clk_data[j].clk_id); + } + } + } + } + + return 0; +} + +static int _clk_cmp(u32 dev_id, u32 clk_id, const struct clk_map *map) +{ + if (map->dev_id == dev_id && map->clk_id == clk_id) + return 0; + if (map->dev_id > dev_id || + (map->dev_id == dev_id && map->clk_id > clk_id)) + return -1; + return 1; +} + +static int bsearch(u32 dev_id, u32 clk_id, struct clk_map *map, int num) +{ + int result; + int idx; + + for (idx = 0; idx < num; idx++) { + result = _clk_cmp(dev_id, clk_id, &map[idx]); + + if (result == 0) + return idx; + } + + return -ENOENT; +} + +static int ti_clk_of_xlate(struct clk *clk, + struct ofnode_phandle_args *args) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + int idx; + + debug("%s(clk=%p, args_count=%d [0]=%d [1]=%d)\n", __func__, clk, args->args_count, args->args[0], args->args[1]); + + if (args->args_count != 2) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + if (!data->size) + return -EPROBE_DEFER; + + idx = bsearch(args->args[0], args->args[1], data->map, data->size); + if (idx < 0) + return idx; + + clk->id = idx; + + return 0; +} + +static ulong ti_clk_get_rate(struct clk *clk) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + + return clk_get_rate(clkp); +} + +static ulong ti_clk_set_rate(struct clk *clk, ulong rate) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + int div = 1; + ulong child_rate; + const struct clk_ops *ops; + ulong new_rate; + ulong diff; + + /* + * We must propagate rate change to parent if current clock type + * does not allow setting it. + */ + while (clkp) { + ops = clkp->dev->driver->ops; + if (ops->set_rate) + break; + + /* + * Store child rate so we can calculate the clock rate + * that must be passed to parent + */ + child_rate = clk_get_rate(clkp); + clkp = clk_get_parent(clkp); + if (clkp) + div *= clk_get_rate(clkp) / child_rate; + } + + if (!clkp) + return -ENOSYS; + + child_rate = clk_get_rate(clkp); + + new_rate = clk_set_rate(clkp, rate / div); + + /* + * If the new rate differs by at least 10% of the target, + * modify parent. This handles typical cases where we have a hsdiv + * following directly a PLL + */ + + diff = abs(new_rate - rate / div); + + debug("%s: clk=%s, div=%d, rate=%u, new_rate=%u, diff=%u\n", __func__, + clkp->dev->name, div, (u32)rate, (u32)new_rate, (u32)diff); + + if (diff > rate / div / 10) { + ulong pll_tgt; + int pll_div; + + clk = clkp; + + debug("%s: propagating rate change to parent, rate=%u.\n", + __func__, (u32)rate / div); + + clkp = clk_get_parent(clkp); + + if (rate > osc_freq) { + pll_div = PLL_MAX_FREQ / rate / div; + pll_tgt = rate / div * pll_div; + } else { + pll_tgt = osc_freq; + pll_div = rate / div / osc_freq; + } + + debug("%s: pll_tgt=%u, rate=%u, div=%u\n", __func__, + (u32)pll_tgt, (u32)rate, pll_div); + + clk_set_rate(clkp, pll_tgt); + + return clk_set_rate(clk, rate / div) * div; + } + + return new_rate; +} + +static int ti_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + struct clk *parentp = data->map[parent->id].clk; + + return clk_set_parent(clkp, parentp); +} + +static int ti_clk_enable(struct clk *clk) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + + return clk_enable(clkp); +} + +static int ti_clk_disable(struct clk *clk) +{ + struct ti_clk_data *data = dev_get_priv(clk->dev); + struct clk *clkp = data->map[clk->id].clk; + + return clk_disable(clkp); +} + +static const struct udevice_id ti_clk_of_match[] = { + { .compatible = "ti,k2g-sci-clk" }, + { /* sentinel */ }, +}; + +static const struct clk_ops ti_clk_ops = { + .of_xlate = ti_clk_of_xlate, + .set_rate = ti_clk_set_rate, + .get_rate = ti_clk_get_rate, + .enable = ti_clk_enable, + .disable = ti_clk_disable, + .set_parent = ti_clk_set_parent, +}; + +U_BOOT_DRIVER(ti_clk) = { + .name = "ti-clk", + .id = UCLASS_CLK, + .of_match = ti_clk_of_match, + .probe = ti_clk_probe, + .priv_auto_alloc_size = sizeof(struct ti_clk_data), + .ops = &ti_clk_ops, +}; diff --git a/include/k3-clk.h b/include/k3-clk.h index fc84378d03..36f0ddf7cc 100644 --- a/include/k3-clk.h +++ b/include/k3-clk.h @@ -7,7 +7,169 @@ #ifndef __K3_CLK_H__ #define __K3_CLK_H__ +#include +#include +#include #include +#include +#include + +struct dev_clk { + int dev_id; + int clk_id; + const char *clk_name; +}; + +#define DEV_CLK(_dev_id, _clk_id, _clk_name) { .dev_id = _dev_id, \ + .clk_id = _clk_id, .clk_name = _clk_name, } + +#define CLK_TYPE_MUX 0x01 +#define CLK_TYPE_DIV 0x02 +#define CLK_TYPE_PLL 0x03 +#define CLK_TYPE_HFOSC 0x04 +#define CLK_TYPE_POSTDIV 0x05 +#define CLK_TYPE_MUX_PLLCTRL 0x06 +#define CLK_TYPE_FIXED_RATE 0x07 + +struct pll_data { + u32 reg; + const char *name; + const char *parent; + u32 flags; +}; + +struct mux_data { + u32 reg; + const char *name; + const char * const *parents; + int num_parents; + u32 flags; + int shift; + int width; +}; + +struct div_data { + u32 reg; + const char *name; + const char *parent; + u32 flags; + int shift; + int width; +}; + +struct hfosc_data { + const char *name; + u32 flags; +}; + +struct fixed_rate_data { + const char *name; + u64 rate; + u32 flags; +}; + +struct postdiv_data { + const char *name; + const char *parent; + int width; + u32 flags; +}; + +struct mux_pllctrl_data { + u32 reg; + const char *name; + const char * const *parents; + int num_parents; + u32 flags; +}; + +struct clk_data { + int type; + u32 default_freq; + union { + struct pll_data pll; + struct mux_data mux; + struct div_data div; + struct hfosc_data hfosc; + struct postdiv_data postdiv; + struct mux_pllctrl_data mux_pllctrl; + struct fixed_rate_data fixed_rate; + } clk; +}; + +#define CLK_MUX(_name, _parents, _num_parents, _reg, _shift, _width, _flags) \ + { \ + .type = CLK_TYPE_MUX, \ + .clk.mux = { .name = _name, .parents = _parents, \ + .reg = _reg, \ + .num_parents = _num_parents, .shift = _shift, \ + .width = _width, .flags = _flags } \ + } + +#define CLK_DIV(_name, _parent, _reg, _shift, _width, _flags) \ + { \ + .type = CLK_TYPE_DIV, \ + .clk.div = {.name = _name, .parent = _parent, .reg = _reg, .shift = _shift, .width = _width, .flags = _flags } \ + } + +#define CLK_DIV_DEFFREQ(_name, _parent, _reg, _shift, _width, _flags, _freq) \ + { \ + .type = CLK_TYPE_DIV, \ + .default_freq = _freq, \ + .clk.div = { \ + .name = _name, .parent = _parent, \ + .reg = _reg, .shift = _shift, \ + .width = _width, .flags = _flags } \ + } + +#define CLK_PLL(_name, _parent, _reg, _flags) \ + { \ + .type = CLK_TYPE_PLL, \ + .clk.pll = {.name = _name, .parent = _parent, .reg = _reg, .flags = _flags } \ + } + +#define CLK_PLL_DEFFREQ(_name, _parent, _reg, _flags, _freq) \ + { \ + .type = CLK_TYPE_PLL, \ + .default_freq = _freq, \ + .clk.pll = { .name = _name, .parent = _parent, \ + .reg = _reg, .flags = _flags } \ + } + +#define CLK_HFOSC(_name, _flags) \ + { \ + .type = CLK_TYPE_HFOSC, \ + .clk.hfosc = { .name = _name, .flags = _flags } \ + } + +#define CLK_FIXED_RATE(_name, _rate, _flags) \ + { \ + .type = CLK_TYPE_FIXED_RATE, \ + .clk.fixed_rate = { .name = _name, .rate = _rate, .flags = _flags } \ + } + +#define CLK_POSTDIV(_name, _parent, _width, _flags) \ + { \ + .type = CLK_TYPE_POSTDIV, \ + .clk.postdiv = {.name = _name, .parent = _parent, .width = _width, .flags = _flags } \ + } + +#define CLK_MUX_PLLCTRL(_name, _parents, _num_parents, _reg, _flags) \ + { \ + .type = CLK_TYPE_MUX, \ + .clk.mux_pllctrl = { .name = _name, .parents = _parents,\ + .num_parents = _num_parents, .flags = _flags } \ + } + +struct ti_k3_clk_platdata { + const struct clk_data *clk_list; + int clk_list_cnt; + const struct dev_clk *soc_dev_clk_data; + int soc_dev_clk_data_cnt; +}; + +extern const struct ti_k3_clk_platdata j721e_clk_platdata; +extern const struct ti_k3_clk_platdata j7200_clk_platdata; struct clk *clk_register_ti_pll(const char *name, const char *parent_name, void __iomem *reg);