GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / net / ethernet / stmicro / stmmac / hwif.c
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /*
3  * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
4  * stmmac HW Interface Handling
5  */
6
7 #include "common.h"
8 #include "stmmac.h"
9 #include "stmmac_ptp.h"
10
11 static u32 stmmac_get_id(struct stmmac_priv *priv, u32 id_reg)
12 {
13         u32 reg = readl(priv->ioaddr + id_reg);
14
15         if (!reg) {
16                 dev_info(priv->device, "Version ID not available\n");
17                 return 0x0;
18         }
19
20         dev_info(priv->device, "User ID: 0x%x, Synopsys ID: 0x%x\n",
21                         (unsigned int)(reg & GENMASK(15, 8)) >> 8,
22                         (unsigned int)(reg & GENMASK(7, 0)));
23         return reg & GENMASK(7, 0);
24 }
25
26 static void stmmac_dwmac_mode_quirk(struct stmmac_priv *priv)
27 {
28         struct mac_device_info *mac = priv->hw;
29
30         if (priv->chain_mode) {
31                 dev_info(priv->device, "Chain mode enabled\n");
32                 priv->mode = STMMAC_CHAIN_MODE;
33                 mac->mode = &chain_mode_ops;
34         } else {
35                 dev_info(priv->device, "Ring mode enabled\n");
36                 priv->mode = STMMAC_RING_MODE;
37                 mac->mode = &ring_mode_ops;
38         }
39 }
40
41 static int stmmac_dwmac1_quirks(struct stmmac_priv *priv)
42 {
43         struct mac_device_info *mac = priv->hw;
44
45         if (priv->plat->enh_desc) {
46                 dev_info(priv->device, "Enhanced/Alternate descriptors\n");
47
48                 /* GMAC older than 3.50 has no extended descriptors */
49                 if (priv->synopsys_id >= DWMAC_CORE_3_50) {
50                         dev_info(priv->device, "Enabled extended descriptors\n");
51                         priv->extend_desc = 1;
52                 } else {
53                         dev_warn(priv->device, "Extended descriptors not supported\n");
54                 }
55
56                 mac->desc = &enh_desc_ops;
57         } else {
58                 dev_info(priv->device, "Normal descriptors\n");
59                 mac->desc = &ndesc_ops;
60         }
61
62         stmmac_dwmac_mode_quirk(priv);
63         return 0;
64 }
65
66 static int stmmac_dwmac4_quirks(struct stmmac_priv *priv)
67 {
68         stmmac_dwmac_mode_quirk(priv);
69         return 0;
70 }
71
72 static const struct stmmac_hwif_entry {
73         bool gmac;
74         bool gmac4;
75         bool xgmac;
76         u32 min_id;
77         const struct stmmac_regs_off regs;
78         const void *desc;
79         const void *dma;
80         const void *mac;
81         const void *hwtimestamp;
82         const void *mode;
83         const void *tc;
84         int (*setup)(struct stmmac_priv *priv);
85         int (*quirks)(struct stmmac_priv *priv);
86 } stmmac_hw[] = {
87         /* NOTE: New HW versions shall go to the end of this table */
88         {
89                 .gmac = false,
90                 .gmac4 = false,
91                 .xgmac = false,
92                 .min_id = 0,
93                 .regs = {
94                         .ptp_off = PTP_GMAC3_X_OFFSET,
95                         .mmc_off = MMC_GMAC3_X_OFFSET,
96                 },
97                 .desc = NULL,
98                 .dma = &dwmac100_dma_ops,
99                 .mac = &dwmac100_ops,
100                 .hwtimestamp = &stmmac_ptp,
101                 .mode = NULL,
102                 .tc = NULL,
103                 .setup = dwmac100_setup,
104                 .quirks = stmmac_dwmac1_quirks,
105         }, {
106                 .gmac = true,
107                 .gmac4 = false,
108                 .xgmac = false,
109                 .min_id = 0,
110                 .regs = {
111                         .ptp_off = PTP_GMAC3_X_OFFSET,
112                         .mmc_off = MMC_GMAC3_X_OFFSET,
113                 },
114                 .desc = NULL,
115                 .dma = &dwmac1000_dma_ops,
116                 .mac = &dwmac1000_ops,
117                 .hwtimestamp = &stmmac_ptp,
118                 .mode = NULL,
119                 .tc = NULL,
120                 .setup = dwmac1000_setup,
121                 .quirks = stmmac_dwmac1_quirks,
122         }, {
123                 .gmac = false,
124                 .gmac4 = true,
125                 .xgmac = false,
126                 .min_id = 0,
127                 .regs = {
128                         .ptp_off = PTP_GMAC4_OFFSET,
129                         .mmc_off = MMC_GMAC4_OFFSET,
130                 },
131                 .desc = &dwmac4_desc_ops,
132                 .dma = &dwmac4_dma_ops,
133                 .mac = &dwmac4_ops,
134                 .hwtimestamp = &stmmac_ptp,
135                 .mode = NULL,
136                 .tc = NULL,
137                 .setup = dwmac4_setup,
138                 .quirks = stmmac_dwmac4_quirks,
139         }, {
140                 .gmac = false,
141                 .gmac4 = true,
142                 .xgmac = false,
143                 .min_id = DWMAC_CORE_4_00,
144                 .regs = {
145                         .ptp_off = PTP_GMAC4_OFFSET,
146                         .mmc_off = MMC_GMAC4_OFFSET,
147                 },
148                 .desc = &dwmac4_desc_ops,
149                 .dma = &dwmac4_dma_ops,
150                 .mac = &dwmac410_ops,
151                 .hwtimestamp = &stmmac_ptp,
152                 .mode = &dwmac4_ring_mode_ops,
153                 .tc = NULL,
154                 .setup = dwmac4_setup,
155                 .quirks = NULL,
156         }, {
157                 .gmac = false,
158                 .gmac4 = true,
159                 .xgmac = false,
160                 .min_id = DWMAC_CORE_4_10,
161                 .regs = {
162                         .ptp_off = PTP_GMAC4_OFFSET,
163                         .mmc_off = MMC_GMAC4_OFFSET,
164                 },
165                 .desc = &dwmac4_desc_ops,
166                 .dma = &dwmac410_dma_ops,
167                 .mac = &dwmac410_ops,
168                 .hwtimestamp = &stmmac_ptp,
169                 .mode = &dwmac4_ring_mode_ops,
170                 .tc = NULL,
171                 .setup = dwmac4_setup,
172                 .quirks = NULL,
173         }, {
174                 .gmac = false,
175                 .gmac4 = true,
176                 .xgmac = false,
177                 .min_id = DWMAC_CORE_5_10,
178                 .regs = {
179                         .ptp_off = PTP_GMAC4_OFFSET,
180                         .mmc_off = MMC_GMAC4_OFFSET,
181                 },
182                 .desc = &dwmac4_desc_ops,
183                 .dma = &dwmac410_dma_ops,
184                 .mac = &dwmac510_ops,
185                 .hwtimestamp = &stmmac_ptp,
186                 .mode = &dwmac4_ring_mode_ops,
187                 .tc = &dwmac510_tc_ops,
188                 .setup = dwmac4_setup,
189                 .quirks = NULL,
190         }, {
191                 .gmac = false,
192                 .gmac4 = false,
193                 .xgmac = true,
194                 .min_id = DWXGMAC_CORE_2_10,
195                 .regs = {
196                         .ptp_off = PTP_XGMAC_OFFSET,
197                         .mmc_off = 0,
198                 },
199                 .desc = &dwxgmac210_desc_ops,
200                 .dma = &dwxgmac210_dma_ops,
201                 .mac = &dwxgmac210_ops,
202                 .hwtimestamp = &stmmac_ptp,
203                 .mode = NULL,
204                 .tc = NULL,
205                 .setup = dwxgmac2_setup,
206                 .quirks = NULL,
207         },
208 };
209
210 int stmmac_hwif_init(struct stmmac_priv *priv)
211 {
212         bool needs_xgmac = priv->plat->has_xgmac;
213         bool needs_gmac4 = priv->plat->has_gmac4;
214         bool needs_gmac = priv->plat->has_gmac;
215         const struct stmmac_hwif_entry *entry;
216         struct mac_device_info *mac;
217         bool needs_setup = true;
218         int i, ret;
219         u32 id;
220
221         if (needs_gmac) {
222                 id = stmmac_get_id(priv, GMAC_VERSION);
223         } else if (needs_gmac4 || needs_xgmac) {
224                 id = stmmac_get_id(priv, GMAC4_VERSION);
225         } else {
226                 id = 0;
227         }
228
229         /* Save ID for later use */
230         priv->synopsys_id = id;
231
232         /* Lets assume some safe values first */
233         priv->ptpaddr = priv->ioaddr +
234                 (needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET);
235         priv->mmcaddr = priv->ioaddr +
236                 (needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET);
237
238         /* Check for HW specific setup first */
239         if (priv->plat->setup) {
240                 mac = priv->plat->setup(priv);
241                 needs_setup = false;
242         } else {
243                 mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
244         }
245
246         if (!mac)
247                 return -ENOMEM;
248
249         /* Fallback to generic HW */
250         for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) {
251                 entry = &stmmac_hw[i];
252
253                 if (needs_gmac ^ entry->gmac)
254                         continue;
255                 if (needs_gmac4 ^ entry->gmac4)
256                         continue;
257                 if (needs_xgmac ^ entry->xgmac)
258                         continue;
259                 /* Use synopsys_id var because some setups can override this */
260                 if (priv->synopsys_id < entry->min_id)
261                         continue;
262
263                 /* Only use generic HW helpers if needed */
264                 mac->desc = mac->desc ? : entry->desc;
265                 mac->dma = mac->dma ? : entry->dma;
266                 mac->mac = mac->mac ? : entry->mac;
267                 mac->ptp = mac->ptp ? : entry->hwtimestamp;
268                 mac->mode = mac->mode ? : entry->mode;
269                 mac->tc = mac->tc ? : entry->tc;
270
271                 priv->hw = mac;
272                 priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
273                 priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off;
274
275                 /* Entry found */
276                 if (needs_setup) {
277                         ret = entry->setup(priv);
278                         if (ret)
279                                 return ret;
280                 }
281
282                 /* Save quirks, if needed for posterior use */
283                 priv->hwif_quirks = entry->quirks;
284                 return 0;
285         }
286
287         dev_err(priv->device, "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n",
288                         id, needs_gmac, needs_gmac4);
289         return -EINVAL;
290 }