From patchwork Fri Sep 15 08:10:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nikita Shubin via B4 Relay X-Patchwork-Id: 140222 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:172:b0:3f2:4152:657d with SMTP id h50csp888351vqi; Fri, 15 Sep 2023 01:23:20 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFJ9RauMPkmuyLBUb+IoDpQulW0E4VzZ5grXa5FMTrM7RQ0IbV1LjyXCXFwAb8/NEbKAAXS X-Received: by 2002:a05:6a21:66c7:b0:154:d3ac:2050 with SMTP id ze7-20020a056a2166c700b00154d3ac2050mr973018pzb.33.1694766200001; Fri, 15 Sep 2023 01:23:20 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1694766199; cv=none; d=google.com; s=arc-20160816; b=kdMdqOwinK/yOiA384z2AS6tmOGQqRc2GscsAsvFK4YWvGRTM3Fmx/pNs5Jv9VegIw X8AYk0yDA0Er+s9RoL48uu97eyLfO4dQ22M3mDjP9ua9NT4iFaWS9ON/JlI8UavH27fW jG8ND19z5I9oU1KXKEAccK1xGpLsiQAA1u6mvRbF9TlB0kN3hxVE5g8eT82pR+M8ymJs XbmmtfuHfpMXaqDqTI9F3fs+82Je+iCD6AHpiFvHO5jNcSCTXhHo00ICOQJcMnqHCAdA w+mBbjg+pIOl74zHCdRJnCScSLcp5nUOSC+LWeQFfEjGaaZEqxhU8gLg6c/X4ZqioMU+ GXwQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:reply-to:cc:to:in-reply-to:references:message-id :content-transfer-encoding:mime-version:subject:date:from :dkim-signature; bh=U+/doRCefpZmdMjgkay4frkLbnKg45ZLg7jrrVrm9OQ=; fh=LwMpCBaUDvmxY0H4uW5WvR63uH8HFUtimTpmy7kEUkQ=; b=HqDnmu2mVVhNfpel9autSgFk0asmgpvDE1VtHuas05Id29Rs4fTaAlpQDNyS/qx9kg ynmz6wAoVJUSlz8mEUIqD6CRXzzHVMl1gsci/RWcnnAk3kZMr/S4QgvJ0MTqXz/8NHcN rs5Pv4GG7e1sAbN5L4g7zeavbRml3t9tL4iLDxRrSzE1i7SOj94kmmsSc+EJ+6g23TAX ftTguhkPI/5/4s7Yid4WqxeSgb8Kze9G/lwECpqYuG2JoOP9xS/HBleX118LBfkHFvT0 61KjhR6hVsrk6VTJTZ1mwk9lgbCr7UPKa0tuHs9kGJN2kPbBNV6XGq9kdUxQKmRRUWsp u+Ag== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=IKM0JHJJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: from howler.vger.email (howler.vger.email. [2620:137:e000::3:4]) by mx.google.com with ESMTPS id ie13-20020a17090b400d00b0026b2bb306f3si4974634pjb.62.2023.09.15.01.23.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 Sep 2023 01:23:19 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) client-ip=2620:137:e000::3:4; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=IKM0JHJJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:4 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by howler.vger.email (Postfix) with ESMTP id AC21885C6475; Fri, 15 Sep 2023 01:12:42 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at howler.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232920AbjIOIMg (ORCPT + 32 others); Fri, 15 Sep 2023 04:12:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34078 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232887AbjIOIMR (ORCPT ); Fri, 15 Sep 2023 04:12:17 -0400 Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 999CA2711; Fri, 15 Sep 2023 01:12:09 -0700 (PDT) Received: by smtp.kernel.org (Postfix) with ESMTPS id 48217C433C8; Fri, 15 Sep 2023 08:12:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1694765528; bh=tzP/nbbftiS7NFqyge3wQDPh9VtyJY7aZuC2sZRJCyg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=IKM0JHJJ1Eul4dE4x454f3nvFZpX8/JuQO95zNlQJ7HiIIn/pvpTyP8YCzGsPHwV9 meXxPPu2qlsGiy45zfh8nuOzsawbjXw7/Dt53mJKYJTwZu1mO6oDLjw4VOfV3U2BoH hhtQxVs6D+5Qmpi/iKEOZrnnccq7ffbAV3wcBUi8uPmVXzzreexCOXFsnPacUNsULt vc11W49EFlrOpvQQKRpXSkZQW05S3ORA2hH/6owcqqiSvI2+lMkWBZWGTzxkw2iL1l O9BviBKvMVzu6aNpYilv6LLrP1D7G0GWWQ7KbpIKQm/s5PGvEmYwUxJDs60GgT6R5x InzjQuHRUM9MQ== 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 367B2EE6437; Fri, 15 Sep 2023 08:12:08 +0000 (UTC) From: Nikita Shubin via B4 Relay Date: Fri, 15 Sep 2023 11:10:46 +0300 Subject: [PATCH v4 04/42] clk: ep93xx: add DT support for Cirrus EP93xx MIME-Version: 1.0 Message-Id: <20230915-ep93xx-v4-4-a1d779dcec10@maquefel.me> References: <20230915-ep93xx-v4-0-a1d779dcec10@maquefel.me> In-Reply-To: <20230915-ep93xx-v4-0-a1d779dcec10@maquefel.me> To: Michael Turquette , Stephen Boyd Cc: linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, Arnd Bergmann , Alexander Sverdlin X-Mailer: b4 0.13-dev-e3e53 X-Developer-Signature: v=1; a=ed25519-sha256; t=1694765525; l=23995; i=nikita.shubin@maquefel.me; s=20230718; h=from:subject:message-id; bh=SnESWjjVgAn8iVjqaVHdNkA5UnLKbSy0iW8lNXn4B+k=; =?utf-8?q?b=3DDqEd6FR99ksw?= =?utf-8?q?VJZYKPeFlNxNBv2yulXC/H6Q+pKu9NW8HdAPyXYjOwcEN6OsutWz1n+Xzqa9ooo2?= d66v6Wn6CLC51Q6LYmvDWB2isLCse+39Ano0y3sjuP7okxRp+DZ7 X-Developer-Key: i=nikita.shubin@maquefel.me; a=ed25519; pk=vqf5YIUJ7BJv3EJFaNNxWZgGuMgDH6rwufTLflwU9ac= X-Endpoint-Received: by B4 Relay for nikita.shubin@maquefel.me/20230718 with auth_id=65 X-Original-From: Nikita Shubin Reply-To: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (howler.vger.email [0.0.0.0]); Fri, 15 Sep 2023 01:12:42 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1777091163177244577 X-GMAIL-MSGID: 1777091163177244577 From: Nikita Shubin Rewrite EP93xx clock driver located in arch/arm/mach-ep93xx/clock.c trying to do everything the device tree way: - provide clock acces via of - drop clk_hw_register_clkdev - drop init code and use builtin_platform_driver_probe Also moved all clocks not accessed via device tree to syscon. Co-developed-by: Alexander Sverdlin Signed-off-by: Alexander Sverdlin Signed-off-by: Nikita Shubin --- drivers/clk/Kconfig | 8 + drivers/clk/Makefile | 1 + drivers/clk/clk-ep93xx.c | 753 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 762 insertions(+) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6b3b424addab..abd8e04a33aa 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -218,6 +218,14 @@ config COMMON_CLK_EN7523 This driver provides the fixed clocks and gates present on Airoha ARM silicon. +config COMMON_CLK_EP93XX + bool "Clock driver for Cirrus Logic ep93xx SoC" + depends on ARCH_EP93XX || COMPILE_TEST + select MFD_SYSCON + select REGMAP + help + This driver supports the SoC clocks on the Cirrus Logic ep93xx. + config COMMON_CLK_FSL_FLEXSPI tristate "Clock driver for FlexSPI on Layerscape SoCs" depends on ARCH_LAYERSCAPE || COMPILE_TEST diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 7cb000549b61..6049a2073a25 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o +obj-$(CONFIG_COMMON_CLK_EP93XX) += clk-ep93xx.o obj-$(CONFIG_ARCH_SPARX5) += clk-sparx5.o obj-$(CONFIG_COMMON_CLK_EN7523) += clk-en7523.o obj-$(CONFIG_COMMON_CLK_FIXED_MMIO) += clk-fixed-mmio.o diff --git a/drivers/clk/clk-ep93xx.c b/drivers/clk/clk-ep93xx.c new file mode 100644 index 000000000000..e8d3bd595255 --- /dev/null +++ b/drivers/clk/clk-ep93xx.c @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Clock control for Cirrus EP93xx chips. + * Copyright (C) 2021 Nikita Shubin + * + * Based on a rewrite of arch/arm/mach-ep93xx/clock.c: + * Copyright (C) 2006 Lennert Buytenhek + */ +#define pr_fmt(fmt) "ep93xx " KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define EP93XX_EXT_RTC_RATE 32768 + +#define EP93XX_SYSCON_POWER_STATE 0x00 +#define EP93XX_SYSCON_PWRCNT 0x04 +#define EP93XX_SYSCON_PWRCNT_UARTBAUD BIT(29) +#define EP93XX_SYSCON_PWRCNT_USH_EN 28 +#define EP93XX_SYSCON_PWRCNT_DMA_M2M1 27 +#define EP93XX_SYSCON_PWRCNT_DMA_M2M0 26 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P8 25 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P9 24 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P6 23 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P7 22 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P4 21 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P5 20 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P2 19 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P3 18 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P0 17 +#define EP93XX_SYSCON_PWRCNT_DMA_M2P1 16 +#define EP93XX_SYSCON_DEVCFG 0x80 +#define EP93XX_SYSCON_DEVCFG_U3EN 24 +#define EP93XX_SYSCON_DEVCFG_U2EN 20 +#define EP93XX_SYSCON_DEVCFG_U1EN 18 +#define EP93XX_SYSCON_VIDCLKDIV 0x84 +#define EP93XX_SYSCON_CLKDIV_ENABLE 15 +#define EP93XX_SYSCON_CLKDIV_ESEL BIT(14) +#define EP93XX_SYSCON_CLKDIV_PSEL BIT(13) +#define EP93XX_SYSCON_CLKDIV_MASK GENMASK(14, 13) +#define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT 8 +#define EP93XX_SYSCON_I2SCLKDIV 0x8c +#define EP93XX_SYSCON_I2SCLKDIV_SENA 31 +#define EP93XX_SYSCON_I2SCLKDIV_ORIDE BIT(29) +#define EP93XX_SYSCON_I2SCLKDIV_SPOL BIT(19) +#define EP93XX_I2SCLKDIV_SDIV (1 << 16) +#define EP93XX_I2SCLKDIV_LRDIV32 (0 << 17) +#define EP93XX_I2SCLKDIV_LRDIV64 (1 << 17) +#define EP93XX_I2SCLKDIV_LRDIV128 (2 << 17) +#define EP93XX_I2SCLKDIV_LRDIV_MASK (3 << 17) +#define EP93XX_SYSCON_KEYTCHCLKDIV 0x90 +#define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN 31 +#define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV 16 +#define EP93XX_SYSCON_KEYTCHCLKDIV_KEN 15 +#define EP93XX_SYSCON_KEYTCHCLKDIV_KDIV 0 +#define EP93XX_SYSCON_CHIPID 0x94 +#define EP93XX_SYSCON_CHIPID_ID 0x9213 + +static const char adc_divisors[] = { 16, 4 }; +static const char sclk_divisors[] = { 2, 4 }; +static const char lrclk_divisors[] = { 32, 64, 128 }; + +static const struct clk_parent_data ep93xx_clk_parents[] = { + { .fw_name = "xtali", .name = "xtali" }, + { .index = -1, .name = "pll1" }, + { .index = -1, .name = "pll2" }, +}; + +struct ep93xx_clk { + struct clk_hw hw; + u16 idx; + u16 reg; + u32 mask; + u8 bit_idx; + u8 shift; + u8 width; + u8 num_div; + const char *div; +}; + +struct ep93xx_clk_priv { + spinlock_t lock; + struct device *dev; + void __iomem *base; + struct regmap *map; + struct clk_hw *fixed[16]; + struct ep93xx_clk reg[]; +}; + +static struct ep93xx_clk *ep93xx_clk_from(struct clk_hw *hw) +{ + return container_of(hw, struct ep93xx_clk, hw); +} + +static struct ep93xx_clk_priv *ep93xx_priv_from(struct ep93xx_clk *clk) +{ + return container_of(clk, struct ep93xx_clk_priv, reg[clk->idx]); +} + +static int ep93xx_clk_is_enabled(struct clk_hw *hw) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + u32 val; + + regmap_read(priv->map, clk->reg, &val); + + return !!(val & BIT(clk->bit_idx)); +} + +static int ep93xx_clk_enable(struct clk_hw *hw) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->lock, flags); + + regmap_read(priv->map, clk->reg, &val); + val |= BIT(clk->bit_idx); + + ep93xx_regmap_write(priv->map, clk->reg, val); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void ep93xx_clk_disable(struct clk_hw *hw) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->lock, flags); + + regmap_read(priv->map, clk->reg, &val); + val &= ~BIT(clk->bit_idx); + + ep93xx_regmap_write(priv->map, clk->reg, val); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static const struct clk_ops clk_ep93xx_gate_ops = { + .enable = ep93xx_clk_enable, + .disable = ep93xx_clk_disable, + .is_enabled = ep93xx_clk_is_enabled, +}; + +static int ep93xx_clk_register_gate(struct ep93xx_clk *clk, + const char *name, + struct clk_parent_data *parent_data, + unsigned long flags, + unsigned int reg, + u8 bit_idx) +{ + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + struct clk_init_data init = { }; + + init.name = name; + init.ops = &clk_ep93xx_gate_ops; + init.flags = flags; + init.parent_data = parent_data; + init.num_parents = 1; + + clk->reg = reg; + clk->bit_idx = bit_idx; + clk->hw.init = &init; + + return devm_clk_hw_register(priv->dev, &clk->hw); +} + +static u8 ep93xx_mux_get_parent(struct clk_hw *hw) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + u32 val; + + regmap_read(priv->map, clk->reg, &val); + + val &= EP93XX_SYSCON_CLKDIV_MASK; + + switch (val) { + case EP93XX_SYSCON_CLKDIV_ESEL: + return 1; /* PLL1 */ + case EP93XX_SYSCON_CLKDIV_MASK: + return 2; /* PLL2 */ + default: + break; + }; + + return 0; /* XTALI */ +} + +static int ep93xx_mux_set_parent_lock(struct clk_hw *hw, u8 index) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + unsigned long flags; + u32 val; + + if (index >= ARRAY_SIZE(ep93xx_clk_parents)) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + regmap_read(priv->map, clk->reg, &val); + val &= ~(EP93XX_SYSCON_CLKDIV_MASK); + if (index) { + val |= EP93XX_SYSCON_CLKDIV_ESEL; + val |= (index - 1) ? EP93XX_SYSCON_CLKDIV_PSEL : 0; + } + + ep93xx_regmap_write(priv->map, clk->reg, val); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static bool is_best(unsigned long rate, unsigned long now, + unsigned long best) +{ + return abs(rate - now) < abs(rate - best); +} + +static int ep93xx_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long best_rate = 0, actual_rate, mclk_rate; + unsigned long rate = req->rate; + struct clk_hw *parent_best = NULL; + unsigned long parent_rate_best; + unsigned long parent_rate; + int div, pdiv; + unsigned int i; + + /* + * Try the two pll's and the external clock + * Because the valid predividers are 2, 2.5 and 3, we multiply + * all the clocks by 2 to avoid floating point math. + * + * This is based on the algorithm in the ep93xx raster guide: + * http://be-a-maverick.com/en/pubs/appNote/AN269REV1.pdf + * + */ + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); + + parent_rate = clk_hw_get_rate(parent); + mclk_rate = parent_rate * 2; + + /* Try each predivider value */ + for (pdiv = 4; pdiv <= 6; pdiv++) { + div = DIV_ROUND_CLOSEST(mclk_rate, rate * pdiv); + if (div < 1 || div > 127) + continue; + + actual_rate = DIV_ROUND_CLOSEST(mclk_rate, pdiv * div); + if (is_best(rate, actual_rate, best_rate)) { + best_rate = actual_rate; + parent_rate_best = parent_rate; + parent_best = parent; + } + } + } + + if (!parent_best) + return -EINVAL; + + req->best_parent_rate = parent_rate_best; + req->best_parent_hw = parent_best; + req->rate = best_rate; + + return 0; +} + +static unsigned long ep93xx_ddiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + unsigned int pdiv, div; + u32 val; + + regmap_read(priv->map, clk->reg, &val); + pdiv = ((val >> EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) & 0x03); + div = val & GENMASK(6, 0); + if (!div) + return 0; + + return DIV_ROUND_CLOSEST(parent_rate * 2, (pdiv + 3) * div); +} + +static int ep93xx_ddiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + int pdiv, div, npdiv, ndiv; + unsigned long actual_rate, mclk_rate, rate_err = ULONG_MAX; + u32 val; + + regmap_read(priv->map, clk->reg, &val); + mclk_rate = parent_rate * 2; + + for (pdiv = 4; pdiv <= 6; pdiv++) { + div = DIV_ROUND_CLOSEST(mclk_rate, rate * pdiv); + if (div < 1 || div > 127) + continue; + + actual_rate = DIV_ROUND_CLOSEST(mclk_rate, pdiv * div); + if (abs(actual_rate - rate) < rate_err) { + npdiv = pdiv - 3; + ndiv = div; + rate_err = abs(actual_rate - rate); + } + } + + if (rate_err == ULONG_MAX) + return -EINVAL; + + /* Clear old dividers */ + val &= ~(GENMASK(9, 0) & ~BIT(7)); + + /* Set the new pdiv and div bits for the new clock rate */ + val |= (npdiv << EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | ndiv; + + ep93xx_regmap_write(priv->map, clk->reg, val); + + return 0; +} + +static const struct clk_ops clk_ddiv_ops = { + .enable = ep93xx_clk_enable, + .disable = ep93xx_clk_disable, + .is_enabled = ep93xx_clk_is_enabled, + .get_parent = ep93xx_mux_get_parent, + .set_parent = ep93xx_mux_set_parent_lock, + .determine_rate = ep93xx_mux_determine_rate, + .recalc_rate = ep93xx_ddiv_recalc_rate, + .set_rate = ep93xx_ddiv_set_rate, +}; + +static int clk_hw_register_ddiv(struct ep93xx_clk *clk, + const char *name, + unsigned int reg, + u8 bit_idx) +{ + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + struct clk_init_data init = { }; + + init.name = name; + init.ops = &clk_ddiv_ops; + init.flags = 0; + init.parent_data = ep93xx_clk_parents; + init.num_parents = ARRAY_SIZE(ep93xx_clk_parents); + + clk->reg = reg; + clk->bit_idx = bit_idx; + clk->hw.init = &init; + + return devm_clk_hw_register(priv->dev, &clk->hw); +} + +static unsigned long ep93xx_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + u32 val; + u8 index; + + regmap_read(priv->map, clk->reg, &val); + index = (val & clk->mask) >> clk->shift; + if (index > clk->num_div) + return 0; + + return DIV_ROUND_CLOSEST(parent_rate, clk->div[index]); +} + +static long ep93xx_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + unsigned long best = 0, now; + unsigned int i; + + for (i = 0; i < clk->num_div; i++) { + if ((rate * clk->div[i]) == *parent_rate) + return rate; + + now = DIV_ROUND_CLOSEST(*parent_rate, clk->div[i]); + if (!best || is_best(rate, now, best)) + best = now; + } + + return best; +} + +static int ep93xx_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ep93xx_clk *clk = ep93xx_clk_from(hw); + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + unsigned int i; + u32 val; + + regmap_read(priv->map, clk->reg, &val); + val &= ~clk->mask; + for (i = 0; i < clk->num_div; i++) + if (rate == DIV_ROUND_CLOSEST(parent_rate, clk->div[i])) { + val |= i << clk->shift; + break; + } + + if (i == clk->num_div) + return -EINVAL; + + ep93xx_regmap_write(priv->map, clk->reg, val); + + return 0; +} + +static const struct clk_ops ep93xx_div_ops = { + .enable = ep93xx_clk_enable, + .disable = ep93xx_clk_disable, + .is_enabled = ep93xx_clk_is_enabled, + .recalc_rate = ep93xx_div_recalc_rate, + .round_rate = ep93xx_div_round_rate, + .set_rate = ep93xx_div_set_rate, +}; + +static int clk_hw_register_div(struct ep93xx_clk *clk, + const char *name, + struct clk_parent_data *parent_data, + // const char *parent_name, + unsigned int reg, + u8 enable_bit, + u8 shift, + u8 width, + const char *clk_divisors, + u8 num_div) +{ + struct ep93xx_clk_priv *priv = ep93xx_priv_from(clk); + struct clk_init_data init = { }; + + init.name = name; + init.ops = &ep93xx_div_ops; + init.flags = 0; + init.parent_data = parent_data; + init.num_parents = 1; + + clk->reg = reg; + clk->bit_idx = enable_bit; + clk->mask = GENMASK(shift + width - 1, shift); + clk->shift = shift; + clk->div = clk_divisors; + clk->num_div = num_div; + clk->hw.init = &init; + + return devm_clk_hw_register(priv->dev, &clk->hw); +} + +struct ep93xx_gate { + unsigned int idx; + unsigned int bit; + const char *name; +}; + +static const struct ep93xx_gate ep93xx_uarts[] = { + { EP93XX_CLK_UART1, EP93XX_SYSCON_DEVCFG_U1EN, "uart1" }, + { EP93XX_CLK_UART2, EP93XX_SYSCON_DEVCFG_U2EN, "uart2" }, + { EP93XX_CLK_UART3, EP93XX_SYSCON_DEVCFG_U3EN, "uart3" }, +}; + +static int ep93xx_uart_clock_init(struct ep93xx_clk_priv *priv) +{ + struct clk_parent_data parent_data = { }; + unsigned int i, ret, clk_uart_div; + struct ep93xx_clk *clk; + u32 val; + + regmap_read(priv->map, EP93XX_SYSCON_PWRCNT, &val); + if (val & EP93XX_SYSCON_PWRCNT_UARTBAUD) + clk_uart_div = 1; + else + clk_uart_div = 2; + + priv->fixed[0] = clk_hw_register_fixed_factor(NULL, "uart", "xtali", 0, 1, clk_uart_div); + parent_data.hw = priv->fixed[0]; + + /* parenting uart gate clocks to uart clock */ + for (i = 0; i < ARRAY_SIZE(ep93xx_uarts); i++) { + clk = &priv->reg[ep93xx_uarts[i].idx]; + clk->idx = ep93xx_uarts[i].idx; + ret = ep93xx_clk_register_gate(clk, + ep93xx_uarts[i].name, + &parent_data, CLK_SET_RATE_PARENT, + EP93XX_SYSCON_DEVCFG, + ep93xx_uarts[i].bit); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to register dma clock\n"); + } + + return 0; +} + +static const struct ep93xx_gate ep93xx_dmas[] = { + { EP93XX_CLK_M2M0, EP93XX_SYSCON_PWRCNT_DMA_M2M0, "m2m0" }, + { EP93XX_CLK_M2M1, EP93XX_SYSCON_PWRCNT_DMA_M2M1, "m2m1" }, + { EP93XX_CLK_M2P0, EP93XX_SYSCON_PWRCNT_DMA_M2P0, "m2p0" }, + { EP93XX_CLK_M2P1, EP93XX_SYSCON_PWRCNT_DMA_M2P1, "m2p1" }, + { EP93XX_CLK_M2P2, EP93XX_SYSCON_PWRCNT_DMA_M2P2, "m2p2" }, + { EP93XX_CLK_M2P3, EP93XX_SYSCON_PWRCNT_DMA_M2P3, "m2p3" }, + { EP93XX_CLK_M2P4, EP93XX_SYSCON_PWRCNT_DMA_M2P4, "m2p4" }, + { EP93XX_CLK_M2P5, EP93XX_SYSCON_PWRCNT_DMA_M2P5, "m2p5" }, + { EP93XX_CLK_M2P6, EP93XX_SYSCON_PWRCNT_DMA_M2P6, "m2p6" }, + { EP93XX_CLK_M2P7, EP93XX_SYSCON_PWRCNT_DMA_M2P7, "m2p7" }, + { EP93XX_CLK_M2P8, EP93XX_SYSCON_PWRCNT_DMA_M2P8, "m2p8" }, + { EP93XX_CLK_M2P9, EP93XX_SYSCON_PWRCNT_DMA_M2P9, "m2p9" }, +}; + +static int ep93xx_dma_clock_init(struct ep93xx_clk_priv *priv) +{ + unsigned int i, idx; + + for (i = 0; i < ARRAY_SIZE(ep93xx_dmas); i++) { + idx = ep93xx_dmas[i].idx - EP93XX_CLK_UART; + priv->fixed[idx] = devm_clk_hw_register_gate(priv->dev, + ep93xx_dmas[i].name, + "hclk", 0, + priv->base + EP93XX_SYSCON_PWRCNT, + ep93xx_dmas[i].bit, + 0, + &priv->lock); + if (IS_ERR(priv->fixed[idx])) + return PTR_ERR(priv->fixed[idx]); + } + + return 0; +} + +static struct clk_hw *ep93xx_clk_get(struct of_phandle_args *clkspec, void *data) +{ + struct ep93xx_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < EP93XX_CLK_UART) + return &priv->reg[idx].hw; + + if (idx < EP93XX_CLK_END) + return priv->fixed[idx - EP93XX_CLK_UART]; + + return ERR_PTR(-EINVAL); +} + +static const struct soc_device_attribute ep93xx_soc_table[] = { + { .revision = "E2", .data = (void *)1 }, + { /* sentinel */ } +}; + +static int ep93xx_clk_probe(struct platform_device *pdev) +{ + struct clk_parent_data xtali = { .name = "xtali" }; + const struct soc_device_attribute *match; + struct clk_parent_data pdata = {}; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct ep93xx_clk_priv *priv; + struct device_node *parent; + struct ep93xx_clk *clk; + unsigned long clk_spi_div; + int ret; + u32 value; + + priv = devm_kzalloc(dev, struct_size(priv, reg, EP93XX_CLK_UART), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + priv->dev = dev; + parent = of_get_parent(np); + if (!parent) + return dev_err_probe(dev, -EINVAL, "no syscon parent for clk node\n"); + + priv->map = syscon_node_to_regmap(parent); + if (IS_ERR(priv->map)) { + of_node_put(parent); + return dev_err_probe(dev, -EINVAL, "no syscon regmap\n"); + } + + priv->base = devm_of_iomap(dev, parent, 0, NULL); + of_node_put(parent); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = ep93xx_uart_clock_init(priv); + if (ret) + return ret; + + ret = ep93xx_dma_clock_init(priv); + if (ret) + return ret; + + /* + * EP93xx SSP clock rate was doubled in version E2. For more information + * see: + * http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf + */ + clk_spi_div = 2; + match = soc_device_match(ep93xx_soc_table); + if (match) + clk_spi_div = (unsigned long)match->data; + + priv->fixed[1] = devm_clk_hw_register_fixed_factor(dev, "ep93xx-spi.0", "xtali", + 0, 1, clk_spi_div); + if (IS_ERR(priv->fixed[1])) + return PTR_ERR(priv->fixed[1]); + + /* PWM clock */ + priv->fixed[2] = devm_clk_hw_register_fixed_factor(dev, "pwm_clk", "xtali", 0, 1, 1); + if (IS_ERR(priv->fixed[2])) + return PTR_ERR(priv->fixed[2]); + + /* USB clock */ + priv->fixed[3] = devm_clk_hw_register_gate(priv->dev, + "ohci-platform", + "usb_clk", 0, + priv->base + EP93XX_SYSCON_PWRCNT, + EP93XX_SYSCON_PWRCNT_USH_EN, + 0, + &priv->lock); + if (IS_ERR(priv->fixed[3])) + return PTR_ERR(priv->fixed[3]); + + /* touchscreen/adc clock */ + clk = &priv->reg[EP93XX_CLK_ADC]; + clk->idx = EP93XX_CLK_ADC; + ret = clk_hw_register_div(clk, "ep93xx-adc", &xtali, + EP93XX_SYSCON_KEYTCHCLKDIV, + EP93XX_SYSCON_KEYTCHCLKDIV_TSEN, + EP93XX_SYSCON_KEYTCHCLKDIV_ADIV, + 1, + adc_divisors, + ARRAY_SIZE(adc_divisors)); + + + /* keypad clock */ + clk = &priv->reg[EP93XX_CLK_KEYPAD]; + clk->idx = EP93XX_CLK_KEYPAD; + ret = clk_hw_register_div(clk, "ep93xx-keypad", &xtali, + EP93XX_SYSCON_KEYTCHCLKDIV, + EP93XX_SYSCON_KEYTCHCLKDIV_KEN, + EP93XX_SYSCON_KEYTCHCLKDIV_KDIV, + 1, + adc_divisors, + ARRAY_SIZE(adc_divisors)); + + /* + * On reset PDIV and VDIV is set to zero, while PDIV zero + * means clock disable, VDIV shouldn't be zero. + * So i set both dividers to minimum. + */ + /* ENA - Enable CLK divider. */ + /* PDIV - 00 - Disable clock */ + /* VDIV - at least 2 */ + /* Check and enable video clk registers */ + regmap_read(priv->map, EP93XX_SYSCON_VIDCLKDIV, &value); + value |= BIT(EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | 2; + ep93xx_regmap_write(priv->map, EP93XX_SYSCON_VIDCLKDIV, value); + + /* check and enable i2s clk registers */ + regmap_read(priv->map, EP93XX_SYSCON_I2SCLKDIV, &value); + value |= BIT(EP93XX_SYSCON_CLKDIV_PDIV_SHIFT) | 2; + /* + * Override the SAI_MSTR_CLK_CFG from the I2S block and use the + * I2SClkDiv Register settings. LRCLK transitions on the falling SCLK + * edge. + */ + value |= EP93XX_SYSCON_I2SCLKDIV_ORIDE | EP93XX_SYSCON_I2SCLKDIV_SPOL; + ep93xx_regmap_write(priv->map, EP93XX_SYSCON_I2SCLKDIV, value); + + /* video clk */ + clk = &priv->reg[EP93XX_CLK_VIDEO]; + clk->idx = EP93XX_CLK_VIDEO; + ret = clk_hw_register_ddiv(clk, "ep93xx-fb", + EP93XX_SYSCON_VIDCLKDIV, + EP93XX_SYSCON_CLKDIV_ENABLE); + + /* i2s clk */ + clk = &priv->reg[EP93XX_CLK_I2S_MCLK]; + clk->idx = EP93XX_CLK_I2S_MCLK; + ret = clk_hw_register_ddiv(clk, "mclk", + EP93XX_SYSCON_I2SCLKDIV, + EP93XX_SYSCON_CLKDIV_ENABLE); + + /* i2s sclk */ + clk = &priv->reg[EP93XX_CLK_I2S_SCLK]; + clk->idx = EP93XX_CLK_I2S_SCLK; + pdata.hw = &priv->reg[EP93XX_CLK_I2S_MCLK].hw; + ret = clk_hw_register_div(clk, "sclk", &pdata, + EP93XX_SYSCON_I2SCLKDIV, + EP93XX_SYSCON_I2SCLKDIV_SENA, + 16, /* EP93XX_I2SCLKDIV_SDIV_SHIFT */ + 1, /* EP93XX_I2SCLKDIV_SDIV_WIDTH */ + sclk_divisors, + ARRAY_SIZE(sclk_divisors)); + + /* i2s lrclk */ + clk = &priv->reg[EP93XX_CLK_I2S_LRCLK]; + clk->idx = EP93XX_CLK_I2S_LRCLK; + pdata.hw = &priv->reg[EP93XX_CLK_I2S_SCLK].hw; + ret = clk_hw_register_div(clk, "lrclk", &pdata, + EP93XX_SYSCON_I2SCLKDIV, + EP93XX_SYSCON_I2SCLKDIV_SENA, + 17, /* EP93XX_I2SCLKDIV_LRDIV32_SHIFT */ + 2, /* EP93XX_I2SCLKDIV_LRDIV32_WIDTH */ + lrclk_divisors, + ARRAY_SIZE(lrclk_divisors)); + + /* IrDa clk uses same pattern but no init code presents in original clock driver */ + return devm_of_clk_add_hw_provider(priv->dev, ep93xx_clk_get, priv); +} + +static const struct of_device_id ep93xx_clk_dt_ids[] = { + { .compatible = "cirrus,ep9301-clk" }, + { /* sentinel */ } +}; + +static struct platform_driver ep93xx_clk_driver = { + .driver = { + .name = "ep93xx-clk", + .of_match_table = ep93xx_clk_dt_ids, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver_probe(ep93xx_clk_driver, ep93xx_clk_probe);