mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-25 04:00:53 +07:00
00db8189d9
ethernet drivers to remain as ignorant as is reasonable of the connected PHY's design and operation details. Signed-off-by: Andy Fleming <afleming@freescale.com> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
289 lines
12 KiB
Plaintext
289 lines
12 KiB
Plaintext
|
|
-------
|
|
PHY Abstraction Layer
|
|
(Updated 2005-07-21)
|
|
|
|
Purpose
|
|
|
|
Most network devices consist of set of registers which provide an interface
|
|
to a MAC layer, which communicates with the physical connection through a
|
|
PHY. The PHY concerns itself with negotiating link parameters with the link
|
|
partner on the other side of the network connection (typically, an ethernet
|
|
cable), and provides a register interface to allow drivers to determine what
|
|
settings were chosen, and to configure what settings are allowed.
|
|
|
|
While these devices are distinct from the network devices, and conform to a
|
|
standard layout for the registers, it has been common practice to integrate
|
|
the PHY management code with the network driver. This has resulted in large
|
|
amounts of redundant code. Also, on embedded systems with multiple (and
|
|
sometimes quite different) ethernet controllers connected to the same
|
|
management bus, it is difficult to ensure safe use of the bus.
|
|
|
|
Since the PHYs are devices, and the management busses through which they are
|
|
accessed are, in fact, busses, the PHY Abstraction Layer treats them as such.
|
|
In doing so, it has these goals:
|
|
|
|
1) Increase code-reuse
|
|
2) Increase overall code-maintainability
|
|
3) Speed development time for new network drivers, and for new systems
|
|
|
|
Basically, this layer is meant to provide an interface to PHY devices which
|
|
allows network driver writers to write as little code as possible, while
|
|
still providing a full feature set.
|
|
|
|
The MDIO bus
|
|
|
|
Most network devices are connected to a PHY by means of a management bus.
|
|
Different devices use different busses (though some share common interfaces).
|
|
In order to take advantage of the PAL, each bus interface needs to be
|
|
registered as a distinct device.
|
|
|
|
1) read and write functions must be implemented. Their prototypes are:
|
|
|
|
int write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
|
|
int read(struct mii_bus *bus, int mii_id, int regnum);
|
|
|
|
mii_id is the address on the bus for the PHY, and regnum is the register
|
|
number. These functions are guaranteed not to be called from interrupt
|
|
time, so it is safe for them to block, waiting for an interrupt to signal
|
|
the operation is complete
|
|
|
|
2) A reset function is necessary. This is used to return the bus to an
|
|
initialized state.
|
|
|
|
3) A probe function is needed. This function should set up anything the bus
|
|
driver needs, setup the mii_bus structure, and register with the PAL using
|
|
mdiobus_register. Similarly, there's a remove function to undo all of
|
|
that (use mdiobus_unregister).
|
|
|
|
4) Like any driver, the device_driver structure must be configured, and init
|
|
exit functions are used to register the driver.
|
|
|
|
5) The bus must also be declared somewhere as a device, and registered.
|
|
|
|
As an example for how one driver implemented an mdio bus driver, see
|
|
drivers/net/gianfar_mii.c and arch/ppc/syslib/mpc85xx_devices.c
|
|
|
|
Connecting to a PHY
|
|
|
|
Sometime during startup, the network driver needs to establish a connection
|
|
between the PHY device, and the network device. At this time, the PHY's bus
|
|
and drivers need to all have been loaded, so it is ready for the connection.
|
|
At this point, there are several ways to connect to the PHY:
|
|
|
|
1) The PAL handles everything, and only calls the network driver when
|
|
the link state changes, so it can react.
|
|
|
|
2) The PAL handles everything except interrupts (usually because the
|
|
controller has the interrupt registers).
|
|
|
|
3) The PAL handles everything, but checks in with the driver every second,
|
|
allowing the network driver to react first to any changes before the PAL
|
|
does.
|
|
|
|
4) The PAL serves only as a library of functions, with the network device
|
|
manually calling functions to update status, and configure the PHY
|
|
|
|
|
|
Letting the PHY Abstraction Layer do Everything
|
|
|
|
If you choose option 1 (The hope is that every driver can, but to still be
|
|
useful to drivers that can't), connecting to the PHY is simple:
|
|
|
|
First, you need a function to react to changes in the link state. This
|
|
function follows this protocol:
|
|
|
|
static void adjust_link(struct net_device *dev);
|
|
|
|
Next, you need to know the device name of the PHY connected to this device.
|
|
The name will look something like, "phy0:0", where the first number is the
|
|
bus id, and the second is the PHY's address on that bus.
|
|
|
|
Now, to connect, just call this function:
|
|
|
|
phydev = phy_connect(dev, phy_name, &adjust_link, flags);
|
|
|
|
phydev is a pointer to the phy_device structure which represents the PHY. If
|
|
phy_connect is successful, it will return the pointer. dev, here, is the
|
|
pointer to your net_device. Once done, this function will have started the
|
|
PHY's software state machine, and registered for the PHY's interrupt, if it
|
|
has one. The phydev structure will be populated with information about the
|
|
current state, though the PHY will not yet be truly operational at this
|
|
point.
|
|
|
|
flags is a u32 which can optionally contain phy-specific flags.
|
|
This is useful if the system has put hardware restrictions on
|
|
the PHY/controller, of which the PHY needs to be aware.
|
|
|
|
Now just make sure that phydev->supported and phydev->advertising have any
|
|
values pruned from them which don't make sense for your controller (a 10/100
|
|
controller may be connected to a gigabit capable PHY, so you would need to
|
|
mask off SUPPORTED_1000baseT*). See include/linux/ethtool.h for definitions
|
|
for these bitfields. Note that you should not SET any bits, or the PHY may
|
|
get put into an unsupported state.
|
|
|
|
Lastly, once the controller is ready to handle network traffic, you call
|
|
phy_start(phydev). This tells the PAL that you are ready, and configures the
|
|
PHY to connect to the network. If you want to handle your own interrupts,
|
|
just set phydev->irq to PHY_IGNORE_INTERRUPT before you call phy_start.
|
|
Similarly, if you don't want to use interrupts, set phydev->irq to PHY_POLL.
|
|
|
|
When you want to disconnect from the network (even if just briefly), you call
|
|
phy_stop(phydev).
|
|
|
|
Keeping Close Tabs on the PAL
|
|
|
|
It is possible that the PAL's built-in state machine needs a little help to
|
|
keep your network device and the PHY properly in sync. If so, you can
|
|
register a helper function when connecting to the PHY, which will be called
|
|
every second before the state machine reacts to any changes. To do this, you
|
|
need to manually call phy_attach() and phy_prepare_link(), and then call
|
|
phy_start_machine() with the second argument set to point to your special
|
|
handler.
|
|
|
|
Currently there are no examples of how to use this functionality, and testing
|
|
on it has been limited because the author does not have any drivers which use
|
|
it (they all use option 1). So Caveat Emptor.
|
|
|
|
Doing it all yourself
|
|
|
|
There's a remote chance that the PAL's built-in state machine cannot track
|
|
the complex interactions between the PHY and your network device. If this is
|
|
so, you can simply call phy_attach(), and not call phy_start_machine or
|
|
phy_prepare_link(). This will mean that phydev->state is entirely yours to
|
|
handle (phy_start and phy_stop toggle between some of the states, so you
|
|
might need to avoid them).
|
|
|
|
An effort has been made to make sure that useful functionality can be
|
|
accessed without the state-machine running, and most of these functions are
|
|
descended from functions which did not interact with a complex state-machine.
|
|
However, again, no effort has been made so far to test running without the
|
|
state machine, so tryer beware.
|
|
|
|
Here is a brief rundown of the functions:
|
|
|
|
int phy_read(struct phy_device *phydev, u16 regnum);
|
|
int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
|
|
|
|
Simple read/write primitives. They invoke the bus's read/write function
|
|
pointers.
|
|
|
|
void phy_print_status(struct phy_device *phydev);
|
|
|
|
A convenience function to print out the PHY status neatly.
|
|
|
|
int phy_clear_interrupt(struct phy_device *phydev);
|
|
int phy_config_interrupt(struct phy_device *phydev, u32 interrupts);
|
|
|
|
Clear the PHY's interrupt, and configure which ones are allowed,
|
|
respectively. Currently only supports all on, or all off.
|
|
|
|
int phy_enable_interrupts(struct phy_device *phydev);
|
|
int phy_disable_interrupts(struct phy_device *phydev);
|
|
|
|
Functions which enable/disable PHY interrupts, clearing them
|
|
before and after, respectively.
|
|
|
|
int phy_start_interrupts(struct phy_device *phydev);
|
|
int phy_stop_interrupts(struct phy_device *phydev);
|
|
|
|
Requests the IRQ for the PHY interrupts, then enables them for
|
|
start, or disables then frees them for stop.
|
|
|
|
struct phy_device * phy_attach(struct net_device *dev, const char *phy_id,
|
|
u32 flags);
|
|
|
|
Attaches a network device to a particular PHY, binding the PHY to a generic
|
|
driver if none was found during bus initialization. Passes in
|
|
any phy-specific flags as needed.
|
|
|
|
int phy_start_aneg(struct phy_device *phydev);
|
|
|
|
Using variables inside the phydev structure, either configures advertising
|
|
and resets autonegotiation, or disables autonegotiation, and configures
|
|
forced settings.
|
|
|
|
static inline int phy_read_status(struct phy_device *phydev);
|
|
|
|
Fills the phydev structure with up-to-date information about the current
|
|
settings in the PHY.
|
|
|
|
void phy_sanitize_settings(struct phy_device *phydev)
|
|
|
|
Resolves differences between currently desired settings, and
|
|
supported settings for the given PHY device. Does not make
|
|
the changes in the hardware, though.
|
|
|
|
int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
|
|
int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);
|
|
|
|
Ethtool convenience functions.
|
|
|
|
int phy_mii_ioctl(struct phy_device *phydev,
|
|
struct mii_ioctl_data *mii_data, int cmd);
|
|
|
|
The MII ioctl. Note that this function will completely screw up the state
|
|
machine if you write registers like BMCR, BMSR, ADVERTISE, etc. Best to
|
|
use this only to write registers which are not standard, and don't set off
|
|
a renegotiation.
|
|
|
|
|
|
PHY Device Drivers
|
|
|
|
With the PHY Abstraction Layer, adding support for new PHYs is
|
|
quite easy. In some cases, no work is required at all! However,
|
|
many PHYs require a little hand-holding to get up-and-running.
|
|
|
|
Generic PHY driver
|
|
|
|
If the desired PHY doesn't have any errata, quirks, or special
|
|
features you want to support, then it may be best to not add
|
|
support, and let the PHY Abstraction Layer's Generic PHY Driver
|
|
do all of the work.
|
|
|
|
Writing a PHY driver
|
|
|
|
If you do need to write a PHY driver, the first thing to do is
|
|
make sure it can be matched with an appropriate PHY device.
|
|
This is done during bus initialization by reading the device's
|
|
UID (stored in registers 2 and 3), then comparing it to each
|
|
driver's phy_id field by ANDing it with each driver's
|
|
phy_id_mask field. Also, it needs a name. Here's an example:
|
|
|
|
static struct phy_driver dm9161_driver = {
|
|
.phy_id = 0x0181b880,
|
|
.name = "Davicom DM9161E",
|
|
.phy_id_mask = 0x0ffffff0,
|
|
...
|
|
}
|
|
|
|
Next, you need to specify what features (speed, duplex, autoneg,
|
|
etc) your PHY device and driver support. Most PHYs support
|
|
PHY_BASIC_FEATURES, but you can look in include/mii.h for other
|
|
features.
|
|
|
|
Each driver consists of a number of function pointers:
|
|
|
|
config_init: configures PHY into a sane state after a reset.
|
|
For instance, a Davicom PHY requires descrambling disabled.
|
|
probe: Does any setup needed by the driver
|
|
suspend/resume: power management
|
|
config_aneg: Changes the speed/duplex/negotiation settings
|
|
read_status: Reads the current speed/duplex/negotiation settings
|
|
ack_interrupt: Clear a pending interrupt
|
|
config_intr: Enable or disable interrupts
|
|
remove: Does any driver take-down
|
|
|
|
Of these, only config_aneg and read_status are required to be
|
|
assigned by the driver code. The rest are optional. Also, it is
|
|
preferred to use the generic phy driver's versions of these two
|
|
functions if at all possible: genphy_read_status and
|
|
genphy_config_aneg. If this is not possible, it is likely that
|
|
you only need to perform some actions before and after invoking
|
|
these functions, and so your functions will wrap the generic
|
|
ones.
|
|
|
|
Feel free to look at the Marvell, Cicada, and Davicom drivers in
|
|
drivers/net/phy/ for examples (the lxt and qsemi drivers have
|
|
not been tested as of this writing)
|