[PATCH v2 02/19] clk: at91: Add support for sam9x60 USB clock
Claudiu.Beznea at microchip.com
Claudiu.Beznea at microchip.com
Mon Dec 12 11:39:33 CET 2022
On 08.12.2022 11:47, Sergiu Moga wrote:
> 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 <sergiu.moga at microchip.com>
> ---
>
>
>
> v1 -> v2:
> - 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 <sergiu.moga at microchip.com>
> + */
> +
> +#include <clk-uclass.h>
> +#include <dm.h>
> +#include <linux/clk-provider.h>
> +
> +#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));
You can use FIELD_PREP() here.
> + 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);
And FIELD_GET() here.
> + 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);
FIELD_PREP()
> + 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);
FIELD_GET()
> +
> + 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,
More information about the U-Boot
mailing list