On 23.12.2022 14:33, 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.m...@microchip.com>

Reviewed-by: Claudiu Beznea <claudiu.bez...@microchip.com>



> ---
>  drivers/clk/at91/Kconfig           |   7 ++
>  drivers/clk/at91/Makefile          |   1 +
>  drivers/clk/at91/clk-sam9x60-usb.c | 157 +++++++++++++++++++++++++++++
>  drivers/clk/at91/pmc.h             |  11 ++
>  4 files changed, 176 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..798fa9eb3c
> --- /dev/null
> +++ b/drivers/clk/at91/clk-sam9x60-usb.c
> @@ -0,0 +1,157 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * SAM9X60's USB Clock support.
> + *
> + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
> + *
> + * Author: Sergiu Moga <sergiu.m...@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);
> +     int index;
> +     u32 val;
> +
> +     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, index;
> +     u32 val;
> +
> +     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,

Reply via email to