November 18, 2024

An ethernet driver for Zynq on Kyu - Round 3 -- Phy coding

I woke up thinking that I knew enough to try something basic with Zynq phy/mdio.

The Zynq has a single register to access the MDIO bus with our phy chip on it. Why a bus is needed and why there would ever be more than one phy device ever attached to an ethernet controller is beyond me, but that is how they did it.

The single register they call "phymntnc" implements what I like to call a "knothole" interface to the Phy device. The device actually has 32 registers, each 16 bits in size. You use the 32 bit phymntnc register to select a register and read or write to it. What I do is to write two routines like so:

phy_read ( reg )
phy_write ( reg, val )
These hide all the details and let me just think in terms of reading or writing to selected mdio registers. The first game is to read registers 2 and 3 which hold the two halves of a 32 bit Phy device ID. It turns out that mdio address "1" talks to my device (I get lucky and discover that on the second try) and after only about an hour of work, I get:
Phy ID = 362 5e62
This is exactly the Phy device ID specified in the Broadcom B50612D datasheet I was lucky enough to find. This surprises me somewhat because my chip is labelled B50612E -- but it is actually a nice surprise as it greatly raises my confidence that the "D" datasheet (all 161 pages of it) will be relevant to my "E" part. Writing code to just read the device ID is a significant milestone as It is my first code accessing any part of the Zynq ethernet and also demonstrates that I can access the phy device. The rest of the Phy business may be easy.

MDIO registers

I took a look at my Phy driver for the Allwinner H3 (orange pi). Here the Phy is on chip and 10/100. The register layout looks very much like what the Broadcom datasheet shows. There is apparently a standard set of registers that may be all that I need to work with.
/* Here are the registers in the PHY device that we use.
 * The first PHY registers are standardized and device
 * independent
 */
#define PHY_BMCR        0
#define PHY_BMSR        1
#define PHY_ID1         2
#define PHY_ID2         3
#define PHY_ADVERT      4
#define PHY_PEER        5
The above code is from the H3 driver. I only needed to work with the first 6 registers. A likely thing to try is just copying that code, making the minimal changes I need to make, and then seeing if I can get it to do autonegotiation. I have not yet written phy_write() but it will be an all but trivial change to phy_read(). Of course I made one mistake doing the change, but caught and fixed it quickly.

A lazy option

We could just ignore the Phy and rely on the configuration done by U-Boot. It has just successfully been used to boot Kyu, so it is ready to go. This would allow us to move on to working on receiving and sending network packets and all the code that deals with the network controller.

The danger of course is that it will be forgotten and never get done. Then someday someone will decide to boot Kyu from NAND memory or an SD card and the network won't work because the Phy was never configured. And I will have forgotten all that I have just refreshed my mind on and will have to start over from scratch.


Feedback? Questions? Drop me a line!

Tom's Computer Info / [email protected]