diff --git a/allwinner/H6_pins.json b/allwinner/H6_pins.json new file mode 100644 index 0000000..2a93d37 --- /dev/null +++ b/allwinner/H6_pins.json @@ -0,0 +1,578 @@ +[ + { + "Name": "PC0", + "function2": "NAND_WE", + "function3": "SDC2_DS", + "function4": "SPI0_CLK", + "function5": "", + "function6": "PC_EINT0" + }, + { + "Name": "PC1", + "function2": "NAND_ALE", + "function3": "SDC2_RST", + "function4": "", + "function5": "", + "function6": "PC_EINT1" + }, + { + "name": "PC2", + "function2": "NAND_CLE", + "function3": "", + "function4": "SPI0_MOSI", + "function5": "", + "function6": "PC_EINT2" + }, + { + "name": "PC3", + "function2": "NAND_CE1", + "function3": "", + "function4": "SPI0_CS0", + "function5": "BOOT_SEL1", + "function6": "PC_EINT3" + }, + { + "name": "PC4", + "function2": "NAND_CE0", + "function3": "", + "function4": "SPI0_MISO", + "function5": "BOOT_SEL2", + "function6": "PC_EINT4" + }, + { + "name": "PC5", + "function2": "NAND_RE", + "function3": "SDC2_CLK", + "function4": "", + "function5": "BOOT_SEL3", + "function6": "PC_EINT5" + }, + { + "name": "PC6", + "function2": "NAND_RB0", + "function3": "SDC2_CMD", + "function4": "", + "function5": "BOOT_SEL4", + "function6": "PC_EINT6" + }, + { + "name": "PC7", + "function2": "NAND_RB1", + "function3": "", + "function4": "SPI0_CS1", + "function5": "", + "function6": "PC_EINT7" + }, + { + "name": "PC8", + "function2": "NAND_DQ7", + "function3": "SDC2_D3", + "function4": "", + "function5": "", + "function6": "PC_EINT8" + }, + { + "name": "PC9", + "function2": "NAND_DQ6", + "function3": "SDC2_D4", + "function4": "", + "function5": "", + "function6": "PC_EINT9" + }, + { + "name": "PC10", + "function2": "NAND_DQ5", + "function3": "SDC2_D0", + "function4": "", + "function5": "", + "function6": "PC_EINT10" + }, + { + "name": "PC11", + "function2": "NAND_DQ4", + "function3": "SDC2_D5", + "function4": "", + "function5": "", + "function6": "PC_EINT11" + }, + { + "name": "PC12", + "function2": "NAND_DQS", + "function3": "", + "function4": "", + "function5": "", + "function6": "PC_EINT12" + }, + { + "name": "PC13", + "function2": "NAND_DQ3", + "function3": "SDC2_D1", + "function4": "", + "function5": "", + "function6": "PC_EINT13" + }, + { + "name": "PC14", + "function2": "NAND_DQ2", + "function3": "SDC2_D6", + "function4": "", + "function5": "", + "function6": "PC_EINT14" + }, + { + "name": "PC15", + "function2": "NAND_DQ1", + "function3": "SDC2_D2", + "function4": "SPI0_WP", + "function5": "", + "function6": "PC_EINT15" + }, + { + "name": "PC16", + "function2": "NAND_DQ0", + "function3": "SDC2_D7", + "function4": "SPI0_HOLD", + "function5": "", + "function6": "PC_EINT16" + }, + { + "name": "PF0", + "function2": "SDC0_D1", + "function3": "JTAG_MS", + "function4": "", + "function5": "", + "function6": "PF_EINT0" + }, + { + "name": "PF1", + "function2": "SDC0_D0", + "function3": "JTAG_DI", + "function4": "", + "function5": "", + "function6": "PF_EINT1" + }, + { + "name": "PF2", + "function2": "SDC0_CLK", + "function3": "UART0_TX", + "function4": "", + "function5": "", + "function6": "PF_EINT2" + }, + { + "name": "PF3", + "function2": "SDC0_CMD", + "function3": "JTAG_DO", + "function4": "", + "function5": "", + "function6": "PF_EINT3" + }, + { + "name": "PF4", + "function2": "SDC0_D3", + "function3": "UART0_RX", + "function4": "", + "function5": "", + "function6": "PF_EINT4" + }, + { + "name": "PF5", + "function2": "SDC0_D2", + "function3": "JTAG_CK", + "function4": "", + "function5": "", + "function6": "PF_EINT5" + }, + { + "name": "PF6", + "function2": "", + "function3": "", + "function4": "", + "function5": "", + "function6": "PF_EINT6" + }, + { + "name": "PG0", + "function2": "SDC1_CLK", + "function3": "", + "function4": "", + "function5": "", + "function6": "PG_EINT0" + }, + { + "name": "PG1", + "function2": "SDC1_CMD", + "function3": "", + "function4": "", + "function5": "", + "function6": "PG_EINT1" + }, + { + "name": "PG2", + "function2": "SDC1_D0", + "function3": "", + "function4": "", + "function5": "", + "function6": "PG_EINT2" + }, + { + "name": "PG3", + "function2": "SDC1_D1", + "function3": "", + "function4": "", + "function5": "", + "function6": "PG_EINT3" + }, + { + "name": "PG4", + "function2": "SDC1_D2", + "function3": "", + "function4": "", + "function5": "", + "function6": "PG_EINT4" + }, + { + "name": "PG5", + "function2": "SDC1_D3", + "function3": "", + "function4": "", + "function5": "", + "function6": "PG_EINT5" + }, + { + "name": "PG6", + "function2": "UART1_TX", + "function3": "", + "function4": "JTAG_MS", + "function5": "", + "function6": "PG_EINT6" + }, + { + "name": "PG7", + "function2": "UART1_RX", + "function3": "", + "function4": "JTAG_CK", + "function5": "", + "function6": "PG_EINT7" + }, + { + "name": "PG8", + "function2": "UART1_RTS", + "function3": "PLL_LOCK_DBG", + "function4": "JTAG_DO", + "function5": "", + "function6": "PG_EINT8" + }, + { + "name": "PG9", + "function2": "UART1_CTS", + "function3": "", + "function4": "JTAG_DI", + "function5": "", + "function6": "PG_EINT9" + }, + { + "name": "PG10", + "function2": "H_I2S2_MCLK", + "function3": "X32KFOUT", + "function4": "", + "function5": "", + "function6": "PG_EINT10" + }, + { + "name": "PG11", + "function2": "H_I2S2_BCLK", + "function3": "", + "function4": "BIST_RESULT0", + "function5": "", + "function6": "PG_EINT11" + }, + { + "name": "PG12", + "function2": "H_I2S2_LRCK", + "function3": "", + "function4": "BIST_RESULT1", + "function5": "", + "function6": "PG_EINT12" + }, + { + "name": "PG13", + "function2": "H_I2S2_DOUT0", + "function3": "H_I2S2_DIN1", + "function4": "BIST_RESULT2", + "function5": "", + "function6": "PG_EINT13" + }, + { + "name": "PG14", + "function2": "H_I2S2_DIN0", + "function3": "H_I2S2_DOUT1", + "function4": "BIST_RESULT3", + "function5": "", + "function6": "PG_EINT14" + }, + { + "name": "PG15", + "function2": "UART2_TX", + "function3": "", + "function4": "", + "function5": "TWI4_SCK", + "function6": "PG_EINT15" + }, + { + "name": "PG16", + "function2": "UART2_RX", + "function3": "", + "function4": "", + "function5": "TWI4_SDA", + "function6": "PG_EINT16" + }, + { + "name": "PG17", + "function2": "UART2_RTS", + "function3": "", + "function4": "", + "function5": "TWI3_SCK", + "function6": "PG_EINT17" + }, + { + "name": "PG18", + "function2": "UART2_CTS", + "function3": "", + "function4": "", + "function5": "TWI3_SDA", + "function6": "PG_EINT18" + }, + { + "name": "PG19", + "function2": "", + "function3": "", + "function4": "PWM1", + "function5": "", + "function6": "PG_EINT19" + }, + { + "name": "PH0", + "function2": "UART0_TX", + "function3": "", + "function4": "PWM3", + "function5": "TWI1_SCK", + "function6": "PH_EINT0" + }, + { + "name": "PH1", + "function2": "UART0_RX", + "function3": "", + "function4": "PWM4", + "function5": "TWI1_SDA", + "function6": "PH_EINT1" + }, + { + "name": "PH2", + "function2": "UART5_TX", + "function3": "OWA_MCLK", + "function4": "PWM2", + "function5": "TWI2_SCK", + "function6": "PH_EINT2" + }, + { + "name": "PH3", + "function2": "UART5_RX", + "function3": "", + "function4": "PWM1", + "function5": "TWI2_SDA", + "function6": "PH_EINT3" + }, + { + "name": "PH4", + "function2": "", + "function3": "OWA_OUT", + "function4": "", + "function5": "TWI3_SCK", + "function6": "PH_EINT4" + }, + { + "name": "PH5", + "function2": "UART2_TX", + "function3": "H_I2S3_MCLK", + "function4": "SPI1_CS0", + "function5": "TWI3_SDA", + "function6": "PH_EINT5" + }, + { + "name": "PH6", + "function2": "UART2_RX", + "function3": "H_I2S3_BCLK", + "function4": "SPI1_CLK", + "function5": "TWI4_SCK", + "function6": "PH_EINT6" + }, + { + "name": "PH7", + "function2": "UART2_RTS", + "function3": "H_I2S3_LRCK", + "function4": "SPI1_MOSI", + "function5": "TWI4_SDA", + "function6": "PH_EINT7" + }, + { + "name": "PH8", + "function2": "UART2_CTS", + "function3": "H_I2S3_DOUT0", + "function4": "SPI1_MISO", + "function5": "H_I2S3_DIN1", + "function6": "PH_EINT8" + }, + { + "name": "PH9", + "function2": "", + "function3": "H_I2S3_DIN0", + "function4": "SPI1_CS1", + "function5": "H_I2S3_DOUT1", + "function6": "PH_EINT9" + }, + { + "name": "PH10", + "function2": "", + "function3": "IR_RX", + "function4": "TCON_TRIG1", + "function5": "", + "function6": "PH_EINT10" + }, + { + "name": "PI0", + "function2": "RGMII_RXD3", + "function3": "DMIC_CLK", + "function4": "H_I2S0_MCLK", + "function5": "HDMI_SCL", + "function6": "PI_EINT0" + }, + { + "name": "PI1", + "function2": "RGMII_RXD2", + "function3": "DMIC_DATA0", + "function4": "H_I2S0_BCLK", + "function5": "HDMI_SDA", + "function6": "PI_EINT1" + }, + { + "name": "PI2", + "function2": "RGMII_RXD1", + "function3": "DMIC_DATA1", + "function4": "H_I2S0_LRCK", + "function5": "HDMI_CEC", + "function6": "PI_EINT2" + }, + { + "name": "PI3", + "function2": "RGMII_RXD0", + "function3": "DMIC_DATA2", + "function4": "H_I2S0_DOUT0", + "function5": "H_I2S0_DIN1", + "function6": "PI_EINT3" + }, + { + "name": "PI4", + "function2": "RGMII_RXCK/", + "function3": "DMIC_DATA3", + "function4": "H_I2S0_DIN0", + "function5": "H_I2S0_DOUT1", + "function6": "PI_EINT4" + }, + { + "name": "PI5", + "function2": "RGMII_RXCTL", + "function3": "UART2_TX", + "function4": "TS0_CLK", + "function5": "TWI0_SCK", + "function6": "PI_EINT5" + }, + { + "name": "PI6", + "function2": "RGMII_NULL", + "function3": "UART2_RX", + "function4": "TS0_ERR", + "function5": "TWI0_SDA", + "function6": "PI_EINT6" + }, + { + "name": "PI7", + "function2": "RGMII_TXD3", + "function3": "UART2_RTS", + "function4": "TS0_SYNC", + "function5": "TWI1_SCK", + "function6": "PI_EINT7" + }, + { + "name": "PI8", + "function2": "RGMII_TXD2", + "function3": "UART2_CTS", + "function4": "TS0_DVLD", + "function5": "TWI1_SDA", + "function6": "PI_EINT8" + }, + { + "name": "PI9", + "function2": "RGMII_TXD1", + "function3": "UART3_TX", + "function4": "TS0_D0", + "function5": "TWI2_SCK", + "function6": "PI_EINT9" + }, + { + "name": "PI10", + "function2": "RGMII_TXD0", + "function3": "UART3_RX", + "function4": "TS0_D1", + "function5": "TWI2_SDA", + "function6": "PI_EINT10" + }, + { + "name": "PI11", + "function2": "RGMII_TXCK", + "function3": "UART3_RTS", + "function4": "TS0_D2", + "function5": "PWM1", + "function6": "PI_EINT11" + }, + { + "name": "PI12", + "function2": "RGMII_TXCTL", + "function3": "UART3_CTS", + "function4": "TS0_D3", + "function5": "PWM2", + "function6": "PI_EINT12" + }, + { + "name": "PI13", + "function2": "RGMII_CLKIN", + "function3": "UART4_TX", + "function4": "TS0_D4", + "function5": "PWM3", + "function6": "PI_EINT13" + }, + { + "name": "PI14", + "function2": "MDC", + "function3": "UART4_RX", + "function4": "TS0_D5", + "function5": "PWM4", + "function6": "PI_EINT14" + }, + { + "name": "PI15", + "function2": "MDIO", + "function3": "UART4_RTS", + "function4": "TS0_D6", + "function5": "CLK_FANOUT0", + "function6": "PI_EINT15" + }, + { + "name": "PI16", + "function2": "EPHY_25M", + "function3": "UART4_CTS", + "function4": "TS0_D7", + "function5": "CLK_FANOUT1", + "function6": "PI_EINT16" + } +] \ No newline at end of file diff --git a/allwinner/address.go b/allwinner/address.go new file mode 100644 index 0000000..3fe6dc7 --- /dev/null +++ b/allwinner/address.go @@ -0,0 +1,106 @@ +// Copyright 2024 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +import ( + "errors" + "os" + "path" + "regexp" + "strconv" + "strings" +) + +// getBaseAddress queries the virtual file system to retrieve the base address +// of the GPIO registers for GPIO pins in groups PA to PI. +// +// Defaults to 0x01C20800 as per datasheet if it could not query the file +// system. +func getBaseAddress() (uint64, error) { + driverDir := "/sys/bus/platform/drivers" + if IsH6() { + return getBaseAddressForH6CPU(driverDir) + } + return getDefaultBaseAddress(driverDir), nil +} + +func getDefaultBaseAddress(driverDir string) uint64 { + base := uint64(0x01C20800) + link, err := os.Readlink(path.Join(driverDir, "sun50i-pinctrl/driver")) + if err != nil { + return base + } + parts := strings.SplitN(path.Base(link), ".", 2) + if len(parts) != 2 { + return base + } + base2, err := strconv.ParseUint(parts[0], 16, 64) + if err != nil { + return base + } + return base2 +} + +func getBaseAddressForH6CPU(driverDir string) (uint64, error) { + items, err := os.ReadDir(driverDir) + if err != nil { + return 0, err + } + return getBaseAddressFromDirItemsForH6CPU(driverDir, items) +} + +func getBaseAddressFromDirItemsForH6CPU(root string, items []os.DirEntry) (uint64, error) { + for _, item := range items { + if ret, ok := getBaseAddressFromDirItemForH6CPU(root, item); ok { + return ret, nil + } + + } + return 0, errors.New("file with base address not found") +} + +func getBaseAddressFromDirItemForH6CPU(root string, item os.DirEntry) (uint64, bool) { + if !item.IsDir() { + return 0, false + } + + if matched, _ := regexp.MatchString(`^sun50i-h6\d*-pinctrl$`, item.Name()); !matched { + return 0, false + } + + return extractBaseAddressFromDriverDirForH6CPU(path.Join(root, item.Name())) +} + +func extractBaseAddressFromDriverDirForH6CPU(dir string) (uint64, bool) { + if fileInfo, err := os.Stat(dir); err != nil || !fileInfo.IsDir() { + return 0, false + } + items, err := os.ReadDir(dir) + if err != nil { + return 0, false + } + for _, item := range items { + if address, ok := extractBaseAddress(item); ok { + return address, ok + } + } + return 0, false +} + +func extractBaseAddress(item os.DirEntry) (uint64, bool) { + if item.IsDir() { + return 0, false + } + if !strings.HasSuffix(item.Name(), ".pinctrl") { + return 0, false + } + prefix := item.Name()[:len(item.Name())-len(".pinctrl")] + if address, err := strconv.ParseUint(prefix, 16, 64); err != nil { + return 0, false + } else { + return address, true + } + +} diff --git a/allwinner/address_test.go b/allwinner/address_test.go new file mode 100644 index 0000000..8d524a1 --- /dev/null +++ b/allwinner/address_test.go @@ -0,0 +1,92 @@ +// Copyright 2024 The Periph Authors. All rights reserved. +// Use of this source code is governed under the Apache License, Version 2.0 +// that can be found in the LICENSE file. + +package allwinner + +import ( + "os" + "path" + "testing" +) + +func createDirs(t *testing.T, root string, dirs ...string) string { + for _, dir := range dirs { + if err := os.MkdirAll(path.Join(root, dir), os.ModePerm); err != nil { + t.Fatal(err) + } + } + return root +} + +func createFiles(t *testing.T, root string, paths ...string) string { + for _, path_ := range paths { + if file, err := os.Create(path.Join(root, path_)); err != nil { + t.Fatal(err) + } else { + file.Close() + } + } + return root +} + +func createSymLink(t *testing.T, root string, source string, destination string) { + + if err := os.Symlink(path.Join(root, source), path.Join(root, destination)); err != nil { + t.Fatal(err) + } +} + +func TestGetDefaultBaseAddress_default(t *testing.T) { + want := uint64(0x01C20800) + if address := getDefaultBaseAddress("/dev/null"); address != want { + t.Errorf("Expected %d received %d", want, address) + } +} + +func TestGetDefaultBaseAddress(t *testing.T) { + root := t.TempDir() + createDirs(t, + root, + "sun50i-pinctrl/bin", + "foo", + ) + createFiles(t, root, "foo/300b000.pinctrl") + createSymLink(t, root, "foo/300b000.pinctrl", "sun50i-pinctrl/driver") + want := uint64(0x300b000) + if address := getDefaultBaseAddress(root); address != want { + t.Errorf("Expected %d received %d", want, address) + } +} + +func TestGetBaseAddressForH6CPU(t *testing.T) { + root := t.TempDir() + createDirs(t, + root, + "sun50i-h6-pinctrl/bin", + "sun50i-h6-pinctrl/uevent", + "sun50i-h6-pinctrl/ubind", + "sun50i-h616-pinctrl/ubind", + "sun50i-h616-pinctrl/uevent", + "sun50i-h616-pinctrl/bin", + ) + createFiles(t, root, "sun50i-h616-pinctrl/300b000.pinctrl") + if val, err := getBaseAddressForH6CPU(root); err != nil { + t.Error(err) + } else if val != uint64(0x300b000) { + t.Fail() + } +} + +func TestGetBaseAddressForH6CPU_default(t *testing.T) { + root := t.TempDir() + createDirs(t, + root, + "sun50i-h6-pinctrl/bin", + "sun50i-h6-pinctrl/uevent", + "sun50i-h6-pinctrl/ubind", + ) + if _, err := getBaseAddressForH6CPU(root); err == nil { + t.Fail() + } +} diff --git a/allwinner/detect.go b/allwinner/detect.go index c804212..13fde23 100644 --- a/allwinner/detect.go +++ b/allwinner/detect.go @@ -60,6 +60,14 @@ func IsH5() bool { return detection.isH5 } +// IsH6 detects whether the host CPU is an Allwinner H6 CPU. +// +// It looks for the string "sun50i-h6" in /proc/device-tree/compatible. +func IsH6() bool { + detection.do() + return detection.isH6 +} + // type detectionS struct { @@ -71,6 +79,7 @@ type detectionS struct { isA64 bool isH3 bool isH5 bool + isH6 bool } var detection detectionS @@ -104,8 +113,11 @@ func (d *detectionS) do() { if strings.Contains(c, "sun50i-h5") { d.isH5 = true } + if strings.Contains(c, "sun50i-h6") { + d.isH6 = true + } } - d.isAllwinner = d.isA64 || d.isR8 || d.isA20 || d.isH3 || d.isH5 + d.isAllwinner = d.isA64 || d.isR8 || d.isA20 || d.isH3 || d.isH5 || d.isH6 if !d.isAllwinner { // The kernel in the image that comes pre-installed on the pcDuino3 Nano diff --git a/allwinner/gpio.go b/allwinner/gpio.go index b8c4946..9b155aa 100644 --- a/allwinner/gpio.go +++ b/allwinner/gpio.go @@ -11,7 +11,6 @@ import ( "errors" "fmt" "os" - "path" "strconv" "strings" "time" @@ -309,7 +308,8 @@ func (p *Pin) Read() gpio.Level { // // This function is very fast. func (p *Pin) FastRead() gpio.Level { - return gpio.Level(drvGPIO.gpioMemory.groups[p.group].data&(1<