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