diff options
Diffstat (limited to 'target/linux/ipq806x/patches-4.1')
31 files changed, 6620 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch b/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch new file mode 100644 index 0000000000..c0ad74c492 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/020-add-ap148-bootargs.patch @@ -0,0 +1,46 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -14,6 +14,14 @@ + }; + }; + ++ alias { ++ serial0 = &uart4; ++ }; ++ ++ chosen { ++ linux,stdout-path = "serial0:115200n8"; ++ }; ++ + soc { + pinmux@800000 { + i2c4_pins: i2c4_pinmux { +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -159,7 +159,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@12490000 { ++ uart2: serial@12490000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x12490000 0x1000>, + <0x12480000 0x1000>; +@@ -197,7 +197,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@16340000 { ++ uart4: serial@16340000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x16340000 0x1000>, + <0x16300000 0x1000>; +@@ -234,7 +234,7 @@ + + syscon-tcsr = <&tcsr>; + +- serial@1a240000 { ++ uart5: serial@1a240000 { + compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm"; + reg = <0x1a240000 0x1000>, + <0x1a200000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch b/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch new file mode 100644 index 0000000000..34eb9c0dfa --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/021-add-ap148-partitions.patch @@ -0,0 +1,35 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -78,13 +78,28 @@ + reg = <0>; + + partition@0 { +- label = "rootfs"; +- reg = <0x0 0x1000000>; ++ label = "lowlevel_init"; ++ reg = <0x0 0x1b0000>; + }; + + partition@1 { +- label = "scratch"; +- reg = <0x1000000 0x1000000>; ++ label = "u-boot"; ++ reg = <0x1b0000 0x80000>; ++ }; ++ ++ partition@2 { ++ label = "u-boot-env"; ++ reg = <0x230000 0x40000>; ++ }; ++ ++ partition@3 { ++ label = "caldata"; ++ reg = <0x270000 0x40000>; ++ }; ++ ++ partition@4 { ++ label = "firmware"; ++ reg = <0x2b0000 0x1d50000>; + }; + }; + }; diff --git a/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch b/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch new file mode 100644 index 0000000000..7d8c8e8a81 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/022-add-db149-dts.patch @@ -0,0 +1,160 @@ +From f26cc3733bdd697bd81ae505fc133fa7c9b6ea19 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Tue, 7 Apr 2015 19:58:58 -0700 +Subject: [PATCH] ARM: dts: qcom: add initial DB149 device-tree + +Add basic DB149 (IPQ806x based platform) device-tree. It supports UART, +SATA, USB2, USB3 and NOR flash. + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 132 +++++++++++++++++++++++++++++++ + 2 files changed, 133 insertions(+) + create mode 100644 arch/arm/boot/dts/qcom-ipq8064-db149.dts + +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -451,6 +451,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \ + qcom-apq8084-ifc6540.dtb \ + qcom-apq8084-mtp.dtb \ + qcom-ipq8064-ap148.dtb \ ++ qcom-ipq8064-db149.dtb \ + qcom-msm8660-surf.dtb \ + qcom-msm8960-cdp.dtb \ + qcom-msm8974-sony-xperia-honami.dtb +--- /dev/null ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -0,0 +1,132 @@ ++#include "qcom-ipq8064-v1.0.dtsi" ++ ++/ { ++ model = "Qualcomm IPQ8064/DB149"; ++ compatible = "qcom,ipq8064-db149", "qcom,ipq8064"; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ rsvd@41200000 { ++ reg = <0x41200000 0x300000>; ++ no-map; ++ }; ++ }; ++ ++ alias { ++ serial0 = &uart2; ++ }; ++ ++ chosen { ++ linux,stdout-path = "serial0:115200n8"; ++ }; ++ ++ soc { ++ pinmux@800000 { ++ i2c4_pins: i2c4_pinmux { ++ pins = "gpio12", "gpio13"; ++ function = "gsbi4"; ++ bias-disable; ++ }; ++ ++ spi_pins: spi_pins { ++ mux { ++ pins = "gpio18", "gpio19", "gpio21"; ++ function = "gsbi5"; ++ drive-strength = <10>; ++ bias-none; ++ }; ++ }; ++ }; ++ ++ gsbi2: gsbi@12480000 { ++ qcom,mode = <GSBI_PROT_I2C_UART>; ++ status = "ok"; ++ uart2: serial@12490000 { ++ status = "ok"; ++ }; ++ }; ++ ++ gsbi5: gsbi@1a200000 { ++ qcom,mode = <GSBI_PROT_SPI>; ++ status = "ok"; ++ ++ spi4: spi@1a280000 { ++ status = "ok"; ++ spi-max-frequency = <50000000>; ++ ++ pinctrl-0 = <&spi_pins>; ++ pinctrl-names = "default"; ++ ++ cs-gpios = <&qcom_pinmux 20 0>; ++ ++ flash: m25p80@0 { ++ compatible = "s25fl256s1"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ spi-max-frequency = <50000000>; ++ reg = <0>; ++ m25p,fast-read; ++ ++ partition@0 { ++ label = "lowlevel_init"; ++ reg = <0x0 0x1b0000>; ++ }; ++ ++ partition@1 { ++ label = "u-boot"; ++ reg = <0x1b0000 0x80000>; ++ }; ++ ++ partition@2 { ++ label = "u-boot-env"; ++ reg = <0x230000 0x40000>; ++ }; ++ ++ partition@3 { ++ label = "caldata"; ++ reg = <0x270000 0x40000>; ++ }; ++ ++ partition@4 { ++ label = "firmware"; ++ reg = <0x2b0000 0x1d50000>; ++ }; ++ }; ++ }; ++ }; ++ ++ sata-phy@1b400000 { ++ status = "ok"; ++ }; ++ ++ sata@29000000 { ++ status = "ok"; ++ }; ++ ++ phy@100f8800 { /* USB3 port 1 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@100f8830 { /* USB3 port 1 SS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8800 { /* USB3 port 0 HS phy */ ++ status = "ok"; ++ }; ++ ++ phy@110f8830 { /* USB3 port 0 SS phy */ ++ status = "ok"; ++ }; ++ ++ usb30@0 { ++ status = "ok"; ++ }; ++ ++ usb30@1 { ++ status = "ok"; ++ }; ++ }; ++}; diff --git a/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch b/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch new file mode 100644 index 0000000000..6b296377b6 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/023-ARM-dts-ipq806x-Disable-i2c-device-on-gsbi4.patch @@ -0,0 +1,53 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -46,15 +46,12 @@ + serial@16340000 { + status = "ok"; + }; +- +- i2c4: i2c@16380000 { +- status = "ok"; +- +- clock-frequency = <200000>; +- +- pinctrl-0 = <&i2c4_pins>; +- pinctrl-names = "default"; +- }; ++ /* ++ * The i2c device on gsbi4 should not be enabled. ++ * On ipq806x designs gsbi4 i2c is meant for exclusive ++ * RPM usage. Turning this on in kernel manifests as ++ * i2c failure for the RPM. ++ */ + }; + + gsbi5: gsbi@1a200000 { +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -807,7 +807,7 @@ static struct clk_rcg gsbi7_qup_src = { + .parent_names = gcc_pxo_pll8, + .num_parents = 2, + .ops = &clk_rcg_ops, +- .flags = CLK_SET_PARENT_GATE, ++ .flags = CLK_SET_PARENT_GATE | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -823,7 +823,7 @@ static struct clk_branch gsbi7_qup_clk = + .parent_names = (const char *[]){ "gsbi7_qup_src" }, + .num_parents = 1, + .ops = &clk_branch_ops, +- .flags = CLK_SET_RATE_PARENT, ++ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, + }, + }; +@@ -871,7 +871,7 @@ static struct clk_branch gsbi4_h_clk = { + .hw.init = &(struct clk_init_data){ + .name = "gsbi4_h_clk", + .ops = &clk_branch_ops, +- .flags = CLK_IS_ROOT, ++ .flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED, + }, + }, + }; diff --git a/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch b/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch new file mode 100644 index 0000000000..41f91fae7c --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/110-DT-PCI-qcom-Document-PCIe-devicetree-bindings.patch @@ -0,0 +1,263 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,3/5] DT: PCI: qcom: Document PCIe devicetree bindings +From: Stanimir Varbanov <svarbanov@mm-sol.com> +X-Patchwork-Id: 6326181 +Message-Id: <1430743338-10441-4-git-send-email-svarbanov@mm-sol.com> +To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>, + Mark Rutland <mark.rutland@arm.com>, + Grant Likely <grant.likely@linaro.org>, + Bjorn Helgaas <bhelgaas@google.com>, + Kishon Vijay Abraham I <kishon@ti.com>, + Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de> +Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, + linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>, + Srinivas Kandagatla <srinivas.kandagatla@linaro.org>, + Stanimir Varbanov <svarbanov@mm-sol.com> +Date: Mon, 4 May 2015 15:42:16 +0300 + +Document Qualcomm PCIe driver devicetree bindings. + +Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> + +--- +.../devicetree/bindings/pci/qcom,pcie.txt | 231 ++++++++++++++++++++ + 1 files changed, 231 insertions(+), 0 deletions(-) + create mode 100644 Documentation/devicetree/bindings/pci/qcom,pcie.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt +@@ -0,0 +1,231 @@ ++* Qualcomm PCI express root complex ++ ++- compatible: ++ Usage: required ++ Value type: <stringlist> ++ Definition: Value shall include ++ - "qcom,pcie-v0" for apq/ipq8064 ++ - "qcom,pcie-v1" for apq8084 ++ ++- reg: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: Register ranges as listed in the reg-names property ++ ++- reg-names: ++ Usage: required ++ Value type: <stringlist> ++ Definition: Must include the following entries ++ - "parf" Qualcomm specific registers ++ - "dbi" Designware PCIe registers ++ - "elbi" External local bus interface registers ++ - "config" PCIe configuration space ++ ++- device_type: ++ Usage: required ++ Value type: <string> ++ Definition: Should be "pci". As specified in designware-pcie.txt ++ ++- #address-cells: ++ Usage: required ++ Value type: <u32> ++ Definition: Should be set to 3. As specified in designware-pcie.txt ++ ++- #size-cells: ++ Usage: required ++ Value type: <u32> ++ Definition: Should be set 2. As specified in designware-pcie.txt ++ ++- ranges: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: As specified in designware-pcie.txt ++ ++- interrupts: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: MSI interrupt ++ ++- interrupt-names: ++ Usage: required ++ Value type: <stringlist> ++ Definition: Should contain "msi" ++ ++- #interrupt-cells: ++ Usage: required ++ Value type: <u32> ++ Definition: Should be 1. As specified in designware-pcie.txt ++ ++- interrupt-map-mask: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: As specified in designware-pcie.txt ++ ++- interrupt-map: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: As specified in designware-pcie.txt ++ ++- clocks: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: List of phandle and clock specifier pairs as listed ++ in clock-names property ++ ++- clock-names: ++ Usage: required ++ Value type: <stringlist> ++ Definition: Should contain the following entries ++ * should be populated for v0 and v1 ++ - "iface" Configuration AHB clock ++ ++ * should be populated for v0 ++ - "core" Clocks the pcie hw block ++ - "phy" Clocks the pcie PHY block ++ ++ * should be populated for v1 ++ - "aux" Auxiliary (AUX) clock ++ - "bus_master" Master AXI clock ++ - "bus_slave" Slave AXI clock ++ ++- resets: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: List of phandle and reset specifier pairs as listed ++ in reset-names property ++ ++- reset-names: ++ Usage: required ++ Value type: <stringlist> ++ Definition: Should contain the following entries ++ * should be populated for v0 ++ - "axi" AXI reset ++ - "ahb" AHB reset ++ - "por" POR reset ++ - "pci" PCI reset ++ - "phy" PHY reset ++ ++ * should be populated for v1 ++ - "core" Core reset ++ ++- power-domains: ++ Usage: required (for v1 only) ++ Value type: <prop-encoded-array> ++ Definition: A phandle and power domain specifier pair to the ++ power domain which is responsible for collapsing ++ and restoring power to the peripheral ++ ++- <name>-supply: ++ Usage: required ++ Value type: <phandle> ++ Definition: List of phandles to the power supply regulator(s) ++ * should be populated for v0 and v1 ++ - "vdda" core analog power supply ++ ++ * should be populated for v0 ++ - "vdda_phy" analog power supply for PHY ++ - "vdda_refclk" analog power supply for IC which generate ++ reference clock ++ ++- phys: ++ Usage: required (for v1 only) ++ Value type: <phandle> ++ Definition: List of phandle(s) as listed in phy-names property ++ ++- phy-names: ++ Usage: required (for v1 only) ++ Value type: <stringlist> ++ Definition: Should contain "pciephy" ++ ++- <name>-gpio: ++ Usage: optional ++ Value type: <prop-encoded-array> ++ Definition: List of phandle and gpio specifier pairs. Should contain ++ - "perst" PCIe endpoint reset signal line ++ - "pewake" PCIe endpoint wake signal line ++ ++- pinctrl-0: ++ Usage: required ++ Value type: <phandle> ++ Definition: List of phandles pointing at a pin(s) configuration ++ ++- pinctrl-names ++ Usage: required ++ Value type: <stringlist> ++ Definition: List of names of pinctrl-0 state ++ ++* Example for v0 ++ pcie0: pci@1b500000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b500000 0x1000 ++ 0x1b502000 0x80 ++ 0x1b600000 0x100 ++ 0x0ff00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* I/O */ ++ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* memory */ ++ interrupts = <GIC_SPI 35 IRQ_TYPE_NONE>; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ clocks = <&gcc PCIE_A_CLK>, ++ <&gcc PCIE_H_CLK>, ++ <&gcc PCIE_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ 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"; ++ }; ++ ++* Example for v1 ++ pcie0@fc520000 { ++ compatible = "qcom,pcie-v1"; ++ reg = <0xfc520000 0x2000>, ++ <0xff000000 0x1000>, ++ <0xff001000 0x1000>, ++ <0xff002000 0x2000>; ++ reg-names = "parf", "dbi", "elbi", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ranges = <0x81000000 0 0 0xff200000 0 0x00100000 /* I/O */ ++ 0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */ ++ interrupts = <GIC_SPI 243 IRQ_TYPE_NONE>; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>, ++ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, ++ <&gcc GCC_PCIE_0_SLV_AXI_CLK>, ++ <&gcc GCC_PCIE_0_AUX_CLK>; ++ clock-names = "iface", "master_bus", "slave_bus", "aux"; ++ resets = <&gcc GCC_PCIE_0_BCR>; ++ reset-names = "core"; ++ power-domains = <&gcc PCIE0_GDSC>; ++ vdda-supply = <&pma8084_l3>; ++ phys = <&pciephy0>; ++ phy-names = "pciephy"; ++ perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>; ++ pinctrl-0 = <&pcie0_pins_default>; ++ pinctrl-names = "default"; ++ }; diff --git a/target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch new file mode 100644 index 0000000000..dbf16a303a --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch @@ -0,0 +1,753 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver +From: Stanimir Varbanov <svarbanov@mm-sol.com> +X-Patchwork-Id: 6326161 +Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com> +To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>, + Mark Rutland <mark.rutland@arm.com>, + Grant Likely <grant.likely@linaro.org>, + Bjorn Helgaas <bhelgaas@google.com>, + Kishon Vijay Abraham I <kishon@ti.com>, + Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de> +Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, + linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, + linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>, + Srinivas Kandagatla <srinivas.kandagatla@linaro.org>, + Stanimir Varbanov <svarbanov@mm-sol.com> +Date: Mon, 4 May 2015 15:42:17 +0300 + +The PCIe driver reuse the Designware common code for host +and MSI initialization, and also program the Qualcomm +application specific registers. + +Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> + +--- +MAINTAINERS | 7 + + drivers/pci/host/Kconfig | 9 + + drivers/pci/host/Makefile | 1 + + drivers/pci/host/pcie-qcom.c | 677 ++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 694 insertions(+), 0 deletions(-) + create mode 100644 drivers/pci/host/pcie-qcom.c + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -7599,6 +7599,13 @@ L: linux-pci@vger.kernel.org + S: Maintained + F: drivers/pci/host/*spear* + ++PCIE DRIVER FOR QUALCOMM MSM ++M: Stanimir Varbanov <svarbanov@mm-sol.com> ++L: linux-pci@vger.kernel.org ++L: linux-arm-msm@vger.kernel.org ++S: Maintained ++F: drivers/pci/host/*qcom* ++ + PCMCIA SUBSYSTEM + P: Linux PCMCIA Team + L: linux-pcmcia@lists.infradead.org +--- a/drivers/pci/host/Kconfig ++++ b/drivers/pci/host/Kconfig +@@ -125,4 +125,13 @@ config PCIE_IPROC_PLATFORM + Say Y here if you want to use the Broadcom iProc PCIe controller + through the generic platform bus interface + ++config PCIE_QCOM ++ bool "Qualcomm PCIe controller" ++ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST) ++ select PCIE_DW ++ select PCIEPORTBUS ++ help ++ Say Y here to enable PCIe controller support on Qualcomm SoCs. The ++ PCIe controller use Designware core plus Qualcomm specific hardware ++ wrappers. + endmenu +--- /dev/null ++++ b/drivers/pci/host/pcie-qcom.c +@@ -0,0 +1,677 @@ ++/* ++ * Copyright (c) 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/clk.h> ++#include <linux/delay.h> ++#include <linux/gpio.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of_gpio.h> ++#include <linux/pci.h> ++#include <linux/platform_device.h> ++#include <linux/phy/phy.h> ++#include <linux/regulator/consumer.h> ++#include <linux/reset.h> ++#include <linux/slab.h> ++#include <linux/types.h> ++ ++#include "pcie-designware.h" ++ ++#define PCIE20_PARF_PHY_CTRL 0x40 ++#define PCIE20_PARF_PHY_REFCLK 0x4C ++#define PCIE20_PARF_DBI_BASE_ADDR 0x168 ++#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c ++#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 ++ ++#define PCIE20_ELBI_SYS_CTRL 0x04 ++#define PCIE20_ELBI_SYS_STTS 0x08 ++#define XMLH_LINK_UP BIT(10) ++ ++#define PCIE20_CAP 0x70 ++#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) ++ ++#define PERST_DELAY_MIN_US 1000 ++#define PERST_DELAY_MAX_US 1005 ++ ++#define LINKUP_DELAY_MIN_US 5000 ++#define LINKUP_DELAY_MAX_US 5100 ++#define LINKUP_RETRIES_COUNT 20 ++ ++#define PCIE_V0 0 /* apq8064 */ ++#define PCIE_V1 1 /* apq8084 */ ++ ++struct qcom_pcie_resources_v0 { ++ struct clk *iface_clk; ++ struct clk *core_clk; ++ struct clk *phy_clk; ++ struct reset_control *pci_reset; ++ struct reset_control *axi_reset; ++ struct reset_control *ahb_reset; ++ struct reset_control *por_reset; ++ struct reset_control *phy_reset; ++ struct regulator *vdda; ++ struct regulator *vdda_phy; ++ struct regulator *vdda_refclk; ++}; ++ ++struct qcom_pcie_resources_v1 { ++ struct clk *iface; ++ struct clk *aux; ++ struct clk *master_bus; ++ struct clk *slave_bus; ++ struct reset_control *core; ++ struct regulator *vdda; ++}; ++ ++union pcie_resources { ++ struct qcom_pcie_resources_v0 v0; ++ struct qcom_pcie_resources_v1 v1; ++}; ++ ++struct qcom_pcie { ++ struct pcie_port pp; ++ struct device *dev; ++ union pcie_resources res; ++ void __iomem *parf; ++ void __iomem *dbi; ++ void __iomem *elbi; ++ struct phy *phy; ++ struct gpio_desc *reset; ++ unsigned int version; ++}; ++ ++#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp) ++ ++static inline void ++writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask) ++{ ++ u32 val = readl(addr); ++ ++ val &= ~clear_mask; ++ val |= set_mask; ++ writel(val, addr); ++} ++ ++static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) ++{ ++ int val, active_low; ++ ++ if (IS_ERR_OR_NULL(pcie->reset)) ++ return; ++ ++ active_low = gpiod_is_active_low(pcie->reset); ++ ++ if (assert) ++ val = !!active_low; ++ else ++ val = !active_low; ++ ++ gpiod_set_value(pcie->reset, val); ++ ++ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); ++} ++ ++static void qcom_ep_reset_assert(struct qcom_pcie *pcie) ++{ ++ qcom_ep_reset_assert_deassert(pcie, 1); ++} ++ ++static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) ++{ ++ qcom_ep_reset_assert_deassert(pcie, 0); ++} ++ ++static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) ++{ ++ struct pcie_port *pp = arg; ++ ++ return dw_handle_msi_irq(pp); ++} ++ ++static int qcom_pcie_link_up(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS); ++ ++ return val & BIT(29) ? 1 : 0; ++} ++ ++static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ ++ reset_control_assert(res->pci_reset); ++ reset_control_assert(res->axi_reset); ++ reset_control_assert(res->ahb_reset); ++ reset_control_assert(res->por_reset); ++ reset_control_assert(res->pci_reset); ++ clk_disable_unprepare(res->iface_clk); ++ clk_disable_unprepare(res->core_clk); ++ clk_disable_unprepare(res->phy_clk); ++ regulator_disable(res->vdda); ++ regulator_disable(res->vdda_phy); ++ regulator_disable(res->vdda_refclk); ++} ++ ++static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ ++ reset_control_assert(res->core); ++ clk_disable_unprepare(res->slave_bus); ++ clk_disable_unprepare(res->master_bus); ++ clk_disable_unprepare(res->iface); ++ clk_disable_unprepare(res->aux); ++ regulator_disable(res->vdda); ++} ++ ++static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ ret = regulator_enable(res->vdda); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda regulator\n"); ++ return ret; ++ } ++ ++ ret = regulator_enable(res->vdda_refclk); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda_refclk regulator\n"); ++ goto err_refclk; ++ } ++ ++ ret = regulator_enable(res->vdda_phy); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda_phy regulator\n"); ++ goto err_vdda_phy; ++ } ++ ++ ret = clk_prepare_enable(res->iface_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable iface clock\n"); ++ goto err_iface; ++ } ++ ++ ret = clk_prepare_enable(res->core_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable core clock\n"); ++ goto err_clk_core; ++ } ++ ++ ret = clk_prepare_enable(res->phy_clk); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable phy clock\n"); ++ goto err_clk_phy; ++ } ++ ++ ret = reset_control_deassert(res->ahb_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert ahb reset\n"); ++ goto err_reset_ahb; ++ } ++ ++ return 0; ++ ++err_reset_ahb: ++ clk_disable_unprepare(res->phy_clk); ++err_clk_phy: ++ clk_disable_unprepare(res->core_clk); ++err_clk_core: ++ clk_disable_unprepare(res->iface_clk); ++err_iface: ++ regulator_disable(res->vdda_phy); ++err_vdda_phy: ++ regulator_disable(res->vdda_refclk); ++err_refclk: ++ regulator_disable(res->vdda); ++ return ret; ++} ++ ++static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ ret = reset_control_deassert(res->core); ++ if (ret) { ++ dev_err(dev, "cannot deassert core reset\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(res->aux); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable aux clock\n"); ++ goto err_res; ++ } ++ ++ ret = clk_prepare_enable(res->iface); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable iface clock\n"); ++ goto err_aux; ++ } ++ ++ ret = clk_prepare_enable(res->master_bus); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable master_bus clock\n"); ++ goto err_iface; ++ } ++ ++ ret = clk_prepare_enable(res->slave_bus); ++ if (ret) { ++ dev_err(dev, "cannot prepare/enable slave_bus clock\n"); ++ goto err_master; ++ } ++ ++ ret = regulator_enable(res->vdda); ++ if (ret) { ++ dev_err(dev, "cannot enable vdda regulator\n"); ++ goto err_slave; ++ } ++ ++ return 0; ++ ++err_slave: ++ clk_disable_unprepare(res->slave_bus); ++err_master: ++ clk_disable_unprepare(res->master_bus); ++err_iface: ++ clk_disable_unprepare(res->iface); ++err_aux: ++ clk_disable_unprepare(res->aux); ++err_res: ++ reset_control_assert(res->core); ++ ++ return ret; ++} ++ ++static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ ++ res->vdda = devm_regulator_get(dev, "vdda"); ++ if (IS_ERR(res->vdda)) ++ return PTR_ERR(res->vdda); ++ ++ res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); ++ if (IS_ERR(res->vdda_phy)) ++ return PTR_ERR(res->vdda_phy); ++ ++ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); ++ if (IS_ERR(res->vdda_refclk)) ++ return PTR_ERR(res->vdda_refclk); ++ ++ res->iface_clk = devm_clk_get(dev, "iface"); ++ if (IS_ERR(res->iface_clk)) ++ return PTR_ERR(res->iface_clk); ++ ++ res->core_clk = devm_clk_get(dev, "core"); ++ if (IS_ERR(res->core_clk)) ++ return PTR_ERR(res->core_clk); ++ ++ res->phy_clk = devm_clk_get(dev, "phy"); ++ if (IS_ERR(res->phy_clk)) ++ return PTR_ERR(res->phy_clk); ++ ++ res->pci_reset = devm_reset_control_get(dev, "pci"); ++ if (IS_ERR(res->pci_reset)) ++ return PTR_ERR(res->pci_reset); ++ ++ res->axi_reset = devm_reset_control_get(dev, "axi"); ++ if (IS_ERR(res->axi_reset)) ++ return PTR_ERR(res->axi_reset); ++ ++ res->ahb_reset = devm_reset_control_get(dev, "ahb"); ++ if (IS_ERR(res->ahb_reset)) ++ return PTR_ERR(res->ahb_reset); ++ ++ res->por_reset = devm_reset_control_get(dev, "por"); ++ if (IS_ERR(res->por_reset)) ++ return PTR_ERR(res->por_reset); ++ ++ res->phy_reset = devm_reset_control_get(dev, "phy"); ++ if (IS_ERR(res->phy_reset)) ++ return PTR_ERR(res->phy_reset); ++ ++ return 0; ++} ++ ++static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) ++{ ++ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; ++ struct device *dev = pcie->dev; ++ ++ res->vdda = devm_regulator_get(dev, "vdda"); ++ if (IS_ERR(res->vdda)) ++ return PTR_ERR(res->vdda); ++ ++ res->iface = devm_clk_get(dev, "iface"); ++ if (IS_ERR(res->iface)) ++ return PTR_ERR(res->iface); ++ ++ res->aux = devm_clk_get(dev, "aux"); ++ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ else if (IS_ERR(res->aux)) ++ res->aux = NULL; ++ ++ res->master_bus = devm_clk_get(dev, "master_bus"); ++ if (IS_ERR(res->master_bus)) ++ return PTR_ERR(res->master_bus); ++ ++ res->slave_bus = devm_clk_get(dev, "slave_bus"); ++ if (IS_ERR(res->slave_bus)) ++ return PTR_ERR(res->slave_bus); ++ ++ res->core = devm_reset_control_get(dev, "core"); ++ if (IS_ERR(res->core)) ++ return PTR_ERR(res->core); ++ ++ return 0; ++} ++ ++static int qcom_pcie_enable_link_training(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ struct device *dev = pp->dev; ++ int retries; ++ u32 val; ++ ++ /* enable link training */ ++ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); ++ ++ /* wait for up to 100ms for the link to come up */ ++ retries = LINKUP_RETRIES_COUNT; ++ do { ++ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS); ++ if (val & XMLH_LINK_UP) ++ break; ++ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US); ++ } while (retries--); ++ ++ if (retries < 0 || !dw_pcie_link_up(pp)) { ++ dev_err(dev, "link initialization failed\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void qcom_pcie_host_init_v1(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ int ret; ++ ++ qcom_ep_reset_assert(pcie); ++ ++ ret = qcom_pcie_enable_resources_v1(pcie); ++ if (ret) ++ return; ++ ++ /* change DBI base address */ ++ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, ++ 0, BIT(31)); ++ ++ ret = phy_init(pcie->phy); ++ if (ret) ++ goto err_res; ++ ++ ret = phy_power_on(pcie->phy); ++ if (ret) ++ goto err_phy; ++ ++ dw_pcie_setup_rc(pp); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ dw_pcie_msi_init(pp); ++ ++ qcom_ep_reset_deassert(pcie); ++ ++ ret = qcom_pcie_enable_link_training(pp); ++ if (ret) ++ goto err; ++ ++ return; ++ ++err: ++ qcom_ep_reset_assert(pcie); ++ phy_power_off(pcie->phy); ++err_phy: ++ phy_exit(pcie->phy); ++err_res: ++ qcom_pcie_disable_resources_v1(pcie); ++} ++ ++static void qcom_pcie_host_init_v0(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; ++ struct device *dev = pcie->dev; ++ int ret; ++ ++ qcom_ep_reset_assert(pcie); ++ ++ ret = qcom_pcie_enable_resources_v0(pcie); ++ if (ret) ++ return; ++ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); ++ ++ /* enable external reference clock */ ++ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); ++ ++ ret = reset_control_deassert(res->phy_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert phy reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->pci_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert pci reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->por_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert por reset\n"); ++ return; ++ } ++ ++ ret = reset_control_deassert(res->axi_reset); ++ if (ret) { ++ dev_err(dev, "cannot deassert axi reset\n"); ++ return; ++ } ++ ++ /* wait 150ms for clock acquisition */ ++ usleep_range(10000, 15000); ++ ++ dw_pcie_setup_rc(pp); ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) ++ dw_pcie_msi_init(pp); ++ ++ qcom_ep_reset_deassert(pcie); ++ ++ ret = qcom_pcie_enable_link_training(pp); ++ if (ret) ++ goto err; ++ ++ return; ++err: ++ qcom_ep_reset_assert(pcie); ++ qcom_pcie_disable_resources_v0(pcie); ++} ++ ++static void qcom_pcie_host_init(struct pcie_port *pp) ++{ ++ struct qcom_pcie *pcie = to_qcom_pcie(pp); ++ ++ if (pcie->version == PCIE_V0) ++ return qcom_pcie_host_init_v0(pp); ++ else ++ return qcom_pcie_host_init_v1(pp); ++} ++ ++static int ++qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) ++{ ++ /* the device class is not reported correctly from the register */ ++ if (where == PCI_CLASS_REVISION && size == 4) { ++ *val = readl(pp->dbi_base + PCI_CLASS_REVISION); ++ *val &= ~(0xffff << 16); ++ *val |= PCI_CLASS_BRIDGE_PCI << 16; ++ return PCIBIOS_SUCCESSFUL; ++ } ++ ++ return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, ++ size, val); ++} ++ ++static struct pcie_host_ops qcom_pcie_ops = { ++ .link_up = qcom_pcie_link_up, ++ .host_init = qcom_pcie_host_init, ++ .rd_own_conf = qcom_pcie_rd_own_conf, ++}; ++ ++static const struct of_device_id qcom_pcie_match[] = { ++ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 }, ++ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 }, ++ { } ++}; ++ ++static int qcom_pcie_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *match; ++ struct resource *res; ++ struct qcom_pcie *pcie; ++ struct pcie_port *pp; ++ int ret; ++ ++ match = of_match_node(qcom_pcie_match, dev->of_node); ++ if (!match) ++ return -ENXIO; ++ ++ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); ++ if (!pcie) ++ return -ENOMEM; ++ ++ pcie->version = (unsigned int)match->data; ++ ++ pcie->reset = devm_gpiod_get_optional(dev, "perst"); ++ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER) ++ return PTR_ERR(pcie->reset); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); ++ pcie->parf = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->parf)) ++ return PTR_ERR(pcie->parf); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); ++ pcie->dbi = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->dbi)) ++ return PTR_ERR(pcie->dbi); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); ++ pcie->elbi = devm_ioremap_resource(dev, res); ++ if (IS_ERR(pcie->elbi)) ++ return PTR_ERR(pcie->elbi); ++ ++ pcie->phy = devm_phy_optional_get(dev, "pciephy"); ++ if (IS_ERR(pcie->phy)) ++ return PTR_ERR(pcie->phy); ++ ++ pcie->dev = dev; ++ ++ if (pcie->version == PCIE_V0) ++ ret = qcom_pcie_get_resources_v0(pcie); ++ else ++ ret = qcom_pcie_get_resources_v1(pcie); ++ ++ if (ret) ++ return ret; ++ ++ pp = &pcie->pp; ++ pp->dev = dev; ++ pp->dbi_base = pcie->dbi; ++ pp->root_bus_nr = -1; ++ pp->ops = &qcom_pcie_ops; ++ ++ if (IS_ENABLED(CONFIG_PCI_MSI)) { ++ pp->msi_irq = platform_get_irq_byname(pdev, "msi"); ++ if (pp->msi_irq < 0) { ++ dev_err(dev, "cannot get msi irq\n"); ++ return pp->msi_irq; ++ } ++ ++ ret = devm_request_irq(dev, pp->msi_irq, ++ qcom_pcie_msi_irq_handler, ++ IRQF_SHARED, "qcom-pcie-msi", pp); ++ if (ret) { ++ dev_err(dev, "cannot request msi irq\n"); ++ return ret; ++ } ++ } ++ ++ ret = dw_pcie_host_init(pp); ++ if (ret) { ++ dev_err(dev, "cannot initialize host\n"); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, pcie); ++ ++ return 0; ++} ++ ++static int qcom_pcie_remove(struct platform_device *pdev) ++{ ++ struct qcom_pcie *pcie = platform_get_drvdata(pdev); ++ ++ qcom_ep_reset_assert(pcie); ++ phy_power_off(pcie->phy); ++ phy_exit(pcie->phy); ++ if (pcie->version == PCIE_V0) ++ qcom_pcie_disable_resources_v0(pcie); ++ else ++ qcom_pcie_disable_resources_v1(pcie); ++ ++ return 0; ++} ++ ++static struct platform_driver qcom_pcie_driver = { ++ .probe = qcom_pcie_probe, ++ .remove = qcom_pcie_remove, ++ .driver = { ++ .name = "qcom-pcie", ++ .of_match_table = qcom_pcie_match, ++ }, ++}; ++ ++module_platform_driver(qcom_pcie_driver); ++ ++MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>"); ++MODULE_DESCRIPTION("Qualcomm PCIe root complex driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-pcie"); +--- a/drivers/pci/host/Makefile ++++ b/drivers/pci/host/Makefile +@@ -15,3 +15,4 @@ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-laye + obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o + obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o + obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o ++obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o diff --git a/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch new file mode 100644 index 0000000000..e7de2bb097 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/112-ARM-dts-qcom-add-pcie-nodes-to-ipq806x-platforms.patch @@ -0,0 +1,260 @@ +From 5b40516b2f5fb9b2a7d6d3e2e924f12ec9d183a8 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Tue, 21 Apr 2015 19:01:42 -0700 +Subject: [PATCH 8/9] ARM: dts: qcom: add pcie nodes to ipq806x platforms + +qcom-pcie driver now supports version 0 of the controller. This change +adds the corresponding entries to the IPQ806x dtsi file and +corresponding platform (AP148). + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 30 ++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 124 +++++++++++++++++++++++++++++++ + 2 files changed, 154 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -30,6 +30,22 @@ + bias-disable; + }; + ++ pcie1_pins: pcie1_pinmux { ++ mux { ++ pins = "gpio3"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie2_pins: pcie2_pinmux { ++ mux { ++ pins = "gpio48"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; +@@ -109,5 +125,19 @@ + sata@29000000 { + status = "ok"; + }; ++ ++ pcie0: pci@1b500000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 3 0>; ++ pinctrl-0 = <&pcie1_pins>; ++ pinctrl-names = "default"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 48 0>; ++ pinctrl-0 = <&pcie2_pins>; ++ pinctrl-names = "default"; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -30,6 +30,30 @@ + bias-disable; + }; + ++ pcie1_pins: pcie1_pinmux { ++ mux { ++ pins = "gpio3"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie2_pins: pcie2_pinmux { ++ mux { ++ pins = "gpio48"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ ++ pcie3_pins: pcie3_pinmux { ++ mux { ++ pins = "gpio63"; ++ drive-strength = <2>; ++ bias-disable; ++ }; ++ }; ++ + spi_pins: spi_pins { + mux { + pins = "gpio18", "gpio19", "gpio21"; +@@ -128,5 +152,26 @@ + usb30@1 { + status = "ok"; + }; ++ ++ pcie0: pci@1b500000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 3 0>; ++ pinctrl-0 = <&pcie1_pins>; ++ pinctrl-names = "default"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 48 0>; ++ pinctrl-0 = <&pcie2_pins>; ++ pinctrl-names = "default"; ++ }; ++ ++ pcie2: pci@1b900000 { ++ status = "ok"; ++ reset-gpio = <&qcom_pinmux 63 0>; ++ pinctrl-0 = <&pcie3_pins>; ++ pinctrl-names = "default"; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -4,6 +4,8 @@ + #include <dt-bindings/clock/qcom,gcc-ipq806x.h> + #include <dt-bindings/clock/qcom,lcc-ipq806x.h> + #include <dt-bindings/soc/qcom,gsbi.h> ++#include <dt-bindings/reset/qcom,gcc-ipq806x.h> ++#include <include/dt-bindings/interrupt-controller/arm-gic.h> + + / { + model = "Qualcomm IPQ8064"; +@@ -329,5 +331,128 @@ + #reset-cells = <1>; + }; + ++ pcie0: pci@1b500000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b500000 0x1000 ++ 0x1b502000 0x80 ++ 0x1b600000 0x100 ++ 0x0ff00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <0>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x00000000 0x08000000 0 0x07e00000>; /* non-prefetchable memory */ ++ ++ interrupts = <GIC_SPI 35 IRQ_TYPE_NONE>; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_A_CLK>, ++ <&gcc PCIE_H_CLK>, ++ <&gcc PCIE_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ 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"; ++ ++ status = "disabled"; ++ }; ++ ++ pcie1: pci@1b700000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b700000 0x1000 ++ 0x1b702000 0x80 ++ 0x1b800000 0x100 ++ 0x31f00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <1>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0 0x31e00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x00000000 0x2e000000 0 0x03e00000>; /* non-prefetchable memory */ ++ ++ interrupts = <GIC_SPI 57 IRQ_TYPE_NONE>; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 58 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 59 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 60 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 61 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_1_A_CLK>, ++ <&gcc PCIE_1_H_CLK>, ++ <&gcc PCIE_1_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ resets = <&gcc PCIE_1_ACLK_RESET>, ++ <&gcc PCIE_1_HCLK_RESET>, ++ <&gcc PCIE_1_POR_RESET>, ++ <&gcc PCIE_1_PCI_RESET>, ++ <&gcc PCIE_1_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ status = "disabled"; ++ }; ++ ++ pcie2: pci@1b900000 { ++ compatible = "qcom,pcie-v0"; ++ reg = <0x1b900000 0x1000 ++ 0x1b902000 0x80 ++ 0x1ba00000 0x100 ++ 0x35f00000 0x100000>; ++ reg-names = "dbi", "elbi", "parf", "config"; ++ device_type = "pci"; ++ linux,pci-domain = <2>; ++ bus-range = <0x00 0xff>; ++ num-lanes = <1>; ++ #address-cells = <3>; ++ #size-cells = <2>; ++ ++ ranges = <0x81000000 0 0 0x35e00000 0 0x00100000 /* downstream I/O */ ++ 0x82000000 0 0x00000000 0x32000000 0 0x03e00000>; /* non-prefetchable memory */ ++ ++ interrupts = <GIC_SPI 71 IRQ_TYPE_NONE>; ++ interrupt-names = "msi"; ++ #interrupt-cells = <1>; ++ interrupt-map-mask = <0 0 0 0x7>; ++ interrupt-map = <0 0 0 1 &intc 0 72 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ ++ <0 0 0 2 &intc 0 73 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ ++ <0 0 0 3 &intc 0 74 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ ++ <0 0 0 4 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ ++ ++ clocks = <&gcc PCIE_2_A_CLK>, ++ <&gcc PCIE_2_H_CLK>, ++ <&gcc PCIE_2_PHY_CLK>; ++ clock-names = "core", "iface", "phy"; ++ ++ resets = <&gcc PCIE_2_ACLK_RESET>, ++ <&gcc PCIE_2_HCLK_RESET>, ++ <&gcc PCIE_2_POR_RESET>, ++ <&gcc PCIE_2_PCI_RESET>, ++ <&gcc PCIE_2_PHY_RESET>; ++ reset-names = "axi", "ahb", "por", "pci", "phy"; ++ ++ status = "disabled"; ++ }; ++ + }; + }; diff --git a/target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch b/target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch new file mode 100644 index 0000000000..e2d31354ed --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/113-ARM-qcom-automatically-select-PCI_DOMAINS-if-PCI-is-.patch @@ -0,0 +1,29 @@ +From f004aa1dec6e2e206be025de15b115d60f2b21e3 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Tue, 21 Apr 2015 19:09:07 -0700 +Subject: [PATCH 9/9] ARM: qcom: automatically select PCI_DOMAINS if PCI is + enabled + +If multiple PCIe devices are present in the system, the kernel will +panic at boot time when trying to scan the PCI buses. This happens on +IPQ806x based platforms, which has 3 PCIe ports. + +Enabling this option allows the kernel to assign the pci-domains +according to the device-tree content. This allows multiple PCIe +controllers to coexist in the system. + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + arch/arm/mach-qcom/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm/mach-qcom/Kconfig ++++ b/arch/arm/mach-qcom/Kconfig +@@ -5,6 +5,7 @@ menuconfig ARCH_QCOM + select ARM_AMBA + select PINCTRL + select QCOM_SCM if SMP ++ select PCI_DOMAINS if PCI + help + Support for Qualcomm's devicetree based systems. + diff --git a/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch b/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch new file mode 100644 index 0000000000..be80f4c02f --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/126-add-rpm-to-ipq8064-dts.patch @@ -0,0 +1,87 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -2,6 +2,7 @@ + + #include "skeleton.dtsi" + #include <dt-bindings/clock/qcom,gcc-ipq806x.h> ++#include <dt-bindings/mfd/qcom-rpm.h> + #include <dt-bindings/clock/qcom,lcc-ipq806x.h> + #include <dt-bindings/soc/qcom,gsbi.h> + #include <dt-bindings/reset/qcom,gcc-ipq806x.h> +@@ -92,6 +93,63 @@ + reg-names = "lpass-lpaif"; + }; + ++ rpm@108000 { ++ compatible = "qcom,rpm-ipq8064"; ++ reg = <0x108000 0x1000>; ++ qcom,ipc = <&l2cc 0x8 2>; ++ ++ interrupts = <0 19 0>, ++ <0 21 0>, ++ <0 22 0>; ++ interrupt-names = "ack", ++ "err", ++ "wakeup"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ smb208_s1a: smb208-s1a { ++ compatible = "qcom,rpm-smb208"; ++ reg = <QCOM_RPM_SMB208_S1a>; ++ ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ ++ }; ++ ++ smb208_s1b: smb208-s1b { ++ compatible = "qcom,rpm-smb208"; ++ reg = <QCOM_RPM_SMB208_S1b>; ++ ++ regulator-min-microvolt = <1050000>; ++ regulator-max-microvolt = <1150000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s2a: smb208-s2a { ++ compatible = "qcom,rpm-smb208"; ++ reg = <QCOM_RPM_SMB208_S2a>; ++ ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ ++ smb208_s2b: smb208-s2b { ++ compatible = "qcom,rpm-smb208"; ++ reg = <QCOM_RPM_SMB208_S2b>; ++ ++ regulator-min-microvolt = < 800000>; ++ regulator-max-microvolt = <1250000>; ++ ++ qcom,switch-mode-frequency = <1200000>; ++ }; ++ }; ++ + qcom_pinmux: pinmux@800000 { + compatible = "qcom,ipq8064-pinctrl"; + reg = <0x800000 0x4000>; +@@ -136,6 +194,12 @@ + reg = <0x02098000 0x1000>, <0x02008000 0x1000>; + }; + ++ l2cc: clock-controller@2011000 { ++ compatible = "qcom,kpss-gcc", "syscon"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; ++ }; ++ + saw0: regulator@2089000 { + compatible = "qcom,saw2"; + reg = <0x02089000 0x1000>, <0x02009000 0x1000>; diff --git a/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch b/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch new file mode 100644 index 0000000000..36a92c858a --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/133-ARM-Add-Krait-L2-register-accessor-functions.patch @@ -0,0 +1,144 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,01/13] ARM: Add Krait L2 register accessor functions +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063051 +Message-Id: <1426920332-9340-2-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org>, + Mark Rutland <mark.rutland@arm.com>, Russell King <linux@arm.linux.org.uk>, + Courtney Cavin <courtney.cavin@sonymobile.com> +Date: Fri, 20 Mar 2015 23:45:20 -0700 + +Krait CPUs have a handful of L2 cache controller registers that +live behind a cp15 based indirection register. First you program +the indirection register (l2cpselr) to point the L2 'window' +register (l2cpdr) at what you want to read/write. Then you +read/write the 'window' register to do what you want. The +l2cpselr register is not banked per-cpu so we must lock around +accesses to it to prevent other CPUs from re-pointing l2cpdr +underneath us. + +Cc: Mark Rutland <mark.rutland@arm.com> +Cc: Russell King <linux@arm.linux.org.uk> +Cc: Courtney Cavin <courtney.cavin@sonymobile.com> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +arch/arm/common/Kconfig | 3 ++ + arch/arm/common/Makefile | 1 + + arch/arm/common/krait-l2-accessors.c | 58 +++++++++++++++++++++++++++++++ + arch/arm/include/asm/krait-l2-accessors.h | 20 +++++++++++ + 4 files changed, 82 insertions(+) + create mode 100644 arch/arm/common/krait-l2-accessors.c + create mode 100644 arch/arm/include/asm/krait-l2-accessors.h + +--- a/arch/arm/common/Kconfig ++++ b/arch/arm/common/Kconfig +@@ -9,6 +9,9 @@ config DMABOUNCE + bool + select ZONE_DMA + ++config KRAIT_L2_ACCESSORS ++ bool ++ + config SHARP_LOCOMO + bool + +--- a/arch/arm/common/Makefile ++++ b/arch/arm/common/Makefile +@@ -7,6 +7,7 @@ obj-y += firmware.o + obj-$(CONFIG_ICST) += icst.o + obj-$(CONFIG_SA1111) += sa1111.o + obj-$(CONFIG_DMABOUNCE) += dmabounce.o ++obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o + obj-$(CONFIG_SHARP_LOCOMO) += locomo.o + obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o + obj-$(CONFIG_SHARP_SCOOP) += scoop.o +--- /dev/null ++++ b/arch/arm/common/krait-l2-accessors.c +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (c) 2011-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/spinlock.h> ++#include <linux/export.h> ++ ++#include <asm/barrier.h> ++#include <asm/krait-l2-accessors.h> ++ ++static DEFINE_RAW_SPINLOCK(krait_l2_lock); ++ ++void krait_set_l2_indirect_reg(u32 addr, u32 val) ++{ ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&krait_l2_lock, flags); ++ /* ++ * Select the L2 window by poking l2cpselr, then write to the window ++ * via l2cpdr. ++ */ ++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); ++ isb(); ++ asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val)); ++ isb(); ++ ++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); ++} ++EXPORT_SYMBOL(krait_set_l2_indirect_reg); ++ ++u32 krait_get_l2_indirect_reg(u32 addr) ++{ ++ u32 val; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&krait_l2_lock, flags); ++ /* ++ * Select the L2 window by poking l2cpselr, then read from the window ++ * via l2cpdr. ++ */ ++ asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); ++ isb(); ++ asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val)); ++ ++ raw_spin_unlock_irqrestore(&krait_l2_lock, flags); ++ ++ return val; ++} ++EXPORT_SYMBOL(krait_get_l2_indirect_reg); +--- /dev/null ++++ b/arch/arm/include/asm/krait-l2-accessors.h +@@ -0,0 +1,20 @@ ++/* ++ * Copyright (c) 2011-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. ++ */ ++ ++#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H ++#define __ASMARM_KRAIT_L2_ACCESSORS_H ++ ++extern void krait_set_l2_indirect_reg(u32 addr, u32 val); ++extern u32 krait_get_l2_indirect_reg(u32 addr); ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch b/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch new file mode 100644 index 0000000000..3a475fba3d --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/134-clk-mux-Split-out-register-accessors-for-reuse.patch @@ -0,0 +1,192 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,02/13] clk: mux: Split out register accessors for reuse +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063111 +Message-Id: <1426920332-9340-3-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org> +Date: Fri, 20 Mar 2015 23:45:21 -0700 + +We want to reuse the logic in clk-mux.c for other clock drivers +that don't use readl as register accessors. Fortunately, there +really isn't much to the mux code besides the table indirection +and quirk flags if you assume any bit shifting and masking has +been done already. Pull that logic out into reusable functions +that operate on an optional table and some flags so that other +drivers can use the same logic. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +drivers/clk/clk-mux.c | 76 +++++++++++++++++++++++++++----------------- + include/linux/clk-provider.h | 9 ++++-- + 2 files changed, 54 insertions(+), 31 deletions(-) + +--- 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_ + + 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); ++} + +- else { +- if (mux->flags & CLK_MUX_INDEX_BIT) +- index = 1 << index; ++unsigned int clk_mux_reindex(u8 index, unsigned int *table, ++ unsigned long flags) ++{ ++ unsigned int val = index; + +- if (mux->flags & CLK_MUX_INDEX_ONE) +- index++; ++ if (table) { ++ val = table[val]; ++ } else { ++ if (flags & CLK_MUX_INDEX_BIT) ++ val = 1 << 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 + } + + 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; +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -409,7 +409,7 @@ void clk_unregister_divider(struct 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 devi + 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/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch b/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch new file mode 100644 index 0000000000..8063860985 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/135-clk-Avoid-sending-high-rates-to-downstream-clocks-during-set_rate.patch @@ -0,0 +1,130 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3, 03/13] clk: Avoid sending high rates to downstream clocks during + set_rate +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063271 +Message-Id: <1426920332-9340-4-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org> +Date: Fri, 20 Mar 2015 23:45:22 -0700 + +If a clock is on and we call clk_set_rate() on it we may get into +a situation where the clock temporarily increases in rate +dramatically while we walk the tree and call .set_rate() ops. For +example, consider a case where a PLL feeds into a divider. +Initially the divider is set to divide by 1 and the PLL is +running fairly slow (100MHz). The downstream consumer of the +divider output can only handle rates =< 400 MHz, but the divider +can only choose between divisors of 1 and 4. + + +-----+ +----------------+ + | PLL |-->| div 1 or div 4 |---> consumer device + +-----+ +----------------+ + +To achieve a rate of 400MHz on the output of the divider, we +would have to set the rate of the PLL to 1.6 GHz and then divide +it by 4. The current code would set the PLL to 1.6GHz first while +the divider is still set to 1, thus causing the downstream +consumer of the clock to receive a few clock cycles of 1.6GHz +clock (far beyond it's maximum acceptable rate). We should be +changing the divider first before increasing the PLL rate to +avoid this problem. + +Therefore, set the rate of any child clocks that are increasing +in rate from their current rate so that they can increase their +dividers if necessary. We assume that there isn't such a thing as +minimum rate requirements. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +drivers/clk/clk.c | 34 ++++++++++++++++++++++------------ + 1 file changed, 22 insertions(+), 12 deletions(-) + +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -1743,21 +1743,24 @@ static struct clk_core *clk_propagate_ra + * walk down a subtree and set the new rates notifying the rate + * change on the way + */ +-static void clk_change_rate(struct clk_core *clk) ++static void ++clk_change_rate(struct clk_core *clk, unsigned long best_parent_rate) + { + struct clk_core *child; + struct hlist_node *tmp; + unsigned long old_rate; +- unsigned long best_parent_rate = 0; + bool skip_set_rate = false; + struct clk_core *old_parent; + +- old_rate = clk->rate; ++ hlist_for_each_entry(child, &clk->children, child_node) { ++ /* Skip children who will be reparented to another clock */ ++ if (child->new_parent && child->new_parent != clk) ++ continue; ++ if (child->new_rate > child->rate) ++ clk_change_rate(child, clk->new_rate); ++ } + +- if (clk->new_parent) +- best_parent_rate = clk->new_parent->rate; +- else if (clk->parent) +- best_parent_rate = clk->parent->rate; ++ old_rate = clk->rate; + + if (clk->new_parent && clk->new_parent != clk->parent) { + old_parent = __clk_set_parent_before(clk, clk->new_parent); +@@ -1783,7 +1786,7 @@ static void clk_change_rate(struct clk_c + + trace_clk_set_rate_complete(clk, clk->new_rate); + +- clk->rate = clk_recalc(clk, best_parent_rate); ++ clk->rate = clk->new_rate; + + if (clk->notifier_count && old_rate != clk->rate) + __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); +@@ -1796,12 +1799,13 @@ static void clk_change_rate(struct clk_c + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) + continue; +- clk_change_rate(child); ++ if (child->new_rate != child->rate) ++ clk_change_rate(child, clk->new_rate); + } + + /* handle the new child who might not be in clk->children yet */ +- if (clk->new_child) +- clk_change_rate(clk->new_child); ++ if (clk->new_child && clk->new_child->new_rate != clk->new_child->rate) ++ clk_change_rate(clk->new_child, clk->new_rate); + } + + static int clk_core_set_rate_nolock(struct clk_core *clk, +@@ -1810,6 +1814,7 @@ static int clk_core_set_rate_nolock(stru + struct clk_core *top, *fail_clk; + unsigned long rate = req_rate; + int ret = 0; ++ unsigned long parent_rate; + + if (!clk) + return 0; +@@ -1835,8 +1840,13 @@ static int clk_core_set_rate_nolock(stru + return -EBUSY; + } + ++ if (top->parent) ++ parent_rate = top->parent->rate; ++ else ++ parent_rate = 0; ++ + /* change the rates */ +- clk_change_rate(top); ++ clk_change_rate(top, parent_rate); + + clk->req_rate = req_rate; + diff --git a/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch b/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch new file mode 100644 index 0000000000..0cf4eb331e --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/136-clk-Add-safe-switch-hook.patch @@ -0,0 +1,164 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,04/13] clk: Add safe switch hook +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063211 +Message-Id: <1426920332-9340-5-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org> +Date: Fri, 20 Mar 2015 23:45:23 -0700 + +Sometimes clocks can't accept their parent source turning off +while the source is reprogrammed to a different rate. Most +notably CPU clocks require a way to switch away from the current +PLL they're running on, reprogram that PLL to a new rate, and +then switch back to the PLL with the new rate once they're done. +Add a hook that drivers can implement allowing them to return a +'safe parent' that they can switch their parent to while the +upstream source is reprogrammed to support this. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +This patch is good enough for Krait, but soon I'll need to +support a "safe rate" where we ask a clock what rate it needs to be running +at to be sure it's within voltage constraints. Right now safe parent +handles that problem on Krait, but on other platforms it won't work. + + drivers/clk/clk.c | 61 ++++++++++++++++++++++++++++++++++++++------ + include/linux/clk-provider.h | 1 + + 2 files changed, 54 insertions(+), 8 deletions(-) + +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -56,9 +56,12 @@ struct clk_core { + struct clk_core **parents; + u8 num_parents; + u8 new_parent_index; ++ u8 safe_parent_index; + unsigned long rate; + unsigned long req_rate; ++ unsigned long old_rate; + unsigned long new_rate; ++ struct clk_core *safe_parent; + struct clk_core *new_parent; + struct clk_core *new_child; + unsigned long flags; +@@ -1595,7 +1598,8 @@ out: + static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate, + struct clk_core *new_parent, u8 p_index) + { +- struct clk_core *child; ++ struct clk_core *child, *parent; ++ struct clk_hw *parent_hw; + + clk->new_rate = new_rate; + clk->new_parent = new_parent; +@@ -1605,6 +1609,18 @@ static void clk_calc_subtree(struct clk_ + if (new_parent && new_parent != clk->parent) + new_parent->new_child = clk; + ++ if (clk->ops->get_safe_parent) { ++ parent_hw = clk->ops->get_safe_parent(clk->hw); ++ if (parent_hw) { ++ parent = parent_hw->core; ++ p_index = clk_fetch_parent_index(clk, parent); ++ clk->safe_parent_index = p_index; ++ clk->safe_parent = parent; ++ } ++ } else { ++ clk->safe_parent = NULL; ++ } ++ + hlist_for_each_entry(child, &clk->children, child_node) { + child->new_rate = clk_recalc(child, new_rate); + clk_calc_subtree(child, child->new_rate, NULL, 0); +@@ -1709,14 +1725,43 @@ static struct clk_core *clk_propagate_ra + unsigned long event) + { + struct clk_core *child, *tmp_clk, *fail_clk = NULL; ++ struct clk_core *old_parent; + int ret = NOTIFY_DONE; + +- if (clk->rate == clk->new_rate) ++ if (clk->rate == clk->new_rate && event != POST_RATE_CHANGE) + return NULL; + ++ switch (event) { ++ case PRE_RATE_CHANGE: ++ if (clk->safe_parent) ++ clk->ops->set_parent(clk->hw, clk->safe_parent_index); ++ clk->old_rate = clk->rate; ++ break; ++ case POST_RATE_CHANGE: ++ if (clk->safe_parent) { ++ old_parent = __clk_set_parent_before(clk, ++ clk->new_parent); ++ if (clk->ops->set_rate_and_parent) { ++ clk->ops->set_rate_and_parent(clk->hw, ++ clk->new_rate, ++ clk->new_parent ? ++ clk->new_parent->rate : 0, ++ clk->new_parent_index); ++ } else if (clk->ops->set_parent) { ++ clk->ops->set_parent(clk->hw, ++ clk->new_parent_index); ++ } ++ __clk_set_parent_after(clk, clk->new_parent, ++ old_parent); ++ } ++ break; ++ } ++ + if (clk->notifier_count) { +- ret = __clk_notify(clk, event, clk->rate, clk->new_rate); +- if (ret & NOTIFY_STOP_MASK) ++ if (event != POST_RATE_CHANGE || clk->old_rate != clk->rate) ++ ret = __clk_notify(clk, event, clk->old_rate, ++ clk->new_rate); ++ if (ret & NOTIFY_STOP_MASK && event != POST_RATE_CHANGE) + fail_clk = clk; + } + +@@ -1762,7 +1807,8 @@ clk_change_rate(struct clk_core *clk, un + + old_rate = clk->rate; + +- if (clk->new_parent && clk->new_parent != clk->parent) { ++ if (clk->new_parent && clk->new_parent != clk->parent && ++ !clk->safe_parent) { + old_parent = __clk_set_parent_before(clk, clk->new_parent); + trace_clk_set_parent(clk, clk->new_parent); + +@@ -1788,9 +1834,6 @@ clk_change_rate(struct clk_core *clk, un + + clk->rate = clk->new_rate; + +- if (clk->notifier_count && old_rate != clk->rate) +- __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); +- + /* + * Use safe iteration, as change_rate can actually swap parents + * for certain clock types. +@@ -1850,6 +1893,8 @@ static int clk_core_set_rate_nolock(stru + + clk->req_rate = req_rate; + ++ clk_propagate_rate_change(top, POST_RATE_CHANGE); ++ + return ret; + } + +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -183,6 +183,7 @@ struct clk_ops { + struct clk_hw **best_parent_hw); + int (*set_parent)(struct clk_hw *hw, u8 index); + u8 (*get_parent)(struct clk_hw *hw); ++ struct clk_hw *(*get_safe_parent)(struct clk_hw *hw); + int (*set_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); + int (*set_rate_and_parent)(struct clk_hw *hw, diff --git a/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch new file mode 100644 index 0000000000..6fad6e8a0c --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch @@ -0,0 +1,351 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,05/13] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063261 +Message-Id: <1426920332-9340-6-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org> +Date: Fri, 20 Mar 2015 23:45:24 -0700 + +HFPLLs are the main frequency source for Krait CPU clocks. Add +support for changing the rate of these PLLs. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +I'd really like to get rid of __clk_hfpll_init_once() if possible... + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-hfpll.h | 54 +++++++++ + 3 files changed, 308 insertions(+) + create mode 100644 drivers/clk/qcom/clk-hfpll.c + create mode 100644 drivers/clk/qcom/clk-hfpll.h + +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o ++clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + + obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.c +@@ -0,0 +1,253 @@ ++/* ++ * Copyright (c) 2013-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/kernel.h> ++#include <linux/export.h> ++#include <linux/regmap.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++#include <linux/spinlock.h> ++ ++#include "clk-regmap.h" ++#include "clk-hfpll.h" ++ ++#define PLL_OUTCTRL BIT(0) ++#define PLL_BYPASSNL BIT(1) ++#define PLL_RESET_N BIT(2) ++ ++/* Initialize a HFPLL at a given rate and enable it. */ ++static void __clk_hfpll_init_once(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ ++ if (likely(h->init_done)) ++ return; ++ ++ /* Configure PLL parameters for integer mode. */ ++ if (hd->config_val) ++ regmap_write(regmap, hd->config_reg, hd->config_val); ++ regmap_write(regmap, hd->m_reg, 0); ++ regmap_write(regmap, hd->n_reg, 1); ++ ++ if (hd->user_reg) { ++ u32 regval = hd->user_val; ++ unsigned long rate; ++ ++ rate = __clk_get_rate(hw->clk); ++ ++ /* Pick the right VCO. */ ++ if (hd->user_vco_mask && rate > hd->low_vco_max_rate) ++ regval |= hd->user_vco_mask; ++ regmap_write(regmap, hd->user_reg, regval); ++ } ++ ++ if (hd->droop_reg) ++ regmap_write(regmap, hd->droop_reg, hd->droop_val); ++ ++ h->init_done = true; ++} ++ ++static void __clk_hfpll_enable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 val; ++ ++ __clk_hfpll_init_once(hw); ++ ++ /* Disable PLL bypass mode. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); ++ ++ /* ++ * H/W requires a 5us delay between disabling the bypass and ++ * de-asserting the reset. Delay 10us just to be safe. ++ */ ++ udelay(10); ++ ++ /* De-assert active-low PLL reset. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); ++ ++ /* Wait for PLL to lock. */ ++ if (hd->status_reg) { ++ do { ++ regmap_read(regmap, hd->status_reg, &val); ++ } while (!(val & BIT(hd->lock_bit))); ++ } else { ++ udelay(60); ++ } ++ ++ /* Enable PLL output. */ ++ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); ++} ++ ++/* Enable an already-configured HFPLL. */ ++static int clk_hfpll_enable(struct clk_hw *hw) ++{ ++ unsigned long flags; ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ regmap_read(regmap, hd->mode_reg, &mode); ++ if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) ++ __clk_hfpll_enable(hw); ++ spin_unlock_irqrestore(&h->lock, flags); ++ ++ return 0; ++} ++ ++static void __clk_hfpll_disable(struct clk_hfpll *h) ++{ ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ ++ /* ++ * Disable the PLL output, disable test mode, enable the bypass mode, ++ * and assert the reset. ++ */ ++ regmap_update_bits(regmap, hd->mode_reg, ++ PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); ++} ++ ++static void clk_hfpll_disable(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ __clk_hfpll_disable(h); ++ spin_unlock_irqrestore(&h->lock, flags); ++} ++ ++static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ unsigned long rrate; ++ ++ rate = clamp(rate, hd->min_rate, hd->max_rate); ++ ++ rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; ++ if (rrate > hd->max_rate) ++ rrate -= *parent_rate; ++ ++ return rrate; ++} ++ ++/* ++ * For optimization reasons, assumes no downstream clocks are actively using ++ * it. ++ */ ++static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ unsigned long flags; ++ u32 l_val, val; ++ bool enabled; ++ ++ l_val = rate / parent_rate; ++ ++ spin_lock_irqsave(&h->lock, flags); ++ ++ enabled = __clk_is_enabled(hw->clk); ++ if (enabled) ++ __clk_hfpll_disable(h); ++ ++ /* Pick the right VCO. */ ++ if (hd->user_reg && hd->user_vco_mask) { ++ regmap_read(regmap, hd->user_reg, &val); ++ if (rate <= hd->low_vco_max_rate) ++ val &= ~hd->user_vco_mask; ++ else ++ val |= hd->user_vco_mask; ++ regmap_write(regmap, hd->user_reg, val); ++ } ++ ++ regmap_write(regmap, hd->l_reg, l_val); ++ ++ if (enabled) ++ __clk_hfpll_enable(hw); ++ ++ spin_unlock_irqrestore(&h->lock, flags); ++ ++ return 0; ++} ++ ++static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 l_val; ++ ++ regmap_read(regmap, hd->l_reg, &l_val); ++ ++ return l_val * parent_rate; ++} ++ ++static void clk_hfpll_init(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode, status; ++ ++ regmap_read(regmap, hd->mode_reg, &mode); ++ if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { ++ __clk_hfpll_init_once(hw); ++ return; ++ } ++ ++ if (hd->status_reg) { ++ regmap_read(regmap, hd->status_reg, &status); ++ if (!(status & BIT(hd->lock_bit))) { ++ WARN(1, "HFPLL %s is ON, but not locked!\n", ++ __clk_get_name(hw->clk)); ++ clk_hfpll_disable(hw); ++ __clk_hfpll_init_once(hw); ++ } ++ } ++} ++ ++static int hfpll_is_enabled(struct clk_hw *hw) ++{ ++ struct clk_hfpll *h = to_clk_hfpll(hw); ++ struct hfpll_data const *hd = h->d; ++ struct regmap *regmap = h->clkr.regmap; ++ u32 mode; ++ ++ regmap_read(regmap, hd->mode_reg, &mode); ++ mode &= 0x7; ++ return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); ++} ++ ++const struct clk_ops clk_ops_hfpll = { ++ .enable = clk_hfpll_enable, ++ .disable = clk_hfpll_disable, ++ .is_enabled = hfpll_is_enabled, ++ .round_rate = clk_hfpll_round_rate, ++ .set_rate = clk_hfpll_set_rate, ++ .recalc_rate = clk_hfpll_recalc_rate, ++ .init = clk_hfpll_init, ++}; ++EXPORT_SYMBOL_GPL(clk_ops_hfpll); +--- /dev/null ++++ b/drivers/clk/qcom/clk-hfpll.h +@@ -0,0 +1,54 @@ ++/* ++ * Copyright (c) 2013-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. ++ */ ++#ifndef __QCOM_CLK_HFPLL_H__ ++#define __QCOM_CLK_HFPLL_H__ ++ ++#include <linux/clk-provider.h> ++#include <linux/spinlock.h> ++#include "clk-regmap.h" ++ ++struct hfpll_data { ++ u32 mode_reg; ++ u32 l_reg; ++ u32 m_reg; ++ u32 n_reg; ++ u32 user_reg; ++ u32 droop_reg; ++ u32 config_reg; ++ u32 status_reg; ++ u8 lock_bit; ++ ++ u32 droop_val; ++ u32 config_val; ++ u32 user_val; ++ u32 user_vco_mask; ++ unsigned long low_vco_max_rate; ++ ++ unsigned long min_rate; ++ unsigned long max_rate; ++}; ++ ++struct clk_hfpll { ++ struct hfpll_data const *d; ++ int init_done; ++ ++ struct clk_regmap clkr; ++ spinlock_t lock; ++}; ++ ++#define to_clk_hfpll(_hw) \ ++ container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) ++ ++extern const struct clk_ops clk_ops_hfpll; ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch new file mode 100644 index 0000000000..4056784287 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/138-clk-qcom-Add-HFPLL-driver.patch @@ -0,0 +1,206 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,06/13] clk: qcom: Add HFPLL driver +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063231 +Message-Id: <1426920332-9340-7-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org> +Date: Fri, 20 Mar 2015 23:45:25 -0700 + +On some devices (MSM8974 for example), the HFPLLs are +instantiated within the Krait processor subsystem as separate +register regions. Add a driver for these PLLs so that we can +provide HFPLL clocks for use by the system. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +.../devicetree/bindings/clock/qcom,hfpll.txt | 40 ++++++++ + drivers/clk/qcom/Kconfig | 8 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/hfpll.c | 109 +++++++++++++++++++++ + 4 files changed, 158 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt + create mode 100644 drivers/clk/qcom/hfpll.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt +@@ -0,0 +1,40 @@ ++High-Frequency PLL (HFPLL) ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: <string> ++ Definition: must be "qcom,hfpll" ++ ++- reg: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: address and size of HPLL registers. An optional second ++ element specifies the address and size of the alias ++ register region. ++ ++- clock-output-names: ++ Usage: required ++ Value type: <string> ++ Definition: Name of the PLL. Typically hfpllX where X is a CPU number ++ starting at 0. Otherwise hfpll_Y where Y is more specific ++ such as "l2". ++ ++Example: ++ ++1) An HFPLL for the L2 cache. ++ ++ clock-controller@f9016000 { ++ compatible = "qcom,hfpll"; ++ reg = <0xf9016000 0x30>; ++ clock-output-names = "hfpll_l2"; ++ }; ++ ++2) An HFPLL for CPU0. This HFPLL has the alias register region. ++ ++ clock-controller@f908a000 { ++ compatible = "qcom,hfpll"; ++ reg = <0xf908a000 0x30>, <0xf900a000 0x30>; ++ clock-output-names = "hfpll0"; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -97,3 +97,11 @@ config MSM_MMCC_8974 + 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_HFPLL ++ tristate "High-Frequency PLL (HFPLL) Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the high-frequency PLLs present on Qualcomm devices. ++ Say Y if you want to support CPU frequency scaling on devices ++ such as MSM8974, APQ8084, etc. +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -22,3 +22,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896 + 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_HFPLL) += hfpll.o +--- /dev/null ++++ b/drivers/clk/qcom/hfpll.c +@@ -0,0 +1,109 @@ ++/* ++ * Copyright (c) 2013-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/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/of.h> ++#include <linux/clk.h> ++#include <linux/clk-provider.h> ++#include <linux/regmap.h> ++ ++#include "clk-regmap.h" ++#include "clk-hfpll.h" ++ ++static const struct hfpll_data hdata = { ++ .mode_reg = 0x00, ++ .l_reg = 0x04, ++ .m_reg = 0x08, ++ .n_reg = 0x0c, ++ .user_reg = 0x10, ++ .config_reg = 0x14, ++ .config_val = 0x430405d, ++ .status_reg = 0x1c, ++ .lock_bit = 16, ++ ++ .user_val = 0x8, ++ .user_vco_mask = 0x100000, ++ .low_vco_max_rate = 1248000000, ++ .min_rate = 537600000UL, ++ .max_rate = 2900000000UL, ++}; ++ ++static const struct of_device_id qcom_hfpll_match_table[] = { ++ { .compatible = "qcom,hfpll" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table); ++ ++static const struct regmap_config hfpll_regmap_config = { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = 0x30, ++ .fast_io = true, ++}; ++ ++static int qcom_hfpll_probe(struct platform_device *pdev) ++{ ++ struct clk *clk; ++ struct resource *res; ++ struct device *dev = &pdev->dev; ++ void __iomem *base; ++ struct regmap *regmap; ++ struct clk_hfpll *h; ++ struct clk_init_data init = { ++ .parent_names = (const char *[]){ "xo" }, ++ .num_parents = 1, ++ .ops = &clk_ops_hfpll, ++ }; ++ ++ h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); ++ if (!h) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ if (of_property_read_string_index(dev->of_node, "clock-output-names", ++ 0, &init.name)) ++ return -ENODEV; ++ ++ h->d = &hdata; ++ h->clkr.hw.init = &init; ++ spin_lock_init(&h->lock); ++ ++ clk = devm_clk_register_regmap(&pdev->dev, &h->clkr); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct platform_driver qcom_hfpll_driver = { ++ .probe = qcom_hfpll_probe, ++ .driver = { ++ .name = "qcom-hfpll", ++ .of_match_table = qcom_hfpll_match_table, ++ }, ++}; ++module_platform_driver(qcom_hfpll_driver); ++ ++MODULE_DESCRIPTION("QCOM HFPLL Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-hfpll"); diff --git a/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch b/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch new file mode 100644 index 0000000000..ffcd462d12 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/139-clk-qcom-Add-IPQ806X-s-HFPLLs.patch @@ -0,0 +1,127 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,08/13] clk: qcom: Add IPQ806X's HFPLLs +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063241 +Message-Id: <1426920332-9340-9-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org> +Date: Fri, 20 Mar 2015 23:45:27 -0700 + +Describe the HFPLLs present on IPQ806X devices. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +drivers/clk/qcom/gcc-ipq806x.c | 83 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 83 insertions(+) + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -30,6 +30,7 @@ + #include "clk-pll.h" + #include "clk-rcg.h" + #include "clk-branch.h" ++#include "clk-hfpll.h" + #include "reset.h" + + static struct clk_pll pll0 = { +@@ -113,6 +114,85 @@ static struct clk_regmap pll8_vote = { + }, + }; + ++static struct hfpll_data hfpll0_data = { ++ .mode_reg = 0x3200, ++ .l_reg = 0x3208, ++ .m_reg = 0x320c, ++ .n_reg = 0x3210, ++ .config_reg = 0x3204, ++ .status_reg = 0x321c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3214, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll0 = { ++ .d = &hfpll0_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll0", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), ++}; ++ ++static struct hfpll_data hfpll1_data = { ++ .mode_reg = 0x3240, ++ .l_reg = 0x3248, ++ .m_reg = 0x324c, ++ .n_reg = 0x3250, ++ .config_reg = 0x3244, ++ .status_reg = 0x325c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll1 = { ++ .d = &hfpll1_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll1", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), ++}; ++ ++static struct hfpll_data hfpll_l2_data = { ++ .mode_reg = 0x3300, ++ .l_reg = 0x3308, ++ .m_reg = 0x330c, ++ .n_reg = 0x3310, ++ .config_reg = 0x3304, ++ .status_reg = 0x331c, ++ .config_val = 0x7845c665, ++ .droop_reg = 0x3314, ++ .droop_val = 0x0108c000, ++ .min_rate = 600000000UL, ++ .max_rate = 1800000000UL, ++}; ++ ++static struct clk_hfpll hfpll_l2 = { ++ .d = &hfpll_l2_data, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .name = "hfpll_l2", ++ .ops = &clk_ops_hfpll, ++ .flags = CLK_IGNORE_UNUSED, ++ }, ++ .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), ++}; ++ ++ + static struct clk_pll pll14 = { + .l_reg = 0x31c4, + .m_reg = 0x31c8, +@@ -2307,6 +2387,9 @@ static struct clk_regmap *gcc_ipq806x_cl + [USB_FS1_SYSTEM_CLK] = &usb_fs1_sys_clk.clkr, + [EBI2_CLK] = &ebi2_clk.clkr, + [EBI2_AON_CLK] = &ebi2_aon_clk.clkr, ++ [PLL9] = &hfpll0.clkr, ++ [PLL10] = &hfpll1.clkr, ++ [PLL12] = &hfpll_l2.clkr, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { diff --git a/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch new file mode 100644 index 0000000000..5fcc787c89 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/140-clk-qcom-Add-support-for-Krait-clocks.patch @@ -0,0 +1,271 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,09/13] clk: qcom: Add support for Krait clocks +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063251 +Message-Id: <1426920332-9340-10-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org> +Date: Fri, 20 Mar 2015 23:45:28 -0700 + +The Krait clocks are made up of a series of muxes and a divider +that choose between a fixed rate clock and dedicated HFPLLs for +each CPU. Instead of using mmio accesses to remux parents, the +Krait implementation exposes the remux control via cp15 +registers. Support these clocks. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +drivers/clk/qcom/Kconfig | 4 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-krait.c | 166 +++++++++++++++++++++++++++++++++++++++++++ + drivers/clk/qcom/clk-krait.h | 49 +++++++++++++ + 4 files changed, 220 insertions(+) + create mode 100644 drivers/clk/qcom/clk-krait.c + create mode 100644 drivers/clk/qcom/clk-krait.h + +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -105,3 +105,7 @@ config QCOM_HFPLL + Support for the high-frequency PLLs present on Qualcomm devices. + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. ++ ++config KRAIT_CLOCKS ++ bool ++ select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o + clk-qcom-y += clk-branch.o + clk-qcom-y += clk-regmap-divider.o + clk-qcom-y += clk-regmap-mux.o ++clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o + clk-qcom-y += clk-hfpll.o + clk-qcom-y += reset.o + +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.c +@@ -0,0 +1,166 @@ ++/* ++ * Copyright (c) 2013-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/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/clk-provider.h> ++#include <linux/spinlock.h> ++ ++#include <asm/krait-l2-accessors.h> ++ ++#include "clk-krait.h" ++ ++/* Secondary and primary muxes share the same cp15 register */ ++static DEFINE_SPINLOCK(krait_clock_reg_lock); ++ ++#define LPL_SHIFT 8 ++static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) ++{ ++ unsigned long flags; ++ u32 regval; ++ ++ spin_lock_irqsave(&krait_clock_reg_lock, flags); ++ regval = krait_get_l2_indirect_reg(mux->offset); ++ regval &= ~(mux->mask << mux->shift); ++ regval |= (sel & mux->mask) << mux->shift; ++ if (mux->lpl) { ++ regval &= ~(mux->mask << (mux->shift + LPL_SHIFT)); ++ regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); ++ } ++ krait_set_l2_indirect_reg(mux->offset, regval); ++ spin_unlock_irqrestore(&krait_clock_reg_lock, flags); ++ ++ /* Wait for switch to complete. */ ++ mb(); ++ udelay(1); ++} ++ ++static int krait_mux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ u32 sel; ++ ++ sel = clk_mux_reindex(index, mux->parent_map, 0); ++ mux->en_mask = sel; ++ /* Don't touch mux if CPU is off as it won't work */ ++ if (__clk_is_enabled(hw->clk)) ++ __krait_mux_set_sel(mux, sel); ++ return 0; ++} ++ ++static u8 krait_mux_get_parent(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ u32 sel; ++ ++ sel = krait_get_l2_indirect_reg(mux->offset); ++ sel >>= mux->shift; ++ sel &= mux->mask; ++ mux->en_mask = sel; ++ ++ return clk_mux_get_parent(hw, sel, mux->parent_map, 0); ++} ++ ++static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw) ++{ ++ int i; ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ int num_parents = __clk_get_num_parents(hw->clk); ++ ++ i = mux->safe_sel; ++ for (i = 0; i < num_parents; i++) ++ if (mux->safe_sel == mux->parent_map[i]) ++ break; ++ ++ return __clk_get_hw(clk_get_parent_by_index(hw->clk, i)); ++} ++ ++static int krait_mux_enable(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ ++ __krait_mux_set_sel(mux, mux->en_mask); ++ ++ return 0; ++} ++ ++static void krait_mux_disable(struct clk_hw *hw) ++{ ++ struct krait_mux_clk *mux = to_krait_mux_clk(hw); ++ ++ __krait_mux_set_sel(mux, mux->safe_sel); ++} ++ ++const struct clk_ops krait_mux_clk_ops = { ++ .enable = krait_mux_enable, ++ .disable = krait_mux_disable, ++ .set_parent = krait_mux_set_parent, ++ .get_parent = krait_mux_get_parent, ++ .determine_rate = __clk_mux_determine_rate_closest, ++ .get_safe_parent = krait_mux_get_safe_parent, ++}; ++EXPORT_SYMBOL_GPL(krait_mux_clk_ops); ++ ++/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */ ++static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ *parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), rate * 2); ++ return DIV_ROUND_UP(*parent_rate, 2); ++} ++ ++static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct krait_div2_clk *d = to_krait_div2_clk(hw); ++ unsigned long flags; ++ u32 val; ++ u32 mask = BIT(d->width) - 1; ++ ++ if (d->lpl) ++ mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; ++ ++ spin_lock_irqsave(&krait_clock_reg_lock, flags); ++ val = krait_get_l2_indirect_reg(d->offset); ++ val &= ~mask; ++ krait_set_l2_indirect_reg(d->offset, val); ++ spin_unlock_irqrestore(&krait_clock_reg_lock, flags); ++ ++ return 0; ++} ++ ++static unsigned long ++krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) ++{ ++ struct krait_div2_clk *d = to_krait_div2_clk(hw); ++ u32 mask = BIT(d->width) - 1; ++ u32 div; ++ ++ div = krait_get_l2_indirect_reg(d->offset); ++ div >>= d->shift; ++ div &= mask; ++ div = (div + 1) * 2; ++ ++ return DIV_ROUND_UP(parent_rate, div); ++} ++ ++const struct clk_ops krait_div2_clk_ops = { ++ .round_rate = krait_div2_round_rate, ++ .set_rate = krait_div2_set_rate, ++ .recalc_rate = krait_div2_recalc_rate, ++}; ++EXPORT_SYMBOL_GPL(krait_div2_clk_ops); +--- /dev/null ++++ b/drivers/clk/qcom/clk-krait.h +@@ -0,0 +1,49 @@ ++/* ++ * 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. ++ */ ++ ++#ifndef __QCOM_CLK_KRAIT_H ++#define __QCOM_CLK_KRAIT_H ++ ++#include <linux/clk-provider.h> ++ ++struct krait_mux_clk { ++ unsigned int *parent_map; ++ bool has_safe_parent; ++ u8 safe_sel; ++ u32 offset; ++ u32 mask; ++ u32 shift; ++ u32 en_mask; ++ bool lpl; ++ ++ struct clk_hw hw; ++}; ++ ++#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw) ++ ++extern const struct clk_ops krait_mux_clk_ops; ++ ++struct krait_div2_clk { ++ u32 offset; ++ u8 width; ++ u32 shift; ++ bool lpl; ++ ++ struct clk_hw hw; ++}; ++ ++#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw) ++ ++extern const struct clk_ops krait_div2_clk_ops; ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch new file mode 100644 index 0000000000..db4388812b --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch @@ -0,0 +1,205 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,10/13] clk: qcom: Add KPSS ACC/GCC driver +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063201 +Message-Id: <1426920332-9340-11-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org> +Date: Fri, 20 Mar 2015 23:45:29 -0700 + +The ACC and GCC regions present in KPSSv1 contain registers to +control clocks and power to each Krait CPU and L2. For CPUfreq +purposes probe these devices and expose a mux clock that chooses +between PXO and PLL8. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +.../devicetree/bindings/arm/msm/qcom,kpss-acc.txt | 7 ++ + .../devicetree/bindings/arm/msm/qcom,kpss-gcc.txt | 28 +++++++ + drivers/clk/qcom/Kconfig | 8 ++ + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/kpss-xcc.c | 95 ++++++++++++++++++++++ + 5 files changed, 139 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt + create mode 100644 drivers/clk/qcom/kpss-xcc.c + +--- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +@@ -21,10 +21,17 @@ PROPERTIES + the register region. An optional second element specifies + the base address and size of the alias register region. + ++- clock-output-names: ++ Usage: optional ++ Value type: <string> ++ Definition: Name of the output clock. Typically acpuX_aux where X is a ++ CPU number starting at 0. ++ + Example: + + clock-controller@2088000 { + compatible = "qcom,kpss-acc-v2"; + reg = <0x02088000 0x1000>, + <0x02008000 0x1000>; ++ clock-output-names = "acpu0_aux"; + }; +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt +@@ -0,0 +1,28 @@ ++Krait Processor Sub-system (KPSS) Global Clock Controller (GCC) ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: <string> ++ Definition: should be one of: ++ "qcom,kpss-gcc" ++ ++- reg: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: base address and size of the register region ++ ++- clock-output-names: ++ Usage: required ++ Value type: <string> ++ Definition: Name of the output clock. Typically acpu_l2_aux indicating ++ an L2 cache auxiliary clock. ++ ++Example: ++ ++ l2cc: clock-controller@2011000 { ++ compatible = "qcom,kpss-gcc"; ++ reg = <0x2011000 0x1000>; ++ clock-output-names = "acpu_l2_aux"; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -106,6 +106,14 @@ config QCOM_HFPLL + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. + ++config KPSS_XCC ++ tristate "KPSS Clock Controller" ++ depends on COMMON_CLK_QCOM ++ help ++ Support for the Krait ACC and GCC clock controllers. Say Y ++ if you want to support CPU frequency scaling on devices such ++ as MSM8960, APQ8064, etc. ++ + config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -23,4 +23,5 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896 + 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_KPSS_XCC) += kpss-xcc.o + obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +--- /dev/null ++++ b/drivers/clk/qcom/kpss-xcc.c +@@ -0,0 +1,95 @@ ++/* 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. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/clk.h> ++#include <linux/clk-provider.h> ++ ++static const char *aux_parents[] = { ++ "pll8_vote", ++ "pxo", ++}; ++ ++static unsigned int aux_parent_map[] = { ++ 3, ++ 0, ++}; ++ ++static const struct of_device_id kpss_xcc_match_table[] = { ++ { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL }, ++ { .compatible = "qcom,kpss-gcc" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, kpss_xcc_match_table); ++ ++static int kpss_xcc_driver_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *id; ++ struct clk *clk; ++ struct resource *res; ++ void __iomem *base; ++ const char *name; ++ ++ id = of_match_device(kpss_xcc_match_table, &pdev->dev); ++ if (!id) ++ return -ENODEV; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ if (id->data) { ++ if (of_property_read_string_index(pdev->dev.of_node, ++ "clock-output-names", 0, &name)) ++ return -ENODEV; ++ base += 0x14; ++ } else { ++ name = "acpu_l2_aux"; ++ base += 0x28; ++ } ++ ++ clk = clk_register_mux_table(&pdev->dev, name, aux_parents, ++ ARRAY_SIZE(aux_parents), 0, base, 0, 0x3, ++ 0, aux_parent_map, NULL); ++ ++ platform_set_drvdata(pdev, clk); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static int kpss_xcc_driver_remove(struct platform_device *pdev) ++{ ++ clk_unregister_mux(platform_get_drvdata(pdev)); ++ return 0; ++} ++ ++static struct platform_driver kpss_xcc_driver = { ++ .probe = kpss_xcc_driver_probe, ++ .remove = kpss_xcc_driver_remove, ++ .driver = { ++ .name = "kpss-xcc", ++ .of_match_table = kpss_xcc_match_table, ++ }, ++}; ++module_platform_driver(kpss_xcc_driver); ++ ++MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:kpss-xcc"); diff --git a/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch new file mode 100644 index 0000000000..59fc44f1cb --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/142-clk-qcom-Add-Krait-clock-controller-driver.patch @@ -0,0 +1,435 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,11/13] clk: qcom: Add Krait clock controller driver +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063121 +Message-Id: <1426920332-9340-12-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org> +Date: Fri, 20 Mar 2015 23:45:30 -0700 + +The Krait CPU clocks are made up of a primary mux and secondary +mux for each CPU and the L2, controlled via cp15 accessors. For +Kraits within KPSSv1 each secondary mux accepts a different aux +source, but on KPSSv2 each secondary mux accepts the same aux +source. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +.../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++ + drivers/clk/qcom/Kconfig | 8 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/krait-cc.c | 352 +++++++++++++++++++++ + 4 files changed, 383 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt + create mode 100644 drivers/clk/qcom/krait-cc.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt +@@ -0,0 +1,22 @@ ++Krait Clock Controller ++ ++PROPERTIES ++ ++- compatible: ++ Usage: required ++ Value type: <string> ++ Definition: must be one of: ++ "qcom,krait-cc-v1" ++ "qcom,krait-cc-v2" ++ ++- #clock-cells: ++ Usage: required ++ Value type: <u32> ++ Definition: must be 1 ++ ++Example: ++ ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -114,6 +114,14 @@ config KPSS_XCC + if you want to support CPU frequency scaling on devices such + as MSM8960, APQ8064, etc. + ++config KRAITCC ++ tristate "Krait Clock Controller" ++ depends on COMMON_CLK_QCOM && ARM ++ select KRAIT_CLOCKS ++ help ++ Support for the Krait CPU clocks on Qualcomm devices. ++ Say Y if you want to support CPU frequency scaling. ++ + config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -25,3 +25,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8 + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o + obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o + obj-$(CONFIG_QCOM_HFPLL) += hfpll.o ++obj-$(CONFIG_KRAITCC) += krait-cc.o +--- /dev/null ++++ b/drivers/clk/qcom/krait-cc.c +@@ -0,0 +1,352 @@ ++/* 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 <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/clk.h> ++#include <linux/clk-provider.h> ++#include <linux/slab.h> ++ ++#include "clk-krait.h" ++ ++static unsigned int sec_mux_map[] = { ++ 2, ++ 0, ++}; ++ ++static unsigned int pri_mux_map[] = { ++ 1, ++ 2, ++ 0, ++}; ++ ++static int ++krait_add_div(struct device *dev, int id, const char *s, unsigned offset) ++{ ++ struct krait_div2_clk *div; ++ struct clk_init_data init = { ++ .num_parents = 1, ++ .ops = &krait_div2_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ const char *p_names[1]; ++ struct clk *clk; ++ ++ div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); ++ if (!div) ++ return -ENOMEM; ++ ++ div->width = 2; ++ div->shift = 6; ++ div->lpl = id >= 0; ++ div->offset = offset; ++ div->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s); ++ if (!init.name) ++ return -ENOMEM; ++ ++ init.parent_names = p_names; ++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); ++ if (!p_names[0]) { ++ kfree(init.name); ++ return -ENOMEM; ++ } ++ ++ clk = devm_clk_register(dev, &div->hw); ++ kfree(p_names[0]); ++ kfree(init.name); ++ ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static int ++krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned offset, ++ bool unique_aux) ++{ ++ struct krait_mux_clk *mux; ++ static const char *sec_mux_list[] = { ++ "acpu_aux", ++ "qsb", ++ }; ++ struct clk_init_data init = { ++ .parent_names = sec_mux_list, ++ .num_parents = ARRAY_SIZE(sec_mux_list), ++ .ops = &krait_mux_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return -ENOMEM; ++ ++ mux->offset = offset; ++ mux->lpl = id >= 0; ++ mux->has_safe_parent = true; ++ mux->safe_sel = 2; ++ mux->mask = 0x3; ++ mux->shift = 2; ++ mux->parent_map = sec_mux_map; ++ mux->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); ++ if (!init.name) ++ return -ENOMEM; ++ ++ if (unique_aux) { ++ sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s); ++ if (!sec_mux_list[0]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_aux; ++ } ++ } ++ ++ clk = devm_clk_register(dev, &mux->hw); ++ ++ if (unique_aux) ++ kfree(sec_mux_list[0]); ++err_aux: ++ kfree(init.name); ++ return PTR_ERR_OR_ZERO(clk); ++} ++ ++static struct clk * ++krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned offset) ++{ ++ struct krait_mux_clk *mux; ++ const char *p_names[3]; ++ struct clk_init_data init = { ++ .parent_names = p_names, ++ .num_parents = ARRAY_SIZE(p_names), ++ .ops = &krait_mux_clk_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }; ++ struct clk *clk; ++ ++ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return ERR_PTR(-ENOMEM); ++ ++ mux->has_safe_parent = true; ++ mux->safe_sel = 0; ++ mux->mask = 0x3; ++ mux->shift = 0; ++ mux->offset = offset; ++ mux->lpl = id >= 0; ++ mux->parent_map = pri_mux_map; ++ mux->hw.init = &init; ++ ++ init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); ++ if (!init.name) ++ return ERR_PTR(-ENOMEM); ++ ++ p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); ++ if (!p_names[0]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p0; ++ } ++ ++ p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s); ++ if (!p_names[1]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p1; ++ } ++ ++ p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); ++ if (!p_names[2]) { ++ clk = ERR_PTR(-ENOMEM); ++ goto err_p2; ++ } ++ ++ clk = devm_clk_register(dev, &mux->hw); ++ ++ kfree(p_names[2]); ++err_p2: ++ kfree(p_names[1]); ++err_p1: ++ kfree(p_names[0]); ++err_p0: ++ kfree(init.name); ++ return clk; ++} ++ ++/* id < 0 for L2, otherwise id == physical CPU number */ ++static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux) ++{ ++ int ret; ++ unsigned offset; ++ void *p = NULL; ++ const char *s; ++ struct clk *clk; ++ ++ if (id >= 0) { ++ offset = 0x4501 + (0x1000 * id); ++ s = p = kasprintf(GFP_KERNEL, "%d", id); ++ if (!s) ++ return ERR_PTR(-ENOMEM); ++ } else { ++ offset = 0x500; ++ s = "_l2"; ++ } ++ ++ ret = krait_add_div(dev, id, s, offset); ++ if (ret) { ++ clk = ERR_PTR(ret); ++ goto err; ++ } ++ ++ ret = krait_add_sec_mux(dev, id, s, offset, unique_aux); ++ if (ret) { ++ clk = ERR_PTR(ret); ++ goto err; ++ } ++ ++ clk = krait_add_pri_mux(dev, id, s, offset); ++err: ++ kfree(p); ++ return clk; ++} ++ ++static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data) ++{ ++ unsigned int idx = clkspec->args[0]; ++ struct clk **clks = data; ++ ++ if (idx >= 5) { ++ pr_err("%s: invalid clock index %d\n", __func__, idx); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return clks[idx] ? : ERR_PTR(-ENODEV); ++} ++ ++static const struct of_device_id krait_cc_match_table[] = { ++ { .compatible = "qcom,krait-cc-v1", (void *)1UL }, ++ { .compatible = "qcom,krait-cc-v2" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, krait_cc_match_table); ++ ++static int krait_cc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *id; ++ unsigned long cur_rate, aux_rate; ++ int cpu; ++ struct clk *clk; ++ struct clk **clks; ++ struct clk *l2_pri_mux_clk; ++ ++ id = of_match_device(krait_cc_match_table, dev); ++ if (!id) ++ return -ENODEV; ++ ++ /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */ ++ clk = clk_register_fixed_rate(dev, "qsb", NULL, CLK_IS_ROOT, 1); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ if (!id->data) { ++ clk = clk_register_fixed_factor(dev, "acpu_aux", ++ "gpll0_vote", 0, 1, 2); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ } ++ ++ /* Krait configurations have at most 4 CPUs and one L2 */ ++ clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL); ++ if (!clks) ++ return -ENOMEM; ++ ++ for_each_possible_cpu(cpu) { ++ clk = krait_add_clks(dev, cpu, id->data); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ clks[cpu] = clk; ++ } ++ ++ l2_pri_mux_clk = krait_add_clks(dev, -1, id->data); ++ if (IS_ERR(l2_pri_mux_clk)) ++ return PTR_ERR(l2_pri_mux_clk); ++ clks[4] = l2_pri_mux_clk; ++ ++ /* ++ * We don't want the CPU or L2 clocks to be turned off at late init ++ * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the ++ * refcount of these clocks. Any cpufreq/hotplug manager can assume ++ * that the clocks have already been prepared and enabled by the time ++ * they take over. ++ */ ++ for_each_online_cpu(cpu) { ++ clk_prepare_enable(l2_pri_mux_clk); ++ WARN(clk_prepare_enable(clks[cpu]), ++ "Unable to turn on CPU%d clock", cpu); ++ } ++ ++ /* ++ * Force reinit of HFPLLs and muxes to overwrite any potential ++ * incorrect configuration of HFPLLs and muxes by the bootloader. ++ * While at it, also make sure the cores are running at known rates ++ * and print the current rate. ++ * ++ * The clocks are set to aux clock rate first to make sure the ++ * secondary mux is not sourcing off of QSB. The rate is then set to ++ * two different rates to force a HFPLL reinit under all ++ * circumstances. ++ */ ++ cur_rate = clk_get_rate(l2_pri_mux_clk); ++ aux_rate = 384000000; ++ if (cur_rate == 1) { ++ pr_info("L2 @ QSB rate. Forcing new rate.\n"); ++ cur_rate = aux_rate; ++ } ++ clk_set_rate(l2_pri_mux_clk, aux_rate); ++ clk_set_rate(l2_pri_mux_clk, 2); ++ clk_set_rate(l2_pri_mux_clk, cur_rate); ++ pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000); ++ for_each_possible_cpu(cpu) { ++ clk = clks[cpu]; ++ cur_rate = clk_get_rate(clk); ++ if (cur_rate == 1) { ++ pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu); ++ cur_rate = aux_rate; ++ } ++ clk_set_rate(clk, aux_rate); ++ clk_set_rate(clk, 2); ++ clk_set_rate(clk, cur_rate); ++ pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000); ++ } ++ ++ of_clk_add_provider(dev->of_node, krait_of_get, clks); ++ ++ return 0; ++} ++ ++static struct platform_driver krait_cc_driver = { ++ .probe = krait_cc_probe, ++ .driver = { ++ .name = "krait-cc", ++ .of_match_table = krait_cc_match_table, ++ }, ++}; ++module_platform_driver(krait_cc_driver); ++ ++MODULE_DESCRIPTION("Krait CPU Clock Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:krait-cc"); diff --git a/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch b/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch new file mode 100644 index 0000000000..fd37c05425 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/143-cpufreq-Add-module-to-register-cpufreq-on-Krait-CPUs.patch @@ -0,0 +1,304 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v3,12/13] cpufreq: Add module to register cpufreq on Krait CPUs +From: Stephen Boyd <sboyd@codeaurora.org> +X-Patchwork-Id: 6063191 +Message-Id: <1426920332-9340-13-git-send-email-sboyd@codeaurora.org> +To: Mike Turquette <mturquette@linaro.org>, Stephen Boyd <sboyd@codeaurora.org> +Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, + Viresh Kumar <viresh.kumar@linaro.org>, <devicetree@vger.kernel.org> +Date: Fri, 20 Mar 2015 23:45:31 -0700 + +Register a cpufreq-generic device whenever we detect that a +"qcom,krait" compatible CPU is present in DT. + +Cc: <devicetree@vger.kernel.org> +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- +.../devicetree/bindings/arm/msm/qcom,pvs.txt | 38 ++++ + drivers/cpufreq/Kconfig.arm | 9 + + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/qcom-cpufreq.c | 204 +++++++++++++++++++++ + 4 files changed, 252 insertions(+) + create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt + create mode 100644 drivers/cpufreq/qcom-cpufreq.c + +--- /dev/null ++++ b/Documentation/devicetree/bindings/arm/msm/qcom,pvs.txt +@@ -0,0 +1,38 @@ ++Qualcomm Process Voltage Scaling Tables ++ ++The node name is required to be "qcom,pvs". There shall only be one ++such node present in the root of the tree. ++ ++PROPERTIES ++ ++- qcom,pvs-format-a or qcom,pvs-format-b: ++ Usage: required ++ Value type: <empty> ++ Definition: Indicates the format of qcom,speedX-pvsY-bin-vZ properties. ++ If qcom,pvs-format-a is used the table is two columns ++ (frequency and voltage in that order). If qcom,pvs-format-b is used the table is three columns (frequency, voltage, ++ and current in that order). ++ ++- qcom,speedX-pvsY-bin-vZ: ++ Usage: required ++ Value type: <prop-encoded-array> ++ Definition: The PVS table corresponding to the speed bin X, pvs bin Y, ++ and version Z. ++Example: ++ ++ qcom,pvs { ++ qcom,pvs-format-a; ++ qcom,speed0-pvs0-bin-v0 = ++ < 384000000 950000 >, ++ < 486000000 975000 >, ++ < 594000000 1000000 >, ++ < 702000000 1025000 >, ++ < 810000000 1075000 >, ++ < 918000000 1100000 >, ++ < 1026000000 1125000 >, ++ < 1134000000 1175000 >, ++ < 1242000000 1200000 >, ++ < 1350000000 1225000 >, ++ < 1458000000 1237500 >, ++ < 1512000000 1250000 >; ++ }; +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -146,6 +146,15 @@ config ARM_OMAP2PLUS_CPUFREQ + depends on ARCH_OMAP2PLUS + default ARCH_OMAP2PLUS + ++config ARM_QCOM_CPUFREQ ++ tristate "Qualcomm based" ++ depends on ARCH_QCOM ++ select PM_OPP ++ help ++ This adds the CPUFreq driver for Qualcomm SoC based boards. ++ ++ If in doubt, say N. ++ + config ARM_S3C_CPUFREQ + bool + help +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -66,6 +66,7 @@ obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += ki + obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o + obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o + obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o ++obj-$(CONFIG_ARM_QCOM_CPUFREQ) += qcom-cpufreq.o + obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o + obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o + obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o +--- /dev/null ++++ b/drivers/cpufreq/qcom-cpufreq.c +@@ -0,0 +1,204 @@ ++/* Copyright (c) 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/cpu.h> ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/pm_opp.h> ++#include <linux/slab.h> ++#include <linux/cpufreq-dt.h> ++ ++static void __init get_krait_bin_format_a(int *speed, int *pvs, int *pvs_ver) ++{ ++ void __iomem *base; ++ u32 pte_efuse; ++ ++ *speed = *pvs = *pvs_ver = 0; ++ ++ base = ioremap(0x007000c0, 4); ++ if (!base) { ++ pr_warn("Unable to read efuse data. Defaulting to 0!\n"); ++ return; ++ } ++ ++ pte_efuse = readl_relaxed(base); ++ iounmap(base); ++ ++ *speed = pte_efuse & 0xf; ++ if (*speed == 0xf) ++ *speed = (pte_efuse >> 4) & 0xf; ++ ++ if (*speed == 0xf) { ++ *speed = 0; ++ pr_warn("Speed bin: Defaulting to %d\n", *speed); ++ } else { ++ pr_info("Speed bin: %d\n", *speed); ++ } ++ ++ *pvs = (pte_efuse >> 10) & 0x7; ++ if (*pvs == 0x7) ++ *pvs = (pte_efuse >> 13) & 0x7; ++ ++ if (*pvs == 0x7) { ++ *pvs = 0; ++ pr_warn("PVS bin: Defaulting to %d\n", *pvs); ++ } else { ++ pr_info("PVS bin: %d\n", *pvs); ++ } ++} ++ ++static void __init get_krait_bin_format_b(int *speed, int *pvs, int *pvs_ver) ++{ ++ u32 pte_efuse, redundant_sel; ++ void __iomem *base; ++ ++ *speed = 0; ++ *pvs = 0; ++ *pvs_ver = 0; ++ ++ base = ioremap(0xfc4b80b0, 8); ++ if (!base) { ++ pr_warn("Unable to read efuse data. Defaulting to 0!\n"); ++ return; ++ } ++ ++ pte_efuse = readl_relaxed(base); ++ redundant_sel = (pte_efuse >> 24) & 0x7; ++ *speed = pte_efuse & 0x7; ++ /* 4 bits of PVS are in efuse register bits 31, 8-6. */ ++ *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); ++ *pvs_ver = (pte_efuse >> 4) & 0x3; ++ ++ switch (redundant_sel) { ++ case 1: ++ *speed = (pte_efuse >> 27) & 0xf; ++ break; ++ case 2: ++ *pvs = (pte_efuse >> 27) & 0xf; ++ break; ++ } ++ ++ /* Check SPEED_BIN_BLOW_STATUS */ ++ if (pte_efuse & BIT(3)) { ++ pr_info("Speed bin: %d\n", *speed); ++ } else { ++ pr_warn("Speed bin not set. Defaulting to 0!\n"); ++ *speed = 0; ++ } ++ ++ /* Check PVS_BLOW_STATUS */ ++ pte_efuse = readl_relaxed(base + 0x4) & BIT(21); ++ if (pte_efuse) { ++ pr_info("PVS bin: %d\n", *pvs); ++ } else { ++ pr_warn("PVS bin not set. Defaulting to 0!\n"); ++ *pvs = 0; ++ } ++ ++ pr_info("PVS version: %d\n", *pvs_ver); ++ iounmap(base); ++} ++ ++static int __init qcom_cpufreq_populate_opps(void) ++{ ++ int len, rows, cols, i, k, speed, pvs, pvs_ver; ++ char table_name[] = "qcom,speedXX-pvsXX-bin-vXX"; ++ struct device_node *np; ++ struct device *dev; ++ int cpu = 0; ++ ++ np = of_find_node_by_name(NULL, "qcom,pvs"); ++ if (!np) ++ return -ENODEV; ++ ++ if (of_property_read_bool(np, "qcom,pvs-format-a")) { ++ get_krait_bin_format_a(&speed, &pvs, &pvs_ver); ++ cols = 2; ++ } else if (of_property_read_bool(np, "qcom,pvs-format-b")) { ++ get_krait_bin_format_b(&speed, &pvs, &pvs_ver); ++ cols = 3; ++ } else { ++ return -ENODEV; ++ } ++ ++ snprintf(table_name, sizeof(table_name), ++ "qcom,speed%d-pvs%d-bin-v%d", speed, pvs, pvs_ver); ++ ++ if (!of_find_property(np, table_name, &len)) ++ return -EINVAL; ++ ++ len /= sizeof(u32); ++ if (len % cols || len == 0) ++ return -EINVAL; ++ ++ rows = len / cols; ++ ++ for (i = 0, k = 0; i < rows; i++) { ++ u32 freq, volt; ++ ++ of_property_read_u32_index(np, table_name, k++, &freq); ++ of_property_read_u32_index(np, table_name, k++, &volt); ++ while (k % cols) ++ k++; /* Skip uA entries if present */ ++ for (cpu = 0; cpu < num_possible_cpus(); cpu++) { ++ dev = get_cpu_device(cpu); ++ if (!dev) ++ return -ENODEV; ++ if (dev_pm_opp_add(dev, freq, volt)) ++ pr_warn("failed to add OPP %u\n", freq); ++ } ++ } ++ ++ return 0; ++} ++ ++static int __init qcom_cpufreq_driver_init(void) ++{ ++ struct cpufreq_dt_platform_data pdata = { .independent_clocks = true }; ++ struct platform_device_info devinfo = { ++ .name = "cpufreq-dt", ++ .data = &pdata, ++ .size_data = sizeof(pdata), ++ }; ++ struct device *cpu_dev; ++ struct device_node *np; ++ int ret; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) ++ return -ENODEV; ++ ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) ++ return -ENOENT; ++ ++ if (!of_device_is_compatible(np, "qcom,krait")) { ++ of_node_put(np); ++ return -ENODEV; ++ } ++ of_node_put(np); ++ ++ ret = qcom_cpufreq_populate_opps(); ++ if (ret) ++ return ret; ++ ++ return PTR_ERR_OR_ZERO(platform_device_register_full(&devinfo)); ++} ++module_init(qcom_cpufreq_driver_init); ++ ++MODULE_DESCRIPTION("Qualcomm CPUfreq driver"); ++MODULE_LICENSE("GPL v2"); diff --git a/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch b/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch new file mode 100644 index 0000000000..46feec366d --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/144-ARM-dts-qcom-Add-necessary-DT-data-for-Krait-cpufreq.patch @@ -0,0 +1,100 @@ +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -25,6 +25,11 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc0>; + qcom,saw = <&saw0>; ++ clocks = <&kraitcc 0>; ++ clock-names = "cpu"; ++ clock-latency = <100000>; ++ core-supply = <&smb208_s2a>; ++ voltage-tolerance = <5>; + }; + + cpu@1 { +@@ -35,11 +40,24 @@ + next-level-cache = <&L2>; + qcom,acc = <&acc1>; + qcom,saw = <&saw1>; ++ clocks = <&kraitcc 1>; ++ clock-names = "cpu"; ++ clock-latency = <100000>; ++ core-supply = <&smb208_s2b>; + }; + + L2: l2-cache { + compatible = "cache"; + cache-level = <2>; ++ clocks = <&kraitcc 4>; ++ clock-names = "cache"; ++ cache-points-kHz = < ++ /* kHz uV CPU kHz */ ++ 1200000 1150000 1200000 ++ 1000000 1100000 600000 ++ 384000 1100000 384000 ++ >; ++ vdd_dig-supply = <&smb208_s1a>; + }; + }; + +@@ -195,6 +213,46 @@ + }; + }; + ++ kraitcc: clock-controller { ++ compatible = "qcom,krait-cc-v1"; ++ #clock-cells = <1>; ++ }; ++ ++ qcom,pvs { ++ qcom,pvs-format-a; ++ qcom,speed0-pvs0-bin-v0 = ++ < 1400000000 1250000 >, ++ < 1200000000 1200000 >, ++ < 1000000000 1150000 >, ++ < 800000000 1100000 >, ++ < 600000000 1050000 >, ++ < 384000000 1000000 >; ++ ++ qcom,speed0-pvs1-bin-v0 = ++ < 1400000000 1175000 >, ++ < 1200000000 1125000 >, ++ < 1000000000 1075000 >, ++ < 800000000 1025000 >, ++ < 600000000 975000 >, ++ < 384000000 925000 >; ++ ++ qcom,speed0-pvs2-bin-v0 = ++ < 1400000000 1125000 >, ++ < 1200000000 1075000 >, ++ < 1000000000 1025000 >, ++ < 800000000 995000 >, ++ < 600000000 925000 >, ++ < 384000000 875000 >; ++ ++ qcom,speed0-pvs3-bin-v0 = ++ < 1400000000 1050000 >, ++ < 1200000000 1000000 >, ++ < 1000000000 950000 >, ++ < 800000000 900000 >, ++ < 600000000 850000 >, ++ < 384000000 800000 >; ++ }; ++ + soc: soc { + #address-cells = <1>; + #size-cells = <1>; +@@ -310,11 +368,13 @@ + 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 { diff --git a/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch b/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch new file mode 100644 index 0000000000..521adc5c1e --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/145-cpufreq-Add-a-cpufreq-krait-based-on-cpufre.patch @@ -0,0 +1,461 @@ +From dd77db4143290689d3a5e1ec61627233d0711b66 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 30 May 2014 16:36:11 -0700 +Subject: [PATCH] FROMLIST: cpufreq: Add a cpufreq-krait based on cpufreq-cpu0 + +Krait processors have individual clocks for each CPU that can +scale independently from one another. cpufreq-cpu0 is fairly +close to this, but assumes that there is only one clock for all +CPUs. Add a driver to support the Krait configuration. + +TODO: Merge into cpufreq-cpu0? Or make generic? + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> + +--- + drivers/cpufreq/Kconfig | 13 +++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/cpufreq-krait.c | 190 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 204 insertions(+) + create mode 100644 drivers/cpufreq/cpufreq-krait.c + +--- a/drivers/cpufreq/Kconfig ++++ b/drivers/cpufreq/Kconfig +@@ -198,6 +198,19 @@ config CPUFREQ_DT + + If in doubt, say N. + ++config GENERIC_CPUFREQ_KRAIT ++ tristate "Krait cpufreq driver" ++ depends on HAVE_CLK && OF ++ # if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y: ++ depends on !CPU_THERMAL || THERMAL ++ select PM_OPP ++ help ++ This adds a generic cpufreq driver for CPU0 frequency management. ++ It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) ++ systems which share clock and voltage across all CPUs. ++ ++ If in doubt, say N. ++ + if X86 + source "drivers/cpufreq/Kconfig.x86" + endif +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -14,6 +14,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) + obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o + + obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o ++obj-$(CONFIG_GENERIC_CPUFREQ_KRAIT) += cpufreq-krait.o + + ################################################################################## + # x86 drivers. +--- /dev/null ++++ b/drivers/cpufreq/cpufreq-krait.c +@@ -0,0 +1,390 @@ ++/* ++ * Copyright (C) 2012 Freescale Semiconductor, Inc. ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * The OPP code in function krait_set_target() is reused from ++ * drivers/cpufreq/omap-cpufreq.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/cpu.h> ++#include <linux/cpu_cooling.h> ++#include <linux/cpufreq.h> ++#include <linux/cpumask.h> ++#include <linux/err.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/pm_opp.h> ++#include <linux/platform_device.h> ++#include <linux/regulator/consumer.h> ++#include <linux/slab.h> ++#include <linux/thermal.h> ++ ++static unsigned int transition_latency; ++static unsigned int voltage_tolerance; /* in percentage */ ++ ++static struct device *cpu_dev; ++static DEFINE_PER_CPU(struct clk *, krait_cpu_clks); ++static DEFINE_PER_CPU(struct regulator *, krait_supply_core); ++static struct cpufreq_frequency_table *freq_table; ++static struct thermal_cooling_device *cdev; ++ ++struct cache_points { ++ unsigned long cache_freq; ++ unsigned int cache_volt; ++ unsigned long cpu_freq; ++}; ++ ++static struct regulator *krait_l2_reg; ++static struct clk *krait_l2_clk; ++static struct cache_points *krait_l2_points; ++static int nr_krait_l2_points; ++ ++static int krait_parse_cache_points(struct device *dev, ++ struct device_node *of_node) ++{ ++ const struct property *prop; ++ const __be32 *val; ++ int nr, i; ++ ++ prop = of_find_property(of_node, "cache-points-kHz", NULL); ++ if (!prop) ++ return -ENODEV; ++ if (!prop->value) ++ return -ENODATA; ++ ++ /* ++ * Each OPP is a set of tuples consisting of frequency and ++ * cpu-frequency like <freq-kHz volt-uV freq-kHz>. ++ */ ++ nr = prop->length / sizeof(u32); ++ if (nr % 3) { ++ dev_err(dev, "%s: Invalid cache points\n", __func__); ++ return -EINVAL; ++ } ++ nr /= 3; ++ ++ krait_l2_points = devm_kcalloc(dev, nr, sizeof(*krait_l2_points), ++ GFP_KERNEL); ++ if (!krait_l2_points) ++ return -ENOMEM; ++ nr_krait_l2_points = nr; ++ ++ for (i = 0, val = prop->value; i < nr; i++) { ++ unsigned long cache_freq = be32_to_cpup(val++) * 1000; ++ unsigned int cache_volt = be32_to_cpup(val++); ++ unsigned long cpu_freq = be32_to_cpup(val++) * 1000; ++ ++ krait_l2_points[i].cache_freq = cache_freq; ++ krait_l2_points[i].cache_volt = cache_volt; ++ krait_l2_points[i].cpu_freq = cpu_freq; ++ } ++ ++ return 0; ++} ++ ++static int krait_set_target(struct cpufreq_policy *policy, unsigned int index) ++{ ++ struct dev_pm_opp *opp; ++ unsigned long volt = 0, volt_old = 0, tol = 0; ++ unsigned long freq, max_cpu_freq = 0; ++ unsigned int old_freq, new_freq; ++ long freq_Hz, freq_exact; ++ int ret, i; ++ struct clk *cpu_clk; ++ struct regulator *core; ++ unsigned int cpu; ++ ++ cpu_clk = per_cpu(krait_cpu_clks, policy->cpu); ++ ++ freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); ++ if (freq_Hz <= 0) ++ freq_Hz = freq_table[index].frequency * 1000; ++ ++ freq_exact = freq_Hz; ++ new_freq = freq_Hz / 1000; ++ old_freq = clk_get_rate(cpu_clk) / 1000; ++ ++ core = per_cpu(krait_supply_core, policy->cpu); ++ ++ rcu_read_lock(); ++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); ++ if (IS_ERR(opp)) { ++ rcu_read_unlock(); ++ pr_err("failed to find OPP for %ld\n", freq_Hz); ++ return PTR_ERR(opp); ++ } ++ volt = dev_pm_opp_get_voltage(opp); ++ rcu_read_unlock(); ++ tol = volt * voltage_tolerance / 100; ++ volt_old = regulator_get_voltage(core); ++ ++ pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n", ++ old_freq / 1000, volt_old ? volt_old / 1000 : -1, ++ new_freq / 1000, volt ? volt / 1000 : -1); ++ ++ /* scaling up? scale voltage before frequency */ ++ if (new_freq > old_freq) { ++ ret = regulator_set_voltage_tol(core, volt, tol); ++ if (ret) { ++ pr_err("failed to scale voltage up: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ ret = clk_set_rate(cpu_clk, freq_exact); ++ if (ret) { ++ pr_err("failed to set clock rate: %d\n", ret); ++ return ret; ++ } ++ ++ /* scaling down? scale voltage after frequency */ ++ if (new_freq < old_freq) { ++ ret = regulator_set_voltage_tol(core, volt, tol); ++ if (ret) { ++ pr_err("failed to scale voltage down: %d\n", ret); ++ clk_set_rate(cpu_clk, old_freq * 1000); ++ } ++ } ++ ++ for_each_possible_cpu(cpu) { ++ freq = clk_get_rate(per_cpu(krait_cpu_clks, cpu)); ++ max_cpu_freq = max(max_cpu_freq, freq); ++ } ++ ++ for (i = 0; i < nr_krait_l2_points; i++) { ++ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) { ++ if (krait_l2_reg) { ++ ret = regulator_set_voltage_tol(krait_l2_reg, ++ krait_l2_points[i].cache_volt, ++ tol); ++ if (ret) { ++ pr_err("failed to scale l2 voltage: %d\n", ++ ret); ++ } ++ } ++ ret = clk_set_rate(krait_l2_clk, ++ krait_l2_points[i].cache_freq); ++ if (ret) ++ pr_err("failed to scale l2 clk: %d\n", ret); ++ break; ++ } ++ ++ } ++ ++ return ret; ++} ++ ++static int krait_cpufreq_init(struct cpufreq_policy *policy) ++{ ++ int ret; ++ ++ policy->clk = per_cpu(krait_cpu_clks, policy->cpu); ++ ++ ret = cpufreq_table_validate_and_show(policy, freq_table); ++ if (ret) { ++ pr_err("%s: invalid frequency table: %d\n", __func__, ret); ++ return ret; ++ } ++ ++ policy->cpuinfo.transition_latency = transition_latency; ++ ++ return 0; ++} ++ ++static struct cpufreq_driver krait_cpufreq_driver = { ++ .flags = CPUFREQ_STICKY, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = krait_set_target, ++ .get = cpufreq_generic_get, ++ .init = krait_cpufreq_init, ++ .name = "generic_krait", ++ .attr = cpufreq_generic_attr, ++}; ++ ++static int krait_cpufreq_probe(struct platform_device *pdev) ++{ ++ struct device_node *np, *cache; ++ int ret, i; ++ unsigned int cpu; ++ struct device *dev; ++ struct clk *clk; ++ struct regulator *core; ++ unsigned long freq_Hz, freq, max_cpu_freq; ++ struct dev_pm_opp *opp; ++ unsigned long volt, tol; ++ ++ cpu_dev = get_cpu_device(0); ++ if (!cpu_dev) { ++ pr_err("failed to get krait device\n"); ++ return -ENODEV; ++ } ++ ++ np = of_node_get(cpu_dev->of_node); ++ if (!np) { ++ pr_err("failed to find krait node\n"); ++ return -ENOENT; ++ } ++ ++ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); ++ if (ret) { ++ pr_err("failed to init cpufreq table: %d\n", ret); ++ goto out_put_node; ++ } ++ ++ of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance); ++ ++ if (of_property_read_u32(np, "clock-latency", &transition_latency)) ++ transition_latency = CPUFREQ_ETERNAL; ++ ++ cache = of_find_next_cache_node(np); ++ if (cache) { ++ struct device_node *vdd; ++ ++ vdd = of_parse_phandle(cache, "vdd_dig-supply", 0); ++ if (vdd) { ++ krait_l2_reg = regulator_get(NULL, vdd->name); ++ if (IS_ERR(krait_l2_reg)) { ++ pr_warn("failed to get l2 vdd_dig supply\n"); ++ krait_l2_reg = NULL; ++ } ++ of_node_put(vdd); ++ } ++ ++ krait_l2_clk = of_clk_get(cache, 0); ++ if (!IS_ERR(krait_l2_clk)) { ++ ret = krait_parse_cache_points(&pdev->dev, cache); ++ if (ret) ++ clk_put(krait_l2_clk); ++ } ++ if (IS_ERR(krait_l2_clk) || ret) ++ krait_l2_clk = NULL; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ dev = get_cpu_device(cpu); ++ if (!dev) { ++ pr_err("failed to get krait device\n"); ++ ret = -ENOENT; ++ goto out_free_table; ++ } ++ per_cpu(krait_cpu_clks, cpu) = clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(clk)) { ++ ret = PTR_ERR(clk); ++ goto out_free_table; ++ } ++ core = devm_regulator_get(dev, "core"); ++ if (IS_ERR(core)) { ++ pr_debug("failed to get core regulator\n"); ++ ret = PTR_ERR(core); ++ goto out_free_table; ++ } ++ per_cpu(krait_supply_core, cpu) = core; ++ ++ freq_Hz = clk_get_rate(clk); ++ ++ rcu_read_lock(); ++ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz); ++ if (IS_ERR(opp)) { ++ rcu_read_unlock(); ++ pr_err("failed to find OPP for %ld\n", freq_Hz); ++ ret = PTR_ERR(opp); ++ goto out_free_table; ++ } ++ volt = dev_pm_opp_get_voltage(opp); ++ rcu_read_unlock(); ++ ++ tol = volt * voltage_tolerance / 100; ++ ret = regulator_set_voltage_tol(core, volt, tol); ++ if (ret) { ++ pr_err("failed to scale voltage up: %d\n", ret); ++ goto out_free_table; ++ } ++ ret = regulator_enable(core); ++ if (ret) { ++ pr_err("failed to enable regulator: %d\n", ret); ++ goto out_free_table; ++ } ++ max_cpu_freq = max(max_cpu_freq, freq); ++ } ++ ++ for (i = 0; i < nr_krait_l2_points; i++) { ++ if (max_cpu_freq >= krait_l2_points[i].cpu_freq) { ++ if (krait_l2_reg) { ++ ret = regulator_set_voltage_tol(krait_l2_reg, ++ krait_l2_points[i].cache_volt, ++ tol); ++ if (ret) ++ pr_err("failed to scale l2 voltage: %d\n", ++ ret); ++ ret = regulator_enable(krait_l2_reg); ++ if (ret) ++ pr_err("failed to enable l2 voltage: %d\n", ++ ret); ++ } ++ break; ++ } ++ ++ } ++ ++ ret = cpufreq_register_driver(&krait_cpufreq_driver); ++ if (ret) { ++ pr_err("failed register driver: %d\n", ret); ++ goto out_free_table; ++ } ++ of_node_put(np); ++ ++ /* ++ * For now, just loading the cooling device; ++ * thermal DT code takes care of matching them. ++ */ ++ for_each_possible_cpu(cpu) { ++ dev = get_cpu_device(cpu); ++ np = of_node_get(dev->of_node); ++ if (of_find_property(np, "#cooling-cells", NULL)) { ++ cdev = of_cpufreq_cooling_register(np, cpumask_of(cpu)); ++ if (IS_ERR(cdev)) ++ pr_err("running cpufreq without cooling device: %ld\n", ++ PTR_ERR(cdev)); ++ } ++ of_node_put(np); ++ } ++ ++ return 0; ++ ++out_free_table: ++ regulator_put(krait_l2_reg); ++ clk_put(krait_l2_clk); ++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++out_put_node: ++ of_node_put(np); ++ return ret; ++} ++ ++static int krait_cpufreq_remove(struct platform_device *pdev) ++{ ++ cpufreq_cooling_unregister(cdev); ++ cpufreq_unregister_driver(&krait_cpufreq_driver); ++ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); ++ clk_put(krait_l2_clk); ++ regulator_put(krait_l2_reg); ++ ++ return 0; ++} ++ ++static struct platform_driver krait_cpufreq_platdrv = { ++ .driver = { ++ .name = "cpufreq-krait", ++ .owner = THIS_MODULE, ++ }, ++ .probe = krait_cpufreq_probe, ++ .remove = krait_cpufreq_remove, ++}; ++module_platform_driver(krait_cpufreq_platdrv); ++ ++MODULE_DESCRIPTION("Krait CPUfreq driver"); ++MODULE_LICENSE("GPL v2"); +--- a/drivers/cpufreq/qcom-cpufreq.c ++++ b/drivers/cpufreq/qcom-cpufreq.c +@@ -168,11 +168,8 @@ static int __init qcom_cpufreq_populate_ + + static int __init qcom_cpufreq_driver_init(void) + { +- struct cpufreq_dt_platform_data pdata = { .independent_clocks = true }; + struct platform_device_info devinfo = { +- .name = "cpufreq-dt", +- .data = &pdata, +- .size_data = sizeof(pdata), ++ .name = "cpufreq-krait", + }; + struct device *cpu_dev; + struct device_node *np; diff --git a/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch b/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch new file mode 100644 index 0000000000..51ebc193b0 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/300-arch-arm-force-ZRELADDR-on-arch-qcom.patch @@ -0,0 +1,62 @@ +From b12e230f09d4481424e6a5d7e2ae566b6954e83f Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Wed, 29 Apr 2015 15:21:46 -0700 +Subject: [PATCH] HACK: arch: arm: force ZRELADDR on arch-qcom + +ARCH_QCOM is using the ARCH_MULTIPLATFORM option, as now recommended +on most ARM architectures. This automatically calculate ZRELADDR by +masking PHYS_OFFSET with 0xf8000000. + +However, on IPQ806x, the first ~20MB of RAM is reserved for the hardware +network accelerators, and the bootloader removes this section from the +layout passed from the ATAGS (when used). + +For newer bootloader, when DT is used, this is not a problem, we just +reserve this memory in the device tree. But if the bootloader doesn't +have DT support, then ATAGS have to be used. In this case, the ARM +decompressor will position the kernel in this low mem, which will not be +in the RAM section mapped by the bootloader, which means the kernel will +freeze in the middle of the boot process trying to map the memory. + +As a work around, this patch allows disabling AUTO_ZRELADDR when +ARCH_QCOM is selected. It makes the zImage usage possible on bootloaders +which don't support device-tree, which is the case on certain early +IPQ806x based designs. + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + arch/arm/Kconfig | 2 +- + arch/arm/Makefile | 2 ++ + arch/arm/mach-qcom/Makefile.boot | 1 + + 3 files changed, 4 insertions(+), 1 deletion(-) + create mode 100644 arch/arm/mach-qcom/Makefile.boot + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -320,7 +320,7 @@ config ARCH_MULTIPLATFORM + select ARCH_WANT_OPTIONAL_GPIOLIB + select ARM_HAS_SG_CHAIN + select ARM_PATCH_PHYS_VIRT +- select AUTO_ZRELADDR ++ select AUTO_ZRELADDR if !ARCH_QCOM + select CLKSRC_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -240,9 +240,11 @@ MACHINE := arch/arm/mach-$(word 1,$(mac + else + MACHINE := + endif ++ifeq ($(CONFIG_ARCH_QCOM),) + ifeq ($(CONFIG_ARCH_MULTIPLATFORM),y) + MACHINE := + endif ++endif + + machdirs := $(patsubst %,arch/arm/mach-%/,$(machine-y)) + platdirs := $(patsubst %,arch/arm/plat-%/,$(sort $(plat-y))) +--- /dev/null ++++ b/arch/arm/mach-qcom/Makefile.boot +@@ -0,0 +1 @@ ++zreladdr-y+= 0x42208000 diff --git a/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch b/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch new file mode 100644 index 0000000000..dfe716953f --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/700-clk-qcom-Add-support-for-NSS-GMAC-clocks-and-resets.patch @@ -0,0 +1,734 @@ +From 2fbb18f85826a9ba308fedb2cf90d3a661a39fd7 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd <sboyd@codeaurora.org> +Date: Fri, 27 Mar 2015 00:16:14 -0700 +Subject: [PATCH] clk: qcom: Add support for NSS/GMAC clocks and resets + +Add the NSS/GMAC clocks and the TCM clock and NSS resets. + +Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> +--- + drivers/clk/qcom/gcc-ipq806x.c | 594 ++++++++++++++++++++++++++- + drivers/clk/qcom/gcc-ipq806x.c.rej | 50 +++ + include/dt-bindings/clock/qcom,gcc-ipq806x.h | 2 + + include/dt-bindings/reset/qcom,gcc-ipq806x.h | 43 ++ + 4 files changed, 688 insertions(+), 1 deletion(-) + create mode 100644 drivers/clk/qcom/gcc-ipq806x.c.rej + +--- a/drivers/clk/qcom/gcc-ipq806x.c ++++ b/drivers/clk/qcom/gcc-ipq806x.c +@@ -220,12 +220,47 @@ static struct clk_regmap pll14_vote = { + }, + }; + ++#define NSS_PLL_RATE(f, _l, _m, _n, i) \ ++ { \ ++ .freq = f, \ ++ .l = _l, \ ++ .m = _m, \ ++ .n = _n, \ ++ .ibits = i, \ ++ } ++ ++static struct pll_freq_tbl pll18_freq_tbl[] = { ++ NSS_PLL_RATE(550000000, 44, 0, 1, 0x01495625), ++ NSS_PLL_RATE(733000000, 58, 16, 25, 0x014b5625), ++}; ++ ++static struct clk_pll pll18 = { ++ .l_reg = 0x31a4, ++ .m_reg = 0x31a8, ++ .n_reg = 0x31ac, ++ .config_reg = 0x31b4, ++ .mode_reg = 0x31a0, ++ .status_reg = 0x31b8, ++ .status_bit = 16, ++ .post_div_shift = 16, ++ .post_div_width = 1, ++ .freq_tbl = pll18_freq_tbl, ++ .clkr.hw.init = &(struct clk_init_data){ ++ .name = "pll18", ++ .parent_names = (const char *[]){ "pxo" }, ++ .num_parents = 1, ++ .ops = &clk_pll_ops, ++ }, ++}; ++ + enum { + P_PXO, + P_PLL8, + P_PLL3, + P_PLL0, + P_CXO, ++ P_PLL14, ++ P_PLL18, + }; + + static const struct parent_map gcc_pxo_pll8_map[] = { +@@ -277,6 +312,22 @@ static const char *gcc_pxo_pll8_pll0_map + "pll0_vote", + }; + ++static const struct parent_map gcc_pxo_pll8_pll14_pll18_pll0_map[] = { ++ { P_PXO, 0 }, ++ { P_PLL8, 4 }, ++ { P_PLL0, 2 }, ++ { P_PLL14, 5 }, ++ { P_PLL18, 1 }, ++}; ++ ++static const char *gcc_pxo_pll8_pll14_pll18_pll0[] = { ++ "pxo", ++ "pll8_vote", ++ "pll0_vote", ++ "pll14", ++ "pll18", ++}; ++ + static struct freq_tbl clk_tbl_gsbi_uart[] = { + { 1843200, P_PLL8, 2, 6, 625 }, + { 3686400, P_PLL8, 2, 12, 625 }, +@@ -2282,6 +2333,472 @@ static struct clk_branch ebi2_aon_clk = + }, + }; + ++static const struct freq_tbl clk_tbl_gmac[] = { ++ { 133000000, P_PLL0, 1, 50, 301 }, ++ { 266000000, P_PLL0, 1, 127, 382 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg gmac_core1_src = { ++ .ns_reg[0] = 0x3cac, ++ .ns_reg[1] = 0x3cb0, ++ .md_reg[0] = 0x3ca4, ++ .md_reg[1] = 0x3ca8, ++ .bank_reg = 0x3ca0, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_gmac, ++ .clkr = { ++ .enable_reg = 0x3ca0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core1_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gmac_core1_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 4, ++ .hwcg_reg = 0x3cb4, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3cb4, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core1_clk", ++ .parent_names = (const char *[]){ ++ "gmac_core1_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_dyn_rcg gmac_core2_src = { ++ .ns_reg[0] = 0x3ccc, ++ .ns_reg[1] = 0x3cd0, ++ .md_reg[0] = 0x3cc4, ++ .md_reg[1] = 0x3cc8, ++ .bank_reg = 0x3ca0, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_gmac, ++ .clkr = { ++ .enable_reg = 0x3cc0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core2_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gmac_core2_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 5, ++ .hwcg_reg = 0x3cd4, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3cd4, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core2_clk", ++ .parent_names = (const char *[]){ ++ "gmac_core2_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_dyn_rcg gmac_core3_src = { ++ .ns_reg[0] = 0x3cec, ++ .ns_reg[1] = 0x3cf0, ++ .md_reg[0] = 0x3ce4, ++ .md_reg[1] = 0x3ce8, ++ .bank_reg = 0x3ce0, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_gmac, ++ .clkr = { ++ .enable_reg = 0x3ce0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core3_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gmac_core3_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 6, ++ .hwcg_reg = 0x3cf4, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3cf4, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core3_clk", ++ .parent_names = (const char *[]){ ++ "gmac_core3_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static struct clk_dyn_rcg gmac_core4_src = { ++ .ns_reg[0] = 0x3d0c, ++ .ns_reg[1] = 0x3d10, ++ .md_reg[0] = 0x3d04, ++ .md_reg[1] = 0x3d08, ++ .bank_reg = 0x3d00, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_gmac, ++ .clkr = { ++ .enable_reg = 0x3d00, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core4_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch gmac_core4_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 7, ++ .hwcg_reg = 0x3d14, ++ .hwcg_bit = 6, ++ .clkr = { ++ .enable_reg = 0x3d14, ++ .enable_mask = BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "gmac_core4_clk", ++ .parent_names = (const char *[]){ ++ "gmac_core4_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_nss_tcm[] = { ++ { 266000000, P_PLL0, 3, 0, 0 }, ++ { 400000000, P_PLL0, 2, 0, 0 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg nss_tcm_src = { ++ .ns_reg[0] = 0x3dc4, ++ .ns_reg[1] = 0x3dc8, ++ .bank_reg = 0x3dc0, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 4, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_nss_tcm, ++ .clkr = { ++ .enable_reg = 0x3dc0, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "nss_tcm_src", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ }, ++ }, ++}; ++ ++static struct clk_branch nss_tcm_clk = { ++ .halt_reg = 0x3c20, ++ .halt_bit = 14, ++ .clkr = { ++ .enable_reg = 0x3dd0, ++ .enable_mask = BIT(6) | BIT(4), ++ .hw.init = &(struct clk_init_data){ ++ .name = "nss_tcm_clk", ++ .parent_names = (const char *[]){ ++ "nss_tcm_src", ++ }, ++ .num_parents = 1, ++ .ops = &clk_branch_ops, ++ .flags = CLK_SET_RATE_PARENT, ++ }, ++ }, ++}; ++ ++static const struct freq_tbl clk_tbl_nss[] = { ++ { 110000000, P_PLL18, 1, 1, 5 }, ++ { 275000000, P_PLL18, 2, 0, 0 }, ++ { 550000000, P_PLL18, 1, 0, 0 }, ++ { 733000000, P_PLL18, 1, 0, 0 }, ++ { } ++}; ++ ++static struct clk_dyn_rcg ubi32_core1_src_clk = { ++ .ns_reg[0] = 0x3d2c, ++ .ns_reg[1] = 0x3d30, ++ .md_reg[0] = 0x3d24, ++ .md_reg[1] = 0x3d28, ++ .bank_reg = 0x3d20, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_nss, ++ .clkr = { ++ .enable_reg = 0x3d20, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ubi32_core1_src_clk", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ }, ++ }, ++}; ++ ++static struct clk_dyn_rcg ubi32_core2_src_clk = { ++ .ns_reg[0] = 0x3d4c, ++ .ns_reg[1] = 0x3d50, ++ .md_reg[0] = 0x3d44, ++ .md_reg[1] = 0x3d48, ++ .bank_reg = 0x3d40, ++ .mn[0] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .mn[1] = { ++ .mnctr_en_bit = 8, ++ .mnctr_reset_bit = 7, ++ .mnctr_mode_shift = 5, ++ .n_val_shift = 16, ++ .m_val_shift = 16, ++ .width = 8, ++ }, ++ .s[0] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .s[1] = { ++ .src_sel_shift = 0, ++ .parent_map = gcc_pxo_pll8_pll14_pll18_pll0_map, ++ }, ++ .p[0] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .p[1] = { ++ .pre_div_shift = 3, ++ .pre_div_width = 2, ++ }, ++ .mux_sel_bit = 0, ++ .freq_tbl = clk_tbl_nss, ++ .clkr = { ++ .enable_reg = 0x3d40, ++ .enable_mask = BIT(1), ++ .hw.init = &(struct clk_init_data){ ++ .name = "ubi32_core2_src_clk", ++ .parent_names = gcc_pxo_pll8_pll14_pll18_pll0, ++ .num_parents = 5, ++ .ops = &clk_dyn_rcg_ops, ++ .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, ++ }, ++ }, ++}; ++ + static struct clk_regmap *gcc_ipq806x_clks[] = { + [PLL0] = &pll0.clkr, + [PLL0_VOTE] = &pll0_vote, +@@ -2291,6 +2808,7 @@ static struct clk_regmap *gcc_ipq806x_cl + [PLL8_VOTE] = &pll8_vote, + [PLL14] = &pll14.clkr, + [PLL14_VOTE] = &pll14_vote, ++ [PLL18] = &pll18.clkr, + [GSBI1_UART_SRC] = &gsbi1_uart_src.clkr, + [GSBI1_UART_CLK] = &gsbi1_uart_clk.clkr, + [GSBI2_UART_SRC] = &gsbi2_uart_src.clkr, +@@ -2390,6 +2908,18 @@ static struct clk_regmap *gcc_ipq806x_cl + [PLL9] = &hfpll0.clkr, + [PLL10] = &hfpll1.clkr, + [PLL12] = &hfpll_l2.clkr, ++ [GMAC_CORE1_CLK_SRC] = &gmac_core1_src.clkr, ++ [GMAC_CORE1_CLK] = &gmac_core1_clk.clkr, ++ [GMAC_CORE2_CLK_SRC] = &gmac_core2_src.clkr, ++ [GMAC_CORE2_CLK] = &gmac_core2_clk.clkr, ++ [GMAC_CORE3_CLK_SRC] = &gmac_core3_src.clkr, ++ [GMAC_CORE3_CLK] = &gmac_core3_clk.clkr, ++ [GMAC_CORE4_CLK_SRC] = &gmac_core4_src.clkr, ++ [GMAC_CORE4_CLK] = &gmac_core4_clk.clkr, ++ [UBI32_CORE1_CLK_SRC] = &ubi32_core1_src_clk.clkr, ++ [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, ++ [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, ++ [NSSTCM_CLK] = &nss_tcm_clk.clkr, + }; + + static const struct qcom_reset_map gcc_ipq806x_resets[] = { +@@ -2508,6 +3038,48 @@ static const struct qcom_reset_map gcc_i + [USB30_1_PHY_RESET] = { 0x3b58, 0 }, + [NSSFB0_RESET] = { 0x3b60, 6 }, + [NSSFB1_RESET] = { 0x3b60, 7 }, ++ [UBI32_CORE1_CLKRST_CLAMP_RESET] = { 0x3d3c, 3}, ++ [UBI32_CORE1_CLAMP_RESET] = { 0x3d3c, 2 }, ++ [UBI32_CORE1_AHB_RESET] = { 0x3d3c, 1 }, ++ [UBI32_CORE1_AXI_RESET] = { 0x3d3c, 0 }, ++ [UBI32_CORE2_CLKRST_CLAMP_RESET] = { 0x3d5c, 3 }, ++ [UBI32_CORE2_CLAMP_RESET] = { 0x3d5c, 2 }, ++ [UBI32_CORE2_AHB_RESET] = { 0x3d5c, 1 }, ++ [UBI32_CORE2_AXI_RESET] = { 0x3d5c, 0 }, ++ [GMAC_CORE1_RESET] = { 0x3cbc, 0 }, ++ [GMAC_CORE2_RESET] = { 0x3cdc, 0 }, ++ [GMAC_CORE3_RESET] = { 0x3cfc, 0 }, ++ [GMAC_CORE4_RESET] = { 0x3d1c, 0 }, ++ [GMAC_AHB_RESET] = { 0x3e24, 0 }, ++ [NSS_CH0_RST_RX_CLK_N_RESET] = { 0x3b60, 0 }, ++ [NSS_CH0_RST_TX_CLK_N_RESET] = { 0x3b60, 1 }, ++ [NSS_CH0_RST_RX_125M_N_RESET] = { 0x3b60, 2 }, ++ [NSS_CH0_HW_RST_RX_125M_N_RESET] = { 0x3b60, 3 }, ++ [NSS_CH0_RST_TX_125M_N_RESET] = { 0x3b60, 4 }, ++ [NSS_CH1_RST_RX_CLK_N_RESET] = { 0x3b60, 5 }, ++ [NSS_CH1_RST_TX_CLK_N_RESET] = { 0x3b60, 6 }, ++ [NSS_CH1_RST_RX_125M_N_RESET] = { 0x3b60, 7 }, ++ [NSS_CH1_HW_RST_RX_125M_N_RESET] = { 0x3b60, 8 }, ++ [NSS_CH1_RST_TX_125M_N_RESET] = { 0x3b60, 9 }, ++ [NSS_CH2_RST_RX_CLK_N_RESET] = { 0x3b60, 10 }, ++ [NSS_CH2_RST_TX_CLK_N_RESET] = { 0x3b60, 11 }, ++ [NSS_CH2_RST_RX_125M_N_RESET] = { 0x3b60, 12 }, ++ [NSS_CH2_HW_RST_RX_125M_N_RESET] = { 0x3b60, 13 }, ++ [NSS_CH2_RST_TX_125M_N_RESET] = { 0x3b60, 14 }, ++ [NSS_CH3_RST_RX_CLK_N_RESET] = { 0x3b60, 15 }, ++ [NSS_CH3_RST_TX_CLK_N_RESET] = { 0x3b60, 16 }, ++ [NSS_CH3_RST_RX_125M_N_RESET] = { 0x3b60, 17 }, ++ [NSS_CH3_HW_RST_RX_125M_N_RESET] = { 0x3b60, 18 }, ++ [NSS_CH3_RST_TX_125M_N_RESET] = { 0x3b60, 19 }, ++ [NSS_RST_RX_250M_125M_N_RESET] = { 0x3b60, 20 }, ++ [NSS_RST_TX_250M_125M_N_RESET] = { 0x3b60, 21 }, ++ [NSS_QSGMII_TXPI_RST_N_RESET] = { 0x3b60, 22 }, ++ [NSS_QSGMII_CDR_RST_N_RESET] = { 0x3b60, 23 }, ++ [NSS_SGMII2_CDR_RST_N_RESET] = { 0x3b60, 24 }, ++ [NSS_SGMII3_CDR_RST_N_RESET] = { 0x3b60, 25 }, ++ [NSS_CAL_PRBS_RST_N_RESET] = { 0x3b60, 26 }, ++ [NSS_LCKDT_RST_N_RESET] = { 0x3b60, 27 }, ++ [NSS_SRDS_N_RESET] = { 0x3b60, 28 }, + }; + + static const struct regmap_config gcc_ipq806x_regmap_config = { +@@ -2536,6 +3108,8 @@ static int gcc_ipq806x_probe(struct plat + { + struct clk *clk; + struct device *dev = &pdev->dev; ++ struct regmap *regmap; ++ int ret; + + /* Temporary until RPM clocks supported */ + clk = clk_register_fixed_rate(dev, "cxo", NULL, CLK_IS_ROOT, 25000000); +@@ -2546,7 +3120,25 @@ static int gcc_ipq806x_probe(struct plat + if (IS_ERR(clk)) + return PTR_ERR(clk); + +- return qcom_cc_probe(pdev, &gcc_ipq806x_desc); ++ ret = qcom_cc_probe(pdev, &gcc_ipq806x_desc); ++ if (ret) ++ return ret; ++ ++ regmap = dev_get_regmap(dev, NULL); ++ if (!regmap) ++ return -ENODEV; ++ ++ /* Setup PLL18 static bits */ ++ regmap_update_bits(regmap, 0x31a4, 0xffffffc0, 0x40000400); ++ regmap_write(regmap, 0x31b0, 0x3080); ++ ++ /* Set GMAC footswitch sleep/wakeup values */ ++ regmap_write(regmap, 0x3cb8, 8); ++ regmap_write(regmap, 0x3cd8, 8); ++ regmap_write(regmap, 0x3cf8, 8); ++ regmap_write(regmap, 0x3d18, 8); ++ ++ return 0; + } + + static int gcc_ipq806x_remove(struct platform_device *pdev) +--- a/include/dt-bindings/reset/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/reset/qcom,gcc-ipq806x.h +@@ -129,4 +129,47 @@ + #define USB30_1_PHY_RESET 112 + #define NSSFB0_RESET 113 + #define NSSFB1_RESET 114 ++#define UBI32_CORE1_CLKRST_CLAMP_RESET 115 ++#define UBI32_CORE1_CLAMP_RESET 116 ++#define UBI32_CORE1_AHB_RESET 117 ++#define UBI32_CORE1_AXI_RESET 118 ++#define UBI32_CORE2_CLKRST_CLAMP_RESET 119 ++#define UBI32_CORE2_CLAMP_RESET 120 ++#define UBI32_CORE2_AHB_RESET 121 ++#define UBI32_CORE2_AXI_RESET 122 ++#define GMAC_CORE1_RESET 123 ++#define GMAC_CORE2_RESET 124 ++#define GMAC_CORE3_RESET 125 ++#define GMAC_CORE4_RESET 126 ++#define GMAC_AHB_RESET 127 ++#define NSS_CH0_RST_RX_CLK_N_RESET 128 ++#define NSS_CH0_RST_TX_CLK_N_RESET 129 ++#define NSS_CH0_RST_RX_125M_N_RESET 130 ++#define NSS_CH0_HW_RST_RX_125M_N_RESET 131 ++#define NSS_CH0_RST_TX_125M_N_RESET 132 ++#define NSS_CH1_RST_RX_CLK_N_RESET 133 ++#define NSS_CH1_RST_TX_CLK_N_RESET 134 ++#define NSS_CH1_RST_RX_125M_N_RESET 135 ++#define NSS_CH1_HW_RST_RX_125M_N_RESET 136 ++#define NSS_CH1_RST_TX_125M_N_RESET 137 ++#define NSS_CH2_RST_RX_CLK_N_RESET 138 ++#define NSS_CH2_RST_TX_CLK_N_RESET 139 ++#define NSS_CH2_RST_RX_125M_N_RESET 140 ++#define NSS_CH2_HW_RST_RX_125M_N_RESET 141 ++#define NSS_CH2_RST_TX_125M_N_RESET 142 ++#define NSS_CH3_RST_RX_CLK_N_RESET 143 ++#define NSS_CH3_RST_TX_CLK_N_RESET 144 ++#define NSS_CH3_RST_RX_125M_N_RESET 145 ++#define NSS_CH3_HW_RST_RX_125M_N_RESET 146 ++#define NSS_CH3_RST_TX_125M_N_RESET 147 ++#define NSS_RST_RX_250M_125M_N_RESET 148 ++#define NSS_RST_TX_250M_125M_N_RESET 149 ++#define NSS_QSGMII_TXPI_RST_N_RESET 150 ++#define NSS_QSGMII_CDR_RST_N_RESET 151 ++#define NSS_SGMII2_CDR_RST_N_RESET 152 ++#define NSS_SGMII3_CDR_RST_N_RESET 153 ++#define NSS_CAL_PRBS_RST_N_RESET 154 ++#define NSS_LCKDT_RST_N_RESET 155 ++#define NSS_SRDS_N_RESET 156 ++ + #endif +--- a/include/dt-bindings/clock/qcom,gcc-ipq806x.h ++++ b/include/dt-bindings/clock/qcom,gcc-ipq806x.h +@@ -289,5 +289,7 @@ + #define UBI32_CORE1_CLK 279 + #define UBI32_CORE2_CLK 280 + #define EBI2_AON_CLK 281 ++#define NSSTCM_CLK_SRC 282 ++#define NSSTCM_CLK 283 + + #endif diff --git a/target/linux/ipq806x/patches-4.1/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch b/target/linux/ipq806x/patches-4.1/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch new file mode 100644 index 0000000000..3711e1282b --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/701-stmmac-add-phy-handle-support-to-the-platform-layer.patch @@ -0,0 +1,105 @@ +From 4f09499bc1d9bb095caccbcd73ff951ee631e521 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Fri, 8 May 2015 15:42:40 -0700 +Subject: [PATCH 1/8] stmmac: add phy-handle support to the platform layer + +On stmmac driver, PHY specification in device-tree was done using the +non-standard property "snps,phy-addr". Specifying a PHY on a different +MDIO bus that the one within the stmmac controller doesn't seem to be +possible when device-tree is used. + +This change adds support for the phy-handle property, as specified in +Documentation/devicetree/bindings/net/ethernet.txt. + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 28 ++++++++++++++-------- + .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 6 ++++- + include/linux/stmmac.h | 1 + + 3 files changed, 24 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -52,6 +52,7 @@ + #include "stmmac_ptp.h" + #include "stmmac.h" + #include <linux/reset.h> ++#include <linux/of_mdio.h> + + #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) + +@@ -816,18 +817,25 @@ static int stmmac_init_phy(struct net_de + priv->speed = 0; + priv->oldduplex = -1; + +- if (priv->plat->phy_bus_name) +- snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", +- priv->plat->phy_bus_name, priv->plat->bus_id); +- else +- snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", +- priv->plat->bus_id); +- +- snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, +- priv->plat->phy_addr); +- pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt); ++ if (priv->plat->phy_node) { ++ phydev = of_phy_connect(dev, priv->plat->phy_node, ++ &stmmac_adjust_link, 0, interface); ++ } else { ++ if (priv->plat->phy_bus_name) ++ snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x", ++ priv->plat->phy_bus_name, priv->plat->bus_id); ++ else ++ snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", ++ priv->plat->bus_id); ++ ++ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, ++ priv->plat->phy_addr); ++ pr_debug("stmmac_init_phy: trying to attach to %s\n", ++ phy_id_fmt); + +- phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface); ++ phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, ++ interface); ++ } + + if (IS_ERR(phydev)) { + pr_err("%s: Could not attach to PHY\n", dev->name); +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -28,6 +28,7 @@ + #include <linux/of.h> + #include <linux/of_net.h> + #include <linux/of_device.h> ++#include <linux/of_mdio.h> + + #include "stmmac.h" + #include "stmmac_platform.h" +@@ -168,13 +169,16 @@ static int stmmac_probe_config_dt(struct + /* Default to phy auto-detection */ + plat->phy_addr = -1; + ++ /* If we find a phy-handle property, use it as the PHY */ ++ plat->phy_node = of_parse_phandle(np, "phy-handle", 0); ++ + /* "snps,phy-addr" is not a standard property. Mark it as deprecated + * and warn of its use. Remove this when phy node support is added. + */ + if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0) + dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n"); + +- if (plat->phy_bus_name) ++ if (plat->phy_node || plat->phy_bus_name) + plat->mdio_bus_data = NULL; + else + plat->mdio_bus_data = +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -99,6 +99,7 @@ struct plat_stmmacenet_data { + int phy_addr; + int interface; + struct stmmac_mdio_bus_data *mdio_bus_data; ++ struct device_node *phy_node; + struct stmmac_dma_cfg *dma_cfg; + int clk_csr; + int has_gmac; diff --git a/target/linux/ipq806x/patches-4.1/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch b/target/linux/ipq806x/patches-4.1/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch new file mode 100644 index 0000000000..37f1002131 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/702-stmmac-move-error-path-at-the-end-of-stmmac_probe_co.patch @@ -0,0 +1,65 @@ +From 0149d275415cd1b2382ce94e5eb32641590097d0 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Fri, 8 May 2015 15:57:12 -0700 +Subject: [PATCH 2/8] stmmac: move error path at the end of + stmmac_probe_config_dt() + +We will want to do additional clean-up on certain errors. Therefore, +this change moves the error path at the end of the function for better +code readability. + +This patch doesn't change anything functionally. + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -130,13 +130,18 @@ static int stmmac_probe_config_dt(struct + struct device_node *np = pdev->dev.of_node; + struct stmmac_dma_cfg *dma_cfg; + const struct of_device_id *device; ++ int ret; + +- if (!np) +- return -ENODEV; ++ if (!np) { ++ ret = -ENODEV; ++ goto err; ++ } + + device = of_match_device(stmmac_dt_ids, &pdev->dev); +- if (!device) +- return -ENODEV; ++ if (!device) { ++ ret = -ENODEV; ++ goto err; ++ } + + if (device->data) { + const struct stmmac_of_data *data = device->data; +@@ -236,8 +241,10 @@ static int stmmac_probe_config_dt(struct + if (of_find_property(np, "snps,pbl", NULL)) { + dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), + GFP_KERNEL); +- if (!dma_cfg) +- return -ENOMEM; ++ if (!dma_cfg) { ++ ret = -ENOMEM; ++ goto err; ++ } + plat->dma_cfg = dma_cfg; + of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); + dma_cfg->fixed_burst = +@@ -255,6 +262,9 @@ static int stmmac_probe_config_dt(struct + } + + return 0; ++ ++err: ++ return ret; + } + #else + static int stmmac_probe_config_dt(struct platform_device *pdev, diff --git a/target/linux/ipq806x/patches-4.1/703-stmmac-add-fixed-link-device-tree-support.patch b/target/linux/ipq806x/patches-4.1/703-stmmac-add-fixed-link-device-tree-support.patch new file mode 100644 index 0000000000..a05875bca5 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/703-stmmac-add-fixed-link-device-tree-support.patch @@ -0,0 +1,64 @@ +From 3a95f75867be562cb919ff23a738f70357188fbd Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Fri, 8 May 2015 16:02:03 -0700 +Subject: [PATCH 3/8] stmmac: add fixed-link device-tree support + +In case DT is used, this change adds the ability to the stmmac driver to +detect a fixed-link PHY, instanciate it, and use it during +phy_connect(). + +Fixed link PHYs DT usage is described in: +Documentation/devicetree/bindings/net/fixed-link.txt + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- + drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 12 +++++++++++- + 2 files changed, 12 insertions(+), 2 deletions(-) + +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -856,7 +856,7 @@ static int stmmac_init_phy(struct net_de + * device as well. + * Note: phydev->phy_id is the result of reading the UID PHY registers. + */ +- if (phydev->phy_id == 0) { ++ if (!priv->plat->phy_node && phydev->phy_id == 0) { + phy_disconnect(phydev); + return -ENODEV; + } +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -177,6 +177,14 @@ static int stmmac_probe_config_dt(struct + /* If we find a phy-handle property, use it as the PHY */ + plat->phy_node = of_parse_phandle(np, "phy-handle", 0); + ++ /* If phy-handle is not specified, check if we have a fixed-phy */ ++ if (!plat->phy_node && of_phy_is_fixed_link(np)) { ++ if ((of_phy_register_fixed_link(np) < 0)) ++ return -ENODEV; ++ ++ plat->phy_node = of_node_get(np); ++ } ++ + /* "snps,phy-addr" is not a standard property. Mark it as deprecated + * and warn of its use. Remove this when phy node support is added. + */ +@@ -243,7 +251,7 @@ static int stmmac_probe_config_dt(struct + GFP_KERNEL); + if (!dma_cfg) { + ret = -ENOMEM; +- goto err; ++ goto err2; + } + plat->dma_cfg = dma_cfg; + of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); +@@ -263,6 +271,8 @@ static int stmmac_probe_config_dt(struct + + return 0; + ++err2: ++ of_node_put(np); + err: + return ret; + } diff --git a/target/linux/ipq806x/patches-4.1/704-stmmac-add-ipq806x-glue-layer.patch b/target/linux/ipq806x/patches-4.1/704-stmmac-add-ipq806x-glue-layer.patch new file mode 100644 index 0000000000..7fd72be5a8 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/704-stmmac-add-ipq806x-glue-layer.patch @@ -0,0 +1,407 @@ +From 69fb970ad3fe05af7cb99ea78230c69c7ca0d03b Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Fri, 8 May 2015 16:10:22 -0700 +Subject: [PATCH 4/8] stmmac: add ipq806x glue layer + +The ethernet controller available in IPQ806x is a Synopsys DesignWare +Gigabit MAC IP core, already supported by the stmmac driver. + +This glue layer implements some platform specific settings required to +get the controller working on an IPQ806x based platform. + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + drivers/net/ethernet/stmicro/stmmac/Kconfig | 1 + + drivers/net/ethernet/stmicro/stmmac/Makefile | 2 +- + drivers/net/ethernet/stmicro/stmmac/dwmac-ipq.c | 324 +++++++++++++++++++++ + .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 1 + + .../net/ethernet/stmicro/stmmac/stmmac_platform.h | 1 + + 5 files changed, 328 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-ipq.c + +--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig ++++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig +@@ -16,6 +16,7 @@ if STMMAC_ETH + config STMMAC_PLATFORM + tristate "STMMAC Platform bus support" + depends on STMMAC_ETH ++ select MFD_SYSCON + default y + ---help--- + This selects the platform specific bus support for the stmmac driver. +--- a/drivers/net/ethernet/stmicro/stmmac/Makefile ++++ b/drivers/net/ethernet/stmicro/stmmac/Makefile +@@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethto + + obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o + stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o \ +- dwmac-sti.o dwmac-socfpga.o dwmac-rk.o ++ dwmac-sti.o dwmac-socfpga.o dwmac-rk.o dwmac-ipq806x.o + + obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o + stmmac-pci-objs:= stmmac_pci.o +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -43,6 +43,7 @@ static const struct of_device_id stmmac_ + { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data}, + { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data}, + { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data }, ++ { .compatible = "qcom,ipq806x-gmac", .data = &ipq806x_gmac_data }, + { .compatible = "st,spear600-gmac"}, + { .compatible = "snps,dwmac-3.610"}, + { .compatible = "snps,dwmac-3.70a"}, +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h +@@ -25,5 +25,6 @@ extern const struct stmmac_of_data stih4 + extern const struct stmmac_of_data stid127_dwmac_data; + extern const struct stmmac_of_data socfpga_gmac_data; + extern const struct stmmac_of_data rk3288_gmac_data; ++extern const struct stmmac_of_data ipq806x_gmac_data; + + #endif /* __STMMAC_PLATFORM_H__ */ +--- /dev/null ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +@@ -0,0 +1,343 @@ ++/* ++ * Qualcomm Atheros IPQ806x GMAC glue layer ++ * ++ * Copyright (C) 2015 The Linux Foundation ++ * ++ * Permission to use, copy, modify, and/or distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/phy.h> ++#include <linux/regmap.h> ++#include <linux/clk.h> ++#include <linux/reset.h> ++#include <linux/of_net.h> ++#include <linux/mfd/syscon.h> ++#include <linux/stmmac.h> ++#include <linux/of_mdio.h> ++ ++#include "stmmac_platform.h" ++ ++#define NSS_COMMON_CLK_GATE 0x8 ++#define NSS_COMMON_CLK_GATE_PTP_EN(x) BIT(0x10 + x) ++#define NSS_COMMON_CLK_GATE_RGMII_RX_EN(x) BIT(0x9 + (x * 2)) ++#define NSS_COMMON_CLK_GATE_RGMII_TX_EN(x) BIT(0x8 + (x * 2)) ++#define NSS_COMMON_CLK_GATE_GMII_RX_EN(x) BIT(0x4 + x) ++#define NSS_COMMON_CLK_GATE_GMII_TX_EN(x) BIT(0x0 + x) ++ ++#define NSS_COMMON_CLK_DIV0 0xC ++#define NSS_COMMON_CLK_DIV_OFFSET(x) (x * 8) ++#define NSS_COMMON_CLK_DIV_MASK 0x7f ++ ++#define NSS_COMMON_CLK_SRC_CTRL 0x14 ++#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (1 << x) ++/* Mode is coded on 1 bit but is different depending on the MAC ID: ++ * MAC0: QSGMII=0 RGMII=1 ++ * MAC1: QSGMII=0 SGMII=0 RGMII=1 ++ * MAC2 & MAC3: QSGMII=0 SGMII=1 ++ */ ++#define NSS_COMMON_CLK_SRC_CTRL_RGMII(x) 1 ++#define NSS_COMMON_CLK_SRC_CTRL_SGMII(x) ((x >= 2) ? 1 : 0) ++ ++#define NSS_COMMON_MACSEC_CTL 0x28 ++#define NSS_COMMON_MACSEC_CTL_EXT_BYPASS_EN(x) (1 << x) ++ ++#define NSS_COMMON_GMAC_CTL(x) (0x30 + (x * 4)) ++#define NSS_COMMON_GMAC_CTL_CSYS_REQ BIT(19) ++#define NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL BIT(16) ++#define NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET 8 ++#define NSS_COMMON_GMAC_CTL_IFG_OFFSET 0 ++#define NSS_COMMON_GMAC_CTL_IFG_MASK 0x3f ++ ++#define NSS_COMMON_CLK_DIV_RGMII_1000 1 ++#define NSS_COMMON_CLK_DIV_RGMII_100 9 ++#define NSS_COMMON_CLK_DIV_RGMII_10 99 ++#define NSS_COMMON_CLK_DIV_SGMII_1000 0 ++#define NSS_COMMON_CLK_DIV_SGMII_100 4 ++#define NSS_COMMON_CLK_DIV_SGMII_10 49 ++ ++#define QSGMII_PCS_MODE_CTL 0x68 ++#define QSGMII_PCS_MODE_CTL_AUTONEG_EN(x) BIT((x * 8) + 7) ++ ++#define QSGMII_PCS_CAL_LCKDT_CTL 0x120 ++#define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19) ++ ++/* Only GMAC1/2/3 support SGMII and their CTL register are not contiguous */ ++#define QSGMII_PHY_SGMII_CTL(x) ((x == 1) ? 0x134 : \ ++ (0x13c + (4 * (x - 2)))) ++#define QSGMII_PHY_CDR_EN BIT(0) ++#define QSGMII_PHY_RX_FRONT_EN BIT(1) ++#define QSGMII_PHY_RX_SIGNAL_DETECT_EN BIT(2) ++#define QSGMII_PHY_TX_DRIVER_EN BIT(3) ++#define QSGMII_PHY_QSGMII_EN BIT(7) ++#define QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET 12 ++#define QSGMII_PHY_PHASE_LOOP_GAIN_MASK 0x7 ++#define QSGMII_PHY_RX_DC_BIAS_OFFSET 18 ++#define QSGMII_PHY_RX_DC_BIAS_MASK 0x3 ++#define QSGMII_PHY_RX_INPUT_EQU_OFFSET 20 ++#define QSGMII_PHY_RX_INPUT_EQU_MASK 0x3 ++#define QSGMII_PHY_CDR_PI_SLEW_OFFSET 22 ++#define QSGMII_PHY_CDR_PI_SLEW_MASK 0x3 ++#define QSGMII_PHY_TX_DRV_AMP_OFFSET 28 ++#define QSGMII_PHY_TX_DRV_AMP_MASK 0xf ++ ++struct ipq806x_gmac { ++ struct platform_device *pdev; ++ struct regmap *nss_common; ++ struct regmap *qsgmii_csr; ++ uint32_t id; ++ struct clk *core_clk; ++ phy_interface_t phy_mode; ++}; ++ ++static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed) ++{ ++ struct device *dev = &gmac->pdev->dev; ++ int div; ++ ++ switch (speed) { ++ case SPEED_1000: ++ div = NSS_COMMON_CLK_DIV_SGMII_1000; ++ break; ++ ++ case SPEED_100: ++ div = NSS_COMMON_CLK_DIV_SGMII_100; ++ break; ++ ++ case SPEED_10: ++ div = NSS_COMMON_CLK_DIV_SGMII_10; ++ break; ++ ++ default: ++ dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed); ++ return -EINVAL; ++ } ++ ++ return div; ++} ++ ++static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed) ++{ ++ struct device *dev = &gmac->pdev->dev; ++ int div; ++ ++ switch (speed) { ++ case SPEED_1000: ++ div = NSS_COMMON_CLK_DIV_RGMII_1000; ++ break; ++ ++ case SPEED_100: ++ div = NSS_COMMON_CLK_DIV_RGMII_100; ++ break; ++ ++ case SPEED_10: ++ div = NSS_COMMON_CLK_DIV_RGMII_10; ++ break; ++ ++ default: ++ dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed); ++ return -EINVAL; ++ } ++ ++ return div; ++} ++ ++static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed) ++{ ++ uint32_t clk_bits, val; ++ int div; ++ ++ switch (gmac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ div = get_clk_div_rgmii(gmac, speed); ++ clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) | ++ NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id); ++ break; ++ ++ case PHY_INTERFACE_MODE_SGMII: ++ div = get_clk_div_sgmii(gmac, speed); ++ clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) | ++ NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id); ++ break; ++ ++ default: ++ dev_err(&gmac->pdev->dev, "Unsupported PHY mode: \"%s\"\n", ++ phy_modes(gmac->phy_mode)); ++ return -EINVAL; ++ } ++ ++ /* Disable the clocks */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); ++ val &= ~clk_bits; ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); ++ ++ /* Set the divider */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val); ++ val &= ~(NSS_COMMON_CLK_DIV_MASK ++ << NSS_COMMON_CLK_DIV_OFFSET(gmac->id)); ++ val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id); ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val); ++ ++ /* Enable the clock back */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); ++ val |= clk_bits; ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); ++ ++ return 0; ++} ++ ++static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) ++{ ++ struct device *dev = &gmac->pdev->dev; ++ ++ gmac->phy_mode = of_get_phy_mode(dev->of_node); ++ if (gmac->phy_mode < 0) { ++ dev_err(dev, "missing phy mode property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) { ++ dev_err(dev, "missing qcom id property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ /* The GMACs are called 1 to 4 in the documentation, but to simplify the ++ * code and keep it consistent with the Linux convention, we'll number ++ * them from 0 to 3 here. ++ */ ++ if (gmac->id < 0 || gmac->id > 3) { ++ dev_err(dev, "invalid gmac id\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ gmac->core_clk = devm_clk_get(dev, "stmmaceth"); ++ if (IS_ERR(gmac->core_clk)) { ++ dev_err(dev, "missing stmmaceth clk property\n"); ++ return gmac->core_clk; ++ } ++ clk_set_rate(gmac->core_clk, 266000000); ++ ++ /* Setup the register map for the nss common registers */ ++ gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "qcom,nss-common"); ++ if (IS_ERR(gmac->nss_common)) { ++ dev_err(dev, "missing nss-common node\n"); ++ return gmac->nss_common; ++ } ++ ++ /* Setup the register map for the qsgmii csr registers */ ++ gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "qcom,qsgmii-csr"); ++ if (IS_ERR(gmac->qsgmii_csr)) { ++ dev_err(dev, "missing qsgmii-csr node\n"); ++ return gmac->qsgmii_csr; ++ } ++ ++ return NULL; ++} ++ ++static void *ipq806x_gmac_setup(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ipq806x_gmac *gmac; ++ int val; ++ void *err; ++ ++ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); ++ if (!gmac) ++ return ERR_PTR(-ENOMEM); ++ ++ gmac->pdev = pdev; ++ ++ err = ipq806x_gmac_of_parse(gmac); ++ if (err) { ++ dev_err(dev, "device tree parsing error\n"); ++ return err; ++ } ++ ++ regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL, ++ QSGMII_PCS_CAL_LCKDT_CTL_RST); ++ ++ /* Inter frame gap is set to 12 */ ++ val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET | ++ 12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET; ++ /* We also initiate an AXI low power exit request */ ++ val |= NSS_COMMON_GMAC_CTL_CSYS_REQ; ++ switch (gmac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; ++ break; ++ default: ++ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", ++ phy_modes(gmac->phy_mode)); ++ return NULL; ++ } ++ regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val); ++ ++ /* Configure the clock src according to the mode */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val); ++ val &= ~NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); ++ switch (gmac->phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) << ++ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); ++ break; ++ case PHY_INTERFACE_MODE_SGMII: ++ val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) << ++ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); ++ break; ++ default: ++ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", ++ phy_modes(gmac->phy_mode)); ++ return NULL; ++ } ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val); ++ ++ /* Enable PTP clock */ ++ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); ++ val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id); ++ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); ++ ++ if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) { ++ regmap_write(gmac->qsgmii_csr, QSGMII_PHY_SGMII_CTL(gmac->id), ++ QSGMII_PHY_CDR_EN | ++ QSGMII_PHY_RX_FRONT_EN | ++ QSGMII_PHY_RX_SIGNAL_DETECT_EN | ++ QSGMII_PHY_TX_DRIVER_EN | ++ QSGMII_PHY_QSGMII_EN | ++ 0x4 << QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET | ++ 0x3 << QSGMII_PHY_RX_DC_BIAS_OFFSET | ++ 0x1 << QSGMII_PHY_RX_INPUT_EQU_OFFSET | ++ 0x2 << QSGMII_PHY_CDR_PI_SLEW_OFFSET | ++ 0xC << QSGMII_PHY_TX_DRV_AMP_OFFSET); ++ } ++ ++ return gmac; ++} ++ ++static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed) ++{ ++ struct ipq806x_gmac *gmac = priv; ++ ++ ipq806x_gmac_set_speed(gmac, speed); ++} ++ ++const struct stmmac_of_data ipq806x_gmac_data = { ++ .has_gmac = 1, ++ .setup = ipq806x_gmac_setup, ++ .fix_mac_speed = ipq806x_gmac_fix_mac_speed, ++}; diff --git a/target/linux/ipq806x/patches-4.1/705-net-stmmac-ipq806x-document-device-tree-bindings.patch b/target/linux/ipq806x/patches-4.1/705-net-stmmac-ipq806x-document-device-tree-bindings.patch new file mode 100644 index 0000000000..3144fa36fe --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/705-net-stmmac-ipq806x-document-device-tree-bindings.patch @@ -0,0 +1,52 @@ +From 0f9605d9409b77a89daef91cc68239fc2ff50457 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Fri, 8 May 2015 16:51:25 -0700 +Subject: [PATCH 5/8] net: stmmac: ipq806x: document device tree bindings + +Add the device tree bindings documentation for the QCA IPQ806x +variant of the Synopsys DesignWare MAC. + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + .../devicetree/bindings/net/ipq806x-dwmac.txt | 35 ++++++++++++++++++++++ + 1 file changed, 35 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/ipq806x-dwmac.txt + +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/ipq806x-dwmac.txt +@@ -0,0 +1,35 @@ ++* IPQ806x DWMAC Ethernet controller ++ ++The device inherits all the properties of the dwmac/stmmac devices ++described in the file net/stmmac.txt with the following changes. ++ ++Required properties: ++ ++- compatible: should be "qcom,ipq806x-gmac" along with "snps,dwmac" ++ and any applicable more detailed version number ++ described in net/stmmac.txt ++ ++- qcom,nss-common: should contain a phandle to a syscon device mapping the ++ nss-common registers. ++ ++- qcom,qsgmii-csr: should contain a phandle to a syscon device mapping the ++ qsgmii-csr registers. ++ ++Example: ++ ++ gmac: ethernet@37000000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37000000 0x200000>; ++ interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE1_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE1_RESET>; ++ reset-names = "stmmaceth"; ++ }; diff --git a/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch b/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch new file mode 100644 index 0000000000..20d850287c --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/707-ARM-dts-qcom-add-mdio-nodes-to-ap148-db149.patch @@ -0,0 +1,145 @@ +From e81de9d28bd0421c236df322872e64edf4ee1852 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Mon, 11 May 2015 16:32:09 -0700 +Subject: [PATCH 7/8] ARM: dts: qcom: add mdio nodes to ap148 & db149 + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 40 ++++++++++++++++++++++++++- + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 46 ++++++++++++++++++++++++++++++++ + 2 files changed, 85 insertions(+), 1 deletion(-) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -14,8 +14,9 @@ + }; + }; + +- alias { ++ aliases { + serial0 = &uart4; ++ mdio-gpio0 = &mdio0; + }; + + chosen { +@@ -54,6 +55,15 @@ + bias-none; + }; + }; ++ ++ mdio0_pins: mdio0_pins { ++ mux { ++ pins = "gpio0", "gpio1"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -139,5 +149,33 @@ + pinctrl-0 = <&pcie2_pins>; + pinctrl-names = "default"; + }; ++ ++ mdio0: mdio { ++ compatible = "virtual,mdio-gpio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; ++ pinctrl-0 = <&mdio0_pins>; ++ pinctrl-names = "default"; ++ ++ phy0: ethernet-phy@0 { ++ device_type = "ethernet-phy"; ++ reg = <0>; ++ qca,ar8327-initvals = < ++ 0x00004 0x7600000 /* PAD0_MODE */ ++ 0x00008 0x1000000 /* PAD5_MODE */ ++ 0x0000c 0x80 /* PAD6_MODE */ ++ 0x000e4 0xaa545 /* MAC_POWER_SEL */ ++ 0x000e0 0xc74164de /* SGMII_CTRL */ ++ 0x0007c 0x4e /* PORT0_STATUS */ ++ 0x00094 0x4e /* PORT6_STATUS */ ++ >; ++ }; ++ ++ phy4: ethernet-phy@4 { ++ device_type = "ethernet-phy"; ++ reg = <4>; ++ }; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -16,6 +16,7 @@ + + alias { + serial0 = &uart2; ++ mdio-gpio0 = &mdio0; + }; + + chosen { +@@ -62,6 +63,15 @@ + bias-none; + }; + }; ++ ++ mdio0_pins: mdio0_pins { ++ mux { ++ pins = "gpio0", "gpio1"; ++ function = "gpio"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi2: gsbi@12480000 { +@@ -173,5 +183,44 @@ + pinctrl-0 = <&pcie3_pins>; + pinctrl-names = "default"; + }; ++ ++ mdio0: mdio { ++ compatible = "virtual,mdio-gpio"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>; ++ ++ pinctrl-0 = <&mdio0_pins>; ++ pinctrl-names = "default"; ++ ++ phy0: ethernet-phy@0 { ++ device_type = "ethernet-phy"; ++ reg = <0>; ++ qca,ar8327-initvals = < ++ 0x00004 0x7600000 /* PAD0_MODE */ ++ 0x00008 0x1000000 /* PAD5_MODE */ ++ 0x0000c 0x80 /* PAD6_MODE */ ++ 0x000e4 0xaa545 /* MAC_POWER_SEL */ ++ 0x000e0 0xc74164de /* SGMII_CTRL */ ++ 0x0007c 0x4e /* PORT0_STATUS */ ++ 0x00094 0x4e /* PORT6_STATUS */ ++ >; ++ }; ++ ++ phy4: ethernet-phy@4 { ++ device_type = "ethernet-phy"; ++ reg = <4>; ++ }; ++ ++ phy6: ethernet-phy@6 { ++ device_type = "ethernet-phy"; ++ reg = <6>; ++ }; ++ ++ phy7: ethernet-phy@7 { ++ device_type = "ethernet-phy"; ++ reg = <7>; ++ }; ++ }; + }; + }; diff --git a/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch new file mode 100644 index 0000000000..de68678d36 --- /dev/null +++ b/target/linux/ipq806x/patches-4.1/708-ARM-dts-qcom-add-gmac-nodes-to-ipq806x-platforms.patch @@ -0,0 +1,210 @@ +From cab1f4720e82f2e17eaeed9a9ad9e4f07c742977 Mon Sep 17 00:00:00 2001 +From: Mathieu Olivari <mathieu@codeaurora.org> +Date: Mon, 11 May 2015 12:29:18 -0700 +Subject: [PATCH 8/8] ARM: dts: qcom: add gmac nodes to ipq806x platforms + +Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org> +--- + arch/arm/boot/dts/qcom-ipq8064-ap148.dts | 31 ++++++++++++ + arch/arm/boot/dts/qcom-ipq8064-db149.dts | 43 ++++++++++++++++ + arch/arm/boot/dts/qcom-ipq8064.dtsi | 86 ++++++++++++++++++++++++++++++++ + 3 files changed, 160 insertions(+) + +--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts +@@ -64,6 +64,16 @@ + bias-disable; + }; + }; ++ ++ rgmii2_pins: rgmii2_pins { ++ mux { ++ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32", ++ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ; ++ function = "rgmii2"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi@16300000 { +@@ -177,5 +187,26 @@ + reg = <4>; + }; + }; ++ ++ gmac1: ethernet@37200000 { ++ status = "ok"; ++ phy-mode = "rgmii"; ++ phy-handle = <&phy4>; ++ qcom,id = <1>; ++ ++ pinctrl-0 = <&rgmii2_pins>; ++ pinctrl-names = "default"; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <2>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064-db149.dts ++++ b/arch/arm/boot/dts/qcom-ipq8064-db149.dts +@@ -72,6 +72,14 @@ + bias-disable; + }; + }; ++ ++ rgmii0_pins: rgmii0_pins { ++ mux { ++ pins = "gpio2", "gpio66"; ++ drive-strength = <8>; ++ bias-disable; ++ }; ++ }; + }; + + gsbi2: gsbi@12480000 { +@@ -222,5 +230,40 @@ + reg = <7>; + }; + }; ++ ++ gmac0: ethernet@37000000 { ++ status = "ok"; ++ phy-mode = "rgmii"; ++ qcom,id = <0>; ++ phy-handle = <&phy4>; ++ ++ pinctrl-0 = <&rgmii0_pins>; ++ pinctrl-names = "default"; ++ }; ++ ++ gmac1: ethernet@37200000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <1>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <2>; ++ phy-handle = <&phy6>; ++ }; ++ ++ gmac3: ethernet@37600000 { ++ status = "ok"; ++ phy-mode = "sgmii"; ++ qcom,id = <3>; ++ phy-handle = <&phy7>; ++ }; + }; + }; +--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi ++++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi +@@ -578,5 +578,91 @@ + status = "disabled"; + }; + ++ nss_common: syscon@03000000 { ++ compatible = "syscon"; ++ reg = <0x03000000 0x0000FFFF>; ++ }; ++ ++ qsgmii_csr: syscon@1bb00000 { ++ compatible = "syscon"; ++ reg = <0x1bb00000 0x000001FF>; ++ }; ++ ++ gmac0: ethernet@37000000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37000000 0x200000>; ++ interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE1_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE1_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac1: ethernet@37200000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37200000 0x200000>; ++ interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE2_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE2_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac2: ethernet@37400000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37400000 0x200000>; ++ interrupts = <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE3_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE3_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ ++ gmac3: ethernet@37600000 { ++ device_type = "network"; ++ compatible = "qcom,ipq806x-gmac"; ++ reg = <0x37600000 0x200000>; ++ interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "macirq"; ++ ++ qcom,nss-common = <&nss_common>; ++ qcom,qsgmii-csr = <&qsgmii_csr>; ++ ++ clocks = <&gcc GMAC_CORE4_CLK>; ++ clock-names = "stmmaceth"; ++ ++ resets = <&gcc GMAC_CORE4_RESET>; ++ reset-names = "stmmaceth"; ++ ++ status = "disabled"; ++ }; ++ + }; + }; |