<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashwin</title>
    <description>The latest articles on DEV Community by Ashwin (@pymaniac).</description>
    <link>https://dev.to/pymaniac</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4022017%2Fde77d8b1-c57a-4685-be69-f74a31063679.png</url>
      <title>DEV Community: Ashwin</title>
      <link>https://dev.to/pymaniac</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pymaniac"/>
    <language>en</language>
    <item>
      <title>How to debug why your PCIe device doesn't enumerate during bring up</title>
      <dc:creator>Ashwin</dc:creator>
      <pubDate>Sat, 11 Jul 2026 00:07:29 +0000</pubDate>
      <link>https://dev.to/pymaniac/how-to-debug-why-your-pcie-device-doesnt-enumerate-during-bring-up-cd4</link>
      <guid>https://dev.to/pymaniac/how-to-debug-why-your-pcie-device-doesnt-enumerate-during-bring-up-cd4</guid>
      <description>&lt;p&gt;Notes from bringing up a PCIe WiFi module on i.MX8MQ; symptoms and how to diagnose them.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Phy link never came up&lt;/code&gt; — what does this mean?
&lt;/h2&gt;

&lt;p&gt;This message is typically seen in &lt;code&gt;dmesg&lt;/code&gt; as shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[    3.828121] imx6q-pcie 33800000.pcie: iATU: unroll T, 4 ob, 4 ib, align 64K, limit 4G
[    4.807241] imx6q-pcie 33c00000.pcie: Phy link never came up
[    4.841482] imx6q-pcie 33800000.pcie: Phy link never came up
[    5.821279] imx6q-pcie 33c00000.pcie: Phy link never came up
[    5.830481] imx6q-pcie 33c00000.pcie: PCI host bridge to bus 0001:00
[    5.854997] imx6q-pcie 33800000.pcie: Phy link never came up
[    5.862205] imx6q-pcie 33800000.pcie: PCI host bridge to bus 0000:00
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It means one of the following&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The PCIe peripheral is not powered up.&lt;/li&gt;
&lt;li&gt;PCIe reset is not deasserted, so the chip is in reset. This could be because the DTB is deasserting an incorrect GPIO.&lt;/li&gt;
&lt;li&gt;Reference clock is not enabled&lt;/li&gt;
&lt;li&gt;Using the incorrect PCIe controller in the device tree.&lt;/li&gt;
&lt;li&gt;As we can see that both the PCIe controllers can report this. So first determine which controller is the peripheral hooked to. More on this in the next section.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Which PCIe controller is my device on? (&lt;code&gt;&amp;amp;pcie0&lt;/code&gt; vs &lt;code&gt;&amp;amp;pcie1&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The rule here is to match by address and not by label/name. If the schematic calls out controllers as PCIE1 and PCIE2, and the device tree lists pcie0 and pcie1, understand the mapping. The DTS label is arbitrary - match by register base (&lt;code&gt;@address&lt;/code&gt; in the node name), which is the same in the DTS reg and the reference memory map. For definitive addresses look in the &lt;code&gt;.dtsi&lt;/code&gt;, as sometimes the manuals are misleading. &lt;/p&gt;

&lt;p&gt;Given below is a mapping table for i.MX8MQ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DTS.     |  ADDRESS (in .dtsi)  | Silicon (RM) Label
&amp;amp;pcie0   | 0x33800000          |     PCIe1 
&amp;amp;pcie1   | 0x33c00000          |     PCIe2 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The addresses are listed in the chip’s memory layout are from processor reference manual. Below is snapshot from the i.MX8MQ reference manual, where the layout for the core A-53 is listed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Start Address | End Address |  Size   | Description
3381_0000     | 3381_3FFF   |  4MB    | PCIe-2   &amp;lt;&amp;lt; incorrect in the RM
3380_0000     | 3380_3FFF   |  4MB    | PCIe-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see from above there are two discrepancies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The size is listed at 4MB (0x400000), but the end address points to a 16K address space. &lt;/li&gt;
&lt;li&gt;PCIe-2's address region lies within PCIe-1, which is incorrect. 
Lesson: When in doubt, look at the definitions in &lt;code&gt;.dtsi&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pcie0: pcie@33800000 {
pcie1: pcie@33c00000 {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we know from the schematic, the peripheral is on PCIe-1, which maps to &lt;code&gt;&amp;amp;pcie0&lt;/code&gt; in the DTB. So focus on &amp;amp;pcie0. As a next step validate that the &lt;code&gt;reset-gpio&lt;/code&gt; of the selected peripheral matches to whats is specified in the device tree.  If it does not match, change the GPIO specified in the device tree to match.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;lspci&lt;/code&gt; shows my device but a &lt;code&gt;Kernel driver in use&lt;/code&gt; is not seen in the output
&lt;/h2&gt;

&lt;p&gt;This could be the next possible step, where &lt;code&gt;lspci&lt;/code&gt; reports the device is enumerated but the bars are disabled. If the bars are enabled -&amp;gt; &lt;strong&gt;Hurrah!! You are done&lt;/strong&gt;. The remainder of this section is for instances where the output of &lt;code&gt;lspci&lt;/code&gt; looks as so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;0000:01:00.0 Network controller: Broadcom Inc. and subsidiaries BCM4356 802.11ac Wireless Network Adapter (rev 02)
        Subsystem: Broadcom Inc. and subsidiaries BCM4356 802.11ac Wireless Network Adapter
        Flags: fast devsel, IRQ 222
        Memory at 18400000 (64-bit, non-prefetchable) [disabled] [size=32K] &amp;lt;&amp;lt; BAR not setup
        Memory at 18000000 (64-bit, non-prefetchable) [disabled] [size=4M] &amp;lt;&amp;lt; BAR not setup
        Capabilities: [48] Power Management version 3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and no &lt;code&gt;Kernel driver in use&lt;/code&gt; shows up in the output. This means that the driver has not claimed the device and setup the BARs. In summary, the PCIe core enumerated the device but driver is not binding.&lt;/p&gt;

&lt;p&gt;For such cases, do the following steps. Note that the steps are written for a WiFi PCIe card on the i.MX8MQ eval kit; meant to serve as an example. Adapt it for your specific bring up.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check what the bus, the driver is built for
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zcat /proc/config.gz | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; brcmfmac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check if the driver is built for the wrong bus, example SDIO enabled but not PCIE
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;CONFIG_BRCMFMAC&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;m&lt;/span&gt;
&lt;span class="py"&gt;CONFIG_BRCMFMAC_SDIO&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;y   &amp;lt;&amp;lt; Will not work for PCIe&lt;/span&gt;
&lt;span class="c"&gt;# CONFIG_BRCMFMAC_PCIE is not set   &amp;lt;&amp;lt; Should be there for PCIe
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, the driver exists but ignores the PCIe device. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable PCIE with &lt;code&gt;CONFIG_BRCMFMAC_PCIE=y&lt;/code&gt;, optionally remove SDIO, rebuild, and load on the device. It might be a good idea to check your new kernel config has the option set, before loading it on device.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;CONFIG_BRCMFMAC&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;m&lt;/span&gt;
&lt;span class="py"&gt;CONFIG_BRCMFMAC_SDIO&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;
&lt;span class="py"&gt;CONFIG_BRCMFMAC_PCIE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;y   &amp;lt;&amp;lt; THIS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once loaded on the device, ensure that the driver is loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;lsmod | grep -i brcmfmac
brcmfmac_wcc           12288  0
brcmfmac              233472  1 brcmfmac_wcc
brcmutil               16384  1 brcmfmac
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need to determine if the driver firmware is available to load. Check &lt;code&gt;/usr/lib/firmware&lt;/code&gt; to see if it’s available.&lt;br&gt;
In case you are wondering where did the BAR addresses come from, these come from the PCIe memory regions defined in the chip address map. For example for the Cortex A-53:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Start Address  | End Address | Region  | Size   | Description
2000_0000      | 27FF_FFFF   | PCIe-2  | 128MB  | PCIe-2
1800_0000      | 1FFF_FFFF   | PCIe-1  | 128MB  | PCIe-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we can see that the chip lies in the PCIe-1 region of the silicon -&amp;gt; &lt;code&gt;&amp;amp;pcie0&lt;/code&gt; of device tree, as we verified in the previous section.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;lspci -v&lt;/code&gt; for a final confirmation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lspci -v
snip … snip
0000:01:00.0 Network controller: Broadcom Inc. and subsidiaries BCM4356 802.11ac Wireless Network Adapter (rev 02)
        Subsystem: Broadcom Inc. and subsidiaries BCM4356 802.11ac Wireless Network Adapter
        Flags: bus master, fast devsel, latency 0, IRQ 234
        Memory at 18400000 (64-bit, non-prefetchable) [size=32K] &amp;lt;&amp;lt; BAR is setup
        Memory at 18000000 (64-bit, non-prefetchable) [size=4M] &amp;lt;&amp;lt; BAR is setup
        Capabilities: [48] Power Management version 3
        Capabilities: [58] MSI: Enable+ Count=1/16 Maskable- 64bit+
        Capabilities: [68] Vendor Specific Information: Len=44 &amp;lt;?&amp;gt;
        Capabilities: [ac] Express Endpoint, IntMsgNum 0
        Capabilities: [100] Advanced Error Reporting
        Capabilities: [13c] Device Serial Number 00-00-38-ff-ff-00-38-0f
        Capabilities: [150] Power Budgeting &amp;lt;?&amp;gt;
        Capabilities: [160] Virtual Channel
        Capabilities: [1b0] Latency Tolerance Reporting
        Capabilities: [220] Physical Resizable BAR
        Capabilities: [240] L1 PM Substates
        Kernel driver in use: brcmfmac &amp;lt;&amp;lt; THIS too!!
        Kernel modules: brcmfmac
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Additional points
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The 100 Mhz reference clock is either provided internally by the i.MX8M or an external clock generator IC on the board. If an external clock generator is used, validate that the chip is enabled.&lt;/li&gt;
&lt;li&gt;Check that the reset polarity specified in the device tree is correct. In the case of PCIe, it is an active low. &lt;/li&gt;
&lt;li&gt;Verify that the fixed-voltage regulators &lt;code&gt;regulator-fixed&lt;/code&gt; for PCIe are enabled in the device tree. These supply a constant 3.3v to the PCI slot.&lt;/li&gt;
&lt;li&gt;Specifically for Broadcom drivers, these need a &lt;code&gt;.txt&lt;/code&gt; NVRAM configuration file, and a &lt;code&gt;.bin&lt;/code&gt; file present in &lt;code&gt;/usr/lib/firmware&lt;/code&gt; directory, otherwise they fail silently. It will manifest as &lt;code&gt;wlan0&lt;/code&gt; not appearing. In such cases run &lt;code&gt;dmesg | grep brcm&lt;/code&gt; to triage.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>pci</category>
      <category>linux</category>
    </item>
    <item>
      <title>Firmware file exists, but kernel cannot find it: i.MX8MQ, imx-sdma error -2</title>
      <dc:creator>Ashwin</dc:creator>
      <pubDate>Thu, 09 Jul 2026 02:56:53 +0000</pubDate>
      <link>https://dev.to/pymaniac/firmware-file-exists-but-kernel-cannot-find-it-imx8mq-imx-sdma-error-2-92n</link>
      <guid>https://dev.to/pymaniac/firmware-file-exists-but-kernel-cannot-find-it-imx8mq-imx-sdma-error-2-92n</guid>
      <description>&lt;p&gt;The firmware file was sitting in the filesystem. The kernel insisted that it was not. Bluetooth on an i.MX8MQ evaluation board was failing with a -110 error. This meant that the chip (CYW4356) was not able to communicate. &lt;/p&gt;

&lt;p&gt;Right after running &lt;code&gt;hciconfig hci0 up&lt;/code&gt; there would be messages like &lt;code&gt;setting baudrate failed (-110)&lt;/code&gt;. After validating that the chip enable was properly being driven, the failures pointed issues in the chip’s communication path. &lt;/p&gt;

&lt;p&gt;There are two modes of chip communication: PIO or SDMA. The device tree said that SDMA was being used. Looking closely at the messages indicated that SDMA was not being loaded. The common causes for those on the i.MX8MQ could be that the SDMA channels for  Bluetooth were misconfigured or the chip was not working. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;dmesg&lt;/code&gt; pointed to SDMA accessibility errors with the message &lt;code&gt;We cannot prepare for the TX/RX slave dma!&lt;/code&gt;.  To narrow this down  I commented out the dma and dma-names properties for uart3, aka Bluetooth and voila - Bluetooth was up!  However, SDMA firmware  loading errors like &lt;code&gt;[2.464348] imx-sdma 302c0000.dma-controller: Direct firmware load for imx/sdma/sdma-imx7d.bin failed with error -2&lt;/code&gt; persisted in &lt;code&gt;dmesg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;ls /usr/lib/firmware&lt;/code&gt;, confirmed that the bin file was there. It &lt;em&gt;almost&lt;/em&gt; looked like the kernel did not want to acknowledge its presence.&lt;/p&gt;

&lt;p&gt;The SDMA firmware loader runs when the driver probes. The driver was loaded by default at boot time (&lt;code&gt;CONFIG_IMX_SDMA=y&lt;/code&gt;). This meant it was trying to load firmware before the root filesystem was up. The firmware lives in the root filesystem!   &lt;/p&gt;

&lt;p&gt;You might think why not set the firmware path in the kernel using &lt;a href="https://www.kernel.org/doc/html/v4.14/driver-api/firmware/built-in-fw.html" rel="noopener noreferrer"&gt;CONFIG_EXTRA_FIRMWARE&lt;/a&gt;. It allows the firmware to be built into vmlinux and avoids the kernel searching for it in the filesystem. This should have worked, but didn’t: darn those yocto dependencies removing it from the kernel build. Faced with the onerous task of threading through all the &lt;code&gt;inc&lt;/code&gt;, &lt;code&gt;bb&lt;/code&gt;, &lt;code&gt;bbappends&lt;/code&gt;, and making multiple builds to determine the root cause, I began to question do we really build the firmware in the kernel. What if, we &lt;strong&gt;delayed&lt;/strong&gt; the SDMA driver load, until something actually needed it: achievable by setting &lt;a href="https://cateee.net/lkddb/web-lkddb/IMX_SDMA.html" rel="noopener noreferrer"&gt;CONFIG_IMX_SDMA=m&lt;/a&gt;. By doing this we move the driver initialization out from vmlinux’s init (aka do_initcalls) to it being loaded via modprobe or insmod of a .ko file available on the rootfs. I was betting that as the filesystem exists, the firmware file will be available for the driver to load!  Suddenly it was a one line change, &lt;em&gt;just one build&lt;/em&gt;. Leading to rebuild, load and success!! &lt;/p&gt;

&lt;p&gt;Below is a comparison of what was seen in the logs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;With &lt;code&gt;CONFIG_IMX_SDMA=y&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[    2.133882] Bluetooth: HIDP (Human Interface Emulation) ver 1.2
[    2.139819] Bluetooth: HIDP socket layer initialized
[    2.464469] imx-sdma 302c0000.dma-controller: Direct firmware load for imx/sdma/sdma-imx7d.bin failed with error -2 &amp;lt;&amp;lt; Load at 2.46s
[    2.483924] imx-sdma 30bd0000.dma-controller: Direct firmware load for imx/sdma/sdma-imx7d.bin failed with error -2
[    2.500030] imx-sdma 302c0000.dma-controller: Direct firmware load for imx/sdma/sdma-imx7d.bin failed with error -2
[    2.510503] imx-sdma 302c0000.dma-controller: external firmware not found, using ROM firmware
[    2.524542] imx-sdma 30bd0000.dma-controller: Direct firmware load for imx/sdma/sdma-imx7d.bin failed with error -2
[    2.535006] imx-sdma 30bd0000.dma-controller: external firmware not found, using ROM firmware
[    3.354871] xhci-hcd xhci-hcd.1.auto: xHCI Host Controller
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;With &lt;code&gt;CONFIG_IMX_SDMA=m&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;root@imx8mq-evk:/etc/systemd/system#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;modprobe &lt;span class="nt"&gt;-D&lt;/span&gt; imx-sdma
&lt;span class="go"&gt;insmod /lib/modules/6.6.52-lts-next-ge0f9e2afd4cf-dirty/kernel/drivers/dma/imx-sdma.ko

&lt;/span&gt;&lt;span class="gp"&gt;root@imx8mq-evk:/etc/systemd/system#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dmesg | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Ei&lt;/span&gt; &lt;span class="s1"&gt;'sdma|mounted filesystem|Run /sbin/init'&lt;/span&gt;
&lt;span class="go"&gt;[    6.090389] EXT4-fs (mmcblk0p1): mounted filesystem 9cb57070-175f-4761-812a-3656cb092bcd r/w with ordered data mode. Quota mode: none. &amp;lt;&amp;lt; Filesystem mounted at 6.09 
[    6.117332] Run /sbin/init as init process
[    9.328588] imx-sdma 302c0000.dma-controller: firmware found.  &amp;lt;&amp;lt; load at 9.32s, after FS mount
[    9.337947] imx-sdma 302c0000.dma-controller: loaded firmware 4.6
[    9.401308] imx-sdma 30bd0000.dma-controller: firmware found.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key takeaway:&lt;/strong&gt; A firmware file being present in the file system does not mean it is available when the driver asks for it. Look out for when the driver tries to load it vs when the filesystem is mounted. As for building the firmware into the kernel: this can wait until something actually requires it.&lt;/p&gt;

&lt;p&gt;Next: the same board's WiFi was on a different PCIe controller than the reference device tree claimed.&lt;/p&gt;

</description>
      <category>embedded</category>
      <category>linux</category>
      <category>bluetooth</category>
      <category>yocto</category>
    </item>
  </channel>
</rss>
