uboot/drivers/net/phy/miiphybb.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2009 Industrie Dial Face S.p.A.
   3 * Luigi 'Comio' Mantellini <luigi.mantellini@idf-hit.com>
   4 *
   5 * (C) Copyright 2001
   6 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
   7 *
   8 * SPDX-License-Identifier:     GPL-2.0+
   9 */
  10
  11/*
  12 * This provides a bit-banged interface to the ethernet MII management
  13 * channel.
  14 */
  15
  16#include <common.h>
  17#include <ioports.h>
  18#include <ppc_asm.tmpl>
  19#include <miiphy.h>
  20
  21#define BB_MII_RELOCATE(v,off) (v += (v?off:0))
  22
  23DECLARE_GLOBAL_DATA_PTR;
  24
  25#ifndef CONFIG_BITBANGMII_MULTI
  26
  27/*
  28 * If CONFIG_BITBANGMII_MULTI is not defined we use a
  29 * compatibility layer with the previous miiphybb implementation
  30 * based on macros usage.
  31 *
  32 */
  33static int bb_mii_init_wrap(struct bb_miiphy_bus *bus)
  34{
  35#ifdef MII_INIT
  36        MII_INIT;
  37#endif
  38        return 0;
  39}
  40
  41static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus)
  42{
  43#ifdef MDIO_DECLARE
  44        MDIO_DECLARE;
  45#endif
  46        MDIO_ACTIVE;
  47        return 0;
  48}
  49
  50static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus)
  51{
  52#ifdef MDIO_DECLARE
  53        MDIO_DECLARE;
  54#endif
  55        MDIO_TRISTATE;
  56        return 0;
  57}
  58
  59static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v)
  60{
  61#ifdef MDIO_DECLARE
  62        MDIO_DECLARE;
  63#endif
  64        MDIO(v);
  65        return 0;
  66}
  67
  68static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v)
  69{
  70#ifdef MDIO_DECLARE
  71        MDIO_DECLARE;
  72#endif
  73        *v = MDIO_READ;
  74        return 0;
  75}
  76
  77static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v)
  78{
  79#ifdef MDC_DECLARE
  80        MDC_DECLARE;
  81#endif
  82        MDC(v);
  83        return 0;
  84}
  85
  86static int bb_delay_wrap(struct bb_miiphy_bus *bus)
  87{
  88        MIIDELAY;
  89        return 0;
  90}
  91
  92struct bb_miiphy_bus bb_miiphy_buses[] = {
  93        {
  94                .name = BB_MII_DEVNAME,
  95                .init = bb_mii_init_wrap,
  96                .mdio_active = bb_mdio_active_wrap,
  97                .mdio_tristate = bb_mdio_tristate_wrap,
  98                .set_mdio = bb_set_mdio_wrap,
  99                .get_mdio = bb_get_mdio_wrap,
 100                .set_mdc = bb_set_mdc_wrap,
 101                .delay = bb_delay_wrap,
 102        }
 103};
 104
 105int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) /
 106                          sizeof(bb_miiphy_buses[0]);
 107#endif
 108
 109void bb_miiphy_init(void)
 110{
 111        int i;
 112
 113        for (i = 0; i < bb_miiphy_buses_num; i++) {
 114#if defined(CONFIG_NEEDS_MANUAL_RELOC)
 115                /* Relocate the hook pointers*/
 116                BB_MII_RELOCATE(bb_miiphy_buses[i].init, gd->reloc_off);
 117                BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_active, gd->reloc_off);
 118                BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_tristate, gd->reloc_off);
 119                BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdio, gd->reloc_off);
 120                BB_MII_RELOCATE(bb_miiphy_buses[i].get_mdio, gd->reloc_off);
 121                BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdc, gd->reloc_off);
 122                BB_MII_RELOCATE(bb_miiphy_buses[i].delay, gd->reloc_off);
 123#endif
 124                if (bb_miiphy_buses[i].init != NULL) {
 125                        bb_miiphy_buses[i].init(&bb_miiphy_buses[i]);
 126                }
 127        }
 128}
 129
 130static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
 131{
 132#ifdef CONFIG_BITBANGMII_MULTI
 133        int i;
 134
 135        /* Search the correct bus */
 136        for (i = 0; i < bb_miiphy_buses_num; i++) {
 137                if (!strcmp(bb_miiphy_buses[i].name, devname)) {
 138                        return &bb_miiphy_buses[i];
 139                }
 140        }
 141        return NULL;
 142#else
 143        /* We have just one bitbanging bus */
 144        return &bb_miiphy_buses[0];
 145#endif
 146}
 147
 148/*****************************************************************************
 149 *
 150 * Utility to send the preamble, address, and register (common to read
 151 * and write).
 152 */
 153static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
 154                       unsigned char addr, unsigned char reg)
 155{
 156        int j;
 157
 158        /*
 159         * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
 160         * The IEEE spec says this is a PHY optional requirement.  The AMD
 161         * 79C874 requires one after power up and one after a MII communications
 162         * error.  This means that we are doing more preambles than we need,
 163         * but it is safer and will be much more robust.
 164         */
 165
 166        bus->mdio_active(bus);
 167        bus->set_mdio(bus, 1);
 168        for (j = 0; j < 32; j++) {
 169                bus->set_mdc(bus, 0);
 170                bus->delay(bus);
 171                bus->set_mdc(bus, 1);
 172                bus->delay(bus);
 173        }
 174
 175        /* send the start bit (01) and the read opcode (10) or write (10) */
 176        bus->set_mdc(bus, 0);
 177        bus->set_mdio(bus, 0);
 178        bus->delay(bus);
 179        bus->set_mdc(bus, 1);
 180        bus->delay(bus);
 181        bus->set_mdc(bus, 0);
 182        bus->set_mdio(bus, 1);
 183        bus->delay(bus);
 184        bus->set_mdc(bus, 1);
 185        bus->delay(bus);
 186        bus->set_mdc(bus, 0);
 187        bus->set_mdio(bus, read);
 188        bus->delay(bus);
 189        bus->set_mdc(bus, 1);
 190        bus->delay(bus);
 191        bus->set_mdc(bus, 0);
 192        bus->set_mdio(bus, !read);
 193        bus->delay(bus);
 194        bus->set_mdc(bus, 1);
 195        bus->delay(bus);
 196
 197        /* send the PHY address */
 198        for (j = 0; j < 5; j++) {
 199                bus->set_mdc(bus, 0);
 200                if ((addr & 0x10) == 0) {
 201                        bus->set_mdio(bus, 0);
 202                } else {
 203                        bus->set_mdio(bus, 1);
 204                }
 205                bus->delay(bus);
 206                bus->set_mdc(bus, 1);
 207                bus->delay(bus);
 208                addr <<= 1;
 209        }
 210
 211        /* send the register address */
 212        for (j = 0; j < 5; j++) {
 213                bus->set_mdc(bus, 0);
 214                if ((reg & 0x10) == 0) {
 215                        bus->set_mdio(bus, 0);
 216                } else {
 217                        bus->set_mdio(bus, 1);
 218                }
 219                bus->delay(bus);
 220                bus->set_mdc(bus, 1);
 221                bus->delay(bus);
 222                reg <<= 1;
 223        }
 224}
 225
 226/*****************************************************************************
 227 *
 228 * Read a MII PHY register.
 229 *
 230 * Returns:
 231 *   0 on success
 232 */
 233int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
 234{
 235        short rdreg; /* register working value */
 236        int v;
 237        int j; /* counter */
 238        struct bb_miiphy_bus *bus;
 239
 240        bus = bb_miiphy_getbus(miidev->name);
 241        if (bus == NULL) {
 242                return -1;
 243        }
 244
 245        miiphy_pre (bus, 1, addr, reg);
 246
 247        /* tri-state our MDIO I/O pin so we can read */
 248        bus->set_mdc(bus, 0);
 249        bus->mdio_tristate(bus);
 250        bus->delay(bus);
 251        bus->set_mdc(bus, 1);
 252        bus->delay(bus);
 253
 254        /* check the turnaround bit: the PHY should be driving it to zero */
 255        bus->get_mdio(bus, &v);
 256        if (v != 0) {
 257                /* puts ("PHY didn't drive TA low\n"); */
 258                for (j = 0; j < 32; j++) {
 259                        bus->set_mdc(bus, 0);
 260                        bus->delay(bus);
 261                        bus->set_mdc(bus, 1);
 262                        bus->delay(bus);
 263                }
 264                /* There is no PHY, return */
 265                return -1;
 266        }
 267
 268        bus->set_mdc(bus, 0);
 269        bus->delay(bus);
 270
 271        /* read 16 bits of register data, MSB first */
 272        rdreg = 0;
 273        for (j = 0; j < 16; j++) {
 274                bus->set_mdc(bus, 1);
 275                bus->delay(bus);
 276                rdreg <<= 1;
 277                bus->get_mdio(bus, &v);
 278                rdreg |= (v & 0x1);
 279                bus->set_mdc(bus, 0);
 280                bus->delay(bus);
 281        }
 282
 283        bus->set_mdc(bus, 1);
 284        bus->delay(bus);
 285        bus->set_mdc(bus, 0);
 286        bus->delay(bus);
 287        bus->set_mdc(bus, 1);
 288        bus->delay(bus);
 289
 290#ifdef DEBUG
 291        printf("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, rdreg);
 292#endif
 293
 294        return rdreg;
 295}
 296
 297
 298/*****************************************************************************
 299 *
 300 * Write a MII PHY register.
 301 *
 302 * Returns:
 303 *   0 on success
 304 */
 305int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
 306                    u16 value)
 307{
 308        struct bb_miiphy_bus *bus;
 309        int j;                  /* counter */
 310
 311        bus = bb_miiphy_getbus(miidev->name);
 312        if (bus == NULL) {
 313                /* Bus not found! */
 314                return -1;
 315        }
 316
 317        miiphy_pre (bus, 0, addr, reg);
 318
 319        /* send the turnaround (10) */
 320        bus->set_mdc(bus, 0);
 321        bus->set_mdio(bus, 1);
 322        bus->delay(bus);
 323        bus->set_mdc(bus, 1);
 324        bus->delay(bus);
 325        bus->set_mdc(bus, 0);
 326        bus->set_mdio(bus, 0);
 327        bus->delay(bus);
 328        bus->set_mdc(bus, 1);
 329        bus->delay(bus);
 330
 331        /* write 16 bits of register data, MSB first */
 332        for (j = 0; j < 16; j++) {
 333                bus->set_mdc(bus, 0);
 334                if ((value & 0x00008000) == 0) {
 335                        bus->set_mdio(bus, 0);
 336                } else {
 337                        bus->set_mdio(bus, 1);
 338                }
 339                bus->delay(bus);
 340                bus->set_mdc(bus, 1);
 341                bus->delay(bus);
 342                value <<= 1;
 343        }
 344
 345        /*
 346         * Tri-state the MDIO line.
 347         */
 348        bus->mdio_tristate(bus);
 349        bus->set_mdc(bus, 0);
 350        bus->delay(bus);
 351        bus->set_mdc(bus, 1);
 352        bus->delay(bus);
 353
 354        return 0;
 355}
 356