linux/drivers/net/phy/vitesse.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Driver for Vitesse PHYs
   4 *
   5 * Author: Kriston Carson
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/mii.h>
  11#include <linux/ethtool.h>
  12#include <linux/phy.h>
  13
  14/* Vitesse Extended Page Magic Register(s) */
  15#define MII_VSC82X4_EXT_PAGE_16E        0x10
  16#define MII_VSC82X4_EXT_PAGE_17E        0x11
  17#define MII_VSC82X4_EXT_PAGE_18E        0x12
  18
  19/* Vitesse Extended Control Register 1 */
  20#define MII_VSC8244_EXT_CON1           0x17
  21#define MII_VSC8244_EXTCON1_INIT       0x0000
  22#define MII_VSC8244_EXTCON1_TX_SKEW_MASK        0x0c00
  23#define MII_VSC8244_EXTCON1_RX_SKEW_MASK        0x0300
  24#define MII_VSC8244_EXTCON1_TX_SKEW     0x0800
  25#define MII_VSC8244_EXTCON1_RX_SKEW     0x0200
  26
  27/* Vitesse Interrupt Mask Register */
  28#define MII_VSC8244_IMASK               0x19
  29#define MII_VSC8244_IMASK_IEN           0x8000
  30#define MII_VSC8244_IMASK_SPEED         0x4000
  31#define MII_VSC8244_IMASK_LINK          0x2000
  32#define MII_VSC8244_IMASK_DUPLEX        0x1000
  33#define MII_VSC8244_IMASK_MASK          0xf000
  34
  35#define MII_VSC8221_IMASK_MASK          0xa000
  36
  37/* Vitesse Interrupt Status Register */
  38#define MII_VSC8244_ISTAT               0x1a
  39#define MII_VSC8244_ISTAT_STATUS        0x8000
  40#define MII_VSC8244_ISTAT_SPEED         0x4000
  41#define MII_VSC8244_ISTAT_LINK          0x2000
  42#define MII_VSC8244_ISTAT_DUPLEX        0x1000
  43#define MII_VSC8244_ISTAT_MASK          (MII_VSC8244_ISTAT_SPEED | \
  44                                         MII_VSC8244_ISTAT_LINK | \
  45                                         MII_VSC8244_ISTAT_DUPLEX)
  46
  47#define MII_VSC8221_ISTAT_MASK          MII_VSC8244_ISTAT_LINK
  48
  49/* Vitesse Auxiliary Control/Status Register */
  50#define MII_VSC8244_AUX_CONSTAT         0x1c
  51#define MII_VSC8244_AUXCONSTAT_INIT     0x0000
  52#define MII_VSC8244_AUXCONSTAT_DUPLEX   0x0020
  53#define MII_VSC8244_AUXCONSTAT_SPEED    0x0018
  54#define MII_VSC8244_AUXCONSTAT_GBIT     0x0010
  55#define MII_VSC8244_AUXCONSTAT_100      0x0008
  56
  57#define MII_VSC8221_AUXCONSTAT_INIT     0x0004 /* need to set this bit? */
  58#define MII_VSC8221_AUXCONSTAT_RESERVED 0x0004
  59
  60/* Vitesse Extended Page Access Register */
  61#define MII_VSC82X4_EXT_PAGE_ACCESS     0x1f
  62
  63/* Vitesse VSC8601 Extended PHY Control Register 1 */
  64#define MII_VSC8601_EPHY_CTL            0x17
  65#define MII_VSC8601_EPHY_CTL_RGMII_SKEW (1 << 8)
  66
  67#define PHY_ID_VSC8234                  0x000fc620
  68#define PHY_ID_VSC8244                  0x000fc6c0
  69#define PHY_ID_VSC8572                  0x000704d0
  70#define PHY_ID_VSC8601                  0x00070420
  71#define PHY_ID_VSC7385                  0x00070450
  72#define PHY_ID_VSC7388                  0x00070480
  73#define PHY_ID_VSC7395                  0x00070550
  74#define PHY_ID_VSC7398                  0x00070580
  75#define PHY_ID_VSC8662                  0x00070660
  76#define PHY_ID_VSC8221                  0x000fc550
  77#define PHY_ID_VSC8211                  0x000fc4b0
  78
  79MODULE_DESCRIPTION("Vitesse PHY driver");
  80MODULE_AUTHOR("Kriston Carson");
  81MODULE_LICENSE("GPL");
  82
  83static int vsc824x_add_skew(struct phy_device *phydev)
  84{
  85        int err;
  86        int extcon;
  87
  88        extcon = phy_read(phydev, MII_VSC8244_EXT_CON1);
  89
  90        if (extcon < 0)
  91                return extcon;
  92
  93        extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK |
  94                        MII_VSC8244_EXTCON1_RX_SKEW_MASK);
  95
  96        extcon |= (MII_VSC8244_EXTCON1_TX_SKEW |
  97                        MII_VSC8244_EXTCON1_RX_SKEW);
  98
  99        err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon);
 100
 101        return err;
 102}
 103
 104static int vsc824x_config_init(struct phy_device *phydev)
 105{
 106        int err;
 107
 108        err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
 109                        MII_VSC8244_AUXCONSTAT_INIT);
 110        if (err < 0)
 111                return err;
 112
 113        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
 114                err = vsc824x_add_skew(phydev);
 115
 116        return err;
 117}
 118
 119#define VSC73XX_EXT_PAGE_ACCESS 0x1f
 120
 121static int vsc73xx_read_page(struct phy_device *phydev)
 122{
 123        return __phy_read(phydev, VSC73XX_EXT_PAGE_ACCESS);
 124}
 125
 126static int vsc73xx_write_page(struct phy_device *phydev, int page)
 127{
 128        return __phy_write(phydev, VSC73XX_EXT_PAGE_ACCESS, page);
 129}
 130
 131static void vsc73xx_config_init(struct phy_device *phydev)
 132{
 133        /* Receiver init */
 134        phy_write(phydev, 0x1f, 0x2a30);
 135        phy_modify(phydev, 0x0c, 0x0300, 0x0200);
 136        phy_write(phydev, 0x1f, 0x0000);
 137
 138        /* Config LEDs 0x61 */
 139        phy_modify(phydev, MII_TPISTATUS, 0xff00, 0x0061);
 140}
 141
 142static int vsc738x_config_init(struct phy_device *phydev)
 143{
 144        u16 rev;
 145        /* This magic sequence appear in the application note
 146         * "VSC7385/7388 PHY Configuration".
 147         *
 148         * Maybe one day we will get to know what it all means.
 149         */
 150        phy_write(phydev, 0x1f, 0x2a30);
 151        phy_modify(phydev, 0x08, 0x0200, 0x0200);
 152        phy_write(phydev, 0x1f, 0x52b5);
 153        phy_write(phydev, 0x10, 0xb68a);
 154        phy_modify(phydev, 0x12, 0xff07, 0x0003);
 155        phy_modify(phydev, 0x11, 0x00ff, 0x00a2);
 156        phy_write(phydev, 0x10, 0x968a);
 157        phy_write(phydev, 0x1f, 0x2a30);
 158        phy_modify(phydev, 0x08, 0x0200, 0x0000);
 159        phy_write(phydev, 0x1f, 0x0000);
 160
 161        /* Read revision */
 162        rev = phy_read(phydev, MII_PHYSID2);
 163        rev &= 0x0f;
 164
 165        /* Special quirk for revision 0 */
 166        if (rev == 0) {
 167                phy_write(phydev, 0x1f, 0x2a30);
 168                phy_modify(phydev, 0x08, 0x0200, 0x0200);
 169                phy_write(phydev, 0x1f, 0x52b5);
 170                phy_write(phydev, 0x12, 0x0000);
 171                phy_write(phydev, 0x11, 0x0689);
 172                phy_write(phydev, 0x10, 0x8f92);
 173                phy_write(phydev, 0x1f, 0x52b5);
 174                phy_write(phydev, 0x12, 0x0000);
 175                phy_write(phydev, 0x11, 0x0e35);
 176                phy_write(phydev, 0x10, 0x9786);
 177                phy_write(phydev, 0x1f, 0x2a30);
 178                phy_modify(phydev, 0x08, 0x0200, 0x0000);
 179                phy_write(phydev, 0x17, 0xff80);
 180                phy_write(phydev, 0x17, 0x0000);
 181        }
 182
 183        phy_write(phydev, 0x1f, 0x0000);
 184        phy_write(phydev, 0x12, 0x0048);
 185
 186        if (rev == 0) {
 187                phy_write(phydev, 0x1f, 0x2a30);
 188                phy_write(phydev, 0x14, 0x6600);
 189                phy_write(phydev, 0x1f, 0x0000);
 190                phy_write(phydev, 0x18, 0xa24e);
 191        } else {
 192                phy_write(phydev, 0x1f, 0x2a30);
 193                phy_modify(phydev, 0x16, 0x0fc0, 0x0240);
 194                phy_modify(phydev, 0x14, 0x6000, 0x4000);
 195                /* bits 14-15 in extended register 0x14 controls DACG amplitude
 196                 * 6 = -8%, 2 is hardware default
 197                 */
 198                phy_write(phydev, 0x1f, 0x0001);
 199                phy_modify(phydev, 0x14, 0xe000, 0x6000);
 200                phy_write(phydev, 0x1f, 0x0000);
 201        }
 202
 203        vsc73xx_config_init(phydev);
 204
 205        return 0;
 206}
 207
 208static int vsc739x_config_init(struct phy_device *phydev)
 209{
 210        /* This magic sequence appears in the VSC7395 SparX-G5e application
 211         * note "VSC7395/VSC7398 PHY Configuration"
 212         *
 213         * Maybe one day we will get to know what it all means.
 214         */
 215        phy_write(phydev, 0x1f, 0x2a30);
 216        phy_modify(phydev, 0x08, 0x0200, 0x0200);
 217        phy_write(phydev, 0x1f, 0x52b5);
 218        phy_write(phydev, 0x10, 0xb68a);
 219        phy_modify(phydev, 0x12, 0xff07, 0x0003);
 220        phy_modify(phydev, 0x11, 0x00ff, 0x00a2);
 221        phy_write(phydev, 0x10, 0x968a);
 222        phy_write(phydev, 0x1f, 0x2a30);
 223        phy_modify(phydev, 0x08, 0x0200, 0x0000);
 224        phy_write(phydev, 0x1f, 0x0000);
 225
 226        phy_write(phydev, 0x1f, 0x0000);
 227        phy_write(phydev, 0x12, 0x0048);
 228        phy_write(phydev, 0x1f, 0x2a30);
 229        phy_modify(phydev, 0x16, 0x0fc0, 0x0240);
 230        phy_modify(phydev, 0x14, 0x6000, 0x4000);
 231        phy_write(phydev, 0x1f, 0x0001);
 232        phy_modify(phydev, 0x14, 0xe000, 0x6000);
 233        phy_write(phydev, 0x1f, 0x0000);
 234
 235        vsc73xx_config_init(phydev);
 236
 237        return 0;
 238}
 239
 240static int vsc73xx_config_aneg(struct phy_device *phydev)
 241{
 242        /* The VSC73xx switches does not like to be instructed to
 243         * do autonegotiation in any way, it prefers that you just go
 244         * with the power-on/reset defaults. Writing some registers will
 245         * just make autonegotiation permanently fail.
 246         */
 247        return 0;
 248}
 249
 250/* This adds a skew for both TX and RX clocks, so the skew should only be
 251 * applied to "rgmii-id" interfaces. It may not work as expected
 252 * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces. */
 253static int vsc8601_add_skew(struct phy_device *phydev)
 254{
 255        int ret;
 256
 257        ret = phy_read(phydev, MII_VSC8601_EPHY_CTL);
 258        if (ret < 0)
 259                return ret;
 260
 261        ret |= MII_VSC8601_EPHY_CTL_RGMII_SKEW;
 262        return phy_write(phydev, MII_VSC8601_EPHY_CTL, ret);
 263}
 264
 265static int vsc8601_config_init(struct phy_device *phydev)
 266{
 267        int ret = 0;
 268
 269        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
 270                ret = vsc8601_add_skew(phydev);
 271
 272        if (ret < 0)
 273                return ret;
 274
 275        return 0;
 276}
 277
 278static int vsc82xx_config_intr(struct phy_device *phydev)
 279{
 280        int err;
 281
 282        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
 283                /* Don't bother to ACK the interrupts since the 824x cannot
 284                 * clear the interrupts if they are disabled.
 285                 */
 286                err = phy_write(phydev, MII_VSC8244_IMASK,
 287                        (phydev->drv->phy_id == PHY_ID_VSC8234 ||
 288                         phydev->drv->phy_id == PHY_ID_VSC8244 ||
 289                         phydev->drv->phy_id == PHY_ID_VSC8572 ||
 290                         phydev->drv->phy_id == PHY_ID_VSC8601) ?
 291                                MII_VSC8244_IMASK_MASK :
 292                                MII_VSC8221_IMASK_MASK);
 293        else {
 294                /* The Vitesse PHY cannot clear the interrupt
 295                 * once it has disabled them, so we clear them first
 296                 */
 297                err = phy_read(phydev, MII_VSC8244_ISTAT);
 298
 299                if (err < 0)
 300                        return err;
 301
 302                err = phy_write(phydev, MII_VSC8244_IMASK, 0);
 303        }
 304
 305        return err;
 306}
 307
 308static irqreturn_t vsc82xx_handle_interrupt(struct phy_device *phydev)
 309{
 310        int irq_status, irq_mask;
 311
 312        if (phydev->drv->phy_id == PHY_ID_VSC8244 ||
 313            phydev->drv->phy_id == PHY_ID_VSC8572 ||
 314            phydev->drv->phy_id == PHY_ID_VSC8601)
 315                irq_mask = MII_VSC8244_ISTAT_MASK;
 316        else
 317                irq_mask = MII_VSC8221_ISTAT_MASK;
 318
 319        irq_status = phy_read(phydev, MII_VSC8244_ISTAT);
 320        if (irq_status < 0) {
 321                phy_error(phydev);
 322                return IRQ_NONE;
 323        }
 324
 325        if (!(irq_status & irq_mask))
 326                return IRQ_NONE;
 327
 328        phy_trigger_machine(phydev);
 329
 330        return IRQ_HANDLED;
 331}
 332
 333static int vsc8221_config_init(struct phy_device *phydev)
 334{
 335        int err;
 336
 337        err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
 338                        MII_VSC8221_AUXCONSTAT_INIT);
 339        return err;
 340
 341        /* Perhaps we should set EXT_CON1 based on the interface?
 342         * Options are 802.3Z SerDes or SGMII
 343         */
 344}
 345
 346/* vsc82x4_config_autocross_enable - Enable auto MDI/MDI-X for forced links
 347 * @phydev: target phy_device struct
 348 *
 349 * Enable auto MDI/MDI-X when in 10/100 forced link speeds by writing
 350 * special values in the VSC8234/VSC8244 extended reserved registers
 351 */
 352static int vsc82x4_config_autocross_enable(struct phy_device *phydev)
 353{
 354        int ret;
 355
 356        if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed > SPEED_100)
 357                return 0;
 358
 359        /* map extended registers set 0x10 - 0x1e */
 360        ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x52b5);
 361        if (ret >= 0)
 362                ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_18E, 0x0012);
 363        if (ret >= 0)
 364                ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_17E, 0x2803);
 365        if (ret >= 0)
 366                ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_16E, 0x87fa);
 367        /* map standard registers set 0x10 - 0x1e */
 368        if (ret >= 0)
 369                ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);
 370        else
 371                phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);
 372
 373        return ret;
 374}
 375
 376/* vsc82x4_config_aneg - restart auto-negotiation or write BMCR
 377 * @phydev: target phy_device struct
 378 *
 379 * Description: If auto-negotiation is enabled, we configure the
 380 *   advertising, and then restart auto-negotiation.  If it is not
 381 *   enabled, then we write the BMCR and also start the auto
 382 *   MDI/MDI-X feature
 383 */
 384static int vsc82x4_config_aneg(struct phy_device *phydev)
 385{
 386        int ret;
 387
 388        /* Enable auto MDI/MDI-X when in 10/100 forced link speeds by
 389         * writing special values in the VSC8234 extended reserved registers
 390         */
 391        if (phydev->autoneg != AUTONEG_ENABLE && phydev->speed <= SPEED_100) {
 392                ret = genphy_setup_forced(phydev);
 393
 394                if (ret < 0) /* error */
 395                        return ret;
 396
 397                return vsc82x4_config_autocross_enable(phydev);
 398        }
 399
 400        return genphy_config_aneg(phydev);
 401}
 402
 403/* Vitesse 82xx */
 404static struct phy_driver vsc82xx_driver[] = {
 405{
 406        .phy_id         = PHY_ID_VSC8234,
 407        .name           = "Vitesse VSC8234",
 408        .phy_id_mask    = 0x000ffff0,
 409        /* PHY_GBIT_FEATURES */
 410        .config_init    = &vsc824x_config_init,
 411        .config_aneg    = &vsc82x4_config_aneg,
 412        .config_intr    = &vsc82xx_config_intr,
 413        .handle_interrupt = &vsc82xx_handle_interrupt,
 414}, {
 415        .phy_id         = PHY_ID_VSC8244,
 416        .name           = "Vitesse VSC8244",
 417        .phy_id_mask    = 0x000fffc0,
 418        /* PHY_GBIT_FEATURES */
 419        .config_init    = &vsc824x_config_init,
 420        .config_aneg    = &vsc82x4_config_aneg,
 421        .config_intr    = &vsc82xx_config_intr,
 422        .handle_interrupt = &vsc82xx_handle_interrupt,
 423}, {
 424        .phy_id         = PHY_ID_VSC8572,
 425        .name           = "Vitesse VSC8572",
 426        .phy_id_mask    = 0x000ffff0,
 427        /* PHY_GBIT_FEATURES */
 428        .config_init    = &vsc824x_config_init,
 429        .config_aneg    = &vsc82x4_config_aneg,
 430        .config_intr    = &vsc82xx_config_intr,
 431        .handle_interrupt = &vsc82xx_handle_interrupt,
 432}, {
 433        .phy_id         = PHY_ID_VSC8601,
 434        .name           = "Vitesse VSC8601",
 435        .phy_id_mask    = 0x000ffff0,
 436        /* PHY_GBIT_FEATURES */
 437        .config_init    = &vsc8601_config_init,
 438        .config_intr    = &vsc82xx_config_intr,
 439        .handle_interrupt = &vsc82xx_handle_interrupt,
 440}, {
 441        .phy_id         = PHY_ID_VSC7385,
 442        .name           = "Vitesse VSC7385",
 443        .phy_id_mask    = 0x000ffff0,
 444        /* PHY_GBIT_FEATURES */
 445        .config_init    = vsc738x_config_init,
 446        .config_aneg    = vsc73xx_config_aneg,
 447        .read_page      = vsc73xx_read_page,
 448        .write_page     = vsc73xx_write_page,
 449}, {
 450        .phy_id         = PHY_ID_VSC7388,
 451        .name           = "Vitesse VSC7388",
 452        .phy_id_mask    = 0x000ffff0,
 453        /* PHY_GBIT_FEATURES */
 454        .config_init    = vsc738x_config_init,
 455        .config_aneg    = vsc73xx_config_aneg,
 456        .read_page      = vsc73xx_read_page,
 457        .write_page     = vsc73xx_write_page,
 458}, {
 459        .phy_id         = PHY_ID_VSC7395,
 460        .name           = "Vitesse VSC7395",
 461        .phy_id_mask    = 0x000ffff0,
 462        /* PHY_GBIT_FEATURES */
 463        .config_init    = vsc739x_config_init,
 464        .config_aneg    = vsc73xx_config_aneg,
 465        .read_page      = vsc73xx_read_page,
 466        .write_page     = vsc73xx_write_page,
 467}, {
 468        .phy_id         = PHY_ID_VSC7398,
 469        .name           = "Vitesse VSC7398",
 470        .phy_id_mask    = 0x000ffff0,
 471        /* PHY_GBIT_FEATURES */
 472        .config_init    = vsc739x_config_init,
 473        .config_aneg    = vsc73xx_config_aneg,
 474        .read_page      = vsc73xx_read_page,
 475        .write_page     = vsc73xx_write_page,
 476}, {
 477        .phy_id         = PHY_ID_VSC8662,
 478        .name           = "Vitesse VSC8662",
 479        .phy_id_mask    = 0x000ffff0,
 480        /* PHY_GBIT_FEATURES */
 481        .config_init    = &vsc824x_config_init,
 482        .config_aneg    = &vsc82x4_config_aneg,
 483        .config_intr    = &vsc82xx_config_intr,
 484        .handle_interrupt = &vsc82xx_handle_interrupt,
 485}, {
 486        /* Vitesse 8221 */
 487        .phy_id         = PHY_ID_VSC8221,
 488        .phy_id_mask    = 0x000ffff0,
 489        .name           = "Vitesse VSC8221",
 490        /* PHY_GBIT_FEATURES */
 491        .config_init    = &vsc8221_config_init,
 492        .config_intr    = &vsc82xx_config_intr,
 493        .handle_interrupt = &vsc82xx_handle_interrupt,
 494}, {
 495        /* Vitesse 8211 */
 496        .phy_id         = PHY_ID_VSC8211,
 497        .phy_id_mask    = 0x000ffff0,
 498        .name           = "Vitesse VSC8211",
 499        /* PHY_GBIT_FEATURES */
 500        .config_init    = &vsc8221_config_init,
 501        .config_intr    = &vsc82xx_config_intr,
 502        .handle_interrupt = &vsc82xx_handle_interrupt,
 503} };
 504
 505module_phy_driver(vsc82xx_driver);
 506
 507static struct mdio_device_id __maybe_unused vitesse_tbl[] = {
 508        { PHY_ID_VSC8234, 0x000ffff0 },
 509        { PHY_ID_VSC8244, 0x000fffc0 },
 510        { PHY_ID_VSC8572, 0x000ffff0 },
 511        { PHY_ID_VSC7385, 0x000ffff0 },
 512        { PHY_ID_VSC7388, 0x000ffff0 },
 513        { PHY_ID_VSC7395, 0x000ffff0 },
 514        { PHY_ID_VSC7398, 0x000ffff0 },
 515        { PHY_ID_VSC8662, 0x000ffff0 },
 516        { PHY_ID_VSC8221, 0x000ffff0 },
 517        { PHY_ID_VSC8211, 0x000ffff0 },
 518        { }
 519};
 520
 521MODULE_DEVICE_TABLE(mdio, vitesse_tbl);
 522