xref: /linux/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c (revision 8f7aa3d3c7323f4ca2768a9e74ebbe359c4f8f88)
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