Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: mmp2: Add support for power islands

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>

authored by

Lubomir Rintel and committed by
Stephen Boyd
ee4df236 17d43046

+170 -1
+2
arch/arm/mach-mmp/Kconfig
··· 125 125 select PINCTRL_SINGLE 126 126 select ARCH_HAS_RESET_CONTROLLER 127 127 select CPU_PJ4 128 + select PM_GENERIC_DOMAINS if PM 129 + select PM_GENERIC_DOMAINS_OF if PM && OF 128 130 help 129 131 Include support for Marvell MMP2 based platforms using 130 132 the device tree.
+1 -1
drivers/clk/mmp/Makefile
··· 8 8 obj-$(CONFIG_RESET_CONTROLLER) += reset.o 9 9 10 10 obj-$(CONFIG_MACH_MMP_DT) += clk-of-pxa168.o clk-of-pxa910.o 11 - obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o clk-pll.o 11 + obj-$(CONFIG_COMMON_CLK_MMP2) += clk-of-mmp2.o clk-pll.o pwr-island.o 12 12 13 13 obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o 14 14 obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o
+42
drivers/clk/mmp/clk-of-mmp2.c
··· 17 17 #include <linux/delay.h> 18 18 #include <linux/err.h> 19 19 #include <linux/of_address.h> 20 + #include <linux/clk.h> 20 21 21 22 #include <dt-bindings/clock/marvell,mmp2.h> 23 + #include <dt-bindings/power/marvell,mmp2.h> 22 24 23 25 #include "clk.h" 24 26 #include "reset.h" ··· 65 63 #define APMU_USBHSIC1 0xfc 66 64 #define APMU_GPU 0xcc 67 65 #define APMU_AUDIO 0x10c 66 + #define APMU_CAMERA 0x1fc 68 67 69 68 #define MPMU_FCCR 0x8 70 69 #define MPMU_POSR 0x10 ··· 89 86 struct mmp2_clk_unit { 90 87 struct mmp_clk_unit unit; 91 88 enum mmp2_clk_model model; 89 + struct genpd_onecell_data pd_data; 90 + struct generic_pm_domain *pm_domains[MMP2_NR_POWER_DOMAINS]; 92 91 void __iomem *mpmu_base; 93 92 void __iomem *apmu_base; 94 93 void __iomem *apbc_base; ··· 478 473 mmp_clk_reset_register(np, cells, nr_resets); 479 474 } 480 475 476 + static void mmp2_pm_domain_init(struct device_node *np, 477 + struct mmp2_clk_unit *pxa_unit) 478 + { 479 + if (pxa_unit->model == CLK_MODEL_MMP3) { 480 + pxa_unit->pm_domains[MMP2_POWER_DOMAIN_GPU] 481 + = mmp_pm_domain_register("gpu", 482 + pxa_unit->apmu_base + APMU_GPU, 483 + 0x0600, 0x40003, 0x18000c, 0, &gpu_lock); 484 + } else { 485 + pxa_unit->pm_domains[MMP2_POWER_DOMAIN_GPU] 486 + = mmp_pm_domain_register("gpu", 487 + pxa_unit->apmu_base + APMU_GPU, 488 + 0x8600, 0x00003, 0x00000c, 489 + MMP_PM_DOMAIN_NO_DISABLE, &gpu_lock); 490 + } 491 + pxa_unit->pd_data.num_domains++; 492 + 493 + pxa_unit->pm_domains[MMP2_POWER_DOMAIN_AUDIO] 494 + = mmp_pm_domain_register("audio", 495 + pxa_unit->apmu_base + APMU_AUDIO, 496 + 0x600, 0x2, 0, 0, &audio_lock); 497 + pxa_unit->pd_data.num_domains++; 498 + 499 + if (pxa_unit->model == CLK_MODEL_MMP3) { 500 + pxa_unit->pm_domains[MMP3_POWER_DOMAIN_CAMERA] 501 + = mmp_pm_domain_register("camera", 502 + pxa_unit->apmu_base + APMU_CAMERA, 503 + 0x600, 0, 0, 0, NULL); 504 + pxa_unit->pd_data.num_domains++; 505 + } 506 + 507 + pxa_unit->pd_data.domains = pxa_unit->pm_domains; 508 + of_genpd_add_provider_onecell(np, &pxa_unit->pd_data); 509 + } 510 + 481 511 static void __init mmp2_clk_init(struct device_node *np) 482 512 { 483 513 struct mmp2_clk_unit *pxa_unit; ··· 543 503 pr_err("failed to map apbc registers\n"); 544 504 goto unmap_apmu_region; 545 505 } 506 + 507 + mmp2_pm_domain_init(np, pxa_unit); 546 508 547 509 mmp_clk_init(np, &pxa_unit->unit, MMP2_NR_CLKS); 548 510
+10
drivers/clk/mmp/clk.h
··· 3 3 #define __MACH_MMP_CLK_H 4 4 5 5 #include <linux/clk-provider.h> 6 + #include <linux/pm_domain.h> 6 7 #include <linux/clkdev.h> 7 8 8 9 #define APBC_NO_BUS_CTRL BIT(0) ··· 260 259 int nr_clks); 261 260 void mmp_clk_add(struct mmp_clk_unit *unit, unsigned int id, 262 261 struct clk *clk); 262 + 263 + /* Power islands */ 264 + #define MMP_PM_DOMAIN_NO_DISABLE BIT(0) 265 + 266 + struct generic_pm_domain *mmp_pm_domain_register(const char *name, 267 + void __iomem *reg, 268 + u32 power_on, u32 reset, u32 clock_enable, 269 + unsigned int flags, spinlock_t *lock); 270 + 263 271 #endif
+115
drivers/clk/mmp/pwr-island.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * MMP PMU power island support 4 + * 5 + * Copyright (C) 2020 Lubomir Rintel <lkundrak@v3.sk> 6 + */ 7 + 8 + #include <linux/pm_domain.h> 9 + #include <linux/slab.h> 10 + #include <linux/io.h> 11 + 12 + #include "clk.h" 13 + 14 + #define to_mmp_pm_domain(genpd) container_of(genpd, struct mmp_pm_domain, genpd) 15 + 16 + struct mmp_pm_domain { 17 + struct generic_pm_domain genpd; 18 + void __iomem *reg; 19 + spinlock_t *lock; 20 + u32 power_on; 21 + u32 reset; 22 + u32 clock_enable; 23 + unsigned int flags; 24 + }; 25 + 26 + static int mmp_pm_domain_power_on(struct generic_pm_domain *genpd) 27 + { 28 + struct mmp_pm_domain *pm_domain = to_mmp_pm_domain(genpd); 29 + unsigned long flags = 0; 30 + u32 val; 31 + 32 + if (pm_domain->lock) 33 + spin_lock_irqsave(pm_domain->lock, flags); 34 + 35 + val = readl(pm_domain->reg); 36 + 37 + /* Turn on the power island */ 38 + val |= pm_domain->power_on; 39 + writel(val, pm_domain->reg); 40 + 41 + /* Disable isolation */ 42 + val |= 0x100; 43 + writel(val, pm_domain->reg); 44 + 45 + /* Some blocks need to be reset after a power up */ 46 + if (pm_domain->reset || pm_domain->clock_enable) { 47 + u32 after_power_on = val; 48 + 49 + val &= ~pm_domain->reset; 50 + writel(val, pm_domain->reg); 51 + 52 + val |= pm_domain->clock_enable; 53 + writel(val, pm_domain->reg); 54 + 55 + val |= pm_domain->reset; 56 + writel(val, pm_domain->reg); 57 + 58 + writel(after_power_on, pm_domain->reg); 59 + } 60 + 61 + if (pm_domain->lock) 62 + spin_unlock_irqrestore(pm_domain->lock, flags); 63 + 64 + return 0; 65 + } 66 + 67 + static int mmp_pm_domain_power_off(struct generic_pm_domain *genpd) 68 + { 69 + struct mmp_pm_domain *pm_domain = to_mmp_pm_domain(genpd); 70 + unsigned long flags = 0; 71 + u32 val; 72 + 73 + if (pm_domain->flags & MMP_PM_DOMAIN_NO_DISABLE) 74 + return 0; 75 + 76 + if (pm_domain->lock) 77 + spin_lock_irqsave(pm_domain->lock, flags); 78 + 79 + /* Turn off and isolate the the power island. */ 80 + val = readl(pm_domain->reg); 81 + val &= ~pm_domain->power_on; 82 + val &= ~0x100; 83 + writel(val, pm_domain->reg); 84 + 85 + if (pm_domain->lock) 86 + spin_unlock_irqrestore(pm_domain->lock, flags); 87 + 88 + return 0; 89 + } 90 + 91 + struct generic_pm_domain *mmp_pm_domain_register(const char *name, 92 + void __iomem *reg, 93 + u32 power_on, u32 reset, u32 clock_enable, 94 + unsigned int flags, spinlock_t *lock) 95 + { 96 + struct mmp_pm_domain *pm_domain; 97 + 98 + pm_domain = kzalloc(sizeof(*pm_domain), GFP_KERNEL); 99 + if (!pm_domain) 100 + return ERR_PTR(-ENOMEM); 101 + 102 + pm_domain->reg = reg; 103 + pm_domain->power_on = power_on; 104 + pm_domain->reset = reset; 105 + pm_domain->clock_enable = clock_enable; 106 + pm_domain->flags = flags; 107 + pm_domain->lock = lock; 108 + 109 + pm_genpd_init(&pm_domain->genpd, NULL, true); 110 + pm_domain->genpd.name = name; 111 + pm_domain->genpd.power_on = mmp_pm_domain_power_on; 112 + pm_domain->genpd.power_off = mmp_pm_domain_power_off; 113 + 114 + return &pm_domain->genpd; 115 + }