aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/msm/ids.txt65
-rw-r--r--Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt81
-rw-r--r--Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt31
-rw-r--r--Documentation/devicetree/bindings/clock/qcom,gcc.txt1
-rw-r--r--arch/arm/boot/dts/qcom-apq8064-cm-qs600.dts74
-rw-r--r--arch/arm/boot/dts/qcom-apq8064-ifc6410.dts107
-rw-r--r--arch/arm/boot/dts/qcom-apq8064.dtsi849
-rw-r--r--arch/arm/boot/dts/qcom-apq8084.dtsi46
-rw-r--r--arch/arm/boot/dts/qcom-msm8960.dtsi49
-rw-r--r--arch/arm/boot/dts/qcom-msm8974.dtsi357
-rw-r--r--arch/arm/configs/multi_v7_defconfig45
-rw-r--r--arch/arm/configs/qcom_defconfig148
-rw-r--r--arch/arm/mach-qcom/Kconfig5
-rw-r--r--arch/arm/mach-qcom/Makefile3
-rw-r--r--arch/arm/mach-qcom/platsmp.c2
-rw-r--r--arch/arm64/Kconfig6
-rw-r--r--arch/arm64/boot/dts/Makefile1
-rw-r--r--arch/arm64/boot/dts/qcom/Makefile5
-rw-r--r--arch/arm64/boot/dts/qcom/msm8916-mtp.dts24
-rw-r--r--arch/arm64/boot/dts/qcom/msm8916-mtp.dtsi36
-rw-r--r--arch/arm64/boot/dts/qcom/msm8916.dtsi505
-rw-r--r--arch/arm64/configs/defconfig6
-rw-r--r--drivers/clk/clk-mux.c74
-rw-r--r--drivers/clk/qcom/Kconfig24
-rw-r--r--drivers/clk/qcom/Makefile3
-rw-r--r--drivers/clk/qcom/clk-rpm.c373
-rw-r--r--drivers/clk/qcom/common.c27
-rw-r--r--drivers/clk/qcom/common.h2
-rw-r--r--drivers/clk/qcom/gcc-apq8084.c38
-rw-r--r--drivers/clk/qcom/gcc-msm8916.c2917
-rw-r--r--drivers/clk/qcom/gcc-msm8960.c16
-rw-r--r--drivers/clk/qcom/gcc-msm8974.c14
-rw-r--r--drivers/clk/qcom/gdsc.c130
-rw-r--r--drivers/clk/qcom/gdsc.h43
-rw-r--r--drivers/clk/qcom/mmcc-msm8974.c54
-rw-r--r--drivers/cpuidle/Kconfig.arm7
-rw-r--r--drivers/cpuidle/Makefile1
-rw-r--r--drivers/cpuidle/cpuidle-qcom.c99
-rw-r--r--drivers/iommu/Kconfig11
-rw-r--r--drivers/iommu/Makefile1
-rw-r--r--drivers/iommu/msm_iommu.c4
-rw-r--r--drivers/iommu/msm_iommu_hw-8xxx.h6
-rw-r--r--drivers/iommu/qcom_iommu_v0.c1223
-rw-r--r--drivers/iommu/qcom_iommu_v0.h99
-rw-r--r--drivers/mfd/pm8921-core.c36
-rw-r--r--drivers/mfd/qcom_rpm.c36
-rw-r--r--drivers/mfd/ssbi.c7
-rw-r--r--drivers/mmc/core/pwrseq_simple.c4
-rw-r--r--drivers/mmc/host/mmci.c10
-rw-r--r--drivers/pci/host/Kconfig5
-rw-r--r--drivers/pci/host/Makefile1
-rw-r--r--drivers/pci/host/pci-qcom.c968
-rw-r--r--drivers/pinctrl/qcom/Kconfig11
-rw-r--r--drivers/pinctrl/qcom/Makefile1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c875
-rw-r--r--drivers/soc/qcom/Kconfig10
-rw-r--r--drivers/soc/qcom/Makefile3
-rw-r--r--drivers/soc/qcom/scm-boot.c (renamed from arch/arm/mach-qcom/scm-boot.c)39
-rw-r--r--drivers/soc/qcom/scm-pas.c112
-rw-r--r--drivers/soc/qcom/scm-pas.h32
-rw-r--r--drivers/soc/qcom/scm.c (renamed from arch/arm/mach-qcom/scm.c)103
-rw-r--r--drivers/soc/qcom/spm.c339
-rw-r--r--drivers/thermal/Kconfig10
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/msm8960_tsens.c752
-rw-r--r--include/dt-bindings/arm/qcom-ids.h33
-rw-r--r--include/dt-bindings/clock/qcom,gcc-apq8084.h6
-rw-r--r--include/dt-bindings/clock/qcom,gcc-msm8916.h164
-rw-r--r--include/dt-bindings/clock/qcom,gcc-msm8974.h3
-rw-r--r--include/dt-bindings/clock/qcom,mmcc-msm8974.h8
-rw-r--r--include/dt-bindings/reset/qcom,gcc-msm8916.h108
-rw-r--r--include/linux/clk-provider.h9
-rw-r--r--include/linux/memblock.h1
-rw-r--r--include/linux/mfd/pm8921-core.h31
-rw-r--r--include/linux/mfd/qcom_rpm.h1
-rw-r--r--include/soc/qcom/pm.h31
-rw-r--r--include/soc/qcom/scm-boot.h (renamed from arch/arm/mach-qcom/scm-boot.h)4
-rw-r--r--include/soc/qcom/scm.h (renamed from arch/arm/mach-qcom/scm.h)16
-rw-r--r--mm/memblock.c6
-rw-r--r--mm/page_alloc.c15
80 files changed, 11299 insertions, 104 deletions
diff --git a/Documentation/devicetree/bindings/arm/msm/ids.txt b/Documentation/devicetree/bindings/arm/msm/ids.txt
new file mode 100644
index 000000000000..9ee8428f4670
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/ids.txt
@@ -0,0 +1,65 @@
+* MSM-ID
+
+The qcom,msm-id entry specifies the MSM chipset and hardware revision. It can
+optionally be an array of these to indicate multiple hardware that use the same
+device tree. It is expected that the bootloader will use this information at
+boot-up to decide which device tree to use when given multiple device trees,
+some of which may not be compatible with the actual hardware. It is the
+bootloader's responsibility to pass the correct device tree to the kernel.
+
+PROPERTIES
+
+- qcom,msm-id:
+ Usage: required
+ Value type: <prop-encoded-array> (<chipset_id, rev_id> [, <c2, r2> ..])
+ Definition:
+ The "chipset_id" consists of three fields as below:
+
+ bits 0-15 = The unique MSM chipset id.
+ bits 16-31 = Reserved. Should be 0
+
+ chipset_id is an exact match value
+
+ The "rev_id" is a chipset specific 32-bit id that represents
+ the version of the chipset.
+
+ The rev_id is a best match id. The bootloader will look for
+ the closest possible patch.
+
+* BOARD-ID
+
+The qcom,board-id entry specifies the board type and revision information. It
+can optionally be an array of these to indicate multiple boards that use the
+same device tree. It is expected that the bootloader will use this information
+at boot-up to decide which device tree to use when given multiple device trees,
+some of which may not be compatible with the actual hardware. It is the
+bootloader's responsibility to pass the correct device tree to the kernel.
+
+PROPERTIES
+
+- qcom,board-id:
+ Usage: required
+ Value type: <prop-encoded-array> (<board_id, subtype_id> [, <b2, s2> ..])
+ Definition:
+ The "board_id" consists of three fields as below:
+
+ bits 31-24 = Unusued.
+ bits 23-16 = Platform Version Major
+ bits 15-8 = Platfrom Version Minor
+ bits 7-0 = Platform Type
+
+ Platform Type field is an exact match value. The Platform
+ Major/Minor field is a best match. The bootloader will look
+ for the closest possible match.
+
+ The "subtype_id" is unique to a Platform Type/Chipset ID. For
+ a given Platform Type, there will typically only be a single
+ board and the subtype_id will be 0. However in some cases board
+ variants may need to be distinquished by different subtype_id
+ values.
+
+ subtype_id is an exact match value.
+
+EXAMPLE:
+ qcom,board-id = <15 2>;
+ qcom,msm-id = <0x1007e 0>;
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt b/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
new file mode 100644
index 000000000000..ae1b07f852c6
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,idle-state.txt
@@ -0,0 +1,81 @@
+QCOM Idle States for cpuidle driver
+
+ARM provides idle-state node to define the cpuidle states, as defined in [1].
+cpuidle-qcom is the cpuidle driver for Qualcomm SoCs and uses these idle
+states. Idle states have different enter/exit latency and residency values.
+The idle states supported by the QCOM SoC are defined as -
+
+ * Standby
+ * Retention
+ * Standalone Power Collapse (Standalone PC or SPC)
+ * Power Collapse (PC)
+
+Standby: Standby does a little more in addition to architectural clock gating.
+When the WFI instruction is executed the ARM core would gate its internal
+clocks. In addition to gating the clocks, QCOM cpus use this instruction as a
+trigger to execute the SPM state machine. The SPM state machine waits for the
+interrupt to trigger the core back in to active. This triggers the cache
+hierarchy to enter standby states, when all cpus are idle. An interrupt brings
+the SPM state machine out of its wait, the next step is to ensure that the
+cache hierarchy is also out of standby, and then the cpu is allowed to resume
+execution.
+
+Retention: Retention is a low power state where the core is clock gated and
+the memory and the registers associated with the core are retained. The
+voltage may be reduced to the minimum value needed to keep the processor
+registers active. The SPM should be configured to execute the retention
+sequence and would wait for interrupt, before restoring the cpu to execution
+state. Retention may have a slightly higher latency than Standby.
+
+Standalone PC: A cpu can power down and warmboot if there is a sufficient time
+between the time it enters idle and the next known wake up. SPC mode is used
+to indicate a core entering a power down state without consulting any other
+cpu or the system resources. This helps save power only on that core. The SPM
+sequence for this idle state is programmed to power down the supply to the
+core, wait for the interrupt, restore power to the core, and ensure the
+system state including cache hierarchy is ready before allowing core to
+resume. Applying power and resetting the core causes the core to warmboot
+back into Elevation Level (EL) which trampolines the control back to the
+kernel. Entering a power down state for the cpu, needs to be done by trapping
+into a EL. Failing to do so, would result in a crash enforced by the warm boot
+code in the EL for the SoC. On SoCs with write-back L1 cache, the cache has to
+be flushed in s/w, before powering down the core.
+
+Power Collapse: This state is similar to the SPC mode, but distinguishes
+itself in that the cpu acknowledges and permits the SoC to enter deeper sleep
+modes. In a hierarchical power domain SoC, this means L2 and other caches can
+be flushed, system bus, clocks - lowered, and SoC main XO clock gated and
+voltages reduced, provided all cpus enter this state. Since the span of low
+power modes possible at this state is vast, the exit latency and the residency
+of this low power mode would be considered high even though at a cpu level,
+this essentially is cpu power down. The SPM in this state also may handshake
+with the Resource power manager processor in the SoC to indicate a complete
+application processor subsystem shut down.
+
+The idle-state for QCOM SoCs are distinguished by the compatible property of
+the idle-states device node.
+The devicetree representation of the idle state should be -
+
+Required properties:
+
+- compatible: Must be one of -
+ "qcom,idle-state-stby",
+ "qcom,idle-state-ret",
+ "qcom,idle-state-spc",
+ "qcom,idle-state-pc",
+ and "arm,idle-state".
+
+Other required and optional properties are specified in [1].
+
+Example:
+
+ idle-states {
+ CPU_SPC: spc {
+ compatible = "qcom,idle-state-spc", "arm,idle-state";
+ entry-latency-us = <150>;
+ exit-latency-us = <200>;
+ min-residency-us = <2000>;
+ };
+ };
+
+[1]. Documentation/devicetree/bindings/arm/idle-states.txt
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt
index 1505fb8e131a..690c3c002ad2 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt
@@ -2,11 +2,20 @@ SPM AVS Wrapper 2 (SAW2)
The SAW2 is a wrapper around the Subsystem Power Manager (SPM) and the
Adaptive Voltage Scaling (AVS) hardware. The SPM is a programmable
-micro-controller that transitions a piece of hardware (like a processor or
+power-controller that transitions a piece of hardware (like a processor or
subsystem) into and out of low power modes via a direct connection to
the PMIC. It can also be wired up to interact with other processors in the
system, notifying them when a low power state is entered or exited.
+Multiple revisions of the SAW hardware are supported using these Device Nodes.
+SAW2 revisions differ in the register offset and configuration data. Also, the
+same revision of the SAW in different SoCs may have different configuration
+data due the the differences in hardware capabilities. Hence the SoC name, the
+version of the SAW hardware in that SoC and the distinction between cpu (big
+or Little) or cache, may be needed to uniquely identify the SAW register
+configuration and initialization data. The compatible string is used to
+indicate this parameter.
+
PROPERTIES
- compatible:
@@ -14,10 +23,13 @@ PROPERTIES
Value type: <string>
Definition: shall contain "qcom,saw2". A more specific value should be
one of:
- "qcom,saw2-v1"
- "qcom,saw2-v1.1"
- "qcom,saw2-v2"
- "qcom,saw2-v2.1"
+ "qcom,saw2-v1"
+ "qcom,saw2-v1.1"
+ "qcom,saw2-v2"
+ "qcom,saw2-v2.1"
+ "qcom,apq8064-saw2-v1.1-cpu"
+ "qcom,msm8974-saw2-v2.1-cpu"
+ "qcom,apq8084-saw2-v2.1-cpu"
- reg:
Usage: required
@@ -26,10 +38,17 @@ PROPERTIES
the register region. An optional second element specifies
the base address and size of the alias register region.
+- regulator:
+ Usage: optional
+ Value type: boolean
+ Definition: Indicates that this SPM device acts as a regulator device
+ device for the core (CPU or Cache) the SPM is attached
+ to.
Example:
- regulator@2099000 {
+ power-controller@2099000 {
compatible = "qcom,saw2";
reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
+ regulator;
};
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
index aba3d254e037..54c23f34f194 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -8,6 +8,7 @@ Required properties :
"qcom,gcc-apq8084"
"qcom,gcc-ipq8064"
"qcom,gcc-msm8660"
+ "qcom,gcc-msm8916"
"qcom,gcc-msm8960"
"qcom,gcc-msm8974"
"qcom,gcc-msm8974pro"
diff --git a/arch/arm/boot/dts/qcom-apq8064-cm-qs600.dts b/arch/arm/boot/dts/qcom-apq8064-cm-qs600.dts
index 5d75666f7f6c..fc8cdadd2cf6 100644
--- a/arch/arm/boot/dts/qcom-apq8064-cm-qs600.dts
+++ b/arch/arm/boot/dts/qcom-apq8064-cm-qs600.dts
@@ -1,4 +1,6 @@
#include "qcom-apq8064-v2.0.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
/ {
model = "CompuLab CM-QS600";
@@ -40,6 +42,77 @@
};
};
+ /* OTG */
+ usb1_phy:phy@12500000 {
+ status = "ok";
+ };
+
+ usb3_phy:phy@12520000 {
+ status = "ok";
+ };
+
+ usb4_phy:phy@12530000 {
+ status = "ok";
+ };
+
+ gadget1:gadget@12500000 {
+ status = "ok";
+ };
+
+ /* OTG */
+ usb1: usb@12500000 {
+ status = "ok";
+ };
+
+ usb3: usb@12520000 {
+ status = "ok";
+ };
+
+ usb4: usb@12530000 {
+ status = "ok";
+ };
+
+ /* on board fixed 3.3v supply */
+ v3p3_pcieclk: v3p3-pcieclk {
+ compatible = "regulator-fixed";
+ regulator-name = "PCIE V3P3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ pci@1b500000 {
+ status = "ok";
+ pcie-clk-supply = <&v3p3_pcieclk>;
+ avdd-supply = <&pm8921_s3>;
+ vdd-supply = <&pm8921_lvs6>;
+ qcom,external-phy-refclk;
+ reset-gpio = <&tlmm_pinmux 27 GPIO_ACTIVE_LOW>;
+ };
+
+ qcom,ssbi@500000 {
+ pmicintc: pmic@0 {
+ pm8921_gpio: gpio@150 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&wlan_default_gpios>;
+ wlan_default_gpios: wlan-gpios {
+ pios {
+ pins = "gpio43";
+ function = "normal";
+ bias-disable;
+ power-source = <PM8921_GPIO_S4>;
+ };
+ };
+ };
+ };
+ };
+
+ sdcc4_pwrseq:pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ /* WLAN reset */
+ reset-gpios = <&pm8921_gpio 42 GPIO_ACTIVE_LOW>;
+ };
+
amba {
/* eMMC */
sdcc1: sdcc@12400000 {
@@ -53,6 +126,7 @@
/* WLAN */
sdcc4: sdcc@121c0000 {
status = "okay";
+ mmc-pwrseq = <&sdcc4_pwrseq>;
};
};
};
diff --git a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
index e641001ca2a7..69ef1816ed7f 100644
--- a/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
+++ b/arch/arm/boot/dts/qcom-apq8064-ifc6410.dts
@@ -1,10 +1,16 @@
#include "qcom-apq8064-v2.0.dtsi"
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
/ {
model = "Qualcomm APQ8064/IFC6410";
compatible = "qcom,apq8064-ifc6410", "qcom,apq8064";
+ aliases {
+ serial0 = &serial0;
+ serial1 = &serial1;
+ };
+
soc {
pinctrl@800000 {
i2c1_pins: i2c1 {
@@ -14,6 +20,13 @@
};
};
+ uart_pins: uart_pins {
+ mux {
+ pins = "gpio14", "gpio15", "gpio16", "gpio17";
+ function = "gsbi6";
+ };
+ };
+
card_detect: card_detect {
mux {
pins = "gpio26";
@@ -41,6 +54,18 @@
};
};
+ gsbi@16500000 {
+ status = "ok";
+ qcom,mode = <GSBI_PROT_I2C_UART>;
+
+ serial@16540000 {
+ status = "ok";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart_pins>;
+ };
+ };
+
gsbi@16600000 {
status = "ok";
qcom,mode = <GSBI_PROT_I2C_UART>;
@@ -49,6 +74,86 @@
};
};
+ /* OTG */
+ usb1_phy:phy@12500000 {
+ status = "ok";
+ };
+
+ usb3_phy:phy@12520000 {
+ status = "ok";
+ };
+
+ usb4_phy:phy@12530000 {
+ status = "ok";
+ };
+
+ gadget1:gadget@12500000 {
+ status = "ok";
+ };
+
+ /* OTG */
+ usb1: usb@12500000 {
+ status = "ok";
+ };
+
+ usb3: usb@12520000 {
+ status = "ok";
+ };
+
+ usb4: usb@12530000 {
+ status = "ok";
+ };
+
+ /* on board fixed 3.3v supply */
+ v3p3_pcieclk: v3p3-pcieclk {
+ compatible = "regulator-fixed";
+ regulator-name = "PCIE V3P3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ pci@1b500000 {
+ status = "ok";
+ pcie-clk-supply = <&v3p3_pcieclk>;
+ avdd-supply = <&pm8921_s3>;
+ vdd-supply = <&pm8921_lvs6>;
+ ext-3p3v-supply = <&ext_3p3v>;
+ qcom,external-phy-refclk;
+ reset-gpio = <&tlmm_pinmux 27 GPIO_ACTIVE_LOW>;
+ };
+
+ qcom,ssbi@500000 {
+ pmicintc: pmic@0 {
+ pm8921_gpio: gpio@150 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&wlan_default_gpios &bt_gpios>;
+ wlan_default_gpios: wlan-gpios {
+ pios {
+ pins = "gpio43";
+ function = "normal";
+ bias-disable;
+ power-source = <PM8921_GPIO_S4>;
+ };
+ };
+
+ bt_gpios: bt-gpio {
+ pios {
+ pins = "gpio44";
+ function = "normal";
+ bias-disable;
+ power-source = <PM8921_GPIO_S4>;
+ };
+ };
+ };
+ };
+ };
+ sdcc4_pwrseq:pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ reset-gpios = <&pm8921_gpio 42 GPIO_ACTIVE_LOW>,
+ <&pm8921_gpio 43 GPIO_ACTIVE_LOW>;
+ };
+
amba {
/* eMMC */
sdcc1: sdcc@12400000 {
@@ -65,6 +170,8 @@
/* WLAN */
sdcc4: sdcc@121c0000 {
status = "okay";
+ mmc-pwrseq = <&sdcc4_pwrseq>;
+
};
};
};
diff --git a/arch/arm/boot/dts/qcom-apq8064.dtsi b/arch/arm/boot/dts/qcom-apq8064.dtsi
index b3154c071652..3254aa0ce34f 100644
--- a/arch/arm/boot/dts/qcom-apq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8064.dtsi
@@ -1,11 +1,14 @@
/dts-v1/;
#include "skeleton.dtsi"
+#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/clock/qcom,gcc-msm8960.h>
+#include <dt-bindings/reset/qcom,gcc-msm8960.h>
#include <dt-bindings/clock/qcom,mmcc-msm8960.h>
+#include <dt-bindings/mfd/qcom-rpm.h>
#include <dt-bindings/soc/qcom,gsbi.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
-
+#include <dt-bindings/gpio/gpio.h>
/ {
model = "Qualcomm APQ8064";
compatible = "qcom,apq8064";
@@ -23,6 +26,10 @@
next-level-cache = <&L2>;
qcom,acc = <&acc0>;
qcom,saw = <&saw0>;
+ clocks = <&kraitcc 0>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
cpu@1 {
@@ -33,6 +40,10 @@
next-level-cache = <&L2>;
qcom,acc = <&acc1>;
qcom,saw = <&saw1>;
+ clocks = <&kraitcc 1>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
cpu@2 {
@@ -43,6 +54,10 @@
next-level-cache = <&L2>;
qcom,acc = <&acc2>;
qcom,saw = <&saw2>;
+ clocks = <&kraitcc 2>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
cpu@3 {
@@ -53,12 +68,33 @@
next-level-cache = <&L2>;
qcom,acc = <&acc3>;
qcom,saw = <&saw3>;
+ clocks = <&kraitcc 3>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
L2: l2-cache {
compatible = "cache";
cache-level = <2>;
};
+
+ idle-states {
+ CPU_STBY: standby {
+ compatible = "qcom,idle-state-stby", "arm,idle-state";
+ entry-latency-us = <1>;
+ exit-latency-us = <1>;
+ min-residency-us = <2>;
+ };
+
+ CPU_SPC: spc {
+ compatible = "qcom,idle-state-spc", "arm,idle-state";
+ entry-latency-us = <400>;
+ exit-latency-us = <900>;
+ min-residency-us = <3000>;
+ };
+ };
+
};
cpu-pmu {
@@ -66,6 +102,222 @@
interrupts = <1 10 0x304>;
};
+ qcom,pvs {
+ qcom,pvs-format-a;
+ qcom,speed0-pvs0-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 975000 >,
+ < 594000000 1000000 >,
+ < 702000000 1025000 >,
+ < 810000000 1075000 >,
+ < 918000000 1100000 >;
+
+ qcom,speed0-pvs1-bin-v0 =
+ < 384000000 900000 >,
+ < 486000000 925000 >,
+ < 594000000 950000 >,
+ < 702000000 975000 >,
+ < 810000000 1025000 >,
+ < 918000000 1050000 >;
+
+ qcom,speed0-pvs3-bin-v0 =
+ < 384000000 850000 >,
+ < 486000000 875000 >,
+ < 594000000 900000 >,
+ < 702000000 925000 >,
+ < 810000000 975000 >,
+ < 918000000 1000000 >;
+
+ qcom,speed0-pvs4-bin-v0 =
+ < 384000000 850000 >,
+ < 486000000 875000 >,
+ < 594000000 900000 >,
+ < 702000000 925000 >,
+ < 810000000 962500 >,
+ < 918000000 975000 >;
+
+ qcom,speed1-pvs0-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 950000 >,
+ < 594000000 950000 >,
+ < 702000000 962500 >,
+ < 810000000 1000000 >,
+ < 918000000 1025000 >;
+
+ qcom,speed1-pvs1-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 950000 >,
+ < 594000000 950000 >,
+ < 702000000 962500 >,
+ < 810000000 975000 >,
+ < 918000000 1000000 >;
+
+ qcom,speed1-pvs2-bin-v0 =
+ < 384000000 925000 >,
+ < 486000000 925000 >,
+ < 594000000 925000 >,
+ < 702000000 925000 >,
+ < 810000000 937500 >,
+ < 918000000 950000 >;
+
+ qcom,speed1-pvs3-bin-v0 =
+ < 384000000 900000 >,
+ < 486000000 900000 >,
+ < 594000000 900000 >,
+ < 702000000 900000 >,
+ < 810000000 900000 >,
+ < 918000000 925000 >;
+
+ qcom,speed1-pvs4-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+
+ qcom,speed1-pvs5-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+
+ qcom,speed1-pvs6-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+
+ qcom,speed2-pvs0-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 950000 >,
+ < 594000000 950000 >,
+ < 702000000 950000 >,
+ < 810000000 962500 >,
+ < 918000000 975000 >;
+
+ qcom,speed2-pvs1-bin-v0 =
+ < 384000000 925000 >,
+ < 486000000 925000 >,
+ < 594000000 925000 >,
+ < 702000000 925000 >,
+ < 810000000 937500 >,
+ < 918000000 950000 >;
+
+ qcom,speed2-pvs2-bin-v0 =
+ < 384000000 900000 >,
+ < 486000000 900000 >,
+ < 594000000 900000 >,
+ < 702000000 900000 >,
+ < 810000000 912500 >,
+ < 918000000 925000 >;
+
+ qcom,speed2-pvs3-bin-v0 =
+ < 384000000 900000 >,
+ < 486000000 900000 >,
+ < 594000000 900000 >,
+ < 702000000 900000 >,
+ < 810000000 900000 >,
+ < 918000000 912500 >;
+
+ qcom,speed2-pvs4-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+
+ qcom,speed2-pvs5-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+
+ qcom,speed2-pvs6-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+
+ qcom,speed14-pvs0-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 950000 >,
+ < 594000000 950000 >,
+ < 702000000 962500 >,
+ < 810000000 1000000 >,
+ < 918000000 1025000 >;
+
+ qcom,speed14-pvs1-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 950000 >,
+ < 594000000 950000 >,
+ < 702000000 962500 >,
+ < 810000000 975000 >,
+ < 918000000 1000000 >;
+
+ qcom,speed14-pvs2-bin-v0 =
+ < 384000000 925000 >,
+ < 486000000 925000 >,
+ < 594000000 925000 >,
+ < 702000000 925000 >,
+ < 810000000 937500 >,
+ < 918000000 950000 >;
+
+ qcom,speed14-pvs3-bin-v0 =
+ < 384000000 900000 >,
+ < 486000000 900000 >,
+ < 594000000 900000 >,
+ < 702000000 900000 >,
+ < 810000000 900000 >,
+ < 918000000 925000 >;
+
+ qcom,speed14-pvs4-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+
+ qcom,speed14-pvs5-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+
+ qcom,speed14-pvs6-bin-v0 =
+ < 384000000 875000 >,
+ < 486000000 875000 >,
+ < 594000000 875000 >,
+ < 702000000 875000 >,
+ < 810000000 887500 >,
+ < 918000000 900000 >;
+ };
+
+ kraitcc: clock-controller {
+ compatible = "qcom,krait-cc-v1";
+ #clock-cells = <1>;
+ };
+
+ clocks {
+ sleep_clk: sleep_clk {
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ #clock-cells = <0>;
+ };
+ };
+
soc: soc {
#address-cells = <1>;
#size-cells = <1>;
@@ -92,6 +344,20 @@
};
};
+ hdmi_pinctrl: hdmi-pinctrl {
+ mux1 {
+ pins = "gpio69", "gpio70", "gpio71";
+ function = "hdmi";
+ bias-pull-up;
+ drive-strength = <2>;
+ };
+ mux2 {
+ pins = "gpio72";
+ function = "hdmi";
+ bias-pull-down;
+ drive-strength = <16>;
+ };
+ };
ps_hold: ps_hold {
mux {
pins = "gpio78";
@@ -119,46 +385,63 @@
cpu-offset = <0x80000>;
};
+ watchdog@208a038 {
+ compatible = "qcom,kpss-wdt-apq8064";
+ reg = <0x0208a038 0x40>;
+ clocks = <&sleep_clk>;
+ timeout-sec = <10>;
+ };
+
acc0: clock-controller@2088000 {
compatible = "qcom,kpss-acc-v1";
reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
+ clock-output-names = "acpu0_aux";
};
acc1: clock-controller@2098000 {
compatible = "qcom,kpss-acc-v1";
reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
+ clock-output-names = "acpu1_aux";
};
acc2: clock-controller@20a8000 {
compatible = "qcom,kpss-acc-v1";
reg = <0x020a8000 0x1000>, <0x02008000 0x1000>;
+ clock-output-names = "acpu2_aux";
};
acc3: clock-controller@20b8000 {
compatible = "qcom,kpss-acc-v1";
reg = <0x020b8000 0x1000>, <0x02008000 0x1000>;
+ clock-output-names = "acpu3_aux";
};
- saw0: regulator@2089000 {
- compatible = "qcom,saw2";
+ l2cc: clock-controller@2011000 {
+ compatible = "qcom,kpss-gcc";
+ reg = <0x2011000 0x1000>;
+ clock-output-names = "acpu_l2_aux";
+ };
+
+ saw0: power-controller@2089000 {
+ compatible = "qcom,apq8064-saw2-v1.1-cpu", "qcom,saw2";
reg = <0x02089000 0x1000>, <0x02009000 0x1000>;
regulator;
};
- saw1: regulator@2099000 {
- compatible = "qcom,saw2";
+ saw1: power-controller@2099000 {
+ compatible = "qcom,apq8064-saw2-v1.1-cpu", "qcom,saw2";
reg = <0x02099000 0x1000>, <0x02009000 0x1000>;
regulator;
};
- saw2: regulator@20a9000 {
- compatible = "qcom,saw2";
+ saw2: power-controller@20a9000 {
+ compatible = "qcom,apq8064-saw2-v1.1-cpu", "qcom,saw2";
reg = <0x020a9000 0x1000>, <0x02009000 0x1000>;
regulator;
};
- saw3: regulator@20b9000 {
- compatible = "qcom,saw2";
+ saw3: power-controller@20b9000 {
+ compatible = "qcom,apq8064-saw2-v1.1-cpu", "qcom,saw2";
reg = <0x020b9000 0x1000>, <0x02009000 0x1000>;
regulator;
};
@@ -205,6 +488,27 @@
};
};
+ gsbi6: gsbi@16500000 {
+ status = "disabled";
+ compatible = "qcom,gsbi-v1.0.0";
+ reg = <0x16500000 0x03>;
+ clocks = <&gcc GSBI6_H_CLK>;
+ clock-names = "iface";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ serial1: serial@16540000 {
+ compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
+ reg = <0x16540000 0x100>,
+ <0x16500000 0x03>;
+ interrupts = <0 156 0x0>;
+ clocks = <&gcc GSBI6_UART_CLK>, <&gcc GSBI6_H_CLK>;
+ clock-names = "core", "iface";
+ status = "disabled";
+ };
+ };
+
gsbi7: gsbi@16600000 {
status = "disabled";
compatible = "qcom,gsbi-v1.0.0";
@@ -215,7 +519,7 @@
#size-cells = <1>;
ranges;
- serial@16640000 {
+ serial0: serial@16640000 {
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
reg = <0x16640000 0x1000>,
<0x16600000 0x1000>;
@@ -226,10 +530,73 @@
};
};
+ ext_3p3v: regulator-fixed@1 {
+ compatible = "regulator-fixed";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "ext_3p3v";
+ regulator-type = "voltage";
+ startup-delay-us = <0>;
+ gpio = <&tlmm_pinmux 77 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ regulator-boot-on;
+ };
+
qcom,ssbi@500000 {
compatible = "qcom,ssbi";
reg = <0x00500000 0x1000>;
qcom,controller-type = "pmic-arbiter";
+
+ pmicintc: pmic@0 {
+ compatible = "qcom,pm8921";
+ interrupt-parent = <&tlmm_pinmux>;
+ interrupts = <74 8>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pm8921_gpio: gpio@150 {
+
+ compatible = "qcom,pm8921-gpio";
+ reg = <0x150>;
+ interrupts = <192 1>, <193 1>, <194 1>,
+ <195 1>, <196 1>, <197 1>,
+ <198 1>, <199 1>, <200 1>,
+ <201 1>, <202 1>, <203 1>,
+ <204 1>, <205 1>, <206 1>,
+ <207 1>, <208 1>, <209 1>,
+ <210 1>, <211 1>, <212 1>,
+ <213 1>, <214 1>, <215 1>,
+ <216 1>, <217 1>, <218 1>,
+ <219 1>, <220 1>, <221 1>,
+ <222 1>, <223 1>, <224 1>,
+ <225 1>, <226 1>, <227 1>,
+ <228 1>, <229 1>, <230 1>,
+ <231 1>, <232 1>, <233 1>,
+ <234 1>, <235 1>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ };
+ };
+ };
+ qfprom: qfprom@00700000 {
+ compatible = "qcom,qfprom";
+ reg = <0x00700000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ tsens_data0: tsens_calib {
+ reg = <0x404 0x10>;
+ };
+
+ tsens_data1:tsens_backup_calib {
+ reg = <0x414 0x10>;
+ };
+
+
};
gcc: clock-controller@900000 {
@@ -237,6 +604,19 @@
reg = <0x00900000 0x4000>;
#clock-cells = <1>;
#reset-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ /* tsens */
+ tsens: tsens {
+ #thermal-sensor-cells = <1>;
+ compatible = "qcom,apq8064-tsens";
+ eeproms = <&tsens_data0>, <&tsens_data1>;
+ eeprom-names = "calib", "backup_calib";
+ interrupts = <0 178 0>;
+ interrupt-names = "tsens-ul";
+ };
};
mmcc: clock-controller@4000000 {
@@ -246,6 +626,278 @@
#reset-cells = <1>;
};
+ apcs: syscon@2011000 {
+ compatible = "syscon";
+ reg = <0x2011000 0x1000>;
+ };
+
+ rpm@108000 {
+ compatible = "qcom,rpm-apq8064";
+ reg = <0x108000 0x1000>;
+ qcom,ipc = <&apcs 0x8 2>;
+
+ interrupts = <0 19 0>, <0 21 0>, <0 22 0>;
+ interrupt-names = "ack", "err", "wakeup";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ rpmcc: rpm-clock-controller {
+ compatible = "qcom,apq8064-rpm-clk";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ pm8921_s3: pm8921-s3 {
+ compatible = "qcom,rpm-pm8921-smps";
+ reg = <QCOM_RPM_PM8921_SMPS3>;
+
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1400000>;
+ qcom,boot-load = <49360>;
+ qcom,switch-mode-frequency = <3200000>;
+ regulator-always-on;
+ };
+
+ pm8921_s4: pm8921-s4 {
+ compatible = "qcom,rpm-pm8921-smps";
+ reg = <QCOM_RPM_PM8921_SMPS4>;
+
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,boot-load = <200000>;
+ qcom,switch-mode-frequency = <3200000>;
+ regulator-always-on;
+ };
+
+ pm8921_l2: pm8921-l2 {
+ compatible = "qcom,rpm-pm8921-pldo";
+ reg = <QCOM_RPM_PM8921_LDO2>;
+
+// ???
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+// qcom,boot-load = <50000>;
+ };
+
+ pm8921_l3: pm8921-l3 {
+ compatible = "qcom,rpm-pm8921-pldo";
+ reg = <QCOM_RPM_PM8921_LDO3>;
+
+ regulator-min-microvolt = <3050000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ qcom,boot-load = <50000>;
+ };
+
+ pm8921_l4: pm8921-l4 {
+ compatible = "qcom,rpm-pm8921-pldo";
+ reg = <QCOM_RPM_PM8921_LDO4>;
+
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ qcom,boot-load = <50000>;
+ };
+
+ pm8921_l23: pm8921-l23 {
+ compatible = "qcom,rpm-pm8921-pldo";
+ reg = <QCOM_RPM_PM8921_LDO23>;
+
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1800000>;
+ qcom,boot-load = <50000>;
+ regulator-always-on;
+ };
+
+
+ pm8921_lvs6: pm8921-lvs6 {
+ compatible = "qcom,rpm-pm8921-switch";
+ reg = <QCOM_RPM_PM8921_LVS6>;
+ regulator-always-on;
+ };
+
+ pm8921_lvs7: pm8921-lvs7 {
+ compatible = "qcom,rpm-pm8921-switch";
+ reg = <QCOM_RPM_PM8921_LVS7>;
+ regulator-always-on;
+ };
+
+ pm8921_hdmi_mvs: pm8921-hdmi-mvs {
+ compatible = "qcom,rpm-pm8921-switch";
+ reg = <QCOM_RPM_HDMI_SWITCH>;
+ regulator-always-on;
+ bias-pull-down;
+ };
+
+ };
+
+ /* fabric clks handoff */
+ fabclk-handoff{
+ compatible = "qcom,apq8064-rpmcc-handoff";
+ clocks = <&rpmcc QCOM_RPM_APPS_FABRIC_CLK>;
+
+ assigned-clocks = <&rpmcc QCOM_RPM_APPS_FABRIC_CLK>;
+ assigned-clock-rates = <0x7fffffff>;
+ };
+
+ /* PCIE */
+
+ pci@1b500000 {
+ compatible = "qcom,pcie-ipq8064";
+ reg = <0x1b500000 0x1000>, <0x1b502000 0x100>, <0x1b600000 0x80>;
+ reg-names = "base", "elbi", "parf";
+ status = "disabled";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ interrupts = <0 35 0x0
+ 0 36 0x0
+ 0 37 0x0
+ 0 38 0x0
+ 0 39 0x0
+ 0 238 0x0>;
+ interrupt-names = "irq1", "irq2", "irq3", "irq4", "iqr5", "msi";
+
+ resets = <&gcc PCIE_ACLK_RESET>,
+ <&gcc PCIE_HCLK_RESET>,
+ <&gcc PCIE_POR_RESET>,
+ <&gcc PCIE_PCI_RESET>,
+ <&gcc PCIE_PHY_RESET>;
+ reset-names = "axi", "ahb", "por", "pci", "phy";
+
+ clocks = <&gcc PCIE_A_CLK>,
+ <&gcc PCIE_H_CLK>,
+ <&gcc PCIE_PHY_REF_CLK>;
+ clock-names = "core", "iface", "phy";
+
+ ranges = <0x00000000 0 0 0x0ff00000 0 0x00100000 /* configuration space */
+ 0x81000000 0 0 0x0fe00000 0 0x00100000 /* downstream I/O */
+ 0x82000000 0 0 0x08000000 0 0x07e00000>; /* non-prefetchable memory */
+
+ };
+
+
+
+ usb1_phy:phy@12500000 {
+ compatible = "qcom,usb-otg-ci";
+ reg = <0x12500000 0x400>;
+ interrupts = <0 100 0>;
+ status = "disabled";
+ dr_mode = "host";
+
+ clocks = <&gcc USB_HS1_XCVR_CLK>,
+ <&gcc USB_HS1_H_CLK>;
+ clock-names = "core", "iface";
+
+ vddcx-supply = <&pm8921_s3>;
+ v3p3-supply = <&pm8921_l3>;
+ v1p8-supply = <&pm8921_l4>;
+
+ resets = <&gcc USB_HS1_RESET>;
+ reset-names = "link";
+ };
+
+ usb3_phy:phy@12520000 {
+ compatible = "qcom,usb-otg-ci";
+ reg = <0x12520000 0x400>;
+ interrupts = <0 188 0>;
+ status = "disabled";
+ dr_mode = "host";
+
+ clocks = <&gcc USB_HS3_XCVR_CLK>,
+ <&gcc USB_HS3_H_CLK>;
+ clock-names = "core", "iface";
+
+ vddcx-supply = <&pm8921_s3>;
+ v3p3-supply = <&pm8921_l3>;
+ v1p8-supply = <&pm8921_l23>;
+
+ resets = <&gcc USB_HS3_RESET>;
+ reset-names = "link";
+ };
+
+ usb4_phy:phy@12530000 {
+ compatible = "qcom,usb-otg-ci";
+ reg = <0x12530000 0x400>;
+ interrupts = <0 215 0>;
+ status = "disabled";
+ dr_mode = "host";
+
+ clocks = <&gcc USB_HS4_XCVR_CLK>,
+ <&gcc USB_HS4_H_CLK>;
+ clock-names = "core", "iface";
+
+ vddcx-supply = <&pm8921_s3>;
+ v3p3-supply = <&pm8921_l3>;
+ v1p8-supply = <&pm8921_l23>;
+
+ resets = <&gcc USB_HS4_RESET>;
+ reset-names = "link";
+ };
+
+ gadget1:gadget@12500000 {
+ compatible = "qcom,ci-hdrc";
+ reg = <0x12500000 0x400>;
+ status = "disabled";
+ dr_mode = "peripheral";
+ interrupts = <0 100 0>;
+ usb-phy = <&usb1_phy>;
+ };
+
+ usb1: usb@12500000 {
+ compatible = "qcom,ehci-host";
+ reg = <0x12500000 0x400>;
+ interrupts = <0 100 0>;
+ status = "disabled";
+ usb-phy = <&usb1_phy>;
+ };
+
+ usb3: usb@12520000 {
+ compatible = "qcom,ehci-host";
+ reg = <0x12520000 0x400>;
+ interrupts = <0 188 0>;
+ status = "disabled";
+ usb-phy = <&usb3_phy>;
+ };
+
+ usb4: usb@12530000 {
+ compatible = "qcom,ehci-host";
+ reg = <0x12530000 0x400>;
+ interrupts = <0 215 0>;
+ status = "disabled";
+ usb-phy = <&usb4_phy>;
+ };
+
+ sata_phy0:sata-phy@1b400000{
+ compatible = "qcom,apq8064-sata-phy";
+ reg = <0x1b400000 0x200>;
+ reg-names = "phy_mem";
+ clocks = <&gcc SATA_PHY_CFG_CLK>;
+ clock-names = "cfg";
+ #phy-cells = <0>;
+ };
+
+ sata0: sata@29000000 {
+ compatible = "generic-ahci";
+ reg = <0x29000000 0x180>;
+ interrupts = <0 209 0>;
+ clocks = <&gcc SFAB_SATA_S_H_CLK>, <&gcc SATA_H_CLK>,
+ <&gcc SATA_A_CLK>, <&gcc SATA_RXOOB_CLK>,
+ <&gcc SATA_PMALIVE_CLK>;
+
+ clock-names = "slave_iface", "iface",
+ "bus", "rxoob",
+ "core_pmalive";
+ assigned-clocks = <&gcc SATA_RXOOB_CLK>,
+ <&gcc SATA_PMALIVE_CLK>;
+ assigned-clock-rates = <100000000>, <100000000>;
+
+ phys = <&sata_phy0>;
+ phy-names = "sata-phy";
+ target-supply = <&pm8921_s4>;
+ };
+
/* Temporary fixed regulator */
vsdcc_fixed: vsdcc-regulator {
compatible = "regulator-fixed";
@@ -349,5 +1001,182 @@
pinctrl-0 = <&sdc4_gpios>;
};
};
+
+ hdmi: qcom,hdmi-tx@4a00000 {
+ compatible = "qcom,hdmi-tx-8960";
+ reg-names = "core_physical";
+ reg = <0x04a00000 0x1000>;
+ interrupts = <GIC_SPI 79 0>;
+ clock-names =
+ "core_clk",
+ "master_iface_clk",
+ "slave_iface_clk";
+ clocks =
+ <&mmcc HDMI_APP_CLK>,
+ <&mmcc HDMI_M_AHB_CLK>,
+ <&mmcc HDMI_S_AHB_CLK>;
+ qcom,hdmi-tx-ddc-clk = <&tlmm_pinmux 70 GPIO_ACTIVE_HIGH>;
+ qcom,hdmi-tx-ddc-data = <&tlmm_pinmux 71 GPIO_ACTIVE_HIGH>;
+ qcom,hdmi-tx-hpd = <&tlmm_pinmux 72 GPIO_ACTIVE_HIGH>;
+ core-vdda-supply = <&pm8921_hdmi_mvs>;
+ hdmi-mux-supply = <&ext_3p3v>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmi_pinctrl>;
+ };
+
+ gpu: qcom,adreno-3xx@4300000 {
+ compatible = "qcom,adreno-3xx";
+ #stream-id-cells = <16>;
+ reg = <0x04300000 0x20000>;
+ reg-names = "kgsl_3d0_reg_memory";
+ interrupts = <GIC_SPI 80 0>;
+ interrupt-names = "kgsl_3d0_irq";
+ clock-names =
+ "core_clk",
+ "iface_clk",
+ "mem_clk",
+ "mem_iface_clk";
+ clocks =
+ <&mmcc GFX3D_CLK>,
+ <&mmcc GFX3D_AHB_CLK>,
+ <&mmcc GFX3D_AXI_CLK>,
+ <&mmcc MMSS_IMEM_AHB_CLK>;
+ qcom,chipid = <0x03020002>;
+ qcom,gpu-pwrlevels {
+ compatible = "qcom,gpu-pwrlevels";
+ qcom,gpu-pwrlevel@0 {
+ qcom,gpu-freq = <450000000>;
+ };
+ qcom,gpu-pwrlevel@1 {
+ qcom,gpu-freq = <27000000>;
+ };
+ };
+ };
+
+ panel: auo,b101xtn01 {
+ compatible = "auo,b101xtn01";
+ // lcd1_pwr_en_n:
+ enable-gpio = <&tlmm_pinmux 36 GPIO_ACTIVE_HIGH>;
+ };
+
+ mdp: qcom,mdp@5100000 {
+ compatible = "qcom,mdp";
+ #stream-id-cells = <2>;
+ reg = <0x05100000 0xf0000>;
+ interrupts = <GIC_SPI 75 0>;
+ connectors = <&hdmi>;
+ gpus = <&gpu>;
+ qcom,lvds-panel = <&panel>;
+ clock-names =
+ "core_clk",
+ "iface_clk",
+ "lut_clk",
+ "src_clk",
+ "hdmi_clk",
+ "mdp_clk",
+ "mdp_axi_clk";
+ clocks =
+ <&mmcc MDP_CLK>,
+ <&mmcc MDP_AHB_CLK>,
+ <&mmcc MDP_LUT_CLK>,
+ <&mmcc TV_SRC>,
+ <&mmcc HDMI_TV_CLK>,
+ <&mmcc MDP_TV_CLK>,
+ <&mmcc MDP_AXI_CLK>;
+// vdd-supply = <&footswitch_mdp>;
+ lvds-vccs-3p3v-supply = <&ext_3p3v>;
+ lvds-pll-vdda-supply = <&pm8921_l2>;
+ lvds-vdda-supply = <&pm8921_lvs7>;
+ };
+
+ mdp_port0: qcom,iommu@7500000 {
+ compatible = "qcom,iommu-v0";
+ clock-names =
+ "smmu_pclk",
+ "iommu_clk";
+ clocks =
+ <&mmcc SMMU_AHB_CLK>,
+ <&mmcc MDP_AXI_CLK>;
+ reg-names = "physbase";
+ reg = <0x07500000 0x100000>;
+ interrupt-names =
+ "secure_irq",
+ "nonsecure_irq";
+ interrupts =
+ <GIC_SPI 63 0>,
+ <GIC_SPI 64 0>;
+ ncb = <2>;
+ mmu-masters = <&mdp 0 2>;
+ };
+
+ mdp_port1: qcom,iommu@7600000 {
+ compatible = "qcom,iommu";
+ clock-names =
+ "smmu_pclk",
+ "iommu_clk";
+ clocks =
+ <&mmcc SMMU_AHB_CLK>,
+ <&mmcc MDP_AXI_CLK>;
+ reg-names = "physbase";
+ reg = <0x07600000 0x100000>;
+ interrupt-names =
+ "secure_irq",
+ "nonsecure_irq";
+ interrupts =
+ <GIC_SPI 61 0>,
+ <GIC_SPI 62 0>;
+ ncb = <2>;
+ mmu-masters = <&mdp 0 2>;
+ };
+
+ gfx3d: qcom,iommu@7c00000 {
+ compatible = "qcom,iommu-v0";
+ clock-names =
+ "smmu_pclk",
+ "iommu_clk";
+ clocks =
+ <&mmcc SMMU_AHB_CLK>,
+ <&mmcc GFX3D_AXI_CLK>;
+ reg-names = "physbase";
+ reg = <0x07c00000 0x100000>;
+ interrupt-names =
+ "secure_irq",
+ "nonsecure_irq";
+ interrupts =
+ <GIC_SPI 69 0>,
+ <GIC_SPI 70 0>;
+ ncb = <3>;
+ ttbr-split = <1>;
+ mmu-masters =
+ /* gfx3d_user: */
+ <&gpu 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15>,
+ /* gfx3d_priv: */
+ <&gpu 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31>;
+ };
+
+ gfx3d1: qcom,iommu@7d00000 {
+ compatible = "qcom,iommu-v0";
+ clock-names =
+ "smmu_pclk",
+ "iommu_clk";
+ clocks =
+ <&mmcc SMMU_AHB_CLK>,
+ <&mmcc GFX3D_AXI_CLK>;
+ reg-names = "physbase";
+ reg = <0x07d00000 0x100000>;
+ interrupt-names =
+ "secure_irq",
+ "nonsecure_irq";
+ interrupts =
+ <GIC_SPI 210 0>,
+ <GIC_SPI 211 0>;
+ ncb = <3>;
+ ttbr-split = <1>;
+ mmu-masters =
+ /* gfx3d_user: */
+ <&gpu 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15>,
+ /* gfx3d_priv: */
+ <&gpu 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31>;
+ };
};
};
diff --git a/arch/arm/boot/dts/qcom-apq8084.dtsi b/arch/arm/boot/dts/qcom-apq8084.dtsi
index 1f130bc16858..207be157b9a7 100644
--- a/arch/arm/boot/dts/qcom-apq8084.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8084.dtsi
@@ -21,6 +21,8 @@
enable-method = "qcom,kpss-acc-v2";
next-level-cache = <&L2>;
qcom,acc = <&acc0>;
+ qcom,saw = <&saw0>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
cpu@1 {
@@ -30,6 +32,8 @@
enable-method = "qcom,kpss-acc-v2";
next-level-cache = <&L2>;
qcom,acc = <&acc1>;
+ qcom,saw = <&saw1>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
cpu@2 {
@@ -39,6 +43,8 @@
enable-method = "qcom,kpss-acc-v2";
next-level-cache = <&L2>;
qcom,acc = <&acc2>;
+ qcom,saw = <&saw2>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
cpu@3 {
@@ -48,6 +54,8 @@
enable-method = "qcom,kpss-acc-v2";
next-level-cache = <&L2>;
qcom,acc = <&acc3>;
+ qcom,saw = <&saw3>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
L2: l2-cache {
@@ -55,6 +63,22 @@
cache-level = <2>;
qcom,saw = <&saw_l2>;
};
+
+ idle-states {
+ CPU_STBY: standby {
+ compatible = "qcom,idle-state-stby", "arm,idle-state";
+ entry-latency-us = <1>;
+ exit-latency-us = <1>;
+ min-residency-us = <2>;
+ };
+
+ CPU_SPC: spc {
+ compatible = "qcom,idle-state-spc", "arm,idle-state";
+ entry-latency-us = <150>;
+ exit-latency-us = <200>;
+ min-residency-us = <2000>;
+ };
+ };
};
cpu-pmu {
@@ -144,7 +168,27 @@
};
};
- saw_l2: regulator@f9012000 {
+ saw0: power-controller@f9089000 {
+ compatible = "qcom,apq8084-saw2-v2.1-cpu";
+ reg = <0xf9089000 0x1000>;
+ };
+
+ saw1: power-controller@f9099000 {
+ compatible = "qcom,apq8084-saw2-v2.1-cpu";
+ reg = <0xf9099000 0x1000>;
+ };
+
+ saw2: power-controller@f90a9000 {
+ compatible = "qcom,apq8084-saw2-v2.1-cpu";
+ reg = <0xf90a9000 0x1000>;
+ };
+
+ saw3: power-controller@f90b9000 {
+ compatible = "qcom,apq8084-saw2-v2.1-cpu";
+ reg = <0xf90b9000 0x1000>;
+ };
+
+ saw_l2: power-controller@f9012000 {
compatible = "qcom,saw2";
reg = <0xf9012000 0x1000>;
regulator;
diff --git a/arch/arm/boot/dts/qcom-msm8960.dtsi b/arch/arm/boot/dts/qcom-msm8960.dtsi
index e1b0d5cd9e3c..ef89daf7a258 100644
--- a/arch/arm/boot/dts/qcom-msm8960.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8960.dtsi
@@ -24,6 +24,10 @@
next-level-cache = <&L2>;
qcom,acc = <&acc0>;
qcom,saw = <&saw0>;
+ clocks = <&kraitcc 0>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+
};
cpu@1 {
@@ -34,6 +38,10 @@
next-level-cache = <&L2>;
qcom,acc = <&acc1>;
qcom,saw = <&saw1>;
+ clocks = <&kraitcc 1>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+
};
L2: l2-cache {
@@ -48,6 +56,39 @@
qcom,no-pc-write;
};
+ qcom,pvs {
+ qcom,pvs-format-a;
+ /* Hz uV */
+ qcom,speed0-pvs0-bin-v0 =
+ < 384000000 950000 >,
+ < 486000000 975000 >,
+ < 594000000 1000000 >,
+ < 702000000 1025000 >,
+ < 810000000 1075000 >,
+ < 918000000 1100000 >;
+
+ qcom,speed0-pvs1-bin-v0 =
+ < 384000000 900000 >,
+ < 486000000 925000 >,
+ < 594000000 950000 >,
+ < 702000000 975000 >,
+ < 810000000 1025000 >,
+ < 918000000 1050000 >;
+
+ qcom,speed0-pvs3-bin-v0 =
+ < 384000000 850000 >,
+ < 486000000 875000 >,
+ < 594000000 900000 >,
+ < 702000000 925000 >,
+ < 810000000 975000 >,
+ < 918000000 1000000 >;
+ };
+
+ kraitcc: clock-controller {
+ compatible = "qcom,krait-cc-v1";
+ #clock-cells = <1>;
+ };
+
soc: soc {
#address-cells = <1>;
#size-cells = <1>;
@@ -101,11 +142,19 @@
acc0: clock-controller@2088000 {
compatible = "qcom,kpss-acc-v1";
reg = <0x02088000 0x1000>, <0x02008000 0x1000>;
+ clock-output-names = "acpu0_aux";
};
acc1: clock-controller@2098000 {
compatible = "qcom,kpss-acc-v1";
reg = <0x02098000 0x1000>, <0x02008000 0x1000>;
+ clock-output-names = "acpu1_aux";
+ };
+
+ l2cc: clock-controller@2011000 {
+ compatible = "qcom,kpss-gcc";
+ reg = <0x2011000 0x1000>;
+ clock-output-names = "acpu_l2_aux";
};
saw0: regulator@2089000 {
diff --git a/arch/arm/boot/dts/qcom-msm8974.dtsi b/arch/arm/boot/dts/qcom-msm8974.dtsi
index e265ec16a787..819a6f407e70 100644
--- a/arch/arm/boot/dts/qcom-msm8974.dtsi
+++ b/arch/arm/boot/dts/qcom-msm8974.dtsi
@@ -14,40 +14,60 @@
#size-cells = <0>;
interrupts = <1 9 0xf04>;
- cpu@0 {
+ cpu0: cpu@0 {
compatible = "qcom,krait";
enable-method = "qcom,kpss-acc-v2";
device_type = "cpu";
reg = <0>;
next-level-cache = <&L2>;
qcom,acc = <&acc0>;
+ clocks = <&kraitcc 0>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+ qcom,saw = <&saw0>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
- cpu@1 {
+ cpu1: cpu@1 {
compatible = "qcom,krait";
enable-method = "qcom,kpss-acc-v2";
device_type = "cpu";
reg = <1>;
next-level-cache = <&L2>;
qcom,acc = <&acc1>;
+ clocks = <&kraitcc 1>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+ qcom,saw = <&saw1>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
- cpu@2 {
+ cpu2: cpu@2 {
compatible = "qcom,krait";
enable-method = "qcom,kpss-acc-v2";
device_type = "cpu";
reg = <2>;
next-level-cache = <&L2>;
qcom,acc = <&acc2>;
+ clocks = <&kraitcc 2>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+ qcom,saw = <&saw2>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
- cpu@3 {
+ cpu3: cpu@3 {
compatible = "qcom,krait";
enable-method = "qcom,kpss-acc-v2";
device_type = "cpu";
reg = <3>;
next-level-cache = <&L2>;
qcom,acc = <&acc3>;
+ clocks = <&kraitcc 3>;
+ clock-names = "cpu";
+ clock-latency = <100000>;
+ qcom,saw = <&saw3>;
+ cpu-idle-states = <&CPU_STBY &CPU_SPC>;
};
L2: l2-cache {
@@ -55,6 +75,22 @@
cache-level = <2>;
qcom,saw = <&saw_l2>;
};
+
+ idle-states {
+ CPU_STBY: standby {
+ compatible = "qcom,idle-state-stby", "arm,idle-state";
+ entry-latency-us = <1>;
+ exit-latency-us = <1>;
+ min-residency-us = <2>;
+ };
+
+ CPU_SPC: spc {
+ compatible = "qcom,idle-state-spc", "arm,idle-state";
+ entry-latency-us = <150>;
+ exit-latency-us = <200>;
+ min-residency-us = <2000>;
+ };
+ };
};
cpu-pmu {
@@ -71,6 +107,267 @@
clock-frequency = <19200000>;
};
+ qcom,pvs {
+ qcom,pvs-format-b;
+ /* Hz uV ua */
+ qcom,speed0-pvs0-bin-v0 =
+ < 300000000 815000 73 >,
+ < 345600000 825000 85 >,
+ < 422400000 835000 104 >,
+ < 499200000 845000 124 >,
+ < 576000000 855000 144 >,
+ < 652800000 865000 165 >,
+ < 729600000 875000 186 >,
+ < 806400000 890000 208 >,
+ < 883200000 900000 229 >,
+ < 960000000 915000 252 >;
+
+ qcom,speed0-pvs1-bin-v0 =
+ < 300000000 800000 73 >,
+ < 345600000 810000 85 >,
+ < 422400000 820000 104 >,
+ < 499200000 830000 124 >,
+ < 576000000 840000 144 >,
+ < 652800000 850000 165 >,
+ < 729600000 860000 186 >,
+ < 806400000 875000 208 >,
+ < 883200000 885000 229 >,
+ < 960000000 895000 252 >;
+
+ qcom,speed0-pvs2-bin-v0 =
+ < 300000000 785000 73 >,
+ < 345600000 795000 85 >,
+ < 422400000 805000 104 >,
+ < 499200000 815000 124 >,
+ < 576000000 825000 144 >,
+ < 652800000 835000 165 >,
+ < 729600000 845000 186 >,
+ < 806400000 855000 208 >,
+ < 883200000 865000 229 >,
+ < 960000000 875000 252 >;
+
+ qcom,speed0-pvs3-bin-v0 =
+ < 300000000 775000 73 >,
+ < 345600000 780000 85 >,
+ < 422400000 790000 104 >,
+ < 499200000 800000 124 >,
+ < 576000000 810000 144 >,
+ < 652800000 820000 165 >,
+ < 729600000 830000 186 >,
+ < 806400000 840000 208 >,
+ < 883200000 850000 229 >,
+ < 960000000 860000 252 >;
+
+ qcom,speed0-pvs4-bin-v0 =
+ < 300000000 775000 73 >,
+ < 345600000 775000 85 >,
+ < 422400000 780000 104 >,
+ < 499200000 790000 124 >,
+ < 576000000 800000 144 >,
+ < 652800000 810000 165 >,
+ < 729600000 820000 186 >,
+ < 806400000 830000 208 >,
+ < 883200000 840000 229 >,
+ < 960000000 850000 252 >;
+
+ qcom,speed0-pvs5-bin-v0 =
+ < 300000000 750000 73 >,
+ < 345600000 760000 85 >,
+ < 422400000 770000 104 >,
+ < 499200000 780000 124 >,
+ < 576000000 790000 144 >,
+ < 652800000 800000 165 >,
+ < 729600000 810000 186 >,
+ < 806400000 820000 208 >,
+ < 883200000 830000 229 >,
+ < 960000000 840000 252 >;
+
+ qcom,speed0-pvs6-bin-v0 =
+ < 300000000 750000 73 >,
+ < 345600000 750000 85 >,
+ < 422400000 760000 104 >,
+ < 499200000 770000 124 >,
+ < 576000000 780000 144 >,
+ < 652800000 790000 165 >,
+ < 729600000 800000 186 >,
+ < 806400000 810000 208 >,
+ < 883200000 820000 229 >,
+ < 960000000 830000 252 >;
+
+ qcom,speed2-pvs0-bin-v0 =
+ < 300000000 800000 72 >,
+ < 345600000 800000 83 >,
+ < 422400000 805000 102 >,
+ < 499200000 815000 121 >,
+ < 576000000 825000 141 >,
+ < 652800000 835000 161 >,
+ < 729600000 845000 181 >,
+ < 806400000 855000 202 >,
+ < 883200000 865000 223 >,
+ < 960000000 875000 245 >;
+
+ qcom,speed2-pvs1-bin-v0 =
+ < 300000000 800000 72 >,
+ < 345600000 800000 83 >,
+ < 422400000 800000 102 >,
+ < 499200000 800000 121 >,
+ < 576000000 810000 141 >,
+ < 652800000 820000 161 >,
+ < 729600000 830000 181 >,
+ < 806400000 840000 202 >,
+ < 883200000 850000 223 >,
+ < 960000000 860000 245 >;
+
+ qcom,speed2-pvs2-bin-v0 =
+ < 300000000 775000 72 >,
+ < 345600000 775000 83 >,
+ < 422400000 775000 102 >,
+ < 499200000 785000 121 >,
+ < 576000000 795000 141 >,
+ < 652800000 805000 161 >,
+ < 729600000 815000 181 >,
+ < 806400000 825000 202 >,
+ < 883200000 835000 223 >,
+ < 960000000 845000 245 >;
+
+ qcom,speed2-pvs3-bin-v0 =
+ < 300000000 775000 72 >,
+ < 345600000 775000 83 >,
+ < 422400000 775000 102 >,
+ < 499200000 775000 121 >,
+ < 576000000 780000 141 >,
+ < 652800000 790000 161 >,
+ < 729600000 800000 181 >,
+ < 806400000 810000 202 >,
+ < 883200000 820000 223 >,
+ < 960000000 830000 245 >;
+
+ qcom,speed2-pvs4-bin-v0 =
+ < 300000000 775000 72 >,
+ < 345600000 775000 83 >,
+ < 422400000 775000 102 >,
+ < 499200000 775000 121 >,
+ < 576000000 775000 141 >,
+ < 652800000 780000 161 >,
+ < 729600000 790000 181 >,
+ < 806400000 800000 202 >,
+ < 883200000 810000 223 >,
+ < 960000000 820000 245 >;
+
+ qcom,speed2-pvs5-bin-v0 =
+ < 300000000 750000 72 >,
+ < 345600000 750000 83 >,
+ < 422400000 750000 102 >,
+ < 499200000 750000 121 >,
+ < 576000000 760000 141 >,
+ < 652800000 770000 161 >,
+ < 729600000 780000 181 >,
+ < 806400000 790000 202 >,
+ < 883200000 800000 223 >,
+ < 960000000 810000 245 >;
+
+ qcom,speed2-pvs6-bin-v0 =
+ < 300000000 750000 72 >,
+ < 345600000 750000 83 >,
+ < 422400000 750000 102 >,
+ < 499200000 750000 121 >,
+ < 576000000 750000 141 >,
+ < 652800000 760000 161 >,
+ < 729600000 770000 181 >,
+ < 806400000 780000 202 >,
+ < 883200000 790000 223 >,
+ < 960000000 800000 245 >;
+
+ qcom,speed1-pvs0-bin-v0 =
+ < 300000000 775000 72 >,
+ < 345600000 775000 83 >,
+ < 422400000 775000 101 >,
+ < 499200000 780000 120 >,
+ < 576000000 790000 139 >,
+ < 652800000 800000 159 >,
+ < 729600000 810000 180 >,
+ < 806400000 820000 200 >,
+ < 883200000 830000 221 >,
+ < 960000000 840000 242 >;
+
+ qcom,speed1-pvs1-bin-v0 =
+ < 300000000 775000 72 >,
+ < 345600000 775000 83 >,
+ < 422400000 775000 101 >,
+ < 499200000 775000 120 >,
+ < 576000000 775000 139 >,
+ < 652800000 785000 159 >,
+ < 729600000 795000 180 >,
+ < 806400000 805000 200 >,
+ < 883200000 815000 221 >,
+ < 960000000 825000 242 >;
+
+ qcom,speed1-pvs2-bin-v0 =
+ < 300000000 750000 72 >,
+ < 345600000 750000 83 >,
+ < 422400000 750000 101 >,
+ < 499200000 750000 120 >,
+ < 576000000 760000 139 >,
+ < 652800000 770000 159 >,
+ < 729600000 780000 180 >,
+ < 806400000 790000 200 >,
+ < 883200000 800000 221 >,
+ < 960000000 810000 242 >;
+
+ qcom,speed1-pvs3-bin-v0 =
+ < 300000000 750000 72 >,
+ < 345600000 750000 83 >,
+ < 422400000 750000 101 >,
+ < 499200000 750000 120 >,
+ < 576000000 750000 139 >,
+ < 652800000 755000 159 >,
+ < 729600000 765000 180 >,
+ < 806400000 775000 200 >,
+ < 883200000 785000 221 >,
+ < 960000000 795000 242 >;
+
+ qcom,speed1-pvs4-bin-v0 =
+ < 300000000 750000 72 >,
+ < 345600000 750000 83 >,
+ < 422400000 750000 101 >,
+ < 499200000 750000 120 >,
+ < 576000000 750000 139 >,
+ < 652800000 750000 159 >,
+ < 729600000 755000 180 >,
+ < 806400000 765000 200 >,
+ < 883200000 775000 221 >,
+ < 960000000 785000 242 >;
+
+ qcom,speed1-pvs5-bin-v0 =
+ < 300000000 725000 72 >,
+ < 345600000 725000 83 >,
+ < 422400000 725000 101 >,
+ < 499200000 725000 120 >,
+ < 576000000 725000 139 >,
+ < 652800000 735000 159 >,
+ < 729600000 745000 180 >,
+ < 806400000 755000 200 >,
+ < 883200000 765000 221 >,
+ < 960000000 775000 242 >;
+
+ qcom,speed1-pvs6-bin-v0 =
+ < 300000000 725000 72 >,
+ < 345600000 725000 83 >,
+ < 422400000 725000 101 >,
+ < 499200000 725000 120 >,
+ < 576000000 725000 139 >,
+ < 652800000 725000 159 >,
+ < 729600000 735000 180 >,
+ < 806400000 745000 200 >,
+ < 883200000 755000 221 >,
+ < 960000000 765000 242 >;
+ };
+
+ kraitcc: clock-controller {
+ compatible = "qcom,krait-cc-v2";
+ #clock-cells = <1>;
+ };
+
soc: soc {
#address-cells = <1>;
#size-cells = <1>;
@@ -144,7 +441,57 @@
};
};
- saw_l2: regulator@f9012000 {
+ clock-controller@f9016000 {
+ compatible = "qcom,hfpll";
+ reg = <0xf9016000 0x30>;
+ clock-output-names = "hfpll_l2";
+ };
+
+ clock-controller@f908a000 {
+ compatible = "qcom,hfpll";
+ reg = <0xf908a000 0x30>, <0xf900a000 0x30>;
+ clock-output-names = "hfpll0";
+ };
+
+ clock-controller@f909a000 {
+ compatible = "qcom,hfpll";
+ reg = <0xf909a000 0x30>, <0xf900a000 0x30>;
+ clock-output-names = "hfpll1";
+ };
+
+ clock-controller@f90aa000 {
+ compatible = "qcom,hfpll";
+ reg = <0xf90aa000 0x30>, <0xf900a000 0x30>;
+ clock-output-names = "hfpll2";
+ };
+
+ clock-controller@f90ba000 {
+ compatible = "qcom,hfpll";
+ reg = <0xf90ba000 0x30>, <0xf900a000 0x30>;
+ clock-output-names = "hfpll3";
+ };
+
+ saw0: power-controller@f9089000 {
+ compatible = "qcom,msm8974-saw2-v2.1-cpu";
+ reg = <0xf9089000 0x1000>;
+ };
+
+ saw1: power-controller@f9099000 {
+ compatible = "qcom,msm8974-saw2-v2.1-cpu";
+ reg = <0xf9099000 0x1000>;
+ };
+
+ saw2: power-controller@f90a9000 {
+ compatible = "qcom,msm8974-saw2-v2.1-cpu";
+ reg = <0xf90a9000 0x1000>;
+ };
+
+ saw3: power-controller@f90b9000 {
+ compatible = "qcom,msm8974-saw2-v2.1-cpu";
+ reg = <0xf90b9000 0x1000>;
+ };
+
+ saw_l2: power-controller@f9012000 {
compatible = "qcom,saw2";
reg = <0xf9012000 0x1000>;
regulator;
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 06075b6d2463..b31f40d1c30b 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -109,6 +109,7 @@ CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_STAT_DETAILS=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
CONFIG_CPU_IDLE=y
+CONFIG_ARM_QCOM_CPUIDLE=y
CONFIG_NEON=y
CONFIG_ARM_ZYNQ_CPUIDLE=y
CONFIG_NET=y
@@ -133,7 +134,18 @@ CONFIG_CAN_BCM=y
CONFIG_CAN_DEV=y
CONFIG_CAN_XILINXCAN=y
CONFIG_CAN_MCP251X=y
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_ATH3K=y
CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
CONFIG_MAC80211=m
CONFIG_RFKILL=y
CONFIG_RFKILL_INPUT=y
@@ -175,6 +187,7 @@ CONFIG_SATA_RCAR=y
CONFIG_NETDEVICES=y
CONFIG_HIX5HD2_GMAC=y
CONFIG_SUN4I_EMAC=y
+CONFIG_ATL1C=y
CONFIG_MACB=y
CONFIG_NET_CALXEDA_XGMAC=y
CONFIG_IGB=y
@@ -197,6 +210,10 @@ CONFIG_USB_PEGASUS=y
CONFIG_USB_USBNET=y
CONFIG_USB_NET_SMSC75XX=y
CONFIG_USB_NET_SMSC95XX=y
+CONFIG_ATH_CARDS=y
+CONFIG_ATH_DEBUG=y
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
CONFIG_BRCMFMAC=m
CONFIG_RT2X00=m
CONFIG_RT2800USB=m
@@ -285,9 +302,14 @@ CONFIG_SPI_TEGRA20_SFLASH=y
CONFIG_SPI_TEGRA20_SLINK=y
CONFIG_SPI_XILINX=y
CONFIG_SPI_SPIDEV=y
+CONFIG_SPMI=y
CONFIG_PINCTRL_AS3722=y
CONFIG_PINCTRL_PALMAS=y
+CONFIG_PINCTRL_APQ8064=y
CONFIG_PINCTRL_APQ8084=y
+CONFIG_PINCTRL_IPQ8064=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_PINCTRL_SSBI_PMIC=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_GENERIC_PLATFORM=y
CONFIG_GPIO_DAVINCI=y
@@ -318,6 +340,7 @@ CONFIG_CPU_THERMAL=y
CONFIG_RCAR_THERMAL=y
CONFIG_ARMADA_THERMAL=y
CONFIG_DAVINCI_WATCHDOG
+CONFIG_THERMAL_TSENS8960=y
CONFIG_ST_THERMAL_SYSCFG=y
CONFIG_ST_THERMAL_MEMMAP=y
CONFIG_WATCHDOG=y
@@ -334,6 +357,8 @@ CONFIG_MFD_CROS_EC=y
CONFIG_MFD_CROS_EC_SPI=y
CONFIG_MFD_MAX77686=y
CONFIG_MFD_MAX8907=y
+CONFIG_MFD_PM8921_CORE=y
+CONFIG_MFD_QCOM_RPM=y
CONFIG_MFD_SEC_CORE=y
CONFIG_MFD_STMPE=y
CONFIG_MFD_PALMAS=y
@@ -353,6 +378,7 @@ CONFIG_REGULATOR_MAX8907=y
CONFIG_REGULATOR_MAX8973=y
CONFIG_REGULATOR_MAX77686=y
CONFIG_REGULATOR_PALMAS=y
+CONFIG_REGULATOR_QCOM_RPM=y
CONFIG_REGULATOR_S2MPS11=y
CONFIG_REGULATOR_S5M8767=y
CONFIG_REGULATOR_TPS51632=y
@@ -412,6 +438,7 @@ CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_MVEBU=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_EHCI_EXYNOS=y
CONFIG_USB_EHCI_TEGRA=y
CONFIG_USB_EHCI_HCD_STI=y
@@ -433,13 +460,14 @@ CONFIG_SAMSUNG_USB2PHY=y
CONFIG_SAMSUNG_USB3PHY=y
CONFIG_USB_GPIO_VBUS=y
CONFIG_USB_ISP1301=y
+CONFIG_USB_MSM_OTG=y
CONFIG_USB_MXS_PHY=y
CONFIG_USB_RCAR_PHY=m
CONFIG_USB_RCAR_GEN2_PHY=m
CONFIG_USB_GADGET=y
CONFIG_USB_RENESAS_USBHS_UDC=m
CONFIG_MMC=y
-CONFIG_MMC_BLOCK_MINORS=16
+CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_ARMMMCI=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -455,6 +483,7 @@ CONFIG_MMC_SDHCI_BCM_KONA=y
CONFIG_MMC_SDHCI_ST=y
CONFIG_MMC_OMAP=y
CONFIG_MMC_OMAP_HS=y
+CONFIG_MMC_SDHCI_MSM=y
CONFIG_MMC_MVSDIO=y
CONFIG_MMC_SDHI=y
CONFIG_MMC_DW=y
@@ -515,6 +544,7 @@ CONFIG_IMX_SDMA=y
CONFIG_IMX_DMA=y
CONFIG_MXS_DMA=y
CONFIG_DMA_OMAP=y
+CONFIG_QCOM_BAM_DMA=y
CONFIG_XILINX_VDMA=y
CONFIG_STAGING=y
CONFIG_SENSORS_ISL29018=y
@@ -525,12 +555,18 @@ CONFIG_SERIO_NVEC_PS2=y
CONFIG_NVEC_POWER=y
CONFIG_NVEC_PAZ00=y
CONFIG_QCOM_GSBI=y
+CONFIG_QCOM_PM=y
CONFIG_COMMON_CLK_QCOM=y
CONFIG_COMMON_CLK_MAX77686=y
CONFIG_APQ_MMCC_8084=y
CONFIG_MSM_GCC_8660=y
CONFIG_MSM_MMCC_8960=y
CONFIG_MSM_MMCC_8974=y
+CONFIG_QCOM_RPMCC=y
+CONFIG_QCOM_HFPLL=y
+CONFIG_KPSS_XCC=y
+CONFIG_KRAITCC=y
+CONFIG_QCOM_IOMMU_V0=y
CONFIG_TEGRA_IOMMU_GART=y
CONFIG_TEGRA_IOMMU_SMMU=y
CONFIG_MEMORY=y
@@ -545,11 +581,14 @@ CONFIG_PWM_VT8500=y
CONFIG_PHY_HIX5HD2_SATA=y
CONFIG_OMAP_USB2=y
CONFIG_TI_PIPE3=y
+CONFIG_PHY_QCOM_APQ8064_SATA=y
CONFIG_PHY_MIPHY28LP=y
CONFIG_PHY_MIPHY365X=y
CONFIG_PHY_STIH41X_USB=y
CONFIG_PHY_STIH407_USB=y
CONFIG_PHY_SUN4I_USB=y
+CONFIG_EEPROM=y
+CONFIG_QCOM_QFPROM=y
CONFIG_EXT4_FS=y
CONFIG_AUTOFS4_FS=y
CONFIG_MSDOS_FS=y
@@ -575,3 +614,7 @@ CONFIG_LOCKUP_DETECTOR=y
CONFIG_CRYPTO_DEV_TEGRA_AES=y
CONFIG_CPUFREQ_DT=y
CONFIG_KEYSTONE_IRQ=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_TRACEPOINT_BENCHMARK=y
diff --git a/arch/arm/configs/qcom_defconfig b/arch/arm/configs/qcom_defconfig
index 8c7da3319d82..4ca12df69ed5 100644
--- a/arch/arm/configs/qcom_defconfig
+++ b/arch/arm/configs/qcom_defconfig
@@ -1,8 +1,10 @@
CONFIG_SYSVIPC=y
+CONFIG_FHANDLE=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
+CONFIG_CGROUPS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS_ALL=y
@@ -21,18 +23,35 @@ CONFIG_ARCH_QCOM=y
CONFIG_ARCH_MSM8X60=y
CONFIG_ARCH_MSM8960=y
CONFIG_ARCH_MSM8974=y
+CONFIG_PCI=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_STUB=y
+CONFIG_PCI_HOST_GENERIC=y
+CONFIG_PCIEPORTBUS=y
CONFIG_SMP=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
CONFIG_HIGHPTE=y
CONFIG_CLEANCACHE=y
+CONFIG_CMA=y
+CONFIG_SECCOMP=y
CONFIG_ARM_APPENDED_DTB=y
CONFIG_ARM_ATAG_DTB_COMPAT=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_ARM_QCOM_CPUFREQ=y
CONFIG_CPU_IDLE=y
+CONFIG_ARM_QCOM_CPUIDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -47,25 +66,43 @@ CONFIG_IP_PNP_DHCP=y
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
# CONFIG_IPV6 is not set
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_ATH3K=y
CONFIG_CFG80211=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=y
CONFIG_RFKILL=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=64
CONFIG_MTD=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_M25P80=y
CONFIG_MTD_SPI_NOR=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-CONFIG_SCSI=y
+CONFIG_EEPROM_AT24=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
CONFIG_CHR_DEV_SCH=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
+CONFIG_ATL1C=y
+CONFIG_KS8851=y
CONFIG_MDIO_BITBANG=y
CONFIG_MDIO_GPIO=y
CONFIG_SLIP=y
@@ -74,6 +111,9 @@ CONFIG_SLIP_MODE_SLIP6=y
CONFIG_USB_USBNET=y
# CONFIG_USB_NET_AX8817X is not set
# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_ATH_CARDS=m
+CONFIG_ATH6KL=m
+CONFIG_ATH6KL_SDIO=m
CONFIG_INPUT_EVDEV=y
# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_MOUSE_PS2 is not set
@@ -86,7 +126,6 @@ CONFIG_SERIO_LIBPS2=y
CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_HW_RANDOM=y
-CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_QUP=y
CONFIG_SPI=y
@@ -97,16 +136,83 @@ CONFIG_PINCTRL_APQ8084=y
CONFIG_PINCTRL_IPQ8064=y
CONFIG_PINCTRL_MSM8960=y
CONFIG_PINCTRL_MSM8X74=y
+CONFIG_PINCTRL_SSBI_PMIC=y
CONFIG_DEBUG_GPIO=y
CONFIG_GPIO_SYSFS=y
CONFIG_POWER_SUPPLY=y
CONFIG_POWER_RESET=y
CONFIG_POWER_RESET_MSM=y
CONFIG_THERMAL=y
+CONFIG_THERMAL_TSENS8960=y
+CONFIG_WATCHDOG=y
+CONFIG_QCOM_WDT=y
+CONFIG_MFD_PM8921_CORE=y
+CONFIG_MFD_QCOM_RPM=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_MFD_SYSCON=y
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_QCOM_RPM=y
CONFIG_MEDIA_SUPPORT=y
-CONFIG_FB=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_USB_GSPCA=y
+CONFIG_USB_M5602=m
+CONFIG_USB_STV06XX=m
+CONFIG_USB_GL860=m
+CONFIG_USB_GSPCA_BENQ=m
+CONFIG_USB_GSPCA_CONEX=m
+CONFIG_USB_GSPCA_CPIA1=m
+CONFIG_USB_GSPCA_DTCS033=m
+CONFIG_USB_GSPCA_ETOMS=m
+CONFIG_USB_GSPCA_FINEPIX=m
+CONFIG_USB_GSPCA_JEILINJ=m
+CONFIG_USB_GSPCA_JL2005BCD=m
+CONFIG_USB_GSPCA_KINECT=m
+CONFIG_USB_GSPCA_KONICA=m
+CONFIG_USB_GSPCA_MARS=m
+CONFIG_USB_GSPCA_MR97310A=m
+CONFIG_USB_GSPCA_NW80X=m
+CONFIG_USB_GSPCA_OV519=m
+CONFIG_USB_GSPCA_OV534=m
+CONFIG_USB_GSPCA_OV534_9=m
+CONFIG_USB_GSPCA_PAC207=m
+CONFIG_USB_GSPCA_PAC7302=m
+CONFIG_USB_GSPCA_PAC7311=m
+CONFIG_USB_GSPCA_SE401=m
+CONFIG_USB_GSPCA_SN9C2028=m
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SONIXB=m
+CONFIG_USB_GSPCA_SONIXJ=m
+CONFIG_USB_GSPCA_SPCA500=m
+CONFIG_USB_GSPCA_SPCA501=m
+CONFIG_USB_GSPCA_SPCA505=m
+CONFIG_USB_GSPCA_SPCA506=m
+CONFIG_USB_GSPCA_SPCA508=m
+CONFIG_USB_GSPCA_SPCA561=m
+CONFIG_USB_GSPCA_SPCA1528=m
+CONFIG_USB_GSPCA_SQ905=m
+CONFIG_USB_GSPCA_SQ905C=m
+CONFIG_USB_GSPCA_SQ930X=m
+CONFIG_USB_GSPCA_STK014=m
+CONFIG_USB_GSPCA_STK1135=m
+CONFIG_USB_GSPCA_STV0680=m
+CONFIG_USB_GSPCA_SUNPLUS=m
+CONFIG_USB_GSPCA_T613=m
+CONFIG_USB_GSPCA_TOPRO=m
+CONFIG_USB_GSPCA_TV8532=m
+CONFIG_USB_GSPCA_VC032X=m
+CONFIG_USB_GSPCA_VICAM=m
+CONFIG_USB_GSPCA_XIRLINK_CIT=m
+CONFIG_USB_GSPCA_ZC3XX=m
+CONFIG_USB_PWC=m
+CONFIG_USB_ZR364XX=m
+CONFIG_DRM=y
+CONFIG_DRM_PANEL_SIMPLE=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_DYNAMIC_MINORS=y
@@ -115,17 +221,34 @@ CONFIG_SND_DYNAMIC_MINORS=y
# CONFIG_SND_USB is not set
CONFIG_SND_SOC=y
CONFIG_HID_BATTERY_STRENGTH=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_MAGICMOUSE=m
+CONFIG_HID_MICROSOFT=m
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_OTG_FSM=y
CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_MSM=y
CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UAS=y
+CONFIG_USB_CHIPIDEA=y
+CONFIG_USB_CHIPIDEA_UDC=y
+CONFIG_USB_CHIPIDEA_HOST=y
CONFIG_USB_SERIAL=y
+CONFIG_USB_MSM_OTG=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_ETH=m
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FUNCTIONFS=m
+CONFIG_USB_FUNCTIONFS_RNDIS=y
+CONFIG_USB_MASS_STORAGE=m
CONFIG_MMC=y
-CONFIG_MMC_BLOCK_MINORS=16
+CONFIG_MMC_BLOCK_MINORS=32
CONFIG_MMC_ARMMMCI=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PLTFM=y
@@ -135,13 +258,18 @@ CONFIG_DMADEVICES=y
CONFIG_QCOM_BAM_DMA=y
CONFIG_STAGING=y
CONFIG_QCOM_GSBI=y
+CONFIG_QCOM_PM=y
CONFIG_COMMON_CLK_QCOM=y
CONFIG_APQ_MMCC_8084=y
CONFIG_IPQ_GCC_806X=y
CONFIG_MSM_GCC_8660=y
CONFIG_MSM_MMCC_8960=y
CONFIG_MSM_MMCC_8974=y
-CONFIG_MSM_IOMMU=y
+CONFIG_QCOM_HFPLL=y
+CONFIG_KPSS_XCC=y
+CONFIG_KRAITCC=y
+CONFIG_QCOM_RPMCC=y
+CONFIG_QCOM_IOMMU_V0=y
CONFIG_PHY_QCOM_APQ8064_SATA=y
CONFIG_PHY_QCOM_IPQ806X_SATA=y
CONFIG_EXT2_FS=y
@@ -149,13 +277,19 @@ CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
CONFIG_EXT4_FS=y
+CONFIG_FANOTIFY=y
+CONFIG_AUTOFS4_FS=y
CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
-CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
CONFIG_JFFS2_FS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V3_ACL=y
CONFIG_CIFS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
@@ -167,5 +301,5 @@ CONFIG_DEBUG_INFO=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_LOCKUP_DETECTOR=y
# CONFIG_DETECT_HUNG_TASK is not set
-# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index 48003ea652b9..afa30dd9521d 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -4,6 +4,8 @@ menuconfig ARCH_QCOM
select ARM_GIC
select ARM_AMBA
select PINCTRL
+ select MIGHT_HAVE_PCI
+ select PCI_DOMAINS if PCI
select QCOM_SCM if SMP
help
Support for Qualcomm's devicetree based systems.
@@ -22,7 +24,4 @@ config ARCH_MSM8974
bool "Enable support for MSM8974"
select HAVE_ARM_ARCH_TIMER
-config QCOM_SCM
- bool
-
endif
diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
index 8f756ae1ae31..e324375fa919 100644
--- a/arch/arm/mach-qcom/Makefile
+++ b/arch/arm/mach-qcom/Makefile
@@ -1,5 +1,2 @@
obj-y := board.o
obj-$(CONFIG_SMP) += platsmp.o
-obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o
-
-CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c
index 09cffed4c0a4..0f8b5147a78f 100644
--- a/arch/arm/mach-qcom/platsmp.c
+++ b/arch/arm/mach-qcom/platsmp.c
@@ -20,7 +20,7 @@
#include <asm/smp_plat.h>
-#include "scm-boot.h"
+#include <soc/qcom/scm-boot.h>
#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0
#define SCSS_CPU1CORE_RESET 0x2d80
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 1b8e97331ffb..610965dd87ce 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -177,6 +177,12 @@ config ARCH_MEDIATEK
help
Support for Mediatek MT65xx & MT81xx ARMv8 SoCs
+config ARCH_QCOM
+ bool "Qualcomm Platforms"
+ select PINCTRL
+ help
+ This enables support for the ARMv8 based Qualcomm chipsets.
+
config ARCH_SEATTLE
bool "AMD Seattle SoC Family"
help
diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile
index e0350caf049e..8517f15870be 100644
--- a/arch/arm64/boot/dts/Makefile
+++ b/arch/arm64/boot/dts/Makefile
@@ -5,5 +5,6 @@ dts-dirs += cavium
dts-dirs += exynos
dts-dirs += freescale
dts-dirs += mediatek
+dts-dirs += qcom
subdir-y := $(dts-dirs)
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
new file mode 100644
index 000000000000..360ec4c464b6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -0,0 +1,5 @@
+dtb-$(CONFIG_ARCH_QCOM) += msm8916-mtp.dtb
+
+always := $(dtb-y)
+subdir-y := $(dts-dirs)
+clean-files := *.dtb
diff --git a/arch/arm64/boot/dts/qcom/msm8916-mtp.dts b/arch/arm64/boot/dts/qcom/msm8916-mtp.dts
new file mode 100644
index 000000000000..8be101c0e903
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8916-mtp.dts
@@ -0,0 +1,24 @@
+/* Copyright (c) 2014-2015, The Linux Foundation. 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 version 2 and
+* only version 2 as published by the Free Software Foundation.
+*
+* 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.
+*/
+
+/dts-v1/;
+
+#include <dt-bindings/arm/qcom-ids.h>
+#include "msm8916-mtp.dtsi"
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM 8916 MTP";
+ compatible = "qcom,msm8916-mtp", "qcom,msm8916-mtp-smb1360",
+ "qcom,msm8916", "qcom,mtp";
+ qcom,board-id = <QCOM_BRD_ID(MTP, 1, 0) QCOM_BRD_SUBTYPE_DEFAULT>,
+ <QCOM_BRD_ID(MTP, 1, 0) QCOM_BRD_SUBTYPE_MTP8916_SMB1360>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8916-mtp.dtsi b/arch/arm64/boot/dts/qcom/msm8916-mtp.dtsi
new file mode 100644
index 000000000000..fa229d01d5ee
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8916-mtp.dtsi
@@ -0,0 +1,36 @@
+/* Copyright (c) 2014-2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 "msm8916.dtsi"
+
+/ {
+ chosen {
+ stdout-path = &blsp1_uart2;
+ };
+
+ soc {
+ serial@78b0000 {
+ status = "okay";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&blsp1_uart2_default>;
+ pinctrl-1 = <&blsp1_uart2_sleep>;
+ };
+ };
+};
+
+&blsp_dma {
+ status = "okay";
+};
+
+&blsp_spi3 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi
new file mode 100644
index 000000000000..b6f9135adf6e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi
@@ -0,0 +1,505 @@
+/* Copyright (c) 2013-2015, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 "skeleton.dtsi"
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/clock/qcom,gcc-msm8916.h>
+#include <dt-bindings/reset/qcom,gcc-msm8916.h>
+#include <dt-bindings/arm/qcom-ids.h>
+
+/ {
+ model = "Qualcomm Technologies, Inc. MSM8916";
+ compatible = "qcom,msm8916";
+ qcom,msm-id = <QCOM_ID_MSM8916 0>,
+ <QCOM_ID_MSM8216 0>,
+ <QCOM_ID_MSM8116 0>,
+ <QCOM_ID_MSM8616 0>;
+
+ interrupt-parent = <&intc>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53", "arm,armv8";
+ reg = <0x0>;
+ };
+
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53", "arm,armv8";
+ reg = <0x1>;
+ };
+
+ CPU2: cpu@2 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53", "arm,armv8";
+ reg = <0x2>;
+ };
+
+ CPU3: cpu@3 {
+ device_type = "cpu";
+ compatible = "arm,cortex-a53", "arm,armv8";
+ reg = <0x3>;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <GIC_PPI 2 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 3 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 4 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+ <GIC_PPI 1 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
+ };
+
+ soc: soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0 0 0xffffffff>;
+ compatible = "simple-bus";
+
+ pinctrl@1000000 {
+ compatible = "qcom,msm8916-pinctrl";
+ reg = <0x1000000 0x300000>;
+ interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ blsp1_uart2_default: blsp1_uart2_default {
+ pinmux {
+ function = "blsp_uart2";
+ pins = "gpio4", "gpio5";
+ };
+ pinconf {
+ pins = "gpio4", "gpio5";
+ drive-strength = <16>;
+ bias-disable;
+ };
+ };
+
+ blsp1_uart2_sleep: blsp1_uart2_sleep {
+ pinmux {
+ function = "blsp_uart2";
+ pins = "gpio4", "gpio5";
+ };
+ pinconf {
+ pins = "gpio4", "gpio5";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ spi1_default: spi1_default {
+ pinmux {
+ function = "blsp_spi1";
+ pins = "gpio0", "gpio1", "gpio3";
+ };
+ pinmux_cs {
+ function = "gpio";
+ pins = "gpio2";
+ };
+ pinconf {
+ pins = "gpio0", "gpio1", "gpio3";
+ drive-strength = <12>;
+ bias-disable;
+ };
+ pinconf_cs {
+ pins = "gpio2";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ spi1_sleep: spi1_sleep {
+ pinmux {
+ function = "gpio";
+ pins = "gpio0", "gpio1", "gpio2", "gpio3";
+ };
+ pinconf {
+ pins = "gpio0", "gpio1", "gpio2", "gpio3";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ spi2_default: spi2_default {
+ pinmux {
+ function = "blsp_spi2";
+ pins = "gpio4", "gpio5", "gpio7";
+ };
+ pinmux_cs {
+ function = "gpio";
+ pins = "gpio6";
+ };
+ pinconf {
+ pins = "gpio4", "gpio5", "gpio6", "gpio7";
+ drive-strength = <12>;
+ bias-disable;
+ };
+ pinconf_cs {
+ pins = "gpio6";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ spi2_sleep: spi2_sleep {
+ pinmux {
+ function = "gpio";
+ pins = "gpio4", "gpio5", "gpio6", "gpio7";
+ };
+ pinconf {
+ pins = "gpio4", "gpio5", "gpio6", "gpio7";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ spi3_default: spi3_default {
+ pinmux {
+ function = "blsp_spi3";
+ pins = "gpio8", "gpio9", "gpio11";
+ };
+ pinmux_cs {
+ function = "gpio";
+ pins = "gpio10";
+ };
+ pinconf {
+ pins = "gpio8", "gpio9", "gpio10", "gpio11";
+ drive-strength = <12>;
+ bias-disable;
+ };
+ pinconf_cs {
+ pins = "gpio10";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ spi3_sleep: spi3_sleep {
+ pinmux {
+ function = "gpio";
+ pins = "gpio8", "gpio9", "gpio10", "gpio11";
+ };
+ pinconf {
+ pins = "gpio8", "gpio9", "gpio10", "gpio11";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ spi4_default: spi4_default {
+ pinmux {
+ function = "blsp_spi4";
+ pins = "gpio12", "gpio13", "gpio15";
+ };
+ pinmux_cs {
+ function = "gpio";
+ pins = "gpio14";
+ };
+ pinconf {
+ pins = "gpio12", "gpio13", "gpio14", "gpio15";
+ drive-strength = <12>;
+ bias-disable;
+ };
+ pinconf_cs {
+ pins = "gpio14";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ spi4_sleep: spi4_sleep {
+ pinmux {
+ function = "gpio";
+ pins = "gpio12", "gpio13", "gpio14", "gpio15";
+ };
+ pinconf {
+ pins = "gpio12", "gpio13", "gpio14", "gpio15";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ spi5_default: spi5_default {
+ pinmux {
+ function = "blsp_spi5";
+ pins = "gpio16", "gpio17", "gpio19";
+ };
+ pinmux_cs {
+ function = "gpio";
+ pins = "gpio18";
+ };
+ pinconf {
+ pins = "gpio16", "gpio17", "gpio18", "gpio19";
+ drive-strength = <12>;
+ bias-disable;
+ };
+ pinconf_cs {
+ pins = "gpio18";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ spi5_sleep: spi5_sleep {
+ pinmux {
+ function = "gpio";
+ pins = "gpio16", "gpio17", "gpio18", "gpio19";
+ };
+ pinconf {
+ pins = "gpio16", "gpio17", "gpio18", "gpio19";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ spi6_default: spi6_default {
+ pinmux {
+ function = "blsp_spi6";
+ pins = "gpio20", "gpio21", "gpio23";
+ };
+ pinmux_cs {
+ function = "gpio";
+ pins = "gpio22";
+ };
+ pinconf {
+ pins = "gpio20", "gpio21", "gpio22", "gpio23";
+ drive-strength = <12>;
+ bias-disable;
+ };
+ pinconf_cs {
+ pins = "gpio22";
+ drive-strength = <2>;
+ bias-disable;
+ output-high;
+ };
+ };
+
+ spi6_sleep: spi6_sleep {
+ pinmux {
+ function = "gpio";
+ pins = "gpio20", "gpio21", "gpio22", "gpio23";
+ };
+ pinconf {
+ pins = "gpio20", "gpio21", "gpio22", "gpio23";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ gcc: qcom,gcc@1800000 {
+ compatible = "qcom,gcc-msm8916";
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ reg = <0x1800000 0x80000>;
+ };
+
+ blsp1_uart2: serial@78b0000 {
+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ reg = <0x78b0000 0x200>;
+ interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ status = "disabled";
+ };
+
+ blsp_dma: dma@7884000 {
+ compatible = "qcom,bam-v1.7.0";
+ reg = <0x07884000 0x23000>;
+ interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "bam_clk";
+ #dma-cells = <1>;
+ qcom,ee = <0>;
+ status = "disabled";
+ };
+
+ blsp_spi1: spi@78b5000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ reg = <0x078b5000 0x600>;
+ interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_QUP1_SPI_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ dmas = <&blsp_dma 5>, <&blsp_dma 4>;
+ dma-names = "rx", "tx";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&spi1_default>;
+ pinctrl-1 = <&spi1_sleep>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ blsp_spi2: spi@78b6000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ reg = <0x078b6000 0x600>;
+ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_QUP2_SPI_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ dmas = <&blsp_dma 7>, <&blsp_dma 6>;
+ dma-names = "rx", "tx";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&spi2_default>;
+ pinctrl-1 = <&spi2_sleep>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ blsp_spi3: spi@78b7000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ reg = <0x078b7000 0x600>;
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_QUP3_SPI_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ dmas = <&blsp_dma 9>, <&blsp_dma 8>;
+ dma-names = "rx", "tx";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&spi3_default>;
+ pinctrl-1 = <&spi3_sleep>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ blsp_spi4: spi@78b8000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ reg = <0x078b8000 0x600>;
+ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_QUP4_SPI_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ dmas = <&blsp_dma 11>, <&blsp_dma 10>;
+ dma-names = "rx", "tx";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&spi4_default>;
+ pinctrl-1 = <&spi4_sleep>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ blsp_spi5: spi@78b9000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ reg = <0x078b9000 0x600>;
+ interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_QUP5_SPI_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ dmas = <&blsp_dma 13>, <&blsp_dma 12>;
+ dma-names = "rx", "tx";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&spi5_default>;
+ pinctrl-1 = <&spi5_sleep>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ blsp_spi6: spi@78ba000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ reg = <0x078ba000 0x600>;
+ interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_QUP6_SPI_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ dmas = <&blsp_dma 15>, <&blsp_dma 14>;
+ dma-names = "rx", "tx";
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&spi6_default>;
+ pinctrl-1 = <&spi6_sleep>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ intc: interrupt-controller@b000000 {
+ compatible = "qcom,msm-qgic2";
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ reg = <0x0b000000 0x1000>, <0x0b002000 0x1000>;
+ };
+
+ timer@b020000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ compatible = "arm,armv7-timer-mem";
+ reg = <0xb020000 0x1000>;
+ clock-frequency = <19200000>;
+
+ frame@b021000 {
+ frame-number = <0>;
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0xb021000 0x1000>,
+ <0xb022000 0x1000>;
+ };
+
+ frame@b023000 {
+ frame-number = <1>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0xb023000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@b024000 {
+ frame-number = <2>;
+ interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0xb024000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@b025000 {
+ frame-number = <3>;
+ interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0xb025000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@b026000 {
+ frame-number = <4>;
+ interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0xb026000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@b027000 {
+ frame-number = <5>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0xb027000 0x1000>;
+ status = "disabled";
+ };
+
+ frame@b028000 {
+ frame-number = <6>;
+ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+ reg = <0xb028000 0x1000>;
+ status = "disabled";
+ };
+ };
+ };
+};
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index be1f12a5a5f0..d54a1f60b5cc 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -33,6 +33,7 @@ CONFIG_MODULE_UNLOAD=y
# CONFIG_IOSCHED_DEADLINE is not set
CONFIG_ARCH_FSL_LS2085A=y
CONFIG_ARCH_MEDIATEK=y
+CONFIG_ARCH_QCOM=y
CONFIG_ARCH_THUNDER=y
CONFIG_ARCH_VEXPRESS=y
CONFIG_ARCH_XGENE=y
@@ -93,11 +94,14 @@ CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_MT6577=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_VIRTIO_CONSOLE=y
# CONFIG_HW_RANDOM is not set
CONFIG_SPI=y
CONFIG_SPI_PL022=y
+CONFIG_PINCTRL_MSM8916=y
CONFIG_GPIO_PL061=y
CONFIG_GPIO_XGENE=y
# CONFIG_HWMON is not set
@@ -127,6 +131,8 @@ CONFIG_RTC_DRV_EFI=y
CONFIG_RTC_DRV_XGENE=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_MMIO=y
+CONFIG_COMMON_CLK_QCOM=y
+CONFIG_MSM_GCC_8916=y
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_PHY_XGENE=y
CONFIG_EXT2_FS=y
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
index 69a094c3783d..8fb7a9edc3a9 100644
--- a/drivers/clk/clk-mux.c
+++ b/drivers/clk/clk-mux.c
@@ -29,35 +29,24 @@
#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw)
-static u8 clk_mux_get_parent(struct clk_hw *hw)
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
+ unsigned int *table, unsigned long flags)
{
- struct clk_mux *mux = to_clk_mux(hw);
int num_parents = __clk_get_num_parents(hw->clk);
- u32 val;
- /*
- * FIXME need a mux-specific flag to determine if val is bitwise or numeric
- * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
- * to 0x7 (index starts at one)
- * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
- * val = 0x4 really means "bit 2, index starts at bit 0"
- */
- val = clk_readl(mux->reg) >> mux->shift;
- val &= mux->mask;
-
- if (mux->table) {
+ if (table) {
int i;
for (i = 0; i < num_parents; i++)
- if (mux->table[i] == val)
+ if (table[i] == val)
return i;
return -EINVAL;
}
- if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+ if (val && (flags & CLK_MUX_INDEX_BIT))
val = ffs(val) - 1;
- if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+ if (val && (flags & CLK_MUX_INDEX_ONE))
val--;
if (val >= num_parents)
@@ -65,24 +54,53 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
return val;
}
+EXPORT_SYMBOL_GPL(clk_mux_get_parent);
-static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+static u8 _clk_mux_get_parent(struct clk_hw *hw)
{
struct clk_mux *mux = to_clk_mux(hw);
u32 val;
- unsigned long flags = 0;
- if (mux->table)
- index = mux->table[index];
+ /*
+ * FIXME need a mux-specific flag to determine if val is bitwise or numeric
+ * e.g. sys_clkin_ck's clksel field is 3 bits wide, but ranges from 0x1
+ * to 0x7 (index starts at one)
+ * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so
+ * val = 0x4 really means "bit 2, index starts at bit 0"
+ */
+ val = clk_readl(mux->reg) >> mux->shift;
+ val &= mux->mask;
+
+ return clk_mux_get_parent(hw, val, mux->table, mux->flags);
+}
+
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
+ unsigned long flags)
+{
+ unsigned int val = index;
- else {
- if (mux->flags & CLK_MUX_INDEX_BIT)
+ if (table) {
+ val = table[val];
+ } else {
+ if (flags & CLK_MUX_INDEX_BIT)
index = 1 << index;
- if (mux->flags & CLK_MUX_INDEX_ONE)
- index++;
+ if (flags & CLK_MUX_INDEX_ONE)
+ val++;
}
+ return val;
+}
+EXPORT_SYMBOL_GPL(clk_mux_reindex);
+
+static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_mux *mux = to_clk_mux(hw);
+ u32 val;
+ unsigned long flags = 0;
+
+ index = clk_mux_reindex(index, mux->table, mux->flags);
+
if (mux->lock)
spin_lock_irqsave(mux->lock, flags);
@@ -102,21 +120,21 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
}
const struct clk_ops clk_mux_ops = {
- .get_parent = clk_mux_get_parent,
+ .get_parent = _clk_mux_get_parent,
.set_parent = clk_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
};
EXPORT_SYMBOL_GPL(clk_mux_ops);
const struct clk_ops clk_mux_ro_ops = {
- .get_parent = clk_mux_get_parent,
+ .get_parent = _clk_mux_get_parent,
};
EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
struct clk *clk_register_mux_table(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u32 mask,
- u8 clk_mux_flags, u32 *table, spinlock_t *lock)
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock)
{
struct clk_mux *mux;
struct clk *clk;
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 0d7ab52b7ab0..6fcd8fa987a4 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -38,6 +38,11 @@ config IPQ_LCC_806X
Say Y if you want to use audio devices such as i2s, pcm,
S/PDIF, etc.
+config QCOM_GDSC
+ bool
+ select PM_GENERIC_DOMAINS if PM
+ depends on COMMON_CLK_QCOM
+
config MSM_GCC_8660
tristate "MSM8660 Global Clock Controller"
depends on COMMON_CLK_QCOM
@@ -46,6 +51,15 @@ config MSM_GCC_8660
Say Y if you want to use peripheral devices such as UART, SPI,
i2c, USB, SD/eMMC, etc.
+config MSM_GCC_8916
+ tristate "MSM8916 Global Clock Controller"
+ select QCOM_GDSC
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the global clock controller on msm8916 devices.
+ Say Y if you want to use devices such as UART, SPI i2c, USB,
+ SD/eMMC, display, graphics, camera etc.
+
config MSM_GCC_8960
tristate "APQ8064/MSM8960 Global Clock Controller"
depends on COMMON_CLK_QCOM
@@ -74,6 +88,7 @@ config MSM_MMCC_8960
config MSM_GCC_8974
tristate "MSM8974 Global Clock Controller"
+ select QCOM_GDSC
depends on COMMON_CLK_QCOM
help
Support for the global clock controller on msm8974 devices.
@@ -83,8 +98,17 @@ config MSM_GCC_8974
config MSM_MMCC_8974
tristate "MSM8974 Multimedia Clock Controller"
select MSM_GCC_8974
+ select QCOM_GDSC
depends on COMMON_CLK_QCOM
help
Support for the multimedia clock controller on msm8974 devices.
Say Y if you want to support multimedia devices such as display,
graphics, video encode/decode, camera, etc.
+
+config QCOM_RPMCC
+ tristate "Qualcomm RPM Clock Controller"
+ depends on COMMON_CLK_QCOM
+ help
+ Support for the RPM clock controller which controls fabric clocks
+ on SoCs like APQ8064.
+ Say Y if you want to use fabric clocks like AFAB, DAYTONA etc.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 617826469595..e6eb968e9dd2 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -9,14 +9,17 @@ clk-qcom-y += clk-branch.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
clk-qcom-y += reset.o
+clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o
obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o
obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o
+obj-$(CONFIG_MSM_GCC_8916) += gcc-msm8916.o
obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o
obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o
obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o
obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
+obj-$(CONFIG_QCOM_RPMCC) += clk-rpm.o
diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c
new file mode 100644
index 000000000000..c9107e3723db
--- /dev/null
+++ b/drivers/clk/qcom/clk-rpm.c
@@ -0,0 +1,373 @@
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk-provider.h>
+#include <linux/mfd/qcom_rpm.h>
+#include <dt-bindings/mfd/qcom-rpm.h>
+
+struct rpm_cc {
+ struct clk_onecell_data data;
+ struct clk *clks[];
+};
+struct rpm_clk {
+ const int rpm_clk_id;
+ struct qcom_rpm *rpm;
+ unsigned last_set_khz;
+ bool enabled;
+ bool branch; /* true: RPM only accepts 1 for ON and 0 for OFF */
+ unsigned factor;
+ struct clk_hw hw;
+};
+
+#define to_rpm_clk(_hw) container_of(_hw, struct rpm_clk, hw)
+
+static int rpm_clk_prepare(struct clk_hw *hw)
+{
+ struct rpm_clk *r = to_rpm_clk(hw);
+ uint32_t value;
+ int rc = 0;
+ unsigned long this_khz;
+
+ this_khz = r->last_set_khz;
+ /* Don't send requests to the RPM if the rate has not been set. */
+ if (r->last_set_khz == 0)
+ goto out;
+
+ value = this_khz;
+ if (r->branch)
+ value = !!value;
+
+ rc = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
+ r->rpm_clk_id, &value, 1);
+ if (rc)
+ goto out;
+
+out:
+ if (!rc)
+ r->enabled = true;
+ return rc;
+}
+
+static void rpm_clk_unprepare(struct clk_hw *hw)
+{
+ struct rpm_clk *r = to_rpm_clk(hw);
+
+ if (r->last_set_khz) {
+ uint32_t value = 0;
+ int rc;
+
+ rc = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
+ r->rpm_clk_id, &value, 1);
+ if (rc)
+ return;
+
+ }
+ r->enabled = false;
+}
+
+int rpm_clk_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long prate)
+{
+ struct rpm_clk *r = to_rpm_clk(hw);
+ unsigned long this_khz;
+ int rc = 0;
+
+ this_khz = DIV_ROUND_UP(rate, r->factor);
+
+ if (r->enabled) {
+ uint32_t value = this_khz;
+
+ rc = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE,
+ r->rpm_clk_id, &value, 1);
+ if (rc)
+ goto out;
+ }
+
+ if (!rc)
+ r->last_set_khz = this_khz;
+
+out:
+ return rc;
+}
+
+static long rpm_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ return rate;
+}
+
+static unsigned long rpm_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rpm_clk *r = to_rpm_clk(hw);
+ u32 val;
+ int rc;
+
+ rc = qcom_rpm_read(r->rpm, r->rpm_clk_id, &val, 1);
+ if (rc < 0)
+ return 0;
+
+ return val * r->factor;
+}
+
+static unsigned long rpm_branch_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rpm_clk *r = to_rpm_clk(hw);
+
+ return r->last_set_khz * r->factor;
+}
+
+static const struct clk_ops branch_clk_ops_rpm = {
+ .prepare = rpm_clk_prepare,
+ .unprepare = rpm_clk_unprepare,
+ .recalc_rate = rpm_branch_clk_recalc_rate,
+ .round_rate = rpm_clk_round_rate,
+};
+
+static const struct clk_ops clk_ops_rpm = {
+ .prepare = rpm_clk_prepare,
+ .unprepare = rpm_clk_unprepare,
+ .set_rate = rpm_clk_set_rate,
+ .recalc_rate = rpm_clk_recalc_rate,
+ .round_rate = rpm_clk_round_rate,
+};
+
+static struct rpm_clk pxo_clk = {
+ .rpm_clk_id = QCOM_RPM_PXO_CLK,
+ .branch = true,
+ .factor = 1000,
+ .last_set_khz = 27000,
+ .hw.init = &(struct clk_init_data){
+ .name = "pxo",
+ .ops = &branch_clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk cxo_clk = {
+ .rpm_clk_id = QCOM_RPM_CXO_CLK,
+ .branch = true,
+ .factor = 1000,
+ .last_set_khz = 19200,
+ .hw.init = &(struct clk_init_data){
+ .name = "cxo",
+ .ops = &branch_clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk afab_clk = {
+ .rpm_clk_id = QCOM_RPM_APPS_FABRIC_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "afab_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk cfpb_clk = {
+ .rpm_clk_id = QCOM_RPM_CFPB_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "cfpb_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk daytona_clk = {
+ .rpm_clk_id = QCOM_RPM_DAYTONA_FABRIC_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "daytona_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk ebi1_clk = {
+ .rpm_clk_id = QCOM_RPM_EBI1_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "ebi1_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk mmfab_clk = {
+ .rpm_clk_id = QCOM_RPM_MM_FABRIC_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "mmfab_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk mmfpb_clk = {
+ .rpm_clk_id = QCOM_RPM_MMFPB_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "mmfpb_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk sfab_clk = {
+ .rpm_clk_id = QCOM_RPM_SYS_FABRIC_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "sfab_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+
+static struct rpm_clk sfpb_clk = {
+ .rpm_clk_id = QCOM_RPM_SFPB_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "sfpb_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+
+/*
+static struct rpm_clk qdss_clk = {
+ .rpm_clk_id = QCOM_RPM_QDSS_CLK,
+ .factor = 1000,
+ .hw.init = &(struct clk_init_data){
+ .name = "qdss_clk",
+ .ops = &clk_ops_rpm,
+ },
+};
+*/
+
+struct rpm_clk *rpm_clks[] = {
+ [QCOM_RPM_PXO_CLK] = &pxo_clk,
+ [QCOM_RPM_CXO_CLK] = &cxo_clk,
+ [QCOM_RPM_APPS_FABRIC_CLK] = &afab_clk,
+ [QCOM_RPM_CFPB_CLK] = &cfpb_clk,
+ [QCOM_RPM_DAYTONA_FABRIC_CLK] = &daytona_clk,
+ [QCOM_RPM_EBI1_CLK] = &ebi1_clk,
+ [QCOM_RPM_MM_FABRIC_CLK] = &mmfab_clk,
+ [QCOM_RPM_MMFPB_CLK] = &mmfpb_clk,
+ [QCOM_RPM_SYS_FABRIC_CLK] = &sfab_clk,
+ [QCOM_RPM_SFPB_CLK] = &sfpb_clk,
+/** [QCOM_RPM_QDSS_CLK] = &qdss_clk, Needs more checking here **/
+};
+
+static int rpm_clk_probe(struct platform_device *pdev)
+{
+ struct clk **clks;
+ struct clk *clk;
+ struct rpm_cc *cc;
+ struct qcom_rpm *rpm;
+ int num_clks = ARRAY_SIZE(rpm_clks);
+ struct clk_onecell_data *data;
+ int i;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ cc = devm_kzalloc(&pdev->dev, sizeof(*cc) + sizeof(*clks) * num_clks,
+ GFP_KERNEL);
+ if (!cc)
+ return -ENOMEM;
+
+ clks = cc->clks;
+ data = &cc->data;
+ data->clks = clks;
+ data->clk_num = num_clks;
+
+ rpm = dev_get_drvdata(pdev->dev.parent);
+ if (!rpm) {
+ dev_err(&pdev->dev, "unable to retrieve handle to rpm\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < num_clks; i++) {
+ if (!rpm_clks[i]) {
+ clks[i] = ERR_PTR(-ENOENT);
+ continue;
+ }
+ rpm_clks[i]->rpm = rpm;
+ clk = devm_clk_register(&pdev->dev, &rpm_clks[i]->hw);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ clks[i] = clk;
+ }
+
+ return of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
+ data);
+}
+
+static int rpm_clk_remove(struct platform_device *pdev)
+{
+ of_clk_del_provider(pdev->dev.of_node);
+ return 0;
+}
+
+static const struct of_device_id rpm_clk_of_match[] = {
+ { .compatible = "qcom,apq8064-rpm-clk" },
+ { },
+};
+
+static struct platform_driver rpm_clk_driver = {
+ .driver = {
+ .name = "qcom-rpm-clk",
+ .of_match_table = rpm_clk_of_match,
+ },
+ .probe = rpm_clk_probe,
+ .remove = rpm_clk_remove,
+};
+
+/* dummy handoff clk handling... */
+static int rpm_clk_ho_probe(struct platform_device *pdev)
+{
+ int i, max_clks;
+ struct clk *clk;
+ max_clks = of_count_phandle_with_args(pdev->dev.of_node,
+ "clocks", "#clock-cells");
+
+ for (i = 0; i < max_clks; i++) {
+ clk = of_clk_get(pdev->dev.of_node, i);
+
+ if (IS_ERR(clk))
+ break;
+
+ clk_prepare_enable(clk);
+ }
+ return 0;
+}
+
+static const struct of_device_id rpm_clk_ho_of_match[] = {
+ { .compatible = "qcom,apq8064-rpmcc-handoff" },
+ { },
+};
+
+static struct platform_driver rpm_clk_ho_driver = {
+ .driver = {
+ .name = "qcom-rpm-clk-handoff",
+ .of_match_table = rpm_clk_ho_of_match,
+ },
+ .probe = rpm_clk_ho_probe,
+};
+
+static int __init rpm_clk_init(void)
+{
+ platform_driver_register(&rpm_clk_driver);
+ return platform_driver_register(&rpm_clk_ho_driver);
+}
+subsys_initcall(rpm_clk_init);
+
+static void __exit rpm_clk_exit(void)
+{
+ platform_driver_unregister(&rpm_clk_ho_driver);
+ platform_driver_unregister(&rpm_clk_driver);
+}
+module_exit(rpm_clk_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
+MODULE_DESCRIPTION("Driver for the RPM clocks");
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c
index e20d947db3e5..d5b65fab7035 100644
--- a/drivers/clk/qcom/common.c
+++ b/drivers/clk/qcom/common.c
@@ -21,10 +21,12 @@
#include "clk-rcg.h"
#include "clk-regmap.h"
#include "reset.h"
+#include "gdsc.h"
struct qcom_cc {
struct qcom_reset_controller reset;
struct clk_onecell_data data;
+ struct genpd_onecell_data pd_data;
struct clk *clks[];
};
@@ -66,10 +68,12 @@ int qcom_cc_really_probe(struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct clk *clk;
struct clk_onecell_data *data;
+ struct genpd_onecell_data *pd;
struct clk **clks;
struct qcom_reset_controller *reset;
struct qcom_cc *cc;
size_t num_clks = desc->num_clks;
+ size_t num_gdscs = desc->num_gdscs;
struct clk_regmap **rclks = desc->clks;
cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
@@ -79,8 +83,11 @@ int qcom_cc_really_probe(struct platform_device *pdev,
clks = cc->clks;
data = &cc->data;
+ pd = &cc->pd_data;
data->clks = clks;
data->clk_num = num_clks;
+ pd->domains = desc->gdscs;
+ pd->num_domains = num_gdscs;
for (i = 0; i < num_clks; i++) {
if (!rclks[i]) {
@@ -108,8 +115,25 @@ int qcom_cc_really_probe(struct platform_device *pdev,
ret = reset_controller_register(&reset->rcdev);
if (ret)
- of_clk_del_provider(dev->of_node);
+ goto err_reset;
+ if (num_gdscs) {
+ for (i = 0; i < num_gdscs; i++) {
+ if (!desc->gdscs[i])
+ continue;
+ gdsc_init(desc->gdscs[i], regmap);
+ }
+
+ ret = of_genpd_add_provider_onecell(dev->of_node, pd);
+ if (ret)
+ goto err_pd;
+ }
+
+ return 0;
+err_pd:
+ reset_controller_unregister(&reset->rcdev);
+err_reset:
+ of_clk_del_provider(dev->of_node);
return ret;
}
EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
@@ -128,6 +152,7 @@ EXPORT_SYMBOL_GPL(qcom_cc_probe);
void qcom_cc_remove(struct platform_device *pdev)
{
+ of_genpd_del_provider(pdev->dev.of_node);
of_clk_del_provider(pdev->dev.of_node);
reset_controller_unregister(platform_get_drvdata(pdev));
}
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index f519322acdf3..55e69c5890d7 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -26,6 +26,8 @@ struct qcom_cc_desc {
size_t num_clks;
const struct qcom_reset_map *resets;
size_t num_resets;
+ struct generic_pm_domain **gdscs;
+ size_t num_gdscs;
};
extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
diff --git a/drivers/clk/qcom/gcc-apq8084.c b/drivers/clk/qcom/gcc-apq8084.c
index e3ef90264214..ccab028fc17f 100644
--- a/drivers/clk/qcom/gcc-apq8084.c
+++ b/drivers/clk/qcom/gcc-apq8084.c
@@ -31,6 +31,7 @@
#include "clk-rcg.h"
#include "clk-branch.h"
#include "reset.h"
+#include "gdsc.h"
#define P_XO 0
#define P_GPLL0 1
@@ -3251,6 +3252,34 @@ static struct clk_branch gcc_usb_hsic_system_clk = {
},
};
+static struct gdsc usb_hs_hsic_gdsc = {
+ .gdscr = 0x404,
+ .pd = {
+ .name = "usb_hs_hsic",
+ },
+};
+
+static struct gdsc pcie0_gdsc = {
+ .gdscr = 0x1ac4,
+ .pd = {
+ .name = "pcie0",
+ },
+};
+
+static struct gdsc pcie1_gdsc = {
+ .gdscr = 0x1b44,
+ .pd = {
+ .name = "pcie1",
+ },
+};
+
+static struct gdsc usb30_gdsc = {
+ .gdscr = 0x1e84,
+ .pd = {
+ .name = "usb30",
+ },
+};
+
static struct clk_regmap *gcc_apq8084_clocks[] = {
[GPLL0] = &gpll0.clkr,
[GPLL0_VOTE] = &gpll0_vote,
@@ -3444,6 +3473,13 @@ static struct clk_regmap *gcc_apq8084_clocks[] = {
[GCC_USB_HSIC_SYSTEM_CLK] = &gcc_usb_hsic_system_clk.clkr,
};
+static struct generic_pm_domain *gcc_apq8084_gdscs[] = {
+ [USB_HS_HSIC_GDSC] = &usb_hs_hsic_gdsc.pd,
+ [PCIE0_GDSC] = &pcie0_gdsc.pd,
+ [PCIE1_GDSC] = &pcie1_gdsc.pd,
+ [USB30_GDSC] = &usb30_gdsc.pd,
+};
+
static const struct qcom_reset_map gcc_apq8084_resets[] = {
[GCC_SYSTEM_NOC_BCR] = { 0x0100 },
[GCC_CONFIG_NOC_BCR] = { 0x0140 },
@@ -3552,6 +3588,8 @@ static const struct qcom_cc_desc gcc_apq8084_desc = {
.num_clks = ARRAY_SIZE(gcc_apq8084_clocks),
.resets = gcc_apq8084_resets,
.num_resets = ARRAY_SIZE(gcc_apq8084_resets),
+ .gdscs = gcc_apq8084_gdscs,
+ .num_gdscs = ARRAY_SIZE(gcc_apq8084_gdscs),
};
static const struct of_device_id gcc_apq8084_match_table[] = {
diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c
new file mode 100644
index 000000000000..7cda3023f337
--- /dev/null
+++ b/drivers/clk/qcom/gcc-msm8916.c
@@ -0,0 +1,2917 @@
+/*
+ * Copyright 2015 Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/clock/qcom,gcc-msm8916.h>
+#include <dt-bindings/reset/qcom,gcc-msm8916.h>
+
+#include "common.h"
+#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+#include "reset.h"
+#include "gdsc.h"
+
+#define P_XO 0
+#define P_GPLL0 1
+#define P_GPLL0_AUX 1
+#define P_BIMC 2
+#define P_GPLL1 2
+#define P_GPLL1_AUX 2
+#define P_GPLL2 3
+#define P_GPLL2_AUX 3
+#define P_SLEEP_CLK 3
+#define P_DSI0_PHYPLL_BYTE 2
+#define P_DSI0_PHYPLL_DSI 2
+
+static const u8 gcc_xo_gpll0_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0] = 1,
+};
+
+static const char *gcc_xo_gpll0[] = {
+ "xo",
+ "gpll0_vote",
+};
+
+static const u8 gcc_xo_gpll0_bimc_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0] = 1,
+ [P_BIMC] = 2,
+};
+
+static const char *gcc_xo_gpll0_bimc[] = {
+ "xo",
+ "gpll0_vote",
+ "bimc_pll_vote",
+};
+
+static const u8 gcc_xo_gpll0a_gpll1_gpll2a_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0_AUX] = 3,
+ [P_GPLL1] = 1,
+ [P_GPLL2_AUX] = 2,
+};
+
+static const char *gcc_xo_gpll0a_gpll1_gpll2a[] = {
+ "xo",
+ "gpll0_vote",
+ "gpll1_vote",
+ "gpll2_vote",
+};
+
+static const u8 gcc_xo_gpll0_gpll2_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0] = 1,
+ [P_GPLL2] = 2,
+};
+
+static const char *gcc_xo_gpll0_gpll2[] = {
+ "xo",
+ "gpll0_vote",
+ "gpll2_vote",
+};
+
+static const u8 gcc_xo_gpll0a_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0_AUX] = 2,
+};
+
+static const char *gcc_xo_gpll0a[] = {
+ "xo",
+ "gpll0_vote",
+};
+
+static const u8 gcc_xo_gpll0_gpll1a_sleep_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0] = 1,
+ [P_GPLL1_AUX] = 2,
+ [P_SLEEP_CLK] = 6,
+};
+
+static const char *gcc_xo_gpll0_gpll1a_sleep[] = {
+ "xo",
+ "gpll0_vote",
+ "gpll1_vote",
+ "sleep_clk",
+};
+
+static const u8 gcc_xo_gpll0_gpll1a_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0] = 1,
+ [P_GPLL1_AUX] = 2,
+};
+
+static const char *gcc_xo_gpll0_gpll1a[] = {
+ "xo",
+ "gpll0_vote",
+ "gpll1_vote",
+};
+
+static const u8 gcc_xo_dsibyte_map[] = {
+ [P_XO] = 0,
+ [P_DSI0_PHYPLL_BYTE] = 2,
+};
+
+static const char *gcc_xo_dsibyte[] = {
+ "xo",
+ "dsi0pllbyte",
+};
+
+static const u8 gcc_xo_gpll0a_dsibyte_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0_AUX] = 2,
+ [P_DSI0_PHYPLL_BYTE] = 1,
+};
+
+static const char *gcc_xo_gpll0a_dsibyte[] = {
+ "xo",
+ "gpll0_vote",
+ "dsi0pllbyte",
+};
+
+static const u8 gcc_xo_gpll0_dsiphy_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0] = 1,
+ [P_DSI0_PHYPLL_DSI] = 2,
+};
+
+static const char *gcc_xo_gpll0_dsiphy[] = {
+ "xo",
+ "gpll0_vote",
+ "dsi0pll",
+};
+
+static const u8 gcc_xo_gpll0a_dsiphy_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0_AUX] = 2,
+ [P_DSI0_PHYPLL_DSI] = 1,
+};
+
+static const char *gcc_xo_gpll0a_dsiphy[] = {
+ "xo",
+ "gpll0_vote",
+ "dsi0pll",
+};
+
+static const u8 gcc_xo_gpll0a_gpll1_gpll2_map[] = {
+ [P_XO] = 0,
+ [P_GPLL0_AUX] = 1,
+ [P_GPLL1] = 3,
+ [P_GPLL2] = 2,
+};
+
+static const char *gcc_xo_gpll0a_gpll1_gpll2[] = {
+ "xo",
+ "gpll0_vote",
+ "gpll1_vote",
+ "gpll2_vote",
+};
+
+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+
+static struct clk_pll gpll0 = {
+ .l_reg = 0x21004,
+ .m_reg = 0x21008,
+ .n_reg = 0x2100c,
+ .config_reg = 0x21014,
+ .mode_reg = 0x21000,
+ .status_reg = 0x2101c,
+ .status_bit = 17,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gpll0",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap gpll0_vote = {
+ .enable_reg = 0x45000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll0_vote",
+ .parent_names = (const char *[]){ "gpll0" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
+static struct clk_pll gpll1 = {
+ .l_reg = 0x20004,
+ .m_reg = 0x20008,
+ .n_reg = 0x2000c,
+ .config_reg = 0x20014,
+ .mode_reg = 0x20000,
+ .status_reg = 0x2001c,
+ .status_bit = 17,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gpll1",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap gpll1_vote = {
+ .enable_reg = 0x45000,
+ .enable_mask = BIT(1),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll1_vote",
+ .parent_names = (const char *[]){ "gpll1" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
+static struct clk_pll gpll2 = {
+ .l_reg = 0x4a004,
+ .m_reg = 0x4a008,
+ .n_reg = 0x4a00c,
+ .config_reg = 0x4a014,
+ .mode_reg = 0x4a000,
+ .status_reg = 0x4a01c,
+ .status_bit = 17,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gpll2",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap gpll2_vote = {
+ .enable_reg = 0x45000,
+ .enable_mask = BIT(2),
+ .hw.init = &(struct clk_init_data){
+ .name = "gpll2_vote",
+ .parent_names = (const char *[]){ "gpll2" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
+static struct clk_pll bimc_pll = {
+ .l_reg = 0x23004,
+ .m_reg = 0x23008,
+ .n_reg = 0x2300c,
+ .config_reg = 0x23014,
+ .mode_reg = 0x23000,
+ .status_reg = 0x2301c,
+ .status_bit = 17,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "bimc_pll",
+ .parent_names = (const char *[]){ "xo" },
+ .num_parents = 1,
+ .ops = &clk_pll_ops,
+ },
+};
+
+static struct clk_regmap bimc_pll_vote = {
+ .enable_reg = 0x45000,
+ .enable_mask = BIT(3),
+ .hw.init = &(struct clk_init_data){
+ .name = "bimc_pll_vote",
+ .parent_names = (const char *[]){ "bimc_pll" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
+static struct clk_rcg2 pcnoc_bfdcd_clk_src = {
+ .cmd_rcgr = 0x27000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_bimc_map,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pcnoc_bfdcd_clk_src",
+ .parent_names = gcc_xo_gpll0_bimc,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 system_noc_bfdcd_clk_src = {
+ .cmd_rcgr = 0x26004,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_bimc_map,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "system_noc_bfdcd_clk_src",
+ .parent_names = gcc_xo_gpll0_bimc,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_ahb_clk[] = {
+ F(40000000, P_GPLL0, 10, 1, 2),
+ F(80000000, P_GPLL0, 10, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 camss_ahb_clk_src = {
+ .cmd_rcgr = 0x5a000,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_camss_ahb_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "camss_ahb_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_apss_ahb_clk[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(50000000, P_GPLL0, 16, 0, 0),
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(133330000, P_GPLL0, 6, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 apss_ahb_clk_src = {
+ .cmd_rcgr = 0x46000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_apss_ahb_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "apss_ahb_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_csi0_1_clk[] = {
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(200000000, P_GPLL0, 4, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 csi0_clk_src = {
+ .cmd_rcgr = 0x4e020,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_camss_csi0_1_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "csi0_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 csi1_clk_src = {
+ .cmd_rcgr = 0x4f020,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_camss_csi0_1_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "csi1_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_oxili_gfx3d_clk[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(50000000, P_GPLL0_AUX, 16, 0, 0),
+ F(80000000, P_GPLL0_AUX, 10, 0, 0),
+ F(100000000, P_GPLL0_AUX, 8, 0, 0),
+ F(160000000, P_GPLL0_AUX, 5, 0, 0),
+ F(177780000, P_GPLL0_AUX, 4.5, 0, 0),
+ F(200000000, P_GPLL0_AUX, 4, 0, 0),
+ F(266670000, P_GPLL0_AUX, 3, 0, 0),
+ F(294912000, P_GPLL1, 3, 0, 0),
+ F(310000000, P_GPLL2, 3, 0, 0),
+ F(400000000, P_GPLL0_AUX, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 gfx3d_clk_src = {
+ .cmd_rcgr = 0x59000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0a_gpll1_gpll2a_map,
+ .freq_tbl = ftbl_gcc_oxili_gfx3d_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gfx3d_clk_src",
+ .parent_names = gcc_xo_gpll0a_gpll1_gpll2a,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_vfe0_clk[] = {
+ F(50000000, P_GPLL0, 16, 0, 0),
+ F(80000000, P_GPLL0, 10, 0, 0),
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(160000000, P_GPLL0, 5, 0, 0),
+ F(177780000, P_GPLL0, 4.5, 0, 0),
+ F(200000000, P_GPLL0, 4, 0, 0),
+ F(266670000, P_GPLL0, 3, 0, 0),
+ F(320000000, P_GPLL0, 2.5, 0, 0),
+ F(400000000, P_GPLL0, 2, 0, 0),
+ F(465000000, P_GPLL2, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 vfe0_clk_src = {
+ .cmd_rcgr = 0x58000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll2_map,
+ .freq_tbl = ftbl_gcc_camss_vfe0_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "vfe0_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll2,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_blsp1_qup1_6_i2c_apps_clk[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ F(50000000, P_GPLL0, 16, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 blsp1_qup1_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x0200c,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_i2c_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup1_i2c_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_blsp1_qup1_6_spi_apps_clk[] = {
+ F(960000, P_XO, 10, 1, 2),
+ F(4800000, P_XO, 4, 0, 0),
+ F(9600000, P_XO, 2, 0, 0),
+ F(16000000, P_GPLL0, 10, 1, 5),
+ F(19200000, P_XO, 1, 0, 0),
+ F(25000000, P_GPLL0, 16, 1, 2),
+ F(50000000, P_GPLL0, 16, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 blsp1_qup1_spi_apps_clk_src = {
+ .cmd_rcgr = 0x02024,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup1_spi_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup2_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x03000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_i2c_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup2_i2c_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup2_spi_apps_clk_src = {
+ .cmd_rcgr = 0x03014,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup2_spi_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup3_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x04000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_i2c_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup3_i2c_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup3_spi_apps_clk_src = {
+ .cmd_rcgr = 0x04024,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup3_spi_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup4_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x05000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_i2c_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup4_i2c_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup4_spi_apps_clk_src = {
+ .cmd_rcgr = 0x05024,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup4_spi_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup5_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x06000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_i2c_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup5_i2c_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup5_spi_apps_clk_src = {
+ .cmd_rcgr = 0x06024,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup5_spi_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup6_i2c_apps_clk_src = {
+ .cmd_rcgr = 0x07000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_i2c_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup6_i2c_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_qup6_spi_apps_clk_src = {
+ .cmd_rcgr = 0x07024,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_qup1_6_spi_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_qup6_spi_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_blsp1_uart1_6_apps_clk[] = {
+ F(3686400, P_GPLL0, 1, 72, 15625),
+ F(7372800, P_GPLL0, 1, 144, 15625),
+ F(14745600, P_GPLL0, 1, 288, 15625),
+ F(16000000, P_GPLL0, 10, 1, 5),
+ F(19200000, P_XO, 1, 0, 0),
+ F(24000000, P_GPLL0, 1, 3, 100),
+ F(25000000, P_GPLL0, 16, 1, 2),
+ F(32000000, P_GPLL0, 1, 1, 25),
+ F(40000000, P_GPLL0, 1, 1, 20),
+ F(46400000, P_GPLL0, 1, 29, 500),
+ F(48000000, P_GPLL0, 1, 3, 50),
+ F(51200000, P_GPLL0, 1, 8, 125),
+ F(56000000, P_GPLL0, 1, 7, 100),
+ F(58982400, P_GPLL0, 1, 1152, 15625),
+ F(60000000, P_GPLL0, 1, 3, 40),
+ { }
+};
+
+static struct clk_rcg2 blsp1_uart1_apps_clk_src = {
+ .cmd_rcgr = 0x02044,
+ .mnd_width = 16,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_uart1_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 blsp1_uart2_apps_clk_src = {
+ .cmd_rcgr = 0x03034,
+ .mnd_width = 16,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_blsp1_uart1_6_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "blsp1_uart2_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_cci_clk[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cci_clk_src = {
+ .cmd_rcgr = 0x51000,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0a_map,
+ .freq_tbl = ftbl_gcc_camss_cci_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "cci_clk_src",
+ .parent_names = gcc_xo_gpll0a,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_gp0_1_clk[] = {
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(200000000, P_GPLL0, 4, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 camss_gp0_clk_src = {
+ .cmd_rcgr = 0x54000,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_sleep_map,
+ .freq_tbl = ftbl_gcc_camss_gp0_1_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "camss_gp0_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a_sleep,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 camss_gp1_clk_src = {
+ .cmd_rcgr = 0x55000,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_sleep_map,
+ .freq_tbl = ftbl_gcc_camss_gp0_1_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "camss_gp1_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a_sleep,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_jpeg0_clk[] = {
+ F(133330000, P_GPLL0, 6, 0, 0),
+ F(266670000, P_GPLL0, 3, 0, 0),
+ F(320000000, P_GPLL0, 2.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 jpeg0_clk_src = {
+ .cmd_rcgr = 0x57000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_camss_jpeg0_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "jpeg0_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_mclk0_1_clk[] = {
+ F(9600000, P_XO, 2, 0, 0),
+ F(23880000, P_GPLL0, 1, 2, 67),
+ F(66670000, P_GPLL0, 12, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 mclk0_clk_src = {
+ .cmd_rcgr = 0x52000,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_sleep_map,
+ .freq_tbl = ftbl_gcc_camss_mclk0_1_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "mclk0_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a_sleep,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 mclk1_clk_src = {
+ .cmd_rcgr = 0x53000,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_sleep_map,
+ .freq_tbl = ftbl_gcc_camss_mclk0_1_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "mclk1_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a_sleep,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_csi0_1phytimer_clk[] = {
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(200000000, P_GPLL0, 4, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 csi0phytimer_clk_src = {
+ .cmd_rcgr = 0x4e000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_map,
+ .freq_tbl = ftbl_gcc_camss_csi0_1phytimer_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "csi0phytimer_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 csi1phytimer_clk_src = {
+ .cmd_rcgr = 0x4f000,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_map,
+ .freq_tbl = ftbl_gcc_camss_csi0_1phytimer_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "csi1phytimer_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_camss_cpp_clk[] = {
+ F(160000000, P_GPLL0, 5, 0, 0),
+ F(320000000, P_GPLL0, 2.5, 0, 0),
+ F(465000000, P_GPLL2, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 cpp_clk_src = {
+ .cmd_rcgr = 0x58018,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll2_map,
+ .freq_tbl = ftbl_gcc_camss_cpp_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "cpp_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll2,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_crypto_clk[] = {
+ F(50000000, P_GPLL0, 16, 0, 0),
+ F(80000000, P_GPLL0, 10, 0, 0),
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(160000000, P_GPLL0, 5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 crypto_clk_src = {
+ .cmd_rcgr = 0x16004,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_crypto_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "crypto_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_gp1_3_clk[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 gp1_clk_src = {
+ .cmd_rcgr = 0x08004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_sleep_map,
+ .freq_tbl = ftbl_gcc_gp1_3_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gp1_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a_sleep,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 gp2_clk_src = {
+ .cmd_rcgr = 0x09004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_sleep_map,
+ .freq_tbl = ftbl_gcc_gp1_3_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gp2_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a_sleep,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_rcg2 gp3_clk_src = {
+ .cmd_rcgr = 0x0a004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_gpll1a_sleep_map,
+ .freq_tbl = ftbl_gcc_gp1_3_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "gp3_clk_src",
+ .parent_names = gcc_xo_gpll0_gpll1a_sleep,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct freq_tbl ftbl_gcc_mdss_byte0_clk[] = {
+ { .src = P_DSI0_PHYPLL_BYTE },
+ { }
+};
+
+static struct clk_rcg2 byte0_clk_src = {
+ .cmd_rcgr = 0x4d044,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0a_dsibyte_map,
+ .freq_tbl = ftbl_gcc_mdss_byte0_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "byte0_clk_src",
+ .parent_names = gcc_xo_gpll0a_dsibyte,
+ .num_parents = 3,
+ .ops = &clk_byte_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_mdss_esc0_clk[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 esc0_clk_src = {
+ .cmd_rcgr = 0x4d05c,
+ .hid_width = 5,
+ .parent_map = gcc_xo_dsibyte_map,
+ .freq_tbl = ftbl_gcc_mdss_esc0_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "esc0_clk_src",
+ .parent_names = gcc_xo_dsibyte,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_mdss_mdp_clk[] = {
+ F(50000000, P_GPLL0, 16, 0, 0),
+ F(80000000, P_GPLL0, 10, 0, 0),
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(160000000, P_GPLL0, 5, 0, 0),
+ F(177780000, P_GPLL0, 4.5, 0, 0),
+ F(200000000, P_GPLL0, 4, 0, 0),
+ F(266670000, P_GPLL0, 3, 0, 0),
+ F(320000000, P_GPLL0, 2.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 mdp_clk_src = {
+ .cmd_rcgr = 0x4d014,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_dsiphy_map,
+ .freq_tbl = ftbl_gcc_mdss_mdp_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "mdp_clk_src",
+ .parent_names = gcc_xo_gpll0_dsiphy,
+ .num_parents = 3,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct freq_tbl ftbl_gcc_mdss_pclk[] = {
+ { .src = P_DSI0_PHYPLL_DSI },
+ { }
+};
+
+static struct clk_rcg2 pclk0_clk_src = {
+ .cmd_rcgr = 0x4d000,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0a_dsiphy_map,
+ .freq_tbl = ftbl_gcc_mdss_pclk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pclk0_clk_src",
+ .parent_names = gcc_xo_gpll0a_dsiphy,
+ .num_parents = 3,
+ .ops = &clk_pixel_ops,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_mdss_vsync_clk[] = {
+ F(19200000, P_XO, 1, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 vsync_clk_src = {
+ .cmd_rcgr = 0x4d02c,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0a_map,
+ .freq_tbl = ftbl_gcc_mdss_vsync_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "vsync_clk_src",
+ .parent_names = gcc_xo_gpll0a,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_pdm2_clk[] = {
+ F(64000000, P_GPLL0, 12.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 pdm2_clk_src = {
+ .cmd_rcgr = 0x44010,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_pdm2_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "pdm2_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk[] = {
+ F(144000, P_XO, 16, 3, 25),
+ F(400000, P_XO, 12, 1, 4),
+ F(20000000, P_GPLL0, 10, 1, 4),
+ F(25000000, P_GPLL0, 16, 1, 2),
+ F(50000000, P_GPLL0, 16, 0, 0),
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(177770000, P_GPLL0, 4.5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 sdcc1_apps_clk_src = {
+ .cmd_rcgr = 0x42004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_sdcc1_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "sdcc1_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk[] = {
+ F(144000, P_XO, 16, 3, 25),
+ F(400000, P_XO, 12, 1, 4),
+ F(20000000, P_GPLL0, 10, 1, 4),
+ F(25000000, P_GPLL0, 16, 1, 2),
+ F(50000000, P_GPLL0, 16, 0, 0),
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(200000000, P_GPLL0, 4, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 sdcc2_apps_clk_src = {
+ .cmd_rcgr = 0x43004,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_sdcc2_apps_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "sdcc2_apps_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_apss_tcu_clk[] = {
+ F(155000000, P_GPLL2, 6, 0, 0),
+ F(310000000, P_GPLL2, 3, 0, 0),
+ F(400000000, P_GPLL0, 2, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 apss_tcu_clk_src = {
+ .cmd_rcgr = 0x1207c,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0a_gpll1_gpll2_map,
+ .freq_tbl = ftbl_gcc_apss_tcu_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "apss_tcu_clk_src",
+ .parent_names = gcc_xo_gpll0a_gpll1_gpll2,
+ .num_parents = 4,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_usb_hs_system_clk[] = {
+ F(80000000, P_GPLL0, 10, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 usb_hs_system_clk_src = {
+ .cmd_rcgr = 0x41010,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_usb_hs_system_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "usb_hs_system_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static const struct freq_tbl ftbl_gcc_venus0_vcodec0_clk[] = {
+ F(100000000, P_GPLL0, 8, 0, 0),
+ F(160000000, P_GPLL0, 5, 0, 0),
+ F(228570000, P_GPLL0, 5, 0, 0),
+ { }
+};
+
+static struct clk_rcg2 vcodec0_clk_src = {
+ .cmd_rcgr = 0x4C000,
+ .mnd_width = 8,
+ .hid_width = 5,
+ .parent_map = gcc_xo_gpll0_map,
+ .freq_tbl = ftbl_gcc_venus0_vcodec0_clk,
+ .clkr.hw.init = &(struct clk_init_data){
+ .name = "vcodec0_clk_src",
+ .parent_names = gcc_xo_gpll0,
+ .num_parents = 2,
+ .ops = &clk_rcg2_ops,
+ },
+};
+
+static struct clk_branch gcc_blsp1_ahb_clk = {
+ .halt_reg = 0x01008,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(10),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_sleep_clk = {
+ .halt_reg = 0x01004,
+ .clkr = {
+ .enable_reg = 0x01004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_sleep_clk",
+ .parent_names = (const char *[]){
+ "sleep_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = {
+ .halt_reg = 0x02008,
+ .clkr = {
+ .enable_reg = 0x02008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup1_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup1_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = {
+ .halt_reg = 0x02004,
+ .clkr = {
+ .enable_reg = 0x02004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup1_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup1_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = {
+ .halt_reg = 0x03010,
+ .clkr = {
+ .enable_reg = 0x03010,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup2_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup2_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = {
+ .halt_reg = 0x0300c,
+ .clkr = {
+ .enable_reg = 0x0300c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup2_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup2_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = {
+ .halt_reg = 0x04020,
+ .clkr = {
+ .enable_reg = 0x04020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup3_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup3_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = {
+ .halt_reg = 0x0401c,
+ .clkr = {
+ .enable_reg = 0x0401c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup3_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup3_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = {
+ .halt_reg = 0x05020,
+ .clkr = {
+ .enable_reg = 0x05020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup4_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup4_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = {
+ .halt_reg = 0x0501c,
+ .clkr = {
+ .enable_reg = 0x0501c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup4_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup4_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup5_i2c_apps_clk = {
+ .halt_reg = 0x06020,
+ .clkr = {
+ .enable_reg = 0x06020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup5_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup5_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup5_spi_apps_clk = {
+ .halt_reg = 0x0601c,
+ .clkr = {
+ .enable_reg = 0x0601c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup5_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup5_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup6_i2c_apps_clk = {
+ .halt_reg = 0x07020,
+ .clkr = {
+ .enable_reg = 0x07020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup6_i2c_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup6_i2c_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_qup6_spi_apps_clk = {
+ .halt_reg = 0x0701c,
+ .clkr = {
+ .enable_reg = 0x0701c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_qup6_spi_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_qup6_spi_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_uart1_apps_clk = {
+ .halt_reg = 0x0203c,
+ .clkr = {
+ .enable_reg = 0x0203c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_uart1_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_uart1_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_blsp1_uart2_apps_clk = {
+ .halt_reg = 0x0302c,
+ .clkr = {
+ .enable_reg = 0x0302c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_blsp1_uart2_apps_clk",
+ .parent_names = (const char *[]){
+ "blsp1_uart2_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_boot_rom_ahb_clk = {
+ .halt_reg = 0x1300c,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(7),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_boot_rom_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_cci_ahb_clk = {
+ .halt_reg = 0x5101c,
+ .clkr = {
+ .enable_reg = 0x5101c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_cci_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_cci_clk = {
+ .halt_reg = 0x51018,
+ .clkr = {
+ .enable_reg = 0x51018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_cci_clk",
+ .parent_names = (const char *[]){
+ "cci_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi0_ahb_clk = {
+ .halt_reg = 0x4e040,
+ .clkr = {
+ .enable_reg = 0x4e040,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi0_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi0_clk = {
+ .halt_reg = 0x4e03c,
+ .clkr = {
+ .enable_reg = 0x4e03c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi0_clk",
+ .parent_names = (const char *[]){
+ "csi0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi0phy_clk = {
+ .halt_reg = 0x4e048,
+ .clkr = {
+ .enable_reg = 0x4e048,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi0phy_clk",
+ .parent_names = (const char *[]){
+ "csi0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi0pix_clk = {
+ .halt_reg = 0x4e058,
+ .clkr = {
+ .enable_reg = 0x4e058,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi0pix_clk",
+ .parent_names = (const char *[]){
+ "csi0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi0rdi_clk = {
+ .halt_reg = 0x4e050,
+ .clkr = {
+ .enable_reg = 0x4e050,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi0rdi_clk",
+ .parent_names = (const char *[]){
+ "csi0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi1_ahb_clk = {
+ .halt_reg = 0x4f040,
+ .clkr = {
+ .enable_reg = 0x4f040,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi1_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi1_clk = {
+ .halt_reg = 0x4f03c,
+ .clkr = {
+ .enable_reg = 0x4f03c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi1_clk",
+ .parent_names = (const char *[]){
+ "csi1_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi1phy_clk = {
+ .halt_reg = 0x4f048,
+ .clkr = {
+ .enable_reg = 0x4f048,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi1phy_clk",
+ .parent_names = (const char *[]){
+ "csi1_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi1pix_clk = {
+ .halt_reg = 0x4f058,
+ .clkr = {
+ .enable_reg = 0x4f058,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi1pix_clk",
+ .parent_names = (const char *[]){
+ "csi1_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi1rdi_clk = {
+ .halt_reg = 0x4f050,
+ .clkr = {
+ .enable_reg = 0x4f050,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi1rdi_clk",
+ .parent_names = (const char *[]){
+ "csi1_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi_vfe0_clk = {
+ .halt_reg = 0x58050,
+ .clkr = {
+ .enable_reg = 0x58050,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi_vfe0_clk",
+ .parent_names = (const char *[]){
+ "vfe0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_gp0_clk = {
+ .halt_reg = 0x54018,
+ .clkr = {
+ .enable_reg = 0x54018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_gp0_clk",
+ .parent_names = (const char *[]){
+ "camss_gp0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_gp1_clk = {
+ .halt_reg = 0x55018,
+ .clkr = {
+ .enable_reg = 0x55018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_gp1_clk",
+ .parent_names = (const char *[]){
+ "camss_gp1_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_ispif_ahb_clk = {
+ .halt_reg = 0x50004,
+ .clkr = {
+ .enable_reg = 0x50004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_ispif_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_jpeg0_clk = {
+ .halt_reg = 0x57020,
+ .clkr = {
+ .enable_reg = 0x57020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_jpeg0_clk",
+ .parent_names = (const char *[]){
+ "jpeg0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_jpeg_ahb_clk = {
+ .halt_reg = 0x57024,
+ .clkr = {
+ .enable_reg = 0x57024,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_jpeg_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_jpeg_axi_clk = {
+ .halt_reg = 0x57028,
+ .clkr = {
+ .enable_reg = 0x57028,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_jpeg_axi_clk",
+ .parent_names = (const char *[]){
+ "system_noc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_mclk0_clk = {
+ .halt_reg = 0x52018,
+ .clkr = {
+ .enable_reg = 0x52018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_mclk0_clk",
+ .parent_names = (const char *[]){
+ "mclk0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_mclk1_clk = {
+ .halt_reg = 0x53018,
+ .clkr = {
+ .enable_reg = 0x53018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_mclk1_clk",
+ .parent_names = (const char *[]){
+ "mclk1_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_micro_ahb_clk = {
+ .halt_reg = 0x5600c,
+ .clkr = {
+ .enable_reg = 0x5600c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_micro_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi0phytimer_clk = {
+ .halt_reg = 0x4e01c,
+ .clkr = {
+ .enable_reg = 0x4e01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi0phytimer_clk",
+ .parent_names = (const char *[]){
+ "csi0phytimer_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_csi1phytimer_clk = {
+ .halt_reg = 0x4f01c,
+ .clkr = {
+ .enable_reg = 0x4f01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_csi1phytimer_clk",
+ .parent_names = (const char *[]){
+ "csi1phytimer_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_ahb_clk = {
+ .halt_reg = 0x5a014,
+ .clkr = {
+ .enable_reg = 0x5a014,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_top_ahb_clk = {
+ .halt_reg = 0x56004,
+ .clkr = {
+ .enable_reg = 0x56004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_top_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_cpp_ahb_clk = {
+ .halt_reg = 0x58040,
+ .clkr = {
+ .enable_reg = 0x58040,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_cpp_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_cpp_clk = {
+ .halt_reg = 0x5803c,
+ .clkr = {
+ .enable_reg = 0x5803c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_cpp_clk",
+ .parent_names = (const char *[]){
+ "cpp_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_vfe0_clk = {
+ .halt_reg = 0x58038,
+ .clkr = {
+ .enable_reg = 0x58038,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_vfe0_clk",
+ .parent_names = (const char *[]){
+ "vfe0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_vfe_ahb_clk = {
+ .halt_reg = 0x58044,
+ .clkr = {
+ .enable_reg = 0x58044,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_vfe_ahb_clk",
+ .parent_names = (const char *[]){
+ "camss_ahb_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_camss_vfe_axi_clk = {
+ .halt_reg = 0x58048,
+ .clkr = {
+ .enable_reg = 0x58048,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_camss_vfe_axi_clk",
+ .parent_names = (const char *[]){
+ "system_noc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_crypto_ahb_clk = {
+ .halt_reg = 0x16024,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_crypto_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_crypto_axi_clk = {
+ .halt_reg = 0x16020,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(1),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_crypto_axi_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_crypto_clk = {
+ .halt_reg = 0x1601c,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(2),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_crypto_clk",
+ .parent_names = (const char *[]){
+ "crypto_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_oxili_gmem_clk = {
+ .halt_reg = 0x59024,
+ .clkr = {
+ .enable_reg = 0x59024,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_oxili_gmem_clk",
+ .parent_names = (const char *[]){
+ "gfx3d_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gp1_clk = {
+ .halt_reg = 0x08000,
+ .clkr = {
+ .enable_reg = 0x08000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gp1_clk",
+ .parent_names = (const char *[]){
+ "gp1_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gp2_clk = {
+ .halt_reg = 0x09000,
+ .clkr = {
+ .enable_reg = 0x09000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gp2_clk",
+ .parent_names = (const char *[]){
+ "gp2_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gp3_clk = {
+ .halt_reg = 0x0a000,
+ .clkr = {
+ .enable_reg = 0x0a000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gp3_clk",
+ .parent_names = (const char *[]){
+ "gp3_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mdss_ahb_clk = {
+ .halt_reg = 0x4d07c,
+ .clkr = {
+ .enable_reg = 0x4d07c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mdss_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mdss_axi_clk = {
+ .halt_reg = 0x4d080,
+ .clkr = {
+ .enable_reg = 0x4d080,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mdss_axi_clk",
+ .parent_names = (const char *[]){
+ "system_noc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mdss_byte0_clk = {
+ .halt_reg = 0x4d094,
+ .clkr = {
+ .enable_reg = 0x4d094,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mdss_byte0_clk",
+ .parent_names = (const char *[]){
+ "byte0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mdss_esc0_clk = {
+ .halt_reg = 0x4d098,
+ .clkr = {
+ .enable_reg = 0x4d098,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mdss_esc0_clk",
+ .parent_names = (const char *[]){
+ "esc0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mdss_mdp_clk = {
+ .halt_reg = 0x4D088,
+ .clkr = {
+ .enable_reg = 0x4D088,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mdss_mdp_clk",
+ .parent_names = (const char *[]){
+ "mdp_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mdss_pclk0_clk = {
+ .halt_reg = 0x4d084,
+ .clkr = {
+ .enable_reg = 0x4d084,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mdss_pclk0_clk",
+ .parent_names = (const char *[]){
+ "pclk0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mdss_vsync_clk = {
+ .halt_reg = 0x4d090,
+ .clkr = {
+ .enable_reg = 0x4d090,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mdss_vsync_clk",
+ .parent_names = (const char *[]){
+ "vsync_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mss_cfg_ahb_clk = {
+ .halt_reg = 0x49000,
+ .clkr = {
+ .enable_reg = 0x49000,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mss_cfg_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_oxili_ahb_clk = {
+ .halt_reg = 0x59028,
+ .clkr = {
+ .enable_reg = 0x59028,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_oxili_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_oxili_gfx3d_clk = {
+ .halt_reg = 0x59020,
+ .clkr = {
+ .enable_reg = 0x59020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_oxili_gfx3d_clk",
+ .parent_names = (const char *[]){
+ "gfx3d_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_pdm2_clk = {
+ .halt_reg = 0x4400c,
+ .clkr = {
+ .enable_reg = 0x4400c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_pdm2_clk",
+ .parent_names = (const char *[]){
+ "pdm2_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_pdm_ahb_clk = {
+ .halt_reg = 0x44004,
+ .clkr = {
+ .enable_reg = 0x44004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_pdm_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_prng_ahb_clk = {
+ .halt_reg = 0x13004,
+ .halt_check = BRANCH_HALT_VOTED,
+ .clkr = {
+ .enable_reg = 0x45004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_prng_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc1_ahb_clk = {
+ .halt_reg = 0x4201c,
+ .clkr = {
+ .enable_reg = 0x4201c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc1_apps_clk = {
+ .halt_reg = 0x42018,
+ .clkr = {
+ .enable_reg = 0x42018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc1_apps_clk",
+ .parent_names = (const char *[]){
+ "sdcc1_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc2_ahb_clk = {
+ .halt_reg = 0x4301c,
+ .clkr = {
+ .enable_reg = 0x4301c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc2_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_sdcc2_apps_clk = {
+ .halt_reg = 0x43018,
+ .clkr = {
+ .enable_reg = 0x43018,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_sdcc2_apps_clk",
+ .parent_names = (const char *[]){
+ "sdcc2_apps_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_gtcu_ahb_clk = {
+ .halt_reg = 0x12044,
+ .clkr = {
+ .enable_reg = 0x4500c,
+ .enable_mask = BIT(13),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_gtcu_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_jpeg_tbu_clk = {
+ .halt_reg = 0x12034,
+ .clkr = {
+ .enable_reg = 0x4500c,
+ .enable_mask = BIT(10),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_jpeg_tbu_clk",
+ .parent_names = (const char *[]){
+ "system_noc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_mdp_tbu_clk = {
+ .halt_reg = 0x1201c,
+ .clkr = {
+ .enable_reg = 0x4500c,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_mdp_tbu_clk",
+ .parent_names = (const char *[]){
+ "system_noc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_smmu_cfg_clk = {
+ .halt_reg = 0x12038,
+ .clkr = {
+ .enable_reg = 0x4500c,
+ .enable_mask = BIT(12),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_smmu_cfg_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_venus_tbu_clk = {
+ .halt_reg = 0x12014,
+ .clkr = {
+ .enable_reg = 0x4500c,
+ .enable_mask = BIT(5),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_venus_tbu_clk",
+ .parent_names = (const char *[]){
+ "system_noc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_vfe_tbu_clk = {
+ .halt_reg = 0x1203c,
+ .clkr = {
+ .enable_reg = 0x4500c,
+ .enable_mask = BIT(9),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_vfe_tbu_clk",
+ .parent_names = (const char *[]){
+ "system_noc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb2a_phy_sleep_clk = {
+ .halt_reg = 0x4102c,
+ .clkr = {
+ .enable_reg = 0x4102c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb2a_phy_sleep_clk",
+ .parent_names = (const char *[]){
+ "sleep_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb_hs_ahb_clk = {
+ .halt_reg = 0x41008,
+ .clkr = {
+ .enable_reg = 0x41008,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb_hs_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_usb_hs_system_clk = {
+ .halt_reg = 0x41004,
+ .clkr = {
+ .enable_reg = 0x41004,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_usb_hs_system_clk",
+ .parent_names = (const char *[]){
+ "usb_hs_system_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_venus0_ahb_clk = {
+ .halt_reg = 0x4c020,
+ .clkr = {
+ .enable_reg = 0x4c020,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_venus0_ahb_clk",
+ .parent_names = (const char *[]){
+ "pcnoc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_venus0_axi_clk = {
+ .halt_reg = 0x4c024,
+ .clkr = {
+ .enable_reg = 0x4c024,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_venus0_axi_clk",
+ .parent_names = (const char *[]){
+ "system_noc_bfdcd_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct clk_branch gcc_venus0_vcodec0_clk = {
+ .halt_reg = 0x4c01c,
+ .clkr = {
+ .enable_reg = 0x4c01c,
+ .enable_mask = BIT(0),
+ .hw.init = &(struct clk_init_data){
+ .name = "gcc_venus0_vcodec0_clk",
+ .parent_names = (const char *[]){
+ "vcodec0_clk_src",
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ .ops = &clk_branch2_ops,
+ },
+ },
+};
+
+static struct gdsc venus_gdsc = {
+ .gdscr = 0x4c018,
+ .pd = {
+ .name = "venus",
+ },
+};
+
+static struct gdsc mdss_gdsc = {
+ .gdscr = 0x4d078,
+ .pd = {
+ .name = "mdss",
+ },
+};
+
+static struct gdsc jpeg_gdsc = {
+ .gdscr = 0x5701c,
+ .pd = {
+ .name = "jpeg",
+ },
+};
+
+static struct gdsc vfe_gdsc = {
+ .gdscr = 0x58034,
+ .pd = {
+ .name = "vfe",
+ },
+};
+
+static struct gdsc oxili_gdsc = {
+ .gdscr = 0x5901c,
+ .pd = {
+ .name = "oxili",
+ },
+};
+
+static struct clk_regmap *gcc_msm8916_clocks[] = {
+ [GPLL0] = &gpll0.clkr,
+ [GPLL0_VOTE] = &gpll0_vote,
+ [BIMC_PLL] = &bimc_pll.clkr,
+ [BIMC_PLL_VOTE] = &bimc_pll_vote,
+ [GPLL1] = &gpll1.clkr,
+ [GPLL1_VOTE] = &gpll1_vote,
+ [GPLL2] = &gpll2.clkr,
+ [GPLL2_VOTE] = &gpll2_vote,
+ [PCNOC_BFDCD_CLK_SRC] = &pcnoc_bfdcd_clk_src.clkr,
+ [SYSTEM_NOC_BFDCD_CLK_SRC] = &system_noc_bfdcd_clk_src.clkr,
+ [CAMSS_AHB_CLK_SRC] = &camss_ahb_clk_src.clkr,
+ [APSS_AHB_CLK_SRC] = &apss_ahb_clk_src.clkr,
+ [CSI0_CLK_SRC] = &csi0_clk_src.clkr,
+ [CSI1_CLK_SRC] = &csi1_clk_src.clkr,
+ [GFX3D_CLK_SRC] = &gfx3d_clk_src.clkr,
+ [VFE0_CLK_SRC] = &vfe0_clk_src.clkr,
+ [BLSP1_QUP1_I2C_APPS_CLK_SRC] = &blsp1_qup1_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP1_SPI_APPS_CLK_SRC] = &blsp1_qup1_spi_apps_clk_src.clkr,
+ [BLSP1_QUP2_I2C_APPS_CLK_SRC] = &blsp1_qup2_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP2_SPI_APPS_CLK_SRC] = &blsp1_qup2_spi_apps_clk_src.clkr,
+ [BLSP1_QUP3_I2C_APPS_CLK_SRC] = &blsp1_qup3_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP3_SPI_APPS_CLK_SRC] = &blsp1_qup3_spi_apps_clk_src.clkr,
+ [BLSP1_QUP4_I2C_APPS_CLK_SRC] = &blsp1_qup4_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP4_SPI_APPS_CLK_SRC] = &blsp1_qup4_spi_apps_clk_src.clkr,
+ [BLSP1_QUP5_I2C_APPS_CLK_SRC] = &blsp1_qup5_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP5_SPI_APPS_CLK_SRC] = &blsp1_qup5_spi_apps_clk_src.clkr,
+ [BLSP1_QUP6_I2C_APPS_CLK_SRC] = &blsp1_qup6_i2c_apps_clk_src.clkr,
+ [BLSP1_QUP6_SPI_APPS_CLK_SRC] = &blsp1_qup6_spi_apps_clk_src.clkr,
+ [BLSP1_UART1_APPS_CLK_SRC] = &blsp1_uart1_apps_clk_src.clkr,
+ [BLSP1_UART2_APPS_CLK_SRC] = &blsp1_uart2_apps_clk_src.clkr,
+ [CCI_CLK_SRC] = &cci_clk_src.clkr,
+ [CAMSS_GP0_CLK_SRC] = &camss_gp0_clk_src.clkr,
+ [CAMSS_GP1_CLK_SRC] = &camss_gp1_clk_src.clkr,
+ [JPEG0_CLK_SRC] = &jpeg0_clk_src.clkr,
+ [MCLK0_CLK_SRC] = &mclk0_clk_src.clkr,
+ [MCLK1_CLK_SRC] = &mclk1_clk_src.clkr,
+ [CSI0PHYTIMER_CLK_SRC] = &csi0phytimer_clk_src.clkr,
+ [CSI1PHYTIMER_CLK_SRC] = &csi1phytimer_clk_src.clkr,
+ [CPP_CLK_SRC] = &cpp_clk_src.clkr,
+ [CRYPTO_CLK_SRC] = &crypto_clk_src.clkr,
+ [GP1_CLK_SRC] = &gp1_clk_src.clkr,
+ [GP2_CLK_SRC] = &gp2_clk_src.clkr,
+ [GP3_CLK_SRC] = &gp3_clk_src.clkr,
+ [BYTE0_CLK_SRC] = &byte0_clk_src.clkr,
+ [ESC0_CLK_SRC] = &esc0_clk_src.clkr,
+ [MDP_CLK_SRC] = &mdp_clk_src.clkr,
+ [PCLK0_CLK_SRC] = &pclk0_clk_src.clkr,
+ [VSYNC_CLK_SRC] = &vsync_clk_src.clkr,
+ [PDM2_CLK_SRC] = &pdm2_clk_src.clkr,
+ [SDCC1_APPS_CLK_SRC] = &sdcc1_apps_clk_src.clkr,
+ [SDCC2_APPS_CLK_SRC] = &sdcc2_apps_clk_src.clkr,
+ [APSS_TCU_CLK_SRC] = &apss_tcu_clk_src.clkr,
+ [USB_HS_SYSTEM_CLK_SRC] = &usb_hs_system_clk_src.clkr,
+ [VCODEC0_CLK_SRC] = &vcodec0_clk_src.clkr,
+ [GCC_BLSP1_AHB_CLK] = &gcc_blsp1_ahb_clk.clkr,
+ [GCC_BLSP1_SLEEP_CLK] = &gcc_blsp1_sleep_clk.clkr,
+ [GCC_BLSP1_QUP1_I2C_APPS_CLK] = &gcc_blsp1_qup1_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP1_SPI_APPS_CLK] = &gcc_blsp1_qup1_spi_apps_clk.clkr,
+ [GCC_BLSP1_QUP2_I2C_APPS_CLK] = &gcc_blsp1_qup2_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP2_SPI_APPS_CLK] = &gcc_blsp1_qup2_spi_apps_clk.clkr,
+ [GCC_BLSP1_QUP3_I2C_APPS_CLK] = &gcc_blsp1_qup3_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP3_SPI_APPS_CLK] = &gcc_blsp1_qup3_spi_apps_clk.clkr,
+ [GCC_BLSP1_QUP4_I2C_APPS_CLK] = &gcc_blsp1_qup4_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP4_SPI_APPS_CLK] = &gcc_blsp1_qup4_spi_apps_clk.clkr,
+ [GCC_BLSP1_QUP5_I2C_APPS_CLK] = &gcc_blsp1_qup5_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP5_SPI_APPS_CLK] = &gcc_blsp1_qup5_spi_apps_clk.clkr,
+ [GCC_BLSP1_QUP6_I2C_APPS_CLK] = &gcc_blsp1_qup6_i2c_apps_clk.clkr,
+ [GCC_BLSP1_QUP6_SPI_APPS_CLK] = &gcc_blsp1_qup6_spi_apps_clk.clkr,
+ [GCC_BLSP1_UART1_APPS_CLK] = &gcc_blsp1_uart1_apps_clk.clkr,
+ [GCC_BLSP1_UART2_APPS_CLK] = &gcc_blsp1_uart2_apps_clk.clkr,
+ [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr,
+ [GCC_CAMSS_CCI_AHB_CLK] = &gcc_camss_cci_ahb_clk.clkr,
+ [GCC_CAMSS_CCI_CLK] = &gcc_camss_cci_clk.clkr,
+ [GCC_CAMSS_CSI0_AHB_CLK] = &gcc_camss_csi0_ahb_clk.clkr,
+ [GCC_CAMSS_CSI0_CLK] = &gcc_camss_csi0_clk.clkr,
+ [GCC_CAMSS_CSI0PHY_CLK] = &gcc_camss_csi0phy_clk.clkr,
+ [GCC_CAMSS_CSI0PIX_CLK] = &gcc_camss_csi0pix_clk.clkr,
+ [GCC_CAMSS_CSI0RDI_CLK] = &gcc_camss_csi0rdi_clk.clkr,
+ [GCC_CAMSS_CSI1_AHB_CLK] = &gcc_camss_csi1_ahb_clk.clkr,
+ [GCC_CAMSS_CSI1_CLK] = &gcc_camss_csi1_clk.clkr,
+ [GCC_CAMSS_CSI1PHY_CLK] = &gcc_camss_csi1phy_clk.clkr,
+ [GCC_CAMSS_CSI1PIX_CLK] = &gcc_camss_csi1pix_clk.clkr,
+ [GCC_CAMSS_CSI1RDI_CLK] = &gcc_camss_csi1rdi_clk.clkr,
+ [GCC_CAMSS_CSI_VFE0_CLK] = &gcc_camss_csi_vfe0_clk.clkr,
+ [GCC_CAMSS_GP0_CLK] = &gcc_camss_gp0_clk.clkr,
+ [GCC_CAMSS_GP1_CLK] = &gcc_camss_gp1_clk.clkr,
+ [GCC_CAMSS_ISPIF_AHB_CLK] = &gcc_camss_ispif_ahb_clk.clkr,
+ [GCC_CAMSS_JPEG0_CLK] = &gcc_camss_jpeg0_clk.clkr,
+ [GCC_CAMSS_JPEG_AHB_CLK] = &gcc_camss_jpeg_ahb_clk.clkr,
+ [GCC_CAMSS_JPEG_AXI_CLK] = &gcc_camss_jpeg_axi_clk.clkr,
+ [GCC_CAMSS_MCLK0_CLK] = &gcc_camss_mclk0_clk.clkr,
+ [GCC_CAMSS_MCLK1_CLK] = &gcc_camss_mclk1_clk.clkr,
+ [GCC_CAMSS_MICRO_AHB_CLK] = &gcc_camss_micro_ahb_clk.clkr,
+ [GCC_CAMSS_CSI0PHYTIMER_CLK] = &gcc_camss_csi0phytimer_clk.clkr,
+ [GCC_CAMSS_CSI1PHYTIMER_CLK] = &gcc_camss_csi1phytimer_clk.clkr,
+ [GCC_CAMSS_AHB_CLK] = &gcc_camss_ahb_clk.clkr,
+ [GCC_CAMSS_TOP_AHB_CLK] = &gcc_camss_top_ahb_clk.clkr,
+ [GCC_CAMSS_CPP_AHB_CLK] = &gcc_camss_cpp_ahb_clk.clkr,
+ [GCC_CAMSS_CPP_CLK] = &gcc_camss_cpp_clk.clkr,
+ [GCC_CAMSS_VFE0_CLK] = &gcc_camss_vfe0_clk.clkr,
+ [GCC_CAMSS_VFE_AHB_CLK] = &gcc_camss_vfe_ahb_clk.clkr,
+ [GCC_CAMSS_VFE_AXI_CLK] = &gcc_camss_vfe_axi_clk.clkr,
+ [GCC_CRYPTO_AHB_CLK] = &gcc_crypto_ahb_clk.clkr,
+ [GCC_CRYPTO_AXI_CLK] = &gcc_crypto_axi_clk.clkr,
+ [GCC_CRYPTO_CLK] = &gcc_crypto_clk.clkr,
+ [GCC_OXILI_GMEM_CLK] = &gcc_oxili_gmem_clk.clkr,
+ [GCC_GP1_CLK] = &gcc_gp1_clk.clkr,
+ [GCC_GP2_CLK] = &gcc_gp2_clk.clkr,
+ [GCC_GP3_CLK] = &gcc_gp3_clk.clkr,
+ [GCC_MDSS_AHB_CLK] = &gcc_mdss_ahb_clk.clkr,
+ [GCC_MDSS_AXI_CLK] = &gcc_mdss_axi_clk.clkr,
+ [GCC_MDSS_BYTE0_CLK] = &gcc_mdss_byte0_clk.clkr,
+ [GCC_MDSS_ESC0_CLK] = &gcc_mdss_esc0_clk.clkr,
+ [GCC_MDSS_MDP_CLK] = &gcc_mdss_mdp_clk.clkr,
+ [GCC_MDSS_PCLK0_CLK] = &gcc_mdss_pclk0_clk.clkr,
+ [GCC_MDSS_VSYNC_CLK] = &gcc_mdss_vsync_clk.clkr,
+ [GCC_MSS_CFG_AHB_CLK] = &gcc_mss_cfg_ahb_clk.clkr,
+ [GCC_OXILI_AHB_CLK] = &gcc_oxili_ahb_clk.clkr,
+ [GCC_OXILI_GFX3D_CLK] = &gcc_oxili_gfx3d_clk.clkr,
+ [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr,
+ [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr,
+ [GCC_PRNG_AHB_CLK] = &gcc_prng_ahb_clk.clkr,
+ [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr,
+ [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr,
+ [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr,
+ [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr,
+ [GCC_GTCU_AHB_CLK] = &gcc_gtcu_ahb_clk.clkr,
+ [GCC_JPEG_TBU_CLK] = &gcc_jpeg_tbu_clk.clkr,
+ [GCC_MDP_TBU_CLK] = &gcc_mdp_tbu_clk.clkr,
+ [GCC_SMMU_CFG_CLK] = &gcc_smmu_cfg_clk.clkr,
+ [GCC_VENUS_TBU_CLK] = &gcc_venus_tbu_clk.clkr,
+ [GCC_VFE_TBU_CLK] = &gcc_vfe_tbu_clk.clkr,
+ [GCC_USB2A_PHY_SLEEP_CLK] = &gcc_usb2a_phy_sleep_clk.clkr,
+ [GCC_USB_HS_AHB_CLK] = &gcc_usb_hs_ahb_clk.clkr,
+ [GCC_USB_HS_SYSTEM_CLK] = &gcc_usb_hs_system_clk.clkr,
+ [GCC_VENUS0_AHB_CLK] = &gcc_venus0_ahb_clk.clkr,
+ [GCC_VENUS0_AXI_CLK] = &gcc_venus0_axi_clk.clkr,
+ [GCC_VENUS0_VCODEC0_CLK] = &gcc_venus0_vcodec0_clk.clkr,
+};
+
+static struct generic_pm_domain *gcc_msm8916_gdscs[] = {
+ [VENUS_GDSC] = &venus_gdsc.pd,
+ [MDSS_GDSC] = &mdss_gdsc.pd,
+ [JPEG_GDSC] = &jpeg_gdsc.pd,
+ [VFE_GDSC] = &vfe_gdsc.pd,
+ [OXILI_GDSC] = &oxili_gdsc.pd,
+};
+
+static const struct qcom_reset_map gcc_msm8916_resets[] = {
+ [GCC_BLSP1_BCR] = { 0x01000 },
+ [GCC_BLSP1_QUP1_BCR] = { 0x02000 },
+ [GCC_BLSP1_UART1_BCR] = { 0x02038 },
+ [GCC_BLSP1_QUP2_BCR] = { 0x03008 },
+ [GCC_BLSP1_UART2_BCR] = { 0x03028 },
+ [GCC_BLSP1_QUP3_BCR] = { 0x04018 },
+ [GCC_BLSP1_QUP4_BCR] = { 0x05018 },
+ [GCC_BLSP1_QUP5_BCR] = { 0x06018 },
+ [GCC_BLSP1_QUP6_BCR] = { 0x07018 },
+ [GCC_IMEM_BCR] = { 0x0e000 },
+ [GCC_SMMU_BCR] = { 0x12000 },
+ [GCC_APSS_TCU_BCR] = { 0x12050 },
+ [GCC_SMMU_XPU_BCR] = { 0x12054 },
+ [GCC_PCNOC_TBU_BCR] = { 0x12058 },
+ [GCC_PRNG_BCR] = { 0x13000 },
+ [GCC_BOOT_ROM_BCR] = { 0x13008 },
+ [GCC_CRYPTO_BCR] = { 0x16000 },
+ [GCC_SEC_CTRL_BCR] = { 0x1a000 },
+ [GCC_AUDIO_CORE_BCR] = { 0x1c008 },
+ [GCC_ULT_AUDIO_BCR] = { 0x1c0b4 },
+ [GCC_DEHR_BCR] = { 0x1f000 },
+ [GCC_SYSTEM_NOC_BCR] = { 0x26000 },
+ [GCC_PCNOC_BCR] = { 0x27018 },
+ [GCC_TCSR_BCR] = { 0x28000 },
+ [GCC_QDSS_BCR] = { 0x29000 },
+ [GCC_DCD_BCR] = { 0x2a000 },
+ [GCC_MSG_RAM_BCR] = { 0x2b000 },
+ [GCC_MPM_BCR] = { 0x2c000 },
+ [GCC_SPMI_BCR] = { 0x2e000 },
+ [GCC_SPDM_BCR] = { 0x2f000 },
+ [GCC_MM_SPDM_BCR] = { 0x2f024 },
+ [GCC_BIMC_BCR] = { 0x31000 },
+ [GCC_RBCPR_BCR] = { 0x33000 },
+ [GCC_TLMM_BCR] = { 0x34000 },
+ [GCC_USB_HS_BCR] = { 0x41000 },
+ [GCC_USB2A_PHY_BCR] = { 0x41028 },
+ [GCC_SDCC1_BCR] = { 0x42000 },
+ [GCC_SDCC2_BCR] = { 0x43000 },
+ [GCC_PDM_BCR] = { 0x44000 },
+ [GCC_SNOC_BUS_TIMEOUT0_BCR] = { 0x47000 },
+ [GCC_PCNOC_BUS_TIMEOUT0_BCR] = { 0x48000 },
+ [GCC_PCNOC_BUS_TIMEOUT1_BCR] = { 0x48008 },
+ [GCC_PCNOC_BUS_TIMEOUT2_BCR] = { 0x48010 },
+ [GCC_PCNOC_BUS_TIMEOUT3_BCR] = { 0x48018 },
+ [GCC_PCNOC_BUS_TIMEOUT4_BCR] = { 0x48020 },
+ [GCC_PCNOC_BUS_TIMEOUT5_BCR] = { 0x48028 },
+ [GCC_PCNOC_BUS_TIMEOUT6_BCR] = { 0x48030 },
+ [GCC_PCNOC_BUS_TIMEOUT7_BCR] = { 0x48038 },
+ [GCC_PCNOC_BUS_TIMEOUT8_BCR] = { 0x48040 },
+ [GCC_PCNOC_BUS_TIMEOUT9_BCR] = { 0x48048 },
+ [GCC_MMSS_BCR] = { 0x4b000 },
+ [GCC_VENUS0_BCR] = { 0x4c014 },
+ [GCC_MDSS_BCR] = { 0x4d074 },
+ [GCC_CAMSS_PHY0_BCR] = { 0x4e018 },
+ [GCC_CAMSS_CSI0_BCR] = { 0x4e038 },
+ [GCC_CAMSS_CSI0PHY_BCR] = { 0x4e044 },
+ [GCC_CAMSS_CSI0RDI_BCR] = { 0x4e04c },
+ [GCC_CAMSS_CSI0PIX_BCR] = { 0x4e054 },
+ [GCC_CAMSS_PHY1_BCR] = { 0x4f018 },
+ [GCC_CAMSS_CSI1_BCR] = { 0x4f038 },
+ [GCC_CAMSS_CSI1PHY_BCR] = { 0x4f044 },
+ [GCC_CAMSS_CSI1RDI_BCR] = { 0x4f04c },
+ [GCC_CAMSS_CSI1PIX_BCR] = { 0x4f054 },
+ [GCC_CAMSS_ISPIF_BCR] = { 0x50000 },
+ [GCC_CAMSS_CCI_BCR] = { 0x51014 },
+ [GCC_CAMSS_MCLK0_BCR] = { 0x52014 },
+ [GCC_CAMSS_MCLK1_BCR] = { 0x53014 },
+ [GCC_CAMSS_GP0_BCR] = { 0x54014 },
+ [GCC_CAMSS_GP1_BCR] = { 0x55014 },
+ [GCC_CAMSS_TOP_BCR] = { 0x56000 },
+ [GCC_CAMSS_MICRO_BCR] = { 0x56008 },
+ [GCC_CAMSS_JPEG_BCR] = { 0x57018 },
+ [GCC_CAMSS_VFE_BCR] = { 0x58030 },
+ [GCC_CAMSS_CSI_VFE0_BCR] = { 0x5804c },
+ [GCC_OXILI_BCR] = { 0x59018 },
+ [GCC_GMEM_BCR] = { 0x5902c },
+ [GCC_CAMSS_AHB_BCR] = { 0x5a018 },
+ [GCC_MDP_TBU_BCR] = { 0x62000 },
+ [GCC_GFX_TBU_BCR] = { 0x63000 },
+ [GCC_GFX_TCU_BCR] = { 0x64000 },
+ [GCC_MSS_TBU_AXI_BCR] = { 0x65000 },
+ [GCC_MSS_TBU_GSS_AXI_BCR] = { 0x66000 },
+ [GCC_MSS_TBU_Q6_AXI_BCR] = { 0x67000 },
+ [GCC_GTCU_AHB_BCR] = { 0x68000 },
+ [GCC_SMMU_CFG_BCR] = { 0x69000 },
+ [GCC_VFE_TBU_BCR] = { 0x6a000 },
+ [GCC_VENUS_TBU_BCR] = { 0x6b000 },
+ [GCC_JPEG_TBU_BCR] = { 0x6c000 },
+ [GCC_PRONTO_TBU_BCR] = { 0x6d000 },
+ [GCC_SMMU_CATS_BCR] = { 0x7c000 },
+};
+
+static const struct regmap_config gcc_msm8916_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0x80000,
+ .fast_io = true,
+};
+
+static const struct qcom_cc_desc gcc_msm8916_desc = {
+ .config = &gcc_msm8916_regmap_config,
+ .clks = gcc_msm8916_clocks,
+ .num_clks = ARRAY_SIZE(gcc_msm8916_clocks),
+ .resets = gcc_msm8916_resets,
+ .num_resets = ARRAY_SIZE(gcc_msm8916_resets),
+ .gdscs = gcc_msm8916_gdscs,
+ .num_gdscs = ARRAY_SIZE(gcc_msm8916_gdscs),
+};
+
+static const struct of_device_id gcc_msm8916_match_table[] = {
+ { .compatible = "qcom,gcc-msm8916" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, gcc_msm8916_match_table);
+
+static int gcc_msm8916_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+
+ /* Temporary until RPM clocks supported */
+ clk = clk_register_fixed_rate(dev, "xo", NULL, CLK_IS_ROOT, 19200000);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ clk = clk_register_fixed_rate(dev, "sleep_clk_src", NULL,
+ CLK_IS_ROOT, 32768);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ regmap = qcom_cc_map(pdev, &gcc_msm8916_desc);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return qcom_cc_really_probe(pdev, &gcc_msm8916_desc, regmap);
+}
+
+static int gcc_msm8916_remove(struct platform_device *pdev)
+{
+ qcom_cc_remove(pdev);
+ return 0;
+}
+
+static struct platform_driver gcc_msm8916_driver = {
+ .probe = gcc_msm8916_probe,
+ .remove = gcc_msm8916_remove,
+ .driver = {
+ .name = "gcc-msm8916",
+ .of_match_table = gcc_msm8916_match_table,
+ },
+};
+
+static int __init gcc_msm8916_init(void)
+{
+ return platform_driver_register(&gcc_msm8916_driver);
+}
+core_initcall(gcc_msm8916_init);
+
+static void __exit gcc_msm8916_exit(void)
+{
+ platform_driver_unregister(&gcc_msm8916_driver);
+}
+module_exit(gcc_msm8916_exit);
+
+MODULE_DESCRIPTION("Qualcomm GCC MSM8916 Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:gcc-msm8916");
diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
index e60feffc10a1..505c3a2fd046 100644
--- a/drivers/clk/qcom/gcc-msm8960.c
+++ b/drivers/clk/qcom/gcc-msm8960.c
@@ -15,6 +15,7 @@
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -3501,24 +3502,13 @@ MODULE_DEVICE_TABLE(of, gcc_msm8960_match_table);
static int gcc_msm8960_probe(struct platform_device *pdev)
{
- struct clk *clk;
- struct device *dev = &pdev->dev;
const struct of_device_id *match;
match = of_match_device(gcc_msm8960_match_table, &pdev->dev);
if (!match)
return -EINVAL;
-
- /* Temporary until RPM clocks supported */
- clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 19200000);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- clk = clk_register_fixed_rate(dev, "pxo", NULL, CLK_IS_ROOT, 27000000);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return qcom_cc_probe(pdev, match->data);
+ qcom_cc_probe(pdev, match->data);
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
}
static int gcc_msm8960_remove(struct platform_device *pdev)
diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c
index a6937fe78d8a..b20b19d140e4 100644
--- a/drivers/clk/qcom/gcc-msm8974.c
+++ b/drivers/clk/qcom/gcc-msm8974.c
@@ -31,6 +31,7 @@
#include "clk-rcg.h"
#include "clk-branch.h"
#include "reset.h"
+#include "gdsc.h"
#define P_XO 0
#define P_GPLL0 1
@@ -2429,6 +2430,13 @@ static struct clk_branch gcc_usb_hsic_system_clk = {
},
};
+static struct gdsc usb_hs_hsic_gdsc = {
+ .gdscr = 0x404,
+ .pd = {
+ .name = "usb_hs_hsic",
+ },
+};
+
static struct clk_regmap *gcc_msm8974_clocks[] = {
[GPLL0] = &gpll0.clkr,
[GPLL0_VOTE] = &gpll0_vote,
@@ -2658,6 +2666,10 @@ static const struct qcom_reset_map gcc_msm8974_resets[] = {
[GCC_VENUS_RESTART] = { 0x1740 },
};
+static struct generic_pm_domain *gcc_msm8974_gdscs[] = {
+ [USB_HS_HSIC_GDSC] = &usb_hs_hsic_gdsc.pd,
+};
+
static const struct regmap_config gcc_msm8974_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -2672,6 +2684,8 @@ static const struct qcom_cc_desc gcc_msm8974_desc = {
.num_clks = ARRAY_SIZE(gcc_msm8974_clocks),
.resets = gcc_msm8974_resets,
.num_resets = ARRAY_SIZE(gcc_msm8974_resets),
+ .gdscs = gcc_msm8974_gdscs,
+ .num_gdscs = ARRAY_SIZE(gcc_msm8974_gdscs),
};
static const struct of_device_id gcc_msm8974_match_table[] = {
diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c
new file mode 100644
index 000000000000..c1dd7622c394
--- /dev/null
+++ b/drivers/clk/qcom/gdsc.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <linux/delay.h>
+#include <linux/err.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/jiffies.h>
+#include <linux/export.h>
+
+#include "gdsc.h"
+
+#define PWR_ON_MASK BIT(31)
+#define EN_REST_WAIT_MASK (0xF << 20)
+#define EN_FEW_WAIT_MASK (0xF << 16)
+#define CLK_DIS_WAIT_MASK (0xF << 12)
+#define SW_OVERRIDE_MASK BIT(2)
+#define HW_CONTROL_MASK BIT(1)
+#define SW_COLLAPSE_MASK BIT(0)
+
+/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
+#define EN_REST_WAIT_VAL (0x2 << 20)
+#define EN_FEW_WAIT_VAL (0x8 << 16)
+#define CLK_DIS_WAIT_VAL (0x2 << 12)
+
+#define TIMEOUT_US 100
+
+static int gdsc_is_enabled(struct gdsc *sc)
+{
+ u32 val;
+
+ regmap_read(sc->regmap, sc->gdscr, &val);
+ return !!(val & PWR_ON_MASK);
+}
+
+static int gdsc_toggle_logic(struct gdsc *sc, bool en)
+{
+ int ret;
+ u32 val = en ? 0 : SW_COLLAPSE_MASK;
+ u32 check = en ? PWR_ON_MASK : 0;
+ unsigned long timeout;
+
+ ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val);
+ if (ret)
+ return ret;
+
+ timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
+ do {
+ regmap_read(sc->regmap, sc->gdscr, &val);
+ if ((val & PWR_ON_MASK) == check)
+ return 0;
+ } while (time_before(jiffies, timeout));
+ regmap_read(sc->regmap, sc->gdscr, &val);
+ if ((val & PWR_ON_MASK) == check)
+ return 0;
+
+ pr_err("%s %s timed out\n", en ? "enabling" : "disabling", sc->pd.name);
+ return -ETIMEDOUT;
+}
+
+static int gdsc_enable(struct generic_pm_domain *domain)
+{
+ struct gdsc *sc = domain_to_gdsc(domain);
+ int ret;
+
+ ret = gdsc_toggle_logic(sc, true);
+ if (ret)
+ return ret;
+ /*
+ * If clocks to this power domain were already on, they will take an
+ * additional 4 clock cycles to re-enable after the power domain is
+ * enabled. Delay to account for this. A delay is also needed to ensure
+ * clocks are not enabled within 400ns of enabling power to the
+ * memories.
+ */
+ udelay(1);
+
+ return 0;
+}
+
+static int gdsc_disable(struct generic_pm_domain *domain)
+{
+ struct gdsc *sc = domain_to_gdsc(domain);
+ int ret = 0;
+
+ ret = gdsc_toggle_logic(sc, false);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+int gdsc_init(struct generic_pm_domain *domain, struct regmap *regmap)
+{
+ struct gdsc *sc = domain_to_gdsc(domain);
+ u32 mask;
+ u32 val;
+ int on;
+
+ sc->regmap = regmap;
+
+ /*
+ * Disable HW trigger: collapse/restore occur based on registers writes.
+ * Disable SW override: Use hardware state-machine for sequencing.
+ * Configure wait time between states.
+ */
+ mask = HW_CONTROL_MASK | SW_OVERRIDE_MASK |
+ EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK;
+ val = EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL;
+ regmap_update_bits(sc->regmap, sc->gdscr, mask, val);
+
+ on = gdsc_is_enabled(sc);
+
+ pm_genpd_init(&sc->pd, NULL, !on);
+ sc->pd.power_off = gdsc_disable;
+ sc->pd.power_on = gdsc_enable;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gdsc_init);
diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h
new file mode 100644
index 000000000000..82a26d98632a
--- /dev/null
+++ b/drivers/clk/qcom/gdsc.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __QCOM_GDSC_H__
+#define __QCOM_GDSC_H__
+
+#include <linux/pm_domain.h>
+
+struct regmap;
+
+/**
+ * struct gdsc - Globally Distributed Switch Controller
+ * pd: power domain
+ * @regmap: regmap for MMIO accesses
+ * @gdscr: gsdc control register
+ */
+struct gdsc {
+ struct generic_pm_domain pd;
+ struct regmap *regmap;
+ unsigned int gdscr;
+};
+
+#define domain_to_gdsc(domain) container_of(domain, struct gdsc, pd)
+
+#ifdef CONFIG_QCOM_GDSC
+int gdsc_init(struct generic_pm_domain *domain, struct regmap *regmap);
+#else
+int gdsc_init(struct generic_pm_domain *domain, struct regmap *regmap)
+{
+ return 0;
+}
+#endif /* CONFIG_QCOM_GDSC */
+#endif /* __QCOM_GDSC_H__ */
diff --git a/drivers/clk/qcom/mmcc-msm8974.c b/drivers/clk/qcom/mmcc-msm8974.c
index be94c54a9a4f..c7af21dfa3f0 100644
--- a/drivers/clk/qcom/mmcc-msm8974.c
+++ b/drivers/clk/qcom/mmcc-msm8974.c
@@ -31,6 +31,7 @@
#include "clk-rcg.h"
#include "clk-branch.h"
#include "reset.h"
+#include "gdsc.h"
#define P_XO 0
#define P_MMPLL0 1
@@ -2347,6 +2348,48 @@ static struct pll_config mmpll3_config = {
.aux_output_mask = BIT(1),
};
+static struct gdsc venus0_gdsc = {
+ .gdscr = 0x1024,
+ .pd = {
+ .name = "venus0",
+ },
+};
+
+static struct gdsc mdss_gdsc = {
+ .gdscr = 0x2304,
+ .pd = {
+ .name = "mdss",
+ },
+};
+
+static struct gdsc camss_jpeg_gdsc = {
+ .gdscr = 0x35a4,
+ .pd = {
+ .name = "camss_jpeg",
+ },
+};
+
+static struct gdsc camss_vfe_gdsc = {
+ .gdscr = 0x36a4,
+ .pd = {
+ .name = "camss_vfe",
+ },
+};
+
+static struct gdsc oxili_gdsc = {
+ .gdscr = 0x4024,
+ .pd = {
+ .name = "oxili",
+ },
+};
+
+static struct gdsc oxilicx_gdsc = {
+ .gdscr = 0x4034,
+ .pd = {
+ .name = "oxilicx",
+ },
+};
+
static struct clk_regmap *mmcc_msm8974_clocks[] = {
[MMSS_AHB_CLK_SRC] = &mmss_ahb_clk_src.clkr,
[MMSS_AXI_CLK_SRC] = &mmss_axi_clk_src.clkr,
@@ -2523,6 +2566,15 @@ static const struct qcom_reset_map mmcc_msm8974_resets[] = {
[OCMEMNOC_RESET] = { 0x50b0 },
};
+static struct generic_pm_domain *mmcc_msm8974_gdscs[] = {
+ [VENUS0_GDSC] = &venus0_gdsc.pd,
+ [MDSS_GDSC] = &mdss_gdsc.pd,
+ [CAMSS_JPEG_GDSC] = &camss_jpeg_gdsc.pd,
+ [CAMSS_VFE_GDSC] = &camss_vfe_gdsc.pd,
+ [OXILI_GDSC] = &oxili_gdsc.pd,
+ [OXILICX_GDSC] = &oxilicx_gdsc.pd,
+};
+
static const struct regmap_config mmcc_msm8974_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -2537,6 +2589,8 @@ static const struct qcom_cc_desc mmcc_msm8974_desc = {
.num_clks = ARRAY_SIZE(mmcc_msm8974_clocks),
.resets = mmcc_msm8974_resets,
.num_resets = ARRAY_SIZE(mmcc_msm8974_resets),
+ .gdscs = mmcc_msm8974_gdscs,
+ .num_gdscs = ARRAY_SIZE(mmcc_msm8974_gdscs),
};
static const struct of_device_id mmcc_msm8974_match_table[] = {
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm
index 8e07c9419153..62da88e40a45 100644
--- a/drivers/cpuidle/Kconfig.arm
+++ b/drivers/cpuidle/Kconfig.arm
@@ -64,3 +64,10 @@ config ARM_MVEBU_V7_CPUIDLE
depends on ARCH_MVEBU
help
Select this to enable cpuidle on Armada 370, 38x and XP processors.
+
+config ARM_QCOM_CPUIDLE
+ bool "CPU Idle drivers for Qualcomm processors"
+ depends on ARCH_QCOM
+ select DT_IDLE_STATES
+ help
+ Select this to enable cpuidle for QCOM processors
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 4d177b916f75..6c222d536005 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o
obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o
obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o
obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o
+obj-$(CONFIG_ARM_QCOM_CPUIDLE) += cpuidle-qcom.o
###############################################################################
# MIPS drivers
diff --git a/drivers/cpuidle/cpuidle-qcom.c b/drivers/cpuidle/cpuidle-qcom.c
new file mode 100644
index 000000000000..aa6a6c9a3590
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-qcom.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014, Linaro Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <linux/cpuidle.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <soc/qcom/pm.h>
+#include "dt_idle_states.h"
+
+static struct qcom_cpu_pm_ops *lpm_ops;
+
+static int qcom_cpu_stby(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ int ret;
+
+ ret = lpm_ops->standby(NULL);
+ if (ret)
+ return ret;
+
+ return index;
+}
+
+static int qcom_cpu_spc(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ int ret;
+
+ ret = lpm_ops->spc(NULL);
+ if (ret)
+ return ret;
+
+ return index;
+}
+
+static struct cpuidle_driver qcom_cpuidle_driver = {
+ .name = "qcom_cpuidle",
+};
+
+static const struct of_device_id qcom_idle_state_match[] = {
+ { .compatible = "qcom,idle-state-stby", .data = qcom_cpu_stby },
+ { .compatible = "qcom,idle-state-spc", .data = qcom_cpu_spc },
+ { },
+};
+
+static int qcom_cpuidle_probe(struct platform_device *pdev)
+{
+ struct cpuidle_driver *drv = &qcom_cpuidle_driver;
+ int ret;
+
+ lpm_ops = pdev->dev.platform_data;
+
+ /* Probe for other states, including standby */
+ ret = dt_init_idle_driver(drv, qcom_idle_state_match, 0);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * We will not register for cpu's cpuidle device here,
+ * they will be registered as and when their power controllers
+ * are ready.
+ */
+ return cpuidle_register_driver(drv);
+}
+
+static struct platform_driver qcom_cpuidle = {
+ .probe = qcom_cpuidle_probe,
+ .driver = {
+ .name = "qcom_cpuidle",
+ },
+};
+
+/*
+ * Register the driver early so the we have a successul registration
+ * when the device shows up.
+ * This way the cpuidle driver could be registered before the cpuidle
+ * devices are registered.
+ */
+static int __init qcom_cpuidle_driver_init(void)
+{
+ return platform_driver_register(&qcom_cpuidle);
+}
+core_initcall(qcom_cpuidle_driver_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CPUIDLE driver for QCOM SoC");
+MODULE_ALIAS("platform:qcom-cpuidle");
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 1ae4e547b419..ba772c400a0e 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -59,6 +59,17 @@ config FSL_PAMU
PAMU can authorize memory access, remap the memory address, and remap I/O
transaction types.
+config QCOM_IOMMU_V0
+ bool "Qualcomm IOMMU v0 Support"
+ depends on ARCH_QCOM
+ select IOMMU_API
+ help
+ Support for the IOMMUs found on certain Qualcomm SOCs.
+ These IOMMUs allow virtualization of the address space used by most
+ cores within the multimedia subsystem.
+
+ If unsure, say N here.
+
# MSM IOMMU support
config MSM_IOMMU
bool "MSM IOMMU Support"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 080ffab4ed1c..a8a4d80a8af1 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
+obj-$(CONFIG_QCOM_IOMMU_V0) += qcom_iommu_v0.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index e1b05379ca0e..19c5e0141aaa 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -422,12 +422,12 @@ static int msm_iommu_map(struct iommu_domain *domain, unsigned long va,
int i = 0;
for (i = 0; i < 16; i++)
*(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION |
- FL_AP_READ | FL_AP_WRITE | FL_TYPE_SECT |
+ FL_AP1 | FL_AP0 | FL_TYPE_SECT |
FL_SHARED | FL_NG | pgprot;
}
if (len == SZ_1M)
- *fl_pte = (pa & 0xFFF00000) | FL_AP_READ | FL_AP_WRITE | FL_NG |
+ *fl_pte = (pa & 0xFFF00000) | FL_AP1 | FL_AP0 | FL_NG |
FL_TYPE_SECT | FL_SHARED | pgprot;
/* Need a 2nd level table */
diff --git a/drivers/iommu/msm_iommu_hw-8xxx.h b/drivers/iommu/msm_iommu_hw-8xxx.h
index fc160101dead..84ba573927d1 100644
--- a/drivers/iommu/msm_iommu_hw-8xxx.h
+++ b/drivers/iommu/msm_iommu_hw-8xxx.h
@@ -61,8 +61,9 @@ do { \
#define FL_TYPE_TABLE (1 << 0)
#define FL_TYPE_SECT (2 << 0)
#define FL_SUPERSECTION (1 << 18)
-#define FL_AP_WRITE (1 << 10)
-#define FL_AP_READ (1 << 11)
+#define FL_AP0 (1 << 10)
+#define FL_AP1 (1 << 11)
+#define FL_AP2 (1 << 15)
#define FL_SHARED (1 << 16)
#define FL_BUFFERABLE (1 << 2)
#define FL_CACHEABLE (1 << 3)
@@ -77,6 +78,7 @@ do { \
#define SL_TYPE_SMALL (2 << 0)
#define SL_AP0 (1 << 4)
#define SL_AP1 (2 << 4)
+#define SL_AP2 (1 << 9)
#define SL_SHARED (1 << 10)
#define SL_BUFFERABLE (1 << 2)
#define SL_CACHEABLE (1 << 3)
diff --git a/drivers/iommu/qcom_iommu_v0.c b/drivers/iommu/qcom_iommu_v0.c
new file mode 100644
index 000000000000..190f41e7f7c6
--- /dev/null
+++ b/drivers/iommu/qcom_iommu_v0.c
@@ -0,0 +1,1223 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2014 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+/* NOTE: originally based on msm_iommu non-DT driver for same hw
+ * but as the structure of the driver changes considerably for DT
+ * it seemed easier to not try to support old platforms with the
+ * same driver.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/clk.h>
+
+#include <asm/cacheflush.h>
+#include <asm/sizes.h>
+
+#include "msm_iommu_hw-8xxx.h"
+#include "qcom_iommu_v0.h"
+
+#define MRC(reg, processor, op1, crn, crm, op2) \
+__asm__ __volatile__ ( \
+" mrc " #processor "," #op1 ", %0," #crn "," #crm "," #op2 "\n" \
+: "=r" (reg))
+
+#define RCP15_PRRR(reg) MRC(reg, p15, 0, c10, c2, 0)
+#define RCP15_NMRR(reg) MRC(reg, p15, 0, c10, c2, 1)
+
+/* bitmap of the page sizes currently supported */
+#define QCOM_IOMMU_PGSIZES (SZ_4K | SZ_64K | SZ_1M | SZ_16M)
+
+static int qcom_iommu_tex_class[4];
+
+// TODO any good reason for global lock vs per-iommu lock?
+static DEFINE_MUTEX(qcom_iommu_lock);
+static LIST_HEAD(qcom_iommu_devices);
+
+/* Note that a single iommu_domain can, for devices sitting behind
+ * more than one IOMMU (ie. one per AXI interface) will have more
+ * than one iommu in the iommu_list. But all are programmed to
+ * point at the same pagetables so from client device perspective
+ * they act as a single IOMMU.
+ */
+struct qcom_domain_priv {
+ unsigned long *pgtable;
+ struct iommu_domain *domain;
+ struct list_head iommu_list; /* list of attached 'struct qcom_iommu' */
+};
+
+static int __enable_clocks(struct qcom_iommu *iommu)
+{
+ int ret;
+
+ ret = clk_prepare_enable(iommu->pclk);
+ if (ret)
+ goto fail;
+
+ if (iommu->clk) {
+ ret = clk_prepare_enable(iommu->clk);
+ if (ret)
+ clk_disable_unprepare(iommu->pclk);
+ }
+fail:
+ return ret;
+}
+
+static void __disable_clocks(struct qcom_iommu *iommu)
+{
+ if (iommu->clk)
+ clk_disable_unprepare(iommu->clk);
+ clk_disable_unprepare(iommu->pclk);
+}
+
+static void __flush_range(struct qcom_iommu *iommu,
+ unsigned long *start, unsigned long *end)
+{
+ dmac_flush_range(start, end);
+}
+
+static int __flush_iotlb_va(struct qcom_domain_priv *priv, unsigned int va)
+{
+ struct qcom_iommu *iommu;
+ int ret = 0;
+
+ list_for_each_entry(iommu, &priv->iommu_list, dom_node) {
+ struct qcom_iommu_ctx *iommu_ctx;
+ list_for_each_entry(iommu_ctx, &iommu->ctx_list, node) {
+ int ctx = iommu_ctx->num;
+ uint32_t asid;
+
+ ret = __enable_clocks(iommu);
+ if (ret)
+ goto fail;
+
+ asid = GET_CONTEXTIDR_ASID(iommu->base, ctx);
+
+ SET_TLBIVA(iommu->base, ctx, asid | (va & TLBIVA_VA));
+ mb();
+
+ __disable_clocks(iommu);
+ }
+ }
+
+fail:
+ return ret;
+}
+
+static int __flush_iotlb(struct qcom_domain_priv *priv)
+{
+ struct qcom_iommu *iommu;
+ int ret = 0;
+
+#ifndef CONFIG_IOMMU_PGTABLES_L2
+ unsigned long *fl_table = priv->pgtable;
+ int i;
+
+ list_for_each_entry(iommu, &priv->iommu_list, dom_node) {
+
+ __flush_range(iommu, fl_table, fl_table + SZ_16K);
+
+ for (i = 0; i < NUM_FL_PTE; i++) {
+ if ((fl_table[i] & 0x03) == FL_TYPE_TABLE) {
+ void *sl_table = __va(fl_table[i] &
+ FL_BASE_MASK);
+ __flush_range(iommu, sl_table, sl_table + SZ_4K);
+ }
+ }
+
+ /*
+ * Only need to flush once, all iommu's attached
+ * to the domain use common set of pagetables:
+ */
+ break;
+ }
+#endif
+
+ list_for_each_entry(iommu, &priv->iommu_list, dom_node) {
+ struct qcom_iommu_ctx *iommu_ctx;
+
+ ret = __enable_clocks(iommu);
+ if (ret)
+ goto fail;
+
+ list_for_each_entry(iommu_ctx, &iommu->ctx_list, node) {
+ int ctx = iommu_ctx->num;
+ uint32_t asid;
+
+ asid = GET_CONTEXTIDR_ASID(iommu->base, ctx);
+
+ SET_TLBIASID(iommu->base, ctx, asid);
+ mb();
+ }
+ __disable_clocks(iommu);
+ }
+
+fail:
+ return ret;
+}
+
+static void __reset_context(struct qcom_iommu *iommu, int ctx)
+{
+ void __iomem *base = iommu->base;
+
+ SET_BPRCOSH(base, ctx, 0);
+ SET_BPRCISH(base, ctx, 0);
+ SET_BPRCNSH(base, ctx, 0);
+ SET_BPSHCFG(base, ctx, 0);
+ SET_BPMTCFG(base, ctx, 0);
+ SET_ACTLR(base, ctx, 0);
+ SET_SCTLR(base, ctx, 0);
+ SET_FSRRESTORE(base, ctx, 0);
+ SET_TTBR0(base, ctx, 0);
+ SET_TTBR1(base, ctx, 0);
+ SET_TTBCR(base, ctx, 0);
+ SET_BFBCR(base, ctx, 0);
+ SET_PAR(base, ctx, 0);
+ SET_FAR(base, ctx, 0);
+ SET_TLBFLPTER(base, ctx, 0);
+ SET_TLBSLPTER(base, ctx, 0);
+ SET_TLBLKCR(base, ctx, 0);
+ SET_PRRR(base, ctx, 0);
+ SET_NMRR(base, ctx, 0);
+ SET_RESUME(base, ctx, 1);
+ mb();
+}
+
+static void __reset_iommu(struct qcom_iommu *iommu)
+{
+ void __iomem *base = iommu->base;
+ int ctx;
+
+ SET_RPUE(base, 0);
+ SET_RPUEIE(base, 0);
+ SET_ESRRESTORE(base, 0);
+ SET_TBE(base, 0);
+ SET_CR(base, 0);
+ SET_SPDMBE(base, 0);
+ SET_TESTBUSCR(base, 0);
+ SET_TLBRSW(base, 0);
+ SET_GLOBAL_TLBIALL(base, 0);
+ SET_RPU_ACR(base, 0);
+ SET_TLBLKCRWE(base, 1);
+
+ for (ctx = 0; ctx < iommu->ncb; ctx++) {
+ SET_BPRCOSH(base, ctx, 0);
+ SET_BPRCISH(base, ctx, 0);
+ SET_BPRCNSH(base, ctx, 0);
+ SET_BPSHCFG(base, ctx, 0);
+ SET_BPMTCFG(base, ctx, 0);
+ SET_ACTLR(base, ctx, 0);
+ SET_SCTLR(base, ctx, 0);
+ SET_FSRRESTORE(base, ctx, 0);
+ SET_TTBR0(base, ctx, 0);
+ SET_TTBR1(base, ctx, 0);
+ SET_TTBCR(base, ctx, 0);
+ SET_BFBCR(base, ctx, 0);
+ SET_PAR(base, ctx, 0);
+ SET_FAR(base, ctx, 0);
+ SET_TLBFLPTER(base, ctx, 0);
+ SET_TLBSLPTER(base, ctx, 0);
+ SET_TLBLKCR(base, ctx, 0);
+ SET_CTX_TLBIALL(base, ctx, 0);
+ SET_TLBIVA(base, ctx, 0);
+ SET_PRRR(base, ctx, 0);
+ SET_NMRR(base, ctx, 0);
+ SET_CONTEXTIDR(base, ctx, 0);
+ }
+ mb();
+}
+
+
+static void __program_context(struct qcom_domain_priv *priv,
+ struct qcom_iommu *iommu, int ctx)
+{
+ void __iomem *base = iommu->base;
+ phys_addr_t pgtable = __pa(priv->pgtable);
+ unsigned int prrr, nmrr;
+ bool found;
+ int i, j;
+
+ __reset_context(iommu, ctx);
+
+ /* Set up HTW mode */
+ /* TLB miss configuration: perform HTW on miss */
+ SET_TLBMCFG(base, ctx, 0x3);
+
+ /* V2P configuration: HTW for access */
+ SET_V2PCFG(base, ctx, 0x3);
+
+ SET_TTBCR(base, ctx, iommu->ttbr_split);
+ SET_TTBR0_PA(base, ctx, (pgtable >> TTBR0_PA_SHIFT));
+ if (iommu->ttbr_split)
+ SET_TTBR1_PA(base, ctx, (pgtable >> TTBR1_PA_SHIFT));
+
+ /* Enable context fault interrupt */
+ SET_CFEIE(base, ctx, 1);
+
+ /* Stall access on a context fault and let the handler deal with it */
+ SET_CFCFG(base, ctx, 1);
+
+ /* Redirect all cacheable requests to L2 slave port. */
+ SET_RCISH(base, ctx, 1);
+ SET_RCOSH(base, ctx, 1);
+ SET_RCNSH(base, ctx, 1);
+
+ /* Turn on TEX Remap */
+ SET_TRE(base, ctx, 1);
+
+ /* Set TEX remap attributes */
+ RCP15_PRRR(prrr);
+ RCP15_NMRR(nmrr);
+ SET_PRRR(base, ctx, prrr);
+ SET_NMRR(base, ctx, nmrr);
+
+ /* Turn on BFB prefetch */
+ SET_BFBDFE(base, ctx, 1);
+
+#ifdef CONFIG_IOMMU_PGTABLES_L2
+ /* Configure page tables as inner-cacheable and shareable to reduce
+ * the TLB miss penalty.
+ */
+ SET_TTBR0_SH(base, ctx, 1);
+ SET_TTBR1_SH(base, ctx, 1);
+
+ SET_TTBR0_NOS(base, ctx, 1);
+ SET_TTBR1_NOS(base, ctx, 1);
+
+ SET_TTBR0_IRGNH(base, ctx, 0); /* WB, WA */
+ SET_TTBR0_IRGNL(base, ctx, 1);
+
+ SET_TTBR1_IRGNH(base, ctx, 0); /* WB, WA */
+ SET_TTBR1_IRGNL(base, ctx, 1);
+
+ SET_TTBR0_ORGN(base, ctx, 1); /* WB, WA */
+ SET_TTBR1_ORGN(base, ctx, 1); /* WB, WA */
+#endif
+
+ /* Find if this page table is used elsewhere, and re-use ASID */
+ found = false;
+ for (i = 0; i < iommu->ncb; i++) {
+ if (i == ctx)
+ continue;
+
+ if (GET_TTBR0_PA(base, i) == (pgtable >> TTBR0_PA_SHIFT)) {
+ SET_CONTEXTIDR_ASID(base, ctx, GET_CONTEXTIDR_ASID(base, i));
+ found = true;
+ break;
+ }
+ }
+
+ /* If page table is new, find an unused ASID */
+ if (!found) {
+ for (i = 0; i < iommu->ncb; i++) {
+ found = false;
+ for (j = 0; j < iommu->ncb; j++) {
+ if (j != ctx)
+ continue;
+ if (GET_CONTEXTIDR_ASID(base, j) == i)
+ found = true;
+ }
+
+ if (!found) {
+ SET_CONTEXTIDR_ASID(base, ctx, i);
+ break;
+ }
+ }
+ BUG_ON(found);
+ }
+
+ /* Enable the MMU */
+ SET_M(base, ctx, 1);
+ mb();
+}
+
+static int qcom_iommu_domain_init(struct iommu_domain *domain)
+{
+ struct qcom_domain_priv *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+
+ if (!priv)
+ goto fail_nomem;
+
+ INIT_LIST_HEAD(&priv->iommu_list);
+ priv->pgtable = (unsigned long *)__get_free_pages(GFP_KERNEL,
+ get_order(SZ_16K));
+
+ if (!priv->pgtable)
+ goto fail_nomem;
+
+ memset(priv->pgtable, 0, SZ_16K);
+ domain->priv = priv;
+ priv->domain = domain;
+
+//XXX I think not needed?
+ dmac_flush_range(priv->pgtable, priv->pgtable + NUM_FL_PTE);
+
+ domain->geometry.aperture_start = 0;
+ domain->geometry.aperture_end = (1ULL << 32) - 1;
+ domain->geometry.force_aperture = true;
+
+ return 0;
+
+fail_nomem:
+ kfree(priv);
+ return -ENOMEM;
+}
+
+static void qcom_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct qcom_domain_priv *priv;
+ unsigned long *fl_table;
+ int i;
+
+ mutex_lock(&qcom_iommu_lock);
+ priv = domain->priv;
+ domain->priv = NULL;
+
+ if (priv) {
+ fl_table = priv->pgtable;
+
+ for (i = 0; i < NUM_FL_PTE; i++)
+ if ((fl_table[i] & 0x03) == FL_TYPE_TABLE)
+ free_page((unsigned long) __va(((fl_table[i]) &
+ FL_BASE_MASK)));
+
+ free_pages((unsigned long)priv->pgtable, get_order(SZ_16K));
+ priv->pgtable = NULL;
+ }
+
+ kfree(priv);
+ mutex_unlock(&qcom_iommu_lock);
+}
+
+static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+ struct qcom_domain_priv *priv = domain->priv;
+ struct qcom_iommu *iommu;
+ struct qcom_iommu_ctx *iommu_ctx = NULL;
+ int ret = 0;
+ bool found = false;
+
+ if (!priv)
+ return -EINVAL;
+
+ mutex_lock(&qcom_iommu_lock);
+ list_for_each_entry(iommu, &qcom_iommu_devices, dev_node) {
+ iommu_ctx = list_first_entry(&iommu->ctx_list,
+ struct qcom_iommu_ctx, node);
+
+ if (iommu_ctx->of_node == dev->of_node) {
+ found = true;
+
+ ret = __enable_clocks(iommu);
+ if (ret)
+ goto fail;
+
+ /* we found a matching device, attach all it's contexts: */
+ list_for_each_entry(iommu_ctx, &iommu->ctx_list, node)
+ __program_context(priv, iommu, iommu_ctx->num);
+
+ __disable_clocks(iommu);
+
+ // TODO check for double attaches, etc..
+
+ list_add_tail(&iommu->dom_node, &priv->iommu_list);
+ iommu->domain = domain;
+ }
+ }
+
+ if (!found) {
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ // TODO might want to get_device(iommu->dev) unless iommu framework
+ // does this somewhere for us?
+
+ ret = __flush_iotlb(priv);
+
+fail:
+ if (ret) {
+ // TODO make sure we completely detach..
+ }
+ mutex_unlock(&qcom_iommu_lock);
+ return ret;
+}
+
+static void qcom_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct qcom_domain_priv *priv = domain->priv;
+ struct qcom_iommu *iommu;
+ struct qcom_iommu_ctx *iommu_ctx;
+ int ret;
+
+ if (!priv)
+ return;
+
+ mutex_lock(&qcom_iommu_lock);
+
+ ret = __flush_iotlb(priv);
+ if (ret)
+ goto fail;
+
+ while (!list_empty(&priv->iommu_list)) {
+ iommu = list_first_entry(&priv->iommu_list,
+ struct qcom_iommu, dom_node);
+
+ ret = __enable_clocks(iommu);
+ if (ret)
+ goto fail;
+
+ /* reset all contexts: */
+ list_for_each_entry(iommu_ctx, &iommu->ctx_list, node) {
+ int ctx = iommu_ctx->num;
+ uint32_t asid = GET_CONTEXTIDR_ASID(iommu->base, ctx);
+ SET_TLBIASID(iommu->base, ctx, asid);
+ __reset_context(iommu, ctx);
+ }
+
+ __disable_clocks(iommu);
+
+ list_del(&iommu->dom_node);
+ }
+
+ // TODO might want to put_device(iommu->dev) unless iommu framework
+ // does this somewhere for us?
+
+fail:
+ mutex_unlock(&qcom_iommu_lock);
+}
+
+static int __get_pgprot(int prot, int len)
+{
+ unsigned int pgprot;
+ int tex;
+
+ if (!(prot & (IOMMU_READ | IOMMU_WRITE))) {
+ prot |= IOMMU_READ | IOMMU_WRITE;
+ WARN_ONCE(1, "No attributes in iommu mapping; assuming RW\n");
+ }
+
+ if ((prot & IOMMU_WRITE) && !(prot & IOMMU_READ)) {
+ prot |= IOMMU_READ;
+ WARN_ONCE(1, "Write-only iommu mappings unsupported; falling back to RW\n");
+ }
+
+ if (prot & IOMMU_CACHE)
+ tex = (pgprot_kernel >> 2) & 0x07;
+ else
+ tex = qcom_iommu_tex_class[QCOM_IOMMU_ATTR_NONCACHED];
+
+ if (tex < 0 || tex > NUM_TEX_CLASS - 1)
+ return 0;
+
+ if (len == SZ_16M || len == SZ_1M) {
+ pgprot = FL_SHARED;
+ pgprot |= tex & 0x01 ? FL_BUFFERABLE : 0;
+ pgprot |= tex & 0x02 ? FL_CACHEABLE : 0;
+ pgprot |= tex & 0x04 ? FL_TEX0 : 0;
+ pgprot |= FL_AP0 | FL_AP1;
+ pgprot |= prot & IOMMU_WRITE ? 0 : FL_AP2;
+ } else {
+ pgprot = SL_SHARED;
+ pgprot |= tex & 0x01 ? SL_BUFFERABLE : 0;
+ pgprot |= tex & 0x02 ? SL_CACHEABLE : 0;
+ pgprot |= tex & 0x04 ? SL_TEX0 : 0;
+ pgprot |= SL_AP0 | SL_AP1;
+ pgprot |= prot & IOMMU_WRITE ? 0 : SL_AP2;
+ }
+
+ return pgprot;
+}
+
+static int qcom_iommu_map(struct iommu_domain *domain, unsigned long va,
+ phys_addr_t pa, size_t len, int prot)
+{
+ struct qcom_domain_priv *priv = domain->priv;
+ struct qcom_iommu *iommu;
+ unsigned long *fl_table, *fl_pte, fl_offset;
+ unsigned long *sl_table, *sl_pte, sl_offset;
+ unsigned int pgprot;
+ int ret = 0;
+
+ mutex_lock(&qcom_iommu_lock);
+
+ if (!priv || list_empty(&priv->iommu_list))
+ goto fail;
+
+ /* all IOMMU's in the domain have same pgtables: */
+ iommu = list_first_entry(&priv->iommu_list,
+ struct qcom_iommu, dom_node);
+
+ fl_table = priv->pgtable;
+
+ if (len != SZ_16M && len != SZ_1M &&
+ len != SZ_64K && len != SZ_4K) {
+ dev_err(iommu->dev, "Bad size: %d\n", len);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (!fl_table) {
+ dev_err(iommu->dev, "Null page table\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ pgprot = __get_pgprot(prot, len);
+
+ if (!pgprot) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ fl_offset = FL_OFFSET(va); /* Upper 12 bits */
+ fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
+
+ if (len == SZ_16M) {
+ int i = 0;
+
+ for (i = 0; i < 16; i++)
+ if (*(fl_pte+i)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ for (i = 0; i < 16; i++)
+ *(fl_pte+i) = (pa & 0xFF000000) | FL_SUPERSECTION
+ | FL_TYPE_SECT | FL_SHARED | FL_NG | pgprot;
+ __flush_range(iommu, fl_pte, fl_pte + 16);
+ }
+
+ if (len == SZ_1M) {
+ if (*fl_pte) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ *fl_pte = (pa & 0xFFF00000) | FL_NG | FL_TYPE_SECT | FL_SHARED
+ | pgprot;
+ __flush_range(iommu, fl_pte, fl_pte + 1);
+ }
+
+ /* Need a 2nd level table */
+ if (len == SZ_4K || len == SZ_64K) {
+
+ if (*fl_pte == 0) {
+ unsigned long *sl;
+ sl = (unsigned long *) __get_free_pages(GFP_ATOMIC,
+ get_order(SZ_4K));
+
+ if (!sl) {
+ dev_err(iommu->dev, "Could not allocate second level table\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ memset(sl, 0, SZ_4K);
+ __flush_range(iommu, sl, sl + NUM_SL_PTE);
+
+ *fl_pte = ((((int)__pa(sl)) & FL_BASE_MASK) | \
+ FL_TYPE_TABLE);
+
+ __flush_range(iommu, fl_pte, fl_pte + 1);
+ }
+
+ if (!(*fl_pte & FL_TYPE_TABLE)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+ }
+
+ sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+ sl_offset = SL_OFFSET(va);
+ sl_pte = sl_table + sl_offset;
+
+ if (len == SZ_4K) {
+ if (*sl_pte) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ *sl_pte = (pa & SL_BASE_MASK_SMALL) | SL_NG | SL_SHARED
+ | SL_TYPE_SMALL | pgprot;
+ __flush_range(iommu, sl_pte, sl_pte + 1);
+ }
+
+ if (len == SZ_64K) {
+ int i;
+
+ for (i = 0; i < 16; i++)
+ if (*(sl_pte+i)) {
+ ret = -EBUSY;
+ goto fail;
+ }
+
+ for (i = 0; i < 16; i++)
+ *(sl_pte+i) = (pa & SL_BASE_MASK_LARGE) | SL_NG
+ | SL_SHARED | SL_TYPE_LARGE | pgprot;
+
+ __flush_range(iommu, sl_pte, sl_pte + 16);
+ }
+
+ ret = __flush_iotlb_va(priv, va);
+
+fail:
+ mutex_unlock(&qcom_iommu_lock);
+ return ret;
+}
+
+static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long va,
+ size_t len)
+{
+ struct qcom_domain_priv *priv = domain->priv;
+ struct qcom_iommu *iommu;
+ unsigned long *fl_table, *fl_pte, fl_offset;
+ unsigned long *sl_table, *sl_pte, sl_offset;
+ int i, ret = 0;
+
+ mutex_lock(&qcom_iommu_lock);
+
+ if (!priv || list_empty(&priv->iommu_list))
+ goto fail;
+
+ /* all IOMMU's in the domain have same pgtables: */
+ iommu = list_first_entry(&priv->iommu_list,
+ struct qcom_iommu, dom_node);
+
+ fl_table = priv->pgtable;
+
+ if (len != SZ_16M && len != SZ_1M &&
+ len != SZ_64K && len != SZ_4K) {
+ dev_err(iommu->dev, "Bad length: %d\n", len);
+ goto fail;
+ }
+
+ if (!fl_table) {
+ dev_err(iommu->dev, "Null page table\n");
+ goto fail;
+ }
+
+ fl_offset = FL_OFFSET(va); /* Upper 12 bits */
+ fl_pte = fl_table + fl_offset; /* int pointers, 4 bytes */
+
+ if (*fl_pte == 0) {
+ dev_err(iommu->dev, "First level PTE is 0\n");
+ goto fail;
+ }
+
+ /* Unmap supersection */
+ if (len == SZ_16M) {
+ for (i = 0; i < 16; i++)
+ *(fl_pte+i) = 0;
+
+ __flush_range(iommu, fl_pte, fl_pte + 16);
+ }
+
+ if (len == SZ_1M) {
+ *fl_pte = 0;
+
+ __flush_range(iommu, fl_pte, fl_pte + 1);
+ }
+
+ sl_table = (unsigned long *) __va(((*fl_pte) & FL_BASE_MASK));
+ sl_offset = SL_OFFSET(va);
+ sl_pte = sl_table + sl_offset;
+
+ if (len == SZ_64K) {
+ for (i = 0; i < 16; i++)
+ *(sl_pte+i) = 0;
+
+ __flush_range(iommu, sl_pte, sl_pte + 16);
+ }
+
+ if (len == SZ_4K) {
+ *sl_pte = 0;
+
+ __flush_range(iommu, sl_pte, sl_pte + 1);
+ }
+
+ if (len == SZ_4K || len == SZ_64K) {
+ int used = 0;
+
+ for (i = 0; i < NUM_SL_PTE; i++)
+ if (sl_table[i])
+ used = 1;
+ if (!used) {
+ free_page((unsigned long)sl_table);
+ *fl_pte = 0;
+
+ __flush_range(iommu, fl_pte, fl_pte + 1);
+ }
+ }
+
+ ret = __flush_iotlb_va(priv, va);
+
+fail:
+ mutex_unlock(&qcom_iommu_lock);
+
+ /* the IOMMU API requires us to return how many bytes were unmapped */
+ len = ret ? 0 : len;
+ return len;
+}
+
+static phys_addr_t qcom_iommu_iova_to_phys(struct iommu_domain *domain,
+ dma_addr_t va)
+{
+ struct qcom_domain_priv *priv = domain->priv;
+ struct qcom_iommu *iommu;
+ struct qcom_iommu_ctx *iommu_ctx;
+ unsigned int par;
+ void __iomem *base;
+ phys_addr_t ret = 0;
+ int ctx;
+
+ mutex_lock(&qcom_iommu_lock);
+
+ if (!priv || list_empty(&priv->iommu_list))
+ goto fail;
+
+ /* all IOMMU's in the domain have same pgtables: */
+ iommu = list_first_entry(&priv->iommu_list,
+ struct qcom_iommu, dom_node);
+
+ if (list_empty(&iommu->ctx_list))
+ goto fail;
+
+ iommu_ctx = list_first_entry(&iommu->ctx_list,
+ struct qcom_iommu_ctx, node);
+
+ base = iommu->base;
+ ctx = iommu_ctx->num;
+
+ ret = __enable_clocks(iommu);
+ if (ret)
+ goto fail;
+
+ SET_V2PPR(base, ctx, va & V2Pxx_VA);
+
+ mb();
+ par = GET_PAR(base, ctx);
+
+ /* We are dealing with a supersection */
+ if (GET_NOFAULT_SS(base, ctx))
+ ret = (par & 0xFF000000) | (va & 0x00FFFFFF);
+ else /* Upper 20 bits from PAR, lower 12 from VA */
+ ret = (par & 0xFFFFF000) | (va & 0x00000FFF);
+
+ if (GET_FAULT(base, ctx))
+ ret = 0;
+
+ __disable_clocks(iommu);
+
+fail:
+ mutex_unlock(&qcom_iommu_lock);
+ return ret;
+}
+
+static void print_ctx_regs(void __iomem *base, int ctx)
+{
+ unsigned int fsr = GET_FSR(base, ctx);
+ pr_err("FAR = %08x PAR = %08x\n",
+ GET_FAR(base, ctx), GET_PAR(base, ctx));
+ pr_err("FSR = %08x [%s%s%s%s%s%s%s%s%s%s]\n", fsr,
+ (fsr & 0x02) ? "TF " : "",
+ (fsr & 0x04) ? "AFF " : "",
+ (fsr & 0x08) ? "APF " : "",
+ (fsr & 0x10) ? "TLBMF " : "",
+ (fsr & 0x20) ? "HTWDEEF " : "",
+ (fsr & 0x40) ? "HTWSEEF " : "",
+ (fsr & 0x80) ? "MHF " : "",
+ (fsr & 0x10000) ? "SL " : "",
+ (fsr & 0x40000000) ? "SS " : "",
+ (fsr & 0x80000000) ? "MULTI " : "");
+
+ pr_err("FSYNR0 = %08x FSYNR1 = %08x\n",
+ GET_FSYNR0(base, ctx), GET_FSYNR1(base, ctx));
+ pr_err("TTBR0 = %08x TTBR1 = %08x\n",
+ GET_TTBR0(base, ctx), GET_TTBR1(base, ctx));
+ pr_err("SCTLR = %08x ACTLR = %08x\n",
+ GET_SCTLR(base, ctx), GET_ACTLR(base, ctx));
+ pr_err("PRRR = %08x NMRR = %08x\n",
+ GET_PRRR(base, ctx), GET_NMRR(base, ctx));
+}
+
+static irqreturn_t __fault_handler(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct qcom_iommu *iommu = platform_get_drvdata(pdev);
+ struct qcom_iommu_ctx *iommu_ctx = NULL;
+ void __iomem *base = iommu->base;
+ int ret;
+ bool first = true;
+
+ mutex_lock(&qcom_iommu_lock);
+
+ ret = __enable_clocks(iommu);
+ if (ret)
+ goto fail;
+
+ list_for_each_entry(iommu_ctx, &iommu->ctx_list, node) {
+ int i = iommu_ctx->num;
+ unsigned int fsr = GET_FSR(base, i);
+ unsigned long iova;
+ int flags;
+
+ if (!fsr)
+ continue;
+
+ iova = GET_FAR(base, i);
+
+ /* TODO without docs, not sure about IOMMU_FAULT_* flags */
+ flags = 0;
+
+ if (!report_iommu_fault(iommu->domain, iommu->dev,
+ iova, flags)) {
+ ret = IRQ_HANDLED;
+ } else {
+ // XXX ratelimited
+ if (first) {
+ /* only print header for first context */
+ pr_err("Unexpected IOMMU page fault!\n");
+ pr_err("base = %08x\n", (unsigned int) base);
+ first = false;
+ }
+ pr_err("Fault occurred in context %d.\n", i);
+ pr_err("name = %s\n", dev_name(&pdev->dev));
+ pr_err("Interesting registers:\n");
+ print_ctx_regs(base, i);
+ }
+
+ SET_FSR(base, i, fsr);
+ SET_RESUME(base, i, 1);
+ }
+ __disable_clocks(iommu);
+
+fail:
+ mutex_unlock(&qcom_iommu_lock);
+ return IRQ_HANDLED;
+}
+
+static struct iommu_ops qcom_iommu_ops = {
+ .domain_init = qcom_iommu_domain_init,
+ .domain_destroy = qcom_iommu_domain_destroy,
+ .attach_dev = qcom_iommu_attach_dev,
+ .detach_dev = qcom_iommu_detach_dev,
+ .map = qcom_iommu_map,
+ .unmap = qcom_iommu_unmap,
+ .iova_to_phys = qcom_iommu_iova_to_phys,
+ .pgsize_bitmap = QCOM_IOMMU_PGSIZES,
+};
+
+/*
+ * IOMMU Platform Driver:
+ */
+
+static int __register_ctx(struct qcom_iommu *iommu,
+ struct of_phandle_args *masterspec, int ctx)
+{
+ struct qcom_iommu_ctx *iommu_ctx;
+ int i, ret;
+
+ if (masterspec->args_count > MAX_NUM_MIDS)
+ return -EINVAL;
+
+ iommu_ctx = kzalloc(sizeof(*iommu_ctx), GFP_KERNEL);
+ if (!iommu) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ iommu_ctx->of_node = masterspec->np;
+ iommu_ctx->num = ctx;
+
+ for (i = 0; i < masterspec->args_count; i++)
+ iommu_ctx->mids[i] = masterspec->args[i];
+ for (; i < ARRAY_SIZE(iommu_ctx->mids); i++)
+ iommu_ctx->mids[i] = -1;
+
+ list_add_tail(&iommu_ctx->node, &iommu->ctx_list);
+
+ return 0;
+
+fail:
+ // TODO cleanup;
+ return ret;
+}
+
+static int qcom_iommu_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct qcom_iommu *iommu;
+ struct qcom_iommu_ctx *iommu_ctx;
+ struct of_phandle_args masterspec;
+ struct resource *r;
+ int ctx, ret, par, i;
+ u32 val;
+
+ iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+ if (!iommu) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ iommu->dev = &pdev->dev;
+ INIT_LIST_HEAD(&iommu->ctx_list);
+
+ ret = of_property_read_u32(node, "ncb", &val);
+ if (ret) {
+ dev_err(iommu->dev, "could not get ncb\n");
+ goto fail;
+ }
+ iommu->ncb = val;
+
+ ret = of_property_read_u32(node, "ttbr-split", &val);
+ if (ret)
+ val = 0;
+ iommu->ttbr_split = val;
+
+ iommu->pclk = devm_clk_get(iommu->dev, "smmu_pclk");
+ if (IS_ERR(iommu->pclk)) {
+ dev_err(iommu->dev, "could not get smmu_pclk\n");
+ ret = PTR_ERR(iommu->pclk);
+ iommu->pclk = NULL;
+ goto fail;
+ }
+
+ iommu->clk = devm_clk_get(iommu->dev, "iommu_clk");
+ if (IS_ERR(iommu->clk)) {
+ dev_err(iommu->dev, "could not get iommu_clk\n");
+ ret = PTR_ERR(iommu->clk);
+ iommu->clk = NULL;
+ goto fail;
+ }
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "physbase");
+ if (!r) {
+ dev_err(iommu->dev, "could not get physbase\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ iommu->base = devm_ioremap_resource(iommu->dev, r);
+ if (IS_ERR(iommu->base)) {
+ ret = PTR_ERR(iommu->base);
+ goto fail;
+ }
+
+ iommu->irq = platform_get_irq_byname(pdev, "nonsecure_irq");
+ if (WARN_ON(iommu->irq < 0)) {
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ /* now find our contexts: */
+ i = 0;
+ while (!of_parse_phandle_with_args(node, "mmu-masters",
+ "#stream-id-cells", i, &masterspec)) {
+ ret = __register_ctx(iommu, &masterspec, i);
+ if (ret) {
+ dev_err(iommu->dev, "failed to add context %s\n",
+ masterspec.np->name);
+ goto fail;
+ }
+
+ i++;
+ }
+ dev_notice(iommu->dev, "registered %d master devices\n", i);
+
+ __enable_clocks(iommu);
+ __reset_iommu(iommu);
+
+ SET_M(iommu->base, 0, 1);
+ SET_PAR(iommu->base, 0, 0);
+ SET_V2PCFG(iommu->base, 0, 1);
+ SET_V2PPR(iommu->base, 0, 0);
+ mb();
+ par = GET_PAR(iommu->base, 0);
+ SET_V2PCFG(iommu->base, 0, 0);
+ SET_M(iommu->base, 0, 0);
+ mb();
+
+ ctx = 0;
+ list_for_each_entry(iommu_ctx, &iommu->ctx_list, node) {
+ for (i = 0; iommu_ctx->mids[i] != -1; i++) {
+ int mid = iommu_ctx->mids[i];
+
+ SET_M2VCBR_N(iommu->base, mid, 0);
+ SET_CBACR_N(iommu->base, ctx, 0);
+
+ /* Set VMID = 0 */
+ SET_VMID(iommu->base, mid, 0);
+
+ /* Set the context number for that MID to this context */
+ SET_CBNDX(iommu->base, mid, ctx);
+
+ /* Set MID associated with this context bank to 0*/
+ SET_CBVMID(iommu->base, ctx, 0);
+
+ /* Set the ASID for TLB tagging for this context */
+ SET_CONTEXTIDR_ASID(iommu->base, ctx, ctx);
+
+ /* Set security bit override to be Non-secure */
+ SET_NSCFG(iommu->base, mid, 3);
+ }
+
+ ctx++;
+ }
+
+ __disable_clocks(iommu);
+
+ if (!par) {
+ dev_err(iommu->dev, "Invalid PAR value detected\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ ret = devm_request_threaded_irq(iommu->dev, iommu->irq, NULL,
+ __fault_handler, IRQF_ONESHOT | IRQF_SHARED,
+ "iommu", pdev);
+ if (ret) {
+ dev_err(iommu->dev, "Request IRQ %d failed with ret=%d\n",
+ iommu->irq, ret);
+ goto fail;
+ }
+
+ dev_info(iommu->dev, "device mapped at %p, irq %d with %d ctx banks\n",
+ iommu->base, iommu->irq, iommu->ncb);
+
+ platform_set_drvdata(pdev, iommu);
+
+ mutex_lock(&qcom_iommu_lock);
+ list_add(&iommu->dev_node, &qcom_iommu_devices);
+ mutex_unlock(&qcom_iommu_lock);
+
+ return 0;
+fail:
+ // TODO cleanup..
+ return ret;
+}
+
+static int qcom_iommu_remove(struct platform_device *pdev)
+{
+ struct qcom_iommu *priv = platform_get_drvdata(pdev);
+
+ if (WARN_ON(!priv))
+ return 0;
+
+ if (priv->clk)
+ clk_disable_unprepare(priv->clk);
+
+ if (priv->pclk)
+ clk_disable_unprepare(priv->pclk);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct of_device_id qcom_iommu_dt_match[] = {
+ { .compatible = "qcom,iommu-v0" },
+ {}
+};
+
+static struct platform_driver qcom_iommu_driver = {
+ .driver = {
+ .name = "qcom-iommu-v0",
+ .of_match_table = qcom_iommu_dt_match,
+ },
+ .probe = qcom_iommu_probe,
+ .remove = qcom_iommu_remove,
+};
+
+static int __init get_tex_class(int icp, int ocp, int mt, int nos)
+{
+ int i = 0;
+ unsigned int prrr = 0;
+ unsigned int nmrr = 0;
+ int c_icp, c_ocp, c_mt, c_nos;
+
+ RCP15_PRRR(prrr);
+ RCP15_NMRR(nmrr);
+
+ for (i = 0; i < NUM_TEX_CLASS; i++) {
+ c_nos = PRRR_NOS(prrr, i);
+ c_mt = PRRR_MT(prrr, i);
+ c_icp = NMRR_ICP(nmrr, i);
+ c_ocp = NMRR_OCP(nmrr, i);
+
+ if (icp == c_icp && ocp == c_ocp && c_mt == mt && c_nos == nos)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+static int __init qcom_iommu_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&qcom_iommu_driver);
+ if (ret) {
+ pr_err("Failed to register IOMMU driver\n");
+ goto error;
+ }
+
+ qcom_iommu_tex_class[QCOM_IOMMU_ATTR_NONCACHED] =
+ get_tex_class(CP_NONCACHED, CP_NONCACHED, MT_NORMAL, 1);
+
+ qcom_iommu_tex_class[QCOM_IOMMU_ATTR_CACHED_WB_WA] =
+ get_tex_class(CP_WB_WA, CP_WB_WA, MT_NORMAL, 1);
+
+ qcom_iommu_tex_class[QCOM_IOMMU_ATTR_CACHED_WB_NWA] =
+ get_tex_class(CP_WB_NWA, CP_WB_NWA, MT_NORMAL, 1);
+
+ qcom_iommu_tex_class[QCOM_IOMMU_ATTR_CACHED_WT] =
+ get_tex_class(CP_WT, CP_WT, MT_NORMAL, 1);
+
+ bus_set_iommu(&platform_bus_type, &qcom_iommu_ops);
+
+ return 0;
+
+error:
+ return ret;
+}
+
+static void __exit qcom_iommu_driver_exit(void)
+{
+ platform_driver_unregister(&qcom_iommu_driver);
+}
+
+subsys_initcall(qcom_iommu_init);
+module_exit(qcom_iommu_driver_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stepan Moskovchenko <stepanm@codeaurora.org>");
+MODULE_AUTHOR("Rob Clark <robdclark@gmail.com>");
diff --git a/drivers/iommu/qcom_iommu_v0.h b/drivers/iommu/qcom_iommu_v0.h
new file mode 100644
index 000000000000..a3d1e37182c8
--- /dev/null
+++ b/drivers/iommu/qcom_iommu_v0.h
@@ -0,0 +1,99 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (C) 2014 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/* NOTE: originally based on msm_iommu non-DT driver for same hw
+ * but as the structure of the driver changes considerably for DT
+ * it seemed easier to not try to support old platforms with the
+ * same driver.
+ */
+
+#ifndef QCOM_IOMMU_V0_H
+#define QCOM_IOMMU_V0_H
+
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+
+/* Sharability attributes of QCOM IOMMU mappings */
+#define QCOM_IOMMU_ATTR_NON_SH 0x0
+#define QCOM_IOMMU_ATTR_SH 0x4
+
+/* Cacheability attributes of QCOM IOMMU mappings */
+#define QCOM_IOMMU_ATTR_NONCACHED 0x0
+#define QCOM_IOMMU_ATTR_CACHED_WB_WA 0x1
+#define QCOM_IOMMU_ATTR_CACHED_WB_NWA 0x2
+#define QCOM_IOMMU_ATTR_CACHED_WT 0x3
+
+/* Mask for the cache policy attribute */
+#define QCOM_IOMMU_CP_MASK 0x03
+
+/* Maximum number of Machine IDs that we are allowing to be mapped to the same
+ * context bank. The number of MIDs mapped to the same CB does not affect
+ * performance, but there is a practical limit on how many distinct MIDs may
+ * be present. These mappings are typically determined at design time and are
+ * not expected to change at run time.
+ */
+#define MAX_NUM_MIDS 32
+
+/**
+ * struct qcom_iommu - a single IOMMU hardware instance
+ * @dev: IOMMU device
+ * @base: IOMMU config port base address (VA)
+ * @irq: Interrupt number
+ * @ncb: Number of context banks present on this IOMMU HW instance
+ * @ttbr_split: ttbr split
+ * @clk: The bus clock for this IOMMU hardware instance
+ * @pclk: The clock for the IOMMU bus interconnect
+ * @ctx_list: list of 'struct qcom_iommu_ctx'
+ * @dev_node: list head in qcom_iommu_devices list
+ * @dom_node: list head in domain
+ * @domain: attached domain. Note that the relationship between domain and
+ * and iommu's is N:1, ie. an IOMMU can only be attached to one domain,
+ * but a domain can be attached to many IOMMUs
+ */
+struct qcom_iommu {
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ int ncb;
+ int ttbr_split;
+ struct clk *clk;
+ struct clk *pclk;
+ struct list_head ctx_list;
+ struct list_head dev_node;
+ struct list_head dom_node;
+ struct iommu_domain *domain;
+};
+
+/**
+ * struct qcom_iommu_ctx - an IOMMU context bank instance
+ * @of_node: node ptr of client device
+ * @num: Index of this context bank within the hardware
+ * @mids: List of Machine IDs that are to be mapped into this context
+ * bank, terminated by -1. The MID is a set of signals on the
+ * AXI bus that identifies the function associated with a specific
+ * memory request. (See ARM spec).
+ * @node: list head in ctx_list
+ */
+struct qcom_iommu_ctx {
+ struct device_node *of_node;
+ int num;
+ int mids[MAX_NUM_MIDS];
+ struct list_head node;
+};
+
+#endif
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index 5a92646a2ccb..d6dbe57ae113 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -26,6 +26,7 @@
#include <linux/regmap.h>
#include <linux/of_platform.h>
#include <linux/mfd/core.h>
+#include <linux/mfd/pm8921-core.h>
#define SSBI_REG_ADDR_IRQ_BASE 0x1BB
@@ -65,6 +66,41 @@ struct pm_irq_chip {
u8 config[0];
};
+int pm8xxx_read_irq_status(int irq)
+{
+ struct irq_data *d = irq_get_irq_data(irq);
+ struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d);
+ unsigned int pmirq = irqd_to_hwirq(d);
+ unsigned int bits;
+ int irq_bit;
+ u8 block;
+ int rc;
+
+ if (!chip) {
+ pr_err("Failed to resolve pm_irq_chip\n");
+ return -EINVAL;
+ }
+
+ block = pmirq / 8;
+ irq_bit = pmirq % 8;
+
+ spin_lock(&chip->pm_irq_lock);
+ rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block);
+ if (rc) {
+ pr_err("Failed Selecting Block %d rc=%d\n", block, rc);
+ goto bail;
+ }
+
+ rc = regmap_read(chip->regmap, SSBI_REG_ADDR_IRQ_RT_STATUS, &bits);
+ if (rc)
+ pr_err("Failed Reading Status rc=%d\n", rc);
+bail:
+ spin_unlock(&chip->pm_irq_lock);
+
+ return rc ? rc : !!(bits & BIT(irq_bit));
+}
+EXPORT_SYMBOL(pm8xxx_read_irq_status);
+
static int pm8xxx_read_block_irq(struct pm_irq_chip *chip, unsigned int bp,
unsigned int *ip)
{
diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c
index f696328c2933..eceb34f503da 100644
--- a/drivers/mfd/qcom_rpm.c
+++ b/drivers/mfd/qcom_rpm.c
@@ -331,6 +331,42 @@ static const struct of_device_id qcom_rpm_of_match[] = {
};
MODULE_DEVICE_TABLE(of, qcom_rpm_of_match);
+#define QCOM_RPM_STATUS_ID_SEQUENCE 7
+int qcom_rpm_read(struct qcom_rpm *rpm,
+ int resource,
+ u32 *val,
+ size_t count)
+{
+ const struct qcom_rpm_resource *res;
+ const struct qcom_rpm_data *data = rpm->data;
+ uint32_t seq_begin;
+ uint32_t seq_end;
+ int rc;
+ int i;
+
+ if (WARN_ON(resource < 0 || resource >= data->n_resources))
+ return -EINVAL;
+
+ res = &data->resource_table[resource];
+ if (WARN_ON(res->size != count))
+ return -EINVAL;
+
+ mutex_lock(&rpm->lock);
+ seq_begin = readl(RPM_STATUS_REG(rpm, QCOM_RPM_STATUS_ID_SEQUENCE));
+
+ for (i = 0; i < count; i++)
+ *val++ = readl(RPM_STATUS_REG(rpm, res->status_id));
+
+ seq_end = readl(RPM_STATUS_REG(rpm, QCOM_RPM_STATUS_ID_SEQUENCE));
+
+ rc = (seq_begin != seq_end || (seq_begin & 0x01)) ? -EBUSY : 0;
+
+ mutex_unlock(&rpm->lock);
+ return rc;
+
+}
+EXPORT_SYMBOL(qcom_rpm_read);
+
int qcom_rpm_write(struct qcom_rpm *rpm,
int state,
int resource,
diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c
index 27986f641f7d..39734999fe40 100644
--- a/drivers/mfd/ssbi.c
+++ b/drivers/mfd/ssbi.c
@@ -330,7 +330,12 @@ static struct platform_driver ssbi_driver = {
.of_match_table = ssbi_match_table,
},
};
-module_platform_driver(ssbi_driver);
+
+static int ssbi_init(void)
+{
+ return platform_driver_register(&ssbi_driver);
+}
+subsys_initcall(ssbi_init);
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index e9f1d8d84613..6f0a875e49ae 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -124,8 +124,8 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev)
PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) {
ret = PTR_ERR(pwrseq->reset_gpios[i]);
- while (--i)
- gpiod_put(pwrseq->reset_gpios[i]);
+ while (i > 0)
+ gpiod_put(pwrseq->reset_gpios[--i]);
goto clk_put;
}
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 7fe16194ebc8..5891c62d04e9 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -78,6 +78,7 @@ static unsigned int fmax = 515633;
* @qcom_fifo: enables qcom specific fifo pio read logic.
* @qcom_dml: enables qcom specific dma glue for dma transfers.
* @reversed_irq_handling: handle data irq before cmd irq.
+ * @any_blksize: true if block any sizes are supported
*/
struct variant_data {
unsigned int clkreg;
@@ -104,6 +105,7 @@ struct variant_data {
bool qcom_fifo;
bool qcom_dml;
bool reversed_irq_handling;
+ bool any_blksize;
};
static struct variant_data variant_arm = {
@@ -200,6 +202,7 @@ static struct variant_data variant_ux500v2 = {
.pwrreg_clkgate = true,
.busy_detect = true,
.pwrreg_nopower = true,
+ .any_blksize = true,
};
static struct variant_data variant_qcom = {
@@ -218,6 +221,7 @@ static struct variant_data variant_qcom = {
.explicit_mclk_control = true,
.qcom_fifo = true,
.qcom_dml = true,
+ .any_blksize = true,
};
static int mmci_card_busy(struct mmc_host *mmc)
@@ -245,10 +249,11 @@ static int mmci_card_busy(struct mmc_host *mmc)
static int mmci_validate_data(struct mmci_host *host,
struct mmc_data *data)
{
+ struct variant_data *variant = host->variant;
+
if (!data)
return 0;
-
- if (!is_power_of_2(data->blksz)) {
+ if (!is_power_of_2(data->blksz) && !variant->any_blksize) {
dev_err(mmc_dev(host->mmc),
"unsupported block size (%d bytes)\n", data->blksz);
return -EINVAL;
@@ -804,7 +809,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
writel(host->size, base + MMCIDATALENGTH);
blksz_bits = ffs(data->blksz) - 1;
- BUG_ON(1 << blksz_bits != data->blksz);
if (variant->blksz_datactrl16)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9cc4fc..366ce65048e9 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -30,6 +30,11 @@ config PCI_IMX6
select PCIEPORTBUS
select PCIE_DW
+config PCI_QCOM
+ bool "Qualcomm PCIe controller"
+ default y
+ depends on ARCH_QCOM && !ARM64
+
config PCI_TEGRA
bool "NVIDIA Tegra PCIe controller"
depends on ARCH_TEGRA && !ARM64
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c92bf1..2ac7bff70663 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
+obj-$(CONFIG_PCI_QCOM) += pci-qcom.o
diff --git a/drivers/pci/host/pci-qcom.c b/drivers/pci/host/pci-qcom.c
new file mode 100644
index 000000000000..c1150d575535
--- /dev/null
+++ b/drivers/pci/host/pci-qcom.c
@@ -0,0 +1,968 @@
+/* Copyright (c) 2012-2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * QCOM MSM PCIe controller driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_gpio.h>
+#include <linux/msi.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/delay.h>
+
+#define INT_PCI_MSI_NR (8 * 32)
+#define MSM_PCIE_MSI_PHY 0xa0000000
+
+#define PCIE20_MSI_CTRL_ADDR (0x820)
+#define PCIE20_MSI_CTRL_UPPER_ADDR (0x824)
+#define PCIE20_MSI_CTRL_INTR_EN (0x828)
+#define PCIE20_MSI_CTRL_INTR_MASK (0x82C)
+#define PCIE20_MSI_CTRL_INTR_STATUS (0x830)
+
+#define PCIE20_MSI_CTRL_MAX 8
+/* Root Complex Port vendor/device IDs */
+#define PCIE_VENDOR_ID_RCP 0x17cb
+#define PCIE_DEVICE_ID_RCP 0x0101
+
+#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
+
+#define PCIE20_PARF_PCS_DEEMPH 0x34
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(x) __set(x, 21, 16)
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) __set(x, 13, 8)
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) __set(x, 5, 0)
+
+#define PCIE20_PARF_PCS_SWING 0x38
+#define PCIE20_PARF_PCS_SWING_TX_SWING_FULL(x) __set(x, 14, 8)
+#define PCIE20_PARF_PCS_SWING_TX_SWING_LOW(x) __set(x, 6, 0)
+
+#define PCIE20_PARF_PHY_CTRL 0x40
+#define PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(x) __set(x, 20, 16)
+#define PCIE20_PARF_PHY_CTRL_PHY_LOS_LEVEL(x) __set(x, 12, 8)
+#define PCIE20_PARF_PHY_CTRL_PHY_RTUNE_REQ (1 << 4)
+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BURNIN (1 << 2)
+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BYPASS (1 << 1)
+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_PWR_DOWN (1 << 0)
+
+#define PCIE20_PARF_PHY_REFCLK 0x4C
+#define PCIE20_PARF_CONFIG_BITS 0x50
+
+#define PCIE20_ELBI_SYS_CTRL 0x04
+#define PCIE20_ELBI_SYS_CTRL_LTSSM_EN 0x01
+
+#define PCIE20_CAP 0x70
+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10)
+
+#define PCIE20_COMMAND_STATUS 0x04
+#define PCIE20_BUSNUMBERS 0x18
+#define PCIE20_MEMORY_BASE_LIMIT 0x20
+
+#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818
+#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c
+#define PCIE20_PLR_IATU_VIEWPORT 0x900
+#define PCIE20_PLR_IATU_CTRL1 0x904
+#define PCIE20_PLR_IATU_CTRL2 0x908
+#define PCIE20_PLR_IATU_LBAR 0x90C
+#define PCIE20_PLR_IATU_UBAR 0x910
+#define PCIE20_PLR_IATU_LAR 0x914
+#define PCIE20_PLR_IATU_LTAR 0x918
+#define PCIE20_PLR_IATU_UTAR 0x91c
+
+#define MSM_PCIE_DEV_CFG_ADDR 0x01000000
+
+#define RD 0
+#define WR 1
+
+#define MAX_RC_NUM 3
+#define PCIE_BUS_PRIV_DATA(pdev) \
+ (((struct pci_sys_data *)pdev->bus->sysdata)->private_data)
+
+/* PCIe TLP types that we are interested in */
+#define PCI_CFG0_RDWR 0x4
+#define PCI_CFG1_RDWR 0x5
+
+#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
+({ \
+ unsigned long timeout = jiffies + usecs_to_jiffies(timeout_us); \
+ might_sleep_if(timeout_us); \
+ for (;;) { \
+ (val) = readl(addr); \
+ if (cond) \
+ break; \
+ if (timeout_us && time_after(jiffies, timeout)) { \
+ (val) = readl(addr); \
+ break; \
+ } \
+ if (sleep_us) \
+ usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
+ } \
+ (cond) ? 0 : -ETIMEDOUT; \
+})
+
+struct qcom_msi {
+ struct msi_controller chip;
+ DECLARE_BITMAP(used, INT_PCI_MSI_NR);
+ struct irq_domain *domain;
+ unsigned long pages;
+ struct mutex lock;
+ int irq;
+};
+
+struct qcom_pcie {
+ void __iomem *elbi_base;
+ void __iomem *parf_base;
+ void __iomem *dwc_base;
+ void __iomem *cfg_base;
+ struct device *dev;
+ int reset_gpio;
+ bool ext_phy_ref_clk;
+ struct clk *iface_clk;
+ struct clk *bus_clk;
+ struct clk *phy_clk;
+ int irq_int[4];
+ struct reset_control *axi_reset;
+ struct reset_control *ahb_reset;
+ struct reset_control *por_reset;
+ struct reset_control *pci_reset;
+ struct reset_control *phy_reset;
+
+ struct resource conf;
+ struct resource io;
+ struct resource mem;
+
+ struct regulator *vdd_supply;
+ struct regulator *avdd_supply;
+ struct regulator *pcie_clk_supply;
+ struct regulator *pcie_ext3p3v_supply;
+
+ struct qcom_msi msi;
+};
+
+static int nr_controllers;
+static DEFINE_SPINLOCK(qcom_hw_pci_lock);
+
+static inline struct qcom_pcie *sys_to_pcie(struct pci_sys_data *sys)
+{
+ return sys->private_data;
+}
+
+
+inline int is_msm_pcie_rc(struct pci_bus *bus)
+{
+ return (bus->number == 0);
+}
+
+static int qcom_pcie_is_link_up(struct qcom_pcie *dev)
+{
+ return readl_relaxed(dev->dwc_base + PCIE20_CAP_LINKCTRLSTATUS) &
+ BIT(29);
+}
+
+inline int msm_pcie_get_cfgtype(struct pci_bus *bus)
+{
+ /*
+ * http://www.tldp.org/LDP/tlk/dd/pci.html
+ * Pass it onto the secondary bus interface unchanged if the
+ * bus number specified is greater than the secondary bus
+ * number and less than or equal to the subordinate bus
+ * number.
+ *
+ * Read/Write to the RC and Device/Switch connected to the RC
+ * are CFG0 type transactions. Rest have to be forwarded
+ * down stream as CFG1 transactions.
+ *
+ */
+ if (bus->number == 0)
+ return PCI_CFG0_RDWR;
+
+ return PCI_CFG0_RDWR;
+}
+
+void msm_pcie_config_cfgtype(struct pci_bus *bus, u32 devfn)
+{
+ uint32_t bdf, cfgtype;
+ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata);
+
+ cfgtype = msm_pcie_get_cfgtype(bus);
+
+ if (cfgtype == PCI_CFG0_RDWR) {
+ bdf = MSM_PCIE_DEV_CFG_ADDR;
+ } else {
+ /*
+ * iATU Lower Target Address Register
+ * Bits Description
+ * *-1:0 Forms bits [*:0] of the
+ * start address of the new
+ * address of the translated
+ * region. The start address
+ * must be aligned to a
+ * CX_ATU_MIN_REGION_SIZE kB
+ * boundary, so these bits are
+ * always 0. A write to this
+ * location is ignored by the
+ * PCIe core.
+ * 31:*1 Forms bits [31:*] of the of
+ * the new address of the
+ * translated region.
+ *
+ * * is log2(CX_ATU_MIN_REGION_SIZE)
+ */
+ bdf = (((bus->number & 0xff) << 24) & 0xff000000) |
+ (((devfn & 0xff) << 16) & 0x00ff0000);
+ }
+
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
+ wmb();
+
+ /* Program Bdf Address */
+ writel_relaxed(bdf, dev->dwc_base + PCIE20_PLR_IATU_LTAR);
+ wmb();
+
+ /* Write Config Request Type */
+ writel_relaxed(cfgtype, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
+ wmb();
+}
+
+static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper,
+ int where, int size, u32 *val)
+{
+ uint32_t word_offset, byte_offset, mask;
+ uint32_t rd_val, wr_val;
+ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata);
+ void __iomem *config_base;
+ int rc;
+
+ rc = is_msm_pcie_rc(bus);
+
+ /*
+ * For downstream bus, make sure link is up
+ */
+ if (rc && (devfn != 0)) {
+ *val = ~0;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ } else if ((!rc) && (!qcom_pcie_is_link_up(dev))) {
+ *val = ~0;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+ msm_pcie_config_cfgtype(bus, devfn);
+
+ word_offset = where & ~0x3;
+ byte_offset = where & 0x3;
+ mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset);
+
+ config_base = (rc) ? dev->dwc_base : dev->cfg_base;
+ rd_val = readl_relaxed(config_base + word_offset);
+
+ if (oper == RD) {
+ *val = ((rd_val & mask) >> (8 * byte_offset));
+ } else {
+ wr_val = (rd_val & ~mask) |
+ ((*val << (8 * byte_offset)) & mask);
+ writel_relaxed(wr_val, config_base + word_offset);
+ wmb(); /* ensure config data is written to hardware register */
+ }
+
+ return 0;
+}
+
+static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+ int size, u32 *val)
+{
+ return msm_pcie_oper_conf(bus, devfn, RD, where, size, val);
+}
+
+static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+ int where, int size, u32 val)
+{
+ /*
+ *Attempt to reset secondary bus is causing PCIE core to reset.
+ *Disable secondary bus reset functionality.
+ */
+ if ((bus->number == 0) && (where == PCI_BRIDGE_CONTROL) &&
+ (val & PCI_BRIDGE_CTL_BUS_RESET)) {
+ pr_info("PCIE secondary bus reset not supported\n");
+ val &= ~PCI_BRIDGE_CTL_BUS_RESET;
+ }
+
+ return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val);
+}
+
+static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct qcom_pcie *pcie_dev = PCIE_BUS_PRIV_DATA(dev);
+
+ return pcie_dev->irq_int[pin-1];
+}
+
+static int qcom_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+ struct qcom_pcie *qcom_pcie = sys->private_data;
+
+ /*
+ * specify linux PCI framework to allocate device memory (BARs)
+ * from msm_pcie_dev.dev_mem_res resource.
+ */
+ sys->mem_offset = 0;
+ sys->io_offset = 0;
+
+ pci_add_resource(&sys->resources, &qcom_pcie->mem);
+ pci_add_resource(&sys->resources, &qcom_pcie->io);
+
+ return 1;
+}
+
+static struct pci_ops qcom_pcie_ops = {
+ .read = msm_pcie_rd_conf,
+ .write = msm_pcie_wr_conf,
+};
+
+static struct hw_pci qcom_hw_pci[MAX_RC_NUM] = {
+ {
+ .ops = &qcom_pcie_ops,
+ .nr_controllers = 1,
+ .swizzle = pci_common_swizzle,
+ .setup = qcom_pcie_setup,
+ .map_irq = qcom_pcie_map_irq,
+ },
+ {
+ .ops = &qcom_pcie_ops,
+ .nr_controllers = 1,
+ .swizzle = pci_common_swizzle,
+ .setup = qcom_pcie_setup,
+ .map_irq = qcom_pcie_map_irq,
+ },
+ {
+ .ops = &qcom_pcie_ops,
+ .nr_controllers = 1,
+ .swizzle = pci_common_swizzle,
+ .setup = qcom_pcie_setup,
+ .map_irq = qcom_pcie_map_irq,
+ },
+};
+
+static inline void qcom_elbi_writel_relaxed(struct qcom_pcie *pcie,
+ u32 val, u32 reg)
+{
+ writel_relaxed(val, pcie->elbi_base + reg);
+}
+
+static inline u32 qcom_elbi_readl_relaxed(struct qcom_pcie *pcie, u32 reg)
+{
+ return readl_relaxed(pcie->elbi_base + reg);
+}
+
+static inline void qcom_parf_writel_relaxed(struct qcom_pcie *pcie,
+ u32 val, u32 reg)
+{
+ writel_relaxed(val, pcie->parf_base + reg);
+}
+
+static inline u32 qcom_parf_readl_relaxed(struct qcom_pcie *pcie, u32 reg)
+{
+ return readl_relaxed(pcie->parf_base + reg);
+}
+
+static void msm_pcie_write_mask(void __iomem *addr,
+ uint32_t clear_mask, uint32_t set_mask)
+{
+ uint32_t val;
+
+ val = (readl_relaxed(addr) & ~clear_mask) | set_mask;
+ writel_relaxed(val, addr);
+ wmb(); /* ensure data is written to hardware register */
+}
+
+static void qcom_pcie_config_controller(struct qcom_pcie *dev)
+{
+ /*
+ * program and enable address translation region 0 (device config
+ * address space); region type config;
+ * axi config address range to device config address range
+ */
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
+ /* ensure that hardware locks the region before programming it */
+ wmb();
+
+ writel_relaxed(4, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
+ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2);
+ writel_relaxed(dev->conf.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR);
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR);
+ writel_relaxed(dev->conf.end, dev->dwc_base + PCIE20_PLR_IATU_LAR);
+ writel_relaxed(MSM_PCIE_DEV_CFG_ADDR,
+ dev->dwc_base + PCIE20_PLR_IATU_LTAR);
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR);
+ /* ensure that hardware registers the configuration */
+ wmb();
+
+ /*
+ * program and enable address translation region 2 (device resource
+ * address space); region type memory;
+ * axi device bar address range to device bar address range
+ */
+ writel_relaxed(2, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT);
+ /* ensure that hardware locks the region before programming it */
+ wmb();
+
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_CTRL1);
+ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2);
+ writel_relaxed(dev->mem.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR);
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR);
+ writel_relaxed(dev->mem.end, dev->dwc_base + PCIE20_PLR_IATU_LAR);
+ writel_relaxed(dev->mem.start,
+ dev->dwc_base + PCIE20_PLR_IATU_LTAR);
+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR);
+ /* ensure that hardware registers the configuration */
+ wmb();
+
+ /* 1K PCIE buffer setting */
+ writel_relaxed(0x3, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0);
+ writel_relaxed(0x1, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1);
+ /* ensure that hardware registers the configuration */
+ wmb();
+}
+
+static int qcom_msi_alloc(struct qcom_msi *chip)
+{
+ int msi;
+
+ mutex_lock(&chip->lock);
+
+ msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR);
+ if (msi < INT_PCI_MSI_NR)
+ set_bit(msi, chip->used);
+ else
+ msi = -ENOSPC;
+
+ mutex_unlock(&chip->lock);
+
+ return msi;
+}
+
+static void qcom_msi_free(struct qcom_msi *chip, unsigned long irq)
+{
+ struct device *dev = chip->chip.dev;
+
+ mutex_lock(&chip->lock);
+
+ if (!test_bit(irq, chip->used))
+ dev_err(dev, "trying to free unused MSI#%lu\n", irq);
+ else
+ clear_bit(irq, chip->used);
+
+ mutex_unlock(&chip->lock);
+}
+
+
+static irqreturn_t handle_msi_irq(int irq, void *data)
+{
+ int i, j, index;
+ unsigned long val;
+ struct qcom_pcie *dev = data;
+ void __iomem *ctrl_status;
+ struct qcom_msi *msi = &dev->msi;
+
+ /* check for set bits, clear it by setting that bit
+ and trigger corresponding irq */
+ for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) {
+ ctrl_status = dev->dwc_base +
+ PCIE20_MSI_CTRL_INTR_STATUS + (i * 12);
+
+ val = readl_relaxed(ctrl_status);
+ while (val) {
+ j = find_first_bit(&val, 32);
+ index = j + (32 * i);
+ writel_relaxed(BIT(j), ctrl_status);
+ /* ensure that interrupt is cleared (acked) */
+ wmb();
+
+ irq = irq_find_mapping(msi->domain, index);
+ if (irq) {
+ if (test_bit(index, msi->used))
+ generic_handle_irq(irq);
+ else
+ dev_info(dev->dev, "unhandled MSI\n");
+ }
+ val = readl_relaxed(ctrl_status);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static inline struct qcom_msi *to_qcom_msi(struct msi_controller *chip)
+{
+ return container_of(chip, struct qcom_msi, chip);
+}
+
+static int qcom_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
+ struct msi_desc *desc)
+{
+ struct qcom_msi *msi = to_qcom_msi(chip);
+ struct msi_msg msg;
+ unsigned int irq;
+ int hwirq;
+
+ hwirq = qcom_msi_alloc(msi);
+ if (hwirq < 0)
+ return hwirq;
+
+ irq = irq_create_mapping(msi->domain, hwirq);
+ if (!irq)
+ return -EINVAL;
+
+ irq_set_msi_desc(irq, desc);
+
+ msg.address_lo = MSM_PCIE_MSI_PHY;
+ /* 32 bit address only */
+ msg.address_hi = 0;
+ msg.data = hwirq;
+
+ write_msi_msg(irq, &msg);
+
+ return 0;
+}
+
+static void qcom_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
+{
+ struct qcom_msi *msi = to_qcom_msi(chip);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ qcom_msi_free(msi, d->hwirq);
+}
+
+static struct irq_chip qcom_msi_irq_chip = {
+ .name = "PCI-MSI",
+ .irq_enable = unmask_msi_irq,
+ .irq_disable = mask_msi_irq,
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+};
+
+
+static int qcom_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &qcom_msi_irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ set_irq_flags(irq, IRQF_VALID);
+
+ return 0;
+}
+
+
+static const struct irq_domain_ops msi_domain_ops = {
+ .map = qcom_pcie_msi_map,
+};
+uint32_t msm_pcie_msi_init(struct qcom_pcie *pcie, struct platform_device *pdev)
+{
+ int i, rc;
+ struct qcom_msi *msi = &pcie->msi;
+ int err;
+
+ mutex_init(&msi->lock);
+
+ msi->chip.dev = pcie->dev;
+ msi->chip.setup_irq = qcom_msi_setup_irq;
+ msi->chip.teardown_irq = qcom_msi_teardown_irq;
+ msi->domain = irq_domain_add_linear(pdev->dev.of_node, INT_PCI_MSI_NR,
+ &msi_domain_ops, &msi->chip);
+ if (!msi->domain) {
+ dev_err(&pdev->dev, "failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+
+ err = platform_get_irq_byname(pdev, "msi");
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+ return err;
+ }
+
+ msi->irq = err;
+
+ /* program MSI controller and enable all interrupts */
+ writel_relaxed(MSM_PCIE_MSI_PHY, pcie->dwc_base + PCIE20_MSI_CTRL_ADDR);
+ writel_relaxed(0, pcie->dwc_base + PCIE20_MSI_CTRL_UPPER_ADDR);
+
+ for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++)
+ writel_relaxed(~0, pcie->dwc_base +
+ PCIE20_MSI_CTRL_INTR_EN + (i * 12));
+
+ /* ensure that hardware is configured before proceeding */
+ wmb();
+
+ /* register handler for physical MSI interrupt line */
+ rc = request_irq(msi->irq, handle_msi_irq, IRQF_TRIGGER_RISING,
+ "msm_pcie_msi", pcie);
+ if (rc) {
+ pr_err("Unable to allocate msi interrupt\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+static int qcom_pcie_vreg_on(struct qcom_pcie *qcom_pcie)
+{
+ int err;
+ /* enable regulators */
+ err = regulator_enable(qcom_pcie->vdd_supply);
+ if (err < 0) {
+ dev_err(qcom_pcie->dev, "failed to enable VDD regulator\n");
+ return err;
+ }
+
+ err = regulator_enable(qcom_pcie->pcie_clk_supply);
+ if (err < 0) {
+ dev_err(qcom_pcie->dev, "failed to enable pcie-clk regulator\n");
+ return err;
+ }
+
+ err = regulator_enable(qcom_pcie->avdd_supply);
+ if (err < 0) {
+ dev_err(qcom_pcie->dev, "failed to enable AVDD regulator\n");
+ return err;
+ }
+
+ err = regulator_enable(qcom_pcie->pcie_ext3p3v_supply);
+ if (err < 0) {
+ dev_err(qcom_pcie->dev, "failed to enable pcie_ext3p3v regulator\n");
+ return err;
+ }
+
+ return err;
+
+}
+
+static int qcom_pcie_parse_dt(struct qcom_pcie *qcom_pcie,
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *elbi_base, *parf_base, *dwc_base;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ int ret, i;
+
+ qcom_pcie->ext_phy_ref_clk = of_property_read_bool(np,
+ "qcom,external-phy-refclk");
+
+ elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
+ qcom_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base);
+ if (IS_ERR(qcom_pcie->elbi_base)) {
+ dev_err(&pdev->dev, "Failed to ioremap elbi space\n");
+ return PTR_ERR(qcom_pcie->elbi_base);
+ }
+
+ parf_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf");
+ qcom_pcie->parf_base = devm_ioremap_resource(&pdev->dev, parf_base);
+ if (IS_ERR(qcom_pcie->parf_base)) {
+ dev_err(&pdev->dev, "Failed to ioremap parf space\n");
+ return PTR_ERR(qcom_pcie->parf_base);
+ }
+
+ dwc_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
+ qcom_pcie->dwc_base = devm_ioremap_resource(&pdev->dev, dwc_base);
+ if (IS_ERR(qcom_pcie->dwc_base)) {
+ dev_err(&pdev->dev, "Failed to ioremap dwc_base space\n");
+ return PTR_ERR(qcom_pcie->dwc_base);
+ }
+
+ if (of_pci_range_parser_init(&parser, np)) {
+ dev_err(&pdev->dev, "missing ranges property\n");
+ return -EINVAL;
+ }
+
+ /* Get the I/O and memory ranges from DT */
+ for_each_of_pci_range(&parser, &range) {
+ switch (range.pci_space & 0x3) {
+ case 0: /* cfg */
+ of_pci_range_to_resource(&range, np, &qcom_pcie->conf);
+ qcom_pcie->conf.flags = IORESOURCE_MEM;
+ break;
+ case 1: /* io */
+ of_pci_range_to_resource(&range, np, &qcom_pcie->io);
+ break;
+ default: /* mem */
+ of_pci_range_to_resource(&range, np, &qcom_pcie->mem);
+ break;
+ }
+ }
+
+ qcom_pcie->vdd_supply = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(qcom_pcie->vdd_supply)) {
+ dev_err(&pdev->dev, "Failed to get vdd supply\n");
+ return PTR_ERR(qcom_pcie->vdd_supply);
+ }
+
+ qcom_pcie->pcie_clk_supply = devm_regulator_get(&pdev->dev, "pcie-clk");
+ if (IS_ERR(qcom_pcie->pcie_clk_supply)) {
+ dev_err(&pdev->dev, "Failed to get pcie clk supply\n");
+ return PTR_ERR(qcom_pcie->pcie_clk_supply);
+ }
+ qcom_pcie->avdd_supply = devm_regulator_get(&pdev->dev, "avdd");
+ if (IS_ERR(qcom_pcie->avdd_supply)) {
+ dev_err(&pdev->dev, "Failed to get avdd supply\n");
+ return PTR_ERR(qcom_pcie->avdd_supply);
+ }
+
+ qcom_pcie->pcie_ext3p3v_supply = devm_regulator_get(&pdev->dev,
+ "ext-3p3v");
+ if (IS_ERR(qcom_pcie->pcie_ext3p3v_supply)) {
+ dev_err(&pdev->dev, "Failed to get pcie_ext3p3v supply\n");
+ return PTR_ERR(qcom_pcie->pcie_ext3p3v_supply);
+ }
+
+ qcom_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+ if (!gpio_is_valid(qcom_pcie->reset_gpio)) {
+ dev_err(&pdev->dev, "pcie reset gpio is not valid\n");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request_one(&pdev->dev, qcom_pcie->reset_gpio,
+ GPIOF_DIR_OUT, "pcie_reset");
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request pcie reset gpio\n");
+ return ret;
+ }
+
+ qcom_pcie->iface_clk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(qcom_pcie->iface_clk)) {
+ dev_err(&pdev->dev, "Failed to get pcie iface clock\n");
+ return PTR_ERR(qcom_pcie->iface_clk);
+ }
+
+ qcom_pcie->phy_clk = devm_clk_get(&pdev->dev, "phy");
+ if (IS_ERR(qcom_pcie->phy_clk)) {
+ dev_err(&pdev->dev, "Failed to get pcie phy clock\n");
+ return PTR_ERR(qcom_pcie->phy_clk);
+ }
+
+ qcom_pcie->bus_clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(qcom_pcie->bus_clk)) {
+ dev_err(&pdev->dev, "Failed to get pcie core clock\n");
+ return PTR_ERR(qcom_pcie->bus_clk);
+ }
+
+ qcom_pcie->axi_reset = devm_reset_control_get(&pdev->dev, "axi");
+ if (IS_ERR(qcom_pcie->axi_reset)) {
+ dev_err(&pdev->dev, "Failed to get axi reset\n");
+ return PTR_ERR(qcom_pcie->axi_reset);
+ }
+
+ qcom_pcie->ahb_reset = devm_reset_control_get(&pdev->dev, "ahb");
+ if (IS_ERR(qcom_pcie->ahb_reset)) {
+ dev_err(&pdev->dev, "Failed to get ahb reset\n");
+ return PTR_ERR(qcom_pcie->ahb_reset);
+ }
+
+ qcom_pcie->por_reset = devm_reset_control_get(&pdev->dev, "por");
+ if (IS_ERR(qcom_pcie->por_reset)) {
+ dev_err(&pdev->dev, "Failed to get por reset\n");
+ return PTR_ERR(qcom_pcie->por_reset);
+ }
+
+ qcom_pcie->pci_reset = devm_reset_control_get(&pdev->dev, "pci");
+ if (IS_ERR(qcom_pcie->pci_reset)) {
+ dev_err(&pdev->dev, "Failed to get pci reset\n");
+ return PTR_ERR(qcom_pcie->pci_reset);
+ }
+
+ qcom_pcie->phy_reset = devm_reset_control_get(&pdev->dev, "phy");
+ if (IS_ERR(qcom_pcie->phy_reset)) {
+ dev_err(&pdev->dev, "Failed to get phy reset\n");
+ return PTR_ERR(qcom_pcie->phy_reset);
+ }
+
+ for (i = 0; i < 4; i++) {
+ qcom_pcie->irq_int[i] = platform_get_irq(pdev, i+1);
+ if (qcom_pcie->irq_int[i] < 0) {
+ dev_err(&pdev->dev, "failed to get irq resource\n");
+ return qcom_pcie->irq_int[i];
+ }
+ }
+
+ return 0;
+}
+
+static int qcom_pcie_probe(struct platform_device *pdev)
+{
+ unsigned long flags;
+ struct qcom_pcie *qcom_pcie;
+ struct hw_pci *hw;
+ int ret;
+ u32 val;
+
+ qcom_pcie = devm_kzalloc(&pdev->dev, sizeof(*qcom_pcie), GFP_KERNEL);
+ if (!qcom_pcie) {
+ dev_err(&pdev->dev, "no memory for qcom_pcie\n");
+ return -ENOMEM;
+ }
+ qcom_pcie->dev = &pdev->dev;
+
+ ret = qcom_pcie_parse_dt(qcom_pcie, pdev);
+ if (IS_ERR_VALUE(ret))
+ return ret;
+
+ qcom_pcie->cfg_base = devm_ioremap_resource(&pdev->dev,
+ &qcom_pcie->conf);
+ if (IS_ERR(qcom_pcie->cfg_base)) {
+ dev_err(&pdev->dev, "Failed to ioremap PCIe cfg space\n");
+ return PTR_ERR(qcom_pcie->cfg_base);
+ }
+
+ gpio_set_value(qcom_pcie->reset_gpio, 0);
+ usleep_range(10000, 15000);
+
+ /* enable power */
+ qcom_pcie_vreg_on(qcom_pcie);
+ /* assert PCIe PARF reset while powering the core */
+ reset_control_assert(qcom_pcie->ahb_reset);
+
+ /* enable clocks */
+ ret = clk_prepare_enable(qcom_pcie->iface_clk);
+ if (ret)
+ return ret;
+ ret = clk_prepare_enable(qcom_pcie->phy_clk);
+ if (ret)
+ return ret;
+ ret = clk_prepare_enable(qcom_pcie->bus_clk);
+ if (ret)
+ return ret;
+
+ /*
+ * de-assert PCIe PARF reset;
+ * wait 1us before accessing PARF registers
+ */
+ reset_control_deassert(qcom_pcie->ahb_reset);
+ udelay(1);
+
+ /* enable PCIe clocks and resets */
+ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_CTRL,
+ BIT(0), 0);
+
+ /* Set Tx Termination Offset */
+ val = qcom_parf_readl_relaxed(qcom_pcie, PCIE20_PARF_PHY_CTRL);
+ val |= PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(7);
+ qcom_parf_writel_relaxed(qcom_pcie, val, PCIE20_PARF_PHY_CTRL);
+
+ /* PARF programming */
+ qcom_parf_writel_relaxed(qcom_pcie,
+ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
+ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
+ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
+ PCIE20_PARF_PCS_DEEMPH);
+ qcom_parf_writel_relaxed(qcom_pcie,
+ PCIE20_PARF_PCS_SWING_TX_SWING_FULL(0x78) |
+ PCIE20_PARF_PCS_SWING_TX_SWING_LOW(0x78),
+ PCIE20_PARF_PCS_SWING);
+ qcom_parf_writel_relaxed(qcom_pcie, (4<<24), PCIE20_PARF_CONFIG_BITS);
+ /* ensure that hardware registers the PARF configuration */
+ wmb();
+
+ /* enable reference clock */
+ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_REFCLK,
+ qcom_pcie->ext_phy_ref_clk ? 0 : BIT(12),
+ BIT(16));
+
+ /* ensure that access is enabled before proceeding */
+ wmb();
+
+ /* de-assert PICe PHY, Core, POR and AXI clk domain resets */
+ reset_control_deassert(qcom_pcie->phy_reset);
+ reset_control_deassert(qcom_pcie->pci_reset);
+ reset_control_deassert(qcom_pcie->por_reset);
+ reset_control_deassert(qcom_pcie->axi_reset);
+
+ /* wait 150ms for clock acquisition */
+ usleep_range(10000, 15000);
+
+ /* de-assert PCIe reset link to bring EP out of reset */
+ gpio_set_value(qcom_pcie->reset_gpio, 1);
+ usleep_range(10000, 15000);
+
+ /* enable link training */
+ val = qcom_elbi_readl_relaxed(qcom_pcie, PCIE20_ELBI_SYS_CTRL);
+ val |= PCIE20_ELBI_SYS_CTRL_LTSSM_EN;
+ qcom_elbi_writel_relaxed(qcom_pcie, val, PCIE20_ELBI_SYS_CTRL);
+ wmb();
+
+ /* poll for link to come up for upto 100ms */
+ ret = readl_poll_timeout(
+ (qcom_pcie->dwc_base + PCIE20_CAP_LINKCTRLSTATUS),
+ val, (val & BIT(29)), 10000, 100000);
+
+ dev_info(&pdev->dev, "link initialized %d\n", ret);
+
+ qcom_pcie_config_controller(qcom_pcie);
+
+ platform_set_drvdata(pdev, qcom_pcie);
+
+ spin_lock_irqsave(&qcom_hw_pci_lock, flags);
+ qcom_hw_pci[nr_controllers].private_data = (void **)&qcom_pcie;
+ hw = &qcom_hw_pci[nr_controllers];
+
+#ifdef CONFIG_PCI_MSI
+ hw->msi_ctrl = &qcom_pcie->msi.chip;
+#endif
+ nr_controllers++;
+ spin_unlock_irqrestore(&qcom_hw_pci_lock, flags);
+
+ ///pci_common_init(hw);
+ pci_common_init_dev(&pdev->dev, hw);
+
+ msm_pcie_msi_init(qcom_pcie, pdev);
+ return 0;
+}
+
+static int __exit qcom_pcie_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct of_device_id qcom_pcie_match[] = {
+ { .compatible = "qcom,pcie-ipq8064", },
+ {}
+};
+
+static struct platform_driver qcom_pcie_driver = {
+ .probe = qcom_pcie_probe,
+ .remove = qcom_pcie_remove,
+ .driver = {
+ .name = "qcom_pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_pcie_match,
+ },
+};
+
+static int qcom_pcie_init(void)
+{
+ return platform_driver_register(&qcom_pcie_driver);
+}
+subsys_initcall_sync(qcom_pcie_init);
+
+/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */
+static void msm_pcie_fixup_early(struct pci_dev *dev)
+{
+ if (dev->hdr_type == 1)
+ dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8);
+}
+DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, msm_pcie_fixup_early);
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index ea575f60f001..caeb402d9d50 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -68,4 +68,15 @@ config PINCTRL_QCOM_SPMI_PMIC
which are using SPMI for communication with SoC. Example PMIC's
devices are pm8841, pm8941 and pma8084.
+config PINCTRL_SSBI_PMIC
+ tristate "Qualcomm SSBI PMIC pin controller driver"
+ depends on GPIOLIB && OF && MFD_PM8921_CORE
+ select PINCONF
+ select PINMUX
+ select GENERIC_PINCONF
+ help
+ This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+ Qualcomm GPIO blocks found in the pm8018, pm8038, pm8058, pm8917 and
+ pm8921 pmics from Qualcomm.
+
endif
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 68958702917d..b429c27348d9 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PINCTRL_MSM8X74) += pinctrl-msm8x74.o
obj-$(CONFIG_PINCTRL_MSM8916) += pinctrl-msm8916.o
obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o
obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o
+obj-$(CONFIG_PINCTRL_SSBI_PMIC) += pinctrl-ssbi-pmic.o
diff --git a/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
new file mode 100644
index 000000000000..e02145e11baf
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-ssbi-pmic.c
@@ -0,0 +1,875 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB.
+ * Copyright (c) 2013, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8921-core.h>
+
+#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
+
+#include "../core.h"
+#include "../pinconf.h"
+#include "../pinctrl-utils.h"
+
+/* direction */
+#define PM8XXX_GPIO_DIR_OUT BIT(0)
+#define PM8XXX_GPIO_DIR_IN BIT(1)
+
+/* output buffer */
+#define PM8XXX_GPIO_PUSH_PULL 0
+#define PM8XXX_GPIO_OPEN_DRAIN 1
+
+/* bias */
+#define PM8XXX_GPIO_BIAS_PU_30 0
+#define PM8XXX_GPIO_BIAS_PU_1P5 1
+#define PM8XXX_GPIO_BIAS_PU_31P5 2
+#define PM8XXX_GPIO_BIAS_PU_1P5_30 3
+#define PM8XXX_GPIO_BIAS_PD 4
+#define PM8XXX_GPIO_BIAS_NP 5
+
+/* GPIO registers */
+#define SSBI_REG_ADDR_GPIO_BASE 0x150
+#define SSBI_REG_ADDR_GPIO(n) (SSBI_REG_ADDR_GPIO_BASE + n)
+
+#define PM8XXX_GPIO_MODE_ENABLE BIT(0)
+#define PM8XXX_GPIO_WRITE BIT(7)
+
+#define PM8XXX_MAX_GPIOS 44
+
+/* Qualcomm specific pin configurations */
+#define PM8XXX_PINCONF_PULL_UP (PIN_CONFIG_END + 1)
+#define PM8XXX_PINCONF_STRENGTH (PIN_CONFIG_END + 2)
+
+struct pm8xxx_pinbindings {
+ const char *property;
+ unsigned param;
+ u32 default_value;
+};
+static struct pm8xxx_pinbindings pm8xxx_pinbindings[] = {
+ /* PMIC_GPIO_PULL_UP_30... */
+ {"qcom,pull-up-strength", PM8XXX_PINCONF_PULL_UP, 0},
+ /* PMIC_GPIO_STRENGTH_NO... */
+ {"qcom,drive-strength", PM8XXX_PINCONF_STRENGTH, 0},
+};
+
+struct pm8xxx_gpio_pin {
+ int irq;
+
+ u8 power_source;
+ u8 direction;
+ u8 output_buffer;
+ u8 output_value;
+ u8 bias;
+ u8 output_strength;
+ u8 disable;
+ u8 function;
+ u8 non_inverted;
+};
+
+struct pm8xxx_gpio_data {
+ int ngpio;
+};
+
+struct pm8xxx_gpio {
+ struct device *dev;
+ struct regmap *regmap;
+ struct pinctrl_dev *pctrl;
+ struct gpio_chip chip;
+
+ const struct pm8xxx_gpio_data *data;
+
+ struct pm8xxx_gpio_pin pins[PM8XXX_MAX_GPIOS];
+};
+
+static inline struct pm8xxx_gpio *to_pm8xxx_gpio(struct gpio_chip *chip)
+{
+ return container_of(chip, struct pm8xxx_gpio, chip);
+};
+
+static const char * const pm8xxx_gpio_groups[PM8XXX_MAX_GPIOS] = {
+ "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", "gpio8",
+ "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", "gpio15",
+ "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
+ "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", "gpio29",
+ "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
+ "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
+ "gpio44",
+};
+
+static const char * const pm8xxx_gpio_functions[] = {
+ PMIC_GPIO_FUNC_NORMAL, PMIC_GPIO_FUNC_PAIRED,
+ PMIC_GPIO_FUNC_FUNC1, PMIC_GPIO_FUNC_FUNC2,
+ PMIC_GPIO_FUNC_DTEST1, PMIC_GPIO_FUNC_DTEST2,
+ PMIC_GPIO_FUNC_DTEST3, PMIC_GPIO_FUNC_DTEST4,
+};
+
+static int pm8xxx_gpio_read(struct pm8xxx_gpio *pctrl, int pin, int bank)
+{
+ int reg = SSBI_REG_ADDR_GPIO(pin);
+ unsigned int val = bank << 4;
+ int ret;
+
+ ret = regmap_write(pctrl->regmap, reg, val);
+ if (ret) {
+ dev_err(pctrl->dev,
+ "failed to select bank %d of pin %d\n", bank, pin);
+ return ret;
+ }
+
+ ret = regmap_read(pctrl->regmap, reg, &val);
+ if (ret) {
+ dev_err(pctrl->dev,
+ "failed to read register %d of pin %d\n", bank, pin);
+ return ret;
+ }
+
+ return val;
+}
+
+static int pm8xxx_gpio_write(struct pm8xxx_gpio *pctrl,
+ int pin, int bank, u8 val)
+{
+ int ret;
+
+ val |= PM8XXX_GPIO_WRITE;
+ val |= bank << 4;
+
+ ret = regmap_write(pctrl->regmap, SSBI_REG_ADDR_GPIO(pin), val);
+ if (ret)
+ dev_err(pctrl->dev, "failed to write register\n");
+
+ return ret;
+}
+
+static int pm8xxx_gpio_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ return pctrl->data->ngpio;
+}
+
+static const char *pm8xxx_gpio_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned group)
+{
+ return pm8xxx_gpio_groups[group];
+}
+
+static int pm8xxx_parse_dt_config(struct device *dev, struct device_node *np,
+ unsigned long **configs, unsigned int *nconfigs)
+{
+ struct pm8xxx_pinbindings *par;
+ unsigned long cfg[ARRAY_SIZE(pm8xxx_pinbindings)];
+ unsigned int ncfg = 0;
+ int ret, idx;
+ u32 val;
+
+ if (!np)
+ return -EINVAL;
+
+ for (idx = 0; idx < ARRAY_SIZE(pm8xxx_pinbindings); idx++) {
+ par = &pm8xxx_pinbindings[idx];
+ ret = of_property_read_u32(np, par->property, &val);
+
+ /* property not found */
+ if (ret == -EINVAL)
+ continue;
+
+ /* use default value, when no value is specified */
+ if (ret)
+ val = par->default_value;
+
+ dev_dbg(dev, "found %s with value %u\n", par->property, val);
+ cfg[ncfg] = pinconf_to_config_packed(par->param, val);
+ ncfg++;
+ }
+
+ ret = 0;
+
+ /* no configs found at qchip->npads */
+ if (ncfg == 0) {
+ *configs = NULL;
+ *nconfigs = 0;
+ goto out;
+ }
+
+ /*
+ * Now limit the number of configs to the real number of
+ * found properties.
+ */
+ *configs = kcalloc(ncfg, sizeof(unsigned long), GFP_KERNEL);
+ if (!*configs) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(*configs, cfg, ncfg * sizeof(unsigned long));
+ *nconfigs = ncfg;
+
+out:
+ return ret;
+}
+
+static int pm8xxx_dt_subnode_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np,
+ struct pinctrl_map **map,
+ unsigned *reserv, unsigned *nmaps,
+ enum pinctrl_map_type type)
+{
+ unsigned long *configs = NULL;
+ unsigned num_configs = 0;
+ struct property *prop;
+ const char *group;
+ int ret;
+
+ ret = pm8xxx_parse_dt_config(pctldev->dev, np, &configs, &num_configs);
+ if (ret < 0)
+ return ret;
+
+ if (!num_configs)
+ return 0;
+
+ ret = of_property_count_strings(np, "pins");
+ if (ret < 0)
+ goto exit;
+
+ ret = pinctrl_utils_reserve_map(pctldev, map, reserv,
+ nmaps, ret);
+ if (ret < 0)
+ goto exit;
+
+ of_property_for_each_string(np, "pins", prop, group) {
+ ret = pinctrl_utils_add_map_configs(pctldev, map,
+ reserv, nmaps, group, configs,
+ num_configs, type);
+ if (ret < 0)
+ break;
+ }
+exit:
+ kfree(configs);
+ return ret;
+}
+
+static int pm8xxx_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map,
+ unsigned *nmaps)
+{
+ struct device_node *np;
+ enum pinctrl_map_type type;
+ unsigned reserv;
+ int ret;
+
+ ret = 0;
+ *map = NULL;
+ *nmaps = 0;
+ reserv = 0;
+ type = PIN_MAP_TYPE_CONFIGS_GROUP;
+
+ for_each_child_of_node(np_config, np) {
+
+ ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map,
+ &reserv, nmaps, type);
+ if (ret)
+ break;
+
+ ret = pm8xxx_dt_subnode_to_map(pctldev, np, map, &reserv,
+ nmaps, type);
+ if (ret)
+ break;
+ }
+
+ if (ret < 0)
+ pinctrl_utils_dt_free_map(pctldev, *map, *nmaps);
+
+ return ret;
+}
+
+static const struct pinctrl_ops pm8xxx_gpio_pinctrl_ops = {
+ .get_groups_count = pm8xxx_gpio_get_groups_count,
+ .get_group_name = pm8xxx_gpio_get_group_name,
+ .dt_node_to_map = pm8xxx_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_dt_free_map,
+};
+
+static int pm8xxx_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(pm8xxx_gpio_functions);
+}
+
+static const char *pm8xxx_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ return pm8xxx_gpio_functions[function];
+}
+
+static int pm8xxx_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned function,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+
+ *groups = pm8xxx_gpio_groups;
+ *num_groups = pctrl->data->ngpio;
+ return 0;
+}
+
+static int pm8xxx_pinmux_enable(struct pinctrl_dev *pctldev,
+ unsigned function,
+ unsigned group)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[group];
+ u8 val;
+
+ pin->function = function;
+ val = pin->function << 1;
+
+ pm8xxx_gpio_write(pctrl, group, 4, val);
+
+ return 0;
+}
+
+static const struct pinmux_ops pm8xxx_pinmux_ops = {
+ .get_functions_count = pm8xxx_get_functions_count,
+ .get_function_name = pm8xxx_get_function_name,
+ .get_function_groups = pm8xxx_get_function_groups,
+ .set_mux = pm8xxx_pinmux_enable,
+};
+
+static int pm8xxx_gpio_config_get(struct pinctrl_dev *pctldev,
+ unsigned int offset,
+ unsigned long *config)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+ unsigned param = pinconf_to_config_param(*config);
+ unsigned arg;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ arg = pin->bias == PM8XXX_GPIO_BIAS_NP;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ arg = pin->bias == PM8XXX_GPIO_BIAS_PD;
+ break;
+ case PM8XXX_PINCONF_PULL_UP:
+ if (pin->bias >= PM8XXX_GPIO_BIAS_PU_30 &&
+ pin->bias <= PM8XXX_GPIO_BIAS_PU_1P5_30)
+ arg = PMIC_GPIO_PULL_UP_30 + pin->bias;
+ else
+ arg = 0;
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ arg = pin->disable;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ arg = pin->direction == PM8XXX_GPIO_DIR_IN;
+ break;
+ case PIN_CONFIG_OUTPUT:
+ arg = pin->output_value;
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ arg = pin->power_source;
+ break;
+ case PM8XXX_PINCONF_STRENGTH:
+ arg = pin->output_strength;
+ break;
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ arg = pin->output_buffer == PM8XXX_GPIO_PUSH_PULL;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ arg = pin->output_buffer == PM8XXX_GPIO_OPEN_DRAIN;
+ break;
+ default:
+ dev_err(pctrl->dev,
+ "unsupported config parameter: %x\n",
+ param);
+ return -EINVAL;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+
+ return 0;
+}
+
+static int pm8xxx_gpio_config_set(struct pinctrl_dev *pctldev,
+ unsigned int offset,
+ unsigned long *configs,
+ unsigned num_configs)
+{
+ struct pm8xxx_gpio *pctrl = pinctrl_dev_get_drvdata(pctldev);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+ unsigned param;
+ unsigned arg;
+ unsigned i;
+ u8 banks = 0;
+ u8 val;
+
+ for (i = 0; i < num_configs; i++) {
+ param = pinconf_to_config_param(configs[i]);
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ pin->bias = PM8XXX_GPIO_BIAS_NP;
+ banks |= BIT(2);
+ pin->disable = 0;
+ banks |= BIT(3);
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ pin->bias = PM8XXX_GPIO_BIAS_PD;
+ banks |= BIT(2);
+ pin->disable = 0;
+ banks |= BIT(3);
+ break;
+ case PM8XXX_PINCONF_PULL_UP:
+ if (arg < PMIC_GPIO_PULL_UP_30 ||
+ arg > PMIC_GPIO_PULL_UP_1P5_30) {
+ dev_err(pctrl->dev, "invalid pull-up level\n");
+ return -EINVAL;
+ }
+ pin->bias = arg - PM8XXX_GPIO_BIAS_PU_30;
+ banks |= BIT(2);
+ pin->disable = 0;
+ banks |= BIT(3);
+ break;
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ pin->disable = 1;
+ banks |= BIT(3);
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ pin->direction = PM8XXX_GPIO_DIR_IN;
+ banks |= BIT(1);
+ break;
+ case PIN_CONFIG_OUTPUT:
+ pin->direction = PM8XXX_GPIO_DIR_OUT;
+ pin->output_value = !!arg;
+ banks |= BIT(1);
+ break;
+ case PIN_CONFIG_POWER_SOURCE:
+ pin->power_source = arg;
+ banks |= BIT(0);
+ break;
+ case PM8XXX_PINCONF_STRENGTH:
+ if (arg > PMIC_GPIO_STRENGTH_LOW) {
+ dev_err(pctrl->dev, "invalid drive strength\n");
+ return -EINVAL;
+ }
+ pin->output_strength = arg;
+ banks |= BIT(3);
+ break;
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ pin->output_buffer = PM8XXX_GPIO_PUSH_PULL;
+ banks |= BIT(1);
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ pin->output_buffer = PM8XXX_GPIO_OPEN_DRAIN;
+ banks |= BIT(1);
+ break;
+ default:
+ dev_err(pctrl->dev,
+ "unsupported config parameter: %x\n",
+ param);
+ return -EINVAL;
+ }
+ }
+
+ if (banks & BIT(0))
+ pm8xxx_gpio_write(pctrl, offset, 0, pin->power_source << 1 |
+ PM8XXX_GPIO_MODE_ENABLE);
+
+ if (banks & BIT(1)) {
+ val = pin->direction << 2;
+ val |= pin->output_buffer << 1;
+ val |= pin->output_value;
+ pm8xxx_gpio_write(pctrl, offset, 1, val);
+ }
+
+ if (banks & BIT(2)) {
+ val = pin->bias << 1;
+ pm8xxx_gpio_write(pctrl, offset, 2, val);
+ }
+
+ if (banks & BIT(3)) {
+ val = pin->output_strength << 2;
+ val |= pin->disable;
+ pm8xxx_gpio_write(pctrl, offset, 3, val);
+ }
+
+ if (banks & BIT(4)) {
+ val = pin->function << 1;
+ pm8xxx_gpio_write(pctrl, offset, 4, val);
+ }
+
+ if (banks & BIT(5)) {
+ val = 0;
+ if (pin->non_inverted)
+ val |= BIT(3);
+ pm8xxx_gpio_write(pctrl, offset, 5, val);
+ }
+
+ return 0;
+}
+
+static const struct pinconf_ops pm8xxx_gpio_pinconf_ops = {
+ .pin_config_group_get = pm8xxx_gpio_config_get,
+ .pin_config_group_set = pm8xxx_gpio_config_set,
+};
+
+static struct pinctrl_desc pm8xxx_gpio_desc = {
+ .pctlops = &pm8xxx_gpio_pinctrl_ops,
+ .pmxops = &pm8xxx_pinmux_ops,
+ .confops = &pm8xxx_gpio_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+static int pm8xxx_gpio_direction_input(struct gpio_chip *chip,
+ unsigned offset)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+ u8 val;
+
+ pin->direction = PM8XXX_GPIO_DIR_IN;
+ val = pin->direction << 2;
+
+ pm8xxx_gpio_write(pctrl, offset, 1, val);
+
+ return 0;
+}
+
+static int pm8xxx_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset,
+ int value)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+ u8 val;
+
+ pin->direction = PM8XXX_GPIO_DIR_OUT;
+ pin->output_value = !!value;
+
+ val = pin->direction << 2;
+ val |= pin->output_buffer << 1;
+ val |= pin->output_value;
+
+ pm8xxx_gpio_write(pctrl, offset, 1, val);
+
+ return 0;
+}
+
+static int pm8xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+
+ if (pin->direction == PM8XXX_GPIO_DIR_OUT)
+ return pin->output_value;
+
+ return pm8xxx_read_irq_status(pin->irq);
+}
+
+static void pm8xxx_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(gc);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+ u8 val;
+
+ pin->output_value = !!value;
+
+ val = pin->direction << 2;
+ val |= pin->output_buffer << 1;
+ val |= pin->output_value;
+
+ pm8xxx_gpio_write(pctrl, offset, 1, val);
+}
+
+static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset - 1];
+
+ return pin->irq;
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+static void pm8xxx_gpio_dbg_show_one(struct seq_file *s,
+ struct pinctrl_dev *pctldev,
+ struct gpio_chip *chip,
+ unsigned offset,
+ unsigned gpio)
+{
+ struct pm8xxx_gpio *pctrl = to_pm8xxx_gpio(chip);
+ struct pm8xxx_gpio_pin *pin = &pctrl->pins[offset];
+
+ static const char * const directions[] = {
+ "off", "out", "in", "both"
+ };
+ static const char * const biases[] = {
+ "pull-up 30uA", "pull-up 1.5uA", "pull-up 31.5uA",
+ "pull-up 1.5uA + 30uA boost", "pull-down 10uA", "no pull"
+ };
+ static const char * const buffer_types[] = {
+ "push-pull", "open-drain"
+ };
+ static const char * const strengths[] = {
+ "no", "high", "medium", "low"
+ };
+
+ seq_printf(s, " gpio%-2d:", offset + 1);
+ if (pin->disable) {
+ seq_puts(s, " ---");
+ } else {
+ seq_printf(s, " %-4s", directions[pin->direction]);
+ seq_printf(s, " %-7s", pm8xxx_gpio_functions[pin->function]);
+ seq_printf(s, " VIN%d", pin->power_source);
+ seq_printf(s, " %-27s", biases[pin->bias]);
+ seq_printf(s, " %-10s", buffer_types[pin->output_buffer]);
+ seq_printf(s, " %-4s", pin->output_value ? "high" : "low");
+ seq_printf(s, " %-7s", strengths[pin->output_strength]);
+ if (!pin->non_inverted)
+ seq_puts(s, " inverted");
+ }
+}
+
+static void pm8xxx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+ unsigned gpio = chip->base;
+ unsigned i;
+
+ for (i = 0; i < chip->ngpio; i++, gpio++) {
+ pm8xxx_gpio_dbg_show_one(s, NULL, chip, i, gpio);
+ seq_puts(s, "\n");
+ }
+}
+
+#else
+#define msm_gpio_dbg_show NULL
+#endif
+
+static struct gpio_chip pm8xxx_gpio_template = {
+ .direction_input = pm8xxx_gpio_direction_input,
+ .direction_output = pm8xxx_gpio_direction_output,
+ .get = pm8xxx_gpio_get,
+ .set = pm8xxx_gpio_set,
+ .to_irq = pm8xxx_gpio_to_irq,
+ .dbg_show = pm8xxx_gpio_dbg_show,
+ .owner = THIS_MODULE,
+};
+
+static int pm8xxx_gpio_populate(struct pm8xxx_gpio *pctrl)
+{
+ struct pm8xxx_gpio_pin *pin;
+ int val;
+ int i;
+
+ for (i = 0; i < pctrl->data->ngpio; i++) {
+ pin = &pctrl->pins[i];
+
+ val = pm8xxx_gpio_read(pctrl, i, 0);
+ if (val < 0)
+ return val;
+
+ pin->power_source = (val >> 1) & 0x7;
+
+ val = pm8xxx_gpio_read(pctrl, i, 1);
+ if (val < 0)
+ return val;
+
+ pin->direction = (val >> 2) & 0x3;
+ pin->output_buffer = !!(val & BIT(1));
+ pin->output_value = val & BIT(0);
+
+ val = pm8xxx_gpio_read(pctrl, i, 2);
+ if (val < 0)
+ return val;
+
+ pin->bias = (val >> 1) & 0x7;
+
+ val = pm8xxx_gpio_read(pctrl, i, 3);
+ if (val < 0)
+ return val;
+
+ pin->output_strength = (val >> 2) & 0x3;
+ pin->disable = val & BIT(0);
+
+ val = pm8xxx_gpio_read(pctrl, i, 4);
+ if (val < 0)
+ return val;
+
+ pin->function = (val >> 1) & 0x7;
+
+ val = pm8xxx_gpio_read(pctrl, i, 5);
+ if (val < 0)
+ return val;
+
+ pin->non_inverted = !!(val & BIT(3));
+ }
+
+ return 0;
+}
+
+static const struct pm8xxx_gpio_data pm8018_gpio_data = {
+ .ngpio = 6,
+};
+
+static const struct pm8xxx_gpio_data pm8038_gpio_data = {
+ .ngpio = 12,
+};
+
+static const struct pm8xxx_gpio_data pm8058_gpio_data = {
+ .ngpio = 40,
+};
+static const struct pm8xxx_gpio_data pm8917_gpio_data = {
+ .ngpio = 38,
+};
+
+static const struct pm8xxx_gpio_data pm8921_gpio_data = {
+ .ngpio = 44,
+};
+
+static const struct of_device_id pm8xxx_gpio_of_match[] = {
+ { .compatible = "qcom,pm8018-gpio", .data = &pm8018_gpio_data },
+ { .compatible = "qcom,pm8038-gpio", .data = &pm8038_gpio_data },
+ { .compatible = "qcom,pm8058-gpio", .data = &pm8058_gpio_data },
+ { .compatible = "qcom,pm8917-gpio", .data = &pm8917_gpio_data },
+ { .compatible = "qcom,pm8921-gpio", .data = &pm8921_gpio_data },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match);
+
+static int pm8xxx_gpio_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct pm8xxx_gpio *pctrl;
+ int ret;
+ int i;
+
+ match = of_match_node(pm8xxx_gpio_of_match, pdev->dev.of_node);
+ if (!match)
+ return -ENXIO;
+
+ pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL);
+ if (!pctrl)
+ return -ENOMEM;
+
+ pctrl->dev = &pdev->dev;
+ pctrl->data = match->data;
+
+ BUG_ON(pctrl->data->ngpio > PM8XXX_MAX_GPIOS);
+
+ pctrl->chip = pm8xxx_gpio_template;
+ pctrl->chip.base = -1;
+ pctrl->chip.dev = &pdev->dev;
+ pctrl->chip.of_node = pdev->dev.of_node;
+ pctrl->chip.label = dev_name(pctrl->dev);
+ pctrl->chip.ngpio = pctrl->data->ngpio;
+
+ pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pctrl->regmap) {
+ dev_err(&pdev->dev, "parent regmap unavailable\n");
+ return -ENXIO;
+ }
+
+ for (i = 0; i < pctrl->data->ngpio; i++) {
+ ret = platform_get_irq(pdev, i);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "missing interrupts for pin %d\n", i);
+ return ret;
+ }
+
+ pctrl->pins[i].irq = ret;
+ }
+
+ ret = pm8xxx_gpio_populate(pctrl);
+ if (ret)
+ return ret;
+
+ pm8xxx_gpio_desc.name = dev_name(&pdev->dev);
+ pctrl->pctrl = pinctrl_register(&pm8xxx_gpio_desc, &pdev->dev, pctrl);
+ if (!pctrl->pctrl) {
+ dev_err(&pdev->dev, "couldn't register pm8xxx gpio driver\n");
+ return -ENODEV;
+ }
+
+ ret = gpiochip_add(&pctrl->chip);
+ if (ret) {
+ dev_err(&pdev->dev, "failed register gpiochip\n");
+ goto unregister_pinctrl;
+ }
+
+ ret = gpiochip_add_pin_range(&pctrl->chip,
+ dev_name(pctrl->dev),
+ 1, 0, pctrl->data->ngpio);
+ if (ret) {
+ dev_err(pctrl->dev, "failed to add pin range\n");
+ goto unregister_gpiochip;
+ }
+
+ platform_set_drvdata(pdev, pctrl);
+
+ dev_dbg(&pdev->dev, "Qualcomm pm8xxx gpio driver probed\n");
+
+ return 0;
+
+unregister_pinctrl:
+ pinctrl_unregister(pctrl->pctrl);
+
+unregister_gpiochip:
+ gpiochip_remove(&pctrl->chip);
+
+ return ret;
+}
+
+static int pm8xxx_gpio_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_gpio *pctrl = platform_get_drvdata(pdev);
+
+ gpiochip_remove(&pctrl->chip);
+
+ pinctrl_unregister(pctrl->pctrl);
+
+ return 0;
+}
+
+static struct platform_driver pm8xxx_gpio_driver = {
+ .driver = {
+ .name = "ssbi-pmic-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = pm8xxx_gpio_of_match,
+ },
+ .probe = pm8xxx_gpio_probe,
+ .remove = pm8xxx_gpio_remove,
+};
+
+static int pm8xxx_gpio_init(void)
+{
+ return platform_driver_register(&pm8xxx_gpio_driver);
+}
+subsys_initcall(pm8xxx_gpio_init);
+
+MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
+MODULE_DESCRIPTION("Qualcomm SSBI PMIC GPIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 7bd2c94f54a4..012fb37b3ba9 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -9,3 +9,13 @@ config QCOM_GSBI
functions for connecting the underlying serial UART, SPI, and I2C
devices to the output pins.
+config QCOM_SCM
+ bool
+
+config QCOM_PM
+ bool "Qualcomm Power Management"
+ depends on ARCH_QCOM
+ help
+ QCOM Platform specific power driver to manage cores and L2 low power
+ modes. It interface with various system drivers to put the cores in
+ low power modes.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 438901257ac1..4d4ff4a09b7d 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1 +1,4 @@
obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
+obj-$(CONFIG_QCOM_PM) += spm.o
+CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
+obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o scm-pas.o
diff --git a/arch/arm/mach-qcom/scm-boot.c b/drivers/soc/qcom/scm-boot.c
index e8ff7beb6218..3b4abe2b7360 100644
--- a/arch/arm/mach-qcom/scm-boot.c
+++ b/drivers/soc/qcom/scm-boot.c
@@ -18,8 +18,25 @@
#include <linux/module.h>
#include <linux/slab.h>
-#include "scm.h"
-#include "scm-boot.h"
+#include <soc/qcom/scm.h>
+#include <soc/qcom/scm-boot.h>
+
+#define SCM_FLAG_WARMBOOT_CPU0 0x04
+#define SCM_FLAG_WARMBOOT_CPU1 0x02
+#define SCM_FLAG_WARMBOOT_CPU2 0x10
+#define SCM_FLAG_WARMBOOT_CPU3 0x40
+
+struct scm_warmboot {
+ int flag;
+ void *entry;
+};
+
+static struct scm_warmboot scm_flags[] = {
+ { .flag = SCM_FLAG_WARMBOOT_CPU0 },
+ { .flag = SCM_FLAG_WARMBOOT_CPU1 },
+ { .flag = SCM_FLAG_WARMBOOT_CPU2 },
+ { .flag = SCM_FLAG_WARMBOOT_CPU3 },
+};
/*
* Set the cold/warm boot address for one of the CPU cores.
@@ -37,3 +54,21 @@ int scm_set_boot_addr(u32 addr, int flags)
&cmd, sizeof(cmd), NULL, 0);
}
EXPORT_SYMBOL(scm_set_boot_addr);
+
+int scm_set_warm_boot_addr(void *entry, int cpu)
+{
+ int ret;
+
+ /*
+ * Reassign only if we are switching from hotplug entry point
+ * to cpuidle entry point or vice versa.
+ */
+ if (entry == scm_flags[cpu].entry)
+ return 0;
+
+ ret = scm_set_boot_addr(virt_to_phys(entry), scm_flags[cpu].flag);
+ if (!ret)
+ scm_flags[cpu].entry = entry;
+
+ return ret;
+}
diff --git a/drivers/soc/qcom/scm-pas.c b/drivers/soc/qcom/scm-pas.c
new file mode 100644
index 000000000000..9b32cc478434
--- /dev/null
+++ b/drivers/soc/qcom/scm-pas.c
@@ -0,0 +1,112 @@
+/* Copyright (c) 2010-2012, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 pr_fmt(fmt) "scm-pas: " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+
+#include <soc/qcom/scm.h>
+#include <asm/cacheflush.h>
+#include "scm-pas.h"
+
+#define PAS_INIT_IMAGE_CMD 1
+#define PAS_MEM_SETUP_CMD 2
+#define PAS_AUTH_AND_RESET_CMD 5
+#define PAS_SHUTDOWN_CMD 6
+#define PAS_IS_SUPPORTED_CMD 7
+
+
+int pas_init_image(enum pas_id id, const u8 *metadata, size_t size)
+{
+ int ret;
+ struct pas_init_image_req {
+ u32 proc;
+ u32 image_addr;
+ } request;
+ u32 scm_ret = 0;
+ void *mdata_buf;
+ dma_addr_t mdata_phys;
+ DEFINE_DMA_ATTRS(attrs);
+
+
+ dma_set_attr(DMA_ATTR_WRITE_BARRIER, &attrs);
+ mdata_buf = dma_alloc_attrs(NULL, size, &mdata_phys, GFP_KERNEL,
+ &attrs);
+ if (!mdata_buf) {
+ pr_err("Allocation for metadata failed.\n");
+ return -ENOMEM;
+ }
+
+ memcpy(mdata_buf, metadata, size);
+
+ request.proc = id;
+ request.image_addr = mdata_phys;
+
+ ret = scm_call(SCM_SVC_PIL, PAS_INIT_IMAGE_CMD, &request,
+ sizeof(request), &scm_ret, sizeof(scm_ret));
+
+ dma_free_attrs(NULL, size, mdata_buf, mdata_phys, &attrs);
+
+ if (ret)
+ return ret;
+ return scm_ret;
+}
+EXPORT_SYMBOL(pas_init_image);
+
+int pas_auth_and_reset(enum pas_id id)
+{
+ int ret;
+ u32 proc = id, scm_ret = 0;
+ ret = scm_call(SCM_SVC_PIL, PAS_AUTH_AND_RESET_CMD, &proc,
+ sizeof(proc), &scm_ret, sizeof(scm_ret));
+ if (ret)
+ scm_ret = ret;
+
+ return scm_ret;
+}
+EXPORT_SYMBOL(pas_auth_and_reset);
+
+int pas_shutdown(enum pas_id id)
+{
+ int ret;
+ u32 proc = id, scm_ret = 0;
+
+ ret = scm_call(SCM_SVC_PIL, PAS_SHUTDOWN_CMD, &proc, sizeof(proc),
+ &scm_ret, sizeof(scm_ret));
+ if (ret)
+ return ret;
+
+ return scm_ret;
+}
+EXPORT_SYMBOL(pas_shutdown);
+
+int pas_supported(enum pas_id id)
+{
+ int ret;
+ u32 periph = id, ret_val = 0;
+
+ if (scm_is_call_available(SCM_SVC_PIL, PAS_IS_SUPPORTED_CMD) <= 0)
+ return 0;
+
+ ret = scm_call(SCM_SVC_PIL, PAS_IS_SUPPORTED_CMD, &periph,
+ sizeof(periph), &ret_val, sizeof(ret_val));
+ if (ret)
+ return ret;
+
+ return ret_val;
+}
+EXPORT_SYMBOL(pas_supported);
diff --git a/drivers/soc/qcom/scm-pas.h b/drivers/soc/qcom/scm-pas.h
new file mode 100644
index 000000000000..86b5b30f5bea
--- /dev/null
+++ b/drivers/soc/qcom/scm-pas.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2010-2012, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __MSM_SCM_PAS_H
+#define __MSM_SCM_PAS_H
+
+enum pas_id {
+ PAS_MODEM,
+ PAS_Q6,
+ PAS_DSPS,
+ PAS_TZAPPS,
+ PAS_MODEM_SW,
+ PAS_MODEM_FW,
+ PAS_WCNSS,
+ PAS_SECAPP,
+ PAS_GSS,
+ PAS_VIDC,
+};
+
+extern int pas_init_image(enum pas_id id, const u8 *metadata, size_t size);
+extern int pas_auth_and_reset(enum pas_id id);
+extern int pas_shutdown(enum pas_id id);
+extern int pas_supported(enum pas_id id);
+#endif
diff --git a/arch/arm/mach-qcom/scm.c b/drivers/soc/qcom/scm.c
index 1d9cf18c7091..4516847e50cf 100644
--- a/arch/arm/mach-qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -22,10 +22,11 @@
#include <linux/errno.h>
#include <linux/err.h>
+#include <soc/qcom/scm.h>
+
#include <asm/outercache.h>
#include <asm/cacheflush.h>
-#include "scm.h"
#define SCM_ENOMEM -5
#define SCM_EOPNOTSUPP -4
@@ -152,9 +153,13 @@ static inline void *scm_get_response_buffer(const struct scm_response *rsp)
return (void *)rsp + le32_to_cpu(rsp->buf_offset);
}
-static int scm_remap_error(int err)
+static int scm_remap_error(const struct scm_command *cmd, int err)
{
- pr_err("scm_call failed with error code %d\n", err);
+ u32 svc_id = cmd->id >> 10;
+ u32 cmd_id = cmd->id & GENMASK(10, 0);
+
+ pr_err("scm_call for svc_id %d & cmd_id %d failed with error code %d\n",
+ svc_id, cmd_id, err);
switch (err) {
case SCM_ERROR:
return -EIO;
@@ -207,7 +212,7 @@ static int __scm_call(const struct scm_command *cmd)
ret = smc(cmd_addr);
if (ret < 0)
- ret = scm_remap_error(ret);
+ ret = scm_remap_error(cmd, ret);
return ret;
}
@@ -289,6 +294,79 @@ out:
}
EXPORT_SYMBOL(scm_call);
+#define SCM_CLASS_REGISTER (0x2 << 8)
+#define SCM_MASK_IRQS BIT(5)
+#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \
+ SCM_CLASS_REGISTER | \
+ SCM_MASK_IRQS | \
+ (n & 0xf))
+
+/**
+ * scm_call_atomic1() - Send an atomic SCM command with one argument
+ * @svc_id: service identifier
+ * @cmd_id: command identifier
+ * @arg1: first argument
+ *
+ * This shall only be used with commands that are guaranteed to be
+ * uninterruptable, atomic and SMP safe.
+ */
+s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
+{
+ int context_id;
+ register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1);
+ register u32 r1 asm("r1") = (u32)&context_id;
+ register u32 r2 asm("r2") = arg1;
+
+ asm volatile(
+ __asmeq("%0", "r0")
+ __asmeq("%1", "r0")
+ __asmeq("%2", "r1")
+ __asmeq("%3", "r2")
+#ifdef REQUIRES_SEC
+ ".arch_extension sec\n"
+#endif
+ "smc #0 @ switch to secure world\n"
+ : "=r" (r0)
+ : "r" (r0), "r" (r1), "r" (r2)
+ : "r3");
+ return r0;
+}
+EXPORT_SYMBOL(scm_call_atomic1);
+
+/**
+ * scm_call_atomic2() - Send an atomic SCM command with two arguments
+ * @svc_id: service identifier
+ * @cmd_id: command identifier
+ * @arg1: first argument
+ * @arg2: second argument
+ *
+ * This shall only be used with commands that are guaranteed to be
+ * uninterruptable, atomic and SMP safe.
+ */
+s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2)
+{
+ int context_id;
+ register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2);
+ register u32 r1 asm("r1") = (u32)&context_id;
+ register u32 r2 asm("r2") = arg1;
+ register u32 r3 asm("r3") = arg2;
+
+ asm volatile(
+ __asmeq("%0", "r0")
+ __asmeq("%1", "r0")
+ __asmeq("%2", "r1")
+ __asmeq("%3", "r2")
+ __asmeq("%4", "r3")
+#ifdef REQUIRES_SEC
+ ".arch_extension sec\n"
+#endif
+ "smc #0 @ switch to secure world\n"
+ : "=r" (r0)
+ : "r" (r0), "r" (r1), "r" (r2), "r" (r3));
+ return r0;
+}
+EXPORT_SYMBOL(scm_call_atomic2);
+
u32 scm_get_version(void)
{
int context_id;
@@ -324,3 +402,20 @@ u32 scm_get_version(void)
return version;
}
EXPORT_SYMBOL(scm_get_version);
+
+#define IS_CALL_AVAIL_CMD 1
+int scm_is_call_available(u32 svc_id, u32 cmd_id)
+{
+ int ret;
+ u32 svc_cmd = (svc_id << 10) | cmd_id;
+ u32 ret_val = 0;
+
+ ret = scm_call(SCM_SVC_INFO, IS_CALL_AVAIL_CMD, &svc_cmd,
+ sizeof(svc_cmd), &ret_val, sizeof(ret_val));
+ if (ret)
+ return ret;
+
+ return ret_val;
+}
+EXPORT_SYMBOL(scm_is_call_available);
+
diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c
new file mode 100644
index 000000000000..e6fbed081b47
--- /dev/null
+++ b/drivers/soc/qcom/spm.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2011-2014, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
+
+#include <asm/proc-fns.h>
+#include <asm/suspend.h>
+
+#include <soc/qcom/pm.h>
+#include <soc/qcom/pm.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/scm-boot.h>
+
+
+#define SCM_CMD_TERMINATE_PC 0x2
+#define SCM_FLUSH_FLAG_MASK 0x3
+#define SCM_L2_ON 0x0
+#define SCM_L2_OFF 0x1
+#define MAX_PMIC_DATA 2
+#define MAX_SEQ_DATA 64
+#define SPM_CTL_INDEX 0x7f
+#define SPM_CTL_INDEX_SHIFT 4
+#define SPM_CTL_EN BIT(0)
+
+enum spm_reg {
+ SPM_REG_CFG,
+ SPM_REG_SPM_CTL,
+ SPM_REG_DLY,
+ SPM_REG_PMIC_DLY,
+ SPM_REG_PMIC_DATA_0,
+ SPM_REG_PMIC_DATA_1,
+ SPM_REG_VCTL,
+ SPM_REG_SEQ_ENTRY,
+ SPM_REG_SPM_STS,
+ SPM_REG_PMIC_STS,
+ SPM_REG_NR,
+};
+
+struct spm_reg_data {
+ const u8 *reg_offset;
+ u32 spm_cfg;
+ u32 spm_dly;
+ u32 pmic_dly;
+ u32 pmic_data[MAX_PMIC_DATA];
+ u8 seq[MAX_SEQ_DATA];
+ u8 start_index[PM_SLEEP_MODE_NR];
+};
+
+struct spm_driver_data {
+ void __iomem *reg_base;
+ const struct spm_reg_data *reg_data;
+};
+
+static const u8 spm_reg_offset_v2_1[SPM_REG_NR] = {
+ [SPM_REG_CFG] = 0x08,
+ [SPM_REG_SPM_CTL] = 0x30,
+ [SPM_REG_DLY] = 0x34,
+ [SPM_REG_SEQ_ENTRY] = 0x80,
+};
+
+/* SPM register data for 8974, 8084 */
+static const struct spm_reg_data spm_reg_8974_8084_cpu = {
+ .reg_offset = spm_reg_offset_v2_1,
+ .spm_cfg = 0x1,
+ .spm_dly = 0x3C102800,
+ .seq = { 0x03, 0x0B, 0x0F, 0x00, 0x20, 0x80, 0x10, 0xE8, 0x5B, 0x03,
+ 0x3B, 0xE8, 0x5B, 0x82, 0x10, 0x0B, 0x30, 0x06, 0x26, 0x30,
+ 0x0F },
+ .start_index[PM_SLEEP_MODE_STBY] = 0,
+ .start_index[PM_SLEEP_MODE_SPC] = 3,
+};
+
+static const u8 spm_reg_offset_v1_1[SPM_REG_NR] = {
+ [SPM_REG_CFG] = 0x08,
+ [SPM_REG_SPM_CTL] = 0x20,
+ [SPM_REG_PMIC_DLY] = 0x24,
+ [SPM_REG_PMIC_DATA_0] = 0x28,
+ [SPM_REG_PMIC_DATA_1] = 0x2C,
+ [SPM_REG_SEQ_ENTRY] = 0x80,
+};
+
+/* SPM register data for 8064 */
+static const struct spm_reg_data spm_reg_8064_cpu = {
+ .reg_offset = spm_reg_offset_v1_1,
+ .spm_cfg = 0x1F,
+ .pmic_dly = 0x02020004,
+ .pmic_data[0] = 0x0084009C,
+ .pmic_data[1] = 0x00A4001C,
+ .seq = { 0x03, 0x0F, 0x00, 0x24, 0x54, 0x10, 0x09, 0x03, 0x01,
+ 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F },
+ .start_index[PM_SLEEP_MODE_STBY] = 0,
+ .start_index[PM_SLEEP_MODE_SPC] = 2,
+};
+
+static DEFINE_PER_CPU_SHARED_ALIGNED(struct spm_driver_data *, cpu_spm_drv);
+
+static inline void spm_register_write(struct spm_driver_data *drv,
+ enum spm_reg reg, u32 val)
+{
+ if (drv->reg_data->reg_offset[reg])
+ writel_relaxed(val, drv->reg_base +
+ drv->reg_data->reg_offset[reg]);
+}
+
+/* Ensure a guaranteed write, before return */
+static inline void spm_register_write_sync(struct spm_driver_data *drv,
+ enum spm_reg reg, u32 val)
+{
+ u32 ret;
+
+ if (!drv->reg_data->reg_offset[reg])
+ return;
+
+ do {
+ writel_relaxed(val, drv->reg_base +
+ drv->reg_data->reg_offset[reg]);
+ ret = readl_relaxed(drv->reg_base +
+ drv->reg_data->reg_offset[reg]);
+ if (ret == val)
+ break;
+ cpu_relax();
+ } while (1);
+}
+
+static inline u32 spm_register_read(struct spm_driver_data *drv,
+ enum spm_reg reg)
+{
+ return readl_relaxed(drv->reg_base + drv->reg_data->reg_offset[reg]);
+}
+
+static void spm_set_low_power_mode(enum pm_sleep_mode mode)
+{
+ struct spm_driver_data *drv = per_cpu(cpu_spm_drv,
+ smp_processor_id());
+ u32 start_index;
+ u32 ctl_val;
+
+ start_index = drv->reg_data->start_index[mode];
+
+ ctl_val = spm_register_read(drv, SPM_REG_SPM_CTL);
+ ctl_val &= ~(SPM_CTL_INDEX << SPM_CTL_INDEX_SHIFT);
+ ctl_val |= start_index << SPM_CTL_INDEX_SHIFT;
+ ctl_val |= SPM_CTL_EN;
+ spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val);
+}
+
+static int qcom_pm_collapse(unsigned long int unused)
+{
+ int ret;
+ u32 flag;
+ int cpu = smp_processor_id();
+
+ ret = scm_set_warm_boot_addr(cpu_resume, cpu);
+ if (ret)
+ return ret;
+
+ flag = SCM_L2_ON & SCM_FLUSH_FLAG_MASK;
+ ret = scm_call_atomic1(SCM_SVC_BOOT, SCM_CMD_TERMINATE_PC, flag);
+
+ /*
+ * Returns here only if there was a pending interrupt and we did not
+ * power down as a result.
+ */
+ /* Hack:: Ignore scm call return values in power down path */
+ return 0;
+}
+
+static int qcom_cpu_standby(void *unused)
+{
+ spm_set_low_power_mode(PM_SLEEP_MODE_STBY);
+ cpu_do_idle();
+
+ return 0;
+}
+
+static int qcom_cpu_spc(void *unused)
+{
+ int ret;
+
+ spm_set_low_power_mode(PM_SLEEP_MODE_STBY);
+ cpu_pm_enter();
+ ret = cpu_suspend(0, qcom_pm_collapse);
+ cpu_pm_exit();
+
+ return ret;
+}
+
+static struct spm_driver_data *spm_get_drv(struct platform_device *pdev,
+ int *spm_cpu)
+{
+ struct spm_driver_data *drv = NULL;
+ struct device_node *cpu_node, *saw_node;
+ int cpu;
+ bool found;
+
+ for_each_possible_cpu(cpu) {
+ cpu_node = of_cpu_device_node_get(cpu);
+ if (!cpu_node)
+ continue;
+ saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0);
+ found = (saw_node == pdev->dev.of_node);
+ of_node_put(saw_node);
+ of_node_put(cpu_node);
+ if (found)
+ break;
+ }
+
+ if (found) {
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (drv)
+ *spm_cpu = cpu;
+ }
+
+ return drv;
+}
+
+static const struct of_device_id spm_match_table[] = {
+ { .compatible = "qcom,msm8974-saw2-v2.1-cpu",
+ .data = &spm_reg_8974_8084_cpu },
+ { .compatible = "qcom,apq8084-saw2-v2.1-cpu",
+ .data = &spm_reg_8974_8084_cpu },
+ { .compatible = "qcom,apq8064-saw2-v1.1-cpu",
+ .data = &spm_reg_8064_cpu },
+ { },
+};
+
+static int spm_dev_probe(struct platform_device *pdev)
+{
+ struct spm_driver_data *drv;
+ struct resource *res;
+ const struct of_device_id *match_id;
+ void __iomem *addr;
+ int cpu;
+ struct cpuidle_device *dev;
+
+ drv = spm_get_drv(pdev, &cpu);
+ if (!drv)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(drv->reg_base))
+ return PTR_ERR(drv->reg_base);
+
+ match_id = of_match_node(spm_match_table, pdev->dev.of_node);
+ if (!match_id)
+ return -ENODEV;
+
+ drv->reg_data = match_id->data;
+
+ /* Write the SPM sequences first.. */
+ addr = drv->reg_base + drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
+ __iowrite32_copy(addr, drv->reg_data->seq,
+ ARRAY_SIZE(drv->reg_data->seq) / 4);
+
+ /*
+ * ..and then the control registers.
+ * On some SoC if the control registers are written first and if the
+ * CPU was held in reset, the reset signal could trigger the SPM state
+ * machine, before the sequences are completely written.
+ */
+ spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
+ spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
+ spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
+ spm_register_write(drv, SPM_REG_PMIC_DATA_0,
+ drv->reg_data->pmic_data[0]);
+ spm_register_write(drv, SPM_REG_PMIC_DATA_1,
+ drv->reg_data->pmic_data[1]);
+
+ per_cpu(cpu_spm_drv, cpu) = drv;
+
+ /* Register the cpuidle device for the cpu, we are ready for cpuidle */
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->cpu = cpu;
+ return cpuidle_register_device(dev);
+}
+
+static struct qcom_cpu_pm_ops lpm_ops = {
+ .standby = qcom_cpu_standby,
+ .spc = qcom_cpu_spc,
+};
+
+static struct platform_device qcom_cpuidle_drv = {
+ .name = "qcom_cpuidle",
+ .id = -1,
+ .dev.platform_data = &lpm_ops,
+};
+
+static struct platform_driver spm_driver = {
+ .probe = spm_dev_probe,
+ .driver = {
+ .name = "saw",
+ .of_match_table = spm_match_table,
+ },
+};
+
+static int __init qcom_spm_init(void)
+{
+ int ret;
+
+ /*
+ * cpuidle driver need to registered before the cpuidle device
+ * for any cpu. Register the device for the the cpuidle driver.
+ */
+ ret = platform_device_register(&qcom_cpuidle_drv);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&spm_driver);
+}
+module_init(qcom_spm_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SAW power controller driver");
+MODULE_ALIAS("platform:saw");
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index af40db0df58e..36549ea4d6b5 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -281,6 +281,16 @@ config INT340X_THERMAL
information to allow the user to select his laptop to run without
turning on the fans.
+config THERMAL_TSENS8960
+ tristate "Qualcomm 8960 Tsens Temperature Alarm"
+ depends on THERMAL && QCOM_QFPROM
+ help
+ This enables the thermal sysfs driver for the Tsens device. It shows
+ up in Sysfs as a thermal zone with mutiple trip points. Disabling the
+ thermal zone device via the mode file results in disabling the sensor.
+ Also able to set threshold temperature for both hot and cold and update
+ when a threshold is reached.
+
config ACPI_THERMAL_REL
tristate
depends on ACPI
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index fa0dc486790f..2e695c0da885 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
+obj-$(CONFIG_THERMAL_TSENS8960) += msm8960_tsens.o
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
obj-$(CONFIG_ST_THERMAL) += st/
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
new file mode 100644
index 000000000000..07eeffd0cbf6
--- /dev/null
+++ b/drivers/thermal/msm8960_tsens.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/thermal.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/pm.h>
+#include <linux/eeprom-consumer.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+#define TSENS_CNTL_ADDR 0x3620
+#define TSENS_EN BIT(0)
+#define TSENS_SW_RST BIT(1)
+#define TSENS_ADC_CLK_SEL BIT(2)
+#define SENSOR0_EN BIT(3)
+#define TSENS_SENSOR0_SHIFT 3
+#define TSENS_MASK1 1
+#define SENSORS_EN_MASK(n) GENMASK((n + 2), 3)
+
+#define TSENS_TH_ADDR 0x3624
+#define TSENS_TH_MAX_LIMIT_SHIFT 24
+#define TSENS_TH_MIN_LIMIT_SHIFT 16
+#define TSENS_TH_UPPER_LIMIT_SHIFT 8
+#define TSENS_TH_LOWER_LIMIT_SHIFT 0
+#define TSENS_TH_MAX_LIMIT_MASK GENMASK(31, 24)
+#define TSENS_TH_MIN_LIMIT_MASK GENMASK(23, 16)
+#define TSENS_TH_UPPER_LIMIT_MASK GENMASK(15, 8)
+#define TSENS_TH_LOWER_LIMIT_MASK GENMASK(7, 0)
+
+#define TSENS_S0_STATUS_ADDR 0x3628
+#define TSENS_STATUS_ADDR_OFFSET 2
+
+#define TSENS_INT_STATUS_ADDR 0x363c
+#define TSENS_LOWER_INT_MASK BIT(1)
+#define TSENS_UPPER_INT_MASK BIT(2)
+#define TSENS_MAX_INT_MASK BIT(3)
+#define TSENS_TRDY_MASK BIT(7)
+
+#define TSENS_TH_MAX_CODE 0xff
+#define TSENS_TH_MIN_CODE 0
+#define TSENS_TRDY_RDY_MIN_TIME 1000
+#define TSENS_TRDY_RDY_MAX_TIME 1100
+
+#define TSENS_MEASURE_PERIOD 1
+/* Initial temperature threshold values */
+#define TSENS_LOWER_LIMIT_TH 0x50
+#define TSENS_UPPER_LIMIT_TH 0xdf
+#define TSENS_MIN_LIMIT_TH 0x0
+#define TSENS_MAX_LIMIT_TH 0xff
+
+#define TSENS_MIN_STATUS_MASK(offset) BIT((offset))
+#define TSENS_LOWER_STATUS_CLR(offset) BIT((offset + 1))
+#define TSENS_UPPER_STATUS_CLR(offset) BIT((offset + 2))
+#define TSENS_MAX_STATUS_MASK(offset) BIT((offset + 3))
+
+/* QFPROM addresses */
+#define TSENS_8960_QFPROM_ADDR0 0x0
+#define TSENS_8960_QFPROM_SPARE_OFFSET 0x10
+/* 8960 Specifics */
+#define TSENS_8960_CONFIG 0x9b
+#define TSENS_8960_CONFIG_MASK GENMASK(7, 0)
+#define TSENS_8960_CONFIG_ADDR 0x3640
+#define TSENS_8064_STATUS_CNTL 0x3660
+#define TSENS_8064_SEQ_SENSORS 5
+#define TSENS_8064_S4_S5_OFFSET 40
+
+#define TSENS_CAL_MILLI_DEGC 30000
+#define TSENS_MAX_SENSORS 11
+
+/* Trips: from very hot to very cold */
+enum tsens_trip_type {
+ TSENS_TRIP_STAGE3 = 0,
+ TSENS_TRIP_STAGE2,
+ TSENS_TRIP_STAGE1,
+ TSENS_TRIP_STAGE0,
+ TSENS_TRIP_NUM,
+};
+
+struct tsens_tm_device;
+
+struct tsens_tm_device_sensor {
+ struct thermal_zone_device *tzone;
+ enum thermal_device_mode mode;
+ struct tsens_tm_device *tmdev;
+ unsigned int sensor_num;
+ int offset;
+ uint32_t slope;
+ bool user_zone;
+};
+
+struct tsens_variant_data {
+ int slope[TSENS_MAX_SENSORS];
+ u32 nsensors;
+ /* enable to tsens slp clock */
+ u32 slp_clk_ena;
+ u32 sctrl_reg;
+ u32 sctrl_offset;
+ u32 config_reg;
+ u32 config_mask;
+ u32 config;
+ int (*calib_sensors)(struct tsens_tm_device *tmdev, struct device *dev);
+};
+
+struct tsens_tm_device {
+ struct regmap *base;
+ struct tsens_variant_data *data;
+ struct work_struct tsens_work;
+ int nsensors;
+ int irq;
+ struct eeprom_cell *eeprom_calib_cell;
+ struct eeprom_cell *eeprom_calib_backup_cell;
+
+ /* tsens ready bit for reading valid temperature */
+ bool trdy;
+ struct tsens_tm_device_sensor sensor[0];
+};
+
+/* Temperature on y axis and ADC-code on x-axis */
+static inline int tsens_code_to_degc(struct tsens_tm_device_sensor *s,
+ int adc_code)
+{
+ return adc_code * s->slope + s->offset;
+}
+
+static inline int tsens_degc_to_code(struct tsens_tm_device_sensor *s, int degc)
+{
+ return (degc - s->offset + s->slope / 2) / s->slope;
+}
+
+static int __tsens_get_temp(void *data, long *temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = data;
+ unsigned int code, offset = 0;
+ struct tsens_tm_device *tmdev = tm_sensor->tmdev;
+ int sensor_num = tm_sensor->sensor_num;
+ struct regmap *base = tmdev->base;
+
+ if (!tmdev->trdy) {
+ u32 val;
+
+ regmap_read(base, TSENS_INT_STATUS_ADDR, &val);
+
+ while (!(val & TSENS_TRDY_MASK)) {
+ usleep_range(TSENS_TRDY_RDY_MIN_TIME,
+ TSENS_TRDY_RDY_MAX_TIME);
+ regmap_read(base, TSENS_INT_STATUS_ADDR, &val);
+ }
+ tmdev->trdy = true;
+ }
+//FIXME
+ if (sensor_num >= TSENS_8064_SEQ_SENSORS)
+ offset = TSENS_8064_S4_S5_OFFSET;
+
+ regmap_read(base, TSENS_S0_STATUS_ADDR + offset +
+ (sensor_num << TSENS_STATUS_ADDR_OFFSET), &code);
+ *temp = tsens_code_to_degc(tm_sensor, code);
+
+ return 0;
+}
+
+static int tsens_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+ if (!tm_sensor || tm_sensor->mode != THERMAL_DEVICE_ENABLED)
+ return -EINVAL;
+
+ return __tsens_get_temp(tm_sensor, temp);
+
+}
+
+static int tsens_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+ if (tm_sensor)
+ *mode = tm_sensor->mode;
+
+ return 0;
+}
+
+/**
+ * Function to enable the mode.
+ * If the main sensor is disabled all the sensors are disable and
+ * the clock is disabled.
+ * If the main sensor is not enabled and sub sensor is enabled
+ * returns with an error stating the main sensor is not enabled.
+ **/
+
+static int tsens_set_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode mode)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ struct tsens_tm_device *tmdev = tm_sensor->tmdev;
+ struct tsens_variant_data *data = tmdev->data;
+ struct regmap *base = tmdev->base;
+ unsigned int reg, mask, i;
+
+ if (!tm_sensor)
+ return -EINVAL;
+
+ if (mode != tm_sensor->mode) {
+ regmap_read(base, TSENS_CNTL_ADDR, &reg);
+ mask = BIT(tm_sensor->sensor_num + TSENS_SENSOR0_SHIFT);
+ if (mode == THERMAL_DEVICE_ENABLED) {
+ if ((mask != SENSOR0_EN) && !(reg & SENSOR0_EN)) {
+ pr_info("Main sensor not enabled\n");
+ return -EINVAL;
+ }
+ regmap_write(base, TSENS_CNTL_ADDR, reg | TSENS_SW_RST);
+ reg |= mask | data->slp_clk_ena | TSENS_EN;
+ tmdev->trdy = false;
+ } else {
+ reg &= ~mask;
+ if (!(reg & SENSOR0_EN)) {
+ reg &= ~(SENSORS_EN_MASK(tmdev->nsensors) |
+ data->slp_clk_ena |
+ TSENS_EN);
+
+ for (i = 1; i < tmdev->nsensors; i++)
+ tmdev->sensor[i].mode = mode;
+
+ }
+ }
+ regmap_write(base, TSENS_CNTL_ADDR, reg);
+ }
+ tm_sensor->mode = mode;
+
+ return 0;
+}
+
+static int tsens_get_trip_type(struct thermal_zone_device *thermal,
+ int trip, enum thermal_trip_type *type)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+
+ if (!tm_sensor || trip < 0 || !type)
+ return -EINVAL;
+
+ switch (trip) {
+ case TSENS_TRIP_STAGE3:
+ *type = THERMAL_TRIP_CRITICAL;
+ break;
+ case TSENS_TRIP_STAGE2:
+ *type = THERMAL_TRIP_HOT;
+ break;
+ case TSENS_TRIP_STAGE1:
+ *type = THERMAL_TRIP_PASSIVE;
+ break;
+ case TSENS_TRIP_STAGE0:
+ *type = THERMAL_TRIP_ACTIVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tsens_get_trip_temp(struct thermal_zone_device *thermal,
+ int trip, unsigned long *temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ struct tsens_tm_device *tmdev = tm_sensor->tmdev;
+ unsigned int reg;
+
+ if (!tm_sensor || trip < 0 || !temp)
+ return -EINVAL;
+
+ regmap_read(tmdev->base, TSENS_TH_ADDR, &reg);
+ switch (trip) {
+ case TSENS_TRIP_STAGE3:
+ reg = (reg & TSENS_TH_MAX_LIMIT_MASK)
+ >> TSENS_TH_MAX_LIMIT_SHIFT;
+ break;
+ case TSENS_TRIP_STAGE2:
+ reg = (reg & TSENS_TH_UPPER_LIMIT_MASK)
+ >> TSENS_TH_UPPER_LIMIT_SHIFT;
+ break;
+ case TSENS_TRIP_STAGE1:
+ reg = (reg & TSENS_TH_LOWER_LIMIT_MASK)
+ >> TSENS_TH_LOWER_LIMIT_SHIFT;
+ break;
+ case TSENS_TRIP_STAGE0:
+ reg = (reg & TSENS_TH_MIN_LIMIT_MASK)
+ >> TSENS_TH_MIN_LIMIT_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *temp = tsens_code_to_degc(tm_sensor, reg);
+
+ return 0;
+}
+
+static int tsens_set_trip_temp(struct thermal_zone_device *thermal,
+ int trip, unsigned long temp)
+{
+ struct tsens_tm_device_sensor *tm_sensor = thermal->devdata;
+ unsigned int reg_th, reg_cntl;
+ int code, hi_code, lo_code, code_err_chk;
+ struct tsens_tm_device *tmdev = tm_sensor->tmdev;
+ struct regmap *base = tmdev->base;
+ struct tsens_variant_data *data = tmdev->data;
+ u32 offset = data->sctrl_offset;
+
+ code_err_chk = code = tsens_degc_to_code(tm_sensor, temp);
+ if (!tm_sensor || trip < 0)
+ return -EINVAL;
+
+ lo_code = TSENS_TH_MIN_CODE;
+ hi_code = TSENS_TH_MAX_CODE;
+
+ regmap_read(base, data->sctrl_reg, &reg_cntl);
+
+ regmap_read(base, TSENS_TH_ADDR, &reg_th);
+ switch (trip) {
+ case TSENS_TRIP_STAGE3:
+ code <<= TSENS_TH_MAX_LIMIT_SHIFT;
+ reg_th &= ~TSENS_TH_MAX_LIMIT_MASK;
+
+ if (!(reg_cntl & TSENS_UPPER_STATUS_CLR(offset)))
+ lo_code = (reg_th & TSENS_TH_UPPER_LIMIT_MASK)
+ >> TSENS_TH_UPPER_LIMIT_SHIFT;
+ else if (!(reg_cntl & TSENS_LOWER_STATUS_CLR(offset)))
+ lo_code = (reg_th & TSENS_TH_LOWER_LIMIT_MASK)
+ >> TSENS_TH_LOWER_LIMIT_SHIFT;
+ else if (!(reg_cntl & TSENS_MIN_STATUS_MASK(offset)))
+ lo_code = (reg_th & TSENS_TH_MIN_LIMIT_MASK)
+ >> TSENS_TH_MIN_LIMIT_SHIFT;
+ break;
+ case TSENS_TRIP_STAGE2:
+ code <<= TSENS_TH_UPPER_LIMIT_SHIFT;
+ reg_th &= ~TSENS_TH_UPPER_LIMIT_MASK;
+
+ if (!(reg_cntl & TSENS_MAX_STATUS_MASK(offset)))
+ hi_code = (reg_th & TSENS_TH_MAX_LIMIT_MASK)
+ >> TSENS_TH_MAX_LIMIT_SHIFT;
+ if (!(reg_cntl & TSENS_LOWER_STATUS_CLR(offset)))
+ lo_code = (reg_th & TSENS_TH_LOWER_LIMIT_MASK)
+ >> TSENS_TH_LOWER_LIMIT_SHIFT;
+ else if (!(reg_cntl & TSENS_MIN_STATUS_MASK(offset)))
+ lo_code = (reg_th & TSENS_TH_MIN_LIMIT_MASK)
+ >> TSENS_TH_MIN_LIMIT_SHIFT;
+ break;
+ case TSENS_TRIP_STAGE1:
+ code <<= TSENS_TH_LOWER_LIMIT_SHIFT;
+ reg_th &= ~TSENS_TH_LOWER_LIMIT_MASK;
+
+ if (!(reg_cntl & TSENS_MIN_STATUS_MASK(offset)))
+ lo_code = (reg_th & TSENS_TH_MIN_LIMIT_MASK)
+ >> TSENS_TH_MIN_LIMIT_SHIFT;
+ if (!(reg_cntl & TSENS_UPPER_STATUS_CLR(offset)))
+ hi_code = (reg_th & TSENS_TH_UPPER_LIMIT_MASK)
+ >> TSENS_TH_UPPER_LIMIT_SHIFT;
+ else if (!(reg_cntl & TSENS_MAX_STATUS_MASK(offset)))
+ hi_code = (reg_th & TSENS_TH_MAX_LIMIT_MASK)
+ >> TSENS_TH_MAX_LIMIT_SHIFT;
+ break;
+ case TSENS_TRIP_STAGE0:
+ code <<= TSENS_TH_MIN_LIMIT_SHIFT;
+ reg_th &= ~TSENS_TH_MIN_LIMIT_MASK;
+
+ if (!(reg_cntl & TSENS_LOWER_STATUS_CLR(offset)))
+ hi_code = (reg_th & TSENS_TH_LOWER_LIMIT_MASK)
+ >> TSENS_TH_LOWER_LIMIT_SHIFT;
+ else if (!(reg_cntl & TSENS_UPPER_STATUS_CLR(offset)))
+ hi_code = (reg_th & TSENS_TH_UPPER_LIMIT_MASK)
+ >> TSENS_TH_UPPER_LIMIT_SHIFT;
+ else if (!(reg_cntl & TSENS_MAX_STATUS_MASK(offset)))
+ hi_code = (reg_th & TSENS_TH_MAX_LIMIT_MASK)
+ >> TSENS_TH_MAX_LIMIT_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (code_err_chk < lo_code || code_err_chk > hi_code)
+ return -EINVAL;
+
+ regmap_write(base, TSENS_TH_ADDR, reg_th | code);
+
+ return 0;
+}
+
+static int tsens_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ return tsens_get_trip_temp(thermal, TSENS_TRIP_STAGE3, temp);
+}
+
+static struct thermal_zone_device_ops tsens_thermal_zone_ops = {
+ .get_temp = tsens_get_temp,
+ .get_mode = tsens_get_mode,
+ .set_mode = tsens_set_mode,
+ .get_trip_type = tsens_get_trip_type,
+ .get_trip_temp = tsens_get_trip_temp,
+ .set_trip_temp = tsens_set_trip_temp,
+ .get_crit_temp = tsens_get_crit_temp,
+};
+
+static const struct thermal_zone_of_device_ops tsens_of_thermal_ops = {
+ .get_temp = __tsens_get_temp,
+};
+
+static void tsens_scheduler_fn(struct work_struct *work)
+{
+ struct tsens_tm_device *tmdev = container_of(work,
+ struct tsens_tm_device, tsens_work);
+ unsigned int threshold, threshold_low, i, code, reg, sensor, mask;
+ unsigned int sensor_addr;
+ bool upper_th_x, lower_th_x;
+ struct regmap *base = tmdev->base;
+ struct tsens_variant_data *data = tmdev->data;
+ u32 offset = data->sctrl_offset;
+
+ regmap_read(base, data->sctrl_reg, &reg);
+ regmap_write(base, data->sctrl_reg, reg |
+ TSENS_LOWER_STATUS_CLR(offset) |
+ TSENS_UPPER_STATUS_CLR(offset));
+
+ mask = ~(TSENS_LOWER_STATUS_CLR(offset) | TSENS_UPPER_STATUS_CLR(offset));
+ regmap_read(tmdev->base, TSENS_TH_ADDR, &threshold);
+ threshold_low = (threshold & TSENS_TH_LOWER_LIMIT_MASK)
+ >> TSENS_TH_LOWER_LIMIT_SHIFT;
+ threshold = (threshold & TSENS_TH_UPPER_LIMIT_MASK)
+ >> TSENS_TH_UPPER_LIMIT_SHIFT;
+
+ regmap_read(base, TSENS_CNTL_ADDR, &sensor);
+ sensor &= SENSORS_EN_MASK(tmdev->nsensors);
+ sensor >>= TSENS_SENSOR0_SHIFT;
+ sensor_addr = TSENS_S0_STATUS_ADDR;
+ for (i = 0; i < tmdev->nsensors; i++) {
+ if (i == TSENS_8064_SEQ_SENSORS)
+ sensor_addr += TSENS_8064_S4_S5_OFFSET;
+ if (sensor & TSENS_MASK1) {
+ regmap_read(base, sensor_addr, &code);
+ upper_th_x = code >= threshold;
+ lower_th_x = code <= threshold_low;
+ if (upper_th_x)
+ mask |= TSENS_UPPER_STATUS_CLR(offset);
+ if (lower_th_x)
+ mask |= TSENS_LOWER_STATUS_CLR(offset);
+ if (upper_th_x || lower_th_x)
+ thermal_zone_device_update(tmdev->sensor[i].tzone);
+ }
+ sensor >>= 1;
+ sensor_addr += 4;
+ }
+ regmap_read(base, data->sctrl_reg, &reg);
+ regmap_write(base, data->sctrl_reg, reg & mask);
+}
+
+static irqreturn_t tsens_isr(int irq, void *dev)
+{
+ struct tsens_tm_device *tmdev = dev;
+
+ schedule_work(&tmdev->tsens_work);
+
+ return IRQ_HANDLED;
+}
+
+static void tsens_disable_mode(struct tsens_tm_device *tmdev)
+{
+ unsigned int reg_cntl = 0;
+ struct regmap *base = tmdev->base;
+ struct tsens_variant_data *data = tmdev->data;
+
+ regmap_read(base, TSENS_CNTL_ADDR, &reg_cntl);
+ regmap_write(base, TSENS_CNTL_ADDR, reg_cntl &
+ ~(SENSORS_EN_MASK(tmdev->nsensors) | data->slp_clk_ena
+ | TSENS_EN));
+}
+
+static void tsens_hw_init(struct tsens_tm_device *tmdev)
+{
+ unsigned int reg_cntl = 0, reg_cfg = 0, reg_thr = 0;
+ unsigned int reg_status_cntl = 0;
+ struct regmap *base = tmdev->base;
+ struct tsens_variant_data *data = tmdev->data;
+ u32 offset = data->sctrl_offset;
+
+ regmap_read(base, TSENS_CNTL_ADDR, &reg_cntl);
+ regmap_write(base, TSENS_CNTL_ADDR, reg_cntl | TSENS_SW_RST);
+
+ reg_cntl |= data->slp_clk_ena |
+ (TSENS_MEASURE_PERIOD << 18) |
+ SENSORS_EN_MASK(tmdev->nsensors);
+ regmap_write(base, TSENS_CNTL_ADDR, reg_cntl);
+
+ regmap_read(base, data->sctrl_reg, &reg_status_cntl);
+ reg_status_cntl |= TSENS_LOWER_STATUS_CLR(offset) |
+ TSENS_UPPER_STATUS_CLR(offset) |
+ TSENS_MIN_STATUS_MASK(offset) |
+ TSENS_MAX_STATUS_MASK(offset);
+
+ regmap_write(base, data->sctrl_reg, reg_status_cntl);
+
+
+ reg_cntl |= TSENS_EN;
+ regmap_write(base, TSENS_CNTL_ADDR, reg_cntl);
+
+ regmap_read(base, data->config_reg, &reg_cfg);
+ reg_cfg = (reg_cfg & ~data->config_mask) | data->config;
+ regmap_write(base, data->config_reg, reg_cfg);
+
+ reg_thr |= (TSENS_LOWER_LIMIT_TH << TSENS_TH_LOWER_LIMIT_SHIFT) |
+ (TSENS_UPPER_LIMIT_TH << TSENS_TH_UPPER_LIMIT_SHIFT) |
+ (TSENS_MIN_LIMIT_TH << TSENS_TH_MIN_LIMIT_SHIFT) |
+ (TSENS_MAX_LIMIT_TH << TSENS_TH_MAX_LIMIT_SHIFT);
+ regmap_write(base, TSENS_TH_ADDR, reg_thr);
+}
+
+static int tsens_8960_calib_sensors(struct tsens_tm_device *tmdev, struct device *dev)
+{
+
+ uint8_t calib_data, calib_data_backup;
+ int sz, i;// = round_up(tmdev->qfprom_size, sizeof(int));
+ u8 *cdata, *cdata_backup;
+
+ tmdev->eeprom_calib_cell = of_eeprom_cell_get(dev, 0);
+ tmdev->eeprom_calib_backup_cell = of_eeprom_cell_get(dev, 1);
+
+ if (IS_ERR(tmdev->eeprom_calib_cell))
+ return PTR_ERR(tmdev->eeprom_calib_cell);
+
+ if (IS_ERR(tmdev->eeprom_calib_backup_cell))
+ return PTR_ERR(tmdev->eeprom_calib_backup_cell);
+
+ cdata = eeprom_cell_read(tmdev->eeprom_calib_cell, &sz);
+ cdata_backup = eeprom_cell_read(tmdev->eeprom_calib_backup_cell, &sz);
+ for (i = 0; i < tmdev->nsensors; i++) {
+ calib_data = cdata[i];
+ calib_data_backup = cdata_backup[i];
+ if (calib_data_backup)
+ calib_data = calib_data_backup;
+
+ if (!calib_data) {
+ pr_err("QFPROM TSENS calibration data not present\n");
+ return -ENODEV;
+ }
+ tmdev->sensor[i].offset = TSENS_CAL_MILLI_DEGC - (calib_data * tmdev->sensor[i].slope);
+ tmdev->trdy = false;
+ }
+
+ return 0;
+}
+
+static int tsens_calib_sensors(struct tsens_tm_device *tmdev, struct device *dev)
+{
+ if (tmdev->data->calib_sensors)
+ return tmdev->data->calib_sensors(tmdev, dev);
+
+ return -ENODEV;
+}
+
+struct tsens_variant_data msm8960_data =
+{
+ /**
+ * Slope for a thermocouple in a given part is always same
+ * for desired range of temperature measurements
+ **/
+ .slope = {910, 910, 910, 910, 910},
+ .nsensors = 5,
+ .slp_clk_ena = BIT(26),
+ .sctrl_reg = TSENS_CNTL_ADDR,
+ .sctrl_offset = 8,
+ .config_reg = TSENS_8960_CONFIG_ADDR,
+ .config_mask = TSENS_8960_CONFIG_MASK,
+ .config = TSENS_8960_CONFIG,
+ .calib_sensors = tsens_8960_calib_sensors,
+};
+
+struct tsens_variant_data apq8064_data = {
+ /**
+ * Slope for a thermocouple in a given part is always same
+ * for desired range of temperature measurements
+ **/
+ .slope = {1176, 1176, 1154, 1176, 1111, 1132,
+ 1132, 1199, 1132, 1199, 1132},
+ .nsensors = 11,
+ .slp_clk_ena = BIT(26),
+ .sctrl_reg = TSENS_8064_STATUS_CNTL,
+ .sctrl_offset = 0,
+ .config_reg = TSENS_8960_CONFIG_ADDR,
+ .config_mask = TSENS_8960_CONFIG_MASK,
+ .config = TSENS_8960_CONFIG,
+ .calib_sensors = tsens_8960_calib_sensors,
+};
+
+static struct of_device_id qcom_tsens_of_match[] = {
+ { .compatible = "qcom,msm8960-tsens", .data = &msm8960_data },
+ { .compatible = "qcom,apq8064-tsens", .data = &apq8064_data },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qcom_tsens_of_match);
+
+static int tsens_tm_probe(struct platform_device *pdev)
+{
+ int rc, i;
+ struct device *dev = &pdev->dev;
+ struct tsens_tm_device *tmdev;
+ struct tsens_variant_data *data;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (!np) {
+ dev_err(dev, "Non DT not supported\n");
+ return -EINVAL;
+ }
+
+ data = (struct tsens_variant_data *)of_match_node(qcom_tsens_of_match, np)->data;
+ tmdev = devm_kzalloc(dev, sizeof(struct tsens_tm_device) +
+ data->nsensors *
+ sizeof(struct tsens_tm_device_sensor),
+ GFP_ATOMIC);
+ if (tmdev == NULL) {
+ pr_err("%s: kzalloc() failed.\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < data->nsensors; i++)
+ tmdev->sensor[i].slope = data->slope[i];
+
+ tmdev->data = data;
+ tmdev->nsensors = data->nsensors;
+ tmdev->base = dev_get_regmap(dev->parent, NULL);
+
+ if (!tmdev->base) {
+ dev_err(&pdev->dev, "Parent regmap unavailable.\n");
+ return -ENXIO;
+ }
+
+ rc = tsens_calib_sensors(tmdev, dev);
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ tsens_hw_init(tmdev);
+
+ for (i = 0; i < tmdev->nsensors; i++) {
+ char name[18];
+ snprintf(name, sizeof(name), "tsens_sensor%d", i);
+ tmdev->sensor[i].mode = THERMAL_DEVICE_ENABLED;
+ tmdev->sensor[i].sensor_num = i;
+ tmdev->sensor[i].tmdev = tmdev;
+
+ tmdev->sensor[i].tzone = thermal_zone_of_sensor_register(dev, i,
+ &tmdev->sensor[i], &tsens_of_thermal_ops);
+
+ if (IS_ERR(tmdev->sensor[i].tzone)) {
+ tmdev->sensor[i].tzone = thermal_zone_device_register(
+ name,
+ TSENS_TRIP_NUM,
+ GENMASK(TSENS_TRIP_NUM - 1 , 0),
+ &tmdev->sensor[i],
+ &tsens_thermal_zone_ops, NULL, 0, 0);
+ if (IS_ERR(tmdev->sensor[i].tzone)) {
+ dev_err(dev, "thermal_zone_device_register() failed.\n");
+ rc = -ENODEV;
+ goto fail;
+ }
+ tmdev->sensor[i].user_zone = true;
+ }
+ }
+
+ tmdev->irq = platform_get_irq_byname(pdev, "tsens-ul");
+ if (tmdev->irq > 0) {
+ rc = devm_request_irq(dev, tmdev->irq, tsens_isr,
+ IRQF_TRIGGER_RISING,
+ "tsens_interrupt", tmdev);
+ if (rc < 0) {
+ pr_err("%s: request_irq FAIL: %d\n", __func__, rc);
+ for (i = 0; i < tmdev->nsensors; i++)
+ if (tmdev->sensor[i].user_zone)
+ thermal_zone_device_unregister(
+ tmdev->sensor[i].tzone);
+ else
+ thermal_zone_of_sensor_unregister(dev,
+ tmdev->sensor[i].tzone);
+ goto fail;
+ }
+ INIT_WORK(&tmdev->tsens_work, tsens_scheduler_fn);
+ }
+ platform_set_drvdata(pdev, tmdev);
+
+ dev_info(dev, "Probed sucessfully\n");
+
+ return 0;
+fail:
+ tsens_disable_mode(tmdev);
+
+ return rc;
+}
+
+static int tsens_tm_remove(struct platform_device *pdev)
+{
+ int i;
+ struct tsens_tm_device *tmdev = platform_get_drvdata(pdev);
+ struct tsens_tm_device_sensor *s;
+
+ tsens_disable_mode(tmdev);
+
+ eeprom_cell_put(tmdev->eeprom_calib_cell);
+ eeprom_cell_put(tmdev->eeprom_calib_backup_cell);
+
+ for (i = 0; i < tmdev->nsensors; i++) {
+ s = &tmdev->sensor[i];
+ if (s->user_zone)
+ thermal_zone_device_unregister(s->tzone);
+ else
+ thermal_zone_of_sensor_unregister(&pdev->dev,
+ s->tzone);
+ }
+
+ return 0;
+}
+
+static struct platform_driver tsens_tm_driver = {
+ .probe = tsens_tm_probe,
+ .remove = tsens_tm_remove,
+ .driver = {
+ .name = "tsens8960-tm",
+ .owner = THIS_MODULE,
+ .of_match_table = qcom_tsens_of_match,
+ },
+};
+
+module_platform_driver(tsens_tm_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM8960 Temperature Sensor driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:tsens8960-tm");
diff --git a/include/dt-bindings/arm/qcom-ids.h b/include/dt-bindings/arm/qcom-ids.h
new file mode 100644
index 000000000000..a18f34e7d965
--- /dev/null
+++ b/include/dt-bindings/arm/qcom-ids.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2015, The Linux Foundation. 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 version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __DT_BINDINGS_QCOM_IDS_H
+#define __DT_BINDINGS_QCOM_IDS_H
+
+/* qcom,msm-id */
+#define QCOM_ID_MSM8916 206
+#define QCOM_ID_APQ8016 247
+#define QCOM_ID_MSM8216 248
+#define QCOM_ID_MSM8116 249
+#define QCOM_ID_MSM8616 250
+
+/* qcom,board-id */
+#define QCOM_BRD_ID(a, major, minor) \
+ (((major & 0xff) << 16) | ((minor & 0xff) << 8) | QCOM_BRD_ID_##a)
+
+#define QCOM_BRD_ID_MTP 8
+#define QCOM_BRD_ID_DRAGONBRD 10
+#define QCOM_BRD_ID_SBC 24
+
+#define QCOM_BRD_SUBTYPE_DEFAULT 0
+#define QCOM_BRD_SUBTYPE_MTP8916_SMB1360 1
+
+#endif
diff --git a/include/dt-bindings/clock/qcom,gcc-apq8084.h b/include/dt-bindings/clock/qcom,gcc-apq8084.h
index 2c0da566c46a..5aa7ebeae411 100644
--- a/include/dt-bindings/clock/qcom,gcc-apq8084.h
+++ b/include/dt-bindings/clock/qcom,gcc-apq8084.h
@@ -348,4 +348,10 @@
#define GCC_PCIE_1_PIPE_CLK 331
#define GCC_PCIE_1_SLV_AXI_CLK 332
+/* gdscs */
+#define USB_HS_HSIC_GDSC 0
+#define PCIE0_GDSC 1
+#define PCIE1_GDSC 2
+#define USB30_GDSC 3
+
#endif
diff --git a/include/dt-bindings/clock/qcom,gcc-msm8916.h b/include/dt-bindings/clock/qcom,gcc-msm8916.h
new file mode 100644
index 000000000000..11566c567356
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,gcc-msm8916.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2015 Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _DT_BINDINGS_CLK_MSM_GCC_8916_H
+#define _DT_BINDINGS_CLK_MSM_GCC_8916_H
+
+#define GPLL0 0
+#define GPLL0_VOTE 1
+#define BIMC_PLL 2
+#define BIMC_PLL_VOTE 3
+#define GPLL1 4
+#define GPLL1_VOTE 5
+#define GPLL2 6
+#define GPLL2_VOTE 7
+#define PCNOC_BFDCD_CLK_SRC 8
+#define SYSTEM_NOC_BFDCD_CLK_SRC 9
+#define CAMSS_AHB_CLK_SRC 10
+#define APSS_AHB_CLK_SRC 11
+#define CSI0_CLK_SRC 12
+#define CSI1_CLK_SRC 13
+#define GFX3D_CLK_SRC 14
+#define VFE0_CLK_SRC 15
+#define BLSP1_QUP1_I2C_APPS_CLK_SRC 16
+#define BLSP1_QUP1_SPI_APPS_CLK_SRC 17
+#define BLSP1_QUP2_I2C_APPS_CLK_SRC 18
+#define BLSP1_QUP2_SPI_APPS_CLK_SRC 19
+#define BLSP1_QUP3_I2C_APPS_CLK_SRC 20
+#define BLSP1_QUP3_SPI_APPS_CLK_SRC 21
+#define BLSP1_QUP4_I2C_APPS_CLK_SRC 22
+#define BLSP1_QUP4_SPI_APPS_CLK_SRC 23
+#define BLSP1_QUP5_I2C_APPS_CLK_SRC 24
+#define BLSP1_QUP5_SPI_APPS_CLK_SRC 25
+#define BLSP1_QUP6_I2C_APPS_CLK_SRC 26
+#define BLSP1_QUP6_SPI_APPS_CLK_SRC 27
+#define BLSP1_UART1_APPS_CLK_SRC 28
+#define BLSP1_UART2_APPS_CLK_SRC 29
+#define CCI_CLK_SRC 30
+#define CAMSS_GP0_CLK_SRC 31
+#define CAMSS_GP1_CLK_SRC 32
+#define JPEG0_CLK_SRC 33
+#define MCLK0_CLK_SRC 34
+#define MCLK1_CLK_SRC 35
+#define CSI0PHYTIMER_CLK_SRC 36
+#define CSI1PHYTIMER_CLK_SRC 37
+#define CPP_CLK_SRC 38
+#define CRYPTO_CLK_SRC 39
+#define GP1_CLK_SRC 40
+#define GP2_CLK_SRC 41
+#define GP3_CLK_SRC 42
+#define BYTE0_CLK_SRC 43
+#define ESC0_CLK_SRC 44
+#define MDP_CLK_SRC 45
+#define PCLK0_CLK_SRC 46
+#define VSYNC_CLK_SRC 47
+#define PDM2_CLK_SRC 48
+#define SDCC1_APPS_CLK_SRC 49
+#define SDCC2_APPS_CLK_SRC 50
+#define APSS_TCU_CLK_SRC 51
+#define USB_HS_SYSTEM_CLK_SRC 52
+#define VCODEC0_CLK_SRC 53
+#define GCC_BLSP1_AHB_CLK 54
+#define GCC_BLSP1_SLEEP_CLK 55
+#define GCC_BLSP1_QUP1_I2C_APPS_CLK 56
+#define GCC_BLSP1_QUP1_SPI_APPS_CLK 57
+#define GCC_BLSP1_QUP2_I2C_APPS_CLK 58
+#define GCC_BLSP1_QUP2_SPI_APPS_CLK 59
+#define GCC_BLSP1_QUP3_I2C_APPS_CLK 60
+#define GCC_BLSP1_QUP3_SPI_APPS_CLK 61
+#define GCC_BLSP1_QUP4_I2C_APPS_CLK 62
+#define GCC_BLSP1_QUP4_SPI_APPS_CLK 63
+#define GCC_BLSP1_QUP5_I2C_APPS_CLK 64
+#define GCC_BLSP1_QUP5_SPI_APPS_CLK 65
+#define GCC_BLSP1_QUP6_I2C_APPS_CLK 66
+#define GCC_BLSP1_QUP6_SPI_APPS_CLK 67
+#define GCC_BLSP1_UART1_APPS_CLK 68
+#define GCC_BLSP1_UART2_APPS_CLK 69
+#define GCC_BOOT_ROM_AHB_CLK 70
+#define GCC_CAMSS_CCI_AHB_CLK 71
+#define GCC_CAMSS_CCI_CLK 72
+#define GCC_CAMSS_CSI0_AHB_CLK 73
+#define GCC_CAMSS_CSI0_CLK 74
+#define GCC_CAMSS_CSI0PHY_CLK 75
+#define GCC_CAMSS_CSI0PIX_CLK 76
+#define GCC_CAMSS_CSI0RDI_CLK 77
+#define GCC_CAMSS_CSI1_AHB_CLK 78
+#define GCC_CAMSS_CSI1_CLK 79
+#define GCC_CAMSS_CSI1PHY_CLK 80
+#define GCC_CAMSS_CSI1PIX_CLK 81
+#define GCC_CAMSS_CSI1RDI_CLK 82
+#define GCC_CAMSS_CSI_VFE0_CLK 83
+#define GCC_CAMSS_GP0_CLK 84
+#define GCC_CAMSS_GP1_CLK 85
+#define GCC_CAMSS_ISPIF_AHB_CLK 86
+#define GCC_CAMSS_JPEG0_CLK 87
+#define GCC_CAMSS_JPEG_AHB_CLK 88
+#define GCC_CAMSS_JPEG_AXI_CLK 89
+#define GCC_CAMSS_MCLK0_CLK 90
+#define GCC_CAMSS_MCLK1_CLK 91
+#define GCC_CAMSS_MICRO_AHB_CLK 92
+#define GCC_CAMSS_CSI0PHYTIMER_CLK 93
+#define GCC_CAMSS_CSI1PHYTIMER_CLK 94
+#define GCC_CAMSS_AHB_CLK 95
+#define GCC_CAMSS_TOP_AHB_CLK 96
+#define GCC_CAMSS_CPP_AHB_CLK 97
+#define GCC_CAMSS_CPP_CLK 98
+#define GCC_CAMSS_VFE0_CLK 99
+#define GCC_CAMSS_VFE_AHB_CLK 100
+#define GCC_CAMSS_VFE_AXI_CLK 101
+#define GCC_CRYPTO_AHB_CLK 102
+#define GCC_CRYPTO_AXI_CLK 103
+#define GCC_CRYPTO_CLK 104
+#define GCC_OXILI_GMEM_CLK 105
+#define GCC_GP1_CLK 106
+#define GCC_GP2_CLK 107
+#define GCC_GP3_CLK 108
+#define GCC_MDSS_AHB_CLK 109
+#define GCC_MDSS_AXI_CLK 110
+#define GCC_MDSS_BYTE0_CLK 111
+#define GCC_MDSS_ESC0_CLK 112
+#define GCC_MDSS_MDP_CLK 113
+#define GCC_MDSS_PCLK0_CLK 114
+#define GCC_MDSS_VSYNC_CLK 115
+#define GCC_MSS_CFG_AHB_CLK 116
+#define GCC_OXILI_AHB_CLK 117
+#define GCC_OXILI_GFX3D_CLK 118
+#define GCC_PDM2_CLK 119
+#define GCC_PDM_AHB_CLK 120
+#define GCC_PRNG_AHB_CLK 121
+#define GCC_SDCC1_AHB_CLK 122
+#define GCC_SDCC1_APPS_CLK 123
+#define GCC_SDCC2_AHB_CLK 124
+#define GCC_SDCC2_APPS_CLK 125
+#define GCC_GTCU_AHB_CLK 126
+#define GCC_JPEG_TBU_CLK 127
+#define GCC_MDP_TBU_CLK 128
+#define GCC_SMMU_CFG_CLK 129
+#define GCC_VENUS_TBU_CLK 130
+#define GCC_VFE_TBU_CLK 131
+#define GCC_USB2A_PHY_SLEEP_CLK 132
+#define GCC_USB_HS_AHB_CLK 133
+#define GCC_USB_HS_SYSTEM_CLK 134
+#define GCC_VENUS0_AHB_CLK 135
+#define GCC_VENUS0_AXI_CLK 136
+#define GCC_VENUS0_VCODEC0_CLK 137
+
+/* Indexes for GDSCs */
+#define BIMC_GDSC 0
+#define VENUS_GDSC 1
+#define MDSS_GDSC 2
+#define JPEG_GDSC 3
+#define VFE_GDSC 4
+#define OXILI_GDSC 5
+
+#endif
diff --git a/include/dt-bindings/clock/qcom,gcc-msm8974.h b/include/dt-bindings/clock/qcom,gcc-msm8974.h
index 51e51c860fe6..81d32f639190 100644
--- a/include/dt-bindings/clock/qcom,gcc-msm8974.h
+++ b/include/dt-bindings/clock/qcom,gcc-msm8974.h
@@ -321,4 +321,7 @@
#define GCC_SDCC1_CDCCAL_SLEEP_CLK 304
#define GCC_SDCC1_CDCCAL_FF_CLK 305
+/* gdscs */
+#define USB_HS_HSIC_GDSC 0
+
#endif
diff --git a/include/dt-bindings/clock/qcom,mmcc-msm8974.h b/include/dt-bindings/clock/qcom,mmcc-msm8974.h
index 032ed87ef0f3..28651e54c9ae 100644
--- a/include/dt-bindings/clock/qcom,mmcc-msm8974.h
+++ b/include/dt-bindings/clock/qcom,mmcc-msm8974.h
@@ -158,4 +158,12 @@
#define SPDM_RM_AXI 141
#define SPDM_RM_OCMEMNOC 142
+/* gdscs */
+#define VENUS0_GDSC 0
+#define MDSS_GDSC 1
+#define CAMSS_JPEG_GDSC 2
+#define CAMSS_VFE_GDSC 3
+#define OXILI_GDSC 4
+#define OXILICX_GDSC 5
+
#endif
diff --git a/include/dt-bindings/reset/qcom,gcc-msm8916.h b/include/dt-bindings/reset/qcom,gcc-msm8916.h
new file mode 100644
index 000000000000..3d90410f09c7
--- /dev/null
+++ b/include/dt-bindings/reset/qcom,gcc-msm8916.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 Linaro Limited
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _DT_BINDINGS_RESET_MSM_GCC_8916_H
+#define _DT_BINDINGS_RESET_MSM_GCC_8916_H
+
+#define GCC_BLSP1_BCR 0
+#define GCC_BLSP1_QUP1_BCR 1
+#define GCC_BLSP1_UART1_BCR 2
+#define GCC_BLSP1_QUP2_BCR 3
+#define GCC_BLSP1_UART2_BCR 4
+#define GCC_BLSP1_QUP3_BCR 5
+#define GCC_BLSP1_QUP4_BCR 6
+#define GCC_BLSP1_QUP5_BCR 7
+#define GCC_BLSP1_QUP6_BCR 8
+#define GCC_IMEM_BCR 9
+#define GCC_SMMU_BCR 10
+#define GCC_APSS_TCU_BCR 11
+#define GCC_SMMU_XPU_BCR 12
+#define GCC_PCNOC_TBU_BCR 13
+#define GCC_PRNG_BCR 14
+#define GCC_BOOT_ROM_BCR 15
+#define GCC_CRYPTO_BCR 16
+#define GCC_SEC_CTRL_BCR 17
+#define GCC_AUDIO_CORE_BCR 18
+#define GCC_ULT_AUDIO_BCR 19
+#define GCC_DEHR_BCR 20
+#define GCC_SYSTEM_NOC_BCR 21
+#define GCC_PCNOC_BCR 22
+#define GCC_TCSR_BCR 23
+#define GCC_QDSS_BCR 24
+#define GCC_DCD_BCR 25
+#define GCC_MSG_RAM_BCR 26
+#define GCC_MPM_BCR 27
+#define GCC_SPMI_BCR 28
+#define GCC_SPDM_BCR 29
+#define GCC_MM_SPDM_BCR 30
+#define GCC_BIMC_BCR 31
+#define GCC_RBCPR_BCR 32
+#define GCC_TLMM_BCR 33
+#define GCC_USB_HS_BCR 34
+#define GCC_USB2A_PHY_BCR 35
+#define GCC_SDCC1_BCR 36
+#define GCC_SDCC2_BCR 37
+#define GCC_PDM_BCR 38
+#define GCC_SNOC_BUS_TIMEOUT0_BCR 39
+#define GCC_PCNOC_BUS_TIMEOUT0_BCR 40
+#define GCC_PCNOC_BUS_TIMEOUT1_BCR 41
+#define GCC_PCNOC_BUS_TIMEOUT2_BCR 42
+#define GCC_PCNOC_BUS_TIMEOUT3_BCR 43
+#define GCC_PCNOC_BUS_TIMEOUT4_BCR 44
+#define GCC_PCNOC_BUS_TIMEOUT5_BCR 45
+#define GCC_PCNOC_BUS_TIMEOUT6_BCR 46
+#define GCC_PCNOC_BUS_TIMEOUT7_BCR 47
+#define GCC_PCNOC_BUS_TIMEOUT8_BCR 48
+#define GCC_PCNOC_BUS_TIMEOUT9_BCR 49
+#define GCC_MMSS_BCR 50
+#define GCC_VENUS0_BCR 51
+#define GCC_MDSS_BCR 52
+#define GCC_CAMSS_PHY0_BCR 53
+#define GCC_CAMSS_CSI0_BCR 54
+#define GCC_CAMSS_CSI0PHY_BCR 55
+#define GCC_CAMSS_CSI0RDI_BCR 56
+#define GCC_CAMSS_CSI0PIX_BCR 57
+#define GCC_CAMSS_PHY1_BCR 58
+#define GCC_CAMSS_CSI1_BCR 59
+#define GCC_CAMSS_CSI1PHY_BCR 60
+#define GCC_CAMSS_CSI1RDI_BCR 61
+#define GCC_CAMSS_CSI1PIX_BCR 62
+#define GCC_CAMSS_ISPIF_BCR 63
+#define GCC_CAMSS_CCI_BCR 64
+#define GCC_CAMSS_MCLK0_BCR 65
+#define GCC_CAMSS_MCLK1_BCR 66
+#define GCC_CAMSS_GP0_BCR 67
+#define GCC_CAMSS_GP1_BCR 68
+#define GCC_CAMSS_TOP_BCR 69
+#define GCC_CAMSS_MICRO_BCR 70
+#define GCC_CAMSS_JPEG_BCR 71
+#define GCC_CAMSS_VFE_BCR 72
+#define GCC_CAMSS_CSI_VFE0_BCR 73
+#define GCC_OXILI_BCR 74
+#define GCC_GMEM_BCR 75
+#define GCC_CAMSS_AHB_BCR 76
+#define GCC_MDP_TBU_BCR 77
+#define GCC_GFX_TBU_BCR 78
+#define GCC_GFX_TCU_BCR 79
+#define GCC_MSS_TBU_AXI_BCR 80
+#define GCC_MSS_TBU_GSS_AXI_BCR 81
+#define GCC_MSS_TBU_Q6_AXI_BCR 82
+#define GCC_GTCU_AHB_BCR 83
+#define GCC_SMMU_CFG_BCR 84
+#define GCC_VFE_TBU_BCR 85
+#define GCC_VENUS_TBU_BCR 86
+#define GCC_JPEG_TBU_BCR 87
+#define GCC_PRONTO_TBU_BCR 88
+#define GCC_SMMU_CATS_BCR 89
+
+#endif
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 5591ea71a8d1..f7a6f60f8e0c 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -409,7 +409,7 @@ void clk_unregister_divider(struct clk *clk);
struct clk_mux {
struct clk_hw hw;
void __iomem *reg;
- u32 *table;
+ unsigned int *table;
u32 mask;
u8 shift;
u8 flags;
@@ -425,6 +425,11 @@ struct clk_mux {
extern const struct clk_ops clk_mux_ops;
extern const struct clk_ops clk_mux_ro_ops;
+unsigned int clk_mux_get_parent(struct clk_hw *hw, unsigned int val,
+ unsigned int *table, unsigned long flags);
+unsigned int clk_mux_reindex(u8 index, unsigned int *table,
+ unsigned long flags);
+
struct clk *clk_register_mux(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
@@ -433,7 +438,7 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
struct clk *clk_register_mux_table(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u32 mask,
- u8 clk_mux_flags, u32 *table, spinlock_t *lock);
+ u8 clk_mux_flags, unsigned int *table, spinlock_t *lock);
void clk_unregister_mux(struct clk *clk);
diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index e8cc45307f8f..96b7073ad452 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -285,6 +285,7 @@ phys_addr_t memblock_end_of_DRAM(void);
void memblock_enforce_memory_limit(phys_addr_t memory_limit);
int memblock_is_memory(phys_addr_t addr);
int memblock_is_region_memory(phys_addr_t base, phys_addr_t size);
+int memblock_overlaps_memory(phys_addr_t base, phys_addr_t size);
int memblock_is_reserved(phys_addr_t addr);
int memblock_is_region_reserved(phys_addr_t base, phys_addr_t size);
diff --git a/include/linux/mfd/pm8921-core.h b/include/linux/mfd/pm8921-core.h
new file mode 100644
index 000000000000..b31d785761f4
--- /dev/null
+++ b/include/linux/mfd/pm8921-core.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, Sony Mobile Communications AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef __MFD_PM8921_CORE_H
+#define __MFD_PM8921_CORE_H
+
+#include <linux/err.h>
+
+#if IS_ENABLED(CONFIG_MFD_PM8921_CORE)
+
+int pm8xxx_read_irq_status(int irq);
+
+#else
+static inline int pm8xxx_read_irq_status(int irq)
+{
+ return -ENOSYS;
+}
+
+#endif
+
+#endif
diff --git a/include/linux/mfd/qcom_rpm.h b/include/linux/mfd/qcom_rpm.h
index 742ebf1b76ca..a7af2edf0f64 100644
--- a/include/linux/mfd/qcom_rpm.h
+++ b/include/linux/mfd/qcom_rpm.h
@@ -9,5 +9,6 @@ struct qcom_rpm;
#define QCOM_RPM_SLEEP_STATE 1
int qcom_rpm_write(struct qcom_rpm *rpm, int state, int resource, u32 *buf, size_t count);
+int qcom_rpm_read(struct qcom_rpm *rpm, int resource, u32 *buf, size_t count);
#endif
diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
new file mode 100644
index 000000000000..d9a56d7c0206
--- /dev/null
+++ b/include/soc/qcom/pm.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __QCOM_PM_H
+#define __QCOM_PM_H
+
+enum pm_sleep_mode {
+ PM_SLEEP_MODE_STBY,
+ PM_SLEEP_MODE_RET,
+ PM_SLEEP_MODE_SPC,
+ PM_SLEEP_MODE_PC,
+ PM_SLEEP_MODE_NR,
+};
+
+struct qcom_cpu_pm_ops {
+ int (*standby)(void *data);
+ int (*spc)(void *data);
+};
+
+#endif /* __QCOM_PM_H */
diff --git a/arch/arm/mach-qcom/scm-boot.h b/include/soc/qcom/scm-boot.h
index 3e210fb818bb..5043e793f3aa 100644
--- a/arch/arm/mach-qcom/scm-boot.h
+++ b/include/soc/qcom/scm-boot.h
@@ -16,11 +16,13 @@
#define SCM_FLAG_COLDBOOT_CPU1 0x01
#define SCM_FLAG_COLDBOOT_CPU2 0x08
#define SCM_FLAG_COLDBOOT_CPU3 0x20
+
#define SCM_FLAG_WARMBOOT_CPU0 0x04
#define SCM_FLAG_WARMBOOT_CPU1 0x02
#define SCM_FLAG_WARMBOOT_CPU2 0x10
#define SCM_FLAG_WARMBOOT_CPU3 0x40
-int scm_set_boot_addr(u32 addr, int flags);
+int scm_set_boot_addr(phys_addr_t addr, int flags);
+int scm_set_warm_boot_addr(void *entry, int cpu);
#endif
diff --git a/arch/arm/mach-qcom/scm.h b/include/soc/qcom/scm.h
index 00b31ea58f29..e486266c2009 100644
--- a/arch/arm/mach-qcom/scm.h
+++ b/include/soc/qcom/scm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2014, The Linux Foundation. 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 version 2 and
@@ -14,12 +14,26 @@
#define SCM_SVC_BOOT 0x1
#define SCM_SVC_PIL 0x2
+#define SCM_SVC_UTIL 0x3
+#define SCM_SVC_TZ 0x4
+#define SCM_SVC_IO 0x5
+#define SCM_SVC_INFO 0x6
+#define SCM_SVC_SSD 0x7
+#define SCM_SVC_FUSE 0x8
+#define SCM_SVC_PWR 0x9
+#define SCM_SVC_CP 0xC
+#define SCM_SVC_DCVS 0xD
+#define SCM_SVC_TZSCHEDULER 0xFC
extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
void *resp_buf, size_t resp_len);
+extern s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1);
+extern s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2);
+
#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
extern u32 scm_get_version(void);
+extern int scm_is_call_available(u32 svc_id, u32 cmd_id);
#endif
diff --git a/mm/memblock.c b/mm/memblock.c
index 252b77bdf65e..d02ae8f9599b 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -1437,6 +1437,12 @@ int __init_memblock memblock_is_region_memory(phys_addr_t base, phys_addr_t size
memblock.memory.regions[idx].size) >= end;
}
+int __init_memblock memblock_overlaps_memory(phys_addr_t base, phys_addr_t size)
+{
+ memblock_cap_size(base, &size);
+ return memblock_overlaps_region(&memblock.memory, base, size) >= 0;
+}
+
/**
* memblock_is_region_reserved - check if a region intersects reserved memory
* @base: base of region to check
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 40e29429e7b0..4e7e4b949b0a 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4953,6 +4953,8 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
{
+ unsigned long __maybe_unused offset = 0;
+
/* Skip empty nodes */
if (!pgdat->node_spanned_pages)
return;
@@ -4969,6 +4971,7 @@ static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
* for the buddy allocator to function correctly.
*/
start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);
+ offset = pgdat->node_start_pfn - start;
end = pgdat_end_pfn(pgdat);
end = ALIGN(end, MAX_ORDER_NR_PAGES);
size = (end - start) * sizeof(struct page);
@@ -4976,7 +4979,7 @@ static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
if (!map)
map = memblock_virt_alloc_node_nopanic(size,
pgdat->node_id);
- pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
+ pgdat->node_mem_map = map + offset;
}
#ifndef CONFIG_NEED_MULTIPLE_NODES
/*
@@ -4984,10 +4987,12 @@ static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
*/
if (pgdat == NODE_DATA(0)) {
mem_map = NODE_DATA(0)->node_mem_map;
-#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
- if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
- mem_map -= (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
-#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
+#if defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP) || defined(CONFIG_FLATMEM)
+ if (page_to_pfn(mem_map) != pgdat->node_start_pfn) {
+ mem_map -= offset;
+ VM_BUG_ON(page_to_pfn(mem_map) != pgdat->node_start_pfn);
+ }
+#endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP || CONFIG_FLATMEM */
}
#endif
#endif /* CONFIG_FLAT_NODE_MEM_MAP */