Diolan Dln-2 Spi Controller on X86_64 Platform

Diolan DLN-2 SPI controller on x86_64 platform

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:

  • adding i2c client devices on x86_64
  • Building a i2c device controller
  • spidev Linux driver on Intel Atom board

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.

adding i2c client devices on x86_64

Since you have an ACPI-enabled platform the best approach is to provide the ASL excerpts for given devices.

Because of Intel Galileo platform for IoT the Atmel 24 series EEPROM has got its own ACPI ID and an excerpt will be simple:

DefinitionBlock ("at24.aml", "SSDT", 5, "", "AT24", 1)
{
External (_SB_.PCI0.I2C2, DeviceObj)

Scope (\_SB.PCI0.I2C2)
{
Device (EEP0) {
Name (_HID, "INT3499")
Name (_DDN, "Atmel AT24 compatible EEPROM")
Name (_CRS, ResourceTemplate () {
I2cSerialBusV2 (
0x0057, // I2C Slave Address
ControllerInitiated,
400000, // Bus speed
AddressingMode7Bit,
"\\_SB.PCI0.I2C2", // Link to ACPI I2C host controller
0
)
})

Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () {"size", 1024},
Package () {"pagesize", 32},
}
})
}
}
}

Note, the size property is being added in a pending patch series (patches add eeprom "size" property and add support to fetch eeprom device property "size").

Note, the address width is 8-bit as hard coded for now. In case you need to have 16-bit you need to create a similar patches as mentioned above.

For LTC2990 power monitor you need the following excerpt:

DefinitionBlock ("ltc2990.aml", "SSDT", 5, "", "PMON", 1)
{
External (\_SB_.PCI0.I2C2, DeviceObj)

Scope (\_SB.PCI0.I2C2)
{
Device (PMON)
{
Name (_HID, "PRP0001")
Name (_DDN, "Linear Technology LTC2990 power monitor")
Name (_CRS, ResourceTemplate () {
I2cSerialBus (
0x4c, // Bus address
ControllerInitiated, // Don't care
400000, // Fast mode (400 kHz)
AddressingMode7Bit, // 7-bit addressing
"\\_SB.PCI0.I2C2", // I2C host controller
0 // Must be 0
)
})

Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () {"compatible", "lltc,ltc2990"},
}
})
}
}
}

Note, unfortunately there is no compatible string in the driver, so, one needs to add it like it's done here.

In the examples above \\_SB.PCI0.I2C2 is an absolute path to the I2C host controller.

How to get those files applied:

  • first of all, create a folder
mkdir -p kernel/firmware/acpi
  • save files under names mentioned in the DefinitionBlock() macro in that folder
  • create the uncompressed cpio archive and concatenate the original initrd on top:
find kernel | cpio -H newc --create > /boot/instrumented_initrd
cat /boot/initrd >> /boot/instrumented_initrd

More details are available in SSDT Overlays.

The other examples and description of the idea behind can be found on meta-acpi GitHub page, some materials from which are copied here.

spidev Linux driver on Intel Atom board

I found that Valley Island (Baytrail) BSP provides much better hardware support for Kontron SMARC than Kontron BSP. It comes with spidev and other drivers necessary to access Atom peripherals.

Not directly related to the question (just to inform those developing on SXBTI SMARC platform): there are couple of things required which are not part of this BSP: Ethernet and eMMC flash. The former can be added by enabling Intel IGB driver in kernel, the latter... I'm still trying to figure it out.

How to configure ACPI *.asl for a virtual mdio-gpio device connected to a I2C gpio expander

First of all let's see the main architecture of the design:

+-------------------+
| HOST | +------+
| MDIO <------>+ MDIO |
| Intf | | Phy |
| | +--^---+
| +------+ | | +-----+
| | I²C | | | | LED |
| | host | | | +--^--+
| +--^---+ | | |
| | | +--+---+ |
+-------------------+ | I²C | |
+----------------------> GPIO +------+
+------+

From this schematic we see how devices are related to each other. Now let's move to ACPI representation. At the beginning we need to define I²C GPIO expander. From the examples in meta-acpi project we can find how PCA9535 can be described. Assuming we found the I²C host controller device (\_SB_.PCI0.D01D as per your post) and the fact that you have expander without latching IRQ events, the following is the mix between original ASL excerpt and how to match it with proper configuration in the driver:

Device (ABC0)
{

Name (_HID, "PRP0001")
Name (_DDN, "NXP PCA9575 GPIO expander")

Name (RBUF, ResourceTemplate()
{
I2cSerialBusV2(0x0020, ControllerInitiated, 400000,
AddressingMode7Bit, "\\_SB.PCI0.D01D",
0x00, ResourceConsumer, , Exclusive, )
})

Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () {"compatible", "nxp,pca9575"},
Package () {"gpio-line-names", Package () {
"LED_Red",
"",
"MDC",
"MDIO",
}},
}
})

Method (_CRS, 0, NotSerialized)
{
Return (RBUF)
}

Method (_STA, 0, NotSerialized)
{
Return (0x0F)
}
}

This excerpt provides us a new GPIO chip in the system, resources of which can be consumed by others.

For example, in your Virtual MDIO Phy case (see _DSD Device Properties Related to GPIO as well)

Device (MD00)
{
Name (_HID, "PRP0001")

Name (_CRS, ResourceTemplate () {
GpioIo (Exclusive, PullDown, 0, 0, IoRestrictionOutputOnly,
"\\_SB.PCI0.D01D.ABC0", 0, ResourceConsumer) {2} // pin 2
GpioIo (Exclusive, PullDown, 0, 0, IoRestrictionOutputOnly,
"\\_SB.PCI0.D01D.ABC0", 0, ResourceConsumer) {3} // pin 3
})

Name (_DSD, Package() {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () { "compatible", "virtual,mdio-gpio" },
Package () {
"gpios", Package () {
^MD00, 0, 0, 0, // index 0 in _CRS -> pin 2
^MD00, 1, 0, 0, // index 1 in _CRS -> pin 3
}
},
}
})
}

Now it is a time to look at the LED binding code. For the sake of clarification hogging is when you want a GPIO provider to consume a resource itself. And it is quite likely not your case. Better is to connect this with the LED GPIO driver:

Device (LEDS)
{
Name (_HID, "PRP0001")
Name (_DDN, "GPIO LEDs device")

Name (_CRS, ResourceTemplate () {
GpioIo (
Exclusive, // Not shared
PullUp, // Default off
0, // Debounce timeout
0, // Drive strength
IoRestrictionOutputOnly, // Only used as output
"\\_SB.PCI0.D01D.ABC0", // GPIO controller
0) // Must be 0
{
0, // LED_Red
}
})

Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () { "compatible", Package() { "gpio-leds" } },
},
ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
Package () {
Package () { "led-0", "LED0" },
}
})

/*
* For more information about these bindings see:
* Documentation/devicetree/bindings/leds/common.yaml,
* Documentation/devicetree/bindings/leds/leds-gpio.yaml and
* Documentation/firmware-guide/acpi/gpio-properties.rst.
*/
Name (LED0, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () { "label", "red" },
Package () { "default-state", "on" },
Package () { "gpios", Package () { ^LEDS, 0, 0, 1 } }, // active low
}
})
}

Also you may look into similar questions (starting from the given link and there are references to the rest) on StackOverflow site.

How to get data from I2C device BH1750 over USB-I2C module in Python3?

There is final solution:

import serial
import time

ser = serial.Serial(port="/dev/ttyUSB0",
baudrate=19200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_TWO,
bytesize=serial.EIGHTBITS,
timeout=0.500,
)

ser.flushInput()
ser.flushOutput()

ADDR_BASE = 0x23 # nebo 0x5c pokud je ADDR pin HIGH
ADDR_READ = ADDR_BASE << 1 | 1
ADDR_WRITE = ADDR_BASE << 1

CMD_WRITE_1B = 0x53
CMD_READ_MULT = 0x54

LUX_RESOLUTION_1X = 0x10
LUX_RESOLUTION_4X = 0x13

# nastavení senzoru
ser.write(bytearray([CMD_WRITE_1B, ADDR_WRITE, LUX_RESOLUTION_1X]))

while True:
time.sleep(1)

# požádám převodník o 2 B
ser.write(bytearray([CMD_READ_MULT, ADDR_READ, 0x02]))

# přečtu je
raw = ser.read(2)

# interpretace bytů podle datasheetu
lx = (raw[1] << 8 | raw[0]) / 1.2

print("--> %.d lx" %(lx))


Related Topics



Leave a reply



Submit