@@ -741,6 +741,7 @@ M: Christian Marangi <ansuelsmth@gmail.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/soc/airoha/airoha,an7581-scu-ssr.yaml
+F: drivers/soc/airoha/airoha-scu-ssr.c
F: include/dt-bindings/soc/airoha,scu-ssr.h
AIROHA SPI SNFI DRIVER
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "SOC (System On Chip) specific Drivers"
+source "drivers/soc/airoha/Kconfig"
source "drivers/soc/amlogic/Kconfig"
source "drivers/soc/apple/Kconfig"
source "drivers/soc/aspeed/Kconfig"
@@ -3,6 +3,7 @@
# Makefile for the Linux Kernel SOC specific device drivers.
#
+obj-$(CONFIG_ARCH_AIROHA) += airoha/
obj-y += apple/
obj-y += aspeed/
obj-$(CONFIG_ARCH_AT91) += atmel/
new file mode 100644
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config AIROHA_SCU_SSR
+ tristate "Airoha SCU SSR Driver"
+ depends on ARCH_AIROHA || COMPILE_TEST
+ depends on OF
+ help
+ Say 'Y' here to add support for Airoha SCU SSR driver.
+
+ Airoha SoC pheriperal (like USB/PCIe/Ethernet port) are
+ selected by toggling specific bit. Serdes Port line
+ are mutually exclusive such as selecting PCIe port 2
+ disable support for USB port 2 3.0 mode.
+
+ This driver is used to configure such bit and expose
+ an API to read the current status from a user of such
+ Serdes lines.
+
new file mode 100644
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_AIROHA_SCU_SSR) += airoha-scu-ssr.o
new file mode 100644
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Christian Marangi <ansuelsmth@gmail.com>
+ */
+
+#include <dt-bindings/soc/airoha,scu-ssr.h>
+#include <linux/bitfield.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/soc/airoha/airoha-scu-ssr.h>
+
+#define AIROHA_SCU_PCIC 0x88
+#define AIROHA_SCU_PCIE_2LANE_MODE BIT(14)
+
+#define AIROHA_SCU_SSR3 0x94
+#define AIROHA_SCU_SSUSB_HSGMII_SEL BIT(29)
+
+#define AIROHA_SCU_SSTR 0x9c
+#define AIROHA_SCU_PCIE_XSI0_SEL GENMASK(14, 13)
+#define AIROHA_SCU_PCIE_XSI0_SEL_PCIE FIELD_PREP_CONST(AIROHA_SCU_PCIE_XSI0_SEL, 0x0)
+#define AIROHA_SCU_PCIE_XSI1_SEL GENMASK(12, 11)
+#define AIROHA_SCU_PCIE_XSI1_SEL_PCIE FIELD_PREP_CONST(AIROHA_SCU_PCIE_XSI0_SEL, 0x0)
+#define AIROHA_SCU_USB_PCIE_SEL BIT(3)
+
+struct airoha_scu_ssr_priv {
+ struct device *dev;
+ struct regmap *regmap;
+
+ u32 serdes_port[AIROHA_SCU_MAX_SERDES_PORT];
+};
+
+int airoha_scu_ssr_get_serdes_mode(struct device *dev,
+ enum airoha_scu_serdes_port port)
+{
+ struct airoha_scu_ssr_priv *priv;
+ struct platform_device *pdev;
+ struct device_node *np;
+
+ np = of_parse_phandle(dev->of_node, "airoha,scu-ssr", 0);
+ if (!np)
+ return -ENODEV;
+
+ if (!of_device_is_available(np)) {
+ of_node_put(np);
+ return -ENODEV;
+ }
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+ if (!pdev || !platform_get_drvdata(pdev)) {
+ if (pdev)
+ put_device(&pdev->dev);
+ return -EPROBE_DEFER;
+ }
+
+ priv = platform_get_drvdata(pdev);
+
+ return priv->serdes_port[port];
+}
+EXPORT_SYMBOL_GPL(airoha_scu_ssr_get_serdes_mode);
+
+static int airoha_scu_ssr_apply_modes(struct airoha_scu_ssr_priv *priv)
+{
+ int ret;
+
+ /*
+ * This is a very bad scenario and needs to be correctly warned
+ * as it cause PCIe malfunction
+ */
+ if ((priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] == AIROHA_SCU_SSR_WIFI1_PCIE0_2LINE &&
+ priv->serdes_port[AIROHA_SCU_SERDES_WIFI2] != AIROHA_SCU_SSR_WIFI2_PCIE0_2LINE) ||
+ (priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] != AIROHA_SCU_SSR_WIFI1_PCIE0_2LINE &&
+ priv->serdes_port[AIROHA_SCU_SERDES_WIFI2] == AIROHA_SCU_SSR_WIFI2_PCIE0_2LINE)) {
+ WARN(true, "Wrong Serdes configuration for PCIe0 2 Line mode. Please check DT.\n");
+ return -EINVAL;
+ }
+
+ /* PCS driver takes case of setting the SCU bit for HSGMII or USXGMII */
+ if (priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] == AIROHA_SCU_SSR_WIFI1_PCIE0_2LINE ||
+ priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] == AIROHA_SCU_SSR_WIFI1_PCIE0) {
+ ret = regmap_update_bits(priv->regmap, AIROHA_SCU_SSTR,
+ AIROHA_SCU_PCIE_XSI0_SEL,
+ AIROHA_SCU_PCIE_XSI0_SEL_PCIE);
+ if (ret)
+ return ret;
+ }
+
+ /* PCS driver takes case of setting the SCU bit for HSGMII or USXGMII */
+ if (priv->serdes_port[AIROHA_SCU_SERDES_WIFI2] == AIROHA_SCU_SSR_WIFI2_PCIE0_2LINE ||
+ priv->serdes_port[AIROHA_SCU_SERDES_WIFI2] == AIROHA_SCU_SSR_WIFI2_PCIE1) {
+ ret = regmap_update_bits(priv->regmap, AIROHA_SCU_SSTR,
+ AIROHA_SCU_PCIE_XSI1_SEL,
+ AIROHA_SCU_PCIE_XSI1_SEL_PCIE);
+ if (ret)
+ return ret;
+ }
+
+ /* Toggle PCIe0 2 Line mode if enabled or not */
+ if (priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] == AIROHA_SCU_SSR_WIFI1_PCIE0_2LINE)
+ ret = regmap_set_bits(priv->regmap, AIROHA_SCU_PCIC,
+ AIROHA_SCU_PCIE_2LANE_MODE);
+ else
+ ret = regmap_clear_bits(priv->regmap, AIROHA_SCU_PCIC,
+ AIROHA_SCU_PCIE_2LANE_MODE);
+ if (ret)
+ return ret;
+
+ if (priv->serdes_port[AIROHA_SCU_SERDES_USB1] == AIROHA_SCU_SSR_USB1_ETHERNET)
+ ret = regmap_clear_bits(priv->regmap, AIROHA_SCU_SSR3,
+ AIROHA_SCU_SSUSB_HSGMII_SEL);
+ else
+ ret = regmap_set_bits(priv->regmap, AIROHA_SCU_SSR3,
+ AIROHA_SCU_SSUSB_HSGMII_SEL);
+ if (ret)
+ return ret;
+
+ if (priv->serdes_port[AIROHA_SCU_SERDES_USB2] == AIROHA_SCU_SSR_USB2_PCIE2)
+ ret = regmap_clear_bits(priv->regmap, AIROHA_SCU_SSTR,
+ AIROHA_SCU_USB_PCIE_SEL);
+ else
+ ret = regmap_set_bits(priv->regmap, AIROHA_SCU_SSTR,
+ AIROHA_SCU_USB_PCIE_SEL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int airoha_scu_ssr_probe(struct platform_device *pdev)
+{
+ struct airoha_scu_ssr_priv *priv;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+
+ /* Get regmap from MFD */
+ priv->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!priv->regmap)
+ return -EINVAL;
+
+ /* If not set, default to PCIE0 1 line */
+ if (of_property_read_u32(dev->of_node, "airoha,serdes-wifi1",
+ &priv->serdes_port[AIROHA_SCU_SERDES_WIFI1]))
+ priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] = AIROHA_SCU_SSR_WIFI1_PCIE0;
+
+ /* If not set, default to PCIE1 1 line */
+ if (of_property_read_u32(dev->of_node, "airoha,serdes-wifi2",
+ &priv->serdes_port[AIROHA_SCU_SERDES_WIFI2]))
+ priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] = AIROHA_SCU_SSR_WIFI2_PCIE1;
+
+ /* If not set, default to USB1 USB 3.0 */
+ if (of_property_read_u32(dev->of_node, "airoha,serdes-usb1",
+ &priv->serdes_port[AIROHA_SCU_SERDES_USB1]))
+ priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] = AIROHA_SCU_SSR_USB1_USB;
+
+ /* If not set, default to USB2 USB 3.0 */
+ if (of_property_read_u32(dev->of_node, "airoha,serdes-usb2",
+ &priv->serdes_port[AIROHA_SCU_SERDES_USB2]))
+ priv->serdes_port[AIROHA_SCU_SERDES_WIFI1] = AIROHA_SCU_SSR_USB2_USB;
+
+ ret = airoha_scu_ssr_apply_modes(priv);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static const struct of_device_id airoha_phy_id_table[] = {
+ { .compatible = "airoha,an7581-scu-ssr" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, airoha_phy_id_table);
+
+static struct platform_driver airoha_scu_ssr_driver = {
+ .probe = airoha_scu_ssr_probe,
+ .driver = {
+ .name = "airoha-scu-ssr",
+ .of_match_table = airoha_phy_id_table,
+ },
+};
+
+module_platform_driver(airoha_scu_ssr_driver);
+
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Airoha SCU SSR/STR driver");
new file mode 100644
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __AIROHA_SCU_SSR__
+#define __AIROHA_SCU_SSR__
+
+enum airoha_scu_serdes_port {
+ AIROHA_SCU_SERDES_WIFI1 = 0,
+ AIROHA_SCU_SERDES_WIFI2,
+ AIROHA_SCU_SERDES_USB1,
+ AIROHA_SCU_SERDES_USB2,
+
+ AIROHA_SCU_MAX_SERDES_PORT,
+};
+
+int airoha_scu_ssr_get_serdes_mode(struct device *dev,
+ enum airoha_scu_serdes_port port);
+
+#endif
new file mode 100644
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __AIROHA_SCU_SSR__
+#define __AIROHA_SCU_SSR__
+
+enum airoha_scu_serdes_port {
+ AIROHA_SCU_SERDES_WIFI1 = 0,
+ AIROHA_SCU_SERDES_WIFI2,
+ AIROHA_SCU_SERDES_USB1,
+ AIROHA_SCU_SERDES_USB2,
+
+ AIROHA_SCU_MAX_SERDES_PORT,
+};
+
+int airoha_scu_ssr_get_serdes_mode(struct device *dev,
+ enum airoha_scu_serdes_port port);
+
+#endif
Add support for configuring SCU SSR Serdes port. Airoha AN7581 SoC can configure the different Serdes port by toggling bits in the SCU register space. Port Serdes mode are mutually exclusive, force example the USB2 Serdes port can either used for USB 3.0 or PCIe 2 port. Enabling USB 3.0 makes the PCIe 2 to not work. The current supported Serdes port are: - WiFi 1 and defaults to PCIe0 1 line mode - Wifi 2 and defaults to PCIe1 1 line mode - USB 1 and defaults to USB 3.0 mode - USB 2 and defaults to USB 3.0 mode WiFi 1, WiFi 2 and USB 1 also support a particular Ethernet mode that can toggle between USXGMII or HSGMII mode (USB 1 only to HSGMII) Such mode doesn't configure bits as specific Ethernet PCS driver will take care of configuring the Serdes mode based on what is required. This driver is to correctly setup these bits and provide an API to read the current status of the Serdes port. Single driver can't independently set the Serdes port mode as that would cause a conflict if someone declare, for example, in DT (and enable) PCIe 2 port and USB2 3.0 port. Drivers will use the airoha_scu_ssr_get_serdes_mode function and will validate the Serdes port is the expected one and fail if it's not. Each driver will have to define in DT the phandle airoha,scu-ssr pointing to the SCU SSR node. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com> --- MAINTAINERS | 1 + drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/airoha/Kconfig | 18 ++ drivers/soc/airoha/Makefile | 3 + drivers/soc/airoha/airoha-scu-ssr.c | 195 ++++++++++++++++++++++ include/linux/soc/airoha/airoha-scu-ssr.h | 17 ++ include/linux/soc/soc/airoha-scu-ssr.h | 17 ++ 8 files changed, 253 insertions(+) create mode 100644 drivers/soc/airoha/Kconfig create mode 100644 drivers/soc/airoha/Makefile create mode 100644 drivers/soc/airoha/airoha-scu-ssr.c create mode 100644 include/linux/soc/airoha/airoha-scu-ssr.h create mode 100644 include/linux/soc/soc/airoha-scu-ssr.h