MAC控制器驱动

MAC控制器与PHY

嵌入式Linux开发模式下,以太网硬件架构一般都是 MAC与PHY是独立的。所以以太网模块的硬件相关的驱动代码也分为两部分实现: GMAC 和 PHY,AC控制器驱动由SoC厂商开发,PHY芯片驱动由PHY厂商开发,PHY 驱动一般使用通用 PHY 驱动,如果有需要修改特殊寄存器,请使用对应的 PHY 驱动,代码都在 drivers/net/phy。当然,驱动之间要完成通信,必须严格按照IEEE802.3制定的协议。

以RK芯片为例,RK系列的Soc中内置了以太网MAC控制器,所以只需要搭配一颗以太网PHY芯片就可以实现以太网功能。按照规范 就算是不同的厂家的PHY,仍然有一部分寄存器的定义是通用的,只要配置了这些通用的寄存器,基本上PHY就可以正常工作。所以一般情况下,如果不需要使用PHY厂家提供的自定义的寄存器配置实现一些个性化的功能,那么PHY驱动就基本不需要修改。所以Linux驱动中有通用的PHY驱动。

关于网络数据的收发

收发数据这种耗时的事情,一般都是使用DMA。CPU只需要告诉DMA起始地址和长度剩下的事情就可以自动完成,一般在MAC中有一组寄存器专门记录数据地址,cpu按照MAC的要求把数据放好后,启动MAC的数据发送就可以了。

关于访问PHY芯片寄存器
PHY芯片上的寄存器CPU不可以直接访问,只能通过MAC上的MIIM寄存器实现间接访问。所以如果发生 通过ifconfig配置网络设备时,提示 ifconfig: SIOCSIFFLAGS: No such device时候,要考虑是不是 MAC控制器与PHY 芯片无法通信而导致 无法获取网络状态引起的。

关于谁提供时钟

RMII 接口需要 50M 参考时钟来保证 MAC 和 PHY 之间数据传输的同步。50M 时钟, 可以由 MAC 来提供, 也可以由 PHY 来提供。 SDK 默认使用MAC, 也就是芯片内部的 MAC 控制器来提供时钟。 一般情况下, 并不需要使用PHY 来提供参考时钟。

本文只对MAC控制器进行简单说明

MAC控制器通过MDIO总线来管理phy设备,mdio总线与i2c总线类似,可以一个主机对应多个从设备,每个从设备都有地址。mdio最多接32个phy设备。

对应的目录是/sys/mdio,在/sys/mdio/devices目录中会有挂载在mdio的phy设备,在/sys/mdio/drivers中会有phy设备的驱动。

如:
 /sys/bus/mdio_bus/devices/stmmac-0:00 
其中 stmmac-0:00 表示 PHY 地址是 0。


该命令会读取 0~31 的所有寄存器,所以可以查看对应的寄存器值
cat /sys/bus/mdio_bus/devices/stmmac-0:00/phy_registers

Linux3.10 版本内核
GMAC 驱动代码 driver/net/ethernet/rockchip/gmac/*

其它内核
GMAC 驱动代码,高于3.10 的内核版本,
GMAC 驱动代码位置 : drivers/net/ethernet/stmicro/stmmac/*

GMAC控制器相关设备树资源:

gmac: ethernet@ffc40000 {
		compatible = "rockchip,rv1126-gmac", "snps,dwmac-4.20a";
		reg = <0xffc40000 0x0ffff>;
		interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>,
			     <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
		interrupt-names = "macirq", "eth_wake_irq";
		rockchip,grf = <&grf>;
	...

		//关于MDC/MDIO通信相关配置信息
		mdio: mdio {
			compatible = "snps,dwmac-mdio";
			#address-cells = <0x1>;
			#size-cells = <0x0>;
		};

	...
	};

&gmac {
	phy-mode = "rmii"; //!!!  rmii模式
	clock_in_out = "output";// output 时钟由MAC输入给PHY input与之相反

    snps,reset-gpio = <&gpio3 RK_PA4 GPIO_ACTIVE_LOW>;//用于复位PHY的GPIO
    snps,reset-active-low;//复位PHY的GPIO低电平有效
    /* Reset time is 20ms, 100ms for rtl8211f */
    snps,reset-delays-us = <0 100000 100000>;// 复位PHY之前延时0ms  拉低维持的时间为100ms 拉高后延时100ms

	 // MAC时钟源
    assigned-clocks = <&cru CLK_GMAC_SRC_M0>, <&cru CLK_GMAC_SRC>, <&cru CLK_GMAC_TX_RX>;
    // MAC父时钟源
    assigned-clock-parents = <&cru CLK_GMAC_RGMII_M0>, <&cru CLK_GMAC_SRC_M0>, <&cru RMII_MODE_CLK>; 
     // MAC时钟频率
    assigned-clock-rates = <0>, <50000000>;

	pinctrl-names = "default";
    pinctrl-0 = <&rmiim0_pins &gmac_clk_m0_drv_level0_pins>;

	status = "okay";
	phy-handle = <&phy>;// 指定与MAC连接的PHY的配置信息
};



&mdio {
  //指定与MAC连接的PHY的配置信息
  phy: phy@1 {
	  // mdio和PHY之间的通信协议为22
    compatible = "ethernet-phy-ieee802.3-c22";
	  // phy硬件地址与与硬件上的phy地址相对应
    reg = <0x1>;
    clocks = <&cru CLK_GMAC_ETHERNET_OUT>;
  };
};

drivers\net\ethernet\stmicro\stmmac\dwmac-rk.c

static const struct of_device_id rk_gmac_dwmac_match[] = {
	...
	{ .compatible = "rockchip,px30-gmac",	.data = &px30_ops   },
	{ .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops },
	{ .compatible = "rockchip,rv1126-gmac", .data = &rv1126_ops },
	{ }
};
MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match);



static int rk_gmac_probe(struct platform_device *pdev)
{
	//gmac控制器所有配置信息
	struct plat_stmmacenet_data *plat_dat;
	
	//gmac控制器资源数据
	struct stmmac_resources stmmac_res;
	
	...

	/* 从设备树解析 gmac控制器所有配置信息
	如:
	struct plat_stmmacenet_data *plat;
		int interface;//接口类型 rmii  PHY_INTERFACE_MODE_RMII
		int phy_addr;// phy 地址 默认设置为-1 表示phy自动检测
		struct device_node *phy_node; //phy配置节点
		struct device_node *mdio_node; // mdio总线配置节点
		struct stmmac_mdio_bus_data *mdio_bus_data;//mdio总线数据	
		...
		
	*/
	plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);


...

	
	/* 主要工作
		struct platform_device
			struct device	dev;
			
		struct plat_stmmacenet_data //gmac控制器所有配置信息

		struct stmmac_resources //gmac控制器资源
	*/
	ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
	if (ret)
		goto err_gmac_powerdown;

	...
}

drivers\net\ethernet\stmicro\stmmac\stmmac_platform.c

struct plat_stmmacenet_data *
stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
{
	struct device_node *np = pdev->dev.of_node;
	struct plat_stmmacenet_data *plat;
	struct stmmac_dma_cfg *dma_cfg;
	int rc;

	// gmac控制器所有配置信息
	plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
	if (!plat)
		return ERR_PTR(-ENOMEM);

	//设备树中没有指定固定的 MAC地址,所以这里还是空
	*mac = of_get_mac_address(np);
	
	//Interface Mode definitions    PHY_INTERFACE_MODE_RMII  -- rmii
	/*
	struct plat_stmmacenet_data *plat;
		int interface;//接口类型 rmii  PHY_INTERFACE_MODE_RMII
	*/
	plat->interface = of_get_phy_mode(np);

	/* Get max speed of operation from device tree */
	//没有
	if (of_property_read_u32(np, "max-speed", &plat->max_speed))
		plat->max_speed = -1;

...

	/* Default to phy auto-detection */
	//默认 phy 自动检测
	/*
	struct plat_stmmacenet_data *plat;
		int phy_addr;// phy 地址 默认设置为-1 表示phy自动检测
	*/
	plat->phy_addr = -1;

...

	/* To Configure PHY by using all device-tree supported properties */
	//解析设备树驱动参数来分配PHY资源
	/*
	struct plat_stmmacenet_data *plat

	struct platform_device
		struct device	dev;
			struct device_node	*of_node;
			
	struct platform_device
		struct device	dev;

	解析设备树获取:
	struct plat_stmmacenet_data *plat;
		struct device_node *phy_node; //phy配置节点
		struct device_node *mdio_node; // mdio总线配置节点
	*/
	rc = stmmac_dt_phy(plat, np, &pdev->dev);
	...
}

.

//解析设备树驱动参数来分配PHY资源
/*
struct plat_stmmacenet_data *plat

struct platform_device
	struct device	dev;
		struct device_node	*of_node;
		
struct platform_device
	struct device	dev;

*/

/*

	gmac: ethernet@ffc40000 {
		...
		mdio: mdio {
			compatible = "snps,dwmac-mdio";
		}
		...
	}

	&gmac{
		...
		 //指定与MAC连接的PHY的配置信息
		 //phy: phy@1
		phy-handle = <&phy>;

	}

*/	

static int stmmac_dt_phy(struct plat_stmmacenet_data *plat,
			 struct device_node *np, struct device *dev)
{
	bool mdio = true;
	static const struct of_device_id need_mdio_ids[] = {
		{ .compatible = "snps,dwc-qos-ethernet-4.10" },
		{},
	};

	/* If phy-handle property is passed from DT, use it as the PHY */
	//获取 设备树中   与MAC连接的PHY的配置信息
	plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
	if (plat->phy_node)
		dev_dbg(dev, "Found phy-handle subnode\n");

	...

	if (of_match_node(need_mdio_ids, np)) {
		plat->mdio_node = of_get_child_by_name(np, "mdio");
	} else {

		// 关于MDC/MDIO通信相关配置信息
		/**
		 * If snps,dwmac-mdio is passed from DT, always register
		 * the MDIO
		 */
		//遍历 gmac: ethernet 的所有子节点 找到 mdio 节点
		/*
			gmac: ethernet@ffc40000 {
				...
				mdio: mdio {
					compatible = "snps,dwmac-mdio";
				}
				...
			}
		*/
		for_each_child_of_node(np, plat->mdio_node) {
			if (of_device_is_compatible(plat->mdio_node,
						    "snps,dwmac-mdio"))
				break;
		}
	}

	//至此 找到了 gmac 控制器节点下的 mdio总线节点信息
	if (plat->mdio_node) {
		dev_dbg(dev, "Found MDIO subnode\n");
		mdio = true;
	}

	if (mdio)
		plat->mdio_bus_data =
			devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data),
				     GFP_KERNEL);
	return 0;
}

drivers\net\ethernet\stmicro\stmmac\stmmac_main.c
工作:

在这里插入图片描述

/*
	struct platform_device
		struct device	dev;
		
	struct plat_stmmacenet_data //gmac控制器所有配置信息

	struct stmmac_resources //gmac控制器资源
*/

int stmmac_dvr_probe(struct device *device,
		     struct plat_stmmacenet_data *plat_dat,
		     struct stmmac_resources *res)
{
	
	struct net_device *ndev = NULL;
	struct stmmac_priv *priv;
	u32 queue, maxq;
	int ret = 0;

	//申请网卡
	ndev = alloc_etherdev_mqs(sizeof(struct stmmac_priv),
				  MTL_MAX_TX_QUEUES,
				  MTL_MAX_RX_QUEUES);
	if (!ndev)
		return -ENOMEM;

	SET_NETDEV_DEV(ndev, device);

//即通过struct net_device *dev首地址加对齐后的偏移量就得到了私有数据的首地址
//直接返回了net_device结构末端地址,也就是网卡私有数据结构的起始地址。当然其中考虑了字节对齐的问题
/*
	struct net_device *ndev 
		...
	}+ 网卡私有数据
	struct stmmac_priv
*/
	priv = netdev_priv(ndev);
	priv->device = device;
	priv->dev = ndev;
	
	
/*网卡操作集合
struct net_device *ndev 
	const struct ethtool_ops *ethtool_ops;
*/
	stmmac_set_ethtool_ops(ndev);
	
	
/*

	struct net_device {

	}++网卡私有数据
	struct stmmac_priv 							  //gmac控制器所有配置信息
		struct plat_stmmacenet_data *plat; ------ struct plat_stmmacenet_data *plat;

*/
	priv->plat = plat_dat;
	priv->ioaddr = res->addr;
	priv->dev->base_addr = (unsigned long)res->addr;
	
	...
	
	/* MDIO bus Registration */	
	ret = stmmac_mdio_register(ndev);
	
	//注册网卡设备
	ret = register_netdev(ndev);
}

drivers\net\ethernet\stmicro\stmmac\stmmac_mdio.c

工作:
1 设置总线信息,
2 遍历 0 - 32 地址中每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 创建phy 注册到mdio总线,即 将该phy添加到 mdio_map[]数组

int stmmac_mdio_register(struct net_device *ndev)
{
	int err = 0;
	struct mii_bus *new_bus;
	struct stmmac_priv *priv = netdev_priv(ndev);
	struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
	struct device_node *mdio_node = priv->plat->mdio_node;
	struct device *dev = ndev->dev.parent;
	int addr, found, max_addr;

...

	//分配 mii_bus
	new_bus = mdiobus_alloc();
	if (!new_bus)
		return -ENOMEM;

	if (mdio_bus_data->irqs)
		memcpy(new_bus->irq, mdio_bus_data->irqs, sizeof(new_bus->irq));

#ifdef CONFIG_OF
	if (priv->device->of_node)
		mdio_bus_data->reset_gpio = -1;
#endif

	//总线名
	new_bus->name = "stmmac";

	//总线读写 复位 函数
	...
	new_bus->read = &stmmac_mdio_read;
	new_bus->write = &stmmac_mdio_write;
	max_addr = PHY_MAX_ADDR;//32
	new_bus->reset = &stmmac_mdio_reset;


	/*
	总线 ---bind--- 网卡设备
	struct mii_bus
		void *priv;---------+ struct net_device *ndev

	*/
	new_bus->priv = ndev;
	new_bus->phy_mask = mdio_bus_data->phy_mask;
	new_bus->parent = priv->device;

	/*

	struct mii_bus

	struct stmmac_priv 
		struct plat_stmmacenet_data *plat
			struct device_node *mdio_node;
	*/
	/* 注册 mdio总线
	
	目前分析的主要工作:
	1
	struct mii_bus
		struct device dev;								struct plat_stmmacenet_data
			struct device_node	*of_node;-------------------struct device_node *phy_node; //phy配置节点
	
	2
	
	遍历 0 - 32 地址中每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 创建phy  注册到mdio总线,
	即 将该phy添加到	mdio_map[]数组

	*/
	err = of_mdiobus_register(new_bus, mdio_node);
	if (err != 0) {
		dev_err(dev, "Cannot register the MDIO bus\n");
		goto bus_register_fail;
	}

...

}

drivers\of\of_mdio.c

/* 注册 mii_bus 并且根据设备树信息创建 PHYS 

struct mii_bus

struct stmmac_priv 
	struct plat_stmmacenet_data *plat
		struct device_node *mdio_node;


目前分析的主要工作:
1
struct mii_bus
	struct device dev;								struct plat_stmmacenet_data
		struct device_node	*of_node;-------------------struct device_node *phy_node; //phy配置节点

2
将总线上 0- 32 地址 识别到但是并未注册的 phy设备,注册到 mdio总线,即添加到 将该phy添加到 mdio_map[]数组

*/
int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
{
	struct device_node *child;
	bool scanphys = false;
	int addr, rc;

	if (!np)
		return mdiobus_register(mdio);

	/* Do not continue if the node is disabled */
	if (!of_device_is_available(np))
		return -ENODEV;

	/* PHY addresses to be ignored when probing */
	//设置为全真
	mdio->phy_mask = ~0;

	/*

	struct mii_bus
		struct device dev; 								struct plat_stmmacenet_data
			struct device_node	*of_node;-------------------struct device_node *phy_node; //phy配置节点

	*/
	mdio->dev.of_node = np;
	mdio->dev.fwnode = of_fwnode_handle(np);


	// Register the MDIO bus 
	/*
	
	主要工作:
	遍历 0 - 32 地址中每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 创建phy  注册到mdio总线,
	即 将该phy添加到	mdio_map[]数组
	*/

	rc = mdiobus_register(mdio);

	...

	return 0;

unregister:
	mdiobus_unregister(mdio);
	return rc;
}
EXPORT_SYMBOL(of_mdiobus_register)

#define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)

drivers\net\phy\mdio_bus.c

/*

主要工作:
	遍历 0 - 32 地址中每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 创建phy  注册到mdio总线,
	即 将该phy添加到	mdio_map[]数组
*/
int __mdiobus_register(struct mii_bus *bus, struct module *owner)
{
	struct mdio_device *mdiodev;
	int i, err;
	struct gpio_desc *gpiod;
...

	bus->owner = owner;
	bus->dev.parent = bus->parent;
	bus->dev.class = &mdio_bus_class;
	bus->dev.groups = NULL;
	dev_set_name(&bus->dev, "%s", bus->id);

...

	//32
	/*
		检索 探测时忽略的PHY地址
	*/
	for (i = 0; i < PHY_MAX_ADDR; i++) {
		if ((bus->phy_mask & (1 << i)) == 0) {
			struct phy_device *phydev;

		 /*
	遍历 0 - 32 地址中每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 创建phy  注册到mdio总线,
	即 将该phy添加到	mdio_map[]数组
		*/

			phydev = mdiobus_scan(bus, i);
			if (IS_ERR(phydev) && (PTR_ERR(phydev) != -ENODEV)) {
				err = PTR_ERR(phydev);
				goto error;
			}
		}
	}

...
}
EXPORT_SYMBOL(__mdiobus_register);


/*
	遍历 0 - 32 地址中每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,没有的话 创建phy  注册到mdio总线,
	即 将该phy添加到	mdio_map[]数组
*/
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
	struct phy_device *phydev;
	int err;

	//检查总线上该地址是否有phy,有的话创建phy设备 !!!
	phydev = get_phy_device(bus, addr, false);
	if (IS_ERR(phydev))
		return phydev;

...

	//注册该 PHY
	//就是将该 phy 添加到 mdio_map[]数组。 即注册
	err = phy_device_register(phydev);
	if (err) {
		phy_device_free(phydev);
		return ERR_PTR(-ENODEV);
	}

	return phydev;
}
EXPORT_SYMBOL(mdiobus_scan);

\drivers\net\phy\phy_device.c

/**
 * get_phy_device - reads the specified PHY device and returns its @phy_device
 *		    struct
 * @bus: the target MII bus
 * @addr: PHY address on the MII bus

 */
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
	struct phy_c45_device_ids c45_ids = {0};
	u32 phy_id = 0;
	int r;

	//利用 MDIO总线的读写函数 查询总线指定地址是否存在phy ,并拿到该phy 的ID
	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);

	//为发现的 phy 设备 构造 phy信息
	/*
	struct mii_bus
	所在地址 0 - 31
	该phy ID 
	...
	
	*/
	return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}
EXPORT_SYMBOL(get_phy_device);




/**  利用 MDIO总线的读写函数 查询总线指定地址是否存在phy ,并拿到该phy 的ID
 * get_phy_id - reads the specified addr for its ID.
 * @bus: the target MII bus  
 * @addr: PHY address on the MII bus  
 * @phy_id: where to store the ID retrieved. 获取 该PHY ID 
 * @is_c45: If true the PHY uses the 802.3 clause 45 protocol
 * @c45_ids: where to store the c45 ID information.
 *

 *
 */
static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id,
		      bool is_c45, struct phy_c45_device_ids *c45_ids)
{
	int phy_reg;

	if (is_c45)
		return get_phy_c45_ids(bus, addr, phy_id, c45_ids);

	/* Grab the bits from PHYIR1, and put them in the upper half */
	phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
	if (phy_reg < 0) {
		/* if there is no device, return without an error so scanning
		 * the bus works properly
		 */
		if (phy_reg == -EIO || phy_reg == -ENODEV) {
			*phy_id = 0xffffffff;
			return 0;
		}

		return -EIO;
	}

	*phy_id = (phy_reg & 0xffff) << 16;

	/* Grab the bits from PHYIR2, and put them in the lower half */
	phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
	if (phy_reg < 0)
		return -EIO;

	*phy_id |= (phy_reg & 0xffff);

	return 0;
}

在这里插入图片描述

/* 为发现的 phy 设备 构造 phy信息
struct mii_bus
所在地址 0 - 31
该phy ID 
...

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

	/* 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;
	//创建状态机 任务
	INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
...

	//返回创建的 phy
	return dev;
}
EXPORT_SYMBOL(phy_device_create);

drivers\net\phy\phy_device.c

 /*
向 mdio总线添加 phy 设备
	
*/
int phy_device_register(struct phy_device *phydev)
{
	int err;

/*
struct phy_device
	struct mdio_device mdio;

*/
/*
工作:
	检查 0 - 32 之前没有检测的地址 是否有phy存在,有的添加到 mdio_map[]数组
*/
	err = mdiobus_register_device(&phydev->mdio);
	if (err)
		return err;

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

	/* Run all of the fixups for this PHY */
	//暂时不知道 phy_fixup_list 是干啥的
	// 如果该phy有设定一些额外的操作 这里就会执行phy 所设定的额外操作 如led灯控制等
	err = phy_scan_fixups(phydev);

	...

}
EXPORT_SYMBOL(phy_device_register);

drivers\net\phy\mdio_bus.c

/*
struct phy_device
	struct mdio_device mdio;

*/
/*
工作:
	检查 0 - 32 之前没有检测的地址 是否有phy存在,有的添加到 mdio_map[]数组
*/

int mdiobus_register_device(struct mdio_device *mdiodev)
{
	int err;

/*
struct phy_device
	struct mdio_device mdio;
		struct mii_bus *bus;
			//list of all PHYs on bus
			struct mdio_device *mdio_map[PHY_MAX_ADDR];
*/
//如果已经存在于 mdio_map[]数组,直接返回
	if (mdiodev->bus->mdio_map[mdiodev->addr])
		return -EBUSY;

	if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) {
		err = mdiobus_register_gpiod(mdiodev);
		if (err)
			return err;
	}

//添加到 mdio_map[]数组。 即注册
	mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;

	return 0;
}
EXPORT_SYMBOL(mdiobus_register_device);

至此暂时分析到的 GMAC主要工作
1 创建网卡设备 struct net_device,并根据 GMAC控制器 设备树资源,整合信息。
2 创建 mido总线 struct mii_bus,并遍历总线上 0 - 31 每一个地址,检查该地址是否有 phy,并检测有没有注册到mdio总线,并创建甚至phy设备, 注册到mdio总线,就是将该phy添加到 mdio_map[]数组
3 注册网卡设备

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值