diff options
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, ®); + 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, ®); + 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, ®_cntl); + + regmap_read(base, TSENS_TH_ADDR, ®_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, ®); + 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, ®); + 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, ®_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, ®_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, ®_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, ®_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 */ |