From nobody Tue Feb 10 00:58:13 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 85F3831352D; Mon, 9 Feb 2026 05:49:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770616147; cv=none; b=hSKzo0JccDVU+18zQVwGbQkxTpqtpRcTn6LN8lcKAKsXuIIGdNPp8H5WotGciJcMk3T0/GM2qoz4TrBsM4Xnf6g7tH5RUDOLzjvV00sPjhBqN02biWQ9YOXWUZM1PDcVVlLkpnEgAehnj0mgv9cYFZZycuIjZZc2VT/n+FYdUNg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770616147; c=relaxed/simple; bh=MOHUGf7vTOMF6lwnsIj7fzyFlWDO1jYxaQ8jI4vnDS0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tXdzHuFOlrNMgJV7I53/P1z1VmlrXFbrsR46XEZ/UZf87jgslMyxbRk76BRKvhI25tp+qwAe/XH0gTSPIvC67J3fNNAvJenjl/FOG3THBt1A63zap6XiWJU0+MimVAHSLM7xsxgUsozQm0hFhkF3eapyJb5ux7/aeGRg8OEfY1s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bD1yHKof; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bD1yHKof" Received: by smtp.kernel.org (Postfix) with ESMTPS id 6A6BFC116C6; Mon, 9 Feb 2026 05:49:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1770616147; bh=MOHUGf7vTOMF6lwnsIj7fzyFlWDO1jYxaQ8jI4vnDS0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=bD1yHKof6KshfPMre7sBQ9IezJ4GNYw3QRBNOSWzh22+RcVCRSgf1FdSOJL9xqF1Y G99188bOR2Zwj4/ABcfIRrnTtw9xQQcGrx09557UBiCma1oIt2E0a7bOKmKX81DtzZ lzFDELzS9WpvwtA+h2ZjVojZO9JNcyVCWGyQS4J/gXezcearCMzRVhHeQRw45+uOdu dBn+T5nGuahJUPj0T6zho+ioxHvDzKDrN2H+FZOE79pvvYTmLJPU7S8MiWa3X7p8BC La3elBc4nwyMY6l7H8zl/vY8X6b1EXL5oOUE+EpKmjZjdL3s14QKjcRgk2hY9EU8w3 N5Uwpf3h5m7mg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5D765EF070F; Mon, 9 Feb 2026 05:49:07 +0000 (UTC) From: Chuan Liu via B4 Relay Date: Mon, 09 Feb 2026 13:48:56 +0800 Subject: [PATCH 10/13] clk: amlogic: Add A9 standardized model clock control units driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260209-a9_clock_driver-v1-10-a9198dc03d2a@amlogic.com> References: <20260209-a9_clock_driver-v1-0-a9198dc03d2a@amlogic.com> In-Reply-To: <20260209-a9_clock_driver-v1-0-a9198dc03d2a@amlogic.com> To: Neil Armstrong , Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-amlogic@lists.infradead.org, linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Chuan Liu X-Mailer: b4 0.14.1 X-Developer-Signature: v=1; a=ed25519-sha256; t=1770616143; l=15986; i=chuan.liu@amlogic.com; s=20240902; h=from:subject:message-id; bh=LIC/rr157/JcmUmsWhff3I7WronpOmEnKJiamdc04jU=; b=Cn+IlwLfHrOKz84vKbMEjcJCgFP2t1fhGCknPdqWQzUKBbwq0EpLcNhUSHVvN+IZdvSD6B9HK uMBtyslg2V7BQypbVB3EUhsVYWfp1VsatS5ODnHB4Tv99kkrXFguZZS X-Developer-Key: i=chuan.liu@amlogic.com; a=ed25519; pk=fnKDB+81SoWGKW2GJNFkKy/ULvsDmJZRGBE7pR5Xcpo= X-Endpoint-Received: by B4 Relay for chuan.liu@amlogic.com/20240902 with auth_id=203 X-Original-From: Chuan Liu Reply-To: chuan.liu@amlogic.com From: Chuan Liu Add support for Amlogic standardized model clock control units on A9 SoC family. The standardized models include: - composite-ccu - noglitch-ccu - sysbus-ccu Signed-off-by: Chuan Liu --- drivers/clk/amlogic/Kconfig | 19 ++ drivers/clk/amlogic/Makefile | 3 + drivers/clk/amlogic/a9-model-ccu.c | 465 +++++++++++++++++++++++++++++++++= ++++ 3 files changed, 487 insertions(+) diff --git a/drivers/clk/amlogic/Kconfig b/drivers/clk/amlogic/Kconfig index 216fe98a413b..6e954c9388dc 100644 --- a/drivers/clk/amlogic/Kconfig +++ b/drivers/clk/amlogic/Kconfig @@ -10,3 +10,22 @@ config COMMON_CLK_AMLOGIC This driver provides the basic clock infrastructure for Amlogic SoCs, offering read and write interfaces for various clock control units. Select Y if your target SoC needs clock driver support. + +config COMMON_CLK_AMLOGIC_MODEL + tristate "Amlogic Standardized Model Clock Control Units" + depends on COMMON_CLK_AMLOGIC + help + Supports standardized model clock control units commonly used in Amlogic + SoC clock trees, such as composite-ccu, noglitch-ccu, and sysbus-ccu. + Most peripheral clock controllers in Amlogic SoCs are composed of + these models. Select Y if the current SoC contains these clock control + unit models. + +config COMMON_CLK_AMLOGIC_A9 + tristate "Amlogic A9 Family Clock Controller" + depends on COMMON_CLK_AMLOGIC + default COMMON_CLK_AMLOGIC + select COMMON_CLK_AMLOGIC_MODEL + help + Support for the clock controller present on the Amlogic A9 family + SoCs. Select Y if A9 family SoC needs to support clock controller. diff --git a/drivers/clk/amlogic/Makefile b/drivers/clk/amlogic/Makefile index 6956592c41c8..ef3fb57cae9f 100644 --- a/drivers/clk/amlogic/Makefile +++ b/drivers/clk/amlogic/Makefile @@ -8,3 +8,6 @@ clk-amlogic-y +=3D clk-composite.o clk-amlogic-y +=3D clk-dualdiv.o clk-amlogic-y +=3D clk-noglitch.o clk-amlogic-y +=3D clk-pll.o +ifneq ($(CONFIG_COMMON_CLK_AMLOGIC_MODEL),) +clk-amlogic-y +=3D a9-model-ccu.o +endif diff --git a/drivers/clk/amlogic/a9-model-ccu.c b/drivers/clk/amlogic/a9-mo= del-ccu.c new file mode 100644 index 000000000000..5d5bf1538f73 --- /dev/null +++ b/drivers/clk/amlogic/a9-model-ccu.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) +/* + * Copyright (c) 2026 Amlogic, Inc. All rights reserved + */ + +#include +#include + +#include "clk.h" +#include "clk-basic.h" +#include "clk-composite.h" +#include "clk-noglitch.h" + +/* + * The standardized model clock control units of Amlogic includes: + * - composite-ccu + * - noglitch-ccu + * - sysbus-ccu + */ +#define MAX_AML_CLK_COMP_PARENTS 8 + +enum aml_clk_model_type { + CLK_MODEL_COMPOSITE =3D 1, + CLK_MODEL_NOGLITCH =3D 2, + CLK_MODEL_SYSBUS =3D 3, +}; + +struct aml_clk_model_data { + enum aml_clk_model_type type; +}; + +static int of_aml_clk_model_get_type(struct device *dev, + enum aml_clk_type *out_type) +{ + const struct aml_clk_model_data *data; + + data =3D of_device_get_match_data(dev); + if (!data) + return -EFAULT; + + switch (data->type) { + case CLK_MODEL_COMPOSITE: + *out_type =3D AML_CLKTYPE_COMPOSITE; + return 0; + + case CLK_MODEL_NOGLITCH: + *out_type =3D AML_CLKTYPE_NOGLITCH; + return 0; + + case CLK_MODEL_SYSBUS: + *out_type =3D AML_CLKTYPE_GATE; + return 0; + + default: + return -EINVAL; + } +} + +/* + * A diagram of the A9 composite-ccu is as follows: + * +----------------------------------+ + * | | + * | |\ | + * clk0 ------>| | | + * clk1 ------>| | | + * clk2 ------>| | | + * clk3 ------>| | +-----+ +------+ | + * | | |---->| div |---->| gate |------> clk out + * clk4 ------>| | +-----+ +------+ | + * clk5 ------>| | | + * clk6 ------>| | | + * clk7 ------>| | | + * | |/ | + * | | + * +----------------------------------+ + */ +static int +of_aml_clk_model_composite_init_register(struct device *dev, + struct clk_hw_onecell_data *hw_data) +{ + struct device_node *np =3D dev_of_node(dev); + struct aml_clk *clk; + struct aml_clk_composite_data *composite; + u32 reg_val[3]; + unsigned int pcnt =3D of_clk_get_parent_count(np); + struct clk_parent_data pdata[MAX_AML_CLK_COMP_PARENTS]; + struct clk_parent_data pdata_compb[MAX_AML_CLK_COMP_PARENTS]; + u8 pnum, pnum_compb; + u32 *ptab, *ptab_compb; + struct clk_init_data init =3D { 0 }; + int index; + int ret, clkid, i; + + ret =3D of_aml_clk_get_parent_data(dev, hw_data->hws, 0, 7, pdata, &pnum); + if (ret) + return ret; + + ptab =3D of_aml_clk_get_parent_table(dev, 0, 7); + if (IS_ERR(ptab)) + return PTR_ERR(ptab); + + /* + * If the number of "clocks" defined in DT is less than or equal + * to MAX_AML_CLK_COMP_PARENTS, composite-ccu_a and composite-ccu_b + * share the same parent clocks. + * + * If the number of "clocks" defined in the DT is greater than + * MAX_AML_CLK_COMP_PARENTS, composite-ccu_a and composite-ccu_b have + * different parent clocks, the parent clocks specified by + * "clocks" follow the rule below: + * - for composite-ccu_a: clocks indices 0-7 in "clocks" + * - for composite-ccu_b: clocks indices 8-15 in "clocks" + */ + if (pcnt > MAX_AML_CLK_COMP_PARENTS) { /* composite-ccu_b */ + ret =3D of_aml_clk_get_parent_data(dev, hw_data->hws, 8, 15, + pdata_compb, &pnum_compb); + if (ret) + return ret; + + ptab_compb =3D of_aml_clk_get_parent_table(dev, 8, 15); + if (IS_ERR(ptab_compb)) + return PTR_ERR(ptab_compb); + } + + init.ops =3D &aml_clk_composite_ops; + for (clkid =3D 0; clkid < hw_data->num; clkid++) { + init.name =3D of_aml_clk_get_name_index(np, clkid); + if (!init.name) + return -EINVAL; + + /* + * The set of "amlogic,reg-layout" attributes of composite_ccu + * contains three u32 data: + * - reg_offset + * - bit_offset + * - div_width + */ + index =3D clkid * 3; + for (i =3D 0; i < 3; i++) { + ret =3D of_property_read_u32_index(np, + "amlogic,reg-layout", + index + i, ®_val[i]); + if (ret) + return ret; + } + + composite =3D devm_kzalloc(dev, sizeof(*composite), GFP_KERNEL); + if (!composite) + return -ENOMEM; + + composite->reg_offset =3D reg_val[0]; + composite->bit_offset =3D reg_val[1]; + composite->div_width =3D reg_val[2]; + + /* + * The register bit allocation for composite-ccu_a and + * composite-ccu_b is as follows: + * - composite-ccu_a: bit[15: 0] + * - composite-ccu_b: bit[31: 16] + * A value of "composite->bit_offset =3D=3D 16" indicates that this + * CCU corresponds to composite-ccu_b. + */ + if (pcnt > MAX_AML_CLK_COMP_PARENTS && + composite->bit_offset =3D=3D 16) { /* composite-ccu_b */ + init.num_parents =3D pnum_compb; + init.parent_data =3D pdata_compb; + + if (ptab_compb) + composite->table =3D ptab_compb; + } else { /* composite-ccu_a */ + init.num_parents =3D pnum; + init.parent_data =3D pdata; + + if (ptab) + composite->table =3D ptab; + } + + clk =3D to_aml_clk(hw_data->hws[clkid]); + clk->data =3D composite; + + hw_data->hws[clkid]->init =3D &init; + ret =3D of_aml_clk_register(dev, hw_data->hws[clkid], clkid); + if (ret) + return ret; + } + + return 0; +} + +/* + * A diagram of the A9 noglitch-ccu is as follows: + * +---------------------------------------------+ + * | |\ | + * clk0 ------>| | | + * clk1 ------>| | | + * clk2 ------>| | | + * clk3 ------>| | +-----+ +------+ |\ | + * | | |----->| div |----->| gate |----->| | | + * clk4 ------>| | +-----+ +------+ | | | + * clk5 ------>| | | | | + * clk6 ------>| | | | | + * clk7 ------>| | | | | + * | |/ | | | + * | | |-------> clk out + * | |\ | | | + * clk0 ------>| | | | | + * clk1 ------>| | | | | + * clk2 ------>| | | | | + * clk3 ------>| | +-----+ +------+ | | | + * | | |----->| div |---->| gate |------>| | | + * clk4 ------>| | +-----+ +------+ |/ | + * clk5 ------>| | | + * clk6 ------>| | | + * clk7 ------>| | | + * | |/ | + * +---------------------------------------------+ + */ +static int +of_aml_clk_model_noglitch_init_register(struct device *dev, + struct clk_hw_onecell_data *hw_data) +{ + struct device_node *np =3D dev_of_node(dev); + struct aml_clk *clk; + struct aml_clk_noglitch_data *noglitch; + struct clk_parent_data pdata[MAX_AML_CLK_COMP_PARENTS]; + u8 pnum; + u32 *ptab; + struct clk_init_data init =3D { 0 }; + int ret, clkid; + + /* noglitch-ccu supports up to eight parent clocks. */ + ret =3D of_aml_clk_get_parent_data(dev, hw_data->hws, 0, 7, pdata, &pnum); + if (ret) + return ret; + + ptab =3D of_aml_clk_get_parent_table(dev, 0, 7); + if (IS_ERR(ptab)) + return PTR_ERR(ptab); + + init.ops =3D &aml_clk_noglitch_ops; + init.num_parents =3D pnum; + init.parent_data =3D pdata; + for (clkid =3D 0; clkid < hw_data->num; clkid++) { + init.name =3D of_aml_clk_get_name_index(np, clkid); + if (!init.name) + return -EINVAL; + + hw_data->hws[clkid]->init =3D &init; + + noglitch =3D devm_kzalloc(dev, sizeof(*noglitch), GFP_KERNEL); + if (!noglitch) + return -ENOMEM; + + ret =3D of_property_read_u32_index(np, "amlogic,reg-layout", + clkid, &noglitch->reg_offset); + if (ret) + return ret; + + if (ptab) + noglitch->table =3D ptab; + + clk =3D to_aml_clk(hw_data->hws[clkid]); + clk->data =3D noglitch; + + ret =3D of_aml_clk_register(dev, hw_data->hws[clkid], clkid); + if (ret) + return ret; + } + + return 0; +} + +/* + * A diagram of the A9 sysbus-ccu is as follows: + * +-------------------+ + * | | + * | +------+ | + * | +---->| gate |----->clkout0 + * | | +------+ | + * | | | + * | | | + * | | | + * | | +------+ | + * clk in-----+---->| gate |----->clkout1 + * | | +------+ | + * | | ... | + * | | | + * | | | + * | | +------+ | + * | +---->| gate |----->clkoutn + * | +------+ | + * | | + * +-------------------+ + */ +static int +of_aml_clk_model_sysbus_init_register(struct device *dev, + struct clk_hw_onecell_data *hw_data) +{ + struct device_node *np =3D dev_of_node(dev); + struct aml_clk *clk; + struct aml_clk_gate_data *gate; + u32 reg_val[2]; + struct clk_parent_data pdata; + u8 pnum; + struct clk_init_data init =3D { 0 }; + int index; + int ret, clkid, i; + + ret =3D of_aml_clk_get_parent_data(dev, hw_data->hws, 0, 0, &pdata, &pnum= ); + if (ret) + return ret; + + init.ops =3D &aml_clk_gate_ops; + init.num_parents =3D pnum; + init.parent_data =3D &pdata; + for (clkid =3D 0; clkid < hw_data->num; clkid++) { + init.name =3D of_aml_clk_get_name_index(np, clkid); + if (!init.name) + return -EINVAL; + + /* + * The set of "amlogic,reg-layout" attributes of sysbus_ccu + * contains three u32 data: + * - reg_offset + * - bit_idx + */ + index =3D clkid * 2; + for (i =3D 0; i < 2; i++) { + ret =3D of_property_read_u32_index(np, + "amlogic,reg-layout", + index + i, ®_val[i]); + if (ret) + return ret; + } + + gate =3D devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return -ENOMEM; + + gate->reg_offset =3D reg_val[0]; + gate->bit_idx =3D reg_val[1]; + + clk =3D to_aml_clk(hw_data->hws[clkid]); + clk->data =3D gate; + + hw_data->hws[clkid]->init =3D &init; + ret =3D of_aml_clk_register(dev, hw_data->hws[clkid], clkid); + if (ret) + return ret; + } + + return 0; +} + +static int of_aml_clk_model_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + const struct aml_clk_model_data *data; + struct device_node *np =3D dev_of_node(dev); + struct regmap *regmap; + struct clk_hw_onecell_data *hw_data; + struct aml_clk *clk; + enum aml_clk_type clk_type; + int clk_num; + int ret, i; + + data =3D of_device_get_match_data(dev); + if (!data) + return -EFAULT; + + clk_num =3D of_aml_clk_get_count(np); + if (clk_num =3D=3D 0) + return -EINVAL; + + regmap =3D aml_clk_regmap_init(pdev); + if (IS_ERR_OR_NULL(regmap)) + return -EIO; + + of_aml_clk_regs_init(dev); + + hw_data =3D devm_kzalloc(dev, struct_size(hw_data, hws, clk_num), + GFP_KERNEL); + if (!hw_data) + return -ENOMEM; + + hw_data->num =3D clk_num; + clk =3D devm_kcalloc(dev, clk_num, sizeof(*clk), GFP_KERNEL); + if (!clk) + return -ENOMEM; + + ret =3D of_aml_clk_model_get_type(dev, &clk_type); + if (ret) + return ret; + + for (i =3D 0; i < clk_num; i++) { + clk[i].map =3D regmap; + clk[i].type =3D clk_type; + hw_data->hws[i] =3D &clk[i].hw; + } + + if (data->type =3D=3D CLK_MODEL_COMPOSITE) + ret =3D of_aml_clk_model_composite_init_register(dev, hw_data); + else if (data->type =3D=3D CLK_MODEL_NOGLITCH) + ret =3D of_aml_clk_model_noglitch_init_register(dev, hw_data); + else if (data->type =3D=3D CLK_MODEL_SYSBUS) + ret =3D of_aml_clk_model_sysbus_init_register(dev, hw_data); + + if (clk_num =3D=3D 1) + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &clk->hw); + else + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + hw_data); +} + +static const struct aml_clk_model_data aml_composite_dev_data =3D { + .type =3D CLK_MODEL_COMPOSITE, +}; + +static const struct aml_clk_model_data aml_noglitch_dev_data =3D { + .type =3D CLK_MODEL_NOGLITCH, +}; + +static const struct aml_clk_model_data aml_sysbus_dev_data =3D { + .type =3D CLK_MODEL_SYSBUS, +}; + +static const struct of_device_id of_aml_clk_model_match_table[] =3D { + { + .compatible =3D "amlogic,a9-composite-ccu", + .data =3D &aml_composite_dev_data, + }, + { + .compatible =3D "amlogic,a9-composite-ccu-mult", + .data =3D &aml_composite_dev_data, + }, + { + .compatible =3D "amlogic,a9-noglitch-ccu", + .data =3D &aml_noglitch_dev_data, + }, + { + .compatible =3D "amlogic,a9-noglitch-ccu-mult", + .data =3D &aml_noglitch_dev_data, + }, + { + .compatible =3D "amlogic,a9-sysbus-ccu", + .data =3D &aml_sysbus_dev_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, of_aml_clk_model_match_table); + +static struct platform_driver of_aml_clk_model_driver =3D { + .probe =3D of_aml_clk_model_probe, + .driver =3D { + .name =3D "aml-clk-model", + .of_match_table =3D of_aml_clk_model_match_table, + }, +}; +module_platform_driver(of_aml_clk_model_driver); + +MODULE_DESCRIPTION("Amlogic A9 Standardized Model Clock Control Units Driv= er"); +MODULE_AUTHOR("Chuan Liu "); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("CLK_AMLOGIC"); --=20 2.42.0