详解PHY设备驱动的工作原理

不管什么驱动,简单的说:我们总是按照设备-驱动-总线的方式去看

设备

phy设备结构体

phy设备的定义如下

struct phy_device {
    struct phy_driver *drv;                // PHY设备驱动  
    struct mii_bus *bus;                   // 对应的MII总线 
    struct device dev;                     // 设备文件  
    u32 phy_id;                            // PHY ID  
    
    struct phy_c45_device_ids c45_ids;       
    bool is_c45;
    bool is_internal;
    bool has_fixups;
    bool suspended;

    enum phy_state state;                   // PHY状态
    u32 dev_flags;
    phy_interface_t interface;              // PHY接口  
    int addr;                               // PHY 总线地址(0~31) 

    int speed;                              // 速度  
    int duplex;                             // 双工模式  
    int pause;                              // 停止  
    int asym_pause;
    int link;

    u32 interrupts;                         // 中断使能标志  
    u32 supported;
    u32 advertising;
    u32 lp_advertising;
    int autoneg;
    int link_timeout;
    int irq;                                 // 中断号                              

    void *priv;                              // 私有数据  
    struct work_struct phy_queue;            // PHY工作队列  
    struct delayed_work state_queue;         // PHY延时工作队列  
    atomic_t irq_disable;

    struct mutex lock;

    struct net_device *attached_dev;         // 网络设备  

    void (*adjust_link)(struct net_device *dev);
};

注册phy设备

创建phy的设备的简化流程如下,一般就是mac驱动初始化的时候,去注册mdio总线的时候,mdio会去扫描,或根据设备树来创建phy设备

emac_mdio_init
	mii->write = emac_mii_write
	of_mdiobus_register
		mdiobus_register
		of_mdiobus_register_phy
            			phy_device_create

驱动

phy驱动结构体

里面的各成员就是设备驱动需要实现的接口

struct phy_driver {
    struct mdio_driver_common mdiodrv;
    u32 phy_id;
    char *name;
    u32 phy_id_mask;
    u32 features;
    u32 flags;
    const void *driver_data;
    
    int (*soft_reset)(struct phy_device *phydev);
    int (*config_init)(struct phy_device *phydev);
    int (*probe)(struct phy_device *phydev);
    int (*suspend)(struct phy_device *phydev);
    int (*resume)(struct phy_device *phydev);
    int (*config_aneg)(struct phy_device *phydev);
    int (*aneg_done)(struct phy_device *phydev);
    int (*read_status)(struct phy_device *phydev);
    int (*ack_interrupt)(struct phy_device *phydev);
    int (*config_intr)(struct phy_device *phydev);
    int (*did_interrupt)(struct phy_device *phydev);
    void (*remove)(struct phy_device *phydev);
    int (*match_phy_device)(struct phy_device *phydev);
    int (*ts_info)(struct phy_device *phydev, struct ethtool_ts_info *ti);
    int  (*hwtstamp)(struct phy_device *phydev, struct ifreq *ifr);
    bool (*rxtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);
    void (*txtstamp)(struct phy_device *dev, struct sk_buff *skb, int type);
    int (*set_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
    void (*get_wol)(struct phy_device *dev, struct ethtool_wolinfo *wol);
    void (*link_change_notify)(struct phy_device *dev);
    int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum);
    int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum,
             u16 val);
    int (*read_page)(struct phy_device *dev);
    int (*write_page)(struct phy_device *dev, int page)
    int (*module_info)(struct phy_device *dev,
               struct ethtool_modinfo *modinfo);
    int (*module_eeprom)(struct phy_device *dev,
                 struct ethtool_eeprom *ee, u8 *data);
    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);
    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);
    ANDROID_KABI_RESERVE(1);
    ANDROID_KABI_RESERVE(2);
};

注册phy驱动

通用的phy_driver_register会将驱动注册到mdio总线上,然后去遍历总线上的设备,是否有id匹配的phy设备,然后执行总线的probe函数

phy_driver_register()
      driver_register(&new_driver->mdiodrv.driver)
          bus_add_driver(drv)
              driver_attach(drv)
                  bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
                      while ((dev = next_device(&i)) && !error)
                          /* 循环到注册的 PHY 设备时 */
                          fn(dev, data) = __driver_attach()
                              /* 匹配设备和驱动 */
                              driver_match_device(drv, dev)
                                  mdio_bus_match(dev, drv)
                                      phy_bus_match(dev, drv)
                                          /* 按 phy_id & phy_id_mask 匹配 */
                                          return (phydrv->phy_id & phydrv->phy_id_mask) == (phydev->phy_id & phydrv->phy_id_mask);
                              /* 匹配到设备和驱动,加载驱动 */
                              driver_probe_device(drv, dev)
                                  really_probe(dev, drv)
                                      dev->driver = drv; /* 绑定设备的驱动 */
                                      drv->probe(dev) = phy_probe

总线

mii总线结构体

在linux中,一般用mii_bus表示mdio总线,重要的就是read和write函数来访问phy的寄存器,来初始配置phy芯片

struct mii_bus {
    const char *name;             // 总线名字
    char id[MII_BUS_ID_SIZE];     // ID MII_BUS_ID_SIZE=61
    void *priv;                   // 私有数据
    int (*read)(struct mii_bus *bus, int phy_id, int regnum);              // 读方式
    int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val);    // 写方式
    int (*reset)(struct mii_bus *bus);     // 复位

    struct mutex mdio_lock;

    struct device *parent;        // 父设备
    enum {
        MDIOBUS_ALLOCATED = 1,
        MDIOBUS_REGISTERED,
        MDIOBUS_UNREGISTERED,
        MDIOBUS_RELEASED,
    } state;                       // 总线状态
    struct device dev;             // 设备文件

    struct phy_device *phy_map[PHY_MAX_ADDR]; // PHY设备数组

    u32 phy_mask;
    int *irq;                   // 中断
};

总线初始化

主要是注册mdio总线,event,以及总线的match函数

在phy_device_create里会调用mdiodev->bus_match = phy_bus_match;
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
	struct mdio_device *mdio = to_mdio_device(dev);

	if (of_driver_match_device(dev, drv))
		return 1;

	if (mdio->bus_match)
		return mdio->bus_match(dev, drv);

	return 0;
}
也即phy_bus_match就是真正的匹配规则
static int phy_bus_match(struct device *dev, struct device_driver *drv)
{
	struct phy_device *phydev = to_phy_device(dev);
	struct phy_driver *phydrv = to_phy_driver(drv);
	const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
	int i;

	if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
		return 0;

	if (phydrv->match_phy_device)
		return phydrv->match_phy_device(phydev);

	if (phydev->is_c45) {
		for (i = 1; i < num_ids; i++) {
			if (phydev->c45_ids.device_ids[i] == 0xffffffff)
				continue;

			if ((phydrv->phy_id & phydrv->phy_id_mask) ==
			    (phydev->c45_ids.device_ids[i] &
			     phydrv->phy_id_mask))
				return 1;
		}
		return 0;
	} else {
		return (phydrv->phy_id & phydrv->phy_id_mask) ==
			(phydev->phy_id & phydrv->phy_id_mask);
	}
}

static int mdio_uevent(struct device *dev, struct kobj_uevent_env *env)
{
	int rc;

	/* Some devices have extra OF data and an OF-style MODALIAS */
	rc = of_device_uevent_modalias(dev, env);
	if (rc != -ENODEV)
		return rc;

	return 0;
}

struct bus_type mdio_bus_type = {
	.name		= "mdio_bus",
	.match		= mdio_bus_match,
	.uevent		= mdio_uevent,
};
EXPORT_SYMBOL(mdio_bus_type);

int __init mdio_bus_init(void)
{
	int ret;

	ret = class_register(&mdio_bus_class);
	if (!ret) {
		ret = bus_register(&mdio_bus_type);
		if (ret)
			class_unregister(&mdio_bus_class);
	}

	return ret;
}

调用总线初始化,并注册genphy_driver驱动

static struct phy_driver genphy_driver = {
	.phy_id		= 0xffffffff,
	.phy_id_mask	= 0xffffffff,
	.name		= "Generic PHY",
	.soft_reset	= genphy_no_soft_reset,
	.get_features	= genphy_read_abilities,
	.aneg_done	= genphy_aneg_done,
	.suspend	= genphy_suspend,
	.resume		= genphy_resume,
	.set_loopback   = genphy_loopback,
};

static int __init phy_init(void)
{
	int rc;

	rc = mdio_bus_init();
	if (rc)
		return rc;

	features_init();

	rc = phy_driver_register(&genphy_c45_driver, THIS_MODULE);
	if (rc)
		goto err_c45;

	rc = phy_driver_register(&genphy_driver, THIS_MODULE);
	if (rc) {
		phy_driver_unregister(&genphy_c45_driver);
err_c45:
		mdio_bus_exit();
	}

	return rc;
}

static void __exit phy_exit(void)
{
	phy_driver_unregister(&genphy_c45_driver);
	phy_driver_unregister(&genphy_driver);
	mdio_bus_exit();
}

subsys_initcall(phy_init);
module_exit(phy_exit);

net设备

net设备结构体

phy只是一个网络设备的硬件层接口,真实的网络设备的结构体成员如下

struct net_device {
    char  name[IFNAMSIZ];         /* 用于存放网络设备的设备名称 */
    char  *ifalias;               /* 网络设备的别名 */
    int   ifindex;                /* 网络设备的接口索引值,独一无二的网络设备标识符 */
    struct hlist_node  name_hlist;  /* 这个字段用于构建网络设备名的哈希散列表,而struct net中的name_hlist就指向每个哈希散列表的链表头 */
    struct hlist_node  index_hlist; /* 用于构建网络设备的接口索引值哈希散列表,在struct net中的index_hlist用于指向接口索引值哈希散列表的链表头 */
    struct list_head   dev_list;    /* 用于将每一个网络设备加入到一个网络命名空间中的网络设备双链表中 */
    unsigned int       flags;       /* 网络设备接口的标识符 */
    unsigned int       priv_flags;  /* 网络设备接口的标识符,但对用户空间不可见;*/
    unsigned short     type;        /* 接口硬件类型 */
    unsigned int       mtu;         /* 网络设备接口的最大传输单元 */
    unsigned short     hard_header_len;   /* 硬件接口头长度 */
    unsigned char      *dev_addr;    /* 网络设备接口的MAC地址 */
    bool           uc_promisc;       /* 网络设备接口的单播模式 */
    unsigned int       promiscuity;  /* 网络设备接口的混杂模式 */
    unsigned int       allmulti;     /* 网络设备接口的全组播模式 */
    struct netdev_hw_addr_list  uc;  /* 辅助单播MAC地址列表 */
    struct netdev_hw_addr_list  mc;  /* 主mac地址列表 */
    struct netdev_hw_addr_list  dev_addrs;  /* hw设备地址列表 */
    unsigned char      broadcast[MAX_ADDR_LEN];   /* hw广播地址 */
    struct netdev_rx_queue  *_rx;       /* 网络设备接口的数据包接收队列 */
    struct netdev_queue *_tx            /* 网络设备接口的数据包发送队列 */
    unsigned int        num_tx_queues;  /* TX队列数 */
    unsigned int        real_num_tx_queues;  /* 当前设备活动的TX队列数 */
    unsigned long      tx_queue_len;    /* 每个队列允许的最大帧 */
    unsigned long      state;           /* 网络设备接口的状态 */
    struct net_device_stats    stats;   /* 网络设备接口的统计情况 */
    possible_net_t         nd_net;      /* 用于执行网络设备所在的命名空间 */
}; 

注册net设备

一般在mac驱动里面会去初始并注册一个net设备

emac_probe
	ndev = alloc_etherdev
	ndev->ethtool_ops = &emac_ethtool_ops; // 供上层ethtool工具使用
    ndev->netdev_ops = &emac_netdev_ops; //里面有发送数据的接口
		.ndo_start_xmit         = emac_start_xmit, //核心调用这个发送数据时,发送完成后触发中断,去解除dma映射
	emac_mdio_init
	register_netdev
	netif_napi_add(ndev, &priv->napi, emac_rx_poll, 32); //通过napi来接受数据,在中断处理函数中接受到中断后,就会关掉中断,调度一次emac_rx_poll,emac_rx_poll直到rx_buffer空了,再开中断

总线框架

不管device_register还是driver_register,都会遍历注册到总线上的device或者driver ,并通过driver_match_device来匹配设备,然后调用driver_probe_device来探测

driver_match_device

match就是单纯的找总线的match,总线没match就直接返回1,比如mmc总线就是这样

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

driver_probe_device

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

	if (!device_is_registered(dev))
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	pm_runtime_get_suppliers(dev);
	if (dev->parent)
		pm_runtime_get_sync(dev->parent);

	pm_runtime_barrier(dev);
	if (initcall_debug)
		ret = really_probe_debug(dev, drv);
	else
		ret = really_probe(dev, drv);
	pm_request_idle(dev);

	if (dev->parent)
		pm_runtime_put(dev->parent);

	pm_runtime_put_suppliers(dev);
	return ret;
}

进really_probe就有dev和drv了,先看有没dev->bus->probe,没有就走drv->probe


	atomic_inc(&probe_count);
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	if (!list_empty(&dev->devres_head)) {
		dev_crit(dev, "Resources present before probing\n");
		ret = -EBUSY;
		goto done;
	}

re_probe:
	dev->driver = drv;

	/* If using pinctrl, bind pins now before probing */
	ret = pinctrl_bind_pins(dev);
	if (ret)
		goto pinctrl_bind_failed;

	if (dev->bus->dma_configure) {
		ret = dev->bus->dma_configure(dev);
		if (ret)
			goto probe_failed;
	}

	if (driver_sysfs_add(dev)) {
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}

	if (dev->pm_domain && dev->pm_domain->activate) {
		ret = dev->pm_domain->activate(dev);
		if (ret)
			goto probe_failed;
	}

	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

	if (device_add_groups(dev, drv->dev_groups)) {
		dev_err(dev, "device_add_groups() failed\n");
		goto dev_groups_failed;
	}

	if (test_remove) {
		test_remove = false;

		device_remove_groups(dev, drv->dev_groups);

		if (dev->bus->remove)
			dev->bus->remove(dev);
		else if (drv->remove)
			drv->remove(dev);

		devres_release_all(dev);
		driver_sysfs_remove(dev);
		dev->driver = NULL;
		dev_set_drvdata(dev, NULL);
		if (dev->pm_domain && dev->pm_domain->dismiss)
			dev->pm_domain->dismiss(dev);
		pm_runtime_reinit(dev);

		goto re_probe;
	}

	pinctrl_init_done(dev);

	if (dev->pm_domain && dev->pm_domain->sync)
		dev->pm_domain->sync(dev);

	driver_bound(dev);
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;

dev_groups_failed:
	if (dev->bus->remove)
		dev->bus->remove(dev);
	else if (drv->remove)
		drv->remove(dev);
probe_failed:
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
pinctrl_bind_failed:
	device_links_no_driver(dev);
	devres_release_all(dev);
	arch_teardown_dma_ops(dev);
	driver_sysfs_remove(dev);
	dev->driver = NULL;
	dev_set_drvdata(dev, NULL);
	if (dev->pm_domain && dev->pm_domain->dismiss)
		dev->pm_domain->dismiss(dev);
	pm_runtime_reinit(dev);
	dev_pm_set_driver_flags(dev, 0);

	switch (ret) {
	case -EPROBE_DEFER:
		/* Driver requested deferred probing */
		dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
		driver_deferred_probe_add_trigger(dev, local_trigger_count);
		break;
	case -ENODEV:
	case -ENXIO:
		pr_debug("%s: probe of %s rejects match %d\n",
			 drv->name, dev_name(dev), ret);
		break;
	default:
		/* driver matched but the probe failed */
		printk(KERN_WARNING
		       "%s: probe of %s failed with error %d\n",
		       drv->name, dev_name(dev), ret);
	}
	/*
	 * Ignore errors returned by ->probe so that the next driver can try
	 * its luck.
	 */
	ret = 0;
done:
	atomic_dec(&probe_count);
	wake_up_all(&probe_waitqueue);
	return ret;
}

mdio总线的match

struct bus_type mdio_bus_type = {
	.name		= "mdio_bus",
	.match		= mdio_bus_match,
	.uevent		= mdio_uevent,
};
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
	struct mdio_device *mdio = to_mdio_device(dev);

	if (of_driver_match_device(dev, drv))
		return 1;

	if (mdio->bus_match)
		return mdio->bus_match(dev, drv);

	return 0;
}

在phy_device_create里会调用mdiodev->bus_match = phy_bus_match;

static int phy_bus_match(struct device *dev, struct device_driver *drv)
{
	struct phy_device *phydev = to_phy_device(dev);
	struct phy_driver *phydrv = to_phy_driver(drv);
	const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
	int i;

	if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
		return 0;

	if (phydrv->match_phy_device)
		return phydrv->match_phy_device(phydev);

	if (phydev->is_c45) {
		for (i = 1; i < num_ids; i++) {
			if (phydev->c45_ids.device_ids[i] == 0xffffffff)
				continue;

			if ((phydrv->phy_id & phydrv->phy_id_mask) ==
			    (phydev->c45_ids.device_ids[i] &
			     phydrv->phy_id_mask))
				return 1;
		}
		return 0;
	} else {
		return (phydrv->phy_id & phydrv->phy_id_mask) ==
			(phydev->phy_id & phydrv->phy_id_mask);
	}
}

mdio总线的probe

mdio总线没这个probe,肯定就是走drv->probe;再调用phy_driver_register注册时,就把phy_probe给driver的probe,这里的注册的driver是device_driver结构;跟__platform_driver_register也是差不多一样
相当于就是先走 device_driver的probe,这个是不同总线的通用probe ;这个不同总线的通用probe里面,再去走驱动里面注册的不同phy_driver的probe

int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
{
	int retval;

	/* Either the features are hard coded, or dynamically
	 * determined. It cannot be both.
	 */
	if (WARN_ON(new_driver->features && new_driver->get_features)) {
		pr_err("%s: features and get_features must not both be set\n",
		       new_driver->name);
		return -EINVAL;
	}

	new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
	new_driver->mdiodrv.driver.name = new_driver->name;
	new_driver->mdiodrv.driver.bus = &mdio_bus_type;
	new_driver->mdiodrv.driver.probe = phy_probe;
	new_driver->mdiodrv.driver.remove = phy_remove;
	new_driver->mdiodrv.driver.owner = owner;

	retval = driver_register(&new_driver->mdiodrv.driver);
	if (retval) {
		pr_err("%s: Error %d in registering driver\n",
		       new_driver->name, retval);

		return retval;
	}

	pr_debug("%s: Registered new driver\n", new_driver->name);

	return 0;
}
EXPORT_SYMBOL(phy_driver_register);

phy_probe如下,这里面才会去走的驱动注册的phy_driver结构的porbe

static int phy_probe(struct device *dev)
{
	struct phy_device *phydev = to_phy_device(dev);
	struct device_driver *drv = phydev->mdio.dev.driver;
	struct phy_driver *phydrv = to_phy_driver(drv);
	int err = 0;

	phydev->drv = phydrv;

	/* Disable the interrupt if the PHY doesn't support it
	 * but the interrupt is still a valid one
	 */
	 if (!phy_drv_supports_irq(phydrv) && phy_interrupt_is_valid(phydev))
		phydev->irq = PHY_POLL;

	if (phydrv->flags & PHY_IS_INTERNAL)
		phydev->is_internal = true;

	mutex_lock(&phydev->lock);

	if (phydev->drv->probe) {
		/* Deassert the reset signal */
		phy_device_reset(phydev, 0);

		err = phydev->drv->probe(phydev);
		if (err) {
			/* Assert the reset signal */
			phy_device_reset(phydev, 1);
			goto out;
		}
	}

	/* Start out supporting everything. Eventually,
	 * a controller will attach, and may modify one
	 * or both of these values
	 */
	if (phydrv->features) {
		linkmode_copy(phydev->supported, phydrv->features);
	} else if (phydrv->get_features) {
		err = phydrv->get_features(phydev);
	} else if (phydev->is_c45) {
		err = genphy_c45_pma_read_abilities(phydev);
	} else {
		err = genphy_read_abilities(phydev);
	}

	if (err)
		goto out;

	if (!linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
			       phydev->supported))
		phydev->autoneg = 0;

	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
			      phydev->supported))
		phydev->is_gigabit_capable = 1;
	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
			      phydev->supported))
		phydev->is_gigabit_capable = 1;

	of_set_phy_supported(phydev);
	phy_advertise_supported(phydev);

	/* Get the EEE modes we want to prohibit. We will ask
	 * the PHY stop advertising these mode later on
	 */
	of_set_phy_eee_broken(phydev);

	/* The Pause Frame bits indicate that the PHY can support passing
	 * pause frames. During autonegotiation, the PHYs will determine if
	 * they should allow pause frames to pass.  The MAC driver should then
	 * use that result to determine whether to enable flow control via
	 * pause frames.
	 *
	 * Normally, PHY drivers should not set the Pause bits, and instead
	 * allow phylib to do that.  However, there may be some situations
	 * (e.g. hardware erratum) where the driver wants to set only one
	 * of these bits.
	 */
	if (!test_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported) &&
	    !test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported)) {
		linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
				 phydev->supported);
		linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
				 phydev->supported);
	}

	/* Set the state to READY by default */
	phydev->state = PHY_READY;

out:
	mutex_unlock(&phydev->lock);

	return err;
}

PHY状态机

创建状态机

创建phy设备的时候调用phy_device_create的里面,就会去创建一个延迟工作队列去执行状态机,里面的执行函数为phy_state_machine,里面就会用到注册phy驱动时,实现的各函数

struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
				     bool is_c45,
				     struct phy_c45_device_ids *c45_ids)
{
	struct phy_device *dev;
	struct mdio_device *mdiodev;
	int ret = 0;

	/* We allocate the device, and initialize the default values */
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
		return ERR_PTR(-ENOMEM);

	mdiodev = &dev->mdio;
	mdiodev->dev.parent = &bus->dev;
	mdiodev->dev.bus = &mdio_bus_type;
	mdiodev->dev.type = &mdio_bus_phy_type;
	mdiodev->bus = bus;
	mdiodev->bus_match = phy_bus_match;
	mdiodev->addr = addr;
	mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
	mdiodev->device_free = phy_mdio_device_free;
	mdiodev->device_remove = phy_mdio_device_remove;

	dev->speed = SPEED_UNKNOWN;
	dev->duplex = DUPLEX_UNKNOWN;
	dev->pause = 0;
	dev->asym_pause = 0;
	dev->link = 0;
	dev->interface = PHY_INTERFACE_MODE_GMII;

	dev->autoneg = AUTONEG_ENABLE;

	dev->is_c45 = is_c45;
	dev->phy_id = phy_id;
	if (c45_ids)
		dev->c45_ids = *c45_ids;
	dev->irq = bus->irq[addr];

	dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
	device_initialize(&mdiodev->dev);

	dev->state = PHY_DOWN;

	mutex_init(&dev->lock);
	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);

	/* Request the appropriate module unconditionally; don't
	 * bother trying to do so only if it isn't already loaded,
	 * because that gets complicated. A hotplug event would have
	 * done an unconditional modprobe anyway.
	 * We don't do normal hotplug because it won't work for MDIO
	 * -- because it relies on the device staying around for long
	 * enough for the driver to get loaded. With MDIO, the NIC
	 * driver will get bored and give up as soon as it finds that
	 * there's no driver _already_ loaded.
	 */
	if (is_c45 && c45_ids) {
		const int num_ids = ARRAY_SIZE(c45_ids->device_ids);
		int i;

		for (i = 1; i < num_ids; i++) {
			if (c45_ids->device_ids[i] == 0xffffffff)
				continue;

			ret = phy_request_driver_module(dev,
						c45_ids->device_ids[i]);
			if (ret)
				break;
		}
	} else {
		ret = phy_request_driver_module(dev, phy_id);
	}

	if (ret) {
		put_device(&mdiodev->dev);
		dev = ERR_PTR(ret);
	}

	return dev;
}

phy_state_machine

里面就是针对个状态做处理

void phy_state_machine(struct work_struct *work)
{

 old_state = phydev->state;

    /* (1) 状态机主体 */
 switch (phydev->state) {
    /* (1.1) 在 PHY_DOWN/PHY_READY 状态下不动作 */
 case PHY_DOWN:
 case PHY_READY:
  break;
    
    /* (1.2) 在 PHY_UP 状态下,表明网口被 up 起来,需要启动自协商并且查询自协商后的 link 状态
             如果自协商结果是 link up,进入 PHY_RUNNING 状态
             如果自协商结果是 link down,进入 PHY_NOLINK 状态
     */
 case PHY_UP:
  needs_aneg = true;

  break;

    /* (1.3) 在运行的过程中定时轮询 link 状态
             如果 link up,进入 PHY_RUNNING 状态
             如果 link down,进入 PHY_NOLINK 状态
     */
 case PHY_NOLINK:
 case PHY_RUNNING:
  err = phy_check_link_status(phydev);
  break;

 }

    /* (2) 如果需要,启动自协商配置 */
 if (needs_aneg)
  err = phy_start_aneg(phydev);

    /* (3) 如果 phy link 状态有变化,通知给对应网口 netdev */
 if (old_state != phydev->state) {
  phydev_dbg(phydev, "PHY state change %s -> %s\n",
      phy_state_to_str(old_state),
      phy_state_to_str(phydev->state));
  if (phydev->drv && phydev->drv->link_change_notify)
   phydev->drv->link_change_notify(phydev);
 }

    /* (4) 重新启动 work,周期为 1s */
 if (phy_polling_mode(phydev) && phy_is_started(phydev))
  phy_queue_state_machine(phydev, PHY_STATE_TIME);
}
自协商流程
phy_state_machine()
`-| phy_start_aneg()
   `-| phy_config_aneg()
      `-| genphy_config_aneg()
         `-| __genphy_config_aneg()
            `-| genphy_setup_master_slave() // (1) 如果是千兆网口,配置其 master/slave
               `-| {
                 |  phy_modify_changed(phydev, MII_CTRL1000,    // 配置 mdio 0x09 寄存器
                 |     (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER | CTL1000_PREFER_MASTER), ctl);
                 | }
              | genphy_config_advert() // (2) 设置本端的 advert 能力
               `-| {
                 |  linkmode_and(phydev->advertising, phydev->advertising, phydev->supported);
                 |  adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
                 |  phy_modify_changed(phydev, MII_ADVERTISE,   // 10M/100M 能力配置到 mdio 0x04 寄存器
                 |       ADVERTISE_ALL | ADVERTISE_100BASE4 |
                 |       ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, adv);
                 |  if (!(bmsr & BMSR_ESTATEN)) return changed;
                 |  adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
                 |  phy_modify_changed(phydev, MII_CTRL1000,    // 1000M 能力配置到 mdio 0x09 寄存器
                 |       ADVERTISE_1000FULL | ADVERTISE_1000HALF, adv);
                 | }
              | genphy_check_and_restart_aneg()
               `-| genphy_restart_aneg() // (3) 启动 phy 自协商
                  `-| {
                    |  phy_modify(phydev, MII_BMCR, BMCR_ISOLATE,   // 配置 mdio 0x00 寄存器
                    |       BMCR_ANENABLE | BMCR_ANRESTART);
                    | }
状态读取
phy_state_machine()
`-| phy_check_link_status()
   `-| phy_read_status()    // (1) 读取 link 状态 
      `-| genphy_read_status()
         `-| {
           |  genphy_update_link(phydev);   // (1.1) 更新 link 状态
           |  if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) return 0;
           |  genphy_read_master_slave(phydev); // (1.2) 如果是千兆网口,更新本端和对端的 master/slave
           |  genphy_read_lpa(phydev);  // (1.3) 更新对端(link partner) 声明的能力
           |  if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
           |    phy_resolve_aneg_linkmode(phydev);  // (1.4.1) 自协商模式,解析 link 结果
           |  } else if (phydev->autoneg == AUTONEG_DISABLE) {
           |    genphy_read_status_fixed(phydev); // (1.4.2) 固定模式,解析 link 结果
           |  }
           | }
     | if (phydev->link && phydev->state != PHY_RUNNING) {  // (2) link 状态 change 事件:变成 link up
     |   phydev->state = PHY_RUNNING;
     |   phy_link_up(phydev);   // link up 事件,通知给 phylink
     | } else if (!phydev->link && phydev->state != PHY_NOLINK) {  // (3) link 状态 change 事件:变成 link down
     |   phydev->state = PHY_NOLINK;
     |   phy_link_down(phydev); // link down 事件,通知给 phylink
     | }
状态通知
phy_link_up()/phy_link_down()
`-| phydev->phy_link_change(phydev, true/false);
   `-| phylink_phy_change()
      `-| {
        |  pl->phy_state.speed = phydev->speed;     // (1) 把 `phy_device`  状态更新给 `phylink`
        |  pl->phy_state.duplex = phydev->duplex;
        |  pl->phy_state.interface = phydev->interface;
        |  pl->phy_state.link = up;
        |  phylink_run_resolve(pl);     // (2) 通知 `phylink` 的轮询任务启动
        | }

你可能感兴趣的:(Linux内核之驱动,驱动开发)