{"id":11,"date":"2021-12-20T19:45:50","date_gmt":"2021-12-20T22:45:50","guid":{"rendered":"http:\/\/blogs.igalia.com\/gpiccoli\/?p=11"},"modified":"2023-02-12T12:23:19","modified_gmt":"2023-02-12T15:23:19","slug":"booting-upstream-kernel-on-inforce-6640","status":"publish","type":"post","link":"https:\/\/blogs.igalia.com\/gpiccoli\/2021\/12\/booting-upstream-kernel-on-inforce-6640\/","title":{"rendered":"Booting upstream kernel on Inforce 6640"},"content":{"rendered":"\nMy first task in the <a href=\"https:\/\/www.igalia.com\/technology\/kernel\">Core team<\/a> at <a rel=\"noreferrer noopener\" aria-label=\"Igalia (opens in a new tab)\" href=\"https:\/\/www.igalia.com\/\" target=\"_blank\">Igalia<\/a> was to boot a more recent kernel in the <a rel=\"noreferrer noopener\" aria-label=\"Inforce 6640 board (opens in a new tab)\" href=\"https:\/\/www.inforcecomputing.com\/inforce6640\" target=\"_blank\">Inforce 6640 board<\/a> , which was very interesting given my very limited experience working in the ARM64 world &#8211; also, the work aimed to benefit Igalia&#8217;s <a rel=\"noreferrer noopener\" aria-label=\"Graphics team (opens in a new tab)\" href=\"https:\/\/www.igalia.com\/technology\/graphics\" target=\"_blank\">Graphics team<\/a> in their<a rel=\"noreferrer noopener\" aria-label=\" Freedreno (opens in a new tab)\" href=\"https:\/\/gitlab.freedesktop.org\/mesa\" target=\"_blank\"> Freedreno<\/a> development. The board itself is pretty powerful and contains a lot of resources; it comes with a quad-core Snapdragon\u2122 820 processor, Adreno\u2122 530 GPU, 4G of RAM plus a fast 64G UFS storage. A picture of my unit below:\n\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<img loading=\"lazy\" decoding=\"async\" width=\"1286\" height=\"732\" class=\"wp-image-12\" style=\"width: 570px\" src=\"http:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640.jpg\" alt=\"i6640_pic\" srcset=\"https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640.jpg 1286w, https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640-580x330.jpg 580w, https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640-768x437.jpg 768w, https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640-940x535.jpg 940w, https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640-1200x683.jpg 1200w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><br \/>\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nSo, I got a packed Debian image with 4.14 kernel and after some looking, noticed that the kernel was a modified \/ custom version from Linaro, which they call <a rel=\"noreferrer noopener\" aria-label=\"Qualcomm LT (Landing Tree) (opens in a new tab)\" href=\"https:\/\/git.linaro.org\/landing-teams\/working\/qualcomm\/kernel.git\/log\/?h=release\/qcomlt-4.14\" target=\"_blank\">Qualcomm LT (Landing Tree)<\/a>. Hence, the first idea was to boot this image and see how well it worked. And unsurprisingly, it was working flawlessly &#8211; given this was provided by the board vendor, the expectation was that good amount of validation was performed, so we had a starting point here &#8211; the working version was the Linaro&#8217;s Qualcomm-LT 4.14. In a rush of optimism, I tried to test the latest released mainline kernel at the moment (v5.14) and&#8230;it didn&#8217;t work! Why would it, right? Where would be the fun?\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nSo, next step was to bisect it and I decided to start by using the Linaro&#8217;s LT tree &#8211; it failed on 5.7+, but succeeded to boot 5.4, although the board seemed pretty slow! First thought was likely a problem in the storage, like a bad device tree config for UFS (the storage device) or a driver change between 4.14 and 5.4. But before looking into that, we needed to understand why kernels 5.4+ weren&#8217;t booting&#8230;so how to proceed here, if ssh wasn&#8217;t working?\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nYeah&#8230;exactly: serial output to the rescue! So I got some jumper cables and decided to use a Raspberry Pi as the other end for serial connection. By connecting all the proper cables and using &#8220;minicom&#8221;, I could boot a 5.7 kernel and check the progress..and guess what? Kernel was booting normally!!!<br \/>But without PCIe &#8211; no network \/ GPU, for example. Also, it was still very slow. And then, an epiphany: it was the device-tree! By dumping the device-tree (from procfs) in both the working 4.14 and the non-working 5.7, a bunch of stuff were missing, like all the PCI structures. But why is that? Somebody just removed stuff from a working device-tree?\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nNah, it was just an obvious thing I didn&#8217;t realize before: up to kernel 5.6, the Inforce 6640 board was relying on the their &#8220;cousin&#8217;s&#8221; device-tree, the Dragonboard 820c! A pretty similar board, so relying on its DT made the board working like a charm, but in kernel 5.6 a proper device-tree for Inforce 6640 <a rel=\"noreferrer noopener\" aria-label=\"was added (opens in a new tab)\" href=\"https:\/\/git.kernel.org\/linus\/6cbdec2d3ca\" target=\"_blank\">was added<\/a>, although it&#8217;s very simple and minimal to boot the board. In order to have a fully working system after that finding, all we needed was to force the usage of the &#8220;old&#8221; db820c.dts, with the following hack patch:\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n\n<pre class=\"wp-block-code\"><code>--- a\/arch\/arm64\/boot\/dts\/qcom\/Makefile\n+++ b\/arch\/arm64\/boot\/dts\/qcom\/Makefile\n dtb-$(CONFIG_ARCH_QCOM)        += apq8096-db820c.dtb\n-dtb-$(CONFIG_ARCH_QCOM)        += apq8096-ifc6640.dtb\n+#dtb-$(CONFIG_ARCH_QCOM)       += apq8096-ifc6640.dtb\n dtb-$(CONFIG_ARCH_QCOM)        += ipq6018-cp01-c1.dtb<\/code><\/pre>\n\n\n\n\nOK, so now we have a working upstream kernel (with network!), so let&#8217;s investigate the remaining: why the board is so slow? And what about graphics, is it working? I started with the graphics and again, had some issues: board had graphics working on Linaro&#8217;s LT tree (v5.13), but not in the upstream 5.13\/5.14. Another round of bisects later plus code inspection, and I found the &#8220;culprit&#8221;: <a rel=\"noreferrer noopener\" aria-label=\"a downstream patch (opens in a new tab)\" href=\"https:\/\/git.linaro.org\/landing-teams\/working\/qualcomm\/kernel.git\/commit\/?id=90f71171d251\" target=\"_blank\">a downstream patch<\/a> that was also required in order to make the GPU work. The patch added a regulator with &#8220;always-on&#8221; property and a higher voltage than found in upstream. So, changed that and voil\u00e1: we had now a working 5.14 with graphics but&#8230;still slow! Moving then to the next journey: why the newer kernels are so much slower?\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nIn order to investigate that, I began by comparing the db-820c device-trees among versions, and despite there were differences, nothing rang a bell. So, jumped to sysfs and started investigating there&#8230;when it comes to my attention that the CPUs were running slow, <em>very<\/em> slow &#8211; CPUs frequency were tiny . Taking a look in the commits and checking cpufreq drivers, the troublemaker was found: after kernel 4.18, <a rel=\"noreferrer noopener\" aria-label=\"a patch (opens in a new tab)\" href=\"https:\/\/git.kernel.org\/linus\/46e2856b8e1\" target=\"_blank\">a patch<\/a> added a driver for handling <a rel=\"noreferrer noopener\" aria-label=\"OPP (opens in a new tab)\" href=\"https:\/\/www.kernel.org\/doc\/html\/latest\/power\/opp.html\" target=\"_blank\">OPP<\/a> voltage scaling for Kryo CPUs, which is the case for Snapdragon\u2122 820. After loading this new module, CPU clocks were back to normal\/fast rates, the board was as fast (or even more) than running the original Linaro&#8217;s LT 4.14 downstream kernel.\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nBut&#8230; (yeah, <em>another<\/em> but)&#8230;after some simple CPU benchmarks, just some seconds after the beginning of the tests, board rebooted into a firmware error state. It was like as if it couldn&#8217;t handle all the CPU speed in the newer kernels. Here, I used the very powerful ftrace tooling to determine the reason of this behavior. And guess what was found?\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nCooling! The responsible for that was kernel lack of throttling. The following trace snippet shows how the governor would reduce the CPU clock\/voltage in order to preserve CPUs health:\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n\n<pre class=\"wp-block-code\"><code> =&gt; cpufreq_governor_limits.part.6\n =&gt; cpufreq_set_policy\n =&gt; cpufreq_update_policy\n =&gt; cpufreq_set_cur_state\n =&gt; thermal_cdev_update\n =&gt; step_wise_throttle\n =&gt; handle_thermal_trip\n =&gt; thermal_zone_device_update.part.10\n =&gt; thermal_zone_device_check\n =&gt; process_one_work\n =&gt; worker_thread<\/code><\/pre>\n\n\n\n\nThe key here is the function  <code>step_wise_throttle<\/code> which downscale the CPU frequency to prevent from overheating. In order to have it properly working, cooling maps\/trip points are required in the device-tree, it&#8217;s present in some downstream patches on Linaro&#8217;s kernel &#8211; but when that work was upstream&#8217;ed, it lacked some portions, hence if we have cpufreq driver and high frequencies, we&#8217;re prone to overheating \/ firmware reboots. The patch that fixes that was re-submitted by another person and <a rel=\"noreferrer noopener\" aria-label=\"we tried to get that accepted (opens in a new tab)\" href=\"https:\/\/lore.kernel.org\/linux-arm-msm\/233effdb-5604-2405-fdaa-ebd35b0c0126@igalia.com\/\" target=\"_blank\">I tried to get that accepted<\/a>, by &#8220;lobbying&#8221; in the mailing-list and showing our test results, but up to 5.16-rc6 it isn&#8217;t present in Linus tree.\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nFinally, worth to mention 2 things: for some reason, building a kernel with ftrace <em>after<\/em> v5.7 prevents it from booting. This led to a very interesting debug, like debug-by-rebooting (using a firmware triggered reboot in assembly), (re-)implementing a very early console to show output much before &#8220;earlycon&#8221; is available &#8211; stay tuned for a blog post about that! Second, we still lack that &#8220;regulator-always-on&#8221; necessary to have a functional GPU &#8211; after <a rel=\"noreferrer noopener\" aria-label=\"submitting a patch (opens in a new tab)\" href=\"https:\/\/lore.kernel.org\/linux-arm-msm\/20210927163745.2066610-1-gpiccoli@igalia.com\/\" target=\"_blank\">submitting a patch<\/a> for that, I&#8217;ve discussed that with the maintainer, and it seems we should have the user of such regulator to request it properly &#8211; due to other tasks I couldn&#8217;t continue this work. So, in summary, if you want to boot an upstream kernel (5.14+) on Inforce 6640, the steps are:\n\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n\n<ul class=\"wp-block-list\"><li>Disable the ifc6640.dts, and rely on db820c DT;<\/li><li>Boot without ftrace and with the Kryo cpufreq driver built-in (<code>ARM_QCOM_CPUFREQ_NVMEM=y<\/code>);<\/li><li>Apply the &#8220;regulator-always-on&#8221; <a rel=\"noreferrer noopener\" aria-label=\"quirk patch (opens in a new tab)\" href=\"https:\/\/lore.kernel.org\/linux-arm-msm\/20210927163745.2066610-1-gpiccoli@igalia.com\/\" target=\"_blank\">quirk patch<\/a>;<\/li><li>Also apply the<a rel=\"noreferrer noopener\" aria-label=\" cooling-maps patch (opens in a new tab)\" href=\"https:\/\/lore.kernel.org\/linux-arm-msm\/jmayJcXoExAK2G7UBIXMz5CDN0BYgYkFZguHlPNRFOU@cp4-web-038.plabs.ch\/\" target=\"_blank\"> cooling-maps patch<\/a>;<\/li><\/ul>\n\n\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nWith that, one should be able to boot a recent kernel in this great board, and even with graphics! Take a look in the Inforce 6640 running glxgears in a recent kernel:\n\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<img loading=\"lazy\" decoding=\"async\" width=\"1280\" height=\"746\" class=\"wp-image-48\" style=\"width: 800px\" src=\"http:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640_2.jpg\" alt=\"i6640_glxgears\" srcset=\"https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640_2.jpg 1280w, https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640_2-580x338.jpg 580w, https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640_2-768x448.jpg 768w, https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640_2-940x548.jpg 940w, https:\/\/blogs.igalia.com\/gpiccoli\/files\/2021\/12\/i6640_2-1200x699.jpg 1200w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/>\n\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\nHope the read was enjoyable and useful, feel free to leave your comment or ping on IRC (gpiccoli @OFTC\/Libera) =]\n","protected":false},"excerpt":{"rendered":"<p>My first task in the Core team at Igalia was to boot a more recent kernel in the Inforce 6640 board , which was very interesting given my very limited experience working in the ARM64 world &#8211; also, the work aimed to benefit Igalia&#8217;s Graphics team in their Freedreno development. The board itself is pretty &hellip; <a href=\"https:\/\/blogs.igalia.com\/gpiccoli\/2021\/12\/booting-upstream-kernel-on-inforce-6640\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Booting upstream kernel on Inforce 6640&#8221;<\/span><\/a><\/p>\n","protected":false},"author":67,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-11","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/posts\/11","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/users\/67"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/comments?post=11"}],"version-history":[{"count":41,"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/posts\/11\/revisions"}],"predecessor-version":[{"id":60,"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/posts\/11\/revisions\/60"}],"wp:attachment":[{"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/media?parent=11"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/categories?post=11"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.igalia.com\/gpiccoli\/wp-json\/wp\/v2\/tags?post=11"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}