This article is a logical continuation of the previous one and its reading is recommended after reading the previous material . The current note is necessary to understand the subsequent material, an additional understanding of the gpio subsystem as a whole and contributes to the development of its own gpio drivers. The provisions of this article are already applicable not only to our virtual gpio driver, but also to any mmio gpio driver as a whole. We will talk about the “optimization” mentioned in the previous part.
Before proceeding to more interesting and useful things, you need to restore order. As mentioned earlier, the amount of code can be reduced. To do this, we use two subsystems (drivers) gpio-generic (gpio-mmio starting from 4.7) and irq_chip_generic.
According to the principles of the linux kernel, it is better to sacrifice performance for the sake of clarity and the absence of a repetitive sample code. And the use of existing implementations in the linux kernel undoubtedly serves this purpose.
A small digression.
Initially, when switching to bgpio, a small successful experiment was conducted for a virtual driver with support for devices with 8, 16 and 32 inputs. The final code can be viewed here . This result was achieved thanks to a small, only 7 lines, ivshmem modification, to add the sub-vendor-id and sub-device-id parameters for the pci device (the patch included in the branch is for qemu 2.5.1.1).
In the kernel, this driver depends on CONFIG_GPIO_GENERIC .
Let's start with a simple driver that was created for various MMIO gpio, and later parts of which were actively used by some drivers. He was introduced by Anton Vorontsov in 2010 ( https://lkml.org/lkml/2010/8/25/303 ). In his patch, he announced the following features for the driver:
In principle, this driver saves us from the need to implement such functions as, setting the state and choosing the direction of the contact.
To use, all you need to do is just transfer the size and data register, everything else is optional and depends on the specific implementation of the gpio controller, the initialization function takes as parameters:
That is, the following situations are possible:
The same is true for directions. Either the job register as an output, or as an input. Or the lack of the ability to switch the contact to the output or input state.
You can simulate any of the above activities. For our purposes, behavior number 3 is sufficient and setting the state as an output, with the state for the default contact as input.
#if IS_ENABLED(CONFIG_GPIO_GENERIC) int bgpio_init(struct gpio_chip *gc, struct device *dev, unsigned long sz, void __iomem *dat, void __iomem *set, void __iomem *clr, void __iomem *dirout, void __iomem *dirin, unsigned long flags); #define BGPIOF_BIG_ENDIAN BIT(0) #define BGPIOF_UNREADABLE_REG_SET BIT(1) /* reg_set is unreadable */ #define BGPIOF_UNREADABLE_REG_DIR BIT(2) /* reg_dir is unreadable */ #define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3) #define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */ #define BGPIOF_NO_OUTPUT BIT(5) /* only input */ #endif
It should be noted that as a size bgpio_init does not accept the number of inputs, but the parameter is a multiple of 8, that is, ngpio / 8.
err = bgpio_init(&vg->chip, dev, BITS_TO_BYTES(VIRTUAL_GPIO_NR_GPIOS), data, NULL, NULL, dir, NULL, 0);
In the kernel, this driver depends on GENERIC_IRQ_CHIP , which is a disadvantage, since it is not possible to enable this parameter via menuconfig or oldconfig.
Now consider the slightly more complicated part. irq_chip_generic was introduced in the kernel version v3.0-rc1 and performs functions similar to gpio-mmio, that is, it provides the standard implementation for many cases of irq_chip.
The standard register read / write functions are 32 bit, this was one of the reasons why I decided to abandon the 8/16 bit driver version, however it is possible to provide the irq_chip_generic subsystem with read / write functions or specify standard ones (for example, ioread8, iowrite8 ).
The memory for irq_chip_generic and initialization is done with irq_alloc_generic_chip. The function, in addition to the trivial parameters, also requires irq_base, which, generally speaking, is unknown to us before calling gpiochip_irqchip_add, which in turn requires struct irq_chip. But irq_alloc_generic_chip is only responsible for allocating memory and initializing some parameters, so we can pass 0 as the irq_base parameter and assign the real value after calling the gpiochip_irqchip_add function.
gc = irq_alloc_generic_chip(VIRTUAL_GPIO_DEV_NAME, 1, 0, vg->data_base_addr, handle_edge_irq);
To use, it is enough to specify the standard functions of masking / demasking, interrupt acknowledgments and registers. The function of setting the interrupt type remains unchanged with us, just like our interrupt handler.
ct->chip.irq_ack = irq_gc_ack_set_bit; ct->chip.irq_mask = irq_gc_mask_clr_bit; ct->chip.irq_unmask = irq_gc_mask_set_bit; ct->chip.irq_set_type = virtual_gpio_irq_type;
Register offset for confirmation and masking / demasking:
ct->regs.ack = VIRTUAL_GPIO_INT_EOI; ct->regs.mask = VIRTUAL_GPIO_INT_EN;
We still manage the interrupt type registers ourselves, in the function virtual_gpio_irq_type.
Accordingly, in the initialization of gpiochip_irqchip_add and gpiochip_set_chained_irqchip, we transfer the irq_chip instance selected to irq_chip_generer-> chip_types [0] (irq_chip_generic can have several irq_chip types associated with the same subset set of the same set in the same subset set of the same figure.
After that we use the received irq_base and complete the irq_chip_generic setting.
irq_setup_generic_chip(gc, 0, 0, 0, 0); gc->irq_cnt = VIRTUAL_GPIO_NR_GPIOS; gc->irq_base = vg->chip.irq_base; u32 msk = IRQ_MSK(32); u32 i; for (i = gc->irq_base; msk; msk >>= 1, i++) { if (!(msk & 0x01)) continue; struct irq_data *d = irq_get_irq_data(i); d->mask = 1 << (i - gc->irq_base); irq_set_chip_data(i, gc); }
We intentionally pass 0 as the msk parameter to avoid reinitializing the irq numbers.
irq_chip_generic is used for quite serious serious irq controllers mostly platform, so the mask for the irq registers is calculated in advance to speed up the interrupt processing and not to waste time on the mask calculation in the process. Since we are passing 0 as a parameter to the irq_setup_generic_chip function, we initialize the masks ourselves.
There remains another problem with irq_set_chip_data (which is nothing more than irq_data.chip_data = (void*)struct irq_chip_generic;
). The fact is that gpiolib also relies on void * chip_data and assumes that there should be a pointer to a struct gpio_chip ( http://lxr.free-electrons.com/source/drivers/gpio/gpiolib.c#L1138 ). The problem is solved by providing our own implementations of the functions irq_request_resources and irq_release_resources instead of the standard gpiochip_irq_reqres and gpiochip_irq_relres.
Strictly speaking, the above manipulations are not obvious, and there may be some difficulty with their understanding. Let's try to abandon gpiochip_irqchip_add.
First of all, we will request irq_base ourselves:
irq_base = irq_alloc_descs(-1, 0, vg->chip.ngpio, 0);
As we remember from the previous article, our linear case (the irq_domain_add_simple function does almost the same thing, but is obsolete).
vg->irq_domain = irq_domain_add_linear(0, vg->chip.ngpio, &irq_domain_simple_ops, vg); irq_domain_associate_many(vg->irq_domain, irq_base, 0, vg->chip.ngpio);
The next step remains almost unchanged and consists in associating gpio_chip with irq_chip:
vg->chip.irqdomain = vg->irq_domain; gpiochip_set_chained_irqchip(&vg->chip, &ct->chip, pdev->irq, NULL);
The only time is that in order for the file "edge" to be available in the corresponding contact management directory ( gpiolib-sysfs ), you must specify the translation function to_irq:
vg->chip.to_irq = virtual_gpio_to_irq;
Thanks to the use of existing subsystems, the code is slightly reduced. In understanding, the driver has also become easier, since the use of these subsystems is quite common, and it is immediately obvious that this is a standard driver without pitfalls. Moreover, with such a structure it will be much easier to use the mechanisms of the Device Tree.
A conditional disadvantage is the dependence on CONFIG_GPIO_GENERIC and GENERIC_IRQ_CHIP , but the first parameter is easily included in the linux yard configuration, and the second one, quite possibly, is already included.
Materials recommended for additional reading:
Source: https://habr.com/ru/post/307840/
All Articles