linux/drivers/net/phy/micrel.c
<<
>>
Prefs
   1/*
   2 * drivers/net/phy/micrel.c
   3 *
   4 * Driver for Micrel PHYs
   5 *
   6 * Author: David J. Choi
   7 *
   8 * Copyright (c) 2010-2013 Micrel, Inc.
   9 *
  10 * This program is free software; you can redistribute  it and/or modify it
  11 * under  the terms of  the GNU General  Public License as published by the
  12 * Free Software Foundation;  either version 2 of the  License, or (at your
  13 * option) any later version.
  14 *
  15 * Support : Micrel Phys:
  16 *              Giga phys: ksz9021, ksz9031
  17 *              100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041
  18 *                         ksz8021, ksz8031, ksz8051,
  19 *                         ksz8081, ksz8091,
  20 *                         ksz8061,
  21 *              Switch : ksz8873, ksz886x
  22 */
  23
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <linux/phy.h>
  27#include <linux/micrel_phy.h>
  28#include <linux/of.h>
  29
  30/* Operation Mode Strap Override */
  31#define MII_KSZPHY_OMSO                         0x16
  32#define KSZPHY_OMSO_B_CAST_OFF                  (1 << 9)
  33#define KSZPHY_OMSO_RMII_OVERRIDE               (1 << 1)
  34#define KSZPHY_OMSO_MII_OVERRIDE                (1 << 0)
  35
  36/* general Interrupt control/status reg in vendor specific block. */
  37#define MII_KSZPHY_INTCS                        0x1B
  38#define KSZPHY_INTCS_JABBER                     (1 << 15)
  39#define KSZPHY_INTCS_RECEIVE_ERR                (1 << 14)
  40#define KSZPHY_INTCS_PAGE_RECEIVE               (1 << 13)
  41#define KSZPHY_INTCS_PARELLEL                   (1 << 12)
  42#define KSZPHY_INTCS_LINK_PARTNER_ACK           (1 << 11)
  43#define KSZPHY_INTCS_LINK_DOWN                  (1 << 10)
  44#define KSZPHY_INTCS_REMOTE_FAULT               (1 << 9)
  45#define KSZPHY_INTCS_LINK_UP                    (1 << 8)
  46#define KSZPHY_INTCS_ALL                        (KSZPHY_INTCS_LINK_UP |\
  47                                                KSZPHY_INTCS_LINK_DOWN)
  48
  49/* general PHY control reg in vendor specific block. */
  50#define MII_KSZPHY_CTRL                 0x1F
  51/* bitmap of PHY register to set interrupt mode */
  52#define KSZPHY_CTRL_INT_ACTIVE_HIGH             (1 << 9)
  53#define KSZ9021_CTRL_INT_ACTIVE_HIGH            (1 << 14)
  54#define KS8737_CTRL_INT_ACTIVE_HIGH             (1 << 14)
  55#define KSZ8051_RMII_50MHZ_CLK                  (1 << 7)
  56
  57/* Write/read to/from extended registers */
  58#define MII_KSZPHY_EXTREG                       0x0b
  59#define KSZPHY_EXTREG_WRITE                     0x8000
  60
  61#define MII_KSZPHY_EXTREG_WRITE                 0x0c
  62#define MII_KSZPHY_EXTREG_READ                  0x0d
  63
  64/* Extended registers */
  65#define MII_KSZPHY_CLK_CONTROL_PAD_SKEW         0x104
  66#define MII_KSZPHY_RX_DATA_PAD_SKEW             0x105
  67#define MII_KSZPHY_TX_DATA_PAD_SKEW             0x106
  68
  69#define PS_TO_REG                               200
  70
  71static int ksz_config_flags(struct phy_device *phydev)
  72{
  73        int regval;
  74
  75        if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) {
  76                regval = phy_read(phydev, MII_KSZPHY_CTRL);
  77                regval |= KSZ8051_RMII_50MHZ_CLK;
  78                return phy_write(phydev, MII_KSZPHY_CTRL, regval);
  79        }
  80        return 0;
  81}
  82
  83static int kszphy_extended_write(struct phy_device *phydev,
  84                                u32 regnum, u16 val)
  85{
  86        phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum);
  87        return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val);
  88}
  89
  90static int kszphy_extended_read(struct phy_device *phydev,
  91                                u32 regnum)
  92{
  93        phy_write(phydev, MII_KSZPHY_EXTREG, regnum);
  94        return phy_read(phydev, MII_KSZPHY_EXTREG_READ);
  95}
  96
  97static int kszphy_ack_interrupt(struct phy_device *phydev)
  98{
  99        /* bit[7..0] int status, which is a read and clear register. */
 100        int rc;
 101
 102        rc = phy_read(phydev, MII_KSZPHY_INTCS);
 103
 104        return (rc < 0) ? rc : 0;
 105}
 106
 107static int kszphy_set_interrupt(struct phy_device *phydev)
 108{
 109        int temp;
 110        temp = (PHY_INTERRUPT_ENABLED == phydev->interrupts) ?
 111                KSZPHY_INTCS_ALL : 0;
 112        return phy_write(phydev, MII_KSZPHY_INTCS, temp);
 113}
 114
 115static int kszphy_config_intr(struct phy_device *phydev)
 116{
 117        int temp, rc;
 118
 119        /* set the interrupt pin active low */
 120        temp = phy_read(phydev, MII_KSZPHY_CTRL);
 121        temp &= ~KSZPHY_CTRL_INT_ACTIVE_HIGH;
 122        phy_write(phydev, MII_KSZPHY_CTRL, temp);
 123        rc = kszphy_set_interrupt(phydev);
 124        return rc < 0 ? rc : 0;
 125}
 126
 127static int ksz9021_config_intr(struct phy_device *phydev)
 128{
 129        int temp, rc;
 130
 131        /* set the interrupt pin active low */
 132        temp = phy_read(phydev, MII_KSZPHY_CTRL);
 133        temp &= ~KSZ9021_CTRL_INT_ACTIVE_HIGH;
 134        phy_write(phydev, MII_KSZPHY_CTRL, temp);
 135        rc = kszphy_set_interrupt(phydev);
 136        return rc < 0 ? rc : 0;
 137}
 138
 139static int ks8737_config_intr(struct phy_device *phydev)
 140{
 141        int temp, rc;
 142
 143        /* set the interrupt pin active low */
 144        temp = phy_read(phydev, MII_KSZPHY_CTRL);
 145        temp &= ~KS8737_CTRL_INT_ACTIVE_HIGH;
 146        phy_write(phydev, MII_KSZPHY_CTRL, temp);
 147        rc = kszphy_set_interrupt(phydev);
 148        return rc < 0 ? rc : 0;
 149}
 150
 151static int kszphy_config_init(struct phy_device *phydev)
 152{
 153        return 0;
 154}
 155
 156static int ksz8021_config_init(struct phy_device *phydev)
 157{
 158        int rc;
 159        const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
 160        phy_write(phydev, MII_KSZPHY_OMSO, val);
 161        rc = ksz_config_flags(phydev);
 162        return rc < 0 ? rc : 0;
 163}
 164
 165static int ks8051_config_init(struct phy_device *phydev)
 166{
 167        int rc;
 168
 169        rc = ksz_config_flags(phydev);
 170        return rc < 0 ? rc : 0;
 171}
 172
 173static int ksz9021_load_values_from_of(struct phy_device *phydev,
 174                                       struct device_node *of_node, u16 reg,
 175                                       char *field1, char *field2,
 176                                       char *field3, char *field4)
 177{
 178        int val1 = -1;
 179        int val2 = -2;
 180        int val3 = -3;
 181        int val4 = -4;
 182        int newval;
 183        int matches = 0;
 184
 185        if (!of_property_read_u32(of_node, field1, &val1))
 186                matches++;
 187
 188        if (!of_property_read_u32(of_node, field2, &val2))
 189                matches++;
 190
 191        if (!of_property_read_u32(of_node, field3, &val3))
 192                matches++;
 193
 194        if (!of_property_read_u32(of_node, field4, &val4))
 195                matches++;
 196
 197        if (!matches)
 198                return 0;
 199
 200        if (matches < 4)
 201                newval = kszphy_extended_read(phydev, reg);
 202        else
 203                newval = 0;
 204
 205        if (val1 != -1)
 206                newval = ((newval & 0xfff0) | ((val1 / PS_TO_REG) & 0xf) << 0);
 207
 208        if (val2 != -1)
 209                newval = ((newval & 0xff0f) | ((val2 / PS_TO_REG) & 0xf) << 4);
 210
 211        if (val3 != -1)
 212                newval = ((newval & 0xf0ff) | ((val3 / PS_TO_REG) & 0xf) << 8);
 213
 214        if (val4 != -1)
 215                newval = ((newval & 0x0fff) | ((val4 / PS_TO_REG) & 0xf) << 12);
 216
 217        return kszphy_extended_write(phydev, reg, newval);
 218}
 219
 220static int ksz9021_config_init(struct phy_device *phydev)
 221{
 222        struct device *dev = &phydev->dev;
 223        struct device_node *of_node = dev->of_node;
 224
 225        if (!of_node && dev->parent->of_node)
 226                of_node = dev->parent->of_node;
 227
 228        if (of_node) {
 229                ksz9021_load_values_from_of(phydev, of_node,
 230                                    MII_KSZPHY_CLK_CONTROL_PAD_SKEW,
 231                                    "txen-skew-ps", "txc-skew-ps",
 232                                    "rxdv-skew-ps", "rxc-skew-ps");
 233                ksz9021_load_values_from_of(phydev, of_node,
 234                                    MII_KSZPHY_RX_DATA_PAD_SKEW,
 235                                    "rxd0-skew-ps", "rxd1-skew-ps",
 236                                    "rxd2-skew-ps", "rxd3-skew-ps");
 237                ksz9021_load_values_from_of(phydev, of_node,
 238                                    MII_KSZPHY_TX_DATA_PAD_SKEW,
 239                                    "txd0-skew-ps", "txd1-skew-ps",
 240                                    "txd2-skew-ps", "txd3-skew-ps");
 241        }
 242        return 0;
 243}
 244
 245#define KSZ8873MLL_GLOBAL_CONTROL_4     0x06
 246#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX      (1 << 6)
 247#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED       (1 << 4)
 248static int ksz8873mll_read_status(struct phy_device *phydev)
 249{
 250        int regval;
 251
 252        /* dummy read */
 253        regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
 254
 255        regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
 256
 257        if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX)
 258                phydev->duplex = DUPLEX_HALF;
 259        else
 260                phydev->duplex = DUPLEX_FULL;
 261
 262        if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED)
 263                phydev->speed = SPEED_10;
 264        else
 265                phydev->speed = SPEED_100;
 266
 267        phydev->link = 1;
 268        phydev->pause = phydev->asym_pause = 0;
 269
 270        return 0;
 271}
 272
 273static int ksz8873mll_config_aneg(struct phy_device *phydev)
 274{
 275        return 0;
 276}
 277
 278static struct phy_driver ksphy_driver[] = {
 279{
 280        .phy_id         = PHY_ID_KS8737,
 281        .phy_id_mask    = 0x00fffff0,
 282        .name           = "Micrel KS8737",
 283        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
 284        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 285        .config_init    = kszphy_config_init,
 286        .config_aneg    = genphy_config_aneg,
 287        .read_status    = genphy_read_status,
 288        .ack_interrupt  = kszphy_ack_interrupt,
 289        .config_intr    = ks8737_config_intr,
 290        .suspend        = genphy_suspend,
 291        .resume         = genphy_resume,
 292        .driver         = { .owner = THIS_MODULE,},
 293}, {
 294        .phy_id         = PHY_ID_KSZ8021,
 295        .phy_id_mask    = 0x00ffffff,
 296        .name           = "Micrel KSZ8021 or KSZ8031",
 297        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
 298                           SUPPORTED_Asym_Pause),
 299        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 300        .config_init    = ksz8021_config_init,
 301        .config_aneg    = genphy_config_aneg,
 302        .read_status    = genphy_read_status,
 303        .ack_interrupt  = kszphy_ack_interrupt,
 304        .config_intr    = kszphy_config_intr,
 305        .suspend        = genphy_suspend,
 306        .resume         = genphy_resume,
 307        .driver         = { .owner = THIS_MODULE,},
 308}, {
 309        .phy_id         = PHY_ID_KSZ8031,
 310        .phy_id_mask    = 0x00ffffff,
 311        .name           = "Micrel KSZ8031",
 312        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
 313                           SUPPORTED_Asym_Pause),
 314        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 315        .config_init    = ksz8021_config_init,
 316        .config_aneg    = genphy_config_aneg,
 317        .read_status    = genphy_read_status,
 318        .ack_interrupt  = kszphy_ack_interrupt,
 319        .config_intr    = kszphy_config_intr,
 320        .suspend        = genphy_suspend,
 321        .resume         = genphy_resume,
 322        .driver         = { .owner = THIS_MODULE,},
 323}, {
 324        .phy_id         = PHY_ID_KSZ8041,
 325        .phy_id_mask    = 0x00fffff0,
 326        .name           = "Micrel KSZ8041",
 327        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
 328                                | SUPPORTED_Asym_Pause),
 329        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 330        .config_init    = kszphy_config_init,
 331        .config_aneg    = genphy_config_aneg,
 332        .read_status    = genphy_read_status,
 333        .ack_interrupt  = kszphy_ack_interrupt,
 334        .config_intr    = kszphy_config_intr,
 335        .suspend        = genphy_suspend,
 336        .resume         = genphy_resume,
 337        .driver         = { .owner = THIS_MODULE,},
 338}, {
 339        .phy_id         = PHY_ID_KSZ8041RNLI,
 340        .phy_id_mask    = 0x00fffff0,
 341        .name           = "Micrel KSZ8041RNLI",
 342        .features       = PHY_BASIC_FEATURES |
 343                          SUPPORTED_Pause | SUPPORTED_Asym_Pause,
 344        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 345        .config_init    = kszphy_config_init,
 346        .config_aneg    = genphy_config_aneg,
 347        .read_status    = genphy_read_status,
 348        .ack_interrupt  = kszphy_ack_interrupt,
 349        .config_intr    = kszphy_config_intr,
 350        .suspend        = genphy_suspend,
 351        .resume         = genphy_resume,
 352        .driver         = { .owner = THIS_MODULE,},
 353}, {
 354        .phy_id         = PHY_ID_KSZ8051,
 355        .phy_id_mask    = 0x00fffff0,
 356        .name           = "Micrel KSZ8051",
 357        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
 358                                | SUPPORTED_Asym_Pause),
 359        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 360        .config_init    = ks8051_config_init,
 361        .config_aneg    = genphy_config_aneg,
 362        .read_status    = genphy_read_status,
 363        .ack_interrupt  = kszphy_ack_interrupt,
 364        .config_intr    = kszphy_config_intr,
 365        .suspend        = genphy_suspend,
 366        .resume         = genphy_resume,
 367        .driver         = { .owner = THIS_MODULE,},
 368}, {
 369        .phy_id         = PHY_ID_KSZ8001,
 370        .name           = "Micrel KSZ8001 or KS8721",
 371        .phy_id_mask    = 0x00ffffff,
 372        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
 373        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 374        .config_init    = kszphy_config_init,
 375        .config_aneg    = genphy_config_aneg,
 376        .read_status    = genphy_read_status,
 377        .ack_interrupt  = kszphy_ack_interrupt,
 378        .config_intr    = kszphy_config_intr,
 379        .suspend        = genphy_suspend,
 380        .resume         = genphy_resume,
 381        .driver         = { .owner = THIS_MODULE,},
 382}, {
 383        .phy_id         = PHY_ID_KSZ8081,
 384        .name           = "Micrel KSZ8081 or KSZ8091",
 385        .phy_id_mask    = 0x00fffff0,
 386        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
 387        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 388        .config_init    = kszphy_config_init,
 389        .config_aneg    = genphy_config_aneg,
 390        .read_status    = genphy_read_status,
 391        .ack_interrupt  = kszphy_ack_interrupt,
 392        .config_intr    = kszphy_config_intr,
 393        .suspend        = genphy_suspend,
 394        .resume         = genphy_resume,
 395        .driver         = { .owner = THIS_MODULE,},
 396}, {
 397        .phy_id         = PHY_ID_KSZ8061,
 398        .name           = "Micrel KSZ8061",
 399        .phy_id_mask    = 0x00fffff0,
 400        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
 401        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 402        .config_init    = kszphy_config_init,
 403        .config_aneg    = genphy_config_aneg,
 404        .read_status    = genphy_read_status,
 405        .ack_interrupt  = kszphy_ack_interrupt,
 406        .config_intr    = kszphy_config_intr,
 407        .suspend        = genphy_suspend,
 408        .resume         = genphy_resume,
 409        .driver         = { .owner = THIS_MODULE,},
 410}, {
 411        .phy_id         = PHY_ID_KSZ9021,
 412        .phy_id_mask    = 0x000ffffe,
 413        .name           = "Micrel KSZ9021 Gigabit PHY",
 414        .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
 415        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 416        .config_init    = ksz9021_config_init,
 417        .config_aneg    = genphy_config_aneg,
 418        .read_status    = genphy_read_status,
 419        .ack_interrupt  = kszphy_ack_interrupt,
 420        .config_intr    = ksz9021_config_intr,
 421        .suspend        = genphy_suspend,
 422        .resume         = genphy_resume,
 423        .driver         = { .owner = THIS_MODULE, },
 424}, {
 425        .phy_id         = PHY_ID_KSZ9031,
 426        .phy_id_mask    = 0x00fffff0,
 427        .name           = "Micrel KSZ9031 Gigabit PHY",
 428        .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause
 429                                | SUPPORTED_Asym_Pause),
 430        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 431        .config_init    = kszphy_config_init,
 432        .config_aneg    = genphy_config_aneg,
 433        .read_status    = genphy_read_status,
 434        .ack_interrupt  = kszphy_ack_interrupt,
 435        .config_intr    = ksz9021_config_intr,
 436        .suspend        = genphy_suspend,
 437        .resume         = genphy_resume,
 438        .driver         = { .owner = THIS_MODULE, },
 439}, {
 440        .phy_id         = PHY_ID_KSZ8873MLL,
 441        .phy_id_mask    = 0x00fffff0,
 442        .name           = "Micrel KSZ8873MLL Switch",
 443        .features       = (SUPPORTED_Pause | SUPPORTED_Asym_Pause),
 444        .flags          = PHY_HAS_MAGICANEG,
 445        .config_init    = kszphy_config_init,
 446        .config_aneg    = ksz8873mll_config_aneg,
 447        .read_status    = ksz8873mll_read_status,
 448        .suspend        = genphy_suspend,
 449        .resume         = genphy_resume,
 450        .driver         = { .owner = THIS_MODULE, },
 451}, {
 452        .phy_id         = PHY_ID_KSZ886X,
 453        .phy_id_mask    = 0x00fffff0,
 454        .name           = "Micrel KSZ886X Switch",
 455        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
 456        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
 457        .config_init    = kszphy_config_init,
 458        .config_aneg    = genphy_config_aneg,
 459        .read_status    = genphy_read_status,
 460        .suspend        = genphy_suspend,
 461        .resume         = genphy_resume,
 462        .driver         = { .owner = THIS_MODULE, },
 463} };
 464
 465static int __init ksphy_init(void)
 466{
 467        return phy_drivers_register(ksphy_driver,
 468                ARRAY_SIZE(ksphy_driver));
 469}
 470
 471static void __exit ksphy_exit(void)
 472{
 473        phy_drivers_unregister(ksphy_driver,
 474                ARRAY_SIZE(ksphy_driver));
 475}
 476
 477module_init(ksphy_init);
 478module_exit(ksphy_exit);
 479
 480MODULE_DESCRIPTION("Micrel PHY driver");
 481MODULE_AUTHOR("David J. Choi");
 482MODULE_LICENSE("GPL");
 483
 484static struct mdio_device_id __maybe_unused micrel_tbl[] = {
 485        { PHY_ID_KSZ9021, 0x000ffffe },
 486        { PHY_ID_KSZ9031, 0x00fffff0 },
 487        { PHY_ID_KSZ8001, 0x00ffffff },
 488        { PHY_ID_KS8737, 0x00fffff0 },
 489        { PHY_ID_KSZ8021, 0x00ffffff },
 490        { PHY_ID_KSZ8031, 0x00ffffff },
 491        { PHY_ID_KSZ8041, 0x00fffff0 },
 492        { PHY_ID_KSZ8051, 0x00fffff0 },
 493        { PHY_ID_KSZ8061, 0x00fffff0 },
 494        { PHY_ID_KSZ8081, 0x00fffff0 },
 495        { PHY_ID_KSZ8873MLL, 0x00fffff0 },
 496        { PHY_ID_KSZ886X, 0x00fffff0 },
 497        { }
 498};
 499
 500MODULE_DEVICE_TABLE(mdio, micrel_tbl);
 501