Commit | Line | Data |
---|---|---|
b1c17215 MO |
1 | /* |
2 | * Qualcomm Atheros IPQ806x GMAC glue layer | |
3 | * | |
4 | * Copyright (C) 2015 The Linux Foundation | |
5 | * | |
6 | * Permission to use, copy, modify, and/or distribute this software for any | |
7 | * purpose with or without fee is hereby granted, provided that the above | |
8 | * copyright notice and this permission notice appear in all copies. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | */ | |
18 | ||
19 | #include <linux/device.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/phy.h> | |
22 | #include <linux/regmap.h> | |
23 | #include <linux/clk.h> | |
24 | #include <linux/reset.h> | |
25 | #include <linux/of_net.h> | |
26 | #include <linux/mfd/syscon.h> | |
27 | #include <linux/stmmac.h> | |
28 | #include <linux/of_mdio.h> | |
29 | #include <linux/module.h> | |
30 | ||
31 | #include "stmmac_platform.h" | |
32 | ||
33 | #define NSS_COMMON_CLK_GATE 0x8 | |
34 | #define NSS_COMMON_CLK_GATE_PTP_EN(x) BIT(0x10 + x) | |
35 | #define NSS_COMMON_CLK_GATE_RGMII_RX_EN(x) BIT(0x9 + (x * 2)) | |
36 | #define NSS_COMMON_CLK_GATE_RGMII_TX_EN(x) BIT(0x8 + (x * 2)) | |
37 | #define NSS_COMMON_CLK_GATE_GMII_RX_EN(x) BIT(0x4 + x) | |
38 | #define NSS_COMMON_CLK_GATE_GMII_TX_EN(x) BIT(0x0 + x) | |
39 | ||
40 | #define NSS_COMMON_CLK_DIV0 0xC | |
41 | #define NSS_COMMON_CLK_DIV_OFFSET(x) (x * 8) | |
42 | #define NSS_COMMON_CLK_DIV_MASK 0x7f | |
43 | ||
44 | #define NSS_COMMON_CLK_SRC_CTRL 0x14 | |
4f7eb70f | 45 | #define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (x) |
b1c17215 MO |
46 | /* Mode is coded on 1 bit but is different depending on the MAC ID: |
47 | * MAC0: QSGMII=0 RGMII=1 | |
48 | * MAC1: QSGMII=0 SGMII=0 RGMII=1 | |
49 | * MAC2 & MAC3: QSGMII=0 SGMII=1 | |
50 | */ | |
51 | #define NSS_COMMON_CLK_SRC_CTRL_RGMII(x) 1 | |
52 | #define NSS_COMMON_CLK_SRC_CTRL_SGMII(x) ((x >= 2) ? 1 : 0) | |
53 | ||
54 | #define NSS_COMMON_MACSEC_CTL 0x28 | |
55 | #define NSS_COMMON_MACSEC_CTL_EXT_BYPASS_EN(x) (1 << x) | |
56 | ||
57 | #define NSS_COMMON_GMAC_CTL(x) (0x30 + (x * 4)) | |
58 | #define NSS_COMMON_GMAC_CTL_CSYS_REQ BIT(19) | |
59 | #define NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL BIT(16) | |
60 | #define NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET 8 | |
61 | #define NSS_COMMON_GMAC_CTL_IFG_OFFSET 0 | |
62 | #define NSS_COMMON_GMAC_CTL_IFG_MASK 0x3f | |
63 | ||
64 | #define NSS_COMMON_CLK_DIV_RGMII_1000 1 | |
65 | #define NSS_COMMON_CLK_DIV_RGMII_100 9 | |
66 | #define NSS_COMMON_CLK_DIV_RGMII_10 99 | |
67 | #define NSS_COMMON_CLK_DIV_SGMII_1000 0 | |
68 | #define NSS_COMMON_CLK_DIV_SGMII_100 4 | |
69 | #define NSS_COMMON_CLK_DIV_SGMII_10 49 | |
70 | ||
71 | #define QSGMII_PCS_MODE_CTL 0x68 | |
72 | #define QSGMII_PCS_MODE_CTL_AUTONEG_EN(x) BIT((x * 8) + 7) | |
73 | ||
74 | #define QSGMII_PCS_CAL_LCKDT_CTL 0x120 | |
75 | #define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19) | |
76 | ||
77 | /* Only GMAC1/2/3 support SGMII and their CTL register are not contiguous */ | |
78 | #define QSGMII_PHY_SGMII_CTL(x) ((x == 1) ? 0x134 : \ | |
79 | (0x13c + (4 * (x - 2)))) | |
80 | #define QSGMII_PHY_CDR_EN BIT(0) | |
81 | #define QSGMII_PHY_RX_FRONT_EN BIT(1) | |
82 | #define QSGMII_PHY_RX_SIGNAL_DETECT_EN BIT(2) | |
83 | #define QSGMII_PHY_TX_DRIVER_EN BIT(3) | |
84 | #define QSGMII_PHY_QSGMII_EN BIT(7) | |
85 | #define QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET 12 | |
86 | #define QSGMII_PHY_PHASE_LOOP_GAIN_MASK 0x7 | |
87 | #define QSGMII_PHY_RX_DC_BIAS_OFFSET 18 | |
88 | #define QSGMII_PHY_RX_DC_BIAS_MASK 0x3 | |
89 | #define QSGMII_PHY_RX_INPUT_EQU_OFFSET 20 | |
90 | #define QSGMII_PHY_RX_INPUT_EQU_MASK 0x3 | |
91 | #define QSGMII_PHY_CDR_PI_SLEW_OFFSET 22 | |
92 | #define QSGMII_PHY_CDR_PI_SLEW_MASK 0x3 | |
93 | #define QSGMII_PHY_TX_DRV_AMP_OFFSET 28 | |
94 | #define QSGMII_PHY_TX_DRV_AMP_MASK 0xf | |
95 | ||
96 | struct ipq806x_gmac { | |
97 | struct platform_device *pdev; | |
98 | struct regmap *nss_common; | |
99 | struct regmap *qsgmii_csr; | |
100 | uint32_t id; | |
101 | struct clk *core_clk; | |
102 | phy_interface_t phy_mode; | |
103 | }; | |
104 | ||
105 | static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed) | |
106 | { | |
107 | struct device *dev = &gmac->pdev->dev; | |
108 | int div; | |
109 | ||
110 | switch (speed) { | |
111 | case SPEED_1000: | |
112 | div = NSS_COMMON_CLK_DIV_SGMII_1000; | |
113 | break; | |
114 | ||
115 | case SPEED_100: | |
116 | div = NSS_COMMON_CLK_DIV_SGMII_100; | |
117 | break; | |
118 | ||
119 | case SPEED_10: | |
120 | div = NSS_COMMON_CLK_DIV_SGMII_10; | |
121 | break; | |
122 | ||
123 | default: | |
124 | dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed); | |
125 | return -EINVAL; | |
126 | } | |
127 | ||
128 | return div; | |
129 | } | |
130 | ||
131 | static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed) | |
132 | { | |
133 | struct device *dev = &gmac->pdev->dev; | |
134 | int div; | |
135 | ||
136 | switch (speed) { | |
137 | case SPEED_1000: | |
138 | div = NSS_COMMON_CLK_DIV_RGMII_1000; | |
139 | break; | |
140 | ||
141 | case SPEED_100: | |
142 | div = NSS_COMMON_CLK_DIV_RGMII_100; | |
143 | break; | |
144 | ||
145 | case SPEED_10: | |
146 | div = NSS_COMMON_CLK_DIV_RGMII_10; | |
147 | break; | |
148 | ||
149 | default: | |
150 | dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed); | |
151 | return -EINVAL; | |
152 | } | |
153 | ||
154 | return div; | |
155 | } | |
156 | ||
157 | static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed) | |
158 | { | |
159 | uint32_t clk_bits, val; | |
160 | int div; | |
161 | ||
162 | switch (gmac->phy_mode) { | |
163 | case PHY_INTERFACE_MODE_RGMII: | |
164 | div = get_clk_div_rgmii(gmac, speed); | |
165 | clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) | | |
166 | NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id); | |
167 | break; | |
168 | ||
169 | case PHY_INTERFACE_MODE_SGMII: | |
170 | div = get_clk_div_sgmii(gmac, speed); | |
171 | clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) | | |
172 | NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id); | |
173 | break; | |
174 | ||
175 | default: | |
176 | dev_err(&gmac->pdev->dev, "Unsupported PHY mode: \"%s\"\n", | |
177 | phy_modes(gmac->phy_mode)); | |
178 | return -EINVAL; | |
179 | } | |
180 | ||
181 | /* Disable the clocks */ | |
182 | regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); | |
183 | val &= ~clk_bits; | |
184 | regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); | |
185 | ||
186 | /* Set the divider */ | |
187 | regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val); | |
188 | val &= ~(NSS_COMMON_CLK_DIV_MASK | |
189 | << NSS_COMMON_CLK_DIV_OFFSET(gmac->id)); | |
190 | val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id); | |
191 | regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val); | |
192 | ||
193 | /* Enable the clock back */ | |
194 | regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); | |
195 | val |= clk_bits; | |
196 | regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
201 | static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac) | |
202 | { | |
203 | struct device *dev = &gmac->pdev->dev; | |
204 | ||
205 | gmac->phy_mode = of_get_phy_mode(dev->of_node); | |
206 | if (gmac->phy_mode < 0) { | |
207 | dev_err(dev, "missing phy mode property\n"); | |
208 | return ERR_PTR(-EINVAL); | |
209 | } | |
210 | ||
211 | if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) { | |
212 | dev_err(dev, "missing qcom id property\n"); | |
213 | return ERR_PTR(-EINVAL); | |
214 | } | |
215 | ||
216 | /* The GMACs are called 1 to 4 in the documentation, but to simplify the | |
217 | * code and keep it consistent with the Linux convention, we'll number | |
218 | * them from 0 to 3 here. | |
219 | */ | |
220 | if (gmac->id < 0 || gmac->id > 3) { | |
221 | dev_err(dev, "invalid gmac id\n"); | |
222 | return ERR_PTR(-EINVAL); | |
223 | } | |
224 | ||
225 | gmac->core_clk = devm_clk_get(dev, "stmmaceth"); | |
226 | if (IS_ERR(gmac->core_clk)) { | |
227 | dev_err(dev, "missing stmmaceth clk property\n"); | |
228 | return gmac->core_clk; | |
229 | } | |
230 | clk_set_rate(gmac->core_clk, 266000000); | |
231 | ||
232 | /* Setup the register map for the nss common registers */ | |
233 | gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node, | |
234 | "qcom,nss-common"); | |
235 | if (IS_ERR(gmac->nss_common)) { | |
236 | dev_err(dev, "missing nss-common node\n"); | |
237 | return gmac->nss_common; | |
238 | } | |
239 | ||
240 | /* Setup the register map for the qsgmii csr registers */ | |
241 | gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node, | |
242 | "qcom,qsgmii-csr"); | |
243 | if (IS_ERR(gmac->qsgmii_csr)) { | |
244 | dev_err(dev, "missing qsgmii-csr node\n"); | |
245 | return gmac->qsgmii_csr; | |
246 | } | |
247 | ||
248 | return NULL; | |
249 | } | |
250 | ||
251 | static void *ipq806x_gmac_setup(struct platform_device *pdev) | |
252 | { | |
253 | struct device *dev = &pdev->dev; | |
254 | struct ipq806x_gmac *gmac; | |
255 | int val; | |
256 | void *err; | |
257 | ||
258 | gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); | |
259 | if (!gmac) | |
260 | return ERR_PTR(-ENOMEM); | |
261 | ||
262 | gmac->pdev = pdev; | |
263 | ||
264 | err = ipq806x_gmac_of_parse(gmac); | |
265 | if (err) { | |
266 | dev_err(dev, "device tree parsing error\n"); | |
267 | return err; | |
268 | } | |
269 | ||
270 | regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL, | |
271 | QSGMII_PCS_CAL_LCKDT_CTL_RST); | |
272 | ||
273 | /* Inter frame gap is set to 12 */ | |
274 | val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET | | |
275 | 12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET; | |
276 | /* We also initiate an AXI low power exit request */ | |
277 | val |= NSS_COMMON_GMAC_CTL_CSYS_REQ; | |
278 | switch (gmac->phy_mode) { | |
279 | case PHY_INTERFACE_MODE_RGMII: | |
280 | val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; | |
281 | break; | |
282 | case PHY_INTERFACE_MODE_SGMII: | |
283 | val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL; | |
284 | break; | |
285 | default: | |
286 | dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", | |
287 | phy_modes(gmac->phy_mode)); | |
288 | return NULL; | |
289 | } | |
290 | regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val); | |
291 | ||
292 | /* Configure the clock src according to the mode */ | |
293 | regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val); | |
4f7eb70f | 294 | val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id)); |
b1c17215 MO |
295 | switch (gmac->phy_mode) { |
296 | case PHY_INTERFACE_MODE_RGMII: | |
297 | val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) << | |
298 | NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); | |
299 | break; | |
300 | case PHY_INTERFACE_MODE_SGMII: | |
301 | val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) << | |
302 | NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); | |
303 | break; | |
304 | default: | |
305 | dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n", | |
306 | phy_modes(gmac->phy_mode)); | |
307 | return NULL; | |
308 | } | |
309 | regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val); | |
310 | ||
311 | /* Enable PTP clock */ | |
312 | regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val); | |
313 | val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id); | |
314 | regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val); | |
315 | ||
316 | if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) { | |
317 | regmap_write(gmac->qsgmii_csr, QSGMII_PHY_SGMII_CTL(gmac->id), | |
318 | QSGMII_PHY_CDR_EN | | |
319 | QSGMII_PHY_RX_FRONT_EN | | |
320 | QSGMII_PHY_RX_SIGNAL_DETECT_EN | | |
321 | QSGMII_PHY_TX_DRIVER_EN | | |
322 | QSGMII_PHY_QSGMII_EN | | |
323 | 0x4 << QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET | | |
324 | 0x3 << QSGMII_PHY_RX_DC_BIAS_OFFSET | | |
325 | 0x1 << QSGMII_PHY_RX_INPUT_EQU_OFFSET | | |
326 | 0x2 << QSGMII_PHY_CDR_PI_SLEW_OFFSET | | |
327 | 0xC << QSGMII_PHY_TX_DRV_AMP_OFFSET); | |
328 | } | |
329 | ||
330 | return gmac; | |
331 | } | |
332 | ||
333 | static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed) | |
334 | { | |
335 | struct ipq806x_gmac *gmac = priv; | |
336 | ||
337 | ipq806x_gmac_set_speed(gmac, speed); | |
338 | } | |
339 | ||
340 | static const struct stmmac_of_data ipq806x_gmac_data = { | |
341 | .has_gmac = 1, | |
342 | .setup = ipq806x_gmac_setup, | |
343 | .fix_mac_speed = ipq806x_gmac_fix_mac_speed, | |
344 | }; | |
345 | ||
346 | static const struct of_device_id ipq806x_gmac_dwmac_match[] = { | |
347 | { .compatible = "qcom,ipq806x-gmac", .data = &ipq806x_gmac_data }, | |
348 | { } | |
349 | }; | |
350 | MODULE_DEVICE_TABLE(of, ipq806x_gmac_dwmac_match); | |
351 | ||
352 | static struct platform_driver ipq806x_gmac_dwmac_driver = { | |
353 | .probe = stmmac_pltfr_probe, | |
354 | .remove = stmmac_pltfr_remove, | |
355 | .driver = { | |
356 | .name = "ipq806x-gmac-dwmac", | |
357 | .pm = &stmmac_pltfr_pm_ops, | |
358 | .of_match_table = ipq806x_gmac_dwmac_match, | |
359 | }, | |
360 | }; | |
361 | module_platform_driver(ipq806x_gmac_dwmac_driver); | |
362 | ||
363 | MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>"); | |
364 | MODULE_DESCRIPTION("Qualcomm Atheros IPQ806x DWMAC specific glue layer"); | |
365 | MODULE_LICENSE("Dual BSD/GPL"); |