Unfortunately the Nvidia Jetson doesn’t support the Intel 9260 cards out of the box. Nvidia’s JetPack OS ships with the Linux kernel 4.9 while support for these cards was added in the newer 4.14. However, there’s a workaround, but we gotta recompile the kernel modules. Without further ado, here’s how to get the Intel 9260 Wireless Card working on the Nvidia Jetson in only 25 easy steps.
UPDATE 2
Not using an Intel 9260? Try these instructions:
https://sfxrescue.com/tutorials/intel-pcie-wifi-with-nvidia-jetson/
UPDATE 1
These instructions might no longer work, as there have been changes to the kernel since that mean the patch may no longer be applicable.
Some updated instructions, courtesy of @Caldarie from the Nvidia Developer Forums:
1. Flash a fresh version of Jetpack on your SD card
2. Clone and build and install the
iwlwifi backport:
1 2 3 4 5 6 7 | cd ~ git clone https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/backport-iwlwifi.git cd backport-iwlwifi make defconfig-iwlwifi-public sed -i 's/CPTCFG_IWLMVM_VENDOR_CMDS=y/# CPTCFG_IWLMVM_VENDOR_CMDS is not set/' .config make -j4 sudo make install |
Clone the linux-firmware git repo and copy relevant files to the system:
1 2 3 4 5 | cd ~ git clone git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git cd linux-firmware sudo cp iwlwifi-9260* /lib/firmware sudo reboot |
3. Install BTUSB from https://github.com/yoffy/jetson-nano-kernel/blob/master/install-btusb.sh
4. Reboot
1 | sudo reboot |
After rebooting, it may take up to a minute for the WiFi connection to be made.
Enjoy!
Original Post:
All these instructions are adapted from this forum post. The patch is from this post in the same thread. Some of the compilation instructions are from this resource.
Download the driver package from here: https://developer.nvidia.com/embedded/dlc/l4t-jetson-driver-package-32-1-jetson-nano and copy it to the home directory of the Jetson. Then extract using:
1 | $ bzip2 -d Jetson-Nano-Tegra210_Linux_R32.1.0_aarch64.tbz2 |
Move into the new directory and run the script to download the source files.
1 2 | $ cd ~/Linux_for_Tegra/ $ ./source_sync.sh |
When prompted, the tag is tegra-l4t-r32.1. You will likely have to enter this twice.
Move to the kernel source directory:
1 | $ cd ~/Linux_for_Tegra/sources/kernel/kernel-4.9 |
Copy this patch (courtesy of @Manikanta from Nvidia devtalk forums) into a new file, called 0001-PCI-tegra-Fix-speed-change-code.txt
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | From d989f4ba5e5819366e25ac19a3dab60713e6780d Mon Sep 17 00:00:00 2001 From: Vidya Sagar <vidyas@nvidia.com> Date: Wed, 15 May 2019 12:15:24 +0530 Subject: [PATCH 1/1] PCI: tegra: Fix speed change code X-NVConfidentiality: public Speed change to Gen-2 should take place when there is no traffic on the bus and the way software can make sure that is to attempt speed change to Gen-2 before registering the devices with PCIe sub-system. This patch does exactly that. It also fixes link retrain timeout value to 100000us. Bug 200519254 Change-Id: I820a78d907a25e25e1e95e22456793d5e33ad833 Signed-off-by: Vidya Sagar <vidyas@nvidia.com> --- drivers/pci/host/pci-tegra.c | 178 ++++++++++++----------------------- 1 file changed, 61 insertions(+), 117 deletions(-) diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index e4249feac226..7d9f376f85ee 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -204,6 +204,7 @@ #define RP_VEND_XP_PRBS_EN (1 << 1) #define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_BW_MGMT_STATUS 0x40000000 #define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 #define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 #define RP_LINK_CONTROL_STATUS_NEG_LINK_WIDTH (0x3F << 20) @@ -374,7 +375,7 @@ #define INT_PCI_MSI_NR (32 * 8) -#define LINK_RETRAIN_TIMEOUT HZ +#define LINK_RETRAIN_TIMEOUT 100000 #define PCIE_LANES_X4_X1 0x14 @@ -489,6 +490,7 @@ struct tegra_pcie { static int tegra_pcie_enable_msi(struct tegra_pcie *pcie, bool no_init); static void tegra_pcie_check_ports(struct tegra_pcie *pcie); +static void tegra_pcie_link_speed(struct tegra_pcie *pcie); static int tegra_pcie_power_off(struct tegra_pcie *pcie); #define MAX_PWR_GPIOS 5 @@ -524,7 +526,6 @@ struct tegra_pcie_bus { /* used to avoid successive hotplug disconnect or connect */ static bool hotplug_event; /* pcie mselect, xclk and emc rate */ -static bool is_gen2_speed; static u16 bdf; static u16 config_offset; static u32 config_val; @@ -2532,6 +2533,8 @@ static void tegra_pcie_check_ports(struct tegra_pcie *pcie) dev_info(pcie->dev, "link %u down, ignoring\n", port->index); tegra_pcie_port_disable(port); } + /* configure all links to gen2 speed by default */ + tegra_pcie_link_speed(pcie); if (pcie->soc_data->mbist_war) mbist_war(pcie, false); } @@ -2706,123 +2709,72 @@ static int tegra_pcie_scale_voltage(struct tegra_pcie *pcie) return err; } -static void tegra_pcie_change_link_speed(struct tegra_pcie *pcie, - struct pci_dev *pdev, bool isGen2) +static void tegra_pcie_change_link_speed(struct tegra_pcie *pcie) { - u16 val, link_sts_up_spd, link_sts_dn_spd; - u16 link_cap_up_spd, link_cap_dn_spd; - u32 rp = 0; - unsigned long start_jiffies; - struct tegra_pcie_port *port = NULL; - struct pci_dev *up_dev, *dn_dev; + struct device *dev = pcie->dev; + struct tegra_pcie_port *port, *tmp; + ktime_t deadline; + u32 value; - PR_FUNC_LINE; - /* skip if current device is not PCI express capable */ - /* or is either a root port or downstream port */ - if (!pci_is_pcie(pdev)) - goto skip; - if ((pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM) || - (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)) - goto skip; - - /* initialize upstream/endpoint and downstream/root port device ptr */ - up_dev = pdev; - dn_dev = pdev->bus->self; - - rp = PCI_SLOT(dn_dev->devfn); - list_for_each_entry(port, &pcie->ports, list) - if (rp == port->index + 1) - break; - if (!port->ep_status) - goto skip; - - /* read link status register to find current speed */ - pcie_capability_read_word(up_dev, PCI_EXP_LNKSTA, &link_sts_up_spd); - link_sts_up_spd &= PCI_EXP_LNKSTA_CLS; - pcie_capability_read_word(dn_dev, PCI_EXP_LNKSTA, &link_sts_dn_spd); - link_sts_dn_spd &= PCI_EXP_LNKSTA_CLS; - /* read link capability register to find max speed supported */ - pcie_capability_read_word(up_dev, PCI_EXP_LNKCAP, &link_cap_up_spd); - link_cap_up_spd &= PCI_EXP_LNKCAP_SLS; - pcie_capability_read_word(dn_dev, PCI_EXP_LNKCAP, &link_cap_dn_spd); - link_cap_dn_spd &= PCI_EXP_LNKCAP_SLS; - /* skip if both devices across the link are already trained to gen2 */ - if (isGen2) { - if (((link_cap_up_spd >= PCI_EXP_LNKSTA_CLS_5_0GB) && - (link_cap_dn_spd >= PCI_EXP_LNKSTA_CLS_5_0GB)) && - ((link_sts_up_spd != PCI_EXP_LNKSTA_CLS_5_0GB) || - (link_sts_dn_spd != PCI_EXP_LNKSTA_CLS_5_0GB))) - goto change; - else - goto skip; - } else { - /* gen1 should be supported by default by all pcie cards */ - if ((link_sts_up_spd != PCI_EXP_LNKSTA_CLS_2_5GB) || - (link_sts_dn_spd != PCI_EXP_LNKSTA_CLS_2_5GB)) - goto change; - else - goto skip; - } + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + /* + * "Supported Link Speeds Vector" in "Link Capabilities 2" + * is not supported by Tegra. tegra_pcie_change_link_speed() + * is called only for Tegra chips which support Gen2. + * So there no harm if supported link speed is not verified. + */ + value = readl(port->base + RP_LINK_CONTROL_STATUS_2); + value &= ~PCI_EXP_LNKSTA_CLS; + value |= PCI_EXP_LNKSTA_CLS_5_0GB; + writel(value, port->base + RP_LINK_CONTROL_STATUS_2); + + /* + * Poll until link comes back from recovery to avoid race + * condition. + */ + deadline = ktime_add_us(ktime_get(), LINK_RETRAIN_TIMEOUT); -change: - /* Set Link Speed */ - pcie_capability_read_word(dn_dev, PCI_EXP_LNKCTL2, &val); - val &= ~PCI_EXP_LNKSTA_CLS; - if (isGen2) { - dev_info(pcie->dev, "speed change : Gen-1 -> Gen-2\n"); - val |= PCI_EXP_LNKSTA_CLS_5_0GB; - } else { - dev_info(pcie->dev, "speed change : Gen-2 -> Gen-1\n"); - val |= PCI_EXP_LNKSTA_CLS_2_5GB; - } - pcie_capability_write_word(dn_dev, PCI_EXP_LNKCTL2, val); + while (ktime_before(ktime_get(), deadline)) { + value = readl(port->base + RP_LINK_CONTROL_STATUS); + if ((value & PCI_EXP_LNKSTA_LT) == 0) + break; - /* LTSSM might be in recovery or configuration, so wait for current - * link training to end. Break out after waiting for timeout */ - start_jiffies = jiffies; - for (;;) { - pcie_capability_read_word(dn_dev, PCI_EXP_LNKSTA, &val); - if (!(val & PCI_EXP_LNKSTA_LT)) - break; - if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) - break; - usleep_range(1000, 1100); - } - if (val & PCI_EXP_LNKSTA_LT) - dev_err(pcie->dev, "Link training failed before speed change\n"); + usleep_range(2000, 3000); + } + if (value & PCI_EXP_LNKSTA_LT) + dev_warn(dev, "PCIe port %u link is in recovery\n", + port->index); - /* Retrain the link */ - pcie_capability_read_word(dn_dev, PCI_EXP_LNKCTL, &val); - val |= PCI_EXP_LNKCTL_RL; - pcie_capability_write_word(dn_dev, PCI_EXP_LNKCTL, val); + /* Clear BW Management Status */ + value = readl(port->base + RP_LINK_CONTROL_STATUS); + value |= RP_LINK_CONTROL_STATUS_BW_MGMT_STATUS; + writel(value, port->base + RP_LINK_CONTROL_STATUS); - /* Wait for link training end. Break out after waiting for timeout */ - start_jiffies = jiffies; - for (;;) { - pcie_capability_read_word(dn_dev, PCI_EXP_LNKSTA, &val); - if (!(val & PCI_EXP_LNKSTA_LT)) - break; - if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) - break; - usleep_range(1000, 1100); - } - if (val & PCI_EXP_LNKSTA_LT) - dev_err(pcie->dev, "Link Re-training failed after speed change\n"); + /* Retrain the link */ + value = readl(port->base + RP_LINK_CONTROL_STATUS); + value |= PCI_EXP_LNKCTL_RL; + writel(value, port->base + RP_LINK_CONTROL_STATUS); -skip: - return; + deadline = ktime_add_us(ktime_get(), LINK_RETRAIN_TIMEOUT); + + while (ktime_before(ktime_get(), deadline)) { + value = readl(port->base + RP_LINK_CONTROL_STATUS); + if (value & RP_LINK_CONTROL_STATUS_BW_MGMT_STATUS) + break; + + usleep_range(2000, 3000); + } + if (value & PCI_EXP_LNKSTA_LT) + dev_err(dev, "failed to retrain link of port %u\n", + port->index); + } } -static void tegra_pcie_link_speed(struct tegra_pcie *pcie, bool isGen2) +static void tegra_pcie_link_speed(struct tegra_pcie *pcie) { - struct pci_dev *pdev = NULL; - PR_FUNC_LINE; - /* Voltage scaling should happen before any device transition */ - /* to Gen2 or after all devices has transitioned to Gen1 */ - for_each_pci_dev(pdev) - tegra_pcie_change_link_speed(pcie, pdev, isGen2); + tegra_pcie_change_link_speed(pcie); tegra_pcie_scale_voltage(pcie); return; @@ -2833,8 +2785,6 @@ static void tegra_pcie_enable_features(struct tegra_pcie *pcie) struct tegra_pcie_port *port; PR_FUNC_LINE; - /* configure all links to gen2 speed by default */ - tegra_pcie_link_speed(pcie, true); list_for_each_entry(port, &pcie->ports, list) { if (port->status) tegra_pcie_apply_sw_war(port, true); @@ -3662,9 +3612,8 @@ static int apply_link_speed(struct seq_file *s, void *data) { struct tegra_pcie *pcie = (struct tegra_pcie *)(s->private); - seq_printf(s, "Changing link speed to %s... ", - (is_gen2_speed) ? "Gen2" : "Gen1"); - tegra_pcie_link_speed(pcie, is_gen2_speed); + seq_puts(s, "Changing link speed to Gen2\n"); + tegra_pcie_link_speed(pcie); seq_printf(s, "Done\n"); return 0; } @@ -4591,11 +4540,6 @@ static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) if (!d) goto remove; - d = debugfs_create_bool("is_gen2_speed(WO)", S_IWUSR, pcie->debugfs, - &is_gen2_speed); - if (!d) - goto remove; - d = create_tegra_pcie_debufs_file("apply_link_speed", &apply_link_speed_fops, pcie->debugfs, (void *)pcie); -- 2.17.1 |
Apply the patch with:
1 | $ git apply 0001-PCI-tegra-Fix-speed-change-code.txt |
Execute the following commands to create the .config file:
1 2 | $ mkdir -p kernel_build $ make ARCH=arm64 O=kernel_build tegra_defconfig |
Execute the following commands to build the kernel including all DTBs and modules:
1 | $ make ARCH=arm64 O=kernel_build -j4 |
Copy the built image to the boot directory:
1 | $ sudo cp kernel_build/arch/arm64/boot/Image /boot/Image |
Reboot:
1 | $ sudo reboot |
Clone and build and install the iwlwifi backport:
1 2 3 4 5 6 7 | $ cd ~ $ git clone https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/backport-iwlwifi.git $ cd backport-iwlwifi $ make defconfig-iwlwifi-public $ sed -i 's/CPTCFG_IWLMVM_VENDOR_CMDS=y/# CPTCFG_IWLMVM_VENDOR_CMDS is not set/' .config $ make -j4 $ sudo make install |
Clone the linux-firmware git repo and copy relevant files to the system:
1 2 3 4 | $ cd ~ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git $ cd linux-firmware $ sudo cp iwlwifi-9260* /lib/firmware |
Final reboot:
1 | $ sudo reboot |
And that’s it. It should just work after that. How to get a Intel 9260 card working on an Nvidia Jetson Nano in only 25 easy steps. Now take a moment, pour yourself a drink and enjoy high speed Wi-Fi on the Nvidia Jetson Nano.
I found this on the forums that helped me get your process to work.
If you downloaded kernel source using git, then copy the above patch and save it as Fix_speed_change_code.patch.
Then apply the patch in kernel source using “git apply Fix_speed_change_code.patch” command. Your patch command is incorrect it does not work.
You’re right, I forgot to specify the correct filename to save as. I’ve updated the post, thanks for letting me know!
I did try, but after rebooting my machine, the boot loader becomes corrupted.
FYI the instructions still say to use “got patch”, it is still incorrect.
Hmmm. Not quite sure what you mean there, I can’t find any mention of
got patch
. Care to elaborate?“git patch” command does not exist
The command should be
git apply
– I’ve updated the blog. 🙂for more recent git tags check website
http://nv-tegra.nvidia.com/gitweb/?p=linux-4.9.git;a=summary