From mboxrd@z Thu Jan 1 00:00:00 1970 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 phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 9628EC4167B for ; Mon, 19 Dec 2022 08:47:52 +0000 (UTC) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 17297853B9; Mon, 19 Dec 2022 09:47:33 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=microchip.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=microchip.com header.i=@microchip.com header.b="o9/XlMVd"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id C41B585016; Mon, 19 Dec 2022 09:47:22 +0100 (CET) Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) (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 4769884FBF for ; Mon, 19 Dec 2022 09:47:13 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=pass (p=quarantine dis=none) header.from=microchip.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=Sergiu.Moga@microchip.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1671439634; x=1702975634; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=bbdgjePzalB9FLonxKFCLQ2CNK8ifVF9wVGjIIFpvus=; b=o9/XlMVdINzeviXsXBdq9dMPTLBg6zBa1R8DhZL2nG4OqR4if2cLry9z 1dmMouYHO5AqI4XAoA0t4mOx55dtYmq+XF0MvlaLpK+fZD1UBYwQAls9v pNhvTlTSgokkA+HIrozcfuBB/VJtnxxtUEr+I2Cc/Lh8egfrjXvt4bNyR u3WPso2Lnu/RWWYA0jjIY0id82FEQaLEIN0Gi3U+qATTPEYEB0M7WxO97 rHolb/AmgJmuUFomjNlASJa0reHNWi0j/6Y1vUXxAx9V8kZ7VwUXWOf+x nUNl1TgPT5uF7Nc5QgzDlXZjcUvRudS/agcTMGLvhje7UqCbSNswqpKCu A==; X-IronPort-AV: E=Sophos;i="5.96,255,1665471600"; d="scan'208";a="188775654" Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa4.microchip.iphmx.com with ESMTP/TLS/AES256-SHA256; 19 Dec 2022 01:47:10 -0700 Received: from chn-vm-ex03.mchp-main.com (10.10.85.151) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.16; Mon, 19 Dec 2022 01:47:09 -0700 Received: from ROB-ULT-M68701.microchip.com (10.10.115.15) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server id 15.1.2507.16 via Frontend Transport; Mon, 19 Dec 2022 01:47:03 -0700 From: Sergiu Moga To: , , , , , , , , , , , , , , , , , , , , , , , , , CC: Subject: [PATCH v4 02/19] clk: at91: Add support for sam9x60 USB clock Date: Mon, 19 Dec 2022 10:46:10 +0200 Message-ID: <20221219084626.34606-3-sergiu.moga@microchip.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20221219084626.34606-1-sergiu.moga@microchip.com> References: <20221219084626.34606-1-sergiu.moga@microchip.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 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.103.6 at phobos.denx.de X-Virus-Status: Clean Implement sam9x60 USB clock driver. This clock has three parents: PLLA, UPLL and MAINXTAL. The driver is aware of the three possible parents with the help of the two mux tables provied to the driver during the registration of the clock. Signed-off-by: Sergiu Moga --- v1 -> v4: - No change drivers/clk/at91/Kconfig | 7 ++ drivers/clk/at91/Makefile | 1 + drivers/clk/at91/clk-sam9x60-usb.c | 156 +++++++++++++++++++++++++++++ drivers/clk/at91/pmc.h | 11 ++ 4 files changed, 175 insertions(+) create mode 100644 drivers/clk/at91/clk-sam9x60-usb.c diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig index 4abc8026b4..4563892647 100644 --- a/drivers/clk/at91/Kconfig +++ b/drivers/clk/at91/Kconfig @@ -61,3 +61,10 @@ config AT91_SAM9X60_PLL help This option is used to enable the AT91 SAM9X60's PLL clock driver. + +config AT91_SAM9X60_USB + bool "USB Clock support for SAM9X60 SoCs" + depends on CLK_AT91 + help + This option is used to enable the AT91 SAM9X60's USB clock + driver. diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 580b406d7b..e53dcb4ca7 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -9,6 +9,7 @@ obj-y += clk-peripheral.o obj-$(CONFIG_AT91_GENERIC_CLK) += clk-generic.o obj-$(CONFIG_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o +obj-$(CONFIG_AT91_SAM9X60_USB) += clk-sam9x60-usb.o obj-$(CONFIG_SAMA7G5) += sama7g5.o obj-$(CONFIG_SAM9X60) += sam9x60.o else diff --git a/drivers/clk/at91/clk-sam9x60-usb.c b/drivers/clk/at91/clk-sam9x60-usb.c new file mode 100644 index 0000000000..0ccdc1494d --- /dev/null +++ b/drivers/clk/at91/clk-sam9x60-usb.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SAM9X60's USB Clock support. + * + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Sergiu Moga + */ + +#include +#include +#include + +#include "pmc.h" + +#define UBOOT_DM_CLK_AT91_SAM9X60_USB "at91-sam9x60-usb-clk" + +struct sam9x60_usb { + const struct clk_usbck_layout *layout; + void __iomem *base; + struct clk clk; + const u32 *clk_mux_table; + const u32 *mux_table; + const char * const *parent_names; + u32 num_parents; + u8 id; +}; + +#define to_sam9x60_usb(_clk) container_of(_clk, struct sam9x60_usb, clk) +#define USB_MAX_DIV 15 + +static int sam9x60_usb_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct sam9x60_usb *usb = to_sam9x60_usb(clk); + u32 val, index; + + index = at91_clk_mux_val_to_index(usb->clk_mux_table, usb->num_parents, + parent->id); + if (index < 0) + return index; + + index = at91_clk_mux_index_to_val(usb->mux_table, usb->num_parents, + index); + if (index < 0) + return index; + + pmc_read(usb->base, usb->layout->offset, &val); + val &= ~usb->layout->usbs_mask; + val |= index << (ffs(usb->layout->usbs_mask - 1)); + pmc_write(usb->base, usb->layout->offset, val); + + return 0; +} + +static ulong sam9x60_usb_clk_get_rate(struct clk *clk) +{ + struct sam9x60_usb *usb = to_sam9x60_usb(clk); + ulong parent_rate = clk_get_parent_rate(clk); + u32 val, usbdiv; + + if (!parent_rate) + return 0; + + pmc_read(usb->base, usb->layout->offset, &val); + usbdiv = (val & usb->layout->usbdiv_mask) >> + (ffs(usb->layout->usbdiv_mask) - 1); + return parent_rate / (usbdiv + 1); +} + +static ulong sam9x60_usb_clk_set_rate(struct clk *clk, ulong rate) +{ + struct sam9x60_usb *usb = to_sam9x60_usb(clk); + ulong parent_rate = clk_get_parent_rate(clk); + u32 usbdiv, val; + + if (!parent_rate) + return 0; + + usbdiv = DIV_ROUND_CLOSEST(parent_rate, rate); + if (usbdiv > USB_MAX_DIV + 1 || !usbdiv) + return 0; + + pmc_read(usb->base, usb->layout->offset, &val); + val &= usb->layout->usbdiv_mask; + val |= (usbdiv - 1) << (ffs(usb->layout->usbdiv_mask) - 1); + pmc_write(usb->base, usb->layout->offset, val); + + return parent_rate / usbdiv; +} + +static const struct clk_ops sam9x60_usb_ops = { + .set_parent = sam9x60_usb_clk_set_parent, + .set_rate = sam9x60_usb_clk_set_rate, + .get_rate = sam9x60_usb_clk_get_rate, +}; + +struct clk * +sam9x60_clk_register_usb(void __iomem *base, const char *name, + const char * const *parent_names, u8 num_parents, + const struct clk_usbck_layout *usbck_layout, + const u32 *clk_mux_table, const u32 *mux_table, u8 id) +{ + struct sam9x60_usb *usb; + struct clk *clk; + int ret; + u32 val, index; + + if (!base || !name || !parent_names || !num_parents || + !clk_mux_table || !mux_table) + return ERR_PTR(-EINVAL); + + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) + return ERR_PTR(-ENOMEM); + + usb->id = id; + usb->base = base; + usb->layout = usbck_layout; + usb->parent_names = parent_names; + usb->num_parents = num_parents; + usb->clk_mux_table = clk_mux_table; + usb->mux_table = mux_table; + + clk = &usb->clk; + clk->flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + pmc_read(usb->base, usb->layout->offset, &val); + + val = (val & usb->layout->usbs_mask) >> + (ffs(usb->layout->usbs_mask) - 1); + + index = at91_clk_mux_val_to_index(usb->mux_table, usb->num_parents, + val); + + if (index < 0) { + kfree(usb); + return ERR_PTR(index); + } + + ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X60_USB, name, + parent_names[index]); + if (ret) { + kfree(usb); + clk = ERR_PTR(ret); + } + + return clk; +} + +U_BOOT_DRIVER(at91_sam9x60_usb_clk) = { + .name = UBOOT_DM_CLK_AT91_SAM9X60_USB, + .id = UCLASS_CLK, + .ops = &sam9x60_usb_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 2b4dd9a3d9..17793b8802 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -71,6 +71,12 @@ struct clk_pcr_layout { u32 pid_mask; }; +struct clk_usbck_layout { + u32 offset; + u32 usbs_mask; + u32 usbdiv_mask; +}; + extern const struct clk_programmable_layout at91rm9200_programmable_layout; extern const struct clk_programmable_layout at91sam9g45_programmable_layout; extern const struct clk_programmable_layout at91sam9x5_programmable_layout; @@ -87,6 +93,11 @@ struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name, const char * const *parent_names, int num_parents, const u32 *mux_table, int type); struct clk * +sam9x60_clk_register_usb(void __iomem *base, const char *name, + const char * const *parent_names, u8 num_parents, + const struct clk_usbck_layout *usbck_layout, + const u32 *clk_mux_table, const u32 *mux_table, u8 id); +struct clk * sam9x60_clk_register_div_pll(void __iomem *base, const char *name, const char *parent_name, u8 id, const struct clk_pll_characteristics *characteristics, -- 2.34.1