1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Loongson-1 DWMAC glue layer 4 * 5 * Copyright (C) 2011-2023 Keguang Zhang <keguang.zhang@gmail.com> 6 */ 7 8 #include <linux/mfd/syscon.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/phy.h> 12 #include <linux/platform_device.h> 13 #include <linux/regmap.h> 14 15 #include "stmmac.h" 16 #include "stmmac_platform.h" 17 18 #define LS1B_GMAC0_BASE (0x1fe10000) 19 #define LS1B_GMAC1_BASE (0x1fe20000) 20 21 /* Loongson-1 SYSCON Registers */ 22 #define LS1X_SYSCON0 (0x0) 23 #define LS1X_SYSCON1 (0x4) 24 25 /* Loongson-1B SYSCON Register Bits */ 26 #define GMAC1_USE_UART1 BIT(4) 27 #define GMAC1_USE_UART0 BIT(3) 28 29 #define GMAC1_SHUT BIT(13) 30 #define GMAC0_SHUT BIT(12) 31 32 #define GMAC1_USE_TXCLK BIT(3) 33 #define GMAC0_USE_TXCLK BIT(2) 34 #define GMAC1_USE_PWM23 BIT(1) 35 #define GMAC0_USE_PWM01 BIT(0) 36 37 /* Loongson-1C SYSCON Register Bits */ 38 #define GMAC_SHUT BIT(6) 39 40 #define PHY_INTF_SELI GENMASK(30, 28) 41 #define PHY_INTF_MII FIELD_PREP(PHY_INTF_SELI, 0) 42 #define PHY_INTF_RMII FIELD_PREP(PHY_INTF_SELI, 4) 43 44 struct ls1x_dwmac { 45 struct plat_stmmacenet_data *plat_dat; 46 struct regmap *regmap; 47 }; 48 49 static int ls1b_dwmac_syscon_init(struct platform_device *pdev, void *priv) 50 { 51 struct ls1x_dwmac *dwmac = priv; 52 struct plat_stmmacenet_data *plat = dwmac->plat_dat; 53 struct regmap *regmap = dwmac->regmap; 54 struct resource *res; 55 unsigned long reg_base; 56 57 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 58 if (!res) { 59 dev_err(&pdev->dev, "Could not get IO_MEM resources\n"); 60 return -EINVAL; 61 } 62 reg_base = (unsigned long)res->start; 63 64 if (reg_base == LS1B_GMAC0_BASE) { 65 switch (plat->phy_interface) { 66 case PHY_INTERFACE_MODE_RGMII_ID: 67 regmap_update_bits(regmap, LS1X_SYSCON0, 68 GMAC0_USE_TXCLK | GMAC0_USE_PWM01, 69 0); 70 break; 71 case PHY_INTERFACE_MODE_MII: 72 regmap_update_bits(regmap, LS1X_SYSCON0, 73 GMAC0_USE_TXCLK | GMAC0_USE_PWM01, 74 GMAC0_USE_TXCLK | GMAC0_USE_PWM01); 75 break; 76 default: 77 dev_err(&pdev->dev, "Unsupported PHY mode %u\n", 78 plat->phy_interface); 79 return -EOPNOTSUPP; 80 } 81 82 regmap_update_bits(regmap, LS1X_SYSCON0, GMAC0_SHUT, 0); 83 } else if (reg_base == LS1B_GMAC1_BASE) { 84 regmap_update_bits(regmap, LS1X_SYSCON0, 85 GMAC1_USE_UART1 | GMAC1_USE_UART0, 86 GMAC1_USE_UART1 | GMAC1_USE_UART0); 87 88 switch (plat->phy_interface) { 89 case PHY_INTERFACE_MODE_RGMII_ID: 90 regmap_update_bits(regmap, LS1X_SYSCON1, 91 GMAC1_USE_TXCLK | GMAC1_USE_PWM23, 92 0); 93 94 break; 95 case PHY_INTERFACE_MODE_MII: 96 regmap_update_bits(regmap, LS1X_SYSCON1, 97 GMAC1_USE_TXCLK | GMAC1_USE_PWM23, 98 GMAC1_USE_TXCLK | GMAC1_USE_PWM23); 99 break; 100 default: 101 dev_err(&pdev->dev, "Unsupported PHY mode %u\n", 102 plat->phy_interface); 103 return -EOPNOTSUPP; 104 } 105 106 regmap_update_bits(regmap, LS1X_SYSCON1, GMAC1_SHUT, 0); 107 } else { 108 dev_err(&pdev->dev, "Invalid Ethernet MAC base address %lx", 109 reg_base); 110 return -EINVAL; 111 } 112 113 return 0; 114 } 115 116 static int ls1c_dwmac_syscon_init(struct platform_device *pdev, void *priv) 117 { 118 struct ls1x_dwmac *dwmac = priv; 119 struct plat_stmmacenet_data *plat = dwmac->plat_dat; 120 struct regmap *regmap = dwmac->regmap; 121 122 switch (plat->phy_interface) { 123 case PHY_INTERFACE_MODE_MII: 124 regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI, 125 PHY_INTF_MII); 126 break; 127 case PHY_INTERFACE_MODE_RMII: 128 regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI, 129 PHY_INTF_RMII); 130 break; 131 default: 132 dev_err(&pdev->dev, "Unsupported PHY-mode %u\n", 133 plat->phy_interface); 134 return -EOPNOTSUPP; 135 } 136 137 regmap_update_bits(regmap, LS1X_SYSCON0, GMAC0_SHUT, 0); 138 139 return 0; 140 } 141 142 static int ls1x_dwmac_probe(struct platform_device *pdev) 143 { 144 struct plat_stmmacenet_data *plat_dat; 145 struct stmmac_resources stmmac_res; 146 struct regmap *regmap; 147 struct ls1x_dwmac *dwmac; 148 int (*init)(struct platform_device *pdev, void *priv); 149 int ret; 150 151 ret = stmmac_get_platform_resources(pdev, &stmmac_res); 152 if (ret) 153 return ret; 154 155 /* Probe syscon */ 156 regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 157 "loongson,ls1-syscon"); 158 if (IS_ERR(regmap)) 159 return dev_err_probe(&pdev->dev, PTR_ERR(regmap), 160 "Unable to find syscon\n"); 161 162 init = of_device_get_match_data(&pdev->dev); 163 if (!init) { 164 dev_err(&pdev->dev, "No of match data provided\n"); 165 return -EINVAL; 166 } 167 168 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 169 if (!dwmac) 170 return -ENOMEM; 171 172 plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); 173 if (IS_ERR(plat_dat)) 174 return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat), 175 "dt configuration failed\n"); 176 177 plat_dat->bsp_priv = dwmac; 178 plat_dat->init = init; 179 dwmac->plat_dat = plat_dat; 180 dwmac->regmap = regmap; 181 182 return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); 183 } 184 185 static const struct of_device_id ls1x_dwmac_match[] = { 186 { 187 .compatible = "loongson,ls1b-gmac", 188 .data = &ls1b_dwmac_syscon_init, 189 }, 190 { 191 .compatible = "loongson,ls1c-emac", 192 .data = &ls1c_dwmac_syscon_init, 193 }, 194 { } 195 }; 196 MODULE_DEVICE_TABLE(of, ls1x_dwmac_match); 197 198 static struct platform_driver ls1x_dwmac_driver = { 199 .probe = ls1x_dwmac_probe, 200 .driver = { 201 .name = "loongson1-dwmac", 202 .of_match_table = ls1x_dwmac_match, 203 }, 204 }; 205 module_platform_driver(ls1x_dwmac_driver); 206 207 MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>"); 208 MODULE_DESCRIPTION("Loongson-1 DWMAC glue layer"); 209 MODULE_LICENSE("GPL"); 210