本文以imx8为例,呈现其phy驱动中主要涉及的文件以及数据结构。phy驱动涉及的数据结构其实本身并不复杂,在整个网络通路中phy处于数据链路层,主要完成物理信号转换的工作,将接收信号传递给MAC或者将MAC过来的数据进行转换后输出到物理链路上。关于什么是mdio,rgmii,sgmii,gmii,mii可以进行百度,了解下其概念即可。对整个网络通路上的设备以及其链接方式有非常明确的了解对理解网络来说也是好事一件。mac通过mdio/mdc对phy中的寄存器进行读写操纵,其读写设计到两种常见的协议C22和C45。一般工业以太phy使用C22,车载以太使用C45。它们之间的差异,可以自行进行百度。下面涉及到的数据结构不用全部了解,但是需要清楚它们之间的包含关系,具体成员含义等到具体代码时再进行分析。
1、简单概念
下面是几个很重要的概念,对于理解phy驱动的框架很重要。
mii_bus,mii介质无关总线,mdio_bus是mii的一部分,但是在驱动软件设计上应该是特指mdc/mdio。
mdio_bus,用于mdio通讯的总线。
mdio_device,mdio设备指能够通过mdio进行访问的设备,如phy设备,switch设备等。
phy_device,phy设备必然能够通过mdio进行访问,phy设备必然是mdio设备,所以在phy_device结构体中嵌入了mdio_device结构。
2、关键数据结构
/include/linux/phy.h
#define PHY_MAX_ADDR 32
struct mii_bus {
struct module *owner;
const char *name;
char id[MII_BUS_ID_SIZE];//61
void *priv; //指向mdiobus_alloc_size()额外分配的空间
int (*read)(struct mii_bus *bus, int addr, int regnum);
int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
int (*reset)(struct mii_bus *bus);
/*A lock to ensure that only one thing can read/write the MDIO bus at a time*/
struct mutex mdio_lock;
struct device *parent;
enum {
MDIOBUS_ALLOCATED = 1,
MDIOBUS_REGISTERED,
MDIOBUS_UNREGISTERED,
MDIOBUS_RELEASED,
} state;
struct device dev;
/* list of all PHYs on bus */
struct mdio_device *mdio_map[PHY_MAX_ADDR];//32
/* PHY addresses to be ignored when probing */
u32 phy_mask;
/* PHY addresses to ignore the TA/read failure */
u32 phy_ignore_ta_mask;
/* An array of interrupts, each PHY's interrupt at the index matching its address*/
int irq[PHY_MAX_ADDR];
/* GPIO reset pulse width in microseconds */
int reset_delay_us;
/* RESET GPIO descriptor pointer */
struct gpio_desc *reset_gpiod;
};
struct mdio_driver_common {
struct device_driver driver;
int flags;
};
//mdio驱动结构
struct mdio_driver {
struct mdio_driver_common mdiodrv;
/*Called during discovery. Used to set up device-specific structures, if any*/
int (*probe)(struct mdio_device *mdiodev);
/* Clears up any memory if needed */
void (*remove)(struct mdio_device *mdiodev);
};
//mdio设备结构
struct mdio_device {
struct device dev;
const struct dev_pm_ops *pm_ops;
struct mii_bus *bus; //mdio设备包含mii_bus总线结构,即mdio_device所属的mii总线
char modalias[MDIO_NAME_SIZE];
int (*bus_match)(struct device *dev, struct device_driver *drv);
void (*device_free)(struct mdio_device *mdiodev);
void (*device_remove)(struct mdio_device *mdiodev);
/* Bus address of the MDIO device (0-31) */
int addr;
int flags;
};
struct phy_device {
struct mdio_device mdio;//phy_device中包含mdio_device
/* Information about the PHY type */
/* And management functions */
struct phy_driver *drv;
u32 phy_id;
struct phy_c45_device_ids c45_ids;
bool is_c45;
bool is_internal;
bool is_pseudo_fixed_link;
bool has_fixups;
bool suspended;
bool sysfs_links;
bool loopback_enabled;
enum phy_state state;
u32 dev_flags;
phy_interface_t interface;
/*
* forced speed & duplex (no autoneg)
* partner speed & duplex & pause (autoneg)
*/
int speed;
int duplex;
int pause;
int asym_pause;
/* The most recently read link state */
int link;
/* Enabled Interrupts */
u32 interrupts;
/* Union of PHY and Attached devices' supported modes */
/* See mii.h for more info */
u32 supported;
u32 advertising;
u32 lp_advertising;
/* Energy efficient ethernet modes which should be prohibited */
u32 eee_broken_modes;
int autoneg;
int link_timeout;
#ifdef CONFIG_LED_TRIGGER_PHY
struct phy_led_trigger *phy_led_triggers;
unsigned int phy_num_led_triggers;
struct phy_led_trigger *last_triggered;
#endif
/*
* Interrupt number for this PHY
* -1 means no interrupt
*/
int irq;
/* private data pointer */
/* For use by PHYs to maintain extra state */
void *priv;
/* Interrupt and Polling infrastructure */
struct work_struct phy_queue;
struct delayed_work state_queue;
atomic_t irq_disable;
struct mutex lock;
struct phylink *phylink;
struct net_device *attached_dev; //phy_device结构中包含struct net_device结构
u8 mdix;
u8 mdix_ctrl;
//phy_link_change
void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier);
//fec_enet_adjust_link
void (*adjust_link)(struct net_device *dev);
};
struct mdio_driver_common内嵌在phy_driver结构中。
struct phy_driver {
struct mdio_driver_common mdiodrv;
u32 phy_id;
char *name;
unsigned int phy_id_mask;
u32 features;
u32 flags;
const void *driver_data;
/*
* Called to issue a PHY software reset
*/
int (*soft_reset)(struct phy_device *phydev);
/*
* Called to initialize the PHY, including after a reset
*/
int (*config_init)(struct phy_device *phydev);
/*
* Called during discovery. Used to set up device-specific structures, if any
*/
int (*probe)(struct phy_device *phydev);
/* PHY Power Management */
int (*suspend)(struct phy_device *phydev);
int (*resume)(struct phy_device *phydev);
/*
* Configures the advertisement and resets autonegotiation if phydev->autoneg is on,
* forces the speed to the current settings in phydev if phydev->autoneg is off
*/
int (*config_aneg)(struct phy_device *phydev);
/* Determines the auto negotiation result */
int (*aneg_done)(struct phy_device *phydev);
/* Determines the negotiated speed and duplex */
int (*read_status)(struct phy_device *phydev);
/* Clears any pending interrupts */
int (*ack_interrupt)(struct phy_device *phydev);
/* Enables or disables interrupts */
int (*config_intr)(struct phy_device *phydev);
/*
* Checks if the PHY generated an interrupt.
* For multi-PHY devices with shared PHY interrupt pin
*/
int (*did_interrupt)(struct phy_device *phydev);
/* Clears up any memory if needed */
void (*remove)(struct phy_device *phydev);
/* Returns true if this is a suitable driver for the given phydev.
If NULL, matching is based on phy_id and phy_id_mask.
*/
int (*match_phy_device)(struct phy_device *phydev);
/* Handles ethtool queries for hardware time stamping. */
int (*ts_info)(struct phy_device *phydev, struct ethtool_ts_info *ti);
/* Handles SIOCSHWTSTAMP ioctl for hardware time stamping. */
int (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr);
/*
* Requests a Rx timestamp for 'skb'. If the skb is accepted,
* the phy driver promises to deliver it using netif_rx() as
* soon as a timestamp becomes available. One of the
* PTP_CLASS_ values is passed in 'type'. The function must
* return true if the skb is accepted for delivery.
*/
bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);
/*
* Requests a Tx timestamp for 'skb'. The phy driver promises
* to deliver it using skb_complete_tx_timestamp() as soon as a
* timestamp becomes available. One of the PTP_CLASS_ values
* is passed in 'type'.
*/
void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);
/* Some devices (e.g. qnap TS-119P II) require PHY register changes to
* enable Wake on LAN, so set_wol is provided to be called in the
* ethernet driver's set_wol function. */
int (*set_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
/* See set_wol, but for checking whether Wake on LAN is enabled. */
void (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
/*
* Called to inform a PHY device driver when the core is about to change the link state.
* This callback is supposed to be used as fixup hook for drivers that need to take action when the link state changes.
* Drivers are by no means allowed to mess with the PHY device structure in their implementations.
*/
void (*link_change_notify)(struct phy_device *dev);
/*
* Phy specific driver override for reading a MMD register.
* This function is optional for PHY specific drivers. When
* not provided, the default MMD read function will be used
* by phy_read_mmd(), which will use either a direct read for
* Clause 45 PHYs or an indirect read for Clause 22 PHYs.
* devnum is the MMD device number within the PHY device,
* regnum is the register within the selected MMD device.
*/
int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum);
/*
* Phy specific driver override for writing a MMD register.
* This function is optional for PHY specific drivers. When
* not provided, the default MMD write function will be used
* by phy_write_mmd(), which will use either a direct write for
* Clause 45 PHYs, or an indirect write for Clause 22 PHYs.
* devnum is the MMD device number within the PHY device,
* regnum is the register within the selected MMD device.
* val is the value to be written.
*/
int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum,u16 val);
/* Get the size and type of the eeprom contained within a plug-in
* module */
int (*module_info)(struct phy_device *dev,struct ethtool_modinfo *modinfo);
/* Get the eeprom information from the plug-in module */
int (*module_eeprom)(struct phy_device *dev,struct ethtool_eeprom *ee, u8 *data);
/* Get statistics from the phy using ethtool */
int (*get_sset_count)(struct phy_device *dev);
void (*get_strings)(struct phy_device *dev, u8 *data);
void (*get_stats)(struct phy_device *dev,struct ethtool_stats *stats, u64 *data);
/* Get and Set PHY tunables (可调谐的)参数*/
int (*get_tunable)(struct phy_device *dev,struct ethtool_tunable *tuna, void *data);
int (*set_tunable)(struct phy_device *dev,struct ethtool_tunable *tuna,const void *data);
int (*set_loopback)(struct phy_device *dev, bool enable);
};
struct fixed_mdio_bus {
struct mii_bus *mii_bus;
struct list_head phys;
};
struct fixed_phy {
int addr;
struct phy_device *phydev;
seqcount_t seqcount;
struct fixed_phy_status status;
int (*link_update)(struct net_device *, struct fixed_phy_status *);
struct list_head node;
int link_gpio;
};
#define PHY_ANY_ID "MATCH ANY PHY"
#define PHY_ANY_UID 0xffffffff