From patchwork Thu Feb 24 16:01:33 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriel FERNANDEZ X-Patchwork-Id: 12758831 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 9EEFCC433EF for ; Thu, 24 Feb 2022 16:04:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=WOZsps+g0IEuU+Lnx8q0n8q6h4s9y9ZAWhYmMCIUQNQ=; b=zcGCN3PElA689y PK1XwyCSaSM6NCWzCk7aZAG0+a/z906hUmXcoCIDdFxIj1gSjRriFzqenVablfRKbnAt8ceWORKa1 1sj08Rk1VUoJvfLww4OAGo6MPOknzrcTTyb5oiU7ZPByR2ZkS/whqZKcCDO63AYNJh440jYMg8BgY qgsHgCiWZLuqLNdvfcFf4USTp9e6Enft3zilw351SDkiu0ROsvXOTZgJ6ZR0FTnB8lOgLUejyVe8K NsoXdTRmozAhlELjkd7bS+G4sX/61lqF1EMAcMb4MdzCp2kirltoVwhMfYQEZuGO9JERORIjuQOYS Igw3wu0GXwl45VYPO7GQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nNGaC-001RF4-BO; Thu, 24 Feb 2022 16:03:08 +0000 Received: from mx08-00178001.pphosted.com ([91.207.212.93] helo=mx07-00178001.pphosted.com) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nNGZS-001QyB-Oo for linux-arm-kernel@lists.infradead.org; Thu, 24 Feb 2022 16:02:26 +0000 Received: from pps.filterd (m0046661.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.16.1.2/8.16.1.2) with ESMTP id 21OA8vWN025634; Thu, 24 Feb 2022 17:02:12 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foss.st.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=selector1; bh=SzLXOSkhzGzN5m22rAwAREfKfOZltcH16yNIJ5ynJA4=; b=CeNdvLBbxuctOZ70LCN8qZZjW9KkY0eln+yrX+wMCQnXRe/JOSjj1mN91ria2wODCRJC w7tqyWXOtYBq2/5y4zplNVgtUMtzZDh6yZW/D/o5/lkiV+v0bn3dBUhAeYL0V6y3oU7E /YqiuwM3lxWEjciIZulwf0WIQetrCgVHB7l5trHxvzCed4qX3DkaxPClKEFXG+NyA82e +gKfkZj2BXUjpOHGJGTiCR8Bj3R4uysrlv9j1z/HkalWM0NjNZXlAYiP5XBFBsuMm138 LmO4MVAggsyjjjJk7jpX/io/l6DoaASFr3rEmgrMUnLDvCwaSvCp8RhV0Io6nw9bQlWn nA== Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com (PPS) with ESMTPS id 3ee2sh47xn-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Thu, 24 Feb 2022 17:02:12 +0100 Received: from euls16034.sgp.st.com (euls16034.sgp.st.com [10.75.44.20]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 2037C10002A; Thu, 24 Feb 2022 17:02:12 +0100 (CET) Received: from Webmail-eu.st.com (sfhdag2node2.st.com [10.75.127.5]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id 144E622A6FC; Thu, 24 Feb 2022 17:02:12 +0100 (CET) Received: from localhost (10.75.127.50) by SFHDAG2NODE2.st.com (10.75.127.5) with Microsoft SMTP Server (TLS) id 15.0.1497.26; Thu, 24 Feb 2022 17:02:11 +0100 From: To: Michael Turquette , Stephen Boyd , Rob Herring , Krzysztof Kozlowski , Maxime Coquelin , Alexandre Torgue , Philipp Zabel , Gabriel Fernandez CC: , , , , Subject: [PATCH 05/13] clk: stm32mp13: add stm32 divider clock Date: Thu, 24 Feb 2022 17:01:33 +0100 Message-ID: <20220224160141.455881-6-gabriel.fernandez@foss.st.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220224160141.455881-1-gabriel.fernandez@foss.st.com> References: <20220224160141.455881-1-gabriel.fernandez@foss.st.com> MIME-Version: 1.0 X-Originating-IP: [10.75.127.50] X-ClientProxiedBy: SFHDAG2NODE1.st.com (10.75.127.4) To SFHDAG2NODE2.st.com (10.75.127.5) X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.205,Aquarius:18.0.816,Hydra:6.0.425,FMLib:17.11.64.514 definitions=2022-02-24_03,2022-02-24_01,2022-02-23_01 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220224_080223_620544_6EE44955 X-CRM114-Status: GOOD ( 20.62 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: Gabriel Fernandez Just to introduce management of a stm32 divider clock Signed-off-by: Gabriel Fernandez --- drivers/clk/stm32/clk-stm32-core.c | 161 +++++++++++++++++++++++++++++ drivers/clk/stm32/clk-stm32-core.h | 36 +++++++ drivers/clk/stm32/clk-stm32mp13.c | 7 ++ 3 files changed, 204 insertions(+) diff --git a/drivers/clk/stm32/clk-stm32-core.c b/drivers/clk/stm32/clk-stm32-core.c index 91eae0386e5e..57d039168779 100644 --- a/drivers/clk/stm32/clk-stm32-core.c +++ b/drivers/clk/stm32/clk-stm32-core.c @@ -195,6 +195,146 @@ const struct clk_ops clk_stm32_gate_ops = { .disable_unused = clk_stm32_gate_disable_unused, }; +static unsigned int _get_table_div(const struct clk_div_table *table, + unsigned int val) +{ + const struct clk_div_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val == val) + return clkt->div; + return 0; +} + +static unsigned int _get_div(const struct clk_div_table *table, + unsigned int val, unsigned long flags, u8 width) +{ + if (flags & CLK_DIVIDER_ONE_BASED) + return val; + if (flags & CLK_DIVIDER_POWER_OF_TWO) + return 1 << val; + if (table) + return _get_table_div(table, val); + return val + 1; +} + +unsigned long clk_stm32_get_rate_divider(void __iomem *base, + struct clk_stm32_clock_data *data, + u16 div_id, unsigned long parent_rate) +{ + const struct stm32_div_cfg *divider = &data->dividers[div_id]; + unsigned int val; + unsigned int div; + + val = readl(base + divider->offset) >> divider->shift; + val &= clk_div_mask(divider->width); + div = _get_div(divider->table, val, divider->flags, divider->width); + + if (!div) { + WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), + "%d: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", + div_id); + return parent_rate; + } + + return DIV_ROUND_UP_ULL((u64)parent_rate, div); +} + +int clk_stm32_set_rate_divider(void __iomem *base, + struct clk_stm32_clock_data *data, + u16 div_id, + unsigned long rate, + unsigned long parent_rate) +{ + const struct stm32_div_cfg *divider = &data->dividers[div_id]; + int value; + u32 val; + + value = divider_get_val(rate, parent_rate, divider->table, + divider->width, divider->flags); + if (value < 0) + return value; + + if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { + val = clk_div_mask(divider->width) << (divider->shift + 16); + } else { + val = readl(base + divider->offset); + val &= ~(clk_div_mask(divider->width) << divider->shift); + } + + val |= (u32)value << divider->shift; + + writel(val, base + divider->offset); + + return 0; +} + +int clk_stm32_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_stm32_div *div = to_clk_stm32_divider(hw); + unsigned long flags = 0; + int ret; + + if (div->div_id == NO_STM32_DIV) + return rate; + + spin_lock_irqsave(div->lock, flags); + + ret = clk_stm32_set_rate_divider(div->base, div->clock_data, div->div_id, + rate, parent_rate); + + spin_unlock_irqrestore(div->lock, flags); + + return ret; +} + +long clk_stm32_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_stm32_div *div = to_clk_stm32_divider(hw); + const struct stm32_div_cfg *divider; + + if (div->div_id == NO_STM32_DIV) + return rate; + + divider = &div->clock_data->dividers[div->div_id]; + + /* if read only, just return current value */ + if (divider->flags & CLK_DIVIDER_READ_ONLY) { + u32 val; + + val = readl(div->base + divider->offset) >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_ro_round_rate(hw, rate, prate, divider->table, + divider->width, divider->flags, + val); + } + + return divider_round_rate_parent(hw, clk_hw_get_parent(hw), + rate, prate, divider->table, + divider->width, divider->flags); +} + +unsigned long clk_stm32_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_stm32_div *div = to_clk_stm32_divider(hw); + + if (div->div_id == NO_STM32_DIV) + return parent_rate; + + return clk_stm32_get_rate_divider(div->base, div->clock_data, + div->div_id, parent_rate); +} + +const struct clk_ops clk_stm32_divider_ops = { + .recalc_rate = clk_stm32_divider_recalc_rate, + .round_rate = clk_stm32_divider_round_rate, + .set_rate = clk_stm32_divider_set_rate, +}; + u8 clk_stm32_get_parent_mux(void __iomem *base, struct clk_stm32_clock_data *data, u16 mux_id) @@ -274,6 +414,27 @@ struct clk_hw *clk_stm32_gate_register(struct device *dev, return hw; } +struct clk_hw *clk_stm32_div_register(struct device *dev, + const struct stm32_rcc_match_data *data, + void __iomem *base, + spinlock_t *lock, + const struct clock_config *cfg) +{ + struct clk_stm32_div *div = cfg->clock_cfg; + struct clk_hw *hw = &div->hw; + int err; + + div->base = base; + div->lock = lock; + div->clock_data = data->clock_data; + + err = clk_hw_register(dev, hw); + if (err) + return ERR_PTR(err); + + return hw; +} + struct clk_hw *clk_stm32_mux_register(struct device *dev, const struct stm32_rcc_match_data *data, void __iomem *base, diff --git a/drivers/clk/stm32/clk-stm32-core.h b/drivers/clk/stm32/clk-stm32-core.h index 1b4a73556512..833a3d4a064f 100644 --- a/drivers/clk/stm32/clk-stm32-core.h +++ b/drivers/clk/stm32/clk-stm32-core.h @@ -94,6 +94,16 @@ struct clk_stm32_gate { #define to_clk_stm32_gate(_hw) container_of(_hw, struct clk_stm32_gate, hw) +struct clk_stm32_div { + u16 div_id; + struct clk_hw hw; + void __iomem *base; + struct clk_stm32_clock_data *clock_data; + spinlock_t *lock; /* spin lock */ +}; + +#define to_clk_stm32_divider(_hw) container_of(_hw, struct clk_stm32_div, hw) + struct clk_stm32_mux { u16 mux_id; struct clk_hw hw; @@ -121,6 +131,21 @@ void clk_stm32_gate_disable(struct clk_hw *hw); int clk_stm32_gate_is_enabled(struct clk_hw *hw); void clk_stm32_gate_disable_unused(struct clk_hw *hw); +int clk_stm32_set_rate_divider(void __iomem *base, + struct clk_stm32_clock_data *data, + u16 div_id, unsigned long rate, + unsigned long parent_rate); +unsigned long clk_stm32_get_rate_divider(void __iomem *base, + struct clk_stm32_clock_data *data, + u16 div_id, unsigned long parent_rate); + +int clk_stm32_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +long clk_stm32_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate); +unsigned long clk_stm32_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate); + u8 clk_stm32_get_parent_mux(void __iomem *base, struct clk_stm32_clock_data *data, u16 mux_id); @@ -132,6 +157,7 @@ u8 clk_stm32_mux_get_parent(struct clk_hw *hw); int clk_stm32_mux_set_parent(struct clk_hw *hw, u8 index); extern const struct clk_ops clk_stm32_gate_ops; +extern const struct clk_ops clk_stm32_divider_ops; extern const struct clk_ops clk_stm32_mux_ops; /* Clock registering */ @@ -141,6 +167,12 @@ struct clk_hw *clk_stm32_gate_register(struct device *dev, spinlock_t *lock, const struct clock_config *cfg); +struct clk_hw *clk_stm32_div_register(struct device *dev, + const struct stm32_rcc_match_data *data, + void __iomem *base, + spinlock_t *lock, + const struct clock_config *cfg); + struct clk_hw *clk_stm32_mux_register(struct device *dev, const struct stm32_rcc_match_data *data, void __iomem *base, @@ -158,6 +190,10 @@ struct clk_hw *clk_stm32_mux_register(struct device *dev, STM32_CLOCK_CFG(_binding, &(_clk), struct clk_stm32_gate *,\ &clk_stm32_gate_register) +#define STM32_DIV_CFG(_binding, _clk)\ + STM32_CLOCK_CFG(_binding, &(_clk), struct clk_stm32_div *,\ + &clk_stm32_div_register) + #define STM32_MUX_CFG(_binding, _clk)\ STM32_CLOCK_CFG(_binding, &(_clk), struct clk_stm32_mux *,\ &clk_stm32_mux_register) diff --git a/drivers/clk/stm32/clk-stm32mp13.c b/drivers/clk/stm32/clk-stm32mp13.c index 24c0c9ff3602..66ebfe810b08 100644 --- a/drivers/clk/stm32/clk-stm32mp13.c +++ b/drivers/clk/stm32/clk-stm32mp13.c @@ -415,9 +415,16 @@ static struct clk_stm32_gate eth1ck_k = { .hw.init = CLK_HW_INIT_HW("eth1ck_k", &ck_ker_eth1.hw, &clk_stm32_gate_ops, 0), }; +static struct clk_stm32_div eth1ptp_k = { + .div_id = DIV_ETH1PTP, + .hw.init = CLK_HW_INIT_HW("eth1ptp_k", &ck_ker_eth1.hw, &clk_stm32_divider_ops, + CLK_SET_RATE_NO_REPARENT), +}; + static const struct clock_config stm32mp13_clock_cfg[] = { STM32_MUX_CFG(NO_ID, ck_ker_eth1), STM32_GATE_CFG(ETH1CK_K, eth1ck_k), + STM32_DIV_CFG(ETH1PTP_K, eth1ptp_k), }; u16 stm32mp13_cpt_gate[GATE_NB];