Merge branches 'clk-rpi-cpufreq', 'clk-tegra', 'clk-simplify-provider.h', 'clk-sprd' and 'clk-at91' into clk-next

- Support for CPU clks on Raspberry Pi devices
 - Slow clk support for AT91 SAM9X60 SoCs

* clk-rpi-cpufreq:
  clk: raspberrypi: register platform device for raspberrypi-cpufreq
  firmware: raspberrypi: register clk device
  clk: bcm283x: add driver interfacing with Raspberry Pi's firmware
  clk: bcm2835: remove pllb

* clk-tegra:
  clk: tegra: Do not enable PLL_RE_VCO on Tegra210
  clk: tegra: Warn if an enabled PLL is in IDDQ
  clk: tegra: Do not warn unnecessarily
  clk: tegra210: fix PLLU and PLLU_OUT1

* clk-simplify-provider.h:
  clk: consoldiate the __clk_get_hw() declarations
  clk: Unexport __clk_of_table
  clk: Remove ifdef for COMMON_CLK in clk-provider.h

* clk-sprd:
  clk: sprd: Add check for return value of sprd_clk_regmap_init()
  clk: sprd: Check error only for devm_regmap_init_mmio()
  clk: sprd: Switch from of_iomap() to devm_ioremap_resource()

* clk-at91:
  clk: at91: sckc: use dedicated functions to unregister clock
  clk: at91: sckc: improve error path for sama5d4 sck registration
  clk: at91: sckc: remove unnecessary line
  clk: at91: sckc: improve error path for sam9x5 sck register
  clk: at91: sckc: add support to free slow clock osclillator
  clk: at91: sckc: add support to free slow rc oscillator
  clk: at91: sckc: add support to free slow oscillator
  clk: at91: sckc: add support for SAM9X60
  dt-bindings: clk: at91: add bindings for SAM9X60's slow clock controller
  clk: at91: sckc: add support to specify registers bit offsets
  clk: at91: sckc: sama5d4 has no bypass support
This commit is contained in:
Stephen Boyd 2019-07-12 11:11:30 -07:00
19 changed files with 590 additions and 114 deletions

View file

@ -9,10 +9,11 @@ Slow Clock controller:
Required properties:
- compatible : shall be one of the following:
"atmel,at91sam9x5-sckc",
"atmel,sama5d3-sckc" or
"atmel,sama5d4-sckc":
"atmel,sama5d3-sckc",
"atmel,sama5d4-sckc" or
"microchip,sam9x60-sckc":
at91 SCKC (Slow Clock Controller)
- #clock-cells : shall be 0.
- #clock-cells : shall be 1 for "microchip,sam9x60-sckc" otherwise shall be 0.
- clocks : shall be the input parent clock phandle for the clock.
Optional properties:

View file

@ -23,14 +23,18 @@
SLOW_CLOCK_FREQ)
#define AT91_SCKC_CR 0x00
#define AT91_SCKC_RCEN (1 << 0)
#define AT91_SCKC_OSC32EN (1 << 1)
#define AT91_SCKC_OSC32BYP (1 << 2)
#define AT91_SCKC_OSCSEL (1 << 3)
struct clk_slow_bits {
u32 cr_rcen;
u32 cr_osc32en;
u32 cr_osc32byp;
u32 cr_oscsel;
};
struct clk_slow_osc {
struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
unsigned long startup_usec;
};
@ -39,6 +43,7 @@ struct clk_slow_osc {
struct clk_sama5d4_slow_osc {
struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
unsigned long startup_usec;
bool prepared;
};
@ -48,6 +53,7 @@ struct clk_sama5d4_slow_osc {
struct clk_slow_rc_osc {
struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
unsigned long frequency;
unsigned long accuracy;
unsigned long startup_usec;
@ -58,6 +64,7 @@ struct clk_slow_rc_osc {
struct clk_sam9x5_slow {
struct clk_hw hw;
void __iomem *sckcr;
const struct clk_slow_bits *bits;
u8 parent;
};
@ -69,10 +76,10 @@ static int clk_slow_osc_prepare(struct clk_hw *hw)
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN))
if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en))
return 0;
writel(tmp | AT91_SCKC_OSC32EN, sckcr);
writel(tmp | osc->bits->cr_osc32en, sckcr);
usleep_range(osc->startup_usec, osc->startup_usec + 1);
@ -85,10 +92,10 @@ static void clk_slow_osc_unprepare(struct clk_hw *hw)
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
if (tmp & AT91_SCKC_OSC32BYP)
if (tmp & osc->bits->cr_osc32byp)
return;
writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
writel(tmp & ~osc->bits->cr_osc32en, sckcr);
}
static int clk_slow_osc_is_prepared(struct clk_hw *hw)
@ -97,10 +104,10 @@ static int clk_slow_osc_is_prepared(struct clk_hw *hw)
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
if (tmp & AT91_SCKC_OSC32BYP)
if (tmp & osc->bits->cr_osc32byp)
return 1;
return !!(tmp & AT91_SCKC_OSC32EN);
return !!(tmp & osc->bits->cr_osc32en);
}
static const struct clk_ops slow_osc_ops = {
@ -114,7 +121,8 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
const char *name,
const char *parent_name,
unsigned long startup,
bool bypass)
bool bypass,
const struct clk_slow_bits *bits)
{
struct clk_slow_osc *osc;
struct clk_hw *hw;
@ -137,10 +145,11 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
osc->hw.init = &init;
osc->sckcr = sckcr;
osc->startup_usec = startup;
osc->bits = bits;
if (bypass)
writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
sckcr);
writel((readl(sckcr) & ~osc->bits->cr_osc32en) |
osc->bits->cr_osc32byp, sckcr);
hw = &osc->hw;
ret = clk_hw_register(NULL, &osc->hw);
@ -152,6 +161,14 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
return hw;
}
static void at91_clk_unregister_slow_osc(struct clk_hw *hw)
{
struct clk_slow_osc *osc = to_clk_slow_osc(hw);
clk_hw_unregister(hw);
kfree(osc);
}
static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@ -173,7 +190,7 @@ static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
void __iomem *sckcr = osc->sckcr;
writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);
usleep_range(osc->startup_usec, osc->startup_usec + 1);
@ -185,14 +202,14 @@ static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
void __iomem *sckcr = osc->sckcr;
writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);
}
static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
{
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
return !!(readl(osc->sckcr) & osc->bits->cr_rcen);
}
static const struct clk_ops slow_rc_osc_ops = {
@ -208,7 +225,8 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr,
const char *name,
unsigned long frequency,
unsigned long accuracy,
unsigned long startup)
unsigned long startup,
const struct clk_slow_bits *bits)
{
struct clk_slow_rc_osc *osc;
struct clk_hw *hw;
@ -230,6 +248,7 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr,
osc->hw.init = &init;
osc->sckcr = sckcr;
osc->bits = bits;
osc->frequency = frequency;
osc->accuracy = accuracy;
osc->startup_usec = startup;
@ -244,6 +263,14 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr,
return hw;
}
static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw)
{
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
clk_hw_unregister(hw);
kfree(osc);
}
static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
@ -255,14 +282,14 @@ static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
tmp = readl(sckcr);
if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
(index && (tmp & AT91_SCKC_OSCSEL)))
if ((!index && !(tmp & slowck->bits->cr_oscsel)) ||
(index && (tmp & slowck->bits->cr_oscsel)))
return 0;
if (index)
tmp |= AT91_SCKC_OSCSEL;
tmp |= slowck->bits->cr_oscsel;
else
tmp &= ~AT91_SCKC_OSCSEL;
tmp &= ~slowck->bits->cr_oscsel;
writel(tmp, sckcr);
@ -275,7 +302,7 @@ static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
{
struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel);
}
static const struct clk_ops sam9x5_slow_ops = {
@ -287,7 +314,8 @@ static struct clk_hw * __init
at91_clk_register_sam9x5_slow(void __iomem *sckcr,
const char *name,
const char **parent_names,
int num_parents)
int num_parents,
const struct clk_slow_bits *bits)
{
struct clk_sam9x5_slow *slowck;
struct clk_hw *hw;
@ -309,7 +337,8 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr,
slowck->hw.init = &init;
slowck->sckcr = sckcr;
slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
slowck->bits = bits;
slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);
hw = &slowck->hw;
ret = clk_hw_register(NULL, &slowck->hw);
@ -321,22 +350,33 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr,
return hw;
}
static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw)
{
struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
clk_hw_unregister(hw);
kfree(slowck);
}
static void __init at91sam9x5_sckc_register(struct device_node *np,
unsigned int rc_osc_startup_us)
unsigned int rc_osc_startup_us,
const struct clk_slow_bits *bits)
{
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
void __iomem *regbase = of_iomap(np, 0);
struct device_node *child = NULL;
const char *xtal_name;
struct clk_hw *hw;
struct clk_hw *slow_rc, *slow_osc, *slowck;
bool bypass;
int ret;
if (!regbase)
return;
hw = at91_clk_register_slow_rc_osc(regbase, parent_names[0], 32768,
50000000, rc_osc_startup_us);
if (IS_ERR(hw))
slow_rc = at91_clk_register_slow_rc_osc(regbase, parent_names[0],
32768, 50000000,
rc_osc_startup_us, bits);
if (IS_ERR(slow_rc))
return;
xtal_name = of_clk_get_parent_name(np, 0);
@ -344,7 +384,7 @@ static void __init at91sam9x5_sckc_register(struct device_node *np,
/* DT backward compatibility */
child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");
if (!child)
return;
goto unregister_slow_rc;
xtal_name = of_clk_get_parent_name(child, 0);
bypass = of_property_read_bool(child, "atmel,osc-bypass");
@ -355,38 +395,133 @@ static void __init at91sam9x5_sckc_register(struct device_node *np,
}
if (!xtal_name)
return;
goto unregister_slow_rc;
hw = at91_clk_register_slow_osc(regbase, parent_names[1], xtal_name,
1200000, bypass);
if (IS_ERR(hw))
return;
slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1],
xtal_name, 1200000, bypass, bits);
if (IS_ERR(slow_osc))
goto unregister_slow_rc;
hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
if (IS_ERR(hw))
return;
of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names,
2, bits);
if (IS_ERR(slowck))
goto unregister_slow_osc;
/* DT backward compatibility */
if (child)
of_clk_add_hw_provider(child, of_clk_hw_simple_get, hw);
ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get,
slowck);
else
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
if (WARN_ON(ret))
goto unregister_slowck;
return;
unregister_slowck:
at91_clk_unregister_sam9x5_slow(slowck);
unregister_slow_osc:
at91_clk_unregister_slow_osc(slow_osc);
unregister_slow_rc:
at91_clk_unregister_slow_rc_osc(slow_rc);
}
static const struct clk_slow_bits at91sam9x5_bits = {
.cr_rcen = BIT(0),
.cr_osc32en = BIT(1),
.cr_osc32byp = BIT(2),
.cr_oscsel = BIT(3),
};
static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
{
at91sam9x5_sckc_register(np, 75);
at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits);
}
CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
of_at91sam9x5_sckc_setup);
static void __init of_sama5d3_sckc_setup(struct device_node *np)
{
at91sam9x5_sckc_register(np, 500);
at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits);
}
CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",
of_sama5d3_sckc_setup);
static const struct clk_slow_bits at91sam9x60_bits = {
.cr_osc32en = BIT(1),
.cr_osc32byp = BIT(2),
.cr_oscsel = BIT(24),
};
static void __init of_sam9x60_sckc_setup(struct device_node *np)
{
void __iomem *regbase = of_iomap(np, 0);
struct clk_hw_onecell_data *clk_data;
struct clk_hw *slow_rc, *slow_osc;
const char *xtal_name;
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
bool bypass;
int ret;
if (!regbase)
return;
slow_rc = clk_hw_register_fixed_rate(NULL, parent_names[0], NULL, 0,
32768);
if (IS_ERR(slow_rc))
return;
xtal_name = of_clk_get_parent_name(np, 0);
if (!xtal_name)
goto unregister_slow_rc;
bypass = of_property_read_bool(np, "atmel,osc-bypass");
slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1],
xtal_name, 5000000, bypass,
&at91sam9x60_bits);
if (IS_ERR(slow_osc))
goto unregister_slow_rc;
clk_data = kzalloc(sizeof(*clk_data) + (2 * sizeof(struct clk_hw *)),
GFP_KERNEL);
if (!clk_data)
goto unregister_slow_osc;
/* MD_SLCK and TD_SLCK. */
clk_data->num = 2;
clk_data->hws[0] = clk_hw_register_fixed_rate(NULL, "md_slck",
parent_names[0],
0, 32768);
if (IS_ERR(clk_data->hws[0]))
goto clk_data_free;
clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
parent_names, 2,
&at91sam9x60_bits);
if (IS_ERR(clk_data->hws[1]))
goto unregister_md_slck;
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
if (WARN_ON(ret))
goto unregister_td_slck;
return;
unregister_td_slck:
at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);
unregister_md_slck:
clk_hw_unregister(clk_data->hws[0]);
clk_data_free:
kfree(clk_data);
unregister_slow_osc:
at91_clk_unregister_slow_osc(slow_osc);
unregister_slow_rc:
clk_hw_unregister(slow_rc);
}
CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",
of_sam9x60_sckc_setup);
static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
{
struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
@ -398,7 +533,7 @@ static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
* Assume that if it has already been selected (for example by the
* bootloader), enough time has aready passed.
*/
if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) {
if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {
osc->prepared = true;
return 0;
}
@ -421,33 +556,35 @@ static const struct clk_ops sama5d4_slow_osc_ops = {
.is_prepared = clk_sama5d4_slow_osc_is_prepared,
};
static const struct clk_slow_bits at91sama5d4_bits = {
.cr_oscsel = BIT(3),
};
static void __init of_sama5d4_sckc_setup(struct device_node *np)
{
void __iomem *regbase = of_iomap(np, 0);
struct clk_hw *hw;
struct clk_hw *slow_rc, *slowck;
struct clk_sama5d4_slow_osc *osc;
struct clk_init_data init;
const char *xtal_name;
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
bool bypass;
int ret;
if (!regbase)
return;
hw = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
NULL, 0, 32768,
250000000);
if (IS_ERR(hw))
slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,
parent_names[0],
NULL, 0, 32768,
250000000);
if (IS_ERR(slow_rc))
return;
xtal_name = of_clk_get_parent_name(np, 0);
bypass = of_property_read_bool(np, "atmel,osc-bypass");
osc = kzalloc(sizeof(*osc), GFP_KERNEL);
if (!osc)
return;
goto unregister_slow_rc;
init.name = parent_names[1];
init.ops = &sama5d4_slow_osc_ops;
@ -458,22 +595,32 @@ static void __init of_sama5d4_sckc_setup(struct device_node *np)
osc->hw.init = &init;
osc->sckcr = regbase;
osc->startup_usec = 1200000;
osc->bits = &at91sama5d4_bits;
if (bypass)
writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase);
hw = &osc->hw;
ret = clk_hw_register(NULL, &osc->hw);
if (ret) {
kfree(osc);
return;
}
if (ret)
goto free_slow_osc_data;
hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
if (IS_ERR(hw))
return;
slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",
parent_names, 2,
&at91sama5d4_bits);
if (IS_ERR(slowck))
goto unregister_slow_osc;
of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
if (WARN_ON(ret))
goto unregister_slowck;
return;
unregister_slowck:
at91_clk_unregister_sam9x5_slow(slowck);
unregister_slow_osc:
clk_hw_unregister(&osc->hw);
free_slow_osc_data:
kfree(osc);
unregister_slow_rc:
clk_hw_unregister(slow_rc);
}
CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
of_sama5d4_sckc_setup);

View file

@ -72,3 +72,10 @@ config CLK_BCM_SR
default ARCH_BCM_IPROC
help
Enable common clock framework support for the Broadcom Stingray SoC
config CLK_RASPBERRYPI
tristate "Raspberry Pi firmware based clock support"
depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
help
Enable common clock framework support for Raspberry Pi's firmware
dependent clocks

View file

@ -7,6 +7,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o
obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o
obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835-aux.o
obj-$(CONFIG_CLK_RASPBERRYPI) += clk-raspberrypi.o
obj-$(CONFIG_ARCH_BCM_53573) += clk-bcm53573-ilp.o
obj-$(CONFIG_CLK_BCM_CYGNUS) += clk-cygnus.o
obj-$(CONFIG_CLK_BCM_HR2) += clk-hr2.o

View file

@ -1651,30 +1651,10 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.fixed_divider = 1,
.flags = CLK_SET_RATE_PARENT),
/* PLLB is used for the ARM's clock. */
[BCM2835_PLLB] = REGISTER_PLL(
.name = "pllb",
.cm_ctrl_reg = CM_PLLB,
.a2w_ctrl_reg = A2W_PLLB_CTRL,
.frac_reg = A2W_PLLB_FRAC,
.ana_reg_base = A2W_PLLB_ANA0,
.reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE,
.lock_mask = CM_LOCK_FLOCKB,
.ana = &bcm2835_ana_default,
.min_rate = 600000000u,
.max_rate = 3000000000u,
.max_fb_rate = BCM2835_MAX_FB_RATE),
[BCM2835_PLLB_ARM] = REGISTER_PLL_DIV(
.name = "pllb_arm",
.source_pll = "pllb",
.cm_reg = CM_PLLB,
.a2w_reg = A2W_PLLB_ARM,
.load_mask = CM_PLLB_LOADARM,
.hold_mask = CM_PLLB_HOLDARM,
.fixed_divider = 1,
.flags = CLK_SET_RATE_PARENT),
/*
* PLLB is used for the ARM's clock. Controlled by firmware, see
* clk-raspberrypi.c.
*/
/*
* PLLC is the core PLL, used to drive the core VPU clock.

View file

@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Raspberry Pi driver for firmware controlled clocks
*
* Even though clk-bcm2835 provides an interface to the hardware registers for
* the system clocks we've had to factor out 'pllb' as the firmware 'owns' it.
* We're not allowed to change it directly as we might race with the
* over-temperature and under-voltage protections provided by the firmware.
*
* Copyright (C) 2019 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
*/
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
#define RPI_FIRMWARE_ARM_CLK_ID 0x00000003
#define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
#define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1)
/*
* Even though the firmware interface alters 'pllb' the frequencies are
* provided as per 'pllb_arm'. We need to scale before passing them trough.
*/
#define RPI_FIRMWARE_PLLB_ARM_DIV_RATE 2
#define A2W_PLL_FRAC_BITS 20
struct raspberrypi_clk {
struct device *dev;
struct rpi_firmware *firmware;
struct platform_device *cpufreq;
unsigned long min_rate;
unsigned long max_rate;
struct clk_hw pllb;
struct clk_hw *pllb_arm;
struct clk_lookup *pllb_arm_lookup;
};
/*
* Structure of the message passed to Raspberry Pi's firmware in order to
* change clock rates. The 'disable_turbo' option is only available to the ARM
* clock (pllb) which we enable by default as turbo mode will alter multiple
* clocks at once.
*
* Even though we're able to access the clock registers directly we're bound to
* use the firmware interface as the firmware ultimately takes care of
* mitigating overheating/undervoltage situations and we would be changing
* frequencies behind his back.
*
* For more information on the firmware interface check:
* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
*/
struct raspberrypi_firmware_prop {
__le32 id;
__le32 val;
__le32 disable_turbo;
} __packed;
static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag,
u32 clk, u32 *val)
{
struct raspberrypi_firmware_prop msg = {
.id = cpu_to_le32(clk),
.val = cpu_to_le32(*val),
.disable_turbo = cpu_to_le32(1),
};
int ret;
ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg));
if (ret)
return ret;
*val = le32_to_cpu(msg.val);
return 0;
}
static int raspberrypi_fw_pll_is_on(struct clk_hw *hw)
{
struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
pllb);
u32 val = 0;
int ret;
ret = raspberrypi_clock_property(rpi->firmware,
RPI_FIRMWARE_GET_CLOCK_STATE,
RPI_FIRMWARE_ARM_CLK_ID, &val);
if (ret)
return 0;
return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT);
}
static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
pllb);
u32 val = 0;
int ret;
ret = raspberrypi_clock_property(rpi->firmware,
RPI_FIRMWARE_GET_CLOCK_RATE,
RPI_FIRMWARE_ARM_CLK_ID,
&val);
if (ret)
return ret;
return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
}
static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
pllb);
u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
int ret;
ret = raspberrypi_clock_property(rpi->firmware,
RPI_FIRMWARE_SET_CLOCK_RATE,
RPI_FIRMWARE_ARM_CLK_ID,
&new_rate);
if (ret)
dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d",
clk_hw_get_name(hw), ret);
return ret;
}
/*
* Sadly there is no firmware rate rounding interface. We borrowed it from
* clk-bcm2835.
*/
static int raspberrypi_pll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
pllb);
u64 div, final_rate;
u32 ndiv, fdiv;
/* We can't use req->rate directly as it would overflow */
final_rate = clamp(req->rate, rpi->min_rate, rpi->max_rate);
div = (u64)final_rate << A2W_PLL_FRAC_BITS;
do_div(div, req->best_parent_rate);
ndiv = div >> A2W_PLL_FRAC_BITS;
fdiv = div & ((1 << A2W_PLL_FRAC_BITS) - 1);
final_rate = ((u64)req->best_parent_rate *
((ndiv << A2W_PLL_FRAC_BITS) + fdiv));
req->rate = final_rate >> A2W_PLL_FRAC_BITS;
return 0;
}
static const struct clk_ops raspberrypi_firmware_pll_clk_ops = {
.is_prepared = raspberrypi_fw_pll_is_on,
.recalc_rate = raspberrypi_fw_pll_get_rate,
.set_rate = raspberrypi_fw_pll_set_rate,
.determine_rate = raspberrypi_pll_determine_rate,
};
static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi)
{
u32 min_rate = 0, max_rate = 0;
struct clk_init_data init;
int ret;
memset(&init, 0, sizeof(init));
/* All of the PLLs derive from the external oscillator. */
init.parent_names = (const char *[]){ "osc" };
init.num_parents = 1;
init.name = "pllb";
init.ops = &raspberrypi_firmware_pll_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED;
/* Get min & max rates set by the firmware */
ret = raspberrypi_clock_property(rpi->firmware,
RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
RPI_FIRMWARE_ARM_CLK_ID,
&min_rate);
if (ret) {
dev_err(rpi->dev, "Failed to get %s min freq: %d\n",
init.name, ret);
return ret;
}
ret = raspberrypi_clock_property(rpi->firmware,
RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
RPI_FIRMWARE_ARM_CLK_ID,
&max_rate);
if (ret) {
dev_err(rpi->dev, "Failed to get %s max freq: %d\n",
init.name, ret);
return ret;
}
if (!min_rate || !max_rate) {
dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n",
min_rate, max_rate);
return -EINVAL;
}
dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n",
min_rate, max_rate);
rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
rpi->pllb.init = &init;
return devm_clk_hw_register(rpi->dev, &rpi->pllb);
}
static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi)
{
rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev,
"pllb_arm", "pllb",
CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
1, 2);
if (IS_ERR(rpi->pllb_arm)) {
dev_err(rpi->dev, "Failed to initialize pllb_arm\n");
return PTR_ERR(rpi->pllb_arm);
}
rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0");
if (!rpi->pllb_arm_lookup) {
dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n");
clk_hw_unregister_fixed_factor(rpi->pllb_arm);
return -ENOMEM;
}
return 0;
}
static int raspberrypi_clk_probe(struct platform_device *pdev)
{
struct device_node *firmware_node;
struct device *dev = &pdev->dev;
struct rpi_firmware *firmware;
struct raspberrypi_clk *rpi;
int ret;
firmware_node = of_find_compatible_node(NULL, NULL,
"raspberrypi,bcm2835-firmware");
if (!firmware_node) {
dev_err(dev, "Missing firmware node\n");
return -ENOENT;
}
firmware = rpi_firmware_get(firmware_node);
of_node_put(firmware_node);
if (!firmware)
return -EPROBE_DEFER;
rpi = devm_kzalloc(dev, sizeof(*rpi), GFP_KERNEL);
if (!rpi)
return -ENOMEM;
rpi->dev = dev;
rpi->firmware = firmware;
platform_set_drvdata(pdev, rpi);
ret = raspberrypi_register_pllb(rpi);
if (ret) {
dev_err(dev, "Failed to initialize pllb, %d\n", ret);
return ret;
}
ret = raspberrypi_register_pllb_arm(rpi);
if (ret)
return ret;
rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq",
-1, NULL, 0);
return 0;
}
static int raspberrypi_clk_remove(struct platform_device *pdev)
{
struct raspberrypi_clk *rpi = platform_get_drvdata(pdev);
platform_device_unregister(rpi->cpufreq);
return 0;
}
static struct platform_driver raspberrypi_clk_driver = {
.driver = {
.name = "raspberrypi-clk",
},
.probe = raspberrypi_clk_probe,
.remove = raspberrypi_clk_remove,
};
module_platform_driver(raspberrypi_clk_driver);
MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
MODULE_DESCRIPTION("Raspberry Pi firmware clock driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:raspberrypi-clk");

View file

@ -4084,6 +4084,7 @@ struct of_clk_provider {
void *data;
};
extern struct of_device_id __clk_of_table;
static const struct of_device_id __clk_of_table_sentinel
__used __section(__clk_of_table_end);

View file

@ -33,10 +33,6 @@ clk_hw_create_clk(struct device *dev, struct clk_hw *hw, const char *dev_id,
{
return (struct clk *)hw;
}
static struct clk_hw *__clk_get_hw(struct clk *clk)
{
return (struct clk_hw *)clk;
}
static inline void __clk_put(struct clk *clk) { }
#endif

View file

@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>

View file

@ -7,6 +7,7 @@
#include <dt-bindings/clock/imx6sll-clock.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>

View file

@ -12,6 +12,7 @@
#include <dt-bindings/clock/imx6sx-clock.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>

View file

@ -12,6 +12,7 @@
#include <dt-bindings/clock/imx6ul-clock.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>

View file

@ -12,6 +12,7 @@
#include <dt-bindings/clock/imx7d-clock.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.h>

View file

@ -42,6 +42,7 @@ int sprd_clk_regmap_init(struct platform_device *pdev,
void __iomem *base;
struct device_node *node = pdev->dev.of_node;
struct regmap *regmap;
struct resource *res;
if (of_find_property(node, "sprd,syscon", NULL)) {
regmap = syscon_regmap_lookup_by_phandle(node, "sprd,syscon");
@ -50,10 +51,14 @@ int sprd_clk_regmap_init(struct platform_device *pdev,
return PTR_ERR(regmap);
}
} else {
base = of_iomap(node, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sprdclk_regmap_config);
if (IS_ERR_OR_NULL(regmap)) {
if (IS_ERR(regmap)) {
pr_err("failed to init regmap\n");
return PTR_ERR(regmap);
}

View file

@ -2023,6 +2023,7 @@ static int sc9860_clk_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct sprd_clk_desc *desc;
int ret;
match = of_match_node(sprd_sc9860_clk_ids, pdev->dev.of_node);
if (!match) {
@ -2031,7 +2032,9 @@ static int sc9860_clk_probe(struct platform_device *pdev)
}
desc = match->data;
sprd_clk_regmap_init(pdev, desc);
ret = sprd_clk_regmap_init(pdev, desc);
if (ret)
return ret;
return sprd_clk_probe(&pdev->dev, desc->hw_clks);
}

View file

@ -995,8 +995,6 @@ static void tegra210_pllre_set_defaults(struct tegra_clk_pll *pllre)
pllre->params->defaults_set = true;
if (val & PLL_ENABLE) {
pr_warn("PLL_RE already enabled. Postponing set full defaults\n");
/*
* PLL is ON: check if defaults already set, then set those
* that can be updated in flight.
@ -1016,13 +1014,20 @@ static void tegra210_pllre_set_defaults(struct tegra_clk_pll *pllre)
_pll_misc_chk_default(clk_base, pllre->params, 0, val,
~mask & PLLRE_MISC0_WRITE_MASK);
/* Enable lock detect */
/* The PLL doesn't work if it's in IDDQ. */
val = readl_relaxed(clk_base + pllre->params->ext_misc_reg[0]);
if (val & PLLRE_MISC0_IDDQ)
pr_warn("unexpected IDDQ bit set for enabled clock\n");
/* Enable lock detect */
val &= ~mask;
val |= PLLRE_MISC0_DEFAULT_VALUE & mask;
writel_relaxed(val, clk_base + pllre->params->ext_misc_reg[0]);
udelay(1);
if (!pllre->params->defaults_set)
pr_warn("PLL_RE already enabled. Postponing set full defaults\n");
return;
}
@ -2215,9 +2220,9 @@ static struct div_nmp pllu_nmp = {
};
static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
{ 12000000, 480000000, 40, 1, 0, 0 },
{ 13000000, 480000000, 36, 1, 0, 0 }, /* actual: 468.0 MHz */
{ 38400000, 480000000, 25, 2, 0, 0 },
{ 12000000, 480000000, 40, 1, 1, 0 },
{ 13000000, 480000000, 36, 1, 1, 0 }, /* actual: 468.0 MHz */
{ 38400000, 480000000, 25, 2, 1, 0 },
{ 0, 0, 0, 0, 0, 0 },
};
@ -3343,7 +3348,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA210_CLK_DFLL_SOC, TEGRA210_CLK_PLL_P, 51000000, 1 },
{ TEGRA210_CLK_DFLL_REF, TEGRA210_CLK_PLL_P, 51000000, 1 },
{ TEGRA210_CLK_SBC4, TEGRA210_CLK_PLL_P, 12000000, 1 },
{ TEGRA210_CLK_PLL_RE_VCO, TEGRA210_CLK_CLK_MAX, 672000000, 1 },
{ TEGRA210_CLK_PLL_U_OUT1, TEGRA210_CLK_CLK_MAX, 48000000, 1 },
{ TEGRA210_CLK_XUSB_GATE, TEGRA210_CLK_CLK_MAX, 0, 1 },
{ TEGRA210_CLK_XUSB_SS_SRC, TEGRA210_CLK_PLL_U_480M, 120000000, 0 },
{ TEGRA210_CLK_XUSB_FS_SRC, TEGRA210_CLK_PLL_U_48M, 48000000, 0 },
@ -3368,7 +3373,6 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA210_CLK_PLL_DP, TEGRA210_CLK_CLK_MAX, 270000000, 0 },
{ TEGRA210_CLK_SOC_THERM, TEGRA210_CLK_PLL_P, 51000000, 0 },
{ TEGRA210_CLK_CCLK_G, TEGRA210_CLK_CLK_MAX, 0, 1 },
{ TEGRA210_CLK_PLL_U_OUT1, TEGRA210_CLK_CLK_MAX, 48000000, 1 },
{ TEGRA210_CLK_PLL_U_OUT2, TEGRA210_CLK_CLK_MAX, 60000000, 1 },
{ TEGRA210_CLK_SPDIF_IN_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },
{ TEGRA210_CLK_I2S0_SYNC, TEGRA210_CLK_CLK_MAX, 24576000, 0 },

View file

@ -20,6 +20,7 @@
#define MBOX_CHAN_PROPERTY 8
static struct platform_device *rpi_hwmon;
static struct platform_device *rpi_clk;
struct rpi_firmware {
struct mbox_client cl;
@ -207,6 +208,12 @@ rpi_register_hwmon_driver(struct device *dev, struct rpi_firmware *fw)
-1, NULL, 0);
}
static void rpi_register_clk_driver(struct device *dev)
{
rpi_clk = platform_device_register_data(dev, "raspberrypi-clk",
-1, NULL, 0);
}
static int rpi_firmware_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -234,6 +241,7 @@ static int rpi_firmware_probe(struct platform_device *pdev)
rpi_firmware_print_firmware_revision(fw);
rpi_register_hwmon_driver(dev, fw);
rpi_register_clk_driver(dev);
return 0;
}
@ -254,6 +262,8 @@ static int rpi_firmware_remove(struct platform_device *pdev)
platform_device_unregister(rpi_hwmon);
rpi_hwmon = NULL;
platform_device_unregister(rpi_clk);
rpi_clk = NULL;
mbox_free_channel(fw->chan);
return 0;

View file

@ -9,8 +9,6 @@
#include <linux/of.h>
#include <linux/of_clk.h>
#ifdef CONFIG_COMMON_CLK
/*
* flags used across common struct clk. these flags should only affect the
* top-level framework. custom flags for dealing with hardware specifics
@ -807,7 +805,14 @@ void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw);
/* helper functions */
const char *__clk_get_name(const struct clk *clk);
const char *clk_hw_get_name(const struct clk_hw *hw);
#ifdef CONFIG_COMMON_CLK
struct clk_hw *__clk_get_hw(struct clk *clk);
#else
static inline struct clk_hw *__clk_get_hw(struct clk *clk)
{
return (struct clk_hw *)clk;
}
#endif
unsigned int clk_hw_get_num_parents(const struct clk_hw *hw);
struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw);
struct clk_hw *clk_hw_get_parent_by_index(const struct clk_hw *hw,
@ -867,8 +872,6 @@ static inline long divider_ro_round_rate(struct clk_hw *hw, unsigned long rate,
*/
unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate);
struct of_device_id;
struct clk_onecell_data {
struct clk **clks;
unsigned int clk_num;
@ -879,8 +882,6 @@ struct clk_hw_onecell_data {
struct clk_hw *hws[];
};
extern struct of_device_id __clk_of_table;
#define CLK_OF_DECLARE(name, compat, fn) OF_DECLARE_1(clk, name, compat, fn)
/*
@ -1108,5 +1109,4 @@ static inline int of_clk_detect_critical(struct device_node *np, int index,
void clk_gate_restore_context(struct clk_hw *hw);
#endif /* CONFIG_COMMON_CLK */
#endif /* CLK_PROVIDER_H */