1 gpio控制器驱动probe函数主要实现3个功能
1、实现struct gpio_chip
注册一个GPIO控制器
对于只能做简单GPIO的引脚控制器,简单的struct gpio_chip就足以处理它。
2、实现struct irq_chip
如果控制器可以在GPIO功能之上产生中断,则必须设置struct irq_chip,并将其注册到IRQ子系统
这个结构实现了一组描述如何驱动中断控制器的方法,这些方法由核心IRQ代码直接调用
3、注册中断处理函数
devm_request_irq或者irq_set_chained_handler_and_data
下面代码是海思gpio控制器中断的注册
中断处理函数主要读寄存器确认是gpio组中具体哪一个gpio产生中断,拿到对应的irq,并调用子gpio注册的中断处理函数
static irqreturn_t pl061_irq_handler(int irq, void *data)
{
unsigned long pending;
int offset;
struct gpio_chip *gc = data;
struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
pending = readb(chip->base + GPIOMIS);//读中断状态寄存器
writeb(pending, chip->base + GPIOIC);
if (pending) {
for_each_set_bit(offset, &pending, PL061_GPIO_NR)
generic_handle_irq(irq_find_mapping(gc->irqdomain,//该函数获取gpio管脚对应的irq,并传给handle处理调用中断处理函数
offset));
}
return IRQ_HANDLED;
}
2 直接代码说明
static struct irq_chip pl061_irqchip = {
.name = "pl061",
//用于CPU对该irq的回应,通常表示cpu希望要清除该irq的pending状态,准备接受下一个irq请求
.irq_ack = pl061_irq_ack,
//irq_mask 这个钩子屏蔽硬件中的中断源,使它不能再被引起中断
.irq_mask = pl061_irq_mask,
//irq_unmask 该钩子取消屏蔽中断源
.irq_unmask = pl061_irq_unmask,
//设置IRQ的流类型(IRQ_TYPE_LEVEL/等)。
.irq_set_type = pl061_irq_type,
//启用/禁用IRQ的电源管理唤醒
.irq_set_wake = pl061_irq_set_wake,
};
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
struct pl061_platform_data *pdata = dev_get_platdata(dev);
struct pl061_gpio *chip;
int ret, irq, i, irq_base;
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
if (pdata) {
chip->gc.base = pdata->gpio_base;
irq_base = pdata->irq_base;
if (irq_base <= 0) {
dev_err(&adev->dev, "invalid IRQ base in pdata\n");
return -ENODEV;
}
} else {
#ifdef CONFIG_ARCH_HISI_BVT
if (dev->of_node) {
i = of_alias_get_id(dev->of_node, "gpio");
chip->gc.base = i * PL061_GPIO_NR;
}
if (chip->gc.base < 0)
chip->gc.base = -1;
#else
chip->gc.base = -1;
#endif
irq_base = 0;
}
chip->base = devm_ioremap_resource(dev, &adev->res);
if (IS_ERR(chip->base))
return PTR_ERR(chip->base);
spin_lock_init(&chip->lock);
if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
chip->gc.request = gpiochip_generic_request;
chip->gc.free = gpiochip_generic_free;
}
chip->gc.get_direction = pl061_get_direction;
chip->gc.direction_input = pl061_direction_input;
chip->gc.direction_output = pl061_direction_output;
chip->gc.get = pl061_get_value;
chip->gc.set = pl061_set_value;
chip->gc.ngpio = PL061_GPIO_NR;
chip->gc.label = dev_name(dev);
chip->gc.parent = dev;
chip->gc.owner = THIS_MODULE;
//注册struct gpio_chip
ret = gpiochip_add_data(&chip->gc, chip);
if (ret)
return ret;
/*
* irq_chip support
*/
writeb(0, chip->base + GPIOIE); /* disable irqs */
irq = adev->irq[0];
if (irq < 0) {
dev_err(&adev->dev, "invalid IRQ\n");
return -ENODEV;
}
//注册struct irq_chip
ret = gpiochip_irqchip_add(&chip->gc, &pl061_irqchip,
irq_base, handle_bad_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_info(&adev->dev, "could not add irqchip\n");
return ret;
}
#ifdef CONFIG_ARCH_HISI_BVT
//注册gpio控制器中断处理函数,一组GPIO共享一个中断源,中断处理函数中区分具体哪个gpio
ret = devm_request_irq(dev, irq, pl061_irq_handler, IRQF_SHARED,
dev_name(dev), &chip->gc);
if (ret) {
dev_info(dev, "request irq failed: %d\n", ret);
return ret;
}
/* Set the parent IRQ for all affected IRQs */
for (i = 0; i < chip->gc.ngpio; i++)
irq_set_parent(irq_find_mapping(chip->gc.irqdomain, i), irq);
#else
gpiochip_set_chained_irqchip(&chip->gc, &pl061_irqchip,
irq, pl061_irq_handler);
#endif
for (i = 0; i < PL061_GPIO_NR; i++) {
if (pdata) {
if (pdata->directions & (BIT(i)))
pl061_direction_output(&chip->gc, i,
pdata->values & (BIT(i)));
else
pl061_direction_input(&chip->gc, i);
}
}
amba_set_drvdata(adev, chip);
dev_info(&adev->dev, "PL061 GPIO chip @%pa registered\n",
&adev->res.start);
return 0;
}