I am trying to use the Xilinx interrupt controller driver in an embedded ARM FPGA system I am developing.
(https://github.com/torvalds/linux/blob/master/drivers/irqchip/irq-xilinx-intc.c)
At the end of this driver is the line:
IRQCHIP_DECLARE(xilinx_intc_xps, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init);
I have added an entry in my device tree for the interrupt controller.
xil_intc: xil_intc#41810000 {
compatible = "xlnx,xps-intc-1.00.a";
interrupt-parent = <&intc>;
interrupts = <0x0 0x1e 0x04>;
reg = <0x41810000 0x10000>;
interrupt-controller;
#interrupt-cells = <2>;
xlnx,kind-of-intr = <0x0>;
xlnx,num-intr-inputs = <0x1>;
};
However, from what I can tell, the xilinx_intc_of_init function is never called during startup. I added a pr_info at the beginning of the function, but I never see it called.
The only message that seems to indicate an issue is:
[ 0.177772] irq: no irq domain found for /amba/xil_intc#41810000 !
I have successfully written and compiled other device drivers, added entries in the device tree, and had them load up and show in dmesg, but for some reason I can't get this one to work.
Any suggestions on debugging this?
For what it's worth, I'm compiling the driver into a kernel module, installing using modules_install and have added an entry to /etc/modules to load it on startup.
Edit: I am using a 4.6 kernel.
IRQCHIP_DECLARE(xilinx_intc_xps, "xlnx,xps-intc-1.00.a", xilinx_intc_of_init);
The driver irq-xilinx-intc.c is using the above call to register with the irq subsystem.
If irq driver uses IRQCHIP_DECLARE macro it will follow the below sequence to invoke the xilinx_intc_of_init() (callback function)
start_kernel() –> init_IRQ() --> irqchip_init() --> of_irq_init() --> call-back function (xilinx_intc_of_init)
If The Driver was registered using IRQCHIP_DECLARE it must be compiled into the kernel and the callback function will be invoked at kernel boot time.
It doesn't work as kernel modules/overlay like other device drivers.
I came to the conclusion that loading the irqchip driver from /lib/modules was too late in the boot process.
I ended up rebuilding the entire kernel with a modified configuration to include this driver, and then the initialization worked properly.
I guess the lesson is that IRQCHIP drivers must be compiled into the kernel and don't work as kernel modules like other device drivers? I can't find any documentation that says so, but that's the behavior I observed.
Related
I am trying to write a linux device driver for a SPI device, using Atmels SAMA5d35 MPU. Linux version is 4.19.30.
I have configured the pins in my device tree and set the status to "okay".
After that the pins changed mode as expected. My question is then, how do I manipulate pinmodes during runtime? My SPI device is not always on, neither is my driver always loaded. When that is the case I want the configured SPI pins to change mode to normal GPIOs (for power-consumption).
Is it the drivers task to do that, or is it something one can define in the device tree, when the driver for example is not loaded?
I have tried to implement a pinctrl-0 for setting the pins to SPI mode, and then a pinctrl-1 for setting the same pins to normal GPIOs. My idea was to switch between these pin configuration in the device driver, but unfortunately the device tree complaints about using the same pins for both pinctrl-0 and pinctrl-1 (compiles fine, but system wont boot). Although this was just my logical thoughts. I am sure there is a more correct way to do the things I stated above.
spi0: spi#f0004000 {
status = "okay";
ext_adc: ads1258#1 {
compatible = "ti,ads1258";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pinctrl_spi0_default>;
pinctrl-1 = <&pinctrl_spi0_sleep>;
reg = <1>; /* Hardware chipselect SPI0_NPCS1 */
spi-max-frequency = <16000000>; /* According to datasheet */
start-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
drdy-gpios = <&pioD 22 GPIO_ACTIVE_LOW>;
enaadc-gpios = <&pioA 26 GPIO_ACTIVE_HIGH>;
};
};
My question is then, how do I manipulate pinmodes during runtime?
That's called "Runtime pinmuxing", which is described in Documentation/pinctrl.txt.
But you may not have to do that.
When that is the case I want the configured SPI pins to change mode to normal GPIOs (for power-consumption).
Is it the drivers task to do that, or is it something one can define in the device tree, when the driver for example is not loaded?
The default mode for a pin (of Atmel/Microchip SoCs) is to be unassigned to any peripheral mode, and be available for use as a GPIO.
All pins that are not assigned to a peripheral or acquired by a driver as a GPIO, end up in the pool of available GPIOs.
So when your SPI driver is not successfully installed, those pins it would use will be in the pool of available GPIOs.
I have tried to implement a pinctrl-0 for setting the pins to SPI mode, and then a pinctrl-1 for setting the same pins to normal GPIOs.
Assigning pins for use by the SPI controller is proper, but there is no mechanism AFAIK for assigning a pin to be an available GPIO.
You can only define properties in the DT for what a device, such as the SPI controller, requires.
There is no mechanism for an "alternate" assignment.
I am sure there is a more correct way to do the things I stated above.
If your driver does not install, then its resources such as its pins should not be allocated. The pins that the SPI would have used will remain unused, and end up in the pool of available GPIOs (unless something else claims/requests one of those pins).
So your DT needs to simply define what your device uses, and leave it at that.
If you still want your driver to modify the state of its pin, then study the section titled "Pin control requests from drivers" in Documentation/pinctrl.txt.
I am learning about how to develop a Linux I2C kernel driver, and I learn from websites below.
How to instantiate I2C devices
I2C Driver for Linux Based Embedded System
...
Next, I found a sample that shows how to implement a I2C touchpad driver, but it really confused me.
linux/drivers/input/mouse/synaptics_i2c.c
My question is, how Linux kernel bind this driver to correctly device? This driver don't provide 'detect' callback, no specify the I2C slave address via i2c_driver.address_list, and there is seems no anyone to call i2c_board_info to register the address info (I grep a whole Linux codebase).
I thought the driver MUST be specify the slave address or provide the 'detect' callback, just like
drivers/hwmon/adc128d818.c
or
linux/drivers/rtc/rtc-ds1307.c (it will be register by i2c_board_info)
Please let me know what I missed, thanks.
The i2c device declaration is start from device tree.
Declare the i2c devices in device tree.
Example:
i2c1: i2c#400a0000 {
/* ... master properties skipped ... */
clock-frequency = <100000>;
flash#50 {
compatible = "atmel,24c256";
reg = <0x50>;
};
pca9532: gpio#60 {
compatible = "nxp,pca9532";
gpio-controller;
#gpio-cells = <2>;
reg = <0x60>;
};
};
Where,
1) 400a000 is a i2c bus address
2) pca9532 and flash are driver names
3) #50 and #60 are slave address
4) the attribute “compatible” to find and map device with the driver
5) other attribute which is inside cell of each entries are specific to the driver which will be used for device initialization during the probe
https://www.kernel.org/doc/Documentation/i2c/instantiating-devices
I finally figured out my question.
Refer to http://www.embedded-bits.co.uk/2009/i2c-in-the-2632-linux-kernel/
There is need to register my I2C device with i2c_new_probed_device() or i2c_new_device on Kernel to let it have a mapping table about slave address and device name.
I am trying to enable pwm under /sys/class/pwm/. What I am anticipating under this is something like pwmchip0/export etc. I have gone through the following things:
Checked that PWM_SYSFS is =y in the config.
The dtsi file has the right entries for the pwm.
The driver for pwm is being compiled properly.
But when I run build on the target I can see only /sys/class/pwm but nothing underneath.
I have not touched the dts file at all.
If anyone can point me towards the right direction or point out list of things to check for starter I would be grateful.
Please ask if I have missed anything to mention in the question.
This was actually simple. The reason the PWM entry was missing is because of the fact that the driver probe was not being called at all which means the kernel had no idea there exits a PWM driver. This I noticed from the kernel logs and extra printks in the driver code.
The reason the PWM driver probe was not being called by the kernel is that the device was not enabled in the dtsi file:
pwm: pwm#xxxxxxxx {
compatible = "aaa,bbb-pwm";
reg = <0xxxxxxxxx 0x200>;
status = "disabled";
};
To enable it I had to modify (add an entry for PWM) the .dts file:
&pwm {
status = "okay";
};
In my case the driver was already written, so I could see the PWM entry immediately after this point. If you are writing a new driver you might have to create all the necessary sysfs entries as required.
What are irq domains, i read kernel documentation (https://www.kernel.org/doc/Documentation/IRQ-domain.txt) they say:
The number of interrupt controllers registered as unique irqchips
show a rising tendency: for example subdrivers of different kinds
such as GPIO controllers avoid reimplementing identical callback
mechanisms as the IRQ core system by modeling their interrupt
handlers as irqchips, i.e. in effect cascading interrupt controllers.
How GPIO controller can be called as interrupt controller?
What are linux irq domains, why are they needed?
It's documented perfectly in the first paragraph of Documentation/IRQ-domain.txt, so I will assume that you already know it. If no -- please ask what is unclear regarding that documentation. The text below explains how to use IRQ domain API and how it works.
How GPIO controller can be called as interrupt controller?
Let me answer this question using max732x.c driver as a reference (driver code). It's a GPIO driver and it also acts like interrupt controller, so it should be a good example of how IRQ domain API works.
Physical level
To completely understand further explanation, let's first look into MAX732x mechanics. Application circuit from datasheet (simplified for our example):
When there is a change of voltage level on P0-P7 pins, MAX7325 will generate interrupt on INT pin. The driver (running on SoC) can read the status of P0-P7 pins via I2C (SCL/SDA pins) and generate separate interrupts for each of P0-P7 pins. This is why this driver acts as interrupt controller.
Consider next configuration:
"Some device" changes level on P4 pin, tempting MAX7325 to generate interrupt. Interrupt from MAX7325 is connected to GPIO4 IP-core (inside of SoC), and it uses line #29 of that GPIO4 module to notify CPU about interrupt. So we can say that MAX7325 is cascaded to GPIO4 controller. GPIO4 also acts as interrupt controller, and it's cascaded to GIC interrupt controller.
Device tree
Let's declare above configuration in device tree. We can use bindings from Documentation/devicetree/bindings/gpio/gpio-max732x.txt as reference:
expander: max7325#6d {
compatible = "maxim,max7325";
reg = <0x6d>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio4>;
interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
};
The meaning of properties is as follows:
interrupt-controller property defines that device generates interrupts; it will be needed further to use this node as interrupt-parent in "Some device" node.
#interrupt-cells defines format of interrupts property; in our case it's 2: 1 cell for line number and 1 cell for interrupt type
interrupt-parent and interrupts properties are describing interrupt line connection
Let's say we have driver for MAX7325 and driver for "Some device". Both are running in CPU, of course. In "Some device" driver we want to request interrupt for event when "Some device" changes level on P4 pin of MAX7325. Let's first declare this in device tree:
some_device: some_device#1c {
reg = <0x1c>;
interrupt-parent = <&expander>;
interrupts = <4 IRQ_TYPE_EDGE_RISING>;
};
Interrupt propagation
Now we can do something like this (in "Some device" driver):
devm_request_threaded_irq(core->dev, core->gpio_irq, NULL,
some_device_isr, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
dev_name(core->dev), core);
And some_device_isr() will be called each time when level on P4 pin of MAX7325 goes from low to high (rising edge). How it works? From left to the right, if you look to the picture above:
"Some device" changes level on P4 of MAX7325
MAX7325 changes level on its INT pin
GPIO4 module is configured to catch such a change, so it's generates interrupt to GIC
GIC notifies CPU
All those actions are happening on hardware level. Let's see what's happening on software level. It actually goes backwards (from right to the left on the picture):
CPU now is in interrupt context in GIC interrupt handler. From gic_handle_irq() it calls handle_domain_irq(), which in turn calls generic_handle_irq(). See Documentation/gpio/driver.txt for details. Now we are in SoC's GPIO controller IRQ handler.
SoC's GPIO driver also calls generic_handle_irq() to run handler, which is set for each particular pin. See for example how it's done in omap_gpio_irq_handler(). Now we are in MAX7325 IRQ handler.
MAX7325 IRQ handler (here) calls handle_nested_irq(), so that all IRQ handlers of devices connected to MAX7325 ("Some device" IRQ handler, in our case) will be called in max732x_irq_handler() thread
finally, IRQ handler of "Some device" driver is called
IRQ domain API
GIC driver, GPIO driver and MAX7325 driver -- they all are using IRQ domain API to represent those drivers as interrupt controllers. Let's take a look how it's done in MAX732x driver. It was added in this commit. It's easy to figure out how it works just by reading IRQ domain documentation and looking to this commit. The most interesting part of that commit is this line (in max732x_irq_handler()):
handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain, level));
irq_find_mapping() will find linux IRQ number by hardware IRQ number (using IRQ domain mapping function). Then handle_nested_irq() function will be called, which will run IRQ handler of "Some device" driver.
GPIOLIB_IRQCHIP
Since many GPIO drivers are using IRQ domain in the same way, it was decided to extract that code to GPIOLIB framework, more specifically to GPIOLIB_IRQCHIP. From Documentation/gpio/driver.txt:
To help out in handling the set-up and management of GPIO irqchips and the
associated irqdomain and resource allocation callbacks, the gpiolib has
some helpers that can be enabled by selecting the GPIOLIB_IRQCHIP Kconfig
symbol:
gpiochip_irqchip_add(): adds an irqchip to a gpiochip. It will pass
the struct gpio_chip* for the chip to all IRQ callbacks, so the callbacks
need to embed the gpio_chip in its state container and obtain a pointer
to the container using container_of().
(See Documentation/driver-model/design-patterns.txt)
gpiochip_set_chained_irqchip(): sets up a chained irq handler for a
gpio_chip from a parent IRQ and passes the struct gpio_chip* as handler
data. (Notice handler data, since the irqchip data is likely used by the
parent irqchip!) This is for the chained type of chip. This is also used
to set up a nested irqchip if NULL is passed as handler.
This commit converts IRQ domain API to GPIOLIB_IRQCHIP API in MAX732x driver.
Next questions
Further discussion is here:
part 2
part 3
Here's a comment I found in include/linux/irqdomain.h:
Interrupt controller "domain" data structure. This could be defined as
a irq domain controller. That is, it handles the mapping between
hardware and virtual interrupt numbers for a given interrupt domain.
the actual structure I think it's referring to there is irq_domain.
I'm going to launch a Linux on my development board, and i need a dts file (device tree file) to describe the whole hardware. But I only know very little about the syntax of this file which is not enough to run Linux properly on the board.
What i know now are only how to describe a unit's interrupt number, frequency, address, parent-unit and its compatible driver type (as described below):
ps7_scuwdt_0: ps7-scuwdt#f8f00620 {
compatible = "xlnx,ps7-scuwdt-1.00.a";
device_type = "watchdog";
interrupt-parent = <&ps7_scugic_0>;
interrupts = < 1 14 769 >;
reg = < 0xf8f00620 0xe0 >;
} ;
Other advanced usage or grammar is unfamiliar to me.
Take a look at the dts of the board which most closely resembles your dev-board. Use that as a reference and make changes to the dts according to the differences between the reference board and your dev-board.
Also checkout the following :
- Device-tree Documentation project at eLinux (has a vast collection of links to start reading).
- Series of articles on the basics of device tree.
- Walkthrough of migrating to device-tree.
Minimal reg + interrupt example with QEMU virtual device
Our example will add the following device tree node to the versatilepb device tree which QEMU will use due to -M versatilepb:
lkmc_platform_device#101e9000 {
compatible = "lkmc_platform_device";
reg = <0x101e9000 0x1000>;
interrupts = <18>;
interrupt-controller;
#interrupt-cells = <2>;
clocks = <&pclk>;
clock-names = "apb_pclk";
lkmc-asdf = <0x12345678>;
};
Then, by using a Linux kernel module to interact with the device, we will test the following DTS features:
registers addresses
IRQs
read custom properties from the driver
These are the main components of the example:
Linux versatile .dts patch on Linux fork
reg and interrupt match numbers hard-coded in the QEMU versatile machine (which represents the SoC)
compatible matches the platform_driver.name in the kernel module, and informs the kernel which module will handle this device
we also pass a custom property to the driver: lkmc-asdf = <0x12345678>;, which is read with of_property_read_u32
the device tree is passed to QEMU's firmware with the -dtb argument
QEMU fork:
device that reads a register and generates interrupts
insert device into -M versatilepb
kernel module Writes to memory on probe to test things out, which also generates an IRQ.
Device trees have many more features that we haven't covered, but this example should get you started, and easily allow you to play around with any new features that come up.
Further resources:
indispensable elinux tutorial: http://elinux.org/Device_Tree_Usage
play around with dtc for purely syntaxical questions. E.g., it shows how nodes are simply merged by path: https://unix.stackexchange.com/a/375923/32558
https://unix.stackexchange.com/questions/118683/what-is-a-device-tree-and-a-device-tree-blob
Lets take a example and I will explain each one of them as below
auart0: serial#8006a000 {
compatible = "fsl,imx28-auart", "fsl,imx23-auart";
reg = <0x8006a000 0x2000>;
interrupts = <112>;
dmas = <&dma_apbx 8>, <&dma_apbx 9>;
dma-names = "rx", "tx";
};
Required properties:
- compatible : Should be "fsl,-auart". The supported SoCs include
imx23 and imx28.
- reg : Address and length of the register set for the device
- interrupts : Should contain the auart interrupt numbers
- dmas: DMA specifier, consisting of a phandle to DMA controller node
and AUART DMA channel ID.
- dma-names: "rx" for RX channel, "tx" for TX channel.
Note: Each auart port should have an alias correctly numbered in "aliases"
node.
For more advance properties, please go to this link, it is very useful
Device Tree Explanation
Hope it helps!
Complementary to the other answers:
Keep in mind, that there is also a section for devicetrees in the official kernel source under root/Documentation/devicetree(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree?h=v5.2-rc5).