dt-bindings: nfc: nxp,nci: Document NQ310 compatible
[linux-2.6-microblaze.git] / drivers / net / dsa / hirschmann / hellcreek.c
1 // SPDX-License-Identifier: (GPL-2.0 or MIT)
2 /*
3  * DSA driver for:
4  * Hirschmann Hellcreek TSN switch.
5  *
6  * Copyright (C) 2019-2021 Linutronix GmbH
7  * Author Kurt Kanzenbach <kurt@linutronix.de>
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/device.h>
13 #include <linux/of.h>
14 #include <linux/of_device.h>
15 #include <linux/of_mdio.h>
16 #include <linux/platform_device.h>
17 #include <linux/bitops.h>
18 #include <linux/if_bridge.h>
19 #include <linux/if_vlan.h>
20 #include <linux/etherdevice.h>
21 #include <linux/random.h>
22 #include <linux/iopoll.h>
23 #include <linux/mutex.h>
24 #include <linux/delay.h>
25 #include <net/dsa.h>
26
27 #include "hellcreek.h"
28 #include "hellcreek_ptp.h"
29 #include "hellcreek_hwtstamp.h"
30
31 static const struct hellcreek_counter hellcreek_counter[] = {
32         { 0x00, "RxFiltered", },
33         { 0x01, "RxOctets1k", },
34         { 0x02, "RxVTAG", },
35         { 0x03, "RxL2BAD", },
36         { 0x04, "RxOverloadDrop", },
37         { 0x05, "RxUC", },
38         { 0x06, "RxMC", },
39         { 0x07, "RxBC", },
40         { 0x08, "RxRS<64", },
41         { 0x09, "RxRS64", },
42         { 0x0a, "RxRS65_127", },
43         { 0x0b, "RxRS128_255", },
44         { 0x0c, "RxRS256_511", },
45         { 0x0d, "RxRS512_1023", },
46         { 0x0e, "RxRS1024_1518", },
47         { 0x0f, "RxRS>1518", },
48         { 0x10, "TxTailDropQueue0", },
49         { 0x11, "TxTailDropQueue1", },
50         { 0x12, "TxTailDropQueue2", },
51         { 0x13, "TxTailDropQueue3", },
52         { 0x14, "TxTailDropQueue4", },
53         { 0x15, "TxTailDropQueue5", },
54         { 0x16, "TxTailDropQueue6", },
55         { 0x17, "TxTailDropQueue7", },
56         { 0x18, "RxTrafficClass0", },
57         { 0x19, "RxTrafficClass1", },
58         { 0x1a, "RxTrafficClass2", },
59         { 0x1b, "RxTrafficClass3", },
60         { 0x1c, "RxTrafficClass4", },
61         { 0x1d, "RxTrafficClass5", },
62         { 0x1e, "RxTrafficClass6", },
63         { 0x1f, "RxTrafficClass7", },
64         { 0x21, "TxOctets1k", },
65         { 0x22, "TxVTAG", },
66         { 0x23, "TxL2BAD", },
67         { 0x25, "TxUC", },
68         { 0x26, "TxMC", },
69         { 0x27, "TxBC", },
70         { 0x28, "TxTS<64", },
71         { 0x29, "TxTS64", },
72         { 0x2a, "TxTS65_127", },
73         { 0x2b, "TxTS128_255", },
74         { 0x2c, "TxTS256_511", },
75         { 0x2d, "TxTS512_1023", },
76         { 0x2e, "TxTS1024_1518", },
77         { 0x2f, "TxTS>1518", },
78         { 0x30, "TxTrafficClassOverrun0", },
79         { 0x31, "TxTrafficClassOverrun1", },
80         { 0x32, "TxTrafficClassOverrun2", },
81         { 0x33, "TxTrafficClassOverrun3", },
82         { 0x34, "TxTrafficClassOverrun4", },
83         { 0x35, "TxTrafficClassOverrun5", },
84         { 0x36, "TxTrafficClassOverrun6", },
85         { 0x37, "TxTrafficClassOverrun7", },
86         { 0x38, "TxTrafficClass0", },
87         { 0x39, "TxTrafficClass1", },
88         { 0x3a, "TxTrafficClass2", },
89         { 0x3b, "TxTrafficClass3", },
90         { 0x3c, "TxTrafficClass4", },
91         { 0x3d, "TxTrafficClass5", },
92         { 0x3e, "TxTrafficClass6", },
93         { 0x3f, "TxTrafficClass7", },
94 };
95
96 static u16 hellcreek_read(struct hellcreek *hellcreek, unsigned int offset)
97 {
98         return readw(hellcreek->base + offset);
99 }
100
101 static u16 hellcreek_read_ctrl(struct hellcreek *hellcreek)
102 {
103         return readw(hellcreek->base + HR_CTRL_C);
104 }
105
106 static u16 hellcreek_read_stat(struct hellcreek *hellcreek)
107 {
108         return readw(hellcreek->base + HR_SWSTAT);
109 }
110
111 static void hellcreek_write(struct hellcreek *hellcreek, u16 data,
112                             unsigned int offset)
113 {
114         writew(data, hellcreek->base + offset);
115 }
116
117 static void hellcreek_select_port(struct hellcreek *hellcreek, int port)
118 {
119         u16 val = port << HR_PSEL_PTWSEL_SHIFT;
120
121         hellcreek_write(hellcreek, val, HR_PSEL);
122 }
123
124 static void hellcreek_select_prio(struct hellcreek *hellcreek, int prio)
125 {
126         u16 val = prio << HR_PSEL_PRTCWSEL_SHIFT;
127
128         hellcreek_write(hellcreek, val, HR_PSEL);
129 }
130
131 static void hellcreek_select_port_prio(struct hellcreek *hellcreek, int port,
132                                        int prio)
133 {
134         u16 val = port << HR_PSEL_PTWSEL_SHIFT;
135
136         val |= prio << HR_PSEL_PRTCWSEL_SHIFT;
137
138         hellcreek_write(hellcreek, val, HR_PSEL);
139 }
140
141 static void hellcreek_select_counter(struct hellcreek *hellcreek, int counter)
142 {
143         u16 val = counter << HR_CSEL_SHIFT;
144
145         hellcreek_write(hellcreek, val, HR_CSEL);
146
147         /* Data sheet states to wait at least 20 internal clock cycles */
148         ndelay(200);
149 }
150
151 static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
152                                   bool pvid)
153 {
154         u16 val = 0;
155
156         /* Set pvid bit first */
157         if (pvid)
158                 val |= HR_VIDCFG_PVID;
159         hellcreek_write(hellcreek, val, HR_VIDCFG);
160
161         /* Set vlan */
162         val |= vid << HR_VIDCFG_VID_SHIFT;
163         hellcreek_write(hellcreek, val, HR_VIDCFG);
164 }
165
166 static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port)
167 {
168         u16 val = port << TR_TGDSEL_TDGSEL_SHIFT;
169
170         hellcreek_write(hellcreek, val, TR_TGDSEL);
171 }
172
173 static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
174 {
175         u16 val;
176
177         /* Wait up to 1ms, although 3 us should be enough */
178         return readx_poll_timeout(hellcreek_read_ctrl, hellcreek,
179                                   val, val & HR_CTRL_C_READY,
180                                   3, 1000);
181 }
182
183 static int hellcreek_wait_until_transitioned(struct hellcreek *hellcreek)
184 {
185         u16 val;
186
187         return readx_poll_timeout_atomic(hellcreek_read_ctrl, hellcreek,
188                                          val, !(val & HR_CTRL_C_TRANSITION),
189                                          1, 1000);
190 }
191
192 static int hellcreek_wait_fdb_ready(struct hellcreek *hellcreek)
193 {
194         u16 val;
195
196         return readx_poll_timeout_atomic(hellcreek_read_stat, hellcreek,
197                                          val, !(val & HR_SWSTAT_BUSY),
198                                          1, 1000);
199 }
200
201 static int hellcreek_detect(struct hellcreek *hellcreek)
202 {
203         u16 id, rel_low, rel_high, date_low, date_high, tgd_ver;
204         u8 tgd_maj, tgd_min;
205         u32 rel, date;
206
207         id        = hellcreek_read(hellcreek, HR_MODID_C);
208         rel_low   = hellcreek_read(hellcreek, HR_REL_L_C);
209         rel_high  = hellcreek_read(hellcreek, HR_REL_H_C);
210         date_low  = hellcreek_read(hellcreek, HR_BLD_L_C);
211         date_high = hellcreek_read(hellcreek, HR_BLD_H_C);
212         tgd_ver   = hellcreek_read(hellcreek, TR_TGDVER);
213
214         if (id != hellcreek->pdata->module_id)
215                 return -ENODEV;
216
217         rel     = rel_low | (rel_high << 16);
218         date    = date_low | (date_high << 16);
219         tgd_maj = (tgd_ver & TR_TGDVER_REV_MAJ_MASK) >> TR_TGDVER_REV_MAJ_SHIFT;
220         tgd_min = (tgd_ver & TR_TGDVER_REV_MIN_MASK) >> TR_TGDVER_REV_MIN_SHIFT;
221
222         dev_info(hellcreek->dev, "Module ID=%02x Release=%04x Date=%04x TGD Version=%02x.%02x\n",
223                  id, rel, date, tgd_maj, tgd_min);
224
225         return 0;
226 }
227
228 static void hellcreek_feature_detect(struct hellcreek *hellcreek)
229 {
230         u16 features;
231
232         features = hellcreek_read(hellcreek, HR_FEABITS0);
233
234         /* Only detect the size of the FDB table. The size and current
235          * utilization can be queried via devlink.
236          */
237         hellcreek->fdb_entries = ((features & HR_FEABITS0_FDBBINS_MASK) >>
238                                HR_FEABITS0_FDBBINS_SHIFT) * 32;
239 }
240
241 static enum dsa_tag_protocol hellcreek_get_tag_protocol(struct dsa_switch *ds,
242                                                         int port,
243                                                         enum dsa_tag_protocol mp)
244 {
245         return DSA_TAG_PROTO_HELLCREEK;
246 }
247
248 static int hellcreek_port_enable(struct dsa_switch *ds, int port,
249                                  struct phy_device *phy)
250 {
251         struct hellcreek *hellcreek = ds->priv;
252         struct hellcreek_port *hellcreek_port;
253         u16 val;
254
255         hellcreek_port = &hellcreek->ports[port];
256
257         dev_dbg(hellcreek->dev, "Enable port %d\n", port);
258
259         mutex_lock(&hellcreek->reg_lock);
260
261         hellcreek_select_port(hellcreek, port);
262         val = hellcreek_port->ptcfg;
263         val |= HR_PTCFG_ADMIN_EN;
264         hellcreek_write(hellcreek, val, HR_PTCFG);
265         hellcreek_port->ptcfg = val;
266
267         mutex_unlock(&hellcreek->reg_lock);
268
269         return 0;
270 }
271
272 static void hellcreek_port_disable(struct dsa_switch *ds, int port)
273 {
274         struct hellcreek *hellcreek = ds->priv;
275         struct hellcreek_port *hellcreek_port;
276         u16 val;
277
278         hellcreek_port = &hellcreek->ports[port];
279
280         dev_dbg(hellcreek->dev, "Disable port %d\n", port);
281
282         mutex_lock(&hellcreek->reg_lock);
283
284         hellcreek_select_port(hellcreek, port);
285         val = hellcreek_port->ptcfg;
286         val &= ~HR_PTCFG_ADMIN_EN;
287         hellcreek_write(hellcreek, val, HR_PTCFG);
288         hellcreek_port->ptcfg = val;
289
290         mutex_unlock(&hellcreek->reg_lock);
291 }
292
293 static void hellcreek_get_strings(struct dsa_switch *ds, int port,
294                                   u32 stringset, uint8_t *data)
295 {
296         int i;
297
298         for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) {
299                 const struct hellcreek_counter *counter = &hellcreek_counter[i];
300
301                 strscpy(data + i * ETH_GSTRING_LEN,
302                         counter->name, ETH_GSTRING_LEN);
303         }
304 }
305
306 static int hellcreek_get_sset_count(struct dsa_switch *ds, int port, int sset)
307 {
308         if (sset != ETH_SS_STATS)
309                 return 0;
310
311         return ARRAY_SIZE(hellcreek_counter);
312 }
313
314 static void hellcreek_get_ethtool_stats(struct dsa_switch *ds, int port,
315                                         uint64_t *data)
316 {
317         struct hellcreek *hellcreek = ds->priv;
318         struct hellcreek_port *hellcreek_port;
319         int i;
320
321         hellcreek_port = &hellcreek->ports[port];
322
323         for (i = 0; i < ARRAY_SIZE(hellcreek_counter); ++i) {
324                 const struct hellcreek_counter *counter = &hellcreek_counter[i];
325                 u8 offset = counter->offset + port * 64;
326                 u16 high, low;
327                 u64 value;
328
329                 mutex_lock(&hellcreek->reg_lock);
330
331                 hellcreek_select_counter(hellcreek, offset);
332
333                 /* The registers are locked internally by selecting the
334                  * counter. So low and high can be read without reading high
335                  * again.
336                  */
337                 high  = hellcreek_read(hellcreek, HR_CRDH);
338                 low   = hellcreek_read(hellcreek, HR_CRDL);
339                 value = ((u64)high << 16) | low;
340
341                 hellcreek_port->counter_values[i] += value;
342                 data[i] = hellcreek_port->counter_values[i];
343
344                 mutex_unlock(&hellcreek->reg_lock);
345         }
346 }
347
348 static u16 hellcreek_private_vid(int port)
349 {
350         return VLAN_N_VID - port + 1;
351 }
352
353 static int hellcreek_vlan_prepare(struct dsa_switch *ds, int port,
354                                   const struct switchdev_obj_port_vlan *vlan,
355                                   struct netlink_ext_ack *extack)
356 {
357         struct hellcreek *hellcreek = ds->priv;
358         int i;
359
360         dev_dbg(hellcreek->dev, "VLAN prepare for port %d\n", port);
361
362         /* Restriction: Make sure that nobody uses the "private" VLANs. These
363          * VLANs are internally used by the driver to ensure port
364          * separation. Thus, they cannot be used by someone else.
365          */
366         for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
367                 const u16 restricted_vid = hellcreek_private_vid(i);
368
369                 if (!dsa_is_user_port(ds, i))
370                         continue;
371
372                 if (vlan->vid == restricted_vid) {
373                         NL_SET_ERR_MSG_MOD(extack, "VID restricted by driver");
374                         return -EBUSY;
375                 }
376         }
377
378         return 0;
379 }
380
381 static void hellcreek_select_vlan_params(struct hellcreek *hellcreek, int port,
382                                          int *shift, int *mask)
383 {
384         switch (port) {
385         case 0:
386                 *shift = HR_VIDMBRCFG_P0MBR_SHIFT;
387                 *mask  = HR_VIDMBRCFG_P0MBR_MASK;
388                 break;
389         case 1:
390                 *shift = HR_VIDMBRCFG_P1MBR_SHIFT;
391                 *mask  = HR_VIDMBRCFG_P1MBR_MASK;
392                 break;
393         case 2:
394                 *shift = HR_VIDMBRCFG_P2MBR_SHIFT;
395                 *mask  = HR_VIDMBRCFG_P2MBR_MASK;
396                 break;
397         case 3:
398                 *shift = HR_VIDMBRCFG_P3MBR_SHIFT;
399                 *mask  = HR_VIDMBRCFG_P3MBR_MASK;
400                 break;
401         default:
402                 *shift = *mask = 0;
403                 dev_err(hellcreek->dev, "Unknown port %d selected!\n", port);
404         }
405 }
406
407 static void hellcreek_apply_vlan(struct hellcreek *hellcreek, int port, u16 vid,
408                                  bool pvid, bool untagged)
409 {
410         int shift, mask;
411         u16 val;
412
413         dev_dbg(hellcreek->dev, "Apply VLAN: port=%d vid=%u pvid=%d untagged=%d",
414                 port, vid, pvid, untagged);
415
416         mutex_lock(&hellcreek->reg_lock);
417
418         hellcreek_select_port(hellcreek, port);
419         hellcreek_select_vlan(hellcreek, vid, pvid);
420
421         /* Setup port vlan membership */
422         hellcreek_select_vlan_params(hellcreek, port, &shift, &mask);
423         val = hellcreek->vidmbrcfg[vid];
424         val &= ~mask;
425         if (untagged)
426                 val |= HELLCREEK_VLAN_UNTAGGED_MEMBER << shift;
427         else
428                 val |= HELLCREEK_VLAN_TAGGED_MEMBER << shift;
429
430         hellcreek_write(hellcreek, val, HR_VIDMBRCFG);
431         hellcreek->vidmbrcfg[vid] = val;
432
433         mutex_unlock(&hellcreek->reg_lock);
434 }
435
436 static void hellcreek_unapply_vlan(struct hellcreek *hellcreek, int port,
437                                    u16 vid)
438 {
439         int shift, mask;
440         u16 val;
441
442         dev_dbg(hellcreek->dev, "Unapply VLAN: port=%d vid=%u\n", port, vid);
443
444         mutex_lock(&hellcreek->reg_lock);
445
446         hellcreek_select_vlan(hellcreek, vid, false);
447
448         /* Setup port vlan membership */
449         hellcreek_select_vlan_params(hellcreek, port, &shift, &mask);
450         val = hellcreek->vidmbrcfg[vid];
451         val &= ~mask;
452         val |= HELLCREEK_VLAN_NO_MEMBER << shift;
453
454         hellcreek_write(hellcreek, val, HR_VIDMBRCFG);
455         hellcreek->vidmbrcfg[vid] = val;
456
457         mutex_unlock(&hellcreek->reg_lock);
458 }
459
460 static int hellcreek_vlan_add(struct dsa_switch *ds, int port,
461                               const struct switchdev_obj_port_vlan *vlan,
462                               struct netlink_ext_ack *extack)
463 {
464         bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
465         bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
466         struct hellcreek *hellcreek = ds->priv;
467         int err;
468
469         err = hellcreek_vlan_prepare(ds, port, vlan, extack);
470         if (err)
471                 return err;
472
473         dev_dbg(hellcreek->dev, "Add VLAN %d on port %d, %s, %s\n",
474                 vlan->vid, port, untagged ? "untagged" : "tagged",
475                 pvid ? "PVID" : "no PVID");
476
477         hellcreek_apply_vlan(hellcreek, port, vlan->vid, pvid, untagged);
478
479         return 0;
480 }
481
482 static int hellcreek_vlan_del(struct dsa_switch *ds, int port,
483                               const struct switchdev_obj_port_vlan *vlan)
484 {
485         struct hellcreek *hellcreek = ds->priv;
486
487         dev_dbg(hellcreek->dev, "Remove VLAN %d on port %d\n", vlan->vid, port);
488
489         hellcreek_unapply_vlan(hellcreek, port, vlan->vid);
490
491         return 0;
492 }
493
494 static void hellcreek_port_stp_state_set(struct dsa_switch *ds, int port,
495                                          u8 state)
496 {
497         struct hellcreek *hellcreek = ds->priv;
498         struct hellcreek_port *hellcreek_port;
499         const char *new_state;
500         u16 val;
501
502         mutex_lock(&hellcreek->reg_lock);
503
504         hellcreek_port = &hellcreek->ports[port];
505         val = hellcreek_port->ptcfg;
506
507         switch (state) {
508         case BR_STATE_DISABLED:
509                 new_state = "DISABLED";
510                 val |= HR_PTCFG_BLOCKED;
511                 val &= ~HR_PTCFG_LEARNING_EN;
512                 break;
513         case BR_STATE_BLOCKING:
514                 new_state = "BLOCKING";
515                 val |= HR_PTCFG_BLOCKED;
516                 val &= ~HR_PTCFG_LEARNING_EN;
517                 break;
518         case BR_STATE_LISTENING:
519                 new_state = "LISTENING";
520                 val |= HR_PTCFG_BLOCKED;
521                 val &= ~HR_PTCFG_LEARNING_EN;
522                 break;
523         case BR_STATE_LEARNING:
524                 new_state = "LEARNING";
525                 val |= HR_PTCFG_BLOCKED;
526                 val |= HR_PTCFG_LEARNING_EN;
527                 break;
528         case BR_STATE_FORWARDING:
529                 new_state = "FORWARDING";
530                 val &= ~HR_PTCFG_BLOCKED;
531                 val |= HR_PTCFG_LEARNING_EN;
532                 break;
533         default:
534                 new_state = "UNKNOWN";
535         }
536
537         hellcreek_select_port(hellcreek, port);
538         hellcreek_write(hellcreek, val, HR_PTCFG);
539         hellcreek_port->ptcfg = val;
540
541         mutex_unlock(&hellcreek->reg_lock);
542
543         dev_dbg(hellcreek->dev, "Configured STP state for port %d: %s\n",
544                 port, new_state);
545 }
546
547 static void hellcreek_setup_ingressflt(struct hellcreek *hellcreek, int port,
548                                        bool enable)
549 {
550         struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
551         u16 ptcfg;
552
553         mutex_lock(&hellcreek->reg_lock);
554
555         ptcfg = hellcreek_port->ptcfg;
556
557         if (enable)
558                 ptcfg |= HR_PTCFG_INGRESSFLT;
559         else
560                 ptcfg &= ~HR_PTCFG_INGRESSFLT;
561
562         hellcreek_select_port(hellcreek, port);
563         hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
564         hellcreek_port->ptcfg = ptcfg;
565
566         mutex_unlock(&hellcreek->reg_lock);
567 }
568
569 static void hellcreek_setup_vlan_awareness(struct hellcreek *hellcreek,
570                                            bool enable)
571 {
572         u16 swcfg;
573
574         mutex_lock(&hellcreek->reg_lock);
575
576         swcfg = hellcreek->swcfg;
577
578         if (enable)
579                 swcfg |= HR_SWCFG_VLAN_UNAWARE;
580         else
581                 swcfg &= ~HR_SWCFG_VLAN_UNAWARE;
582
583         hellcreek_write(hellcreek, swcfg, HR_SWCFG);
584
585         mutex_unlock(&hellcreek->reg_lock);
586 }
587
588 /* Default setup for DSA: VLAN <X>: CPU and Port <X> egress untagged. */
589 static void hellcreek_setup_vlan_membership(struct dsa_switch *ds, int port,
590                                             bool enabled)
591 {
592         const u16 vid = hellcreek_private_vid(port);
593         int upstream = dsa_upstream_port(ds, port);
594         struct hellcreek *hellcreek = ds->priv;
595
596         /* Apply vid to port as egress untagged and port vlan id */
597         if (enabled)
598                 hellcreek_apply_vlan(hellcreek, port, vid, true, true);
599         else
600                 hellcreek_unapply_vlan(hellcreek, port, vid);
601
602         /* Apply vid to cpu port as well */
603         if (enabled)
604                 hellcreek_apply_vlan(hellcreek, upstream, vid, false, true);
605         else
606                 hellcreek_unapply_vlan(hellcreek, upstream, vid);
607 }
608
609 static void hellcreek_port_set_ucast_flood(struct hellcreek *hellcreek,
610                                            int port, bool enable)
611 {
612         struct hellcreek_port *hellcreek_port;
613         u16 val;
614
615         hellcreek_port = &hellcreek->ports[port];
616
617         dev_dbg(hellcreek->dev, "%s unicast flooding on port %d\n",
618                 enable ? "Enable" : "Disable", port);
619
620         mutex_lock(&hellcreek->reg_lock);
621
622         hellcreek_select_port(hellcreek, port);
623         val = hellcreek_port->ptcfg;
624         if (enable)
625                 val &= ~HR_PTCFG_UUC_FLT;
626         else
627                 val |= HR_PTCFG_UUC_FLT;
628         hellcreek_write(hellcreek, val, HR_PTCFG);
629         hellcreek_port->ptcfg = val;
630
631         mutex_unlock(&hellcreek->reg_lock);
632 }
633
634 static void hellcreek_port_set_mcast_flood(struct hellcreek *hellcreek,
635                                            int port, bool enable)
636 {
637         struct hellcreek_port *hellcreek_port;
638         u16 val;
639
640         hellcreek_port = &hellcreek->ports[port];
641
642         dev_dbg(hellcreek->dev, "%s multicast flooding on port %d\n",
643                 enable ? "Enable" : "Disable", port);
644
645         mutex_lock(&hellcreek->reg_lock);
646
647         hellcreek_select_port(hellcreek, port);
648         val = hellcreek_port->ptcfg;
649         if (enable)
650                 val &= ~HR_PTCFG_UMC_FLT;
651         else
652                 val |= HR_PTCFG_UMC_FLT;
653         hellcreek_write(hellcreek, val, HR_PTCFG);
654         hellcreek_port->ptcfg = val;
655
656         mutex_unlock(&hellcreek->reg_lock);
657 }
658
659 static int hellcreek_pre_bridge_flags(struct dsa_switch *ds, int port,
660                                       struct switchdev_brport_flags flags,
661                                       struct netlink_ext_ack *extack)
662 {
663         if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
664                 return -EINVAL;
665
666         return 0;
667 }
668
669 static int hellcreek_bridge_flags(struct dsa_switch *ds, int port,
670                                   struct switchdev_brport_flags flags,
671                                   struct netlink_ext_ack *extack)
672 {
673         struct hellcreek *hellcreek = ds->priv;
674
675         if (flags.mask & BR_FLOOD)
676                 hellcreek_port_set_ucast_flood(hellcreek, port,
677                                                !!(flags.val & BR_FLOOD));
678
679         if (flags.mask & BR_MCAST_FLOOD)
680                 hellcreek_port_set_mcast_flood(hellcreek, port,
681                                                !!(flags.val & BR_MCAST_FLOOD));
682
683         return 0;
684 }
685
686 static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
687                                       struct dsa_bridge bridge,
688                                       bool *tx_fwd_offload,
689                                       struct netlink_ext_ack *extack)
690 {
691         struct hellcreek *hellcreek = ds->priv;
692
693         dev_dbg(hellcreek->dev, "Port %d joins a bridge\n", port);
694
695         /* When joining a vlan_filtering bridge, keep the switch VLAN aware */
696         if (!ds->vlan_filtering)
697                 hellcreek_setup_vlan_awareness(hellcreek, false);
698
699         /* Drop private vlans */
700         hellcreek_setup_vlan_membership(ds, port, false);
701
702         return 0;
703 }
704
705 static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port,
706                                         struct dsa_bridge bridge)
707 {
708         struct hellcreek *hellcreek = ds->priv;
709
710         dev_dbg(hellcreek->dev, "Port %d leaves a bridge\n", port);
711
712         /* Enable VLAN awareness */
713         hellcreek_setup_vlan_awareness(hellcreek, true);
714
715         /* Enable private vlans */
716         hellcreek_setup_vlan_membership(ds, port, true);
717 }
718
719 static int __hellcreek_fdb_add(struct hellcreek *hellcreek,
720                                const struct hellcreek_fdb_entry *entry)
721 {
722         u16 meta = 0;
723
724         dev_dbg(hellcreek->dev, "Add static FDB entry: MAC=%pM, MASK=0x%02x, "
725                 "OBT=%d, PASS_BLOCKED=%d, REPRIO_EN=%d, PRIO=%d\n", entry->mac,
726                 entry->portmask, entry->is_obt, entry->pass_blocked,
727                 entry->reprio_en, entry->reprio_tc);
728
729         /* Add mac address */
730         hellcreek_write(hellcreek, entry->mac[1] | (entry->mac[0] << 8), HR_FDBWDH);
731         hellcreek_write(hellcreek, entry->mac[3] | (entry->mac[2] << 8), HR_FDBWDM);
732         hellcreek_write(hellcreek, entry->mac[5] | (entry->mac[4] << 8), HR_FDBWDL);
733
734         /* Meta data */
735         meta |= entry->portmask << HR_FDBWRM0_PORTMASK_SHIFT;
736         if (entry->is_obt)
737                 meta |= HR_FDBWRM0_OBT;
738         if (entry->pass_blocked)
739                 meta |= HR_FDBWRM0_PASS_BLOCKED;
740         if (entry->reprio_en) {
741                 meta |= HR_FDBWRM0_REPRIO_EN;
742                 meta |= entry->reprio_tc << HR_FDBWRM0_REPRIO_TC_SHIFT;
743         }
744         hellcreek_write(hellcreek, meta, HR_FDBWRM0);
745
746         /* Commit */
747         hellcreek_write(hellcreek, 0x00, HR_FDBWRCMD);
748
749         /* Wait until done */
750         return hellcreek_wait_fdb_ready(hellcreek);
751 }
752
753 static int __hellcreek_fdb_del(struct hellcreek *hellcreek,
754                                const struct hellcreek_fdb_entry *entry)
755 {
756         dev_dbg(hellcreek->dev, "Delete FDB entry: MAC=%pM!\n", entry->mac);
757
758         /* Delete by matching idx */
759         hellcreek_write(hellcreek, entry->idx | HR_FDBWRCMD_FDBDEL, HR_FDBWRCMD);
760
761         /* Wait until done */
762         return hellcreek_wait_fdb_ready(hellcreek);
763 }
764
765 static void hellcreek_populate_fdb_entry(struct hellcreek *hellcreek,
766                                          struct hellcreek_fdb_entry *entry,
767                                          size_t idx)
768 {
769         unsigned char addr[ETH_ALEN];
770         u16 meta, mac;
771
772         /* Read values */
773         meta    = hellcreek_read(hellcreek, HR_FDBMDRD);
774         mac     = hellcreek_read(hellcreek, HR_FDBRDL);
775         addr[5] = mac & 0xff;
776         addr[4] = (mac & 0xff00) >> 8;
777         mac     = hellcreek_read(hellcreek, HR_FDBRDM);
778         addr[3] = mac & 0xff;
779         addr[2] = (mac & 0xff00) >> 8;
780         mac     = hellcreek_read(hellcreek, HR_FDBRDH);
781         addr[1] = mac & 0xff;
782         addr[0] = (mac & 0xff00) >> 8;
783
784         /* Populate @entry */
785         memcpy(entry->mac, addr, sizeof(addr));
786         entry->idx          = idx;
787         entry->portmask     = (meta & HR_FDBMDRD_PORTMASK_MASK) >>
788                 HR_FDBMDRD_PORTMASK_SHIFT;
789         entry->age          = (meta & HR_FDBMDRD_AGE_MASK) >>
790                 HR_FDBMDRD_AGE_SHIFT;
791         entry->is_obt       = !!(meta & HR_FDBMDRD_OBT);
792         entry->pass_blocked = !!(meta & HR_FDBMDRD_PASS_BLOCKED);
793         entry->is_static    = !!(meta & HR_FDBMDRD_STATIC);
794         entry->reprio_tc    = (meta & HR_FDBMDRD_REPRIO_TC_MASK) >>
795                 HR_FDBMDRD_REPRIO_TC_SHIFT;
796         entry->reprio_en    = !!(meta & HR_FDBMDRD_REPRIO_EN);
797 }
798
799 /* Retrieve the index of a FDB entry by mac address. Currently we search through
800  * the complete table in hardware. If that's too slow, we might have to cache
801  * the complete FDB table in software.
802  */
803 static int hellcreek_fdb_get(struct hellcreek *hellcreek,
804                              const unsigned char *dest,
805                              struct hellcreek_fdb_entry *entry)
806 {
807         size_t i;
808
809         /* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
810          * should reset the internal pointer. But, that doesn't work. The vendor
811          * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
812          */
813         hellcreek_read(hellcreek, HR_FDBMAX);
814         hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
815
816         /* We have to read the complete table, because the switch/driver might
817          * enter new entries anywhere.
818          */
819         for (i = 0; i < hellcreek->fdb_entries; ++i) {
820                 struct hellcreek_fdb_entry tmp = { 0 };
821
822                 /* Read entry */
823                 hellcreek_populate_fdb_entry(hellcreek, &tmp, i);
824
825                 /* Force next entry */
826                 hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
827
828                 if (memcmp(tmp.mac, dest, ETH_ALEN))
829                         continue;
830
831                 /* Match found */
832                 memcpy(entry, &tmp, sizeof(*entry));
833
834                 return 0;
835         }
836
837         return -ENOENT;
838 }
839
840 static int hellcreek_fdb_add(struct dsa_switch *ds, int port,
841                              const unsigned char *addr, u16 vid,
842                              struct dsa_db db)
843 {
844         struct hellcreek_fdb_entry entry = { 0 };
845         struct hellcreek *hellcreek = ds->priv;
846         int ret;
847
848         dev_dbg(hellcreek->dev, "Add FDB entry for MAC=%pM\n", addr);
849
850         mutex_lock(&hellcreek->reg_lock);
851
852         ret = hellcreek_fdb_get(hellcreek, addr, &entry);
853         if (ret) {
854                 /* Not found */
855                 memcpy(entry.mac, addr, sizeof(entry.mac));
856                 entry.portmask = BIT(port);
857
858                 ret = __hellcreek_fdb_add(hellcreek, &entry);
859                 if (ret) {
860                         dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
861                         goto out;
862                 }
863         } else {
864                 /* Found */
865                 ret = __hellcreek_fdb_del(hellcreek, &entry);
866                 if (ret) {
867                         dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
868                         goto out;
869                 }
870
871                 entry.portmask |= BIT(port);
872
873                 ret = __hellcreek_fdb_add(hellcreek, &entry);
874                 if (ret) {
875                         dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
876                         goto out;
877                 }
878         }
879
880 out:
881         mutex_unlock(&hellcreek->reg_lock);
882
883         return ret;
884 }
885
886 static int hellcreek_fdb_del(struct dsa_switch *ds, int port,
887                              const unsigned char *addr, u16 vid,
888                              struct dsa_db db)
889 {
890         struct hellcreek_fdb_entry entry = { 0 };
891         struct hellcreek *hellcreek = ds->priv;
892         int ret;
893
894         dev_dbg(hellcreek->dev, "Delete FDB entry for MAC=%pM\n", addr);
895
896         mutex_lock(&hellcreek->reg_lock);
897
898         ret = hellcreek_fdb_get(hellcreek, addr, &entry);
899         if (ret) {
900                 /* Not found */
901                 dev_err(hellcreek->dev, "FDB entry for deletion not found!\n");
902         } else {
903                 /* Found */
904                 ret = __hellcreek_fdb_del(hellcreek, &entry);
905                 if (ret) {
906                         dev_err(hellcreek->dev, "Failed to delete FDB entry!\n");
907                         goto out;
908                 }
909
910                 entry.portmask &= ~BIT(port);
911
912                 if (entry.portmask != 0x00) {
913                         ret = __hellcreek_fdb_add(hellcreek, &entry);
914                         if (ret) {
915                                 dev_err(hellcreek->dev, "Failed to add FDB entry!\n");
916                                 goto out;
917                         }
918                 }
919         }
920
921 out:
922         mutex_unlock(&hellcreek->reg_lock);
923
924         return ret;
925 }
926
927 static int hellcreek_fdb_dump(struct dsa_switch *ds, int port,
928                               dsa_fdb_dump_cb_t *cb, void *data)
929 {
930         struct hellcreek *hellcreek = ds->priv;
931         u16 entries;
932         int ret = 0;
933         size_t i;
934
935         mutex_lock(&hellcreek->reg_lock);
936
937         /* Set read pointer to zero: The read of HR_FDBMAX (read-only register)
938          * should reset the internal pointer. But, that doesn't work. The vendor
939          * suggested a subsequent write as workaround. Same for HR_FDBRDH below.
940          */
941         entries = hellcreek_read(hellcreek, HR_FDBMAX);
942         hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
943
944         dev_dbg(hellcreek->dev, "FDB dump for port %d, entries=%d!\n", port, entries);
945
946         /* Read table */
947         for (i = 0; i < hellcreek->fdb_entries; ++i) {
948                 struct hellcreek_fdb_entry entry = { 0 };
949
950                 /* Read entry */
951                 hellcreek_populate_fdb_entry(hellcreek, &entry, i);
952
953                 /* Force next entry */
954                 hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
955
956                 /* Check valid */
957                 if (is_zero_ether_addr(entry.mac))
958                         continue;
959
960                 /* Check port mask */
961                 if (!(entry.portmask & BIT(port)))
962                         continue;
963
964                 ret = cb(entry.mac, 0, entry.is_static, data);
965                 if (ret)
966                         break;
967         }
968
969         mutex_unlock(&hellcreek->reg_lock);
970
971         return ret;
972 }
973
974 static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
975                                     bool vlan_filtering,
976                                     struct netlink_ext_ack *extack)
977 {
978         struct hellcreek *hellcreek = ds->priv;
979
980         dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
981                 vlan_filtering ? "Enable" : "Disable", port);
982
983         /* Configure port to drop packages with not known vids */
984         hellcreek_setup_ingressflt(hellcreek, port, vlan_filtering);
985
986         /* Enable VLAN awareness on the switch. This save due to
987          * ds->vlan_filtering_is_global.
988          */
989         hellcreek_setup_vlan_awareness(hellcreek, vlan_filtering);
990
991         return 0;
992 }
993
994 static int hellcreek_enable_ip_core(struct hellcreek *hellcreek)
995 {
996         int ret;
997         u16 val;
998
999         mutex_lock(&hellcreek->reg_lock);
1000
1001         val = hellcreek_read(hellcreek, HR_CTRL_C);
1002         val |= HR_CTRL_C_ENABLE;
1003         hellcreek_write(hellcreek, val, HR_CTRL_C);
1004         ret = hellcreek_wait_until_transitioned(hellcreek);
1005
1006         mutex_unlock(&hellcreek->reg_lock);
1007
1008         return ret;
1009 }
1010
1011 static void hellcreek_setup_cpu_and_tunnel_port(struct hellcreek *hellcreek)
1012 {
1013         struct hellcreek_port *tunnel_port = &hellcreek->ports[TUNNEL_PORT];
1014         struct hellcreek_port *cpu_port = &hellcreek->ports[CPU_PORT];
1015         u16 ptcfg = 0;
1016
1017         ptcfg |= HR_PTCFG_LEARNING_EN | HR_PTCFG_ADMIN_EN;
1018
1019         mutex_lock(&hellcreek->reg_lock);
1020
1021         hellcreek_select_port(hellcreek, CPU_PORT);
1022         hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
1023
1024         hellcreek_select_port(hellcreek, TUNNEL_PORT);
1025         hellcreek_write(hellcreek, ptcfg, HR_PTCFG);
1026
1027         cpu_port->ptcfg    = ptcfg;
1028         tunnel_port->ptcfg = ptcfg;
1029
1030         mutex_unlock(&hellcreek->reg_lock);
1031 }
1032
1033 static void hellcreek_setup_tc_identity_mapping(struct hellcreek *hellcreek)
1034 {
1035         int i;
1036
1037         /* The switch has multiple egress queues per port. The queue is selected
1038          * via the PCP field in the VLAN header. The switch internally deals
1039          * with traffic classes instead of PCP values and this mapping is
1040          * configurable.
1041          *
1042          * The default mapping is (PCP - TC):
1043          *  7 - 7
1044          *  6 - 6
1045          *  5 - 5
1046          *  4 - 4
1047          *  3 - 3
1048          *  2 - 1
1049          *  1 - 0
1050          *  0 - 2
1051          *
1052          * The default should be an identity mapping.
1053          */
1054
1055         for (i = 0; i < 8; ++i) {
1056                 mutex_lock(&hellcreek->reg_lock);
1057
1058                 hellcreek_select_prio(hellcreek, i);
1059                 hellcreek_write(hellcreek,
1060                                 i << HR_PRTCCFG_PCP_TC_MAP_SHIFT,
1061                                 HR_PRTCCFG);
1062
1063                 mutex_unlock(&hellcreek->reg_lock);
1064         }
1065 }
1066
1067 static int hellcreek_setup_fdb(struct hellcreek *hellcreek)
1068 {
1069         static struct hellcreek_fdb_entry l2_ptp = {
1070                 /* MAC: 01-1B-19-00-00-00 */
1071                 .mac          = { 0x01, 0x1b, 0x19, 0x00, 0x00, 0x00 },
1072                 .portmask     = 0x03,   /* Management ports */
1073                 .age          = 0,
1074                 .is_obt       = 0,
1075                 .pass_blocked = 0,
1076                 .is_static    = 1,
1077                 .reprio_tc    = 6,      /* TC: 6 as per IEEE 802.1AS */
1078                 .reprio_en    = 1,
1079         };
1080         static struct hellcreek_fdb_entry udp4_ptp = {
1081                 /* MAC: 01-00-5E-00-01-81 */
1082                 .mac          = { 0x01, 0x00, 0x5e, 0x00, 0x01, 0x81 },
1083                 .portmask     = 0x03,   /* Management ports */
1084                 .age          = 0,
1085                 .is_obt       = 0,
1086                 .pass_blocked = 0,
1087                 .is_static    = 1,
1088                 .reprio_tc    = 6,
1089                 .reprio_en    = 1,
1090         };
1091         static struct hellcreek_fdb_entry udp6_ptp = {
1092                 /* MAC: 33-33-00-00-01-81 */
1093                 .mac          = { 0x33, 0x33, 0x00, 0x00, 0x01, 0x81 },
1094                 .portmask     = 0x03,   /* Management ports */
1095                 .age          = 0,
1096                 .is_obt       = 0,
1097                 .pass_blocked = 0,
1098                 .is_static    = 1,
1099                 .reprio_tc    = 6,
1100                 .reprio_en    = 1,
1101         };
1102         static struct hellcreek_fdb_entry l2_p2p = {
1103                 /* MAC: 01-80-C2-00-00-0E */
1104                 .mac          = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e },
1105                 .portmask     = 0x03,   /* Management ports */
1106                 .age          = 0,
1107                 .is_obt       = 0,
1108                 .pass_blocked = 1,
1109                 .is_static    = 1,
1110                 .reprio_tc    = 6,      /* TC: 6 as per IEEE 802.1AS */
1111                 .reprio_en    = 1,
1112         };
1113         static struct hellcreek_fdb_entry udp4_p2p = {
1114                 /* MAC: 01-00-5E-00-00-6B */
1115                 .mac          = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x6b },
1116                 .portmask     = 0x03,   /* Management ports */
1117                 .age          = 0,
1118                 .is_obt       = 0,
1119                 .pass_blocked = 1,
1120                 .is_static    = 1,
1121                 .reprio_tc    = 6,
1122                 .reprio_en    = 1,
1123         };
1124         static struct hellcreek_fdb_entry udp6_p2p = {
1125                 /* MAC: 33-33-00-00-00-6B */
1126                 .mac          = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x6b },
1127                 .portmask     = 0x03,   /* Management ports */
1128                 .age          = 0,
1129                 .is_obt       = 0,
1130                 .pass_blocked = 1,
1131                 .is_static    = 1,
1132                 .reprio_tc    = 6,
1133                 .reprio_en    = 1,
1134         };
1135         static struct hellcreek_fdb_entry stp = {
1136                 /* MAC: 01-80-C2-00-00-00 */
1137                 .mac          = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 },
1138                 .portmask     = 0x03,   /* Management ports */
1139                 .age          = 0,
1140                 .is_obt       = 0,
1141                 .pass_blocked = 1,
1142                 .is_static    = 1,
1143                 .reprio_tc    = 6,
1144                 .reprio_en    = 1,
1145         };
1146         int ret;
1147
1148         mutex_lock(&hellcreek->reg_lock);
1149         ret = __hellcreek_fdb_add(hellcreek, &l2_ptp);
1150         if (ret)
1151                 goto out;
1152         ret = __hellcreek_fdb_add(hellcreek, &udp4_ptp);
1153         if (ret)
1154                 goto out;
1155         ret = __hellcreek_fdb_add(hellcreek, &udp6_ptp);
1156         if (ret)
1157                 goto out;
1158         ret = __hellcreek_fdb_add(hellcreek, &l2_p2p);
1159         if (ret)
1160                 goto out;
1161         ret = __hellcreek_fdb_add(hellcreek, &udp4_p2p);
1162         if (ret)
1163                 goto out;
1164         ret = __hellcreek_fdb_add(hellcreek, &udp6_p2p);
1165         if (ret)
1166                 goto out;
1167         ret = __hellcreek_fdb_add(hellcreek, &stp);
1168 out:
1169         mutex_unlock(&hellcreek->reg_lock);
1170
1171         return ret;
1172 }
1173
1174 static int hellcreek_devlink_info_get(struct dsa_switch *ds,
1175                                       struct devlink_info_req *req,
1176                                       struct netlink_ext_ack *extack)
1177 {
1178         struct hellcreek *hellcreek = ds->priv;
1179         int ret;
1180
1181         ret = devlink_info_driver_name_put(req, "hellcreek");
1182         if (ret)
1183                 return ret;
1184
1185         return devlink_info_version_fixed_put(req,
1186                                               DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
1187                                               hellcreek->pdata->name);
1188 }
1189
1190 static u64 hellcreek_devlink_vlan_table_get(void *priv)
1191 {
1192         struct hellcreek *hellcreek = priv;
1193         u64 count = 0;
1194         int i;
1195
1196         mutex_lock(&hellcreek->reg_lock);
1197         for (i = 0; i < VLAN_N_VID; ++i)
1198                 if (hellcreek->vidmbrcfg[i])
1199                         count++;
1200         mutex_unlock(&hellcreek->reg_lock);
1201
1202         return count;
1203 }
1204
1205 static u64 hellcreek_devlink_fdb_table_get(void *priv)
1206 {
1207         struct hellcreek *hellcreek = priv;
1208         u64 count = 0;
1209
1210         /* Reading this register has side effects. Synchronize against the other
1211          * FDB operations.
1212          */
1213         mutex_lock(&hellcreek->reg_lock);
1214         count = hellcreek_read(hellcreek, HR_FDBMAX);
1215         mutex_unlock(&hellcreek->reg_lock);
1216
1217         return count;
1218 }
1219
1220 static int hellcreek_setup_devlink_resources(struct dsa_switch *ds)
1221 {
1222         struct devlink_resource_size_params size_vlan_params;
1223         struct devlink_resource_size_params size_fdb_params;
1224         struct hellcreek *hellcreek = ds->priv;
1225         int err;
1226
1227         devlink_resource_size_params_init(&size_vlan_params, VLAN_N_VID,
1228                                           VLAN_N_VID,
1229                                           1, DEVLINK_RESOURCE_UNIT_ENTRY);
1230
1231         devlink_resource_size_params_init(&size_fdb_params,
1232                                           hellcreek->fdb_entries,
1233                                           hellcreek->fdb_entries,
1234                                           1, DEVLINK_RESOURCE_UNIT_ENTRY);
1235
1236         err = dsa_devlink_resource_register(ds, "VLAN", VLAN_N_VID,
1237                                             HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE,
1238                                             DEVLINK_RESOURCE_ID_PARENT_TOP,
1239                                             &size_vlan_params);
1240         if (err)
1241                 goto out;
1242
1243         err = dsa_devlink_resource_register(ds, "FDB", hellcreek->fdb_entries,
1244                                             HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE,
1245                                             DEVLINK_RESOURCE_ID_PARENT_TOP,
1246                                             &size_fdb_params);
1247         if (err)
1248                 goto out;
1249
1250         dsa_devlink_resource_occ_get_register(ds,
1251                                               HELLCREEK_DEVLINK_PARAM_ID_VLAN_TABLE,
1252                                               hellcreek_devlink_vlan_table_get,
1253                                               hellcreek);
1254
1255         dsa_devlink_resource_occ_get_register(ds,
1256                                               HELLCREEK_DEVLINK_PARAM_ID_FDB_TABLE,
1257                                               hellcreek_devlink_fdb_table_get,
1258                                               hellcreek);
1259
1260         return 0;
1261
1262 out:
1263         dsa_devlink_resources_unregister(ds);
1264
1265         return err;
1266 }
1267
1268 static int hellcreek_devlink_region_vlan_snapshot(struct devlink *dl,
1269                                                   const struct devlink_region_ops *ops,
1270                                                   struct netlink_ext_ack *extack,
1271                                                   u8 **data)
1272 {
1273         struct hellcreek_devlink_vlan_entry *table, *entry;
1274         struct dsa_switch *ds = dsa_devlink_to_ds(dl);
1275         struct hellcreek *hellcreek = ds->priv;
1276         int i;
1277
1278         table = kcalloc(VLAN_N_VID, sizeof(*entry), GFP_KERNEL);
1279         if (!table)
1280                 return -ENOMEM;
1281
1282         entry = table;
1283
1284         mutex_lock(&hellcreek->reg_lock);
1285         for (i = 0; i < VLAN_N_VID; ++i, ++entry) {
1286                 entry->member = hellcreek->vidmbrcfg[i];
1287                 entry->vid    = i;
1288         }
1289         mutex_unlock(&hellcreek->reg_lock);
1290
1291         *data = (u8 *)table;
1292
1293         return 0;
1294 }
1295
1296 static int hellcreek_devlink_region_fdb_snapshot(struct devlink *dl,
1297                                                  const struct devlink_region_ops *ops,
1298                                                  struct netlink_ext_ack *extack,
1299                                                  u8 **data)
1300 {
1301         struct dsa_switch *ds = dsa_devlink_to_ds(dl);
1302         struct hellcreek_fdb_entry *table, *entry;
1303         struct hellcreek *hellcreek = ds->priv;
1304         size_t i;
1305
1306         table = kcalloc(hellcreek->fdb_entries, sizeof(*entry), GFP_KERNEL);
1307         if (!table)
1308                 return -ENOMEM;
1309
1310         entry = table;
1311
1312         mutex_lock(&hellcreek->reg_lock);
1313
1314         /* Start table read */
1315         hellcreek_read(hellcreek, HR_FDBMAX);
1316         hellcreek_write(hellcreek, 0x00, HR_FDBMAX);
1317
1318         for (i = 0; i < hellcreek->fdb_entries; ++i, ++entry) {
1319                 /* Read current entry */
1320                 hellcreek_populate_fdb_entry(hellcreek, entry, i);
1321
1322                 /* Advance read pointer */
1323                 hellcreek_write(hellcreek, 0x00, HR_FDBRDH);
1324         }
1325
1326         mutex_unlock(&hellcreek->reg_lock);
1327
1328         *data = (u8 *)table;
1329
1330         return 0;
1331 }
1332
1333 static struct devlink_region_ops hellcreek_region_vlan_ops = {
1334         .name       = "vlan",
1335         .snapshot   = hellcreek_devlink_region_vlan_snapshot,
1336         .destructor = kfree,
1337 };
1338
1339 static struct devlink_region_ops hellcreek_region_fdb_ops = {
1340         .name       = "fdb",
1341         .snapshot   = hellcreek_devlink_region_fdb_snapshot,
1342         .destructor = kfree,
1343 };
1344
1345 static int hellcreek_setup_devlink_regions(struct dsa_switch *ds)
1346 {
1347         struct hellcreek *hellcreek = ds->priv;
1348         struct devlink_region_ops *ops;
1349         struct devlink_region *region;
1350         u64 size;
1351         int ret;
1352
1353         /* VLAN table */
1354         size = VLAN_N_VID * sizeof(struct hellcreek_devlink_vlan_entry);
1355         ops  = &hellcreek_region_vlan_ops;
1356
1357         region = dsa_devlink_region_create(ds, ops, 1, size);
1358         if (IS_ERR(region))
1359                 return PTR_ERR(region);
1360
1361         hellcreek->vlan_region = region;
1362
1363         /* FDB table */
1364         size = hellcreek->fdb_entries * sizeof(struct hellcreek_fdb_entry);
1365         ops  = &hellcreek_region_fdb_ops;
1366
1367         region = dsa_devlink_region_create(ds, ops, 1, size);
1368         if (IS_ERR(region)) {
1369                 ret = PTR_ERR(region);
1370                 goto err_fdb;
1371         }
1372
1373         hellcreek->fdb_region = region;
1374
1375         return 0;
1376
1377 err_fdb:
1378         dsa_devlink_region_destroy(hellcreek->vlan_region);
1379
1380         return ret;
1381 }
1382
1383 static void hellcreek_teardown_devlink_regions(struct dsa_switch *ds)
1384 {
1385         struct hellcreek *hellcreek = ds->priv;
1386
1387         dsa_devlink_region_destroy(hellcreek->fdb_region);
1388         dsa_devlink_region_destroy(hellcreek->vlan_region);
1389 }
1390
1391 static int hellcreek_setup(struct dsa_switch *ds)
1392 {
1393         struct hellcreek *hellcreek = ds->priv;
1394         u16 swcfg = 0;
1395         int ret, i;
1396
1397         dev_dbg(hellcreek->dev, "Set up the switch\n");
1398
1399         /* Let's go */
1400         ret = hellcreek_enable_ip_core(hellcreek);
1401         if (ret) {
1402                 dev_err(hellcreek->dev, "Failed to enable IP core!\n");
1403                 return ret;
1404         }
1405
1406         /* Enable CPU/Tunnel ports */
1407         hellcreek_setup_cpu_and_tunnel_port(hellcreek);
1408
1409         /* Switch config: Keep defaults, enable FDB aging and learning and tag
1410          * each frame from/to cpu port for DSA tagging.  Also enable the length
1411          * aware shaping mode. This eliminates the need for Qbv guard bands.
1412          */
1413         swcfg |= HR_SWCFG_FDBAGE_EN |
1414                 HR_SWCFG_FDBLRN_EN  |
1415                 HR_SWCFG_ALWAYS_OBT |
1416                 (HR_SWCFG_LAS_ON << HR_SWCFG_LAS_MODE_SHIFT);
1417         hellcreek->swcfg = swcfg;
1418         hellcreek_write(hellcreek, swcfg, HR_SWCFG);
1419
1420         /* Initial vlan membership to reflect port separation */
1421         for (i = 0; i < ds->num_ports; ++i) {
1422                 if (!dsa_is_user_port(ds, i))
1423                         continue;
1424
1425                 hellcreek_setup_vlan_membership(ds, i, true);
1426         }
1427
1428         /* Configure PCP <-> TC mapping */
1429         hellcreek_setup_tc_identity_mapping(hellcreek);
1430
1431         /* The VLAN awareness is a global switch setting. Therefore, mixed vlan
1432          * filtering setups are not supported.
1433          */
1434         ds->vlan_filtering_is_global = true;
1435         ds->needs_standalone_vlan_filtering = true;
1436
1437         /* Intercept _all_ PTP multicast traffic */
1438         ret = hellcreek_setup_fdb(hellcreek);
1439         if (ret) {
1440                 dev_err(hellcreek->dev,
1441                         "Failed to insert static PTP FDB entries\n");
1442                 return ret;
1443         }
1444
1445         /* Register devlink resources with DSA */
1446         ret = hellcreek_setup_devlink_resources(ds);
1447         if (ret) {
1448                 dev_err(hellcreek->dev,
1449                         "Failed to setup devlink resources!\n");
1450                 return ret;
1451         }
1452
1453         ret = hellcreek_setup_devlink_regions(ds);
1454         if (ret) {
1455                 dev_err(hellcreek->dev,
1456                         "Failed to setup devlink regions!\n");
1457                 goto err_regions;
1458         }
1459
1460         return 0;
1461
1462 err_regions:
1463         dsa_devlink_resources_unregister(ds);
1464
1465         return ret;
1466 }
1467
1468 static void hellcreek_teardown(struct dsa_switch *ds)
1469 {
1470         hellcreek_teardown_devlink_regions(ds);
1471         dsa_devlink_resources_unregister(ds);
1472 }
1473
1474 static void hellcreek_phylink_get_caps(struct dsa_switch *ds, int port,
1475                                        struct phylink_config *config)
1476 {
1477         struct hellcreek *hellcreek = ds->priv;
1478
1479         __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces);
1480         __set_bit(PHY_INTERFACE_MODE_RGMII, config->supported_interfaces);
1481
1482         /* Include GMII - the hardware does not support this interface
1483          * mode, but it's the default interface mode for phylib, so we
1484          * need it for compatibility with existing DT.
1485          */
1486         __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces);
1487
1488         /* The MAC settings are a hardware configuration option and cannot be
1489          * changed at run time or by strapping. Therefore the attached PHYs
1490          * should be programmed to only advertise settings which are supported
1491          * by the hardware.
1492          */
1493         if (hellcreek->pdata->is_100_mbits)
1494                 config->mac_capabilities = MAC_100FD;
1495         else
1496                 config->mac_capabilities = MAC_1000FD;
1497 }
1498
1499 static int
1500 hellcreek_port_prechangeupper(struct dsa_switch *ds, int port,
1501                               struct netdev_notifier_changeupper_info *info)
1502 {
1503         struct hellcreek *hellcreek = ds->priv;
1504         bool used = true;
1505         int ret = -EBUSY;
1506         u16 vid;
1507         int i;
1508
1509         dev_dbg(hellcreek->dev, "Pre change upper for port %d\n", port);
1510
1511         /*
1512          * Deny VLAN devices on top of lan ports with the same VLAN ids, because
1513          * it breaks the port separation due to the private VLANs. Example:
1514          *
1515          * lan0.100 *and* lan1.100 cannot be used in parallel. However, lan0.99
1516          * and lan1.100 works.
1517          */
1518
1519         if (!is_vlan_dev(info->upper_dev))
1520                 return 0;
1521
1522         vid = vlan_dev_vlan_id(info->upper_dev);
1523
1524         /* For all ports, check bitmaps */
1525         mutex_lock(&hellcreek->vlan_lock);
1526         for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
1527                 if (!dsa_is_user_port(ds, i))
1528                         continue;
1529
1530                 if (port == i)
1531                         continue;
1532
1533                 used = used && test_bit(vid, hellcreek->ports[i].vlan_dev_bitmap);
1534         }
1535
1536         if (used)
1537                 goto out;
1538
1539         /* Update bitmap */
1540         set_bit(vid, hellcreek->ports[port].vlan_dev_bitmap);
1541
1542         ret = 0;
1543
1544 out:
1545         mutex_unlock(&hellcreek->vlan_lock);
1546
1547         return ret;
1548 }
1549
1550 static void hellcreek_setup_maxsdu(struct hellcreek *hellcreek, int port,
1551                                    const struct tc_taprio_qopt_offload *schedule)
1552 {
1553         int tc;
1554
1555         for (tc = 0; tc < 8; ++tc) {
1556                 u32 max_sdu = schedule->max_sdu[tc] + VLAN_ETH_HLEN - ETH_FCS_LEN;
1557                 u16 val;
1558
1559                 if (!schedule->max_sdu[tc])
1560                         continue;
1561
1562                 dev_dbg(hellcreek->dev, "Configure max-sdu %u for tc %d on port %d\n",
1563                         max_sdu, tc, port);
1564
1565                 hellcreek_select_port_prio(hellcreek, port, tc);
1566
1567                 val = (max_sdu & HR_PTPRTCCFG_MAXSDU_MASK) << HR_PTPRTCCFG_MAXSDU_SHIFT;
1568
1569                 hellcreek_write(hellcreek, val, HR_PTPRTCCFG);
1570         }
1571 }
1572
1573 static void hellcreek_reset_maxsdu(struct hellcreek *hellcreek, int port)
1574 {
1575         int tc;
1576
1577         for (tc = 0; tc < 8; ++tc) {
1578                 u16 val;
1579
1580                 hellcreek_select_port_prio(hellcreek, port, tc);
1581
1582                 val = (HELLCREEK_DEFAULT_MAX_SDU & HR_PTPRTCCFG_MAXSDU_MASK)
1583                         << HR_PTPRTCCFG_MAXSDU_SHIFT;
1584
1585                 hellcreek_write(hellcreek, val, HR_PTPRTCCFG);
1586         }
1587 }
1588
1589 static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
1590                                 const struct tc_taprio_qopt_offload *schedule)
1591 {
1592         const struct tc_taprio_sched_entry *cur, *initial, *next;
1593         size_t i;
1594
1595         cur = initial = &schedule->entries[0];
1596         next = cur + 1;
1597
1598         for (i = 1; i <= schedule->num_entries; ++i) {
1599                 u16 data;
1600                 u8 gates;
1601
1602                 if (i == schedule->num_entries)
1603                         gates = initial->gate_mask ^
1604                                 cur->gate_mask;
1605                 else
1606                         gates = next->gate_mask ^
1607                                 cur->gate_mask;
1608
1609                 data = gates;
1610
1611                 if (i == schedule->num_entries)
1612                         data |= TR_GCLDAT_GCLWRLAST;
1613
1614                 /* Gates states */
1615                 hellcreek_write(hellcreek, data, TR_GCLDAT);
1616
1617                 /* Time interval */
1618                 hellcreek_write(hellcreek,
1619                                 cur->interval & 0x0000ffff,
1620                                 TR_GCLTIL);
1621                 hellcreek_write(hellcreek,
1622                                 (cur->interval & 0xffff0000) >> 16,
1623                                 TR_GCLTIH);
1624
1625                 /* Commit entry */
1626                 data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) |
1627                         (initial->gate_mask <<
1628                          TR_GCLCMD_INIT_GATE_STATES_SHIFT);
1629                 hellcreek_write(hellcreek, data, TR_GCLCMD);
1630
1631                 cur++;
1632                 next++;
1633         }
1634 }
1635
1636 static void hellcreek_set_cycle_time(struct hellcreek *hellcreek,
1637                                      const struct tc_taprio_qopt_offload *schedule)
1638 {
1639         u32 cycle_time = schedule->cycle_time;
1640
1641         hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL);
1642         hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH);
1643 }
1644
1645 static void hellcreek_switch_schedule(struct hellcreek *hellcreek,
1646                                       ktime_t start_time)
1647 {
1648         struct timespec64 ts = ktime_to_timespec64(start_time);
1649
1650         /* Start schedule at this point of time */
1651         hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL);
1652         hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH);
1653
1654         /* Arm timer, set seconds and switch schedule */
1655         hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG |
1656                         ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) <<
1657                          TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD);
1658 }
1659
1660 static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int port)
1661 {
1662         struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
1663         s64 base_time_ns, current_ns;
1664
1665         /* The switch allows a schedule to be started only eight seconds within
1666          * the future. Therefore, check the current PTP time if the schedule is
1667          * startable or not.
1668          */
1669
1670         /* Use the "cached" time. That should be alright, as it's updated quite
1671          * frequently in the PTP code.
1672          */
1673         mutex_lock(&hellcreek->ptp_lock);
1674         current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
1675         mutex_unlock(&hellcreek->ptp_lock);
1676
1677         /* Calculate difference to admin base time */
1678         base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time);
1679
1680         return base_time_ns - current_ns < (s64)4 * NSEC_PER_SEC;
1681 }
1682
1683 static void hellcreek_start_schedule(struct hellcreek *hellcreek, int port)
1684 {
1685         struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
1686         ktime_t base_time, current_time;
1687         s64 current_ns;
1688         u32 cycle_time;
1689
1690         /* First select port */
1691         hellcreek_select_tgd(hellcreek, port);
1692
1693         /* Forward base time into the future if needed */
1694         mutex_lock(&hellcreek->ptp_lock);
1695         current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
1696         mutex_unlock(&hellcreek->ptp_lock);
1697
1698         current_time = ns_to_ktime(current_ns);
1699         base_time    = hellcreek_port->current_schedule->base_time;
1700         cycle_time   = hellcreek_port->current_schedule->cycle_time;
1701
1702         if (ktime_compare(current_time, base_time) > 0) {
1703                 s64 n;
1704
1705                 n = div64_s64(ktime_sub_ns(current_time, base_time),
1706                               cycle_time);
1707                 base_time = ktime_add_ns(base_time, (n + 1) * cycle_time);
1708         }
1709
1710         /* Set admin base time and switch schedule */
1711         hellcreek_switch_schedule(hellcreek, base_time);
1712
1713         taprio_offload_free(hellcreek_port->current_schedule);
1714         hellcreek_port->current_schedule = NULL;
1715
1716         dev_dbg(hellcreek->dev, "Armed EST timer for port %d\n",
1717                 hellcreek_port->port);
1718 }
1719
1720 static void hellcreek_check_schedule(struct work_struct *work)
1721 {
1722         struct delayed_work *dw = to_delayed_work(work);
1723         struct hellcreek_port *hellcreek_port;
1724         struct hellcreek *hellcreek;
1725         bool startable;
1726
1727         hellcreek_port = dw_to_hellcreek_port(dw);
1728         hellcreek = hellcreek_port->hellcreek;
1729
1730         mutex_lock(&hellcreek->reg_lock);
1731
1732         /* Check starting time */
1733         startable = hellcreek_schedule_startable(hellcreek,
1734                                                  hellcreek_port->port);
1735         if (startable) {
1736                 hellcreek_start_schedule(hellcreek, hellcreek_port->port);
1737                 mutex_unlock(&hellcreek->reg_lock);
1738                 return;
1739         }
1740
1741         mutex_unlock(&hellcreek->reg_lock);
1742
1743         /* Reschedule */
1744         schedule_delayed_work(&hellcreek_port->schedule_work,
1745                               HELLCREEK_SCHEDULE_PERIOD);
1746 }
1747
1748 static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
1749                                        struct tc_taprio_qopt_offload *taprio)
1750 {
1751         struct hellcreek *hellcreek = ds->priv;
1752         struct hellcreek_port *hellcreek_port;
1753         bool startable;
1754         u16 ctrl;
1755
1756         hellcreek_port = &hellcreek->ports[port];
1757
1758         dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
1759                 port);
1760
1761         /* First cancel delayed work */
1762         cancel_delayed_work_sync(&hellcreek_port->schedule_work);
1763
1764         mutex_lock(&hellcreek->reg_lock);
1765
1766         if (hellcreek_port->current_schedule) {
1767                 taprio_offload_free(hellcreek_port->current_schedule);
1768                 hellcreek_port->current_schedule = NULL;
1769         }
1770         hellcreek_port->current_schedule = taprio_offload_get(taprio);
1771
1772         /* Configure max sdu */
1773         hellcreek_setup_maxsdu(hellcreek, port, hellcreek_port->current_schedule);
1774
1775         /* Select tdg */
1776         hellcreek_select_tgd(hellcreek, port);
1777
1778         /* Enable gating and keep defaults */
1779         ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
1780         hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
1781
1782         /* Cancel pending schedule */
1783         hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
1784
1785         /* Setup a new schedule */
1786         hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule);
1787
1788         /* Configure cycle time */
1789         hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule);
1790
1791         /* Check starting time */
1792         startable = hellcreek_schedule_startable(hellcreek, port);
1793         if (startable) {
1794                 hellcreek_start_schedule(hellcreek, port);
1795                 mutex_unlock(&hellcreek->reg_lock);
1796                 return 0;
1797         }
1798
1799         mutex_unlock(&hellcreek->reg_lock);
1800
1801         /* Schedule periodic schedule check */
1802         schedule_delayed_work(&hellcreek_port->schedule_work,
1803                               HELLCREEK_SCHEDULE_PERIOD);
1804
1805         return 0;
1806 }
1807
1808 static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
1809 {
1810         struct hellcreek *hellcreek = ds->priv;
1811         struct hellcreek_port *hellcreek_port;
1812
1813         hellcreek_port = &hellcreek->ports[port];
1814
1815         dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port);
1816
1817         /* First cancel delayed work */
1818         cancel_delayed_work_sync(&hellcreek_port->schedule_work);
1819
1820         mutex_lock(&hellcreek->reg_lock);
1821
1822         if (hellcreek_port->current_schedule) {
1823                 taprio_offload_free(hellcreek_port->current_schedule);
1824                 hellcreek_port->current_schedule = NULL;
1825         }
1826
1827         /* Reset max sdu */
1828         hellcreek_reset_maxsdu(hellcreek, port);
1829
1830         /* Select tgd */
1831         hellcreek_select_tgd(hellcreek, port);
1832
1833         /* Disable gating and return to regular switching flow */
1834         hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT,
1835                         TR_TGDCTRL);
1836
1837         mutex_unlock(&hellcreek->reg_lock);
1838
1839         return 0;
1840 }
1841
1842 static bool hellcreek_validate_schedule(struct hellcreek *hellcreek,
1843                                         struct tc_taprio_qopt_offload *schedule)
1844 {
1845         size_t i;
1846
1847         /* Does this hellcreek version support Qbv in hardware? */
1848         if (!hellcreek->pdata->qbv_support)
1849                 return false;
1850
1851         /* cycle time can only be 32bit */
1852         if (schedule->cycle_time > (u32)-1)
1853                 return false;
1854
1855         /* cycle time extension is not supported */
1856         if (schedule->cycle_time_extension)
1857                 return false;
1858
1859         /* Only set command is supported */
1860         for (i = 0; i < schedule->num_entries; ++i)
1861                 if (schedule->entries[i].command != TC_TAPRIO_CMD_SET_GATES)
1862                         return false;
1863
1864         return true;
1865 }
1866
1867 static int hellcreek_tc_query_caps(struct tc_query_caps_base *base)
1868 {
1869         switch (base->type) {
1870         case TC_SETUP_QDISC_TAPRIO: {
1871                 struct tc_taprio_caps *caps = base->caps;
1872
1873                 caps->supports_queue_max_sdu = true;
1874
1875                 return 0;
1876         }
1877         default:
1878                 return -EOPNOTSUPP;
1879         }
1880 }
1881
1882 static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
1883                                    enum tc_setup_type type, void *type_data)
1884 {
1885         struct hellcreek *hellcreek = ds->priv;
1886
1887         switch (type) {
1888         case TC_QUERY_CAPS:
1889                 return hellcreek_tc_query_caps(type_data);
1890         case TC_SETUP_QDISC_TAPRIO: {
1891                 struct tc_taprio_qopt_offload *taprio = type_data;
1892
1893                 if (!hellcreek_validate_schedule(hellcreek, taprio))
1894                         return -EOPNOTSUPP;
1895
1896                 if (taprio->enable)
1897                         return hellcreek_port_set_schedule(ds, port, taprio);
1898
1899                 return hellcreek_port_del_schedule(ds, port);
1900         }
1901         default:
1902                 return -EOPNOTSUPP;
1903         }
1904 }
1905
1906 static const struct dsa_switch_ops hellcreek_ds_ops = {
1907         .devlink_info_get      = hellcreek_devlink_info_get,
1908         .get_ethtool_stats     = hellcreek_get_ethtool_stats,
1909         .get_sset_count        = hellcreek_get_sset_count,
1910         .get_strings           = hellcreek_get_strings,
1911         .get_tag_protocol      = hellcreek_get_tag_protocol,
1912         .get_ts_info           = hellcreek_get_ts_info,
1913         .phylink_get_caps      = hellcreek_phylink_get_caps,
1914         .port_bridge_flags     = hellcreek_bridge_flags,
1915         .port_bridge_join      = hellcreek_port_bridge_join,
1916         .port_bridge_leave     = hellcreek_port_bridge_leave,
1917         .port_disable          = hellcreek_port_disable,
1918         .port_enable           = hellcreek_port_enable,
1919         .port_fdb_add          = hellcreek_fdb_add,
1920         .port_fdb_del          = hellcreek_fdb_del,
1921         .port_fdb_dump         = hellcreek_fdb_dump,
1922         .port_hwtstamp_set     = hellcreek_port_hwtstamp_set,
1923         .port_hwtstamp_get     = hellcreek_port_hwtstamp_get,
1924         .port_pre_bridge_flags = hellcreek_pre_bridge_flags,
1925         .port_prechangeupper   = hellcreek_port_prechangeupper,
1926         .port_rxtstamp         = hellcreek_port_rxtstamp,
1927         .port_setup_tc         = hellcreek_port_setup_tc,
1928         .port_stp_state_set    = hellcreek_port_stp_state_set,
1929         .port_txtstamp         = hellcreek_port_txtstamp,
1930         .port_vlan_add         = hellcreek_vlan_add,
1931         .port_vlan_del         = hellcreek_vlan_del,
1932         .port_vlan_filtering   = hellcreek_vlan_filtering,
1933         .setup                 = hellcreek_setup,
1934         .teardown              = hellcreek_teardown,
1935 };
1936
1937 static int hellcreek_probe(struct platform_device *pdev)
1938 {
1939         struct device *dev = &pdev->dev;
1940         struct hellcreek *hellcreek;
1941         struct resource *res;
1942         int ret, i;
1943
1944         hellcreek = devm_kzalloc(dev, sizeof(*hellcreek), GFP_KERNEL);
1945         if (!hellcreek)
1946                 return -ENOMEM;
1947
1948         hellcreek->vidmbrcfg = devm_kcalloc(dev, VLAN_N_VID,
1949                                             sizeof(*hellcreek->vidmbrcfg),
1950                                             GFP_KERNEL);
1951         if (!hellcreek->vidmbrcfg)
1952                 return -ENOMEM;
1953
1954         hellcreek->pdata = of_device_get_match_data(dev);
1955
1956         hellcreek->ports = devm_kcalloc(dev, hellcreek->pdata->num_ports,
1957                                         sizeof(*hellcreek->ports),
1958                                         GFP_KERNEL);
1959         if (!hellcreek->ports)
1960                 return -ENOMEM;
1961
1962         for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
1963                 struct hellcreek_port *port = &hellcreek->ports[i];
1964
1965                 port->counter_values =
1966                         devm_kcalloc(dev,
1967                                      ARRAY_SIZE(hellcreek_counter),
1968                                      sizeof(*port->counter_values),
1969                                      GFP_KERNEL);
1970                 if (!port->counter_values)
1971                         return -ENOMEM;
1972
1973                 port->vlan_dev_bitmap = devm_bitmap_zalloc(dev, VLAN_N_VID,
1974                                                            GFP_KERNEL);
1975                 if (!port->vlan_dev_bitmap)
1976                         return -ENOMEM;
1977
1978                 port->hellcreek = hellcreek;
1979                 port->port      = i;
1980
1981                 INIT_DELAYED_WORK(&port->schedule_work,
1982                                   hellcreek_check_schedule);
1983         }
1984
1985         mutex_init(&hellcreek->reg_lock);
1986         mutex_init(&hellcreek->vlan_lock);
1987         mutex_init(&hellcreek->ptp_lock);
1988
1989         hellcreek->dev = dev;
1990
1991         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tsn");
1992         if (!res) {
1993                 dev_err(dev, "No memory region provided!\n");
1994                 return -ENODEV;
1995         }
1996
1997         hellcreek->base = devm_ioremap_resource(dev, res);
1998         if (IS_ERR(hellcreek->base))
1999                 return PTR_ERR(hellcreek->base);
2000
2001         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ptp");
2002         if (!res) {
2003                 dev_err(dev, "No PTP memory region provided!\n");
2004                 return -ENODEV;
2005         }
2006
2007         hellcreek->ptp_base = devm_ioremap_resource(dev, res);
2008         if (IS_ERR(hellcreek->ptp_base))
2009                 return PTR_ERR(hellcreek->ptp_base);
2010
2011         ret = hellcreek_detect(hellcreek);
2012         if (ret) {
2013                 dev_err(dev, "No (known) chip found!\n");
2014                 return ret;
2015         }
2016
2017         ret = hellcreek_wait_until_ready(hellcreek);
2018         if (ret) {
2019                 dev_err(dev, "Switch didn't become ready!\n");
2020                 return ret;
2021         }
2022
2023         hellcreek_feature_detect(hellcreek);
2024
2025         hellcreek->ds = devm_kzalloc(dev, sizeof(*hellcreek->ds), GFP_KERNEL);
2026         if (!hellcreek->ds)
2027                 return -ENOMEM;
2028
2029         hellcreek->ds->dev           = dev;
2030         hellcreek->ds->priv          = hellcreek;
2031         hellcreek->ds->ops           = &hellcreek_ds_ops;
2032         hellcreek->ds->num_ports     = hellcreek->pdata->num_ports;
2033         hellcreek->ds->num_tx_queues = HELLCREEK_NUM_EGRESS_QUEUES;
2034
2035         ret = dsa_register_switch(hellcreek->ds);
2036         if (ret) {
2037                 dev_err_probe(dev, ret, "Unable to register switch\n");
2038                 return ret;
2039         }
2040
2041         ret = hellcreek_ptp_setup(hellcreek);
2042         if (ret) {
2043                 dev_err(dev, "Failed to setup PTP!\n");
2044                 goto err_ptp_setup;
2045         }
2046
2047         ret = hellcreek_hwtstamp_setup(hellcreek);
2048         if (ret) {
2049                 dev_err(dev, "Failed to setup hardware timestamping!\n");
2050                 goto err_tstamp_setup;
2051         }
2052
2053         platform_set_drvdata(pdev, hellcreek);
2054
2055         return 0;
2056
2057 err_tstamp_setup:
2058         hellcreek_ptp_free(hellcreek);
2059 err_ptp_setup:
2060         dsa_unregister_switch(hellcreek->ds);
2061
2062         return ret;
2063 }
2064
2065 static int hellcreek_remove(struct platform_device *pdev)
2066 {
2067         struct hellcreek *hellcreek = platform_get_drvdata(pdev);
2068
2069         if (!hellcreek)
2070                 return 0;
2071
2072         hellcreek_hwtstamp_free(hellcreek);
2073         hellcreek_ptp_free(hellcreek);
2074         dsa_unregister_switch(hellcreek->ds);
2075
2076         return 0;
2077 }
2078
2079 static void hellcreek_shutdown(struct platform_device *pdev)
2080 {
2081         struct hellcreek *hellcreek = platform_get_drvdata(pdev);
2082
2083         if (!hellcreek)
2084                 return;
2085
2086         dsa_switch_shutdown(hellcreek->ds);
2087
2088         platform_set_drvdata(pdev, NULL);
2089 }
2090
2091 static const struct hellcreek_platform_data de1soc_r1_pdata = {
2092         .name            = "r4c30",
2093         .num_ports       = 4,
2094         .is_100_mbits    = 1,
2095         .qbv_support     = 1,
2096         .qbv_on_cpu_port = 1,
2097         .qbu_support     = 0,
2098         .module_id       = 0x4c30,
2099 };
2100
2101 static const struct of_device_id hellcreek_of_match[] = {
2102         {
2103                 .compatible = "hirschmann,hellcreek-de1soc-r1",
2104                 .data       = &de1soc_r1_pdata,
2105         },
2106         { /* sentinel */ },
2107 };
2108 MODULE_DEVICE_TABLE(of, hellcreek_of_match);
2109
2110 static struct platform_driver hellcreek_driver = {
2111         .probe  = hellcreek_probe,
2112         .remove = hellcreek_remove,
2113         .shutdown = hellcreek_shutdown,
2114         .driver = {
2115                 .name = "hellcreek",
2116                 .of_match_table = hellcreek_of_match,
2117         },
2118 };
2119 module_platform_driver(hellcreek_driver);
2120
2121 MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>");
2122 MODULE_DESCRIPTION("Hirschmann Hellcreek driver");
2123 MODULE_LICENSE("Dual MIT/GPL");