Skip to content
Advertisement

Diolan DLN-2 SPI controller on x86_64 platform

I am attempting to utilize the DLN-2 in an x86_64 Linux environment (kernel version 4.18) to provide SPI and I2C bus controllers to the userspace, in a similar manner you would using an ARM platform with DTS/DTB file modifications. I am having trouble identifying the proper method to attach a SPI slave device or mount the device to userspace with the spidev driver.

The kernel modules are loading successfully and the SPI bus is mounted as a spi_master. I am certain the chip itself is working because the I2C (/dev/i2c-#) and GPIO (/dev/gpiochip#) interfaces can be successfully manipulated. For reference, here is a list of all references in the Linux system tree for “dln”:

# find /sys -name *dln*
/sys/devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2:1.0/dln2-i2c.1.auto
/sys/devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2:1.0/dln2-spi.2.auto
/sys/devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2:1.0/dln2-gpio.0.auto
/sys/fs/selinux/booleans/minidlna_read_generic_user_content
/sys/bus/platform/devices/dln2-i2c.1.auto
/sys/bus/platform/devices/dln2-spi.2.auto
/sys/bus/platform/devices/dln2-gpio.0.auto
/sys/bus/platform/drivers/dln2-gpio
/sys/bus/platform/drivers/dln2-gpio/dln2-gpio.0.auto
/sys/bus/platform/drivers/dln2-adc
/sys/bus/platform/drivers/dln2-spi
/sys/bus/platform/drivers/dln2-spi/dln2-spi.2.auto
/sys/bus/platform/drivers/dln2-i2c
/sys/bus/platform/drivers/dln2-i2c/dln2-i2c.1.auto
/sys/bus/usb/drivers/dln2
/sys/module/i2c_dln2
/sys/module/i2c_dln2/drivers/platform:dln2-i2c
/sys/module/industrialio_triggered_buffer/holders/dln2_adc
/sys/module/spi_dln2
/sys/module/spi_dln2/drivers/platform:dln2-spi
/sys/module/industrialio/holders/dln2_adc
/sys/module/dln2_adc
/sys/module/dln2_adc/drivers/platform:dln2-adc
/sys/module/gpio_dln2
/sys/module/gpio_dln2/drivers/platform:dln2-gpio
/sys/module/dln2
/sys/module/dln2/holders/i2c_dln2
/sys/module/dln2/holders/spi_dln2
/sys/module/dln2/holders/dln2_adc
/sys/module/dln2/holders/gpio_dln2
/sys/module/dln2/drivers/usb:dln2

Here is the matching portion of the boot log:

[    1.578110] usb 1-2: new full-speed USB device number 2 using xhci_hcd
[    1.705306] usb 1-2: New USB device found, idVendor=a257, idProduct=2013, bcdDevice= 1.00
[    1.705310] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[    1.705312] usb 1-2: Product: Diolan DLN2
[    1.705314] usb 1-2: Manufacturer: Diolan
[   10.485997] dln2 1-2:1.0: Diolan DLN2 serial 33632920
[   10.486182] usbcore: registered new interface driver dln2

And the relevant portion of the usb device tree:

Bus 001 Device 002: ID a257:2013
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass          255 Vendor Specific Class
  bDeviceSubClass       255 Vendor Specific Subclass
  bDeviceProtocol       255 Vendor Specific Protocol
  bMaxPacketSize0        64
  idVendor           0xa257
  idProduct          0x2013
  bcdDevice            1.00
  iManufacturer           1 Diolan
  iProduct                2 Diolan DLN2
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0020
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    255 Vendor Specific Subclass
      bInterfaceProtocol    255 Vendor Specific Protocol
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Device Status:     0x0000
  (Bus Powered)

At this point, I am assuming that an ACPI patch is the correct method. however, the device does not appear in the ACPI device tree, or I am not searching with the correct string. I am assuming it will be similar to the following, which I pulled from a kernel patch (https://lore.kernel.org/patchwork/patch/527210/) that appears to have since been removed from the current kernel.

DefinitionBlock ("dln2.aml", "SSDT", 1, "INTEL", "CpuDptf", 3)
{
    Device (DLN0)
    {
        Name (_ADR, Zero)
        Name (_HID, "DLN2000")

        Device (TP40) {
            Name (_HID, "SPT0001")
            Name (_DDN, "SPI4-CS0")
            Name (_CRS, ResourceTemplate () {
                SpiSerialBus (
                    1,                      // Chip select
                    PolarityLow,            // Chip select is active low
                    FourWireMode,           // Full duplex
                    8,                      // Bits per word is 8 (byte)
                    ControllerInitiated,    // Don't care
                    1000000,                // 1 MHz
                    ClockPolarityLow,       // SPI mode 0
                    ClockPhaseFirst,        // SPI mode 0
                    "\DLN0.SPI0",           // SPI host controller
                    0                       // Must be 0
                )
            })
        }

    }
}

I have also tried udev rules, but my knowledge of udev is slim so they are likely incorrect. None of these appeared to have done the trick:

DEVPATH=="/devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2:1.0/dln2-spi.2.auto/spi_master/spi0", DRIVER="spidev"

DEVPATH=="/devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2:1.0/dln2-spi.2.auto/spi_master/spi0", KERNEL="spi-SPT0001:02", SUBSYSTEM="spi", DRIVER="spidev", ATTRS{driver_override}==""

DEVPATH=="/devices/pci0000:00/0000:00:15.0/usb1/1-2/1-2:1.0/dln2-spi.2.auto/spi_master/spi0/spi-SPT0001:02", KERNEL="spidev2.0", SUBSYSTEM="spidev", DRIVER=""

Advertisement

Answer

Okay, now I’m able to answer to the question.

First of all, assume that DSDT on the host machine, i.e. USB host controller excerpt, looks like this (some names maybe different, some methods may or may not be provided, just interesting us part):

Device (XHC)
{   
    Name (_ADR, 0x00110000)

    ...

    Device (RHUB)
    {   
        Name (_ADR, Zero)

        // GPLD: Generate Port Location Data (PLD)
        Method (GPLD, 1, Serialized) {
            Name (PCKG, Package () {
                Buffer (0x10) {}
            })

            // REV: Revision 0x02 for ACPI 5.0
            CreateField (DerefOf (Index (PCKG, Zero)), Zero, 0x07, REV)
            Store (0x02, REV)

            // VISI: Port visibility to user per port
            CreateField (DerefOf (Index (PCKG, Zero)), 0x40, One, VISI)
            Store (Arg0, VISI)
            Return (PCKG)
        }

        Device (HS01) { Name (_ADR, 1) }
        Device (HS02) { Name (_ADR, 2) }
        Device (SS01) { Name (_ADR, 3) }
        Device (SS02) { Name (_ADR, 4) }
        ...
    }
}

Important thing above is that port devices (HS01, SS01, etc) does not have _UPC() or _PLD() methods. If they are, you will need to override complete DSDT or upgrade it and ACPI SSDT overlays won’t work.

Assume that Diolan device is connected to HS02 USB port. In this case we have to provide the following ACPI excerpt, either in DSDT or as SSDT overlay (Note, method GPLD(), if not present, should be copied from above excerpt):

External (_SB.PCI0.XHC.RHUB.HS02, DeviceObj)
External (_SB.PCI0.XHC.RHUB.GPLD, MethodObj)

/*
 * We set the port to hard wired state to get the connected device
 * enumerated properly. See more details here:
 * https://learn.microsoft.com/en-us/windows-hardware/drivers/bringup/other-acpi-namespace-objects#acpi-namespace-hierarchy-and-_adr-for-embedded-usb-devices
 */

Scope (_SB.PCI0.XHC.RHUB.HS02)
{
    Name (_UPC, Package ()
    {
        0xFF,
        0xFF,
        Zero,
        Zero,
    })

    Method (_PLD, 0, NotSerialized)
    {
            Return (GPLD (Zero))
    }

    Device (GPIO)
    {
        Name (_ADR, Zero)
        Name (_STA, 0x0F)
    }

    Device (I2C)
    {
        Name (_ADR, One)
        Name (_STA, 0x0F)
    }

    Device (SPI)
    {
        Name (_ADR, 0x02)
        Name (_STA, 0x0F)
    }
}

Note, this example now is a part of meta-acpi project.

After loading these tables we will get everything enumerated like:

$ grep -H HS02 /sys/bus/acpi/devices/device:*/path
/sys/bus/acpi/devices/device:09/path:_SB_.PCI0.XHC.RHUB.HS02
/sys/bus/acpi/devices/device:0e/path:_SB_.PCI0.XHC.RHUB.HS02.GPIO
/sys/bus/acpi/devices/device:0f/path:_SB_.PCI0.XHC.RHUB.HS02.I2C_
/sys/bus/acpi/devices/device:10/path:_SB_.PCI0.XHC.RHUB.HS02.SPI_

$ ls -l /sys/bus/platform/devices/dln2-*/firmware_node
lrwxrwxrwx    1 root     root             0 Jan  1 00:04 /sys/bus/platform/devices/dln2-gpio.2.auto/firmware_node -> ../../../../../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:07/device:08/device:09/device:0e
lrwxrwxrwx    1 root     root             0 Jan  1 00:04 /sys/bus/platform/devices/dln2-i2c.3.auto/firmware_node -> ../../../../../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:07/device:08/device:09/device:0f
lrwxrwxrwx    1 root     root             0 Jan  1 00:04 /sys/bus/platform/devices/dln2-spi.4.auto/firmware_node -> ../../../../../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:07/device:08/device:09/device:10

This gets us the device object we can attach our slave devices to. So, more detailed this part has been answered in the following SO posts:

Note, there are two patches to make it possible in Linux. One is a599a0fb629a (“Add ACPI support for USB interface devices”) and one is pending e3fadb35bc1b (“Allow to be enumerated via ACPI”). Both will be in v5.7-rc1.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement