qualcommax: add ipq50xx target

Introduce support for the Qualcomm IPQ50xx SoC.
This series adds support for the following components:
- minimal boot support: GCC/pinctrl/watchdog/CPUFreq/SDI (upstreamed)
- USB2 (upstreamed)
- Thermal/Tsens
- PCIe gen2 1&2-lane PHY and controller
- PWM and PWM LED
- QPIC SPI NAND controller
- CMN PLL Block (provider of fixed rate clocks to GCC/ethernet/more.)
- Ethernet: IPQ5018 Internal GE PHY (1 gbps)
- Remoteproc MPD driver for IPQ5018 (2.4G) & QCN6122 (5/6G) Wifi

Co-developed-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
Link: https://github.com/openwrt/openwrt/pull/17182
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
George Moussalem 2024-10-07 10:28:09 +04:00 committed by Robert Marko
parent 39750798f7
commit 34d9172655
84 changed files with 13689 additions and 1 deletions

View File

@ -6,7 +6,7 @@ BOARDNAME:=Qualcomm Atheros 802.11ax WiSoC-s
FEATURES:=squashfs ramdisk fpu nand rtc emmc
KERNELNAME:=Image
CPU_TYPE:=cortex-a53
SUBTARGETS:=ipq807x ipq60xx
SUBTARGETS:=ipq807x ipq60xx ipq50xx
KERNEL_PATCHVER:=6.6

View File

@ -0,0 +1,116 @@
#include <dt-bindings/net/qcom-ipq-ess.h>
&soc {
ess_instance: ess-instance {
#address-cells = <1>;
#size-cells = <1>;
num_devices = <1>;
switch: ess-switch@39c00000 {
compatible = "qcom,ess-switch-ipq50xx";
device_id = <0>;
cmnblk_clk = "internal_96MHz";
reg = <0x39c00000 0x200000>;
switch_access_mode = "local bus";
clocks = <&gcc GCC_CMN_BLK_AHB_CLK>,
<&gcc GCC_CMN_BLK_SYS_CLK>,
<&gcc GCC_UNIPHY_AHB_CLK>,
<&gcc GCC_UNIPHY_SYS_CLK>,
<&gcc GCC_MDIO0_AHB_CLK>,
<&gcc GCC_MDIO1_AHB_CLK>,
<&gcc GCC_GMAC0_CFG_CLK>,
<&gcc GCC_GMAC0_SYS_CLK>,
<&gcc GCC_GMAC1_CFG_CLK>,
<&gcc GCC_GMAC1_SYS_CLK>,
<&gcc GCC_GEPHY_RX_CLK>,
<&gcc GCC_GEPHY_TX_CLK>,
<&gcc GCC_UNIPHY_RX_CLK>,
<&gcc GCC_UNIPHY_TX_CLK>,
<&gcc GCC_GMAC0_RX_CLK>,
<&gcc GCC_GMAC0_TX_CLK>,
<&gcc GCC_GMAC1_RX_CLK>,
<&gcc GCC_GMAC1_TX_CLK>,
<&gcc GCC_SNOC_GMAC0_AHB_CLK>,
<&gcc GCC_SNOC_GMAC1_AHB_CLK>,
<&gcc GCC_GMAC0_PTP_CLK>,
<&gcc GCC_GMAC1_PTP_CLK>;
clock-names = "cmn_ahb_clk",
"cmn_sys_clk",
"uniphy_ahb_clk",
"uniphy_sys_clk",
"gcc_mdio0_ahb_clk",
"gcc_mdio1_ahb_clk",
"gcc_gmac0_cfg_clk",
"gcc_gmac0_sys_clk",
"gcc_gmac1_cfg_clk",
"gcc_gmac1_sys_clk",
"uniphy0_port1_rx_clk",
"uniphy0_port1_tx_clk",
"uniphy1_port5_rx_clk",
"uniphy1_port5_tx_clk",
"nss_port1_rx_clk",
"nss_port1_tx_clk",
"nss_port2_rx_clk",
"nss_port2_tx_clk",
"gcc_snoc_gmac0_ahb_clk",
"gcc_snoc_gmac1_ahb_clk",
"gcc_gmac0_ptp_clk",
"gcc_gmac1_ptp_clk";
resets = <&gcc GCC_GMAC0_BCR>,
<&gcc GCC_GMAC1_BCR>,
<&gcc GCC_UNIPHY_BCR>,
<&gcc GCC_UNIPHY_SOFT_RESET>;
reset-names = "gmac0_bcr_rst",
"gmac1_bcr_rst",
"uniphy_bcr_rst",
"uniphy1_soft_rst";
status = "disabled";
};
};
ess-uniphy@98000 {
compatible = "qcom,ess-uniphy";
reg = <0x98000 0x800>;
uniphy_access_mode = "local bus";
};
nss-dp-common {
compatible = "qcom,nss-dp-common";
qcom,tcsr-base = <0x01937000>;
};
dp1: dp1 {
device_type = "network";
compatible = "qcom,nss-dp";
qcom,id = <1>;
reg = <0x39C00000 0x10000>;
interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_SNOC_GMAC0_AXI_CLK>;
clock-names = "nss-snoc-gmac-axi-clk";
qcom,mactype = <2>; /* GMAC_HAL_TYPE_SYN_GMAC */
local-mac-address = [000000000000];
phy-mode = "internal";
phy-handle = <&ge_phy>;
status = "disabled";
};
dp2: dp2 {
device_type = "network";
compatible = "qcom,nss-dp";
qcom,id = <2>;
reg = <0x39D00000 0x10000>;
interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_SNOC_GMAC1_AXI_CLK>;
clock-names = "nss-snoc-gmac-axi-clk";
qcom,mactype = <2>; /* GMAC_HAL_TYPE_SYN_GMAC */
local-mac-address = [000000000000];
phy-mode = "sgmii";
status = "disabled";
};
};

View File

View File

@ -0,0 +1,26 @@
CONFIG_QCOM_APM=y
CONFIG_IPQ_GCC_5018=y
CONFIG_PINCTRL_IPQ5018=y
CONFIG_MTD_SPI_NAND=y
CONFIG_SPI_QPIC_SNAND=y
CONFIG_IPQ_CMN_PLL=y
CONFIG_IPQ5018_PHY=y
CONFIG_NET_DSA=y
CONFIG_NET_DSA_QCA8K=y
CONFIG_NET_DSA_TAG_QCA=y
CONFIG_QCA83XX_PHY=y
CONFIG_QCOM_Q6V5_MPD=y
CONFIG_QCOM_QMI_HELPERS=y
CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP=y
CONFIG_PCIE_QCOM=y
CONFIG_PWM=y
CONFIG_PWM_IPQ=y
CONFIG_LEDS_PWM=y
CONFIG_PHY_QCOM_M31_USB=y
CONFIG_USB_DWC3_QCOM=y

View File

@ -0,0 +1,7 @@
SUBTARGET:=ipq50xx
BOARDNAME:=Qualcomm Atheros IPQ50xx
DEFAULT_PACKAGES += ath11k-firmware-ipq5018
define Target/Description
Build firmware images for Qualcomm Atheros IPQ50xx based boards.
endef

View File

@ -0,0 +1,31 @@
From 9cbaee8379e620f82112002f973adde19679df31 Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
Date: Wed, 16 Aug 2023 18:14:00 +0200
Subject: [PATCH] arm64: dts: qcom: ipq5018: add watchdog
Add the required DT node for watchdog operation.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
Link: https://lore.kernel.org/r/20230816161455.3310629-2-robimarko@gmail.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -181,6 +181,13 @@
};
};
+ watchdog: watchdog@b017000 {
+ compatible = "qcom,apss-wdt-ipq5018", "qcom,kpss-wdt";
+ reg = <0x0b017000 0x40>;
+ interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&sleep_clk>;
+ };
+
timer@b120000 {
compatible = "arm,armv7-timer-mem";
reg = <0x0b120000 0x1000>;

View File

@ -0,0 +1,41 @@
From 92dab9ea5f389c12828283146c60054642453a91 Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
Date: Wed, 16 Aug 2023 18:45:38 +0200
Subject: [PATCH] dt-bindings: firmware: qcom,scm: support indicating SDI
default state
IPQ5018 has SDI (Secure Debug Image) enabled by TZ by default, and that
means that WDT being asserted or just trying to reboot will hang the board
in the debug mode and only pulling the power and repowering will help.
Some IPQ4019 boards like Google WiFI have it enabled as well.
So, lets add a boolean property to indicate that SDI is enabled by default
and thus needs to be disabled by the kernel.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Acked-by: Mukesh Ojha <quic_mojha@quicinc.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Reviewed-by: Brian Norris <computersforpeace@gmail.com>
Link: https://lore.kernel.org/r/20230816164641.3371878-1-robimarko@gmail.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
+++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
@@ -89,6 +89,14 @@ properties:
protocol to handle sleeping SCM calls.
maxItems: 1
+ qcom,sdi-enabled:
+ description:
+ Indicates that the SDI (Secure Debug Image) has been enabled by TZ
+ by default and it needs to be disabled.
+ If not disabled WDT assertion or reboot will cause the board to hang
+ in the debug mode.
+ type: boolean
+
qcom,dload-mode:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:

View File

@ -0,0 +1,83 @@
From ff4aa3bc98258a240b9bbab53fd8d2fb8184c485 Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
Date: Wed, 16 Aug 2023 18:45:39 +0200
Subject: [PATCH] firmware: qcom_scm: disable SDI if required
IPQ5018 has SDI (Secure Debug Image) enabled by TZ by default, and that
means that WDT being asserted or just trying to reboot will hang the board
in the debug mode and only pulling the power and repowering will help.
Some IPQ4019 boards like Google WiFI have it enabled as well.
Luckily, SDI can be disabled via an SCM call.
So, lets use the boolean DT property to identify boards that have SDI
enabled by default and use the SCM call to disable SDI during SCM probe.
It is important to disable it as soon as possible as we might have a WDT
assertion at any time which would then leave the board in debug mode,
thus disabling it during SCM removal is not enough.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Reviewed-by: Guru Das Srinagesh <quic_gurus@quicinc.com>
Link: https://lore.kernel.org/r/20230816164641.3371878-2-robimarko@gmail.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
drivers/firmware/qcom_scm.c | 30 ++++++++++++++++++++++++++++++
drivers/firmware/qcom_scm.h | 1 +
2 files changed, 31 insertions(+)
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -410,6 +410,29 @@ int qcom_scm_set_remote_state(u32 state,
}
EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
+static int qcom_scm_disable_sdi(void)
+{
+ int ret;
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_BOOT,
+ .cmd = QCOM_SCM_BOOT_SDI_CONFIG,
+ .args[0] = 1, /* Disable watchdog debug */
+ .args[1] = 0, /* Disable SDI */
+ .arginfo = QCOM_SCM_ARGS(2),
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+
+ qcom_scm_clk_disable();
+
+ return ret ? : res.result[0];
+}
+
static int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
{
struct qcom_scm_desc desc = {
@@ -1473,6 +1496,13 @@ static int qcom_scm_probe(struct platfor
__get_convention();
+
+ /*
+ * Disable SDI if indicated by DT that it is enabled by default.
+ */
+ if (of_property_read_bool(pdev->dev.of_node, "qcom,sdi-enabled"))
+ qcom_scm_disable_sdi();
+
/*
* If requested enable "download mode", from this point on warmboot
* will cause the boot stages to enter download mode, unless
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -80,6 +80,7 @@ extern int scm_legacy_call(struct device
#define QCOM_SCM_SVC_BOOT 0x01
#define QCOM_SCM_BOOT_SET_ADDR 0x01
#define QCOM_SCM_BOOT_TERMINATE_PC 0x02
+#define QCOM_SCM_BOOT_SDI_CONFIG 0x09
#define QCOM_SCM_BOOT_SET_DLOAD_MODE 0x10
#define QCOM_SCM_BOOT_SET_ADDR_MC 0x11
#define QCOM_SCM_BOOT_SET_REMOTE_STATE 0x0a

View File

@ -0,0 +1,25 @@
From f6aa7386bc40b552eea8ec1b1d2168afe3b31110 Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
Date: Wed, 16 Aug 2023 18:45:40 +0200
Subject: [PATCH] dt-bindings: firmware: qcom,scm: document IPQ5018 compatible
It seems that IPQ5018 compatible was never documented in the bindings.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Link: https://lore.kernel.org/r/20230816164641.3371878-3-robimarko@gmail.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 1 +
1 file changed, 1 insertion(+)
--- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
+++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
@@ -24,6 +24,7 @@ properties:
- qcom,scm-apq8064
- qcom,scm-apq8084
- qcom,scm-ipq4019
+ - qcom,scm-ipq5018
- qcom,scm-ipq5332
- qcom,scm-ipq6018
- qcom,scm-ipq806x

View File

@ -0,0 +1,26 @@
From 79796e87215db9587d6c66ec6f6781e091bc6464 Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
Date: Wed, 16 Aug 2023 18:45:41 +0200
Subject: [PATCH] arm64: dts: qcom: ipq5018: indicate that SDI should be
disabled
Now that SCM has support for indicating that SDI has been enabled by
default, lets set the property so SCM disables it during probing.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Link: https://lore.kernel.org/r/20230816164641.3371878-4-robimarko@gmail.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 1 +
1 file changed, 1 insertion(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -57,6 +57,7 @@
firmware {
scm {
compatible = "qcom,scm-ipq5018", "qcom,scm";
+ qcom,sdi-enabled;
};
};

View File

@ -0,0 +1,28 @@
From 1852dfaacd3f4358bbfca134b63a02bbb30c1136 Mon Sep 17 00:00:00 2001
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Date: Mon, 4 Sep 2023 12:06:32 +0530
Subject: [PATCH] dt-bindings: phy: qcom,m31: Add IPQ5018 compatible
IPQ5332 qcom,m31 phy driver can support IPQ5018.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
Link: https://lore.kernel.org/r/20230904063635.24975-2-quic_nsekar@quicinc.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
.../devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
--- a/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml
@@ -17,7 +17,9 @@ description:
properties:
compatible:
items:
- - const: qcom,ipq5332-usb-hsphy
+ - enum:
+ - qcom,ipq5018-usb-hsphy
+ - qcom,ipq5332-usb-hsphy
"#phy-cells":
const: 0

View File

@ -0,0 +1,89 @@
From 68320e35f8cb1987b4ad34347fc7033832da99e3 Mon Sep 17 00:00:00 2001
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Date: Mon, 4 Sep 2023 12:06:33 +0530
Subject: [PATCH] phy: qcom-m31: Add compatible, phy init sequence for IPQ5018
Add phy init sequence and compatible string for IPQ5018
chipset.
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
Link: https://lore.kernel.org/r/20230904063635.24975-3-quic_nsekar@quicinc.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
drivers/phy/qualcomm/phy-qcom-m31.c | 51 +++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
--- a/drivers/phy/qualcomm/phy-qcom-m31.c
+++ b/drivers/phy/qualcomm/phy-qcom-m31.c
@@ -82,6 +82,50 @@ struct m31_priv_data {
unsigned int nregs;
};
+static const struct m31_phy_regs m31_ipq5018_regs[] = {
+ {
+ .off = USB_PHY_CFG0,
+ .val = UTMI_PHY_OVERRIDE_EN
+ },
+ {
+ .off = USB_PHY_UTMI_CTRL5,
+ .val = POR_EN,
+ .delay = 15
+ },
+ {
+ .off = USB_PHY_FSEL_SEL,
+ .val = FREQ_SEL
+ },
+ {
+ .off = USB_PHY_HS_PHY_CTRL_COMMON0,
+ .val = COMMONONN | FSEL | RETENABLEN
+ },
+ {
+ .off = USB_PHY_REFCLK_CTRL,
+ .val = CLKCORE
+ },
+ {
+ .off = USB_PHY_UTMI_CTRL5,
+ .val = POR_EN
+ },
+ {
+ .off = USB_PHY_HS_PHY_CTRL2,
+ .val = USB2_SUSPEND_N_SEL | USB2_SUSPEND_N | USB2_UTMI_CLK_EN
+ },
+ {
+ .off = USB_PHY_UTMI_CTRL5,
+ .val = 0x0
+ },
+ {
+ .off = USB_PHY_HS_PHY_CTRL2,
+ .val = USB2_SUSPEND_N | USB2_UTMI_CLK_EN
+ },
+ {
+ .off = USB_PHY_CFG0,
+ .val = 0x0
+ },
+};
+
static struct m31_phy_regs m31_ipq5332_regs[] = {
{
USB_PHY_CFG0,
@@ -267,6 +311,12 @@ static int m31usb_phy_probe(struct platf
return PTR_ERR_OR_ZERO(phy_provider);
}
+static const struct m31_priv_data m31_ipq5018_data = {
+ .ulpi_mode = false,
+ .regs = m31_ipq5018_regs,
+ .nregs = ARRAY_SIZE(m31_ipq5018_regs),
+};
+
static const struct m31_priv_data m31_ipq5332_data = {
.ulpi_mode = false,
.regs = m31_ipq5332_regs,
@@ -274,6 +324,7 @@ static const struct m31_priv_data m31_ip
};
static const struct of_device_id m31usb_phy_id_table[] = {
+ { .compatible = "qcom,ipq5018-usb-hsphy", .data = &m31_ipq5018_data },
{ .compatible = "qcom,ipq5332-usb-hsphy", .data = &m31_ipq5332_data },
{ },
};

View File

@ -0,0 +1,41 @@
From 3865a64284cc4845c61cf3dc6c7246349d80cc49 Mon Sep 17 00:00:00 2001
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Date: Thu, 31 Aug 2023 08:35:03 +0530
Subject: [PATCH] dt-bindings: usb: dwc3: Add IPQ5018 compatible
Document the IPQ5018 dwc3 compatible.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
Link: https://lore.kernel.org/r/20230831030503.17100-1-quic_nsekar@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 3 +++
1 file changed, 3 insertions(+)
--- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
+++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
@@ -14,6 +14,7 @@ properties:
items:
- enum:
- qcom,ipq4019-dwc3
+ - qcom,ipq5018-dwc3
- qcom,ipq5332-dwc3
- qcom,ipq6018-dwc3
- qcom,ipq8064-dwc3
@@ -238,6 +239,7 @@ allOf:
compatible:
contains:
enum:
+ - qcom,ipq5018-dwc3
- qcom,ipq5332-dwc3
- qcom,msm8994-dwc3
- qcom,qcs404-dwc3
@@ -411,6 +413,7 @@ allOf:
compatible:
contains:
enum:
+ - qcom,ipq5018-dwc3
- qcom,ipq5332-dwc3
- qcom,sdm660-dwc3
then:

View File

@ -0,0 +1,86 @@
From e7166f2774aafefd29ff26ffbbb7f6d40ac8ea1c Mon Sep 17 00:00:00 2001
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Date: Mon, 4 Sep 2023 12:06:34 +0530
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add USB related nodes
Add USB phy and controller nodes.
Co-developed-by: Amandeep Singh <quic_amansing@quicinc.com>
Signed-off-by: Amandeep Singh <quic_amansing@quicinc.com>
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
Link: https://lore.kernel.org/r/20230904063635.24975-4-quic_nsekar@quicinc.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 54 +++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -94,6 +94,19 @@
#size-cells = <1>;
ranges = <0 0 0 0xffffffff>;
+ usbphy0: phy@5b000 {
+ compatible = "qcom,ipq5018-usb-hsphy";
+ reg = <0x0005b000 0x120>;
+
+ clocks = <&gcc GCC_USB0_PHY_CFG_AHB_CLK>;
+
+ resets = <&gcc GCC_QUSB2_0_PHY_BCR>;
+
+ #phy-cells = <0>;
+
+ status = "disabled";
+ };
+
tlmm: pinctrl@1000000 {
compatible = "qcom,ipq5018-tlmm";
reg = <0x01000000 0x300000>;
@@ -156,6 +169,47 @@
status = "disabled";
};
+ usb: usb@8af8800 {
+ compatible = "qcom,ipq5018-dwc3", "qcom,dwc3";
+ reg = <0x08af8800 0x400>;
+
+ interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "hs_phy_irq";
+
+ clocks = <&gcc GCC_USB0_MASTER_CLK>,
+ <&gcc GCC_SYS_NOC_USB0_AXI_CLK>,
+ <&gcc GCC_USB0_SLEEP_CLK>,
+ <&gcc GCC_USB0_MOCK_UTMI_CLK>;
+ clock-names = "core",
+ "iface",
+ "sleep",
+ "mock_utmi";
+
+ resets = <&gcc GCC_USB0_BCR>;
+
+ qcom,select-utmi-as-pipe-clk;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ status = "disabled";
+
+ usb_dwc: usb@8a00000 {
+ compatible = "snps,dwc3";
+ reg = <0x08a00000 0xe000>;
+ clocks = <&gcc GCC_USB0_MOCK_UTMI_CLK>;
+ clock-names = "ref";
+ interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
+ phy-names = "usb2-phy";
+ phys = <&usbphy0>;
+ tx-fifo-resize;
+ snps,is-utmi-l1-suspend;
+ snps,hird-threshold = /bits/ 8 <0x0>;
+ snps,dis_u2_susphy_quirk;
+ snps,dis_u3_susphy_quirk;
+ };
+ };
+
intc: interrupt-controller@b000000 {
compatible = "qcom,msm-qgic2";
reg = <0x0b000000 0x1000>, /* GICD */

View File

@ -0,0 +1,56 @@
From a1f42e08f0f04b72a6597f080db4bfbb3737910c Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
Date: Wed, 4 Oct 2023 21:12:30 +0200
Subject: [PATCH] arm64: dts: qcom: ipq5018: add QUP1 SPI controller
Add the required BAM and QUP nodes for the QUP1 SPI controller on IPQ5018.
Signed-off-by: Robert Marko <robimarko@gmail.com>
Reviewed-by: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
Link: https://lore.kernel.org/r/20231004191303.331055-1-robimarko@gmail.com
[bjorn: Padded address to 8 digits, fixed node sort order]
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -159,6 +159,16 @@
status = "disabled";
};
+ blsp_dma: dma-controller@7884000 {
+ compatible = "qcom,bam-v1.7.0";
+ reg = <0x07884000 0x1d000>;
+ interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "bam_clk";
+ #dma-cells = <1>;
+ qcom,ee = <0>;
+ };
+
blsp1_uart1: serial@78af000 {
compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
reg = <0x078af000 0x200>;
@@ -169,6 +179,20 @@
status = "disabled";
};
+ blsp1_spi1: spi@78b5000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x078b5000 0x600>;
+ interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_QUP1_SPI_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ dmas = <&blsp_dma 4>, <&blsp_dma 5>;
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
+
usb: usb@8af8800 {
compatible = "qcom,ipq5018-dwc3", "qcom,dwc3";
reg = <0x08af8800 0x400>;

View File

@ -0,0 +1,25 @@
From 4d45d56e17348c6b6bb2bce126a4a5ea97b19900 Mon Sep 17 00:00:00 2001
From: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
Date: Mon, 25 Sep 2023 15:58:24 +0530
Subject: [PATCH] dt-bindings: clock: qcom,a53pll: add IPQ5018 compatible
Add IPQ5018 compatible to A53 PLL bindings.
Signed-off-by: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Link: https://lore.kernel.org/r/20230925102826.405446-2-quic_gokulsri@quicinc.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
Documentation/devicetree/bindings/clock/qcom,a53pll.yaml | 1 +
1 file changed, 1 insertion(+)
--- a/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml
+++ b/Documentation/devicetree/bindings/clock/qcom,a53pll.yaml
@@ -16,6 +16,7 @@ description:
properties:
compatible:
enum:
+ - qcom,ipq5018-a53pll
- qcom,ipq5332-a53pll
- qcom,ipq6018-a53pll
- qcom,ipq8074-a53pll

View File

@ -0,0 +1,62 @@
From 50492f929486c044b43cb3e2c0e040aa9b61ea2b Mon Sep 17 00:00:00 2001
From: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
Date: Mon, 25 Sep 2023 15:58:25 +0530
Subject: [PATCH] clk: qcom: apss-ipq-pll: add support for IPQ5018
IPQ5018 APSS PLL is of type Stromer. Reuse Stromer Plus PLL offsets,
add configuration values and the compatible.
Co-developed-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Signed-off-by: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Link: https://lore.kernel.org/r/20230925102826.405446-3-quic_gokulsri@quicinc.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
drivers/clk/qcom/apss-ipq-pll.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
--- a/drivers/clk/qcom/apss-ipq-pll.c
+++ b/drivers/clk/qcom/apss-ipq-pll.c
@@ -73,6 +73,20 @@ static struct clk_alpha_pll ipq_pll_stro
},
};
+static const struct alpha_pll_config ipq5018_pll_config = {
+ .l = 0x32,
+ .config_ctl_val = 0x4001075b,
+ .config_ctl_hi_val = 0x304,
+ .main_output_mask = BIT(0),
+ .aux_output_mask = BIT(1),
+ .early_output_mask = BIT(3),
+ .alpha_en_mask = BIT(24),
+ .status_val = 0x3,
+ .status_mask = GENMASK(10, 8),
+ .lock_det = BIT(2),
+ .test_ctl_hi_val = 0x00400003,
+};
+
static const struct alpha_pll_config ipq5332_pll_config = {
.l = 0x2d,
.config_ctl_val = 0x4001075b,
@@ -129,6 +143,12 @@ struct apss_pll_data {
const struct alpha_pll_config *pll_config;
};
+static const struct apss_pll_data ipq5018_pll_data = {
+ .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS,
+ .pll = &ipq_pll_stromer_plus,
+ .pll_config = &ipq5018_pll_config,
+};
+
static struct apss_pll_data ipq5332_pll_data = {
.pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS,
.pll = &ipq_pll_stromer_plus,
@@ -195,6 +215,7 @@ static int apss_ipq_pll_probe(struct pla
}
static const struct of_device_id apss_ipq_pll_match_table[] = {
+ { .compatible = "qcom,ipq5018-a53pll", .data = &ipq5018_pll_data },
{ .compatible = "qcom,ipq5332-a53pll", .data = &ipq5332_pll_data },
{ .compatible = "qcom,ipq6018-a53pll", .data = &ipq6018_pll_data },
{ .compatible = "qcom,ipq8074-a53pll", .data = &ipq8074_pll_data },

View File

@ -0,0 +1,98 @@
From 3e4b53e04281ed3d9c7a4329c027097265c04d54 Mon Sep 17 00:00:00 2001
From: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
Date: Mon, 25 Sep 2023 15:58:26 +0530
Subject: [PATCH] arm64: dts: qcom: ipq5018: enable the CPUFreq support
Add the APCS, A53 PLL, cpu-opp-table nodes to set
the CPU frequency at 800MHz (idle) or 1.008GHz.
Co-developed-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Signed-off-by: Gokul Sriram Palanisamy <quic_gokulsri@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Link: https://lore.kernel.org/r/20230925102826.405446-4-quic_gokulsri@quicinc.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 40 +++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -5,6 +5,7 @@
* Copyright (c) 2023 The Linux Foundation. All rights reserved.
*/
+#include <dt-bindings/clock/qcom,apss-ipq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/qcom,gcc-ipq5018.h>
#include <dt-bindings/reset/qcom,gcc-ipq5018.h>
@@ -36,6 +37,8 @@
reg = <0x0>;
enable-method = "psci";
next-level-cache = <&L2_0>;
+ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>;
+ operating-points-v2 = <&cpu_opp_table>;
};
CPU1: cpu@1 {
@@ -44,6 +47,8 @@
reg = <0x1>;
enable-method = "psci";
next-level-cache = <&L2_0>;
+ clocks = <&apcs_glb APCS_ALIAS0_CORE_CLK>;
+ operating-points-v2 = <&cpu_opp_table>;
};
L2_0: l2-cache {
@@ -54,6 +59,25 @@
};
};
+ cpu_opp_table: opp-table-cpu {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ /*
+ opp-800000000 {
+ opp-hz = /bits/ 64 <800000000>;
+ opp-microvolt = <1100000>;
+ clock-latency-ns = <200000>;
+ };
+ */
+
+ opp-1008000000 {
+ opp-hz = /bits/ 64 <1008000000>;
+ opp-microvolt = <1100000>;
+ clock-latency-ns = <200000>;
+ };
+ };
+
firmware {
scm {
compatible = "qcom,scm-ipq5018", "qcom,scm";
@@ -267,6 +291,24 @@
clocks = <&sleep_clk>;
};
+ apcs_glb: mailbox@b111000 {
+ compatible = "qcom,ipq5018-apcs-apps-global",
+ "qcom,ipq6018-apcs-apps-global";
+ reg = <0x0b111000 0x1000>;
+ #clock-cells = <1>;
+ clocks = <&a53pll>, <&xo_board_clk>, <&gcc GPLL0>;
+ clock-names = "pll", "xo", "gpll0";
+ #mbox-cells = <1>;
+ };
+
+ a53pll: clock@b116000 {
+ compatible = "qcom,ipq5018-a53pll";
+ reg = <0x0b116000 0x40>;
+ #clock-cells = <0>;
+ clocks = <&xo_board_clk>;
+ clock-names = "xo";
+ };
+
timer@b120000 {
compatible = "arm,armv7-timer-mem";
reg = <0x0b120000 0x1000>;

View File

@ -0,0 +1,66 @@
From a427dd16e61f3d145bc24f0ed09692fc25931250 Mon Sep 17 00:00:00 2001
From: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
Date: Wed, 25 Oct 2023 22:12:12 +0530
Subject: [PATCH] arm64: dts: qcom: ipq5018: add few more reserved memory
regions
Like all other IPQ SoCs, bootloader will collect the system RAM contents
upon crash for the post morterm analysis. If we don't reserve the memory
region used by bootloader, obviously linux will consume it and upon next
boot on crash, bootloader will be loaded in the same region, which will
lead to loose some of the data, sometimes we may miss out critical
information. So lets reserve the region used by the bootloader.
Similarly SBL copies some data into the reserved region and it will be
used in the crash scenario. So reserve 1MB for SBL as well.
While at it, enable the SMEM support along with TCSR mutex.
Signed-off-by: Kathiravan Thirumoorthy <quic_kathirav@quicinc.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
Link: https://lore.kernel.org/r/20231025-ipq5018-misc-v1-1-7d14fde97fe7@quicinc.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -106,6 +106,24 @@
#size-cells = <2>;
ranges;
+ bootloader@4a800000 {
+ reg = <0x0 0x4a800000 0x0 0x200000>;
+ no-map;
+ };
+
+ sbl@4aa00000 {
+ reg = <0x0 0x4aa00000 0x0 0x100000>;
+ no-map;
+ };
+
+ smem@4ab00000 {
+ compatible = "qcom,smem";
+ reg = <0x0 0x4ab00000 0x0 0x100000>;
+ no-map;
+
+ hwlocks = <&tcsr_mutex 3>;
+ };
+
tz_region: tz@4ac00000 {
reg = <0x0 0x4ac00000 0x0 0x200000>;
no-map;
@@ -166,6 +184,12 @@
#power-domain-cells = <1>;
};
+ tcsr_mutex: hwlock@1905000 {
+ compatible = "qcom,tcsr-mutex";
+ reg = <0x01905000 0x20000>;
+ #hwlock-cells = <1>;
+ };
+
sdhc_1: mmc@7804000 {
compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5";
reg = <0x7804000 0x1000>;

View File

@ -0,0 +1,83 @@
From: Gabor Juhos <j4g8y7@gmail.com>
Subject: [PATCH] clk: qcom: apss-ipq-pll: use stromer ops for IPQ5018 to fix boot failure
Date: Fri, 15 Mar 2024 17:16:41 +0100
Booting v6.8 results in a hang on various IPQ5018 based boards.
Investigating the problem showed that the hang happens when the
clk_alpha_pll_stromer_plus_set_rate() function tries to write
into the PLL_MODE register of the APSS PLL.
Checking the downstream code revealed that it uses [1] stromer
specific operations for IPQ5018, whereas in the current code
the stromer plus specific operations are used.
The ops in the 'ipq_pll_stromer_plus' clock definition can't be
changed since that is needed for IPQ5332, so add a new alpha pll
clock declaration which uses the correct stromer ops and use this
new clock for IPQ5018 to avoid the boot failure.
Also, change pll_type in 'ipq5018_pll_data' to
CLK_ALPHA_PLL_TYPE_STROMER to better reflect that it is a Stromer
PLL and change the apss_ipq_pll_probe() function accordingly.
1. https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/blob/NHSS.QSDK.12.4/drivers/clk/qcom/apss-ipq5018.c#L67
Fixes: 50492f929486 ("clk: qcom: apss-ipq-pll: add support for IPQ5018")
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
---
drivers/clk/qcom/apss-ipq-pll.c | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
--- a/drivers/clk/qcom/apss-ipq-pll.c
+++ b/drivers/clk/qcom/apss-ipq-pll.c
@@ -55,6 +55,29 @@ static struct clk_alpha_pll ipq_pll_huay
},
};
+static struct clk_alpha_pll ipq_pll_stromer = {
+ .offset = 0x0,
+ /*
+ * Reuse CLK_ALPHA_PLL_TYPE_STROMER_PLUS register offsets.
+ * Although this is a bit confusing, but the offset values
+ * are correct nevertheless.
+ */
+ .regs = ipq_pll_offsets[CLK_ALPHA_PLL_TYPE_STROMER_PLUS],
+ .flags = SUPPORTS_DYNAMIC_UPDATE,
+ .clkr = {
+ .enable_reg = 0x0,
+ .enable_mask = BIT(0),
+ .hw.init = &(const struct clk_init_data) {
+ .name = "a53pll",
+ .parent_data = &(const struct clk_parent_data) {
+ .fw_name = "xo",
+ },
+ .num_parents = 1,
+ .ops = &clk_alpha_pll_stromer_ops,
+ },
+ },
+};
+
static struct clk_alpha_pll ipq_pll_stromer_plus = {
.offset = 0x0,
.regs = ipq_pll_offsets[CLK_ALPHA_PLL_TYPE_STROMER_PLUS],
@@ -144,8 +167,8 @@ struct apss_pll_data {
};
static const struct apss_pll_data ipq5018_pll_data = {
- .pll_type = CLK_ALPHA_PLL_TYPE_STROMER_PLUS,
- .pll = &ipq_pll_stromer_plus,
+ .pll_type = CLK_ALPHA_PLL_TYPE_STROMER,
+ .pll = &ipq_pll_stromer,
.pll_config = &ipq5018_pll_config,
};
@@ -203,7 +226,8 @@ static int apss_ipq_pll_probe(struct pla
if (data->pll_type == CLK_ALPHA_PLL_TYPE_HUAYRA)
clk_alpha_pll_configure(data->pll, regmap, data->pll_config);
- else if (data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER_PLUS)
+ else if (data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER ||
+ data->pll_type == CLK_ALPHA_PLL_TYPE_STROMER_PLUS)
clk_stromer_pll_configure(data->pll, regmap, data->pll_config);
ret = devm_clk_register_regmap(dev, &data->pll->clkr);

View File

@ -0,0 +1,32 @@
From: Gabor Juhos <j4g8y7@gmail.com>
Subject: [PATCH] clk: qcom: apss-ipq-pll: fix PLL rate for IPQ5018
Date: Tue, 26 Mar 2024 14:34:11 +0100
According to ipq5018.dtsi, the maximum supported rate by the
CPU is 1.008 GHz on the IPQ5018 platform, however the current
configuration of the PLL results in 1.2 GHz rate.
Change the 'L' value in the PLL configuration to limit the
rate to 1.008 GHz. The downstream kernel also uses the same
value [1]. Also add a comment to indicate the desired
frequency.
[1] https://git.codelinaro.org/clo/qsdk/oss/kernel/linux-ipq-5.4/-/blob/NHSS.QSDK.12.4/drivers/clk/qcom/apss-ipq5018.c?ref_type=heads#L151
Fixes: 50492f929486 ("clk: qcom: apss-ipq-pll: add support for IPQ5018")
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
---
drivers/clk/qcom/apss-ipq-pll.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/drivers/clk/qcom/apss-ipq-pll.c
+++ b/drivers/clk/qcom/apss-ipq-pll.c
@@ -97,7 +97,7 @@ static struct clk_alpha_pll ipq_pll_stro
};
static const struct alpha_pll_config ipq5018_pll_config = {
- .l = 0x32,
+ .l = 0x2a,
.config_ctl_val = 0x4001075b,
.config_ctl_hi_val = 0x304,
.main_output_mask = BIT(0),

View File

@ -0,0 +1,60 @@
From: Devi Priya <quic_devipriy@quicinc.com>
Date: Thu, 5 Oct 2023 21:35:48 +0530
Subject: [PATCH] dt-bindings: pwm: add IPQ6018 binding
DT binding for the PWM block in Qualcomm IPQ6018 SoC.
Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Co-developed-by: Baruch Siach <baruch.siach@siklu.com>
Signed-off-by: Baruch Siach <baruch.siach@siklu.com>
Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
---
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/qcom,ipq6018-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ6018 PWM controller
+
+maintainers:
+ - Baruch Siach <baruch@tkos.co.il>
+
+properties:
+ compatible:
+ const: qcom,ipq6018-pwm
+
+ reg:
+ description: Offset of PWM register in the TCSR block.
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ "#pwm-cells":
+ const: 2
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - "#pwm-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-ipq6018.h>
+
+ pwm: pwm@a010 {
+ compatible = "qcom,ipq6018-pwm";
+ reg = <0xa010 0x20>;
+ clocks = <&gcc GCC_ADSS_PWM_CLK>;
+ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>;
+ assigned-clock-rates = <100000000>;
+ #pwm-cells = <2>;
+ };

View File

@ -0,0 +1,330 @@
From: Devi Priya <quic_devipriy@quicinc.com>
Date: Thu, 5 Oct 2023 21:35:47 +0530
Subject: [PATCH] pwm: driver for qualcomm ipq6018 pwm block
Driver for the PWM block in Qualcomm IPQ6018 line of SoCs. Based on
driver from downstream Codeaurora kernel tree. Removed support for older
(V1) variants because I have no access to that hardware.
Tested on IPQ6010 based hardware.
Co-developed-by: Baruch Siach <baruch.siach@siklu.com>
Signed-off-by: Baruch Siach <baruch.siach@siklu.com>
Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
---
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -282,6 +282,18 @@ config PWM_INTEL_LGM
To compile this driver as a module, choose M here: the module
will be called pwm-intel-lgm.
+config PWM_IPQ
+ tristate "IPQ PWM support"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on HAVE_CLK && HAS_IOMEM
+ help
+ Generic PWM framework driver for IPQ PWM block which supports
+ 4 pwm channels. Each of the these channels can be configured
+ independent of each other.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-ipq.
+
config PWM_IQS620A
tristate "Azoteq IQS620A PWM support"
depends on MFD_IQS62X || COMPILE_TEST
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o
obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o
obj-$(CONFIG_PWM_INTEL_LGM) += pwm-intel-lgm.o
+obj-$(CONFIG_PWM_IPQ) += pwm-ipq.o
obj-$(CONFIG_PWM_IQS620A) += pwm-iqs620a.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o
--- /dev/null
+++ b/drivers/pwm/pwm-ipq.c
@@ -0,0 +1,282 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/math64.h>
+#include <linux/of_device.h>
+#include <linux/bitfield.h>
+#include <linux/units.h>
+
+/* The frequency range supported is 1 Hz to clock rate */
+#define IPQ_PWM_MAX_PERIOD_NS ((u64)NSEC_PER_SEC)
+
+/*
+ * The max value specified for each field is based on the number of bits
+ * in the pwm control register for that field
+ */
+#define IPQ_PWM_MAX_DIV 0xFFFF
+
+/*
+ * Two 32-bit registers for each PWM: REG0, and REG1.
+ * Base offset for PWM #i is at 8 * #i.
+ */
+#define IPQ_PWM_REG0 0
+#define IPQ_PWM_REG0_PWM_DIV GENMASK(15, 0)
+#define IPQ_PWM_REG0_HI_DURATION GENMASK(31, 16)
+
+#define IPQ_PWM_REG1 4
+#define IPQ_PWM_REG1_PRE_DIV GENMASK(15, 0)
+/*
+ * Enable bit is set to enable output toggling in pwm device.
+ * Update bit is set to reflect the changed divider and high duration
+ * values in register.
+ */
+#define IPQ_PWM_REG1_UPDATE BIT(30)
+#define IPQ_PWM_REG1_ENABLE BIT(31)
+
+struct ipq_pwm_chip {
+ struct pwm_chip chip;
+ struct clk *clk;
+ void __iomem *mem;
+};
+
+static struct ipq_pwm_chip *ipq_pwm_from_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct ipq_pwm_chip, chip);
+}
+
+static unsigned int ipq_pwm_reg_read(struct pwm_device *pwm, unsigned int reg)
+{
+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip);
+ unsigned int off = 8 * pwm->hwpwm + reg;
+
+ return readl(ipq_chip->mem + off);
+}
+
+static void ipq_pwm_reg_write(struct pwm_device *pwm, unsigned int reg,
+ unsigned int val)
+{
+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(pwm->chip);
+ unsigned int off = 8 * pwm->hwpwm + reg;
+
+ writel(val, ipq_chip->mem + off);
+}
+
+static void config_div_and_duty(struct pwm_device *pwm, unsigned int pre_div,
+ unsigned int pwm_div, unsigned long rate, u64 duty_ns,
+ bool enable)
+{
+ unsigned long hi_dur;
+ unsigned long val = 0;
+
+ /*
+ * high duration = pwm duty * (pwm div + 1)
+ * pwm duty = duty_ns / period_ns
+ */
+ hi_dur = div64_u64(duty_ns * rate, (pre_div + 1) * NSEC_PER_SEC);
+
+ val = FIELD_PREP(IPQ_PWM_REG0_HI_DURATION, hi_dur) |
+ FIELD_PREP(IPQ_PWM_REG0_PWM_DIV, pwm_div);
+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG0, val);
+
+ val = FIELD_PREP(IPQ_PWM_REG1_PRE_DIV, pre_div);
+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val);
+
+ /* PWM enable toggle needs a separate write to REG1 */
+ val |= IPQ_PWM_REG1_UPDATE;
+ if (enable)
+ val |= IPQ_PWM_REG1_ENABLE;
+ ipq_pwm_reg_write(pwm, IPQ_PWM_REG1, val);
+}
+
+static int ipq_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip);
+ unsigned int pre_div, pwm_div, best_pre_div, best_pwm_div;
+ unsigned long rate = clk_get_rate(ipq_chip->clk);
+ u64 period_ns, duty_ns, period_rate;
+ u64 min_diff;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (state->period < DIV64_U64_ROUND_UP(NSEC_PER_SEC, rate))
+ return -ERANGE;
+
+ period_ns = min(state->period, IPQ_PWM_MAX_PERIOD_NS);
+ duty_ns = min(state->duty_cycle, period_ns);
+
+ /*
+ * period_ns is 1G or less. As long as rate is less than 16 GHz,
+ * period_rate does not overflow. Make that explicit.
+ */
+ if ((unsigned long long)rate > 16ULL * GIGA)
+ return -EINVAL;
+ period_rate = period_ns * rate;
+ best_pre_div = IPQ_PWM_MAX_DIV;
+ best_pwm_div = IPQ_PWM_MAX_DIV;
+ /*
+ * We don't need to consider pre_div values smaller than
+ *
+ * period_rate
+ * pre_div_min := ------------------------------------
+ * NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1)
+ *
+ * because pre_div = pre_div_min results in a better
+ * approximation.
+ */
+ pre_div = div64_u64(period_rate,
+ (u64)NSEC_PER_SEC * (IPQ_PWM_MAX_DIV + 1));
+ min_diff = period_rate;
+
+ for (; pre_div <= IPQ_PWM_MAX_DIV; pre_div++) {
+ u64 remainder;
+
+ pwm_div = div64_u64_rem(period_rate,
+ (u64)NSEC_PER_SEC * (pre_div + 1), &remainder);
+ /* pwm_div is unsigned; the check below catches underflow */
+ pwm_div--;
+
+ /*
+ * Swapping values for pre_div and pwm_div produces the same
+ * period length. So we can skip all settings with pre_div >
+ * pwm_div which results in bigger constraints for selecting
+ * the duty_cycle than with the two values swapped.
+ */
+ if (pre_div > pwm_div)
+ break;
+
+ /*
+ * Make sure we can do 100% duty cycle where
+ * hi_dur == pwm_div + 1
+ */
+ if (pwm_div > IPQ_PWM_MAX_DIV - 1)
+ continue;
+
+ if (remainder < min_diff) {
+ best_pre_div = pre_div;
+ best_pwm_div = pwm_div;
+ min_diff = remainder;
+
+ if (min_diff == 0) /* bingo */
+ break;
+ }
+ }
+
+ /* config divider values for the closest possible frequency */
+ config_div_and_duty(pwm, best_pre_div, best_pwm_div,
+ rate, duty_ns, state->enabled);
+
+ return 0;
+}
+
+static int ipq_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct ipq_pwm_chip *ipq_chip = ipq_pwm_from_chip(chip);
+ unsigned long rate = clk_get_rate(ipq_chip->clk);
+ unsigned int pre_div, pwm_div, hi_dur;
+ u64 effective_div, hi_div;
+ u32 reg0, reg1;
+
+ reg0 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG0);
+ reg1 = ipq_pwm_reg_read(pwm, IPQ_PWM_REG1);
+
+ state->polarity = PWM_POLARITY_NORMAL;
+ state->enabled = reg1 & IPQ_PWM_REG1_ENABLE;
+
+ pwm_div = FIELD_GET(IPQ_PWM_REG0_PWM_DIV, reg0);
+ hi_dur = FIELD_GET(IPQ_PWM_REG0_HI_DURATION, reg0);
+ pre_div = FIELD_GET(IPQ_PWM_REG1_PRE_DIV, reg1);
+
+ /* No overflow here, both pre_div and pwm_div <= 0xffff */
+ effective_div = (u64)(pre_div + 1) * (pwm_div + 1);
+ state->period = DIV64_U64_ROUND_UP(effective_div * NSEC_PER_SEC, rate);
+
+ hi_div = hi_dur * (pre_div + 1);
+ state->duty_cycle = DIV64_U64_ROUND_UP(hi_div * NSEC_PER_SEC, rate);
+
+ return 0;
+}
+
+static const struct pwm_ops ipq_pwm_ops = {
+ .apply = ipq_pwm_apply,
+ .get_state = ipq_pwm_get_state,
+ .owner = THIS_MODULE,
+};
+
+static int ipq_pwm_probe(struct platform_device *pdev)
+{
+ struct ipq_pwm_chip *pwm;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
+ if (!pwm)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, pwm);
+
+ pwm->mem = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pwm->mem))
+ return dev_err_probe(dev, PTR_ERR(pwm->mem),
+ "regs map failed");
+
+ pwm->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(pwm->clk))
+ return dev_err_probe(dev, PTR_ERR(pwm->clk),
+ "failed to get clock");
+
+ ret = clk_prepare_enable(pwm->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "clock enable failed");
+
+ pwm->chip.dev = dev;
+ pwm->chip.ops = &ipq_pwm_ops;
+ pwm->chip.npwm = 4;
+
+ ret = pwmchip_add(&pwm->chip);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "pwmchip_add() failed\n");
+ clk_disable_unprepare(pwm->clk);
+ }
+
+ return ret;
+}
+
+static int ipq_pwm_remove(struct platform_device *pdev)
+{
+ struct ipq_pwm_chip *pwm = platform_get_drvdata(pdev);
+
+ pwmchip_remove(&pwm->chip);
+ clk_disable_unprepare(pwm->clk);
+
+ return 0;
+}
+
+static const struct of_device_id pwm_ipq_dt_match[] = {
+ { .compatible = "qcom,ipq6018-pwm", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pwm_ipq_dt_match);
+
+static struct platform_driver ipq_pwm_driver = {
+ .driver = {
+ .name = "ipq-pwm",
+ .of_match_table = pwm_ipq_dt_match,
+ },
+ .probe = ipq_pwm_probe,
+ .remove = ipq_pwm_remove,
+};
+
+module_platform_driver(ipq_pwm_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");

View File

@ -0,0 +1,148 @@
From: Devi Priya <quic_devipriy@quicinc.com>
Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add simple-mfd support for IPQ6018
Date: Thu, 5 Oct 2023 21:35:49 +0530
Update the binding to include pwm as the child node to TCSR block and
add simple-mfd support for IPQ6018.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
---
.../devicetree/bindings/mfd/qcom,tcsr.yaml | 112 +++++++++++++-----
1 file changed, 81 insertions(+), 31 deletions(-)
--- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml
+++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml
@@ -15,49 +15,101 @@ description:
properties:
compatible:
- items:
- - enum:
- - qcom,msm8976-tcsr
- - qcom,msm8998-tcsr
- - qcom,qcs404-tcsr
- - qcom,sc7180-tcsr
- - qcom,sc7280-tcsr
- - qcom,sc8280xp-tcsr
- - qcom,sdm630-tcsr
- - qcom,sdm845-tcsr
- - qcom,sdx55-tcsr
- - qcom,sdx65-tcsr
- - qcom,sm8150-tcsr
- - qcom,sm8450-tcsr
- - qcom,tcsr-apq8064
- - qcom,tcsr-apq8084
- - qcom,tcsr-ipq5332
- - qcom,tcsr-ipq6018
- - qcom,tcsr-ipq8064
- - qcom,tcsr-ipq8074
- - qcom,tcsr-ipq9574
- - qcom,tcsr-mdm9615
- - qcom,tcsr-msm8226
- - qcom,tcsr-msm8660
- - qcom,tcsr-msm8916
- - qcom,tcsr-msm8953
- - qcom,tcsr-msm8960
- - qcom,tcsr-msm8974
- - qcom,tcsr-msm8996
- - const: syscon
+ oneOf:
+ - items:
+ - enum:
+ - qcom,msm8976-tcsr
+ - qcom,msm8998-tcsr
+ - qcom,qcs404-tcsr
+ - qcom,sc7180-tcsr
+ - qcom,sc7280-tcsr
+ - qcom,sc8280xp-tcsr
+ - qcom,sdm630-tcsr
+ - qcom,sdm845-tcsr
+ - qcom,sdx55-tcsr
+ - qcom,sdx65-tcsr
+ - qcom,sm4450-tcsr
+ - qcom,sm8150-tcsr
+ - qcom,sm8450-tcsr
+ - qcom,tcsr-apq8064
+ - qcom,tcsr-apq8084
+ - qcom,tcsr-ipq5332
+ - qcom,tcsr-ipq8064
+ - qcom,tcsr-ipq8074
+ - qcom,tcsr-ipq9574
+ - qcom,tcsr-mdm9615
+ - qcom,tcsr-msm8226
+ - qcom,tcsr-msm8660
+ - qcom,tcsr-msm8916
+ - qcom,tcsr-msm8953
+ - qcom,tcsr-msm8960
+ - qcom,tcsr-msm8974
+ - qcom,tcsr-msm8996
+ - const: syscon
+ - items:
+ - const: qcom,tcsr-ipq6018
+ - const: syscon
+ - const: simple-mfd
reg:
maxItems: 1
+ ranges: true
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 1
+
+patternProperties:
+ "pwm@[a-f0-9]+$":
+ type: object
+ $ref: /schemas/pwm/qcom,ipq6018-pwm.yaml
+
+
required:
- compatible
- reg
+allOf:
+ - if:
+ not:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,tcsr-ipq6018
+ then:
+ patternProperties:
+ "pwm@[a-f0-9]+$": false
+
additionalProperties: false
examples:
+ # Example 1 - Syscon node found on MSM8960
- |
syscon@1a400000 {
compatible = "qcom,tcsr-msm8960", "syscon";
reg = <0x1a400000 0x100>;
};
+ # Example 2 - Syscon node found on IPQ6018
+ - |
+ #include <dt-bindings/clock/qcom,gcc-ipq6018.h>
+
+ syscon@1937000 {
+ compatible = "qcom,tcsr-ipq6018", "syscon", "simple-mfd";
+ reg = <0x01937000 0x21000>;
+ ranges = <0 0x1937000 0x21000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ pwm: pwm@a010 {
+ compatible = "qcom,ipq6018-pwm";
+ reg = <0xa010 0x20>;
+ clocks = <&gcc GCC_ADSS_PWM_CLK>;
+ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>;
+ assigned-clock-rates = <100000000>;
+ #pwm-cells = <2>;
+ };
+ };
\ No newline at end of file

View File

@ -0,0 +1,22 @@
From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Subject: [PATCH V2 1/1] dt-bindings: nvmem: Add compatible for IPQ5018
Date: Fri, 15 Sep 2023 17:31:20 +0530
Document the QFPROM on IPQ5018.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
---
Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml | 1 +
1 file changed, 1 insertion(+)
--- a/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
+++ b/Documentation/devicetree/bindings/nvmem/qcom,qfprom.yaml
@@ -18,6 +18,7 @@ properties:
- enum:
- qcom,apq8064-qfprom
- qcom,apq8084-qfprom
+ - qcom,ipq5018-qfprom
- qcom,ipq5332-qfprom
- qcom,ipq6018-qfprom
- qcom,ipq8064-qfprom

View File

@ -0,0 +1,26 @@
From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Date: Fri, 22 Sep 2023 17:21:13 +0530
Subject: [PATCH] dt-bindings: thermal: qcom-tsens: Add ipq5018 compatible
IPQ5018 has tsens v1.0 block with 4 sensors and 1 interrupt.
Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
---
--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
@@ -39,6 +39,7 @@ properties:
- description: v1 of TSENS
items:
- enum:
+ - qcom,ipq5018-tsens
- qcom,msm8956-tsens
- qcom,msm8976-tsens
- qcom,qcs404-tsens
@@ -232,6 +233,7 @@ allOf:
compatible:
contains:
enum:
+ - qcom,ipq5018-tsens
- qcom,ipq8064-tsens
- qcom,msm8960-tsens
- qcom,tsens-v0_1

View File

@ -0,0 +1,45 @@
From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Subject: [PATCH] thermal/drivers/qcom: Add new feat for soc without rpm
Date: Fri, 22 Sep 2023 17:21:14 +0530
In IPQ5018, Tsens IP doesn't have RPM. Hence the early init to
enable tsens would not be done. So add a flag for that in feat
and skip enable checks. Without this, tsens probe fails.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
---
drivers/thermal/qcom/tsens.c | 2 +-
drivers/thermal/qcom/tsens.h | 3 +++
2 files changed, 4 insertions(+), 1 deletion(-)
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -974,7 +974,7 @@ int __init init_common(struct tsens_priv
ret = regmap_field_read(priv->rf[TSENS_EN], &enabled);
if (ret)
goto err_put_device;
- if (!enabled) {
+ if (!enabled && !(priv->feat->ignore_enable)) {
dev_err(dev, "%s: device not enabled\n", __func__);
ret = -ENODEV;
goto err_put_device;
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -505,6 +505,8 @@ enum regfield_ids {
* @srot_split: does the IP neatly splits the register space into SROT and TM,
* with SROT only being available to secure boot firmware?
* @has_watchdog: does this IP support watchdog functionality?
+ * @ignore_enable: does this IP reside in a soc that does not have rpm to
+ * do pre-init.
* @max_sensors: maximum sensors supported by this version of the IP
* @trip_min_temp: minimum trip temperature supported by this version of the IP
* @trip_max_temp: maximum trip temperature supported by this version of the IP
@@ -516,6 +518,7 @@ struct tsens_features {
unsigned int adc:1;
unsigned int srot_split:1;
unsigned int has_watchdog:1;
+ unsigned int ignore_enable:1;
unsigned int max_sensors;
int trip_min_temp;
int trip_max_temp;

View File

@ -0,0 +1,118 @@
From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Subject: [PATCH] thermal/drivers/tsens: Add support for IPQ5018 tsens
Date: Fri, 22 Sep 2023 17:21:15 +0530
IPQ5018 has tsens IP V1.0, 4 sensors and 1 interrupt.
The soc does not have a RPM, hence tsens has to be reset and
enabled in the driver init. Adding the driver support for same.
Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
---
drivers/thermal/qcom/tsens-v1.c | 60 +++++++++++++++++++++++++++++++++
drivers/thermal/qcom/tsens.c | 3 ++
drivers/thermal/qcom/tsens.h | 2 +-
3 files changed, 64 insertions(+), 1 deletion(-)
--- a/drivers/thermal/qcom/tsens-v1.c
+++ b/drivers/thermal/qcom/tsens-v1.c
@@ -79,6 +79,18 @@ static struct tsens_features tsens_v1_fe
.trip_max_temp = 120000,
};
+static struct tsens_features tsens_v1_ipq5018_feat = {
+ .ver_major = VER_1_X,
+ .crit_int = 0,
+ .combo_int = 0,
+ .adc = 1,
+ .srot_split = 1,
+ .max_sensors = 11,
+ .trip_min_temp = -40000,
+ .trip_max_temp = 120000,
+ .ignore_enable = 1,
+};
+
static const struct reg_field tsens_v1_regfields[MAX_REGFIELDS] = {
/* ----- SROT ------ */
/* VERSION */
@@ -150,6 +162,41 @@ static int __init init_8956(struct tsens
return init_common(priv);
}
+static int __init init_ipq5018(struct tsens_priv *priv)
+{
+ int ret;
+ u32 mask;
+
+ ret = init_common(priv);
+ if (ret < 0) {
+ dev_err(priv->dev, "Init common failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_field_write(priv->rf[TSENS_SW_RST], 1);
+ if (ret) {
+ dev_err(priv->dev, "Reset failed\n");
+ return ret;
+ }
+
+ mask = GENMASK(priv->num_sensors, 0);
+ ret = regmap_field_update_bits(priv->rf[SENSOR_EN], mask, mask);
+ if (ret) {
+ dev_err(priv->dev, "Sensor Enable failed\n");
+ return ret;
+ }
+
+ ret = regmap_field_write(priv->rf[TSENS_EN], 1);
+ if (ret) {
+ dev_err(priv->dev, "Enable failed\n");
+ return ret;
+ }
+
+ ret = regmap_field_write(priv->rf[TSENS_SW_RST], 0);
+
+ return ret;
+}
+
static const struct tsens_ops ops_generic_v1 = {
.init = init_common,
.calibrate = calibrate_v1,
@@ -194,3 +241,16 @@ struct tsens_plat_data data_8976 = {
.feat = &tsens_v1_feat,
.fields = tsens_v1_regfields,
};
+
+const struct tsens_ops ops_ipq5018 = {
+ .init = init_ipq5018,
+ .calibrate = tsens_calibrate_common,
+ .get_temp = get_temp_tsens_valid,
+};
+
+struct tsens_plat_data data_ipq5018 = {
+ .num_sensors = 5,
+ .ops = &ops_ipq5018,
+ .feat = &tsens_v1_ipq5018_feat,
+ .fields = tsens_v1_regfields,
+};
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -1101,6 +1101,9 @@ static SIMPLE_DEV_PM_OPS(tsens_pm_ops, t
static const struct of_device_id tsens_table[] = {
{
+ .compatible = "qcom,ipq5018-tsens",
+ .data = &data_ipq5018,
+ }, {
.compatible = "qcom,ipq8064-tsens",
.data = &data_8960,
}, {
--- a/drivers/thermal/qcom/tsens.h
+++ b/drivers/thermal/qcom/tsens.h
@@ -645,7 +645,7 @@ extern struct tsens_plat_data data_8960;
extern struct tsens_plat_data data_8226, data_8909, data_8916, data_8939, data_8974, data_9607;
/* TSENS v1 targets */
-extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956;
+extern struct tsens_plat_data data_tsens_v1, data_8937, data_8976, data_8956, data_ipq5018;
/* TSENS v2 targets */
extern struct tsens_plat_data data_8996, data_ipq8074, data_tsens_v2;

View File

@ -0,0 +1,200 @@
From: Sricharan Ramabadhran <quic_srichara@quicinc.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add tsens node
Date: Fri, 22 Sep 2023 17:21:16 +0530
IPQ5018 has tsens V1.0 IP with 4 sensors.
There is no RPM, so tsens has to be manually enabled. Adding the tsens
and nvmem node and IPQ5018 has 4 thermal sensors (zones). With the
critical temperature being 120'C and action is to reboot. Adding all
the 4 zones here.
Signed-off-by: Sricharan Ramabadhran <quic_srichara@quicinc.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 169 ++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -149,6 +149,117 @@
status = "disabled";
};
+ qfprom: qfprom@a0000 {
+ compatible = "qcom,ipq5018-qfprom", "qcom,qfprom";
+ reg = <0xa0000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ tsens_mode: mode@249 {
+ reg = <0x249 1>;
+ bits = <0 3>;
+ };
+
+ tsens_base1: base1@249 {
+ reg = <0x249 2>;
+ bits = <3 8>;
+ };
+
+ tsens_base2: base2@24a {
+ reg = <0x24a 2>;
+ bits = <3 8>;
+ };
+
+ tsens_s0_p1: s0-p1@24b {
+ reg = <0x24b 0x2>;
+ bits = <2 6>;
+ };
+
+ tsens_s0_p2: s0-p2@24c {
+ reg = <0x24c 0x1>;
+ bits = <1 6>;
+ };
+
+ tsens_s1_p1: s1-p1@24c {
+ reg = <0x24c 0x2>;
+ bits = <7 6>;
+ };
+
+ tsens_s1_p2: s1-p2@24d {
+ reg = <0x24d 0x2>;
+ bits = <5 6>;
+ };
+
+ tsens_s2_p1: s2-p1@24e {
+ reg = <0x24e 0x2>;
+ bits = <3 6>;
+ };
+
+ tsens_s2_p2: s2-p2@24f {
+ reg = <0x24f 0x1>;
+ bits = <1 6>;
+ };
+
+ tsens_s3_p1: s3-p1@24f {
+ reg = <0x24f 0x2>;
+ bits = <7 6>;
+ };
+
+ tsens_s3_p2: s3-p2@250 {
+ reg = <0x250 0x2>;
+ bits = <5 6>;
+ };
+
+ tsens_s4_p1: s4-p1@251 {
+ reg = <0x251 0x2>;
+ bits = <3 6>;
+ };
+
+ tsens_s4_p2: s4-p2@254 {
+ reg = <0x254 0x1>;
+ bits = <0 6>;
+ };
+ };
+
+ tsens: thermal-sensor@4a9000 {
+ compatible = "qcom,ipq5018-tsens";
+ reg = <0x4a9000 0x1000>, /* TM */
+ <0x4a8000 0x1000>; /* SROT */
+
+ nvmem-cells = <&tsens_mode>,
+ <&tsens_base1>,
+ <&tsens_base2>,
+ <&tsens_s0_p1>,
+ <&tsens_s0_p2>,
+ <&tsens_s1_p1>,
+ <&tsens_s1_p2>,
+ <&tsens_s2_p1>,
+ <&tsens_s2_p2>,
+ <&tsens_s3_p1>,
+ <&tsens_s3_p2>,
+ <&tsens_s4_p1>,
+ <&tsens_s4_p2>;
+
+ nvmem-cell-names = "mode",
+ "base1",
+ "base2",
+ "s0_p1",
+ "s0_p2",
+ "s1_p1",
+ "s1_p2",
+ "s2_p1",
+ "s2_p2",
+ "s3_p1",
+ "s3_p2",
+ "s4_p1",
+ "s4_p2";
+
+ interrupts = <GIC_SPI 184 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "uplow";
+ #qcom,sensors = <5>;
+ #thermal-sensor-cells = <1>;
+ };
+
tlmm: pinctrl@1000000 {
compatible = "qcom,ipq5018-tlmm";
reg = <0x01000000 0x300000>;
@@ -391,6 +502,64 @@
};
};
};
+
+ thermal-zones {
+ cpu-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens 2>;
+
+ trips {
+ cpu-critical {
+ temperature = <120000>;
+ hysteresis = <2>;
+ type = "critical";
+ };
+ };
+ };
+
+ gephy-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens 4>;
+
+ trips {
+ gephy-critical {
+ temperature = <120000>;
+ hysteresis = <2>;
+ type = "critical";
+ };
+ };
+ };
+
+ top-glue-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens 3>;
+
+ trips {
+ top_glue-critical {
+ temperature = <120000>;
+ hysteresis = <2>;
+ type = "critical";
+ };
+ };
+ };
+
+ ubi32-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tsens 1>;
+
+ trips {
+ ubi32-critical {
+ temperature = <120000>;
+ hysteresis = <2>;
+ type = "critical";
+ };
+ };
+ };
+ };
timer {
compatible = "arm,armv8-timer";

View File

@ -0,0 +1,85 @@
From: Varadarajan Narayanan <quic_varada@quicinc.com>
Date: Thu, 2 Jan 2025 17:00:15 +0530
Subject: [PATCH] dt-bindings: phy: qcom,uniphy-pcie: Document PCIe uniphy
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Document the Qualcomm UNIPHY PCIe 28LP present in IPQ5332.
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
Signed-off-by: Varadarajan Narayanan <quic_varada@quicinc.com>
---
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/qcom,ipq5332-uniphy-pcie-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm UNIPHY PCIe 28LP PHY
+
+maintainers:
+ - Nitheesh Sekar <quic_nsekar@quicinc.com>
+ - Varadarajan Narayanan <quic_varada@quicinc.com>
+
+description:
+ PCIe and USB combo PHY found in Qualcomm IPQ5332 SoC
+
+properties:
+ compatible:
+ enum:
+ - qcom,ipq5332-uniphy-pcie-phy
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: pcie pipe clock
+ - description: pcie ahb clock
+
+ resets:
+ items:
+ - description: phy reset
+ - description: ahb reset
+ - description: cfg reset
+
+ "#phy-cells":
+ const: 0
+
+ "#clock-cells":
+ const: 0
+
+ num-lanes: true
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - resets
+ - "#phy-cells"
+ - "#clock-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,ipq5332-gcc.h>
+
+ pcie0_phy: phy@4b0000 {
+ compatible = "qcom,ipq5332-uniphy-pcie-phy";
+ reg = <0x004b0000 0x800>;
+
+ clocks = <&gcc GCC_PCIE3X1_0_PIPE_CLK>,
+ <&gcc GCC_PCIE3X1_PHY_AHB_CLK>;
+
+ resets = <&gcc GCC_PCIE3X1_0_PHY_BCR>,
+ <&gcc GCC_PCIE3X1_PHY_AHB_CLK_ARES>,
+ <&gcc GCC_PCIE3X1_0_PHY_PHY_BCR>;
+
+ #clock-cells = <0>;
+
+ #phy-cells = <0>;
+ };

View File

@ -0,0 +1,332 @@
From: Varadarajan Narayanan <quic_varada@quicinc.com>
Date: Thu, 2 Jan 2025 17:00:16 +0530
Subject: [PATCH] phy: qcom: Introduce PCIe UNIPHY 28LP driver
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Add Qualcomm PCIe UNIPHY 28LP driver support present
in Qualcomm IPQ5332 SoC and the phy init sequence.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
Signed-off-by: Varadarajan Narayanan <quic_varada@quicinc.com>
---
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -154,6 +154,18 @@ config PHY_QCOM_M31_USB
management. This driver is required even for peripheral only or
host only mode configurations.
+config PHY_QCOM_UNIPHY_PCIE_28LP
+ bool "PCIE UNIPHY 28LP PHY driver"
+ depends on ARCH_QCOM
+ depends on HAS_IOMEM
+ depends on OF
+ select GENERIC_PHY
+ help
+ Enable this to support the PCIe UNIPHY 28LP phy transceiver that
+ is used with PCIe controllers on Qualcomm IPQ5332 chips. It
+ handles PHY initialization, clock management required after
+ resetting the hardware and power management.
+
config PHY_QCOM_USB_HS
tristate "Qualcomm USB HS PHY module"
depends on USB_ULPI_BUS
--- a/drivers/phy/qualcomm/Makefile
+++ b/drivers/phy/qualcomm/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY) +=
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
obj-$(CONFIG_PHY_QCOM_SNPS_EUSB2) += phy-qcom-snps-eusb2.o
obj-$(CONFIG_PHY_QCOM_EUSB2_REPEATER) += phy-qcom-eusb2-repeater.o
+obj-$(CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP) += phy-qcom-uniphy-pcie-28lp.o
obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define RST_ASSERT_DELAY_MIN_US 100
+#define RST_ASSERT_DELAY_MAX_US 150
+#define PIPE_CLK_DELAY_MIN_US 5000
+#define PIPE_CLK_DELAY_MAX_US 5100
+#define CLK_EN_DELAY_MIN_US 30
+#define CLK_EN_DELAY_MAX_US 50
+#define CDR_CTRL_REG_1 0x80
+#define CDR_CTRL_REG_2 0x84
+#define CDR_CTRL_REG_3 0x88
+#define CDR_CTRL_REG_4 0x8c
+#define CDR_CTRL_REG_5 0x90
+#define CDR_CTRL_REG_6 0x94
+#define CDR_CTRL_REG_7 0x98
+#define SSCG_CTRL_REG_1 0x9c
+#define SSCG_CTRL_REG_2 0xa0
+#define SSCG_CTRL_REG_3 0xa4
+#define SSCG_CTRL_REG_4 0xa8
+#define SSCG_CTRL_REG_5 0xac
+#define SSCG_CTRL_REG_6 0xb0
+#define PCS_INTERNAL_CONTROL_2 0x2d8
+
+#define PHY_CFG_PLLCFG 0x220
+#define PHY_CFG_EIOS_DTCT_REG 0x3e4
+#define PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME 0x3e8
+
+#define PHY_MODE_FIXED 0x1
+
+enum qcom_uniphy_pcie_type {
+ PHY_TYPE_PCIE = 1,
+ PHY_TYPE_PCIE_GEN2,
+ PHY_TYPE_PCIE_GEN3,
+};
+
+struct qcom_uniphy_pcie_regs {
+ u32 offset;
+ u32 val;
+};
+
+struct qcom_uniphy_pcie_data {
+ int lane_offset; /* offset between the lane register bases */
+ u32 phy_type;
+ const struct qcom_uniphy_pcie_regs *init_seq;
+ u32 init_seq_num;
+ u32 pipe_clk_rate;
+};
+
+struct qcom_uniphy_pcie {
+ struct phy phy;
+ struct device *dev;
+ const struct qcom_uniphy_pcie_data *data;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct reset_control *resets;
+ void __iomem *base;
+ int lanes;
+};
+
+#define phy_to_dw_phy(x) container_of((x), struct qca_uni_pcie_phy, phy)
+
+static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = {
+ {
+ .offset = PHY_CFG_PLLCFG,
+ .val = 0x30,
+ }, {
+ .offset = PHY_CFG_EIOS_DTCT_REG,
+ .val = 0x53ef,
+ }, {
+ .offset = PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME,
+ .val = 0xcf,
+ },
+};
+
+static const struct qcom_uniphy_pcie_data ipq5332_data = {
+ .lane_offset = 0x800,
+ .phy_type = PHY_TYPE_PCIE_GEN3,
+ .init_seq = ipq5332_regs,
+ .init_seq_num = ARRAY_SIZE(ipq5332_regs),
+ .pipe_clk_rate = 250000000,
+};
+
+static void qcom_uniphy_pcie_init(struct qcom_uniphy_pcie *phy)
+{
+ const struct qcom_uniphy_pcie_data *data = phy->data;
+ const struct qcom_uniphy_pcie_regs *init_seq;
+ void __iomem *base = phy->base;
+ int lane, i;
+
+ for (lane = 0; lane < phy->lanes; lane++) {
+ init_seq = data->init_seq;
+
+ for (i = 0; i < data->init_seq_num; i++)
+ writel(init_seq[i].val, base + init_seq[i].offset);
+
+ base += data->lane_offset;
+ }
+}
+
+static int qcom_uniphy_pcie_power_off(struct phy *x)
+{
+ struct qcom_uniphy_pcie *phy = phy_get_drvdata(x);
+
+ clk_bulk_disable_unprepare(phy->num_clks, phy->clks);
+
+ return reset_control_assert(phy->resets);
+}
+
+static int qcom_uniphy_pcie_power_on(struct phy *x)
+{
+ struct qcom_uniphy_pcie *phy = phy_get_drvdata(x);
+ int ret;
+
+ ret = reset_control_assert(phy->resets);
+ if (ret) {
+ dev_err(phy->dev, "reset assert failed (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(RST_ASSERT_DELAY_MIN_US, RST_ASSERT_DELAY_MAX_US);
+
+ ret = reset_control_deassert(phy->resets);
+ if (ret) {
+ dev_err(phy->dev, "reset deassert failed (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US);
+
+ ret = clk_bulk_prepare_enable(phy->num_clks, phy->clks);
+ if (ret) {
+ dev_err(phy->dev, "clk prepare and enable failed %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CLK_EN_DELAY_MIN_US, CLK_EN_DELAY_MAX_US);
+
+ qcom_uniphy_pcie_init(phy);
+ return 0;
+}
+
+static inline int qcom_uniphy_pcie_get_resources(struct platform_device *pdev,
+ struct qcom_uniphy_pcie *phy)
+{
+ struct resource *res;
+
+ phy->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(phy->base))
+ return PTR_ERR(phy->base);
+
+ phy->num_clks = devm_clk_bulk_get_all(phy->dev, &phy->clks);
+ if (phy->num_clks < 0)
+ return phy->num_clks;
+
+ phy->resets = devm_reset_control_array_get_exclusive(phy->dev);
+ if (IS_ERR(phy->resets))
+ return PTR_ERR(phy->resets);
+
+ return 0;
+}
+
+/*
+ * Register a fixed rate pipe clock.
+ *
+ * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
+ * controls it. The <s>_pipe_clk coming out of the GCC is requested
+ * by the PHY driver for its operations.
+ * We register the <s>_pipe_clksrc here. The gcc driver takes care
+ * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
+ * Below picture shows this relationship.
+ *
+ * +---------------+
+ * | PHY block |<<---------------------------------------+
+ * | | |
+ * | +-------+ | +-----+ |
+ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
+ * clk | +-------+ | +-----+
+ * +---------------+
+ */
+static inline int phy_pipe_clk_register(struct qcom_uniphy_pcie *phy, int id)
+{
+ const struct qcom_uniphy_pcie_data *data = phy->data;
+ struct clk_hw *hw;
+ char name[64];
+
+ snprintf(name, sizeof(name), "phy%d_pipe_clk_src", id);
+ hw = devm_clk_hw_register_fixed_rate(phy->dev, name, NULL, 0,
+ data->pipe_clk_rate);
+ if (IS_ERR(hw))
+ return dev_err_probe(phy->dev, PTR_ERR(hw),
+ "Unable to register %s\n", name);
+
+ return devm_of_clk_add_hw_provider(phy->dev, of_clk_hw_simple_get, hw);
+}
+
+static const struct of_device_id qcom_uniphy_pcie_id_table[] = {
+ {
+ .compatible = "qcom,ipq5332-uniphy-pcie-phy",
+ .data = &ipq5332_data,
+ }, {
+ /* Sentinel */
+ },
+};
+MODULE_DEVICE_TABLE(of, qcom_uniphy_pcie_id_table);
+
+static const struct phy_ops pcie_ops = {
+ .power_on = qcom_uniphy_pcie_power_on,
+ .power_off = qcom_uniphy_pcie_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_uniphy_pcie_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct qcom_uniphy_pcie *phy;
+ struct phy *generic_phy;
+ int ret;
+
+ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, phy);
+ phy->dev = &pdev->dev;
+
+ phy->data = of_device_get_match_data(dev);
+ if (!phy->data)
+ return -EINVAL;
+
+ phy->lanes = 1;
+ ret = of_property_read_u32(dev->of_node, "num-lanes", &phy->lanes);
+
+ ret = qcom_uniphy_pcie_get_resources(pdev, phy);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to get resources: %d\n", ret);
+
+ generic_phy = devm_phy_create(phy->dev, NULL, &pcie_ops);
+ if (IS_ERR(generic_phy))
+ return PTR_ERR(generic_phy);
+
+ phy_set_drvdata(generic_phy, phy);
+
+ ret = phy_pipe_clk_register(phy, generic_phy->id);
+ if (ret)
+ dev_err(&pdev->dev, "failed to register phy pipe clk\n");
+
+ phy_provider = devm_of_phy_provider_register(phy->dev,
+ of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ return 0;
+}
+
+static struct platform_driver qcom_uniphy_pcie_driver = {
+ .probe = qcom_uniphy_pcie_probe,
+ .driver = {
+ .name = "qcom-uniphy-pcie",
+ .of_match_table = qcom_uniphy_pcie_id_table,
+ },
+};
+
+module_platform_driver(qcom_uniphy_pcie_driver);
+
+MODULE_DESCRIPTION("PCIE QCOM UNIPHY driver");
+MODULE_LICENSE("GPL");

View File

@ -0,0 +1,25 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Tue, 07 Jan 2025 17:34:13 +0400
Subject: [PATCH] phy: qualcomm: qcom-uniphy-pcie add IPQ5018 compatible
The Qualcomm UNIPHY PCIe PHY 28lp part of the IPQ5332 SoC is also present on
the IPQ5018 SoC, so adding the compatible for IPQ5018.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-uniphy-pcie-phy.yaml
@@ -11,11 +11,12 @@ maintainers:
- Varadarajan Narayanan <quic_varada@quicinc.com>
description:
- PCIe and USB combo PHY found in Qualcomm IPQ5332 SoC
+ PCIe and USB combo PHY found in Qualcomm IPQ5018 and IPQ5332 SoCs
properties:
compatible:
enum:
+ - qcom,ipq5018-uniphy-pcie-phy
- qcom,ipq5332-uniphy-pcie-phy
reg:

View File

@ -0,0 +1,77 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Tue, 07 Jan 2025 17:34:13 +0400
Subject: [PATCH] phy: qualcomm: qcom-uniphy-pcie 28lp add support for IPQ5018
The Qualcomm UNIPHY PCIe PHY 28lp is found on both IPQ5332 and IPQ5018.
Adding the PHY init sequence, pipe clock rate, and compatible for IPQ5018.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c
+++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c
@@ -76,6 +76,40 @@ struct qcom_uniphy_pcie {
#define phy_to_dw_phy(x) container_of((x), struct qca_uni_pcie_phy, phy)
+static const struct qcom_uniphy_pcie_regs ipq5018_regs[] = {
+ {
+ .offset = SSCG_CTRL_REG_4,
+ .val = 0x1cb9,
+ }, {
+ .offset = SSCG_CTRL_REG_5,
+ .val = 0x023a,
+ }, {
+ .offset = SSCG_CTRL_REG_3,
+ .val = 0xd360,
+ }, {
+ .offset = SSCG_CTRL_REG_1,
+ .val = 0x1,
+ }, {
+ .offset = SSCG_CTRL_REG_2,
+ .val = 0xeb,
+ }, {
+ .offset = CDR_CTRL_REG_4,
+ .val = 0x3f9,
+ }, {
+ .offset = CDR_CTRL_REG_5,
+ .val = 0x1c9,
+ }, {
+ .offset = CDR_CTRL_REG_2,
+ .val = 0x419,
+ }, {
+ .offset = CDR_CTRL_REG_1,
+ .val = 0x200,
+ }, {
+ .offset = PCS_INTERNAL_CONTROL_2,
+ .val = 0xf101,
+ },
+};
+
static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = {
{
.offset = PHY_CFG_PLLCFG,
@@ -89,6 +123,14 @@ static const struct qcom_uniphy_pcie_reg
},
};
+static const struct qcom_uniphy_pcie_data ipq5018_data = {
+ .lane_offset = 0x800,
+ .phy_type = PHY_TYPE_PCIE_GEN2,
+ .init_seq = ipq5018_regs,
+ .init_seq_num = ARRAY_SIZE(ipq5018_regs),
+ .pipe_clk_rate = 125000000,
+};
+
static const struct qcom_uniphy_pcie_data ipq5332_data = {
.lane_offset = 0x800,
.phy_type = PHY_TYPE_PCIE_GEN3,
@@ -212,6 +254,9 @@ static inline int phy_pipe_clk_register(
static const struct of_device_id qcom_uniphy_pcie_id_table[] = {
{
+ .compatible = "qcom,ipq5018-uniphy-pcie-phy",
+ .data = &ipq5018_data,
+ }, {
.compatible = "qcom,ipq5332-uniphy-pcie-phy",
.data = &ipq5332_data,
}, {

View File

@ -0,0 +1,78 @@
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Date: Tue, 3 Oct 2023 17:38:42 +0530
Subject: [PATCH] dt-bindings: PCI: qcom: Add IPQ5108 SoC
Add support for the PCIe controller on the Qualcomm
IPQ5108 SoC to the bindings.
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
---
.../devicetree/bindings/pci/qcom,pcie.yaml | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
--- a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
@@ -21,6 +21,7 @@ properties:
- qcom,pcie-apq8064
- qcom,pcie-apq8084
- qcom,pcie-ipq4019
+ - qcom,pcie-ipq5018
- qcom,pcie-ipq6018
- qcom,pcie-ipq8064
- qcom,pcie-ipq8064-v2
@@ -170,6 +171,7 @@ allOf:
compatible:
contains:
enum:
+ - qcom,pcie-ipq5018
- qcom,pcie-ipq6018
- qcom,pcie-ipq8074-gen3
then:
@@ -337,6 +339,39 @@ allOf:
compatible:
contains:
enum:
+ - qcom,pcie-ipq5018
+ then:
+ properties:
+ clocks:
+ minItems: 6
+ maxItems: 6
+ clock-names:
+ items:
+ - const: iface # PCIe to SysNOC BIU clock
+ - const: axi_m # AXI Master clock
+ - const: axi_s # AXI Slave clock
+ - const: ahb # AHB clock
+ - const: aux # Auxiliary clock
+ - const: axi_bridge # AXI bridge clock
+ resets:
+ minItems: 8
+ maxItems: 8
+ reset-names:
+ items:
+ - const: pipe # PIPE reset
+ - const: sleep # Sleep reset
+ - const: sticky # Core sticky reset
+ - const: axi_m # AXI master reset
+ - const: axi_s # AXI slave reset
+ - const: ahb # AHB reset
+ - const: axi_m_sticky # AXI master sticky reset
+ - const: axi_s_sticky # AXI slave sticky reset
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
- qcom,pcie-msm8996
then:
properties:
@@ -875,6 +910,7 @@ allOf:
- qcom,pcie-apq8064
- qcom,pcie-apq8084
- qcom,pcie-ipq4019
+ - qcom,pcie-ipq5018
- qcom,pcie-ipq6018
- qcom,pcie-ipq8064
- qcom,pcie-ipq8064-v2

View File

@ -0,0 +1,77 @@
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Subject: [PATCH] PCI: qcom: Add support for IPQ5018
Date: Tue, 3 Oct 2023 17:38:44 +0530
Added a new compatible 'qcom,pcie-ipq5018' and modified
get_resources of 'ops 2_9_0' to get the clocks from the
device-tree.
Co-developed-by: Anusha Rao <quic_anusha@quicinc.com>
Signed-off-by: Anusha Rao <quic_anusha@quicinc.com>
Co-developed-by: Devi Priya <quic_devipriy@quicinc.com>
Signed-off-by: Devi Priya <quic_devipriy@quicinc.com>
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
---
drivers/pci/controller/dwc/pcie-qcom.c | 22 ++++++++--------------
1 file changed, 8 insertions(+), 14 deletions(-)
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -202,8 +202,9 @@ struct qcom_pcie_resources_2_7_0 {
#define QCOM_PCIE_2_9_0_MAX_CLOCKS 5
struct qcom_pcie_resources_2_9_0 {
- struct clk_bulk_data clks[QCOM_PCIE_2_9_0_MAX_CLOCKS];
+ struct clk_bulk_data *clks;
struct reset_control *rst;
+ int num_clks;
};
union qcom_pcie_resources {
@@ -1073,17 +1074,10 @@ static int qcom_pcie_get_resources_2_9_0
struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0;
struct dw_pcie *pci = pcie->pci;
struct device *dev = pci->dev;
- int ret;
-
- res->clks[0].id = "iface";
- res->clks[1].id = "axi_m";
- res->clks[2].id = "axi_s";
- res->clks[3].id = "axi_bridge";
- res->clks[4].id = "rchng";
- ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks);
- if (ret < 0)
- return ret;
+ res->num_clks = devm_clk_bulk_get_all(dev, &res->clks);
+ if (res->num_clks < 0)
+ return res->num_clks;
res->rst = devm_reset_control_array_get_exclusive(dev);
if (IS_ERR(res->rst))
@@ -1096,7 +1090,7 @@ static void qcom_pcie_deinit_2_9_0(struc
{
struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0;
- clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
+ clk_bulk_disable_unprepare(res->num_clks, res->clks);
}
static int qcom_pcie_init_2_9_0(struct qcom_pcie *pcie)
@@ -1125,7 +1119,7 @@ static int qcom_pcie_init_2_9_0(struct q
usleep_range(2000, 2500);
- return clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks);
+ return clk_bulk_prepare_enable(res->num_clks, res->clks);
}
static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
@@ -1641,6 +1635,7 @@ static const struct of_device_id qcom_pc
{ .compatible = "qcom,pcie-apq8064", .data = &cfg_2_1_0 },
{ .compatible = "qcom,pcie-apq8084", .data = &cfg_1_0_0 },
{ .compatible = "qcom,pcie-ipq4019", .data = &cfg_2_4_0 },
+ { .compatible = "qcom,pcie-ipq5018", .data = &cfg_2_9_0 },
{ .compatible = "qcom,pcie-ipq6018", .data = &cfg_2_9_0 },
{ .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 },
{ .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 },

View File

@ -0,0 +1,216 @@
From: Nitheesh Sekar <quic_nsekar@quicinc.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PCIe related nodes
Date: Tue, 3 Oct 2023 17:38:45 +0530
Add phy and controller nodes for PCIe0 and PCIe1.
PCIe0 is 2-lane Gen2 and PCIe1 is 1-lane Gen2.
Signed-off-by: Nitheesh Sekar <quic_nsekar@quicinc.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 186 +++++++++++++++++++++++++-
1 file changed, 184 insertions(+), 2 deletions(-)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -149,6 +149,42 @@
status = "disabled";
};
+ pcie1_phy: phy@7e000{
+ compatible = "qcom,ipq5018-uniphy-pcie-phy";
+ reg = <0x0007e000 0x800>;
+
+ clocks = <&gcc GCC_PCIE1_PIPE_CLK>;
+
+ resets = <&gcc GCC_PCIE1_PHY_BCR>,
+ <&gcc GCC_PCIE1PHY_PHY_BCR>;
+
+ #clock-cells = <0>;
+
+ #phy-cells = <0>;
+
+ num-lanes = <1>;
+
+ status = "disabled";
+ };
+
+ pcie0_phy: phy@86000{
+ compatible = "qcom,ipq5018-uniphy-pcie-phy";
+ reg = <0x00086000 0x800>;
+
+ clocks = <&gcc GCC_PCIE0_PIPE_CLK>;
+
+ resets = <&gcc GCC_PCIE0_PHY_BCR>,
+ <&gcc GCC_PCIE0PHY_PHY_BCR>;
+
+ #clock-cells = <0>;
+
+ #phy-cells = <0>;
+
+ num-lanes = <2>;
+
+ status = "disabled";
+ };
+
qfprom: qfprom@a0000 {
compatible = "qcom,ipq5018-qfprom", "qcom,qfprom";
reg = <0xa0000 0x1000>;
@@ -283,8 +319,8 @@
reg = <0x01800000 0x80000>;
clocks = <&xo_board_clk>,
<&sleep_clk>,
- <0>,
- <0>,
+ <&pcie0_phy>,
+ <&pcie1_phy>,
<0>,
<0>,
<0>,
@@ -501,6 +537,146 @@
status = "disabled";
};
};
+
+ pcie1: pcie@80000000 {
+ compatible = "qcom,pcie-ipq5018";
+ reg = <0x80000000 0xf1d>,
+ <0x80000f20 0xa8>,
+ <0x80001000 0x1000>,
+ <0x00078000 0x3000>,
+ <0x80100000 0x1000>;
+ reg-names = "dbi",
+ "elbi",
+ "atu",
+ "parf",
+ "config";
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ max-link-speed = <2>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ phys = <&pcie1_phy>;
+ phy-names ="pciephy";
+
+ ranges = <0x81000000 0 0x80200000 0x80200000 0 0x00100000>, /* I/O */
+ <0x82000000 0 0x80300000 0x80300000 0 0x10000000>; /* MEM */
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 142 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc 0 143 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc 0 144 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc 0 145 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "global_irq";
+
+ clocks = <&gcc GCC_SYS_NOC_PCIE1_AXI_CLK>,
+ <&gcc GCC_PCIE1_AXI_M_CLK>,
+ <&gcc GCC_PCIE1_AXI_S_CLK>,
+ <&gcc GCC_PCIE1_AHB_CLK>,
+ <&gcc GCC_PCIE1_AUX_CLK>,
+ <&gcc GCC_PCIE1_AXI_S_BRIDGE_CLK>;
+ clock-names = "iface",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "aux",
+ "axi_bridge";
+
+ resets = <&gcc GCC_PCIE1_PIPE_ARES>,
+ <&gcc GCC_PCIE1_SLEEP_ARES>,
+ <&gcc GCC_PCIE1_CORE_STICKY_ARES>,
+ <&gcc GCC_PCIE1_AXI_MASTER_ARES>,
+ <&gcc GCC_PCIE1_AXI_SLAVE_ARES>,
+ <&gcc GCC_PCIE1_AHB_ARES>,
+ <&gcc GCC_PCIE1_AXI_MASTER_STICKY_ARES>,
+ <&gcc GCC_PCIE1_AXI_SLAVE_STICKY_ARES>;
+ reset-names = "pipe",
+ "sleep",
+ "sticky",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "axi_m_sticky",
+ "axi_s_sticky";
+
+ msi-map = <0x0 &v2m0 0x0 0xff8>;
+ status = "disabled";
+ };
+
+ pcie0: pcie@a0000000 {
+ compatible = "qcom,pcie-ipq5018";
+ reg = <0xa0000000 0xf1d>,
+ <0xa0000f20 0xa8>,
+ <0xa0001000 0x1000>,
+ <0x00080000 0x3000>,
+ <0xa0100000 0x1000>;
+ reg-names = "dbi",
+ "elbi",
+ "atu",
+ "parf",
+ "config";
+ device_type = "pci";
+ linux,pci-domain = <1>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <2>;
+ max-link-speed = <2>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ phys = <&pcie0_phy>;
+ phy-names ="pciephy";
+
+ ranges = <0x81000000 0 0xa0200000 0xa0200000 0 0x00100000>, /* I/O */
+ <0x82000000 0 0xa0300000 0xa0300000 0 0x10000000>; /* MEM */
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 75 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc 0 78 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc 0 79 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc 0 83 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "global_irq";
+
+ clocks = <&gcc GCC_SYS_NOC_PCIE0_AXI_CLK>,
+ <&gcc GCC_PCIE0_AXI_M_CLK>,
+ <&gcc GCC_PCIE0_AXI_S_CLK>,
+ <&gcc GCC_PCIE0_AHB_CLK>,
+ <&gcc GCC_PCIE0_AUX_CLK>,
+ <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>;
+ clock-names = "iface",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "aux",
+ "axi_bridge";
+
+ resets = <&gcc GCC_PCIE0_PIPE_ARES>,
+ <&gcc GCC_PCIE0_SLEEP_ARES>,
+ <&gcc GCC_PCIE0_CORE_STICKY_ARES>,
+ <&gcc GCC_PCIE0_AXI_MASTER_ARES>,
+ <&gcc GCC_PCIE0_AXI_SLAVE_ARES>,
+ <&gcc GCC_PCIE0_AHB_ARES>,
+ <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>,
+ <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>;
+ reset-names = "pipe",
+ "sleep",
+ "sticky",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "axi_m_sticky",
+ "axi_s_sticky";
+
+ msi-map = <0x0 &v2m0 0x0 0xff8>;
+ status = "disabled";
+ };
};
thermal-zones {

View File

@ -0,0 +1,18 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add IPQ5018 compatible
Date: Sun, 06 Oct 2024 16:34:11 +0400
Document the qcom,tcsr-ipq5018 compatible.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml
+++ b/Documentation/devicetree/bindings/mfd/qcom,tcsr.yaml
@@ -33,6 +33,7 @@ properties:
- qcom,sm8450-tcsr
- qcom,tcsr-apq8064
- qcom,tcsr-apq8084
+ - qcom,tcsr-ipq5018
- qcom,tcsr-ipq5332
- qcom,tcsr-ipq8064
- qcom,tcsr-ipq8074

View File

@ -0,0 +1,22 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add TCSR node
Date: Sun, 06 Oct 2024 16:34:11 +0400
Add TCSR node.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -337,6 +337,11 @@
#hwlock-cells = <1>;
};
+ tcsr: syscon@1937000 {
+ compatible = "qcom,tcsr-ipq5018", "syscon", "simple-mfd";
+ reg = <0x01937000 0x21000>;
+ };
+
sdhc_1: mmc@7804000 {
compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5";
reg = <0x7804000 0x1000>;

View File

@ -0,0 +1,19 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: enable the download mode support
Date: Sun, 06 Oct 2024 16:34:11 +0400
IPQ5018 also supports the download mode to collect the RAM dumps if system crashes, to perform
the post mortem analysis. Add support for the same.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -82,6 +82,7 @@
scm {
compatible = "qcom,scm-ipq5018", "qcom,scm";
qcom,sdi-enabled;
+ qcom,dload-mode = <&tcsr 0x6100>;
};
};

View File

@ -0,0 +1,22 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] dt-bindings: mfd: qcom,tcsr: Add IPQ5018 compatible
Date: Sun, 06 Oct 2024 16:34:11 +0400
Add compatible for IPQ5018.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml
+++ b/Documentation/devicetree/bindings/pwm/qcom,ipq6018-pwm.yaml
@@ -11,7 +11,10 @@ maintainers:
properties:
compatible:
- const: qcom,ipq6018-pwm
+ items:
+ - enum:
+ - qcom,ipq5018-pwm
+ - const: qcom,ipq6018-pwm
reg:
description: Offset of PWM register in the TCSR block.

View File

@ -0,0 +1,65 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] pinctrl: qcom: IPQ5018: update pwm groups
Date: Wed, 27 Nov 2024 09:14:11 +0400
GPIO 1, 30, and 46 are used to control PWM1, PWM3, and PWM0 respectively which
in turn drive the PWM led, so let's update the pwm# and pingroups accordingly.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/drivers/pinctrl/qcom/pinctrl-ipq5018.c
+++ b/drivers/pinctrl/qcom/pinctrl-ipq5018.c
@@ -541,7 +541,7 @@ static const char * const qdss_tracectl_
};
static const char * const pwm0_groups[] = {
- "gpio42",
+ "gpio42", "gpio46",
};
static const char * const qdss_cti_trig_out_b0_groups[] = {
@@ -549,7 +549,7 @@ static const char * const qdss_cti_trig_
};
static const char * const pwm1_groups[] = {
- "gpio43",
+ "gpio43", "gpio1",
};
static const char * const qdss_cti_trig_in_b0_groups[] = {
@@ -565,7 +565,7 @@ static const char * const qdss_cti_trig_
};
static const char * const pwm3_groups[] = {
- "gpio45",
+ "gpio45", "gpio30",
};
static const char * const qdss_cti_trig_in_b1_groups[] = {
@@ -679,7 +679,7 @@ static const struct pinfunction ipq5018_
static const struct msm_pingroup ipq5018_groups[] = {
PINGROUP(0, atest_char, _, qdss_cti_trig_out_a0, wci_txd, wci_rxd, xfem, _, _, _),
- PINGROUP(1, atest_char, _, qdss_cti_trig_in_a0, wci_txd, wci_rxd, xfem, _, _, _),
+ PINGROUP(1, atest_char, pwm1, qdss_cti_trig_in_a0, wci_txd, wci_rxd, xfem, _, _, _),
PINGROUP(2, atest_char, _, qdss_cti_trig_out_a1, wci_txd, wci_rxd, xfem, _, _, _),
PINGROUP(3, atest_char, _, qdss_cti_trig_in_a1, wci_txd, wci_rxd, xfem, _, _, _),
PINGROUP(4, sdc1_data, qspi_data, blsp1_spi1, btss, dbg_out, qdss_traceclk_a, _, burn0, _),
@@ -708,7 +708,7 @@ static const struct msm_pingroup ipq5018
PINGROUP(27, audio_txmclk, wsa_swrm, audio_txmclk, blsp2_spi, btss, _, qdss_tracedata_b, _, _),
PINGROUP(28, audio_txbclk, wsa_swrm, blsp0_uart1, btss, qdss_tracedata_b, _, _, _, _),
PINGROUP(29, audio_txfsync, _, blsp0_uart1, _, qdss_tracedata_b, _, _, _, _),
- PINGROUP(30, audio_txd, led2, led0, _, _, _, _, _, _),
+ PINGROUP(30, audio_txd, led2, led0, pwm3, _, _, _, _, _),
PINGROUP(31, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _, _),
PINGROUP(32, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _, _),
PINGROUP(33, blsp2_i2c0, blsp2_spi0, blsp1_uart1, _, qdss_tracedata_b, eud_gpio, _, _, _),
@@ -724,7 +724,7 @@ static const struct msm_pingroup ipq5018
PINGROUP(43, pwm1, qdss_cti_trig_in_b0, wci_txd, wci_rxd, xfem, _, _, _, _),
PINGROUP(44, pwm2, qdss_cti_trig_out_b1, wci_txd, wci_rxd, xfem, _, _, _, _),
PINGROUP(45, pwm3, qdss_cti_trig_in_b1, wci_txd, wci_rxd, xfem, _, _, _, _),
- PINGROUP(46, led0, _, _, _, _, _, _, _, _),
+ PINGROUP(46, led0, pwm0, _, _, _, _, _, _, _),
};
static const struct msm_pinctrl_soc_data ipq5018_pinctrl = {

View File

@ -0,0 +1,27 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PWM node
Date: Sun, 06 Oct 2024 16:34:11 +0400
Add PWM node.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -343,6 +343,16 @@
reg = <0x01937000 0x21000>;
};
+ pwm: pwm@1941010 {
+ compatible = "qcom,ipq5018-pwm", "qcom,ipq6018-pwm";
+ reg = <0x01941010 0x20>;
+ clocks = <&gcc GCC_ADSS_PWM_CLK>;
+ assigned-clocks = <&gcc GCC_ADSS_PWM_CLK>;
+ assigned-clock-rates = <100000000>;
+ #pwm-cells = <2>;
+ status = "disabled";
+ };
+
sdhc_1: mmc@7804000 {
compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5";
reg = <0x7804000 0x1000>;

View File

@ -0,0 +1,41 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add crypto nodes
Date: Sun, 06 Oct 2024 16:34:11 +0400
Add dma controller and crypto nodes.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -297,6 +297,30 @@
#thermal-sensor-cells = <1>;
};
+ cryptobam: dma-controller@704000 {
+ compatible = "qcom,bam-v1.7.0";
+ reg = <0x00704000 0x20000>;
+ interrupts = <GIC_SPI 207 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_CRYPTO_AHB_CLK>;
+ clock-names = "bam_clk";
+ #dma-cells = <1>;
+ qcom,ee = <1>;
+ qcom,controlled-remotely;
+ status = "disabled";
+ };
+
+ crypto: crypto@73a000 {
+ compatible = "qcom,crypto-v5.1";
+ reg = <0x0073a000 0x6000>;
+ clocks = <&gcc GCC_CRYPTO_AHB_CLK>,
+ <&gcc GCC_CRYPTO_AXI_CLK>,
+ <&gcc GCC_CRYPTO_CLK>;
+ clock-names = "iface", "bus", "core";
+ dmas = <&cryptobam 2>, <&cryptobam 3>;
+ dma-names = "rx", "tx";
+ status = "disabled";
+ };
+
tlmm: pinctrl@1000000 {
compatible = "qcom,ipq5018-tlmm";
reg = <0x01000000 0x300000>;

View File

@ -0,0 +1,25 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add PRNG node
Date: Sun, 06 Oct 2024 16:34:11 +0400
Add PRNG node.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -258,6 +258,14 @@
};
};
+ prng: rng@e3000 {
+ compatible = "qcom,prng-ee";
+ reg = <0x000e3000 0x1000>;
+ clocks = <&gcc GCC_PRNG_AHB_CLK>;
+ clock-names = "core";
+ status = "disabled";
+ };
+
tsens: thermal-sensor@4a9000 {
compatible = "qcom,ipq5018-tsens";
reg = <0x4a9000 0x1000>, /* TM */

View File

@ -0,0 +1,27 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add QUP1-UART2 node
Date: Sun, 06 Oct 2024 16:34:11 +0400
Add QUP1-UART2 node.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -422,6 +422,16 @@
status = "disabled";
};
+ blsp1_uart2: serial@78b0000 {
+ compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+ reg = <0x078b0000 0x200>;
+ interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ status = "disabled";
+ };
+
blsp1_spi1: spi@78b5000 {
compatible = "qcom,spi-qup-v2.2.1";
#address-cells = <1>;

View File

@ -0,0 +1,32 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add QUP3 I2C node
Date: Sun, 06 Oct 2024 16:34:11 +0400
Add QUP3-I2C node.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -446,6 +446,21 @@
status = "disabled";
};
+ blsp1_i2c3: i2c@78b7000 {
+ compatible = "qcom,i2c-qup-v2.2.1";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x078b7000 0x600>;
+ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_BLSP1_QUP3_I2C_APPS_CLK>,
+ <&gcc GCC_BLSP1_AHB_CLK>;
+ clock-names = "core", "iface";
+ clock-frequency = <400000>;
+ dmas = <&blsp_dma 9>, <&blsp_dma 8>;
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
+
usb: usb@8af8800 {
compatible = "qcom,ipq5018-dwc3", "qcom,dwc3";
reg = <0x08af8800 0x400>;

View File

@ -0,0 +1,97 @@
From: Md Sadre Alam <quic_mdalam@quicinc.com>
Date: Sun, 22 Sep 2024 17:03:44 +0530
Subject: [PATCH] spi: dt-bindings: Introduce qcom,spi-qpic-snand
Document the QPIC-SPI-NAND flash controller present in the IPQ SoCs.
It can work both in serial and parallel mode and supports typical
SPI-NAND page cache operations.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
---
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/qcom,spi-qpic-snand.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm QPIC NAND controller
+
+maintainers:
+ - Md sadre Alam <quic_mdalam@quicinc.com>
+
+description:
+ The QCOM QPIC-SPI-NAND flash controller is an extended version of
+ the QCOM QPIC NAND flash controller. It can work both in serial
+ and parallel mode. It supports typical SPI-NAND page cache
+ operations in single, dual or quad IO mode with pipelined ECC
+ encoding/decoding using the QPIC ECC HW engine.
+
+allOf:
+ - $ref: /schemas/spi/spi-controller.yaml#
+
+properties:
+ compatible:
+ enum:
+ - qcom,spi-qpic-snand
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 3
+
+ clock-names:
+ items:
+ - const: core
+ - const: aon
+ - const: iom
+
+ dmas:
+ items:
+ - description: tx DMA channel
+ - description: rx DMA channel
+ - description: cmd DMA channel
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+ - const: cmd
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
+ spi@79b0000 {
+ compatible = "qcom,spi-qpic-snand";
+ reg = <0x1ac00000 0x800>;
+
+ clocks = <&gcc GCC_QPIC_CLK>,
+ <&gcc GCC_QPIC_AHB_CLK>,
+ <&gcc GCC_QPIC_IO_MACRO_CLK>;
+ clock-names = "core", "aon", "iom";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ flash@0 {
+ compatible = "spi-nand";
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ nand-ecc-engine = <&qpic_nand>;
+ nand-ecc-strength = <4>;
+ nand-ecc-step-size = <512>;
+ };
+ };

View File

@ -0,0 +1,983 @@
From: Md Sadre Alam <quic_mdalam@quicinc.com>
Date: Sun, 22 Sep 2024 17:03:45 +0530
Subject: [PATCH] mtd: rawnand: qcom: cleanup qcom_nandc driver
cleanup qcom_nandc driver as below
- Remove register value indirection api
- Remove set_reg() api
- Convert read_loc_first & read_loc_last macro to function
- Renamed multiple variables
Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
---
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -189,17 +189,6 @@
#define ECC_BCH_4BIT BIT(2)
#define ECC_BCH_8BIT BIT(3)
-#define nandc_set_read_loc_first(chip, reg, cw_offset, read_size, is_last_read_loc) \
-nandc_set_reg(chip, reg, \
- ((cw_offset) << READ_LOCATION_OFFSET) | \
- ((read_size) << READ_LOCATION_SIZE) | \
- ((is_last_read_loc) << READ_LOCATION_LAST))
-
-#define nandc_set_read_loc_last(chip, reg, cw_offset, read_size, is_last_read_loc) \
-nandc_set_reg(chip, reg, \
- ((cw_offset) << READ_LOCATION_OFFSET) | \
- ((read_size) << READ_LOCATION_SIZE) | \
- ((is_last_read_loc) << READ_LOCATION_LAST))
/*
* Returns the actual register address for all NAND_DEV_ registers
* (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD)
@@ -257,8 +246,6 @@ nandc_set_reg(chip, reg, \
* @tx_sgl_start - start index in data sgl for tx.
* @rx_sgl_pos - current index in data sgl for rx.
* @rx_sgl_start - start index in data sgl for rx.
- * @wait_second_completion - wait for second DMA desc completion before making
- * the NAND transfer completion.
*/
struct bam_transaction {
struct bam_cmd_element *bam_ce;
@@ -275,7 +262,6 @@ struct bam_transaction {
u32 tx_sgl_start;
u32 rx_sgl_pos;
u32 rx_sgl_start;
- bool wait_second_completion;
};
/*
@@ -471,9 +457,9 @@ struct qcom_op {
unsigned int data_instr_idx;
unsigned int rdy_timeout_ms;
unsigned int rdy_delay_ns;
- u32 addr1_reg;
- u32 addr2_reg;
- u32 cmd_reg;
+ __le32 addr1_reg;
+ __le32 addr2_reg;
+ __le32 cmd_reg;
u8 flag;
};
@@ -549,17 +535,17 @@ struct qcom_nand_host {
* among different NAND controllers.
* @ecc_modes - ecc mode for NAND
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
- * @is_bam - whether NAND controller is using BAM
- * @is_qpic - whether NAND CTRL is part of qpic IP
- * @qpic_v2 - flag to indicate QPIC IP version 2
+ * @supports_bam - whether NAND controller is using BAM
+ * @nandc_part_of_qpic - whether NAND controller is part of qpic IP
+ * @qpic_version2 - flag to indicate QPIC IP version 2
* @use_codeword_fixup - whether NAND has different layout for boot partitions
*/
struct qcom_nandc_props {
u32 ecc_modes;
u32 dev_cmd_reg_start;
- bool is_bam;
- bool is_qpic;
- bool qpic_v2;
+ bool supports_bam;
+ bool nandc_part_of_qpic;
+ bool qpic_version2;
bool use_codeword_fixup;
};
@@ -613,19 +599,11 @@ static void clear_bam_transaction(struct
{
struct bam_transaction *bam_txn = nandc->bam_txn;
- if (!nandc->props->is_bam)
+ if (!nandc->props->supports_bam)
return;
- bam_txn->bam_ce_pos = 0;
- bam_txn->bam_ce_start = 0;
- bam_txn->cmd_sgl_pos = 0;
- bam_txn->cmd_sgl_start = 0;
- bam_txn->tx_sgl_pos = 0;
- bam_txn->tx_sgl_start = 0;
- bam_txn->rx_sgl_pos = 0;
- bam_txn->rx_sgl_start = 0;
+ memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8);
bam_txn->last_data_desc = NULL;
- bam_txn->wait_second_completion = false;
sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
QPIC_PER_CW_CMD_SGL);
@@ -640,17 +618,7 @@ static void qpic_bam_dma_done(void *data
{
struct bam_transaction *bam_txn = data;
- /*
- * In case of data transfer with NAND, 2 callbacks will be generated.
- * One for command channel and another one for data channel.
- * If current transaction has data descriptors
- * (i.e. wait_second_completion is true), then set this to false
- * and wait for second DMA descriptor completion.
- */
- if (bam_txn->wait_second_completion)
- bam_txn->wait_second_completion = false;
- else
- complete(&bam_txn->txn_done);
+ complete(&bam_txn->txn_done);
}
static inline struct qcom_nand_host *to_qcom_nand_host(struct nand_chip *chip)
@@ -676,10 +644,9 @@ static inline void nandc_write(struct qc
iowrite32(val, nandc->base + offset);
}
-static inline void nandc_read_buffer_sync(struct qcom_nand_controller *nandc,
- bool is_cpu)
+static inline void nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu)
{
- if (!nandc->props->is_bam)
+ if (!nandc->props->supports_bam)
return;
if (is_cpu)
@@ -694,93 +661,90 @@ static inline void nandc_read_buffer_syn
DMA_FROM_DEVICE);
}
-static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset)
-{
- switch (offset) {
- case NAND_FLASH_CMD:
- return &regs->cmd;
- case NAND_ADDR0:
- return &regs->addr0;
- case NAND_ADDR1:
- return &regs->addr1;
- case NAND_FLASH_CHIP_SELECT:
- return &regs->chip_sel;
- case NAND_EXEC_CMD:
- return &regs->exec;
- case NAND_FLASH_STATUS:
- return &regs->clrflashstatus;
- case NAND_DEV0_CFG0:
- return &regs->cfg0;
- case NAND_DEV0_CFG1:
- return &regs->cfg1;
- case NAND_DEV0_ECC_CFG:
- return &regs->ecc_bch_cfg;
- case NAND_READ_STATUS:
- return &regs->clrreadstatus;
- case NAND_DEV_CMD1:
- return &regs->cmd1;
- case NAND_DEV_CMD1_RESTORE:
- return &regs->orig_cmd1;
- case NAND_DEV_CMD_VLD:
- return &regs->vld;
- case NAND_DEV_CMD_VLD_RESTORE:
- return &regs->orig_vld;
- case NAND_EBI2_ECC_BUF_CFG:
- return &regs->ecc_buf_cfg;
- case NAND_READ_LOCATION_0:
- return &regs->read_location0;
- case NAND_READ_LOCATION_1:
- return &regs->read_location1;
- case NAND_READ_LOCATION_2:
- return &regs->read_location2;
- case NAND_READ_LOCATION_3:
- return &regs->read_location3;
- case NAND_READ_LOCATION_LAST_CW_0:
- return &regs->read_location_last0;
- case NAND_READ_LOCATION_LAST_CW_1:
- return &regs->read_location_last1;
- case NAND_READ_LOCATION_LAST_CW_2:
- return &regs->read_location_last2;
- case NAND_READ_LOCATION_LAST_CW_3:
- return &regs->read_location_last3;
- default:
- return NULL;
- }
-}
-
-static void nandc_set_reg(struct nand_chip *chip, int offset,
- u32 val)
-{
- struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- struct nandc_regs *regs = nandc->regs;
- __le32 *reg;
-
- reg = offset_to_nandc_reg(regs, offset);
-
- if (reg)
- *reg = cpu_to_le32(val);
-}
-
/* Helper to check the code word, whether it is last cw or not */
static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw)
{
return cw == (ecc->steps - 1);
}
+/**
+ * nandc_set_read_loc_first() - to set read location first register
+ * @chip: NAND Private Flash Chip Data
+ * @reg_base: location register base
+ * @cw_offset: code word offset
+ * @read_size: code word read length
+ * @is_last_read_loc: is this the last read location
+ *
+ * This function will set location register value
+ */
+static void nandc_set_read_loc_first(struct nand_chip *chip,
+ int reg_base, u32 cw_offset,
+ u32 read_size, u32 is_last_read_loc)
+{
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ __le32 locreg_val;
+ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) |
+ ((read_size) << READ_LOCATION_SIZE) |
+ ((is_last_read_loc) << READ_LOCATION_LAST));
+
+ locreg_val = cpu_to_le32(val);
+
+ if (reg_base == NAND_READ_LOCATION_0)
+ nandc->regs->read_location0 = locreg_val;
+ else if (reg_base == NAND_READ_LOCATION_1)
+ nandc->regs->read_location1 = locreg_val;
+ else if (reg_base == NAND_READ_LOCATION_2)
+ nandc->regs->read_location2 = locreg_val;
+ else if (reg_base == NAND_READ_LOCATION_3)
+ nandc->regs->read_location3 = locreg_val;
+}
+
+/**
+ * nandc_set_read_loc_last - to set read location last register
+ * @chip: NAND Private Flash Chip Data
+ * @reg_base: location register base
+ * @cw_offset: code word offset
+ * @read_size: code word read length
+ * @is_last_read_loc: is this the last read location
+ *
+ * This function will set location last register value
+ */
+static void nandc_set_read_loc_last(struct nand_chip *chip,
+ int reg_base, u32 cw_offset,
+ u32 read_size, u32 is_last_read_loc)
+{
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
+ __le32 locreg_val;
+ u32 val = (((cw_offset) << READ_LOCATION_OFFSET) |
+ ((read_size) << READ_LOCATION_SIZE) |
+ ((is_last_read_loc) << READ_LOCATION_LAST));
+
+ locreg_val = cpu_to_le32(val);
+
+ if (reg_base == NAND_READ_LOCATION_LAST_CW_0)
+ nandc->regs->read_location_last0 = locreg_val;
+ else if (reg_base == NAND_READ_LOCATION_LAST_CW_1)
+ nandc->regs->read_location_last1 = locreg_val;
+ else if (reg_base == NAND_READ_LOCATION_LAST_CW_2)
+ nandc->regs->read_location_last2 = locreg_val;
+ else if (reg_base == NAND_READ_LOCATION_LAST_CW_3)
+ nandc->regs->read_location_last3 = locreg_val;
+}
+
/* helper to configure location register values */
static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg,
- int cw_offset, int read_size, int is_last_read_loc)
+ u32 cw_offset, u32 read_size, u32 is_last_read_loc)
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
int reg_base = NAND_READ_LOCATION_0;
- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
+ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
reg_base = NAND_READ_LOCATION_LAST_CW_0;
reg_base += reg * 4;
- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
+ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
return nandc_set_read_loc_last(chip, reg_base, cw_offset,
read_size, is_last_read_loc);
else
@@ -792,12 +756,13 @@ static void nandc_set_read_loc(struct na
static void set_address(struct qcom_nand_host *host, u16 column, int page)
{
struct nand_chip *chip = &host->chip;
+ struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
- nandc_set_reg(chip, NAND_ADDR0, page << 16 | column);
- nandc_set_reg(chip, NAND_ADDR1, page >> 16 & 0xff);
+ nandc->regs->addr0 = cpu_to_le32(page << 16 | column);
+ nandc->regs->addr1 = cpu_to_le32(page >> 16 & 0xff);
}
/*
@@ -811,41 +776,43 @@ static void set_address(struct qcom_nand
static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, int cw)
{
struct nand_chip *chip = &host->chip;
- u32 cmd, cfg0, cfg1, ecc_bch_cfg;
+ __le32 cmd, cfg0, cfg1, ecc_bch_cfg;
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
if (read) {
if (host->use_ecc)
- cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE;
+ cmd = cpu_to_le32(OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE);
else
- cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE;
+ cmd = cpu_to_le32(OP_PAGE_READ | PAGE_ACC | LAST_PAGE);
} else {
- cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE;
+ cmd = cpu_to_le32(OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE);
}
if (host->use_ecc) {
- cfg0 = (host->cfg0 & ~(7U << CW_PER_PAGE)) |
- (num_cw - 1) << CW_PER_PAGE;
+ cfg0 = cpu_to_le32((host->cfg0 & ~(7U << CW_PER_PAGE)) |
+ (num_cw - 1) << CW_PER_PAGE);
- cfg1 = host->cfg1;
- ecc_bch_cfg = host->ecc_bch_cfg;
+ cfg1 = cpu_to_le32(host->cfg1);
+ ecc_bch_cfg = cpu_to_le32(host->ecc_bch_cfg);
} else {
- cfg0 = (host->cfg0_raw & ~(7U << CW_PER_PAGE)) |
- (num_cw - 1) << CW_PER_PAGE;
+ cfg0 = cpu_to_le32((host->cfg0_raw & ~(7U << CW_PER_PAGE)) |
+ (num_cw - 1) << CW_PER_PAGE);
- cfg1 = host->cfg1_raw;
- ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE;
+ cfg1 = cpu_to_le32(host->cfg1_raw);
+ ecc_bch_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE);
}
- nandc_set_reg(chip, NAND_FLASH_CMD, cmd);
- nandc_set_reg(chip, NAND_DEV0_CFG0, cfg0);
- nandc_set_reg(chip, NAND_DEV0_CFG1, cfg1);
- nandc_set_reg(chip, NAND_DEV0_ECC_CFG, ecc_bch_cfg);
- if (!nandc->props->qpic_v2)
- nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg);
- nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus);
- nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus);
- nandc_set_reg(chip, NAND_EXEC_CMD, 1);
+ nandc->regs->cmd = cmd;
+ nandc->regs->cfg0 = cfg0;
+ nandc->regs->cfg1 = cfg1;
+ nandc->regs->ecc_bch_cfg = ecc_bch_cfg;
+
+ if (!nandc->props->qpic_version2)
+ nandc->regs->ecc_buf_cfg = cpu_to_le32(host->ecc_buf_cfg);
+
+ nandc->regs->clrflashstatus = cpu_to_le32(host->clrflashstatus);
+ nandc->regs->clrreadstatus = cpu_to_le32(host->clrreadstatus);
+ nandc->regs->exec = cpu_to_le32(1);
if (read)
nandc_set_read_loc(chip, cw, 0, 0, host->use_ecc ?
@@ -1121,7 +1088,7 @@ static int read_reg_dma(struct qcom_nand
if (first == NAND_DEV_CMD_VLD || first == NAND_DEV_CMD1)
first = dev_cmd_reg_addr(nandc, first);
- if (nandc->props->is_bam)
+ if (nandc->props->supports_bam)
return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
num_regs, flags);
@@ -1136,25 +1103,16 @@ static int read_reg_dma(struct qcom_nand
* write_reg_dma: prepares a descriptor to write a given number of
* contiguous registers
*
+ * @vaddr: contnigeous memory from where register value will
+ * be written
* @first: offset of the first register in the contiguous block
* @num_regs: number of registers to write
* @flags: flags to control DMA descriptor preparation
*/
-static int write_reg_dma(struct qcom_nand_controller *nandc, int first,
- int num_regs, unsigned int flags)
+static int write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr,
+ int first, int num_regs, unsigned int flags)
{
bool flow_control = false;
- struct nandc_regs *regs = nandc->regs;
- void *vaddr;
-
- vaddr = offset_to_nandc_reg(regs, first);
-
- if (first == NAND_ERASED_CW_DETECT_CFG) {
- if (flags & NAND_ERASED_CW_SET)
- vaddr = &regs->erased_cw_detect_cfg_set;
- else
- vaddr = &regs->erased_cw_detect_cfg_clr;
- }
if (first == NAND_EXEC_CMD)
flags |= NAND_BAM_NWD;
@@ -1165,7 +1123,7 @@ static int write_reg_dma(struct qcom_nan
if (first == NAND_DEV_CMD_VLD_RESTORE || first == NAND_DEV_CMD_VLD)
first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
- if (nandc->props->is_bam)
+ if (nandc->props->supports_bam)
return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
num_regs, flags);
@@ -1188,7 +1146,7 @@ static int write_reg_dma(struct qcom_nan
static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
const u8 *vaddr, int size, unsigned int flags)
{
- if (nandc->props->is_bam)
+ if (nandc->props->supports_bam)
return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
@@ -1206,7 +1164,7 @@ static int read_data_dma(struct qcom_nan
static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
const u8 *vaddr, int size, unsigned int flags)
{
- if (nandc->props->is_bam)
+ if (nandc->props->supports_bam)
return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
@@ -1220,13 +1178,14 @@ static void config_nand_page_read(struct
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- write_reg_dma(nandc, NAND_ADDR0, 2, 0);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
- if (!nandc->props->qpic_v2)
- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0);
- write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1, 0);
- write_reg_dma(nandc, NAND_ERASED_CW_DETECT_CFG, 1,
- NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
+ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
+ if (!nandc->props->qpic_version2)
+ write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
+ write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr,
+ NAND_ERASED_CW_DETECT_CFG, 1, 0);
+ write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set,
+ NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
}
/*
@@ -1239,16 +1198,16 @@ config_nand_cw_read(struct nand_chip *ch
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
- int reg = NAND_READ_LOCATION_0;
+ __le32 *reg = &nandc->regs->read_location0;
- if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw))
- reg = NAND_READ_LOCATION_LAST_CW_0;
+ if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
+ reg = &nandc->regs->read_location_last0;
- if (nandc->props->is_bam)
- write_reg_dma(nandc, reg, 4, NAND_BAM_NEXT_SGL);
+ if (nandc->props->supports_bam)
+ write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
if (use_ecc) {
read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
@@ -1279,10 +1238,10 @@ static void config_nand_page_write(struc
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- write_reg_dma(nandc, NAND_ADDR0, 2, 0);
- write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0);
- if (!nandc->props->qpic_v2)
- write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1,
+ write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
+ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
+ if (!nandc->props->qpic_version2)
+ write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1,
NAND_BAM_NEXT_SGL);
}
@@ -1294,13 +1253,13 @@ static void config_nand_cw_write(struct
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0);
- write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
+ write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
}
/* helpers to submit/free our list of dma descriptors */
@@ -1311,7 +1270,7 @@ static int submit_descs(struct qcom_nand
struct bam_transaction *bam_txn = nandc->bam_txn;
int ret = 0;
- if (nandc->props->is_bam) {
+ if (nandc->props->supports_bam) {
if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
if (ret)
@@ -1336,14 +1295,9 @@ static int submit_descs(struct qcom_nand
list_for_each_entry(desc, &nandc->desc_list, node)
cookie = dmaengine_submit(desc->dma_desc);
- if (nandc->props->is_bam) {
+ if (nandc->props->supports_bam) {
bam_txn->last_cmd_desc->callback = qpic_bam_dma_done;
bam_txn->last_cmd_desc->callback_param = bam_txn;
- if (bam_txn->last_data_desc) {
- bam_txn->last_data_desc->callback = qpic_bam_dma_done;
- bam_txn->last_data_desc->callback_param = bam_txn;
- bam_txn->wait_second_completion = true;
- }
dma_async_issue_pending(nandc->tx_chan);
dma_async_issue_pending(nandc->rx_chan);
@@ -1365,7 +1319,7 @@ err_unmap_free_desc:
list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
list_del(&desc->node);
- if (nandc->props->is_bam)
+ if (nandc->props->supports_bam)
dma_unmap_sg(nandc->dev, desc->bam_sgl,
desc->sgl_cnt, desc->dir);
else
@@ -1382,7 +1336,7 @@ err_unmap_free_desc:
static void clear_read_regs(struct qcom_nand_controller *nandc)
{
nandc->reg_read_pos = 0;
- nandc_read_buffer_sync(nandc, false);
+ nandc_dev_to_mem(nandc, false);
}
/*
@@ -1446,7 +1400,7 @@ static int check_flash_errors(struct qco
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
int i;
- nandc_read_buffer_sync(nandc, true);
+ nandc_dev_to_mem(nandc, true);
for (i = 0; i < cw_cnt; i++) {
u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
@@ -1476,7 +1430,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *
clear_read_regs(nandc);
host->use_ecc = false;
- if (nandc->props->qpic_v2)
+ if (nandc->props->qpic_version2)
raw_cw = ecc->steps - 1;
clear_bam_transaction(nandc);
@@ -1497,7 +1451,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *
oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
}
- if (nandc->props->is_bam) {
+ if (nandc->props->supports_bam) {
nandc_set_read_loc(chip, cw, 0, read_loc, data_size1, 0);
read_loc += data_size1;
@@ -1621,7 +1575,7 @@ static int parse_read_errors(struct qcom
u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
buf = (struct read_stats *)nandc->reg_read_buf;
- nandc_read_buffer_sync(nandc, true);
+ nandc_dev_to_mem(nandc, true);
for (i = 0; i < ecc->steps; i++, buf++) {
u32 flash, buffer, erased_cw;
@@ -1734,7 +1688,7 @@ static int read_page_ecc(struct qcom_nan
oob_size = host->ecc_bytes_hw + host->spare_bytes;
}
- if (nandc->props->is_bam) {
+ if (nandc->props->supports_bam) {
if (data_buf && oob_buf) {
nandc_set_read_loc(chip, i, 0, 0, data_size, 0);
nandc_set_read_loc(chip, i, 1, data_size,
@@ -2455,14 +2409,14 @@ static int qcom_nand_attach_chip(struct
mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
/* Free the initially allocated BAM transaction for reading the ONFI params */
- if (nandc->props->is_bam)
+ if (nandc->props->supports_bam)
free_bam_transaction(nandc);
nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
cwperpage);
/* Now allocate the BAM transaction based on updated max_cwperpage */
- if (nandc->props->is_bam) {
+ if (nandc->props->supports_bam) {
nandc->bam_txn = alloc_bam_transaction(nandc);
if (!nandc->bam_txn) {
dev_err(nandc->dev,
@@ -2522,7 +2476,7 @@ static int qcom_nand_attach_chip(struct
| ecc_mode << ECC_MODE
| host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH;
- if (!nandc->props->qpic_v2)
+ if (!nandc->props->qpic_version2)
host->ecc_buf_cfg = 0x203 << NUM_STEPS;
host->clrflashstatus = FS_READY_BSY_N;
@@ -2556,7 +2510,7 @@ static int qcom_op_cmd_mapping(struct na
cmd = OP_FETCH_ID;
break;
case NAND_CMD_PARAM:
- if (nandc->props->qpic_v2)
+ if (nandc->props->qpic_version2)
cmd = OP_PAGE_READ_ONFI_READ;
else
cmd = OP_PAGE_READ;
@@ -2609,7 +2563,7 @@ static int qcom_parse_instructions(struc
if (ret < 0)
return ret;
- q_op->cmd_reg = ret;
+ q_op->cmd_reg = cpu_to_le32(ret);
q_op->rdy_delay_ns = instr->delay_ns;
break;
@@ -2619,10 +2573,10 @@ static int qcom_parse_instructions(struc
addrs = &instr->ctx.addr.addrs[offset];
for (i = 0; i < min_t(unsigned int, 4, naddrs); i++)
- q_op->addr1_reg |= addrs[i] << (i * 8);
+ q_op->addr1_reg |= cpu_to_le32(addrs[i] << (i * 8));
if (naddrs > 4)
- q_op->addr2_reg |= addrs[4];
+ q_op->addr2_reg |= cpu_to_le32(addrs[4]);
q_op->rdy_delay_ns = instr->delay_ns;
break;
@@ -2663,7 +2617,7 @@ static int qcom_wait_rdy_poll(struct nan
unsigned long start = jiffies + msecs_to_jiffies(time_ms);
u32 flash;
- nandc_read_buffer_sync(nandc, true);
+ nandc_dev_to_mem(nandc, true);
do {
flash = le32_to_cpu(nandc->reg_read_buf[0]);
@@ -2706,11 +2660,11 @@ static int qcom_read_status_exec(struct
clear_read_regs(nandc);
clear_bam_transaction(nandc);
- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg);
- nandc_set_reg(chip, NAND_EXEC_CMD, 1);
+ nandc->regs->cmd = q_op.cmd_reg;
+ nandc->regs->exec = cpu_to_le32(1);
- write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
ret = submit_descs(nandc);
@@ -2719,7 +2673,7 @@ static int qcom_read_status_exec(struct
goto err_out;
}
- nandc_read_buffer_sync(nandc, true);
+ nandc_dev_to_mem(nandc, true);
for (i = 0; i < num_cw; i++) {
flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
@@ -2763,16 +2717,14 @@ static int qcom_read_id_type_exec(struct
clear_read_regs(nandc);
clear_bam_transaction(nandc);
- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg);
- nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg);
- nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg);
- nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT,
- nandc->props->is_bam ? 0 : DM_EN);
+ nandc->regs->cmd = q_op.cmd_reg;
+ nandc->regs->addr0 = q_op.addr1_reg;
+ nandc->regs->addr1 = q_op.addr2_reg;
+ nandc->regs->chip_sel = cpu_to_le32(nandc->props->supports_bam ? 0 : DM_EN);
+ nandc->regs->exec = cpu_to_le32(1);
- nandc_set_reg(chip, NAND_EXEC_CMD, 1);
-
- write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
@@ -2786,7 +2738,7 @@ static int qcom_read_id_type_exec(struct
op_id = q_op.data_instr_idx;
len = nand_subop_get_data_len(subop, op_id);
- nandc_read_buffer_sync(nandc, true);
+ nandc_dev_to_mem(nandc, true);
memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len);
err_out:
@@ -2807,15 +2759,14 @@ static int qcom_misc_cmd_type_exec(struc
if (q_op.flag == OP_PROGRAM_PAGE) {
goto wait_rdy;
- } else if (q_op.cmd_reg == OP_BLOCK_ERASE) {
- q_op.cmd_reg |= PAGE_ACC | LAST_PAGE;
- nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg);
- nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg);
- nandc_set_reg(chip, NAND_DEV0_CFG0,
- host->cfg0_raw & ~(7 << CW_PER_PAGE));
- nandc_set_reg(chip, NAND_DEV0_CFG1, host->cfg1_raw);
+ } else if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE)) {
+ q_op.cmd_reg |= cpu_to_le32(PAGE_ACC | LAST_PAGE);
+ nandc->regs->addr0 = q_op.addr1_reg;
+ nandc->regs->addr1 = q_op.addr2_reg;
+ nandc->regs->cfg0 = cpu_to_le32(host->cfg0_raw & ~(7 << CW_PER_PAGE));
+ nandc->regs->cfg1 = cpu_to_le32(host->cfg1_raw);
instrs = 3;
- } else if (q_op.cmd_reg != OP_RESET_DEVICE) {
+ } else if (q_op.cmd_reg != cpu_to_le32(OP_RESET_DEVICE)) {
return 0;
}
@@ -2826,14 +2777,14 @@ static int qcom_misc_cmd_type_exec(struc
clear_read_regs(nandc);
clear_bam_transaction(nandc);
- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg);
- nandc_set_reg(chip, NAND_EXEC_CMD, 1);
+ nandc->regs->cmd = q_op.cmd_reg;
+ nandc->regs->exec = cpu_to_le32(1);
- write_reg_dma(nandc, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL);
- if (q_op.cmd_reg == OP_BLOCK_ERASE)
- write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL);
+ if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE))
+ write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
ret = submit_descs(nandc);
@@ -2864,7 +2815,7 @@ static int qcom_param_page_type_exec(str
if (ret)
return ret;
- q_op.cmd_reg |= PAGE_ACC | LAST_PAGE;
+ q_op.cmd_reg |= cpu_to_le32(PAGE_ACC | LAST_PAGE);
nandc->buf_count = 0;
nandc->buf_start = 0;
@@ -2872,38 +2823,38 @@ static int qcom_param_page_type_exec(str
clear_read_regs(nandc);
clear_bam_transaction(nandc);
- nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg);
+ nandc->regs->cmd = q_op.cmd_reg;
+ nandc->regs->addr0 = 0;
+ nandc->regs->addr1 = 0;
+
+ nandc->regs->cfg0 = cpu_to_le32(0 << CW_PER_PAGE
+ | 512 << UD_SIZE_BYTES
+ | 5 << NUM_ADDR_CYCLES
+ | 0 << SPARE_SIZE_BYTES);
+
+ nandc->regs->cfg1 = cpu_to_le32(7 << NAND_RECOVERY_CYCLES
+ | 0 << CS_ACTIVE_BSY
+ | 17 << BAD_BLOCK_BYTE_NUM
+ | 1 << BAD_BLOCK_IN_SPARE_AREA
+ | 2 << WR_RD_BSY_GAP
+ | 0 << WIDE_FLASH
+ | 1 << DEV0_CFG1_ECC_DISABLE);
- nandc_set_reg(chip, NAND_ADDR0, 0);
- nandc_set_reg(chip, NAND_ADDR1, 0);
- nandc_set_reg(chip, NAND_DEV0_CFG0, 0 << CW_PER_PAGE
- | 512 << UD_SIZE_BYTES
- | 5 << NUM_ADDR_CYCLES
- | 0 << SPARE_SIZE_BYTES);
- nandc_set_reg(chip, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES
- | 0 << CS_ACTIVE_BSY
- | 17 << BAD_BLOCK_BYTE_NUM
- | 1 << BAD_BLOCK_IN_SPARE_AREA
- | 2 << WR_RD_BSY_GAP
- | 0 << WIDE_FLASH
- | 1 << DEV0_CFG1_ECC_DISABLE);
- if (!nandc->props->qpic_v2)
- nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE);
+ if (!nandc->props->qpic_version2)
+ nandc->regs->ecc_buf_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE);
/* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
- if (!nandc->props->qpic_v2) {
- nandc_set_reg(chip, NAND_DEV_CMD_VLD,
- (nandc->vld & ~READ_START_VLD));
- nandc_set_reg(chip, NAND_DEV_CMD1,
- (nandc->cmd1 & ~(0xFF << READ_ADDR))
- | NAND_CMD_PARAM << READ_ADDR);
+ if (!nandc->props->qpic_version2) {
+ nandc->regs->vld = cpu_to_le32((nandc->vld & ~READ_START_VLD));
+ nandc->regs->cmd1 = cpu_to_le32((nandc->cmd1 & ~(0xFF << READ_ADDR))
+ | NAND_CMD_PARAM << READ_ADDR);
}
- nandc_set_reg(chip, NAND_EXEC_CMD, 1);
-
- if (!nandc->props->qpic_v2) {
- nandc_set_reg(chip, NAND_DEV_CMD1_RESTORE, nandc->cmd1);
- nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld);
+ nandc->regs->exec = cpu_to_le32(1);
+
+ if (!nandc->props->qpic_version2) {
+ nandc->regs->orig_cmd1 = cpu_to_le32(nandc->cmd1);
+ nandc->regs->orig_vld = cpu_to_le32(nandc->vld);
}
instr = q_op.data_instr;
@@ -2912,9 +2863,9 @@ static int qcom_param_page_type_exec(str
nandc_set_read_loc(chip, 0, 0, 0, len, 1);
- if (!nandc->props->qpic_v2) {
- write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0);
- write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
+ if (!nandc->props->qpic_version2) {
+ write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);
+ write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
}
nandc->buf_count = len;
@@ -2926,9 +2877,10 @@ static int qcom_param_page_type_exec(str
nandc->buf_count, 0);
/* restore CMD1 and VLD regs */
- if (!nandc->props->qpic_v2) {
- write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0);
- write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL);
+ if (!nandc->props->qpic_version2) {
+ write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0);
+ write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1,
+ NAND_BAM_NEXT_SGL);
}
ret = submit_descs(nandc);
@@ -3017,7 +2969,7 @@ static const struct nand_controller_ops
static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc)
{
- if (nandc->props->is_bam) {
+ if (nandc->props->supports_bam) {
if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma))
dma_unmap_single(nandc->dev, nandc->reg_read_dma,
MAX_REG_RD *
@@ -3070,7 +3022,7 @@ static int qcom_nandc_alloc(struct qcom_
if (!nandc->reg_read_buf)
return -ENOMEM;
- if (nandc->props->is_bam) {
+ if (nandc->props->supports_bam) {
nandc->reg_read_dma =
dma_map_single(nandc->dev, nandc->reg_read_buf,
MAX_REG_RD *
@@ -3151,15 +3103,15 @@ static int qcom_nandc_setup(struct qcom_
u32 nand_ctrl;
/* kill onenand */
- if (!nandc->props->is_qpic)
+ if (!nandc->props->nandc_part_of_qpic)
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
- if (!nandc->props->qpic_v2)
+ if (!nandc->props->qpic_version2)
nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
NAND_DEV_CMD_VLD_VAL);
/* enable ADM or BAM DMA */
- if (nandc->props->is_bam) {
+ if (nandc->props->supports_bam) {
nand_ctrl = nandc_read(nandc, NAND_CTRL);
/*
@@ -3176,7 +3128,7 @@ static int qcom_nandc_setup(struct qcom_
}
/* save the original values of these registers */
- if (!nandc->props->qpic_v2) {
+ if (!nandc->props->qpic_version2) {
nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
nandc->vld = NAND_DEV_CMD_VLD_VAL;
}
@@ -3349,7 +3301,7 @@ static int qcom_nandc_parse_dt(struct pl
struct device_node *np = nandc->dev->of_node;
int ret;
- if (!nandc->props->is_bam) {
+ if (!nandc->props->supports_bam) {
ret = of_property_read_u32(np, "qcom,cmd-crci",
&nandc->cmd_crci);
if (ret) {
@@ -3474,30 +3426,30 @@ static void qcom_nandc_remove(struct pla
static const struct qcom_nandc_props ipq806x_nandc_props = {
.ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
- .is_bam = false,
+ .supports_bam = false,
.use_codeword_fixup = true,
.dev_cmd_reg_start = 0x0,
};
static const struct qcom_nandc_props ipq4019_nandc_props = {
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
- .is_bam = true,
- .is_qpic = true,
+ .supports_bam = true,
+ .nandc_part_of_qpic = true,
.dev_cmd_reg_start = 0x0,
};
static const struct qcom_nandc_props ipq8074_nandc_props = {
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
- .is_bam = true,
- .is_qpic = true,
+ .supports_bam = true,
+ .nandc_part_of_qpic = true,
.dev_cmd_reg_start = 0x7000,
};
static const struct qcom_nandc_props sdx55_nandc_props = {
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
- .is_bam = true,
- .is_qpic = true,
- .qpic_v2 = true,
+ .supports_bam = true,
+ .nandc_part_of_qpic = true,
+ .qpic_version2 = true,
.dev_cmd_reg_start = 0x7000,
};

View File

@ -0,0 +1,874 @@
From: Md Sadre Alam <quic_mdalam@quicinc.com>
Date: Sun, 22 Sep 2024 17:03:46 +0530
Subject: [PATCH] mtd: rawnand: qcom: Add qcom prefix to common api
Add qcom prefix to all the api which will be commonly
used by spi nand driver and raw nand driver.
Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
---
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -53,7 +53,7 @@
#define NAND_READ_LOCATION_LAST_CW_2 0xf48
#define NAND_READ_LOCATION_LAST_CW_3 0xf4c
-/* dummy register offsets, used by write_reg_dma */
+/* dummy register offsets, used by qcom_write_reg_dma */
#define NAND_DEV_CMD1_RESTORE 0xdead
#define NAND_DEV_CMD_VLD_RESTORE 0xbeef
@@ -211,7 +211,7 @@
/*
* Flags used in DMA descriptor preparation helper functions
- * (i.e. read_reg_dma/write_reg_dma/read_data_dma/write_data_dma)
+ * (i.e. qcom_read_reg_dma/qcom_write_reg_dma/qcom_read_data_dma/qcom_write_data_dma)
*/
/* Don't set the EOT in current tx BAM sgl */
#define NAND_BAM_NO_EOT BIT(0)
@@ -550,7 +550,7 @@ struct qcom_nandc_props {
};
/* Frees the BAM transaction memory */
-static void free_bam_transaction(struct qcom_nand_controller *nandc)
+static void qcom_free_bam_transaction(struct qcom_nand_controller *nandc)
{
struct bam_transaction *bam_txn = nandc->bam_txn;
@@ -559,7 +559,7 @@ static void free_bam_transaction(struct
/* Allocates and Initializes the BAM transaction */
static struct bam_transaction *
-alloc_bam_transaction(struct qcom_nand_controller *nandc)
+qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc)
{
struct bam_transaction *bam_txn;
size_t bam_txn_size;
@@ -595,7 +595,7 @@ alloc_bam_transaction(struct qcom_nand_c
}
/* Clears the BAM transaction indexes */
-static void clear_bam_transaction(struct qcom_nand_controller *nandc)
+static void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc)
{
struct bam_transaction *bam_txn = nandc->bam_txn;
@@ -614,7 +614,7 @@ static void clear_bam_transaction(struct
}
/* Callback for DMA descriptor completion */
-static void qpic_bam_dma_done(void *data)
+static void qcom_qpic_bam_dma_done(void *data)
{
struct bam_transaction *bam_txn = data;
@@ -644,7 +644,7 @@ static inline void nandc_write(struct qc
iowrite32(val, nandc->base + offset);
}
-static inline void nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu)
+static inline void qcom_nandc_dev_to_mem(struct qcom_nand_controller *nandc, bool is_cpu)
{
if (!nandc->props->supports_bam)
return;
@@ -824,9 +824,9 @@ static void update_rw_regs(struct qcom_n
* for BAM. This descriptor will be added in the NAND DMA descriptor queue
* which will be submitted to DMA engine.
*/
-static int prepare_bam_async_desc(struct qcom_nand_controller *nandc,
- struct dma_chan *chan,
- unsigned long flags)
+static int qcom_prepare_bam_async_desc(struct qcom_nand_controller *nandc,
+ struct dma_chan *chan,
+ unsigned long flags)
{
struct desc_info *desc;
struct scatterlist *sgl;
@@ -903,9 +903,9 @@ static int prepare_bam_async_desc(struct
* NAND_BAM_NEXT_SGL will be used for starting the separate SGL
* after the current command element.
*/
-static int prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
- int reg_off, const void *vaddr,
- int size, unsigned int flags)
+static int qcom_prep_bam_dma_desc_cmd(struct qcom_nand_controller *nandc, bool read,
+ int reg_off, const void *vaddr,
+ int size, unsigned int flags)
{
int bam_ce_size;
int i, ret;
@@ -943,9 +943,9 @@ static int prep_bam_dma_desc_cmd(struct
bam_txn->bam_ce_start = bam_txn->bam_ce_pos;
if (flags & NAND_BAM_NWD) {
- ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
- DMA_PREP_FENCE |
- DMA_PREP_CMD);
+ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan,
+ DMA_PREP_FENCE |
+ DMA_PREP_CMD);
if (ret)
return ret;
}
@@ -958,9 +958,8 @@ static int prep_bam_dma_desc_cmd(struct
* Prepares the data descriptor for BAM DMA which will be used for NAND
* data reads and writes.
*/
-static int prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
- const void *vaddr,
- int size, unsigned int flags)
+static int qcom_prep_bam_dma_desc_data(struct qcom_nand_controller *nandc, bool read,
+ const void *vaddr, int size, unsigned int flags)
{
int ret;
struct bam_transaction *bam_txn = nandc->bam_txn;
@@ -979,8 +978,8 @@ static int prep_bam_dma_desc_data(struct
* is not set, form the DMA descriptor
*/
if (!(flags & NAND_BAM_NO_EOT)) {
- ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
- DMA_PREP_INTERRUPT);
+ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan,
+ DMA_PREP_INTERRUPT);
if (ret)
return ret;
}
@@ -989,9 +988,9 @@ static int prep_bam_dma_desc_data(struct
return 0;
}
-static int prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
- int reg_off, const void *vaddr, int size,
- bool flow_control)
+static int qcom_prep_adm_dma_desc(struct qcom_nand_controller *nandc, bool read,
+ int reg_off, const void *vaddr, int size,
+ bool flow_control)
{
struct desc_info *desc;
struct dma_async_tx_descriptor *dma_desc;
@@ -1069,15 +1068,15 @@ err:
}
/*
- * read_reg_dma: prepares a descriptor to read a given number of
+ * qcom_read_reg_dma: prepares a descriptor to read a given number of
* contiguous registers to the reg_read_buf pointer
*
* @first: offset of the first register in the contiguous block
* @num_regs: number of registers to read
* @flags: flags to control DMA descriptor preparation
*/
-static int read_reg_dma(struct qcom_nand_controller *nandc, int first,
- int num_regs, unsigned int flags)
+static int qcom_read_reg_dma(struct qcom_nand_controller *nandc, int first,
+ int num_regs, unsigned int flags)
{
bool flow_control = false;
void *vaddr;
@@ -1089,18 +1088,18 @@ static int read_reg_dma(struct qcom_nand
first = dev_cmd_reg_addr(nandc, first);
if (nandc->props->supports_bam)
- return prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
+ return qcom_prep_bam_dma_desc_cmd(nandc, true, first, vaddr,
num_regs, flags);
if (first == NAND_READ_ID || first == NAND_FLASH_STATUS)
flow_control = true;
- return prep_adm_dma_desc(nandc, true, first, vaddr,
+ return qcom_prep_adm_dma_desc(nandc, true, first, vaddr,
num_regs * sizeof(u32), flow_control);
}
/*
- * write_reg_dma: prepares a descriptor to write a given number of
+ * qcom_write_reg_dma: prepares a descriptor to write a given number of
* contiguous registers
*
* @vaddr: contnigeous memory from where register value will
@@ -1109,8 +1108,8 @@ static int read_reg_dma(struct qcom_nand
* @num_regs: number of registers to write
* @flags: flags to control DMA descriptor preparation
*/
-static int write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr,
- int first, int num_regs, unsigned int flags)
+static int qcom_write_reg_dma(struct qcom_nand_controller *nandc, __le32 *vaddr,
+ int first, int num_regs, unsigned int flags)
{
bool flow_control = false;
@@ -1124,18 +1123,18 @@ static int write_reg_dma(struct qcom_nan
first = dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD);
if (nandc->props->supports_bam)
- return prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
+ return qcom_prep_bam_dma_desc_cmd(nandc, false, first, vaddr,
num_regs, flags);
if (first == NAND_FLASH_CMD)
flow_control = true;
- return prep_adm_dma_desc(nandc, false, first, vaddr,
+ return qcom_prep_adm_dma_desc(nandc, false, first, vaddr,
num_regs * sizeof(u32), flow_control);
}
/*
- * read_data_dma: prepares a DMA descriptor to transfer data from the
+ * qcom_read_data_dma: prepares a DMA descriptor to transfer data from the
* controller's internal buffer to the buffer 'vaddr'
*
* @reg_off: offset within the controller's data buffer
@@ -1143,17 +1142,17 @@ static int write_reg_dma(struct qcom_nan
* @size: DMA transaction size in bytes
* @flags: flags to control DMA descriptor preparation
*/
-static int read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
- const u8 *vaddr, int size, unsigned int flags)
+static int qcom_read_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+ const u8 *vaddr, int size, unsigned int flags)
{
if (nandc->props->supports_bam)
- return prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
+ return qcom_prep_bam_dma_desc_data(nandc, true, vaddr, size, flags);
- return prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
+ return qcom_prep_adm_dma_desc(nandc, true, reg_off, vaddr, size, false);
}
/*
- * write_data_dma: prepares a DMA descriptor to transfer data from
+ * qcom_write_data_dma: prepares a DMA descriptor to transfer data from
* 'vaddr' to the controller's internal buffer
*
* @reg_off: offset within the controller's data buffer
@@ -1161,13 +1160,13 @@ static int read_data_dma(struct qcom_nan
* @size: DMA transaction size in bytes
* @flags: flags to control DMA descriptor preparation
*/
-static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
- const u8 *vaddr, int size, unsigned int flags)
+static int qcom_write_data_dma(struct qcom_nand_controller *nandc, int reg_off,
+ const u8 *vaddr, int size, unsigned int flags)
{
if (nandc->props->supports_bam)
- return prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
+ return qcom_prep_bam_dma_desc_data(nandc, false, vaddr, size, flags);
- return prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
+ return qcom_prep_adm_dma_desc(nandc, false, reg_off, vaddr, size, false);
}
/*
@@ -1178,14 +1177,14 @@ static void config_nand_page_read(struct
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
if (!nandc->props->qpic_version2)
- write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
- write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr,
- NAND_ERASED_CW_DETECT_CFG, 1, 0);
- write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set,
- NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr,
+ NAND_ERASED_CW_DETECT_CFG, 1, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set,
+ NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
}
/*
@@ -1204,17 +1203,17 @@ config_nand_cw_read(struct nand_chip *ch
reg = &nandc->regs->read_location_last0;
if (nandc->props->supports_bam)
- write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
if (use_ecc) {
- read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
- read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
- NAND_BAM_NEXT_SGL);
+ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 2, 0);
+ qcom_read_reg_dma(nandc, NAND_ERASED_CW_DETECT_STATUS, 1,
+ NAND_BAM_NEXT_SGL);
} else {
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
}
}
@@ -1238,11 +1237,11 @@ static void config_nand_page_write(struc
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
if (!nandc->props->qpic_version2)
- write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1,
- NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1,
+ NAND_BAM_NEXT_SGL);
}
/*
@@ -1253,17 +1252,18 @@ static void config_nand_cw_write(struct
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
- write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1,
+ NAND_BAM_NEXT_SGL);
}
/* helpers to submit/free our list of dma descriptors */
-static int submit_descs(struct qcom_nand_controller *nandc)
+static int qcom_submit_descs(struct qcom_nand_controller *nandc)
{
struct desc_info *desc, *n;
dma_cookie_t cookie = 0;
@@ -1272,21 +1272,21 @@ static int submit_descs(struct qcom_nand
if (nandc->props->supports_bam) {
if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) {
- ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
+ ret = qcom_prepare_bam_async_desc(nandc, nandc->rx_chan, 0);
if (ret)
goto err_unmap_free_desc;
}
if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) {
- ret = prepare_bam_async_desc(nandc, nandc->tx_chan,
- DMA_PREP_INTERRUPT);
+ ret = qcom_prepare_bam_async_desc(nandc, nandc->tx_chan,
+ DMA_PREP_INTERRUPT);
if (ret)
goto err_unmap_free_desc;
}
if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) {
- ret = prepare_bam_async_desc(nandc, nandc->cmd_chan,
- DMA_PREP_CMD);
+ ret = qcom_prepare_bam_async_desc(nandc, nandc->cmd_chan,
+ DMA_PREP_CMD);
if (ret)
goto err_unmap_free_desc;
}
@@ -1296,7 +1296,7 @@ static int submit_descs(struct qcom_nand
cookie = dmaengine_submit(desc->dma_desc);
if (nandc->props->supports_bam) {
- bam_txn->last_cmd_desc->callback = qpic_bam_dma_done;
+ bam_txn->last_cmd_desc->callback = qcom_qpic_bam_dma_done;
bam_txn->last_cmd_desc->callback_param = bam_txn;
dma_async_issue_pending(nandc->tx_chan);
@@ -1314,7 +1314,7 @@ static int submit_descs(struct qcom_nand
err_unmap_free_desc:
/*
* Unmap the dma sg_list and free the desc allocated by both
- * prepare_bam_async_desc() and prep_adm_dma_desc() functions.
+ * qcom_prepare_bam_async_desc() and qcom_prep_adm_dma_desc() functions.
*/
list_for_each_entry_safe(desc, n, &nandc->desc_list, node) {
list_del(&desc->node);
@@ -1333,10 +1333,10 @@ err_unmap_free_desc:
}
/* reset the register read buffer for next NAND operation */
-static void clear_read_regs(struct qcom_nand_controller *nandc)
+static void qcom_clear_read_regs(struct qcom_nand_controller *nandc)
{
nandc->reg_read_pos = 0;
- nandc_dev_to_mem(nandc, false);
+ qcom_nandc_dev_to_mem(nandc, false);
}
/*
@@ -1400,7 +1400,7 @@ static int check_flash_errors(struct qco
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
int i;
- nandc_dev_to_mem(nandc, true);
+ qcom_nandc_dev_to_mem(nandc, true);
for (i = 0; i < cw_cnt; i++) {
u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
@@ -1427,13 +1427,13 @@ qcom_nandc_read_cw_raw(struct mtd_info *
nand_read_page_op(chip, page, 0, NULL, 0);
nandc->buf_count = 0;
nandc->buf_start = 0;
- clear_read_regs(nandc);
+ qcom_clear_read_regs(nandc);
host->use_ecc = false;
if (nandc->props->qpic_version2)
raw_cw = ecc->steps - 1;
- clear_bam_transaction(nandc);
+ qcom_clear_bam_transaction(nandc);
set_address(host, host->cw_size * cw, page);
update_rw_regs(host, 1, true, raw_cw);
config_nand_page_read(chip);
@@ -1466,18 +1466,18 @@ qcom_nandc_read_cw_raw(struct mtd_info *
config_nand_cw_read(chip, false, raw_cw);
- read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
+ qcom_read_data_dma(nandc, reg_off, data_buf, data_size1, 0);
reg_off += data_size1;
- read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
+ qcom_read_data_dma(nandc, reg_off, oob_buf, oob_size1, 0);
reg_off += oob_size1;
- read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0);
+ qcom_read_data_dma(nandc, reg_off, data_buf + data_size1, data_size2, 0);
reg_off += data_size2;
- read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0);
+ qcom_read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0);
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure to read raw cw %d\n", cw);
return ret;
@@ -1575,7 +1575,7 @@ static int parse_read_errors(struct qcom
u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
buf = (struct read_stats *)nandc->reg_read_buf;
- nandc_dev_to_mem(nandc, true);
+ qcom_nandc_dev_to_mem(nandc, true);
for (i = 0; i < ecc->steps; i++, buf++) {
u32 flash, buffer, erased_cw;
@@ -1704,8 +1704,8 @@ static int read_page_ecc(struct qcom_nan
config_nand_cw_read(chip, true, i);
if (data_buf)
- read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
- data_size, 0);
+ qcom_read_data_dma(nandc, FLASH_BUF_ACC, data_buf,
+ data_size, 0);
/*
* when ecc is enabled, the controller doesn't read the real
@@ -1720,8 +1720,8 @@ static int read_page_ecc(struct qcom_nan
for (j = 0; j < host->bbm_size; j++)
*oob_buf++ = 0xff;
- read_data_dma(nandc, FLASH_BUF_ACC + data_size,
- oob_buf, oob_size, 0);
+ qcom_read_data_dma(nandc, FLASH_BUF_ACC + data_size,
+ oob_buf, oob_size, 0);
}
if (data_buf)
@@ -1730,7 +1730,7 @@ static int read_page_ecc(struct qcom_nan
oob_buf += oob_size;
}
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure to read page/oob\n");
return ret;
@@ -1751,7 +1751,7 @@ static int copy_last_cw(struct qcom_nand
int size;
int ret;
- clear_read_regs(nandc);
+ qcom_clear_read_regs(nandc);
size = host->use_ecc ? host->cw_data : host->cw_size;
@@ -1763,9 +1763,9 @@ static int copy_last_cw(struct qcom_nand
config_nand_single_cw_page_read(chip, host->use_ecc, ecc->steps - 1);
- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
+ qcom_read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0);
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret)
dev_err(nandc->dev, "failed to copy last codeword\n");
@@ -1851,14 +1851,14 @@ static int qcom_nandc_read_page(struct n
nandc->buf_count = 0;
nandc->buf_start = 0;
host->use_ecc = true;
- clear_read_regs(nandc);
+ qcom_clear_read_regs(nandc);
set_address(host, 0, page);
update_rw_regs(host, ecc->steps, true, 0);
data_buf = buf;
oob_buf = oob_required ? chip->oob_poi : NULL;
- clear_bam_transaction(nandc);
+ qcom_clear_bam_transaction(nandc);
return read_page_ecc(host, data_buf, oob_buf, page);
}
@@ -1899,8 +1899,8 @@ static int qcom_nandc_read_oob(struct na
if (host->nr_boot_partitions)
qcom_nandc_codeword_fixup(host, page);
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
+ qcom_clear_read_regs(nandc);
+ qcom_clear_bam_transaction(nandc);
host->use_ecc = true;
set_address(host, 0, page);
@@ -1927,8 +1927,8 @@ static int qcom_nandc_write_page(struct
set_address(host, 0, page);
nandc->buf_count = 0;
nandc->buf_start = 0;
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
+ qcom_clear_read_regs(nandc);
+ qcom_clear_bam_transaction(nandc);
data_buf = (u8 *)buf;
oob_buf = chip->oob_poi;
@@ -1949,8 +1949,8 @@ static int qcom_nandc_write_page(struct
oob_size = ecc->bytes;
}
- write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
- i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);
+ qcom_write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size,
+ i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0);
/*
* when ECC is enabled, we don't really need to write anything
@@ -1962,8 +1962,8 @@ static int qcom_nandc_write_page(struct
if (qcom_nandc_is_last_cw(ecc, i)) {
oob_buf += host->bbm_size;
- write_data_dma(nandc, FLASH_BUF_ACC + data_size,
- oob_buf, oob_size, 0);
+ qcom_write_data_dma(nandc, FLASH_BUF_ACC + data_size,
+ oob_buf, oob_size, 0);
}
config_nand_cw_write(chip);
@@ -1972,7 +1972,7 @@ static int qcom_nandc_write_page(struct
oob_buf += oob_size;
}
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure to write page\n");
return ret;
@@ -1997,8 +1997,8 @@ static int qcom_nandc_write_page_raw(str
qcom_nandc_codeword_fixup(host, page);
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
+ qcom_clear_read_regs(nandc);
+ qcom_clear_bam_transaction(nandc);
data_buf = (u8 *)buf;
oob_buf = chip->oob_poi;
@@ -2024,28 +2024,28 @@ static int qcom_nandc_write_page_raw(str
oob_size2 = host->ecc_bytes_hw + host->spare_bytes;
}
- write_data_dma(nandc, reg_off, data_buf, data_size1,
- NAND_BAM_NO_EOT);
+ qcom_write_data_dma(nandc, reg_off, data_buf, data_size1,
+ NAND_BAM_NO_EOT);
reg_off += data_size1;
data_buf += data_size1;
- write_data_dma(nandc, reg_off, oob_buf, oob_size1,
- NAND_BAM_NO_EOT);
+ qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size1,
+ NAND_BAM_NO_EOT);
reg_off += oob_size1;
oob_buf += oob_size1;
- write_data_dma(nandc, reg_off, data_buf, data_size2,
- NAND_BAM_NO_EOT);
+ qcom_write_data_dma(nandc, reg_off, data_buf, data_size2,
+ NAND_BAM_NO_EOT);
reg_off += data_size2;
data_buf += data_size2;
- write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
+ qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
oob_buf += oob_size2;
config_nand_cw_write(chip);
}
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure to write raw page\n");
return ret;
@@ -2075,7 +2075,7 @@ static int qcom_nandc_write_oob(struct n
qcom_nandc_codeword_fixup(host, page);
host->use_ecc = true;
- clear_bam_transaction(nandc);
+ qcom_clear_bam_transaction(nandc);
/* calculate the data and oob size for the last codeword/step */
data_size = ecc->size - ((ecc->steps - 1) << 2);
@@ -2090,11 +2090,11 @@ static int qcom_nandc_write_oob(struct n
update_rw_regs(host, 1, false, 0);
config_nand_page_write(chip);
- write_data_dma(nandc, FLASH_BUF_ACC,
- nandc->data_buffer, data_size + oob_size, 0);
+ qcom_write_data_dma(nandc, FLASH_BUF_ACC,
+ nandc->data_buffer, data_size + oob_size, 0);
config_nand_cw_write(chip);
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure to write oob\n");
return ret;
@@ -2121,7 +2121,7 @@ static int qcom_nandc_block_bad(struct n
*/
host->use_ecc = false;
- clear_bam_transaction(nandc);
+ qcom_clear_bam_transaction(nandc);
ret = copy_last_cw(host, page);
if (ret)
goto err;
@@ -2148,8 +2148,8 @@ static int qcom_nandc_block_markbad(stru
struct nand_ecc_ctrl *ecc = &chip->ecc;
int page, ret;
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
+ qcom_clear_read_regs(nandc);
+ qcom_clear_bam_transaction(nandc);
/*
* to mark the BBM as bad, we flash the entire last codeword with 0s.
@@ -2166,11 +2166,11 @@ static int qcom_nandc_block_markbad(stru
update_rw_regs(host, 1, false, ecc->steps - 1);
config_nand_page_write(chip);
- write_data_dma(nandc, FLASH_BUF_ACC,
- nandc->data_buffer, host->cw_size, 0);
+ qcom_write_data_dma(nandc, FLASH_BUF_ACC,
+ nandc->data_buffer, host->cw_size, 0);
config_nand_cw_write(chip);
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure to update BBM\n");
return ret;
@@ -2410,14 +2410,14 @@ static int qcom_nand_attach_chip(struct
mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops);
/* Free the initially allocated BAM transaction for reading the ONFI params */
if (nandc->props->supports_bam)
- free_bam_transaction(nandc);
+ qcom_free_bam_transaction(nandc);
nandc->max_cwperpage = max_t(unsigned int, nandc->max_cwperpage,
cwperpage);
/* Now allocate the BAM transaction based on updated max_cwperpage */
if (nandc->props->supports_bam) {
- nandc->bam_txn = alloc_bam_transaction(nandc);
+ nandc->bam_txn = qcom_alloc_bam_transaction(nandc);
if (!nandc->bam_txn) {
dev_err(nandc->dev,
"failed to allocate bam transaction\n");
@@ -2617,7 +2617,7 @@ static int qcom_wait_rdy_poll(struct nan
unsigned long start = jiffies + msecs_to_jiffies(time_ms);
u32 flash;
- nandc_dev_to_mem(nandc, true);
+ qcom_nandc_dev_to_mem(nandc, true);
do {
flash = le32_to_cpu(nandc->reg_read_buf[0]);
@@ -2657,23 +2657,23 @@ static int qcom_read_status_exec(struct
nandc->buf_start = 0;
host->use_ecc = false;
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
+ qcom_clear_read_regs(nandc);
+ qcom_clear_bam_transaction(nandc);
nandc->regs->cmd = q_op.cmd_reg;
nandc->regs->exec = cpu_to_le32(1);
- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure in submitting status descriptor\n");
goto err_out;
}
- nandc_dev_to_mem(nandc, true);
+ qcom_nandc_dev_to_mem(nandc, true);
for (i = 0; i < num_cw; i++) {
flash_status = le32_to_cpu(nandc->reg_read_buf[i]);
@@ -2714,8 +2714,8 @@ static int qcom_read_id_type_exec(struct
nandc->buf_start = 0;
host->use_ecc = false;
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
+ qcom_clear_read_regs(nandc);
+ qcom_clear_bam_transaction(nandc);
nandc->regs->cmd = q_op.cmd_reg;
nandc->regs->addr0 = q_op.addr1_reg;
@@ -2723,12 +2723,12 @@ static int qcom_read_id_type_exec(struct
nandc->regs->chip_sel = cpu_to_le32(nandc->props->supports_bam ? 0 : DM_EN);
nandc->regs->exec = cpu_to_le32(1);
- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
+ qcom_read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure in submitting read id descriptor\n");
goto err_out;
@@ -2738,7 +2738,7 @@ static int qcom_read_id_type_exec(struct
op_id = q_op.data_instr_idx;
len = nand_subop_get_data_len(subop, op_id);
- nandc_dev_to_mem(nandc, true);
+ qcom_nandc_dev_to_mem(nandc, true);
memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len);
err_out:
@@ -2774,20 +2774,20 @@ static int qcom_misc_cmd_type_exec(struc
nandc->buf_start = 0;
host->use_ecc = false;
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
+ qcom_clear_read_regs(nandc);
+ qcom_clear_bam_transaction(nandc);
nandc->regs->cmd = q_op.cmd_reg;
nandc->regs->exec = cpu_to_le32(1);
- write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL);
if (q_op.cmd_reg == cpu_to_le32(OP_BLOCK_ERASE))
- write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL);
- write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
- read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
+ qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure in submitting misc descriptor\n");
goto err_out;
@@ -2820,8 +2820,8 @@ static int qcom_param_page_type_exec(str
nandc->buf_count = 0;
nandc->buf_start = 0;
host->use_ecc = false;
- clear_read_regs(nandc);
- clear_bam_transaction(nandc);
+ qcom_clear_read_regs(nandc);
+ qcom_clear_bam_transaction(nandc);
nandc->regs->cmd = q_op.cmd_reg;
nandc->regs->addr0 = 0;
@@ -2864,8 +2864,8 @@ static int qcom_param_page_type_exec(str
nandc_set_read_loc(chip, 0, 0, 0, len, 1);
if (!nandc->props->qpic_version2) {
- write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);
- write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
}
nandc->buf_count = len;
@@ -2873,17 +2873,17 @@ static int qcom_param_page_type_exec(str
config_nand_single_cw_page_read(chip, false, 0);
- read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
- nandc->buf_count, 0);
+ qcom_read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer,
+ nandc->buf_count, 0);
/* restore CMD1 and VLD regs */
if (!nandc->props->qpic_version2) {
- write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0);
- write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1,
- NAND_BAM_NEXT_SGL);
+ qcom_write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0);
+ qcom_write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1,
+ NAND_BAM_NEXT_SGL);
}
- ret = submit_descs(nandc);
+ ret = qcom_submit_descs(nandc);
if (ret) {
dev_err(nandc->dev, "failure in submitting param page descriptor\n");
goto err_out;
@@ -3067,7 +3067,7 @@ static int qcom_nandc_alloc(struct qcom_
* maximum codeword size
*/
nandc->max_cwperpage = 1;
- nandc->bam_txn = alloc_bam_transaction(nandc);
+ nandc->bam_txn = qcom_alloc_bam_transaction(nandc);
if (!nandc->bam_txn) {
dev_err(nandc->dev,
"failed to allocate bam transaction\n");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,191 @@
From: Md Sadre Alam <quic_mdalam@quicinc.com>
Date: Sun, 22 Sep 2024 17:03:48 +0530
Subject: [PATCH] mtd: rawnand: qcom: use FIELD_PREP and GENMASK
Use the bitfield macro FIELD_PREP, and GENMASK to
do the shift and mask in one go. This makes the code
more readable.
Signed-off-by: Md Sadre Alam <quic_mdalam@quicinc.com>
---
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -281,7 +281,7 @@ static void update_rw_regs(struct qcom_n
(num_cw - 1) << CW_PER_PAGE);
cfg1 = cpu_to_le32(host->cfg1_raw);
- ecc_bch_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE);
+ ecc_bch_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE);
}
nandc->regs->cmd = cmd;
@@ -1494,42 +1494,41 @@ static int qcom_nand_attach_chip(struct
host->cw_size = host->cw_data + ecc->bytes;
bad_block_byte = mtd->writesize - host->cw_size * (cwperpage - 1) + 1;
- host->cfg0 = (cwperpage - 1) << CW_PER_PAGE
- | host->cw_data << UD_SIZE_BYTES
- | 0 << DISABLE_STATUS_AFTER_WRITE
- | 5 << NUM_ADDR_CYCLES
- | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_RS
- | 0 << STATUS_BFR_READ
- | 1 << SET_RD_MODE_AFTER_STATUS
- | host->spare_bytes << SPARE_SIZE_BYTES;
-
- host->cfg1 = 7 << NAND_RECOVERY_CYCLES
- | 0 << CS_ACTIVE_BSY
- | bad_block_byte << BAD_BLOCK_BYTE_NUM
- | 0 << BAD_BLOCK_IN_SPARE_AREA
- | 2 << WR_RD_BSY_GAP
- | wide_bus << WIDE_FLASH
- | host->bch_enabled << ENABLE_BCH_ECC;
-
- host->cfg0_raw = (cwperpage - 1) << CW_PER_PAGE
- | host->cw_size << UD_SIZE_BYTES
- | 5 << NUM_ADDR_CYCLES
- | 0 << SPARE_SIZE_BYTES;
-
- host->cfg1_raw = 7 << NAND_RECOVERY_CYCLES
- | 0 << CS_ACTIVE_BSY
- | 17 << BAD_BLOCK_BYTE_NUM
- | 1 << BAD_BLOCK_IN_SPARE_AREA
- | 2 << WR_RD_BSY_GAP
- | wide_bus << WIDE_FLASH
- | 1 << DEV0_CFG1_ECC_DISABLE;
-
- host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
- | 0 << ECC_SW_RESET
- | host->cw_data << ECC_NUM_DATA_BYTES
- | 1 << ECC_FORCE_CLK_OPEN
- | ecc_mode << ECC_MODE
- | host->ecc_bytes_hw << ECC_PARITY_SIZE_BYTES_BCH;
+ host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) |
+ FIELD_PREP(UD_SIZE_BYTES_MASK, host->cw_data) |
+ FIELD_PREP(DISABLE_STATUS_AFTER_WRITE, 0) |
+ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) |
+ FIELD_PREP(ECC_PARITY_SIZE_BYTES_RS, host->ecc_bytes_hw) |
+ FIELD_PREP(STATUS_BFR_READ, 0) |
+ FIELD_PREP(SET_RD_MODE_AFTER_STATUS, 1) |
+ FIELD_PREP(SPARE_SIZE_BYTES_MASK, host->spare_bytes);
+
+ host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) |
+ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, bad_block_byte) |
+ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 0) |
+ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) |
+ FIELD_PREP(WIDE_FLASH, wide_bus) |
+ FIELD_PREP(ENABLE_BCH_ECC, host->bch_enabled);
+
+ host->cfg0_raw = FIELD_PREP(CW_PER_PAGE_MASK, (cwperpage - 1)) |
+ FIELD_PREP(UD_SIZE_BYTES_MASK, host->cw_size) |
+ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) |
+ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0);
+
+ host->cfg1_raw = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) |
+ FIELD_PREP(CS_ACTIVE_BSY, 0) |
+ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) |
+ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) |
+ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) |
+ FIELD_PREP(WIDE_FLASH, wide_bus) |
+ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1);
+
+ host->ecc_bch_cfg = FIELD_PREP(ECC_CFG_ECC_DISABLE, !host->bch_enabled) |
+ FIELD_PREP(ECC_SW_RESET, 0) |
+ FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, host->cw_data) |
+ FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
+ FIELD_PREP(ECC_MODE_MASK, ecc_mode) |
+ FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, host->ecc_bytes_hw);
if (!nandc->props->qpic_version2)
host->ecc_buf_cfg = 0x203 << NUM_STEPS;
@@ -1882,21 +1881,21 @@ static int qcom_param_page_type_exec(str
nandc->regs->addr0 = 0;
nandc->regs->addr1 = 0;
- nandc->regs->cfg0 = cpu_to_le32(0 << CW_PER_PAGE
- | 512 << UD_SIZE_BYTES
- | 5 << NUM_ADDR_CYCLES
- | 0 << SPARE_SIZE_BYTES);
-
- nandc->regs->cfg1 = cpu_to_le32(7 << NAND_RECOVERY_CYCLES
- | 0 << CS_ACTIVE_BSY
- | 17 << BAD_BLOCK_BYTE_NUM
- | 1 << BAD_BLOCK_IN_SPARE_AREA
- | 2 << WR_RD_BSY_GAP
- | 0 << WIDE_FLASH
- | 1 << DEV0_CFG1_ECC_DISABLE);
+ host->cfg0 = FIELD_PREP(CW_PER_PAGE_MASK, 0) |
+ FIELD_PREP(UD_SIZE_BYTES_MASK, 512) |
+ FIELD_PREP(NUM_ADDR_CYCLES_MASK, 5) |
+ FIELD_PREP(SPARE_SIZE_BYTES_MASK, 0);
+
+ host->cfg1 = FIELD_PREP(NAND_RECOVERY_CYCLES_MASK, 7) |
+ FIELD_PREP(BAD_BLOCK_BYTE_NUM_MASK, 17) |
+ FIELD_PREP(CS_ACTIVE_BSY, 0) |
+ FIELD_PREP(BAD_BLOCK_IN_SPARE_AREA, 1) |
+ FIELD_PREP(WR_RD_BSY_GAP_MASK, 2) |
+ FIELD_PREP(WIDE_FLASH, 0) |
+ FIELD_PREP(DEV0_CFG1_ECC_DISABLE, 1);
if (!nandc->props->qpic_version2)
- nandc->regs->ecc_buf_cfg = cpu_to_le32(1 << ECC_CFG_ECC_DISABLE);
+ nandc->regs->ecc_buf_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE);
/* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
if (!nandc->props->qpic_version2) {
--- a/include/linux/mtd/nand-qpic-common.h
+++ b/include/linux/mtd/nand-qpic-common.h
@@ -70,35 +70,42 @@
#define BS_CORRECTABLE_ERR_MSK 0x1f
/* NAND_DEVn_CFG0 bits */
-#define DISABLE_STATUS_AFTER_WRITE 4
+#define DISABLE_STATUS_AFTER_WRITE BIT(4)
#define CW_PER_PAGE 6
+#define CW_PER_PAGE_MASK GENMASK(8, 6)
#define UD_SIZE_BYTES 9
#define UD_SIZE_BYTES_MASK GENMASK(18, 9)
-#define ECC_PARITY_SIZE_BYTES_RS 19
+#define ECC_PARITY_SIZE_BYTES_RS GENMASK(22, 19)
#define SPARE_SIZE_BYTES 23
#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23)
#define NUM_ADDR_CYCLES 27
-#define STATUS_BFR_READ 30
-#define SET_RD_MODE_AFTER_STATUS 31
+#define NUM_ADDR_CYCLES_MASK GENMASK(29, 27)
+#define STATUS_BFR_READ BIT(30)
+#define SET_RD_MODE_AFTER_STATUS BIT(31)
/* NAND_DEVn_CFG0 bits */
-#define DEV0_CFG1_ECC_DISABLE 0
-#define WIDE_FLASH 1
+#define DEV0_CFG1_ECC_DISABLE BIT(0)
+#define WIDE_FLASH BIT(1)
#define NAND_RECOVERY_CYCLES 2
-#define CS_ACTIVE_BSY 5
+#define NAND_RECOVERY_CYCLES_MASK GENMASK(4, 2)
+#define CS_ACTIVE_BSY BIT(5)
#define BAD_BLOCK_BYTE_NUM 6
-#define BAD_BLOCK_IN_SPARE_AREA 16
+#define BAD_BLOCK_BYTE_NUM_MASK GENMASK(15, 6)
+#define BAD_BLOCK_IN_SPARE_AREA BIT(16)
#define WR_RD_BSY_GAP 17
-#define ENABLE_BCH_ECC 27
+#define WR_RD_BSY_GAP_MASK GENMASK(22, 17)
+#define ENABLE_BCH_ECC BIT(27)
/* NAND_DEV0_ECC_CFG bits */
-#define ECC_CFG_ECC_DISABLE 0
-#define ECC_SW_RESET 1
+#define ECC_CFG_ECC_DISABLE BIT(0)
+#define ECC_SW_RESET BIT(1)
#define ECC_MODE 4
+#define ECC_MODE_MASK GENMASK(5, 4)
#define ECC_PARITY_SIZE_BYTES_BCH 8
+#define ECC_PARITY_SIZE_BYTES_BCH_MASK GENMASK(12, 8)
#define ECC_NUM_DATA_BYTES 16
#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16)
-#define ECC_FORCE_CLK_OPEN 30
+#define ECC_FORCE_CLK_OPEN BIT(30)
/* NAND_DEV_CMD1 bits */
#define READ_ADDR 0

View File

@ -0,0 +1,45 @@
From: George Moussalem <george.moussalem@outlook.com>
Subject: [PATCH] spi: spi-qpic: fix compilation issues
Date: Sun, 06 Oct 2024 16:34:11 +0400
The compiler will throw a warning when freeing a variable, setting values
of u32 to zero using memset, when the number of bytes is greater than the
size of the variable passed, so let's set each of the 8 variables
contiguously set in memory as part of the structure to zero.
The output type of the remove function is void while it should return an
integer indicating success (0) or a negative number as an error. So let's
switch to use the new .remove_new function which expects nothing to be
returned
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/drivers/mtd/nand/qpic_common.c
+++ b/drivers/mtd/nand/qpic_common.c
@@ -82,7 +82,14 @@ void qcom_clear_bam_transaction(struct q
if (!nandc->props->supports_bam)
return;
- memset(&bam_txn->bam_ce_pos, 0, sizeof(u32) * 8);
+ bam_txn->bam_ce_pos = 0;
+ bam_txn->bam_ce_start = 0;
+ bam_txn->cmd_sgl_pos = 0;
+ bam_txn->cmd_sgl_start = 0;
+ bam_txn->tx_sgl_pos = 0;
+ bam_txn->tx_sgl_start = 0;
+ bam_txn->rx_sgl_pos = 0;
+ bam_txn->rx_sgl_start = 0;
bam_txn->last_data_desc = NULL;
sg_init_table(bam_txn->cmd_sgl, nandc->max_cwperpage *
--- a/drivers/spi/spi-qpic-snand.c
+++ b/drivers/spi/spi-qpic-snand.c
@@ -1624,7 +1624,7 @@ static struct platform_driver qcom_spi_d
.of_match_table = qcom_snandc_of_match,
},
.probe = qcom_spi_probe,
- .remove = qcom_spi_remove,
+ .remove_new = qcom_spi_remove,
};
module_platform_driver(qcom_spi_driver);

View File

@ -0,0 +1,53 @@
From 396886e8644d5b601126b97e0b36c40c5fb5cecf Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:11 +0800
Subject: [PATCH 1/2] spi: spi-qpic-snand: support BCH8
Add BCH8 error-correcting code support for QPIC SPI Nand controller.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/spi/spi-qpic-snand.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
--- a/drivers/spi/spi-qpic-snand.c
+++ b/drivers/spi/spi-qpic-snand.c
@@ -252,6 +252,7 @@ static int qcom_spi_ecc_init_ctx_pipelin
struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
struct mtd_info *mtd = nanddev_to_mtd(nand);
int cwperpage, bad_block_byte;
+ int ecc_mode;
struct qpic_ecc *ecc_cfg;
cwperpage = mtd->writesize / NANDC_STEP_SIZE;
@@ -270,14 +271,17 @@ static int qcom_spi_ecc_init_ctx_pipelin
nand->ecc.ctx.priv = ecc_cfg;
snandc->qspi->mtd = mtd;
- ecc_cfg->ecc_bytes_hw = 7;
- ecc_cfg->spare_bytes = 4;
+ /* BCH8 or BCH4 */
+ ecc_mode = mtd->oobsize > 64 ? 1 : 0;
+
+ ecc_cfg->ecc_bytes_hw = ecc_mode ? 13 : 7;
+ ecc_cfg->spare_bytes = ecc_mode ? 2 : 4;
ecc_cfg->bbm_size = 1;
ecc_cfg->bch_enabled = true;
ecc_cfg->bytes = ecc_cfg->ecc_bytes_hw + ecc_cfg->spare_bytes + ecc_cfg->bbm_size;
ecc_cfg->steps = 4;
- ecc_cfg->strength = 4;
+ ecc_cfg->strength = ecc_mode ? 8 : 4;
ecc_cfg->step_size = 512;
ecc_cfg->cw_data = 516;
ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes;
@@ -319,7 +323,7 @@ static int qcom_spi_ecc_init_ctx_pipelin
FIELD_PREP(ECC_SW_RESET, 0) |
FIELD_PREP(ECC_NUM_DATA_BYTES_MASK, ecc_cfg->cw_data) |
FIELD_PREP(ECC_FORCE_CLK_OPEN, 1) |
- FIELD_PREP(ECC_MODE_MASK, 0) |
+ FIELD_PREP(ECC_MODE_MASK, ecc_mode) |
FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
ecc_cfg->ecc_buf_cfg = 0x203 << NUM_STEPS;

View File

@ -0,0 +1,25 @@
From 3d550dc3eb4eaa2fe1d0668ed67e835c91487d61 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:11 +0800
Subject: [PATCH 2/2] mtd: spinand: qpic only support max 4 bytes ID
The QPIC SPI NAND controller supports a max of 4 bytes of device ID.
As such, set a maximum of 4 bytes.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/mtd/nand/spi/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1087,7 +1087,7 @@ int spinand_match_and_init(struct spinan
if (rdid_method != info->devid.method)
continue;
- if (memcmp(id + 1, info->devid.id, info->devid.len))
+ if (memcmp(id + 1, info->devid.id, min(3, info->devid.len)))
continue;
nand->memorg = table[i].memorg;

View File

@ -0,0 +1,52 @@
From c2019f64539dd24e6e0da3cea2219d6f9e6b03e4 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:11 +0800
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add SPI nand node
Add SPI NAND support for IPQ5018 SoC.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 40 +++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -461,6 +461,36 @@
status = "disabled";
};
+ qpic_bam: dma@7984000 {
+ compatible = "qcom,bam-v1.7.0";
+ reg = <0x07984000 0x1c000>;
+ interrupts = <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_QPIC_AHB_CLK>;
+ clock-names = "bam_clk";
+ #dma-cells = <1>;
+ qcom,ee = <0>;
+ status = "disabled";
+ };
+
+ qpic_nand: qpic-nand@79b0000 {
+ compatible = "qcom,spi-qpic-snand";
+ reg = <0x079b0000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ clocks = <&gcc GCC_QPIC_CLK>,
+ <&gcc GCC_QPIC_AHB_CLK>,
+ <&gcc GCC_QPIC_IO_MACRO_CLK>;
+ clock-names = "core", "aon", "iom";
+
+ dmas = <&qpic_bam 0>,
+ <&qpic_bam 1>,
+ <&qpic_bam 2>,
+ <&qpic_bam 3>;
+ dma-names = "tx", "rx", "cmd", "status";
+
+ status = "disabled";
+ };
+
usb: usb@8af8800 {
compatible = "qcom,ipq5018-dwc3", "qcom,dwc3";
reg = <0x08af8800 0x400>;

View File

@ -0,0 +1,23 @@
From b76a7649402d3eb1245ab463832133fc7efda194 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:11 +0800
Subject: [PATCH] arm64: dts: qcom: ipq5018: Add more nand compatible for
uboot to fix partitions
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -473,7 +473,7 @@
};
qpic_nand: qpic-nand@79b0000 {
- compatible = "qcom,spi-qpic-snand";
+ compatible = "qcom,spi-qpic-snand", "qcom,ebi2-nandc-bam-v2.1.1";
reg = <0x079b0000 0x10000>;
#address-cells = <1>;
#size-cells = <0>;

View File

@ -0,0 +1,113 @@
From 7b89dbf5c7dcd8a9c131721e93c1292e5993968b Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@quicinc.com>
Date: Tue, 20 Aug 2024 22:02:42 +0800
Subject: [PATCH] dt-bindings: clock: qcom: Add CMN PLL clock controller
for IPQ SoC
The CMN PLL controller provides clocks to networking hardware blocks
on Qualcomm IPQ9574 SoC. It receives input clock from the on-chip Wi-Fi,
and produces output clocks at fixed rates. These output rates are
predetermined, and are unrelated to the input clock rate. The output
clocks are supplied to the Ethernet hardware such as PPE (packet
process engine) and the externally connected switch or PHY device.
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
.../bindings/clock/qcom,ipq9574-cmn-pll.yaml | 70 +++++++++++++++++++
include/dt-bindings/clock/qcom,ipq-cmn-pll.h | 15 ++++
2 files changed, 85 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml
create mode 100644 include/dt-bindings/clock/qcom,ipq-cmn-pll.h
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/qcom,ipq9574-cmn-pll.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm CMN PLL Clock Controller on IPQ SoC
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Luo Jie <quic_luoj@quicinc.com>
+
+description:
+ The CMN PLL clock controller expects a reference input clock.
+ This reference clock is from the on-board Wi-Fi. The CMN PLL
+ supplies a number of fixed rate output clocks to the Ethernet
+ devices including PPE (packet process engine) and the connected
+ switch or PHY device.
+
+properties:
+ compatible:
+ enum:
+ - qcom,ipq9574-cmn-pll
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: The reference clock. The supported clock rates include
+ 25000000, 31250000, 40000000, 48000000, 50000000 and 96000000 HZ.
+ - description: The AHB clock
+ - description: The SYS clock
+ description:
+ The reference clock is the source clock of CMN PLL, which is from the
+ Wi-Fi. The AHB and SYS clocks must be enabled to access CMN PLL
+ clock registers.
+
+ clock-names:
+ items:
+ - const: ref
+ - const: ahb
+ - const: sys
+
+ "#clock-cells":
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - "#clock-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
+
+ clock-controller@9b000 {
+ compatible = "qcom,ipq9574-cmn-pll";
+ reg = <0x0009b000 0x800>;
+ clocks = <&cmn_pll_ref_clk>,
+ <&gcc GCC_CMN_12GPLL_AHB_CLK>,
+ <&gcc GCC_CMN_12GPLL_SYS_CLK>;
+ clock-names = "ref", "ahb", "sys";
+ #clock-cells = <1>;
+ };
+...
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,ipq-cmn-pll.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _DT_BINDINGS_CLK_QCOM_IPQ_CMN_PLL_H
+#define _DT_BINDINGS_CLK_QCOM_IPQ_CMN_PLL_H
+
+/* The output clocks from CMN PLL of IPQ9574. */
+#define PPE_353MHZ_CLK 0
+#define ETH0_50MHZ_CLK 1
+#define ETH1_50MHZ_CLK 2
+#define ETH2_50MHZ_CLK 3
+#define ETH_25MHZ_CLK 4
+#endif

View File

@ -0,0 +1,288 @@
From a7e8397e2db6133e3435054a3f312dbd9cab05ed Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@quicinc.com>
Date: Tue, 20 Aug 2024 22:02:43 +0800
Subject: [PATCH] clk: qcom: Add CMN PLL clock controller driver for IPQ
SoC
The CMN PLL clock controller supplies clocks to the hardware
blocks that together make up the Ethernet function on Qualcomm
IPQ SoCs. The driver is initially supported for IPQ9574 SoC.
The CMN PLL clock controller expects a reference input clock
from the on-board Wi-Fi block acting as clock source. The input
reference clock needs to be configured to one of the supported
clock rates.
The controller supplies a number of fixed-rate output clocks.
For the IPQ9574, there is one output clock of 353 MHZ to PPE
(Packet Process Engine) hardware block, three 50 MHZ output
clocks and an additional 25 MHZ output clock supplied to the
connected Ethernet devices.
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
---
drivers/clk/qcom/Kconfig | 10 ++
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/clk-ipq-cmn-pll.c | 227 +++++++++++++++++++++++++++++
3 files changed, 238 insertions(+)
create mode 100644 drivers/clk/qcom/clk-ipq-cmn-pll.c
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -139,6 +139,16 @@ config IPQ_APSS_6018
Say Y if you want to support CPU frequency scaling on
ipq based devices.
+config IPQ_CMN_PLL
+ tristate "IPQ CMN PLL Clock Controller"
+ depends on IPQ_GCC_9574
+ help
+ Support for CMN PLL clock controller on IPQ platform. The
+ CMN PLL feeds the reference clocks to the Ethernet devices
+ based on IPQ SoC.
+ Say Y or M if you want to support CMN PLL clock on the IPQ
+ based devices.
+
config IPQ_GCC_4019
tristate "IPQ4019 Global Clock Controller"
help
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8
obj-$(CONFIG_CLK_GFM_LPASS_SM8250) += lpass-gfm-sm8250.o
obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o
obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o
+obj-$(CONFIG_IPQ_CMN_PLL) += clk-ipq-cmn-pll.o
obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o
obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o
obj-$(CONFIG_IPQ_GCC_5332) += gcc-ipq5332.o
--- /dev/null
+++ b/drivers/clk/qcom/clk-ipq-cmn-pll.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+/*
+ * CMN PLL block expects the reference clock from on-board Wi-Fi block, and
+ * supplies fixed rate clocks as output to the Ethernet hardware blocks.
+ * The Ethernet related blocks include PPE (packet process engine) and the
+ * external connected PHY (or switch) chip receiving clocks from the CMN PLL.
+ *
+ * On the IPQ9574 SoC, There are three clocks with 50 MHZ, one clock with
+ * 25 MHZ which are output from the CMN PLL to Ethernet PHY (or switch),
+ * and one clock with 353 MHZ to PPE.
+ *
+ * +---------+
+ * | GCC |
+ * +--+---+--+
+ * AHB CLK| |SYS CLK
+ * V V
+ * +-------+---+------+
+ * | +-------------> eth0-50mhz
+ * REF CLK | IPQ9574 |
+ * -------->+ +-------------> eth1-50mhz
+ * | CMN PLL block |
+ * | +-------------> eth2-50mhz
+ * | |
+ * +---------+--------+-------------> eth-25mhz
+ * |
+ * V
+ * ppe-353mhz
+ */
+
+#include <dt-bindings/clock/qcom,ipq-cmn-pll.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define CMN_PLL_REFCLK_SRC_SELECTION 0x28
+#define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8)
+
+#define CMN_PLL_REFCLK_CONFIG 0x784
+#define CMN_PLL_REFCLK_EXTERNAL BIT(9)
+#define CMN_PLL_REFCLK_DIV GENMASK(8, 4)
+#define CMN_PLL_REFCLK_INDEX GENMASK(3, 0)
+
+#define CMN_PLL_POWER_ON_AND_RESET 0x780
+#define CMN_ANA_EN_SW_RSTN BIT(6)
+
+/**
+ * struct cmn_pll_fixed_output_clk - CMN PLL output clocks information
+ * @id: Clock specifier to be supplied
+ * @name: Clock name to be registered
+ * @rate: Clock rate
+ */
+struct cmn_pll_fixed_output_clk {
+ unsigned int id;
+ const char *name;
+ const unsigned long rate;
+};
+
+#define CLK_PLL_OUTPUT(_id, _name, _rate) { \
+ .id = _id, \
+ .name = _name, \
+ .rate = _rate, \
+}
+
+static const struct cmn_pll_fixed_output_clk ipq9574_output_clks[] = {
+ CLK_PLL_OUTPUT(PPE_353MHZ_CLK, "ppe-353mhz", 353000000UL),
+ CLK_PLL_OUTPUT(ETH0_50MHZ_CLK, "eth0-50mhz", 50000000UL),
+ CLK_PLL_OUTPUT(ETH1_50MHZ_CLK, "eth1-50mhz", 50000000UL),
+ CLK_PLL_OUTPUT(ETH2_50MHZ_CLK, "eth2-50mhz", 50000000UL),
+ CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL),
+};
+
+static int ipq_cmn_pll_config(struct device *dev, unsigned long parent_rate)
+{
+ void __iomem *base;
+ u32 val;
+
+ base = devm_of_iomap(dev, dev->of_node, 0, NULL);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ val = readl(base + CMN_PLL_REFCLK_CONFIG);
+ val &= ~(CMN_PLL_REFCLK_EXTERNAL | CMN_PLL_REFCLK_INDEX);
+
+ /*
+ * Configure the reference input clock selection as per the given rate.
+ * The output clock rates are always of fixed value.
+ */
+ switch (parent_rate) {
+ case 25000000:
+ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 3);
+ break;
+ case 31250000:
+ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 4);
+ break;
+ case 40000000:
+ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 6);
+ break;
+ case 48000000:
+ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
+ break;
+ case 50000000:
+ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 8);
+ break;
+ case 96000000:
+ val |= FIELD_PREP(CMN_PLL_REFCLK_INDEX, 7);
+ val &= ~CMN_PLL_REFCLK_DIV;
+ val |= FIELD_PREP(CMN_PLL_REFCLK_DIV, 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ writel(val, base + CMN_PLL_REFCLK_CONFIG);
+
+ /* Update the source clock rate selection. Only 96 MHZ uses 0. */
+ val = readl(base + CMN_PLL_REFCLK_SRC_SELECTION);
+ val &= ~CMN_PLL_REFCLK_SRC_DIV;
+ if (parent_rate != 96000000)
+ val |= FIELD_PREP(CMN_PLL_REFCLK_SRC_DIV, 1);
+
+ writel(val, base + CMN_PLL_REFCLK_SRC_SELECTION);
+
+ /*
+ * Reset the CMN PLL block by asserting/de-asserting for 100 ms
+ * each, to ensure the updated configurations take effect.
+ */
+ val = readl(base + CMN_PLL_POWER_ON_AND_RESET);
+ val &= ~CMN_ANA_EN_SW_RSTN;
+ writel(val, base);
+ msleep(100);
+
+ val |= CMN_ANA_EN_SW_RSTN;
+ writel(val, base + CMN_PLL_POWER_ON_AND_RESET);
+ msleep(100);
+
+ return 0;
+}
+
+static int ipq_cmn_pll_clk_register(struct device *dev, const char *parent)
+{
+ const struct cmn_pll_fixed_output_clk *fixed_clk;
+ struct clk_hw_onecell_data *data;
+ unsigned int num_clks;
+ struct clk_hw *hw;
+ int i;
+
+ num_clks = ARRAY_SIZE(ipq9574_output_clks);
+ fixed_clk = ipq9574_output_clks;
+
+ data = devm_kzalloc(dev, struct_size(data, hws, num_clks), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ for (i = 0; i < num_clks; i++) {
+ hw = devm_clk_hw_register_fixed_rate(dev, fixed_clk[i].name,
+ parent, 0,
+ fixed_clk[i].rate);
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ data->hws[fixed_clk[i].id] = hw;
+ }
+ data->num = num_clks;
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
+}
+
+static int ipq_cmn_pll_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct clk *clk;
+ int ret;
+
+ /*
+ * To access the CMN PLL registers, the GCC AHB & SYSY clocks
+ * for CMN PLL block need to be enabled.
+ */
+ clk = devm_clk_get_enabled(dev, "ahb");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Enable AHB clock failed\n");
+
+ clk = devm_clk_get_enabled(dev, "sys");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Enable SYS clock failed\n");
+
+ clk = devm_clk_get(dev, "ref");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Get reference clock failed\n");
+
+ /* Configure CMN PLL to apply the reference clock. */
+ ret = ipq_cmn_pll_config(dev, clk_get_rate(clk));
+ if (ret)
+ return dev_err_probe(dev, ret, "Configure CMN PLL failed\n");
+
+ return ipq_cmn_pll_clk_register(dev, __clk_get_name(clk));
+}
+
+static const struct of_device_id ipq_cmn_pll_clk_ids[] = {
+ { .compatible = "qcom,ipq9574-cmn-pll", },
+ { }
+};
+
+static struct platform_driver ipq_cmn_pll_clk_driver = {
+ .probe = ipq_cmn_pll_clk_probe,
+ .driver = {
+ .name = "ipq_cmn_pll",
+ .of_match_table = ipq_cmn_pll_clk_ids,
+ },
+};
+
+module_platform_driver(ipq_cmn_pll_clk_driver);
+
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPQ CMN PLL Driver");
+MODULE_LICENSE("GPL");

View File

@ -0,0 +1,78 @@
From a28797563b8c97c9abced82e0cf89302fcd2bf37 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:11 +0800
Subject: [PATCH 1/2] clk: qcom: cmn-pll: add IPQ5018 support
Add support for IPQ5018 (and removing dependency on the IPQ9574 platform).
The common network block in IPQ5018 must be enabled first through a
specific register at a fixed offset in the TCSR area, set in the DTS.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/clk/qcom/Kconfig | 1 -
drivers/clk/qcom/clk-ipq-cmn-pll.c | 29 +++++++++++++++++++++++++++++
2 files changed, 29 insertions(+), 1 deletion(-)
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -141,7 +141,6 @@ config IPQ_APSS_6018
config IPQ_CMN_PLL
tristate "IPQ CMN PLL Clock Controller"
- depends on IPQ_GCC_9574
help
Support for CMN PLL clock controller on IPQ platform. The
CMN PLL feeds the reference clocks to the Ethernet devices
--- a/drivers/clk/qcom/clk-ipq-cmn-pll.c
+++ b/drivers/clk/qcom/clk-ipq-cmn-pll.c
@@ -42,6 +42,9 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
+#define TCSR_ETH_CMN 0x0
+#define TCSR_ETH_CMN_ENABLE BIT(0)
+
#define CMN_PLL_REFCLK_SRC_SELECTION 0x28
#define CMN_PLL_REFCLK_SRC_DIV GENMASK(9, 8)
@@ -79,6 +82,28 @@ static const struct cmn_pll_fixed_output
CLK_PLL_OUTPUT(ETH_25MHZ_CLK, "eth-25mhz", 25000000UL),
};
+static int ipq_cmn_pll_tcsr_enable(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *tcsr_base;
+ u32 val;
+
+ /* For IPQ50xx, tcsr is necessary to enable cmn block */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcsr");
+ if (!res)
+ return 0;
+
+ tcsr_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR_OR_NULL(tcsr_base))
+ return PTR_ERR(tcsr_base);
+
+ val = readl(tcsr_base + TCSR_ETH_CMN);
+ val |= TCSR_ETH_CMN_ENABLE;
+ writel(val, (tcsr_base + TCSR_ETH_CMN));
+
+ return 0;
+}
+
static int ipq_cmn_pll_config(struct device *dev, unsigned long parent_rate)
{
void __iomem *base;
@@ -181,6 +206,10 @@ static int ipq_cmn_pll_clk_probe(struct
struct clk *clk;
int ret;
+ ret = ipq_cmn_pll_tcsr_enable(pdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Enable CMN PLL failed\n");
+
/*
* To access the CMN PLL registers, the GCC AHB & SYSY clocks
* for CMN PLL block need to be enabled.

View File

@ -0,0 +1,45 @@
From 1b625a37b96b0448aac126d7720eec38de8e5956 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:11 +0800
Subject: [PATCH 2/2] arm64: dts: qcom: ipq5018: Add ethernet cmn node
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -16,6 +16,12 @@
#size-cells = <2>;
clocks {
+ cmn_pll_ref_clk: cmn-pll-ref-clk {
+ compatible = "fixed-clock";
+ clock-frequency = <96000000>;
+ #clock-cells = <0>;
+ };
+
sleep_clk: sleep-clk {
compatible = "fixed-clock";
#clock-cells = <0>;
@@ -186,6 +192,19 @@
status = "disabled";
};
+ cmn_pll: clock-controller@9b000 {
+ compatible = "qcom,ipq9574-cmn-pll";
+ reg = <0x0009b000 0x800>,
+ <0x19475c4 0x4>;
+ reg-names = "cmn",
+ "tcsr";
+ clocks = <&cmn_pll_ref_clk>,
+ <&gcc GCC_CMN_BLK_AHB_CLK>,
+ <&gcc GCC_CMN_BLK_SYS_CLK>;
+ clock-names = "ref", "ahb", "sys";
+ #clock-cells = <1>;
+ };
+
qfprom: qfprom@a0000 {
compatible = "qcom,ipq5018-qfprom", "qcom,qfprom";
reg = <0xa0000 0x1000>;

View File

@ -0,0 +1,184 @@
From 77ad12b3a5e21cae859247c0b82cf9a5b661e531 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:11 +0800
Subject: [PATCH 1/3] net: phy: qcom: Introduce IPQ5018 internal PHY driver
Introduce the internal GE PHY driver, part of the Qualcomm IPQ50xx SoC.
The driver registers two clock providers needed and referenced by the GCC
using DT properties and phandles.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/net/phy/qcom/Kconfig | 6 ++
drivers/net/phy/qcom/Makefile | 1 +
drivers/net/phy/qcom/ipq5018.c | 138 +++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+)
create mode 100644 drivers/net/phy/qcom/ipq5018.c
--- a/drivers/net/phy/qcom/Kconfig
+++ b/drivers/net/phy/qcom/Kconfig
@@ -9,6 +9,12 @@ config AT803X_PHY
help
Currently supports the AR8030, AR8031, AR8033, AR8035 model
+config IPQ5018_PHY
+ tristate "Qualcomm IPQ5018 internal PHYs"
+ select QCOM_NET_PHYLIB
+ help
+ Currently supports the Qualcomm IPQ5018 internal PHY
+
config QCA83XX_PHY
tristate "Qualcomm Atheros QCA833x PHYs"
select QCOM_NET_PHYLIB
--- a/drivers/net/phy/qcom/Makefile
+++ b/drivers/net/phy/qcom/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-phy-lib.o
obj-$(CONFIG_AT803X_PHY) += at803x.o
+obj-$(CONFIG_IPQ5018_PHY) += ipq5018.o
obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
obj-$(CONFIG_QCA808X_PHY) += qca808x.o
obj-$(CONFIG_QCA807X_PHY) += qca807x.o
--- /dev/null
+++ b/drivers/net/phy/qcom/ipq5018.c
@@ -0,0 +1,138 @@
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/phy.h>
+#include <linux/reset.h>
+
+#include "qcom.h"
+
+#define IPQ5018_PHY_ID 0x004dd0c0
+
+#define TX_RX_CLK_RATE 125000000 /* 125M */
+
+#define IPQ5018_PHY_FIFO_CONTROL 0x19
+#define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0)
+
+struct ipq5018_phy {
+ int num_clks;
+ struct clk_bulk_data *clks;
+ struct reset_control *rst;
+
+ struct clk_hw *clk_rx, *clk_tx;
+ struct clk_hw_onecell_data *clk_data;
+};
+
+static int ipq5018_probe(struct phy_device *phydev)
+{
+ struct ipq5018_phy *priv;
+ struct device *dev = &phydev->mdio.dev;
+ char name[64];
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return dev_err_probe(dev, -ENOMEM,
+ "failed to allocate priv\n");
+
+ priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
+ if (priv->num_clks < 0)
+ return dev_err_probe(dev, priv->num_clks,
+ "failed to acquire clocks\n");
+
+ ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to enable clocks\n");
+
+ priv->rst = devm_reset_control_array_get_exclusive(dev);
+ if (IS_ERR_OR_NULL(priv->rst))
+ return dev_err_probe(dev, PTR_ERR(priv->rst),
+ "failed to acquire reset\n");
+
+ ret = reset_control_reset(priv->rst);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to reset\n");
+
+ snprintf(name, sizeof(name), "%s#rx", dev_name(dev));
+ priv->clk_rx = clk_hw_register_fixed_rate(dev, name, NULL, 0,
+ TX_RX_CLK_RATE);
+ if (IS_ERR_OR_NULL(priv->clk_rx))
+ return dev_err_probe(dev, PTR_ERR(priv->clk_rx),
+ "failed to register rx clock\n");
+
+ snprintf(name, sizeof(name), "%s#tx", dev_name(dev));
+ priv->clk_tx = clk_hw_register_fixed_rate(dev, name, NULL, 0,
+ TX_RX_CLK_RATE);
+ if (IS_ERR_OR_NULL(priv->clk_tx))
+ return dev_err_probe(dev, PTR_ERR(priv->clk_tx),
+ "failed to register tx clock\n");
+
+ priv->clk_data = devm_kzalloc(dev,
+ struct_size(priv->clk_data, hws, 2),
+ GFP_KERNEL);
+ if (!priv->clk_data)
+ return dev_err_probe(dev, -ENOMEM,
+ "failed to allocate clk_data\n");
+
+ priv->clk_data->num = 2;
+ priv->clk_data->hws[0] = priv->clk_rx;
+ priv->clk_data->hws[1] = priv->clk_tx;
+ ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
+ priv->clk_data);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "fail to register clock provider\n");
+
+ return 0;
+}
+
+static int ipq5018_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_modify(phydev, IPQ5018_PHY_FIFO_CONTROL,
+ IPQ5018_PHY_FIFO_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ msleep(50);
+
+ ret = phy_modify(phydev, IPQ5018_PHY_FIFO_CONTROL,
+ IPQ5018_PHY_FIFO_RESET, IPQ5018_PHY_FIFO_RESET);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ipq5018_cable_test_start(struct phy_device *phydev)
+{
+ /* we do all the (time consuming) work later */
+ return 0;
+}
+
+static struct phy_driver ipq5018_internal_phy_driver[] = {
+ {
+ PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID),
+ .name = "Qualcomm IPQ5018 internal PHY",
+ .flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST,
+ .probe = ipq5018_probe,
+ .soft_reset = ipq5018_soft_reset,
+ .read_status = at803x_read_status,
+ .config_intr = at803x_config_intr,
+ .handle_interrupt = at803x_handle_interrupt,
+ .cable_test_start = ipq5018_cable_test_start,
+ .cable_test_get_status = qca808x_cable_test_get_status,
+ },
+};
+module_phy_driver(ipq5018_internal_phy_driver);
+
+static struct mdio_device_id __maybe_unused ipq5018_internal_phy_ids[] = {
+ { PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE(mdio, ipq5018_internal_phy_ids);
+
+MODULE_DESCRIPTION("Qualcomm IPQ5018 internal PHY driver");
+MODULE_AUTHOR("Ziyang Huang <hzyitc@outlook.com>");

View File

@ -0,0 +1,43 @@
From d2cdc83fb2c7360856e598810b88211d815fc851 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH 2/3] arm64: dts: qcom: ipq5018: add mdio node
The IPQ5018 SoC contains two MDIO controllers. MDIO0 is used to control
its internal GE Phy, while MDIO1 is wired to external PHYs/switch.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -192,6 +192,26 @@
status = "disabled";
};
+ mdio0: mdio@88000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "qcom,ipq5018-mdio", "qcom,qca-mdio";
+ reg = <0x88000 0x64>;
+ clocks = <&gcc GCC_MDIO0_AHB_CLK>;
+ clock-names = "gcc_mdio_ahb_clk";
+ status = "disabled";
+ };
+
+ mdio1: mdio@90000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "qcom,ipq5018-mdio";
+ reg = <0x90000 0x64>;
+ clocks = <&gcc GCC_MDIO1_AHB_CLK>;
+ clock-names = "gcc_mdio_ahb_clk";
+ status = "disabled";
+ };
+
cmn_pll: clock-controller@9b000 {
compatible = "qcom,ipq9574-cmn-pll";
reg = <0x0009b000 0x800>,

View File

@ -0,0 +1,45 @@
From 28490d95fe9e059c5ce74b2289d66e0d7ede2d50 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH 3/3] arm64: dts: qcom: ipq5018: add ge_phy node
Add the GE PHY node and register the output clocks in the GCC node.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -200,6 +200,18 @@
clocks = <&gcc GCC_MDIO0_AHB_CLK>;
clock-names = "gcc_mdio_ahb_clk";
status = "disabled";
+
+ ge_phy: ethernet-phy@7 {
+ compatible = "ethernet-phy-id004d.d0c0";
+ reg = <7>;
+ resets = <&gcc GCC_GEPHY_BCR>,
+ <&gcc GCC_GEPHY_MDC_SW_ARES>,
+ <&gcc GCC_GEPHY_DSP_HW_ARES>,
+ <&gcc GCC_GEPHY_RX_ARES>,
+ <&gcc GCC_GEPHY_TX_ARES>;
+ clocks = <&gcc GCC_GEPHY_RX_CLK>,
+ <&gcc GCC_GEPHY_TX_CLK>;
+ };
};
mdio1: mdio@90000 {
@@ -394,8 +406,8 @@
<&pcie0_phy>,
<&pcie1_phy>,
<0>,
- <0>,
- <0>,
+ <&ge_phy 0>,
+ <&ge_phy 1>,
<0>,
<0>;
#clock-cells = <1>;

View File

@ -0,0 +1,111 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Sun, 19 Jan 2025 11:25:27 +0400
Subject: [PATCH] net: phy: qcom: ipq5018 enable configuration of DAC settings
Allow setting amplitude and bias current as needed on the IPQ5018 Internal
GE PHY. When the "qcom,dac" property is set in the DTS, the driver expects
a pair of u32 values:
(from QCA8337 datasheet)
11: follow DSP setting
10: bypass half amplitude and follow DSP half bias current
01: half amplitude follow DSP and bypass half bias current
00: full amplitude and full bias current
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/drivers/net/phy/qcom/ipq5018.c
+++ b/drivers/net/phy/qcom/ipq5018.c
@@ -13,6 +13,10 @@
#define IPQ5018_PHY_FIFO_CONTROL 0x19
#define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0)
+#define IPQ5018_PHY_DEBUG_EDAC 0x4380
+#define IPQ5018_PHY_MMD1_MDAC 0x8100
+#define IPQ5018_PHY_DAC_MASK GENMASK(15,8)
+
struct ipq5018_phy {
int num_clks;
struct clk_bulk_data *clks;
@@ -20,20 +24,35 @@ struct ipq5018_phy {
struct clk_hw *clk_rx, *clk_tx;
struct clk_hw_onecell_data *clk_data;
+
+ u32 mdac;
+ u32 edac;
};
static int ipq5018_probe(struct phy_device *phydev)
{
- struct ipq5018_phy *priv;
struct device *dev = &phydev->mdio.dev;
+ struct ipq5018_phy *priv;
+ u32 mdac, edac = 0;
char name[64];
- int ret;
+ int ret, cnt;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return dev_err_probe(dev, -ENOMEM,
"failed to allocate priv\n");
+ cnt = of_property_count_u32_elems(dev->of_node, "qcom,dac");
+ if (cnt == 2) {
+ ret = of_property_read_u32_index(dev->of_node, "qcom,dac", 0, &mdac);
+ if (!ret)
+ priv->mdac = mdac;
+
+ ret = of_property_read_u32_index(dev->of_node, "qcom,dac", 1, &edac);
+ if (!ret)
+ priv->edac = edac;
+ }
+
priv->num_clks = devm_clk_bulk_get_all(dev, &priv->clks);
if (priv->num_clks < 0)
return dev_err_probe(dev, priv->num_clks,
@@ -84,6 +103,8 @@ static int ipq5018_probe(struct phy_devi
return dev_err_probe(dev, ret,
"fail to register clock provider\n");
+ phydev->priv = priv;
+
return 0;
}
@@ -112,12 +133,34 @@ static int ipq5018_cable_test_start(stru
return 0;
}
+static int ipq5018_config_init(struct phy_device *phydev)
+{
+ struct ipq5018_phy *priv = phydev->priv;
+ int ret;
+
+ /* setting mdac in MMD1 */
+ if (priv->mdac) {
+ ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
+ IPQ5018_PHY_DAC_MASK, priv->mdac);
+ if (ret)
+ return ret;
+ }
+
+ /* setting edac in debug register */
+ if (priv->edac)
+ return at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC,
+ IPQ5018_PHY_DAC_MASK, priv->edac);
+
+ return 0;
+}
+
static struct phy_driver ipq5018_internal_phy_driver[] = {
{
PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID),
.name = "Qualcomm IPQ5018 internal PHY",
.flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST,
.probe = ipq5018_probe,
+ .config_init = ipq5018_config_init,
.soft_reset = ipq5018_soft_reset,
.read_status = at803x_read_status,
.config_intr = at803x_config_intr,

View File

@ -0,0 +1,108 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Fri, 24 Jan 2025 17:18:12 +0400
Subject: [PATCH] net: phy: qcom: add IPQ5018 initvals and CDT feature
The Cable Diagnostics Test for IPQ5018 follows the same logic as qca808x.
However, the IPQ5018 GE PHY has its own threshold values. So let's set the
CDT thresholds for the IPQ5018 internal GE PHY. While add it, add and set
thesholds for MSE for signal quality measurement and 8023az for EEE.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/drivers/net/phy/qcom/ipq5018.c
+++ b/drivers/net/phy/qcom/ipq5018.c
@@ -17,6 +17,38 @@
#define IPQ5018_PHY_MMD1_MDAC 0x8100
#define IPQ5018_PHY_DAC_MASK GENMASK(15,8)
+#define IPQ5018_PHY_MMD1_MSE_THRESH1 0x1000
+#define IPQ5018_PHY_MMD1_MSE_THRESH2 0x1001
+#define IPQ5018_PHY_MMD3_AZ_CTRL1 0x8008
+#define IPQ5018_PHY_MMD3_AZ_CTRL2 0x8009
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3 0x8074
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4 0x8075
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5 0x8076
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6 0x8077
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7 0x8078
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9 0x807a
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13 0x807e
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL14 0x807f
+
+#define IPQ5018_PHY_MMD1_MSE_THRESH1_VAL 0xf1
+#define IPQ5018_PHY_MMD1_MSE_THRESH2_VAL 0x1f6
+#define IPQ5018_PHY_MMD3_AZ_CTRL1_VAL 0x7880
+#define IPQ5018_PHY_MMD3_AZ_CTRL2_VAL 0xc8
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3_VAL 0xc040
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4_VAL 0xa060
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5_VAL 0xc040
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6_VAL 0xa060
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7_VAL 0xc24c
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9_VAL 0xc060
+#define IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13_VAL 0xb060
+#define IPQ5018_PHY_MMD3_NEAR_ECHO_THRESH_VAL 0x90b0
+
+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE 0x1
+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK GENMASK(7,4)
+#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT 0x50
+
+#define IPQ5018_PHY_DEBUG_ANA_DAC_FILTER 0xa080
+
struct ipq5018_phy {
int num_clks;
struct clk_bulk_data *clks;
@@ -129,6 +161,24 @@ static int ipq5018_soft_reset(struct phy
static int ipq5018_cable_test_start(struct phy_device *phydev)
{
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3,
+ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4,
+ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL4_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5,
+ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL5_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6,
+ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL6_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7,
+ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL7_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9,
+ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL9_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS,
+ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13,
+ IPQ5018_PHY_MMD3_CDT_THRESH_CTRL13_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_CDT_THRESH_CTRL3,
+ IPQ5018_PHY_MMD3_NEAR_ECHO_THRESH_VAL);
+
/* we do all the (time consuming) work later */
return 0;
}
@@ -136,8 +186,30 @@ static int ipq5018_cable_test_start(stru
static int ipq5018_config_init(struct phy_device *phydev)
{
struct ipq5018_phy *priv = phydev->priv;
+ u16 val = 0;
int ret;
+ /* set LDO efuse: first temporarily store ANA_DAC_FILTER value from
+ debug register as it will be reset once the ANA_LDO_EFUSE register
+ is written to */
+ val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER);
+ at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE,
+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK,
+ IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT);
+ at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val);
+
+ /* set 8023AZ CTRL values */
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_AZ_CTRL1,
+ IPQ5018_PHY_MMD3_AZ_CTRL1_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_MMD3_AZ_CTRL2,
+ IPQ5018_PHY_MMD3_AZ_CTRL2_VAL);
+
+ /* set MSE threshold values */
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1,
+ IPQ5018_PHY_MMD1_MSE_THRESH1_VAL);
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2,
+ IPQ5018_PHY_MMD1_MSE_THRESH2_VAL);
+
/* setting mdac in MMD1 */
if (priv->mdac) {
ret = phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,

View File

@ -0,0 +1,35 @@
From f71366e0530db2c5cecbbbb6edfbf7344bd6f83b Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH 1/2] clk: gcc-ipq5018: remove the unsupported clk
combination for gmac
Comment out the unsupported clock combination in the frequency table
for GMAC1.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/clk/qcom/gcc-ipq5018.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/drivers/clk/qcom/gcc-ipq5018.c
+++ b/drivers/clk/qcom/gcc-ipq5018.c
@@ -677,7 +677,7 @@ static const struct freq_tbl ftbl_gmac1_
F(2500000, P_UNIPHY_RX, 12.5, 0, 0),
F(24000000, P_XO, 1, 0, 0),
F(25000000, P_UNIPHY_RX, 2.5, 0, 0),
- F(125000000, P_UNIPHY_RX, 2.5, 0, 0),
+ /* F(125000000, P_UNIPHY_RX, 2.5, 0, 0), */
F(125000000, P_UNIPHY_RX, 1, 0, 0),
F(312500000, P_UNIPHY_RX, 1, 0, 0),
{ }
@@ -717,7 +717,7 @@ static const struct freq_tbl ftbl_gmac1_
F(2500000, P_UNIPHY_TX, 12.5, 0, 0),
F(24000000, P_XO, 1, 0, 0),
F(25000000, P_UNIPHY_TX, 2.5, 0, 0),
- F(125000000, P_UNIPHY_TX, 2.5, 0, 0),
+ /* F(125000000, P_UNIPHY_TX, 2.5, 0, 0), */
F(125000000, P_UNIPHY_TX, 1, 0, 0),
F(312500000, P_UNIPHY_TX, 1, 0, 0),
{ }

View File

@ -0,0 +1,62 @@
From ce9e56a436e486690097cfbdda2d0c11b60db4c2 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH] clk: gcc-ipq5018: refer to GE PHY rx and tx clk providers by name
QCA-SSDK does not register the output clocks of the onboard GE Phy and
uniphy so the GCC and DTS can't reference them by their index.
The SSDK references them by name, so let's change the GCC driver
accordingly.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/clk/qcom/gcc-ipq5018.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
--- a/drivers/clk/qcom/gcc-ipq5018.c
+++ b/drivers/clk/qcom/gcc-ipq5018.c
@@ -335,8 +335,8 @@ static const struct parent_map gcc_xo_gp
static const struct clk_parent_data gcc_xo_gephy_gcc_rx_gephy_gcc_tx_ubi32_pll_gpll0[] = {
{ .index = DT_XO },
- { .index = DT_GEPHY_RX_CLK },
- { .index = DT_GEPHY_TX_CLK },
+ { .name = "gephy_gcc_rx", .index = -1 },
+ { .name = "gephy_gcc_tx", .index = -1 },
{ .hw = &ubi32_pll.clkr.hw },
{ .hw = &gpll0.clkr.hw },
};
@@ -351,8 +351,8 @@ static const struct parent_map gcc_xo_ge
static const struct clk_parent_data gcc_xo_gephy_gcc_tx_gephy_gcc_rx_ubi32_pll_gpll0[] = {
{ .index = DT_XO },
- { .index = DT_GEPHY_TX_CLK },
- { .index = DT_GEPHY_RX_CLK },
+ { .name = "gephy_gcc_tx", .index = -1 },
+ { .name = "gephy_gcc_rx", .index = -1 },
{ .hw = &ubi32_pll.clkr.hw },
{ .hw = &gpll0.clkr.hw },
};
@@ -367,8 +367,8 @@ static const struct parent_map gcc_xo_ge
static const struct clk_parent_data gcc_xo_uniphy_gcc_rx_uniphy_gcc_tx_ubi32_pll_gpll0[] = {
{ .index = DT_XO },
- { .index = DT_UNIPHY_RX_CLK },
- { .index = DT_UNIPHY_TX_CLK },
+ { .name = "uniphy_gcc_rx", .index = -1 },
+ { .name = "uniphy_gcc_tx", .index = -1 },
{ .hw = &ubi32_pll.clkr.hw },
{ .hw = &gpll0.clkr.hw },
};
@@ -383,8 +383,8 @@ static const struct parent_map gcc_xo_un
static const struct clk_parent_data gcc_xo_uniphy_gcc_tx_uniphy_gcc_rx_ubi32_pll_gpll0[] = {
{ .index = DT_XO },
- { .index = DT_UNIPHY_TX_CLK },
- { .index = DT_UNIPHY_RX_CLK },
+ { .name = "uniphy_gcc_tx", .index = -1 },
+ { .name = "uniphy_gcc_rx", .index = -1 },
{ .hw = &ubi32_pll.clkr.hw },
{ .hw = &gpll0.clkr.hw },
};

View File

@ -0,0 +1,30 @@
From d7a41a3ab6b8e3a3158997cda13f1fe28a37268c Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH] net: dsa: qca8k: always enable SGMII auto-negotiation
fixed-link can't work well without this
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1545,11 +1545,10 @@ static int qca8k_pcs_config(struct phyli
return -EINVAL;
}
- /* Enable/disable SerDes auto-negotiation as necessary */
- val = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ?
- 0 : QCA8K_PWS_SERDES_AEN_DIS;
-
- ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8K_PWS_SERDES_AEN_DIS, val);
+ /* Enable SerDes auto-negotiation always.
+ * So fixed-link can work.
+ */
+ ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8K_PWS_SERDES_AEN_DIS, 0);
if (ret)
return ret;

View File

@ -0,0 +1,49 @@
From 8a56ac86c2eed13024413aa23a6cda85613d60f9 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sat, 18 Jan 2025 16:18:40 +0800
Subject: [PATCH 1/2] net: dsa: qca8k: support PHY-to-PHY CPU link
PHY-to-PHY CPU link is a common/demo design in IPQ50xx platform, since it only has a SGMII/SGMII+ link and a MDI link.
For DSA, CPU tag is the only requirement. Fortunately, qca8337 can enable it on any port. So it's ok to trust a PHY-to-PHY link as a CPU link.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1013,7 +1013,7 @@ qca8k_setup_mdio_bus(struct qca8k_priv *
return err;
}
- if (!dsa_is_user_port(priv->ds, reg))
+ if (reg == 0 || reg == 6)
continue;
of_get_phy_mode(port, &mode);
@@ -1088,17 +1088,19 @@ qca8k_setup_mac_pwr_sel(struct qca8k_pri
static int qca8k_find_cpu_port(struct dsa_switch *ds)
{
- struct qca8k_priv *priv = ds->priv;
+ int i;
- /* Find the connected cpu port. Valid port are 0 or 6 */
if (dsa_is_cpu_port(ds, 0))
return 0;
- dev_dbg(priv->dev, "port 0 is not the CPU port. Checking port 6");
-
if (dsa_is_cpu_port(ds, 6))
return 6;
+ /* PHY-to-PHY link */
+ for (i = 1; i <= 5; i++)
+ if (dsa_is_cpu_port(ds, i))
+ return i;
+
return -EINVAL;
}

View File

@ -0,0 +1,202 @@
From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Date: Fri, 10 Nov 2023 14:49:29 +0530
Subject: [PATCH] dt-bindings: remoteproc: qcom: Add support for multipd model
Add new binding document for multipd model remoteproc.
IPQ5332, IPQ9574 follows multipd model.
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,multipd-pil.yaml
@@ -0,0 +1,189 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/remoteproc/qcom,multipd-pil.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Multipd Secure Peripheral Image Loader
+
+maintainers:
+ - Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
+
+description:
+ Multipd Peripheral Image Loader loads firmware and boots Q6 protection domain,
+ WCSS protection domain remoteproc's on the Qualcomm IPQ9574, IPQ5332 SoC.
+ Protection domain is similar to process in Linux. Here QDSP6 processor runs
+ each wifi radio functionality on a separate process. One process can't access
+ other process resources, so this is termed as PD i.e protection domain.
+
+properties:
+ compatible:
+ enum:
+ - qcom,ipq5332-q6-mpd
+ - qcom,ipq9574-q6-mpd
+
+ reg:
+ maxItems: 1
+
+ firmware-name:
+ maxItems: 2
+
+ interrupts:
+ items:
+ - description: Watchdog interrupt
+ - description: Fatal interrupt
+ - description: Ready interrupt
+ - description: Handover interrupt
+ - description: Stop acknowledge interrupt
+
+ interrupt-names:
+ items:
+ - const: wdog
+ - const: fatal
+ - const: ready
+ - const: handover
+ - const: stop-ack
+
+ qcom,smem-states:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: States used by the AP to signal the remote processor
+ items:
+ - description: Shutdown Q6
+ - description: Stop Q6
+
+ qcom,smem-state-names:
+ description:
+ Names of the states used by the AP to signal the remote processor
+ items:
+ - const: shutdown
+ - const: stop
+
+ memory-region:
+ items:
+ - description: Q6 reserved region
+
+ glink-edge:
+ $ref: /schemas/remoteproc/qcom,glink-edge.yaml#
+ description:
+ Qualcomm G-Link subnode which represents communication edge, channels
+ and devices related to the Modem.
+ unevaluatedProperties: false
+
+patternProperties:
+ "^pd-1|pd-2|pd-3":
+ type: object
+ description:
+ WCSS means 'wireless connectivity sub system', in simple words it's a
+ wifi radio block. In multipd model, Q6 root protection domain will
+ provide services to WCSS user protection domain. In other words WCSS
+ protection domains depends on Q6 protection domain i.e Q6 should be up
+ before WCSS, similarly Q6 should shut down after all WCSS domains are
+ down. It can be achieved by building parent-child topology between Q6
+ and WCSS domain's i.e keeping wcss node as sub node of Q6 device node.
+
+ properties:
+ firmware-name:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: Fatal interrupt
+ - description: Ready interrupt
+ - description: Spawn acknowledge interrupt
+ - description: Stop acknowledge interrupt
+
+ interrupt-names:
+ items:
+ - const: fatal
+ - const: ready
+ - const: spawn-ack
+ - const: stop-ack
+
+ qcom,smem-states:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: States used by the AP to signal the remote processor
+ items:
+ - description: Shutdown WCSS pd
+ - description: Stop WCSS pd
+ - description: Spawn WCSS pd
+
+ qcom,smem-state-names:
+ description:
+ Names of the states used by the AP to signal the remote processor
+ items:
+ - const: shutdown
+ - const: stop
+ - const: spawn
+
+ required:
+ - firmware-name
+ - interrupts
+ - interrupt-names
+ - qcom,smem-states
+ - qcom,smem-state-names
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - firmware-name
+ - reg
+ - interrupts
+ - interrupt-names
+ - qcom,smem-states
+ - qcom,smem-state-names
+ - memory-region
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ q6v5_wcss: remoteproc@d100000 {
+ compatible = "qcom,ipq5332-q6-mpd";
+ reg = <0xd100000 0x4040>;
+ firmware-name = "ath11k/IPQ5332/hw1.0/q6_fw0.mdt",
+ "ath11k/IPQ5332/hw1.0/iu_fw.mdt";
+ interrupts-extended = <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>,
+ <&wcss_smp2p_in 0 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 1 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 2 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 3 IRQ_TYPE_NONE>;
+ interrupt-names = "wdog",
+ "fatal",
+ "ready",
+ "handover",
+ "stop-ack";
+
+ qcom,smem-states = <&wcss_smp2p_out 0>,
+ <&wcss_smp2p_out 1>;
+ qcom,smem-state-names = "shutdown",
+ "stop";
+
+ memory-region = <&q6_region>;
+
+ glink-edge {
+ interrupts = <GIC_SPI 417 IRQ_TYPE_EDGE_RISING>;
+ label = "rtr";
+ qcom,remote-pid = <1>;
+ mboxes = <&apcs_glb 8>;
+ };
+
+ pd-1 {
+ firmware-name = "ath11k/IPQ5332/hw1.0/q6_fw1.mdt";
+ interrupts-extended = <&wcss_smp2p_in 8 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 9 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 12 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 11 IRQ_TYPE_NONE>;
+ interrupt-names = "fatal",
+ "ready",
+ "spawn-ack",
+ "stop-ack";
+ qcom,smem-states = <&wcss_smp2p_out 8>,
+ <&wcss_smp2p_out 9>,
+ <&wcss_smp2p_out 10>;
+ qcom,smem-state-names = "shutdown",
+ "stop",
+ "spawn";
+ };
+ };

View File

@ -0,0 +1,43 @@
From 50799703c6c8ec0860e19b102dd7cca3d29028e1 Mon Sep 17 00:00:00 2001
From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Date: Fri, 10 Nov 2023 14:49:34 +0530
Subject: [PATCH] firmware: qcom_scm: ipq5332: add support to pass
metadata size
IPQ5332 security software running under trustzone
requires metadata size. With V2 cmd, pass metadata
size as well.
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
---
drivers/firmware/qcom_scm.c | 8 ++++++++
drivers/firmware/qcom_scm.h | 1 +
2 files changed, 9 insertions(+)
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -592,6 +592,14 @@ int qcom_scm_pas_mem_setup(u32 periphera
if (ret)
goto disable_clk;
+ if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PAS_INIT_IMAGE_V2)) {
+ desc.cmd = QCOM_SCM_PAS_INIT_IMAGE_V2;
+ desc.arginfo =
+ QCOM_SCM_ARGS(3, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL);
+ desc.args[2] = size;
+ }
+
ret = qcom_scm_call(__scm->dev, &desc, &res);
qcom_scm_bw_disable();
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -92,6 +92,7 @@ extern int scm_legacy_call(struct device
#define QCOM_SCM_SVC_PIL 0x02
#define QCOM_SCM_PIL_PAS_INIT_IMAGE 0x01
+#define QCOM_SCM_PAS_INIT_IMAGE_V2 0x1a
#define QCOM_SCM_PIL_PAS_MEM_SETUP 0x02
#define QCOM_SCM_PIL_PAS_AUTH_AND_RESET 0x05
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06

View File

@ -0,0 +1,126 @@
From 217fbbc122663c5a3dac752cebef44fb3e0cc179 Mon Sep 17 00:00:00 2001
From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Date: Fri, 10 Nov 2023 14:49:35 +0530
Subject: [PATCH] firmware: qcom_scm: ipq5332: add msa lock/unlock
support
IPQ5332 user pd remoteproc firmwares need to be locked
with MSA(modem secure access) features. This patch add
support to lock/unlock MSA features.
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
---
drivers/firmware/qcom_scm.c | 78 ++++++++++++++++++++++++++
drivers/firmware/qcom_scm.h | 2 +
include/linux/firmware/qcom/qcom_scm.h | 2 +
3 files changed, 82 insertions(+)
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -712,6 +712,84 @@ bool qcom_scm_pas_supported(u32 peripher
}
EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
+/**
+ * qcom_scm_msa_lock() - Lock given peripheral firmware region as MSA
+ *
+ * @peripheral: peripheral id
+ *
+ * Return 0 on success.
+ */
+int qcom_scm_msa_lock(u32 peripheral)
+{
+ int ret;
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_MSA_LOCK,
+ .arginfo = QCOM_SCM_ARGS(1),
+ .args[0] = peripheral,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+
+ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_MSA_LOCK))
+ return 0;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+ qcom_scm_bw_disable();
+ qcom_scm_clk_disable();
+
+ return ret ? : res.result[0];
+}
+EXPORT_SYMBOL_GPL(qcom_scm_msa_lock);
+
+/**
+ * qcom_scm_msa_unlock() - Unlock given peripheral MSA firmware region
+ *
+ * @peripheral: peripheral id
+ *
+ * Return 0 on success.
+ */
+int qcom_scm_msa_unlock(u32 peripheral)
+{
+ int ret;
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_MSA_UNLOCK,
+ .arginfo = QCOM_SCM_ARGS(1),
+ .args[0] = peripheral,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+
+ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_MSA_UNLOCK))
+ return 0;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+ qcom_scm_bw_disable();
+ qcom_scm_clk_disable();
+
+ return ret ? : res.result[0];
+}
+EXPORT_SYMBOL_GPL(qcom_scm_msa_unlock);
+
static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
{
struct qcom_scm_desc desc = {
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -98,6 +98,8 @@ extern int scm_legacy_call(struct device
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+#define QCOM_SCM_MSA_LOCK 0x24
+#define QCOM_SCM_MSA_UNLOCK 0x25
#define QCOM_SCM_SVC_IO 0x05
#define QCOM_SCM_IO_READ 0x01
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -81,6 +81,8 @@ extern int qcom_scm_pas_mem_setup(u32 pe
extern int qcom_scm_pas_auth_and_reset(u32 peripheral);
extern int qcom_scm_pas_shutdown(u32 peripheral);
extern bool qcom_scm_pas_supported(u32 peripheral);
+extern int qcom_scm_msa_lock(u32 peripheral);
+extern int qcom_scm_msa_unlock(u32 peripheral);
extern int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);
extern int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);

View File

@ -0,0 +1,148 @@
From cae691d32306966065df869fa7424728d1b16b14 Mon Sep 17 00:00:00 2001
From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Date: Fri, 10 Nov 2023 14:49:36 +0530
Subject: [PATCH] remoteproc: qcom: q6v5: Add multipd interrupts support
In multipd model, root & user pd remoteproc's interrupts are
different. User pd needs additional interrupts like spawn.
Instead of going with qcom_q6v5_init(), we defined a new
function to register userpd rproc interrupts in mpd driver.
Since userpd rproc uses some of common interrupts like fatal,
ready, static is removed from ISR handler and used in userpd
interrupt registration.
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
---
drivers/remoteproc/qcom_q6v5.c | 41 +++++++++++++++++++++++++++++++---
drivers/remoteproc/qcom_q6v5.h | 11 +++++++++
2 files changed, 49 insertions(+), 3 deletions(-)
--- a/drivers/remoteproc/qcom_q6v5.c
+++ b/drivers/remoteproc/qcom_q6v5.c
@@ -112,7 +112,7 @@ static irqreturn_t q6v5_wdog_interrupt(i
return IRQ_HANDLED;
}
-static irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
+irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
{
struct qcom_q6v5 *q6v5 = data;
size_t len;
@@ -132,8 +132,9 @@ static irqreturn_t q6v5_fatal_interrupt(
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL(q6v5_fatal_interrupt);
-static irqreturn_t q6v5_ready_interrupt(int irq, void *data)
+irqreturn_t q6v5_ready_interrupt(int irq, void *data)
{
struct qcom_q6v5 *q6v5 = data;
@@ -141,6 +142,7 @@ static irqreturn_t q6v5_ready_interrupt(
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL(q6v5_ready_interrupt);
/**
* qcom_q6v5_wait_for_start() - wait for remote processor start signal
@@ -177,7 +179,17 @@ static irqreturn_t q6v5_handover_interru
return IRQ_HANDLED;
}
-static irqreturn_t q6v5_stop_interrupt(int irq, void *data)
+irqreturn_t q6v5_spawn_interrupt(int irq, void *data)
+{
+ struct qcom_q6v5 *q6v5 = data;
+
+ complete(&q6v5->spawn_done);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(q6v5_spawn_interrupt);
+
+irqreturn_t q6v5_stop_interrupt(int irq, void *data)
{
struct qcom_q6v5 *q6v5 = data;
@@ -185,6 +197,7 @@ static irqreturn_t q6v5_stop_interrupt(i
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL(q6v5_stop_interrupt);
/**
* qcom_q6v5_request_stop() - request the remote processor to stop
@@ -215,6 +228,28 @@ int qcom_q6v5_request_stop(struct qcom_q
EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop);
/**
+ * qcom_q6v5_request_spawn() - request the remote processor to spawn
+ * @q6v5: reference to qcom_q6v5 context
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5)
+{
+ int ret;
+
+ ret = qcom_smem_state_update_bits(q6v5->spawn_state,
+ BIT(q6v5->spawn_bit), BIT(q6v5->spawn_bit));
+
+ ret = wait_for_completion_timeout(&q6v5->spawn_done, 5 * HZ);
+
+ qcom_smem_state_update_bits(q6v5->spawn_state,
+ BIT(q6v5->spawn_bit), 0);
+
+ return ret == 0 ? -ETIMEDOUT : 0;
+}
+EXPORT_SYMBOL_GPL(qcom_q6v5_request_spawn);
+
+/**
* qcom_q6v5_panic() - panic handler to invoke a stop on the remote
* @q6v5: reference to qcom_q6v5 context
*
--- a/drivers/remoteproc/qcom_q6v5.h
+++ b/drivers/remoteproc/qcom_q6v5.h
@@ -18,21 +18,27 @@ struct qcom_q6v5 {
struct qcom_smem_state *state;
struct qmp *qmp;
+ struct qcom_smem_state *shutdown_state;
+ struct qcom_smem_state *spawn_state;
struct icc_path *path;
unsigned stop_bit;
+ unsigned shutdown_bit;
+ unsigned spawn_bit;
int wdog_irq;
int fatal_irq;
int ready_irq;
int handover_irq;
int stop_irq;
+ int spawn_irq;
bool handover_issued;
struct completion start_done;
struct completion stop_done;
+ struct completion spawn_done;
int crash_reason;
@@ -50,7 +56,12 @@ void qcom_q6v5_deinit(struct qcom_q6v5 *
int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5);
int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5);
int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon);
+int qcom_q6v5_request_spawn(struct qcom_q6v5 *q6v5);
int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout);
unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5);
+irqreturn_t q6v5_fatal_interrupt(int irq, void *data);
+irqreturn_t q6v5_ready_interrupt(int irq, void *data);
+irqreturn_t q6v5_spawn_interrupt(int irq, void *data);
+irqreturn_t q6v5_stop_interrupt(int irq, void *data);
#endif

View File

@ -0,0 +1,901 @@
From 3f61ff1fb5c90f8b6c28a3a2b4a29203000ee585 Mon Sep 17 00:00:00 2001
From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
Date: Fri, 10 Nov 2023 14:49:37 +0530
Subject: [PATCH] remoteproc: qcom: Add Hexagon based multipd rproc
driver
It adds support to bring up remoteproc's on multipd model.
Pd means protection domain. It's similar to process in Linux.
Here QDSP6 processor runs each wifi radio functionality on a
separate process. One process can't access other process
resources, so this is termed as PD i.e protection domain.
Here we have two pd's called root and user pd. We can correlate
Root pd as root and user pd as user in linux. Root pd has more
privileges than user pd. Root will provide services to user pd.
>From remoteproc driver perspective, root pd corresponds to QDSP6
processor bring up and user pd corresponds to Wifi radio (WCSS)
bring up.
Here WCSS(user) PD is dependent on Q6(root) PD, so first
q6 pd should be up before wcss pd. After wcss pd goes down,
q6 pd should be turned off.
APPS QDSP6
------- -------------
| | Crash notification | | ------
| |<---------------------|----------|-------|User|
| | | | |->|PD1 |
| | | ------- | | ------
| | | | | | |
|Root | Start/stop Q6 | | R | | |
|PD |<---------------------|->| | | |
|rproc| Crash notification | | O | | |
| | | | | | |
|User |Start/stop UserPD1 | | O | | |
|PD1 |----------------------|->| |-|----|
|rproc| | | T | | |
| | | | | | |
|User |Start/stop UserPD2 | | P | | |
|PD2 |----------------------|->| |-|----|
|rproc| | | D | | |
| | | ------- | | ------
| | Crash notification | | |->|User|
| |<---------------------|----------|-------|PD2 |
------- | | ------
------------
IPQ5332, IPQ9574 supports multipd remoteproc driver.
Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
---
drivers/remoteproc/Kconfig | 19 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/qcom_q6v5_mpd.c | 802 +++++++++++++++++++++++++++++
3 files changed, 822 insertions(+)
create mode 100644 drivers/remoteproc/qcom_q6v5_mpd.c
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -234,6 +234,25 @@ config QCOM_Q6V5_PAS
CDSP (Compute DSP), MPSS (Modem Peripheral SubSystem), and
SLPI (Sensor Low Power Island).
+config QCOM_Q6V5_MPD
+ tristate "Qualcomm Hexagon based MPD model Peripheral Image Loader"
+ depends on OF && ARCH_QCOM
+ depends on QCOM_SMEM
+ depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n
+ depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
+ depends on QCOM_SYSMON || QCOM_SYSMON=n
+ depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n
+ depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n
+ select QCOM_MDT_LOADER
+ select QCOM_PIL_INFO
+ select QCOM_Q6V5_COMMON
+ select QCOM_RPROC_COMMON
+ select QCOM_SCM
+ help
+ Say y here to support the Qualcomm Secure Peripheral Image Loader
+ for the Hexagon based MultiPD model remote processors on e.g. IPQ5018.
+ This is trustZone wireless subsystem.
+
config QCOM_Q6V5_WCSS
tristate "Qualcomm Hexagon based WCSS Peripheral Image Loader"
depends on OF && ARCH_QCOM
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil
obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o
obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o
+obj-$(CONFIG_QCOM_Q6V5_MPD) += qcom_q6v5_mpd.o
obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o
obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o
obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o
--- /dev/null
+++ b/drivers/remoteproc/qcom_q6v5_mpd.c
@@ -0,0 +1,802 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016-2018 Linaro Ltd.
+ * Copyright (C) 2014 Sony Mobile Communications AB
+ * Copyright (c) 2012-2018, 2021 The Linux Foundation. All rights reserved.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/soc/qcom/mdt_loader.h>
+#include <linux/soc/qcom/smem.h>
+#include <linux/soc/qcom/smem_state.h>
+#include "qcom_common.h"
+#include "qcom_q6v5.h"
+
+#include "remoteproc_internal.h"
+
+#define WCSS_CRASH_REASON 421
+#define WCSS_SMEM_HOST 1
+
+#define WCNSS_PAS_ID 6
+#define MPD_WCNSS_PAS_ID 0xD
+
+#define BUF_SIZE 35
+
+#define MAX_FIRMWARE 3
+
+#define RPD_SWID MPD_WCNSS_PAS_ID
+#define UPD_SWID 0x12
+#define REMOTE_PID 1
+#define UPD_BOOT_INFO_SMEM_SIZE 4096
+#define UPD_BOOT_INFO_HEADER_TYPE 0x2
+#define UPD_BOOT_INFO_SMEM_ID 507
+#define VERSION2 2
+
+static LIST_HEAD(upd_rproc_list);
+enum {
+ Q6_IPQ,
+ WCSS_IPQ,
+};
+
+/**
+ * struct userpd_boot_info_header - header of user pd bootinfo
+ * @type: type of bootinfo passing over smem
+ * @length: length of header in bytes
+ */
+struct userpd_boot_info_header {
+ u8 type;
+ u8 length;
+};
+
+/**
+ * struct userpd_boot_info - holds info required to boot user pd
+ * @header: pointer to header
+ * @pid: unique id represents each user pd process
+ * @bootaddr: load address of user pd firmware
+ * @data_size: user pd firmware memory size
+ */
+struct userpd_boot_info {
+ struct userpd_boot_info_header header;
+ u8 pid;
+ u32 bootaddr;
+ u32 data_size;
+} __packed;
+
+struct q6_wcss {
+ struct device *dev;
+ struct qcom_rproc_glink glink_subdev;
+ struct qcom_rproc_ssr ssr_subdev;
+ struct qcom_q6v5 q6;
+ phys_addr_t mem_phys;
+ phys_addr_t mem_reloc;
+ void *mem_region;
+ size_t mem_size;
+ u8 pd_asid;
+ const struct wcss_data *desc;
+ const char **firmware;
+ u32 version;
+};
+
+struct wcss_data {
+ u32 pasid;
+ bool share_upd_info_to_q6;
+};
+
+/**
+ * qcom_get_pd_asid() - get the pd asid number from PD spawn bit
+ * @rproc: rproc handle
+ *
+ * Returns asid on success
+ */
+static u8 qcom_get_pd_asid(struct rproc *rproc)
+{
+ struct q6_wcss *wcss = rproc->priv;
+ u8 bit = wcss->q6.spawn_bit;
+
+ return bit / 8;
+}
+
+static int q6_wcss_start(struct rproc *rproc)
+{
+ struct q6_wcss *wcss = rproc->priv;
+ int ret;
+ const struct wcss_data *desc = wcss->desc;
+
+ qcom_q6v5_prepare(&wcss->q6);
+
+ ret = qcom_scm_pas_auth_and_reset(desc->pasid);
+ if (ret) {
+ dev_err(wcss->dev, "wcss_reset failed\n");
+ return ret;
+ }
+
+ ret = qcom_q6v5_wait_for_start(&wcss->q6, 5 * HZ);
+ if (ret == -ETIMEDOUT)
+ dev_err(wcss->dev, "start timed out\n");
+
+ return ret;
+}
+
+static int q6_wcss_spawn_pd(struct rproc *rproc)
+{
+ int ret;
+ struct q6_wcss *wcss = rproc->priv;
+
+ ret = qcom_q6v5_request_spawn(&wcss->q6);
+ if (ret == -ETIMEDOUT) {
+ dev_err(wcss->dev, "%s spawn timedout\n", rproc->name);
+ return ret;
+ }
+
+ ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000));
+ if (ret == -ETIMEDOUT) {
+ dev_err(wcss->dev, "%s start timedout\n", rproc->name);
+ wcss->q6.running = false;
+ return ret;
+ }
+ wcss->q6.running = true;
+ return ret;
+}
+
+static int wcss_pd_start(struct rproc *rproc)
+{
+ struct q6_wcss *wcss = rproc->priv;
+ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
+ int ret;
+
+ ret = qcom_scm_msa_lock(pasid);
+ if (ret) {
+ dev_err(wcss->dev, "failed to power up pd\n");
+ return ret;
+ }
+
+ if (wcss->q6.spawn_bit) {
+ ret = q6_wcss_spawn_pd(rproc);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int q6_wcss_stop(struct rproc *rproc)
+{
+ struct q6_wcss *wcss = rproc->priv;
+ const struct wcss_data *desc = wcss->desc;
+ int ret;
+
+ ret = qcom_scm_pas_shutdown(desc->pasid);
+ if (ret) {
+ dev_err(wcss->dev, "not able to shutdown\n");
+ return ret;
+ }
+ qcom_q6v5_unprepare(&wcss->q6);
+
+ return 0;
+}
+
+/**
+ * wcss_pd_stop() - Stop WCSS user pd
+ * @rproc: rproc handle
+ *
+ * Stop root pd after user pd down. Root pd
+ * is used to provide services to user pd, so
+ * keeping root pd alive when user pd is down
+ * is invalid.
+ * ---------------------------------------------
+ *
+ * -----------
+ * |-------->| User PD1 |
+ * | -----------
+ * |
+ * |
+ * ----- | -----------
+ * | Q6 |---------------->| User Pd2 |
+ * ----- | -----------
+ * |
+ * |
+ * | -----------
+ * |--------->| User Pd3 |
+ * -----------
+ * ----------------------------------------------
+ */
+static int wcss_pd_stop(struct rproc *rproc)
+{
+ struct q6_wcss *wcss = rproc->priv;
+ struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
+ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
+ int ret;
+
+ if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) {
+ ret = qcom_q6v5_request_stop(&wcss->q6, NULL);
+ if (ret) {
+ dev_err(&rproc->dev, "pd not stopped\n");
+ return ret;
+ }
+ }
+
+ ret = qcom_scm_msa_unlock(pasid);
+ if (ret) {
+ dev_err(wcss->dev, "failed to power down pd\n");
+ return ret;
+ }
+
+ rproc_shutdown(rpd_rproc);
+
+ return 0;
+}
+
+static void *q6_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len,
+ bool *is_iomem)
+{
+ struct q6_wcss *wcss = rproc->priv;
+ int offset;
+
+ offset = da - wcss->mem_reloc;
+ if (offset < 0 || offset + len > wcss->mem_size)
+ return NULL;
+
+ return wcss->mem_region + offset;
+}
+
+/**
+ * share_upd_bootinfo_to_q6() - Share userpd boot info to Q6 root pd
+ * @rproc: rproc handle
+ *
+ * Q6 needs user pd parameters like loadaddress and
+ * PIL size to authenticate user pd with underlying
+ * security software. If authenticatoin success then
+ * only Q6 spawns user pd and sends spawn ack to rproc
+ * driver. This API is passing userpd boot info to Q6
+ * over SMEM.
+ *
+ * User pd boot-info format mentioned below
+ * <Version> <No of elements passing over smem> <Header type> <Header Length>
+ * <Process Id> <Load address> <firmware mem Size>
+ *
+ * Returns 0 on success else negative value on failure.
+ */
+static int share_upd_bootinfo_to_q6(struct rproc *rproc)
+{
+ int ret;
+ size_t size;
+ u16 cnt = 0, version;
+ void *ptr;
+ struct q6_wcss *wcss = rproc->priv, *upd_wcss;
+ struct rproc *upd_rproc;
+ struct userpd_boot_info upd_bootinfo = {0};
+ const struct firmware *fw;
+
+ ret = qcom_smem_alloc(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID,
+ UPD_BOOT_INFO_SMEM_SIZE);
+ if (ret && ret != -EEXIST) {
+ dev_err(wcss->dev,
+ "failed to allocate q6 bootinfo smem segment\n");
+ return ret;
+ }
+
+ ptr = qcom_smem_get(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID, &size);
+ if (IS_ERR(ptr) || size != UPD_BOOT_INFO_SMEM_SIZE) {
+ dev_err(wcss->dev,
+ "Unable to acquire smp2p item(%d) ret:%ld\n",
+ UPD_BOOT_INFO_SMEM_ID, PTR_ERR(ptr));
+ return PTR_ERR(ptr);
+ }
+
+ /*Version*/
+ version = VERSION2;
+ memcpy_toio(ptr, &version, sizeof(version));
+ ptr += sizeof(version);
+
+ list_for_each_entry(upd_rproc, &upd_rproc_list, node)
+ cnt++;
+
+ /* No of elements */
+ cnt = (sizeof(upd_bootinfo) * cnt);
+ memcpy_toio(ptr, &cnt, sizeof(u16));
+ ptr += sizeof(u16);
+
+ list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
+ upd_wcss = upd_rproc->priv;
+
+ /* TYPE */
+ upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE;
+
+ /* LENGTH */
+ upd_bootinfo.header.length =
+ sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header);
+
+ /* Process ID */
+ upd_bootinfo.pid = upd_wcss->pd_asid + 1;
+
+ ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev);
+ if (ret < 0) {
+ dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Load address */
+ upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw);
+
+ /* Firmware mem size */
+ upd_bootinfo.data_size = qcom_mdt_get_size(fw);
+
+ release_firmware(fw);
+
+ /* copy into smem */
+ memcpy_toio(ptr, &upd_bootinfo, sizeof(upd_bootinfo));
+ ptr += sizeof(upd_bootinfo);
+ }
+ return 0;
+}
+
+static int q6_wcss_load(struct rproc *rproc, const struct firmware *fw)
+{
+ struct q6_wcss *wcss = rproc->priv;
+ const struct firmware *fw_hdl;
+ int ret;
+ const struct wcss_data *desc = wcss->desc;
+ int loop;
+
+ /* Share user pd boot info to Q6 remote processor */
+ if (desc->share_upd_info_to_q6) {
+ ret = share_upd_bootinfo_to_q6(rproc);
+ if (ret) {
+ dev_err(wcss->dev,
+ "user pd boot info sharing with q6 failed %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware,
+ desc->pasid, wcss->mem_region,
+ wcss->mem_phys, wcss->mem_size,
+ &wcss->mem_reloc);
+ if (ret)
+ return ret;
+
+ for (loop = 1; loop < MAX_FIRMWARE; loop++) {
+ if (!wcss->firmware[loop])
+ continue;
+
+ ret = request_firmware(&fw_hdl, wcss->firmware[loop],
+ wcss->dev);
+ if (ret)
+ continue;
+
+ ret = qcom_mdt_load_no_init(wcss->dev, fw_hdl,
+ wcss->firmware[loop], 0,
+ wcss->mem_region,
+ wcss->mem_phys,
+ wcss->mem_size,
+ &wcss->mem_reloc);
+
+ release_firmware(fw_hdl);
+
+ if (ret) {
+ dev_err(wcss->dev,
+ "can't load %s ret:%d\n", wcss->firmware[loop], ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/**
+ * wcss_pd_load() - Load WCSS user pd firmware
+ * @rproc: rproc handle
+ * @fw: firmware handle
+ *
+ * User pd get services from root pd. So first
+ * bring up root pd and then load userpd firmware.
+ * ---------------------------------------------
+ *
+ * -----------
+ * |-------->| User PD1 |
+ * | -----------
+ * |
+ * |
+ * ----- | -----------
+ * | Q6 |---------------->| User Pd2 |
+ * ----- | -----------
+ * |
+ * |
+ * | -----------
+ * |--------->| User Pd3 |
+ * -----------
+ * ----------------------------------------------
+ *
+ */
+static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw)
+{
+ struct q6_wcss *wcss = rproc->priv;
+ struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
+ u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
+ int ret;
+
+ ret = rproc_boot(rpd_rproc);
+ if (ret)
+ return ret;
+
+ return qcom_mdt_load(wcss->dev, fw, rproc->firmware,
+ pasid, wcss->mem_region,
+ wcss->mem_phys, wcss->mem_size,
+ &wcss->mem_reloc);
+}
+
+static unsigned long q6_wcss_panic(struct rproc *rproc)
+{
+ struct q6_wcss *wcss = rproc->priv;
+
+ return qcom_q6v5_panic(&wcss->q6);
+}
+
+static const struct rproc_ops wcss_ops = {
+ .start = wcss_pd_start,
+ .stop = wcss_pd_stop,
+ .load = wcss_pd_load,
+ .get_boot_addr = rproc_elf_get_boot_addr,
+};
+
+static const struct rproc_ops q6_wcss_ops = {
+ .start = q6_wcss_start,
+ .stop = q6_wcss_stop,
+ .da_to_va = q6_wcss_da_to_va,
+ .load = q6_wcss_load,
+ .get_boot_addr = rproc_elf_get_boot_addr,
+ .panic = q6_wcss_panic,
+};
+
+static int q6_alloc_memory_region(struct q6_wcss *wcss)
+{
+ struct reserved_mem *rmem = NULL;
+ struct device_node *node;
+ struct device *dev = wcss->dev;
+
+ if (wcss->version == Q6_IPQ) {
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (node)
+ rmem = of_reserved_mem_lookup(node);
+
+ of_node_put(node);
+
+ if (!rmem) {
+ dev_err(dev, "unable to acquire memory-region\n");
+ return -EINVAL;
+ }
+ } else {
+ struct rproc *rpd_rproc = dev_get_drvdata(dev->parent);
+ struct q6_wcss *rpd_wcss = rpd_rproc->priv;
+
+ wcss->mem_phys = rpd_wcss->mem_phys;
+ wcss->mem_reloc = rpd_wcss->mem_reloc;
+ wcss->mem_size = rpd_wcss->mem_size;
+ wcss->mem_region = rpd_wcss->mem_region;
+ return 0;
+ }
+
+ wcss->mem_phys = rmem->base;
+ wcss->mem_reloc = rmem->base;
+ wcss->mem_size = rmem->size;
+ wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
+ if (!wcss->mem_region) {
+ dev_err(dev, "unable to map memory region: %pa+%pa\n",
+ &rmem->base, &rmem->size);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int q6_get_inbound_irq(struct qcom_q6v5 *q6,
+ struct platform_device *pdev,
+ const char *int_name,
+ int index, int *pirq,
+ irqreturn_t (*handler)(int irq, void *data))
+{
+ int ret, irq;
+ char *interrupt, *tmp = (char *)int_name;
+ struct q6_wcss *wcss = q6->rproc->priv;
+
+ irq = platform_get_irq(pdev, index);
+ if (irq < 0)
+ return irq;
+
+ *pirq = irq;
+
+ interrupt = devm_kzalloc(&pdev->dev, BUF_SIZE, GFP_KERNEL);
+ if (!interrupt)
+ return -ENOMEM;
+
+ snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp);
+
+ ret = devm_request_threaded_irq(&pdev->dev, *pirq,
+ NULL, handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ interrupt, q6);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to acquire %s irq\n", interrupt);
+ return 0;
+}
+
+static int q6_get_outbound_irq(struct qcom_q6v5 *q6,
+ struct platform_device *pdev,
+ const char *int_name)
+{
+ struct qcom_smem_state *tmp_state;
+ unsigned bit;
+
+ tmp_state = qcom_smem_state_get(&pdev->dev, int_name, &bit);
+ if (IS_ERR(tmp_state))
+ return dev_err_probe(&pdev->dev, PTR_ERR(tmp_state),
+ "failed to acquire %s state\n", int_name);
+
+ if (!strcmp(int_name, "stop")) {
+ q6->state = tmp_state;
+ q6->stop_bit = bit;
+ } else if (!strcmp(int_name, "spawn")) {
+ q6->spawn_state = tmp_state;
+ q6->spawn_bit = bit;
+ }
+
+ return 0;
+}
+
+static int init_irq(struct qcom_q6v5 *q6,
+ struct platform_device *pdev, struct rproc *rproc,
+ int crash_reason, const char *load_state,
+ void (*handover)(struct qcom_q6v5 *q6))
+{
+ int ret;
+ struct q6_wcss *wcss = rproc->priv;
+
+ q6->rproc = rproc;
+ q6->dev = &pdev->dev;
+ q6->crash_reason = crash_reason;
+ q6->handover = handover;
+
+ init_completion(&q6->start_done);
+ init_completion(&q6->stop_done);
+ init_completion(&q6->spawn_done);
+
+ ret = q6_get_outbound_irq(q6, pdev, "stop");
+ if (ret)
+ return ret;
+
+ ret = q6_get_outbound_irq(q6, pdev, "spawn");
+ if (ret)
+ return ret;
+
+ /* Get pd_asid to prepare interrupt names */
+ wcss->pd_asid = qcom_get_pd_asid(rproc);
+
+ ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq,
+ q6v5_fatal_interrupt);
+ if (ret)
+ return ret;
+
+ ret = q6_get_inbound_irq(q6, pdev, "ready", 1, &q6->ready_irq,
+ q6v5_ready_interrupt);
+ if (ret)
+ return ret;
+
+ ret = q6_get_inbound_irq(q6, pdev, "stop-ack", 3, &q6->stop_irq,
+ q6v5_stop_interrupt);
+ if (ret)
+ return ret;
+
+ ret = q6_get_inbound_irq(q6, pdev, "spawn-ack", 2, &q6->spawn_irq,
+ q6v5_spawn_interrupt);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static void q6_release_resources(void)
+{
+ struct rproc *upd_rproc;
+
+ /* Release userpd resources */
+ list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
+ rproc_del(upd_rproc);
+ rproc_free(upd_rproc);
+ }
+}
+
+static int q6_register_userpd(struct platform_device *pdev,
+ struct device_node *userpd_np)
+{
+ struct q6_wcss *wcss;
+ struct rproc *rproc = NULL;
+ int ret;
+ struct platform_device *userpd_pdev;
+ const char *firmware_name = NULL;
+ const char *label = NULL;
+
+ ret = of_property_read_string(userpd_np, "firmware-name",
+ &firmware_name);
+ if (ret < 0) {
+ /* All userpd's who want to register as rproc must have firmware.
+ * Other than userpd like glink they don't need any firmware.
+ * So for glink child simply return success.
+ */
+ if (ret == -EINVAL) {
+ /* Confirming userpd_np is glink node or not */
+ if (!of_property_read_string(userpd_np, "label", &label))
+ return 0;
+ }
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "%s node found\n", userpd_np->name);
+
+ userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name,
+ &pdev->dev);
+ if (!userpd_pdev)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "failed to create %s platform device\n",
+ userpd_np->name);
+
+ userpd_pdev->dev.driver = pdev->dev.driver;
+ rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops,
+ firmware_name, sizeof(*wcss));
+ if (!rproc) {
+ ret = -ENOMEM;
+ goto free_rproc;
+ }
+
+ wcss = rproc->priv;
+ wcss->dev = &userpd_pdev->dev;
+ wcss->version = WCSS_IPQ;
+
+ ret = q6_alloc_memory_region(wcss);
+ if (ret)
+ goto free_rproc;
+
+ ret = init_irq(&wcss->q6, userpd_pdev, rproc,
+ WCSS_CRASH_REASON, NULL, NULL);
+ if (ret)
+ goto free_rproc;
+
+ rproc->auto_boot = false;
+ ret = rproc_add(rproc);
+ if (ret)
+ goto free_rproc;
+
+ list_add(&rproc->node, &upd_rproc_list);
+ platform_set_drvdata(userpd_pdev, rproc);
+ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name);
+ return 0;
+
+free_rproc:
+ kfree(rproc);
+ return ret;
+}
+
+static int q6_wcss_probe(struct platform_device *pdev)
+{
+ const struct wcss_data *desc;
+ struct q6_wcss *wcss;
+ struct rproc *rproc;
+ int ret;
+ const char **firmware;
+ struct device_node *userpd_np;
+ const struct rproc_ops *ops = &q6_wcss_ops;
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ firmware = devm_kcalloc(&pdev->dev, MAX_FIRMWARE,
+ sizeof(*firmware), GFP_KERNEL);
+ if (!firmware)
+ return -ENOMEM;
+
+ ret = of_property_read_string_array(pdev->dev.of_node, "firmware-name",
+ firmware, MAX_FIRMWARE);
+ if (ret < 0)
+ return ret;
+
+ rproc = rproc_alloc(&pdev->dev, pdev->name, ops,
+ firmware[0], sizeof(*wcss));
+ if (!rproc)
+ return -ENOMEM;
+
+ wcss = rproc->priv;
+ wcss->dev = &pdev->dev;
+ wcss->desc = desc;
+ wcss->firmware = firmware;
+ wcss->version = Q6_IPQ;
+
+ ret = q6_alloc_memory_region(wcss);
+ if (ret)
+ goto free_rproc;
+
+ ret = qcom_q6v5_init(&wcss->q6, pdev, rproc,
+ WCSS_CRASH_REASON, NULL, NULL);
+ if (ret)
+ goto free_rproc;
+
+ qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");
+ qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");
+
+ rproc->auto_boot = false;
+ ret = rproc_add(rproc);
+ if (ret)
+ goto free_rproc;
+
+ platform_set_drvdata(pdev, rproc);
+
+ /* Iterate over userpd child's and register with rproc */
+ for_each_available_child_of_node(pdev->dev.of_node, userpd_np) {
+ ret = q6_register_userpd(pdev, userpd_np);
+ if (ret) {
+ /* release resources of successfully allocated userpd rproc's */
+ q6_release_resources();
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to register userpd(%s)\n",
+ userpd_np->name);
+ }
+ }
+ return 0;
+
+free_rproc:
+ rproc_free(rproc);
+
+ return ret;
+}
+
+static int q6_wcss_remove(struct platform_device *pdev)
+{
+ struct rproc *rproc = platform_get_drvdata(pdev);
+ struct q6_wcss *wcss = rproc->priv;
+
+ qcom_q6v5_deinit(&wcss->q6);
+
+ rproc_del(rproc);
+ rproc_free(rproc);
+
+ return 0;
+}
+
+static const struct wcss_data q6_ipq5332_res_init = {
+ .pasid = MPD_WCNSS_PAS_ID,
+ .share_upd_info_to_q6 = true,
+};
+
+static const struct wcss_data q6_ipq9574_res_init = {
+ .pasid = WCNSS_PAS_ID,
+};
+
+static const struct of_device_id q6_wcss_of_match[] = {
+ { .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init },
+ { .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init },
+ { },
+};
+MODULE_DEVICE_TABLE(of, q6_wcss_of_match);
+
+static struct platform_driver q6_wcss_driver = {
+ .probe = q6_wcss_probe,
+ .remove = q6_wcss_remove,
+ .driver = {
+ .name = "qcom-q6-mpd",
+ .of_match_table = q6_wcss_of_match,
+ },
+};
+module_platform_driver(q6_wcss_driver);
+
+MODULE_DESCRIPTION("Hexagon WCSS Multipd Peripheral Image Loader");
+MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,321 @@
From 6c66dff196cbba8515380110dd3599cde31dd896 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH 1/2] rproc: qcom_q6v5_mpd: split q6_wcss to rootpd and
userpd
Split the q6_wcss structure and create a separate userpd struct to clearly
differentiate between the process to bring up the QDSP6 processor vs
process(es) to bring up the Wifi radio(s) (WCSS) for better readability.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/remoteproc/qcom_q6v5_mpd.c | 126 +++++++++++++----------------
1 file changed, 56 insertions(+), 70 deletions(-)
--- a/drivers/remoteproc/qcom_q6v5_mpd.c
+++ b/drivers/remoteproc/qcom_q6v5_mpd.c
@@ -44,10 +44,6 @@
#define VERSION2 2
static LIST_HEAD(upd_rproc_list);
-enum {
- Q6_IPQ,
- WCSS_IPQ,
-};
/**
* struct userpd_boot_info_header - header of user pd bootinfo
@@ -82,10 +78,15 @@ struct q6_wcss {
phys_addr_t mem_reloc;
void *mem_region;
size_t mem_size;
- u8 pd_asid;
const struct wcss_data *desc;
const char **firmware;
- u32 version;
+};
+
+struct userpd {
+ u8 pd_asid;
+ struct device *dev;
+ struct qcom_rproc_ssr ssr_subdev;
+ struct qcom_q6v5 q6;
};
struct wcss_data {
@@ -101,8 +102,8 @@ struct wcss_data {
*/
static u8 qcom_get_pd_asid(struct rproc *rproc)
{
- struct q6_wcss *wcss = rproc->priv;
- u8 bit = wcss->q6.spawn_bit;
+ struct userpd *upd = rproc->priv;
+ u8 bit = upd->q6.spawn_bit;
return bit / 8;
}
@@ -131,37 +132,37 @@ static int q6_wcss_start(struct rproc *r
static int q6_wcss_spawn_pd(struct rproc *rproc)
{
int ret;
- struct q6_wcss *wcss = rproc->priv;
+ struct userpd *upd = rproc->priv;
- ret = qcom_q6v5_request_spawn(&wcss->q6);
+ ret = qcom_q6v5_request_spawn(&upd->q6);
if (ret == -ETIMEDOUT) {
- dev_err(wcss->dev, "%s spawn timedout\n", rproc->name);
+ dev_err(upd->dev, "%s spawn timedout\n", rproc->name);
return ret;
}
- ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000));
+ ret = qcom_q6v5_wait_for_start(&upd->q6, msecs_to_jiffies(10000));
if (ret == -ETIMEDOUT) {
- dev_err(wcss->dev, "%s start timedout\n", rproc->name);
- wcss->q6.running = false;
+ dev_err(upd->dev, "%s start timedout\n", rproc->name);
+ upd->q6.running = false;
return ret;
}
- wcss->q6.running = true;
+ upd->q6.running = true;
return ret;
}
static int wcss_pd_start(struct rproc *rproc)
{
- struct q6_wcss *wcss = rproc->priv;
- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
+ struct userpd *upd = rproc->priv;
+ u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
int ret;
ret = qcom_scm_msa_lock(pasid);
if (ret) {
- dev_err(wcss->dev, "failed to power up pd\n");
+ dev_err(upd->dev, "failed to power up pd\n");
return ret;
}
- if (wcss->q6.spawn_bit) {
+ if (upd->q6.spawn_bit) {
ret = q6_wcss_spawn_pd(rproc);
if (ret)
return ret;
@@ -213,22 +214,22 @@ static int q6_wcss_stop(struct rproc *rp
*/
static int wcss_pd_stop(struct rproc *rproc)
{
- struct q6_wcss *wcss = rproc->priv;
- struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
+ struct userpd *upd = rproc->priv;
+ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
+ u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
int ret;
- if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) {
- ret = qcom_q6v5_request_stop(&wcss->q6, NULL);
+ if (rproc->state != RPROC_CRASHED && upd->q6.stop_bit) {
+ ret = qcom_q6v5_request_stop(&upd->q6, NULL);
if (ret) {
- dev_err(&rproc->dev, "pd not stopped\n");
+ dev_err(upd->dev, "pd not stopped\n");
return ret;
}
}
ret = qcom_scm_msa_unlock(pasid);
if (ret) {
- dev_err(wcss->dev, "failed to power down pd\n");
+ dev_err(upd->dev, "failed to power down pd\n");
return ret;
}
@@ -273,7 +274,8 @@ static int share_upd_bootinfo_to_q6(stru
size_t size;
u16 cnt = 0, version;
void *ptr;
- struct q6_wcss *wcss = rproc->priv, *upd_wcss;
+ struct q6_wcss *wcss = rproc->priv;
+ struct userpd *upd;
struct rproc *upd_rproc;
struct userpd_boot_info upd_bootinfo = {0};
const struct firmware *fw;
@@ -308,7 +310,7 @@ static int share_upd_bootinfo_to_q6(stru
ptr += sizeof(u16);
list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
- upd_wcss = upd_rproc->priv;
+ upd = upd_rproc->priv;
/* TYPE */
upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE;
@@ -318,11 +320,11 @@ static int share_upd_bootinfo_to_q6(stru
sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header);
/* Process ID */
- upd_bootinfo.pid = upd_wcss->pd_asid + 1;
+ upd_bootinfo.pid = upd->pd_asid + 1;
- ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev);
+ ret = request_firmware(&fw, upd_rproc->firmware, upd->dev);
if (ret < 0) {
- dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret);
+ dev_err(upd->dev, "request_firmware failed: %d\n", ret);
return ret;
}
@@ -421,19 +423,20 @@ static int q6_wcss_load(struct rproc *rp
*/
static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw)
{
- struct q6_wcss *wcss = rproc->priv;
- struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
- u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
+ struct userpd *upd = rproc->priv;
+ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
+ struct q6_wcss *wcss = rpd_rproc->priv;
+ u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
int ret;
ret = rproc_boot(rpd_rproc);
if (ret)
return ret;
- return qcom_mdt_load(wcss->dev, fw, rproc->firmware,
+ return qcom_mdt_load(upd->dev, fw, rproc->firmware,
pasid, wcss->mem_region,
wcss->mem_phys, wcss->mem_size,
- &wcss->mem_reloc);
+ NULL);
}
static unsigned long q6_wcss_panic(struct rproc *rproc)
@@ -465,26 +468,15 @@ static int q6_alloc_memory_region(struct
struct device_node *node;
struct device *dev = wcss->dev;
- if (wcss->version == Q6_IPQ) {
- node = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (node)
- rmem = of_reserved_mem_lookup(node);
-
- of_node_put(node);
-
- if (!rmem) {
- dev_err(dev, "unable to acquire memory-region\n");
- return -EINVAL;
- }
- } else {
- struct rproc *rpd_rproc = dev_get_drvdata(dev->parent);
- struct q6_wcss *rpd_wcss = rpd_rproc->priv;
-
- wcss->mem_phys = rpd_wcss->mem_phys;
- wcss->mem_reloc = rpd_wcss->mem_reloc;
- wcss->mem_size = rpd_wcss->mem_size;
- wcss->mem_region = rpd_wcss->mem_region;
- return 0;
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (node)
+ rmem = of_reserved_mem_lookup(node);
+
+ of_node_put(node);
+
+ if (!rmem) {
+ dev_err(dev, "unable to acquire memory-region\n");
+ return -EINVAL;
}
wcss->mem_phys = rmem->base;
@@ -508,7 +500,7 @@ static int q6_get_inbound_irq(struct qco
{
int ret, irq;
char *interrupt, *tmp = (char *)int_name;
- struct q6_wcss *wcss = q6->rproc->priv;
+ struct userpd *upd = q6->rproc->priv;
irq = platform_get_irq(pdev, index);
if (irq < 0)
@@ -520,7 +512,7 @@ static int q6_get_inbound_irq(struct qco
if (!interrupt)
return -ENOMEM;
- snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp);
+ snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", upd->pd_asid, tmp);
ret = devm_request_threaded_irq(&pdev->dev, *pirq,
NULL, handler,
@@ -561,7 +553,7 @@ static int init_irq(struct qcom_q6v5 *q6
void (*handover)(struct qcom_q6v5 *q6))
{
int ret;
- struct q6_wcss *wcss = rproc->priv;
+ struct userpd *upd = rproc->priv;
q6->rproc = rproc;
q6->dev = &pdev->dev;
@@ -581,7 +573,7 @@ static int init_irq(struct qcom_q6v5 *q6
return ret;
/* Get pd_asid to prepare interrupt names */
- wcss->pd_asid = qcom_get_pd_asid(rproc);
+ upd->pd_asid = qcom_get_pd_asid(rproc);
ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq,
q6v5_fatal_interrupt);
@@ -619,7 +611,7 @@ static void q6_release_resources(void)
static int q6_register_userpd(struct platform_device *pdev,
struct device_node *userpd_np)
{
- struct q6_wcss *wcss;
+ struct userpd *upd;
struct rproc *rproc = NULL;
int ret;
struct platform_device *userpd_pdev;
@@ -652,21 +644,16 @@ static int q6_register_userpd(struct pla
userpd_pdev->dev.driver = pdev->dev.driver;
rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops,
- firmware_name, sizeof(*wcss));
+ firmware_name, sizeof(*upd));
if (!rproc) {
ret = -ENOMEM;
goto free_rproc;
}
- wcss = rproc->priv;
- wcss->dev = &userpd_pdev->dev;
- wcss->version = WCSS_IPQ;
-
- ret = q6_alloc_memory_region(wcss);
- if (ret)
- goto free_rproc;
+ upd = rproc->priv;
+ upd->dev = &userpd_pdev->dev;
- ret = init_irq(&wcss->q6, userpd_pdev, rproc,
+ ret = init_irq(&upd->q6, userpd_pdev, rproc,
WCSS_CRASH_REASON, NULL, NULL);
if (ret)
goto free_rproc;
@@ -678,7 +665,7 @@ static int q6_register_userpd(struct pla
list_add(&rproc->node, &upd_rproc_list);
platform_set_drvdata(userpd_pdev, rproc);
- qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name);
+ qcom_add_ssr_subdev(rproc, &upd->ssr_subdev, userpd_pdev->name);
return 0;
free_rproc:
@@ -719,7 +706,6 @@ static int q6_wcss_probe(struct platform
wcss->dev = &pdev->dev;
wcss->desc = desc;
wcss->firmware = firmware;
- wcss->version = Q6_IPQ;
ret = q6_alloc_memory_region(wcss);
if (ret)

View File

@ -0,0 +1,206 @@
From 0fa7bdb855247b738d1d227d6f4b3417ebdf21a8 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH 2/2] remoteproc: qcom_q6v5_mpd: fix incorrent use of
rproc->node
1.817524] list_add corruption. next->prev should be prev (ffffffc0814bbfc8), but was ffffffc0814bc358. (next=ffffff8003b56800).
[ 1.822435] WARNING: CPU: 1 PID: 24 at lib/list_debug.c:29 __list_add_valid_or_report+0x8c/0xdc
[ 1.833923] Modules linked in:
[ 1.842425] CPU: 1 PID: 24 Comm: kworker/u4:1 Not tainted 6.6.47 #0
[ 1.845552] Hardware name: Qualcomm MP03 (DT)
[ 1.851716] Workqueue: events_unbound deferred_probe_work_func
[ 1.856229] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 1.861959] pc : __list_add_valid_or_report+0x8c/0xdc
[ 1.868816] lr : __list_add_valid_or_report+0x8c/0xdc
[ 1.874022] sp : ffffffc081603a50
[ 1.879055] x29: ffffffc081603a50 x28: ffffff8000fa2810 x27: ffffff8003cba800
[ 1.882358] x26: ffffff8000fa2800 x25: ffffff8003cbac80 x24: 0000000000000000
[ 1.889476] x23: ffffffc08088b968 x22: ffffffc0814bb000 x21: ffffffc0814bbfc8
[ 1.896593] x20: ffffffc08088b8e8 x19: ffffff8003cba800 x18: 00000000000000b1
[ 1.903713] x17: 3863666262343138 x16: 3063666666666666 x15: ffffffc081416e20
[ 1.910830] x14: 0000000000000213 x13: 00000000000000b1 x12: 00000000ffffffea
[ 1.917948] x11: 00000000ffffefff x10: ffffffc08146ee20 x9 : ffffffc081416dc8
[ 1.925066] x8 : 0000000000017fe8 x7 : c0000000ffffefff x6 : 0000000000057fa8
[ 1.932184] x5 : 0000000000000fff x4 : 0000000000000000 x3 : ffffffc081603850
[ 1.939302] x2 : ffffffc081416d60 x1 : ffffffc081416d60 x0 : 0000000000000075
[ 1.946422] Call trace:
[ 1.953535] __list_add_valid_or_report+0x8c/0xdc
[ 1.955793] rproc_add+0x1f4/0x25c
[ 1.960653] q6_wcss_probe+0x510/0x634
[ 1.963950] platform_probe+0x68/0xc4
[ 1.967684] really_probe+0x148/0x2b0
[ 1.971417] __driver_probe_device+0x78/0x128
[ 1.975063] driver_probe_device+0x40/0xdc
[ 1.979402] __device_attach_driver+0xb8/0xf8
[ 1.983397] bus_for_each_drv+0x70/0xb8
[ 1.987823] __device_attach+0xa0/0x184
[ 1.991468] device_initial_probe+0x14/0x20
[ 1.995289] bus_probe_device+0xac/0xb0
[ 1.999455] deferred_probe_work_func+0xa4/0xec
[ 2.003275] process_one_work+0x178/0x2d4
[ 2.007788] worker_thread+0x2ec/0x4d8
[ 2.011954] kthread+0xdc/0xe0
[ 2.015600] ret_from_fork+0x10/0x20
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
drivers/remoteproc/qcom_q6v5_mpd.c | 53 +++++++++++++++++-------------
1 file changed, 30 insertions(+), 23 deletions(-)
--- a/drivers/remoteproc/qcom_q6v5_mpd.c
+++ b/drivers/remoteproc/qcom_q6v5_mpd.c
@@ -33,6 +33,7 @@
#define BUF_SIZE 35
+#define MAX_UPD 3
#define MAX_FIRMWARE 3
#define RPD_SWID MPD_WCNSS_PAS_ID
@@ -43,8 +44,6 @@
#define UPD_BOOT_INFO_SMEM_ID 507
#define VERSION2 2
-static LIST_HEAD(upd_rproc_list);
-
/**
* struct userpd_boot_info_header - header of user pd bootinfo
* @type: type of bootinfo passing over smem
@@ -80,6 +79,7 @@ struct q6_wcss {
size_t mem_size;
const struct wcss_data *desc;
const char **firmware;
+ struct userpd *upd[MAX_UPD];
};
struct userpd {
@@ -270,13 +270,12 @@ static void *q6_wcss_da_to_va(struct rpr
*/
static int share_upd_bootinfo_to_q6(struct rproc *rproc)
{
- int ret;
+ int i, ret;
size_t size;
u16 cnt = 0, version;
void *ptr;
struct q6_wcss *wcss = rproc->priv;
struct userpd *upd;
- struct rproc *upd_rproc;
struct userpd_boot_info upd_bootinfo = {0};
const struct firmware *fw;
@@ -301,16 +300,19 @@ static int share_upd_bootinfo_to_q6(stru
memcpy_toio(ptr, &version, sizeof(version));
ptr += sizeof(version);
- list_for_each_entry(upd_rproc, &upd_rproc_list, node)
- cnt++;
+ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++)
+ if (wcss->upd[i])
+ cnt++;
/* No of elements */
cnt = (sizeof(upd_bootinfo) * cnt);
memcpy_toio(ptr, &cnt, sizeof(u16));
ptr += sizeof(u16);
- list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
- upd = upd_rproc->priv;
+ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) {
+ upd = wcss->upd[i];
+ if (!upd)
+ continue;
/* TYPE */
upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE;
@@ -322,14 +324,14 @@ static int share_upd_bootinfo_to_q6(stru
/* Process ID */
upd_bootinfo.pid = upd->pd_asid + 1;
- ret = request_firmware(&fw, upd_rproc->firmware, upd->dev);
+ ret = request_firmware(&fw, upd->q6.rproc->firmware, upd->dev);
if (ret < 0) {
dev_err(upd->dev, "request_firmware failed: %d\n", ret);
return ret;
}
/* Load address */
- upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw);
+ upd_bootinfo.bootaddr = rproc_get_boot_addr(upd->q6.rproc, fw);
/* Firmware mem size */
upd_bootinfo.data_size = qcom_mdt_get_size(fw);
@@ -597,18 +599,23 @@ static int init_irq(struct qcom_q6v5 *q6
return 0;
}
-static void q6_release_resources(void)
+static void q6_release_resources(struct q6_wcss *wcss)
{
- struct rproc *upd_rproc;
+ struct userpd *upd;
+ int i;
/* Release userpd resources */
- list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
- rproc_del(upd_rproc);
- rproc_free(upd_rproc);
+ for (i = 0; i < ARRAY_SIZE(wcss->upd); i++) {
+ upd = wcss->upd[i];
+ if (!upd)
+ continue;
+
+ rproc_del(upd->q6.rproc);
+ rproc_free(upd->q6.rproc);
}
}
-static int q6_register_userpd(struct platform_device *pdev,
+static int q6_register_userpd(struct q6_wcss *wcss,
struct device_node *userpd_np)
{
struct userpd *upd;
@@ -633,16 +640,16 @@ static int q6_register_userpd(struct pla
return ret;
}
- dev_info(&pdev->dev, "%s node found\n", userpd_np->name);
+ dev_info(wcss->dev, "%s node found\n", userpd_np->name);
userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name,
- &pdev->dev);
+ wcss->dev);
if (!userpd_pdev)
- return dev_err_probe(&pdev->dev, -ENODEV,
+ return dev_err_probe(wcss->dev, -ENODEV,
"failed to create %s platform device\n",
userpd_np->name);
- userpd_pdev->dev.driver = pdev->dev.driver;
+ userpd_pdev->dev.driver = wcss->dev->driver;
rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops,
firmware_name, sizeof(*upd));
if (!rproc) {
@@ -663,7 +670,7 @@ static int q6_register_userpd(struct pla
if (ret)
goto free_rproc;
- list_add(&rproc->node, &upd_rproc_list);
+ wcss->upd[upd->pd_asid] = upd;
platform_set_drvdata(userpd_pdev, rproc);
qcom_add_ssr_subdev(rproc, &upd->ssr_subdev, userpd_pdev->name);
return 0;
@@ -728,10 +735,10 @@ static int q6_wcss_probe(struct platform
/* Iterate over userpd child's and register with rproc */
for_each_available_child_of_node(pdev->dev.of_node, userpd_np) {
- ret = q6_register_userpd(pdev, userpd_np);
+ ret = q6_register_userpd(wcss, userpd_np);
if (ret) {
/* release resources of successfully allocated userpd rproc's */
- q6_release_resources();
+ q6_release_resources(wcss);
return dev_err_probe(&pdev->dev, ret,
"Failed to register userpd(%s)\n",
userpd_np->name);

View File

@ -0,0 +1,128 @@
From 6553d598cdb507f7ede020f25da646ba084a23c6 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH] firmware: qcom_scm: support MPD
Add SCM calls to power up / down the SoC's internal WiFi radio and to
load PIL segments to support the MPD architecture.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/firmware/qcom_scm.c | 79 ++++++++++++++++++++++++++
drivers/firmware/qcom_scm.h | 3 +
include/linux/firmware/qcom/qcom_scm.h | 3 +
3 files changed, 85 insertions(+)
--- a/drivers/firmware/qcom_scm.c
+++ b/drivers/firmware/qcom_scm.c
@@ -713,6 +713,85 @@ bool qcom_scm_pas_supported(u32 peripher
EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
/**
+ * qcom_scm_internal_wifi_powerup() - Bring up internal wifi
+ * @peripheral: peripheral id
+ *
+ * Return 0 on success.
+ */
+int qcom_scm_internal_wifi_powerup(u32 peripheral)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_INTERNAL_WIFI_POWERUP,
+ .arginfo = QCOM_SCM_ARGS(1),
+ .args[0] = peripheral,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+ int ret;
+
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+
+ return ret ? : res.result[0];
+}
+EXPORT_SYMBOL(qcom_scm_internal_wifi_powerup);
+
+/**
+ * qcom_scm_internal_wifi_shutdown() - Shut down internal wifi
+ * @peripheral: peripheral id
+ *
+ * Returns 0 on success.
+ */
+int qcom_scm_internal_wifi_shutdown(u32 peripheral)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_INTERNAL_WIFI_SHUTDOWN,
+ .arginfo = QCOM_SCM_ARGS(1),
+ .args[0] = peripheral,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+ int ret;
+
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+
+ return ret ? : res.result[0];
+}
+EXPORT_SYMBOL(qcom_scm_internal_wifi_shutdown);
+
+/**
+ * qcom_scm_pas_load_segment() - copy userpd PIL segments data to dma blocks
+ * @peripheral: peripheral id
+ * @segment: segment id
+ * @dma: handle of dma region
+ * @seg_cnt: no of dma blocks
+ *
+ * Returns 0 if trustzone successfully loads userpd PIL segments from dma
+ * blocks to DDR
+ */
+int qcom_scm_pas_load_segment(u32 peripheral, int segment, dma_addr_t dma, int seg_cnt)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_PIL_PAS_LOAD_SEG,
+ .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL, QCOM_SCM_RW, QCOM_SCM_VAL),
+ .args[0] = peripheral,
+ .args[1] = segment,
+ .args[2] = dma,
+ .args[3] = seg_cnt,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+ int ret;
+
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+
+ return ret ? : res.result[0];
+}
+EXPORT_SYMBOL(qcom_scm_pas_load_segment);
+
+/**
* qcom_scm_msa_lock() - Lock given peripheral firmware region as MSA
*
* @peripheral: peripheral id
--- a/drivers/firmware/qcom_scm.h
+++ b/drivers/firmware/qcom_scm.h
@@ -98,6 +98,9 @@ extern int scm_legacy_call(struct device
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+#define QCOM_SCM_INTERNAL_WIFI_POWERUP 0x17
+#define QCOM_SCM_INTERNAL_WIFI_SHUTDOWN 0x18
+#define QCOM_SCM_PIL_PAS_LOAD_SEG 0x19
#define QCOM_SCM_MSA_LOCK 0x24
#define QCOM_SCM_MSA_UNLOCK 0x25
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -81,6 +81,9 @@ extern int qcom_scm_pas_mem_setup(u32 pe
extern int qcom_scm_pas_auth_and_reset(u32 peripheral);
extern int qcom_scm_pas_shutdown(u32 peripheral);
extern bool qcom_scm_pas_supported(u32 peripheral);
+extern int qcom_scm_internal_wifi_powerup(u32 peripheral);
+extern int qcom_scm_internal_wifi_shutdown(u32 peripheral);
+extern int qcom_scm_pas_load_segment(u32 peripheral, int segment, dma_addr_t dma, int seg_cnt);
extern int qcom_scm_msa_lock(u32 peripheral);
extern int qcom_scm_msa_unlock(u32 peripheral);

View File

@ -0,0 +1,199 @@
From bf42d84868bc82a9cb334a33930f2d1da24f7070 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH] soc: qcom: mdt_loader: support MPD
Add support for loading user PD specific PIL segments as required by the
MPD architecture.
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/soc/qcom/mdt_loader.c | 110 ++++++++++++++++++++++++++--
include/linux/soc/qcom/mdt_loader.h | 5 ++
2 files changed, 110 insertions(+), 5 deletions(-)
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -16,6 +16,16 @@
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/soc/qcom/mdt_loader.h>
+#include <linux/dma-mapping.h>
+
+#include "../../remoteproc/qcom_common.h"
+
+#define QCOM_MDT_PF_ASID_MASK GENMASK(19, 16)
+
+struct segment_load_args {
+ __le64 addr;
+ __le64 blk_size;
+};
static bool mdt_phdr_valid(const struct elf32_phdr *phdr)
{
@@ -69,6 +79,56 @@ static ssize_t mdt_load_split_segment(vo
return ret;
}
+static int mdt_load_split_segment_dma(int pas_id, unsigned int segment,
+ const struct elf32_phdr *phdrs,
+ const char *fw_name,
+ struct device *dev)
+{
+ const struct elf32_phdr *phdr = &phdrs[segment];
+ struct segment_load_args *args;
+ dma_addr_t *addrs;
+ void *ptr;
+ dma_addr_t dma_args, dma_addrs, dma_ptr;
+ int ret;
+
+ args = dma_alloc_coherent(dev, sizeof(*args) + sizeof(*addrs), &dma_args, GFP_DMA);
+ if (!args) {
+ dev_err(dev, "Error in dma alloc regin: %ld\n", sizeof(*args));
+ return -ENOMEM;
+ }
+
+ addrs = (void *) args + sizeof(*args);
+ dma_addrs = dma_args + sizeof(*args);
+
+ ptr = dma_alloc_coherent(dev, phdr->p_filesz, &dma_ptr, GFP_DMA);
+ if (!ptr) {
+ dev_err(dev, "Error in dma alloc ptr: %d\n", phdr->p_filesz);
+ return -ENOMEM;
+ }
+
+ args->addr = dma_addrs;
+ args->blk_size = phdr->p_filesz;
+
+ addrs[0] = dma_ptr;
+
+ ret = mdt_load_split_segment(ptr, phdrs, segment, fw_name, dev);
+ if (ret < 0) {
+ dev_err(dev, "Error in mdt_load_split_segment: %d\n", ret);
+ return ret;
+ }
+
+ ret = qcom_scm_pas_load_segment(pas_id, segment, dma_args, 1);
+ if (ret < 0) {
+ dev_err(dev, "Error in qcom_scm_pas_load_segment: %d\n", ret);
+ return ret;
+ }
+
+ dma_free_coherent(dev, phdr->p_filesz, ptr, dma_ptr);
+ dma_free_coherent(dev, sizeof(*args) + sizeof(*addrs), args, dma_args);
+
+ return 0;
+}
+
/**
* qcom_mdt_get_size() - acquire size of the memory region needed to load mdt
* @fw: firmware object for the mdt file
@@ -295,7 +355,8 @@ static bool qcom_mdt_bins_are_split(cons
static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
const char *fw_name, int pas_id, void *mem_region,
phys_addr_t mem_phys, size_t mem_size,
- phys_addr_t *reloc_base, bool pas_init)
+ phys_addr_t *reloc_base, bool pas_init,
+ bool dma_require, int pd_asid)
{
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
@@ -349,6 +410,14 @@ static int __qcom_mdt_load(struct device
if (!mdt_phdr_valid(phdr))
continue;
+ /*
+ * While doing PD specific reloading, load only that PD
+ * specific writeable entries. Skip others
+ */
+ if (pd_asid && (FIELD_GET(QCOM_MDT_PF_ASID_MASK, phdr->p_flags) != pd_asid ||
+ (phdr->p_flags & PF_W) == 0))
+ continue;
+
offset = phdr->p_paddr - mem_reloc;
if (offset < 0 || offset + phdr->p_memsz > mem_size) {
dev_err(dev, "segment outside memory range\n");
@@ -366,7 +435,11 @@ static int __qcom_mdt_load(struct device
ptr = mem_region + offset;
- if (phdr->p_filesz && !is_split) {
+ if (dma_require && phdr->p_filesz) {
+ ret = mdt_load_split_segment_dma(pas_id, i, phdrs, fw_name, dev);
+ if (ret)
+ break;
+ } else if (phdr->p_filesz && !is_split) {
/* Firmware is large enough to be non-split */
if (phdr->p_offset + phdr->p_filesz > fw->size) {
dev_err(dev, "file %s segment %d would be truncated\n",
@@ -383,7 +456,7 @@ static int __qcom_mdt_load(struct device
break;
}
- if (phdr->p_memsz > phdr->p_filesz)
+ if (!dma_require && phdr->p_memsz > phdr->p_filesz)
memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
}
@@ -418,7 +491,7 @@ int qcom_mdt_load(struct device *dev, co
return ret;
return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys,
- mem_size, reloc_base, true);
+ mem_size, reloc_base, true, false, 0);
}
EXPORT_SYMBOL_GPL(qcom_mdt_load);
@@ -441,9 +514,36 @@ int qcom_mdt_load_no_init(struct device
size_t mem_size, phys_addr_t *reloc_base)
{
return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys,
- mem_size, reloc_base, false);
+ mem_size, reloc_base, false, false, 0);
}
EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init);
+/**
+ * qcom_mdt_load_pd_seg() - load userpd specific PIL segements
+ * @dev: device handle to associate resources with
+ * @fw: firmware object for the mdt file
+ * @firmware: name of the firmware, for construction of segment file names
+ * @pas_id: PAS identifier
+ * @mem_region: allocated memory region to load firmware into
+ * @mem_phys: physical address of allocated memory region
+ * @mem_size: size of the allocated memory region
+ * @reloc_base: adjusted physical address after relocation
+ *
+ * Here userpd PIL segements are stitched with rootpd firmware.
+ * This function reloads userpd specific PIL segments during SSR
+ * of userpd.
+ *
+ * Returns 0 on success, negative errno otherwise.
+ */
+int qcom_mdt_load_pd_seg(struct device *dev, const struct firmware *fw,
+ const char *firmware, int pas_id, int pd_asid, void *mem_region,
+ phys_addr_t mem_phys, size_t mem_size,
+ phys_addr_t *reloc_base)
+{
+ return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys,
+ mem_size, reloc_base, false, true, pd_asid);
+}
+EXPORT_SYMBOL_GPL(qcom_mdt_load_pd_seg);
+
MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
MODULE_LICENSE("GPL v2");
--- a/include/linux/soc/qcom/mdt_loader.h
+++ b/include/linux/soc/qcom/mdt_loader.h
@@ -30,6 +30,11 @@ int qcom_mdt_load_no_init(struct device
void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len,
const char *fw_name, struct device *dev);
+int qcom_mdt_load_pd_seg(struct device *dev, const struct firmware *fw,
+ const char *firmware, int pas_id, int pd_asid, void *mem_region,
+ phys_addr_t mem_phys, size_t mem_size,
+ phys_addr_t *reloc_base);
+
#else /* !IS_ENABLED(CONFIG_QCOM_MDT_LOADER) */
static inline ssize_t qcom_mdt_get_size(const struct firmware *fw)

View File

@ -0,0 +1,38 @@
From e83215d5d22946885fa388d375b12f1b991a43c1 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH 3/5] remoteproc: qcom_q6v5_mpd: enable clocks
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
drivers/remoteproc/qcom_q6v5_mpd.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
--- a/drivers/remoteproc/qcom_q6v5_mpd.c
+++ b/drivers/remoteproc/qcom_q6v5_mpd.c
@@ -77,6 +77,8 @@ struct q6_wcss {
phys_addr_t mem_reloc;
void *mem_region;
size_t mem_size;
+ struct clk_bulk_data *clks;
+ int num_clks;
const struct wcss_data *desc;
const char **firmware;
struct userpd *upd[MAX_UPD];
@@ -718,6 +720,16 @@ static int q6_wcss_probe(struct platform
if (ret)
goto free_rproc;
+ wcss->num_clks = devm_clk_bulk_get_all(wcss->dev, &wcss->clks);
+ if (wcss->num_clks < 0)
+ return dev_err_probe(wcss->dev, wcss->num_clks,
+ "failed to acquire clocks\n");
+
+ ret = clk_bulk_prepare_enable(wcss->num_clks, wcss->clks);
+ if (ret)
+ return dev_err_probe(wcss->dev, ret,
+ "failed to enable clocks\n");
+
ret = qcom_q6v5_init(&wcss->q6, pdev, rproc,
WCSS_CRASH_REASON, NULL, NULL);
if (ret)

View File

@ -0,0 +1,111 @@
From 4ae334127f073aa5f7c9209c9f0a17fd9e331db1 Mon Sep 17 00:00:00 2001
From: Ziyang Huang <hzyitc@outlook.com>
Date: Sun, 8 Sep 2024 16:40:12 +0800
Subject: [PATCH] remoteproc: qcom_q6v5_mpd: support ipq5018
Signed-off-by: Ziyang Huang <hzyitc@outlook.com>
---
drivers/remoteproc/qcom_q6v5_mpd.c | 37 +++++++++++++++++++++++++++---
1 file changed, 34 insertions(+), 3 deletions(-)
--- a/drivers/remoteproc/qcom_q6v5_mpd.c
+++ b/drivers/remoteproc/qcom_q6v5_mpd.c
@@ -155,6 +155,8 @@ static int q6_wcss_spawn_pd(struct rproc
static int wcss_pd_start(struct rproc *rproc)
{
struct userpd *upd = rproc->priv;
+ struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
+ struct q6_wcss *wcss = rpd_rproc->priv;
u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
int ret;
@@ -170,6 +172,14 @@ static int wcss_pd_start(struct rproc *r
return ret;
}
+ if (upd->pd_asid == 1) {
+ ret = qcom_scm_internal_wifi_powerup(wcss->desc->pasid);
+ if (ret) {
+ dev_err(upd->dev, "failed to power up internal radio\n");
+ return ret;
+ }
+ }
+
return ret;
}
@@ -179,6 +189,12 @@ static int q6_wcss_stop(struct rproc *rp
const struct wcss_data *desc = wcss->desc;
int ret;
+ ret = qcom_q6v5_request_stop(&wcss->q6, NULL);
+ if (ret) {
+ dev_err(wcss->dev, "pd not stopped\n");
+ return ret;
+ }
+
ret = qcom_scm_pas_shutdown(desc->pasid);
if (ret) {
dev_err(wcss->dev, "not able to shutdown\n");
@@ -218,6 +234,7 @@ static int wcss_pd_stop(struct rproc *rp
{
struct userpd *upd = rproc->priv;
struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
+ struct q6_wcss *wcss = rpd_rproc->priv;
u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
int ret;
@@ -229,6 +246,14 @@ static int wcss_pd_stop(struct rproc *rp
}
}
+ if (upd->pd_asid == 1) {
+ ret = qcom_scm_internal_wifi_shutdown(wcss->desc->pasid);
+ if (ret) {
+ dev_err(upd->dev, "failed to power down internal radio\n");
+ return ret;
+ }
+ }
+
ret = qcom_scm_msa_unlock(pasid);
if (ret) {
dev_err(upd->dev, "failed to power down pd\n");
@@ -430,15 +455,14 @@ static int wcss_pd_load(struct rproc *rp
struct userpd *upd = rproc->priv;
struct rproc *rpd_rproc = dev_get_drvdata(upd->dev->parent);
struct q6_wcss *wcss = rpd_rproc->priv;
- u32 pasid = (upd->pd_asid << 8) | UPD_SWID;
int ret;
ret = rproc_boot(rpd_rproc);
if (ret)
return ret;
- return qcom_mdt_load(upd->dev, fw, rproc->firmware,
- pasid, wcss->mem_region,
+ return qcom_mdt_load_pd_seg(upd->dev, fw, rproc->firmware,
+ wcss->desc->pasid, upd->pd_asid, wcss->mem_region,
wcss->mem_phys, wcss->mem_size,
NULL);
}
@@ -777,6 +801,12 @@ static int q6_wcss_remove(struct platfor
return 0;
}
+static const struct wcss_data q6_ipq5018_res_init = {
+ .pasid = MPD_WCNSS_PAS_ID,
+ // .share_upd_info_to_q6 = true, /* Version 1 */
+ // .mdt_load_sec = qcom_mdt_load_pd_seg,
+};
+
static const struct wcss_data q6_ipq5332_res_init = {
.pasid = MPD_WCNSS_PAS_ID,
.share_upd_info_to_q6 = true,
@@ -787,6 +817,7 @@ static const struct wcss_data q6_ipq9574
};
static const struct of_device_id q6_wcss_of_match[] = {
+ { .compatible = "qcom,ipq5018-q6-mpd", .data = &q6_ipq5018_res_init },
{ .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init },
{ .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init },
{ },

View File

@ -0,0 +1,163 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Mon, 09 Dec 2024 09:59:38 +0400
Subject: [PATCH] remoteproc: qcom_q6v5_mpd: add support for passing v1 bootargs
On multi-PD platforms such as IPQ5018, boot args are passed to the root PD
run on the Q6 processor which in turn boots the user PDs for internal
(IPQ5018) and external wifi radios (such as QCN6122). These boot args
let the user PD process know details like what PCIE index, user PD ID, and
reset GPIO is used. These are otherwise hardcoded in the firmware.
Below is the structure expected of the version 1 boot args including the
default values hardcoded in the firmware for IPQ5018:
+------------+------+--------------+--------------+
| Argument | type | def val UPD2 | def val UPD3 |
+------------+------+--------------+--------------+
| PCIE Index | u32 | 0x02 (PCIE1) | 0x01 (PCIE0) |
| Length | u32 | 0x04 | 0x04 |
| User PD ID | u32 | 0x02 | 0x03 |
| Reset GPIO | u32 | 0x12 | 0x0f |
| Reserved 1 | u32 | 0x00 | 0x00 |
| Reserved 2 | u32 | 0x00 | 0x00 |
+------------+------+--------------+--------------+
On IPQ5018/QCN6122 boards, the default mapping is as follows:
+-> UPD1 ----> IPQ5018 Internal 2.4G Radio
/
/
Root PD +---> UPD2 ----> QCN6122 6G Radio on PCIE1 (if available)
\
\
+-> UPD3 ----> QCN6102 5G Radio on PCIE0
To support (future) boards with other mappings or control what UPD ID is
used, let's add support for passing boot args for more flexibility.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/drivers/remoteproc/qcom_q6v5_mpd.c
+++ b/drivers/remoteproc/qcom_q6v5_mpd.c
@@ -42,7 +42,11 @@
#define UPD_BOOT_INFO_SMEM_SIZE 4096
#define UPD_BOOT_INFO_HEADER_TYPE 0x2
#define UPD_BOOT_INFO_SMEM_ID 507
-#define VERSION2 2
+
+enum q6_bootargs_version {
+ VERSION1 = 1,
+ VERSION2,
+};
/**
* struct userpd_boot_info_header - header of user pd bootinfo
@@ -94,6 +98,7 @@ struct userpd {
struct wcss_data {
u32 pasid;
bool share_upd_info_to_q6;
+ u8 bootargs_version;
};
/**
@@ -298,10 +303,13 @@ static void *q6_wcss_da_to_va(struct rpr
static int share_upd_bootinfo_to_q6(struct rproc *rproc)
{
int i, ret;
+ u32 rd_val;
size_t size;
u16 cnt = 0, version;
void *ptr;
+ u8 *bootargs_arr;
struct q6_wcss *wcss = rproc->priv;
+ struct device_node *np = wcss->dev->of_node;
struct userpd *upd;
struct userpd_boot_info upd_bootinfo = {0};
const struct firmware *fw;
@@ -323,10 +331,47 @@ static int share_upd_bootinfo_to_q6(stru
}
/*Version*/
- version = VERSION2;
+ version = (wcss->desc->bootargs_version) ? wcss->desc->bootargs_version : VERSION2;
memcpy_toio(ptr, &version, sizeof(version));
ptr += sizeof(version);
+ cnt = ret = of_property_count_u32_elems(np, "boot-args");
+ if (ret < 0) {
+ if (ret == -ENODATA) {
+ dev_err(wcss->dev, "failed to read boot args ret:%d\n", ret);
+ return ret;
+ }
+ cnt = 0;
+ }
+
+ /* No of elements */
+ memcpy_toio(ptr, &cnt, sizeof(u16));
+ ptr += sizeof(u16);
+
+ bootargs_arr = kzalloc(cnt, GFP_KERNEL);
+ if (!bootargs_arr) {
+ dev_err(wcss->dev, "failed to allocate memory\n");
+ return PTR_ERR(bootargs_arr);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ ret = of_property_read_u32_index(np, "boot-args", i, &rd_val);
+ if (ret) {
+ dev_err(wcss->dev, "failed to read boot args\n");
+ kfree(bootargs_arr);
+ return ret;
+ }
+ bootargs_arr[i] = (u8)rd_val;
+ }
+
+ /* Copy bootargs */
+ memcpy_toio(ptr, bootargs_arr, cnt);
+ ptr += (cnt);
+
+ of_node_put(np);
+ kfree(bootargs_arr);
+ cnt = 0;
+
for (i = 0; i < ARRAY_SIZE(wcss->upd); i++)
if (wcss->upd[i])
cnt++;
@@ -382,12 +427,14 @@ static int q6_wcss_load(struct rproc *rp
/* Share user pd boot info to Q6 remote processor */
if (desc->share_upd_info_to_q6) {
- ret = share_upd_bootinfo_to_q6(rproc);
- if (ret) {
- dev_err(wcss->dev,
- "user pd boot info sharing with q6 failed %d\n",
- ret);
- return ret;
+ if (of_property_present(wcss->dev->of_node, "boot-args")) {
+ ret = share_upd_bootinfo_to_q6(rproc);
+ if (ret) {
+ dev_err(wcss->dev,
+ "user pd boot info sharing with q6 failed %d\n",
+ ret);
+ return ret;
+ }
}
}
@@ -803,13 +850,15 @@ static int q6_wcss_remove(struct platfor
static const struct wcss_data q6_ipq5018_res_init = {
.pasid = MPD_WCNSS_PAS_ID,
- // .share_upd_info_to_q6 = true, /* Version 1 */
+ .share_upd_info_to_q6 = true,
+ .bootargs_version = VERSION1,
// .mdt_load_sec = qcom_mdt_load_pd_seg,
};
static const struct wcss_data q6_ipq5332_res_init = {
.pasid = MPD_WCNSS_PAS_ID,
.share_upd_info_to_q6 = true,
+ .bootargs_version = VERSION2,
};
static const struct wcss_data q6_ipq9574_res_init = {

View File

@ -0,0 +1,241 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Wed, 27 Oct 2024 16:34:11 +0400
Subject: [PATCH] arm64: dts: qcom: ipq5018: add wifi support
The IPQ5018 SoC comes with an internal 2x2 2.4Ghz wifi radio.
QCN6122 is a PCIe based wifi solution specific to the IPQ5018 platform which
comes optinally packed with 1 or 2 QCN6122 chips or with an external
PCIe based wifi solution (such as QCN9074) for 5/6 Ghz support.
As such, add wifi nodes for both IPQ5018 and QCN6122.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -692,6 +692,225 @@
};
};
+ wifi0: wifi@c000000 {
+ compatible = "qcom,ipq5018-wifi";
+ reg = <0xc000000 0x1000000>;
+
+ interrupts = <GIC_SPI 288 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 289 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 290 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 291 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 292 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 293 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 294 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 295 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 296 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 297 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 298 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 299 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 300 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 301 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 302 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 303 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 304 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 305 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 306 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 307 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 308 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 309 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 310 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 311 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 312 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 313 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 314 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 315 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 316 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 317 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 318 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 319 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 320 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 321 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 322 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 323 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 324 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 325 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 326 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 327 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 328 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 329 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 330 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 331 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 332 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 333 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 334 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 335 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 336 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 337 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 338 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 339 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "misc-pulse1",
+ "misc-latch",
+ "sw-exception",
+ "watchdog",
+ "ce0",
+ "ce1",
+ "ce2",
+ "ce3",
+ "ce4",
+ "ce5",
+ "ce6",
+ "ce7",
+ "ce8",
+ "ce9",
+ "ce10",
+ "ce11",
+ "host2wbm-desc-feed",
+ "host2reo-re-injection",
+ "host2reo-command",
+ "host2rxdma-monitor-ring3",
+ "host2rxdma-monitor-ring2",
+ "host2rxdma-monitor-ring1",
+ "reo2ost-exception",
+ "wbm2host-rx-release",
+ "reo2host-status",
+ "reo2host-destination-ring4",
+ "reo2host-destination-ring3",
+ "reo2host-destination-ring2",
+ "reo2host-destination-ring1",
+ "rxdma2host-monitor-destination-mac3",
+ "rxdma2host-monitor-destination-mac2",
+ "rxdma2host-monitor-destination-mac1",
+ "ppdu-end-interrupts-mac3",
+ "ppdu-end-interrupts-mac2",
+ "ppdu-end-interrupts-mac1",
+ "rxdma2host-monitor-status-ring-mac3",
+ "rxdma2host-monitor-status-ring-mac2",
+ "rxdma2host-monitor-status-ring-mac1",
+ "host2rxdma-host-buf-ring-mac3",
+ "host2rxdma-host-buf-ring-mac2",
+ "host2rxdma-host-buf-ring-mac1",
+ "rxdma2host-destination-ring-mac3",
+ "rxdma2host-destination-ring-mac2",
+ "rxdma2host-destination-ring-mac1",
+ "host2tcl-input-ring4",
+ "host2tcl-input-ring3",
+ "host2tcl-input-ring2",
+ "host2tcl-input-ring1",
+ "wbm2host-tx-completions-ring3",
+ "wbm2host-tx-completions-ring2",
+ "wbm2host-tx-completions-ring1",
+ "tcl2host-status-ring";
+
+ status = "disabled";
+ };
+
+ //QCN6102 5G
+ wifi1: wifi1@c000000 {
+ reg = <0x0b00a040 0x0>;
+ compatible = "qcom,qcn6122-wifi";
+ interrupts = <GIC_SPI 416 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 417 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 418 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 419 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 420 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 421 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 422 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 423 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 424 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 425 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 426 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 427 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 428 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ };
+
+ //QCN6122 5G/6G
+ wifi2: wifi2@c000000 {
+ reg = <0x0b00a040 0x0>;
+ compatible = "qcom,qcn6122-wifi";
+ interrupts = <GIC_SPI 448 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 449 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 450 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 451 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 452 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 453 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 454 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 455 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 456 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 457 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 458 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 459 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 460 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ };
+
+ q6v5_wcss: remoteproc@cd00000 {
+ compatible = "qcom,ipq5018-q6-mpd";
+ reg = <0x0cd00000 0x4040>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ clocks = <&gcc GCC_XO_CLK>,
+ <&gcc GCC_SLEEP_CLK_SRC>,
+ <&gcc GCC_SYS_NOC_WCSS_AHB_CLK>;
+
+ interrupts-extended = <&intc GIC_SPI 291 IRQ_TYPE_EDGE_RISING>,
+ <&wcss_smp2p_in 0 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 1 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 2 IRQ_TYPE_NONE>,
+ <&wcss_smp2p_in 3 IRQ_TYPE_NONE>;
+ interrupt-names = "wdog",
+ "fatal",
+ "ready",
+ "handover",
+ "stop-ack";
+
+ qcom,smem-states = <&wcss_smp2p_out 0>,
+ <&wcss_smp2p_out 1>;
+ qcom,smem-state-names = "shutdown",
+ "stop";
+
+ status = "disabled";
+
+ glink-edge {
+ interrupts = <GIC_SPI 179 IRQ_TYPE_EDGE_RISING>;
+ label = "rtr";
+ qcom,remote-pid = <1>;
+ mboxes = <&apcs_glb 8>;
+
+ qrtr_requests {
+ qcom,glink-channels = "IPCRTR";
+ };
+ };
+ };
+
+ wcss: smp2p-wcss {
+ compatible = "qcom,smp2p";
+ qcom,smem = <435>, <428>;
+
+ interrupt-parent = <&intc>;
+ interrupts = <GIC_SPI 177 IRQ_TYPE_EDGE_RISING>;
+
+ mboxes = <&apcs_glb 9>;
+
+ qcom,local-pid = <0>;
+ qcom,remote-pid = <1>;
+
+ wcss_smp2p_out: master-kernel {
+ qcom,entry-name = "master-kernel";
+ qcom,smp2p-feature-ssr-ack;
+ #qcom,smem-state-cells = <1>;
+ };
+
+ wcss_smp2p_in: slave-kernel {
+ qcom,entry-name = "slave-kernel";
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+
pcie1: pcie@80000000 {
compatible = "qcom,pcie-ipq5018";
reg = <0x80000000 0xf1d>,

View File

@ -0,0 +1,22 @@
From: George Moussalem <george.moussalem@outlook.com>
Date: Wed, 05 Feb 2025 12:12:47 +0400
Subject: [PATCH] arm64: dts: qcom: ipq5018: add tz_apps reserved memory region
Add tz_apps memory region needed for wifi to work.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -113,6 +113,11 @@
#size-cells = <2>;
ranges;
+ tz_apps@4a400000 {
+ reg = <0x0 0x4a400000 0x0 0x400000>;
+ no-map;
+ };
+
bootloader@4a800000 {
reg = <0x0 0x4a800000 0x0 0x200000>;
no-map;