• Linux ARM平台开发系列讲解(网络篇)1.8 PHY中断机制分析


    1. phy如何获取phy中断中的irq编号

    phy_device_create函数里面, dev->irqbus->irq[addr]获得, 所以如果希望phy设备跟中断联系起来,需要在mdio bus里面为irq[addr]赋值

    • 源码路径: drivers\net\phy\phy_device.c
    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->port = PORT_TP;
    	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);
    	/* 注意这里初始化了工作队列 */
        /* dev->state_queue这个工作队列在这里只是初始化了一下,但并没有调度,在什么地方调度呢? */
    	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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    2. net_device的和phy_device何时关联到一起

    net_deviceopen函数通常会调用phy_connect函数将net_device的和phy_device关联到一起。

    cpsw.c为例,函数调用的顺序为:

    • 源码路径:drivers\net\ethernet\ti\cpsw.c

    cpsw_ndo_open->
    cpsw_slave_open->
    phy_connect(priv->ndev, slave->data->phy_id,&cpsw_adjust_link, slave->data->phy_if);->
    phy_connect_direct->
    phy_start_machine(phydev);->
    queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);

    cpsw_slave_open函数中,调用了phy_connect,如下:

    phy = phy_connect(priv->ndev, slave->data->phy_id,
    		 &cpsw_adjust_link, slave->data->phy_if);
    
    • 1
    • 2
    struct phy_device *phy_connect(struct net_device *dev, const char *bus_id,
    			       void (*handler)(struct net_device *),
    			       phy_interface_t interface)
    {
    	struct phy_device *phydev;
    	struct device *d;
    	int rc;
    
    	/* Search the list of PHY devices on the mdio bus for the
    	 * PHY with the requested name
    	 */
    	d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id);
    	if (!d) {
    		pr_err("PHY %s not found\n", bus_id);
    		return ERR_PTR(-ENODEV);
    	}
    	phydev = to_phy_device(d);
    
    	rc = phy_connect_direct(dev, phydev, handler, interface);
    	put_device(d);
    	if (rc)
    		return ERR_PTR(rc);
    
    	return phydev;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这里phy_connect主要是将mdio总线在scan时,已经调用phy_device_create函数创建了phy_device, 并且把这个device注册到mdio总线上了,此处从总线上找到名称为bus_idphy_device. 将net_devicephy_device建立起联系。

    3. phy_device_create 时对队列的初始化

    phy_device_create中会有对state_queue的初始化

    INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
    
    • 1

    4. phy_device和phy_driver建立的关键

    在上述中,提到了phy_connect,它主要是对phy_devicephy_driver进行连接,其中,调用了phy_connect_direct函数,这个函数才是devdrv建立的关键。如下源代码中:phy_attach_direct会找到phy_device,然后将其连接到phy_driver上。

    int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
    		       void (*handler)(struct net_device *),
    		       phy_interface_t interface)
    {
    	int rc;
    
    	if (!dev)
    		return -EINVAL;
    	/* 将设备连接到phy指针 */
    	rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
    	if (rc)
    		return rc;
    
    	phy_prepare_link(phydev, handler);
    	 /* 如果这个phy设备支持中断,就会进行中断的注册 */
    	if (phy_interrupt_is_valid(phydev))
    		phy_request_interrupt(phydev);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    5. phy中断的注册

    在上述第4小节函数phy_connect_direct中,phydriverdevice进行了匹配连接,在连接完成后,判断了phy是否支持中断,如果支持中断,就会进行phy中断的注册

    	if (phy_interrupt_is_valid(phydev))
    		phy_request_interrupt(phydev);
    
    • 1
    • 2
    void phy_request_interrupt(struct phy_device *phydev)
    {
    	int err;
    
    	err = request_threaded_irq(phydev->irq, NULL, phy_interrupt,
    				   IRQF_ONESHOT | IRQF_SHARED,
    				   phydev_name(phydev), phydev);
    	if (err) {
    		phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n",
    			    err, phydev->irq);
    		phydev->irq = PHY_POLL;
    	} else {
    		if (phy_enable_interrupts(phydev)) {
    			phydev_warn(phydev, "Can't enable interrupt, falling back to polling\n");
    			phy_free_interrupt(phydev);
    			phydev->irq = PHY_POLL;
    		}
    	}
    }
    EXPORT_SYMBOL(phy_request_interrupt);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6. phy中断的回调函数

    在注册phy中断的过程中,注册了phy中断回调函数,进入回调函数后,会调用phy的工作队列,这个工作队列前面也提到了,在phy_device_create时已经对其进行了初始化。

    static irqreturn_t phy_interrupt(int irq, void *phy_dat)
    {
    	struct phy_device *phydev = phy_dat;
    	struct phy_driver *drv = phydev->drv;
    
    	if (drv->handle_interrupt)
    		return drv->handle_interrupt(phydev);
    
    	if (drv->did_interrupt && !drv->did_interrupt(phydev))
    		return IRQ_NONE;
    
    	/* reschedule state queue work to run as soon as possible */
    	phy_trigger_machine(phydev);
    
    	/* did_interrupt() may have cleared the interrupt already */
    	if (!drv->did_interrupt && phy_clear_interrupt(phydev)) {
    		phy_error(phydev);
    		return IRQ_NONE;
    	}
    
    	return IRQ_HANDLED;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    返回总目录

  • 相关阅读:
    JSP application 对象
    C++面向对象三大特性之一---->继承详解
    datalist 是什么?以及作用是什么?
    windows 驱动与内核调试 学习3
    16.面试重点Cookie&Session
    百钱买百鸡
    UE4 Android端使用MediaPlayer注意事项
    git基本命令
    Ubuntu安装ufw
    顽固污渍一键去除,还有紫外线除菌功能,希亦超声波清洗机体验
  • 原文地址:https://blog.csdn.net/DSMGUOGUO/article/details/125225784