phy连接过程

mii_bus

mii_bus 用于读写phy芯片。
其中的重点是mii_bus->read 和mii_bus->write。

/*
 * The Bus class for PHYs.  Devices which provide access to
 * PHYs should register using this structure
 */
struct mii_bus {
	struct module *owner;
	const char *name;
	char id[MII_BUS_ID_SIZE];
	void *priv;
	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];

	/* 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;

	/* bus capabilities, used for probing */
	enum {
		MDIOBUS_NO_CAP = 0,
		MDIOBUS_C22,
		MDIOBUS_C45,
		MDIOBUS_C22_C45,
	} probe_capabilities;
};

ifconfig ethx up

of_phy_connect		//网口up时会调用此函数
	->of_phy_find_device	//(of_mdio.c)	//通过设备树找到phy 设备
		->bus_find_device_by_of_node		
			->to_mdio_device
				->to_phy_device
	->phy_connect_direct					//将以太网设备连接到特定的phy设备
		->phy_attach_direct					//将网络设备与指定的phy设备绑定

of_phy_find_device 内容如下:

struct phy_device *of_phy_find_device(struct device_node *phy_np)
{
	struct device *d;
	struct mdio_device *mdiodev;

	if (!phy_np)
		return NULL;

	d = bus_find_device_by_of_node(&mdio_bus_type, phy_np);
	if (d) {
		mdiodev = to_mdio_device(d);
		if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY)
			return to_phy_device(d);
		put_device(d);
	}

	return NULL;
}

to_mdio_device 与to_phy_device的定义如下

#define to_mdio_device(d) container_of(d, struct mdio_device, dev)

#define to_phy_device(d) container_of(to_mdio_device(d), \
				      struct phy_device, mdio)

container of 函数简介

container_of(ptr,type,member)
container_of(to_mdio_device(d), struct phy_device, mdio)
该函数的意义时得到type 类型的结构体首地址(即得到该结构体)。

原理很简单: 已知结构体type的成员member的地址ptr,求解结构体type的起始地址。
type的起始地址 = ptr - size。 size 不用计较它如何得知,我们只要知道该函数会返回member成员所在结构体的首地址就好了。

phy_attach_direct 函数定义在 phy_device.c。

int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
		      u32 flags, phy_interface_t interface)
{
	struct mii_bus *bus = phydev->mdio.bus;
	struct device *d = &phydev->mdio.dev;
	struct module *ndev_owner = NULL;
	bool using_genphy = false;
	int err;

	/* For Ethernet device drivers that register their own MDIO bus, we
	 * will have bus->owner match ndev_mod, so we do not want to increment
	 * our own module->refcnt here, otherwise we would not be able to
	 * unload later on.
	 */
	if (dev)
		ndev_owner = dev->dev.parent->driver->owner;
	if (ndev_owner != bus->owner && !try_module_get(bus->owner)) {
		phydev_err(phydev, "failed to get the bus module\n");
		return -EIO;
	}

	get_device(d);

	/* Assume that if there is no driver, that it doesn't
	 * exist, and we should use the genphy driver.
	 */
	if (!d->driver) {			//获取mdio驱动
		if (phydev->is_c45)
			d->driver = &genphy_c45_driver.mdiodrv.driver;		//c45的
		else
			d->driver = &genphy_driver.mdiodrv.driver;			//通用的mdio驱动

		using_genphy = true;
	}

	if (!try_module_get(d->driver->owner)) {
		phydev_err(phydev, "failed to get the device driver module\n");
		err = -EIO;
		goto error_put_device;
	}

	if (using_genphy) {
		err = d->driver->probe(d);
		if (err >= 0)
			err = device_bind_driver(d);

		if (err)
			goto error_module_put;
	}

	if (phydev->attached_dev) {				//判断是否已经绑定,attached_dev 即net_device ;
		dev_err(&dev->dev, "PHY already attached\n");
		err = -EBUSY;
		goto error;
	}

	phydev->phy_link_change = phy_link_change;		//连接变换函数
	if (dev) {				//绑定网络设备与phy设备
		phydev->attached_dev = dev;
		dev->phydev = phydev;
	}
	/* Some Ethernet drivers try to connect to a PHY device before
	 * calling register_netdevice() -> netdev_register_kobject() and
	 * does the dev->dev.kobj initialization. Here we only check for
	 * success which indicates that the network device kobject is
	 * ready. Once we do that we still need to keep track of whether
	 * links were successfully set up or not for phy_detach() to
	 * remove them accordingly.
	 */
	phydev->sysfs_links = false;

	phy_sysfs_create_links(phydev);

	if (!phydev->attached_dev) {
		err = sysfs_create_file(&phydev->mdio.dev.kobj,
					&dev_attr_phy_standalone.attr);
		if (err)
			phydev_err(phydev, "error creating 'phy_standalone' sysfs entry\n");
	}

	phydev->dev_flags = flags;

	phydev->interface = interface;		//phy 设备接口

	phydev->state = PHY_READY;			

	/* Initial carrier state is off as the phy is about to be
	 * (re)initialized.
	 */
	if (dev)
		netif_carrier_off(phydev->attached_dev);

	/* Do initial configuration here, now that
	 * we have certain key parameters
	 * (dev_flags and interface)
	 */
	err = phy_init_hw(phydev);	//初始化硬件,估计是设置寄存器。
	if (err)
		goto error;

	phy_resume(phydev);			//恢复phy 设备(使能BMCR(0x00) power位)
	phy_led_triggers_register(phydev);		//phy led触发注册

	return err;
error:
	/* phy_detach() does all of the cleanup below */
	phy_detach(phydev);
	return err;

error_module_put:
	module_put(d->driver->owner);
error_put_device:
	put_device(d);
	if (ndev_owner != bus->owner)
		module_put(bus->owner);
	return err;
}

phy_init_hw 函数,此函数会调用到phy 驱动函数。

int phy_init_hw(struct phy_device *phydev)
{
	int ret = 0;

	/* Deassert the reset signal */
	phy_device_reset(phydev, 0);		

	if (!phydev->drv)		//phy 驱动
		return 0;

	if (phydev->drv->soft_reset)			//phy 驱动软复位
		ret = phydev->drv->soft_reset(phydev);

	if (ret < 0)
		return ret;

	ret = phy_scan_fixups(phydev);
	if (ret < 0)
		return ret;

	if (phydev->drv->config_init)
		ret = phydev->drv->config_init(phydev);			//config_init

	return ret;
}

phy设备驱动工作流程:
phy_devic.c 下会注册mdio总线、通用phy驱动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();			//注册mdio总线
	if (rc)
		return rc;

	features_init();		//features:特性
	
	// phy驱动注册========================
	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);

mdio 总线注册

struct bus_type mdio_bus_type = {		//mdio 总线类型结构体
	.name		= "mdio_bus",
	.match		= mdio_bus_match,		//用于匹配phy_device 与 phy_driver	(匹配之类的程序属于纯软件的核心层,与硬件无关)
	.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;
}

mdio_bus_match 定义在drivers/net/phy/mdio_bus.c。
值得注意的是 to_mdio_device、to_phy_device、to_phy_driver 这3个函数可以通过struct device *dev 和struct device_driver *drv获得想要的结构体类型。利用前面的container_of 也可以做到。

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;
}

其它内核版本的mdio_bus_match

1 static int mdio_bus_match(struct device *dev,
struct device_driver *drv)
2 {
3 struct phy_device *phydev = to_phy_device(dev);		
4 struct phy_driver *phydrv = to_phy_driver(drv);
5 6
if (of_driver_match_device(dev, drv))
7 return 1;
8 9
if (phydrv->match_phy_device)
10 return phydrv->match_phy_device(phydev);
11
12 return (phydrv->phy_id & phydrv->phy_id_mask) ==
13 (phydev->phy_id & phydrv->phy_id_mask);
14 }

phy_device 创建

get_phy_device
	->get_phy_id->phy_device_create
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
	struct phy_c45_device_ids c45_ids;
	u32 phy_id = 0;
	int r;

	c45_ids.devices_in_package = 0;
	memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));

	r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);
	if (r)
		return ERR_PTR(r);

	/* If the phy_id is mostly Fs, there is no device there */
	if ((phy_id & 0x1fffffff) == 0x1fffffff)
		return ERR_PTR(-ENODEV);

	return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}
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);

	dev->state = PHY_DOWN;

	mutex_init(&dev->lock);
	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
	
	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) {
		device_initialize(&mdiodev->dev);
	} else {
		kfree(dev);
		dev = ERR_PTR(ret);
	}

	return dev;
}

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);
	}
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值