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