Linux PCI Driver calls init, but not probe - linux

I'm developing a driver for an FPGA-board connected to my machine via an PCIe expansion slot, and everything works great if the board is powered on prior to the PC. However, if I book up my computer first and then the FPGA board, I get the rather unusual behavior of the device being recognized and loading my module (I see the "init" function called in my syslog), however the "probe" function is never called.
I think this is due to an invalid BAR0. Output from dmesg when I power on the board:
[ 71.287587] pci 0000:3b:00.0: [0ae5:0001] type 00 class 0x000000
[ 71.287613] pci 0000:3b:00.0: reg 0x10: [mem 0x00000000-0x0000ffff]
[ 71.287821] pci 0000:3b:00.0: System wakeup disabled by ACPI
[ 71.328537] my_driver:
[ 71.328537] ****************************************************************
[ 71.328542] my_driver: init debug=2
That first reg should be something like 0xb4000000-0xb400ffff but instead it's coming up as 0. (Like I said, it works perfectly if it's powered on before the computer).
Is there an additional step required to get it to allocate this block? Or somehow to indicate to the kernel that it needs to do this?

The solution wound up being a manual call to pci_assign_resource ( http://lxr.free-electrons.com/source/drivers/pci/setup-res.c#L283 ).
Calling this right before pci_enable_device caused the OS, rather than the BIOS, to allocate the required BAR's and now it all works!
I do still have to manually trigger a PCI bus rescan ( echo 1 > /sys/bus/pci/rescan ).

Your PCI device must be powered up prior to the BIOS PCI enumeration phase.
On enumeration phase, the BIOS tries to read the ID of the PCI devices that might be connected.
If it reads invalid ID (0xfffff) it skips that PCI device.
I don't have a reference, but AFAIK, you have about a second before you must populate the configuration space of the PCI.

Are you sure you register the PCI driver and don't return non-zero from mod_init? Please try to manually bind the device to your driver:
echo -n "0000:3b:00.0" > /sys/bus/pci/drivers/my_driver/bind
Unallocated BAR should not be an issue when loading the driver.
As for the BAR being 0 and HotPlug: find out what is your platform and if and how HotPlug is supported. You need to have the right HotPlug driver in the kernel for this sort of thing to work. BARs are allocated by the kernel (or initially firmware/BIOS) so you can't set them to anything meaningful from the FPGA side - there you can only set the size. Kernel has to do the rescanning and reassignment after device appears. I vaguely recall that there should be some reservation going on during boot, otherwise kernel will not have to space to give to your devices' BAR and it will not reassign the windows on bridges below your device as they can be actively used by other devs. Other option is to just do the BAR programming yourself from the driver. It ain't that hard but you would probably don't want to ship this kind of hacks to customers. Also, even though your device does seem to come up fine, make sure you don't have HP disabled in FW/BIOS.

Related

how to find out which ioports be assigned to my devices

has linux reserved io port numbers for all manufactured devices.
I have devices like intel built-in network card. or another device I have for wifi (usb) from realtek.
On linux repository on github, device drivers use specific io ports to register. And kernel assign those ports to device driver. device drivers normally request for ports using call to request_region function. so for some ethernet device it requests like following
for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10)
{
if (!request_region(id_port, 1, "3c509-control"))
continue;
outb(0x00, id_port);
outb(0xff, id_port);
if (inb(id_port) & 0x01)
break;
else
release_region(id_port, 1);
}
above starts with 0x110 to 0x200, any port can be assigned in this range by kernel to driver and appear in /proc/ioports file means driver is using that io port by the time of success return from request_region.
Question : So my question is has linux assigned io ports to all manufactured devices usable with kernel 5.7 or latest kernel version?
Question : What if I want to write device driver for any device. How can I find the io ports number range to request to. I dont not expect that I have to look into kernel code and find similer driver port range. so How can I find that io port number range. how to achieve this first step required in writing device driver (any device. be it wifi internet device or ethernet device)
Question : So my question is has linux assigned io ports to all manufactured devices usable with kernel 5.7 or latest kernel version?
No.
Question : What if I want to write device driver for any device. How can I find the io ports number range to request to.
You ask the user for it. After all it's the user who set them using jumpers on the ISA card.
Here's a picture of an old Sound Blaster card (taken from Wikipedia, I'm too lazy to rummage around in my basement now). I've highlighted a specific area in the picture:
That jumper header I highlighted: That's the port configuration jumper. As a user you literally connect two of the pins with a jumper connector and that connects a specific address line that comes from the card connectors to the circuitry on the rest of the card. This address line is part of the AT bus port I/O scheme. The user sets this jumper, writes down the number and then tells the driver, which number it was set to. That's how AT style I/O ports.
Or the driver uses one of the well known port numbers for specific hardware (like network controllers) that dates back to the era, where ISA style ports were still are thing. Also there's old ISA-P'n'P where the BIOS and the add-in cards would negotiate the port assignments at power up, before the OS even started. You can read those port numbers with the ISA-P'n'P API provided by the kernel.
We no longer use this kind of hardware in practice! Except for legacy and retro computing purposes.
Over a quarter of century ago, the old AT / ISA bus was superseeded with PCI. Today we use PCIe which, from the point of view of software still looks like PCI. One of the important things about PCI was, that it completely dropped the whole concept of ports.
With ISA what you had were 8 data lines and 16 address lines, plus two read/write enable lines, one for memory mapped I/O and one for port I/O. You can find the details here https://archive.is/3jjZj. But what happens when you're reading from say, port 0x0104, it would physically set the bit pattern of 0x0104 to the address lines on the ISA bus, pull low the read enable line, and then read the voltage level on the data lines. And all of that is implemented as an actual set of instructions of the x86: https://c9x.me/x86/html/file_module_x86_id_139.html
Now look at the PCI bus: There's no longer separate data and address lines. Instead read/write commands would be sent, and everything happens through memory mappings. PCI devices have something called a BAR: a Base Address Register. This is configured by the PCI root complex and assigns the hardware the region of actual physical bus addresses where it appears. The OS has to get those BAR information from the PCI root complex. The driver uses the PCI IDs to have the hardware discovered and the BAR information told to it. It can then do memory reads/writes to talk to the hardware. No I/O ports involved. And that is just the lowest level. USB and Ethernet happen a lot further up. USB is quite abstract, as is Ethernet.
Your other question Looking for driver developer datasheet of Intel(R) Core(TM) i5-2450M CPU # 2.50GHz suggests, that you have some serious misconceptions of what is actually going on. You were asking about USB devices, and Ethernet ports. Neither of those in any way directly interact with this part of the computer.
Your question per se is interesting. But we're also running into a massive XYZ problem here; it's worse than an XY problem; you're asking about X, although you want to solve Y. But Y isn't even the problem you're dealing with in the first place.
You're obviously smart, and curious, and I applaud that. But I have to tell you, that you've to backtrack quite a bit, to clear up some of the misconceptions you have.

Linux: writing to the i2c/SMBus

I have a problem with the i2c/SMBus on a Linux System with an Intel Apollo Lake processor. I am trying to read/write from/to an EEPROM but I face some issues. My EEPROM is located at the address 0x56, and I am able to watch the Bus with my Logic Analyzer.
When I try to read from the device via i2ctools (i2cget) the System behaves as expected. My issue occurs when I try to perform a write command via i2cset for example. i2cset ends with an error (Write failed). Because I am able to watch the Bus electrically I can also say that all lines stay HIGH and the Bus is not touched. I was able to activate the dev_dbg() functions in the i2c driver i2c_i801 and when I perform i2cset I am able to find (dmesg) the debug message:
[ 765.095591] [2753] i2c_i801:i801_check_post:433: i801_smbus 0000:00:1f.1: No response
When running my minimal I²C Python code using the smbus2 lib, I get the following error message and the above mentioned debug message:
from smbus2 import SMBus
bus = SMBus(0)
b = bus.read_byte_data(86,10) #<- This is performed
b = bus.write_byte_data(86,10,12) #<- This is not performed
bus.close()
Error:
File "usr/local/lib/python3.8/dist-packagers/smbus2-0.4.0-py3.8.egg/smbus/smbus2.py", line 455, in write_byte_data
ioctl(self.fd, I2C_SMBUS, msg)
OSERROR: [Errno 6] No such device or adress
A big hint for me is that I am not able to perform a write command in the address space form 0x50 to 0x57. My guess is that some driver locks the address space to prevent write command to that "dangerous" area.
My question is: "Does anyone know this kind of behavior and is there a solution so that I can write to my EEPROM at address 0x56? OR Is there a lock surrounding the i2c adress space from 0x50 to 0x57 and who is my opponent?"
I am kind of a newbie to the whole driver and kernel world so please be kind and it is quite possible that I made a beginner mistake.
I would appreciate hints and tips I can look after surrounding my problem.
It seems that I found the cause of my problem. In this Forum post is described that Intel changed a configuration Bit at the SMBus controller.
OK, I know what's going on.
Starting with the 8-Series/C220 chipsets, Intel introduced a new
configuration bit for the SMBus controller in register HOSTC (PCI
D31:F3 Address Offset 40h):
Bit 4 SPD Write Disable - R/WO.
0 = SPD write enabled.
1 = SPD write disabled. Writes to SMBus addresses 50h - 57h are
disabled.
This badly documented change in the configuration explains the issues.
One Challenge is, that to apply and enable changes to the SPD-write Bit the System needs to be rebooted. Unfortunately while rebooting the BIOS will change the Bit back to the default. The only solution seems to be an adaption in the BIOS.
For me, this issue is resolved. I just wanted to share this information in case someone faces the same issues.

MCP23017 I2C Device driver probe function is not called

I am using the following I2C/GPIO Device driver to access the MCP23017 GPIOs. With the insmod command I am able to load the driver and its listed in /proc/modules. I have two MCP23017 chips connected to my Raspberry Pi. Both are detected at addresses 0x20 and 0x21. The initcall to the driver registers the driver. I checked this by printing out a message. But the driver probe function is not called. The devices are not opened/ cannot be located elsewhere.
How is the probe function called?
Should the probe be done manually to locate the devices?
Is the probe call similar to the open call?
I tried this echo mcp23017 0x20 > new_device to manually create a new device with the address. But it didnt work. I got the followin message: Driver 'mcp23s08' is already registered, aborting...
Any help would be appreciated.
probe() function is called when driver is matched with your device description in Device Tree. Matching happens when compatible field of your driver found in Device Tree (for your driver it's "microchip,mcp23017" string).
Apparently you don't have your device (MCP23017) described in Device Tree, that's why probe() is not called. You can load corresponding Device Tree Overlay to overcome this issue. The one you pointed out in your comment seems to be correct. Read more about loading overlays in Raspberry Pi ecosystem here.
You can try to load your overlay like described in that article:
$ sudo dtoverlay mcp23017.dtbo
Or you can try to use Capemgr for this purpose. Personally I didn't try any of those, so you should look which works for you best.
Update
Replying to your questions in comments.
But when I try the i2cdetect command it shows UU.
See man i2cdetect. So "UU" means that i2cdetect skipped probing because device at the address you specified is already used by driver. I guess it what you intended, so it's ok.
With a rmmod mcp23017 command I see the device still under devices but i2cdetect shows 0x20
So you unloaded the driver and now i2cdetect shows you that there is some device on 0x20 address. I guess it's correct behavior. Also if you want to get rid completely of your device -- try to unload DT overlay along with driver.
Also I have connected two MCP23017 chips. But I can see only the device at 0x20 under devices. The I2C chip at 0x21 is still not detected, though the driver says it supports up to 8 chips
I can see two possible causes to this issue.
DT overlay only has description for device with 0x20 address, but missing description for device with 0x21 address. If this is the case, you should find sources for your DT overlay, add description for rest of your devices, compile that modified DT overlay and then load it instead of pre-built one.
All devices may be configured for using 0x20 address. See section 1.4
Hardware Address Decoder in MCP23017 datasheet for details. Check A0, A1, A2 pins on your chips.

i2cdetect doesn't find anything on goodix chip

I have a goodix chip for the touchscreen on my tablet PC and even though I compiled the latest kernel module for it, things are not working.
I am using exactly this kernel version with the patched driver:
https://github.com/NimbleX/kernel
For starters, the picture of the said chip is the following:
The DSDT tables contain information regarding the touchscreen.
From what I understand the touchscreen is connected via an I2C serial interface but lshw shows that *-serial is UNCLAIMED.
Nevertheless I can see that the i2c_i801 module for the SMBus controller is loaded.
With the help of Aleksei I was able to determine that the toucscreen is connected to i2c-1 buss and that the controller must use 0x14 or 0x5d address.
Unfortunatelly, i2cdetect doesn't find anything, as it can be seen here.
I created a lengthy gist with the output of the following:
dmesg
DSDT.dsl
lshw
lspci
lsusb
/proc/bus/input/devices
xinput
I know that some of these are redundant and that others are useless but nevertheless it's better to have where to search than to miss something out.
I measured with a multimeter and the chip is powered both when running Windows and Linux so this rules out that I need to somehow tell Linux to power this thing out.
So, what do do next in order to debug this thing?
Hi can you check where pin 5,6 are connected specifically 6 which is reset ic so if that may be reseting the ic. just a posiblity.

Controlling a USB power supply (on/off) with Linux

Is it possible to turn on/off power supplies from USB manually with Linux?
There's this external USB cooling fan (the kind you use to cool yourself off, not the PC), and it would be nice to be able to control it from the terminal, because I want to position the fan somewhere far away.
I suppose this could also be useful for a variety of other things as well, because there's a lot of USB toys out there. Maybe air purifiers, etc. (I heard they don't really work though).
According to the docs, there were several changes to the USB power management from kernels 2.6.32, which seem to settle in 2.6.38. Now you'll need to wait for the device to become idle, which is governed by the particular device driver. The driver needs to support it, otherwise the device will never reach this state. Unluckily, now the user has no chance to force this. However, if you're lucky and your device can become idle, then to turn this off you need to:
echo "0" > "/sys/bus/usb/devices/usbX/power/autosuspend"
echo "auto" > "/sys/bus/usb/devices/usbX/power/level"
or, for kernels around 2.6.38 and above:
echo "0" > "/sys/bus/usb/devices/usbX/power/autosuspend_delay_ms"
echo "auto" > "/sys/bus/usb/devices/usbX/power/control"
This literally means, go suspend at the moment the device becomes idle.
So unless your fan is something "intelligent" that can be seen as a device and controlled by a driver, you probably won't have much luck on current kernels.
Note. The information in this answer is relevant for the older kernels (up to 2.6.32). See tlwhitec's answer for the information on the newer kernels.
# disable external wake-up; do this only once
echo disabled > /sys/bus/usb/devices/usb1/power/wakeup
echo on > /sys/bus/usb/devices/usb1/power/level # turn on
echo suspend > /sys/bus/usb/devices/usb1/power/level # turn off
(You may need to change usb1 to usb n)
Source: Documentation/usb/power-management.txt.gz
PowerTOP from Intel allows you to toggle devices such as usb peripherals in real-time. These are called 'tunables'.
sudo apt install powertop
sudo powertop
Tab over to 'tunables'.
Scroll down to your device.
Hit enter to toggle power saving mode (Good/Bad)
Note that Bad means the device is always on. Toggling to Good will turn off the device after the preset inactive saving time (default is 2000ms).
See the PowerTOP docs for details on how to make these changes permanent.It generates the config scripts for you (pretty much as described by other posters on this thread).
NOTE: These scripts do not affect USB pin power (which is always on).
These only send the driver protocol to activate and deactivate a device.
If you want to control pin power, you could use either a supported smart USB hub, or better yet a microcontroller.
I have found these solutions that at least work for properly configured Terminus FE 1.1 USB hub chip:
1.To turn off power on all USB ports of a hub, you may unbind the hub from kernel using:
echo "1-4.4.4" > /sys/bus/usb/drivers/usb/unbind
to turn power back on - you may bind it back using
echo "1-4.4.4" > /sys/bus/usb/drivers/usb/bind
2.Switching power at each port individually is trickier: I was able to use hubpower to control each port - but it comes with a downside: hubpower first disconnects the usbdevfs wich causes all of the USB devices to disconect from system, at least on ubuntu:
usb_ioctl.ioctl_code = USBDEVFS_DISCONNECT;
rc = ioctl(fd, USBDEVFS_IOCTL, &usb_ioctl);
With this ioctl disabled I was able to switch off individual port power without detaching all devices - but the power goes back on immediately (probably due to kernel seeing an uninitialized device) which causes USB device just to do a "cold restart" which is what I generally wanted to do. My patched hubpower is here
You could use my tool uhubctl to control USB power per port for compatible USB hubs.
I wanted to do this, and with my USB hardware I couldn't. I wrote a hacky way how to do it here:
http://pintant.cat/2012/05/12/power-off-usb-device/ .
In a short way: I used a USB relay to open/close the VCC of another USB cable...
echo '2-1' |sudo tee /sys/bus/usb/drivers/usb/unbind
works for ubuntu
The reason why folks post questions such as this is due to the dreaded- indeed "EVIL"- USB Auto-Suspend "feature".
Auto suspend winds-down the power to an "idle" USB device and unless the device's driver supports this feature correctly, the device can become uncontactable. So powering a USB port on/off is a symptom of the problem, not the problem in itself.
I'll show you how to GLOBALLY disable auto-suspend, negating the need to manually toggle the USB ports on & off:
Short Answer:
You do NOT need to edit "autosuspend_delay_ms" individually: USB autosuspend can be disabled globally and PERSISTENTLY using the following commands:
sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/&usbcore.autosuspend=-1 /' /etc/default/grub
update-grub
systemctl reboot
An Ubuntu 18.04 screen-grab follows at the end of the "Long Answer" illustrating how my results were achieved.
Long Answer:
It's true that the USB Power Management Kernel Documentation states autosuspend is to be deprecated and in in its' place "autosuspend_delay_ms" used to disable USB autosuspend:
"In 2.6.38 the "autosuspend" file will be deprecated
and replaced by the "autosuspend_delay_ms" file."
HOWEVER my testing reveals that setting usbcore.autosuspend=-1 in /etc/default/grub as below can be used as a GLOBAL toggle for USB autosuspend functionality- you do NOT need to edit individual "autosuspend_delay_ms" files.
The same document linked above states a value of "0" is ENABLED and a negative value is DISABLED:
power/autosuspend_delay_ms
<snip> 0 means to autosuspend
as soon as the device becomes idle, and negative
values mean never to autosuspend. You can write a
number to the file to change the autosuspend
idle-delay time.
In the annotated Ubuntu 18.04 screen-grab below illustrating how my results were achieved (and reproducible), please remark the default is "0" (enabled) in autosuspend_delay_ms.
Then note that after ONLY setting usbcore.autosuspend=-1 in Grub, these values are now negative (disabled) after reboot. This will save me the bother of editing individual values and can now script disabling USB autosuspend.
Hope this makes disabling USB autosuspend a little easier and more scriptable-
I had a problem when connecting my android phone, I couldn't charge my phone because the power switch on and then off ...
PowerTop let me find this setting and was useful to fix the issue ( auto value was causing issue):
echo 'on' | sudo tee /sys/bus/usb/devices/1-1/power/control
USB 5v power is always on (even when the computer is turned off, on some computers and on some ports.) You will probably need to program an Arduino with some sort of switch, and control it via Serial library from USB plugged in to the computer.
In other words, a combination of this switch tutorial and this tutorial on communicating via Serial libary to Arduino plugged in via USB.
So far I came to the conclusion that you cannot control the power of a USB port. The 5V USB is always provided, and it's up to the device to use it or not. You can check this with a 5V fan or light.
I've tried various methods (disconnect/reconnect/bind/unbind/reset signal). Best so far are bind/unbind as it forces a cold restart of the device (but no power cycle).
I came up with a solution to reset USB devices, ports and controllers in a python script, which supports all of the above methods.
You can find the script at my Github page
Usage:
usb_reset.py -d 8086:1001 --reset-hub
The script uses among others the following solution to reset USB hubs/controllers:
Unbindind a USB port / controller works best via:
echo "myhub" > "/sys/bus/usb/drivers/usb/unbind"
echo "myhub" > "/sys/bus/usb/drivers/usb/bind"
Where myhub is found in /sys/bus/usb/devices/*
Or litteral controllers:
echo "mycontroller" > "/sys/bus/pci/drivers/unbind"
echo "mycontroller" > "/sys/bus/pci/drivers/bind"
Where mycontroller is found in /sys/bus/pci/drivers/[uoex]hci_hcd/*:*

Resources