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,