我是靠谱客的博主 欣慰老虎,最近开发中收集的这篇文章主要介绍6 -->详解《switch 数据接收驱动框架、mtk7621集成交换芯片mt7530》之二,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
一、 MTK7621 网络通讯原理简述
本篇博文分析的是mtk7621的芯片所内嵌的交换芯片mt7530的驱动程序,MTK7621采用内部的 MDIO 接口管理MT7530 的 switch 芯片;MT7530包含多个7个 PHY 接口,其中第7个口连接到MTK7621芯片的eth0网口。请参考 驱动框架原理分析
二、交换机数据接收流程
- 文件 gsw_mt7621.c 中,在 switch 驱动加载时,注册 中断接收函数。
int mtk_gsw_init(struct fe_priv *priv)
{
struct device_node *np = priv->switch_np;
struct platform_device *pdev = of_find_device_by_node(np);
struct mt7620_gsw *gsw;
if (!pdev)
return -ENODEV;
if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
return -EINVAL;
gsw = platform_get_drvdata(pdev);
priv->soc->swpriv = gsw;
/* 注册中断接收函数 gsw_interrupt_mt7621() ,当网络中断数据到来时,回调此函数。 */
if (gsw->irq) {
request_irq(gsw->irq, gsw_interrupt_mt7621, 0,
"gsw", priv);
disable_irq(gsw->irq);
}
mt7621_hw_init(gsw, np);
if (gsw->irq)
enable_irq(gsw->irq);
return 0;
}
- MT7530 网络数据中断接收函数如下:
static irqreturn_t gsw_interrupt_mt7621(int irq, void *_priv)
{
struct fe_priv *priv = (struct fe_priv *)_priv;
struct mt7620_gsw *gsw = (struct mt7620_gsw *)priv->soc->swpriv;
u32 reg, i;
/* 保护、锁定switch芯片状态 */
reg = mt7530_mdio_r32(gsw, 0x700c);
mt7530_mdio_w32(gsw, 0x700c, reg);
/* 1.轮询 switch port 接口,输出端口状态 */
for (i = 0; i < 5; i++)
if (reg & BIT(i)) {
unsigned int link;
link = mt7530_mdio_r32(gsw,0x3008 + (i * 0x100)) & 0x1;
if (link != priv->link[i]) {
priv->link[i] = link;
if (link)
netdev_info(priv->netdev,"port %d link upn", i);
else
netdev_info(priv->netdev,"port %d link downn", i);
}
}
/* 2. 网口的载波侦听函数 */
mt7620_handle_carrier(priv);
return IRQ_HANDLED;
}
/* 3. 载波侦听函数 */
void mt7620_handle_carrier(struct fe_priv *priv)
{
if (!priv->phy)
return;
if (mt7620_has_carrier(priv))
netif_carrier_on(priv->netdev);
else
netif_carrier_off(priv->netdev);
}
- 文件 sch_generic.c 中的中断数据接收函数
/**
* netif_carrier_on - set carrier
* @dev: network device
*
* Device has detected that carrier.
*/
void netif_carrier_on(struct net_device *dev)
{
if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
if (dev->reg_state == NETREG_UNINITIALIZED)
return;
atomic_inc(&dev->carrier_changes);
linkwatch_fire_event(dev);
/* 开启中断下半部: 开启中断接收数据 workqueue 队列 */
if (netif_running(dev))
/* 检测网卡状态位,设置下次检测时间看门狗。
*/
__netdev_watchdog_up(dev);
}
}
EXPORT_SYMBOL(netif_carrier_on);
- 中断下半部如何开启的?
在中断函数中调用 netif_carrier_on()函数,接下来的调用过程如下:
–> (文件 link_watch.c中函数)linkwatch_fire_event(dev) --> linkwatch_schedule_work(urgent); --> schedule_delayed_work(&linkwatch_work, delay) || mod_delayed_work(system_wq, &linkwatch_work, 0)
最终执行到 __queue_delayed_work() 函数,函数内容如下:
static void __queue_delayed_work(int cpu, struct workqueue_struct *wq,
struct delayed_work *dwork, unsigned long delay)
{
struct timer_list *timer = &dwork->timer;
struct work_struct *work = &dwork->work;
/* 1. 处理软中断定时器相关参数
*/
WARN_ON_ONCE(!wq);
WARN_ON_ONCE(timer->function != delayed_work_timer_fn ||
timer->data != (unsigned long)dwork);
WARN_ON_ONCE(timer_pending(timer));
WARN_ON_ONCE(!list_empty(&work->entry));
/*
* If @delay is 0, queue @dwork->work immediately.
This is for
* both optimization and correctness.
The earliest @timer can
* expire is on the closest next tick and delayed_work users depend
* on that there's no such delay when @delay is 0.
*/
if (!delay) {
__queue_work(cpu, wq, &dwork->work);
return;
}
dwork->wq = wq;
dwork->cpu = cpu;
timer->expires = jiffies + delay;
/* 开启定时器、触发软中断
*/
if (unlikely(cpu != WORK_CPU_UNBOUND))
add_timer_on(timer, cpu);
else
add_timer(timer);
}
- 中断下半部分数据接收过程,函数内容如下:
static void __queue_work(int cpu, struct workqueue_struct *wq,
struct work_struct *work)
{
struct pool_workqueue *pwq;
struct worker_pool *last_pool;
struct list_head *worklist;
unsigned int work_flags;
unsigned int req_cpu = cpu;
/*
* While a work item is PENDING && off queue, a task trying to
* steal the PENDING will busy-loop waiting for it to either get
* queued or lose PENDING.
Grabbing PENDING and queueing should
* happen with IRQ disabled.
*/
WARN_ON_ONCE(!irqs_disabled());
debug_work_activate(work);
/* if draining, only works from the same workqueue are allowed */
if (unlikely(wq->flags & __WQ_DRAINING) &&
WARN_ON_ONCE(!is_chained_work(wq)))
return;
retry:
/* pwq which will be used unless @work is executing elsewhere */
if (wq->flags & WQ_UNBOUND) {
if (req_cpu == WORK_CPU_UNBOUND)
cpu = wq_select_unbound_cpu(raw_smp_processor_id());
pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu));
} else {
if (req_cpu == WORK_CPU_UNBOUND)
cpu = raw_smp_processor_id();
pwq = per_cpu_ptr(wq->cpu_pwqs, cpu);
}
/*
* If @work was previously on a different pool, it might still be
* running there, in which case the work needs to be queued on that
* pool to guarantee non-reentrancy.
*/
last_pool = get_work_pool(work);
if (last_pool && last_pool != pwq->pool) {
struct worker *worker;
spin_lock(&last_pool->lock);
/* 1. 查找 queue 池中待处理的数据 */
worker = find_worker_executing_work(last_pool, work);
if (worker && worker->current_pwq->wq == wq) {
pwq = worker->current_pwq;
} else {
/* meh... not running there, queue here */
spin_unlock(&last_pool->lock);
spin_lock(&pwq->pool->lock);
}
} else {
spin_lock(&pwq->pool->lock);
}
/*
* pwq is determined and locked.
For unbound pools, we could have
* raced with pwq release and it could already be dead.
If its
* refcnt is zero, repeat pwq selection.
Note that pwqs never die
* without another pwq replacing it in the numa_pwq_tbl or while
* work items are executing on it, so the retrying is guaranteed to
* make forward-progress.
*/
if (unlikely(!pwq->refcnt)) {
if (wq->flags & WQ_UNBOUND) {
spin_unlock(&pwq->pool->lock);
cpu_relax();
goto retry;
}
/* oops */
WARN_ONCE(true, "workqueue: per-cpu pwq for %s on cpu%d has 0 refcnt",
wq->name, cpu);
}
/* pwq determined, queue */
trace_workqueue_queue_work(req_cpu, pwq, work);
if (WARN_ON(!list_empty(&work->entry))) {
spin_unlock(&pwq->pool->lock);
return;
}
pwq->nr_in_flight[pwq->work_color]++;
work_flags = work_color_to_flags(pwq->work_color);
if (likely(pwq->nr_active < pwq->max_active)) {
trace_workqueue_activate_work(work);
pwq->nr_active++;
worklist = &pwq->pool->worklist;
if (list_empty(worklist))
pwq->pool->watchdog_ts = jiffies;
} else {
work_flags |= WORK_STRUCT_DELAYED;
worklist = &pwq->delayed_works;
}
insert_work(pwq, work, worklist, work_flags);
spin_unlock(&pwq->pool->lock);
}
- 工作队列 workqueue.c
/**
* insert_work - insert a work into a pool
* @pwq: pwq @work belongs to
* @work: work to insert
* @head: insertion point
* @extra_flags: extra WORK_STRUCT_* flags to set
*
* Insert @work which belongs to @pwq after @head.
@extra_flags is or'd to
* work_struct flags.
*
* CONTEXT:
* spin_lock_irq(pool->lock).
*/
static void insert_work(struct pool_workqueue *pwq, struct work_struct *work,
struct list_head *head, unsigned int extra_flags)
{
struct worker_pool *pool = pwq->pool;
/* we own @work, set data and link */
set_work_pwq(work, pwq, extra_flags);
list_add_tail(&work->entry, head);
get_pwq(pwq);
/*
* Ensure either wq_worker_sleeping() sees the above
* list_add_tail() or we see zero nr_running to avoid workers lying
* around lazily while there are works to be processed.
*/
smp_mb();
if (__need_more_worker(pool))
wake_up_worker(pool);
}
- workqueue 工作机制请参考《7. switch 数据技术驱动框架》。
最后
以上就是欣慰老虎为你收集整理的6 -->详解《switch 数据接收驱动框架、mtk7621集成交换芯片mt7530》之二的全部内容,希望文章能够帮你解决6 -->详解《switch 数据接收驱动框架、mtk7621集成交换芯片mt7530》之二所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复