- Fix and improve microchip's clock driver to allow sync'ing DTS with linux
- Improve the help message in "SBI_V02" Kconfig
- Improve DTS property "isa-string" parsing rule
This commit is contained in:
Tom Rini 2022-11-16 11:08:28 -05:00
commit d78cccb1ac
12 changed files with 249 additions and 83 deletions

View File

@ -257,16 +257,16 @@ config SBI_V01
deprecated in future once legacy M-mode software are no longer in use.
config SBI_V02
bool "SBI v0.2 support"
bool "SBI v0.2 or later support"
depends on SBI
help
This config allows kernel to use SBI v0.2 APIs. SBI v0.2 is more
scalable and extendable to handle future needs for RISC-V supervisor
interfaces. For example, with SBI v0.2 HSM extension, only a single
hart need to boot and enter operating system. The booting hart can
bring up secondary harts one by one afterwards.
The SBI specification introduced the concept of extensions in version
v0.2. With this configuration option U-Boot can detect and use SBI
extensions. With the HSM extension introduced in SBI 0.2, only a
single hart needs to boot and enter the operating system. The booting
hart can bring up secondary harts one by one afterwards.
Choose this option if OpenSBI v0.7 or above release is used together
Choose this option if OpenSBI release v0.7 or above is used together
with U-Boot.
endchoice

View File

@ -36,6 +36,7 @@ static inline bool supports_extension(char ext)
#ifdef CONFIG_CPU
struct udevice *dev;
char desc[32];
int i;
uclass_find_first_device(UCLASS_CPU, &dev);
if (!dev) {
@ -43,9 +44,16 @@ static inline bool supports_extension(char ext)
return false;
}
if (!cpu_get_desc(dev, desc, sizeof(desc))) {
/* skip the first 4 characters (rv32|rv64) */
if (strchr(desc + 4, ext))
return true;
/*
* skip the first 4 characters (rv32|rv64) and
* check until underscore
*/
for (i = 4; i < sizeof(desc); i++) {
if (desc[i] == '_' || desc[i] == '\0')
break;
if (desc[i] == ext)
return true;
}
}
return false;

View File

@ -54,6 +54,10 @@
};
};
&refclk {
clock-frequency = <125000000>;
};
&uart1 {
status = "okay";
};

View File

@ -170,6 +170,11 @@
};
};
refclk: refclk {
compatible = "fixed-clock";
#clock-cells = <0>;
};
soc {
#address-cells = <2>;
#size-cells = <2>;
@ -225,16 +230,9 @@
&cpu4_intc HART_INT_M_EXT &cpu4_intc HART_INT_S_EXT>;
};
refclk: refclk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <600000000>;
clock-output-names = "msspllclk";
};
clkcfg: clkcfg@20002000 {
compatible = "microchip,mpfs-clkcfg";
reg = <0x0 0x20002000 0x0 0x1000>;
reg = <0x0 0x20002000 0x0 0x1000>, <0x0 0x3E001000 0x0 0x1000>;
reg-names = "mss_sysreg";
clocks = <&refclk>;
#clock-cells = <1>;

View File

@ -21,3 +21,5 @@ CONFIG_SYS_BOOTM_LEN=0x4000000
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_BOOTP_SEND_HOSTNAME=y
CONFIG_DM_MTD=y
CONFIG_SYSRESET=y
CONFIG_SYSRESET_SBI=y

View File

@ -1 +1 @@
obj-y += mpfs_clk.o mpfs_clk_cfg.o mpfs_clk_periph.o
obj-y += mpfs_clk.o mpfs_clk_cfg.o mpfs_clk_periph.o mpfs_clk_msspll.o

View File

@ -11,34 +11,51 @@
#include <dm/device.h>
#include <dm/devres.h>
#include <dm/uclass.h>
#include <dt-bindings/clock/microchip-mpfs-clock.h>
#include <linux/err.h>
#include "mpfs_clk.h"
static int mpfs_clk_probe(struct udevice *dev)
{
int ret;
struct clk *parent_clk = dev_get_priv(dev);
struct clk clk_msspll = { .id = CLK_MSSPLL };
void __iomem *base;
u32 clk_rate;
const char *parent_clk_name;
struct clk *clk = dev_get_priv(dev);
void __iomem *msspll_base;
int ret;
base = dev_read_addr_ptr(dev);
base = dev_read_addr_index_ptr(dev, 0);
if (!base)
return -EINVAL;
ret = clk_get_by_index(dev, 0, clk);
ret = clk_get_by_index(dev, 0, parent_clk);
if (ret)
return ret;
dev_read_u32(clk->dev, "clock-frequency", &clk_rate);
parent_clk_name = clk->dev->name;
/*
* The original devicetrees for mpfs messed up & defined the msspll's
* output as a fixed-frequency, 600 MHz clock & used that as the input
* for the clock controller node. The msspll is however not a fixed
* frequency clock and later devicetrees handled this properly. Check
* the devicetree & if it is one of the fixed ones, register the msspll.
* Otherwise, skip registering it & pass the reference clock directly
* to the cfg clock registration function.
*/
msspll_base = dev_read_addr_index_ptr(dev, 1);
if (msspll_base) {
ret = mpfs_clk_register_msspll(msspll_base, parent_clk);
if (ret)
return ret;
ret = mpfs_clk_register_cfgs(base, clk_rate, parent_clk_name);
clk_request(dev, &clk_msspll);
parent_clk = &clk_msspll;
}
ret = mpfs_clk_register_cfgs(base, parent_clk);
if (ret)
return ret;
ret = mpfs_clk_register_periphs(base, clk_rate, "clk_ahb");
ret = mpfs_clk_register_periphs(base, dev);
return ret;
}

View File

@ -11,22 +11,26 @@
* mpfs_clk_register_cfgs() - register configuration clocks
*
* @base: base address of the mpfs system register.
* @clk_rate: the mpfs pll clock rate.
* @parent_name: a pointer to parent clock name.
* @parent: a pointer to parent clock.
* Return: zero on success, or a negative error code.
*/
int mpfs_clk_register_cfgs(void __iomem *base, u32 clk_rate,
const char *parent_name);
int mpfs_clk_register_cfgs(void __iomem *base, struct clk *parent);
/**
* mpfs_clk_register_msspll() - register the mss pll
*
* @base: base address of the mpfs system register.
* @parent: a pointer to parent clock.
* Return: zero on success, or a negative error code.
*/
int mpfs_clk_register_msspll(void __iomem *base, struct clk *parent);
/**
* mpfs_clk_register_periphs() - register peripheral clocks
*
* @base: base address of the mpfs system register.
* @clk_rate: the mpfs pll clock rate.
* @parent_name: a pointer to parent clock name.
* @dev: udevice representing the clock controller.
* Return: zero on success, or a negative error code.
*/
int mpfs_clk_register_periphs(void __iomem *base, u32 clk_rate,
const char *parent_name);
int mpfs_clk_register_periphs(void __iomem *base, struct udevice *dev);
/**
* divider_get_val() - get the clock divider value
*

View File

@ -117,8 +117,7 @@ static struct mpfs_cfg_hw_clock mpfs_cfg_clks[] = {
CLK_CFG(CLK_AHB, "clk_ahb", 4, 2, mpfs_div_ahb_table, 0),
};
int mpfs_clk_register_cfgs(void __iomem *base, u32 clk_rate,
const char *parent_name)
int mpfs_clk_register_cfgs(void __iomem *base, struct clk *parent)
{
int ret;
int i, id, num_clks;
@ -129,9 +128,9 @@ int mpfs_clk_register_cfgs(void __iomem *base, u32 clk_rate,
for (i = 0; i < num_clks; i++) {
hw = &mpfs_cfg_clks[i].hw;
mpfs_cfg_clks[i].sys_base = base;
mpfs_cfg_clks[i].prate = clk_rate;
mpfs_cfg_clks[i].prate = clk_get_rate(parent);
name = mpfs_cfg_clks[i].cfg.name;
ret = clk_register(hw, MPFS_CFG_CLOCK, name, parent_name);
ret = clk_register(hw, MPFS_CFG_CLOCK, name, parent->dev->name);
if (ret)
ERR_PTR(ret);
id = mpfs_cfg_clks[i].cfg.id;

View File

@ -0,0 +1,119 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2022 Microchip Technology Inc.
*/
#include <common.h>
#include <clk.h>
#include <clk-uclass.h>
#include <asm/io.h>
#include <dm/device.h>
#include <dm/devres.h>
#include <dm/uclass.h>
#include <dt-bindings/clock/microchip-mpfs-clock.h>
#include <linux/err.h>
#include "mpfs_clk.h"
#define MPFS_MSSPLL_CLOCK "mpfs_msspll_clock"
/* address offset of control registers */
#define REG_MSSPLL_REF_CR 0x08u
#define REG_MSSPLL_POSTDIV_CR 0x10u
#define REG_MSSPLL_SSCG_2_CR 0x2Cu
#define MSSPLL_FBDIV_SHIFT 0x00u
#define MSSPLL_FBDIV_WIDTH 0x0Cu
#define MSSPLL_REFDIV_SHIFT 0x08u
#define MSSPLL_REFDIV_WIDTH 0x06u
#define MSSPLL_POSTDIV_SHIFT 0x08u
#define MSSPLL_POSTDIV_WIDTH 0x07u
#define MSSPLL_FIXED_DIV 4u
/**
* struct mpfs_msspll_hw_clock
* @id: index of the msspll clock
* @name: the msspll clocks name
* @reg_offset: offset to the core complex's output of the msspll
* @shift: shift to the divider bit field of a msspll clock output
* @width: width of the divider bit field of the msspll clock output
* @flags: common clock framework flags
* @prate: the reference clock rate
* @hw: clock instance
*/
struct mpfs_msspll_hw_clock {
void __iomem *base;
unsigned int id;
const char *name;
u32 reg_offset;
u32 shift;
u32 width;
u32 flags;
u32 prate;
struct clk hw;
};
#define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw)
static unsigned long mpfs_clk_msspll_recalc_rate(struct clk *hw)
{
struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw);
void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset;
void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR;
void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR;
u32 mult, ref_div, postdiv;
unsigned long temp;
mult = readl(mult_addr) >> MSSPLL_FBDIV_SHIFT;
mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH);
ref_div = readl(ref_div_addr) >> MSSPLL_REFDIV_SHIFT;
ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH);
postdiv = readl(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT;
postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH);
temp = msspll_hw->prate / (ref_div * MSSPLL_FIXED_DIV * postdiv);
return temp * mult;
}
#define CLK_PLL(_id, _name, _shift, _width, _reg_offset, _flags) { \
.id = _id, \
.name = _name, \
.shift = _shift, \
.width = _width, \
.reg_offset = _reg_offset, \
.flags = _flags, \
}
static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = {
CLK_PLL(CLK_MSSPLL, "clk_msspll", MSSPLL_FBDIV_SHIFT,
MSSPLL_FBDIV_WIDTH, REG_MSSPLL_SSCG_2_CR, 0),
};
int mpfs_clk_register_msspll(void __iomem *base, struct clk *parent)
{
int id, ret;
const char *name;
struct clk *hw;
hw = &mpfs_msspll_clks[0].hw;
mpfs_msspll_clks[0].base = base;
mpfs_msspll_clks[0].prate = clk_get_rate(parent);
name = mpfs_msspll_clks[0].name;
ret = clk_register(hw, MPFS_MSSPLL_CLOCK, name, parent->dev->name);
if (ret)
ERR_PTR(ret);
id = mpfs_msspll_clks[0].id;
clk_dm(id, hw);
return 0;
}
const struct clk_ops mpfs_msspll_clk_ops = {
.get_rate = mpfs_clk_msspll_recalc_rate,
};
U_BOOT_DRIVER(mpfs_msspll_clock) = {
.name = MPFS_MSSPLL_CLOCK,
.id = UCLASS_CLK,
.ops = &mpfs_msspll_clk_ops,
};

View File

@ -29,12 +29,14 @@
/**
* struct mpfs_periph_clock - per instance of peripheral clock
* @id: index of a peripheral clock
* @parent_id: index of the parent clock
* @name: name of a peripheral clock
* @shift: shift to a peripheral clock bit field
* @flags: common clock framework flags
*/
struct mpfs_periph_clock {
unsigned int id;
unsigned int parent_id;
const char *name;
u8 shift;
unsigned long flags;
@ -99,59 +101,66 @@ static int mpfs_periph_clk_disable(struct clk *hw)
static ulong mpfs_periph_clk_recalc_rate(struct clk *hw)
{
struct mpfs_periph_hw_clock *periph_hw = to_mpfs_periph_clk(hw);
void __iomem *base_addr = periph_hw->sys_base;
unsigned long rate;
u32 val;
val = readl(base_addr + REG_CLOCK_CONFIG_CR) >> CFG_AHB_SHIFT;
val &= clk_div_mask(CFG_WIDTH);
rate = periph_hw->prate / (1u << val);
hw->rate = rate;
return periph_hw->prate;
return rate;
}
#define CLK_PERIPH(_id, _name, _shift, _flags) { \
#define CLK_PERIPH(_id, _name, _parent_id, _shift, _flags) { \
.periph.id = _id, \
.periph.parent_id = _parent_id, \
.periph.name = _name, \
.periph.shift = _shift, \
.periph.flags = _flags, \
}
/*
* Critical clocks:
* - CLK_ENVM: reserved by hart software services (hss) superloop monitor/m mode interrupt
* trap handler
* - CLK_MMUART0: reserved by the hss
* - CLK_DDRC: provides clock to the ddr subsystem
* - CLK_RTC: the onboard RTC's AHB bus clock must be kept running as the rtc will stop
* if the AHB interface clock is disabled
* - CLK_FICx: these provide the processor side clocks to the "FIC" (Fabric InterConnect)
* clock domain crossers which provide the interface to the FPGA fabric. Disabling them
* causes the FPGA fabric to go into reset.
* - CLK_ATHENA: The athena clock is FIC4, which is reserved for the Athena TeraFire.
*/
static struct mpfs_periph_hw_clock mpfs_periph_clks[] = {
CLK_PERIPH(CLK_ENVM, "clk_periph_envm", 0, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_MAC0, "clk_periph_mac0", 1, 0),
CLK_PERIPH(CLK_MAC1, "clk_periph_mac1", 2, 0),
CLK_PERIPH(CLK_MMC, "clk_periph_mmc", 3, 0),
CLK_PERIPH(CLK_TIMER, "clk_periph_timer", 4, 0),
CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0", 5, 0),
CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1", 6, 0),
CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2", 7, 0),
CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3", 8, 0),
CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4", 9, 0),
CLK_PERIPH(CLK_SPI0, "clk_periph_spi0", 10, 0),
CLK_PERIPH(CLK_SPI1, "clk_periph_spi1", 11, 0),
CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0", 12, 0),
CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1", 13, 0),
CLK_PERIPH(CLK_CAN0, "clk_periph_can0", 14, 0),
CLK_PERIPH(CLK_CAN1, "clk_periph_can1", 15, 0),
CLK_PERIPH(CLK_USB, "clk_periph_usb", 16, 0),
CLK_PERIPH(CLK_RTC, "clk_periph_rtc", 18, 0),
CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", 19, 0),
CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", 20, 0),
CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", 21, 0),
CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2", 22, 0),
CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc", 23, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_FIC0, "clk_periph_fic0", 24, 0),
CLK_PERIPH(CLK_FIC1, "clk_periph_fic1", 25, 0),
CLK_PERIPH(CLK_FIC2, "clk_periph_fic2", 26, 0),
CLK_PERIPH(CLK_FIC3, "clk_periph_fic3", 27, 0),
CLK_PERIPH(CLK_ATHENA, "clk_periph_athena", 28, 0),
CLK_PERIPH(CLK_CFM, "clk_periph_cfm", 29, 0),
CLK_PERIPH(CLK_ENVM, "clk_periph_envm", CLK_AHB, 0, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_MAC0, "clk_periph_mac0", CLK_AHB, 1, 0),
CLK_PERIPH(CLK_MAC1, "clk_periph_mac1", CLK_AHB, 2, 0),
CLK_PERIPH(CLK_MMC, "clk_periph_mmc", CLK_AHB, 3, 0),
CLK_PERIPH(CLK_TIMER, "clk_periph_timer", CLK_RTCREF, 4, 0),
CLK_PERIPH(CLK_MMUART0, "clk_periph_mmuart0", CLK_AHB, 5, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_MMUART1, "clk_periph_mmuart1", CLK_AHB, 6, 0),
CLK_PERIPH(CLK_MMUART2, "clk_periph_mmuart2", CLK_AHB, 7, 0),
CLK_PERIPH(CLK_MMUART3, "clk_periph_mmuart3", CLK_AHB, 8, 0),
CLK_PERIPH(CLK_MMUART4, "clk_periph_mmuart4", CLK_AHB, 9, 0),
CLK_PERIPH(CLK_SPI0, "clk_periph_spi0", CLK_AHB, 10, 0),
CLK_PERIPH(CLK_SPI1, "clk_periph_spi1", CLK_AHB, 11, 0),
CLK_PERIPH(CLK_I2C0, "clk_periph_i2c0", CLK_AHB, 12, 0),
CLK_PERIPH(CLK_I2C1, "clk_periph_i2c1", CLK_AHB, 13, 0),
CLK_PERIPH(CLK_CAN0, "clk_periph_can0", CLK_AHB, 14, 0),
CLK_PERIPH(CLK_CAN1, "clk_periph_can1", CLK_AHB, 15, 0),
CLK_PERIPH(CLK_USB, "clk_periph_usb", CLK_AHB, 16, 0),
CLK_PERIPH(CLK_RTC, "clk_periph_rtc", CLK_AHB, 18, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_QSPI, "clk_periph_qspi", CLK_AHB, 19, 0),
CLK_PERIPH(CLK_GPIO0, "clk_periph_gpio0", CLK_AHB, 20, 0),
CLK_PERIPH(CLK_GPIO1, "clk_periph_gpio1", CLK_AHB, 21, 0),
CLK_PERIPH(CLK_GPIO2, "clk_periph_gpio2", CLK_AHB, 22, 0),
CLK_PERIPH(CLK_DDRC, "clk_periph_ddrc", CLK_AHB, 23, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_FIC0, "clk_periph_fic0", CLK_AXI, 24, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_FIC1, "clk_periph_fic1", CLK_AXI, 25, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_FIC2, "clk_periph_fic2", CLK_AXI, 26, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_FIC3, "clk_periph_fic3", CLK_AXI, 27, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_ATHENA, "clk_periph_athena", CLK_AXI, 28, CLK_IS_CRITICAL),
CLK_PERIPH(CLK_CFM, "clk_periph_cfm", CLK_AHB, 29, 0),
};
int mpfs_clk_register_periphs(void __iomem *base, u32 clk_rate,
const char *parent_name)
int mpfs_clk_register_periphs(void __iomem *base, struct udevice *dev)
{
int ret;
int i, id, num_clks;
@ -160,11 +169,14 @@ int mpfs_clk_register_periphs(void __iomem *base, u32 clk_rate,
num_clks = ARRAY_SIZE(mpfs_periph_clks);
for (i = 0; i < num_clks; i++) {
struct clk parent = { .id = mpfs_periph_clks[i].periph.parent_id };
clk_request(dev, &parent);
hw = &mpfs_periph_clks[i].hw;
mpfs_periph_clks[i].sys_base = base;
mpfs_periph_clks[i].prate = clk_rate;
mpfs_periph_clks[i].prate = clk_get_rate(&parent);
name = mpfs_periph_clks[i].periph.name;
ret = clk_register(hw, MPFS_PERIPH_CLOCK, name, parent_name);
ret = clk_register(hw, MPFS_PERIPH_CLOCK, name, parent.dev->name);
if (ret)
ERR_PTR(ret);
id = mpfs_periph_clks[i].periph.id;

View File

@ -42,4 +42,7 @@
#define CLK_ATHENA 31
#define CLK_CFM 32
#define CLK_RTCREF 33
#define CLK_MSSPLL 34
#endif /* _DT_BINDINGS_CLK_MICROCHIP_MPFS_H_ */