Skip to content

Commit

Permalink
clk: mmp2: Add support for power islands
Browse files Browse the repository at this point in the history
Apart from the clocks and resets, the PMU hardware also controls power
to peripherals that are on separate power islands. On MMP2, that's the
GC860 GPU and the SSPA audio interface, while on MMP3 also the camera
interface is on a separate island, along with the pair of GC2000 and GC300
GPUs and the SSPA.

Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
Link: https://lkml.kernel.org/r/20200519224151.2074597-12-lkundrak@v3.sk
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
  • Loading branch information
Lubomir Rintel authored and Stephen Boyd committed May 28, 2020
1 parent 17d4304 commit ee4df23
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 1 deletion.
2 changes: 2 additions & 0 deletions arch/arm/mach-mmp/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ config MACH_MMP2_DT
select PINCTRL_SINGLE
select ARCH_HAS_RESET_CONTROLLER
select CPU_PJ4
select PM_GENERIC_DOMAINS if PM
select PM_GENERIC_DOMAINS_OF if PM && OF
help
Include support for Marvell MMP2 based platforms using
the device tree.
Expand Down
2 changes: 1 addition & 1 deletion drivers/clk/mmp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ obj-y += clk-apbc.o clk-apmu.o clk-frac.o clk-mix.o clk-gate.o clk.o
obj-$(CONFIG_RESET_CONTROLLER) += reset.o

obj-$(CONFIG_MACH_MMP_DT) += clk-of-pxa168.o clk-of-pxa910.o
obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o clk-pll.o
obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o clk-pll.o pwr-island.o

obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o
obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
Expand Down
42 changes: 42 additions & 0 deletions drivers/clk/mmp/clk-of-mmp2.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/of_address.h>
#include <linux/clk.h>

#include <dt-bindings/clock/marvell,mmp2.h>
#include <dt-bindings/power/marvell,mmp2.h>

#include "clk.h"
#include "reset.h"
Expand Down Expand Up @@ -63,6 +65,7 @@
#define APMU_USBHSIC1 0xfc
#define APMU_GPU 0xcc
#define APMU_AUDIO 0x10c
#define APMU_CAMERA 0x1fc

#define MPMU_FCCR 0x8
#define MPMU_POSR 0x10
Expand All @@ -86,6 +89,8 @@ enum mmp2_clk_model {
struct mmp2_clk_unit {
struct mmp_clk_unit unit;
enum mmp2_clk_model model;
struct genpd_onecell_data pd_data;
struct generic_pm_domain *pm_domains[MMP2_NR_POWER_DOMAINS];
void __iomem *mpmu_base;
void __iomem *apmu_base;
void __iomem *apbc_base;
Expand Down Expand Up @@ -473,6 +478,41 @@ static void mmp2_clk_reset_init(struct device_node *np,
mmp_clk_reset_register(np, cells, nr_resets);
}

static void mmp2_pm_domain_init(struct device_node *np,
struct mmp2_clk_unit *pxa_unit)
{
if (pxa_unit->model == CLK_MODEL_MMP3) {
pxa_unit->pm_domains[MMP2_POWER_DOMAIN_GPU]
= mmp_pm_domain_register("gpu",
pxa_unit->apmu_base + APMU_GPU,
0x0600, 0x40003, 0x18000c, 0, &gpu_lock);
} else {
pxa_unit->pm_domains[MMP2_POWER_DOMAIN_GPU]
= mmp_pm_domain_register("gpu",
pxa_unit->apmu_base + APMU_GPU,
0x8600, 0x00003, 0x00000c,
MMP_PM_DOMAIN_NO_DISABLE, &gpu_lock);
}
pxa_unit->pd_data.num_domains++;

pxa_unit->pm_domains[MMP2_POWER_DOMAIN_AUDIO]
= mmp_pm_domain_register("audio",
pxa_unit->apmu_base + APMU_AUDIO,
0x600, 0x2, 0, 0, &audio_lock);
pxa_unit->pd_data.num_domains++;

if (pxa_unit->model == CLK_MODEL_MMP3) {
pxa_unit->pm_domains[MMP3_POWER_DOMAIN_CAMERA]
= mmp_pm_domain_register("camera",
pxa_unit->apmu_base + APMU_CAMERA,
0x600, 0, 0, 0, NULL);
pxa_unit->pd_data.num_domains++;
}

pxa_unit->pd_data.domains = pxa_unit->pm_domains;
of_genpd_add_provider_onecell(np, &pxa_unit->pd_data);
}

static void __init mmp2_clk_init(struct device_node *np)
{
struct mmp2_clk_unit *pxa_unit;
Expand Down Expand Up @@ -504,6 +544,8 @@ static void __init mmp2_clk_init(struct device_node *np)
goto unmap_apmu_region;
}

mmp2_pm_domain_init(np, pxa_unit);

mmp_clk_init(np, &pxa_unit->unit, MMP2_NR_CLKS);

mmp2_main_clk_init(pxa_unit);
Expand Down
10 changes: 10 additions & 0 deletions drivers/clk/mmp/clk.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define __MACH_MMP_CLK_H

#include <linux/clk-provider.h>
#include <linux/pm_domain.h>
#include <linux/clkdev.h>

#define APBC_NO_BUS_CTRL BIT(0)
Expand Down Expand Up @@ -259,4 +260,13 @@ void mmp_clk_init(struct device_node *np, struct mmp_clk_unit *unit,
int nr_clks);
void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id,
struct clk *clk);

/* Power islands */
#define MMP_PM_DOMAIN_NO_DISABLE BIT(0)

struct generic_pm_domain *mmp_pm_domain_register(const char *name,
void __iomem *reg,
u32 power_on, u32 reset, u32 clock_enable,
unsigned int flags, spinlock_t *lock);

#endif
115 changes: 115 additions & 0 deletions drivers/clk/mmp/pwr-island.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MMP PMU power island support
*
* Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk>
*/

#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/io.h>

#include "clk.h"

#define to_mmp_pm_domain(genpd) container_of(genpd, struct mmp_pm_domain, genpd)

struct mmp_pm_domain {
struct generic_pm_domain genpd;
void __iomem *reg;
spinlock_t *lock;
u32 power_on;
u32 reset;
u32 clock_enable;
unsigned int flags;
};

static int mmp_pm_domain_power_on(struct generic_pm_domain *genpd)
{
struct mmp_pm_domain *pm_domain = to_mmp_pm_domain(genpd);
unsigned long flags = 0;
u32 val;

if (pm_domain->lock)
spin_lock_irqsave(pm_domain->lock, flags);

val = readl(pm_domain->reg);

/* Turn on the power island */
val |= pm_domain->power_on;
writel(val, pm_domain->reg);

/* Disable isolation */
val |= 0x100;
writel(val, pm_domain->reg);

/* Some blocks need to be reset after a power up */
if (pm_domain->reset || pm_domain->clock_enable) {
u32 after_power_on = val;

val &= ~pm_domain->reset;
writel(val, pm_domain->reg);

val |= pm_domain->clock_enable;
writel(val, pm_domain->reg);

val |= pm_domain->reset;
writel(val, pm_domain->reg);

writel(after_power_on, pm_domain->reg);
}

if (pm_domain->lock)
spin_unlock_irqrestore(pm_domain->lock, flags);

return 0;
}

static int mmp_pm_domain_power_off(struct generic_pm_domain *genpd)
{
struct mmp_pm_domain *pm_domain = to_mmp_pm_domain(genpd);
unsigned long flags = 0;
u32 val;

if (pm_domain->flags & MMP_PM_DOMAIN_NO_DISABLE)
return 0;

if (pm_domain->lock)
spin_lock_irqsave(pm_domain->lock, flags);

/* Turn off and isolate the the power island. */
val = readl(pm_domain->reg);
val &= ~pm_domain->power_on;
val &= ~0x100;
writel(val, pm_domain->reg);

if (pm_domain->lock)
spin_unlock_irqrestore(pm_domain->lock, flags);

return 0;
}

struct generic_pm_domain *mmp_pm_domain_register(const char *name,
void __iomem *reg,
u32 power_on, u32 reset, u32 clock_enable,
unsigned int flags, spinlock_t *lock)
{
struct mmp_pm_domain *pm_domain;

pm_domain = kzalloc(sizeof(*pm_domain), GFP_KERNEL);
if (!pm_domain)
return ERR_PTR(-ENOMEM);

pm_domain->reg = reg;
pm_domain->power_on = power_on;
pm_domain->reset = reset;
pm_domain->clock_enable = clock_enable;
pm_domain->flags = flags;
pm_domain->lock = lock;

pm_genpd_init(&pm_domain->genpd, NULL, true);
pm_domain->genpd.name = name;
pm_domain->genpd.power_on = mmp_pm_domain_power_on;
pm_domain->genpd.power_off = mmp_pm_domain_power_off;

return &pm_domain->genpd;
}

0 comments on commit ee4df23

Please sign in to comment.