Linux内核设备驱动之GIC驱动

对于可提供中断这种功能的IC来说,具体中断IC应该具有哪些功能参见IC描述,下面我们着重
讲解GIC,并且是GIC-V3版本的IC。
对于中断控制器来说,内核对其抽象,用数据结构struct irq_chip来对其描述。对于GIC来说,实现下面对象:
 

static struct irq_chip gic_chip = {  .name   = "GICv3",  .irq_mask  = gic_mask_irq,  .irq_unmask  = gic_unmask_irq,  .irq_eoi  = gic_eoi_irq,  .irq_set_type  = gic_set_type,  .irq_set_affinity = gic_set_affinity,  .irq_get_irqchip_state = gic_irq_get_irqchip_state,  .irq_set_irqchip_state = gic_irq_set_irqchip_state,  .flags   = IRQCHIP_SET_TYPE_MASKED, };

static struct irq_chip gic_eoimode1_chip = {  .name   = "GICv3",  .irq_mask  = gic_eoimode1_mask_irq,  .irq_unmask  = gic_unmask_irq,  .irq_eoi  = gic_eoimode1_eoi_irq,  .irq_set_type  = gic_set_type,  .irq_set_affinity = gic_set_affinity,  .irq_get_irqchip_state = gic_irq_get_irqchip_state,  .irq_set_irqchip_state = gic_irq_set_irqchip_state,  .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity,  .flags   = IRQCHIP_SET_TYPE_MASKED, };

当然,这里设置的函数都是对GIC控制器进行的操作。

对于中断控制来说,Linux内核定义了一个宏,用于在系统启动时候可以对GIC提供的初始化函数调用:

IRQCHIP_DECLARE

所以,这里我们定义GIC初始化如下:

IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);

这样的话,在内核启动时,会对这些定义的IRQCHIP_DECLARE宏下的回调函数调用,以对中断控制器初始化。

这里我们就是调用函数gic_of_init。

 

函数gic_of_init实现如下: static int __init gic_of_init(struct device_node *node, struct device_node *parent) {  void __iomem *dist_base;  struct redist_region *rdist_regs;  u64 redist_stride;  u32 nr_redist_regions;  int err, i;

 dist_base = of_iomap(node, 0);  if (!dist_base) {   pr_err("%s: unable to map gic dist registers\n",    node->full_name);   return -ENXIO;  }

 err = gic_validate_dist_version(dist_base);  if (err) {   pr_err("%s: no distributor detected, giving up\n",    node->full_name);   goto out_unmap_dist;  }

 if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))   nr_redist_regions = 1;

 rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL);  if (!rdist_regs) {   err = -ENOMEM;   goto out_unmap_dist;  }

 for (i = 0; i < nr_redist_regions; i++) {   struct resource res;   int ret;

  ret = of_address_to_resource(node, 1 + i, &res);   rdist_regs[i].redist_base = of_iomap(node, 1 + i);   if (ret || !rdist_regs[i].redist_base) {    pr_err("%s: couldn't map region %d\n",           node->full_name, i);    err = -ENODEV;    goto out_unmap_rdist;   }   rdist_regs[i].phys_base = res.start;  }

 if (of_property_read_u64(node, "redistributor-stride", &redist_stride))   redist_stride = 0;

 err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,         redist_stride, &node->fwnode);  if (err)   goto out_unmap_rdist;

 gic_populate_ppi_partitions(node);  gic_of_setup_kvm_info(node);  return 0;

out_unmap_rdist:  for (i = 0; i < nr_redist_regions; i++)   if (rdist_regs[i].redist_base)    iounmap(rdist_regs[i].redist_base);  kfree(rdist_regs); out_unmap_dist:  iounmap(dist_base);  return err; }

这里,我们需要了解GIC控制器提供的基础寄存器,然后对其配置。

static int __init gic_init_bases(void __iomem *dist_base,      struct redist_region *rdist_regs,      u32 nr_redist_regions,      u64 redist_stride,      struct fwnode_handle *handle) {  u32 typer;  int gic_irqs;  int err;

 if (!is_hyp_mode_available())   static_key_slow_dec(&supports_deactivate);

 if (static_key_true(&supports_deactivate))   pr_info("GIC: Using split EOI/Deactivate mode\n");

 gic_data.fwnode = handle;  gic_data.dist_base = dist_base;  gic_data.redist_regions = rdist_regs;  gic_data.nr_redist_regions = nr_redist_regions;  gic_data.redist_stride = redist_stride;

 gicv3_enable_quirks();

 /*   * Find out how many interrupts are supported.   * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)   */  typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);  gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer);  gic_irqs = GICD_TYPER_IRQS(typer);  if (gic_irqs > 1020)   gic_irqs = 1020;  gic_data.irq_nr = gic_irqs;

 gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,        &gic_data);  gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));

 if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {   err = -ENOMEM;   goto out_free;  }

 set_handle_irq(gic_handle_irq);

 if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())   its_init(handle, &gic_data.rdists, gic_data.domain);

 gic_smp_init();  gic_dist_init();  gic_cpu_init();  gic_cpu_pm_init();

 return 0;

out_free:  if (gic_data.domain)   irq_domain_remove(gic_data.domain);  free_percpu(gic_data.rdists.rdist);  return err; }

这里最为重要的是关注函数set_handle_irq(),其设置了GIC控制器中断发生时,中断调用的处理函数,这里调用函数

gic_handle_irq()

此中断处理程序会根据GIC中断控制器状态,获取中断号,然后调用不同的中断处理程序。

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) {  u32 irqnr;

 do {   irqnr = gic_read_iar();

  if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {    int err;

   if (static_key_true(&supports_deactivate))     gic_write_eoir(irqnr);

   err = handle_domain_irq(gic_data.domain, irqnr, regs);    if (err) {     WARN_ONCE(true, "Unexpected interrupt received!\n");     if (static_key_true(&supports_deactivate)) {      if (irqnr < 8192)       gic_write_dir(irqnr);     } else {      gic_write_eoir(irqnr);     }    }    continue;   }

  if (irqnr < 16) {    gic_write_eoir(irqnr);    if (static_key_true(&supports_deactivate))     gic_write_dir(irqnr); #ifdef CONFIG_SMP    /*     * Unlike GICv2, we don't need an smp_rmb() here.     * The control dependency from gic_read_iar to     * the ISB in gic_write_eoir is enough to ensure     * that any shared data read by handle_IPI will     * be read after the ACK.     */    handle_IPI(irqnr, regs); #else    WARN_ONCE(true, "Unexpected SGI received!\n"); #endif    continue;   }  } while (irqnr != ICC_IAR1_EL1_SPURIOUS); }

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值