Embedded Linux device blocking RS485 bus during startup - embedded-linux

I'm having trouble with an industrial Linux computer I'm working with to achieve communication over an RS485 bus with multiple connected devices. What I've encountered is that the IO pins used by the RS485 USART driver are set to different levels at startup instead of going to the RS485 idle/tri-state. As a result, the other devices on the bus are blocked for more than 30 seconds while the device boots up, triggering all sorts of external problems. The course of events can be viewed in the attached image, where I've measured the output voltages with an oscilloscope during startup.
My guess is that the actual driver is not started until the voltage levels reach their tri-state levels (e.g. ~2.2V for this device). After that everything works as expected.
I've tried to find any config-files to set the default IO level of the pins at boot (thinking this may be set by the bootloader) to no avail.
Also, I've tried to apply a startup-script to run "early enough" to set DATA- high, but the device in question does not provide any interface to control these pins as regular GPIOs as far as I can tell.
Any help, tips or insights would be much appreciated!
EDIT: I am not an experienced Linux developer, so please highlight if I've left out any important details.
Some specs:
ARM920T rev 0 (v41) CPU
Proprietary distribution of Linux 2.6
Uses Busybox
Atmel USART drivers
Extract from boot log:
Linux version 2.6.28.10 (root#) (gcc version 4.1.2) #94 PREEMPT Tue Oct 29 10:22:19 CET 2013
CPU: ARM920T [41129200] revision 0 (ARMv4T), cr=c0003177
/...
.../
RS485 mode for port /dev/ttyS3 enabled
/...
... (I'm guessing the ~30 secs elapse here)
.../
atmel_usart.3: ttyS3 at MMIO 0xfffcc000 (irq = 9) is a ATMEL_SERIAL
atmel_serial.3: Putting the RS485 RTS pin down
/...
...
.../
Full boot log: https://drive.google.com/file/d/0B2XYl1mNCa8jNUZ5V0Nic1hkU0U/view
Similar issue:
Possibly a similar issue is discussed here: UART initialisation: Prevent UART to pull RTS high
But I'm not sure how to proceed with the suggested solution.

This is little more than wild speculation, but it might be worth adding a start-up script that echoes a NULL character to the device (e.g. /dev/ttyS1 or whatever) as early as possible during start-up. This might be enough to kick the driver into initialising the hardware.
You could also try to locate the driver in the Linux source to look at how it starts up.

Probably you have access to the source code, so you can investigate who and when mess with the that GPIO. Just grep the kernel source for the atmel gpio controller port addresses to figure out what happen. If you are lucky may be there will be kernel command line option that you can pass from the bootloader to set the line to what you need in advance.

this answer may work if you can find required things mentioned below
of your board!
Once I also had a same issue on PWM. There I found that my bootloader was responsible for the same, I changed in bootloader configuration and it started working fine.
Check your BSP provided by board vendor or third party (If you have the source), If your bootloader is U-boot you can find it inside U-boot-(source)/include/configs/(your-board).h there you can find configuration for RS484. As per your datasheet for the board you can check for other things which are muxed on the same pin and disable those if not required for boot time and enable RS485.
enabling/disabling can be done by changing the values 0, 1 or 2 as per your configuration and also you can simply disable anything just by commenting // out the line.

Related

How to tell linux retrain and scan PCIe bus?

We have an embedded board that has an iMX8M-Plus Processor and Linux v5.4.161. This board has one PCIe bus and that one is connected to an FPGA. When we power up the board, the FPGA is not yet configured, so it acts as if it was not on the PCIe bus.
Once the Linux is fully booted, we configure the FPGA and only after that it starts acting as a PCIe endpoint (device).
At this point, when I run lspci -> it returns nothing.
When I first execute echo "1" > /sys/bus/pci/rescan as suggested here and here and then lspci, I still get nothing.
But if I reboot the linux without reseting the FPGA, it starts being visible in the lspci list. Rebooting the linux is not an option for us. Somehow I need to tell the linux that whatever it's doing at the boot time, please do it again at runtime. But I couldn't find a solution for this so far.
According to the Texas Instrument support forum, they said if the PCIe link is not trained at the boot time, rescan command never works.
At the boot time, while linux loads a pci driver, it tries to establish a PCIe link, I can see that with an oscilloscope, PERST pin is asserted and PCIE_CLK generated for a while and then stops if it can not detect any device. But the rescan command never does that.
Also in the system there is no pcie device to executeecho 1 > $pcidevice/remove in order to make rescan functional. Or there is no device or bus to set power off and on back like echo 0 > /sys/bus/pci/slots/.../power
I also learned that there was a method in old linux times (v2.6) called adding a Fake PCIe Device which physically doesn't exist to solve this problem. For that I took the fakephp.c driver from an old linux repo and ported it to ours. After solving a couple of deprecated function problems, it is compiled for Linux Kernel v5.4. modprobe fakephp worked and driver loaded but somehow I didn't get this fake device in my device list. Here it is mentioned that the fakephp driver was removed from mainstream linux since PCI core has similar functionality, but he never mentioned how.
Short of the story is that, I am stuck here, I need my FPGA to be visible in the lspci list without restarting the linux.
I recommend configuring the FPGA in u-boot to get away from these kinds of problems. Connect up SPI pins to FPGA's config pins & run it in Slave configuration mode.

How does the ARM Linux kernel map console output to a hardware device on boot?

I'm currently struggling to determine how I can get an emulated environment via QEMU to correctly display output on the command line. I have an environment that displays perfectly well using the virt reference board, a cortex-a9CPU, and the 4.1 Linux kernel cross-compiled for ARM. However, if I swap out the 4.1 kernel for 2.6 or 3.1, suddenly I can no longer see console output.
While solving this issue is my main goal, I feel like I lack a critical understanding of how Linux and the hardware initially integrate before userspace configurations via boot scripts and whatnot have a chance to execute. I am aware of the device tree, and have a loose understanding of how it works. But the issue I ran into where a different kernel version broke console availability entirely confounds me. Can someone explain how Linux initially maps console output to a hardware device on the ARM architecture?
Thank you!
The answer depends quite a bit on which kernel version, what config options are set, what hardware, and also possibly on kernel command line arguments.
For modern kernels, the answer is that it looks in the device tree blob it is passed for descriptions of devices, some of which will be serial ports, and it initializes those. The kernel config or command line will specify which of those is to be used for the console. For earlier kernels, especially if you go all the way back to 2.6, use of device tree was less universal, and for some hardware the boot loader simply said "this is a versatile express board" (for instance) and the kernel had compiled-in data structures to tell it where the devices were for each board that it supported. As the transition to device tree progressed, boards were converted one by one, and sometimes a few devices at a time, so what exactly the situation was for any specific kernel version depends on which board you're using.
The other thing that I rather suspect you're running into is that if the kernel crashes early in bootup (ie before it finds the serial port at all) then it will never output anything. So if the kernel is just too early to support the "virt" board properly at all, or if your kernel config is missing something important, then the chances are good that it crashes in early boot without being able to print you a useful message. (Sometimes "earlycon" or "earlyprintk" kernel arguments can assist here, but not always.)

Sparkfun Edge bootloader problems

Finally the sparkfun board edge boards arrived today ;-)
Following this well written guide : https://codelabs.developers.google.com/codelabs/sparkfun-tensorflow/#3 i am stuck with the following NoResponseError when trying to flash the code on the Ambiq, with the uart_wired_update.pyscript, that comes with tensorflow examples
opprud$ python3 tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/uart_wired_update.py -b 115200 /dev/cu.usbserial-1430 -r 1 -f main_nonsecure_wire.bin -i 6
MOJ/Connecting with Corvette over serial port /dev/cu.usbserial-1430...
Sending Hello.
No response for command 0x00000000
Traceback (most recent call last):
File "tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/uart_wired_update.py", line 336, in <module>
main()
File "tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/uart_wired_update.py", line 38, in main
connect_device(ser)
File "tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/uart_wired_update.py", line 58, in connect_device
response = send_command(hello, 88, ser)
File "tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/apollo3_scripts/uart_wired_update.py", line 235, in send_command
raise NoResponseError
__main__.NoResponseError
My setup:
Macbook pro, tried both old 15" & new 13"
Sparkfun serial basic breakout, USBC version (default jumped to 3v3)
FTDI 3v3 serial cable
I have tried
two different edge boards, with the correct Key14 & reset combo + misc variants and timing
legacy USB on old Macbook
new Macbook w USB C
FTDI 3v3 serial cable as alternative to sparkfun serial board
Running an alternative uart_boot_host.py script in tensorflow/lite/experimental/micro/tools/make/downloads/AmbiqSuite-Rel2.0.0/tools/bootloader_scripts/uart_boot_host.py, also no response
I can measure, with a scope, the handshake bytes '0x14', '0x55', '0x9d', '0xe9' '0x0', '0x0', '0x8', '0x0' being transmitted initially at 115200 on the TXO pin on the programming header - but the ambiq is not replying anything.
btw. The onboard demo is running, blue led flashing, an some "yes's" are being recognized.
Any inputs welcome.
Does anyone know the protocol for the corvette bootloader ?
Are there any CPU revision changes from the first batch of boards, or possibly any lock bits programmed accidentally from sparkfun ?
rgds from an eager TF lite user ;-)
I tried measuring the actual baudrate with a scope on rx/tx pins, and saw that the bit timing using default OSX serial driver is rather imprecise, app 10% off, causing faulty readings, and ultimately missing bytes, when the baudrate are high.
After updating to the ch340 serial driver, timing improved, and the bit timings were correct.
At 921600bps, a single byte 8N1 is supposed to be10.9uS
Driver install
https://github.com/adrianmihalko/ch340g-ch34g-ch34x-mac-os-x-driver
This is what worked for me: (source: github.com/sparkfun/SparkFun_Edge_BSP/issues/3, the SparkFunEdge tutorial and my teammates!). I am running this on a Linux machine (x86_64; Run $ uname -a) and my SparkfunEdge DEVICENAME=/dev/ttyUSB0
The tutorial does warn you about this problem at Step 4:
Note: Some users have reported issues with their operating system's
default drivers for programmer, so we recommend installing the
driver before you continue.
Click on the driver link and follow the instructions under "Other Linux distributions" as follows:
Install the correct version of the ch34 library.
$ git clone https://github.com/juliagoda/CH341SER.git
$ cd CH341SER/
$ make
$ sudo insmod ch34x.ko
$ sudo rmmod ch341
To verify that the correct driver is being used, run:
$ dmesg
..
[889247.585301] usb 1-7: ch341-uart converter now attached to ttyUSB0
[955698.718839] usbcore: registered new interface driver ch34x
[955698.718848] usbserial: USB Serial support registered for ch34x
[955759.196437] usbserial: USB Serial deregistering driver ch341-uart
[955759.196576] ch341-uart ttyUSB0: ch341-uart converter now disconnected from ttyUSB0
[955759.196601] usbcore: deregistering interface driver ch341
[955759.196643] ch341 1-7:1.0: device disconnected
Now unplug the USB-C from the SparkfunEdge Board, and plug it back again
$ dmesg
....
[955876.176950] ch34x 1-7:1.0: ch34x converter detected
[955876.177320] usb 1-7: ch34x converter now attached to ttyUSB0
glad to hear that you're so excited about the board. I have a hunch that this will be an easy fix.
The Edge boards handed out at the conference have a bootloader set for 115200 baud, however the Edge boards that have come out in the second batch are upgraded to flash at 921600 baud, greatly reducing flashing time. Try changing the baud rate in your serial upload script.
You can also set up the Ambiq Software Development Kit to write your own applications for the Apollo3 microcontroller. Check out the tutorial here: Using the Edge Board with Ambiq SDK
Since I can't comment on your post (not enough reputation.... thanks SE) I'll be responding here.
If the baud rate accuracy is a problem I'm slightly unsure that that would be caused by the OS, but rather I'd think it is a problem with the USB-serial converter chip. I've been using the CH340G whereas on the USB-C version there is the CH340C IC. The difference between the two is that the "C" version includes an internal oscillator to provide the frequency reference. It is possible that that one is less accurate...? I'll try it out over here (but on windows) and let you know.
If this is a persisting problem would you mind making a post on the SparkFun forums? That way our tech support can get linked in (they are the people who could get you replacement hardware in case it is defective, also). Here's a forum for the Edge: SparkFun Edge Forums
If the problem is coming from the OS then the only fix that we can do with the Edge is to reduce the bootloader speed. We're working on a short tutorial about how to do that, but it would require having a programmer/debugger for Cortex-M processors. The Ambiq Apollo3 Evaluation Board has a built-in SEGGER J-Link which is what we used to program the boards.
On MacOS Mojave, Installing/Reinstalling CH340 works for me:
https://learn.sparkfun.com/tutorials/how-to-install-ch340-drivers#mac-osx
Before doing install, check that you actually see the sparkfun edge device with:
ls -l /dev/cu*
If driver is correctly installed, you should detect:
/dev/cu.wchusbserial1420
I got similar issue whereas I only had /dev/cu.usbserial-1420 and thought that was expected device to access, whereas it is /dev/cu.wchusbserial1420 which was only detected after installed ch340 driver.
Then flashing device works successfully for me.

Detect spi device from another driver

I have a Freescale imx.6q (arm) based board.
Hardware is configured with devicetree.
It had a change major incompatible change to timings and voltage for an onboard fpga, but these changes are invisible to the kernel.
The EE's tell us we shouldn't load the old fpga firmware for fear of damaging it. I would like to support both hardware from the same code (It is already causing confusion)
The solution I have thought of is this:
There are several new spi temperature sensors on the board. If I can read from one of those devices, I can infer that I need the new firmware.
How can I (in one driver) grab an spi device and then release it?
I suspect that I might be able to do something like this with device tree,
But I don't want to make the device unavailable.
Any ideas or examples of something like this being done?
After reading question i think your concern is how to add software support for more than one hardware.
If that is the case i think we can write two drivers supporting both hardware's with different configuration such as irq, voltage, register set etc.
So i will enable both drivers in Makefile and config file.
So at the time of boot when probe of drivers gets called we can check the hardware id by using spi_read command from driver.
If hardware id matches then driver probe gets successful and driver can be used to interact with hardware.
If spi_read fails then driver probe itself will fail.
I think this will do the trick.
EDIT (answer the question)
To detect use an SPI device from another driver use a reference to the device in the devicetree structure.
Short answer: add a reference to the spi device in your devices dts entry.
Slightly longer answer:
When adding spi to another device driver, you are effectively adding a subdevice, which may want its own driver. I have an FPGA which loads its firmware over (something close enough to be considered) SPI. I started with the idea of just treating the spi device as part of the larger driver, but the more work went into it, the more obvious it was that it is its own device, with a purpose and function that is distinct from the rest of the driver. I separated that code into its own driver.
Now instead of a reference to an SPI device, my driver just has a reference to an FPGA Manager device.
See line 98, 370 of https://github.com/d4ddi0/linux/blob/v4.12evi/arch/arm/boot/dts/imx6q-evi.dts
and
make sure the spi driver is loaded before your driver completes loading
My original answer to my question (for historical purposes):
What I ended up doing was using different devicetree files. The difference is know at initial install time (based on the serial number). The bootloader knows which dts filename to load.
There are multiple FPGA firmware versions and the right one is chosen based on the description in the dts.
This way, I can still update the driver and/or dts without breakage.
This works well in practice even though it does not detect anything at runtime.
One problem still exists, if I take an SD card from a new revision, and put it into an old one, the incorrect firmware will be loaded. To really solve this last problem, we've talked about adding an EEPROM to uniquely identify the hardware revision on future boards.

unable to export a gpio pin

I am trying to export a pin (no. 110) using the following in linux:
echo 110 > /sys/class/gpio/export
When I try to do so, I getthe error message
ash: write error: Device or resource busy
As per my knowledge the pin is not being used by any module, but I may be wrong. I had the hardware line probed and the voltage is changing, the processor seems to be driving something to the line.
The pin according to the user manual is not multiplexed and is "commonly available".
The pin according to the user manual is not multiplexed and is "commonly available".
Apparently you are referring to a SoC manual.
Such a statement will only imply that the pin does not have a dedicated application by an integrated peripheral (as shipped by the SoC manufacturer).
That statement is invalidated when the SoC is designed into a circuit and/or installed on a board.
The document that you really need to consult is the board manual or the board schematics.
That should be the accurate documentation as to how the board designer used the available GPIO pins in that specific application.
When I try to do so, I getthe error message ...
Fortunately for you, the device driver that does use that pin (that you want to use) has properly performed the GPIO reserve/request call to prevent a hijack.
This prevented that other driver from breaking and/or a device/board malfunction.
As per my knowledge the pin is not being used by any module, but I may be wrong
How did you attain this "knowledge"?
Did you scan the .dts and .dtsi files used for your board?
Did you check the source code of every device driver used by your board?

Resources