xref: /linux/drivers/net/dsa/mv88e6xxx/tcam.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Marvell 88E6xxx Switch TCAM support
4  *
5  * Copyright (c) 2026 Luminex Network Intelligence
6  */
7 
8 #include "linux/list.h"
9 
10 #include "chip.h"
11 #include "tcam.h"
12 
13 /* TCAM operatation register */
14 #define MV88E6XXX_TCAM_OP			0x00
15 #define MV88E6XXX_TCAM_OP_BUSY			0x8000
16 #define MV88E6XXX_TCAM_OP_OP_MASK		0x7000
17 #define MV88E6XXX_TCAM_OP_OP_FLUSH_ALL		0x1000
18 #define MV88E6XXX_TCAM_OP_OP_FLUSH		0x2000
19 #define MV88E6XXX_TCAM_OP_OP_LOAD		0x3000
20 #define MV88E6XXX_TCAM_OP_OP_GET_NEXT		0x4000
21 #define MV88E6XXX_TCAM_OP_OP_READ		0x5000
22 
23 /* TCAM extension register */
24 #define MV88E6XXX_TCAM_EXTENSION		0x01
25 
26 /* TCAM keys register 1 */
27 #define MV88E6XXX_TCAM_KEYS1			0x02
28 #define MV88E6XXX_TCAM_KEYS1_FT_MASK		0xC000
29 #define MV88E6XXX_TCAM_KEYS1_SPV_MASK		0x0007
30 #define MV88E6XXX_TCAM_KEYS1_SPV_MASK_MASK	0x0700
31 
32 /* TCAM keys register 2 */
33 #define MV88E6XXX_TCAM_KEYS2			0x03
34 #define MV88E6XXX_TCAM_KEYS2_SPV_MASK		0x00ff
35 #define MV88E6XXX_TCAM_KEYS2_SPV_MASK_MASK	0xff00
36 
37 #define MV88E6XXX_TCAM_ING_ACT3			0x04
38 #define MV88E6XXX_TCAM_ING_ACT3_SF		0x0800
39 #define MV88E6XXX_TCAM_ING_ACT3_DPV_MASK	0x07ff
40 
41 #define MV88E6XXX_TCAM_ING_ACT5			0x06
42 #define MV88E6XXX_TCAM_ING_ACT5_DPV_MODE_MASK	0xc000
43 
44 static int mv88e6xxx_tcam_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
45 {
46 	return mv88e6xxx_write(chip, chip->info->tcam_addr, reg, val);
47 }
48 
49 static int mv88e6xxx_tcam_wait(struct mv88e6xxx_chip *chip)
50 {
51 	int bit = __bf_shf(MV88E6XXX_TCAM_OP_BUSY);
52 
53 	return mv88e6xxx_wait_bit(chip, chip->info->tcam_addr,
54 				  MV88E6XXX_TCAM_OP, bit, 0);
55 }
56 
57 static int mv88e6xxx_tcam_read_page(struct mv88e6xxx_chip *chip, u8 page,
58 				    u8 entry)
59 
60 {
61 	u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_READ |
62 		  (page & 0x3) << 10 | entry;
63 	int err;
64 
65 	err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val);
66 	if (err)
67 		return err;
68 
69 	return mv88e6xxx_tcam_wait(chip);
70 }
71 
72 static int mv88e6xxx_tcam_load_page(struct mv88e6xxx_chip *chip, u8 page,
73 				    u8 entry)
74 {
75 	u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_LOAD |
76 		  (page & 0x3) << 10 | entry;
77 	int err;
78 
79 	err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val);
80 	if (err)
81 		return err;
82 
83 	return mv88e6xxx_tcam_wait(chip);
84 }
85 
86 static int mv88e6xxx_tcam_flush_entry(struct mv88e6xxx_chip *chip, u8 entry)
87 {
88 	u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_FLUSH | entry;
89 	int err;
90 
91 	err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val);
92 	if (err)
93 		return err;
94 
95 	return mv88e6xxx_tcam_wait(chip);
96 }
97 
98 static int mv88e6xxx_tcam_flush_all(struct mv88e6xxx_chip *chip)
99 {
100 	u16 val = MV88E6XXX_TCAM_OP_BUSY | MV88E6XXX_TCAM_OP_OP_FLUSH_ALL;
101 	int err;
102 
103 	err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_OP, val);
104 	if (err)
105 		return err;
106 
107 	return mv88e6xxx_tcam_wait(chip);
108 }
109 
110 struct mv88e6xxx_tcam_entry *
111 mv88e6xxx_tcam_entry_find(struct mv88e6xxx_chip *chip, unsigned long cookie)
112 {
113 	struct mv88e6xxx_tcam_entry *entry;
114 
115 	list_for_each_entry(entry, &chip->tcam.entries, list)
116 		if (entry->cookie == cookie)
117 			return entry;
118 
119 	return NULL;
120 }
121 
122 /* insert tcam entry in ordered list and move existing entries if necessary */
123 static int mv88e6xxx_tcam_insert_entry(struct mv88e6xxx_chip *chip,
124 				       struct mv88e6xxx_tcam_entry *entry)
125 {
126 	struct mv88e6xxx_tcam_entry *elem;
127 	struct list_head *hpos;
128 	int err;
129 
130 	list_for_each_prev(hpos, &chip->tcam.entries) {
131 		u8 move_idx;
132 
133 		elem = list_entry(hpos, struct mv88e6xxx_tcam_entry, list);
134 		if (entry->prio >= elem->prio)
135 			break;
136 
137 		move_idx = elem->hw_idx + 1;
138 
139 		err = mv88e6xxx_tcam_flush_entry(chip, move_idx);
140 		if (err)
141 			return err;
142 
143 		err = chip->info->ops->tcam_ops->entry_add(chip, elem,
144 							   move_idx);
145 		if (err)
146 			return err;
147 
148 		elem->hw_idx = move_idx;
149 	}
150 
151 	if (list_is_head(hpos, &chip->tcam.entries)) {
152 		entry->hw_idx = 0;
153 	} else {
154 		elem = list_entry(hpos, struct mv88e6xxx_tcam_entry, list);
155 		entry->hw_idx = elem->hw_idx + 1;
156 	}
157 	list_add(&entry->list, hpos);
158 	return 0;
159 }
160 
161 int mv88e6xxx_tcam_entry_add(struct mv88e6xxx_chip *chip,
162 			     struct mv88e6xxx_tcam_entry *entry)
163 {
164 	int err;
165 	struct mv88e6xxx_tcam_entry *last;
166 
167 	last = list_last_entry_or_null(&chip->tcam.entries,
168 				       struct mv88e6xxx_tcam_entry, list);
169 	if (last && last->hw_idx == chip->info->num_tcam_entries - 1)
170 		return -ENOSPC;
171 
172 	err = mv88e6xxx_tcam_insert_entry(chip, entry);
173 	if (err)
174 		return err;
175 
176 	err = mv88e6xxx_tcam_flush_entry(chip, entry->hw_idx);
177 	if (err)
178 		goto unlink_out;
179 
180 	err = chip->info->ops->tcam_ops->entry_add(chip, entry, entry->hw_idx);
181 	if (err)
182 		goto unlink_out;
183 
184 	return 0;
185 
186 unlink_out:
187 	list_del(&entry->list);
188 	return err;
189 }
190 
191 int mv88e6xxx_tcam_entry_del(struct mv88e6xxx_chip *chip,
192 			     struct mv88e6xxx_tcam_entry *entry)
193 {
194 	struct mv88e6xxx_tcam_entry *elem = entry;
195 	u8 move_idx = entry->hw_idx;
196 	int err;
197 
198 	err = mv88e6xxx_tcam_flush_entry(chip, entry->hw_idx);
199 	if (err)
200 		return err;
201 
202 	/* move entries that come after the deleted entry forward */
203 	list_for_each_entry_continue(elem, &chip->tcam.entries, list) {
204 		u8 tmp_idx = elem->hw_idx;
205 
206 		err = mv88e6xxx_tcam_flush_entry(chip, move_idx);
207 		if (err)
208 			break;
209 
210 		err = chip->info->ops->tcam_ops->entry_add(chip, elem,
211 							   move_idx);
212 		if (err)
213 			break;
214 
215 		elem->hw_idx =  move_idx;
216 		move_idx = tmp_idx;
217 
218 		/* flush the last entry after moving entries */
219 		if (list_is_last(&elem->list, &chip->tcam.entries))
220 			err = mv88e6xxx_tcam_flush_entry(chip, tmp_idx);
221 	}
222 
223 	list_del(&entry->list);
224 	kfree(entry);
225 	return err;
226 }
227 
228 static int mv88e6390_tcam_entry_add(struct mv88e6xxx_chip *chip,
229 				    struct mv88e6xxx_tcam_entry *entry, u8 idx)
230 {
231 	int err = 0;
232 	int i;
233 	u16 val, spv_mask, spv;
234 
235 	err = mv88e6xxx_tcam_read_page(chip, 2, idx);
236 	if (err)
237 		return err;
238 	if (entry->action.dpv_mode != 0) {
239 		val = MV88E6XXX_TCAM_ING_ACT3_SF |
240 		      (entry->action.dpv & MV88E6XXX_TCAM_ING_ACT3_DPV_MASK);
241 
242 		err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_ING_ACT3, val);
243 		if (err)
244 			return err;
245 
246 		val = entry->action.dpv_mode << 14;
247 		err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_ING_ACT5, val);
248 		if (err)
249 			return err;
250 	}
251 	err = mv88e6xxx_tcam_load_page(chip, 2, idx);
252 	if (err)
253 		return err;
254 
255 	err = mv88e6xxx_tcam_read_page(chip, 1, idx);
256 	if (err)
257 		return err;
258 
259 	for (i = PAGE0_MATCH_SIZE;
260 	     i < PAGE0_MATCH_SIZE + PAGE1_MATCH_SIZE; i++) {
261 		if (entry->key.frame_mask[i]) {
262 			val = entry->key.frame_mask[i] << 8 |
263 			      entry->key.frame_data[i];
264 
265 			err = mv88e6xxx_tcam_write(chip,
266 						   i - PAGE0_MATCH_SIZE + 2,
267 						   val);
268 			if (err)
269 				return err;
270 		}
271 	}
272 	err = mv88e6xxx_tcam_load_page(chip, 1, idx);
273 	if (err)
274 		return err;
275 
276 	err = mv88e6xxx_tcam_read_page(chip, 0, idx);
277 	if (err)
278 		return err;
279 
280 	for (i = 0; i < PAGE0_MATCH_SIZE; i++) {
281 		if (entry->key.frame_mask[i]) {
282 			val = entry->key.frame_mask[i] << 8 |
283 			      entry->key.frame_data[i];
284 
285 			err = mv88e6xxx_tcam_write(chip, i + 6, val);
286 			if (err)
287 				return err;
288 		}
289 	}
290 
291 	spv_mask = entry->key.spv_mask & mv88e6xxx_port_mask(chip);
292 	spv = entry->key.spv & mv88e6xxx_port_mask(chip);
293 	/* frame type mask bits must be set for a valid entry */
294 	val = MV88E6XXX_TCAM_KEYS1_FT_MASK |
295 	      (spv_mask & MV88E6XXX_TCAM_KEYS1_SPV_MASK_MASK) |
296 	      ((spv >> 8) & MV88E6XXX_TCAM_KEYS1_SPV_MASK);
297 	err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_KEYS1, val);
298 	if (err)
299 		return err;
300 
301 	val = ((spv_mask << 8) & MV88E6XXX_TCAM_KEYS2_SPV_MASK_MASK) |
302 	      (spv & MV88E6XXX_TCAM_KEYS2_SPV_MASK);
303 	err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_KEYS2, val);
304 	if (err)
305 		return err;
306 
307 	err = mv88e6xxx_tcam_load_page(chip, 0, idx);
308 	if (err)
309 		return err;
310 
311 	entry->hw_idx = idx;
312 	return 0;
313 }
314 
315 static int mv88e6393_tcam_entry_add(struct mv88e6xxx_chip *chip,
316 				    struct mv88e6xxx_tcam_entry *entry, u8 idx)
317 {
318 	int err;
319 
320 	/* select block 0 port 0, then adding an entry is the same as 6390 as
321 	 * other blocks aren't used at the moment
322 	 */
323 	err = mv88e6xxx_tcam_write(chip, MV88E6XXX_TCAM_EXTENSION, 0x00);
324 	if (err)
325 		return err;
326 
327 	return mv88e6390_tcam_entry_add(chip, entry, idx);
328 }
329 
330 const struct mv88e6xxx_tcam_ops mv88e6390_tcam_ops = {
331 	.entry_add = mv88e6390_tcam_entry_add,
332 	.flush_tcam = mv88e6xxx_tcam_flush_all,
333 };
334 
335 const struct mv88e6xxx_tcam_ops mv88e6393_tcam_ops = {
336 	.entry_add = mv88e6393_tcam_entry_add,
337 	.flush_tcam = mv88e6xxx_tcam_flush_all,
338 };
339