linux get phy id不对,linux kernel对phy中断的设计

1. phy设备何处获得irq编号

在phy_device_create函数里面, dev->irq从bus->irq[addr]获得, 所以如果希望phy

设备跟中断联系起来,需要在mdio bus里面为irq[addr]赋值。

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;

/* We allocate the device, and initialize the default values */

dev = kzalloc(sizeof(*dev), GFP_KERNEL);

if (NULL == dev)

return (struct phy_device *)PTR_ERR((void *)-ENOMEM);

dev->dev.release = phy_device_release;

dev->speed = 0;

dev->duplex = -1;

dev->pause = 0;

dev->asym_pause = 0;

dev->link = 1;

dev->interface = PHY_INTERFACE_MODE_GMII;

dev->autoneg = AUTONEG_ENABLE;

dev->is_c45 = is_c45;

dev->addr = addr;

dev->phy_id = phy_id;

if (c45_ids)

dev->c45_ids = *c45_ids;

dev->bus = bus;

dev->dev.parent = bus->parent;

dev->dev.bus = &mdio_bus_type;

dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL;

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

dev->state = PHY_DOWN;

mutex_init(&dev->lock);

/* 注意这里初始化了两个工作队列*/

/* dev->state_queue这个工作队列在这里只是初始化了一下,当并没有调度,在什么地方调度呢?*/

INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);

INIT_WORK(&dev->phy_queue, phy_change);

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

*/

request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));

device_initialize(&dev->dev);

return dev;

}

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

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

以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);

static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv)

{

.......

slave->phy = phy_connect(priv->ndev, slave->data->phy_id,

&cpsw_adjust_link, slave->data->phy_if);

if (IS_ERR(slave->phy)) {

dev_err(priv->dev, "phy %s not found on slave %d\n",

slave->data->phy_id, slave->slave_num);

slave->phy = NULL;

} else {

dev_info(priv->dev, "phy found : id is : 0x%x\n",

slave->phy->phy_id);

phy_start(slave->phy);

/* Configure GMII_SEL register */

cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface,

slave->slave_num);

}

}

------

看看phy_connect实际干了什么

mdio总线在scan时,已经调用phy_device_create函数创建了phy_device, 并且把这个device注册到mdio总线上了,此处从总线上找到名称为bus_id的phy_device. 将net_device与

phy_device建立起联系。

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

if (rc)

return ERR_PTR(rc);

return phydev;

}

在phy_device_create中会有对state_queue的初始化

{

...

INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);

...

}

void phy_start_machine(struct phy_device *phydev)

{

queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);

}

真正将net_device与phy_device建立起联系的函数是phy_connect_direct

int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,

void (*handler)(struct net_device *),

phy_interface_t interface)

{

int rc;

rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);

if (rc)

return rc;

phy_prepare_link(phydev, handler);

/* phy_start_machine实际上是queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);

将phydev->state_queue这个工作队列调度到system_power_efficient_wq上等待执行 */

phy_start_machine(phydev);

/* 如果这个phy设备支持中断,就会进行中断的注册 */

if (phydev->irq > 0)

phy_start_interrupts(phydev);

return 0;

}

3. phy中断的注册

函数phy_connect_direct中,如果phy设备支持中断,就会调用

phy_start_interrupts进行中断的注册

cpsw_slave_open->

phy_connect(priv->ndev, slave->data->phy_id,

&cpsw_adjust_link, slave->data->phy_if);

->phy_start_interrupts

/**

* phy_start_interrupts - request and enable interrupts for a PHY device

* @phydev: target phy_device struct

*

* Description: Request the interrupt for the given PHY.

*   If this fails, then we set irq to PHY_POLL.

*   Otherwise, we enable the interrupts in the PHY.

*   This should only be called with a valid IRQ number.

*   Returns 0 on success or < 0 on error.

*/

int phy_start_interrupts(struct phy_device *phydev)

{

atomic_set(&phydev->irq_disable, 0);

if (request_irq(phydev->irq, phy_interrupt, 0, "phy_interrupt",

phydev) < 0) {

pr_warn("%s: Can't get IRQ %d (PHY)\n",

phydev->bus->name, phydev->irq);

phydev->irq = PHY_POLL;

return 0;

}

return phy_enable_interrupts(phydev);/* clean 中断,config中断,

实际上是调用phydev->drv->ack_interrupt

phydev->drv->config_intr  */

}

/**

* phy_enable_interrupts - Enable the interrupts from the PHY side

* @phydev: target phy_device struct

*/

static int phy_enable_interrupts(struct phy_device *phydev)

{

int err = phy_clear_interrupt(phydev); /* phydev->drv->ack_interrupt */

if (err < 0)

return err;

return phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); /* phydev->drv->config_intr  */

}

看看中断服务程序干了些什么

phy_interrupt这个isr会把phydev->phy_queue这个工作队列调度到system_power_efficient_wq上

static irqreturn_t phy_interrupt(int irq, void *phy_dat)

{

struct phy_device *phydev = phy_dat;

if (PHY_HALTED == phydev->state)

return IRQ_NONE;        /* It can't be ours.  */

/* The MDIO bus is not allowed to be written in interrupt

* context, so we need to disable the irq here.  A work

* queue will write the PHY to disable and clear the

* interrupt, and then reenable the irq line.

*/

disable_irq_nosync(irq);

atomic_inc(&phydev->irq_disable);

/* 调度phydev->phy_queue。 phy_queue在phy_device_create已经初始化了 */

queue_work(system_power_efficient_wq, &phydev->phy_queue);

return IRQ_HANDLED;

}

在phy_device_create函数里,已经初始化了phy_queue,相应的处理函数是phy_change.

phy_device_create

{...

INIT_WORK(&dev->phy_queue, phy_change);

....

}

看看phy_change干了什么?

/**

* phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes

* @work: work_struct that describes the work to be done

*/

void phy_change(struct work_struct *work)

{

struct phy_device *phydev =

container_of(work, struct phy_device, phy_queue);

if (phydev->drv->did_interrupt &&

!phydev->drv->did_interrupt(phydev))

goto ignore;

if (phy_disable_interrupts(phydev))

goto phy_err;

mutex_lock(&phydev->lock);

if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))

phydev->state = PHY_CHANGELINK;

mutex_unlock(&phydev->lock);

atomic_dec(&phydev->irq_disable);

enable_irq(phydev->irq);

/* Reenable interrupts */

if (PHY_HALTED != phydev->state &&

phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED))

goto irq_enable_err;

/* reschedule state queue work to run as soon as possible */

cancel_delayed_work_sync(&phydev->state_queue);/* phydev->state_queue本来是一个delayed work_queue,中断来时,将这个delayed的wq取消掉,改成立刻调度 */

queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0);  /* 立刻调度state_queue */

return;

ignore:

atomic_dec(&phydev->irq_disable);

enable_irq(phydev->irq);

return;

irq_enable_err:

disable_irq(phydev->irq);

atomic_inc(&phydev->irq_disable);

phy_err:

phy_error(phydev);

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值