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:
Stephen Boyd 2017-10-31 16:25:38 -07:00
commit b177571b9d
15 changed files with 217 additions and 36 deletions

View File

@ -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. CPU and GPU clocks, and several fixed ratio dividers.
The CPG also provides a Clock Domain for SoC devices, in combination with the The CPG also provides a Clock Domain for SoC devices, in combination with the
CPG Module Stop (MSTP) Clocks. CPG Module Stop (MSTP) Clocks.

View File

@ -11428,6 +11428,7 @@ F: include/linux/rpmsg/
RENESAS CLOCK DRIVERS RENESAS CLOCK DRIVERS
M: Geert Uytterhoeven <geert+renesas@glider.be> M: Geert Uytterhoeven <geert+renesas@glider.be>
L: linux-renesas-soc@vger.kernel.org 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 S: Supported
F: drivers/clk/renesas/ F: drivers/clk/renesas/

View File

@ -14,8 +14,10 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/notifier.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/pm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "clk-div6.h" #include "clk-div6.h"
@ -32,6 +34,7 @@
* @src_shift: Shift to access the register bits to select the parent clock * @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) * @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 * @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 div6_clock {
struct clk_hw hw; struct clk_hw hw;
@ -40,6 +43,7 @@ struct div6_clock {
u32 src_shift; u32 src_shift;
u32 src_width; u32 src_width;
u8 *parents; u8 *parents;
struct notifier_block nb;
}; };
#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw) #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, .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 * 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) * @num_parents: Number of parent clocks of the DIV6 clock (1, 4, or 8)
* @parent_names: Array containing the names of the parent clocks * @parent_names: Array containing the names of the parent clocks
* @reg: Mapped register used to control the DIV6 clock * @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, struct clk * __init cpg_div6_register(const char *name,
unsigned int num_parents, unsigned int num_parents,
const char **parent_names, const char **parent_names,
void __iomem *reg) void __iomem *reg,
struct raw_notifier_head *notifiers)
{ {
unsigned int valid_parents; unsigned int valid_parents;
struct clk_init_data init; struct clk_init_data init;
@ -258,6 +287,11 @@ struct clk * __init cpg_div6_register(const char *name,
if (IS_ERR(clk)) if (IS_ERR(clk))
goto free_parents; goto free_parents;
if (notifiers) {
clock->nb.notifier_call = cpg_div6_clock_notifier_call;
raw_notifier_chain_register(notifiers, &clock->nb);
}
return clk; return clk;
free_parents: free_parents:
@ -301,7 +335,7 @@ static void __init cpg_div6_clock_init(struct device_node *np)
for (i = 0; i < num_parents; i++) for (i = 0; i < num_parents; i++)
parent_names[i] = of_clk_get_parent_name(np, 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)) { if (IS_ERR(clk)) {
pr_err("%s: failed to register %s DIV6 clock (%ld)\n", pr_err("%s: failed to register %s DIV6 clock (%ld)\n",
__func__, np->name, PTR_ERR(clk)); __func__, np->name, PTR_ERR(clk));

View File

@ -2,6 +2,7 @@
#define __RENESAS_CLK_DIV6_H__ #define __RENESAS_CLK_DIV6_H__
struct clk *cpg_div6_register(const char *name, unsigned int num_parents, 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 #endif

View File

@ -1,5 +1,5 @@
/* /*
* rz Core CPG Clocks * RZ/A1 Core CPG Clocks
* *
* Copyright (C) 2013 Ideas On Board SPRL * Copyright (C) 2013 Ideas On Board SPRL
* Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> * Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>

View File

@ -129,6 +129,7 @@ static const struct mssr_mod_clk r8a7745_mod_clks[] __initconst = {
DEF_MOD("scif2", 719, R8A7745_CLK_P), DEF_MOD("scif2", 719, R8A7745_CLK_P),
DEF_MOD("scif1", 720, R8A7745_CLK_P), DEF_MOD("scif1", 720, R8A7745_CLK_P),
DEF_MOD("scif0", 721, 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("du0", 724, R8A7745_CLK_ZX),
DEF_MOD("ipmmu-sgx", 800, R8A7745_CLK_ZX), DEF_MOD("ipmmu-sgx", 800, R8A7745_CLK_ZX),
DEF_MOD("vin1", 810, R8A7745_CLK_ZG), DEF_MOD("vin1", 810, R8A7745_CLK_ZG),

View File

@ -149,7 +149,7 @@ static struct mssr_mod_clk r8a7795_mod_clks[] __initdata = {
DEF_MOD("usb-dmac1", 331, R8A7795_CLK_S3D1), DEF_MOD("usb-dmac1", 331, R8A7795_CLK_S3D1),
DEF_MOD("rwdt", 402, R8A7795_CLK_R), DEF_MOD("rwdt", 402, R8A7795_CLK_R),
DEF_MOD("intc-ex", 407, R8A7795_CLK_CP), 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("audmac1", 501, R8A7795_CLK_S0D3),
DEF_MOD("audmac0", 502, R8A7795_CLK_S0D3), DEF_MOD("audmac0", 502, R8A7795_CLK_S0D3),
DEF_MOD("drif7", 508, R8A7795_CLK_S3D2), 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(217), R8A7795_CLK_S3D1 }, /* SYS-DMAC2 */
{ MOD_CLK_ID(218), R8A7795_CLK_S3D1 }, /* SYS-DMAC1 */ { MOD_CLK_ID(218), R8A7795_CLK_S3D1 }, /* SYS-DMAC1 */
{ MOD_CLK_ID(219), R8A7795_CLK_S3D1 }, /* SYS-DMAC0 */ { 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(501), R8A7795_CLK_S3D1 }, /* AUDMAC1 */
{ MOD_CLK_ID(502), R8A7795_CLK_S3D1 }, /* AUDMAC0 */ { MOD_CLK_ID(502), R8A7795_CLK_S3D1 }, /* AUDMAC0 */
{ MOD_CLK_ID(523), R8A7795_CLK_S3D4 }, /* PWM */ { MOD_CLK_ID(523), R8A7795_CLK_S3D4 }, /* PWM */

View File

@ -143,7 +143,7 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = {
DEF_MOD("usb-dmac1", 331, R8A7796_CLK_S3D1), DEF_MOD("usb-dmac1", 331, R8A7796_CLK_S3D1),
DEF_MOD("rwdt", 402, R8A7796_CLK_R), DEF_MOD("rwdt", 402, R8A7796_CLK_R),
DEF_MOD("intc-ex", 407, R8A7796_CLK_CP), 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("audmac1", 501, R8A7796_CLK_S0D3),
DEF_MOD("audmac0", 502, R8A7796_CLK_S0D3), DEF_MOD("audmac0", 502, R8A7796_CLK_S0D3),
DEF_MOD("drif7", 508, R8A7796_CLK_S3D2), DEF_MOD("drif7", 508, R8A7796_CLK_S3D2),

View File

@ -127,7 +127,7 @@ static const struct mssr_mod_clk r8a77995_mod_clks[] __initconst = {
DEF_MOD("usb-dmac1", 331, R8A77995_CLK_S3D1), DEF_MOD("usb-dmac1", 331, R8A77995_CLK_S3D1),
DEF_MOD("rwdt", 402, R8A77995_CLK_R), DEF_MOD("rwdt", 402, R8A77995_CLK_R),
DEF_MOD("intc-ex", 407, R8A77995_CLK_CP), 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("audmac0", 502, R8A77995_CLK_S3D1),
DEF_MOD("hscif3", 517, R8A77995_CLK_S3D1C), DEF_MOD("hscif3", 517, R8A77995_CLK_S3D1C),
DEF_MOD("hscif0", 520, R8A77995_CLK_S3D1C), DEF_MOD("hscif0", 520, R8A77995_CLK_S3D1C),

View File

@ -262,10 +262,9 @@ static unsigned int cpg_pll0_div __initdata;
static u32 cpg_mode __initdata; static u32 cpg_mode __initdata;
struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev, struct clk * __init rcar_gen2_cpg_clk_register(struct device *dev,
const struct cpg_core_clk *core, const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
const struct cpg_mssr_info *info, struct clk **clks, void __iomem *base,
struct clk **clks, struct raw_notifier_head *notifiers)
void __iomem *base)
{ {
const struct clk_div_table *table = NULL; const struct clk_div_table *table = NULL;
const struct clk *parent; const struct clk *parent;

View File

@ -34,9 +34,9 @@ struct rcar_gen2_cpg_pll_config {
}; };
struct clk *rcar_gen2_cpg_clk_register(struct device *dev, struct clk *rcar_gen2_cpg_clk_register(struct device *dev,
const struct cpg_core_clk *core, const struct cpg_core_clk *core, const struct cpg_mssr_info *info,
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_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config, int rcar_gen2_cpg_init(const struct rcar_gen2_cpg_pll_config *config,
unsigned int pll0_div, u32 mode); unsigned int pll0_div, u32 mode);

View File

@ -19,6 +19,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/pm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sys_soc.h> #include <linux/sys_soc.h>
@ -29,6 +30,36 @@
#define CPG_PLL2CR 0x002c #define CPG_PLL2CR 0x002c
#define CPG_PLL4CR 0x01f4 #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 * SDn Clock
@ -55,8 +86,8 @@ struct sd_div_table {
struct sd_clock { struct sd_clock {
struct clk_hw hw; struct clk_hw hw;
void __iomem *reg;
const struct sd_div_table *div_table; const struct sd_div_table *div_table;
struct cpg_simple_notifier csn;
unsigned int div_num; unsigned int div_num;
unsigned int div_min; unsigned int div_min;
unsigned int div_max; 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) static int cpg_sd_clock_enable(struct clk_hw *hw)
{ {
struct sd_clock *clock = to_sd_clock(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 &= ~(CPG_SD_STP_MASK);
val |= clock->div_table[clock->cur_div_idx].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; return 0;
} }
@ -111,14 +142,14 @@ static void cpg_sd_clock_disable(struct clk_hw *hw)
{ {
struct sd_clock *clock = to_sd_clock(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) static int cpg_sd_clock_is_enabled(struct clk_hw *hw)
{ {
struct sd_clock *clock = to_sd_clock(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, 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; clock->cur_div_idx = i;
val = readl(clock->reg); val = readl(clock->csn.reg);
val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK);
val |= clock->div_table[i].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; 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, static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
void __iomem *base, void __iomem *base, const char *parent_name,
const char *parent_name) struct raw_notifier_head *notifiers)
{ {
struct clk_init_data init; struct clk_init_data init;
struct sd_clock *clock; 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.parent_names = &parent_name;
init.num_parents = 1; init.num_parents = 1;
clock->reg = base + core->offset; clock->csn.reg = base + core->offset;
clock->hw.init = &init; clock->hw.init = &init;
clock->div_table = cpg_sd_div_table; clock->div_table = cpg_sd_div_table;
clock->div_num = ARRAY_SIZE(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++) for (i = 0; i < clock->div_num; i++)
if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK))
break; 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); clk = clk_register(NULL, &clock->hw);
if (IS_ERR(clk)) if (IS_ERR(clk))
kfree(clock); goto free_clock;
cpg_simple_notifier_register(notifiers, &clock->csn);
return clk;
free_clock:
kfree(clock);
return clk; 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, struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
const struct cpg_core_clk *core, const struct cpg_mssr_info *info, 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; const struct clk *parent;
unsigned int mult = 1; unsigned int mult = 1;
@ -331,22 +368,32 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
break; break;
case CLK_TYPE_GEN3_SD: 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: case CLK_TYPE_GEN3_R:
if (cpg_quirks & RCKCR_CKSEL) { 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. * RINT is default.
* Only if EXTALR is populated, we switch to it. * 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])) { if (clk_get_rate(clks[cpg_clk_extalr])) {
parent = clks[cpg_clk_extalr]; parent = clks[cpg_clk_extalr];
value |= BIT(15); value |= BIT(15);
} }
writel(value, base + CPG_RCKCR); writel(value, csn->reg);
cpg_simple_notifier_register(notifiers, csn);
break; break;
} }

View File

@ -44,7 +44,8 @@ struct rcar_gen3_cpg_pll_config {
struct clk *rcar_gen3_cpg_clk_register(struct device *dev, struct clk *rcar_gen3_cpg_clk_register(struct device *dev,
const struct cpg_core_clk *core, const struct cpg_mssr_info *info, 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, int rcar_gen3_cpg_init(const struct rcar_gen3_cpg_pll_config *config,
unsigned int clk_extalr, u32 mode); unsigned int clk_extalr, u32 mode);

View File

@ -26,6 +26,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_clock.h> #include <linux/pm_clock.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/psci.h>
#include <linux/reset-controller.h> #include <linux/reset-controller.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -106,6 +107,9 @@ static const u16 srcr[] = {
* @num_core_clks: Number of Core Clocks in clks[] * @num_core_clks: Number of Core Clocks in clks[]
* @num_mod_clks: Number of Module 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 * @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 { struct cpg_mssr_priv {
#ifdef CONFIG_RESET_CONTROLLER #ifdef CONFIG_RESET_CONTROLLER
@ -119,6 +123,12 @@ struct cpg_mssr_priv {
unsigned int num_core_clks; unsigned int num_core_clks;
unsigned int num_mod_clks; unsigned int num_mod_clks;
unsigned int last_dt_core_clk; 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) { if (core->type == CLK_TYPE_DIV6P1) {
clk = cpg_div6_register(core->name, 1, &parent_name, clk = cpg_div6_register(core->name, 1, &parent_name,
priv->base + core->offset); priv->base + core->offset,
&priv->notifiers);
} else { } else {
clk = clk_register_fixed_factor(NULL, core->name, clk = clk_register_fixed_factor(NULL, core->name,
parent_name, 0, parent_name, 0,
@ -304,7 +315,8 @@ static void __init cpg_mssr_register_core_clk(const struct cpg_core_clk *core,
default: default:
if (info->cpg_clk_register) if (info->cpg_clk_register)
clk = info->cpg_clk_register(dev, core, info, clk = info->cpg_clk_register(dev, core, info,
priv->clks, priv->base); priv->clks, priv->base,
&priv->notifiers);
else else
dev_err(dev, "%s has unsupported core clock type %u\n", dev_err(dev, "%s has unsupported core clock type %u\n",
core->name, core->type); 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); dev_dbg(dev, "Module clock %pC at %pCr Hz\n", clk, clk);
priv->clks[id] = clk; priv->clks[id] = clk;
priv->smstpcr_saved[clock->index / 32].mask |= BIT(clock->index % 32);
return; return;
fail: fail:
@ -700,6 +713,85 @@ static void cpg_mssr_del_clk_provider(void *data)
of_clk_del_provider(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) static int __init cpg_mssr_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -735,10 +827,12 @@ static int __init cpg_mssr_probe(struct platform_device *pdev)
if (!clks) if (!clks)
return -ENOMEM; return -ENOMEM;
dev_set_drvdata(dev, priv);
priv->clks = clks; priv->clks = clks;
priv->num_core_clks = info->num_total_core_clks; priv->num_core_clks = info->num_total_core_clks;
priv->num_mod_clks = info->num_hw_mod_clks; priv->num_mod_clks = info->num_hw_mod_clks;
priv->last_dt_core_clk = info->last_dt_core_clk; priv->last_dt_core_clk = info->last_dt_core_clk;
RAW_INIT_NOTIFIER_HEAD(&priv->notifiers);
for (i = 0; i < nclks; i++) for (i = 0; i < nclks; i++)
clks[i] = ERR_PTR(-ENOENT); clks[i] = ERR_PTR(-ENOENT);
@ -775,6 +869,7 @@ static struct platform_driver cpg_mssr_driver = {
.driver = { .driver = {
.name = "renesas-cpg-mssr", .name = "renesas-cpg-mssr",
.of_match_table = cpg_mssr_match, .of_match_table = cpg_mssr_match,
.pm = DEV_PM_OPS,
}, },
}; };

View File

@ -127,7 +127,8 @@ struct cpg_mssr_info {
struct clk *(*cpg_clk_register)(struct device *dev, struct clk *(*cpg_clk_register)(struct device *dev,
const struct cpg_core_clk *core, const struct cpg_core_clk *core,
const struct cpg_mssr_info *info, 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; extern const struct cpg_mssr_info r8a7743_cpg_mssr_info;