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