x86_64 平台上的 Diolan DLN-2 SPI 控制器

Diolan DLN-2 SPI controller on x86_64 platform

我正在尝试在 x86_64 Linux 环境(内核版本 4.18)中使用 DLN-2 以类似的方式向用户空间提供 SPI 和 I2C 总线控制器具有 DTS/DTB 文件修改的 ARM 平台。我无法确定连接 SPI 从属设备或使用 spidev 驱动程序将设备安装到用户空间的正确方法。

内核模块加载成功,SPI 总线挂载为 spi_master。我确信芯片本身可以正常工作,因为可以成功操作 I2C (/dev/i2c-#) 和 GPIO (/dev/gpiochip#) 接口。作为参考,以下是 Linux 系统树中“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

这是启动日志的匹配部分:

[    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

USB 设备树的相关部分:

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)

此时,我假设 ACPI 补丁是正确的方法。但是,该设备没有出现在 ACPI 设备树中,或者我没有使用正确的字符串进行搜索。我假设它类似于以下内容,我从内核补丁 (https://lore.kernel.org/patchwork/patch/527210/) 中提取,该补丁似乎已从当前内核中删除。

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
                )
            })
        }

    }
}

我也尝试过 udev 规则,但我对 udev 的了解很少,所以它们可能不正确。 None 似乎成功了:

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=""

好的,现在我可以回答这个问题了。

首先,假设主机上的DSDT,即USB主机控制器摘录,看起来像这样(有些名称可能不同,有些方法可能提供也可能不提供,只是我们感兴趣的部分):

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) }
        ...
    }
}

上面重要的是端口设备(HS01SS01 等)没有 _UPC()_PLD() 方法。如果是,您将需要 override complete DSDT or upgrade it and ACPI SSDT overlays 不起作用。

假设 Diolan 设备连接到 HS02 USB 端口。在这种情况下,我们必须提供以下 ACPI 摘录,无论是在 DSDT 中还是作为 SSDT 覆盖(注意,方法 GPLD(),如果不存在,应从上面的摘录中复制):

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://docs.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)
    }
}

请注意,此示例现在是 meta-acpi 项目的一部分。

加载这些表后,我们将得到枚举的所有内容,如:

$ 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

这为我们提供了可以将我们的从属设备附加到的设备对象。因此,更详细的这部分已在以下 SO 帖子中得到解答:

  • Building a i2c device controller

请注意,有两个补丁可以在 Linux 中实现。 一种是 a599a0fb629a ("Add ACPI support for USB interface devices") and one is pending e3fadb35bc1b ("Allow to be enumerated via ACPI")。两者都将在 v5.7-rc1 中。