This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

66AK2G12: Booting Linux from a USB Mass Storage Device

Part Number: 66AK2G12

Hello,

I have a system with fully functioning USB hardware and a U-Boot image that can load the kernel and other necessary firmwares from a USB Drive and jump to the kernel.  Driver configuration issues aside, this wasn't too complicated to accomplish.

That being said, I have now discovered a chicken-and-egg problem.  The Linux drivers for USB are the proprietary DWC3 kernel modules, and they don't seem to want to be built into the kernel.  Since they are loadable modules, they live in the filesystem.  I am attempting to boot a system with a USB filesystem, but the drivers to mount a USB filesystem live in the filesystem.

I have two requests for information:

1) It seems that I need to add these files to the initrd, but I lack enough context/understanding to know exactly what that should contain.  I have a list of modules that need to be loaded and I have the initrd provided by the PSDK (In this case PSDK 6.3).  What's odd is that the k2-fw-initrd image provided only contains ./lib/firmware/ and a handful of IP firmwares.  I do not understand how these files are loaded or used by the kernel in the early boot.  I'm certain I could read the kernel source and figure that out, but if there are any experts out there who have a concise explanation or direction on where to look, I'd love to hear it.  

2) Additionally, we're using Yocto/Arago as our build system.  This makes the issue a little more complicated than building the initrd we need and compressing it into the correct format.  The details of how to accomplish this may be better asked on a Yocto forum, but since it pertains to a TI-provided recipe I thought I'd ask for some info here.  The recipe for k2-fw-initrd has some hacks and explanations that it's an unconventional recipe.  I'd like to write a bbappend for this recipe that adds the USB kernel modules to the image it generates.  It seems this will involve modifying the linux-ti-staging recipe as well because the generated modules are a part of the kernel recipe and need to be made available to the initrd recipe.  Can anyone offer some recommendations about how to approach adding the kernel modules to the initrd?

  • Hi Jeff,

    I don't think you need to use initrd image. I believe I have done configuring all USB modules to kernel image, not for K2G though, but a device which uses the same USB controller as on K2G, so all the kernel modules are the same.

    Can you please elaborate on the kernel build issue you have when configuring all USB kernel drivers to kernel? I don't think there is any proprietary DWC3 driver in kernel.

  • It seems my build issue was related to my machine definition in my layer.  I removed the dependencies on the kernel modules and the build completed. I take this as proof that the new configuration has been applied to the build.

    I took this image and put it on the unit, forgetting that I needed to also modify the image on the USB stick, so I spun my wheels a bit on that.

    Now I have a kernel crash, which feels like progress.

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    [ 1.630842] Unhandled fault: asynchronous external abort (0x211) at 0x00000000
    [ 1.638051] pgd = (ptrval)
    [ 1.640750] [00000000] *pgd=80000800004003, *pmd=00000000
    [ 1.646144] Internal error: : 211 [#1] PREEMPT SMP ARM
    [ 1.651269] Modules linked in:
    [ 1.654320] CPU: 0 PID: 18 Comm: kworker/0:1 Not tainted 4.19.94-gbe5389fd85 #1
    [ 1.661611] Hardware name: Keystone
    [ 1.665098] Workqueue: events deferred_probe_work_func
    [ 1.670229] PC is at kdwc3_probe+0x178/0x1e0
    [ 1.674492] LR is at devres_add+0x54/0x5c
    [ 1.678490] pc : [<c074986c>] lr : [<c0681644>] psr: 60000013
    [ 1.684742] sp : d70e1d68 ip : d70e1d10 fp : d70e1d9c
    [ 1.689954] r10: 00000027 r9 : 00000036 r8 : d76e7814
    [ 1.695166] r7 : d7100210 r6 : d7100200 r5 : d6b3bf40 r4 : 00000001
    [ 1.701677] r3 : e0ed0000 r2 : 00000000 r1 : 60000013 r0 : 00000000
    [ 1.708191] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
    [ 1.715310] Control: 30c5387d Table: 00003000 DAC: fffffffd
    [ 1.721043] Process kworker/0:1 (pid: 18, stack limit = 0x(ptrval))
    [ 1.727296] Stack: (0xd70e1d68 to 0xd70e2000)
    [ 1.731643] 1d60: 00000080 d70fe540 d6b3bf40 c0691f5c d7100210 00000000
    [ 1.739805] 1d80: c102c8e4 00000000 00000000 c102c8e4 d70e1dbc d70e1da0 c067fc48 c0749700
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    Following Section 3.1.1.6.8 of the Processor SDK Linux guide, I have the following configs set:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CONFIG_USB=y
    CONFIG_USB_XHCI_HCD=y
    CONFIG_USB_XHCI_PCI=y
    CONFIG_USB_XHCI_PLATFORM=y
    CONFIG_USB_STORAGE=y
    CONFIG_USB_DWC3=y
    CONFIG_USB_DWC3_HOST=y
    CONFIG_USB_DWC3_KEYSTONE=y
    CONFIG_EXTCON=y
    CONFIG_EXTCON_USB_GPIO=y
    CONFIG_SCSI_MOD=y
    CONFIG_SCSI=y
    CONFIG_BLK_DEV_SD=y
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    The rest of the config with commented options deleted is shown below:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #
    # Automatically generated file; DO NOT EDIT.
    # Linux/arm 4.19.94 Kernel Configuration
    #
    #
    # Compiler: arm-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 8.3.0
    #
    CONFIG_CC_IS_GCC=y
    CONFIG_GCC_VERSION=80300
    CONFIG_CLANG_VERSION=0
    CONFIG_CC_HAS_ASM_GOTO=y
    CONFIG_IRQ_WORK=y
    CONFIG_BUILDTIME_EXTABLE_SORT=y
    #
    # General setup
    #
    CONFIG_INIT_ENV_ARG_LIMIT=32
    CONFIG_LOCALVERSION=""
    CONFIG_LOCALVERSION_AUTO=y
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

  • Hi Jeff,

    > [    1.670229] PC is at kdwc3_probe+0x178/0x1e0

    Can you please check which line in the kernel source code causes this crash?

    Please let me know If you don't know how to get the source line number from this log message, I believe there is a training video on training.ti.com explains how to do it.

  • I incorrectly marked that as resolved.

    The Program counter is pointing to dcw3-keystone.c:55, which is the function kdwc3_enable_irqs.

  • The exact instruction that the PC refers to is:

    ldr r6, [r5, #4]

  • Line 55 seems to be the first time accessing the USB module (read a register) in the _probe() function, so I would think the issue is because the USB module is not clocked yet. I am not sure how to solve this, but did you build the USB PHY into kernel?

  • I have the Keystone USB PHY Driver and NOP USB Transceiver Driver built into the kernel.

    I checked the PSC Registers using the JTAG debugger at the moment the error message began to print.  The register PSC_PDSTAT14 (0x235023C) reads 0xFFFFFFFD, which I believe indicates that the PSC domain is on.

  • Jeff,

    PSC on is only for the power to USB module, it is still possible the USB module is not clocked, which would cause such type of register read failure.

  • I have solved this problem.  If someone from TI can shed some light on why this is the solution, that may prove useful to others who read this post.

    After a series of tests, I determined that the USB subsystem needs to be turned off before jumping to the Linux kernel.  I had actually read that in the U-Boot documentation, but there is no mention of this in the TI documentation. 

    In U-Boot source doc/README.usb:

    The USB (at least the USB UHCI) needs a frame list (4k), transfer
    descripor and queue headers which are all located in the main memory.
    The UHCI allocates every milisecond the PCI bus and reads the current
    frame pointer. This may cause to crash the OS during boot. So the USB
    _MUST_ be stopped during OS boot. This is the reason, why the USB is
    NOT automatically started during start-up. If someone needs the USB
    he has to start it and should therefore be aware that he had to stop
    it before booting the OS.

    I had tried adding a usb stop to the bootcmd before run_kern, but that didn't fix the issue.  I did eventually prove that USB needed to be turned off and that it needed to be turned off specifically before the PMMC firmware is loaded and run.

    For those curious, here's what it took.

    The reference deisgn has the following bootcmd:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    run findfdt;
    run envboot;
    run init_${boot};
    run get_mon_${boot} run_mon;
    run set_name_pmmc get_pmmc_${boot}; run run_pmmc;
    run get_kern_${boot};
    run init_fw_rd_${boot};
    run get_fdt_${boot};
    run run_kern
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


    If the user simply moves 'run run_pmmc' to just before 'run run_kern', the system will not boot.
    Since get_kern_${boot} will load the linux kernel to the same address that get_pmmc_${boot} uses, get_pmmc_${boot} needs to be modified to use a different address.  I created an environment variable called pmmcaddr to use in all of the pmmc-related commands.
    The file size will also need to be saved.  The load commands populate the variable 'filesize' at the end of every load operation.  I simply copy this value to 'pmmcsize' and use that for the pmmc operations.
    This allowed me to do load all of the necessary files from the USB drive prior to starting the pmmc, then stop USB and start the PMMC.

    These are my additions to the environment:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    args_usb=setenv bootargs console=${console} ${optargs} root=/dev/sda2 rw rootfstype=ext4 rootwait
    get_mon_usb=ext4load usb ${bootpart} ${addr_mon} ${bootdir}/${name_mon}
    init_usb=run args_all args_usb; usb start
    get_kern_usb=ext4load usb ${bootpart} ${loadaddr} ${bootdir}/${name_kern}
    init_fw_rd_usb=ext4load usb ${bootpart} ${rdaddr} ${bootdir}/${name_fw_rd}; run set_rd_spec
    get_fdt_usb=ext4load usb ${bootpart} ${fdtaddr} ${bootdir}/${name_fdt}
    bootcmd=run findfdt; run envboot; run init_${boot}; run get_mon_${boot} run_mon; run set_name_pmmc get_pmmc_${boot}; run get_kern_${boot}; run init_fw_rd_${boot}; run get_fdt_${boot}; if test ${boot} = usb; then usb stop; fi; run run_pmmc; run run_kern
    get_pmmc_usb=ext4load usb ${bootpart} ${pmmcaddr} ${bootdir}/${name_pmmc}; setenv pmmcsize ${filesize}
    run_pmmc=rproc init; rproc list; rproc load ${dev_pmmc} ${pmmcaddr} 0x${pmmcsize}; rproc start ${dev_pmmc}
    pmmcaddr=0x88100000
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    With the information in this post and my other one, this should allow a customer to boot Linux from a USB flash drive.

    Much thanks to the TI folks who looked into this for me.