1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /*
3  * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable
4  * switches (VSC9959)
5  *
6  * Copyright 2022-2023 NXP
7  */
8 #include <linux/ethtool.h>
9 #include <soc/mscc/ocelot.h>
10 #include <soc/mscc/ocelot_dev.h>
11 #include <soc/mscc/ocelot_qsys.h>
12 
13 #include "ocelot.h"
14 
15 static const char *
16 mm_verify_state_to_string(enum ethtool_mm_verify_status state)
17 {
18 	switch (state) {
19 	case ETHTOOL_MM_VERIFY_STATUS_INITIAL:
20 		return "INITIAL";
21 	case ETHTOOL_MM_VERIFY_STATUS_VERIFYING:
22 		return "VERIFYING";
23 	case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED:
24 		return "SUCCEEDED";
25 	case ETHTOOL_MM_VERIFY_STATUS_FAILED:
26 		return "FAILED";
27 	case ETHTOOL_MM_VERIFY_STATUS_DISABLED:
28 		return "DISABLED";
29 	default:
30 		return "UNKNOWN";
31 	}
32 }
33 
34 static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val)
35 {
36 	switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) {
37 	case 0:
38 		return ETHTOOL_MM_VERIFY_STATUS_INITIAL;
39 	case 1:
40 		return ETHTOOL_MM_VERIFY_STATUS_VERIFYING;
41 	case 2:
42 		return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED;
43 	case 3:
44 		return ETHTOOL_MM_VERIFY_STATUS_FAILED;
45 	case 4:
46 		return ETHTOOL_MM_VERIFY_STATUS_DISABLED;
47 	default:
48 		return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN;
49 	}
50 }
51 
52 void ocelot_port_mm_irq(struct ocelot *ocelot, int port)
53 {
54 	struct ocelot_port *ocelot_port = ocelot->ports[port];
55 	struct ocelot_mm_state *mm = &ocelot->mm[port];
56 	enum ethtool_mm_verify_status verify_status;
57 	u32 val;
58 
59 	mutex_lock(&mm->lock);
60 
61 	val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
62 
63 	verify_status = ocelot_mm_verify_status(val);
64 	if (mm->verify_status != verify_status) {
65 		dev_dbg(ocelot->dev,
66 			"Port %d MAC Merge verification state %s\n",
67 			port, mm_verify_state_to_string(verify_status));
68 		mm->verify_status = verify_status;
69 	}
70 
71 	if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) {
72 		mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS);
73 
74 		dev_dbg(ocelot->dev, "Port %d TX preemption %s\n",
75 			port, mm->tx_active ? "active" : "inactive");
76 	}
77 
78 	if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) {
79 		dev_err(ocelot->dev,
80 			"Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n",
81 			port);
82 	}
83 
84 	if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) {
85 		dev_err(ocelot->dev,
86 			"Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n",
87 			port);
88 	}
89 
90 	ocelot_port_writel(ocelot_port, val, DEV_MM_STATUS);
91 
92 	mutex_unlock(&mm->lock);
93 }
94 EXPORT_SYMBOL_GPL(ocelot_port_mm_irq);
95 
96 int ocelot_port_set_mm(struct ocelot *ocelot, int port,
97 		       struct ethtool_mm_cfg *cfg,
98 		       struct netlink_ext_ack *extack)
99 {
100 	struct ocelot_port *ocelot_port = ocelot->ports[port];
101 	u32 mm_enable = 0, verify_disable = 0, add_frag_size;
102 	struct ocelot_mm_state *mm;
103 	int err;
104 
105 	if (!ocelot->mm_supported)
106 		return -EOPNOTSUPP;
107 
108 	mm = &ocelot->mm[port];
109 
110 	err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size,
111 					      &add_frag_size, extack);
112 	if (err)
113 		return err;
114 
115 	if (cfg->pmac_enabled)
116 		mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA;
117 
118 	if (cfg->tx_enabled)
119 		mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA;
120 
121 	if (!cfg->verify_enabled)
122 		verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS;
123 
124 	mutex_lock(&mm->lock);
125 
126 	ocelot_port_rmwl(ocelot_port, mm_enable,
127 			 DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA |
128 			 DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA,
129 			 DEV_MM_ENABLE_CONFIG);
130 
131 	ocelot_port_rmwl(ocelot_port, verify_disable |
132 			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time),
133 			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS |
134 			 DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M,
135 			 DEV_MM_VERIF_CONFIG);
136 
137 	ocelot_rmw_rix(ocelot,
138 		       QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size),
139 		       QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M,
140 		       QSYS_PREEMPTION_CFG,
141 		       port);
142 
143 	mutex_unlock(&mm->lock);
144 
145 	return 0;
146 }
147 EXPORT_SYMBOL_GPL(ocelot_port_set_mm);
148 
149 int ocelot_port_get_mm(struct ocelot *ocelot, int port,
150 		       struct ethtool_mm_state *state)
151 {
152 	struct ocelot_port *ocelot_port = ocelot->ports[port];
153 	struct ocelot_mm_state *mm;
154 	u32 val, add_frag_size;
155 
156 	if (!ocelot->mm_supported)
157 		return -EOPNOTSUPP;
158 
159 	mm = &ocelot->mm[port];
160 
161 	mutex_lock(&mm->lock);
162 
163 	val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG);
164 	state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA);
165 	state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA);
166 
167 	val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG);
168 	state->verify_enabled = !(val & DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS);
169 	state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val);
170 	state->max_verify_time = 128;
171 
172 	val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port);
173 	add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val);
174 	state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size);
175 	state->rx_min_frag_size = ETH_ZLEN;
176 
177 	state->verify_status = mm->verify_status;
178 	state->tx_active = mm->tx_active;
179 
180 	mutex_unlock(&mm->lock);
181 
182 	return 0;
183 }
184 EXPORT_SYMBOL_GPL(ocelot_port_get_mm);
185 
186 int ocelot_mm_init(struct ocelot *ocelot)
187 {
188 	struct ocelot_port *ocelot_port;
189 	struct ocelot_mm_state *mm;
190 	int port;
191 
192 	if (!ocelot->mm_supported)
193 		return 0;
194 
195 	ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
196 				  sizeof(*ocelot->mm), GFP_KERNEL);
197 	if (!ocelot->mm)
198 		return -ENOMEM;
199 
200 	for (port = 0; port < ocelot->num_phys_ports; port++) {
201 		u32 val;
202 
203 		mm = &ocelot->mm[port];
204 		mutex_init(&mm->lock);
205 		ocelot_port = ocelot->ports[port];
206 
207 		/* Update initial status variable for the
208 		 * verification state machine
209 		 */
210 		val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS);
211 		mm->verify_status = ocelot_mm_verify_status(val);
212 	}
213 
214 	return 0;
215 }
216