How do Linux GPIO numbers get their values? - linux-kernel

I am trying to understand how the Linux GPIO numbers get their values.
e.g. GPIO mapping for Joule.
I tried reading linux documentation on Pinctrl Subsystem and also looked at the code of GPIO driver being used in Intel Joule :
https://elixir.bootlin.com/linux/latest/source/drivers/pinctrl/intel/pinctrl-broxton.c
However going this way looks very platform-specific. I am looking for some generic industry standard. Please help or please direct me to some good article.

First of all, one has to get the difference between Global System GPIO number (GSGN) and relative to the certain GPIO controller. Earlier, before the era of GPIO descriptors, the GSGN had been used. After switching to the descriptor scheme the numbering scheme moves from (semi-)static GSGN to dynamic one and thus makes nonsense to the user. Instead the label of the pin, if any, or a pair of GPIO controller handle with a number relative to it became in use. This is being dictated by the resource providers, such as ACPI and Device Tree. If, by some reason, user wants to get the pair of the controller and relative number, the libgpiod library and tools gives the possibility to achieve this.
So, the link to Joule GPIO numbering scheme is really fragile, users are not suppose to know the GSGN. There are ways how to get the controller and relative number on a running system. But usually it's all related to the driver and ACPI tables and doesn't require any user involvement.
Example:
Take into consideration the pin UART_1_TXD (by some reason it's named in that document wrongly, should be LPSS_UART1_TXD). According to pinctrl-broxton.c this is pin 43 on a GPIO controller with ACPI _HID INT34D1 and _UID 1.
List all enumerated GPIO controllers (optional step):
# gpiodetect
gpiochip0 [INT34D1:00] (83 lines)
gpiochip1 [INT34D1:01] (72 lines)
gpiochip2 [INT34D1:02] (42 lines)
gpiochip3 [INT34D1:03] (31 lines)
gpiochip4 [INT34D1:04] (20 lines)
Find one with _UID 1:
# grep -w 1 /sys/bus/acpi/devices/INT34D1\:0*/uid
/sys/bus/acpi/devices/INT34D1:00/uid:1
# gpiodetect | grep -w INT34D1:00
gpiochip0 [INT34D1:00] (83 lines)
So, the interesting pair is: gpiochip0 43.
In the actual resource provider it will look like this (taken from meta-acpi project):
...
* pin name pin number led
* -----------------------------------------
* ISH_GPIO_0_LS 35 heartbeat
* ISH_GPIO_1_LS 33 sd-card
* ISH_GPIO_2_LS 31 wifi
* ISH_GPIO_3_LS 29 led-3
...
GpioIo (
...
"\\_SB.GPO2", // GPIO controller
0) // Must be 0
{
22, // ISH_GPIO_0_LS
23, // ISH_GPIO_1_LS
24, // ISH_GPIO_2_LS
25 // ISH_GPIO_3_LS
}
...
Here you see the reference to the Device object thru a full path, i.e. \_SB.GPO2.
You may find more examples in meta-acpi project.
If by any weird case user really wants a non-sense number, this is the way:
# mount -t debugfs none /sys/kernel/debug/
# cat /sys/kernel/debug/pinctrl/INT34D1\:00/gpio-ranges
GPIO ranges handled:
0: INT34D1:00 GPIOS [429 - 460] PINS [0 - 31]
32: INT34D1:00 GPIOS [461 - 492] PINS [32 - 63]
64: INT34D1:00 GPIOS [493 - 511] PINS [64 - 82]
# echo $((43-32+461))
472
More details about GPIO library and subsystem can be found in GPIO in-kernel documentation.

Related

How to define a relay in a device tree

I have a board, SoC running Linux 5+, with a electrical relay. The relay is triggered by a GPIO. I am looking for a good way to define a relay in a device tree file.
I define LEDs as
led {
compatible = "gpio-leds";
debug {
label = "debug";
gpios = ...
default-state = "off";
};
};
This results in
# ls /sys/class/leds/
debug
I would like to have the relay be something similar such as
# ls /sys/class/{relays,outputs,gpios}/
relay1
What is a good way to achieve this?
Since relay behaves as simply as GPIO output (or more precisely GPO), what you need is just name the corresponding line. It can be done by assigning gpio-line-names property of the GPIO controller in the ACPI or Device Tree. With a use of libgpiod tools (such as gpiofind, gpioinfo), that access GPIO controller via character device node, you may find your line and do the operations on it. Note, GPIO sysfs interface is deprecated and it will be removed from the kernel on a horizon of ~5 years or so.

Esp32 Micropython Max31865 Spi connection and data read

I need to read temperature data with using MAX31865 SPI communication. First of all, I tried to read 4 byte data:
import machine
import ubinascii
spi = machine.SPI(1, baudrate=5000000, polarity=0, phase=0)
#baudrate controls the speed of the clock line in hertz.
#polarity controls the polarity of the clock line, i.e. if it's idle at a low or high level.
#phase controls the phase of the clock line, i.e. when data is read and written during a clock cycle
cs = machine.Pin(15, machine.Pin.OUT)
cs.off()
cs.on()
data = spi.read(4)
cs.off()
print(ubinascii.hexlify(data))
I tried many times with different codes but result is always similar b'00000000'.
I am using ESP32 WROOM.
I used this pins:
ESP32 : D12 - D14 - 3V3 - GND - D15
Max31865: SDO - CLK - VIN - GND - CS
I am new on micropython and esp32.
I don't know what should I do. Is there any suggestions , recommended tutorials or idea?
Short answer: see if you can use CircuitPython and its drivers for MAX31865.
Long answer: a bunch of stuff. I suspect you've been following the Adafruit tutorial for MAX31855, but its SPI interface is very different from the MAX31865.
Your SPI connection is missing the SDI pin. You have to connect it, as communication is bidirectional. Also, I suggest using the default SPI pinout on ESP32 side as described in the micropython documetation for ESP32.
The SPI startup looks to be missing stuff. Looking at the SPI documentation a call to machine.SPI() requires that you assign values to arguments sck, mosi, miso. Those would probably be the pins on ESP32 side where you've connected SCLK, SDI, SDO on MAX31865 (note mosi means "master out, slave in" and miso is "master in, slave out").
The chip select signal on the MAX is inverted (that's what the line above CS input in the datasheet means). You have to set it low to activate the chip and high to disable it.
You can't just read data out of the chip, it has a protocol you must follow. First you have to request a temperature-to-resistance conversion from the chip. The datasheet for your chip explains how to do that, the relevant info starts on page 13 (it's a bit difficult to read for a beginner, but try anyway as it's the authoritative source of information for this chip). On a high level, it works like this:
Write to Configuration register a value which initiates the conversion.
Wait for the conversion to complete.
Read from the RTD (Resistance-To-Digital) registers to get the conversion result.
Calculate the temperature value from the conversion result.
The code might be closer to this (not tested, and very likely to not work off the bat - but it should convey the idea):
import ubinascii, time
from machine import Pin, SPI
cs = Pin(15, Pin.OUT)
# Assuming you've rewired according to default SPI pinout
spi = machine.SPI(1, baudrate=100000, polarity=0, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
# Enable chip
cs.off()
# Prime a 1-shot read by writing 0x40 to Configration register 0x00
spi.write(b'\x00\x40')
# Wait for conversion to complete (up to 66 ms)
time.sleep_ms(100)
# Select the RTD MSBs register (0x01) and read 1 byte from it
spi.write(b'\x01')
msb = spi.read(1)
# Select the RTD LSBs register (0x02) and read 1 byte from it
spi.write(b'\x02')
lsb = spi.read(1)
# Disable chip
cs.on()
# Join the 2 bytes
result = msb * 256 + lsb
print(ubinascii.hexlify(result))
Convert result to temperature according to section "Converting RTD Data Register
Values to Temperature" in datasheet.
Side note 1: here spi = machine.SPI(1, baudrate=5000000, polarity=0, phase=0) you've configured a baud rate of 5MHz which is the maximum for this chip. Depending on how you've connected your devices, it may not work. The SPI protocol is synchronous and driven by master device, so you can set any baud rate you want. Start with a much, much lower value, maybe 100KHz or so. Increase this after you've figured out how to talk to the chip.
Side note 2: if you want your conversion result faster than the 100ms sleep in my code, connect the DRDY line from MAX to ESP32 and wait for it to go low. This means the conversion is finished and you can read out the result immediately.

How to change the amperage output on the gpio pins to power a relay

I have been unable to find any documentation regarding using python to change the GPIO outputs, and was wondering whether there was any specific code i could use to change it or if there were any third party programs that allowed me to do so.
You cannot change the current of GPIO pin, that's not how it works.
If you want to know the maximum value of current for GPIO pins, this article says it's 16mA per pin, with maximum of 51mA for all pins.

Are devices on pci bus always probed in the same order?

I have 2 wireless pci cards (same model, but can work under 2 different modes) on the bus. They share the same driver. What I want to do is to hack the driver like this: check the pci index, the first probed device (index 0) will be configured as mode A, the next one (index 1) will be configured as mode B.
so I want to know if they are probed in the same order every time the system init.
If the probe order is random, is there any other way can do that?
I tried this: plug the same card into difference slots, and check what I got under /sys/devices/pci0000:00. the result are all the same.
so the kernel know nothing about the physical slot at all? I was thinking maybe the kernel know which physical slot the card was mounted on. pity..
To answer: No, they are not in general.
To what you are trying to resolve: No need to do that since it's fixed in modern kernels Linux systems, The name of network interface is linked to the physical slot of the device. You will always have same names until you physically move the cards.
Correction. Initially I thought that this is provided by kernel. No, it's provided by user space helper, i.e. udev.
Names incorporating Firmware/BIOS provided index numbers for on-board devices (example: eno1)
Names incorporating Firmware/BIOS provided PCI Express hotplug slot index numbers (example: ens1)
Names incorporating physical/geographical location of the connector of the hardware (example: enp2s0)
Names incorporating the interfaces's MAC address (example: enx78e7d1ea46da)
Classic, unpredictable kernel-native ethX naming (example: eth0)
Origin: https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/
Example:
% ip link list dev enp0s20u2c2
42: enp0s20u2c2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 4a:06:8b:65:72:36 brd ff:ff:ff:ff:ff:ff
% ls -l /sys/class/net/enp0s20u2c2
lrwxrwxrwx 1 root root 0 Dec 23 14:59 /sys/class/net/enp0s20u2c2 -> ../../devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:2.0/net/enp0s20u2c2

Failed to request_irq for kernel module

I am trying to port drivers from old kernel to new one on ARM based platform. While porting one of the drivers I have noticed that request_irq fails on new kernel. Actually, what this driver have is a number of hard coded irq numbers, and it tries to request_irq for this HW lines. I started to search what is the reason of request_irq failure, - the reason is that the appropriate IRQ descriptor (irq_desc) have IRQ_NOREQUEST flag set.
I started to search where this flag is cleared, and found that it happens here:
of_platform_populate
|
...
|
of_device_alloc
|
...
|
irq_of_parse_and_map
(some levels below this flag is dropped)
So that code is invoked from mach init code and parse DTB, all interrupt numbers that are mentioned in DTB would be mapped to irq virtual space and will be accessible through appropriate devices structures, for example:
irq = platform_get_irq(pdev, 0);
As I already said, this old - fashion drivers just have hard-coded irq numbers in include files, and they don't have no appropriate dtb entries.
The question is what is the right way, from the kernel perspective of view, to port this? Do I need to make this old driver a platform device (now it is just a char device) and create appropriate dtb description for it? Or I can just use some kernel API to mark this interrupts as available? Is it a common/normal style?

Resources