| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Marvell 88E6xxx Switch Port Registers support |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 5 | 6 | * |
|---|
| 6 | 7 | * Copyright (c) 2016-2017 Savoir-faire Linux Inc. |
|---|
| 7 | 8 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 10 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 11 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 12 | | - * (at your option) any later version. |
|---|
| 13 | 9 | */ |
|---|
| 14 | 10 | |
|---|
| 15 | 11 | #include <linux/bitfield.h> |
|---|
| .. | .. |
|---|
| 166 | 162 | return 0; |
|---|
| 167 | 163 | } |
|---|
| 168 | 164 | |
|---|
| 169 | | -int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup) |
|---|
| 170 | | -{ |
|---|
| 171 | | - u16 reg; |
|---|
| 172 | | - int err; |
|---|
| 173 | | - |
|---|
| 174 | | - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); |
|---|
| 175 | | - if (err) |
|---|
| 176 | | - return err; |
|---|
| 177 | | - |
|---|
| 178 | | - reg &= ~(MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | |
|---|
| 179 | | - MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); |
|---|
| 180 | | - |
|---|
| 181 | | - switch (dup) { |
|---|
| 182 | | - case DUPLEX_HALF: |
|---|
| 183 | | - reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; |
|---|
| 184 | | - break; |
|---|
| 185 | | - case DUPLEX_FULL: |
|---|
| 186 | | - reg |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | |
|---|
| 187 | | - MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; |
|---|
| 188 | | - break; |
|---|
| 189 | | - case DUPLEX_UNFORCED: |
|---|
| 190 | | - /* normal duplex detection */ |
|---|
| 191 | | - break; |
|---|
| 192 | | - default: |
|---|
| 193 | | - return -EOPNOTSUPP; |
|---|
| 194 | | - } |
|---|
| 195 | | - |
|---|
| 196 | | - err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_MAC_CTL, reg); |
|---|
| 197 | | - if (err) |
|---|
| 198 | | - return err; |
|---|
| 199 | | - |
|---|
| 200 | | - dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, |
|---|
| 201 | | - reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", |
|---|
| 202 | | - reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); |
|---|
| 203 | | - |
|---|
| 204 | | - return 0; |
|---|
| 205 | | -} |
|---|
| 206 | | - |
|---|
| 207 | | -static int mv88e6xxx_port_set_speed(struct mv88e6xxx_chip *chip, int port, |
|---|
| 208 | | - int speed, bool alt_bit, bool force_bit) |
|---|
| 165 | +static int mv88e6xxx_port_set_speed_duplex(struct mv88e6xxx_chip *chip, |
|---|
| 166 | + int port, int speed, bool alt_bit, |
|---|
| 167 | + bool force_bit, int duplex) |
|---|
| 209 | 168 | { |
|---|
| 210 | 169 | u16 reg, ctrl; |
|---|
| 211 | 170 | int err; |
|---|
| .. | .. |
|---|
| 243 | 202 | return -EOPNOTSUPP; |
|---|
| 244 | 203 | } |
|---|
| 245 | 204 | |
|---|
| 205 | + switch (duplex) { |
|---|
| 206 | + case DUPLEX_HALF: |
|---|
| 207 | + ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX; |
|---|
| 208 | + break; |
|---|
| 209 | + case DUPLEX_FULL: |
|---|
| 210 | + ctrl |= MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | |
|---|
| 211 | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL; |
|---|
| 212 | + break; |
|---|
| 213 | + case DUPLEX_UNFORCED: |
|---|
| 214 | + /* normal duplex detection */ |
|---|
| 215 | + break; |
|---|
| 216 | + default: |
|---|
| 217 | + return -EOPNOTSUPP; |
|---|
| 218 | + } |
|---|
| 219 | + |
|---|
| 246 | 220 | err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_MAC_CTL, ®); |
|---|
| 247 | 221 | if (err) |
|---|
| 248 | 222 | return err; |
|---|
| 249 | 223 | |
|---|
| 250 | | - reg &= ~MV88E6XXX_PORT_MAC_CTL_SPEED_MASK; |
|---|
| 224 | + reg &= ~(MV88E6XXX_PORT_MAC_CTL_SPEED_MASK | |
|---|
| 225 | + MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX | |
|---|
| 226 | + MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL); |
|---|
| 227 | + |
|---|
| 251 | 228 | if (alt_bit) |
|---|
| 252 | 229 | reg &= ~MV88E6390_PORT_MAC_CTL_ALTSPEED; |
|---|
| 253 | 230 | if (force_bit) { |
|---|
| .. | .. |
|---|
| 265 | 242 | dev_dbg(chip->dev, "p%d: Speed set to %d Mbps\n", port, speed); |
|---|
| 266 | 243 | else |
|---|
| 267 | 244 | dev_dbg(chip->dev, "p%d: Speed unforced\n", port); |
|---|
| 245 | + dev_dbg(chip->dev, "p%d: %s %s duplex\n", port, |
|---|
| 246 | + reg & MV88E6XXX_PORT_MAC_CTL_FORCE_DUPLEX ? "Force" : "Unforce", |
|---|
| 247 | + reg & MV88E6XXX_PORT_MAC_CTL_DUPLEX_FULL ? "full" : "half"); |
|---|
| 268 | 248 | |
|---|
| 269 | 249 | return 0; |
|---|
| 270 | 250 | } |
|---|
| 271 | 251 | |
|---|
| 272 | 252 | /* Support 10, 100, 200 Mbps (e.g. 88E6065 family) */ |
|---|
| 273 | | -int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) |
|---|
| 253 | +int mv88e6065_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, |
|---|
| 254 | + int speed, int duplex) |
|---|
| 274 | 255 | { |
|---|
| 275 | 256 | if (speed == SPEED_MAX) |
|---|
| 276 | 257 | speed = 200; |
|---|
| .. | .. |
|---|
| 279 | 260 | return -EOPNOTSUPP; |
|---|
| 280 | 261 | |
|---|
| 281 | 262 | /* Setting 200 Mbps on port 0 to 3 selects 100 Mbps */ |
|---|
| 282 | | - return mv88e6xxx_port_set_speed(chip, port, speed, false, false); |
|---|
| 263 | + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, |
|---|
| 264 | + duplex); |
|---|
| 283 | 265 | } |
|---|
| 284 | 266 | |
|---|
| 285 | 267 | /* Support 10, 100, 1000 Mbps (e.g. 88E6185 family) */ |
|---|
| 286 | | -int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) |
|---|
| 268 | +int mv88e6185_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, |
|---|
| 269 | + int speed, int duplex) |
|---|
| 287 | 270 | { |
|---|
| 288 | 271 | if (speed == SPEED_MAX) |
|---|
| 289 | 272 | speed = 1000; |
|---|
| .. | .. |
|---|
| 291 | 274 | if (speed == 200 || speed > 1000) |
|---|
| 292 | 275 | return -EOPNOTSUPP; |
|---|
| 293 | 276 | |
|---|
| 294 | | - return mv88e6xxx_port_set_speed(chip, port, speed, false, false); |
|---|
| 277 | + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, |
|---|
| 278 | + duplex); |
|---|
| 279 | +} |
|---|
| 280 | + |
|---|
| 281 | +/* Support 10, 100 Mbps (e.g. 88E6250 family) */ |
|---|
| 282 | +int mv88e6250_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, |
|---|
| 283 | + int speed, int duplex) |
|---|
| 284 | +{ |
|---|
| 285 | + if (speed == SPEED_MAX) |
|---|
| 286 | + speed = 100; |
|---|
| 287 | + |
|---|
| 288 | + if (speed > 100) |
|---|
| 289 | + return -EOPNOTSUPP; |
|---|
| 290 | + |
|---|
| 291 | + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, false, false, |
|---|
| 292 | + duplex); |
|---|
| 295 | 293 | } |
|---|
| 296 | 294 | |
|---|
| 297 | 295 | /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6341) */ |
|---|
| 298 | | -int mv88e6341_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) |
|---|
| 296 | +int mv88e6341_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, |
|---|
| 297 | + int speed, int duplex) |
|---|
| 299 | 298 | { |
|---|
| 300 | 299 | if (speed == SPEED_MAX) |
|---|
| 301 | 300 | speed = port < 5 ? 1000 : 2500; |
|---|
| .. | .. |
|---|
| 309 | 308 | if (speed == 2500 && port < 5) |
|---|
| 310 | 309 | return -EOPNOTSUPP; |
|---|
| 311 | 310 | |
|---|
| 312 | | - return mv88e6xxx_port_set_speed(chip, port, speed, !port, true); |
|---|
| 311 | + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, !port, true, |
|---|
| 312 | + duplex); |
|---|
| 313 | +} |
|---|
| 314 | + |
|---|
| 315 | +phy_interface_t mv88e6341_port_max_speed_mode(int port) |
|---|
| 316 | +{ |
|---|
| 317 | + if (port == 5) |
|---|
| 318 | + return PHY_INTERFACE_MODE_2500BASEX; |
|---|
| 319 | + |
|---|
| 320 | + return PHY_INTERFACE_MODE_NA; |
|---|
| 313 | 321 | } |
|---|
| 314 | 322 | |
|---|
| 315 | 323 | /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ |
|---|
| 316 | | -int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) |
|---|
| 324 | +int mv88e6352_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, |
|---|
| 325 | + int speed, int duplex) |
|---|
| 317 | 326 | { |
|---|
| 318 | 327 | if (speed == SPEED_MAX) |
|---|
| 319 | 328 | speed = 1000; |
|---|
| .. | .. |
|---|
| 324 | 333 | if (speed == 200 && port < 5) |
|---|
| 325 | 334 | return -EOPNOTSUPP; |
|---|
| 326 | 335 | |
|---|
| 327 | | - return mv88e6xxx_port_set_speed(chip, port, speed, true, false); |
|---|
| 336 | + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, false, |
|---|
| 337 | + duplex); |
|---|
| 328 | 338 | } |
|---|
| 329 | 339 | |
|---|
| 330 | 340 | /* Support 10, 100, 200, 1000, 2500 Mbps (e.g. 88E6390) */ |
|---|
| 331 | | -int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) |
|---|
| 341 | +int mv88e6390_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, |
|---|
| 342 | + int speed, int duplex) |
|---|
| 332 | 343 | { |
|---|
| 333 | 344 | if (speed == SPEED_MAX) |
|---|
| 334 | 345 | speed = port < 9 ? 1000 : 2500; |
|---|
| .. | .. |
|---|
| 342 | 353 | if (speed == 2500 && port < 9) |
|---|
| 343 | 354 | return -EOPNOTSUPP; |
|---|
| 344 | 355 | |
|---|
| 345 | | - return mv88e6xxx_port_set_speed(chip, port, speed, true, true); |
|---|
| 356 | + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true, |
|---|
| 357 | + duplex); |
|---|
| 358 | +} |
|---|
| 359 | + |
|---|
| 360 | +phy_interface_t mv88e6390_port_max_speed_mode(int port) |
|---|
| 361 | +{ |
|---|
| 362 | + if (port == 9 || port == 10) |
|---|
| 363 | + return PHY_INTERFACE_MODE_2500BASEX; |
|---|
| 364 | + |
|---|
| 365 | + return PHY_INTERFACE_MODE_NA; |
|---|
| 346 | 366 | } |
|---|
| 347 | 367 | |
|---|
| 348 | 368 | /* Support 10, 100, 200, 1000, 2500, 10000 Mbps (e.g. 88E6190X) */ |
|---|
| 349 | | -int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) |
|---|
| 369 | +int mv88e6390x_port_set_speed_duplex(struct mv88e6xxx_chip *chip, int port, |
|---|
| 370 | + int speed, int duplex) |
|---|
| 350 | 371 | { |
|---|
| 351 | 372 | if (speed == SPEED_MAX) |
|---|
| 352 | 373 | speed = port < 9 ? 1000 : 10000; |
|---|
| .. | .. |
|---|
| 357 | 378 | if (speed >= 2500 && port < 9) |
|---|
| 358 | 379 | return -EOPNOTSUPP; |
|---|
| 359 | 380 | |
|---|
| 360 | | - return mv88e6xxx_port_set_speed(chip, port, speed, true, true); |
|---|
| 381 | + return mv88e6xxx_port_set_speed_duplex(chip, port, speed, true, true, |
|---|
| 382 | + duplex); |
|---|
| 361 | 383 | } |
|---|
| 362 | 384 | |
|---|
| 363 | | -int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, |
|---|
| 364 | | - phy_interface_t mode) |
|---|
| 385 | +phy_interface_t mv88e6390x_port_max_speed_mode(int port) |
|---|
| 365 | 386 | { |
|---|
| 366 | | - int lane; |
|---|
| 387 | + if (port == 9 || port == 10) |
|---|
| 388 | + return PHY_INTERFACE_MODE_XAUI; |
|---|
| 389 | + |
|---|
| 390 | + return PHY_INTERFACE_MODE_NA; |
|---|
| 391 | +} |
|---|
| 392 | + |
|---|
| 393 | +static int mv88e6xxx_port_set_cmode(struct mv88e6xxx_chip *chip, int port, |
|---|
| 394 | + phy_interface_t mode, bool force) |
|---|
| 395 | +{ |
|---|
| 396 | + u8 lane; |
|---|
| 367 | 397 | u16 cmode; |
|---|
| 368 | 398 | u16 reg; |
|---|
| 369 | 399 | int err; |
|---|
| 370 | 400 | |
|---|
| 401 | + /* Default to a slow mode, so freeing up SERDES interfaces for |
|---|
| 402 | + * other ports which might use them for SFPs. |
|---|
| 403 | + */ |
|---|
| 371 | 404 | if (mode == PHY_INTERFACE_MODE_NA) |
|---|
| 372 | | - return 0; |
|---|
| 373 | | - |
|---|
| 374 | | - if (port != 9 && port != 10) |
|---|
| 375 | | - return -EOPNOTSUPP; |
|---|
| 405 | + mode = PHY_INTERFACE_MODE_1000BASEX; |
|---|
| 376 | 406 | |
|---|
| 377 | 407 | switch (mode) { |
|---|
| 378 | 408 | case PHY_INTERFACE_MODE_1000BASEX: |
|---|
| 379 | | - cmode = MV88E6XXX_PORT_STS_CMODE_1000BASE_X; |
|---|
| 409 | + cmode = MV88E6XXX_PORT_STS_CMODE_1000BASEX; |
|---|
| 380 | 410 | break; |
|---|
| 381 | 411 | case PHY_INTERFACE_MODE_SGMII: |
|---|
| 382 | 412 | cmode = MV88E6XXX_PORT_STS_CMODE_SGMII; |
|---|
| .. | .. |
|---|
| 395 | 425 | cmode = 0; |
|---|
| 396 | 426 | } |
|---|
| 397 | 427 | |
|---|
| 398 | | - /* cmode doesn't change, nothing to do for us */ |
|---|
| 399 | | - if (cmode == chip->ports[port].cmode) |
|---|
| 428 | + /* cmode doesn't change, nothing to do for us unless forced */ |
|---|
| 429 | + if (cmode == chip->ports[port].cmode && !force) |
|---|
| 400 | 430 | return 0; |
|---|
| 401 | 431 | |
|---|
| 402 | | - lane = mv88e6390x_serdes_get_lane(chip, port); |
|---|
| 403 | | - if (lane < 0 && lane != -ENODEV) |
|---|
| 404 | | - return lane; |
|---|
| 405 | | - |
|---|
| 406 | | - if (lane >= 0) { |
|---|
| 432 | + lane = mv88e6xxx_serdes_get_lane(chip, port); |
|---|
| 433 | + if (lane) { |
|---|
| 407 | 434 | if (chip->ports[port].serdes_irq) { |
|---|
| 408 | | - err = mv88e6390_serdes_irq_disable(chip, port, lane); |
|---|
| 435 | + err = mv88e6xxx_serdes_irq_disable(chip, port, lane); |
|---|
| 409 | 436 | if (err) |
|---|
| 410 | 437 | return err; |
|---|
| 411 | 438 | } |
|---|
| 412 | 439 | |
|---|
| 413 | | - err = mv88e6390x_serdes_power(chip, port, false); |
|---|
| 440 | + err = mv88e6xxx_serdes_power_down(chip, port, lane); |
|---|
| 414 | 441 | if (err) |
|---|
| 415 | 442 | return err; |
|---|
| 416 | 443 | } |
|---|
| .. | .. |
|---|
| 431 | 458 | |
|---|
| 432 | 459 | chip->ports[port].cmode = cmode; |
|---|
| 433 | 460 | |
|---|
| 434 | | - lane = mv88e6390x_serdes_get_lane(chip, port); |
|---|
| 435 | | - if (lane < 0) |
|---|
| 436 | | - return lane; |
|---|
| 461 | + lane = mv88e6xxx_serdes_get_lane(chip, port); |
|---|
| 462 | + if (!lane) |
|---|
| 463 | + return -ENODEV; |
|---|
| 437 | 464 | |
|---|
| 438 | | - err = mv88e6390x_serdes_power(chip, port, true); |
|---|
| 465 | + err = mv88e6xxx_serdes_power_up(chip, port, lane); |
|---|
| 439 | 466 | if (err) |
|---|
| 440 | 467 | return err; |
|---|
| 441 | 468 | |
|---|
| 442 | 469 | if (chip->ports[port].serdes_irq) { |
|---|
| 443 | | - err = mv88e6390_serdes_irq_enable(chip, port, lane); |
|---|
| 470 | + err = mv88e6xxx_serdes_irq_enable(chip, port, lane); |
|---|
| 444 | 471 | if (err) |
|---|
| 445 | 472 | return err; |
|---|
| 446 | 473 | } |
|---|
| 447 | 474 | } |
|---|
| 448 | 475 | |
|---|
| 449 | 476 | return 0; |
|---|
| 477 | +} |
|---|
| 478 | + |
|---|
| 479 | +int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, |
|---|
| 480 | + phy_interface_t mode) |
|---|
| 481 | +{ |
|---|
| 482 | + if (port != 9 && port != 10) |
|---|
| 483 | + return -EOPNOTSUPP; |
|---|
| 484 | + |
|---|
| 485 | + return mv88e6xxx_port_set_cmode(chip, port, mode, false); |
|---|
| 486 | +} |
|---|
| 487 | + |
|---|
| 488 | +int mv88e6390_port_set_cmode(struct mv88e6xxx_chip *chip, int port, |
|---|
| 489 | + phy_interface_t mode) |
|---|
| 490 | +{ |
|---|
| 491 | + if (port != 9 && port != 10) |
|---|
| 492 | + return -EOPNOTSUPP; |
|---|
| 493 | + |
|---|
| 494 | + switch (mode) { |
|---|
| 495 | + case PHY_INTERFACE_MODE_NA: |
|---|
| 496 | + return 0; |
|---|
| 497 | + case PHY_INTERFACE_MODE_XGMII: |
|---|
| 498 | + case PHY_INTERFACE_MODE_XAUI: |
|---|
| 499 | + case PHY_INTERFACE_MODE_RXAUI: |
|---|
| 500 | + return -EINVAL; |
|---|
| 501 | + default: |
|---|
| 502 | + break; |
|---|
| 503 | + } |
|---|
| 504 | + |
|---|
| 505 | + return mv88e6xxx_port_set_cmode(chip, port, mode, false); |
|---|
| 506 | +} |
|---|
| 507 | + |
|---|
| 508 | +static int mv88e6341_port_set_cmode_writable(struct mv88e6xxx_chip *chip, |
|---|
| 509 | + int port) |
|---|
| 510 | +{ |
|---|
| 511 | + int err, addr; |
|---|
| 512 | + u16 reg, bits; |
|---|
| 513 | + |
|---|
| 514 | + if (port != 5) |
|---|
| 515 | + return -EOPNOTSUPP; |
|---|
| 516 | + |
|---|
| 517 | + addr = chip->info->port_base_addr + port; |
|---|
| 518 | + |
|---|
| 519 | + err = mv88e6xxx_port_hidden_read(chip, 0x7, addr, 0, ®); |
|---|
| 520 | + if (err) |
|---|
| 521 | + return err; |
|---|
| 522 | + |
|---|
| 523 | + bits = MV88E6341_PORT_RESERVED_1A_FORCE_CMODE | |
|---|
| 524 | + MV88E6341_PORT_RESERVED_1A_SGMII_AN; |
|---|
| 525 | + |
|---|
| 526 | + if ((reg & bits) == bits) |
|---|
| 527 | + return 0; |
|---|
| 528 | + |
|---|
| 529 | + reg |= bits; |
|---|
| 530 | + return mv88e6xxx_port_hidden_write(chip, 0x7, addr, 0, reg); |
|---|
| 531 | +} |
|---|
| 532 | + |
|---|
| 533 | +int mv88e6341_port_set_cmode(struct mv88e6xxx_chip *chip, int port, |
|---|
| 534 | + phy_interface_t mode) |
|---|
| 535 | +{ |
|---|
| 536 | + int err; |
|---|
| 537 | + |
|---|
| 538 | + if (port != 5) |
|---|
| 539 | + return -EOPNOTSUPP; |
|---|
| 540 | + |
|---|
| 541 | + switch (mode) { |
|---|
| 542 | + case PHY_INTERFACE_MODE_NA: |
|---|
| 543 | + return 0; |
|---|
| 544 | + case PHY_INTERFACE_MODE_XGMII: |
|---|
| 545 | + case PHY_INTERFACE_MODE_XAUI: |
|---|
| 546 | + case PHY_INTERFACE_MODE_RXAUI: |
|---|
| 547 | + return -EINVAL; |
|---|
| 548 | + default: |
|---|
| 549 | + break; |
|---|
| 550 | + } |
|---|
| 551 | + |
|---|
| 552 | + err = mv88e6341_port_set_cmode_writable(chip, port); |
|---|
| 553 | + if (err) |
|---|
| 554 | + return err; |
|---|
| 555 | + |
|---|
| 556 | + return mv88e6xxx_port_set_cmode(chip, port, mode, true); |
|---|
| 450 | 557 | } |
|---|
| 451 | 558 | |
|---|
| 452 | 559 | int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) |
|---|
| .. | .. |
|---|
| 475 | 582 | *cmode = reg & MV88E6XXX_PORT_STS_CMODE_MASK; |
|---|
| 476 | 583 | |
|---|
| 477 | 584 | return 0; |
|---|
| 478 | | -} |
|---|
| 479 | | - |
|---|
| 480 | | -int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, |
|---|
| 481 | | - struct phylink_link_state *state) |
|---|
| 482 | | -{ |
|---|
| 483 | | - int err; |
|---|
| 484 | | - u16 reg; |
|---|
| 485 | | - |
|---|
| 486 | | - err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); |
|---|
| 487 | | - if (err) |
|---|
| 488 | | - return err; |
|---|
| 489 | | - |
|---|
| 490 | | - switch (reg & MV88E6XXX_PORT_STS_SPEED_MASK) { |
|---|
| 491 | | - case MV88E6XXX_PORT_STS_SPEED_10: |
|---|
| 492 | | - state->speed = SPEED_10; |
|---|
| 493 | | - break; |
|---|
| 494 | | - case MV88E6XXX_PORT_STS_SPEED_100: |
|---|
| 495 | | - state->speed = SPEED_100; |
|---|
| 496 | | - break; |
|---|
| 497 | | - case MV88E6XXX_PORT_STS_SPEED_1000: |
|---|
| 498 | | - state->speed = SPEED_1000; |
|---|
| 499 | | - break; |
|---|
| 500 | | - case MV88E6XXX_PORT_STS_SPEED_10000: |
|---|
| 501 | | - if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) == |
|---|
| 502 | | - MV88E6XXX_PORT_STS_CMODE_2500BASEX) |
|---|
| 503 | | - state->speed = SPEED_2500; |
|---|
| 504 | | - else |
|---|
| 505 | | - state->speed = SPEED_10000; |
|---|
| 506 | | - break; |
|---|
| 507 | | - } |
|---|
| 508 | | - |
|---|
| 509 | | - state->duplex = reg & MV88E6XXX_PORT_STS_DUPLEX ? |
|---|
| 510 | | - DUPLEX_FULL : DUPLEX_HALF; |
|---|
| 511 | | - state->link = !!(reg & MV88E6XXX_PORT_STS_LINK); |
|---|
| 512 | | - state->an_enabled = 1; |
|---|
| 513 | | - state->an_complete = state->link; |
|---|
| 514 | | - |
|---|
| 515 | | - return 0; |
|---|
| 516 | | -} |
|---|
| 517 | | - |
|---|
| 518 | | -int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, |
|---|
| 519 | | - struct phylink_link_state *state) |
|---|
| 520 | | -{ |
|---|
| 521 | | - if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { |
|---|
| 522 | | - u8 cmode = chip->ports[port].cmode; |
|---|
| 523 | | - |
|---|
| 524 | | - /* When a port is in "Cross-chip serdes" mode, it uses |
|---|
| 525 | | - * 1000Base-X full duplex mode, but there is no automatic |
|---|
| 526 | | - * link detection. Use the sync OK status for link (as it |
|---|
| 527 | | - * would do for 1000Base-X mode.) |
|---|
| 528 | | - */ |
|---|
| 529 | | - if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) { |
|---|
| 530 | | - u16 mac; |
|---|
| 531 | | - int err; |
|---|
| 532 | | - |
|---|
| 533 | | - err = mv88e6xxx_port_read(chip, port, |
|---|
| 534 | | - MV88E6XXX_PORT_MAC_CTL, &mac); |
|---|
| 535 | | - if (err) |
|---|
| 536 | | - return err; |
|---|
| 537 | | - |
|---|
| 538 | | - state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK); |
|---|
| 539 | | - state->an_enabled = 1; |
|---|
| 540 | | - state->an_complete = |
|---|
| 541 | | - !!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE); |
|---|
| 542 | | - state->duplex = |
|---|
| 543 | | - state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; |
|---|
| 544 | | - state->speed = |
|---|
| 545 | | - state->link ? SPEED_1000 : SPEED_UNKNOWN; |
|---|
| 546 | | - |
|---|
| 547 | | - return 0; |
|---|
| 548 | | - } |
|---|
| 549 | | - } |
|---|
| 550 | | - |
|---|
| 551 | | - return mv88e6352_port_link_state(chip, port, state); |
|---|
| 552 | 585 | } |
|---|
| 553 | 586 | |
|---|
| 554 | 587 | /* Offset 0x02: Jamming Control |
|---|
| .. | .. |
|---|
| 969 | 1002 | return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg); |
|---|
| 970 | 1003 | } |
|---|
| 971 | 1004 | |
|---|
| 1005 | +int mv88e6xxx_port_set_mirror(struct mv88e6xxx_chip *chip, int port, |
|---|
| 1006 | + enum mv88e6xxx_egress_direction direction, |
|---|
| 1007 | + bool mirror) |
|---|
| 1008 | +{ |
|---|
| 1009 | + bool *mirror_port; |
|---|
| 1010 | + u16 reg; |
|---|
| 1011 | + u16 bit; |
|---|
| 1012 | + int err; |
|---|
| 1013 | + |
|---|
| 1014 | + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®); |
|---|
| 1015 | + if (err) |
|---|
| 1016 | + return err; |
|---|
| 1017 | + |
|---|
| 1018 | + switch (direction) { |
|---|
| 1019 | + case MV88E6XXX_EGRESS_DIR_INGRESS: |
|---|
| 1020 | + bit = MV88E6XXX_PORT_CTL2_INGRESS_MONITOR; |
|---|
| 1021 | + mirror_port = &chip->ports[port].mirror_ingress; |
|---|
| 1022 | + break; |
|---|
| 1023 | + case MV88E6XXX_EGRESS_DIR_EGRESS: |
|---|
| 1024 | + bit = MV88E6XXX_PORT_CTL2_EGRESS_MONITOR; |
|---|
| 1025 | + mirror_port = &chip->ports[port].mirror_egress; |
|---|
| 1026 | + break; |
|---|
| 1027 | + default: |
|---|
| 1028 | + return -EINVAL; |
|---|
| 1029 | + } |
|---|
| 1030 | + |
|---|
| 1031 | + reg &= ~bit; |
|---|
| 1032 | + if (mirror) |
|---|
| 1033 | + reg |= bit; |
|---|
| 1034 | + |
|---|
| 1035 | + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, reg); |
|---|
| 1036 | + if (!err) |
|---|
| 1037 | + *mirror_port = mirror; |
|---|
| 1038 | + |
|---|
| 1039 | + return err; |
|---|
| 1040 | +} |
|---|
| 1041 | + |
|---|
| 972 | 1042 | int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port, |
|---|
| 973 | 1043 | u16 mode) |
|---|
| 974 | 1044 | { |
|---|
| .. | .. |
|---|
| 1011 | 1081 | { |
|---|
| 1012 | 1082 | u16 reg; |
|---|
| 1013 | 1083 | int err; |
|---|
| 1084 | + |
|---|
| 1085 | + size += VLAN_ETH_HLEN + ETH_FCS_LEN; |
|---|
| 1014 | 1086 | |
|---|
| 1015 | 1087 | err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, ®); |
|---|
| 1016 | 1088 | if (err) |
|---|
| .. | .. |
|---|
| 1129 | 1201 | |
|---|
| 1130 | 1202 | return 0; |
|---|
| 1131 | 1203 | } |
|---|
| 1204 | + |
|---|
| 1205 | +/* Offset 0x0E: Policy Control Register */ |
|---|
| 1206 | + |
|---|
| 1207 | +int mv88e6352_port_set_policy(struct mv88e6xxx_chip *chip, int port, |
|---|
| 1208 | + enum mv88e6xxx_policy_mapping mapping, |
|---|
| 1209 | + enum mv88e6xxx_policy_action action) |
|---|
| 1210 | +{ |
|---|
| 1211 | + u16 reg, mask, val; |
|---|
| 1212 | + int shift; |
|---|
| 1213 | + int err; |
|---|
| 1214 | + |
|---|
| 1215 | + switch (mapping) { |
|---|
| 1216 | + case MV88E6XXX_POLICY_MAPPING_DA: |
|---|
| 1217 | + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_DA_MASK); |
|---|
| 1218 | + mask = MV88E6XXX_PORT_POLICY_CTL_DA_MASK; |
|---|
| 1219 | + break; |
|---|
| 1220 | + case MV88E6XXX_POLICY_MAPPING_SA: |
|---|
| 1221 | + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_SA_MASK); |
|---|
| 1222 | + mask = MV88E6XXX_PORT_POLICY_CTL_SA_MASK; |
|---|
| 1223 | + break; |
|---|
| 1224 | + case MV88E6XXX_POLICY_MAPPING_VTU: |
|---|
| 1225 | + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VTU_MASK); |
|---|
| 1226 | + mask = MV88E6XXX_PORT_POLICY_CTL_VTU_MASK; |
|---|
| 1227 | + break; |
|---|
| 1228 | + case MV88E6XXX_POLICY_MAPPING_ETYPE: |
|---|
| 1229 | + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK); |
|---|
| 1230 | + mask = MV88E6XXX_PORT_POLICY_CTL_ETYPE_MASK; |
|---|
| 1231 | + break; |
|---|
| 1232 | + case MV88E6XXX_POLICY_MAPPING_PPPOE: |
|---|
| 1233 | + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK); |
|---|
| 1234 | + mask = MV88E6XXX_PORT_POLICY_CTL_PPPOE_MASK; |
|---|
| 1235 | + break; |
|---|
| 1236 | + case MV88E6XXX_POLICY_MAPPING_VBAS: |
|---|
| 1237 | + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK); |
|---|
| 1238 | + mask = MV88E6XXX_PORT_POLICY_CTL_VBAS_MASK; |
|---|
| 1239 | + break; |
|---|
| 1240 | + case MV88E6XXX_POLICY_MAPPING_OPT82: |
|---|
| 1241 | + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK); |
|---|
| 1242 | + mask = MV88E6XXX_PORT_POLICY_CTL_OPT82_MASK; |
|---|
| 1243 | + break; |
|---|
| 1244 | + case MV88E6XXX_POLICY_MAPPING_UDP: |
|---|
| 1245 | + shift = __bf_shf(MV88E6XXX_PORT_POLICY_CTL_UDP_MASK); |
|---|
| 1246 | + mask = MV88E6XXX_PORT_POLICY_CTL_UDP_MASK; |
|---|
| 1247 | + break; |
|---|
| 1248 | + default: |
|---|
| 1249 | + return -EOPNOTSUPP; |
|---|
| 1250 | + } |
|---|
| 1251 | + |
|---|
| 1252 | + switch (action) { |
|---|
| 1253 | + case MV88E6XXX_POLICY_ACTION_NORMAL: |
|---|
| 1254 | + val = MV88E6XXX_PORT_POLICY_CTL_NORMAL; |
|---|
| 1255 | + break; |
|---|
| 1256 | + case MV88E6XXX_POLICY_ACTION_MIRROR: |
|---|
| 1257 | + val = MV88E6XXX_PORT_POLICY_CTL_MIRROR; |
|---|
| 1258 | + break; |
|---|
| 1259 | + case MV88E6XXX_POLICY_ACTION_TRAP: |
|---|
| 1260 | + val = MV88E6XXX_PORT_POLICY_CTL_TRAP; |
|---|
| 1261 | + break; |
|---|
| 1262 | + case MV88E6XXX_POLICY_ACTION_DISCARD: |
|---|
| 1263 | + val = MV88E6XXX_PORT_POLICY_CTL_DISCARD; |
|---|
| 1264 | + break; |
|---|
| 1265 | + default: |
|---|
| 1266 | + return -EOPNOTSUPP; |
|---|
| 1267 | + } |
|---|
| 1268 | + |
|---|
| 1269 | + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_POLICY_CTL, ®); |
|---|
| 1270 | + if (err) |
|---|
| 1271 | + return err; |
|---|
| 1272 | + |
|---|
| 1273 | + reg &= ~mask; |
|---|
| 1274 | + reg |= (val << shift) & mask; |
|---|
| 1275 | + |
|---|
| 1276 | + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_POLICY_CTL, reg); |
|---|
| 1277 | +} |
|---|