Merge tag 'u-boot-at91-2023.07-a' of https://source.denx.de/u-boot/custodians/u-boot-at91 into next
First set of u-boot-at91 features for the 2023.07 cycle: This feature set includes the clock changes required for sam9x60 SoC to support USB host.
This commit is contained in:
@@ -58,6 +58,7 @@ CONFIG_CLK_CCF=y
|
||||
CONFIG_CLK_AT91=y
|
||||
CONFIG_AT91_GENERIC_CLK=y
|
||||
CONFIG_AT91_SAM9X60_PLL=y
|
||||
CONFIG_AT91_SAM9X60_USB=y
|
||||
CONFIG_CPU=y
|
||||
CONFIG_AT91_GPIO=y
|
||||
CONFIG_DM_I2C=y
|
||||
|
||||
@@ -54,6 +54,7 @@ CONFIG_CLK_CCF=y
|
||||
CONFIG_CLK_AT91=y
|
||||
CONFIG_AT91_GENERIC_CLK=y
|
||||
CONFIG_AT91_SAM9X60_PLL=y
|
||||
CONFIG_AT91_SAM9X60_USB=y
|
||||
CONFIG_CPU=y
|
||||
CONFIG_AT91_GPIO=y
|
||||
CONFIG_DM_I2C=y
|
||||
|
||||
@@ -59,6 +59,7 @@ CONFIG_CLK_CCF=y
|
||||
CONFIG_CLK_AT91=y
|
||||
CONFIG_AT91_GENERIC_CLK=y
|
||||
CONFIG_AT91_SAM9X60_PLL=y
|
||||
CONFIG_AT91_SAM9X60_USB=y
|
||||
CONFIG_CPU=y
|
||||
CONFIG_AT91_GPIO=y
|
||||
CONFIG_DM_I2C=y
|
||||
|
||||
@@ -61,6 +61,7 @@ CONFIG_CLK_CCF=y
|
||||
CONFIG_CLK_AT91=y
|
||||
CONFIG_AT91_GENERIC_CLK=y
|
||||
CONFIG_AT91_SAM9X60_PLL=y
|
||||
CONFIG_AT91_SAM9X60_USB=y
|
||||
CONFIG_CPU=y
|
||||
CONFIG_AT91_GPIO=y
|
||||
CONFIG_DM_I2C=y
|
||||
|
||||
@@ -61,6 +61,7 @@ CONFIG_CLK_CCF=y
|
||||
CONFIG_CLK_AT91=y
|
||||
CONFIG_AT91_GENERIC_CLK=y
|
||||
CONFIG_AT91_SAM9X60_PLL=y
|
||||
CONFIG_AT91_SAM9X60_USB=y
|
||||
CONFIG_CPU=y
|
||||
CONFIG_AT91_GPIO=y
|
||||
CONFIG_DM_I2C=y
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user