diff mbox series

[2/2] drivers: soc: qcom: add TCSR driver

Message ID 20220122001609.15904-3-ansuelsmth@gmail.com (mailing list archive)
State Changes Requested
Headers show
Series Add QCOM TCSR driver | expand

Commit Message

Christian Marangi Jan. 22, 2022, 12:16 a.m. UTC
Add QCOM Top Control and Status Registers driver required to control and
configure various peripherals for ipq8064 and ipq4019. This is required
to configure usb3 mode, gsbi configuration for ipq8064 and various
devices (WiFi, USB mode, WiFi memtype, ESS interface mode) for ipq4019.

Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
---
 drivers/soc/qcom/Kconfig     |   8 ++
 drivers/soc/qcom/Makefile    |   1 +
 drivers/soc/qcom/qcom_tcsr.c | 198 +++++++++++++++++++++++++++++++++++
 3 files changed, 207 insertions(+)
 create mode 100644 drivers/soc/qcom/qcom_tcsr.c
diff mbox series

Patch

diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index e718b8735444..20dd341ae369 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -209,6 +209,14 @@  config QCOM_STATS
 	  various SoC level low power modes statistics and export to debugfs
 	  interface.
 
+config QCOM_TCSR
+	tristate "QCOM Top Control and Status Registers"
+	depends on ARCH_QCOM || COMPILE_TEST
+	select MFD_SYSCON
+	help
+	  Say y here to enable TCSR support. The TCSR provides control
+	  functions for various peripherals (USB, WiFi, ESS).
+
 config QCOM_WCNSS_CTRL
 	tristate "Qualcomm WCNSS control driver"
 	depends on ARCH_QCOM || COMPILE_TEST
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 70d5de69fd7b..b17dd46ed1fa 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -28,3 +28,4 @@  obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o
 obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o
 obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
 obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) +=	kryo-l2-accessors.o
+obj-$(CONFIG_QCOM_TCSR)	 += qcom_tcsr.o
diff --git a/drivers/soc/qcom/qcom_tcsr.c b/drivers/soc/qcom/qcom_tcsr.c
new file mode 100644
index 000000000000..dc80768d57c2
--- /dev/null
+++ b/drivers/soc/qcom/qcom_tcsr.c
@@ -0,0 +1,198 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define TCSR_USB_PORT_SEL_REG			0xb0
+#define TCSR_USB_PORT_SEL_MASK			GENMASK(1, 0)
+
+#define TCSR_USB_SELECT_USB3_P0			FIELD_PREP(TCSR_USB_PORT_SEL_MASK, 0x1)
+#define TCSR_USB_SELECT_USB3_P1			FIELD_PREP(TCSR_USB_PORT_SEL_MASK, 0x2)
+#define TCSR_USB_SELECT_USB3_DUAL		FIELD_PREP(TCSR_USB_PORT_SEL_MASK, 0x3)
+
+/* IPQ40xx HS PHY Mode Select */
+#define TCSR_USB_HSPHY_CONFIG_REG		0xc
+#define TCSR_USB_HSPHY_MODE_MASK		BIT(21)
+#define TCSR_USB_HSPHY_MODE_HOST_MODE		FIELD_PREP(TCSR_USB_HSPHY_MODE_MASK, 0x0)
+#define TCSR_USB_HSPHY_MODE_DEVICE_MODE		FIELD_PREP(TCSR_USB_HSPHY_MODE_MASK, 0x1)
+
+/* IPQ40xx ess interface mode select */
+#define TCSR_ESS_INTERFACE_SEL_REG		0x0
+#define TCSR_ESS_INTERFACE_SEL_MASK		GENMASK(3, 0)
+#define TCSR_ESS_PSGMII				FIELD_PREP(TCSR_ESS_INTERFACE_SEL_MASK, 0x0)
+#define TCSR_ESS_PSGMII_RGMII5			FIELD_PREP(TCSR_ESS_INTERFACE_SEL_MASK, 0x1)
+#define TCSR_ESS_PSGMII_RMII0			FIELD_PREP(TCSR_ESS_INTERFACE_SEL_MASK, 0x2)
+#define TCSR_ESS_PSGMII_RMII1			FIELD_PREP(TCSR_ESS_INTERFACE_SEL_MASK, 0x4)
+#define TCSR_ESS_PSGMII_RMII0_RMII1		FIELD_PREP(TCSR_ESS_INTERFACE_SEL_MASK, 0x6)
+#define TCSR_ESS_PSGMII_RGMII4			FIELD_PREP(TCSR_ESS_INTERFACE_SEL_MASK, 0x9)
+
+/* IPQ40xx WiFi Global Config */
+#define TCSR_WIFI0_GLB_CFG_OFFSET_REG		0x0
+#define TCSR_WIFI1_GLB_CFG_OFFSET_REG		0x4
+/* Enable AXI master bus Axid translating to confirm all txn submitted by order */
+#define TCSR_WIFI_GLB_CFG_AXID_EN		BIT(30)
+/* 1:  use locally generate socslv_wxi_bvalid for performance.
+ * 0:  use SNOC socslv_wxi_bvalid.
+ */
+#define TCSR_WIFI_GLB_CFG_SOCSLV_WXI_BVALID	BIT(24)
+#define TCSR_WIFI_GLB_CFG_SOCSLV_SNOC		FIELD_PREP(TCSR_WIFI_GLB_CFG_SOCSLV_WXI_BVALID, 0x0)
+#define TCSR_WIFI_GLB_CFG_SOCSLV_LOCAL		FIELD_PREP(TCSR_WIFI_GLB_CFG_SOCSLV_WXI_BVALID, 0x1)
+
+/* Configure special wifi memory type needed for some IPQ40xx devices */
+#define TCSR_PNOC_SNOC_MEMTYPE_M0_M2_REG	0x4
+#define TCSR_WIFI_NOC_MEMTYPE_MASK		GENMASK(26, 24)
+#define TCSR_WIFI_NOC_MEMTYPE_M0_M2		FIELD_PREP(TCSR_WIFI_NOC_MEMTYPE_MASK, 0x2)
+
+static int qcom_tcsr_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node;
+	struct regmap *tcsr;
+	int ret, val;
+
+	node = dev->of_node;
+	tcsr = syscon_node_to_regmap(node);
+	if (IS_ERR(tcsr))
+		return PTR_ERR(tcsr);
+
+	if (of_find_property(node, "qcom,usb-ctrl-select", NULL) &&
+	    of_device_is_compatible(node, "qcom,tcsr-ipq8064")) {
+		if (of_property_match_string(node, "qcom,usb-ctrl-select",
+					     "p0")) {
+			val = TCSR_USB_SELECT_USB3_P0;
+		} else if (of_property_match_string(node, "qcom,usb-ctrl-select",
+						  "p1")) {
+			val = TCSR_USB_SELECT_USB3_P1;
+		} else if (of_property_match_string(node, "qcom,usb-ctrl-select",
+						  "dual")) {
+			val = TCSR_USB_SELECT_USB3_DUAL;
+		} else {
+			dev_err(dev, "invalid value for qcom,usb-ctrl-select");
+			return -EINVAL;
+		}
+
+		ret = regmap_update_bits(tcsr, TCSR_USB_PORT_SEL_REG,
+					 TCSR_USB_PORT_SEL_MASK, val);
+		if (ret)
+			return ret;
+	}
+
+	if (of_find_property(node, "qcom,usb-hsphy-mode-select", NULL) &&
+	    of_device_is_compatible(node, "qcom,tcsr-ipq4019")) {
+		if (of_property_match_string(node, "qcom,usb-hsphy-mode-select",
+					     "host")) {
+			val = TCSR_USB_HSPHY_MODE_HOST_MODE;
+		} else if (of_property_match_string(node, "qcom,usb-hsphy-mode-select",
+						  "device")) {
+			val = TCSR_USB_HSPHY_MODE_DEVICE_MODE;
+		} else {
+			dev_err(dev, "invalid value for qcom,usb-hsphy-mode-select");
+			return -EINVAL;
+		}
+
+		ret = regmap_update_bits(tcsr, TCSR_USB_HSPHY_CONFIG_REG,
+					 TCSR_USB_HSPHY_MODE_MASK, val);
+		if (ret)
+			return ret;
+	}
+
+	if (of_find_property(node, "qcom,ess-interface-select", NULL) &&
+	    of_device_is_compatible(node, "qcom,tcsr-ipq4019")) {
+		if (of_property_match_string(node, "qcom,ess-interface-select",
+					     "psgmii")) {
+			val = TCSR_ESS_PSGMII;
+		} else if (of_property_match_string(node, "qcom,ess-interface-select",
+						  "rgmii5")) {
+			val = TCSR_ESS_PSGMII_RGMII5;
+		} else if (of_property_match_string(node, "qcom,ess-interface-select",
+						  "rmii0")) {
+			val = TCSR_ESS_PSGMII_RMII0;
+		} else if (of_property_match_string(node, "qcom,ess-interface-select",
+						  "rmii1")) {
+			val = TCSR_ESS_PSGMII_RMII1;
+		} else if (of_property_match_string(node, "qcom,ess-interface-select",
+						  "rmii0_rmii1")) {
+			val = TCSR_ESS_PSGMII_RMII0_RMII1;
+		} else if (of_property_match_string(node, "qcom,ess-interface-select",
+						  "rgmii4")) {
+			val = TCSR_ESS_PSGMII_RGMII4;
+		} else {
+			dev_err(dev, "invalid value for qcom,ess-interface-select");
+			return -EINVAL;
+		}
+
+		ret = regmap_update_bits(tcsr, TCSR_ESS_INTERFACE_SEL_REG,
+					 TCSR_ESS_INTERFACE_SEL_MASK, val);
+		if (ret)
+			return ret;
+	}
+
+	if (of_find_property(node, "qcom,wifi-glb-cfg-enable-axid", NULL) &&
+	    of_device_is_compatible(node, "qcom,tcsr-ipq4019")) {
+		ret = regmap_set_bits(tcsr, TCSR_WIFI0_GLB_CFG_OFFSET_REG,
+				      TCSR_WIFI_GLB_CFG_AXID_EN);
+		ret = regmap_set_bits(tcsr, TCSR_WIFI1_GLB_CFG_OFFSET_REG,
+				      TCSR_WIFI_GLB_CFG_AXID_EN);
+		if (ret)
+			return ret;
+	}
+
+	if (of_find_property(node, "qcom,wifi-glb-cfg-socslv-mode", NULL) &&
+	    of_device_is_compatible(node, "qcom,tcsr-ipq4019")) {
+		if (of_property_match_string(node, "qcom,wifi-glb-cfg-socslv-mode",
+					     "snoc")) {
+			val = TCSR_WIFI_GLB_CFG_SOCSLV_SNOC;
+		} else if (of_property_match_string(node, "qcom,wifi-glb-cfg-socslv-mode",
+						  "local")) {
+			val = TCSR_WIFI_GLB_CFG_SOCSLV_SNOC;
+		} else {
+			dev_err(dev, "invalid value for qcom,wifi-glb-cfg-socslv-mode");
+			return -EINVAL;
+		}
+
+		ret = regmap_update_bits(tcsr, TCSR_WIFI0_GLB_CFG_OFFSET_REG,
+					 TCSR_WIFI_GLB_CFG_SOCSLV_WXI_BVALID, val);
+		ret = regmap_update_bits(tcsr, TCSR_WIFI1_GLB_CFG_OFFSET_REG,
+					 TCSR_WIFI_GLB_CFG_SOCSLV_WXI_BVALID, val);
+	}
+
+	if (of_find_property(node, "qcom,wifi_noc_memtype_m0_m2", NULL) &&
+	    of_device_is_compatible(node, "qcom,tcsr-ipq4019")) {
+		ret = regmap_update_bits(tcsr, TCSR_PNOC_SNOC_MEMTYPE_M0_M2_REG,
+					 TCSR_WIFI_NOC_MEMTYPE_MASK,
+					 TCSR_WIFI_NOC_MEMTYPE_M0_M2);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id qcom_tcsr_dt_match[] = {
+	{ .compatible = "qcom,tcsr-ipq8064", },
+	{ .compatible = "qcom,tcsr-ipq4019", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qcom_tcsr_dt_match);
+
+static struct platform_driver qcom_tcsr_driver = {
+	.probe = qcom_tcsr_probe,
+	.driver = {
+		.name		= "qcom-tcsr",
+		.of_match_table	= qcom_tcsr_dt_match,
+	},
+};
+
+module_platform_driver(qcom_tcsr_driver);
+
+MODULE_AUTHOR("Ansuel Smith <ansuelsmth@gmail.com>");
+MODULE_DESCRIPTION("QCOM TCSR driver");
+MODULE_LICENSE("GPL v2");