diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/at91/Kconfig | 7 | ||||
-rw-r--r-- | drivers/clk/at91/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/at91/clk-sam9x60-usb.c | 157 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.c | 42 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.h | 27 | ||||
-rw-r--r-- | drivers/clk/at91/sam9x60.c | 63 | ||||
-rw-r--r-- | drivers/clk/at91/sama7g5.c | 48 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx8mn.c | 22 | ||||
-rw-r--r-- | drivers/clk/nuvoton/clk_npcm7xx.c | 2 |
9 files changed, 315 insertions, 54 deletions
diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig index 4abc802..4563892 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 580b406..e53dcb4 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 0000000..798fa9e --- /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.moga@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.c b/drivers/clk/at91/pmc.c index 2708925..87d2069 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -120,3 +120,45 @@ int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index) return table[index]; } + +int at91_clk_setup(const struct pmc_clk_setup *setup, int size) +{ + struct clk *c, *parent; + int i, ret; + + if (!size) + return 0; + + if (!setup) + return -EINVAL; + + for (i = 0; i < size; i++) { + ret = clk_get_by_id(setup[i].cid, &c); + if (ret) + return ret; + + if (setup[i].pid) { + ret = clk_get_by_id(setup[i].pid, &parent); + if (ret) + return ret; + + ret = clk_set_parent(c, parent); + if (ret) + return ret; + + if (setup[i].prate) { + ret = clk_set_rate(parent, setup[i].prate); + if (ret < 0) + return ret; + } + } + + if (setup[i].rate) { + ret = clk_set_rate(c, setup[i].rate); + if (ret < 0) + return ret; + } + } + + return 0; +} diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 2b4dd9a..ff46452 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -71,6 +71,26 @@ struct clk_pcr_layout { u32 pid_mask; }; +struct clk_usbck_layout { + u32 offset; + u32 usbs_mask; + u32 usbdiv_mask; +}; + +/** + * Clock setup description + * @cid: clock id corresponding to clock subsystem + * @pid: parent clock id corresponding to clock subsystem + * @rate: clock rate + * @prate: parent rate + */ +struct pmc_clk_setup { + unsigned int cid; + unsigned int pid; + unsigned long rate; + unsigned long prate; +}; + 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 +107,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, @@ -149,4 +174,6 @@ void pmc_write(void __iomem *base, unsigned int off, unsigned int val); void pmc_update_bits(void __iomem *base, unsigned int off, unsigned int mask, unsigned int bits); +int at91_clk_setup(const struct pmc_clk_setup *setup, int size); + #endif diff --git a/drivers/clk/at91/sam9x60.c b/drivers/clk/at91/sam9x60.c index 6b5486c..e2f7244 100644 --- a/drivers/clk/at91/sam9x60.c +++ b/drivers/clk/at91/sam9x60.c @@ -76,6 +76,8 @@ enum pmc_clk_ids { ID_QSPI = 18, ID_MCK_PRES = 19, + ID_USBCK = 20, + ID_UHPCK = 21, ID_MAX, }; @@ -99,6 +101,7 @@ static const char *clk_names[] = { [ID_PLL_A_DIV] = "plla_divpmcck", [ID_MCK_PRES] = "mck_pres", [ID_MCK_DIV] = "mck_div", + [ID_USBCK] = "usbck", }; /* Fractional PLL output range. */ @@ -171,6 +174,13 @@ static const struct clk_pcr_layout pcr_layout = { .pid_mask = GENMASK(6, 0), }; +/* USB clock layout */ +static const struct clk_usbck_layout usbck_layout = { + .offset = 0x38, + .usbs_mask = GENMASK(1, 0), + .usbdiv_mask = GENMASK(11, 8), +}; + /** * PLL clocks description * @n: clock name @@ -266,6 +276,7 @@ static const struct { u8 cid; } sam9x60_systemck[] = { { .n = "ddrck", .p = "mck_div", .id = 2, .cid = ID_DDR, }, + { .n = "uhpck", .p = "usbck", .id = 6, .cid = ID_UHPCK }, { .n = "pck0", .p = "prog0", .id = 8, .cid = ID_PCK0, }, { .n = "pck1", .p = "prog1", .id = 9, .cid = ID_PCK1, }, { .n = "qspick", .p = "mck_div", .id = 19, .cid = ID_QSPI, }, @@ -367,6 +378,31 @@ static const struct { { .n = "dbgu_gclk", .id = 47, }, }; +/** + * Clock setup description + * @cid: clock id corresponding to clock subsystem + * @pid: parent clock id corresponding to clock subsystem + * @rate: clock rate + * @prate: parent rate + */ +static const struct pmc_clk_setup sam9x60_clk_setup[] = { + { + .cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_U_FRAC), + .rate = 960000000, + }, + + { + .cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_U_DIV), + .rate = 480000000, + }, + + { + .cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_USBCK), + .pid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_U_DIV), + .rate = 48000000, + }, +}; + #define prepare_mux_table(_allocs, _index, _dst, _src, _num, _label) \ do { \ int _i; \ @@ -543,6 +579,28 @@ static int sam9x60_clk_probe(struct udevice *dev) } clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK_DIV), c); + /* Register usbck. */ + p[0] = clk_names[ID_PLL_A_DIV]; + p[1] = clk_names[ID_PLL_U_DIV]; + p[2] = clk_names[ID_MAIN_XTAL]; + m[0] = 0; + m[1] = 1; + m[2] = 2; + cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_A_DIV); + cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_U_DIV); + cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_XTAL); + prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, + 3, fail); + prepare_mux_table(muxallocs, muxallocindex, tmpmux, m, 3, fail); + c = sam9x60_clk_register_usb(base, clk_names[ID_USBCK], p, 3, + &usbck_layout, tmpclkmux, tmpmux, + ID_USBCK); + if (IS_ERR(c)) { + ret = PTR_ERR(c); + goto fail; + } + clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_USBCK), c); + /* Register programmable clocks. */ p[0] = clk_names[ID_MD_SLCK]; p[1] = clk_names[ID_TD_SLCK]; @@ -635,6 +693,11 @@ static int sam9x60_clk_probe(struct udevice *dev) clk_dm(AT91_TO_CLK_ID(PMC_TYPE_GCK, sam9x60_gck[i].id), c); } + /* Setup clocks. */ + ret = at91_clk_setup(sam9x60_clk_setup, ARRAY_SIZE(sam9x60_clk_setup)); + if (ret) + goto fail; + return 0; fail: diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c index d1ec3c8..8bd9c14 100644 --- a/drivers/clk/at91/sama7g5.c +++ b/drivers/clk/at91/sama7g5.c @@ -1070,19 +1070,8 @@ static const struct { }, }; -/** - * Clock setup description - * @cid: clock id corresponding to clock subsystem - * @pid: parent clock id corresponding to clock subsystem - * @rate: clock rate - * @prate: parent rate - */ -static const struct pmc_clk_setup { - unsigned int cid; - unsigned int pid; - unsigned long rate; - unsigned long prate; -} sama7g5_clk_setup[] = { +/* Clock setup description */ +static const struct pmc_clk_setup sama7g5_clk_setup[] = { { .cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_FRAC), .rate = 625000000, @@ -1119,7 +1108,7 @@ static int sama7g5_clk_probe(struct udevice *dev) unsigned int *muxallocs[SAMA7G5_MAX_MUX_ALLOCS]; const char *p[10]; unsigned int cm[10], m[10], *tmpclkmux, *tmpmux; - struct clk clk, *c, *parent; + struct clk clk, *c; bool main_osc_bypass; int ret, muxallocindex = 0, clkmuxallocindex = 0, i, j; @@ -1353,34 +1342,9 @@ static int sama7g5_clk_probe(struct udevice *dev) } /* Setup clocks. */ - for (i = 0; i < ARRAY_SIZE(sama7g5_clk_setup); i++) { - ret = clk_get_by_id(sama7g5_clk_setup[i].cid, &c); - if (ret) - goto fail; - - if (sama7g5_clk_setup[i].pid) { - ret = clk_get_by_id(sama7g5_clk_setup[i].pid, &parent); - if (ret) - goto fail; - - ret = clk_set_parent(c, parent); - if (ret) - goto fail; - - if (sama7g5_clk_setup[i].prate) { - ret = clk_set_rate(parent, - sama7g5_clk_setup[i].prate); - if (ret < 0) - goto fail; - } - } - - if (sama7g5_clk_setup[i].rate) { - ret = clk_set_rate(c, sama7g5_clk_setup[i].rate); - if (ret < 0) - goto fail; - } - } + ret = at91_clk_setup(sama7g5_clk_setup, ARRAY_SIZE(sama7g5_clk_setup)); + if (ret) + goto fail; return 0; diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index 35e0d93..692823e 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -28,20 +28,20 @@ static const char *imx8mn_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; static const char *imx8mn_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", - "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; + "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", }; static const char *imx8mn_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", - "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; + "sys_pll2_200m", "audio_pll1_out", "video_pll_out", "sys_pll3_out", }; #ifndef CONFIG_SPL_BUILD static const char *imx8mn_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", - "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; + "sys_pll1_160m", "audio_pll1_out", "video_pll_out", "clk_ext4", }; static const char *imx8mn_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", - "clk_ext3", "clk_ext4", "video_pll1_out", }; + "clk_ext3", "clk_ext4", "video_pll_out", }; static const char *imx8mn_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", - "sys_pll2_500m", "video_pll1_out", "audio_pll2_out", }; + "sys_pll2_500m", "audio_pll1_out", "video_pll_out", "audio_pll2_out", }; #endif static const char *imx8mn_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", @@ -72,18 +72,18 @@ static const char *imx8mn_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1 #endif static const char *imx8mn_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", - "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; static const char *imx8mn_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", - "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; static const char *imx8mn_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", - "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; static const char *imx8mn_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", - "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char *imx8mn_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out", +static const char *imx8mn_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", "m7_alt_pll", "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", }; static const char *imx8mn_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", @@ -94,7 +94,7 @@ static const char *imx8mn_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_ static const char * const imx8mn_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", "sys_pll1_400m", "audio_pll2_out", "sys_pll3_out", - "sys_pll2_250m", "video_pll1_out", }; + "sys_pll2_250m", "video_pll_out", }; static const char * const imx8mn_usb_core_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", diff --git a/drivers/clk/nuvoton/clk_npcm7xx.c b/drivers/clk/nuvoton/clk_npcm7xx.c index a12aaa2..b23dd37 100644 --- a/drivers/clk/nuvoton/clk_npcm7xx.c +++ b/drivers/clk/nuvoton/clk_npcm7xx.c @@ -25,7 +25,7 @@ static const struct parent_data apb_parent[] = {{NPCM7XX_CLK_AHB, 0}}; static struct npcm_clk_pll npcm7xx_clk_plls[] = { {NPCM7XX_CLK_PLL0, NPCM7XX_CLK_REFCLK, PLLCON0, 0}, - {NPCM7XX_CLK_PLL1, NPCM7XX_CLK_REFCLK, PLLCON1, 0}, + {NPCM7XX_CLK_PLL1, NPCM7XX_CLK_REFCLK, PLLCON1, POST_DIV2}, {NPCM7XX_CLK_PLL2, NPCM7XX_CLK_REFCLK, PLLCON2, 0}, {NPCM7XX_CLK_PLL2DIV2, NPCM7XX_CLK_REFCLK, PLLCON2, POST_DIV2} }; |