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 unsigned int id; 48 }; 49 50 struct ls1x_data { 51 int (*setup)(struct platform_device *pdev, 52 struct plat_stmmacenet_data *plat_dat); 53 int (*init)(struct platform_device *pdev, void *bsp_priv); 54 }; 55 56 static int ls1b_dwmac_setup(struct platform_device *pdev, 57 struct plat_stmmacenet_data *plat_dat) 58 { 59 struct ls1x_dwmac *dwmac = plat_dat->bsp_priv; 60 struct resource *res; 61 62 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 63 if (!res) { 64 /* This shouldn't fail - stmmac_get_platform_resources() 65 * already mapped this resource. 66 */ 67 dev_err(&pdev->dev, "Could not get IO_MEM resources\n"); 68 return -EINVAL; 69 } 70 71 if (res->start == LS1B_GMAC0_BASE) { 72 dwmac->id = 0; 73 } else if (res->start == LS1B_GMAC1_BASE) { 74 dwmac->id = 1; 75 } else { 76 dev_err(&pdev->dev, "Invalid Ethernet MAC base address %pR", 77 res); 78 return -EINVAL; 79 } 80 81 return 0; 82 } 83 84 static int ls1b_dwmac_syscon_init(struct platform_device *pdev, void *priv) 85 { 86 struct ls1x_dwmac *dwmac = priv; 87 struct plat_stmmacenet_data *plat = dwmac->plat_dat; 88 struct regmap *regmap = dwmac->regmap; 89 90 if (dwmac->id == 0) { 91 switch (plat->phy_interface) { 92 case PHY_INTERFACE_MODE_RGMII_ID: 93 regmap_update_bits(regmap, LS1X_SYSCON0, 94 GMAC0_USE_TXCLK | GMAC0_USE_PWM01, 95 0); 96 break; 97 case PHY_INTERFACE_MODE_MII: 98 regmap_update_bits(regmap, LS1X_SYSCON0, 99 GMAC0_USE_TXCLK | GMAC0_USE_PWM01, 100 GMAC0_USE_TXCLK | GMAC0_USE_PWM01); 101 break; 102 default: 103 dev_err(&pdev->dev, "Unsupported PHY mode %u\n", 104 plat->phy_interface); 105 return -EOPNOTSUPP; 106 } 107 108 regmap_update_bits(regmap, LS1X_SYSCON0, GMAC0_SHUT, 0); 109 } else if (dwmac->id == 1) { 110 regmap_update_bits(regmap, LS1X_SYSCON0, 111 GMAC1_USE_UART1 | GMAC1_USE_UART0, 112 GMAC1_USE_UART1 | GMAC1_USE_UART0); 113 114 switch (plat->phy_interface) { 115 case PHY_INTERFACE_MODE_RGMII_ID: 116 regmap_update_bits(regmap, LS1X_SYSCON1, 117 GMAC1_USE_TXCLK | GMAC1_USE_PWM23, 118 0); 119 120 break; 121 case PHY_INTERFACE_MODE_MII: 122 regmap_update_bits(regmap, LS1X_SYSCON1, 123 GMAC1_USE_TXCLK | GMAC1_USE_PWM23, 124 GMAC1_USE_TXCLK | GMAC1_USE_PWM23); 125 break; 126 default: 127 dev_err(&pdev->dev, "Unsupported PHY mode %u\n", 128 plat->phy_interface); 129 return -EOPNOTSUPP; 130 } 131 132 regmap_update_bits(regmap, LS1X_SYSCON1, GMAC1_SHUT, 0); 133 } 134 135 return 0; 136 } 137 138 static int ls1c_dwmac_syscon_init(struct platform_device *pdev, void *priv) 139 { 140 struct ls1x_dwmac *dwmac = priv; 141 struct plat_stmmacenet_data *plat = dwmac->plat_dat; 142 struct regmap *regmap = dwmac->regmap; 143 144 switch (plat->phy_interface) { 145 case PHY_INTERFACE_MODE_MII: 146 regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI, 147 PHY_INTF_MII); 148 break; 149 case PHY_INTERFACE_MODE_RMII: 150 regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI, 151 PHY_INTF_RMII); 152 break; 153 default: 154 dev_err(&pdev->dev, "Unsupported PHY-mode %u\n", 155 plat->phy_interface); 156 return -EOPNOTSUPP; 157 } 158 159 regmap_update_bits(regmap, LS1X_SYSCON0, GMAC0_SHUT, 0); 160 161 return 0; 162 } 163 164 static int ls1x_dwmac_probe(struct platform_device *pdev) 165 { 166 struct plat_stmmacenet_data *plat_dat; 167 struct stmmac_resources stmmac_res; 168 const struct ls1x_data *data; 169 struct regmap *regmap; 170 struct ls1x_dwmac *dwmac; 171 int ret; 172 173 ret = stmmac_get_platform_resources(pdev, &stmmac_res); 174 if (ret) 175 return ret; 176 177 /* Probe syscon */ 178 regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 179 "loongson,ls1-syscon"); 180 if (IS_ERR(regmap)) 181 return dev_err_probe(&pdev->dev, PTR_ERR(regmap), 182 "Unable to find syscon\n"); 183 184 data = of_device_get_match_data(&pdev->dev); 185 if (!data) { 186 dev_err(&pdev->dev, "No of match data provided\n"); 187 return -EINVAL; 188 } 189 190 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 191 if (!dwmac) 192 return -ENOMEM; 193 194 plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); 195 if (IS_ERR(plat_dat)) 196 return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat), 197 "dt configuration failed\n"); 198 199 plat_dat->bsp_priv = dwmac; 200 plat_dat->init = data->init; 201 dwmac->plat_dat = plat_dat; 202 dwmac->regmap = regmap; 203 204 if (data->setup) { 205 ret = data->setup(pdev, plat_dat); 206 if (ret) 207 return ret; 208 } 209 210 return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); 211 } 212 213 static const struct ls1x_data ls1b_dwmac_data = { 214 .setup = ls1b_dwmac_setup, 215 .init = ls1b_dwmac_syscon_init, 216 }; 217 218 static const struct ls1x_data ls1c_dwmac_data = { 219 .init = ls1c_dwmac_syscon_init, 220 }; 221 222 static const struct of_device_id ls1x_dwmac_match[] = { 223 { 224 .compatible = "loongson,ls1b-gmac", 225 .data = &ls1b_dwmac_data, 226 }, 227 { 228 .compatible = "loongson,ls1c-emac", 229 .data = &ls1c_dwmac_data, 230 }, 231 { } 232 }; 233 MODULE_DEVICE_TABLE(of, ls1x_dwmac_match); 234 235 static struct platform_driver ls1x_dwmac_driver = { 236 .probe = ls1x_dwmac_probe, 237 .driver = { 238 .name = "loongson1-dwmac", 239 .of_match_table = ls1x_dwmac_match, 240 }, 241 }; 242 module_platform_driver(ls1x_dwmac_driver); 243 244 MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>"); 245 MODULE_DESCRIPTION("Loongson-1 DWMAC glue layer"); 246 MODULE_LICENSE("GPL"); 247