clk: renesas: Updates for v4.15 (take two)
- Add support for the second display unit clock on RZ/G1E, - Add git repository to MAINTAINERS, - Add suspend/resume support for R-Car Gen3 CPG/MSSR, - Small fixes and cleanups. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJZ6cIgAAoJEEgEtLw/Ve77p20P/jGAE3EV3vWHmzjE3KenFv8O mxFMEIK0aCvP80BfbWrYUJqBD36hncTodT3k3drZ52HrkrKUE21CqkzS7sn4OpvG RbMssZWg6LntfqfJU4skQ3KeJsdz2Zz3L8MkbOqPO8bZSKCEKddU5buKjXhBzygJ zXXVqMLvMiUrq9JXqLQesxnkBOclsSyO9TyXrBNU9bTBA+wqGe4Ofk64i0wQrcNh Miz4VYgYL8ul1SjVCVu1vTe+l3/JoSCb9ZqrGA5r+8dQiTKMvQPiUEhnzloU2PRK ufZo7c3CWSQabVWONpttPrR81F/yn6ps6GVPN6Lm2QzXs3UQGzMdd55gnZqHq6T4 AxCrt38cE7VdrOwqnKAp37NBRH7hP2Fi4DXQwZJE2QTc6hlb8YYBbgRgYaNE1k3F WTKYCmd2VAqBYUw+SF+ZC+1/hJUgtOi09PFXwV9aH2WWnMuNMoOi2+CFPCg0jRkQ 9GKGbmy9cKMurBqb/29tW0EeCUbtQCxIuEiPBJx8lhlghc3jeEvtCdpvx8JiSjPg T68q9kI5H7/H9X6jUt0liOUBzZHhdWHdQ3+g2cnXFjgrQsBibzi3bC6YBx1u6ly1 x20inLXx5ZTQyVwAJN8SBdgkWgBPlHT6KQd2eiPnibtqZwVrGae2sWcMb587+d8n 9y9STlh6/xn+R78epvj2 =2sIL -----END PGP SIGNATURE----- Merge tag 'clk-renesas-for-v4.15-tag2' of git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers into clk-next Pull Renesas clk driver updates from Geert Uytterhoeven: - Add support for the second display unit clock on RZ/G1E, - Add git repository to MAINTAINERS, - Add suspend/resume support for R-Car Gen3 CPG/MSSR, - Small fixes and cleanups. * tag 'clk-renesas-for-v4.15-tag2' of git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers: clk: renesas: rcar-gen3: Restore R clock during resume clk: renesas: rcar-gen3: Restore SDHI clocks during resume clk: renesas: div6: Restore clock state during resume clk: renesas: cpg-mssr: Add support to restore core clocks during resume clk: renesas: cpg-mssr: Restore module clocks during resume MAINTAINERS: Add git repository to Renesas clock driver section clk: renesas: cpg-mssr: Add du1 clock to R8A7745 clk: renesas: rz: clk-rz is meant for RZ/A1 clk: renesas: r8a77995: Correct parent clock of INTC-AP clk: renesas: r8a7796: Correct parent clock of INTC-AP clk: renesas: r8a7795: Correct parent clock of INTC-AP
This commit is contained in:
Коммит
b177571b9d
|
@ -1,6 +1,6 @@
|
|||
* Renesas RZ Clock Pulse Generator (CPG)
|
||||
* Renesas RZ/A1 Clock Pulse Generator (CPG)
|
||||
|
||||
The CPG generates core clocks for the RZ SoCs. It includes the PLL, variable
|
||||
The CPG generates core clocks for the RZ/A1 SoCs. It includes the PLL, variable
|
||||
CPU and GPU clocks, and several fixed ratio dividers.
|
||||
The CPG also provides a Clock Domain for SoC devices, in combination with the
|
||||
CPG Module Stop (MSTP) Clocks.
|
||||
|
|
|
@ -11428,6 +11428,7 @@ F: include/linux/rpmsg/
|
|||
RENESAS CLOCK DRIVERS
|
||||
M: Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git clk-renesas
|
||||
S: Supported
|
||||
F: drivers/clk/renesas/
|
||||
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk-div6.h"
|
||||
|
@ -32,6 +34,7 @@
|
|||
* @src_shift: Shift to access the register bits to select the parent clock
|
||||
* @src_width: Number of register bits to select the parent clock (may be 0)
|
||||
* @parents: Array to map from valid parent clocks indices to hardware indices
|
||||
* @nb: Notifier block to save/restore clock state for system resume
|
||||
*/
|
||||
struct div6_clock {
|
||||
struct clk_hw hw;
|
||||
|
@ -40,6 +43,7 @@ struct div6_clock {
|
|||
u32 src_shift;
|
||||
u32 src_width;
|
||||
u8 *parents;
|
||||
struct notifier_block nb;
|
||||
};
|
||||
|
||||
#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw)
|
||||
|
@ -176,6 +180,29 @@ static const struct clk_ops cpg_div6_clock_ops = {
|
|||
.set_rate = cpg_div6_clock_set_rate,
|
||||
};
|
||||
|
||||
static int cpg_div6_clock_notifier_call(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct div6_clock *clock = container_of(nb, struct div6_clock, nb);
|
||||
|
||||
switch (action) {
|
||||
case PM_EVENT_RESUME:
|
||||
/*
|
||||
* TODO: This does not yet support DIV6 clocks with multiple
|
||||
* parents, as the parent selection bits are not restored.
|
||||
* Fortunately so far such DIV6 clocks are found only on
|
||||
* R/SH-Mobile SoCs, while the resume functionality is only
|
||||
* needed on R-Car Gen3.
|
||||
*/
|
||||
if (__clk_get_enable_count(clock->hw.clk))
|
||||
cpg_div6_clock_enable(&clock->hw);
|
||||
else
|
||||
cpg_div6_clock_disable(&clock->hw);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* cpg_div6_register - Register a DIV6 clock
|
||||
|
@ -183,11 +210,13 @@ static const struct clk_ops cpg_div6_clock_ops = {
|
|||
* @num_parents: Number of parent clocks of the DIV6 clock (1, 4, or 8)
|
||||
* @parent_names: Array containing the names of the parent clocks
|
||||
* @reg: Mapped register used to control the DIV6 clock
|
||||
* @notifiers: Optional notifier chain to save/restore state for system resume
|
||||
*/
|
||||
struct clk * __init cpg_div6_register(const char *name,
|
||||
unsigned int num_parents,
|
||||
const char **parent_names,
|
||||
void __iomem *reg)
|
||||
void __iomem *reg,
|
||||
struct raw_notifier_head *notifiers)
|
||||
{
|
||||
unsigned int valid_parents;
|
||||
struct clk_init_data init;
|
||||
|
@ -258,6 +287,11 @@ struct clk * __init cpg_div6_register(const char *name,
|
|||
if (IS_ERR(clk))
|
||||
goto free_parents;
|
||||
|
||||
if (notifiers) {
|
||||
clock->nb.notifier_call = cpg_div6_clock_notifier_call;
|
||||
raw_notifier_chain_register(notifiers, &clock->nb);
|
||||
}
|
||||
|
||||
return clk;
|
||||
|
||||
free_parents:
|
||||
|
@ -301,7 +335,7 @@ static void __init cpg_div6_clock_init(struct device_node *np)
|
|||
for (i = 0; i < num_parents; i++)
|
||||
parent_names[i] = of_clk_get_parent_name(np, i);
|
||||
|
||||
clk = cpg_div6_register(clk_name, num_parents, parent_names, reg);
|
||||
clk = cpg_div6_register(clk_name, num_parents, parent_names, reg, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register %s DIV6 clock (%ld)\n",
|
||||
__func__, np->name, PTR_ERR(clk));
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define __RENESAS_CLK_DIV6_H__
|
||||
|
||||
struct clk *cpg_div6_register(const char *name, unsigned int num_parents,
|
||||
const char **parent_names, void __iomem *reg);
|
||||
const char **parent_names, void __iomem *reg,
|
||||
struct raw_notifier_head *notifiers);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* rz Core CPG Clocks
|
||||
* RZ/A1 Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2013 Ideas On Board SPRL
|
||||
* Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
|
||||
|
|
|
@ -129,6 +129,7 @@ static const struct mssr_mod_clk r8a7745_mod_clks[] __initconst = {
|
|||
DEF_MOD("scif2", 719, R8A7745_CLK_P),
|
||||
DEF_MOD("scif1", 720, R8A7745_CLK_P),
|
||||
DEF_MOD("scif0", 721, R8A7745_CLK_P),
|
||||
DEF_MOD("du1", 723, R8A7745_CLK_ZX),
|
||||
DEF_MOD("du0", 724, R8A7745_CLK_ZX),
|
||||
DEF_MOD("ipmmu-sgx", 800, R8A7745_CLK_ZX),
|
||||
DEF_MOD("vin1", 810, R8A7745_CLK_ZG),
|
||||
|
|
|
@ -149,7 +149,7 @@ static struct mssr_mod_clk r8a7795_mod_clks[] __initdata = {
|
|||
DEF_MOD("usb-dmac1", 331, R8A7795_CLK_S3D1),
|
||||
DEF_MOD("rwdt", 402, R8A7795_CLK_R),
|
||||
DEF_MOD("intc-ex", 407, R8A7795_CLK_CP),
|
||||
DEF_MOD("intc-ap", 408, R8A7795_CLK_S3D1),
|
||||
DEF_MOD("intc-ap", 408, R8A7795_CLK_S0D3),
|
||||
DEF_MOD("audmac1", 501, R8A7795_CLK_S0D3),
|
||||
DEF_MOD("audmac0", 502, R8A7795_CLK_S0D3),
|
||||
DEF_MOD("drif7", 508, R8A7795_CLK_S3D2),
|
||||
|
@ -348,6 +348,7 @@ static const struct mssr_mod_reparent r8a7795es1_mod_reparent[] __initconst = {
|
|||
{ MOD_CLK_ID(217), R8A7795_CLK_S3D1 }, /* SYS-DMAC2 */
|
||||
{ MOD_CLK_ID(218), R8A7795_CLK_S3D1 }, /* SYS-DMAC1 */
|
||||
{ MOD_CLK_ID(219), R8A7795_CLK_S3D1 }, /* SYS-DMAC0 */
|
||||
{ MOD_CLK_ID(408), R8A7795_CLK_S3D1 }, /* INTC-AP */
|
||||
{ MOD_CLK_ID(501), R8A7795_CLK_S3D1 }, /* AUDMAC1 */
|
||||
{ MOD_CLK_ID(502), R8A7795_CLK_S3D1 }, /* AUDMAC0 */
|
||||
{ MOD_CLK_ID(523), R8A7795_CLK_S3D4 }, /* PWM */
|
||||
|
|
|
@ -143,7 +143,7 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = {
|
|||
DEF_MOD("usb-dmac1", 331, R8A7796_CLK_S3D1),
|
||||
DEF_MOD("rwdt", 402, R8A7796_CLK_R),
|
||||
DEF_MOD("intc-ex", 407, R8A7796_CLK_CP),
|
||||
DEF_MOD("intc-ap", 408, R8A7796_CLK_S3D1),
|
||||
DEF_MOD("intc-ap", 408, R8A7796_CLK_S0D3),
|
||||
DEF_MOD("audmac1", 501, R8A7796_CLK_S0D3),
|
||||
DEF_MOD("audmac0", 502, R8A7796_CLK_S0D3),
|
||||
DEF_MOD("drif7", 508, R8A7796_CLK_S3D2),
|
||||
|
|
|
@ -127,7 +127,7 @@ static const struct mssr_mod_clk r8a77995_mod_clks[] __initconst = {
|
|||
DEF_MOD("usb-dmac1", 331, R8A77995_CLK_S3D1),
|
||||
DEF_MOD("rwdt", 402, R8A77995_CLK_R),
|
||||
DEF_MOD("intc-ex", 407, R8A77995_CLK_CP),
|
||||
DEF_MOD("intc-ap", 408, R8A77995_CLK_S3D1),
|
||||
DEF_MOD("intc-ap", 408, R8A77995_CLK_S1D2),
|
||||
DEF_MOD("audmac0", 502, R8A77995_CLK_S3D1),
|
||||
DEF_MOD("hscif3", 517, R8A77995_CLK_S3D1C),
|
||||
DEF_MOD("hscif0", 520, R8A77995_CLK_S3D1C),
|
||||
|
|
|
@ -262,10 +262,9 @@ static unsigned int cpg_pll0_div __initdata;
|
|||
static u32 cpg_mode __initdata;
|
||||
|
||||
struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev,
|
||||
const struct cpg_core_clk *core,
|
||||
const struct cpg_mssr_info *info,
|
||||
struct clk **clks,
|
||||
void __iomem *base)
|
||||
const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
|
||||
struct clk **clks, void __iomem *base,
|
||||
struct raw_notifier_head *notifiers)
|
||||
{
|
||||
const struct clk_div_table *table = NULL;
|
||||
const struct clk *parent;
|
||||
|
|
|
@ -34,9 +34,9 @@ struct rcar_gen2_cpg_pll_config {
|
|||
};
|
||||
|
||||
struct clk *rcar_gen2_cpg_clk_register(struct device *dev,
|
||||
const struct cpg_core_clk *core,
|
||||
const struct cpg_mssr_info *info,
|
||||
struct clk **clks, void __iomem *base);
|
||||
const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
|
||||
struct clk **clks, void __iomem *base,
|
||||
struct raw_notifier_head *notifiers);
|
||||
int rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config,
|
||||
unsigned int pll0_div, u32 mode);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sys_soc.h>
|
||||
|
||||
|
@ -29,6 +30,36 @@
|
|||
#define CPG_PLL2CR 0x002c
|
||||
#define CPG_PLL4CR 0x01f4
|
||||
|
||||
struct cpg_simple_notifier {
|
||||
struct notifier_block nb;
|
||||
void __iomem *reg;
|
||||
u32 saved;
|
||||
};
|
||||
|
||||
static int cpg_simple_notifier_call(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct cpg_simple_notifier *csn =
|
||||
container_of(nb, struct cpg_simple_notifier, nb);
|
||||
|
||||
switch (action) {
|
||||
case PM_EVENT_SUSPEND:
|
||||
csn->saved = readl(csn->reg);
|
||||
return NOTIFY_OK;
|
||||
|
||||
case PM_EVENT_RESUME:
|
||||
writel(csn->saved, csn->reg);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void cpg_simple_notifier_register(struct raw_notifier_head *notifiers,
|
||||
struct cpg_simple_notifier *csn)
|
||||
{
|
||||
csn->nb.notifier_call = cpg_simple_notifier_call;
|
||||
raw_notifier_chain_register(notifiers, &csn->nb);
|
||||
}
|
||||
|
||||
/*
|
||||
* SDn Clock
|
||||
|
@ -55,8 +86,8 @@ struct sd_div_table {
|
|||
|
||||
struct sd_clock {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
const struct sd_div_table *div_table;
|
||||
struct cpg_simple_notifier csn;
|
||||
unsigned int div_num;
|
||||
unsigned int div_min;
|
||||
unsigned int div_max;
|
||||
|
@ -97,12 +128,12 @@ static const struct sd_div_table cpg_sd_div_table[] = {
|
|||
static int cpg_sd_clock_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct sd_clock *clock = to_sd_clock(hw);
|
||||
u32 val = readl(clock->reg);
|
||||
u32 val = readl(clock->csn.reg);
|
||||
|
||||
val &= ~(CPG_SD_STP_MASK);
|
||||
val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK;
|
||||
|
||||
writel(val, clock->reg);
|
||||
writel(val, clock->csn.reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -111,14 +142,14 @@ static void cpg_sd_clock_disable(struct clk_hw *hw)
|
|||
{
|
||||
struct sd_clock *clock = to_sd_clock(hw);
|
||||
|
||||
writel(readl(clock->reg) | CPG_SD_STP_MASK, clock->reg);
|
||||
writel(readl(clock->csn.reg) | CPG_SD_STP_MASK, clock->csn.reg);
|
||||
}
|
||||
|
||||
static int cpg_sd_clock_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct sd_clock *clock = to_sd_clock(hw);
|
||||
|
||||
return !(readl(clock->reg) & CPG_SD_STP_MASK);
|
||||
return !(readl(clock->csn.reg) & CPG_SD_STP_MASK);
|
||||
}
|
||||
|
||||
static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw,
|
||||
|
@ -170,10 +201,10 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
|
||||
clock->cur_div_idx = i;
|
||||
|
||||
val = readl(clock->reg);
|
||||
val = readl(clock->csn.reg);
|
||||
val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK);
|
||||
val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK);
|
||||
writel(val, clock->reg);
|
||||
writel(val, clock->csn.reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -188,8 +219,8 @@ static const struct clk_ops cpg_sd_clock_ops = {
|
|||
};
|
||||
|
||||
static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
|
||||
void __iomem *base,
|
||||
const char *parent_name)
|
||||
void __iomem *base, const char *parent_name,
|
||||
struct raw_notifier_head *notifiers)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct sd_clock *clock;
|
||||
|
@ -207,12 +238,12 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
|
|||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
clock->reg = base + core->offset;
|
||||
clock->csn.reg = base + core->offset;
|
||||
clock->hw.init = &init;
|
||||
clock->div_table = cpg_sd_div_table;
|
||||
clock->div_num = ARRAY_SIZE(cpg_sd_div_table);
|
||||
|
||||
sd_fc = readl(clock->reg) & CPG_SD_FC_MASK;
|
||||
sd_fc = readl(clock->csn.reg) & CPG_SD_FC_MASK;
|
||||
for (i = 0; i < clock->div_num; i++)
|
||||
if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK))
|
||||
break;
|
||||
|
@ -233,8 +264,13 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
|
|||
|
||||
clk = clk_register(NULL, &clock->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(clock);
|
||||
goto free_clock;
|
||||
|
||||
cpg_simple_notifier_register(notifiers, &clock->csn);
|
||||
return clk;
|
||||
|
||||
free_clock:
|
||||
kfree(clock);
|
||||
return clk;
|
||||
}
|
||||
|
||||
|
@ -265,7 +301,8 @@ static const struct soc_device_attribute cpg_quirks_match[] __initconst = {
|
|||
|
||||
struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
|
||||
const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
|
||||
struct clk **clks, void __iomem *base)
|
||||
struct clk **clks, void __iomem *base,
|
||||
struct raw_notifier_head *notifiers)
|
||||
{
|
||||
const struct clk *parent;
|
||||
unsigned int mult = 1;
|
||||
|
@ -331,22 +368,32 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
|
|||
break;
|
||||
|
||||
case CLK_TYPE_GEN3_SD:
|
||||
return cpg_sd_clk_register(core, base, __clk_get_name(parent));
|
||||
return cpg_sd_clk_register(core, base, __clk_get_name(parent),
|
||||
notifiers);
|
||||
|
||||
case CLK_TYPE_GEN3_R:
|
||||
if (cpg_quirks & RCKCR_CKSEL) {
|
||||
struct cpg_simple_notifier *csn;
|
||||
|
||||
csn = kzalloc(sizeof(*csn), GFP_KERNEL);
|
||||
if (!csn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
csn->reg = base + CPG_RCKCR;
|
||||
|
||||
/*
|
||||
* RINT is default.
|
||||
* Only if EXTALR is populated, we switch to it.
|
||||
*/
|
||||
value = readl(base + CPG_RCKCR) & 0x3f;
|
||||
value = readl(csn->reg) & 0x3f;
|
||||
|
||||
if (clk_get_rate(clks[cpg_clk_extalr])) {
|
||||
parent = clks[cpg_clk_extalr];
|
||||
value |= BIT(15);
|
||||
}
|
||||
|
||||
writel(value, base + CPG_RCKCR);
|
||||
writel(value, csn->reg);
|
||||
cpg_simple_notifier_register(notifiers, csn);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,8 @@ struct rcar_gen3_cpg_pll_config {
|
|||
|
||||
struct clk *rcar_gen3_cpg_clk_register(struct device *dev,
|
||||
const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
|
||||
struct clk **clks, void __iomem *base);
|
||||
struct clk **clks, void __iomem *base,
|
||||
struct raw_notifier_head *notifiers);
|
||||
int rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config,
|
||||
unsigned int clk_extalr, u32 mode);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_clock.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/psci.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -106,6 +107,9 @@ static const u16 srcr[] = {
|
|||
* @num_core_clks: Number of Core Clocks in clks[]
|
||||
* @num_mod_clks: Number of Module Clocks in clks[]
|
||||
* @last_dt_core_clk: ID of the last Core Clock exported to DT
|
||||
* @notifiers: Notifier chain to save/restore clock state for system resume
|
||||
* @smstpcr_saved[].mask: Mask of SMSTPCR[] bits under our control
|
||||
* @smstpcr_saved[].val: Saved values of SMSTPCR[]
|
||||
*/
|
||||
struct cpg_mssr_priv {
|
||||
#ifdef CONFIG_RESET_CONTROLLER
|
||||
|
@ -119,6 +123,12 @@ struct cpg_mssr_priv {
|
|||
unsigned int num_core_clks;
|
||||
unsigned int num_mod_clks;
|
||||
unsigned int last_dt_core_clk;
|
||||
|
||||
struct raw_notifier_head notifiers;
|
||||
struct {
|
||||
u32 mask;
|
||||
u32 val;
|
||||
} smstpcr_saved[ARRAY_SIZE(smstpcr)];
|
||||
};
|
||||
|
||||
|
||||
|
@ -293,7 +303,8 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core,
|
|||
|
||||
if (core->type == CLK_TYPE_DIV6P1) {
|
||||
clk = cpg_div6_register(core->name, 1, &parent_name,
|
||||
priv->base + core->offset);
|
||||
priv->base + core->offset,
|
||||
&priv->notifiers);
|
||||
} else {
|
||||
clk = clk_register_fixed_factor(NULL, core->name,
|
||||
parent_name, 0,
|
||||
|
@ -304,7 +315,8 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core,
|
|||
default:
|
||||
if (info->cpg_clk_register)
|
||||
clk = info->cpg_clk_register(dev, core, info,
|
||||
priv->clks, priv->base);
|
||||
priv->clks, priv->base,
|
||||
&priv->notifiers);
|
||||
else
|
||||
dev_err(dev, "%s has unsupported core clock type %u\n",
|
||||
core->name, core->type);
|
||||
|
@ -382,6 +394,7 @@ static void __init cpg_mssr_register_mod_clk(const struct mssr_mod_clk *mod,
|
|||
|
||||
dev_dbg(dev, "Module clock %pC at %pCr Hz\n", clk, clk);
|
||||
priv->clks[id] = clk;
|
||||
priv->smstpcr_saved[clock->index / 32].mask |= BIT(clock->index % 32);
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
@ -700,6 +713,85 @@ static void cpg_mssr_del_clk_provider(void *data)
|
|||
of_clk_del_provider(data);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM_PSCI_FW)
|
||||
static int cpg_mssr_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct cpg_mssr_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned int reg;
|
||||
|
||||
/* This is the best we can do to check for the presence of PSCI */
|
||||
if (!psci_ops.cpu_suspend)
|
||||
return 0;
|
||||
|
||||
/* Save module registers with bits under our control */
|
||||
for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) {
|
||||
if (priv->smstpcr_saved[reg].mask)
|
||||
priv->smstpcr_saved[reg].val =
|
||||
readl(priv->base + SMSTPCR(reg));
|
||||
}
|
||||
|
||||
/* Save core clocks */
|
||||
raw_notifier_call_chain(&priv->notifiers, PM_EVENT_SUSPEND, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpg_mssr_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct cpg_mssr_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned int reg, i;
|
||||
u32 mask, oldval, newval;
|
||||
|
||||
/* This is the best we can do to check for the presence of PSCI */
|
||||
if (!psci_ops.cpu_suspend)
|
||||
return 0;
|
||||
|
||||
/* Restore core clocks */
|
||||
raw_notifier_call_chain(&priv->notifiers, PM_EVENT_RESUME, NULL);
|
||||
|
||||
/* Restore module clocks */
|
||||
for (reg = 0; reg < ARRAY_SIZE(priv->smstpcr_saved); reg++) {
|
||||
mask = priv->smstpcr_saved[reg].mask;
|
||||
if (!mask)
|
||||
continue;
|
||||
|
||||
oldval = readl(priv->base + SMSTPCR(reg));
|
||||
newval = oldval & ~mask;
|
||||
newval |= priv->smstpcr_saved[reg].val & mask;
|
||||
if (newval == oldval)
|
||||
continue;
|
||||
|
||||
writel(newval, priv->base + SMSTPCR(reg));
|
||||
|
||||
/* Wait until enabled clocks are really enabled */
|
||||
mask &= ~priv->smstpcr_saved[reg].val;
|
||||
if (!mask)
|
||||
continue;
|
||||
|
||||
for (i = 1000; i > 0; --i) {
|
||||
oldval = readl(priv->base + MSTPSR(reg));
|
||||
if (!(oldval & mask))
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (!i)
|
||||
dev_warn(dev, "Failed to enable SMSTP %p[0x%x]\n",
|
||||
priv->base + SMSTPCR(reg), oldval & mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cpg_mssr_pm = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(cpg_mssr_suspend_noirq,
|
||||
cpg_mssr_resume_noirq)
|
||||
};
|
||||
#define DEV_PM_OPS &cpg_mssr_pm
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */
|
||||
|
||||
static int __init cpg_mssr_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -735,10 +827,12 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
|
|||
if (!clks)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
priv->clks = clks;
|
||||
priv->num_core_clks = info->num_total_core_clks;
|
||||
priv->num_mod_clks = info->num_hw_mod_clks;
|
||||
priv->last_dt_core_clk = info->last_dt_core_clk;
|
||||
RAW_INIT_NOTIFIER_HEAD(&priv->notifiers);
|
||||
|
||||
for (i = 0; i < nclks; i++)
|
||||
clks[i] = ERR_PTR(-ENOENT);
|
||||
|
@ -775,6 +869,7 @@ static struct platform_driver cpg_mssr_driver = {
|
|||
.driver = {
|
||||
.name = "renesas-cpg-mssr",
|
||||
.of_match_table = cpg_mssr_match,
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -127,7 +127,8 @@ struct cpg_mssr_info {
|
|||
struct clk *(*cpg_clk_register)(struct device *dev,
|
||||
const struct cpg_core_clk *core,
|
||||
const struct cpg_mssr_info *info,
|
||||
struct clk **clks, void __iomem *base);
|
||||
struct clk **clks, void __iomem *base,
|
||||
struct raw_notifier_head *notifiers);
|
||||
};
|
||||
|
||||
extern const struct cpg_mssr_info r8a7743_cpg_mssr_info;
|
||||
|
|
Загрузка…
Ссылка в новой задаче