xref: /openbmc/linux/drivers/net/dsa/hirschmann/hellcreek.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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_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  
hellcreek_read(struct hellcreek * hellcreek,unsigned int offset)95  static u16 hellcreek_read(struct hellcreek *hellcreek, unsigned int offset)
96  {
97  	return readw(hellcreek->base + offset);
98  }
99  
hellcreek_read_ctrl(struct hellcreek * hellcreek)100  static u16 hellcreek_read_ctrl(struct hellcreek *hellcreek)
101  {
102  	return readw(hellcreek->base + HR_CTRL_C);
103  }
104  
hellcreek_read_stat(struct hellcreek * hellcreek)105  static u16 hellcreek_read_stat(struct hellcreek *hellcreek)
106  {
107  	return readw(hellcreek->base + HR_SWSTAT);
108  }
109  
hellcreek_write(struct hellcreek * hellcreek,u16 data,unsigned int offset)110  static void hellcreek_write(struct hellcreek *hellcreek, u16 data,
111  			    unsigned int offset)
112  {
113  	writew(data, hellcreek->base + offset);
114  }
115  
hellcreek_select_port(struct hellcreek * hellcreek,int port)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  
hellcreek_select_prio(struct hellcreek * hellcreek,int prio)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  
hellcreek_select_port_prio(struct hellcreek * hellcreek,int port,int prio)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  
hellcreek_select_counter(struct hellcreek * hellcreek,int counter)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  
hellcreek_select_vlan(struct hellcreek * hellcreek,int vid,bool pvid)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  
hellcreek_select_tgd(struct hellcreek * hellcreek,int port)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  
hellcreek_wait_until_ready(struct hellcreek * hellcreek)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  
hellcreek_wait_until_transitioned(struct hellcreek * hellcreek)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  
hellcreek_wait_fdb_ready(struct hellcreek * hellcreek)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  
hellcreek_detect(struct hellcreek * hellcreek)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  
hellcreek_feature_detect(struct hellcreek * hellcreek)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  
hellcreek_get_tag_protocol(struct dsa_switch * ds,int port,enum dsa_tag_protocol mp)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  
hellcreek_port_enable(struct dsa_switch * ds,int port,struct phy_device * phy)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  
hellcreek_port_disable(struct dsa_switch * ds,int port)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  
hellcreek_get_strings(struct dsa_switch * ds,int port,u32 stringset,uint8_t * data)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  
hellcreek_get_sset_count(struct dsa_switch * ds,int port,int sset)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  
hellcreek_get_ethtool_stats(struct dsa_switch * ds,int port,uint64_t * data)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  
hellcreek_private_vid(int port)347  static u16 hellcreek_private_vid(int port)
348  {
349  	return VLAN_N_VID - port + 1;
350  }
351  
hellcreek_vlan_prepare(struct dsa_switch * ds,int port,const struct switchdev_obj_port_vlan * vlan,struct netlink_ext_ack * extack)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  
hellcreek_select_vlan_params(struct hellcreek * hellcreek,int port,int * shift,int * mask)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  
hellcreek_apply_vlan(struct hellcreek * hellcreek,int port,u16 vid,bool pvid,bool untagged)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  
hellcreek_unapply_vlan(struct hellcreek * hellcreek,int port,u16 vid)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  
hellcreek_vlan_add(struct dsa_switch * ds,int port,const struct switchdev_obj_port_vlan * vlan,struct netlink_ext_ack * extack)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  
hellcreek_vlan_del(struct dsa_switch * ds,int port,const struct switchdev_obj_port_vlan * vlan)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  
hellcreek_port_stp_state_set(struct dsa_switch * ds,int port,u8 state)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  
hellcreek_setup_ingressflt(struct hellcreek * hellcreek,int port,bool enable)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  
hellcreek_setup_vlan_awareness(struct hellcreek * hellcreek,bool enable)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. */
hellcreek_setup_vlan_membership(struct dsa_switch * ds,int port,bool enabled)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  
hellcreek_port_set_ucast_flood(struct hellcreek * hellcreek,int port,bool enable)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  
hellcreek_port_set_mcast_flood(struct hellcreek * hellcreek,int port,bool enable)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  
hellcreek_pre_bridge_flags(struct dsa_switch * ds,int port,struct switchdev_brport_flags flags,struct netlink_ext_ack * extack)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  
hellcreek_bridge_flags(struct dsa_switch * ds,int port,struct switchdev_brport_flags flags,struct netlink_ext_ack * extack)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  
hellcreek_port_bridge_join(struct dsa_switch * ds,int port,struct dsa_bridge bridge,bool * tx_fwd_offload,struct netlink_ext_ack * extack)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  
hellcreek_port_bridge_leave(struct dsa_switch * ds,int port,struct dsa_bridge bridge)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  
__hellcreek_fdb_add(struct hellcreek * hellcreek,const struct hellcreek_fdb_entry * entry)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  
__hellcreek_fdb_del(struct hellcreek * hellcreek,const struct hellcreek_fdb_entry * entry)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  
hellcreek_populate_fdb_entry(struct hellcreek * hellcreek,struct hellcreek_fdb_entry * entry,size_t idx)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   */
hellcreek_fdb_get(struct hellcreek * hellcreek,const unsigned char * dest,struct hellcreek_fdb_entry * entry)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  
hellcreek_fdb_add(struct dsa_switch * ds,int port,const unsigned char * addr,u16 vid,struct dsa_db db)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  
hellcreek_fdb_del(struct dsa_switch * ds,int port,const unsigned char * addr,u16 vid,struct dsa_db db)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  
hellcreek_fdb_dump(struct dsa_switch * ds,int port,dsa_fdb_dump_cb_t * cb,void * data)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  
hellcreek_vlan_filtering(struct dsa_switch * ds,int port,bool vlan_filtering,struct netlink_ext_ack * extack)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  
hellcreek_enable_ip_core(struct hellcreek * hellcreek)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  
hellcreek_setup_cpu_and_tunnel_port(struct hellcreek * hellcreek)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  
hellcreek_setup_tc_identity_mapping(struct hellcreek * hellcreek)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  
hellcreek_setup_fdb(struct hellcreek * hellcreek)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  
hellcreek_devlink_info_get(struct dsa_switch * ds,struct devlink_info_req * req,struct netlink_ext_ack * extack)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  
hellcreek_devlink_vlan_table_get(void * priv)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  
hellcreek_devlink_fdb_table_get(void * priv)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  
hellcreek_setup_devlink_resources(struct dsa_switch * ds)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  
hellcreek_devlink_region_vlan_snapshot(struct devlink * dl,const struct devlink_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)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  
hellcreek_devlink_region_fdb_snapshot(struct devlink * dl,const struct devlink_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)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  
hellcreek_setup_devlink_regions(struct dsa_switch * ds)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  
hellcreek_teardown_devlink_regions(struct dsa_switch * ds)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  
hellcreek_setup(struct dsa_switch * ds)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  
hellcreek_teardown(struct dsa_switch * ds)1462  static void hellcreek_teardown(struct dsa_switch *ds)
1463  {
1464  	hellcreek_teardown_devlink_regions(ds);
1465  	dsa_devlink_resources_unregister(ds);
1466  }
1467  
hellcreek_phylink_get_caps(struct dsa_switch * ds,int port,struct phylink_config * config)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
hellcreek_port_prechangeupper(struct dsa_switch * ds,int port,struct netdev_notifier_changeupper_info * info)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  
hellcreek_setup_maxsdu(struct hellcreek * hellcreek,int port,const struct tc_taprio_qopt_offload * schedule)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  
hellcreek_reset_maxsdu(struct hellcreek * hellcreek,int port)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  
hellcreek_setup_gcl(struct hellcreek * hellcreek,int port,const struct tc_taprio_qopt_offload * schedule)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  
hellcreek_set_cycle_time(struct hellcreek * hellcreek,const struct tc_taprio_qopt_offload * schedule)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  
hellcreek_switch_schedule(struct hellcreek * hellcreek,ktime_t start_time)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  
hellcreek_schedule_startable(struct hellcreek * hellcreek,int port)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  
hellcreek_start_schedule(struct hellcreek * hellcreek,int port)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  
hellcreek_check_schedule(struct work_struct * work)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  
hellcreek_port_set_schedule(struct dsa_switch * ds,int port,struct tc_taprio_qopt_offload * taprio)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  
hellcreek_port_del_schedule(struct dsa_switch * ds,int port)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  
hellcreek_validate_schedule(struct hellcreek * hellcreek,struct tc_taprio_qopt_offload * schedule)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  
hellcreek_tc_query_caps(struct tc_query_caps_base * base)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  
hellcreek_port_setup_tc(struct dsa_switch * ds,int port,enum tc_setup_type type,void * type_data)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  
hellcreek_probe(struct platform_device * pdev)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  
hellcreek_remove(struct platform_device * pdev)2063  static int hellcreek_remove(struct platform_device *pdev)
2064  {
2065  	struct hellcreek *hellcreek = platform_get_drvdata(pdev);
2066  
2067  	if (!hellcreek)
2068  		return 0;
2069  
2070  	hellcreek_hwtstamp_free(hellcreek);
2071  	hellcreek_ptp_free(hellcreek);
2072  	dsa_unregister_switch(hellcreek->ds);
2073  
2074  	return 0;
2075  }
2076  
hellcreek_shutdown(struct platform_device * pdev)2077  static void hellcreek_shutdown(struct platform_device *pdev)
2078  {
2079  	struct hellcreek *hellcreek = platform_get_drvdata(pdev);
2080  
2081  	if (!hellcreek)
2082  		return;
2083  
2084  	dsa_switch_shutdown(hellcreek->ds);
2085  
2086  	platform_set_drvdata(pdev, NULL);
2087  }
2088  
2089  static const struct hellcreek_platform_data de1soc_r1_pdata = {
2090  	.name		 = "r4c30",
2091  	.num_ports	 = 4,
2092  	.is_100_mbits	 = 1,
2093  	.qbv_support	 = 1,
2094  	.qbv_on_cpu_port = 1,
2095  	.qbu_support	 = 0,
2096  	.module_id	 = 0x4c30,
2097  };
2098  
2099  static const struct of_device_id hellcreek_of_match[] = {
2100  	{
2101  		.compatible = "hirschmann,hellcreek-de1soc-r1",
2102  		.data	    = &de1soc_r1_pdata,
2103  	},
2104  	{ /* sentinel */ },
2105  };
2106  MODULE_DEVICE_TABLE(of, hellcreek_of_match);
2107  
2108  static struct platform_driver hellcreek_driver = {
2109  	.probe	= hellcreek_probe,
2110  	.remove = hellcreek_remove,
2111  	.shutdown = hellcreek_shutdown,
2112  	.driver = {
2113  		.name = "hellcreek",
2114  		.of_match_table = hellcreek_of_match,
2115  	},
2116  };
2117  module_platform_driver(hellcreek_driver);
2118  
2119  MODULE_AUTHOR("Kurt Kanzenbach <kurt@linutronix.de>");
2120  MODULE_DESCRIPTION("Hirschmann Hellcreek driver");
2121  MODULE_LICENSE("Dual MIT/GPL");
2122