cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hellcreek.c (53070B)


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