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