1 /* 2 * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support 3 * 4 * Copyright (c) 2008 Marvell Semiconductor 5 * Copyright (c) 2015 CMC Electronics, Inc. 6 * Copyright (c) 2017 Savoir-faire Linux, Inc. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 */ 13 14 #include "mv88e6xxx.h" 15 #include "global1.h" 16 17 /* Offset 0x02: VTU FID Register */ 18 19 static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, 20 struct mv88e6xxx_vtu_entry *entry) 21 { 22 u16 val; 23 int err; 24 25 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val); 26 if (err) 27 return err; 28 29 entry->fid = val & GLOBAL_VTU_FID_MASK; 30 31 return 0; 32 } 33 34 static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, 35 struct mv88e6xxx_vtu_entry *entry) 36 { 37 u16 val = entry->fid & GLOBAL_VTU_FID_MASK; 38 39 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, val); 40 } 41 42 /* Offset 0x03: VTU SID Register */ 43 44 static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, 45 struct mv88e6xxx_vtu_entry *entry) 46 { 47 u16 val; 48 int err; 49 50 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val); 51 if (err) 52 return err; 53 54 entry->sid = val & GLOBAL_VTU_SID_MASK; 55 56 return 0; 57 } 58 59 static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, 60 struct mv88e6xxx_vtu_entry *entry) 61 { 62 u16 val = entry->sid & GLOBAL_VTU_SID_MASK; 63 64 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, val); 65 } 66 67 /* Offset 0x05: VTU Operation Register */ 68 69 static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) 70 { 71 return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY); 72 } 73 74 static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) 75 { 76 int err; 77 78 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op); 79 if (err) 80 return err; 81 82 return mv88e6xxx_g1_vtu_op_wait(chip); 83 } 84 85 /* Offset 0x06: VTU VID Register */ 86 87 static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, 88 struct mv88e6xxx_vtu_entry *entry) 89 { 90 u16 val; 91 int err; 92 93 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val); 94 if (err) 95 return err; 96 97 entry->vid = val & 0xfff; 98 99 if (val & GLOBAL_VTU_VID_PAGE) 100 entry->vid |= 0x1000; 101 102 entry->valid = !!(val & GLOBAL_VTU_VID_VALID); 103 104 return 0; 105 } 106 107 static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, 108 struct mv88e6xxx_vtu_entry *entry) 109 { 110 u16 val = entry->vid & 0xfff; 111 112 if (entry->vid & 0x1000) 113 val |= GLOBAL_VTU_VID_PAGE; 114 115 if (entry->valid) 116 val |= GLOBAL_VTU_VID_VALID; 117 118 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, val); 119 } 120 121 /* Offset 0x07: VTU/STU Data Register 1 122 * Offset 0x08: VTU/STU Data Register 2 123 * Offset 0x09: VTU/STU Data Register 3 124 */ 125 126 static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, 127 struct mv88e6xxx_vtu_entry *entry) 128 { 129 u16 regs[3]; 130 int i; 131 132 /* Read all 3 VTU/STU Data registers */ 133 for (i = 0; i < 3; ++i) { 134 u16 *reg = ®s[i]; 135 int err; 136 137 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg); 138 if (err) 139 return err; 140 } 141 142 /* Extract MemberTag and PortState data */ 143 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 144 unsigned int member_offset = (i % 4) * 4; 145 unsigned int state_offset = member_offset + 2; 146 147 entry->member[i] = (regs[i / 4] >> member_offset) & 0x3; 148 entry->state[i] = (regs[i / 4] >> state_offset) & 0x3; 149 } 150 151 return 0; 152 } 153 154 static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, 155 struct mv88e6xxx_vtu_entry *entry) 156 { 157 u16 regs[3] = { 0 }; 158 int i; 159 160 /* Insert MemberTag and PortState data */ 161 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 162 unsigned int member_offset = (i % 4) * 4; 163 unsigned int state_offset = member_offset + 2; 164 165 regs[i / 4] |= (entry->member[i] & 0x3) << member_offset; 166 regs[i / 4] |= (entry->state[i] & 0x3) << state_offset; 167 } 168 169 /* Write all 3 VTU/STU Data registers */ 170 for (i = 0; i < 3; ++i) { 171 u16 reg = regs[i]; 172 int err; 173 174 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg); 175 if (err) 176 return err; 177 } 178 179 return 0; 180 } 181 182 static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data) 183 { 184 u16 regs[2]; 185 int i; 186 187 /* Read the 2 VTU/STU Data registers */ 188 for (i = 0; i < 2; ++i) { 189 u16 *reg = ®s[i]; 190 int err; 191 192 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg); 193 if (err) 194 return err; 195 } 196 197 /* Extract data */ 198 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 199 unsigned int offset = (i % 8) * 2; 200 201 data[i] = (regs[i / 8] >> offset) & 0x3; 202 } 203 204 return 0; 205 } 206 207 static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data) 208 { 209 u16 regs[2] = { 0 }; 210 int i; 211 212 /* Insert data */ 213 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { 214 unsigned int offset = (i % 8) * 2; 215 216 regs[i / 8] |= (data[i] & 0x3) << offset; 217 } 218 219 /* Write the 2 VTU/STU Data registers */ 220 for (i = 0; i < 2; ++i) { 221 u16 reg = regs[i]; 222 int err; 223 224 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg); 225 if (err) 226 return err; 227 } 228 229 return 0; 230 } 231 232 /* VLAN Translation Unit Operations */ 233 234 static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, 235 struct mv88e6xxx_vtu_entry *entry) 236 { 237 int err; 238 239 err = mv88e6xxx_g1_vtu_sid_write(chip, entry); 240 if (err) 241 return err; 242 243 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_GET_NEXT); 244 if (err) 245 return err; 246 247 err = mv88e6xxx_g1_vtu_sid_read(chip, entry); 248 if (err) 249 return err; 250 251 return mv88e6xxx_g1_vtu_vid_read(chip, entry); 252 } 253 254 static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, 255 struct mv88e6xxx_vtu_entry *vtu) 256 { 257 struct mv88e6xxx_vtu_entry stu; 258 int err; 259 260 err = mv88e6xxx_g1_vtu_sid_read(chip, vtu); 261 if (err) 262 return err; 263 264 stu.sid = vtu->sid - 1; 265 266 err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu); 267 if (err) 268 return err; 269 270 if (stu.sid != vtu->sid || !stu.valid) 271 return -EINVAL; 272 273 return 0; 274 } 275 276 static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, 277 struct mv88e6xxx_vtu_entry *entry) 278 { 279 int err; 280 281 err = mv88e6xxx_g1_vtu_op_wait(chip); 282 if (err) 283 return err; 284 285 /* To get the next higher active VID, the VTU GetNext operation can be 286 * started again without setting the VID registers since it already 287 * contains the last VID. 288 * 289 * To save a few hardware accesses and abstract this to the caller, 290 * write the VID only once, when the entry is given as invalid. 291 */ 292 if (!entry->valid) { 293 err = mv88e6xxx_g1_vtu_vid_write(chip, entry); 294 if (err) 295 return err; 296 } 297 298 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_GET_NEXT); 299 if (err) 300 return err; 301 302 return mv88e6xxx_g1_vtu_vid_read(chip, entry); 303 } 304 305 int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, 306 struct mv88e6xxx_vtu_entry *entry) 307 { 308 u16 val; 309 int err; 310 311 err = mv88e6xxx_g1_vtu_getnext(chip, entry); 312 if (err) 313 return err; 314 315 if (entry->valid) { 316 err = mv88e6185_g1_vtu_data_read(chip, entry); 317 if (err) 318 return err; 319 320 /* VTU DBNum[3:0] are located in VTU Operation 3:0 321 * VTU DBNum[7:4] are located in VTU Operation 11:8 322 */ 323 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val); 324 if (err) 325 return err; 326 327 entry->fid = val & 0x000f; 328 entry->fid |= (val & 0x0f00) >> 4; 329 } 330 331 return 0; 332 } 333 334 int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, 335 struct mv88e6xxx_vtu_entry *entry) 336 { 337 int err; 338 339 /* Fetch VLAN MemberTag data from the VTU */ 340 err = mv88e6xxx_g1_vtu_getnext(chip, entry); 341 if (err) 342 return err; 343 344 if (entry->valid) { 345 /* Fetch (and mask) VLAN PortState data from the STU */ 346 err = mv88e6xxx_g1_vtu_stu_get(chip, entry); 347 if (err) 348 return err; 349 350 err = mv88e6185_g1_vtu_data_read(chip, entry); 351 if (err) 352 return err; 353 354 err = mv88e6xxx_g1_vtu_fid_read(chip, entry); 355 if (err) 356 return err; 357 } 358 359 return 0; 360 } 361 362 int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, 363 struct mv88e6xxx_vtu_entry *entry) 364 { 365 int err; 366 367 /* Fetch VLAN MemberTag data from the VTU */ 368 err = mv88e6xxx_g1_vtu_getnext(chip, entry); 369 if (err) 370 return err; 371 372 if (entry->valid) { 373 err = mv88e6390_g1_vtu_data_read(chip, entry->member); 374 if (err) 375 return err; 376 377 /* Fetch VLAN PortState data from the STU */ 378 err = mv88e6xxx_g1_vtu_stu_get(chip, entry); 379 if (err) 380 return err; 381 382 err = mv88e6390_g1_vtu_data_read(chip, entry->state); 383 if (err) 384 return err; 385 386 err = mv88e6xxx_g1_vtu_fid_read(chip, entry); 387 if (err) 388 return err; 389 } 390 391 return 0; 392 } 393 394 int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, 395 struct mv88e6xxx_vtu_entry *entry) 396 { 397 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE; 398 int err; 399 400 err = mv88e6xxx_g1_vtu_op_wait(chip); 401 if (err) 402 return err; 403 404 err = mv88e6xxx_g1_vtu_vid_write(chip, entry); 405 if (err) 406 return err; 407 408 if (entry->valid) { 409 err = mv88e6185_g1_vtu_data_write(chip, entry); 410 if (err) 411 return err; 412 413 /* VTU DBNum[3:0] are located in VTU Operation 3:0 414 * VTU DBNum[7:4] are located in VTU Operation 11:8 415 */ 416 op |= entry->fid & 0x000f; 417 op |= (entry->fid & 0x00f0) << 8; 418 } 419 420 return mv88e6xxx_g1_vtu_op(chip, op); 421 } 422 423 int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, 424 struct mv88e6xxx_vtu_entry *entry) 425 { 426 int err; 427 428 err = mv88e6xxx_g1_vtu_op_wait(chip); 429 if (err) 430 return err; 431 432 err = mv88e6xxx_g1_vtu_vid_write(chip, entry); 433 if (err) 434 return err; 435 436 if (entry->valid) { 437 /* Write MemberTag and PortState data */ 438 err = mv88e6185_g1_vtu_data_write(chip, entry); 439 if (err) 440 return err; 441 442 err = mv88e6xxx_g1_vtu_sid_write(chip, entry); 443 if (err) 444 return err; 445 446 /* Load STU entry */ 447 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); 448 if (err) 449 return err; 450 451 err = mv88e6xxx_g1_vtu_fid_write(chip, entry); 452 if (err) 453 return err; 454 } 455 456 /* Load/Purge VTU entry */ 457 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE); 458 } 459 460 int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, 461 struct mv88e6xxx_vtu_entry *entry) 462 { 463 int err; 464 465 err = mv88e6xxx_g1_vtu_op_wait(chip); 466 if (err) 467 return err; 468 469 err = mv88e6xxx_g1_vtu_vid_write(chip, entry); 470 if (err) 471 return err; 472 473 if (entry->valid) { 474 /* Write PortState data */ 475 err = mv88e6390_g1_vtu_data_write(chip, entry->state); 476 if (err) 477 return err; 478 479 err = mv88e6xxx_g1_vtu_sid_write(chip, entry); 480 if (err) 481 return err; 482 483 /* Load STU entry */ 484 err = mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE); 485 if (err) 486 return err; 487 488 /* Write MemberTag data */ 489 err = mv88e6390_g1_vtu_data_write(chip, entry->member); 490 if (err) 491 return err; 492 493 err = mv88e6xxx_g1_vtu_fid_write(chip, entry); 494 if (err) 495 return err; 496 } 497 498 /* Load/Purge VTU entry */ 499 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_VTU_LOAD_PURGE); 500 } 501 502 int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) 503 { 504 int err; 505 506 err = mv88e6xxx_g1_vtu_op_wait(chip); 507 if (err) 508 return err; 509 510 return mv88e6xxx_g1_vtu_op(chip, GLOBAL_VTU_OP_FLUSH_ALL); 511 } 512