LAN969x USB

1. LAN969x USB

1.1. SoC Resources

LAN969x SoC supports one instance of the Dual Role SuperSpeed DesignWare USB3 controller.

The current implementation supports only the host mode part of dual role

1.2. Kernel configurations

Following kernel config options should be enabled to use LAN969x USB in host mode:

1.3. Devicetree Configuration

To enable LAN969x USB, following configurations are required in device tree:

  • compatible string must be set to "microchip,lan966x-dwc3"

  • clocks property must be set to <&clks GCK_GATE_USB_DRD>

  • #address-cells must be set to 1.

  • #size-cells must be set to 1.

  • ranges property must be present (this is an empty address translation)

  • This node must have a child node that specifies the glue for the DWC3 controller:

    • compatible string must be set to = "snps,dwc3".

    • reg property must be set to <0x300000 0x80000>.

    • interrupts must be set to <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>

      Peripheral Shared Peripheral Interrupt CPU INTR

      USB Host

      112

      80

    • dr_mode property must be set to "host".

    • clocks property must be set to <&clks GCK_ID_USB_REFCLK>.

    • clock-names property must be set to "ref".

    • assigned-clocks property must be set to <&clks GCK_ID_USB_REFCLK>.

    • assigned-clock-rates property must be set to 60Mhz.

    • maximum-speed property must be set to "full-speed".

    • phy-names property must be set to "usb2-phy" and "usb3-phy"

1.3.2. Example

Following example shows how the LAN969x USB controller should be defined in DT:

dtsi file:

usb0: usb@300000 {
	compatible = "microchip,lan966x-dwc3";
	clocks = <&clks GCK_GATE_USB_DRD>;
	#address-cells = <1>; #size-cells = <1>;
	ranges;
	status = "disabled";

	usb0_dwc3: usb_dwc3@300000 {
		compatible = "snps,dwc3";
		reg = <0x300000 0x80000>;
		interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
		dr_mode = "host";
		clocks = <&clks GCK_ID_USB_REFCLK>;
		clock-names = "ref";
		assigned-clocks = <&clks GCK_ID_USB_REFCLK>;
		assigned-clock-rates = <60000000>;
		maximum-speed = "full-speed";
		phy-names = "usb2-phy", "usb3-phy";
		status = "okay";
	};
};

dts file changes:

&gpio {
	usb_ulpi_pins: usb-ulpi-pins {
		pins = "GPIO_30", "GPIO_31", "GPIO_32", "GPIO_33",
		"GPIO_34", "GPIO_35", "GPIO_36", "GPIO_37",
		"GPIO_38", "GPIO_39", "GPIO_40", "GPIO_41";
		function = "usb_ulpi";
	};

	usb_rst_pins: usb-rst-pins {
		pins = "GPIO_12";
		function = "usb2phy_rst";
	};

	usb_over_pins: usb-over-pins {
		pins = "GPIO_13";
		function = "usb_over_detect";
	};

	usb_power_pins: usb-power-pins {
		pins = "GPIO_1";
		function = "usb_power";
	};
};

&usb0 {
	status = "okay";
	pinctrl-0 = <&usb_ulpi_pins>, <&usb_rst_pins>, <&usb_over_pins>, <&usb_power_pins>;
	pinctrl-names = "default";
};

1.4. UserSpace

You can verify that the driver is available by checking its mode setting is host, like this:

# cat /sys/kernel/debug/usb/300000.usb_dwc3/mode
host

1.4.1. Mounting a USB Memory Stick

Prepare a USB Memory Stick with the following properties on a Linux Desktop:

  • Make a GTP partition table

  • Add one primary partition using the EXT4 filesystem that uses the total USB Memory Stick space.

  • Format the partition with the ext4 formatter

  • Mount the partition on an empty folder

  • Copy a wellknown file to the mounted folder

  • Sync and unmount the folder

Now insert the memory stick in the USB vertical USB port on the EVB-LAN9696-24port and check the logs:

# [  382.641801] usb 1-1: new high-speed USB device number 2 using xhci-hcd
[  382.790319] usb 1-1: New USB device found, idVendor=0781, idProduct=5406, bcdDevice= 2.00
[  382.795753] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  382.803050] usb 1-1: Product: U3 Cruzer Micro
[  382.807278] usb 1-1: Manufacturer: SanDisk
[  382.811348] usb 1-1: SerialNumber: 194253029D50FD3B
[  382.817444] usb-storage 1-1:1.0: USB Mass Storage device detected
[  382.822855] scsi host0: usb-storage 1-1:1.0
[  383.842201] scsi 0:0:0:0: Direct-Access     SanDisk  Cruzer           8.02 PQ: 0 ANSI: 0 CCS
[  383.848550] sd 0:0:0:0: [sda] Media removed, stopped polling
[  383.854874] sd 0:0:0:0: [sda] Attached SCSI removable disk

# ls /dev/sda*
/dev/sda

The log announces a new sda block device which is visible in the /dev folder, but what about the partition that we created?

Here you need to probe the new device for partition information:

# partprobe /dev/sda
[  405.656881] sd 0:0:0:0: [sda] 15704063 512-byte logical blocks: (8.04 gb/7.49 gib)
[  405.662434] sda: detected capacity change from 0 to 15704063
[  405.693476]  sda: sda1
[  405.720651]  sda: sda1

Partprobe informs the kernel about the partitions on device sda which in turn causes the kernel to create a new sda1 block device that represents the first (and only) partition on sda.

On Desktop Linux systems it is common to run the udev daemon that takes care of retrieving partition information and spawn process that will mount these partitions with specific user rights, but udev and its helpers are not always available on embedded systems.

Now you can mount the sda1 partition to access the file that was stored there:

# mount /dev/sda1 /mnt/
[  421.617746] EXT4-fs (sda1): mounted filesystem with ordered data mode. Quota mode: disabled.
# ls -l /mnt/
total 28K
drwxr-xr-x    3 root     root        4.0K Dec 12  2023 .
drwxr-xr-x   19 1001     1001        4.0K Sep 28  2023 ..
drwx------    2 root     root       16.0K Dec 12  2023 lost+found
-rw-r--r--    1 root     root        1.4K Dec 12  2023 main.rs
# cat /mnt/main.rs
use std::fs::File;
use std::io::Read;
... file listing continues...
#

You can now read and write to the folder as you please, and the changes will be stored in the first partition of the USB Memory Stick.

Remember to sync and unmount the device before unplugging it.

# sync
# umount /mnt/
[  897.689004] EXT4-fs (sda1): unmounting filesystem.
#