// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2018-19, Linaro Limited #include #include #include #include #include #include "stmmac.h" #include "stmmac_platform.h" #define RGMII_IO_MACRO_CONFIG 0x0 #define SDCC_HC_REG_DLL_CONFIG 0x4 #define SDCC_HC_REG_DDR_CONFIG 0xC #define SDCC_HC_REG_DLL_CONFIG2 0x10 #define SDC4_STATUS 0x14 #define SDCC_USR_CTL 0x18 #define RGMII_IO_MACRO_CONFIG2 0x1C #define RGMII_IO_MACRO_DEBUG1 0x20 #define EMAC_SYSTEM_LOW_POWER_DEBUG 0x28 /* RGMII_IO_MACRO_CONFIG fields */ #define RGMII_CONFIG_FUNC_CLK_EN BIT(30) #define RGMII_CONFIG_POS_NEG_DATA_SEL BIT(23) #define RGMII_CONFIG_GPIO_CFG_RX_INT GENMASK(21, 20) #define RGMII_CONFIG_GPIO_CFG_TX_INT GENMASK(19, 17) #define RGMII_CONFIG_MAX_SPD_PRG_9 GENMASK(16, 8) #define RGMII_CONFIG_MAX_SPD_PRG_2 GENMASK(7, 6) #define RGMII_CONFIG_INTF_SEL GENMASK(5, 4) #define RGMII_CONFIG_BYPASS_TX_ID_EN BIT(3) #define RGMII_CONFIG_LOOPBACK_EN BIT(2) #define RGMII_CONFIG_PROG_SWAP BIT(1) #define RGMII_CONFIG_DDR_MODE BIT(0) /* SDCC_HC_REG_DLL_CONFIG fields */ #define SDCC_DLL_CONFIG_DLL_RST BIT(30) #define SDCC_DLL_CONFIG_PDN BIT(29) #define SDCC_DLL_CONFIG_MCLK_FREQ GENMASK(26, 24) #define SDCC_DLL_CONFIG_CDR_SELEXT GENMASK(23, 20) #define SDCC_DLL_CONFIG_CDR_EXT_EN BIT(19) #define SDCC_DLL_CONFIG_CK_OUT_EN BIT(18) #define SDCC_DLL_CONFIG_CDR_EN BIT(17) #define SDCC_DLL_CONFIG_DLL_EN BIT(16) #define SDCC_DLL_MCLK_GATING_EN BIT(5) #define SDCC_DLL_CDR_FINE_PHASE GENMASK(3, 2) /* SDCC_HC_REG_DDR_CONFIG fields */ #define SDCC_DDR_CONFIG_PRG_DLY_EN BIT(31) #define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY GENMASK(26, 21) #define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE GENMASK(29, 27) #define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN BIT(30) #define SDCC_DDR_CONFIG_PRG_RCLK_DLY GENMASK(8, 0) /* SDCC_HC_REG_DLL_CONFIG2 fields */ #define SDCC_DLL_CONFIG2_DLL_CLOCK_DIS BIT(21) #define SDCC_DLL_CONFIG2_MCLK_FREQ_CALC GENMASK(17, 10) #define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL GENMASK(3, 2) #define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW BIT(1) #define SDCC_DLL_CONFIG2_DDR_CAL_EN BIT(0) /* SDC4_STATUS bits */ #define SDC4_STATUS_DLL_LOCK BIT(7) /* RGMII_IO_MACRO_CONFIG2 fields */ #define RGMII_CONFIG2_RSVD_CONFIG15 GENMASK(31, 17) #define RGMII_CONFIG2_RGMII_CLK_SEL_CFG BIT(16) #define RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN BIT(13) #define RGMII_CONFIG2_CLK_DIVIDE_SEL BIT(12) #define RGMII_CONFIG2_RX_PROG_SWAP BIT(7) #define RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL BIT(6) #define RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN BIT(5) struct ethqos_emac_por { unsigned int offset; unsigned int value; }; struct ethqos_emac_driver_data { const struct ethqos_emac_por *por; unsigned int num_por; }; struct qcom_ethqos { struct platform_device *pdev; void __iomem *rgmii_base; unsigned int rgmii_clk_rate; struct clk *rgmii_clk; unsigned int speed; const struct ethqos_emac_por *por; unsigned int num_por; }; static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset) { return readl(ethqos->rgmii_base + offset); } static void rgmii_writel(struct qcom_ethqos *ethqos, int value, unsigned int offset) { writel(value, ethqos->rgmii_base + offset); } static void rgmii_updatel(struct qcom_ethqos *ethqos, int mask, int val, unsigned int offset) { unsigned int temp; temp = rgmii_readl(ethqos, offset); temp = (temp & ~(mask)) | val; rgmii_writel(ethqos, temp, offset); } static void rgmii_dump(struct qcom_ethqos *ethqos) { dev_dbg(ðqos->pdev->dev, "Rgmii register dump\n"); dev_dbg(ðqos->pdev->dev, "RGMII_IO_MACRO_CONFIG: %x\n", rgmii_readl(ethqos, RGMII_IO_MACRO_CONFIG)); dev_dbg(ðqos->pdev->dev, "SDCC_HC_REG_DLL_CONFIG: %x\n", rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG)); dev_dbg(ðqos->pdev->dev, "SDCC_HC_REG_DDR_CONFIG: %x\n", rgmii_readl(ethqos, SDCC_HC_REG_DDR_CONFIG)); dev_dbg(ðqos->pdev->dev, "SDCC_HC_REG_DLL_CONFIG2: %x\n", rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG2)); dev_dbg(ðqos->pdev->dev, "SDC4_STATUS: %x\n", rgmii_readl(ethqos, SDC4_STATUS)); dev_dbg(ðqos->pdev->dev, "SDCC_USR_CTL: %x\n", rgmii_readl(ethqos, SDCC_USR_CTL)); dev_dbg(ðqos->pdev->dev, "RGMII_IO_MACRO_CONFIG2: %x\n", rgmii_readl(ethqos, RGMII_IO_MACRO_CONFIG2)); dev_dbg(ðqos->pdev->dev, "RGMII_IO_MACRO_DEBUG1: %x\n", rgmii_readl(ethqos, RGMII_IO_MACRO_DEBUG1)); dev_dbg(ðqos->pdev->dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %x\n", rgmii_readl(ethqos, EMAC_SYSTEM_LOW_POWER_DEBUG)); } /* Clock rates */ #define RGMII_1000_NOM_CLK_FREQ (250 * 1000 * 1000UL) #define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ (50 * 1000 * 1000UL) #define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ (5 * 1000 * 1000UL) static void ethqos_update_rgmii_clk(struct qcom_ethqos *ethqos, unsigned int speed) { switch (speed) { case SPEED_1000: ethqos->rgmii_clk_rate = RGMII_1000_NOM_CLK_FREQ; break; case SPEED_100: ethqos->rgmii_clk_rate = RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ; break; case SPEED_10: ethqos->rgmii_clk_rate = RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ; break; } clk_set_rate(ethqos->rgmii_clk, ethqos->rgmii_clk_rate); } static void ethqos_set_func_clk_en(struct qcom_ethqos *ethqos) { rgmii_updatel(ethqos, RGMII_CONFIG_FUNC_CLK_EN, RGMII_CONFIG_FUNC_CLK_EN, RGMII_IO_MACRO_CONFIG); } static const struct ethqos_emac_por emac_v2_3_0_por[] = { { .offset = RGMII_IO_MACRO_CONFIG, .value = 0x00C01343 }, { .offset = SDCC_HC_REG_DLL_CONFIG, .value = 0x2004642C }, { .offset = SDCC_HC_REG_DDR_CONFIG, .value = 0x00000000 }, { .offset = SDCC_HC_REG_DLL_CONFIG2, .value = 0x00200000 }, { .offset = SDCC_USR_CTL, .value = 0x00010800 }, { .offset = RGMII_IO_MACRO_CONFIG2, .value = 0x00002060 }, }; static const struct ethqos_emac_driver_data emac_v2_3_0_data = { .por = emac_v2_3_0_por, .num_por = ARRAY_SIZE(emac_v2_3_0_por), }; static int ethqos_dll_configure(struct qcom_ethqos *ethqos) { unsigned int val; int retry = 1000; /* Set CDR_EN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CDR_EN, SDCC_DLL_CONFIG_CDR_EN, SDCC_HC_REG_DLL_CONFIG); /* Set CDR_EXT_EN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CDR_EXT_EN, SDCC_DLL_CONFIG_CDR_EXT_EN, SDCC_HC_REG_DLL_CONFIG); /* Clear CK_OUT_EN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN, 0, SDCC_HC_REG_DLL_CONFIG); /* Set DLL_EN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_EN, SDCC_DLL_CONFIG_DLL_EN, SDCC_HC_REG_DLL_CONFIG); rgmii_updatel(ethqos, SDCC_DLL_MCLK_GATING_EN, 0, SDCC_HC_REG_DLL_CONFIG); rgmii_updatel(ethqos, SDCC_DLL_CDR_FINE_PHASE, 0, SDCC_HC_REG_DLL_CONFIG); /* Wait for CK_OUT_EN clear */ do { val = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG); val &= SDCC_DLL_CONFIG_CK_OUT_EN; if (!val) break; mdelay(1); retry--; } while (retry > 0); if (!retry) dev_err(ðqos->pdev->dev, "Clear CK_OUT_EN timedout\n"); /* Set CK_OUT_EN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN, SDCC_DLL_CONFIG_CK_OUT_EN, SDCC_HC_REG_DLL_CONFIG); /* Wait for CK_OUT_EN set */ retry = 1000; do { val = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG); val &= SDCC_DLL_CONFIG_CK_OUT_EN; if (val) break; mdelay(1); retry--; } while (retry > 0); if (!retry) dev_err(ðqos->pdev->dev, "Set CK_OUT_EN timedout\n"); /* Set DDR_CAL_EN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_CAL_EN, SDCC_DLL_CONFIG2_DDR_CAL_EN, SDCC_HC_REG_DLL_CONFIG2); rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DLL_CLOCK_DIS, 0, SDCC_HC_REG_DLL_CONFIG2); rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10, SDCC_HC_REG_DLL_CONFIG2); rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2), SDCC_HC_REG_DLL_CONFIG2); rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW, SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW, SDCC_HC_REG_DLL_CONFIG2); return 0; } static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos) { /* Disable loopback mode */ rgmii_updatel(ethqos, RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN, 0, RGMII_IO_MACRO_CONFIG2); /* Select RGMII, write 0 to interface select */ rgmii_updatel(ethqos, RGMII_CONFIG_INTF_SEL, 0, RGMII_IO_MACRO_CONFIG); switch (ethqos->speed) { case SPEED_1000: rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE, RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN, 0, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL, RGMII_CONFIG_POS_NEG_DATA_SEL, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP, RGMII_CONFIG_PROG_SWAP, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL, 0, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15, 0, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP, RGMII_CONFIG2_RX_PROG_SWAP, RGMII_IO_MACRO_CONFIG2); /* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */ rgmii_updatel(ethqos, SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57, SDCC_HC_REG_DDR_CONFIG); rgmii_updatel(ethqos, SDCC_DDR_CONFIG_PRG_DLY_EN, SDCC_DDR_CONFIG_PRG_DLY_EN, SDCC_HC_REG_DDR_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG); break; case SPEED_100: rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE, RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN, RGMII_CONFIG_BYPASS_TX_ID_EN, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL, 0, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP, 0, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL, 0, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6), RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15, 0, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP, 0, RGMII_IO_MACRO_CONFIG2); /* Write 0x5 to PRG_RCLK_DLY_CODE */ rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, (BIT(29) | BIT(27)), SDCC_HC_REG_DDR_CONFIG); rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY, SDCC_HC_REG_DDR_CONFIG); rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN, SDCC_HC_REG_DDR_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG); break; case SPEED_10: rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE, RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN, RGMII_CONFIG_BYPASS_TX_ID_EN, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL, 0, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP, 0, RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL, 0, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN, 0, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG_MAX_SPD_PRG_9, BIT(12) | GENMASK(9, 8), RGMII_IO_MACRO_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15, 0, RGMII_IO_MACRO_CONFIG2); rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP, 0, RGMII_IO_MACRO_CONFIG2); /* Write 0x5 to PRG_RCLK_DLY_CODE */ rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, (BIT(29) | BIT(27)), SDCC_HC_REG_DDR_CONFIG); rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY, SDCC_HC_REG_DDR_CONFIG); rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN, SDCC_HC_REG_DDR_CONFIG); rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG); break; default: dev_err(ðqos->pdev->dev, "Invalid speed %d\n", ethqos->speed); return -EINVAL; } return 0; } static int ethqos_configure(struct qcom_ethqos *ethqos) { volatile unsigned int dll_lock; unsigned int i, retry = 1000; /* Reset to POR values and enable clk */ for (i = 0; i < ethqos->num_por; i++) rgmii_writel(ethqos, ethqos->por[i].value, ethqos->por[i].offset); ethqos_set_func_clk_en(ethqos); /* Initialize the DLL first */ /* Set DLL_RST */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_RST, SDCC_DLL_CONFIG_DLL_RST, SDCC_HC_REG_DLL_CONFIG); /* Set PDN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_PDN, SDCC_DLL_CONFIG_PDN, SDCC_HC_REG_DLL_CONFIG); /* Clear DLL_RST */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_RST, 0, SDCC_HC_REG_DLL_CONFIG); /* Clear PDN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_PDN, 0, SDCC_HC_REG_DLL_CONFIG); if (ethqos->speed != SPEED_100 && ethqos->speed != SPEED_10) { /* Set DLL_EN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_EN, SDCC_DLL_CONFIG_DLL_EN, SDCC_HC_REG_DLL_CONFIG); /* Set CK_OUT_EN */ rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN, SDCC_DLL_CONFIG_CK_OUT_EN, SDCC_HC_REG_DLL_CONFIG); /* Set USR_CTL bit 26 with mask of 3 bits */ rgmii_updatel(ethqos, GENMASK(26, 24), BIT(26), SDCC_USR_CTL); /* wait for DLL LOCK */ do { mdelay(1); dll_lock = rgmii_readl(ethqos, SDC4_STATUS); if (dll_lock & SDC4_STATUS_DLL_LOCK) break; retry--; } while (retry > 0); if (!retry) dev_err(ðqos->pdev->dev, "Timeout while waiting for DLL lock\n"); } if (ethqos->speed == SPEED_1000) ethqos_dll_configure(ethqos); ethqos_rgmii_macro_init(ethqos); return 0; } static void ethqos_fix_mac_speed(void *priv, unsigned int speed) { struct qcom_ethqos *ethqos = priv; ethqos->speed = speed; ethqos_update_rgmii_clk(ethqos, speed); ethqos_configure(ethqos); } static int qcom_ethqos_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct plat_stmmacenet_data *plat_dat; struct stmmac_resources stmmac_res; const struct ethqos_emac_driver_data *data; struct qcom_ethqos *ethqos; struct resource *res; int ret; ret = stmmac_get_platform_resources(pdev, &stmmac_res); if (ret) return ret; plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); if (IS_ERR(plat_dat)) { dev_err(&pdev->dev, "dt configuration failed\n"); return PTR_ERR(plat_dat); } ethqos = devm_kzalloc(&pdev->dev, sizeof(*ethqos), GFP_KERNEL); if (!ethqos) { ret = -ENOMEM; goto err_mem; } ethqos->pdev = pdev; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rgmii"); ethqos->rgmii_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ethqos->rgmii_base)) { ret = PTR_ERR(ethqos->rgmii_base); goto err_mem; } data = of_device_get_match_data(&pdev->dev); ethqos->por = data->por; ethqos->num_por = data->num_por; ethqos->rgmii_clk = devm_clk_get(&pdev->dev, "rgmii"); if (IS_ERR(ethqos->rgmii_clk)) { ret = PTR_ERR(ethqos->rgmii_clk); goto err_mem; } ret = clk_prepare_enable(ethqos->rgmii_clk); if (ret) goto err_mem; ethqos->speed = SPEED_1000; ethqos_update_rgmii_clk(ethqos, SPEED_1000); ethqos_set_func_clk_en(ethqos); plat_dat->bsp_priv = ethqos; plat_dat->fix_mac_speed = ethqos_fix_mac_speed; plat_dat->has_gmac4 = 1; plat_dat->pmt = 1; plat_dat->tso_en = of_property_read_bool(np, "snps,tso"); ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); if (ret) goto err_clk; rgmii_dump(ethqos); return ret; err_clk: clk_disable_unprepare(ethqos->rgmii_clk); err_mem: stmmac_remove_config_dt(pdev, plat_dat); return ret; } static int qcom_ethqos_remove(struct platform_device *pdev) { struct qcom_ethqos *ethqos; int ret; ethqos = get_stmmac_bsp_priv(&pdev->dev); if (!ethqos) return -ENODEV; ret = stmmac_pltfr_remove(pdev); clk_disable_unprepare(ethqos->rgmii_clk); return ret; } static const struct of_device_id qcom_ethqos_match[] = { { .compatible = "qcom,qcs404-ethqos", .data = &emac_v2_3_0_data}, { } }; MODULE_DEVICE_TABLE(of, qcom_ethqos_match); static struct platform_driver qcom_ethqos_driver = { .probe = qcom_ethqos_probe, .remove = qcom_ethqos_remove, .driver = { .name = "qcom-ethqos", .pm = &stmmac_pltfr_pm_ops, .of_match_table = of_match_ptr(qcom_ethqos_match), }, }; module_platform_driver(qcom_ethqos_driver); MODULE_DESCRIPTION("Qualcomm ETHQOS driver"); MODULE_LICENSE("GPL v2");