From 7b1af7284f3afddd2ac67f6f3aa406f9ecaa5da7 Mon Sep 17 00:00:00 2001 From: zhiqiang liang Date: Thu, 28 Nov 2019 15:30:30 +0800 Subject: [PATCH] power: add the power domain control API [1/1] PD#SWPL-17563 Problem: add power domain control API for TM2 and SM1 Solution: add power domain control API for TM2 and SM1 Verify: T962E2 Change-Id: I2587b2b554281ee7c81d77e8978a2640e5f73be5 Signed-off-by: zhiqiang liang Signed-off-by: chunlong.cao --- .../bindings/power/amlogic,power_domain.txt | 32 + MAINTAINERS | 16 + arch/arm/boot/dts/amlogic/mesonsm1.dtsi | 13 +- arch/arm/boot/dts/amlogic/mesontm2.dtsi | 13 +- arch/arm64/boot/dts/amlogic/mesonsm1.dtsi | 13 +- arch/arm64/boot/dts/amlogic/mesontm2.dtsi | 13 +- drivers/amlogic/power/Makefile | 1 + drivers/amlogic/power/power_domain.c | 596 ++++++++++++++++++ include/dt-bindings/power/amlogic,pd.h | 19 + include/linux/amlogic/power_domain.h | 22 + 10 files changed, 722 insertions(+), 16 deletions(-) create mode 100644 Documentation/devicetree/bindings/power/amlogic,power_domain.txt create mode 100644 drivers/amlogic/power/power_domain.c create mode 100644 include/dt-bindings/power/amlogic,pd.h create mode 100644 include/linux/amlogic/power_domain.h diff --git a/Documentation/devicetree/bindings/power/amlogic,power_domain.txt b/Documentation/devicetree/bindings/power/amlogic,power_domain.txt new file mode 100644 index 000000000000..63b8717409b8 --- /dev/null +++ b/Documentation/devicetree/bindings/power/amlogic,power_domain.txt @@ -0,0 +1,32 @@ +Amlogic power domain controller +============================== + +The Amlogic Meson SoCs embeds an internal Power domain controller. + +Power Domain +---------------- + +There are many power domains controlled by this power controller. + + +The bindings must respect the power domain bindings as described in the file +power_domain.txt + +Device Tree Bindings: +--------------------- + +Required properties: +- compatible: + +Example: +------- + +pwrdm: power-domains { + compatible = "amlogic,tm2-power-domain"; + status = "okay"; +}; + +pwrdm: power-domains { + compatible = "amlogic,sm1-power-domain"; + status = "okay"; +}; diff --git a/MAINTAINERS b/MAINTAINERS index ff28cfbe447d..41ef8b86eeb4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15231,3 +15231,19 @@ F: arch/arm64/boot/dts/amlogic/tm2_t962x3_ab301_drm.dts F: arch/arm64/boot/dts/amlogic/tm2_t962e2_ab311_drm.dts F: arch/arm64/boot/dts/amlogic/mesontm2_drm.dtsi F: arch/arm/boot/dts/amlogic/mesontm2_drm.dtsi + +AMLOGIC TM2 C4A SBR DTS +M: Bing Jiang +F: arch/arm/boot/dts/amlogic/partition_sbr_gva.dtsi +F: arch/arm/boot/dts/amlogic/tm2_t962e2_ab311_gva_sbr.dts +F: arch/arm64/boot/dts/amlogic/partition_sbr_gva.dtsi +F: arch/arm64/boot/dts/amlogic/tm2_t962e2_ab311_gva_sbr.dts + +AMLOGIC V4L HEADFILE +M: Nanxin Qin +F: include/linux/amlogic/media/video_sink/v4lvideo_ext.h + +AMLOGIC POWER DOMAIN DRIVER +M: Zhiqiang Liang +F: drivers/amlogic/power/power_domain.c + diff --git a/arch/arm/boot/dts/amlogic/mesonsm1.dtsi b/arch/arm/boot/dts/amlogic/mesonsm1.dtsi index 446191418edb..a50658743884 100644 --- a/arch/arm/boot/dts/amlogic/mesonsm1.dtsi +++ b/arch/arm/boot/dts/amlogic/mesonsm1.dtsi @@ -495,10 +495,15 @@ reg = <0xff809000 0x48>; }; - power_ctrl: power_ctrl@ff8000e8 { - compatible = "amlogic, sm1-powerctrl"; - reg = <0xff8000e8 0x10>, - <0xff63c100 0x10>; + pwrdm: power-domains { + compatible = "amlogic,sm1-power-domain"; + reg = <0xff62fc00 0xf0>, + <0xff8000e8 0x10>, + <0xff63c100 0x40>, + <0xffd01080 0x20>; + vpu_mempd_reg3 = <0xc>; + vpu_mempd_reg4 = <0x10>; + status = "okay"; }; bl40: bl40 { diff --git a/arch/arm/boot/dts/amlogic/mesontm2.dtsi b/arch/arm/boot/dts/amlogic/mesontm2.dtsi index 537afe7187ea..f23e348ceb8e 100644 --- a/arch/arm/boot/dts/amlogic/mesontm2.dtsi +++ b/arch/arm/boot/dts/amlogic/mesontm2.dtsi @@ -628,10 +628,15 @@ quality = /bits/ 16 <1000>; }; - power_ctrl: power_ctrl@ff8000e8 { - compatible = "amlogic, sm1-powerctrl"; - reg = <0xff8000e8 0x10>, - <0xff63c100 0x10>; + pwrdm: power-domains { + compatible = "amlogic,tm2-power-domain"; + reg = <0xff62fc00 0xd0>, + <0xff8000e8 0x10>, + <0xff63c100 0x40>, + <0xffd01080 0x20>; + vpu_mempd_reg3 = <0x38>; + vpu_mempd_reg4 = <0x30>; + status = "okay"; }; soc { diff --git a/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi b/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi index 6eae61580613..8eef12c3c637 100644 --- a/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesonsm1.dtsi @@ -495,10 +495,15 @@ reg = <0x0 0xff809000 0x0 0x48>; }; - power_ctrl: power_ctrl@ff8000e8 { - compatible = "amlogic, sm1-powerctrl"; - reg = <0x0 0xff8000e8 0x0 0x10>, - <0x0 0xff63c100 0x0 0x10>; + pwrdm: power-domains { + compatible = "amlogic,sm1-power-domain"; + reg = <0x0 0xff62fc00 0x0 0xf0>, + <0x0 0xff8000e8 0x0 0x10>, + <0x0 0xff63c100 0x0 0x40>, + <0x0 0xffd01080 0x0 0x20>; + vpu_mempd_reg3 = <0xc>; + vpu_mempd_reg4 = <0x10>; + status = "okay"; }; bl40: bl40 { diff --git a/arch/arm64/boot/dts/amlogic/mesontm2.dtsi b/arch/arm64/boot/dts/amlogic/mesontm2.dtsi index aed60331c2a4..120eb126f011 100644 --- a/arch/arm64/boot/dts/amlogic/mesontm2.dtsi +++ b/arch/arm64/boot/dts/amlogic/mesontm2.dtsi @@ -627,10 +627,15 @@ quality = /bits/ 16 <1000>; }; - power_ctrl: power_ctrl@ff8000e8 { - compatible = "amlogic, sm1-powerctrl"; - reg = <0x0 0xff8000e8 0x0 0x10>, - <0x0 0xff63c100 0x0 0x10>; + pwrdm: power-domains { + compatible = "amlogic,tm2-power-domain"; + reg = <0x0 0xff62fc00 0x0 0xd0>, + <0x0 0xff8000e8 0x0 0x10>, + <0x0 0xff63c100 0x0 0x40>, + <0x0 0xffd01080 0x0 0x20>; + vpu_mempd_reg3 = <0x38>; + vpu_mempd_reg4 = <0x30>; + status = "okay"; }; soc { diff --git a/drivers/amlogic/power/Makefile b/drivers/amlogic/power/Makefile index ea86bf8f9f8a..a7637e80c531 100644 --- a/drivers/amlogic/power/Makefile +++ b/drivers/amlogic/power/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_AMLOGIC_PMU_OF) += aml_pmu_of_common.o obj-$(CONFIG_AMLOGIC_M8B_DVFS) += aml_dvfs/ obj-$(CONFIG_AMLOGIC_POWER) += power_ctrl.o +obj-$(CONFIG_AMLOGIC_POWER) += power_domain.o diff --git a/drivers/amlogic/power/power_domain.c b/drivers/amlogic/power/power_domain.c new file mode 100644 index 000000000000..eb00cd23c511 --- /dev/null +++ b/drivers/amlogic/power/power_domain.c @@ -0,0 +1,596 @@ +/* + * drivers/amlogic/power/power_domain.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//dos reg +#define DOS_SW_RESET0 0x0 +#define DOS_SW_RESET1 0x1c +#define DOS_SW_RESET3 0xd0 +#define DOS_SW_RESET4 0xdc +#define DOS_MEM_PD_VDEC 0xc0 +#define DOS_MEM_PD_HCODEC 0xc8 +#define DOS_MEM_PD_HEVC 0xcc +#define DOS_MEM_PD_WAVE420L 0xe4 + +//ao reg +#define AO_RTI_GEN_PWR_SLEEP0 0x0 +#define AO_RTI_GEN_PWR_ISO0 0x4 + +//mempd reg +#define HHI_MEM_PD_REG0 0x0 +#define HHI_VPU_MEM_PD_REG0 0x4 +#define HHI_VPU_MEM_PD_REG1 0x8 +#define HHI_DEMOD_MEM_PD_REG 0xc +#define HHI_DSP_MEM_PD_REG0 0x10 +#define HHI_NANOQ_MEM_PD_REG0 0x18 +#define HHI_NANOQ_MEM_PD_REG1 0x1c +#define HHI_VPU_MEM_PD_REG2 0x34 + +//reset reg +#define RESET0_LEVEL 0x0 +#define RESET1_LEVEL 0x4 +#define RESET2_LEVEL 0x8 +#define RESET3_LEVEL 0xc +#define RESET4_LEVEL 0x10 +#define RESET5_LEVEL 0x14 +#define RESET6_LEVEL 0x18 +#define RESET7_LEVEL 0x1c + +static u32 vpu_mem_pd_reg3; +static u32 vpu_mem_pd_reg4; + +struct power_domains { + void __iomem *dos_addr; + void __iomem *ao_addr; + void __iomem *mempd_addr; + void __iomem *reset_addr; + /**used for power reg concurrent access protect **/ + spinlock_t power_lock; + /**used for mempd reg concurrent access protect **/ + spinlock_t mem_pd_lock; + /**used for reset reg concurrent access protect **/ + spinlock_t reset_lock; + /**used for iso reg concurrent access protect **/ + spinlock_t iso_lock; +}; + +static struct power_domains *s_pd; + +static void power_switch(int pwr_domain, bool pwr_switch) +{ + unsigned int value; + unsigned long flags; + + spin_lock_irqsave(&s_pd->power_lock, flags); + value = readl(s_pd->ao_addr + AO_RTI_GEN_PWR_SLEEP0); + if (pwr_switch == PWR_ON) + value &= ~(1 << pwr_domain); + else + value |= (1 << pwr_domain); + writel(value, (s_pd->ao_addr + AO_RTI_GEN_PWR_SLEEP0)); + spin_unlock_irqrestore(&s_pd->power_lock, flags); +} + +static void mem_pd_switch(int pwr_domain, bool pwr_switch) +{ + unsigned int value; + unsigned long flags; + + spin_lock_irqsave(&s_pd->mem_pd_lock, flags); + if (pwr_switch == PWR_ON) { + switch (pwr_domain) { + case PM_DOS_HCODEC: + writel(0x0, (s_pd->dos_addr + DOS_MEM_PD_HCODEC)); + break; + case PM_DOS_VDEC: + writel(0x0, (s_pd->dos_addr + DOS_MEM_PD_VDEC)); + break; + case PM_DOS_HEVC: + writel(0x0, (s_pd->dos_addr + DOS_MEM_PD_HEVC)); + break; + case PM_WAVE420L: + writel(0x0, (s_pd->dos_addr + DOS_MEM_PD_WAVE420L)); + break; + case PM_CSI: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value &= ~(0x3 << 6); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_VPU: + writel(0x0, (s_pd->mempd_addr + HHI_VPU_MEM_PD_REG0)); + writel(0x0, (s_pd->mempd_addr + HHI_VPU_MEM_PD_REG1)); + writel(0x0, (s_pd->mempd_addr + HHI_VPU_MEM_PD_REG2)); + writel(0x0, (s_pd->mempd_addr + vpu_mem_pd_reg3)); + writel(0x0, (s_pd->mempd_addr + vpu_mem_pd_reg4)); + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value &= ~(0xff << 8); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_NN: + writel(0x0, (s_pd->mempd_addr + HHI_NANOQ_MEM_PD_REG0)); + writel(0x0, (s_pd->mempd_addr + HHI_NANOQ_MEM_PD_REG1)); + break; + case PM_USB: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value &= ~(0x3 << 30); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_PCIE0: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value &= ~(0xf << 26); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_GE2D: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value &= ~(0xff << 18); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_PCIE1: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value &= ~(0xf << 4); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_DSPA: + value = readl(s_pd->mempd_addr + HHI_DSP_MEM_PD_REG0); + value &= ~(0xffff); + writel(value, (s_pd->mempd_addr + HHI_DSP_MEM_PD_REG0)); + break; + case PM_DSPB: + value = readl(s_pd->mempd_addr + HHI_DSP_MEM_PD_REG0); + value &= ~(0xffff << 16); + writel(value, (s_pd->mempd_addr + HHI_DSP_MEM_PD_REG0)); + break; + case PM_DEMOD: + value = readl(s_pd->mempd_addr + HHI_DEMOD_MEM_PD_REG); + value &= ~0x2fff; + writel(value, (s_pd->mempd_addr + + HHI_DEMOD_MEM_PD_REG)); + break; + } + } else { + switch (pwr_domain) { + case PM_DOS_HCODEC: + writel(0xffffffff, (s_pd->dos_addr + + DOS_MEM_PD_HCODEC)); + break; + case PM_DOS_VDEC: + writel(0xffffffff, (s_pd->dos_addr + DOS_MEM_PD_VDEC)); + break; + case PM_DOS_HEVC: + writel(0xffffffff, (s_pd->dos_addr + DOS_MEM_PD_HEVC)); + break; + case PM_WAVE420L: + writel(0xffffffff, (s_pd->dos_addr + + DOS_MEM_PD_WAVE420L)); + break; + case PM_CSI: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value |= (0x3 << 6); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_VPU: + writel(0xffffffff, (s_pd->mempd_addr + + HHI_VPU_MEM_PD_REG0)); + writel(0xffffffff, (s_pd->mempd_addr + + HHI_VPU_MEM_PD_REG1)); + writel(0xffffffff, (s_pd->mempd_addr + + HHI_VPU_MEM_PD_REG2)); + writel(0xffffffff, (s_pd->mempd_addr + + vpu_mem_pd_reg3)); + writel(0xffffffff, (s_pd->mempd_addr + + vpu_mem_pd_reg4)); + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value |= (0xff << 8); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_NN: + writel(0xffffffff, (s_pd->mempd_addr + + HHI_NANOQ_MEM_PD_REG0)); + writel(0xffffffff, (s_pd->mempd_addr + + HHI_NANOQ_MEM_PD_REG1)); + break; + case PM_USB: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value |= (0x3 << 30); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_PCIE0: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value |= (0xf << 26); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_GE2D: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value |= (0xff << 18); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_PCIE1: + value = readl(s_pd->mempd_addr + HHI_MEM_PD_REG0); + value |= (0xf << 4); + writel(value, (s_pd->mempd_addr + HHI_MEM_PD_REG0)); + break; + case PM_DSPA: + value = readl(s_pd->mempd_addr + HHI_DSP_MEM_PD_REG0); + value |= (0xffff); + writel(value, (s_pd->mempd_addr + HHI_DSP_MEM_PD_REG0)); + break; + case PM_DSPB: + value = readl(s_pd->mempd_addr + HHI_DSP_MEM_PD_REG0); + value |= (0xffff << 16); + writel(value, (s_pd->mempd_addr + HHI_DSP_MEM_PD_REG0)); + break; + case PM_DEMOD: + value = readl(s_pd->mempd_addr + HHI_DEMOD_MEM_PD_REG); + value |= 0x2fff; + writel(value, (s_pd->mempd_addr + + HHI_DEMOD_MEM_PD_REG)); + break; + } + } + spin_unlock_irqrestore(&s_pd->mem_pd_lock, flags); +} + +static void reset_switch(int pwr_domain, bool pwr_switch) +{ + unsigned int value; + unsigned int tmp; + unsigned long flags; + + spin_lock_irqsave(&s_pd->reset_lock, flags); + if (pwr_switch == PWR_ON) { + switch (pwr_domain) { + case PM_DOS_HCODEC: + value = readl(s_pd->dos_addr + DOS_SW_RESET1); + value &= ~(0xffff << 2); + writel(value, (s_pd->dos_addr + DOS_SW_RESET1)); + break; + case PM_DOS_VDEC: + value = readl(s_pd->dos_addr + DOS_SW_RESET0); + value &= ~(0x1fff << 2); + writel(value, (s_pd->dos_addr + DOS_SW_RESET0)); + break; + case PM_DOS_HEVC: + value = readl(s_pd->dos_addr + DOS_SW_RESET3); + value &= ~(0x3ffff << 2 | 1 << 24); + writel(value, (s_pd->dos_addr + DOS_SW_RESET3)); + break; + case PM_WAVE420L: + value = readl(s_pd->dos_addr + DOS_SW_RESET4); + value &= ~(0xf << 8); + writel(value, (s_pd->dos_addr + DOS_SW_RESET4)); + break; + case PM_VPU: + tmp = 0x1 << 5 | 0x1 << 10 | 0x1 << 19 | 0x1 << 13; + value = readl(s_pd->reset_addr + RESET0_LEVEL); + value |= tmp; + writel(value, (s_pd->reset_addr + RESET0_LEVEL)); + tmp = 0x1 << 5 | 0x1 << 4; + value = readl(s_pd->reset_addr + RESET1_LEVEL); + value |= tmp; + writel(value, (s_pd->reset_addr + RESET1_LEVEL)); + tmp = 0x1 << 15; + value = readl(s_pd->reset_addr + RESET2_LEVEL); + value |= tmp; + writel(value, (s_pd->reset_addr + RESET2_LEVEL)); + tmp = 0x1 << 6 | 0x1 << 7 | 0x1 << 13 | + 0x1 << 5 | 0x1 << 9 | 0x1 << 4 | 0x1 << 12; + value = readl(s_pd->reset_addr + RESET4_LEVEL); + value |= tmp; + writel(value, (s_pd->reset_addr + RESET4_LEVEL)); + tmp = 0x1 << 7; + value = readl(s_pd->reset_addr + RESET7_LEVEL); + value |= tmp; + writel(value, (s_pd->reset_addr + RESET7_LEVEL)); + break; + case PM_NN: + value = readl(s_pd->reset_addr + RESET2_LEVEL); + value |= (0x1 << 12); + writel(value, (s_pd->reset_addr + RESET2_LEVEL)); + break; + case PM_USB: + value = readl(s_pd->reset_addr + RESET1_LEVEL); + value |= (0x1 << 2); + writel(value, (s_pd->reset_addr + RESET1_LEVEL)); + break; + case PM_PCIE0: + value = readl(s_pd->reset_addr + RESET0_LEVEL); + value |= ((0x1 << 12) | (0x3 << 14)); + writel(value, (s_pd->reset_addr + RESET0_LEVEL)); + break; + case PM_GE2D: + value = readl(s_pd->reset_addr + RESET2_LEVEL); + value |= (0x1 << 6); + writel(value, (s_pd->reset_addr + RESET2_LEVEL)); + break; + case PM_PCIE1: + value = readl(s_pd->reset_addr + RESET0_LEVEL); + value |= (0x7 << 28); + writel(value, (s_pd->reset_addr + RESET0_LEVEL)); + break; + case PM_DSPA: + value = readl(s_pd->reset_addr + RESET4_LEVEL); + value |= 0x1; + writel(value, (s_pd->reset_addr + RESET4_LEVEL)); + value = readl(s_pd->reset_addr + RESET1_LEVEL); + value |= (0x1 << 20); + writel(value, (s_pd->reset_addr + RESET1_LEVEL)); + break; + case PM_DSPB: + value = readl(s_pd->reset_addr + RESET4_LEVEL); + value |= (0x1 << 1); + writel(value, (s_pd->reset_addr + RESET4_LEVEL)); + value = readl(s_pd->reset_addr + RESET1_LEVEL); + value |= (0x1 << 21); + writel(value, (s_pd->reset_addr + RESET1_LEVEL)); + break; + case PM_DEMOD: + value = readl(s_pd->reset_addr + RESET0_LEVEL); + value |= (0x1 << 8); + writel(value, (s_pd->reset_addr + RESET0_LEVEL)); + break; + } + } else { + switch (pwr_domain) { + case PM_DOS_HCODEC: + value = readl(s_pd->dos_addr + DOS_SW_RESET1); + value |= (0xffff << 2); + writel(value, (s_pd->dos_addr + DOS_SW_RESET1)); + break; + case PM_DOS_VDEC: + value = readl(s_pd->dos_addr + DOS_SW_RESET0); + value |= (0x1fff << 2); + writel(value, (s_pd->dos_addr + DOS_SW_RESET0)); + break; + case PM_DOS_HEVC: + value = readl(s_pd->dos_addr + DOS_SW_RESET3); + value |= (0x3ffff << 2 | 1 << 24); + writel(value, (s_pd->dos_addr + DOS_SW_RESET3)); + break; + case PM_WAVE420L: + value = readl(s_pd->dos_addr + DOS_SW_RESET4); + value |= (0xf << 8); + writel(value, (s_pd->dos_addr + DOS_SW_RESET4)); + break; + case PM_VPU: + tmp = 0x1 << 5 | 0x1 << 10 | 0x1 << 19 | 0x1 << 13; + value = readl(s_pd->reset_addr + RESET0_LEVEL); + value &= ~tmp; + writel(value, (s_pd->reset_addr + RESET0_LEVEL)); + tmp = 0x1 << 5 | 0x1 << 4; + value = readl(s_pd->reset_addr + RESET1_LEVEL); + value &= ~tmp; + writel(value, (s_pd->reset_addr + RESET1_LEVEL)); + tmp = 0x1 << 15; + value = readl(s_pd->reset_addr + RESET2_LEVEL); + value &= ~tmp; + writel(value, (s_pd->reset_addr + RESET2_LEVEL)); + tmp = 0x1 << 6 | 0x1 << 7 | 0x1 << 13 | + 0x1 << 5 | 0x1 << 9 | 0x1 << 4 | 0x1 << 12; + value = readl(s_pd->reset_addr + RESET4_LEVEL); + value &= ~tmp; + writel(value, (s_pd->reset_addr + RESET4_LEVEL)); + tmp = 0x1 << 7; + value = readl(s_pd->reset_addr + RESET7_LEVEL); + value &= ~tmp; + writel(value, (s_pd->reset_addr + RESET7_LEVEL)); + break; + case PM_NN: + value = readl(s_pd->reset_addr + RESET2_LEVEL); + value &= ~(0x1 << 12); + writel(value, (s_pd->reset_addr + RESET2_LEVEL)); + break; + case PM_USB: + value = readl(s_pd->reset_addr + RESET1_LEVEL); + value &= ~(0x1 << 2); + writel(value, (s_pd->reset_addr + RESET1_LEVEL)); + break; + case PM_PCIE0: + value = readl(s_pd->reset_addr + RESET0_LEVEL); + value &= ~((0x1 << 12) | (0x3 << 14)); + writel(value, (s_pd->reset_addr + RESET0_LEVEL)); + break; + case PM_GE2D: + value = readl(s_pd->reset_addr + RESET2_LEVEL); + value &= ~(0x1 << 6); + writel(value, (s_pd->reset_addr + RESET2_LEVEL)); + break; + case PM_PCIE1: + value = readl(s_pd->reset_addr + RESET0_LEVEL); + value &= ~(0x7 << 28); + writel(value, (s_pd->reset_addr + RESET0_LEVEL)); + break; + case PM_DSPA: + value = readl(s_pd->reset_addr + RESET4_LEVEL); + value &= ~0x1; + writel(value, (s_pd->reset_addr + RESET4_LEVEL)); + value = readl(s_pd->reset_addr + RESET1_LEVEL); + value &= ~(0x1 << 20); + writel(value, (s_pd->reset_addr + RESET1_LEVEL)); + break; + case PM_DSPB: + value = readl(s_pd->reset_addr + RESET4_LEVEL); + value &= ~(0x1 << 1); + writel(value, (s_pd->reset_addr + RESET4_LEVEL)); + value = readl(s_pd->reset_addr + RESET1_LEVEL); + value &= ~(0x1 << 21); + writel(value, (s_pd->reset_addr + RESET1_LEVEL)); + break; + case PM_DEMOD: + value = readl(s_pd->reset_addr + RESET0_LEVEL); + value &= ~(0x1 << 8); + writel(value, (s_pd->reset_addr + RESET0_LEVEL)); + break; + } + } + spin_unlock_irqrestore(&s_pd->reset_lock, flags); +} + +static void iso_switch(int pwr_domain, bool pwr_switch) +{ + unsigned int value; + unsigned long flags; + + spin_lock_irqsave(&s_pd->iso_lock, flags); + value = readl(s_pd->ao_addr + AO_RTI_GEN_PWR_ISO0); + if (pwr_switch == PWR_ON) + value &= ~(1 << pwr_domain); + else + value |= (1 << pwr_domain); + writel(value, (s_pd->ao_addr + AO_RTI_GEN_PWR_ISO0)); + spin_unlock_irqrestore(&s_pd->iso_lock, flags); +} + +void power_domain_switch(int pwr_domain, bool pwr_switch) +{ + if (pwr_switch == PWR_ON) { + /* Powerup Power Domain */ + power_switch(pwr_domain, PWR_ON); + usleep_range(40, 50); + + /* Powerup memories */ + mem_pd_switch(pwr_domain, PWR_ON); + usleep_range(100, 150); + + reset_switch(pwr_domain, PWR_OFF); + + /* remove isolations */ + iso_switch(pwr_domain, PWR_ON); + + /* deassert reset */ + reset_switch(pwr_domain, PWR_ON); + + } else { + /* reset */ + reset_switch(pwr_domain, PWR_OFF); + + /* add isolation to domain */ + iso_switch(pwr_domain, PWR_OFF); + + /* Power down memories */ + mem_pd_switch(pwr_domain, PWR_OFF); + usleep_range(40, 50); + + /* Power off domain */ + power_switch(pwr_domain, PWR_OFF); + } +} +EXPORT_SYMBOL(power_domain_switch); + +static int pd_probe(struct platform_device *pdev) +{ + struct resource *res; + struct power_domains *power_domains; + int ret; + u32 offset; + + power_domains = devm_kzalloc(&pdev->dev, sizeof(*power_domains), + GFP_KERNEL); + if (!power_domains) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Fail to get dos addr res\n"); + return -ENXIO; + } + + power_domains->dos_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(power_domains->dos_addr)) + return PTR_ERR(power_domains->dos_addr); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "Fail to get ao addr res\n"); + return -ENXIO; + } + + power_domains->ao_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(power_domains->ao_addr)) + return PTR_ERR(power_domains->ao_addr); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) { + dev_err(&pdev->dev, "Fail to get mempd addr res\n"); + return -ENXIO; + } + + power_domains->mempd_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(power_domains->mempd_addr)) + return PTR_ERR(power_domains->mempd_addr); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (!res) { + dev_err(&pdev->dev, "Fail to get reset addr res\n"); + return -ENXIO; + } + + power_domains->reset_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(power_domains->reset_addr)) + return PTR_ERR(power_domains->reset_addr); + + ret = of_property_read_u32(pdev->dev.of_node, + "vpu_mempd_reg3", &offset); + if (!ret) { + pr_info("vpu_mempd_reg3: 0x%x\n", offset); + vpu_mem_pd_reg3 = offset; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "vpu_mempd_reg4", &offset); + if (!ret) { + pr_info("vpu_mempd_reg4: 0x%x\n", offset); + vpu_mem_pd_reg4 = offset; + } + + spin_lock_init(&power_domains->power_lock); + spin_lock_init(&power_domains->mem_pd_lock); + spin_lock_init(&power_domains->reset_lock); + spin_lock_init(&power_domains->iso_lock); + + s_pd = power_domains; + + return 0; +} + +static const struct of_device_id pd_match_table[] = { + { .compatible = "amlogic,tm2-power-domain", }, + { .compatible = "amlogic,sm1-power-domain", }, + {} +}; + +static struct platform_driver pd_driver = { + .driver = { + .name = "amlogic,power-domain", + .of_match_table = pd_match_table, + }, + .probe = pd_probe, +}; + +static int __init pd_init(void) +{ + return platform_driver_register(&pd_driver); +} +arch_initcall(pd_init); diff --git a/include/dt-bindings/power/amlogic,pd.h b/include/dt-bindings/power/amlogic,pd.h new file mode 100644 index 000000000000..90a314bdd502 --- /dev/null +++ b/include/dt-bindings/power/amlogic,pd.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * Copyright (c) 2019 Amlogic, Inc. All rights reserved. + */ + +#define PM_DOS_HCODEC 0 +#define PM_DOS_VDEC 1 +#define PM_DOS_HEVC 2 +#define PM_WAVE420L 3 +#define PM_CSI 6 +#define PM_VPU 8 +#define PM_NN 16 +#define PM_USB 17 +#define PM_PCIE0 18 +#define PM_GE2D 19 +#define PM_PCIE1 20 +#define PM_DSPA 21 +#define PM_DSPB 22 +#define PM_DEMOD 23 diff --git a/include/linux/amlogic/power_domain.h b/include/linux/amlogic/power_domain.h new file mode 100644 index 000000000000..6be08c18d9e5 --- /dev/null +++ b/include/linux/amlogic/power_domain.h @@ -0,0 +1,22 @@ +/* + * include/linux/amlogic/power_domain.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#define PWR_ON 0 +#define PWR_OFF 1 + +void power_domain_switch(int pwr_domain, bool pwr_switch); +