// SPDX-License-Identifier: GPL-2.0 /* * Lantiq / Intel GSWIP switch driver for VRX200, xRX300 and xRX330 SoCs * * Copyright (C) 2025 Daniel Golle * Copyright (C) 2017 - 2019 Hauke Mehrtens * Copyright (C) 2012 John Crispin * Copyright (C) 2010 Lantiq Deutschland */ #include "lantiq_gswip.h" #include "lantiq_pce.h" #include #include #include #include #include #include #include #include #include #include #include struct xway_gphy_match_data { char *fe_firmware_name; char *ge_firmware_name; }; static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { switch (port) { case 0: case 1: phy_interface_set_rgmii(config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_REVMII, config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_RMII, config->supported_interfaces); break; case 2: case 3: case 4: case 6: __set_bit(PHY_INTERFACE_MODE_INTERNAL, config->supported_interfaces); break; case 5: phy_interface_set_rgmii(config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_INTERNAL, config->supported_interfaces); break; } config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000; } static void gswip_xrx300_phylink_get_caps(struct dsa_switch *ds, int port, struct phylink_config *config) { switch (port) { case 0: phy_interface_set_rgmii(config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_RMII, config->supported_interfaces); break; case 1: case 2: case 3: case 4: case 6: __set_bit(PHY_INTERFACE_MODE_INTERNAL, config->supported_interfaces); break; case 5: phy_interface_set_rgmii(config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_INTERNAL, config->supported_interfaces); __set_bit(PHY_INTERFACE_MODE_RMII, config->supported_interfaces); break; } config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000; } static const struct xway_gphy_match_data xrx200a1x_gphy_data = { .fe_firmware_name = "lantiq/xrx200_phy22f_a14.bin", .ge_firmware_name = "lantiq/xrx200_phy11g_a14.bin", }; static const struct xway_gphy_match_data xrx200a2x_gphy_data = { .fe_firmware_name = "lantiq/xrx200_phy22f_a22.bin", .ge_firmware_name = "lantiq/xrx200_phy11g_a22.bin", }; static const struct xway_gphy_match_data xrx300_gphy_data = { .fe_firmware_name = "lantiq/xrx300_phy22f_a21.bin", .ge_firmware_name = "lantiq/xrx300_phy11g_a21.bin", }; static const struct of_device_id xway_gphy_match[] __maybe_unused = { { .compatible = "lantiq,xrx200-gphy-fw", .data = NULL }, { .compatible = "lantiq,xrx200a1x-gphy-fw", .data = &xrx200a1x_gphy_data }, { .compatible = "lantiq,xrx200a2x-gphy-fw", .data = &xrx200a2x_gphy_data }, { .compatible = "lantiq,xrx300-gphy-fw", .data = &xrx300_gphy_data }, { .compatible = "lantiq,xrx330-gphy-fw", .data = &xrx300_gphy_data }, {}, }; static int gswip_gphy_fw_load(struct gswip_priv *priv, struct gswip_gphy_fw *gphy_fw) { struct device *dev = priv->dev; const struct firmware *fw; void *fw_addr; dma_addr_t dma_addr; dma_addr_t dev_addr; size_t size; int ret; ret = clk_prepare_enable(gphy_fw->clk_gate); if (ret) return ret; reset_control_assert(gphy_fw->reset); /* The vendor BSP uses a 200ms delay after asserting the reset line. * Without this some users are observing that the PHY is not coming up * on the MDIO bus. */ msleep(200); ret = request_firmware(&fw, gphy_fw->fw_name, dev); if (ret) return dev_err_probe(dev, ret, "failed to load firmware: %s\n", gphy_fw->fw_name); /* GPHY cores need the firmware code in a persistent and contiguous * memory area with a 16 kB boundary aligned start address. */ size = fw->size + XRX200_GPHY_FW_ALIGN; fw_addr = dmam_alloc_coherent(dev, size, &dma_addr, GFP_KERNEL); if (fw_addr) { fw_addr = PTR_ALIGN(fw_addr, XRX200_GPHY_FW_ALIGN); dev_addr = ALIGN(dma_addr, XRX200_GPHY_FW_ALIGN); memcpy(fw_addr, fw->data, fw->size); } else { release_firmware(fw); return -ENOMEM; } release_firmware(fw); ret = regmap_write(priv->rcu_regmap, gphy_fw->fw_addr_offset, dev_addr); if (ret) return ret; reset_control_deassert(gphy_fw->reset); return ret; } static int gswip_gphy_fw_probe(struct gswip_priv *priv, struct gswip_gphy_fw *gphy_fw, struct device_node *gphy_fw_np, int i) { struct device *dev = priv->dev; u32 gphy_mode; int ret; char gphyname[10]; snprintf(gphyname, sizeof(gphyname), "gphy%d", i); gphy_fw->clk_gate = devm_clk_get(dev, gphyname); if (IS_ERR(gphy_fw->clk_gate)) { return dev_err_probe(dev, PTR_ERR(gphy_fw->clk_gate), "Failed to lookup gate clock\n"); } ret = of_property_read_u32(gphy_fw_np, "reg", &gphy_fw->fw_addr_offset); if (ret) return ret; ret = of_property_read_u32(gphy_fw_np, "lantiq,gphy-mode", &gphy_mode); /* Default to GE mode */ if (ret) gphy_mode = GPHY_MODE_GE; switch (gphy_mode) { case GPHY_MODE_FE: gphy_fw->fw_name = priv->gphy_fw_name_cfg->fe_firmware_name; break; case GPHY_MODE_GE: gphy_fw->fw_name = priv->gphy_fw_name_cfg->ge_firmware_name; break; default: return dev_err_probe(dev, -EINVAL, "Unknown GPHY mode %d\n", gphy_mode); } gphy_fw->reset = of_reset_control_array_get_exclusive(gphy_fw_np); if (IS_ERR(gphy_fw->reset)) return dev_err_probe(dev, PTR_ERR(gphy_fw->reset), "Failed to lookup gphy reset\n"); return gswip_gphy_fw_load(priv, gphy_fw); } static void gswip_gphy_fw_remove(struct gswip_priv *priv, struct gswip_gphy_fw *gphy_fw) { int ret; /* check if the device was fully probed */ if (!gphy_fw->fw_name) return; ret = regmap_write(priv->rcu_regmap, gphy_fw->fw_addr_offset, 0); if (ret) dev_err(priv->dev, "can not reset GPHY FW pointer\n"); clk_disable_unprepare(gphy_fw->clk_gate); reset_control_put(gphy_fw->reset); } static int gswip_gphy_fw_list(struct gswip_priv *priv, struct device_node *gphy_fw_list_np, u32 version) { struct device *dev = priv->dev; struct device_node *gphy_fw_np; const struct of_device_id *match; int err; int i = 0; /* The VRX200 rev 1.1 uses the GSWIP 2.0 and needs the older * GPHY firmware. The VRX200 rev 1.2 uses the GSWIP 2.1 and also * needs a different GPHY firmware. */ if (of_device_is_compatible(gphy_fw_list_np, "lantiq,xrx200-gphy-fw")) { switch (version) { case GSWIP_VERSION_2_0: priv->gphy_fw_name_cfg = &xrx200a1x_gphy_data; break; case GSWIP_VERSION_2_1: priv->gphy_fw_name_cfg = &xrx200a2x_gphy_data; break; default: return dev_err_probe(dev, -ENOENT, "unknown GSWIP version: 0x%x\n", version); } } match = of_match_node(xway_gphy_match, gphy_fw_list_np); if (match && match->data) priv->gphy_fw_name_cfg = match->data; if (!priv->gphy_fw_name_cfg) return dev_err_probe(dev, -ENOENT, "GPHY compatible type not supported\n"); priv->num_gphy_fw = of_get_available_child_count(gphy_fw_list_np); if (!priv->num_gphy_fw) return -ENOENT; priv->rcu_regmap = syscon_regmap_lookup_by_phandle(gphy_fw_list_np, "lantiq,rcu"); if (IS_ERR(priv->rcu_regmap)) return PTR_ERR(priv->rcu_regmap); priv->gphy_fw = devm_kmalloc_array(dev, priv->num_gphy_fw, sizeof(*priv->gphy_fw), GFP_KERNEL | __GFP_ZERO); if (!priv->gphy_fw) return -ENOMEM; for_each_available_child_of_node(gphy_fw_list_np, gphy_fw_np) { err = gswip_gphy_fw_probe(priv, &priv->gphy_fw[i], gphy_fw_np, i); if (err) { of_node_put(gphy_fw_np); goto remove_gphy; } i++; } /* The standalone PHY11G requires 300ms to be fully * initialized and ready for any MDIO communication after being * taken out of reset. For the SoC-internal GPHY variant there * is no (known) documentation for the minimum time after a * reset. Use the same value as for the standalone variant as * some users have reported internal PHYs not being detected * without any delay. */ msleep(300); return 0; remove_gphy: for (i = 0; i < priv->num_gphy_fw; i++) gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]); return err; } static const struct regmap_config sw_regmap_config = { .name = "switch", .reg_bits = 32, .val_bits = 32, .reg_shift = REGMAP_UPSHIFT(2), .val_format_endian = REGMAP_ENDIAN_NATIVE, .max_register = GSWIP_SDMA_PCTRLp(6), }; static const struct regmap_config mdio_regmap_config = { .name = "mdio", .reg_bits = 32, .val_bits = 32, .reg_shift = REGMAP_UPSHIFT(2), .val_format_endian = REGMAP_ENDIAN_NATIVE, .max_register = GSWIP_MDIO_PHYp(0), }; static const struct regmap_config mii_regmap_config = { .name = "mii", .reg_bits = 32, .val_bits = 32, .reg_shift = REGMAP_UPSHIFT(2), .val_format_endian = REGMAP_ENDIAN_NATIVE, .max_register = GSWIP_MII_CFGp(6), }; static int gswip_probe(struct platform_device *pdev) { struct device_node *np, *gphy_fw_np; __iomem void *gswip, *mdio, *mii; struct device *dev = &pdev->dev; struct gswip_priv *priv; int err; int i; u32 version; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; gswip = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gswip)) return PTR_ERR(gswip); mdio = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(mdio)) return PTR_ERR(mdio); mii = devm_platform_ioremap_resource(pdev, 2); if (IS_ERR(mii)) return PTR_ERR(mii); priv->gswip = devm_regmap_init_mmio(dev, gswip, &sw_regmap_config); if (IS_ERR(priv->gswip)) return PTR_ERR(priv->gswip); priv->mdio = devm_regmap_init_mmio(dev, mdio, &mdio_regmap_config); if (IS_ERR(priv->mdio)) return PTR_ERR(priv->mdio); priv->mii = devm_regmap_init_mmio(dev, mii, &mii_regmap_config); if (IS_ERR(priv->mii)) return PTR_ERR(priv->mii); priv->hw_info = of_device_get_match_data(dev); if (!priv->hw_info) return -EINVAL; priv->ds = devm_kzalloc(dev, sizeof(*priv->ds), GFP_KERNEL); if (!priv->ds) return -ENOMEM; priv->dev = dev; regmap_read(priv->gswip, GSWIP_VERSION, &version); np = dev->of_node; switch (version) { case GSWIP_VERSION_2_0: case GSWIP_VERSION_2_1: if (!of_device_is_compatible(np, "lantiq,xrx200-gswip")) return -EINVAL; break; case GSWIP_VERSION_2_2: case GSWIP_VERSION_2_2_ETC: if (!of_device_is_compatible(np, "lantiq,xrx300-gswip") && !of_device_is_compatible(np, "lantiq,xrx330-gswip")) return -EINVAL; break; default: return dev_err_probe(dev, -ENOENT, "unknown GSWIP version: 0x%x\n", version); } /* bring up the mdio bus */ gphy_fw_np = of_get_compatible_child(dev->of_node, "lantiq,gphy-fw"); if (gphy_fw_np) { err = gswip_gphy_fw_list(priv, gphy_fw_np, version); of_node_put(gphy_fw_np); if (err) return dev_err_probe(dev, err, "gphy fw probe failed\n"); } err = gswip_probe_common(priv, version); if (err) goto gphy_fw_remove; platform_set_drvdata(pdev, priv); return 0; gphy_fw_remove: for (i = 0; i < priv->num_gphy_fw; i++) gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]); return err; } static void gswip_remove(struct platform_device *pdev) { struct gswip_priv *priv = platform_get_drvdata(pdev); int i; if (!priv) return; /* disable the switch */ gswip_disable_switch(priv); dsa_unregister_switch(priv->ds); for (i = 0; i < priv->num_gphy_fw; i++) gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]); } static void gswip_shutdown(struct platform_device *pdev) { struct gswip_priv *priv = platform_get_drvdata(pdev); if (!priv) return; dsa_switch_shutdown(priv->ds); platform_set_drvdata(pdev, NULL); } static const struct gswip_hw_info gswip_xrx200 = { .max_ports = 7, .allowed_cpu_ports = BIT(6), .mii_ports = BIT(0) | BIT(1) | BIT(5), .mii_port_reg_offset = 0, .phylink_get_caps = gswip_xrx200_phylink_get_caps, .pce_microcode = &gswip_pce_microcode, .pce_microcode_size = ARRAY_SIZE(gswip_pce_microcode), .tag_protocol = DSA_TAG_PROTO_GSWIP, }; static const struct gswip_hw_info gswip_xrx300 = { .max_ports = 7, .allowed_cpu_ports = BIT(6), .mii_ports = BIT(0) | BIT(5), .mii_port_reg_offset = 0, .phylink_get_caps = gswip_xrx300_phylink_get_caps, .pce_microcode = &gswip_pce_microcode, .pce_microcode_size = ARRAY_SIZE(gswip_pce_microcode), .tag_protocol = DSA_TAG_PROTO_GSWIP, }; static const struct of_device_id gswip_of_match[] = { { .compatible = "lantiq,xrx200-gswip", .data = &gswip_xrx200 }, { .compatible = "lantiq,xrx300-gswip", .data = &gswip_xrx300 }, { .compatible = "lantiq,xrx330-gswip", .data = &gswip_xrx300 }, {}, }; MODULE_DEVICE_TABLE(of, gswip_of_match); static struct platform_driver gswip_driver = { .probe = gswip_probe, .remove = gswip_remove, .shutdown = gswip_shutdown, .driver = { .name = "gswip", .of_match_table = gswip_of_match, }, }; module_platform_driver(gswip_driver); MODULE_FIRMWARE("lantiq/xrx300_phy11g_a21.bin"); MODULE_FIRMWARE("lantiq/xrx300_phy22f_a21.bin"); MODULE_FIRMWARE("lantiq/xrx200_phy11g_a14.bin"); MODULE_FIRMWARE("lantiq/xrx200_phy11g_a22.bin"); MODULE_FIRMWARE("lantiq/xrx200_phy22f_a14.bin"); MODULE_FIRMWARE("lantiq/xrx200_phy22f_a22.bin"); MODULE_AUTHOR("Hauke Mehrtens "); MODULE_DESCRIPTION("Lantiq / Intel GSWIP driver"); MODULE_LICENSE("GPL v2");