l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
## This file states the license of this package and possibly its subpackages
## in machine and human readable format. The PackageName refers to the package
## whose license is defined by PackageLicenseConcluded.
## For more information about this file format visit the SPDX website at
## https://spdx.org
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentNamespace: spdx:kernkonzept/io-24a7fb8e-e7f5-11e8-8581-5399ab9122a8
DocumentName: io
Creator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
Created: 2018-11-13T00:00:00Z
## Package Information
PackageName: io
SPDXID: SPDXRef-io
PackageOriginator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
PackageLicenseDeclared: GPL-2.0-only
PackageLicenseConcluded: GPL-2.0-only
FilesAnalyzed: true
PackageCopyrightText: NOASSERTION
PackageDownloadLocation: NOASSERTION
PackageName: io-libpciids
SPDXID: SPDXRef-io-libpciids
PackageOriginator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
PackageLicenseDeclared: GPL-2.0-only
PackageLicenseConcluded: GPL-2.0-only
FilesAnalyzed: true
PackageCopyrightText: NOASSERTION
PackageDownloadLocation: NOASSERTION
FileName: ./io/server/libpciids/src/pci.ids
SPDXID: SPDXRef-libpciids-pci.ids
LicenseConcluded: (GPL-2.0-or-later OR BSD-3-Clause)
LicenseInfoInFile: NOASSERTION
FileCopyrightText: NOASSERTION
PackageName: io-libio
SPDXID: SPDXRef-io-libio
PackageOriginator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
PackageLicenseDeclared: GPL-2.0-only WITH GCC-exception-2.0
PackageLicenseConcluded: GPL-2.0-only WITH GCC-exception-2.0
FilesAnalyzed: false
PackageCopyrightText: NOASSERTION
PackageDownloadLocation: NOASSERTION
PackageName: io-libio-direct
SPDXID: SPDXRef-io-libio-direct
PackageOriginator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
PackageLicenseDeclared: GPL-2.0-only WITH GCC-exception-2.0
PackageLicenseConcluded: GPL-2.0-only WITH GCC-exception-2.0
FilesAnalyzed: false
PackageCopyrightText: NOASSERTION
PackageDownloadLocation: NOASSERTION
PackageName: io-libio-io
SPDXID: SPDXRef-io-libio-io
PackageOriginator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
PackageLicenseDeclared: GPL-2.0-only WITH GCC-exception-2.0
PackageLicenseConcluded: GPL-2.0-only WITH GCC-exception-2.0
FilesAnalyzed: false
PackageCopyrightText: NOASSERTION
PackageDownloadLocation: NOASSERTION
PackageName: io-libvbus
SPDXID: SPDXRef-io-libvbus
PackageOriginator: Organization: Kernkonzept GmbH (info@kernkonzept.com)
PackageLicenseDeclared: GPL-2.0-only WITH GCC-exception-2.0
PackageLicenseConcluded: GPL-2.0-only WITH GCC-exception-2.0
FilesAnalyzed: false
PackageCopyrightText: NOASSERTION
PackageDownloadLocation: NOASSERTION
Relationship: SPDXRef-io CONTAINS SPDXRef-io-libpciids
Relationship: SPDXRef-io CONTAINS SPDXRef-io-libio
Relationship: SPDXRef-io CONTAINS SPDXRef-io-libio-direct
Relationship: SPDXRef-io CONTAINS SPDXRef-io-libio-io
Relationship: SPDXRef-io CONTAINS SPDXRef-io-libvbus

3
src/l4/pkg/io/Makefile Normal file
View File

@@ -0,0 +1,3 @@
L4DIR ?= ../..
include $(L4DIR)/mk/project.mk

29
src/l4/pkg/io/README.md Normal file
View File

@@ -0,0 +1,29 @@
# L4Re IO
IO is the management component that handles access to platform devices and
resources such as I/O memory, ports (on x86) and interrupts. It grants and
controls access to these resources for all other L4Re components.
This package includes the following components:
* io - main server and resource manager
* libvbus - IO client interface and definitions
* libio-direct - convenience functions for requesting hardware resources
directly from the kernel/sigma0
* libio-io - convenience functions for requesting hardware resources from IO
# Documentation
This package is part of the L4Re operating system. For documentation and
build instructions see the
[L4Re wiki](https://kernkonzept.com/L4Re/guides/l4re).
# Contributions
We welcome contributions. Please see our contributors guide on
[how to contribute](https://kernkonzept.com/L4Re/contributing/l4re).
# License
Detailed licensing and copyright information can be found in
the [LICENSE](LICENSE.spdx) file.

29
src/l4/pkg/io/SECURITY.md Normal file
View File

@@ -0,0 +1,29 @@
# Security Policy
This document outlines security procedures for the open-source projects of the
L4Re Operating System Framework as found on https://github.com/kernkonzept.
# Reporting a vulnerability
Security is very important to us and we take all security vulnerabilities
seriously. Thank you for improving the security of our open source software. If
you have discovered a security issue, we appreciate your efforts and your
responsible disclosure.
Please report a security vulnerability by sending an encrypted email to our
security team using our [public
key](https://www.kernkonzept.com/dl/security-at-kernkonzept.pub)
to **security@kernkonzept.com**. The fingerprint of our public key is
````
C4DC 2909 A22E D080 C012 5373 4055 CBA2 A4FD 855B
````
Please include the following in your report:
* A description of the vulnerability
* Steps to reproduce the vulnerability
A member of Kernkonzept's security team will confirm the vulnerability,
determine its impact, and develop a fix. The fix will be applied to the master
branch, tested, and released.

5
src/l4/pkg/io/io/Control Normal file
View File

@@ -0,0 +1,5 @@
requires: acpica[arch(x86) || arch(amd64)] libc libstdc++ l4sys libio-io
libsigma0 stdlibs lua++ drivers-frst
optional: acpica[arch(arm64)]
provides: libpciids
maintainer: warg@os.inf.tu-dresden.de

View File

@@ -0,0 +1,40 @@
menu "io platform manager"
config L4IO_ACPI
bool "Include the ACPI subsystem" if !BUILD_ARCH_x86 && !BUILD_ARCH_amd64
depends on (BUILD_ARCH_x86 || BUILD_ARCH_amd64 || BUILD_ARCH_arm64) && HAVE_BIDPC_ACPICA
default y
select L4IO_PCI
comment "ACPI not available due to missing acpica package"
depends on !HAVE_BIDPC_ACPICA
config L4IO_PCI
bool "Include the PCI subsystem"
select L4IO_SCU_IMX8QM
default y
config L4IO_PCIID_DB
bool "Include PCI-ID database for descriptive PCI devices"
depends on L4IO_PCI
default y
config L4IO_PCI_SRIOV
bool "Include support for PCI SR-IOV devices"
depends on EXPERIMENTAL
depends on L4IO_PCI
config L4IO_PCI_SRIOV_MAX_VFS
int "Maximum number of virtual functions (VFs)"
depends on L4IO_PCI_SRIOV
default 255
menu "Device drivers"
source "server/src/drivers/Kconfig.L4"
source "server/src/drivers/gpio/Kconfig.L4"
endmenu
endmenu

View File

@@ -0,0 +1,9 @@
PKGDIR ?= .
L4DIR ?= $(PKGDIR)/../../..
TARGET := server configs
include $(L4DIR)/mk/subdir.mk
#server: lib

View File

@@ -0,0 +1,7 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
SRC_ASSETS_IO = $(patsubst $(SRC_DIR)/%,%,$(wildcard $(SRC_DIR)/plat-*/*.io)) \
x86-legacy.devs
include $(L4DIR)/mk/assets.mk

View File

@@ -0,0 +1,47 @@
-- vi:ft=lua
local Res = Io.Res
local Hw = Io.Hw
if string.packsize("T") * 8 == 64 then
-- QEMU: VIRT_HIGH_PCIE_ECAM
cfg_base = 0x4010000000
cfg_size = 0x0010000000 -- 256 buses x 256 devs x 4KB
-- QEMU: VIRT_HIGH_PCIE_MMIO
mmio_base_64 = 0x8000000000
mmio_size_64 = 0x8000000000 -- 512GB (for memory I/O access)
else
-- see QEMU: VIRT_PCIE_ECAM
cfg_base = 0x3f000000
cfg_size = 0x01000000 -- 16 buses x 256 devs x 4KB
end
Io.hw_add_devices(function()
pciec0 = Io.Hw.Ecam_pcie_bridge(function()
-- QEMU: VIRT_PCIE_MMIO / VIRT_HIGH_PCIE_MMIO
Property.regs_base = 0x10000000
Property.regs_size = 0x2eff0000
-- QEMU: VIRT_PCIE_ECAM / VIRT_HIGH_PCIE_ECAM
Property.cfg_base = cfg_base
Property.cfg_size = cfg_size
-- QEMU: VIRT_PCIE_MMIO
Property.mmio_base = 0x10000000 -- 32-bit MMIO resources
Property.mmio_size = 0x2eff0000
-- QEMU: VIRT_HIGH_PCIE_MMIO
Property.mmio_base_64 = mmio_base_64
Property.mmio_size_64 = mmio_size_64
Property.int_a = 32 + 3
Property.int_b = 32 + 4
Property.int_c = 32 + 5
Property.int_d = 32 + 6
end);
rtc = Hw.Device(function()
compatible = {"arm,pl031"};
Resource.req0 = Res.mmio(0x9010000, 0x9010fff);
Resource.irq0 = Res.irq(32 + 2, Io.Resource.Irq_type_level_high);
end);
end)

View File

@@ -0,0 +1,134 @@
-- vim:set ft=lua:
--
-- (c) 2008-2009 Technische Universität Dresden
-- 2014 Matthias Lange <matthias.lange@kernkonzept.com>
-- License: see LICENSE.spdx (in this directory or the directories above)
--
-- OMAP3 (OMAP3EVM, Beagleboard)
local Hw = Io.Hw
local Res = Io.Res
Io.hw_add_devices(function()
Scm = Hw.Scm_omap(function()
Property.hid = "System Control";
compatible = {"ti,omap3-scrm"};
Resource.regs = Res.mmio(0x48002000, 0x48002fff);
end);
-- The values in this table are taken from the OMAP35x TRM chapter 7
-- System Control Module, table 7-4 (pad configuration register fields)
GPIO = Hw.Device(function()
GPIO1 = Hw.Gpio_omap35x_chip(function()
Property.hid = "gpio-omap35x-GPIO1";
compatible = {"ti,omap3-gpio"};
Resource.regs = Res.mmio(0x48310000, 0x48310fff);
Resource.irq = Res.irq(29);
Property.scm =
{
Scm,
0x1e0, 0xa06, 0xa08, 0xa0c, 0xa0e, 0xa10, 0xa12, 0xa14,
0xa16, 0xa18, 0xa1a, 0x424, 0x5d8, 0x5da, 0x5dc, 0x5de,
0x5e0, 0x5e2, 0x5e4, 0x5e6, 0x5e8, 0x5ea, 0x5ec, 0x5ee,
0x5f0, 0x5f2, 0x5f4, 0x5f6, 0x5f8, 0x5fa, 0xa08, 0xa26
};
end);
GPIO2 = Hw.Gpio_omap35x_chip(function()
Property.hid = "gpio-omap35x-GPIO2";
compatible = {"ti,omap3-gpio"};
Resource.regs = Res.mmio(0x49050000, 0x49050fff);
Resource.irq = Res.irq(30);
Property.scm =
{
Scm,
0x23a, -1, 0x07a, 0x07c, 0x07e, 0x080, 0x082, 0x084,
0x086, 0x088, 0x08a, 0x08c, 0x09e, 0x0a0, 0x0a2, 0x0a4,
0x0a6, 0x0a8, 0x0aa, 0x0ac, 0x0b0, 0x0b2, 0x0b4, 0x0b6,
0x0b8, 0x0ba, 0x0bc, 0x0be, 0x0c6, 0x0c8, 0x0ca, 0x0ce
};
end);
GPIO3 = Hw.Gpio_omap35x_chip(function()
Property.hid = "gpio-omap35x-GPIO3";
compatible = {"ti,omap3-gpio"};
Resource.regs = Res.mmio(0x49052000, 0x49052fff);
Resource.irq = Res.irq(31);
Property.scm =
{
Scm,
0x0d0, 0x0d2, 0x0d4, 0x0d6, 0x0d8, 0x0da, 0x0dc, 0x0de,
0x0e0, 0x0e2, 0x0e4, 0x0e6, 0x0e8, 0x0ea, 0x0ac, 0x0ee,
0x0f0, 0x0f2, 0x0f4, 0x0f6, 0x0f8, 0x0fa, 0x0fc, 0x0fe,
0x100, 0x102, 0x104, 0x106, 0x108, 0x10a, 0x10c, 0x10e
};
end);
GPIO4 = Hw.Gpio_omap35x_chip(function()
Property.hid = "gpio-omap35x-GPIO4";
compatible = {"ti,omap3-gpio"};
Resource.regs = Res.mmio(0x49054000, 0x49054fff);
Resource.irq = Res.irq(32);
Property.scm =
{
Scm,
0x110, 0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e,
0x120, 0x122, 0x124, 0x126, 0x128, 0x12a, 0x12c, 0x12e,
0x134, 0x136, 0x138, 0x13a, 0x13c, 0x13e, 0x140, 0x142,
0x144, 0x146, 0x148, 0x14a, 0x14c, 0x14e, 0x150, 0x152
};
end);
GPIO5 = Hw.Gpio_omap35x_chip(function()
Property.hid = "gpio-omap35x-GPIO5";
compatible = {"ti,omap3-gpio"};
Resource.regs = Res.mmio(0x49056000, 0x49056fff);
Resource.irq = Res.irq(33);
Property.scm =
{
Scm,
0x154, 0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162,
0x164, 0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172,
0x174, 0x176, 0x178, 0x17a, 0x17c, 0x17e, 0x180, 0x182,
0x184, 0x186, 0x188, 0x18a, 0x18c, 0x18e, 0x190, 0x192
};
end);
GPIO6 = Hw.Gpio_omap35x_chip(function()
Property.hid = "gpio-omap35x-GPIO6";
compatible = {"ti,omap3-gpio"};
Resource.regs = Res.mmio(0x49058000, 0x49058fff);
Resource.irq = Res.irq(34);
Property.scm =
{
Scm,
0x194, 0x196, 0x198, 0x19a, 0x19c, 0x19e, 0x1a0, 0x130,
0x1be, 0x1b0, 0x1c6, 0x1c8, 0x1ca, 0x1cc, 0x1ce, 0x1d0,
0x1d2, 0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, 0x1de, 0x1c0,
0x1c2, 0x1c4, 0x1e2, 0x238, 0x1b0, 0x1b4, 0x1b6, 0x1b8
};
end);
end)
prcm = Hw.Device(function()
Property.hid = "Omap3 Clock Module";
compatible = {"ti,omap3-cm"};
Resource.regs = Res.mmio(0x48004000, 0x48007fff);
end)
dss = Hw.Device(function()
Property.hid = "OMAP_LCD";
compatible = {"ti,omap3-dss"};
Resource.regs = Res.mmio(0x48050000, 0x480501ff);
Child.dispc = Hw.Device(function()
Property.hid = "Omap3 Display Controller";
compatible = {"ti,omap3-dispc"};
Resource.regs = Res.mmio(0x48050400, 0x480507ff);
Resource.irq = Res.irq(25);
end);
end)
i2c1 = Hw.Device(function()
Property.hid = "omap3-i2c";
compatible = {"ti,omap3-i2c"};
Resource.regs = Res.mmio(0x48070000, 0x48070fff);
Resource.irq = Res.irq(56);
end)
end)

View File

@@ -0,0 +1,164 @@
-- vi:ft=lua
local Res = Io.Res
local Hw = Io.Hw
local add_children = Io.Dt.add_children
add_children(Io.system_bus(), function()
CPUID = Hw.Device(function()
compatible = { "samsung,exynos4210-chipid" };
Property.hid = "exynos-cpuid";
Resource.reg0 = Res.mmio(0x10000000, 0x10000fff);
end)
CLOCK = Hw.Device(function()
Property.hid="exynos5250-clock";
Resource.reg0 = Res.mmio(0x10010000, 0x1003ffff);
end)
SATA = Hw.Device(function()
Property.hid = "exynos5-sata-ahci";
Resource.reg0 = Res.mmio(0x122F0000, 0x122f01ff);
Resource.irq0 = Res.irq(147);
end)
SATA_PHY = Hw.Device(function()
Property.hid = "exynos5-sata-phy";
Resource.reg0 = Res.mmio(0x12170000, 0x121701ff);
-- Resource.irq = Res.irq(140);
-- Resource.mmio = Res.mmio(0x10040000, 0x10040fff);
end)
SATA_PHY_I2C = Hw.Device(function()
Property.hid = "exynos5-sata-phy-i2c";
Resource.reg0 = Res.mmio(0x121D0000, 0x121D0100);
end)
USB_EHCI = Hw.Device(function()
Property.hid = "exynos4210-ehci";
Resource.reg0 = Res.mmio(0x12110000, 0x121100ff);
Resource.irq0 = Res.irq(103);
end)
USB_OHCI = Hw.Device(function()
Property.hid = "exynos4210-ohci";
Resource.reg0 = Res.mmio(0x12120000, 0x121200ff);
Resource.irq0 = Res.irq(103);
end)
USB_PHY = Hw.Device(function()
Property.hid = "exynos5250-usb2phy";
Resource.reg0 = Res.mmio(0x12130000, 0x121300ff);
-- Resource.mmio = Res.mmio(0x10040704, 0x1004070b);
-- Resource.mm1 = Res.mmio(0x10050230, 0x10050233);
-- Resource.mm2 = Res.mmio(0x10050000, 0x10050fff);
end)
USB3 = Hw.Device(function()
Property.hid = "exynos5-usb3";
Resource.reg0 = Res.mmio(0x12000000, 0x1210ffff);
Resource.irq0 = Res.irq(104);
end)
INT_COMB = Hw.Device(function()
Property.hid = "exynos-comb";
Resource.reg0 = Res.mmio(0x10440000, 0x1045ffff);
for z = 32, 63 do
Resource[#Resource + 1] = Res.irq(z);
end
end)
PDMA = Hw.Device(function()
Property.hid = "exynos";
Resource.reg0 = Res.mmio(0x121a0000, 0x121bffff);
Resource.irq0 = Res.irq(66);
Resource.irq1 = Res.irq(67);
end)
MDMA0 = Hw.Device(function()
Property.hid = "exynos";
Resource.reg0 = Res.mmio(0x10800000, 0x1080ffff);
Resource.irq0 = Res.irq(65);
end)
MDMA1 = Hw.Device(function()
Property.hid = "exynos";
Resource.reg0 = Res.mmio(0x11c10000, 0x11c1ffff);
Resource.irq0 = Res.irq(156);
end)
ALIVE = Hw.Device(function()
Property.hid = "exynos";
_res = { Res.mmio(0x10040000, 0x1004ffff) }
end)
SYSREG = Hw.Device(function()
Property.hid = "exynos";
_res = { Res.mmio(0x10050000, 0x1005ffff) }
end)
I2Cs = Hw.Device(function()
Property.hid = "exynos";
Resource.reg0 = Res.mmio(0x12c60000, 0x12ceffff);
Resource.irq0 = Res.irq(88);
Resource.irq1 = Res.irq(89);
Resource.irq2 = Res.irq(90);
Resource.irq3 = Res.irq(91);
Resource.irq4 = Res.irq(92);
Resource.irq5 = Res.irq(93);
Resource.irq6 = Res.irq(94);
Resource.irq7 = Res.irq(95);
Resource.irq8 = Res.irq(96);
end)
PWM = Hw.Device(function()
Property.hid = "exynos";
Resource.reg0 = Res.mmio(0x12dd0000, 0x12dd0fff);
end)
GPIO = Hw.Device(function()
Property.hid = "exynos";
Resource.irq0 = Res.irq(77);
Resource.irq1 = Res.irq(78);
Resource.irq2 = Res.irq(79);
Resource.irq3 = Res.irq(82);
Resource.reg0 = Res.mmio(0x10d10000, 0x10d1ffff);
Resource.reg1 = Res.mmio(0x11400000, 0x1140ffff);
Resource.reg2 = Res.mmio(0x13400000, 0x1340ffff);
end)
RTC = Hw.Device(function()
Property.hid = "exynos-rtc";
Resource.reg0 = Res.mmio(0x101e0000, 0x101e0fff);
Resource.irq0 = Res.irq(75);
Resource.irq1 = Res.irq(76);
end)
AUDSS = Hw.Device(function()
Property.hid = "exynos-audss";
Resource.reg0 = Res.mmio(0x03810000, 0x03810fff);
end)
UART0 = Hw.Device(function()
Property.hid = "exynos-serial0";
Resource.reg0 = Res.mmio(0x12C00000, 0x12C0ffff);
end)
UART1 = Hw.Device(function()
Property.hid = "exynos-serial1";
Resource.reg0 = Res.mmio(0x12C10000, 0x12C1ffff);
end)
UART2 = Hw.Device(function()
Property.hid = "exynos-serial2";
Resource.reg0 = Res.mmio(0x12C20000, 0x12C2ffff);
end)
UART3 = Hw.Device(function()
Property.hid = "exynos-serial3";
Resource.reg0 = Res.mmio(0x12C30000, 0x12C3ffff);
end)
end)

View File

@@ -0,0 +1,167 @@
-- vim:set ft=lua:
--
-- Copyright (C) 2016 Kernkonzept GmbH
-- Author(s): Matthias Lange <matthias.lange@kernkonzept.com>
--
-- License: see LICENSE.spdx (in this directory or the directories above)
-- NXP/Freescale i.MX6q
local Hw = Io.Hw
local Res = Io.Res
Io.hw_add_devices(function()
usdhc1 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-usdhc"};
Resource.reg0 = Res.mmio(0x2190000, 0x2193fff);
Resource.irq0 = Res.irq(54, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
end);
usdhc3 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-usdhc"};
Resource.reg0 = Res.mmio(0x2198000, 0x219bfff);
Resource.irq0 = Res.irq(56, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
end);
ccm = Io.Hw.Device(function()
compatible = "fsl,imx6q-ccm";
Resource.reg0 = Res.mmio(0x20c4000, 0x20c7fff);
Resource.irq0 = Res.irq(119, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(120, Io.Resource.Irq_type_level_high);
end);
anatop = Io.Hw.Device(function()
compatible = {"fsl,imx6q-anatop"};
Resource.reg0 = Res.mmio(0x20c8000, 0x20c8fff);
Resource.irq0 = Res.irq(81, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(86, Io.Resource.Irq_type_level_high);
Resource.irq2 = Res.irq(159, Io.Resource.Irq_type_level_high);
end);
iomuxc = Io.Hw.Device(function()
compatible = {"fsl,imx6q-iomuxc"};
Resource.reg0 = Res.mmio(0x20e0000, 0x20e3fff);
end);
gpio1 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-gpio,1", "fsl,imx6q-gpio", "fsl,imx35-gpio"};
Resource.reg0 = Res.mmio(0x209c000, 0x209ffff);
Resource.irq0 = Res.irq(98, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(99, Io.Resource.Irq_type_level_high);
end);
gpio2 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-gpio,2", "fsl,imx6q-gpio", "fsl,imx35-gpio"};
Resource.reg0 = Res.mmio(0x20a0000, 0x20a3fff);
Resource.irq0 = Res.irq(100, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(101, Io.Resource.Irq_type_level_high);
end);
gpio3 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-gpio,3", "fsl,imx6q-gpio", "fsl,imx35-gpio"};
Resource.reg0 = Res.mmio(0x20a4000, 0x20a7fff);
Resource.irq0 = Res.irq(102, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(103, Io.Resource.Irq_type_level_high);
end);
gpio4 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-gpio,4", "fsl,imx6q-gpio", "fsl,imx35-gpio"};
Resource.reg0 = Res.mmio(0x20a8000, 0x20abfff);
Resource.irq0 = Res.irq(104, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(105, Io.Resource.Irq_type_level_high);
end);
gpio5 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-gpio,5", "fsl,imx6q-gpio", "fsl,imx35-gpio"};
Resource.reg0 = Res.mmio(0x20ac000, 0x20affff);
Resource.irq0 = Res.irq(106, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(107, Io.Resource.Irq_type_level_high);
end);
gpio6 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-gpio,6", "fsl,imx6q-gpio", "fsl,imx35-gpio"};
Resource.reg0 = Res.mmio(0x20b0000, 0x20b3fff);
Resource.irq0 = Res.irq(108, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(109, Io.Resource.Irq_type_level_high);
end);
-- gpio7 = Io.Hw.Device(function()
-- compatible = {"fsl,imx6q-gpio,7", "fsl,imx6q-gpio", "fsl,imx35-gpio"};
-- Resource.reg0 = Res.mmio(0x20bc000, 0x20bffff);
-- Resource.irq0 = Res.irq(110, Io.Resource.Irq_type_level_high);
-- Resource.irq1 = Res.irq(111, Io.Resource.Irq_type_level_high);
-- end);
can1 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-flexcan"};
Resource.reg0 = Res.mmio(0x2090000, 0x2093fff);
Resource.irq0 = Res.irq(142, Io.Resource.Irq_type_level_high);
end);
can2 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-flexcan"};
Resource.reg0 = Res.mmio(0x2094000, 0x2097fff);
Resource.irq0 = Res.irq(143, Io.Resource.Irq_type_level_high);
end);
usbphy1 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-usbphy", "fsl,imx23-usbphy"};
Resource.reg0 = Res.mmio(0x20c9000, 0x20c9fff);
Resource.irq0 = Res.irq(76, Io.Resource.Irq_type_level_high);
end);
usbphy2 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-usbphy", "fsl,imx23-usbphy"};
Resource.reg0 = Res.mmio(0x20ca000, 0x20cafff);
Resource.irq0 = Res.irq(77, Io.Resource.Irq_type_level_high);
end);
usbotg = Io.Hw.Device(function()
compatible = {"fsl,imx6q-usb", "fsl,imx27-usb"};
Resource.reg0 = Res.mmio(0x2184000, 0x21841ff);
Resource.irq0 = Res.irq(75, Io.Resource.Irq_type_level_high);
end);
usbh1 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-usb", "fsl,imx27-usb"};
Resource.reg0 = Res.mmio(0x2184200, 0x21843ff);
Resource.irq0 = Res.irq(72, Io.Resource.Irq_type_level_high);
end);
usbmisc = Io.Hw.Device(function()
compatible = {"fsl,imx6q-usbmisc"};
Resource.reg0 = Res.mmio(0x2184800, 0x21849ff);
end);
i2c3 = Io.Hw.Device(function()
compatible = {"fsl,imx6q-i2c", "fsl,imx21-i2c"};
Resource.reg0 = Res.mmio(0x21a8000, 0x21abfff);
Resource.irq0 = Res.irq(70, Io.Resource.Irq_type_level_high);
end);
ethernet = Io.Hw.Device(function()
compatible = {"fsl,imx6q-fec"};
Resource.reg0 = Res.mmio(0x2188000, 0x218bfff);
Resource.irq0 = Res.irq(150, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(151, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
end);
apbhdma = Io.Hw.Device(function()
compatible = {"fsl,imx6q-dma-apbh", "fsl,imx28-dma-apbh"};
Resource.reg0 = Res.mmio(0x110000, 0x111fff);
Resource.irq0 = Res.irq(45, Io.Resource.Irq_type_level_high);
end);
gpmi = Io.Hw.Device(function()
compatible = {"fsl,imx6q-gpmi-nand"};
Resource.reg0 = Res.mmio(0x112000, 0x113fff);
Resource.reg1 = Res.mmio(0x114000, 0x117fff);
Resource.irq0 = Res.irq(47, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
end);
end)

View File

@@ -0,0 +1,880 @@
-- vi:ft=lua
local hw = Io.system_bus()
local type_map = {
[0] = Io.Resource.Irq_type_none,
[1] = Io.Resource.Irq_type_raising_edge,
[2] = Io.Resource.Irq_type_falling_edge,
[3] = Io.Resource.Irq_type_both_edges,
[4] = Io.Resource.Irq_type_level_high,
[8] = Io.Resource.Irq_type_level_low
}
function reg_irq(spi, irq, irq_type)
if spi ~= 0 then
error("Unable to handle non SPI irqs", 2);
end
if type_map[irq_type] == nil then
error("Undefined interrupt type " .. irq_type, 2);
end
return Io.Res.irq(irq + 32, type_map[irq_type]);
end
function reg_mmio(base, size)
return Io.Res.mmio(base, base + size - 1)
end
Io.hw_add_devices(function()
-- / (0) ignored
-- memory@40000000 (1) ignored
-- /reserved-memory/ocram@900000 (2)
ocram_900000 = Io.Hw.Device(function()
Resource.reg0 = reg_mmio(0x00900000, 0x00070000);
end) -- ocram@900000
-- /reserved-memory/gpu_reserved@100000000 (2)
gpu_reserved_100000000 = Io.Hw.Device(function()
Resource.reg0 = reg_mmio(0x100000000, 0x10000000);
end) -- gpu_reserved@100000000
-- /reserved-memory/dsp_reserved_heap@93400000 (2)
dsp_reserved_heap_93400000 = Io.Hw.Device(function()
Resource.reg0 = reg_mmio(0x93400000, 0x00ef0000);
end) -- dsp_reserved_heap@93400000
-- /reserved-memory/m7@77000000 (2)
m7_77000000 = Io.Hw.Device(function()
-- disabled
Resource.reg0 = reg_mmio(0x77000000, 0x01000000);
end) -- m7@77000000
-- /reserved-memory/vdev0vring0@55000000 (2)
vdev0vring0_55000000 = Io.Hw.Device(function()
-- disabled
Resource.reg0 = reg_mmio(0x55000000, 0x00008000);
end) -- vdev0vring0@55000000
-- /reserved-memory/vdev0vring1@55008000 (2)
vdev0vring1_55008000 = Io.Hw.Device(function()
-- disabled
Resource.reg0 = reg_mmio(0x55008000, 0x00008000);
end) -- vdev0vring1@55008000
-- /reserved-memory/rsc-table@550ff000 (2)
rsc_table_550ff000 = Io.Hw.Device(function()
-- disabled
Resource.reg0 = reg_mmio(0x550ff000, 0x00001000);
end) -- rsc-table@550ff000
-- /reserved-memory/vdevbuffer@55400000 (2)
vdevbuffer_55400000 = Io.Hw.Device(function()
-- disabled
compatible = { "shared-dma-pool" };
Resource.reg0 = reg_mmio(0x55400000, 0x00100000);
end) -- vdevbuffer@55400000
-- /pmu (1)
-- pmu = Io.Hw.Device(function()
-- compatible = { "arm,cortex-a53-pmu" };
-- Resource.irq0 = reg_irq(0x1, 0x7, 0xf04);
-- end) -- pmu
-- timer (1) ignored
-- /caam_secvio (1)
caam_secvio = Io.Hw.Device(function()
compatible = { "fsl,imx6q-caam-secvio" };
Resource.irq0 = reg_irq(0x0, 0x14, 0x4);
end) -- caam_secvio
-- /soc@0/caam-sm@100000 (2)
caam_sm_100000 = Io.Hw.Device(function()
compatible = { "fsl,imx6q-caam-sm" };
Resource.reg0 = reg_mmio(0x00100000, 0x00008000);
end) -- caam-sm@100000
-- /soc@0/bus@30000000/gpio@30200000 (3)
gpio0 = Io.Hw.Gpio_imx_chip(function()
compatible = { "fsl,imx8mp-gpio", "fsl,imx35-gpio" };
Resource.regs = reg_mmio(0x30200000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x40, 0x4);
Resource.irq1 = reg_irq(0x0, 0x41, 0x4);
end) -- gpio@30200000
-- /soc@0/bus@30000000/gpio@30210000 (3)
gpio1 = Io.Hw.Gpio_imx_chip(function()
compatible = { "fsl,imx8mp-gpio", "fsl,imx35-gpio" };
Resource.regs = reg_mmio(0x30210000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x42, 0x4);
Resource.irq1 = reg_irq(0x0, 0x43, 0x4);
end) -- gpio@30210000
-- /soc@0/bus@30000000/gpio@30220000 (3)
gpio2 = Io.Hw.Gpio_imx_chip(function()
compatible = { "fsl,imx8mp-gpio", "fsl,imx35-gpio" };
Resource.regs = reg_mmio(0x30220000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x44, 0x4);
Resource.irq1 = reg_irq(0x0, 0x45, 0x4);
end) -- gpio@30220000
-- /soc@0/bus@30000000/gpio@30230000 (3)
gpio3 = Io.Hw.Gpio_imx_chip(function()
compatible = { "fsl,imx8mp-gpio", "fsl,imx35-gpio" };
Resource.regs = reg_mmio(0x30230000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x46, 0x4);
Resource.irq1 = reg_irq(0x0, 0x47, 0x4);
end) -- gpio@30230000
-- /soc@0/bus@30000000/gpio@30240000 (3)
gpio4 = Io.Hw.Gpio_imx_chip(function()
compatible = { "fsl,imx8mp-gpio", "fsl,imx35-gpio" };
Resource.regs = reg_mmio(0x30240000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x48, 0x4);
Resource.irq1 = reg_irq(0x0, 0x49, 0x4);
end) -- gpio@30240000
-- /soc@0/bus@30000000/tmu@30260000 (3)
tmu_30260000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-tmu" };
Resource.reg0 = reg_mmio(0x30260000, 0x00010000);
end) -- tmu@30260000
-- /soc@0/bus@30000000/watchdog@30280000 (3)
watchdog_30280000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-wdt", "fsl,imx21-wdt" };
Resource.reg0 = reg_mmio(0x30280000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x4e, 0x4);
end) -- watchdog@30280000
-- /soc@0/bus@30000000/watchdog@30290000 (3)
watchdog_30290000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-wdt", "fsl,imx21-wdt" };
Resource.reg0 = reg_mmio(0x30290000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x4f, 0x4);
end) -- watchdog@30290000
-- /soc@0/bus@30000000/watchdog@302a0000 (3)
watchdog_302a0000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-wdt", "fsl,imx21-wdt" };
Resource.reg0 = reg_mmio(0x302a0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0xa, 0x4);
end) -- watchdog@302a0000
-- /soc@0/bus@30000000/timer@302d0000 (3)
timer_302d0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-gpt", "fsl,imx6dl-gpt" };
Resource.reg0 = reg_mmio(0x302d0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x37, 0x4);
end) -- timer@302d0000
-- /soc@0/bus@30000000/timer@302e0000 (3)
timer_302e0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-gpt", "fsl,imx6dl-gpt" };
Resource.reg0 = reg_mmio(0x302e0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x36, 0x4);
end) -- timer@302e0000
-- /soc@0/bus@30000000/timer@302f0000 (3)
timer_302f0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-gpt", "fsl,imx6dl-gpt" };
Resource.reg0 = reg_mmio(0x302f0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x35, 0x4);
end) -- timer@302f0000
-- /soc@0/bus@30000000/pinctrl@30330000 (3)
pinctrl_30330000 = Io.Hw.Iomuxc_imx8mp(function()
compatible = { "fsl,imx8mp-iomuxc" };
Resource.reg0 = reg_mmio(0x30330000, 0x00010000);
-- Resource.reg1 = reg_mmio(0x30340000, 0x00010000); -- gpr
end) -- pinctrl@30330000
-- /soc@0/bus@30000000/syscon@30340000 (3)
syscon_30340000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-iomuxc-gpr", "syscon" };
Resource.reg0 = reg_mmio(0x30340000, 0x00010000);
end) -- syscon@30340000
-- /soc@0/bus@30000000/efuse@30350000 (3)
efuse_30350000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-ocotp", "fsl,imx8mm-ocotp", "syscon", "simple-mfd" };
Resource.reg0 = reg_mmio(0x30350000, 0x00010000);
end) -- efuse@30350000
-- /soc@0/bus@30000000/clock-controller@30360000 (3)
clock_controller_30360000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-anatop", "fsl,imx8mm-anatop" };
Resource.reg0 = reg_mmio(0x30360000, 0x00010000);
end) -- clock-controller@30360000
-- /soc@0/bus@30000000/caam-snvs@30370000 (3)
caam_snvs_30370000 = Io.Hw.Device(function()
compatible = { "fsl,imx6q-caam-snvs" };
Resource.reg0 = reg_mmio(0x30370000, 0x00010000);
end) -- caam-snvs@30370000
-- /soc@0/bus@30000000/snvs@30370000 (3)
snvs_30370000 = Io.Hw.Device(function()
compatible = { "fsl,sec-v4.0-mon", "syscon", "simple-mfd" };
Resource.reg0 = reg_mmio(0x30370000, 0x00010000);
-- /soc@0/bus@30000000/snvs@30370000/snvs-rtc-lp (4)
snvs_rtc_lp = Io.Hw.Device(function()
compatible = { "fsl,sec-v4.0-mon-rtc-lp" };
Resource.irq0 = reg_irq(0x0, 0x13, 0x4);
Resource.irq1 = reg_irq(0x0, 0x14, 0x4);
end) -- snvs-rtc-lp
-- /soc@0/bus@30000000/snvs@30370000/snvs-powerkey (4)
snvs_powerkey = Io.Hw.Device(function()
compatible = { "fsl,sec-v4.0-pwrkey" };
Resource.irq0 = reg_irq(0x0, 0x4, 0x4);
end) -- snvs-powerkey
end) -- snvs@30370000
-- /soc@0/bus@30000000/clock-controller@30380000 (3)
clock_controller_30380000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-ccm" };
Resource.reg0 = reg_mmio(0x30380000, 0x00010000);
end) -- clock-controller@30380000
-- /soc@0/bus@30000000/reset-controller@30390000 (3)
reset_controller_30390000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-src", "syscon" };
Resource.reg0 = reg_mmio(0x30390000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x59, 0x4);
end) -- reset-controller@30390000
-- /soc@0/bus@30000000/gpc@303a0000 (3)
gpc_303a0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-gpc" };
Resource.reg0 = reg_mmio(0x303a0000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x57, 0x4);
end) -- gpc@303a0000
-- /soc@0/bus@30400000/pwm@30660000 (3)
pwm_30660000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-pwm", "fsl,imx27-pwm" };
Resource.reg0 = reg_mmio(0x30660000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x51, 0x4);
end) -- pwm@30660000
-- /soc@0/bus@30400000/pwm@30670000 (3)
pwm_30670000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-pwm", "fsl,imx27-pwm" };
Resource.reg0 = reg_mmio(0x30670000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x52, 0x4);
end) -- pwm@30670000
-- /soc@0/bus@30400000/pwm@30680000 (3)
pwm_30680000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-pwm", "fsl,imx27-pwm" };
Resource.reg0 = reg_mmio(0x30680000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x53, 0x4);
end) -- pwm@30680000
-- /soc@0/bus@30400000/pwm@30690000 (3)
pwm_30690000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-pwm", "fsl,imx27-pwm" };
Resource.reg0 = reg_mmio(0x30690000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x54, 0x4);
end) -- pwm@30690000
-- /soc@0/bus@30400000/timer@306a0000 (3)
timer_306a0000 = Io.Hw.Device(function()
compatible = { "nxp,sysctr-timer" };
Resource.reg0 = reg_mmio(0x306a0000, 0x00020000);
Resource.irq0 = reg_irq(0x0, 0x2f, 0x4);
end) -- timer@306a0000
-- /soc@0/bus@30400000/timer@306e0000 (3)
timer_306e0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-gpt", "fsl,imx6dl-gpt" };
Resource.reg0 = reg_mmio(0x306e0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x33, 0x4);
end) -- timer@306e0000
-- /soc@0/bus@30400000/timer@306f0000 (3)
timer_306f0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-gpt", "fsl,imx6dl-gpt" };
Resource.reg0 = reg_mmio(0x306f0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x33, 0x4);
end) -- timer@306f0000
-- /soc@0/bus@30400000/timer@30700000 (3)
timer_30700000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-gpt", "fsl,imx6dl-gpt" };
Resource.reg0 = reg_mmio(0x30700000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x34, 0x4);
end) -- timer@30700000
-- /soc@0/bus@30800000/spba-bus@30800000/spi@30820000 (4)
spi_30820000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-ecspi", "fsl,imx6ul-ecspi" };
Resource.reg0 = reg_mmio(0x30820000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x1f, 0x4);
end) -- spi@30820000
-- /soc@0/bus@30800000/spba-bus@30800000/spi@30830000 (4)
spi_30830000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-ecspi", "fsl,imx6ul-ecspi" };
Resource.reg0 = reg_mmio(0x30830000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x20, 0x4);
end) -- spi@30830000
-- /soc@0/bus@30800000/spba-bus@30800000/spi@30840000 (4)
spi_30840000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-ecspi", "fsl,imx6ul-ecspi" };
Resource.reg0 = reg_mmio(0x30840000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x21, 0x4);
end) -- spi@30840000
-- /soc@0/bus@30800000/spba-bus@30800000/serial@30860000 (4)
serial_30860000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-uart", "fsl,imx6q-uart" };
Resource.reg0 = reg_mmio(0x30860000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x1a, 0x4);
end) -- serial@30860000
-- /soc@0/bus@30800000/spba-bus@30800000/serial@30880000 (4)
serial_30880000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-uart", "fsl,imx6q-uart" };
Resource.reg0 = reg_mmio(0x30880000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x1c, 0x4);
end) -- serial@30880000
-- /soc@0/bus@30800000/spba-bus@30800000/serial@30890000 (4)
serial_30890000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-uart", "fsl,imx6q-uart" };
Resource.reg0 = reg_mmio(0x30890000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x1b, 0x4);
end) -- serial@30890000
-- /soc@0/bus@30800000/spba-bus@30800000/can@308c0000 (4)
can_308c0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-flexcan" };
Resource.reg0 = reg_mmio(0x308c0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x8e, 0x4);
end) -- can@308c0000
-- /soc@0/bus@30800000/spba-bus@30800000/can@308d0000 (4)
can_308d0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-flexcan" };
Resource.reg0 = reg_mmio(0x308d0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x90, 0x4);
end) -- can@308d0000
-- /soc@0/bus@30800000/crypto@30900000 (3)
crypto_30900000 = Io.Hw.Device(function()
compatible = { "fsl,sec-v4.0" };
Resource.reg0 = reg_mmio(0x30900000, 0x00040000);
Resource.irq0 = reg_irq(0x0, 0x5b, 0x4);
end) -- crypto@30900000
-- /soc@0/bus@30800000/crypto@30900000/jr@1000 (4)
jr_1000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,sec-v4.0-job-ring" };
-- [0x00001000, 0x00001000]
Resource.reg0 = reg_mmio(0x30901000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x69, 0x4);
end) -- jr@1000
-- /soc@0/bus@30800000/crypto@30900000/jr@2000 (4)
jr_2000 = Io.Hw.Device(function()
compatible = { "fsl,sec-v4.0-job-ring" };
-- [0x00002000, 0x00001000]
Resource.reg0 = reg_mmio(0x30902000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x6a, 0x4);
end) -- jr@2000
-- /soc@0/bus@30800000/crypto@30900000/jr@3000 (4)
jr_3000 = Io.Hw.Device(function()
compatible = { "fsl,sec-v4.0-job-ring" };
-- [0x00003000, 0x00001000]
Resource.reg0 = reg_mmio(0x30903000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x72, 0x4);
end) -- jr@3000
-- /soc@0/bus@30800000/i2c@30a20000 (3)
i2c_30a20000 = Io.Hw.I2c_imx8mp_ctrl(function()
compatible = { "fsl,imx8mp-i2c", "fsl,imx21-i2c" };
Resource.reg0 = reg_mmio(0x30a20000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x23, 0x4);
Property.iomuxc = pinctrl_30330000;
-- The `pads` property takes six values per pad:
-- three fields offsets of the IOMUXC MMIO registers and
-- three values to write to these offsets.
-- The order is:
-- field offsets: Mux, Config, Input
-- values to set: Mux, Input, Config
-- The difference in value order is in sync with the linux DT.
Property.pads = { 0x200, 0x460, 0x5a4, 0x10, 0x02, 0x000001e2,
0x204, 0x464, 0x5a8, 0x10, 0x02, 0x000001e2 };
-- /soc@0/bus@30800000/i2c@30a20000/pmic@25 (4)
pmic_25 = Io.Hw.Device(function()
compatible = { "nxp,pca9450c" };
-- Resource.reg0 not translatable
-- [ ( 0x00000025 ) ( ) ]
-- ignore IRQs routed to interrupt-parent gpio@30200000
-- Resource.irq0 = reg_irq(0x8, 0x8);
end) -- pmic@25
-- /soc@0/bus@30800000/i2c@30a20000/rtc@51 (4)
rtc_51 = Io.Hw.Device(function()
compatible = { "nxp,pcf85063a" };
-- Resource.reg0 not translatable
-- [ ( 0x00000051 ) ( ) ]
-- ignore IRQs routed to interrupt-parent gpio@30230000
-- Resource.irq0 = reg_irq(0x1c, 0x2);
end) -- rtc@51
end) -- i2c@30a20000
-- /soc@0/bus@30800000/i2c@30a30000 (3)
i2c_30a30000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-i2c", "fsl,imx21-i2c" };
Resource.reg0 = reg_mmio(0x30a30000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x24, 0x4);
end) -- i2c@30a30000
-- /soc@0/bus@30800000/i2c@30a40000 (3)
i2c_30a40000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-i2c", "fsl,imx21-i2c" };
Resource.reg0 = reg_mmio(0x30a40000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x25, 0x4);
end) -- i2c@30a40000
-- /soc@0/bus@30800000/i2c@30a50000 (3)
i2c_30a50000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-i2c", "fsl,imx21-i2c" };
Resource.reg0 = reg_mmio(0x30a50000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x26, 0x4);
end) -- i2c@30a50000
-- /soc@0/bus@30800000/serial@30a60000 (3)
serial_30a60000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-uart", "fsl,imx6q-uart" };
Resource.reg0 = reg_mmio(0x30a60000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x1d, 0x4);
end) -- serial@30a60000
-- /soc@0/bus@30800000/mailbox@30aa0000 (3)
mailbox_30aa0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-mu", "fsl,imx6sx-mu" };
Resource.reg0 = reg_mmio(0x30aa0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x58, 0x4);
end) -- mailbox@30aa0000
-- /soc@0/bus@30800000/mailbox@30e60000 (3)
mailbox_30e60000 = Io.Hw.Device(function()
compatible = { "fsl,imx8-mu-dsp", "fsl,imx6sx-mu" };
Resource.reg0 = reg_mmio(0x30e60000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x88, 0x4);
end) -- mailbox@30e60000
-- /soc@0/bus@30800000/i2c@30ad0000 (3)
i2c_30ad0000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-i2c", "fsl,imx21-i2c" };
Resource.reg0 = reg_mmio(0x30ad0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x4c, 0x4);
end) -- i2c@30ad0000
-- /soc@0/bus@30800000/i2c@30ae0000 (3)
i2c_30ae0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-i2c", "fsl,imx21-i2c" };
Resource.reg0 = reg_mmio(0x30ae0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x4d, 0x4);
end) -- i2c@30ae0000
-- /soc@0/bus@30800000/mmc@30b40000 (3)
mmc_30b40000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-usdhc", "fsl,imx8mm-usdhc", "fsl,imx7d-usdhc" };
Resource.reg0 = reg_mmio(0x30b40000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x16, 0x4);
end) -- mmc@30b40000
-- /soc@0/bus@30800000/mmc@30b50000 (3)
mmc_30b50000 = Io.Hw.Arm_dma_device(function()
compatible = { "fsl,imx8mp-usdhc", "fsl,imx8mm-usdhc", "fsl,imx7d-usdhc" };
Resource.reg0 = reg_mmio(0x30b50000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x17, 0x4);
end) -- mmc@30b50000
-- /soc@0/bus@30800000/mmc@30b60000 (3)
mmc_30b60000 = Io.Hw.Arm_dma_device(function()
compatible = { "fsl,imx8mp-usdhc", "fsl,imx8mm-usdhc", "fsl,imx7d-usdhc" };
Resource.reg0 = reg_mmio(0x30b60000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x18, 0x4);
end) -- mmc@30b60000
-- /soc@0/bus@30800000/spi@30bb0000 (3)
spi_30bb0000 = Io.Hw.Device(function()
compatible = { "nxp,imx8mp-fspi" };
Resource.reg0 = reg_mmio(0x30bb0000, 0x00010000);
Resource.reg1 = reg_mmio(0x08000000, 0x10000000);
Resource.irq0 = reg_irq(0x0, 0x6b, 0x4);
end) -- spi@30bb0000
-- /soc@0/bus@30800000/dma-controller@30bd0000 (3)
dma_controller_30bd0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mq-sdma" };
Resource.reg0 = reg_mmio(0x30bd0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x2, 0x4);
end) -- dma-controller@30bd0000
-- /soc@0/bus@30800000/ethernet@30be0000 (3)
ethernet_30be0000 = Io.Hw.Arm_dma_device(function()
compatible = { "fsl,imx8mp-fec", "fsl,imx8mq-fec", "fsl,imx6sx-fec" };
Resource.reg0 = reg_mmio(0x30be0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x76, 0x4);
Resource.irq1 = reg_irq(0x0, 0x77, 0x4);
Resource.irq2 = reg_irq(0x0, 0x78, 0x4);
Resource.irq3 = reg_irq(0x0, 0x79, 0x4);
-- /soc@0/bus@30800000/ethernet@30be0000/mdio/ethernet-phy@0 (5)
ethernet_phy_0 = Io.Hw.Device(function()
compatible = { "ethernet-phy-ieee802.3-c22" };
-- Resource.reg0 not translatable
-- [ ( ) ( ) ]
-- ignore IRQs routed to interrupt-parent gpio@30230000
-- Resource.irq0 = reg_irq(0x1, 0x2);
end) -- ethernet-phy@0
end) -- ethernet@30be0000
-- /soc@0/bus@30800000/ethernet@30bf0000 (3)
ethernet_30bf0000 = Io.Hw.Arm_dma_device(function()
compatible = { "nxp,imx8mp-dwmac-eqos", "snps,dwmac-5.10a" };
Resource.reg0 = reg_mmio(0x30bf0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x87, 0x4);
Resource.irq1 = reg_irq(0x0, 0x86, 0x4);
-- /soc@0/bus@30800000/ethernet@30bf0000/mdio/ethernet-phy@3 (5)
ethernet_phy_3 = Io.Hw.Device(function()
compatible = { "ethernet-phy-ieee802.3-c22" };
-- Resource.reg0 not translatable
-- [ ( 0x00000003 ) ( ) ]
-- ignore IRQs routed to interrupt-parent gpio@30230000
-- Resource.irq0 = reg_irq(0x3, 0x2);
end) -- ethernet-phy@3
end) -- ethernet@30bf0000
-- /soc@0/interconnect@32700000 (2)
interconnect_32700000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-noc", "fsl,imx8m-noc", "syscon" };
Resource.reg0 = reg_mmio(0x32700000, 0x00100000);
end) -- interconnect@32700000
-- /soc@0/bus@32c00000/mipi_dsi@32e60000 (3)
mipi_dsi_32e60000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-mipi-dsim" };
Resource.reg0 = reg_mmio(0x32e60000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x12, 0x4);
end) -- mipi_dsi@32e60000
-- /soc@0/bus@32c00000/lcd-controller@32e80000 (3)
lcd_controller_32e80000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-lcdif1" };
Resource.reg0 = reg_mmio(0x32e80000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x5, 0x4);
end) -- lcd-controller@32e80000
-- /soc@0/bus@32c00000/lcd-controller@32e90000 (3)
lcd_controller_32e90000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-lcdif2" };
Resource.reg0 = reg_mmio(0x32e90000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x6, 0x4);
end) -- lcd-controller@32e90000
-- /soc@0/bus@32c00000/blk-ctrl@32ec0000 (3)
blk_ctrl_32ec0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-media-blk-ctrl", "syscon" };
Resource.reg0 = reg_mmio(0x32ec0000, 0x00010000);
end) -- blk-ctrl@32ec0000
-- /soc@0/bus@32c00000/pcie-phy@32f00000 (3)
pcie_phy_32f00000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-pcie-phy" };
Resource.reg0 = reg_mmio(0x32f00000, 0x00010000);
end) -- pcie-phy@32f00000
-- /soc@0/bus@32c00000/blk-ctrl@32f10000 (3)
blk_ctrl_32f10000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-hsio-blk-ctrl", "syscon" };
Resource.reg0 = reg_mmio(0x32f10000, 0x00000024);
end) -- blk-ctrl@32f10000
-- /soc@0/bus@32c00000/blk-ctrl@32fc0000 (3)
blk_ctrl_32fc0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-hdmi-blk-ctrl", "syscon" };
Resource.reg0 = reg_mmio(0x32fc0000, 0x0000023c);
end) -- blk-ctrl@32fc0000
-- /soc@0/bus@32c00000/media_gpr@32ec0008 (3)
media_gpr_32ec0008 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-iomuxc-gpr", "syscon" };
Resource.reg0 = reg_mmio(0x32ec0008, 0x00000004);
end) -- media_gpr@32ec0008
-- /soc@0/bus@32c00000/gasket@32ec0060 (3)
gasket_32ec0060 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-iomuxc-gpr", "syscon" };
Resource.reg0 = reg_mmio(0x32ec0060, 0x00000028);
end) -- gasket@32ec0060
-- /soc@0/bus@32c00000/gasket@32ec0090 (3)
gasket_32ec0090 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-iomuxc-gpr", "syscon" };
Resource.reg0 = reg_mmio(0x32ec0090, 0x00000028);
end) -- gasket@32ec0090
-- /soc@0/bus@32c00000/isi_chain@32e02000 (3)
isi_chain_32e02000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-iomuxc-gpr", "syscon" };
Resource.reg0 = reg_mmio(0x32e02000, 0x00000004);
end) -- isi_chain@32e02000
-- /soc@0/bus@32c00000/camera/isi@32e00000 (4)
isi_32e00000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-isi", "fsl,imx8mn-isi" };
Resource.reg0 = reg_mmio(0x32e00000, 0x00002000);
Resource.irq0 = reg_irq(0x0, 0x10, 0x4);
end) -- isi@32e00000
-- /soc@0/bus@32c00000/camera/isi@32e02000 (4)
isi_32e02000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-isi", "fsl,imx8mn-isi" };
Resource.reg0 = reg_mmio(0x32e02000, 0x00002000);
Resource.irq0 = reg_irq(0x0, 0x2a, 0x4);
end) -- isi@32e02000
-- /soc@0/bus@32c00000/camera/dwe@32e30000 (4)
dwe_32e30000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-dwe" };
Resource.reg0 = reg_mmio(0x32e30000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x64, 0x4);
end) -- dwe@32e30000
-- /soc@0/bus@32c00000/camera/isp@32e10000 (4)
isp_32e10000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-isp" };
Resource.reg0 = reg_mmio(0x32e10000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x4a, 0x4);
end) -- isp@32e10000
-- /soc@0/bus@32c00000/camera/isp@32e20000 (4)
isp_32e20000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-isp" };
Resource.reg0 = reg_mmio(0x32e20000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x4b, 0x4);
end) -- isp@32e20000
-- /soc@0/bus@32c00000/camera/csi@32e40000 (4)
csi_32e40000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-mipi-csi", "fsl,imx8mn-mipi-csi" };
Resource.reg0 = reg_mmio(0x32e40000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x11, 0x4);
end) -- csi@32e40000
-- /soc@0/bus@32c00000/camera/csi@32e50000 (4)
csi_32e50000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-mipi-csi", "fsl,imx8mn-mipi-csi" };
Resource.reg0 = reg_mmio(0x32e50000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x50, 0x4);
end) -- csi@32e50000
-- /soc@0/bus@30c00000/spba-bus@30c00000/sai@30c10000 (4)
sai_30c10000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-sai", "fsl,imx8mm-sai" };
Resource.reg0 = reg_mmio(0x30c10000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x5f, 0x4);
end) -- sai@30c10000
-- /soc@0/bus@30c00000/spba-bus@30c00000/sai@30c20000 (4)
sai_30c20000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-sai", "fsl,imx8mm-sai" };
Resource.reg0 = reg_mmio(0x30c20000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x60, 0x4);
end) -- sai@30c20000
-- /soc@0/bus@30c00000/spba-bus@30c00000/sai@30c30000 (4)
sai_30c30000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-sai", "fsl,imx8mm-sai" };
Resource.reg0 = reg_mmio(0x30c30000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x32, 0x4);
end) -- sai@30c30000
-- /soc@0/bus@30c00000/spba-bus@30c00000/sai@30c50000 (4)
sai_30c50000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-sai", "fsl,imx8mm-sai" };
Resource.reg0 = reg_mmio(0x30c50000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x5a, 0x4);
end) -- sai@30c50000
-- /soc@0/bus@30c00000/spba-bus@30c00000/sai@30c60000 (4)
sai_30c60000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-sai", "fsl,imx8mm-sai" };
Resource.reg0 = reg_mmio(0x30c60000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x5a, 0x4);
end) -- sai@30c60000
-- /soc@0/bus@30c00000/spba-bus@30c00000/sai@30c80000 (4)
sai_30c80000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-sai", "fsl,imx8mm-sai" };
Resource.reg0 = reg_mmio(0x30c80000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x6f, 0x4);
end) -- sai@30c80000
-- /soc@0/bus@30c00000/spba-bus@30c00000/easrc@30c90000 (4)
easrc_30c90000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mn-easrc" };
Resource.reg0 = reg_mmio(0x30c90000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x7a, 0x4);
end) -- easrc@30c90000
-- /soc@0/bus@30c00000/spba-bus@30c00000/micfil@30ca0000 (4)
micfil_30ca0000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-micfil" };
Resource.reg0 = reg_mmio(0x30ca0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x6d, 0x4);
Resource.irq1 = reg_irq(0x0, 0x6e, 0x4);
Resource.irq2 = reg_irq(0x0, 0x2c, 0x4);
Resource.irq3 = reg_irq(0x0, 0x2d, 0x4);
end) -- micfil@30ca0000
-- /soc@0/bus@30c00000/spba-bus@30c00000/aud2htx@30cb0000 (4)
aud2htx_30cb0000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-aud2htx" };
Resource.reg0 = reg_mmio(0x30cb0000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x82, 0x4);
end) -- aud2htx@30cb0000
-- /soc@0/bus@30c00000/spba-bus@30c00000/xcvr@30cc0000 (4)
xcvr_30cc0000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-xcvr" };
Resource.reg0 = reg_mmio(0x30cc0000, 0x00000800);
Resource.reg1 = reg_mmio(0x30cc0800, 0x00000400);
Resource.reg2 = reg_mmio(0x30cc0c00, 0x00000080);
Resource.reg3 = reg_mmio(0x30cc0e00, 0x00000080);
Resource.irq0 = reg_irq(0x0, 0x80, 0x4);
Resource.irq1 = reg_irq(0x0, 0x81, 0x4);
Resource.irq2 = reg_irq(0x0, 0x92, 0x4);
end) -- xcvr@30cc0000
-- /soc@0/bus@30c00000/dma-controller@30e00000 (3)
dma_controller_30e00000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-sdma", "fsl,imx7d-sdma" };
Resource.reg0 = reg_mmio(0x30e00000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x22, 0x4);
end) -- dma-controller@30e00000
-- /soc@0/bus@30c00000/dma-controller@30e10000 (3)
dma_controller_30e10000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-sdma", "fsl,imx7d-sdma" };
Resource.reg0 = reg_mmio(0x30e10000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x67, 0x4);
end) -- dma-controller@30e10000
-- /soc@0/bus@30c00000/audio-blk-ctrl@30e20000 (3)
audio_blk_ctrl_30e20000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-audio-blk-ctrl", "syscon" };
Resource.reg0 = reg_mmio(0x30e20000, 0x0000050c);
end) -- audio-blk-ctrl@30e20000
-- /soc@0/bus@30c00000/irqsteer@32fc2000 (3)
irqsteer_32fc2000 = Io.Hw.Device(function()
compatible = { "fsl,imx-irqsteer" };
Resource.reg0 = reg_mmio(0x32fc2000, 0x00001000);
Resource.irq0 = reg_irq(0x0, 0x2b, 0x4);
end) -- irqsteer@32fc2000
-- /soc@0/bus@30c00000/hdmi-pai-pvi@32fc4000 (3)
hdmi_pai_pvi_32fc4000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-hdmi-pavi" };
Resource.reg0 = reg_mmio(0x32fc4000, 0x00001000);
end) -- hdmi-pai-pvi@32fc4000
-- /soc@0/bus@30c00000/lcd-controller@32fc6000 (3)
lcd_controller_32fc6000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-lcdif1" };
Resource.reg0 = reg_mmio(0x32fc6000, 0x00010000);
-- ignore IRQs routed to interrupt-parent irqsteer@32fc2000
-- Resource.irq0 = reg_irq(0x8);
-- Resource.irq1 = reg_irq(0x4);
end) -- lcd-controller@32fc6000
-- /soc@0/bus@30c00000/hdmi@32fd8000 (3)
hdmi_32fd8000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-hdmi" };
Resource.reg0 = reg_mmio(0x32fd8000, 0x00007eff);
-- ignore IRQs routed to interrupt-parent irqsteer@32fc2000
-- Resource.irq0 = reg_irq(0x0);
-- Resource.irq1 = reg_irq(0x4);
end) -- hdmi@32fd8000
-- /soc@0/bus@30c00000/hdmiphy@32fdff00 (3)
hdmiphy_32fdff00 = Io.Hw.Device(function()
compatible = { "fsl,samsung-hdmi-phy" };
Resource.reg0 = reg_mmio(0x32fdff00, 0x00000100);
end) -- hdmiphy@32fdff00
-- /soc@0/dma-apbh@33000000 (2)
dma_apbh_33000000 = Io.Hw.Device(function()
compatible = { "fsl,imx7d-dma-apbh", "fsl,imx28-dma-apbh" };
Resource.reg0 = reg_mmio(0x33000000, 0x00002000);
Resource.irq0 = reg_irq(0x0, 0xc, 0x4);
end) -- dma-apbh@33000000
-- /soc@0/gpmi-nand@33002000 (2)
gpmi_nand_33002000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx7d-gpmi-nand" };
Resource.reg0 = reg_mmio(0x33002000, 0x00002000);
Resource.reg1 = reg_mmio(0x33004000, 0x00004000);
Resource.irq0 = reg_irq(0x0, 0xe, 0x4);
end) -- gpmi-nand@33002000
-- /soc@0/pcie@33800000 (2)
pcie_33800000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-pcie" };
Resource.reg0 = reg_mmio(0x33800000, 0x00400000);
Resource.reg1 = reg_mmio(0x1ff00000, 0x00080000);
Resource.irq0 = reg_irq(0x0, 0x8c, 0x4);
end) -- pcie@33800000
-- /soc@0/pcie-ep@33800000 (2)
pcie_ep_33800000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-pcie-ep" };
Resource.reg0 = reg_mmio(0x33800000, 0x00100000);
Resource.reg1 = reg_mmio(0x33900000, 0x00100000);
Resource.reg2 = reg_mmio(0x33b00000, 0x00100000);
Resource.reg3 = reg_mmio(0x18000000, 0x08000000);
Resource.irq0 = reg_irq(0x0, 0x7f, 0x4);
end) -- pcie-ep@33800000
-- interrupt-controller@38800000 (2) ignored
-- /soc@0/blk-ctl@38330000 (2)
blk_ctl_38330000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-vpu-blk-ctrl", "syscon" };
Resource.reg0 = reg_mmio(0x38330000, 0x00000100);
end) -- blk-ctl@38330000
-- /soc@0/memory-controller@3d400000 (2)
memory_controller_3d400000 = Io.Hw.Device(function()
compatible = { "snps,ddrc-3.80a" };
Resource.reg0 = reg_mmio(0x3d400000, 0x00400000);
Resource.irq0 = reg_irq(0x0, 0x93, 0x4);
end) -- memory-controller@3d400000
-- /soc@0/ddr-pmu@3d800000 (2)
ddr_pmu_3d800000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-ddr-pmu", "fsl,imx8m-ddr-pmu" };
Resource.reg0 = reg_mmio(0x3d800000, 0x00400000);
Resource.irq0 = reg_irq(0x0, 0x62, 0x4);
end) -- ddr-pmu@3d800000
-- /soc@0/usb-phy@381f0040 (2)
usb_phy_381f0040 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-usb-phy" };
Resource.reg0 = reg_mmio(0x381f0040, 0x00000040);
end) -- usb-phy@381f0040
-- /soc@0/usb@32f10100 (2)
usb_32f10100 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-dwc3" };
Resource.reg0 = reg_mmio(0x32f10100, 0x00000008);
Resource.reg1 = reg_mmio(0x381f0000, 0x00000020);
Resource.irq0 = reg_irq(0x0, 0x94, 0x4);
end) -- usb@32f10100
-- /soc@0/usb@32f10100/usb@38100000 (3)
usb_38100000 = Io.Hw.Device(function()
compatible = { "snps,dwc3" };
Resource.reg0 = reg_mmio(0x38100000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x28, 0x4);
end) -- usb@38100000
-- /soc@0/usb-phy@382f0040 (2)
usb_phy_382f0040 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-usb-phy" };
Resource.reg0 = reg_mmio(0x382f0040, 0x00000040);
end) -- usb-phy@382f0040
-- /soc@0/usb@32f10108 (2)
usb_32f10108 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-dwc3" };
Resource.reg0 = reg_mmio(0x32f10108, 0x00000008);
Resource.reg1 = reg_mmio(0x382f0000, 0x00000020);
Resource.irq0 = reg_irq(0x0, 0x95, 0x4);
end) -- usb@32f10108
-- /soc@0/usb@32f10108/usb@38200000 (3)
usb_38200000 = Io.Hw.Device(function()
compatible = { "snps,dwc3" };
Resource.reg0 = reg_mmio(0x38200000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x29, 0x4);
end) -- usb@38200000
-- /vpu_g1@38300000 (1)
vpu_g1_38300000 = Io.Hw.Device(function()
compatible = { "nxp,imx8mm-hantro", "nxp,imx8mp-hantro" };
Resource.reg0 = reg_mmio(0x38300000, 0x00100000);
Resource.irq0 = reg_irq(0x0, 0x7, 0x4);
end) -- vpu_g1@38300000
-- /vpu_g2@38310000 (1)
vpu_g2_38310000 = Io.Hw.Device(function()
compatible = { "nxp,imx8mm-hantro", "nxp,imx8mp-hantro" };
Resource.reg0 = reg_mmio(0x38310000, 0x00100000);
Resource.irq0 = reg_irq(0x0, 0x8, 0x4);
end) -- vpu_g2@38310000
-- /vpu_vc8000e@38320000 (1)
vpu_vc8000e_38320000 = Io.Hw.Device(function()
compatible = { "nxp,imx8mp-hantro-vc8000e" };
Resource.reg0 = reg_mmio(0x38320000, 0x00010000);
Resource.irq0 = reg_irq(0x0, 0x1e, 0x4);
end) -- vpu_vc8000e@38320000
-- /gpu3d@38000000 (1)
gpu3d_38000000 = Io.Hw.Arm_dma_device(function()
compatible = { "fsl,imx8-gpu" };
Resource.reg0 = reg_mmio(0x38000000, 0x00008000);
Resource.irq0 = reg_irq(0x0, 0x3, 0x4);
end) -- gpu3d@38000000
-- /gpu2d@38008000 (1)
gpu2d_38008000 = Io.Hw.Device(function()
compatible = { "fsl,imx8-gpu" };
Resource.reg0 = reg_mmio(0x38008000, 0x00008000);
Resource.irq0 = reg_irq(0x0, 0x19, 0x4);
end) -- gpu2d@38008000
-- /vipsi@38500000 (1)
vipsi_38500000 = Io.Hw.Device(function()
compatible = { "fsl,imx8-gpu", "fsl,imx8-vipsi" };
Resource.reg0 = reg_mmio(0x38500000, 0x00020000);
Resource.irq0 = reg_irq(0x0, 0xd, 0x4);
end) -- vipsi@38500000
-- /mix_gpu_ml@40000000 (1)
mix_gpu_ml_40000000 = Io.Hw.Device(function()
compatible = { "fsl,imx8mp-gpu", "fsl,imx8-gpu-ss" };
Resource.reg0 = reg_mmio(0x40000000, 0xc0000000);
Resource.reg1 = reg_mmio(0x00000000, 0x10000000);
end) -- mix_gpu_ml@40000000
-- /dsp@3b6e8000 (1)
dsp_3b6e8000 = Io.Hw.Device(function()
-- disabled
compatible = { "fsl,imx8mp-hifi4" };
Resource.reg0 = reg_mmio(0x3b6e8000, 0x00088000);
end) -- dsp@3b6e8000
-- /pwm-fan (1)
pwm_fan = Io.Hw.Device(function()
-- disabled
compatible = { "pwm-fan" };
-- ignore IRQs routed to interrupt-parent gpio@30240000
-- Resource.irq0 = reg_irq(0x12, 0x2);
end) -- pwm-fan
end)

View File

@@ -0,0 +1,297 @@
-- vim:set ft=lua:
--
-- Copyright (C) 2023 Kernkonzept GmbH.
-- Author(s): Christian Pötzsch <christian.poetzsch@kernkonzept.com>
-- Frank Mehnert <frank.mehnert@kernkonzept.com>
--
-- License: see LICENSE.spdx (in this directory or the directories above)
--
-- NXP/Freescale i.MX8
local Hw = Io.Hw
local Res = Io.Res
Io.hw_add_devices(function()
scu_5d1c0000 = Io.Hw.Scu_imx8qm(function()
compatible = { "fsl,imx8qm-mu", "fsl,imx6sx-mu" }
Resource.regs = reg_mmio(0x5d1c0000, 0x00010000)
Resource.irq0 = Io.Res.irq(32 + 177, Io.Resource.Irq_type_level_high)
end)
gpio_5d0b0000 = Io.Hw.Gpio_imx8qm_chip(function()
compatible = { "fsl,imx8qm-gpio", "l4vmm,imx8qm-gpio" }
Property.hid = "l4vmm,imx8qm-gpio"
Property.scu = scu_5d1c0000
Property.power = 202 -- IMX_SC_R_GPIO_3
Resource.regs = reg_mmio(0x5d0b0000, 0x00001000)
Resource.irq0 = Io.Res.irq(32 + 139, Io.Resource.Irq_type_level_high)
end)
gpio_5d0c0000 = Io.Hw.Gpio_imx8qm_chip(function()
compatible = { "fsl,imx8qm-gpio", "l4vmm,imx8qm-gpio" }
Property.hid = "l4vmm,imx8qm-gpio"
Property.scu = scu_5d1c0000
Property.power = 203 -- IMX_SC_R_GPIO_4
Resource.regs = reg_mmio(0x5d0c0000, 0x00001000)
Resource.irq0 = Io.Res.irq(32 + 140, Io.Resource.Irq_type_level_high)
end)
scu = scu_5d1c0000
gpio3 = gpio_5d0b0000
gpio4 = gpio_5d0c0000
end)
Io.Dt.add_children(Io.system_bus(), function()
bus_30000000 = Hw.Device(function() -- AIPS1
compatible = { "fsl,aips-bus", "simple-bus" }
Property.hid = "fsl,aips-bus"
Resource.req0 = Io.Res.mmio(0x30000000, 0x303fffff)
end)
bus_30400000 = Hw.Device(function() -- AIPS2
compatible = { "fsl,aips-bus", "simple-bus" }
Property.hid = "fsl,aips-bus"
Resource.req0 = Io.Res.mmio(0x30400000, 0x307fffff)
end)
bus_30800000 = Hw.Device(function() -- AIPS3
compatible = { "fsl,aips-bus", "simple-bus" }
Property.hid = "fsl,aips-bus"
Resource.req0 = Io.Res.mmio(0x30800000, 0x30bfffff)
end)
bus_32c00000 = Hw.Device(function() -- AIPS4
compatible = { "fsl,aips-bus", "simple-bus" }
Property.hid = "fsl,aips-bus"
Resource.req0 = Io.Res.mmio(0x32c00000, 0x32ffffff)
end)
clock_controller_30380000 = Hw.Device(function()
compatible = { "fsl,imx8mq-ccm" }
Property.hid = "fsl,imx8mq-ccm"
Resource.req0 = Io.Res.mmio(0x30380000, 0x3038ffff)
Resource.irq0 = Io.Res.irq(32 + 85, Io.Resource.Irq_type_level_high)
Resource.irq1 = Io.Res.irq(32 + 86, Io.Resource.Irq_type_level_high)
end)
gpc_303a0000 = Hw.Device(function()
compatible = { "fsl,imx8mq-gpc" }
Property.hid = "fsl,imx8mq-gpc"
Resource.req0 = Io.Res.mmio(0x303a0000, 0x303affff)
end)
timer_306a0000 = Hw.Device(function()
compatible = { "nxp,sysctr-timer" }
Property.hid = "nxp,sysctr-timer"
Resource.req0 = Io.Res.mmio(0x306a0000, 0x306bffff)
Resource.irq0 = Io.Res.irq(32 + 47, Io.Resource.Irq_type_level_high)
end)
efuse_30350000 = Hw.Device(function() -- ocotp
compatible = { "fsl,imx8mq-ocotp", "syscon" }
Property.hid = "fsl,imx8mq-ocotp"
Resource.req0 = Io.Res.mmio(0x30350000, 0x3035ffff)
end)
syscon_30360000 = Hw.Device(function()
compatible = { "fsl,imx8mq-anatop", "syscon" }
Property.hid = "fsl,imx8mq-anatop"
Resource.req0 = Io.Res.mmio(0x30360000, 0x3036ffff)
end)
mmc_30b40000 = Hw.Device(function() -- usdhc1
compatible = { "fsl,imx8mq-usdhc", "fsl,imx7d-usdhc" }
Property.hid = "fsl,imx8mq-usdhc"
Resource.req0 = Io.Res.mmio(0x30b40000, 0x30b4ffff)
Resource.irq0 = Io.Res.irq(32 + 22, Io.Resource.Irq_type_level_high)
end)
mmc_30b50000 = Hw.Device(function() -- usdhc2
compatible = { "fsl,imx8mq-usdhc", "fsl,imx7d-usdhc" }
Property.hid = "fsl,imx8mq-usdhc"
Resource.req0 = Io.Res.mmio(0x30b50000, 0x30b5ffff)
Resource.irq0 = Io.Res.irq(32 + 23, Io.Resource.Irq_type_level_high)
end)
gpio_30200000 = Hw.Device(function() -- gpio1
compatible = { "fsl,imx8mq-gpio", "fsl,imx35-gpio" }
Property.hid = "fsl,imx8mq-gpio"
Resource.req0 = Io.Res.mmio(0x30200000, 0x3020ffff)
Resource.irq0 = Io.Res.irq(32 + 64, Io.Resource.Irq_type_level_high)
Resource.irq1 = Io.Res.irq(32 + 65, Io.Resource.Irq_type_level_high)
end)
gpio_30210000 = Hw.Device(function() -- gpio2
compatible = { "fsl,imx8mq-gpio", "fsl,imx35-gpio" }
Property.hid = "fsl,imx8mq-gpio"
Resource.req0 = Io.Res.mmio(0x30210000, 0x3021ffff)
Resource.irq0 = Io.Res.irq(32 + 66, Io.Resource.Irq_type_level_high)
Resource.irq1 = Io.Res.irq(32 + 67, Io.Resource.Irq_type_level_high)
end)
gpio_30220000 = Hw.Device(function() -- gpio3
compatible = { "fsl,imx8mq-gpio", "fsl,imx35-gpio" }
Property.hid = "fsl,imx8mq-gpio"
Resource.req0 = Io.Res.mmio(0x30220000, 0x3022ffff)
Resource.irq0 = Io.Res.irq(32 + 68, Io.Resource.Irq_type_level_high)
Resource.irq1 = Io.Res.irq(32 + 69, Io.Resource.Irq_type_level_high)
end)
gpio_30230000 = Hw.Device(function() -- gpio4
compatible = { "fsl,imx8mq-gpio", "fsl,imx35-gpio" }
Property.hid = "fsl,imx8mq-gpio"
Resource.req0 = Io.Res.mmio(0x30230000, 0x3023ffff)
Resource.irq0 = Io.Res.irq(32 + 70, Io.Resource.Irq_type_level_high)
Resource.irq1 = Io.Res.irq(32 + 71, Io.Resource.Irq_type_level_high)
end)
gpio_30240000 = Hw.Device(function() -- gpio5
compatible = { "fsl,imx8mq-gpio", "fsl,imx35-gpio" }
Property.hid = "fsl,imx8mq-gpio"
Resource.req0 = Io.Res.mmio(0x30240000, 0x3024ffff)
Resource.irq0 = Io.Res.irq(32 + 72, Io.Resource.Irq_type_level_high)
Resource.irq1 = Io.Res.irq(32 + 73, Io.Resource.Irq_type_level_high)
end)
spi_30bb0000 = Hw.Device(function() -- qspi0
compatible = { "fsl,imx8mq-qspi", "fsl,imx7d-qspi" }
Property.hid = "fsl,imx8mq-qspi"
Resource.req0 = Io.Res.mmio(0x30bb0000, 0x30bbffff)
Resource.req1 = Io.Res.mmio(0x08000000, 0x17ffffff)
Resource.irq0 = Io.Res.irq(32 + 107, Io.Resource.Irq_type_level_high)
end)
ethernet_30be0000 = Hw.Device(function()
compatible = { "fsl,imx8mq-fec", "fsl,imx6sx-fec" }
Property.hid = "fsl,imx8mq-fec"
Resource.req0 = Io.Res.mmio(0x30be0000, 0x30beffff)
Resource.irq0 = Io.Res.irq(32 + 118, Io.Resource.Irq_type_level_high)
Resource.irq0 = Io.Res.irq(32 + 119, Io.Resource.Irq_type_level_high)
Resource.irq0 = Io.Res.irq(32 + 120, Io.Resource.Irq_type_level_high)
end)
ddr_pmu_3d800000 = Hw.Device(function()
compatible = { "fsl,imx8mq-ddr-pmu", "fsl,imx8m-ddr-pm" }
Property.hid = "fsl,imx8mq-ddr-pmu"
Resource.req0 = Io.Res.mmio(0x3d800000, 0x3dbfffff)
Resource.irq0 = Io.Res.irq(32 + 98, Io.Resource.Irq_type_level_high)
end)
pinctrl_30330000 = Hw.Device(function()
compatible = { "fsl,imx8mq-iomuxc" }
Property.hid = "fsl,imx8mq-iomuxc"
Resource.req0 = Io.Res.mmio(0x30330000, 0x3033ffff)
end)
pwm_30660000 = Hw.Device(function()
compatible = { "fsl,imx8mq-pwm", "fsl,imx27-pwm" }
Property.hid = "fsl,imx8mq-pwm"
Resource.req0 = Io.Res.mmio(0x30660000, 0x3066ffff)
Resource.irq0 = Io.Res.irq(32 + 81, Io.Resource.Irq_type_level_high)
end)
pwm_30670000 = Hw.Device(function()
compatible = { "fsl,imx8mq-pwm", "fsl,imx27-pwm" }
Property.hid = "fsl,imx8mq-pwm"
Resource.req0 = Io.Res.mmio(0x30670000, 0x3067ffff)
Resource.irq0 = Io.Res.irq(32 + 82, Io.Resource.Irq_type_level_high)
end)
pwm_30680000 = Hw.Device(function()
compatible = { "fsl,imx8mq-pwm", "fsl,imx27-pwm" }
Property.hid = "fsl,imx8mq-pwm"
Resource.req0 = Io.Res.mmio(0x30680000, 0x3068ffff)
Resource.irq0 = Io.Res.irq(32 + 83, Io.Resource.Irq_type_level_high)
end)
pwm_30690000 = Hw.Device(function()
compatible = { "fsl,imx8mq-pwm", "fsl,imx27-pwm" }
Property.hid = "fsl,imx8mq-pwm"
Resource.req0 = Io.Res.mmio(0x30690000, 0x3069ffff)
Resource.irq0 = Io.Res.irq(32 + 84, Io.Resource.Irq_type_level_high)
end)
reset_controller_30390000 = Hw.Device(function()
compatible = { "fsl,imx8mq-src", "syscon" }
Property.hid = "fsl,imx8mq-src", "syscon"
Resource.req0 = Io.Res.mmio(0x30390000, 0x3039ffff)
Resource.irq0 = Io.Res.irq(32 + 89, Io.Resource.Irq_type_level_high)
end)
i2c_30a20000 = Hw.Device(function()
compatible = { "fsl,imx8mq-i2c", "fsl,imx21-i2c" }
Property.hid = "fsl,imx8mq-i2c"
Resource.req0 = Io.Res.mmio(0x30a20000, 0x30a2ffff)
Resource.irq0 = Io.Res.irq(32 + 35, Io.Resource.Irq_type_level_high)
end)
irqsteer_32e2d000 = Hw.Device(function()
compatible = { "fsl,imx8m-irqsteer", "fsl,imx-irqsteer" }
Property.hid = "fsl,imx8m-irqsteer"
Resource.req0 = Io.Res.mmio(0x32e2d000, 0x32e2dfff)
Resource.irq0 = Io.Res.irq(32 + 18, Io.Resource.Irq_type_level_high)
end)
syscon_3034000 = Hw.Device(function()
compatible = { "fsl,imx8mq-iomuxc-gpr", "fsl,imx6q-iomuxc-gpr",
"syscon", "simple-mfd" }
Property.hid = "fsl,imx8mq-iomuxc-gpr"
Resource.req = Io.Res.mmio(0x30340000, 0x3034ffff)
end)
-- IO drives the PCIe controller and provides the attached PCIe devices to
-- clients.
--[[
pciec0 = Io.Hw.Pcie_imx8qm(function()
Property.scu = scu
Property.gpio3 = gpio3
Property.gpio4 = gpio4
Property.power =
{
152, -- IMX_SC_R_PCIE_A (pciea and HSIO crr)
153, -- IMX_SC_R_SERDES_0 (pciea and HSIO crr)
172, -- IMX_SC_R_HSIO_GPIO (pciea and HSIO crr)
169, -- IMX_SC_R_PCIE_B (pcieb and HSIO crr)
171, -- IMX_SC_R_SERDES_1 (pcieb and HSIO crr)
}
Property.pads =
{
129, 3, 0x00000021, -- IMX8QM_SAI1_RXFS_LSIO_GPIO3_IO14
}
Property.clks = {} -- no clocks need to be enabled
Property.regs_base = 0x5f000000 -- reg: dbi (verified by pci-imx6.c)
Property.regs_size = 0x00010000
Property.cfg_base = 0x6ff00000 -- reg: config
Property.cfg_size = 0x00080000
-- ignore the 1st ranges statement in pciea because that's port I/O
Property.mem_base = 0x60000000 -- downstream non-prefetchable 32-bit MMIO
Property.mem_size = 0x0ff00000
Property.cpu_fixup = 0x40000000 -- see 'local-addr'
Property.irq_pin_a = 32 + 73
Property.irq_pin_b = 32 + 74
Property.irq_pin_c = 32 + 75
Property.irq_pin_d = 32 + 76
Property.lanes = 1
end)
--]]
-- IO makes the PCIe controller available to a client. The client must know
-- how to drive the controller.
--[[
pcie_5f000000 = Hw.Device(function()
compatible = { "fsl,imx8qm-pcie" }
Property.hid= "fsl,imx8qm-pcie"
Resource.req0 = Io.Res.mmio(0x5f000000, 0x5f00ffff) -- controller reg
Resource.req1 = Io.Res.mmio(0x6ff00000, 0x6ff7ffff) -- config space
Resource.irq0 = Io.Res.irq(32 + 73, Io.Resource.Irq_type_level_high)
Resource.irq1 = Io.Res.irq(32 + 74, Io.Resource.Irq_type_level_high)
Resource.irq2 = Io.Res.irq(32 + 75, Io.Resource.Irq_type_level_high)
Resource.irq3 = Io.Res.irq(32 + 76, Io.Resource.Irq_type_level_high)
end)
--]]
end)

View File

@@ -0,0 +1,122 @@
-- vim:set ft=lua:
--
-- Copyright (C) 2021-2023 Stephan Gerhold <stephan@gerhold.net>
--
-- License: see LICENSE.spdx (in this directory or the directories above)
--
-- Qualcomm Snapdragon 410 (MSM8916)
local Hw = Io.Hw
local Res = Io.Res
Io.hw_add_devices(function()
TLMM = Hw.Gpio_qcom_chip(function()
compatible = {"qcom,msm8916-pinctrl", "qcom,tlmm"};
Resource.reg0 = Res.mmio(0x01000000, 0x010fffff);
Resource.irq0 = Res.irq(32+208, Io.Resource.Irq_type_level_high);
Property.ngpios = 122;
Property.target_proc = 0x4;
Property.reg_gpio_size = 0x1000;
end);
GCC = Hw.Device(function()
compatible = {"qcom,gcc-msm8916"};
Resource.reg0 = Res.mmio(0x01800000, 0x0187ffff);
-- Linux expects the clock controller to be represented as one large device,
-- covering all clocks of the SoC. With minor driver changes it can be also
-- partitioned into smaller parts that can be assigned separately to
-- different virtual machines.
Child.USB = Hw.Device(function()
compatible = {"qcom,gcc-msm8916-usb"};
Resource.reg0 = Res.mmio(0x01841000, 0x01841fff);
end);
Child.SDCC1 = Hw.Device(function()
compatible = {"qcom,gcc-msm8916-sdcc1"};
Resource.reg0 = Res.mmio(0x01842000, 0x01842fff);
end);
Child.SDCC2 = Hw.Device(function()
compatible = {"qcom,gcc-msm8916-sdcc2"};
Resource.reg0 = Res.mmio(0x01843000, 0x01843fff);
end);
end);
MDSS = Hw.Arm_dma_device(function()
compatible = {"qcom,msm8916-mdss", "qcom,mdss"};
Resource.reg0 = Res.mmio(0x01a00000, 0x01afffff);
Resource.irq0 = Res.irq(32+72, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
Property.iommu = 0;
Property.sid = 0xc00;
Child.MDP5 = Hw.Arm_dma_device(function()
compatible = {"qcom,msm8916-mdp5", "qcom,mdp5"};
Resource.reg0 = Res.mmio(0x01a01000, 0x01a8ffff);
Property.flags = Io.Hw_device_DF_dma_supported;
Property.iommu = 0;
Property.sid = 0xc00;
Property.hid = "qcom,mdp5"; -- Used by lcd driver
end);
end);
SDHC1 = Hw.Arm_dma_device(function()
compatible = {"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"};
Resource.reg0 = Res.mmio(0x07824000, 0x07824aff);
Resource.irq0 = Res.irq(32+123, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(32+138, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
Property.iommu = 0;
Property.sid = 0x100;
end)
SDHC2 = Hw.Arm_dma_device(function()
compatible = {"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"};
Resource.reg0 = Res.mmio(0x07864000, 0x07864aff);
Resource.irq0 = Res.irq(32+125, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(32+221, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
Property.iommu = 0;
Property.sid = 0x140;
end)
I2C1 = Hw.Device(function()
compatible = {"qcom,i2c-qup-v2.2.1", "qcom,i2c-qup"};
Resource.reg0 = Res.mmio(0x078b5000, 0x078b54ff);
Resource.irq0 = Res.irq(32+95, Io.Resource.Irq_type_level_high);
end);
I2C2 = Hw.Device(function()
compatible = {"qcom,i2c-qup-v2.2.1", "qcom,i2c-qup"};
Resource.reg0 = Res.mmio(0x078b6000, 0x078b64ff);
Resource.irq0 = Res.irq(32+96, Io.Resource.Irq_type_level_high);
end);
I2C3 = Hw.Device(function()
compatible = {"qcom,i2c-qup-v2.2.1", "qcom,i2c-qup"};
Resource.reg0 = Res.mmio(0x078b7000, 0x078b74ff);
Resource.irq0 = Res.irq(32+97, Io.Resource.Irq_type_level_high);
end);
I2C4 = Hw.Device(function()
compatible = {"qcom,i2c-qup-v2.2.1", "qcom,i2c-qup"};
Resource.reg0 = Res.mmio(0x078b8000, 0x078b84ff);
Resource.irq0 = Res.irq(32+98, Io.Resource.Irq_type_level_high);
end);
I2C5 = Hw.Device(function()
compatible = {"qcom,i2c-qup-v2.2.1", "qcom,i2c-qup"};
Resource.reg0 = Res.mmio(0x078b9000, 0x078b94ff);
Resource.irq0 = Res.irq(32+99, Io.Resource.Irq_type_level_high);
end);
I2C6 = Hw.Device(function()
compatible = {"qcom,i2c-qup-v2.2.1", "qcom,i2c-qup"};
Resource.reg0 = Res.mmio(0x078ba000, 0x078ba4ff);
Resource.irq0 = Res.irq(32+100, Io.Resource.Irq_type_level_high);
end);
USB = Hw.Arm_dma_device(function()
compatible = {"qcom,ci-hdrc"};
Resource.reg0 = Res.mmio(0x078d9000, 0x078d9fff);
Resource.irq0 = Res.irq(32+134, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(32+140, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
Property.iommu = 0;
Property.sid = 0x2c0;
end)
end)

View File

@@ -0,0 +1,36 @@
-- vim:set ft=lua:
--
-- Copyright (C) 2021-2023 Stephan Gerhold <stephan@gerhold.net>
--
-- License: see LICENSE.spdx (in this directory or the directories above)
--
-- Qualcomm Snapdragon 800 (MSM8974)
local Hw = Io.Hw
local Res = Io.Res
Io.hw_add_devices(function()
TLMM = Hw.Gpio_qcom_chip(function()
compatible = {"qcom,msm8974-pinctrl", "qcom,tlmm"};
Resource.reg0 = Res.mmio(0xfd511000, 0xfd513fff);
Resource.irq0 = Res.irq(32+208, Io.Resource.Irq_type_level_high);
Property.ngpios = 146;
Property.target_proc = 0x4;
Property.reg_gpio_size = 0x10;
end);
MDSS = Hw.Device(function()
compatible = {"qcom,msm8974-mdss", "qcom,mdss"};
Resource.reg0 = Res.mmio(0xfd900000, 0xfd9fffff);
Resource.irq0 = Res.irq(32+72, Io.Resource.Irq_type_level_high);
Property.flags = Io.Hw_device_DF_dma_supported;
Child.MDP5 = Hw.Device(function()
compatible = {"qcom,msm8974-mdp5", "qcom,mdp5"};
Resource.reg0 = Res.mmio(0xfd900100, 0xfd9220ff);
Property.flags = Io.Hw_device_DF_dma_supported;
Property.hid = "qcom,mdp5"; -- Used by lcd driver
end);
end);
end)

View File

@@ -0,0 +1,148 @@
-- vim:set ft=lua:
--
-- Copyright (C) 2015 Kernkonzept GmbH.
-- Author(s): Matthias Lange <matthias.lange@kernkonzept.com>
--
-- License: see LICENSE.spdx (in this directory or the directories above)
--
-- OMAP5 (OMAP5, omap5uevm)
local Hw = Io.Hw
local Res = Io.Res
Io.hw_add_devices(function()
Property.hid = "System Bus";
Scm_pad_core = Hw.Scm_omap(function()
Property.hid = "Sysctrl_Padconf_Core";
compatible = {"ti,omap5-scrm"};
Resource.regs = Res.mmio(0x4a002800, 0x4a002fff);
end);
Scm_pad_wkup = Hw.Scm_omap(function()
Property.hid = "Sysctrl_Padconf_Wkup";
compatible = {"ti,omap5-scrm"};
Resource.regs = Res.mmio(0x4ae0c800, 0x4ae0cfff);
end);
GPIO = Hw.Device(function()
Property.hid = "Omap54xx GPIO";
-- The offset values for the scm are taken from the OMAP543x TRM
-- chapter 18 - System Control Module, table 18-8 (wakeup pad configuration
-- register fields) and table 18-9 (core pad configuration register fields)
GPIO1 = Hw.Gpio_omap54x_chip(function()
compatible = {"ti,omap5-gpio"};
Resource.regs = Res.mmio(0x4ae10000, 0x4ae10fff);
Resource.irq = Res.irq(61, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap54x-GPIO1";
Property.scm =
{
Scm_pad_wkup,
0x070, 0x072, 0x074, 0x076, 0x078, 0x07a, 0x044, 0x046,
0x060, 0x05e, 0x05c, 0x05a, 0x05c, 0x056, 0x040, 0x042,
0x068, 0x06a, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
};
end);
GPIO2 = Hw.Gpio_omap54x_chip(function()
compatible = {"ti,omap5-gpio"};
Resource.regs = Res.mmio(0x48055000, 0x48055fff);
Resource.irq = Res.irq(62, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap54x-GPIO2";
Property.scm =
{
Scm_pad_core,
0x08e, 0x054, 0x056, 0x058, 0x05a, 0x05c, 0x05e, 0x060,
0x062, 0x064, 0x066, 0x068, 0x06a, 0x08c, 0x040, 0x042,
0x044, 0x046, 0x048, 0x04a, 0x04c, 0x04e, 0x050, 0x052,
0x06c, 0x06e, 0x070, 0x072, 0x074, 0x076, 0x078, 0x07a
};
end);
GPIO3 = Hw.Gpio_omap54x_chip(function()
compatible = {"ti,omap5-gpio"};
Resource.regs = Res.mmio(0x48057000, 0x48057fff);
Resource.irq = Res.irq(63, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap54x-GPIO3";
Property.scm =
{
Scm_pad_core,
0x090, 0x092, 0x094, 0x096, 0x098, 0x09a, 0x09c, 0x09e,
0x0a0, 0x0a2, 0x0a4, 0x0a6, 0x0a8, 0x0aa, 0x0ac, 0x0ae,
0x0b0, 0x0b2, 0x0b4, 0x0b6, 0x0b8, 0x0ba, 0x0bc, 0x0be,
-1, -1, -1, -1, 0x0c0, 0x0c2, 0x0c4, 0x0c6
};
end);
GPIO4 = Hw.Gpio_omap54x_chip(function()
compatible = {"ti,omap5-gpio"};
Resource.regs = Res.mmio(0x48059000, 0x48059fff);
Resource.irq = Res.irq(64, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap54x-GPIO4";
Property.scm =
{
Scm_pad_core,
0x182, 0x184, 0x186, 0x188, 0x18a, 0x18c, 0x18e, 0x190,
0x192, 0x194, 0x196, 0x198, 0x19a, 0x19c, 0x19e, 0x1a0,
0x1a2, 0x07c, 0x07e, 0x080, 0x082, 0x084, 0x086, 0x088,
0x08a, -1, -1, -1, -1, -1, -1, -1
};
end);
GPIO5 = Hw.Gpio_omap54x_chip(function()
compatible = {"ti,omap5-gpio"};
Resource.regs = Res.mmio(0x4805b000, 0x4805bfff);
Resource.irq = Res.irq(65, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap54x-GPIO5";
Property.scm =
{
Scm_pad_core,
0x1a4, 0x1a6, 0x1a8, 0x1aa, 0x1ac, 0x1ae, 0x1b0, 0x1b2,
0x1b4, 0x1b6, 0x1b8, 0x1ba, 0x1bc, 0x1be, 0x1c0, 0x1c2,
0x1c4, 0x1ca, 0x1cc, 0x1c6, 0x1c8, 0x1ce, 0x1d0, 0x1d2,
0x1d4, 0x1d6, 0x1d8, 0x1da, 0x1dc, -1, 0x1de, 0x1e0
};
end);
GPIO6 = Hw.Gpio_omap54x_chip(function()
compatible = {"ti,omap5-gpio"};
Resource.regs = Res.mmio(0x4805d000, 0x4805dfff);
Resource.irq = Res.irq(66, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap54x-GPIO6";
Property.scm =
{
Scm_pad_core,
0x12c, 0x12e, 0x124, 0x126, 0x12a, 0x128, 0x122, 0x120,
0x11e, 0x11c, 0x11a, 0x118, 0x116, 0x114, 0x112, 0x110,
0x10e, 0x10c, 0x10a, 0x108, 0x106, 0x104, 0x130, 0x132,
0x134, 0x136, 0x138, 0x13a, 0x0c8, 0x0ca, 0x0f6, 0x0f4
};
end);
GPIO7 = Hw.Gpio_omap54x_chip(function()
compatible = {"ti,omap5-gpio"};
Resource.regs = Res.mmio(0x48051000, 0x48051fff);
Resource.irq = Res.irq(67, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap54x-GPIO7";
Property.scm =
{
Scm_pad_core,
0x13c, 0x13e, 0x140, 0x142, 0x102, 0x0fc, 0x0fe, 0x100,
0x0f8, 0x0fa, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1
};
end);
GPIO8 = Hw.Gpio_omap54x_chip(function()
compatible = {"ti,omap5-gpio"};
Resource.regs = Res.mmio(0x48053000, 0x48053fff);
Resource.irq = Res.irq(153, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap54x-GPIO8";
Property.scm =
{
Scm_pad_core,
0x16c, 0x16e, 0x170, 0x172, 0x174, 0x176, 0x178, 0x17a,
0x17c, 0x17e, 0x180, -1, 0x158, 0x15a, 0x15e, 0x15c,
0x162, 0x160, 0x164, 0x166, 0x168, 0x16a, 0x14c, 0x14e,
0x152, 0x150, 0x156, 0x154, 0x146, 0x144, 0x14a, 0x148
};
end);
end);
end)

View File

@@ -0,0 +1,120 @@
-- vim:set ft=lua:
--
-- Copyright (C) 2014 Kernkonzept GmbH.
-- Author(s): Matthias Lange <matthias.lange@kernkonzept.com>
--
-- License: see LICENSE.spdx (in this directory or the directories above)
--
-- OMAP4 (OMAP4, Pandaboard)
local Hw = Io.Hw
local Res = Io.Res
Io.hw_add_devices(function()
Property.hid = "System Bus";
Scm_pad_core = Hw.Scm_omap(function()
Property.hid = "Sysctrl_Padconf_Core";
compatible = {"ti,omap4-scrm"};
Resource.regs = Res.mmio(0x4a100000, 0x4a100fff);
end);
Scm_pad_wkup = Hw.Scm_omap(function()
Property.hid = "Sysctrl_Padconf_Wkup";
compatible = {"ti,omap4-scrm"};
Resource.regs = Res.mmio(0x4a31e000, 0x4a31efff);
end);
GPIO = Hw.Device(function()
Property.hid = "Omap44x GPIO";
-- The offset values for the scm are taken from the OMAP4430 ES TRM
-- chapter 18 - System Control Module, table 18-8 (wakeup pad configuration
-- register fields) and table 18-9 (core pad configuration register fields)
GPIO1 = Hw.Gpio_omap44x_chip(function()
compatible = {"ti,omap4-gpio"};
Resource.regs = Res.mmio(0x4a310000, 0x4a310fff);
Resource.irq = Res.irq(61, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap44x-GPIO1";
Property.scm =
{
Scm_pad_wkup,
0x184, 0x186, 0x190, 0x192, 0x048, 0x050, 0x054, 0x05a,
0x05c, 0x068, 0x06a, 0x1ae, 0x1b0, 0x1b2, 0x1b4, 0x1b6,
0x1b8, 0x1ba, 0x1bc, 0x1be, 0x1c0, 0x1c2, 0x1c4, 0x1c6,
0x1c8, 0x1ca, 0x1cc, 0x1ce, 0x1d0, 0x066, 0x056, 0x058
};
end);
GPIO2 = Hw.Gpio_omap44x_chip(function()
compatible = {"ti,omap4-gpio"};
Resource.regs = Res.mmio(0x48055000, 0x48055fff);
Resource.irq = Res.irq(62, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap44x-GPIO2";
Property.scm =
{
Scm_pad_core,
0x050, 0x052, 0x054, 0x056, 0x058, 0x05a, 0x05c, 0x05e,
0x060, 0x062, 0x064, 0x064, 0x068, 0x06a, 0x06c, 0x06e,
0x070, 0x072, 0x074, 0x076, 0x078, 0x07a, 0x07c, 0x07e,
0x080, 0x040, 0x042, 0x086, 0x088, 0x08a, 0x08c, 0x098
};
end);
GPIO3 = Hw.Gpio_omap44x_chip(function()
compatible = {"ti,omap4-gpio"};
Resource.regs = Res.mmio(0x48057000, 0x48057fff);
Resource.irq = Res.irq(63, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap44x-GPIO3";
Property.scm =
{
Scm_pad_core,
0x09a, 0x09c, 0x09e, 0x0a0, 0x0a2, 0x0a4, 0x0a6, 0x0a8,
0x0aa, 0x0ac, 0x0ae, 0x0b0, 0x0b2, 0x0b4, 0x0b6, 0x0b8,
0x0ba, 0x0bc, 0x0be, 0x0c0, 0x0c2, 0x0c4, 0x0c6, 0x0c8,
0x0ca, 0x0cc, 0x0ce, 0x0d0, 0x0d2, 0x0d4, 0x0d6, 0x0d8
};
end);
GPIO4 = Hw.Gpio_omap44x_chip(function()
compatible = {"ti,omap4-gpio"};
Resource.regs = Res.mmio(0x48059000, 0x48059fff);
Resource.irq = Res.irq(64, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap44x-GPIO4";
Property.scm =
{
Scm_pad_core,
0x0da, 0x0dc, 0x0de, 0x0e0, 0x0e2, 0x0e4, 0x0e6, 0x0e8,
0x0ea, 0x0ec, 0x0ee, 0x0f0, 0x0f2, 0x0f4, 0x0f6, 0x0f8,
0x0fa, 0x0fc, 0x0fe, 0x100, 0x102, 0x104, 0x10e, 0x110,
0x112, 0x114, 0x116, 0x118, 0x11a, 0x11c, 0x11e, 0x120
};
end);
GPIO5 = Hw.Gpio_omap44x_chip(function()
compatible = {"ti,omap4-gpio"};
Resource.regs = Res.mmio(0x4805b000, 0x4805bfff);
Resource.irq = Res.irq(65, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap44x-GPIO5";
Property.scm =
{
Scm_pad_core,
0x126, 0x128, 0x12a, 0x12c, 0x12e, 0x130, 0x132, 0x134,
0x136, 0x138, 0x13a, 0x13c, 0x13e, 0x140, 0x142, 0x144,
0x146, 0x148, 0x14a, 0x14c, 0x14e, 0x150, 0x152, 0x154,
0x156, 0x158, 0x15a, 0x15c, 0x15e, 0x160, 0x162, 0x164
};
end);
GPIO6 = Hw.Gpio_omap44x_chip(function()
compatible = {"ti,omap4-gpio"};
Resource.regs = Res.mmio(0x4805d000, 0x4805dfff);
Resource.irq = Res.irq(66, Io.Resource.Irq_type_level_high);
Property.hid = "gpio-omap44x-GPIO6";
Property.scm =
{
Scm_pad_core,
0x166, 0x168, 0x16a, 0x16c, 0x16e, 0x170, 0x172, 0x174,
0x176, 0x178, 0x17a, 0x17c, 0x17e, 0x180, 0x182, 0x188,
0x18a, 0x18c, 0x18e, 0x044, 0x046, 0x19a, 0x19c, 0x1a0,
0x1a2, 0x1a4, 0x1a6, 0x1a8, 0x1aa, 0x1ac, 0x1d2, 0x1d4
};
end);
end);
end)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
-- vi:ft=lua
local Res = Io.Res
local Hw = Io.Hw
Io.hw_add_devices(function()
GPIO = Hw.Gpio_bcm2835_chip(function()
compatible = {"brcm,bcm2835-gpio"};
Property.hid = "gpio-bcm2835-GPIO";
Resource.regs = Res.mmio(0xfe200000, 0xfe2000b4 - 1);
Property.pins = 58;
Resource.int0 = Res.irq(145, Io.Resource.Irq_type_level_high)
Resource.int2 = Res.irq(147, Io.Resource.Irq_type_level_high)
end);
MBOX = Hw.Device(function()
compatible = {"brcm,bcm2835-mbox"};
Property.hid = "BCM2835_mbox";
Property.flags = Io.Hw_device_DF_dma_supported;
Resource.regs = Res.mmio(0xfe00b880, 0xfe00b8bf);
Resource.irq0 = Res.irq(32 + 0x21, Io.Resource.Irq_type_level_high);
end);
FB = Hw.Device(function()
Property.hid = "BCM2835_fb";
Resource.mem = Res.mmio(0x5c006000, 0x60005fff);
end);
i2c1 = Hw.Device(function()
compatible = {"brcm,bcm2835-i2c"};
Property.hid = "BCM2835_bsc1";
Resource.regs = Res.mmio(0xfe804000, 0xfe804fff);
Resource.irq = Res.irq(32 + 117, Io.Resource.Irq_type_level_high);
Resource.pins = Io.Gpio_resource(GPIO, 2, 3)
end);
cprman = Hw.Device(function()
compatible = {"brcm,bcm2835-cprman"};
Resource.regs = Res.mmio(0xfe101000, 0xfe103fff);
end);
EMMC = Hw.Device(function()
compatible = {"brcm,bcm2711-emmc2"};
Property.hid = "EMMC-bcm2711-emmc2";
Property.flags = Io.Hw_device_DF_dma_supported;
-- The device lives at the local address 0x7e340000, but the bus translates
-- that range.
Resource.regs = Res.mmio(0xfe340000, 0xfe340000 + 0x100 - 1);
Resource.irq = Res.irq(32 + 126, Io.Resource.Irq_type_level_high);
end);
SPI = Hw.Device(function()
compatible = {"brcm,bcm2835-spi"};
Property.hid = "BCM2835_spi";
Resource.regs = Res.mmio(0xFE204000, 0xFE204000 + 0x200 - 1);
Resource.irq0 = Res.irq(32 + 118, Io.Resource.Irq_type_level_high);
Resource.pins = Io.Gpio_resource(GPIO, 7, 11);
end);
dma_7e007000 = Io.Hw.Device(function()
compatible = { "brcm,bcm2835-dma" };
-- [0x7e007000, 0x00000b00]
Property.flags = Io.Hw_device_DF_dma_supported;
Resource.reg0 = Res.mmio(0xfe007000, 0xfe007b00 - 1);
Resource.irq0 = Res.irq(32 + 0x50, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(32 + 0x51, Io.Resource.Irq_type_level_high);
Resource.irq2 = Res.irq(32 + 0x52, Io.Resource.Irq_type_level_high);
Resource.irq3 = Res.irq(32 + 0x53, Io.Resource.Irq_type_level_high);
Resource.irq4 = Res.irq(32 + 0x54, Io.Resource.Irq_type_level_high);
Resource.irq5 = Res.irq(32 + 0x55, Io.Resource.Irq_type_level_high);
Resource.irq6 = Res.irq(32 + 0x56, Io.Resource.Irq_type_level_high);
Resource.irq7 = Res.irq(32 + 0x57, Io.Resource.Irq_type_level_high);
Resource.irq9 = Res.irq(32 + 0x58, Io.Resource.Irq_type_level_high);
end) -- dma@7e007000
ethernet_7d580000 = Io.Hw.Device(function()
compatible = { "brcm,bcm2711-genet-v5", "brcm,genet-v5" };
Property.flags = Io.Hw_device_DF_dma_supported;
Resource.reg0 = Res.mmio(0xfd580000, 0xfd580000 + 0x10000 - 1);
Resource.irq0 = Res.irq(32 + 0x9d, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(32 + 0x9e, Io.Resource.Irq_type_level_high);
end)
pcie_7d500000 = Io.Hw.Device(function()
compatible = { "brcm,bcm2711-pcie" };
-- [0x7d500000, 0x00009310]
Resource.reg0 = Res.mmio(0xfd500000, 0xfd500000 + 0x00009310 - 1);
-- from ranges [ 0xf8000000 -> 0x600000000, 0x4000000 ]
Resource.reg1 = Res.mmio(0x600000000, 0x600000000 + 0x4000000 - 1);
Resource.irq0 = Res.irq(32 + 0x93, Io.Resource.Irq_type_level_high);
Resource.irq1 = Res.irq(32 + 0x94, Io.Resource.Irq_type_level_high);
end)
end)

View File

@@ -0,0 +1,44 @@
-- vi:ft=lua
local Res = Io.Res
local Hw = Io.Hw
Io.hw_add_devices(function()
GPIO = Hw.Gpio_bcm2835_chip(function()
compatible = {"brcm,bcm2835-gpio"};
Property.hid = "gpio-bcm2835-GPIO";
Property.pins = 54;
Resource.regs = Res.mmio(0x20200000, 0x202000b4);
Resource.int0 = Res.irq(49);
Resource.int2 = Res.irq(51);
end);
MBOX = Hw.Device(function()
Property.hid = "BCM2835_mbox";
Resource.regs = Res.mmio(0x2000b880, 0x2000bfff);
end);
FB = Hw.Device(function()
Property.hid = "BCM2835_fb";
Resource.mem = Res.mmio(0x5c006000, 0x60005fff);
end);
BSC2 = Hw.Device(function()
compatible = {"brcm,bcm2835-i2c"};
Property.hid = "BCM2835_bsc2";
Resource.regs = Res.mmio(0x20805000, 0x20805fff);
Resource.irq = Res.irq(53, Io.Resource.Irq_type_raising_edge);
end);
EMMC = Hw.Device(function()
compatible = {"brcm,bcm2711-emmc2"};
Property.hid = "EMMC-bcm2711-emmc2";
Property.flags = Io.Hw_device_DF_dma_supported;
-- The device lives at the local address 0x7e340000, but the bus translates
-- that range.
Resource.regs = Res.mmio(0xfe340000, 0xfe340000 + 0x100 - 1);
Resource.irq = Res.irq(32 + 126, Io.Resource.Irq_type_level_high);
end);
end)

View File

@@ -0,0 +1,77 @@
-- vi:ft=lua
--
-- (c) 2008-2009 Technische Universität Dresden
-- License: see LICENSE.spdx (in this directory or the directories above)
-- multi-core EB (PB11MP)
local Hw = Io.Hw
local Res = Io.Res
Io.hw_add_devices(function()
CTRL = Hw.Device(function()
Property.hid = "System Control";
compatible = {"arm,sysctl"};
Resource.regs = Res.mmio(0x10000000, 0x10000fff);
end);
LCD = Hw.Device(function()
Property.hid = "AMBA PL110";
compatible = {"arm,pl111","arm,primecell"};
Resource.regs = Res.mmio(0x10020000, 0x10020fff);
Resource.irq = Res.irq(55);
end);
KBD = Hw.Device(function()
Property.hid = "AMBA KMI Kbd";
compatible = {"arm,pl050","arm,primecell"};
Resource.irq = Res.irq(39);
Resource.regs = Res.mmio(0x10006000, 0x10006fff);
end);
MOUSE = Hw.Device(function()
Property.hid = "AMBA KMI mou";
compatible = {"arm,pl050","arm,primecell"};
Resource.regs = Res.mmio(0x10007000, 0x10007fff);
Resource.irq = Res.irq(40);
end);
GPIO0 = Hw.Device(function()
Property.hid = "AMBA PL061 dev0";
compatible = {"arm,pl061", "arm,primecell"};
Resource.regs = Res.mmio(0x10013000, 0x10013fff);
Resource.irq = Res.irq(6);
end);
GPIO1 = Hw.Device(function()
Property.hid = "AMBA PL061 dev1";
compatible = {"arm,pl061", "arm,primecell"};
Resource.regs = Res.mmio(0x10014000, 0x10014fff);
Resource.irq = Res.irq(7);
end);
COMPACTFLASH = Hw.Device(function()
Property.hid = "compactflash"; -- FIXME: should be "XXX flash" or something
Resource.mem0 = Res.mmio(0x18000000, 0x180000ff);
Resource.mem1 = Res.mmio(0x18000100, 0x180003ff);
end);
AACI = Hw.Device(function()
Property.hid = "aaci";
compatible = {"arm,pl041", "arm,primecell"};
Resource.regs = Res.mmio(0x10004000, 0x10004fff);
Resource.irq = Res.irq(32);
end);
NIC = Hw.Device(function()
Property.hid = "smsc911x";
Resource.regs = Res.mmio(0x4e000000, 0x4e000fff);
Resource.irq = Res.irq(41);
end);
MEM1 = Hw.Device(function()
Property.hid = "foomem";
Resource.mmio = Io.Mmio_data_space(0x10000, 0);
end);
end)

View File

@@ -0,0 +1,64 @@
-- vi:ft=lua
-- (c) 2008-2009 Technische Universität Dresden
-- License: see LICENSE.spdx (in this directory or the directories above)
-- Device tree for ARM Realview Emulation Baseboard (ARM926EJ-S)
-- This device tree works with the Qemu 'realview-eb' machine
local Res = Io.Res
local Hw = Io.Hw
Io.hw_add_devices(function()
CTRL = Hw.Device(function()
Property.hid = "System Control";
compatible = {"arm,sysctl"};
Resource.regs = Res.mmio(0x10000000, 0x10000fff);
end);
LCD = Hw.Device(function()
Property.hid = "AMBA PL110";
compatible = {"arm,pl111","arm,primecell"};
Resource.regs = Res.mmio(0x10020000, 0x10020fff);
end);
KBD = Hw.Device(function()
Property.hid = "AMBA KMI Kbd";
compatible = {"arm,pl050","arm,primecell"};
Resource.irq = Res.irq(52);
Resource.regs = Res.mmio(0x10006000, 0x10006fff);
end)
MOUSE = Hw.Device(function()
Property.hid = "AMBA KMI mou";
compatible = {"arm,pl050","arm,primecell"};
Resource.regs = Res.mmio(0x10007000, 0x10007fff);
Resource.irq = Res.irq(53);
end);
NIC = Hw.Device(function()
Property.hid = "smc91x";
compatible = {"smsc,lan9118"};
Resource.regs = Res.mmio(0x4e000000, 0x4e000fff);
Resource.irq = Res.irq(60);
end);
-- this device is only available on a real machine and
-- not part of Qemu's machine emulation
PCI0 = Hw.Pci_iomem_root_bridge(function()
Property.hid = "PNP0A03";
Property.iobase = 0x62000000;
Property.iosize = 0x00010000;
Property.dev_start = 0x64000000;
Property.dev_end = 0x6fffffff;
Property.int_a = 80;
Property.int_b = 81;
Property.int_c = 82;
Property.int_d = 83;
end);
MEM1 = Hw.Device(function()
Property.hid = "foomem";
Resource.mmio = Io.Mmio_data_space(0x10000, 0);
end);
end)

View File

@@ -0,0 +1,89 @@
-- vi:ft=lua
-- ARM Realview PBX Platform
local Res = Io.Res
local Hw = Io.Hw
Io.hw_add_devices(function()
CTRL = Hw.Device(function()
Property.hid = "System Control";
compatible = {"arm,sysctl"};
Resource.regs = Res.mmio(0x10000000, 0x10000fff);
end);
LCD = Hw.Device(function()
Property.hid = "AMBA PL110";
compatible = {"arm,pl111","arm,primecell"};
Resource.regs = Res.mmio(0x10020000, 0x10020fff);
Resource.irq = Res.irq(55);
end);
KBD = Hw.Device(function()
Property.hid = "AMBA KMI Kbd";
compatible = {"arm,pl050","arm,primecell"};
Resource.irq = Res.irq(52);
Resource.regs = Res.mmio(0x10006000, 0x10006fff);
end);
MOUSE = Hw.Device(function()
Property.hid = "AMBA KMI mou";
compatible = {"arm,pl050","arm,primecell"};
Resource.regs = Res.mmio(0x10007000, 0x10007fff);
Resource.irq = Res.irq(53);
end);
GPIO0 = Hw.Device(function()
Property.hid = "AMBA PL061 dev0";
compatible = {"arm,pl061", "arm,primecell"};
Resource.regs = Res.mmio(0x10013000, 0x10013fff);
Resource.irq = Res.irq(38);
end);
GPIO1 = Hw.Device(function()
Property.hid = "AMBA PL061 dev1";
compatible = {"arm,pl061", "arm,primecell"};
Resource.regs = Res.mmio(0x10014000, 0x10014fff);
Resource.irq = Res.irq(39);
end);
GPIO2 = Hw.Device(function()
Property.hid = "AMBA PL061 dev2";
compatible = {"arm,pl061", "arm,primecell"};
Resource.regs = Res.mmio(0x10015000, 0x10015fff);
Resource.irq = Res.irq(40);
end);
COMPACTFLASH = Hw.Device(function()
Property.hid = "compactflash"; -- FIXME: should be something like "ARM RV FLASH"
Resource.regs = Res.mmio(0x18000000, 0x1b000fff);
Resource.irq = Res.irq(59);
end);
AACI = Hw.Device(function()
Property.hid = "aaci";
compatible = {"arm,pl041", "arm,primecell"};
Resource.regs = Res.mmio(0x10004000, 0x10004fff);
Resource.irq = Res.irq(51);
end);
NIC = Hw.Device(function()
Property.hid = "smsc911x";
Resource.regs = Res.mmio(0x4e000000, 0x4e000fff);
Resource.irq = Res.irq(60);
end);
USB = Hw.Device(function()
Property.hid = "usb";
Resource.regs = Res.mmio(0x4f000000, 0x4fffffff);
Resource.irq = Res.irq(61);
end);
RTC = Hw.Device(function()
Property.hid = "rtc";
compatible = {"arm,pl031"};
Resource.regs = Res.mmio(0x10017000, 0x10017fff);
Resource.irq = Res.irq(42);
end);
end)

View File

@@ -0,0 +1,38 @@
-- vim:set ft=lua:
-- (c) 2014 Kernkonzept GmbH
-- License: see LICENSE.spdx (in this directory or the directories above)
local Res = Io.Res
local Hw = Io.Hw
Io.hw_add_devices(function()
virtio_mmio3 = Hw.Device(function()
compatible = {"virtio,mmio"};
Resource.mem = Res.mmio(0x10013600, 0x100137ff);
Resource.irq = Res.irq(75);
end);
CTRL = Hw.Device(function()
Property.hid = "System Control";
Resource.regs = Res.mmio(0x10000000, 0x10000fff);
end);
clcd = Hw.Device(function()
Property.hid = "AMBA PL110";
compatible = {"arm,pl111","arm,primecell"};
Resource.regs = Res.mmio(0x10020000, 0x10020fff);
end);
kmi0 = Hw.Device(function()
compatible = {"arm,pl050","arm,primecell"};
Resource.regs = Res.mmio(0x10006000, 0x10006fff);
Resource.irq = Res.irq(44);
end);
kmi1 = Hw.Device(function()
compatible = {"arm,pl050","arm,primecell"};
Resource.regs = Res.mmio(0x10007000, 0x10007fff);
Resource.irq = Res.irq(45);
end);
end)

View File

@@ -0,0 +1,111 @@
-- vi:ft=lua
local Res = Io.Res
local Hw = Io.Hw
local Vi = Io.Vi
local root = Io.system_bus();
local sb = {}
local add_children = Io.Dt.add_children
add_children(Io.system_bus(), function()
ethernet_gmac = Hw.Device(function()
compatible = "fsl,s32cc-dwmac";
Property.hid = "fsl,s32cc-dwmac";
Resource.reg0 = Res.mmio(0x4033c000, 0x4033c000 + 0x2000 - 1);
Resource.reg1 = Res.mmio(0x4007c004, 0x4007c007);
Resource.irq0 = Res.irq(32 + 57, Io.Resource.Irq_type_level_high);
end)
ethernet_syscon = Hw.Device(function()
compatible = "fsl,s32-xxx1";
Property.hid = "fsl,s32-xxx1";
Resource.reg0 = Res.mmio(0x4007C000, 0x4007C000 + 0x1000 - 1);
end)
clks = Hw.Device(function()
compatible = "fsl,s32g275-clocking";
Property.hid = "fsl,s32g275-clocking";
Resource.reg0 = Res.mmio(0x40038000, 0x40038000 + 0x3000 - 1); -- armpll
Resource.reg1 = Res.mmio(0x4003C000, 0x4003C000 + 0x3000 - 1); -- periphpll
Resource.reg2 = Res.mmio(0x40040000, 0x40040000 + 0x3000 - 1); -- accelpll
Resource.reg3 = Res.mmio(0x40044000, 0x40044000 + 0x3000 - 1); -- ddrpll
Resource.reg4 = Res.mmio(0x40054000, 0x40054000 + 0x3000 - 1); -- armdfs
Resource.reg5 = Res.mmio(0x40058000, 0x40058000 + 0x3000 - 1); -- periphdfs
end)
mc_cgm0 = Hw.Device(function()
compatible = "fsl,s32gen1-mc_cgm0";
Property.hid = "fsl,s32gen1-mc_cgm0";
Resource.reg0 = Res.mmio(0x40030000, 0x40030000 + 0x3000 - 1);
end)
mc_cgm1 = Hw.Device(function()
compatible = "fsl,s32gen1-mc_cgm1";
Property.hid = "fsl,s32gen1-mc_cgm1";
Resource.reg0 = Res.mmio(0x40034000, 0x40034000 + 0x3000 - 1);
end)
mc_cgm2 = Hw.Device(function()
compatible = "fsl,s32gen1-mc_cgm2";
Property.hid = "fsl,s32gen1-mc_cgm2";
Resource.reg0 = Res.mmio(0x44018000, 0x44018000 + 0x3000 - 1);
end)
mc_cgm5 = Hw.Device(function()
compatible = "fsl,s32gen1-mc_cgm5";
Property.hid = "fsl,s32gen1-mc_cgm5";
Resource.reg0 = Res.mmio(0x40068000, 0x40068000 + 0x3000 - 1);
end)
usdhc0 = Hw.Device(function()
compatible = "fsl,s32gen1-usdhc";
Property.hid = "fsl,s32gen1-usdhc";
Property.flags = Io.Hw_device_DF_dma_supported;
Resource.reg0 = Res.mmio(0x402F0000, 0x402F0fff);
Resource.irq0 = Res.irq(32 + 36, Io.Resource.Irq_type_level_high);
end)
rtc = Hw.Device(function()
compatible = "fsl,s32gen1-rtc";
Property.hid = "fsl,s32gen1-rtc";
Resource.reg0 = Res.mmio(0x40060000, 0x40060fff);
Resource.irq0 = Res.irq(32 + 121, Io.Resource.Irq_type_level_high);
end)
mu0 = Hw.Device(function()
compatible = "fsl,s32gen1-hse";
Property.hid = "fsl,s32gen1-hse";
Resource.reg0 = Res.mmio(0x40210000, 0x40210000 + 0x1000 - 1);
Resource.irq0 = Res.irq(32 + 103, Io.Resource.Irq_type_raising_edge);
Resource.irq1 = Res.irq(32 + 104, Io.Resource.Irq_type_raising_edge);
Resource.irq2 = Res.irq(32 + 105, Io.Resource.Irq_type_raising_edge);
end)
mu1 = Hw.Device(function()
compatible = "fsl,s32gen1-hse";
Property.hid = "fsl,s32gen1-hse";
Resource.reg0 = Res.mmio(0x40220000, 0x40220000 + 0x1000 - 1);
Resource.irq0 = Res.irq(32 + 106, Io.Resource.Irq_type_raising_edge);
Resource.irq1 = Res.irq(32 + 107, Io.Resource.Irq_type_raising_edge);
Resource.irq2 = Res.irq(32 + 108, Io.Resource.Irq_type_raising_edge);
end)
mu2 = Hw.Device(function()
compatible = "fsl,s32gen1-hse";
Property.hid = "fsl,s32gen1-hse";
Resource.reg0 = Res.mmio(0x40230000, 0x40230000 + 0x1000 - 1);
Resource.irq0 = Res.irq(32 + 109, Io.Resource.Irq_type_raising_edge);
Resource.irq1 = Res.irq(32 + 110, Io.Resource.Irq_type_raising_edge);
Resource.irq2 = Res.irq(32 + 111, Io.Resource.Irq_type_raising_edge);
end)
mu3 = Hw.Device(function()
compatible = "fsl,s32gen1-hse";
Property.hid = "fsl,s32gen1-hse";
Resource.reg0 = Res.mmio(0x40240000, 0x40240000 + 0x1000 - 1);
Resource.irq0 = Res.irq(32 + 112, Io.Resource.Irq_type_raising_edge);
Resource.irq1 = Res.irq(32 + 113, Io.Resource.Irq_type_raising_edge);
Resource.irq2 = Res.irq(32 + 114, Io.Resource.Irq_type_raising_edge);
end)
end)

View File

@@ -0,0 +1,33 @@
-- vi:ft=lua
local Res = Io.Res
local Hw = Io.Hw
Io.hw_add_devices(function()
P80 = Hw.Device(function()
Property.hid = "P80";
Resource.iop1 = Res.io(0x80);
end);
BIOS = Hw.Device(function()
Property.hid = "BIOS";
Resource.reg1 = Res.mmio(0x0, 0xfff);
Resource.reg2 = Res.mmio(0x9f000, 0x9ffff);
Resource.reg3 = Res.mmio(0xc0000, 0xfffff);
end);
VGA = Hw.Device(function()
Property.hid = "PNP0900";
Resource.iop1 = Res.io(0x3b0, 0x3bf); -- MDA
Resource.iop2 = Res.io(0x3c0, 0x3df); -- EGA/VGA
Resource.iop3 = Res.io(0x1ce, 0x1d0); -- Vbox
Resource.iop4 = Res.io(0x402, 0x402); -- QEMU Debug Port
Resource.mmio = Res.mmio(0xa0000, 0xbffff);
end);
RTC = Hw.Device(function()
Property.hid = "PNP0B00";
Resource.iop1 = Res.io(0x70, 0x71);
end);
end)

View File

@@ -0,0 +1,349 @@
System devices - PNP0XXX
Device ID Description
Interrupt Controllers
PNP0000 AT Interrupt Controller
PNP0001 EISA Interrupt Controller
PNP0002 MCA Interrupt Controller
PNP0003 APIC
PNP0004 Cyrix SLiC MP interrupt controller
Timers
PNP0100 AT Timer
PNP0101 EISA Timer
PNP0102 MCA Timer
DMA
PNP0200 AT DMA Controller
PNP0201 EISA DMA Controller
PNP0202 MCA DMA Controller
Keyboards
PNP0300 IBM PC/XT keyboard controller (83-key)
PNP0301 IBM PC/AT keyboard controller (86-key)
PNP0302 IBM PC/XT keyboard controller (84-key)
PNP0303 IBM Enhanced (101/102-key, PS/2 mouse support)
PNP0304 Olivetti Keyboard (83-key)
PNP0305 Olivetti Keyboard (102-key)
PNP0306 Olivetti Keyboard (86-key)
PNP0307 Microsoft Windows(R) Keyboard
PNP0308 General Input Device Emulation Interface (GIDEI) legacy
PNP0309 Olivetti Keyboard (A101/102 key)
PNP030A AT&T 302 keyboard
PNP030B Reserved by Microsoft
PNP0320 Japanese 101-key keyboard
PNP0321 Japanese AX keyboard
PNP0322 Japanese 106-key keyboard A01
PNP0323 Japanese 106-key keyboard 002/003
PNP0324 Japanese 106-key keyboard 001
PNP0325 Japanese Toshiba Desktop keyboard
PNP0326 Japanese Toshiba Laptop keyboard
PNP0327 Japanese Toshiba Notebook keyboard
PNP0340 Korean 84-key keyboard
PNP0341 Korean 86-key keyboard
PNP0342 Korean Enhanced keyboard
PNP0343 Korean Enhanced keyboard 101b
PNP0343 Korean Enhanced keyboard 101c
PNP0344 Korean Enhanced keyboard 103
Parallel Devices
PNP0400 Standard LPT printer port
PNP0401 ECP printer port
Serial Devices
PNP0500 Standard PC COM port
PNP0501 16550A-compatible COM port
PNP0510 Generic IRDA-compatible device
Disk Controllers
PNP0600 Generic ESDI/IDE/ATA compatible hard disk controller
PNP0601 Plus Hardcard II
PNP0602 Plus Hardcard IIXL/EZ
PNP0603 Generic IDE supporting Microsoft Device Bay Specification
PNP0700 PC standard floppy disk controller
PNP0701 Standard floppy controller supporting MS Device Bay Spec
Compatibility device
PNP0802 Microsoft Sound System compatible device (obsolete, use PNPB0xx instead)
Display Adapters
PNP0900 VGA Compatible
PNP0901 Video Seven VRAM/VRAM II/1024i
PNP0902 IBM 8514/A Compatible
PNP0903 Trident VGA
PNP0904 Cirrus Logic Laptop VGA
PNP0905 Cirrus Logic VGA
PNP0906 Tseng Labs ET4000
PNP0907 Western Digital VGA
PNP0908 Western Digital Laptop VGA
PNP0909 S3 Inc. 911/924
PNP090A ATI Ultra Pro/Plus (Mach 32)
PNP090B ATI Ultra (Mach 8)
PNP090C IBM XGA Compatible
PNP090D ATI VGA Wonder
PNP090E Weitek P9000 Graphics Adapter
PNP090F Oak Technology VGA
PNP0910 Compaq QVision
PNP0911 IBM XGA/2
PNP0912 Tseng Labs ET4000 W32/W32i/W32p
PNP0913 S3 Inc. 801/928/964
PNP0914 Cirrus Logic 5429/5434 (memory mapped)
PNP0915 Compaq Advanced VGA (AVGA)
PNP0916 ATI Ultra Pro Turbo (Mach64)
PNP0917 Reserved by Microsoft
PNP0918 Matrox MGA
PNP0919 Compaq QVision 2000
PNP091A Tseng Labs W128
PNP0930 Chips & Technologies Super VGA
PNP0931 Chips & Technologies Accelerator
PNP0940 NCR 77c22e Super VGA
PNP0941 NCR 77c32blt
PNP09FF Plug and Play Monitors (VESA DDC)
Peripheral Buses
PNP0A00 ISA Bus
PNP0A01 EISA Bus
PNP0A02 MCA Bus
PNP0A03 PCI Bus
PNP0A04 VESA/VL Bus
PNP0A05 Generic ACPI Bus
PNP0A06 Generic ACPI Extended-IO Bus (EIO bus)
RTC, BIOS, System board devices
PNP0800 AT-style speaker sound
PNP0B00 AT Real-Time Clock
PNP0C00 Plug and Play BIOS (only created by the root enumerator)
PNP0C01 System Board
PNP0C02 General ID for reserving resources required by PnP motherboard registers. (Not device specific.)
PNP0C03 Plug and Play BIOS Event Notification Interrupt
PNP0C04 Math Coprocessor
PNP0C05 APM BIOS (Version independent)
PNP0C06 Reserved for identification of early Plug and Play BIOS implementation
PNP0C07 Reserved for identification of early Plug and Play BIOS implementation
PNP0C08 ACPI system board hardware
PNP0C09 ACPI Embedded Controller
PNP0C0A ACPI Control Method Battery
PNP0C0B ACPI Fan
PNP0C0C ACPI power button device
PNP0C0D ACPI lid device
PNP0C0E ACPI sleep button device
PNP0C0F PCI interrupt link device
PNP0C10 ACPI system indicator device
PNP0C11 ACPI thermal zone
PNP0C12 Device Bay Controller
PCMCIA Controller Chipsets
PNP0E00 Intel 82365-Compatible PCMCIA Controller
PNP0E01 Cirrus Logic CL-PD6720 PCMCIA Controller
PNP0E02 VLSI VL82C146 PCMCIA Controller
PNP0E03 Intel 82365-compatible CardBus controller
Mouse
PNP0F00 Microsoft Bus Mouse
PNP0F01 Microsoft Serial Mouse
PNP0F02 Microsoft InPort Mouse
PNP0F03 Microsoft PS/2-style Mouse
PNP0F04 Mouse Systems Mouse
PNP0F05 Mouse Systems 3-Button Mouse (COM2)
PNP0F06 Genius Mouse (COM1)
PNP0F07 Genius Mouse (COM2)
PNP0F08 Logitech Serial Mouse
PNP0F09 Microsoft BallPoint Serial Mouse
PNP0F0A Microsoft Plug and Play Mouse
PNP0F0B Microsoft Plug and Play BallPoint Mouse
PNP0F0C Microsoft-compatible Serial Mouse
PNP0F0D Microsoft-compatible InPort-compatible Mouse
PNP0F0E Microsoft-compatible PS/2-style Mouse
PNP0F0F Microsoft-compatible Serial BallPoint-compatible Mouse
PNP0F10 Texas Instruments QuickPort Mouse
PNP0F11 Microsoft-compatible Bus Mouse
PNP0F12 Logitech PS/2-style Mouse
PNP0F13 PS/2 Port for PS/2-style Mice
PNP0F14 Microsoft Kids Mouse
PNP0F15 Logitech bus mouse
PNP0F16 Logitech SWIFT device
PNP0F17 Logitech-compatible serial mouse
PNP0F18 Logitech-compatible bus mouse
PNP0F19 Logitech-compatible PS/2-style Mouse
PNP0F1A Logitech-compatible SWIFT Device
PNP0F1B HP Omnibook Mouse
PNP0F1C Compaq LTE Trackball PS/2-style Mouse
PNP0F1D Compaq LTE Trackball Serial Mouse
PNP0F1E Microsoft Kids Trackball Mouse
PNP0F1F Reserved by Microsoft Input Device Group
PNP0F20 Reserved by Microsoft Input Device Group
PNP0F21 Reserved by Microsoft Input Device Group
PNP0F22 Reserved by Microsoft Input Device Group
PNP0F23 Reserved by Microsoft Input Device Group
PNP0FFF Reserved by Microsoft Systems
Network adapters - PNP8XXX
PNP8001 Novell/Anthem NE3200
PNP8004 Compaq NE3200
PNP8006 Intel EtherExpress/32
PNP8008 HP EtherTwist EISA LAN Adapter/32 (HP27248A)
PNP8065 Ungermann-Bass NIUps or NIUps/EOTP
PNP8072 DEC (DE211) EtherWorks MC/TP
PNP8073 DEC (DE212) EtherWorks MC/TP_BNC
PNP8078 DCA 10 Mb MCA
PNP8074 HP MC LAN Adapter/16 TP (PC27246)
PNP80C9 IBM Token Ring
PNP80CA IBM Token Ring II
PNP80CB IBM Token Ring II/Short
PNP80CC IBM Token Ring 4/16Mbs
PNP80D3 Novell/Anthem NE1000
PNP80D4 Novell/Anthem NE2000
PNP80D5 NE1000 Compatible
PNP80D6 NE2000 Compatible
PNP80D7 Novell/Anthem NE1500T
PNP80D8 Novell/Anthem NE2100
PNP80DD SMC ARCNETPC
PNP80DE SMC ARCNET PC100, PC200
PNP80DF SMC ARCNET PC110, PC210, PC250
PNP80E0 SMC ARCNET PC130/E
PNP80E1 SMC ARCNET PC120, PC220, PC260
PNP80E2 SMC ARCNET PC270/E
PNP80E5 SMC ARCNET PC600W, PC650W
PNP80E7 DEC DEPCA
PNP80E8 DEC (DE100) EtherWorks LC
PNP80E9 DEC (DE200) EtherWorks Turbo
PNP80EA DEC (DE101) EtherWorks LC/TP
PNP80EB DEC (DE201) EtherWorks Turbo/TP
PNP80EC DEC (DE202) EtherWorks Turbo/TP_BNC
PNP80ED DEC (DE102) EtherWorks LC/TP_BNC
PNP80EE DEC EE101 (Built-In)
PNP80EF DEC PC 433 WS (Built-In)
PNP80F1 3Com EtherLink Plus
PNP80F3 3Com EtherLink II or IITP (8 or 16-bit)
PNP80F4 3Com TokenLink
PNP80F6 3Com EtherLink 16
PNP80F7 3Com EtherLink III
PNP80F8 3Com Generic Etherlink Plug and Play Device
PNP80FB Thomas Conrad TC6045
PNP80FC Thomas Conrad TC6042
PNP80FD Thomas Conrad TC6142
PNP80FE Thomas Conrad TC6145
PNP80FF Thomas Conrad TC6242
PNP8100 Thomas Conrad TC6245
PNP8105 DCA 10 MB
PNP8106 DCA 10 MB Fiber Optic
PNP8107 DCA 10 MB Twisted Pair
PNP8113 Racal NI6510
PNP811C Ungermann-Bass NIUpc
PNP8120 Ungermann-Bass NIUpc/EOTP
PNP8123 SMC StarCard PLUS (WD/8003S)
PNP8124 SMC StarCard PLUS With On Board Hub (WD/8003SH)
PNP8125 SMC EtherCard PLUS (WD/8003E)
PNP8126 SMC EtherCard PLUS With Boot ROM Socket (WD/8003EBT)
PNP8127 SMC EtherCard PLUS With Boot ROM Socket (WD/8003EB)
PNP8128 SMC EtherCard PLUS TP (WD/8003WT)
PNP812A SMC EtherCard PLUS 16 With Boot ROM Socket (WD/8013EBT)
PNP812D Intel EtherExpress 16 or 16TP
PNP812F Intel TokenExpress 16/4
PNP8130 Intel TokenExpress MCA 16/4
PNP8132 Intel EtherExpress 16 (MCA)
PNP8137 Artisoft AE-1
PNP8138 Artisoft AE-2 or AE-3
PNP8141 Amplicard AC 210/XT
PNP8142 Amplicard AC 210/AT
PNP814B Everex SpeedLink /PC16 (EV2027)
PNP8155 HP PC LAN Adapter/8 TP (HP27245)
PNP8156 HP PC LAN Adapter/16 TP (HP27247A)
PNP8157 HP PC LAN Adapter/8 TL (HP27250)
PNP8158 HP PC LAN Adapter/16 TP Plus (HP27247B)
PNP8159 HP PC LAN Adapter/16 TL Plus (HP27252)
PNP815F National Semiconductor Ethernode *16AT
PNP8160 National Semiconductor AT/LANTIC EtherNODE 16-AT3
PNP816A NCR Token-Ring 4 Mbs ISA
PNP816D NCR Token-Ring 16/4 Mbs ISA
PNP8191 Olicom 16/4 Token-Ring Adapter
PNP81C3 SMC EtherCard PLUS Elite (WD/8003EP)
PNP81C4 SMC EtherCard PLUS 10T (WD/8003W)
PNP81C5 SMC EtherCard PLUS Elite 16 (WD/8013EP)
PNP81C6 SMC EtherCard PLUS Elite 16T (WD/8013W)
PNP81C7 SMC EtherCard PLUS Elite 16 Combo (WD/8013EW or 8013EWC)
PNP81C8 SMC EtherElite Ultra 16
PNP81E4 Pure Data PDI9025-32 (Token Ring)
PNP81E6 Pure Data PDI508+ (ArcNet)
PNP81E7 Pure Data PDI516+ (ArcNet)
PNP81EB Proteon Token Ring (P1390)
PNP81EC Proteon Token Ring (P1392)
PNP81ED Proteon Token Ring ISA (P1340)
PNP81EE Proteon Token Ring ISA (P1342)
PNP81EF Proteon Token Ring ISA (P1346)
PNP81F0 Proteon Token Ring ISA (P1347)
PNP81FF Cabletron E2000 Series DNI
PNP8200 Cabletron E2100 Series DNI
PNP8209 Zenith Data Systems Z-Note
PNP820A Zenith Data Systems NE2000-Compatible
PNP8213 Xircom Pocket Ethernet II
PNP8214 Xircom Pocket Ethernet I
PNP821D RadiSys EXM-10
PNP8227 SMC 3000 Series
PNP8228 SMC 91C2 controller
PNP8231 AMD AM2100/AM1500T
PNP8263 Tulip NCC-16
PNP8277 Exos 105
PNP828A Intel '595 based Ethernet
PNP828B TI2000-style Token Ring
PNP828C AMD PCNet Family cards
PNP828D AMD PCNet32 (VL version)
PNP8294 IrDA Infrared NDIS driver (Microsoft-supplied)
PNP82BD IBM PCMCIA-NIC
PNP82C2 Xircom CE10
PNP82C3 Xircom CEM2
PNP8321 DEC Ethernet (All Types)
PNP8323 SMC EtherCard (All Types except 8013/A)
PNP8324 ARCNET Compatible
PNP8326 Thomas Conrad (All Arcnet Types)
PNP8327 IBM Token Ring (All Types)
PNP8385 Remote Network Access [RNA] Driver
PNP8387 Remote Network Access [RNA] PPP Driver
PNP8388 Reserved for Microsoft Networking components
PNP8389 Peer IrLAN infrared driver (Microsoft-supplied)
SCSI, Proprietary CD adapters - PNPAXXX
PNPA002 Future Domain 16-700 compatible controller
PNPA003 Mitsumi CD-ROM adapter (Panasonic spec., used on SBPro/SB16)
PNPA01B Trantor 128 SCSI Controller
PNPA01D Trantor T160 SCSI Controller
PNPA01E Trantor T338 Parallel SCSI controller
PNPA01F Trantor T348 Parallel SCSI controller
PNPA020 Trantor Media Vision SCSI controller
PNPA022 Always IN-2000 SCSI controller
PNPA02B Sony proprietary CD-ROM controller
PNPA02D Trantor T13b 8-bit SCSI controller
PNPA02F Trantor T358 Parallel SCSI controller
PNPA030 Mitsumi LU-005 Single Speed CD-ROM controller + drive
PNPA031 Mitsumi FX-001 Single Speed CD-ROM controller + drive
PNPA032 Mitsumi FX-001 Double Speed CD-ROM controller + drive
Sound / Video-Capture, Multimedia - PNPBXXX
PNPB000 Creative Labs Sound Blaster 1.5 (or compatible sound device)
PNPB001 Creative Labs Sound Blaster 2.0 (or compatible sound device)
PNPB002 Creative Labs Sound Blaster Pro (or compatible sound device)
PNPB003 Creative Labs Sound Blaster 16 (or compatible sound device)
PNPB004 MediaVision Thunderboard (or compatible sound device)
PNPB005 Adlib-compatible FM synthesizer device
PNPB006 MPU401 compatible
PNPB007 Microsoft Windows Sound System-compatible sound device
PNPB008 Compaq Business Audio
PNPB009 Plug and Play Microsoft Windows Sound System Device
PNPB00A MediaVision Pro Audio Spectrum (Trantor SCSI enabled, Thunder Chip Disabled)
PNPB00B MediaVision Pro Audio 3D
PNPB00C MusicQuest MQX-32M
PNPB00D MediaVision Pro Audio Spectrum Basic (No Trantor SCSI, Thunder Chip Enabled)
PNPB00E MediaVision Pro Audio Spectrum (Trantor SCSI enabled, Thunder Chip Disabled)
PNPB00F MediaVision Jazz-16 chipset (OEM Versions)
PNPB010 Orchid Videola - Auravision VxP500 chipset
PNPB018 MediaVision Pro Audio Spectrum 8-bit
PNPB019 MediaVision Pro Audio Spectrum Basic (No Trantor SCSI, Thunder Chip Enabled)
PNPB020 Yamaha OPL3-compatible FM synthesizer device
PNPB02F Joystick/Game port
Modems - PNPCXXX
PNPC000 Compaq 14400 Modem (TBD)
PNPC001 Compaq 2400/9600 Modem (TBD)
Modems - PNPCXXX

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
-- vim:ft=lua
-- Example configuration for io
-- Configure two platform devices to be known to io
Io.Dt.add_children(Io.system_bus(), function()
-- create a new hardware device called "FOODEVICE"
FOODEVICE = Io.Hw.Device(function()
-- set the compatibility IDs for this device
-- a client tries to match against these IDs and configures
-- itself accordingly
-- the list should be sorted from specific to less specific IDs
compatible = {"dev-foo,mmio", "dev-foo"};
-- set the 'hid' property of the device, the hid can also be used
-- as a compatible ID when matching clients
Property.hid = "dev-foo,Example";
-- note: names for resources are truncated to 4 letters and a client
-- can determine the name from the ID field of a l4vbus_resource_t
-- add two resources 'irq0' and 'reg0' to the device
Resource.irq0 = Io.Res.irq(17);
Resource.reg0 = Io.Res.mmio(0x6f000000, 0x6f007fff);
end);
-- create a new hardware device called "BARDEVICE"
BARDEVICE = Io.Hw.Device(function()
-- set the compatibility IDs for this device
-- a client tries to match against these IDs and configures
-- itself accordingly
-- the list should be sorted from specific to less specific IDs
compatible = {"dev-bar,mmio", "dev-bar"};
-- set the 'hid' property of the device, the hid can also be used
-- as a compatible ID when matching clients
Property.hid = "dev-bar,Example";
-- Specify that this device is able to use direct memory access (DMA).
-- This is needed to allow clients to gain access to DMA addresses
-- used by this device to directly access memory.
Property.flags = Io.Hw_device_DF_dma_supported;
-- note: names for resources are truncated to 4 letters and a client
-- can determine the name from the ID field of a l4vbus_resource_t
-- add three resources 'irq0', 'irq1', and 'reg0' to the device
Resource.irq0 = Io.Res.irq(19);
Resource.irq1 = Io.Res.irq(20);
Resource.reg0 = Io.Res.mmio(0x6f100000, 0x6f100fff);
end);
end);
Io.add_vbusses
{
-- Create a virtual bus for a client and give access to FOODEVICE
client1 = Io.Vi.System_bus(function ()
dev = wrap(Io.system_bus():match("dev-foo,mmio"));
end);
-- Create a virtual bus for another client and give it access to BARDEVICE
client2 = Io.Vi.System_bus(function ()
dev = wrap(Io.system_bus():match("dev-bar,Example"));
end);
}

View File

@@ -0,0 +1,14 @@
-- This is a configuration snippet for PCI device selection
local hw = Io.system_bus();
Io.add_vbusses
{
pciclient = Io.Vi.System_bus(function ()
PCI = Io.Vi.PCI_bus(function ()
pci_mm = wrap(hw:match("PCI/CC_04"));
pci_net = wrap(hw:match("PCI/CC_02"));
pci_storage = wrap(hw:match("PCI/CC_01"));
end)
end)
}

View File

@@ -0,0 +1 @@
INPUT += %PKGDIR%/doc/io.dox

244
src/l4/pkg/io/io/doc/io.dox Normal file
View File

@@ -0,0 +1,244 @@
// vi:ft=c
/**
* \page io Io, the Io Server
*
* The Io server handles all platform devices and resources such as I/O
* memory, ports (on x86) and interrupts, and grants access to those to
* clients.
*
* Upon startup Io discovers all platform devices using available means on
* the system, e.g. on x86 the PCI bus is scanned and the ACPI subsystem
* initialised. Available I/O resource can also be configured via
* configuration scripts.
*
* Io's configuration consists of two parts:
* - the description of the real hardware
* - the description of virtual buses
*
* Both descriptions represent a hierarchical (tree) structure of device nodes.
* Where each device has a set of resources attached to it. And a device that
* has child devices can be considered a bus.
*
* Hardware Description
* --------------------
*
* The hardware description represents the devices that are available on the
* particular platform including their resource descriptions, such as MMIO
* regions, IO-Port regions, IRQs, bus numbers etc.
*
* The root of the hardware devices is formed by a system bus device
* (accessible in the configuration via Io.system_bus()).
* As mentioned before, platforms that support methods for device discovery may
* populate the hardware description automatically, for example from ACPI. On
* platforms that do not have support for such methods you have to specify the
* hardware description by hand. A simple example for this is
* <tt>x86-legacy.devs</tt>.
*
* Virtual Bus Description \anchor vbus_desc
* -----------------------
*
* Each Io server client is provided with its own virtual bus which
* it can iterate to find devices. A virtual PCI bus may be a part
* of this virtual bus.
*
* \image html io-overview.png "IO Service Architecture Overview"
* \image latex io-overview.pdf "IO Service Architecture Overview"
*
* The Io server must be configured to create virtual buses for its
* clients.
*
* This is done with at least one configuration file
* specifying static resources as well as virtual buses for clients. The
* configuration may be split across several configuration files passed
* to Io through the command line.
*
* To allow clients access to available devices, a virtual system bus needs
* to be created that lists the devices and their resources that should be
* available to that client. The names of the buses correspond to the
* capabilities given to Io in its launch configuration.
*
* A very simple configuration for Io could look like this:
*
* \include io/doc/example.io
*
* Each device supports a 'compatible' property. It is a list of compatibility
* strings. A client matches itself against one (or multiple) compatibility IDs
* and configures itself accordingly. All other device members are handled
* according to their type. If the type is a resource (Io.Res) it is added as a
* named resource. Note that resource names are truncated to 4 letters and are
* stored in the ID field of a l4vbus_resource_t. If the type is a device it is
* added as a child device to the current one. All other types are treated as a
* device property which can be used to configure a device driver. Right now,
* device properties are internal to Io only.
*
* Matching and Assigning PCI Devices
* ----------------------------------
* Assigning clients PCI devices could look like this:
*
* \include io/doc/example_pci.io
*
* The "PCI/" is followed by a bus-specific ID string. The format of the PCI ID
* string may be one of the following:
* - PCI/CC_cc
* - PCI/CC_ccss
* - PCI/CC_ccsspp
* - PCI/VEN_vvvv
* - PCI/DEV_dddd
* - PCI/SUBSYS_ssssssss
* - PCI/REV_rr
* - PCI/ADR_xxxx:xx:xx.x
*
* Where:
* - `cc` is the hexadecimal representation of the class code byte
* - `ss` is the hexadecimal representation of the subclass code byte
* - `pp` is the hexadecimal representation of the programming interface byte
* - `vvvv` is the hexadecimal representation of the vendor ID
* - `dddd` is the hexadecimal representation of the device ID
* - `ssssssss` is the hexadecimal representation of the subsystem ID
* - `rr` is the hexadecimal representation of the revision byte
* - `xxxx:xx:xx.x` is the bus address in PCI nomenclature
*
* As a special extension Io supports replacing the ID string with a
* human-readable common PCI class name. The following table gives an overview
* of the names known to Io and their respective PCI class and subclass.
*
* Common Name | Description | PCI ID string
* ---------------------|------------------------------------|--------------
* storage | Mass storage controller | CC_01
* scsi | SCSI storage controller | CC_0100
* ide | IDE interface | CC_0101
* floppy | Floppy disk controller | CC_0102
* raid | RAID bus controller | CC_0104
* ata | ATA controller | CC_0105
* sata | SATA controller | CC_0106
* sas | Serial attached SCSI controller | CC_0107
* nvm | Non-volatile memory controller | CC_0108
* - | - | -
* network | Network controller | CC_02
* ethernet | Ethernet controller | CC_0200
* token_ring | Token ring network controller | CC_0201
* fddi | FDDI network controller | CC_0202
* atm | ATM network controller | CC_0203
* isdn | ISDN controller | CC_0204
* picmg | PICMG controller | CC_0206
* net_infiniband | Infiniband controller | CC_0207
* fabric | Fabric controller | CC_0208
* network_nw | Network controller e.g. Wifi | CC_0280
* - | - | -
* display | Display controller | CC_03
* vga | VGA compatible controller | CC_0300
* xga | XGA compatible controller | CC_0301
* - | - | -
* media | Multimedia controller | CC_04
* mm_video | Multimedia video controller | CC_0400
* mm_audio | Multimedia audio controller | CC_0401
* telephony | Computer telephony device | CC_0402
* audio | Audio device | CC_0403
* - | - | -
* bridge | Bridge | CC_06
* br_host | Host bridge | CC_0600
* br_isa | ISA bridge | CC_0601
* br_eisa | EISA bridge | CC_0602
* br_microchannel | MicroChannel bridge | CC_0603
* br_pci | PCI bridge | CC_0604
* br_pcmcia | PCMCIA bridge | CC_0605
* br_nubus | NuBus bridge | CC_0606
* br_cardbus | CardBus bridge | CC_0607
* br_raceway | RACEway bridge | CC_0608
* br_semi_pci | Semi-transparent PCI-to-PCI bridge | CC_0609
* br_infiniband_to_pci | InfiniBand to PCI host bridge | CC_060a
* - | - | -
* com | Communication controller | CC_07
* com_serial | Serial controller | CC_0700
* com_parallel | Parallel controller | CC_0701
* com_multiport_ser | Multiport serial controller | CC_0702
* com_modem | Modem | CC_0703
* com_gpib | GPIB controller | CC_0704
* com_smart_card | Smart card controller | CC_0705
* - | - | -
* serial_bus | Serial bus controller | CC_0c
* firewire | FireWire (IEEE 1394) | CC_0c00
* access_bus | ACCESS bus | CC_0c01
* ssa | SSA | CC_0c02
* usb | USB controller | CC_0c03
* fibre_channel | Fibre channel | CC_0c04
* smbus | SMBus | CC_0c05
* bus_infiniband | InfiniBand | CC_0c06
* ipmi_smic | IPMI SMIC interface | CC_0c07
* sercos | SERCOS interface | CC_0c08
* canbus | CAN bus | CC_0c09
* - | - | -
* wireless | Wireless controller | CC_0d
* bluetooth | Bluetooth | CC_0d11
* w_8021a | 802.1a controller | CC_0d20
* w_8021b | 802.1b controller | CC_0d21
*
* ### Strong Matching of PCI Devices ###
* If more specific matching of PCI devices is required it is possible to
* concatenate multiple ID strings using `&`. An example where a specific
* device from a specific vendor at a fixed bus address is matched would use
* the string `PCI/VEN_vvvv&DEV_dddd&ADR_xxxx:xx:xx.x`.
*
* ### Isolation of PCIe devices ###
*
* PCIe encodes device communication with a network-like protocol with
* destination headers and packet fragmentation allowing a devices to talk
* directly to other devices. This potentially works against security
* boundaries for a system. E.g. two network cards could exchange packets and
* thereby leak information from one security domain to the other without
* involvement of the OS.
*
* PCIe introduced an optional capability named PCI Access Control Services
* (PCI/ACS) to control communication between PCIe devices.
*
* With PCI/ACS it is possible to restrict inter-device communication between
* PCIe devices.
*
* PCI/ACS is optional and for Intel chipsets, it is usually only implemented
* on high-end PCI platform controller hubs (PCHs), and is missing on low-end
* and mobile PCHs. On some Intel-PCHs there exist facilities that allow for
* similar isolation.
*
* If IO encounters a supported PCH, it will enable those facilities in order
* to enforce device isolation.
*
* Command Line Options
* -----------------------
* The Io Server supports the following optional parameters:
*
* [--verbose|v] [--transparent-msi] [--trace <trace_mask>] [--acpi-debug-level <debug_level>] [config_files]
*
* - **verbose|v**
*
* By default, error debug messages are enabled. This option increments the
* verboseness level, and can be applied multiple times to reach the desired
* debug level. The available debug levels are ordered as: `DBG_ERR` (default,
* level 1), `DBG_WARN`, `DBG_INFO`, `DBG_DEBUG`, `DBG_DEBUG2` and `DBG_ALL`
* (level 6).
*
* - **transparent-msi**
*
* Enable MSI on PCI devices which support this feature. This is transparent
* to clients, as there are no changes in the API used to interact
* with PCI device via interrupts.
*
* - **acpi-debug-level \<level_mask>**
*
* Set the ACPI debug level. The `<level_mask>` is a mask that selects
* components of interest for debugging. It can be constructed from the
* ACPI debug constants defined in the linux kernel, see <a href=
* "https://www.kernel.org/doc/html/latest/firmware-guide/acpi/debug.html"
* target="_blank"> ACPI Debug Output</a> for details. By default,
* the ACPI debug level is set to
* `ACPI_LV_INIT | ACPI_LV_TABLES | ACPI_LV_VERBOSE_INFO`.
*
* - **trace \<trace_mask>**
*
* Enable tracing of events matching `trace_mask`. The only supported trace
* mask is `1` and this matches ACPI events.
*
* - **config_files**
*
* Space separated list of Lua configuration files specifying real hardware
* and virtual buses. See example on \ref vbus_desc "Virtual Bus Description".
*/

View File

@@ -0,0 +1,41 @@
-- vim:set ft=lua:
--
-- Create a virtual BUS named 'bus'
--
-- This BUS is registered in io's name space under the name 'bus'.
--
local hw = Io.hw_bus
Io.add_vbus("bus", Io.Vi.System_bus
{
-- Scan the hw-root bus for devices that have a compatibility ID (CID)
-- match for a keyboard or mouse. And add them with virtual
-- device wrappers to the BUS.
ps2 = wrap(hw:match("PNP0[3F]??"));
})
-- Create a virtual BUS 'bus1'
Io.add_vbusses
{
bus1 = Io.Vi.System_bus
{
-- Add the RTC (PNP0B00) as a virtual device
rtc = wrap(hw:match("PNP0B00"));
-- Add a new virtual PCI root bridge
PCI0 = Io.Vi.PCI_bus
{
-- Add all real PCI devices that match the CIDs to the virtual PCI bus
-- NOTE: PCI/CC_xx means PCI class code == xx.
-- CC_01 => storage devices
-- CC_02 => network devices
-- CC_04 => multimedia devices
-- CC_0c => serial bus devices (USB)
pci = wrap(hw:match("PCI/CC_01","PCI/CC_02","PCI/CC_04","PCI/CC_0c"));
-- human-readable version (also supported):
-- pci = wrap(hw:match("PCI/storage","PCI/network","PCI/media","PCI/usb");
}
}
}

View File

@@ -0,0 +1,22 @@
Io.add_vbusses
{
vm_hw = Io.Vi.System_bus(function()
VCPUID = wrap(Io.system_bus():match("exynos-cpuid"))
CLOCK = wrap(Io.system_bus():match("exynos5250-clock"))
SATA = wrap(Io.system_bus():match("exynos5-sata-ahci"))
SATA_PHY = wrap(Io.system_bus():match("exynos5-sata-phy"))
SATA_PHY_I2C = wrap(Io.system_bus():match("exynos5-sata-phy-i2c"))
USB2 = wrap(Io.system_bus():match("exynos4210-ehci"))
USB1 = wrap(Io.system_bus():match("exynos4210-ohci"))
USB_PHY = wrap(Io.system_bus():match("exynos5250-usb2phy"))
USB3 = wrap(Io.system_bus():match("exynos5-usb3"));
INT_COMB = wrap(Io.system_bus():match("exynos-comb"))
RTC = wrap(Io.system_bus():match("exynos-rtc"))
AUDSS = wrap(Io.system_bus():match("exynos-audss"))
UART0 = wrap(Io.system_bus():match("exynos-serial0"))
UART1 = wrap(Io.system_bus():match("exynos-serial1"))
UART3 = wrap(Io.system_bus():match("exynos-serial3"))
ex = wrap(Io.system_bus():match("exynos"));
end);
}

View File

@@ -0,0 +1,8 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET = libpciids src
include $(L4DIR)/mk/subdir.mk
src: libpciids

View File

@@ -0,0 +1,8 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := src
include $(L4DIR)/mk/subdir.mk
src: include

View File

@@ -0,0 +1,19 @@
/*
* (c) 2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <sys/cdefs.h>
#include <l4/sys/compiler.h>
L4_BEGIN_DECLS
L4_EXPORT
void libpciids_name_device(char *name, int len,
unsigned vendor, unsigned device);
L4_END_DECLS

View File

@@ -0,0 +1,36 @@
PKGDIR ?= ../../..
L4DIR ?= $(PKGDIR)/../../..
PC_FILENAME = libpciids
TARGET = libpciids.a libpciids.so
SRC_C = names.c
PRIVATE_INCDIR = . $(SRC_DIR)/../include
include $(L4DIR)/mk/lib.mk
ifeq ($(BID_COMPILER_TYPE),gcc)
CFLAGS += -fvisibility=internal
endif
$(SRC_DIR)/names.c: devlist.h classlist.h
devlist.h classlist.h: $(SRC_DIR)/pci.ids gen-devlist $(GENERIC_LOC_D)
$(VERBOSE)echo -e " ... Generating devlist.h/classlist.h"
$(VERBOSE)./gen-devlist < $<
gen-devlist: gen-devlist.c $(GENERIC_LOC_D)
@$(BUILD_MESSAGE)
$(VERBOSE)$(HOST_CC) -o $@ $<
update:
$(VERBOSE)wget -O - https://pci-ids.ucw.cz/v2.2/pci.ids.bz2 \
| bzcat > pci.ids
update-ci: update
git commit -m "Update PCI-IDs" .
clean::
$(VERBOSE)$(RM) devlist.h classlist.h gen-devlist
.PHONY: update update-ci

View File

@@ -0,0 +1,140 @@
/*
* Generate devlist.h and classlist.h from the PCI ID file.
*
* (c) 1999--2002 Martin Mares <mj@ucw.cz>
*/
#include <stdio.h>
#include <string.h>
#define MAX_NAME_SIZE 130
static void
pq(FILE *f, const char *c)
{
while (*c) {
if (*c == '"')
fprintf(f, "\\\"");
else if (*c == '\\')
fprintf(f, "\\\\"); // escape backslash
else {
fputc(*c, f);
if (*c == '?' && c[1] == '?') {
/* Avoid trigraphs */
fprintf(f, "\" \"");
}
}
c++;
}
}
int
main(void)
{
char line[1024], *c, *bra, vend[8];
int vendors = 0;
int mode = 0;
int lino = 0;
int vendor_len = 0;
FILE *devf, *clsf;
devf = fopen("devlist.h", "w");
clsf = fopen("classlist.h", "w");
if (!devf || !clsf) {
fprintf(stderr, "Cannot create output file!\n");
if (devf)
fclose(devf);
if (clsf)
fclose(clsf);
return 1;
}
while (fgets(line, sizeof(line)-1, stdin)) {
lino++;
if ((c = strchr(line, '\n')))
*c = 0;
if (!line[0] || line[0] == '#')
continue;
if (line[1] == ' ') {
if (line[0] == 'C' && strlen(line) > 4 && line[4] == ' ') {
vend[0] = line[2];
vend[1] = line[3];
vend[2] = 0;
mode = 2;
} else goto err;
}
else if (line[0] == '\t') {
if (line[1] == '\t')
continue;
switch (mode) {
case 1:
if (strlen(line) > 5 && line[5] == ' ') {
c = line + 5;
while (*c == ' ')
*c++ = 0;
if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) {
/* Too long, try cutting off long description */
bra = strchr(c, '[');
if (bra && bra > c && bra[-1] == ' ')
bra[-1] = 0;
if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) {
// fprintf(stderr, "Line %d: Device name too long\n", lino);
// fprintf(stderr, "%s\n", c);
c[MAX_NAME_SIZE-1-vendor_len] = 0;
}
}
fprintf(devf, "\tDEVICE(%s,%s,\"", vend, line+1);
pq(devf, c);
fputs("\")\n", devf);
} else goto err;
break;
case 2:
if (strlen(line) > 3 && line[3] == ' ') {
c = line + 3;
while (*c == ' ')
*c++ = 0;
fprintf(clsf, "CLASS(%s%s, \"%s\")\n", vend, line+1, c);
} else goto err;
break;
default:
goto err;
}
} else if (strlen(line) > 4 && line[4] == ' ') {
c = line + 4;
while (*c == ' ')
*c++ = 0;
if (vendors)
fputs("ENDVENDOR()\n\n", devf);
vendors++;
strcpy(vend, line);
vendor_len = strlen(c);
if (vendor_len + 24 > MAX_NAME_SIZE) {
fprintf(stderr, "Line %d: Vendor name too long (%d)\n", lino, vendor_len + 24);
fclose(devf);
fclose(clsf);
return 1;
}
fprintf(devf, "VENDOR(%s,\"", vend);
pq(devf, c);
fputs("\")\n", devf);
mode = 1;
} else {
err:
fprintf(stderr, "Line %d: Syntax error in mode %d: %s\n", lino, mode, line);
fclose(devf);
fclose(clsf);
return 1;
}
}
fputs("ENDVENDOR()\n\
\n\
#undef VENDOR\n\
#undef DEVICE\n\
#undef ENDVENDOR\n", devf);
fputs("\n#undef CLASS\n", clsf);
fclose(devf);
fclose(clsf);
return 0;
}

View File

@@ -0,0 +1,138 @@
/*
* PCI Class and Device Name Tables
*
* Copyright 1993--1999 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang, Martin Mares
*/
//#include <linux/types.h>
//#include <linux/kernel.h>
//#include <linux/pci.h>
#include <stdio.h>
#include "pciids.h"
struct pci_device_info {
unsigned short device;
unsigned short seen;
const char *name;
};
struct pci_vendor_info {
unsigned short vendor;
unsigned short nr;
const char *name;
struct pci_device_info *devices;
};
#define __devinitdata
/*
* This is ridiculous, but we want the strings in
* the .init section so that they don't take up
* real memory.. Parse the same file multiple times
* to get all the info.
*/
#define VENDOR( vendor, name ) static char __vendorstr_##vendor[] __devinitdata = name;
#define ENDVENDOR()
#define DEVICE( vendor, device, name ) static char __devicestr_##vendor##device[] __devinitdata = name;
#include "devlist.h"
#undef VENDOR
#undef ENDVENDOR
#undef DEVICE
#define VENDOR( vendor, name ) static struct pci_device_info __devices_##vendor[] __devinitdata = {
#define ENDVENDOR() };
#define DEVICE( vendor, device, name ) { 0x##device, 0, __devicestr_##vendor##device },
#include "devlist.h"
#undef VENDOR
#undef ENDVENDOR
#undef DEVICE
static struct pci_vendor_info __devinitdata pci_vendor_list[] = {
#define VENDOR( vendor, name ) { 0x##vendor, sizeof(__devices_##vendor) / sizeof(struct pci_device_info), __vendorstr_##vendor, __devices_##vendor },
#define ENDVENDOR()
#define DEVICE( vendor, device, name )
#include "devlist.h"
#undef VENDOR
#undef ENDVENDOR
#undef DEVICE
};
#define VENDORS (sizeof(pci_vendor_list)/sizeof(struct pci_vendor_info))
//void libpci_name_device(char *name, struct pci_dev *dev)
void libpciids_name_device(char *name, int len,
unsigned vendor, unsigned device)
{
const struct pci_vendor_info *vendor_p = pci_vendor_list;
int i = VENDORS;
do {
if (vendor_p->vendor == vendor)
goto match_vendor;
vendor_p++;
} while (--i);
/* Couldn't find either the vendor nor the device */
snprintf(name, len, "PCI device %04x:%04x", vendor, device);
name[len - 1] = 0;
return;
match_vendor: {
struct pci_device_info *device_p = vendor_p->devices;
int i = vendor_p->nr;
while (i > 0) {
if (device_p->device == device)
goto match_device;
device_p++;
i--;
}
/* Ok, found the vendor, but unknown device */
snprintf(name, len, "PCI device %04x:%04x (%s)", vendor, device, vendor_p->name);
name[len - 1] = 0;
return;
/* Full match */
match_device: {
int w = snprintf(name, len, "%s %s", vendor_p->name, device_p->name);
int nr = device_p->seen + 1;
device_p->seen = nr;
if (nr > 1)
snprintf(name + w, len - w, " (#%d)", nr);
name[len - 1] = 0;
}
}
}
/*
* Class names. Not in .init section as they are needed in runtime.
*/
#if 0
static u16 pci_class_numbers[] = {
#define CLASS(x,y) 0x##x,
#include "classlist.h"
};
static char *pci_class_names[] = {
#define CLASS(x,y) y,
#include "classlist.h"
};
char *
libpci_class_name(u32 class)
{
int i;
for(i=0; i<sizeof(pci_class_numbers)/sizeof(pci_class_numbers[0]); i++)
if (pci_class_numbers[i] == class)
return pci_class_names[i];
return NULL;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
# vi:ft=make
#MODE := shared
TARGET = io
DEFINES-$(CONFIG_L4IO_PCIID_DB) += -DCONFIG_L4IO_PCIID_DB
SUBDIRS = drivers
SRC_CC_ACPI-arm64 = platform_arm64_iort.cc
SRC_CC_ACPI-amd64 = platform_intel_vtd.cc
SRC_CC_ACPI-x86 = platform_intel_vtd.cc
SRC_CC_ACPI = acpi.cc acpi_sci.cc acpi_osl.cc pci-acpi.cc $(SRC_CC_ACPI-$(ARCH))
$(foreach i,$(SRC_CC_ACPI),$(eval CXXFLAGS_acpi/$(i) = -DL4_ACPICA))
SRC_CC-$(CONFIG_L4IO_ACPI) += $(addprefix acpi/,$(SRC_CC_ACPI))
SRC_CC-$(CONFIG_L4IO_PCI) += virt/pci/vpci.cc \
virt/pci/vpci_bridge.cc \
virt/pci/vpci_pci_bridge.cc \
virt/pci/vpci_proxy_dev.cc \
virt/pci/vpci_virtual_dev.cc \
virt/pci/vpci_virtual_root.cc \
pci/pci-bridge.cc \
pci/pci-dev.cc \
pci/pci-driver.cc \
pci/pci-root.cc \
pci/pci-saved-config.cc \
pci/msi.cc \
pci/pm.cc \
pci/acs.cc \
pci/ari.cc
SRC_CC-$(CONFIG_L4IO_PCI_SRIOV) += virt/pci/vpci_sriov.cc \
pci/sriov.cc
SRC_CC := main.cc res.cc phys_space.cc resource.cc hw_device.cc \
resource_provider.cc \
hw_root_bus.cc device.cc hw_irqs.cc \
hw_register_block.cc \
dma_domain.cc \
gpio.cc \
server.cc irqs.cc debug.cc \
irq_server.cc \
lua_glue.swg.cc \
pm.cc \
virt/vdevice.cc \
virt/vmsi.cc \
virt/vicu.cc \
virt/vbus.cc \
virt/vproxy_dev.cc \
virt/vbus_factory.cc \
virt/gpio/vgpio.cc \
inhibitor_mux.cc \
platform_control.cc
# WARNING EXCEPTION: This is auto generated code and thus the code may contain
# variables that are set but never read.
WARNINGS_lua_glue.swg.cc += -Wno-unused-but-set-variable
SRC_DATA := io.lua
SRC_CC_arm-l4f += arm_dma_device.cc optee.cc
SRC_CC_arm64-l4f += arm_dma_device.cc optee.cc
OPTS := -g -O3
REQUIRES_LIBS = libsigma0 libstdc++ lua++ libpthread
REQUIRES_LIBS-$(CONFIG_L4IO_PCIID_DB) += libpciids
REQUIRES_LIBS-$(CONFIG_L4IO_ACPI) += acpica
REQUIRES_CFLAGS = libio-vbus libio-io
PRIVATE_INCDIR += $(SRC_DIR) $(SRC_DIR)/../libpciids/include
# implementation of lib_subdir, similar to lib_subdir.mk
SUBDIRS += $(SUBDIRS_$(ARCH)) $(SUBDIRS_$(OSYSTEM))
SUBDIR_TARGETS := $(addsuffix /OBJ-$(SYSTEM)/builtin.thin.a,$(SUBDIRS))
SUBDIR_OBJS = $(addprefix $(OBJ_DIR)/,$(SUBDIR_TARGETS))
# ACPI contrib code has many unused parameters
CPPFLAGS_acpi/acpi.cc += -Wno-unused-parameter
CPPFLAGS_acpi/acpi_sci.cc += -Wno-unused-parameter
CPPFLAGS_acpi/acpi_osl.cc += -Wno-unused-parameter
CPPFLAGS_acpi/pci-acpi.cc += -Wno-unused-parameter
all::
$(SUBDIR_OBJS): $(OBJ_DIR)/%/OBJ-$(SYSTEM)/builtin.thin.a: %
$(VERBOSE)$(MAKE) $(MAKECMDGOALS) OBJ_BASE=$(OBJ_BASE)\
-C $(SRC_DIR)/$*
$(TARGET): $(SUBDIR_OBJS)
EXTRA_LIBS += --whole-archive $(SUBDIR_OBJS) --no-whole-archive
clean-subdir-%:
$(VERBOSE)$(MAKE) clean OBJ_BASE=$(OBJ_BASE) \
-C $(SRC_DIR)/$*
clean:: $(addprefix clean-subdir-,$(SUBDIRS))
# regenerate shipped files
ifneq ($(REGEN_FILES),)
all:: $(SRC_DIR)/lua_glue.swg.cc
-include .lua_glue.swg.c_cc.d
SWIG_RM_EXT_C ?= $(L4DIR)/tool/bin/swig-rm-extern-c.pl
SWIG ?= swig
SWIG_INCDIR := -I$(SRC_DIR) -I$(SRC_DIR)/../../include -I$(L4DIR)/pkg
%.swg:
$(SRC_DIR)/%.swg.cc: %.swg.c_cc $(SRC_DIR)/Make.rules $(SRC_DIR)/Makefile
@$(GEN_MESSAGE)
$(VERBOSE)$(SWIG_RM_EXT_C) $< >$@
%.swg.c_cc: $(SRC_DIR)/%.swg $(SRC_DIR)/Make.rules $(SRC_DIR)/Makefile
@$(GEN_MESSAGE)
$(VERBOSE)$(SWIG) $(SWIG_INCDIR) -MD -MF .$*.swg.c_cc.d -c++ -small -lua -o $@ $<
endif

View File

@@ -0,0 +1,5 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../../..
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,6 @@
This package contains the IO server. It handles all platform devices and
resources such as I/O memory, ports (on x86) and interrupts, and grants
access to those to clients.
The file lua_glue.swg.cc was generated by swig (https://github.com/swig/swig)
4.0.2 release, commit cb5d7398b562e77436e5766fb17a40bfe8c4f973.

View File

@@ -0,0 +1,42 @@
/*
* (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/compiler.h>
class Pci_survey_config;
class Acpi_config
{
public:
virtual Pci_survey_config *pci_survey_config() = 0;
virtual ~Acpi_config() = 0;
};
inline Acpi_config::~Acpi_config() {}
struct acpica_pci_irq
{
unsigned int irq;
unsigned char trigger;
unsigned char polarity;
};
#ifdef CONFIG_L4IO_ACPI
int acpica_init();
void acpi_late_setup();
#else
static inline int acpica_init() { return 0; }
static inline void acpi_late_setup() {}
#endif
#if defined(CONFIG_L4IO_ACPI) && (defined(ARCH_x86) || defined(ARCH_amd64))
int acpi_ecdt_scan();
#else
static inline int acpi_ecdt_scan() { return 0; }
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
/**
* Copyright (C) 2024 Kernkonzept GmbH.
* Author(s): Philipp Eppelt <philipp.eppelt@kernkonzept.com>
* Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <acpi.h>
#include <cstdint>
/**
* Cast an object to a different type with an offset.
*
* \tparam T1 Target object type.
* \tparam T2 Source object type.
* \param offset Byte offset within the source object.
*
* \return Target object.
*/
template<typename T1, typename T2> inline
T1 offset_cast(T2 ptr, uintptr_t offset)
{
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr) + offset;
return reinterpret_cast<T1>(addr);
}
namespace Hw {
namespace Acpi {
struct Dmar_head
{
Dmar_head const *next() const
{ return offset_cast<Dmar_head const *>(this, length); }
template<typename T>
T const *cast() const
{
if (type == T::Id)
return static_cast<T const *>(this);
return nullptr;
}
l4_uint16_t type;
l4_uint16_t length;
} __attribute__((packed));
template<typename T>
struct Next_iter
{
Next_iter() : _c(0) {}
Next_iter(T const *h) : _c(h) {}
Next_iter const &operator ++ ()
{
_c = _c->next();
return *this;
}
T const &operator * () const { return *_c; }
T const *operator -> () const { return _c; }
bool operator == (Next_iter const &o) const { return _c == o._c; }
bool operator != (Next_iter const &o) const { return _c != o._c; }
private:
T const *_c;
};
struct Dmar_dev_scope
{
enum Types
{
Pci_endpoint = 1,
Pci_subhierarchy,
Io_apic,
Hpet_msi,
Acpi_namespace_device
};
Dmar_dev_scope const *next() const
{ return offset_cast<Dmar_dev_scope const *>(this, length); }
struct Path_entry
{
l4_uint8_t dev;
l4_uint8_t func;
};
Path_entry const *begin() const { return path; }
Path_entry const *end() const
{ return offset_cast<Path_entry const *>(this, length); }
l4_uint8_t type;
l4_uint8_t length;
l4_uint16_t _rsvd;
l4_uint8_t enum_id;
l4_uint8_t start_bus_nr;
Path_entry path[];
} __attribute__((packed));
struct Dev_scope_vect
{
typedef Next_iter<Dmar_dev_scope> Iterator;
Dev_scope_vect() = default;
Dev_scope_vect(Iterator b, Iterator e)
: _begin(b), _end(e)
{}
Iterator begin() { return _begin; }
Iterator end() { return _end; }
Iterator begin() const { return _begin; }
Iterator end() const { return _end; }
private:
Iterator _begin, _end;
};
template<typename TYPE>
struct Dmar_dev_scope_mixin
{
Dev_scope_vect devs() const
{
TYPE const *t = static_cast<TYPE const*>(this);
return Dev_scope_vect(reinterpret_cast<Dmar_dev_scope const *>(t + 1),
offset_cast<Dmar_dev_scope const *>(t, t->length));
}
};
struct Dmar_drhd : Dmar_head, Dmar_dev_scope_mixin<Dmar_drhd>
{
enum { Id = 0 };
l4_uint8_t flags;
l4_uint8_t _rsvd;
l4_uint16_t segment;
l4_uint64_t register_base;
CXX_BITFIELD_MEMBER_RO(0, 0, include_pci_all, flags);
} __attribute__((packed));
struct Dmar_rmrr : Dmar_head, Dmar_dev_scope_mixin<Dmar_rmrr>
{
enum { Id = 1 };
l4_uint16_t _rsvd;
l4_uint16_t segment;
l4_uint64_t base;
l4_uint64_t limit;
} __attribute__((packed));
struct Dmar_atsr : Dmar_head, Dmar_dev_scope_mixin<Dmar_atsr>
{
enum { Id = 2 };
l4_uint8_t flags;
l4_uint8_t _rsvd;
l4_uint16_t segment;
} __attribute__((packed));
struct Dmar_rhsa : Dmar_head
{
enum { Id = 3 };
private:
l4_uint32_t _rsvd;
l4_uint64_t register_base;
l4_uint32_t proximity_domain;
} __attribute__((packed));
struct Dmar_andd : Dmar_head
{
enum { Id = 4 };
private:
l4_uint8_t _rsvd[3];
l4_uint8_t acpi_dev_nr;
l4_uint8_t acpi_name[];
} __attribute__((packed));
}} // namespace Hw::Acpi
/**
* Header fields common to all ACPI tables.
*/
class Acpi_table_head
{
public:
char signature[4];
l4_uint32_t len;
l4_uint8_t rev;
l4_uint8_t chk_sum;
char oem_id[6];
char oem_tid[8];
l4_uint32_t oem_rev;
l4_uint32_t creator_id;
l4_uint32_t creator_rev;
bool checksum_ok() const
{
l4_uint8_t sum = 0;
for (unsigned i = 0; i < len; ++i)
sum += *(reinterpret_cast<l4_uint8_t const *>(this) + i);
return !sum;
}
} __attribute__((packed));
/**
* Representation of the ACPI DMAR table.
*/
struct Acpi_dmar : Acpi_table_head
{
typedef Hw::Acpi::Next_iter<Hw::Acpi::Dmar_head> Iterator;
Iterator begin() const
{ return reinterpret_cast<Hw::Acpi::Dmar_head const *>(this + 1); }
Iterator end() const
{ return offset_cast<Hw::Acpi::Dmar_head const *>(this, len); }
struct Flags
{
l4_uint8_t raw;
CXX_BITFIELD_MEMBER( 0, 0, intr_remap, raw);
CXX_BITFIELD_MEMBER( 1, 1, x2apic_opt_out, raw);
};
l4_uint8_t haw; ///< Host Address Width
Flags flags;
l4_uint8_t _rsvd[10];
} __attribute__((packed));

View File

@@ -0,0 +1,601 @@
/*
* (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/sys/compiler.h>
#include <l4/sigma0/sigma0.h>
#include <l4/sys/kip>
#include <cstdint>
#include "debug.h"
L4_BEGIN_DECLS
#include "acpi.h"
#include "acpiosxf.h"
L4_END_DECLS
#include <pci-root.h>
#include "res.h"
using L4::Kip::Mem_desc;
ACPI_STATUS
AcpiOsEnterSleep (
UINT8 SleepState,
UINT32 RegaValue,
UINT32 RegbValue)
{
(void) SleepState;
(void) RegaValue;
(void) RegbValue;
d_printf(DBG_ERR, "error: AcpiOsEnterSleep must never be used in our case...\n");
return AE_ERROR;
}
#if defined(ARCH_amd64) || defined(ARCH_x86)
#include <l4/util/port_io.h>
#define DEBUG_OSL_PORT_IO 0
/*
* Platform and hardware-independent I/O interfaces
*/
ACPI_STATUS
AcpiOsReadPort (
ACPI_IO_ADDRESS address,
UINT32 *value,
UINT32 width)
{
if (DEBUG_OSL_PORT_IO)
d_printf(DBG_ALL, "IN: adr=0x%lx, width=%i\n",
(unsigned long)address, width);
if (address == 0x80)
return AE_OK;
switch (width)
{
case 8:
if (res_get_ioport(address, 0) < 0)
return AE_BAD_PARAMETER;
*value = l4util_in8((l4_uint16_t)address);
break;
case 16:
if (res_get_ioport(address, 1) < 0)
return AE_BAD_PARAMETER;
*value = l4util_in16((l4_uint16_t)address);
break;
case 32:
if (res_get_ioport(address, 2) < 0)
return AE_BAD_PARAMETER;
*value = l4util_in32((l4_uint16_t)address);
break;
default :
return AE_BAD_PARAMETER;
}
if (DEBUG_OSL_PORT_IO)
d_printf(DBG_ALL, "\tport(0x%lx)=>0x%x\n", (unsigned long)address, *value);
return AE_OK;
}
ACPI_STATUS
AcpiOsWritePort (
ACPI_IO_ADDRESS address,
UINT32 value,
UINT32 width)
{
if (DEBUG_OSL_PORT_IO)
d_printf(DBG_ALL, "\tport(0x%lx)<=0x%x\n", (unsigned long)address, value);
if (address == 0x80)
return AE_OK;
switch (width)
{
case 8:
if (res_get_ioport(address, 0) < 0)
return AE_BAD_PARAMETER;
l4util_out8((l4_uint8_t)value,(l4_uint16_t)address);
break;
case 16:
if (res_get_ioport(address, 1) < 0)
return AE_BAD_PARAMETER;
l4util_out16((l4_uint16_t)value,(l4_uint16_t)address);
break;
case 32:
if (res_get_ioport(address, 2) < 0)
return AE_BAD_PARAMETER;
l4util_out32((l4_uint32_t)value,(l4_uint32_t)address);
break;
default :
return AE_BAD_PARAMETER;
}
return AE_OK;
}
#else
ACPI_STATUS
AcpiOsReadPort (
ACPI_IO_ADDRESS /*address*/,
UINT32 * /*value*/,
UINT32 /*width*/)
{
return AE_NO_MEMORY;
}
ACPI_STATUS
AcpiOsWritePort (
ACPI_IO_ADDRESS /*address*/,
UINT32 /*value*/,
UINT32 /*width*/)
{
return AE_NO_MEMORY;
}
#endif
void *
AcpiOsMapMemory (
ACPI_PHYSICAL_ADDRESS where,
ACPI_SIZE length)
{
bool cached = false;
// If the memory is in a known (reserved) RAM region it must be mapped
// cachable. This is required by Arm architectures because of the unaligned
// members in ACPI tables.
for (auto const &md: Mem_desc::all(l4re_kip()))
{
if (md.is_virtual())
continue;
if (where < md.start() || where > md.end())
continue;
switch (md.type())
{
case Mem_desc::Conventional:
case Mem_desc::Reserved:
case Mem_desc::Dedicated:
case Mem_desc::Bootloader:
break;
case Mem_desc::Info:
if (md.sub_type() != Mem_desc::Info_acpi_rsdp)
continue;
break;
case Mem_desc::Arch:
if (md.sub_type() != Mem_desc::Arch_acpi_tables)
continue;
break;
default:
continue;
}
cached = true;
break;
}
void *virt = (void*)res_map_iomem(where, length, cached);
d_printf(DBG_DEBUG, "%s(%lx, %lx) = %lx, %s\n",
__func__, (unsigned long)where, (unsigned long)length,
(unsigned long)virt, cached ? "cached" : "uncached");
return virt;
}
void
AcpiOsUnmapMemory (
void *logical_address,
ACPI_SIZE size)
{
(void)logical_address;
(void)size;
// l4io_release_iomem((l4_addr_t)logical_address, size);
return;
}
/******************************************************************************
*
* FUNCTION: AcpiOsReadPciConfiguration
*
* PARAMETERS: PciId Seg/Bus/Dev
* Register Device Register
* Value Buffer where value is placed
* Width Number of bits
*
* RETURN: Status
*
* DESCRIPTION: Read data from PCI configuration space
*
*****************************************************************************/
extern inline
l4_uint32_t
pci_conf_addr(l4_uint32_t bus, l4_uint32_t dev, l4_uint32_t fn, l4_uint32_t reg)
{ return 0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3); }
ACPI_STATUS
AcpiOsReadPciConfiguration (
ACPI_PCI_ID *PciId,
UINT32 Register,
UINT64 *Value,
UINT32 Width)
{
//printf("%s: ...\n", __func__);
Hw::Pci::Root_bridge *rb = Hw::Pci::root_bridge(PciId->Segment);
if (!rb)
{
#if defined(ARCH_x86) || defined(ARCH_amd64)
if (Register >= 0x100)
{
d_printf(DBG_ERR, "error: PCI config space register out of range\n");
return AE_BAD_PARAMETER;
}
Hw::Pci::Port_root_bridge prb(0, 0, 0, nullptr);
int r = prb.cfg_read(Hw::Pci::Cfg_addr(PciId->Bus, PciId->Device,
PciId->Function, Register),
(l4_uint32_t *)Value, Hw::Pci::cfg_w_to_o(Width));
if (r < 0)
return AE_BAD_PARAMETER;
return AE_OK;
#else
d_printf(DBG_ERR, "error: no PCI bridge for seg %u\n", PciId->Segment);
return AE_BAD_PARAMETER;
#endif
}
int r = rb->cfg_read(Hw::Pci::Cfg_addr(PciId->Bus, PciId->Device,
PciId->Function, Register),
(l4_uint32_t *)Value,
Hw::Pci::cfg_w_to_o(Width));
if (r < 0)
return AE_BAD_PARAMETER;
return AE_OK;
}
/******************************************************************************
*
* FUNCTION: AcpiOsWritePciConfiguration
*
* PARAMETERS: PciId Seg/Bus/Dev
* Register Device Register
* Value Value to be written
* Width Number of bits
*
* RETURN: Status.
*
* DESCRIPTION: Write data to PCI configuration space
*
*****************************************************************************/
ACPI_STATUS
AcpiOsWritePciConfiguration (
ACPI_PCI_ID *PciId,
UINT32 Register,
ACPI_INTEGER Value,
UINT32 Width)
{
//printf("%s: ...\n", __func__);
Hw::Pci::Root_bridge *rb = Hw::Pci::root_bridge(PciId->Segment);
if (!rb)
{
#if defined(ARCH_x86) || defined(ARCH_amd64)
if (Register >= 0x100)
{
d_printf(DBG_ERR, "error: PCI config space register out of range\n");
return AE_BAD_PARAMETER;
}
Hw::Pci::Port_root_bridge prb(0, 0, 0, nullptr);
int r = prb.cfg_write(Hw::Pci::Cfg_addr(PciId->Bus, PciId->Device,
PciId->Function, Register),
Value, Hw::Pci::cfg_w_to_o(Width));
if (r < 0)
return AE_BAD_PARAMETER;
return AE_OK;
#else
d_printf(DBG_ERR, "error: no PCI bridge for seg %u\n", PciId->Segment);
return AE_BAD_PARAMETER;
#endif
}
int r = rb->cfg_write(Hw::Pci::Cfg_addr(PciId->Bus, PciId->Device,
PciId->Function, Register),
Value, Hw::Pci::cfg_w_to_o(Width));
if (r < 0)
return AE_BAD_PARAMETER;
return AE_OK;
return (AE_OK);
}
// multi-threading -------------------------------------------------
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
#include <l4/cxx/unique_ptr>
namespace {
// fwd, see below
class Async_work_queue;
/**
* Asynchronous work item, runs a callback with a data pointer.
*/
class Async_work
{
public:
/// function pointer for the callback
typedef void (*Callback)(void *);
/// create work item, with callback and data
Async_work(Callback callback, void *data)
: _d(data), _cb(callback)
{}
/// execute the work item, ussually handled by a work queue
void exec()
{ _cb(_d); }
private:
// sets _q directly
friend class Async_work_queue;
void *_d;
Callback _cb;
Async_work_queue *_q = 0;
};
/**
* Simple asynchronous work queue.
*
* This implementation executes all work items independently and
* possibly in parallel (in any order).
*/
class Async_work_queue
{
public:
/// flush the work queue (blocks until all work items are finished.
void flush()
{
if (!_num_running)
return;
pthread_mutex_lock(&_l);
while (_num_running)
pthread_cond_wait(&_c, &_l);
pthread_mutex_unlock(&_l);
}
/// schedule the given work item
int schedule(cxx::unique_ptr<Async_work> work)
{
pthread_t t;
pthread_mutex_lock(&_l);
++_num_running;
pthread_mutex_unlock(&_l);
work->_q = this;
int r = pthread_create(&t, NULL, _exec, work);
if (r != 0)
{
bool signal = false;
pthread_mutex_lock(&_l);
signal = (--_num_running == 0);
pthread_mutex_unlock(&_l);
if (signal)
pthread_cond_broadcast(&_c);
return r;
}
work.release();
pthread_detach(t);
return 0;
}
private:
void work_done()
{
bool signal = false;
pthread_mutex_lock(&_l);
signal = (--_num_running == 0);
pthread_mutex_unlock(&_l);
if (signal)
pthread_cond_broadcast(&_c);
}
static void *_exec(void *work)
{
Async_work *w = static_cast<Async_work *>(work);
w->exec();
w->_q->work_done();
delete w;
return 0;
}
pthread_mutex_t _l = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t _c = PTHREAD_COND_INITIALIZER;
unsigned _num_running = 0;
};
/// work queue for AcpiOsExecute
static Async_work_queue acpi_work_queue;
}
ACPI_THREAD_ID
AcpiOsGetThreadId(void)
{
return (ACPI_THREAD_ID)pthread_self();
}
ACPI_STATUS
AcpiOsExecute(
ACPI_EXECUTE_TYPE type,
ACPI_OSD_EXEC_CALLBACK function,
void *context)
{
(void)type;
int r = acpi_work_queue.schedule(cxx::make_unique<Async_work>(function, context));
if (r < 0)
return AE_ERROR;
return AE_OK;
}
void
AcpiOsWaitEventsComplete()
{
acpi_work_queue.flush();
}
ACPI_STATUS
AcpiOsCreateSemaphore (
uint32_t,
uint32_t initial_units,
ACPI_SEMAPHORE *out_handle)
{
cxx::unique_ptr<sem_t> sem(new sem_t);
if (sem_init(sem.get(), 0, initial_units) < 0)
{
perror("error: cannot initialize semaphore");
return AE_ERROR;
}
*out_handle = sem.release();
return AE_OK;
}
ACPI_STATUS
AcpiOsDeleteSemaphore (
ACPI_SEMAPHORE handle)
{
sem_t *s = static_cast<sem_t*>(handle);
sem_destroy(s);
delete s;
return AE_OK;
}
inline ACPI_STATUS calc_timeout(uint16_t timeout, struct timespec *to)
{
if (timeout == 0xffff)
return AE_OK;
if (clock_gettime(CLOCK_REALTIME, to) < 0)
{
perror("error: cannot get current time");
return AE_ERROR;
}
// check for normalized time (tv_nsec < 1s)
if (L4_UNLIKELY(to->tv_nsec >= 1000000000))
return AE_ERROR;
to->tv_sec += timeout / 1000;
to->tv_nsec += (timeout % 1000) * 1000000;
if (to->tv_nsec >= 1000000000)
{
++to->tv_sec;
to->tv_nsec -= 1000000000;
}
return AE_OK;
}
ACPI_STATUS
AcpiOsWaitSemaphore (
ACPI_SEMAPHORE handle,
uint32_t units,
uint16_t timeout)
{
struct timespec to;
ACPI_STATUS r = calc_timeout(timeout, &to);
if (ACPI_FAILURE(r))
return r;
sem_t *sem = static_cast<sem_t*>(handle);
while (units > 0)
{
int r;
if (timeout == 0xffff)
r = sem_wait(sem);
else
r = sem_timedwait(sem, &to);
if (r < 0)
{
switch (errno)
{
case EINVAL: return AE_BAD_PARAMETER;
case EINTR: continue; // retry after signal
case EAGAIN: continue;
case ETIMEDOUT: return AE_TIME;
default: return AE_ERROR;
}
}
// got the semaphore, do again unit units == 0
--units;
}
return AE_OK;
}
ACPI_STATUS
AcpiOsSignalSemaphore (
ACPI_SEMAPHORE handle,
uint32_t units)
{
for (; units; --units)
if (sem_post(static_cast<sem_t*>(handle)) < 0)
return AE_BAD_PARAMETER;
return AE_OK;
}
ACPI_STATUS
AcpiOsCreateLock (ACPI_SPINLOCK *out_handle)
{
pthread_mutex_t *m = new pthread_mutex_t(PTHREAD_MUTEX_INITIALIZER);
*out_handle = (ACPI_SPINLOCK)m;
return AE_OK;
}
void
AcpiOsDeleteLock (ACPI_SPINLOCK handle)
{
delete static_cast<pthread_mutex_t*>(handle);
return;
}
ACPI_CPU_FLAGS
AcpiOsAcquireLock (ACPI_SPINLOCK handle)
{
pthread_mutex_lock(static_cast<pthread_mutex_t*>(handle));
return AE_OK;
}
void
AcpiOsReleaseLock (
ACPI_SPINLOCK handle,
ACPI_CPU_FLAGS)
{
pthread_mutex_unlock(static_cast<pthread_mutex_t *>(handle));
return;
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2014-2020, 2024 Kernkonzept GmbH.
* Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "debug.h"
#include "irq_server.h"
#include <stdio.h>
#include <l4/sys/compiler.h>
L4_BEGIN_DECLS
#include "acpi.h"
#include "acpiosxf.h"
#include "actypes.h"
L4_END_DECLS
#include <l4/sys/cxx/ipc_epiface>
#include <l4/re/env>
#include "../io_acpi.h"
namespace {
class Acpi_sci :
public Kernel_irq_pin,
public L4::Irqep_t<Acpi_sci>
{
public:
Acpi_sci(ACPI_OSD_HANDLER isr, void *context, int irqnum)
: Kernel_irq_pin(irqnum), _isr(isr), _context(context) {}
void handle_irq()
{
if (!_isr(_context))
trace_event(TRACE_ACPI_EVENT, "SCI not handled\n");
unmask();
}
private:
ACPI_OSD_HANDLER _isr;
void *_context;
};
}
ACPI_STATUS
AcpiOsInstallInterruptHandler (
UINT32 interrupt_number,
ACPI_OSD_HANDLER service_routine,
void *context)
{
int err;
L4::Cap<L4::Icu> icu = L4Re::Env::env()->get_cap<L4::Icu>("icu");
if (!icu)
{
d_printf(DBG_ERR, "error: could not find ICU capability.\n");
return AE_BAD_PARAMETER;
}
Acpi_sci *sci = new Acpi_sci(service_routine, context, interrupt_number);
L4::Cap<L4::Irq> irq = irq_queue()->register_irq_obj(sci);
if (!irq.is_valid())
{
d_printf(DBG_ERR, "error: could not register ACPI event server\n");
return AE_BAD_PARAMETER;
}
if ((err = sci->bind(irq, L4_IRQ_F_NONE)) < 0)
{
d_printf(DBG_ERR, "error: irq bind failed: %d\n", err);
return AE_BAD_PARAMETER;
}
d_printf(DBG_INFO, "created ACPI event server and attached it to irq %d\n",
interrupt_number);
sci->unmask();
Hw::Acpi::register_sci(sci);
return AE_OK;
};
ACPI_STATUS
AcpiOsRemoveInterruptHandler (
UINT32 interrupt_number,
ACPI_OSD_HANDLER service_routine)
{
printf("%s:%d:%s(%d, %p): UNINPLEMENTED\n",
__FILE__, __LINE__, __func__,
interrupt_number, service_routine);
return AE_OK;
}

View File

@@ -0,0 +1,295 @@
#include "debug.h"
#include "io_acpi.h"
#include "__acpi.h"
#include <pci-dev.h>
#include <pci-bridge.h>
#include <l4/cxx/list>
#include <l4/cxx/unique_ptr>
namespace {
using namespace Hw;
static void pci_acpi_wake_dev(ACPI_HANDLE, l4_uint32_t event, void *context)
{
Pci::Dev *pci_dev = static_cast<Pci::Dev*>(context);
if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev)
return;
pci_dev->check_pme_status();
}
struct Acpi_pci_handler : Hw::Feature_manager<Pci::Dev, Acpi_dev>
{
bool setup(Hw::Device *, Pci::Dev *pci, Acpi_dev *acpi_dev) const override
{
ACPI_STATUS status;
status = AcpiInstallNotifyHandler(acpi_dev->handle(),
ACPI_SYSTEM_NOTIFY,
pci_acpi_wake_dev, pci);
if (ACPI_FAILURE(status))
d_printf(DBG_ERR, "error: cannot install notification handler "
"for ACPI PCI wakeup events: %s\n",
AcpiFormatException(status));
return false;
}
};
static Acpi_pci_handler _acpi_pci_handler;
struct Prt_entry : public cxx::List_item
{
unsigned slot;
unsigned char pin;
acpica_pci_irq irq;
};
class Acpi_pci_irq_router_rs : public Resource_space
{
public:
Prt_entry *_prt;
public:
Acpi_pci_irq_router_rs() : _prt(0) {}
char const *res_type_name() const override
{ return "PCI ACPI IRQ router"; }
int add_prt_entry(ACPI_HANDLE obj, ACPI_PCI_ROUTING_TABLE *e);
int find(int device, int pin, struct acpica_pci_irq **irq);
bool request(Resource *parent, ::Device *,
Resource *child, ::Device *cdev) override;
bool alloc(Resource *, ::Device *, Resource *, ::Device *, bool) override
{ return false; }
void assign(Resource *, Resource *) override
{
d_printf(DBG_ERR, "internal error: cannot assign to root Acpi_pci_irq_router_rs\n");
}
bool adjust_children(Resource *) override
{
d_printf(DBG_ERR, "internal error: cannot adjust root Acpi_pci_irq_router_rs\n");
return false;
}
};
bool
Acpi_pci_irq_router_rs::request(Resource *parent, ::Device *,
Resource *child, ::Device *cdev)
{
if (dlevel(DBG_ALL))
{
printf("requesting IRQ resource: ");
cdev->dump(2);
child->dump(2);
printf(" at ACPI IRQ routing resource\n");
}
Hw::Device *cd = dynamic_cast<Hw::Device*>(cdev);
assert (cd);
struct acpica_pci_irq *irq = 0;
if (find(cd->adr() >> 16, child->start(), &irq) < 0)
{
child->disable();
if (dlevel(DBG_WARN))
{
d_printf(DBG_WARN,
"warning: could not allocate PCI IRQ: missing PRT entry: %s\n",
cdev->get_full_path().c_str());
child->dump(2);
cdev->dump(2);
}
return false;
}
assert (irq);
child->del_flags(Resource::F_relative);
child->start(irq->irq);
child->del_flags(Resource::Irq_type_mask);
unsigned flags = Resource::Irq_type_base;
flags |= (!irq->trigger) * Resource::Irq_type_base * L4_IRQ_F_LEVEL;
flags |= (!!irq->polarity) * Resource::Irq_type_base * L4_IRQ_F_NEG;
child->add_flags(flags);
child->parent(parent);
return true;
}
static ACPI_STATUS
get_irq_cb(ACPI_RESOURCE *res, void *ctxt)
{
acpica_pci_irq *irq = (acpica_pci_irq*)ctxt;
if (!res)
return AE_OK;
switch (res->Type)
{
case ACPI_RESOURCE_TYPE_IRQ:
irq->irq = res->Data.Irq.Interrupts[0];
irq->polarity = res->Data.Irq.Polarity;
irq->trigger = res->Data.Irq.Triggering;
return AE_OK;
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
irq->irq = res->Data.ExtendedIrq.Interrupts[0];
irq->polarity = res->Data.ExtendedIrq.Polarity;
irq->trigger = res->Data.ExtendedIrq.Triggering;
return AE_OK;
default:
return AE_OK;
}
}
int
Acpi_pci_irq_router_rs::add_prt_entry(ACPI_HANDLE obj,
ACPI_PCI_ROUTING_TABLE *e)
{
if (!e)
return -EINVAL;
cxx::unique_ptr<Prt_entry> ne(new Prt_entry());
if (!ne)
return -ENOMEM;
ne->slot = (e->Address >> 16) & 0xffff;
ne->pin = e->Pin;
ne->irq.irq = e->SourceIndex;
ne->irq.polarity = ACPI_ACTIVE_LOW;
ne->irq.trigger = ACPI_LEVEL_SENSITIVE;
if (e->Source[0])
{
ACPI_HANDLE link;
d_printf(DBG_DEBUG, " (dev[%s][%d]) ", e->Source, e->SourceIndex);
ACPI_STATUS status;
status = AcpiGetHandle(obj, e->Source, &link);
if (ACPI_FAILURE(status))
{
d_printf(DBG_WARN, "\nWARNING: Could not find PCI IRQ Link Device...\n");
return -ENODEV;
}
status = AcpiWalkResources(link, ACPI_STRING("_CRS"), get_irq_cb, &ne->irq);
if (ACPI_FAILURE(status))
{
d_printf(DBG_WARN, "\nWARNING: Could not evaluate _CRS of PCI IRQ Link Device\n");
return -ENODEV;
}
}
_prt = cxx::List_item::push_back(_prt, ne.release());
return 0;
}
int
Acpi_pci_irq_router_rs::find(int device, int pin, struct acpica_pci_irq **irq)
{
Prt_entry::T_iter<Prt_entry> c = _prt;
while (*c)
{
if (c->slot == (unsigned)device && c->pin == pin)
{
*irq = &c->irq;
return 0;
}
++c;
}
return -ENODEV;
}
static
Resource *discover_prt(Acpi_dev *adev)
{
ACPI_STATUS status;
ACPI_HANDLE handle;
status = AcpiGetHandle(adev->handle(), ACPI_STRING("_PRT"), &handle);
// no PRT!!
if (ACPI_FAILURE(status))
return 0;
Acpi_auto_buffer buf;
buf.Length = ACPI_ALLOCATE_BUFFER;
status = AcpiGetIrqRoutingTable(adev->handle(), &buf);
if (ACPI_FAILURE(status))
{
d_printf(DBG_ERR, "ERROR: while getting PRT for [%s]\n", "buffer");
return 0;
}
typedef Hw::Pci::Irq_router_res<Acpi_pci_irq_router_rs> Irq_res;
Irq_res *r = new Irq_res();
char *p = (char*)buf.Pointer;
char *e = (char*)buf.Pointer + buf.Length;
while (1)
{
ACPI_PCI_ROUTING_TABLE *prt = (ACPI_PCI_ROUTING_TABLE *)p;
if (prt->Length == 0)
break;
if (p + prt->Length > e)
break;
int err = r->provided()->add_prt_entry(adev->handle(), prt);
if (err < 0)
{
d_printf(DBG_ERR, "error: adding PRT entry: %d\n", err);
delete r;
return 0;
}
p += prt->Length;
}
return r;
}
struct Acpi_pci_bridge_handler : Hw::Feature_manager<Acpi_dev>
{
bool setup(Hw::Device *dev, Acpi_dev *acpi_dev) const override
{
if (Resource *router = discover_prt(acpi_dev))
{
if (dev->resources()->find_if(Resource::is_irq_provider_s))
{
d_printf(DBG_WARN, "warning: multiple IRQ routing tables for device: %s\n",
dev->get_full_path().c_str());
delete router;
return false;
}
dev->add_resource_rq(router);
}
return false;
}
};
static Acpi_pci_bridge_handler _acpi_pci_bridge_handler;
}

View File

@@ -0,0 +1,453 @@
/* SPDX-License-Identifier: GPL-2.0-only OR License-Ref-kk-custom */
/*
* Copyright (C) 2023-2025 Kernkonzept GmbH.
* Author(s): Jan Klötzke <jan.kloetzke@kernkonzept.com>
*/
#include <map>
#include <vector>
#include "debug.h"
#include "io_acpi.h"
namespace {
/**
* Internal IORT translation node.
*/
struct Iort_node
{
enum : l4_uint64_t { Translation_failed = ~(l4_uint64_t)0 };
virtual ~Iort_node() = default;
/**
* Do the translation for ITS Device-IDs.
*
* \return Device-ID or Translation_failed on error.
*/
virtual l4_uint64_t translate_device_id(l4_uint64_t src) const = 0;
/**
* Do the translation for SMMU Stream-IDs.
*
* \return Stream-ID or Translation_failed on error.
*/
virtual l4_uint64_t translate_stream_id(l4_uint64_t src) const = 0;
/**
* Recursively parse IORT nodes.
*/
static Iort_node *parse_mappings(ACPI_TABLE_IORT *iort, ACPI_IORT_NODE *node,
ACPI_TABLE_MADT *madt);
};
/**
* An opaque Stream-/Device-ID translation node.
*
* Used for SMMU and root complex nodes in the IORT.
*/
class Translator : public Iort_node
{
struct Mapping
{
l4_uint32_t in_base;
l4_uint32_t num; // Attention: number of IDs in the range minus one
l4_uint32_t out_base;
Iort_node *out_node;
Mapping(l4_uint32_t in_base, l4_uint32_t num, l4_uint32_t out_base,
Iort_node *out_node)
: in_base(in_base), num(num), out_base(out_base), out_node(out_node)
{}
};
public:
l4_uint64_t translate_device_id(l4_uint64_t src) const override
{
for (auto const &m : _mappings)
{
if (src < m.in_base || src > m.in_base + m.num)
continue;
src += m.out_base;
src -= m.in_base;
return m.out_node->translate_device_id(src);
}
return ~(l4_uint64_t)0;
}
l4_uint64_t translate_stream_id(l4_uint64_t src) const override
{
for (auto const &m : _mappings)
{
if (src < m.in_base || src > m.in_base + m.num)
continue;
src += m.out_base;
src -= m.in_base;
return m.out_node->translate_stream_id(src);
}
return ~(l4_uint64_t)0;
}
Translator(ACPI_TABLE_IORT *iort, ACPI_IORT_NODE *node, ACPI_TABLE_MADT *madt)
{
_mappings.reserve(node->MappingCount);
auto *mappings = ACPI_ADD_PTR(ACPI_IORT_ID_MAPPING, node,
node->MappingOffset);
for (UINT32 i = 0; i < node->MappingCount; i++)
{
ACPI_IORT_ID_MAPPING const &mapping = mappings[i];
// TODO: single mappings are for SMMU or RC originating MSIs
if (mapping.Flags & ACPI_IORT_ID_SINGLE_MAPPING)
continue;
auto *out_node = ACPI_ADD_PTR(ACPI_IORT_NODE, iort,
mapping.OutputReference);
auto *out_mapping = Iort_node::parse_mappings(iort, out_node, madt);
if (!out_mapping)
continue;
_mappings.emplace_back(mapping.InputBase, mapping.IdCount,
mapping.OutputBase, out_mapping);
}
}
private:
std::vector<Mapping> _mappings;
};
using Root_complex = Translator;
class Smmu : public Translator
{
public:
Smmu(unsigned idx, ACPI_TABLE_IORT *iort, ACPI_IORT_NODE *smmu_node,
ACPI_TABLE_MADT *madt)
: Translator(iort, smmu_node, madt), _idx(idx)
{}
protected:
l4_uint64_t translate_stream_id(l4_uint64_t src) const override
{
/*
* Fiasco src encoding:
* 63-48: reserved
* 47-32: smmu_idx
* 31- 0: device_id
*/
return ((l4_uint64_t)_idx << 32) | src;
}
private:
unsigned _idx;
};
/**
* ITS IORT node.
*/
struct Its : public Iort_node
{
explicit Its(unsigned idx) : _idx(idx) {}
l4_uint64_t translate_device_id(l4_uint64_t src) const override
{
/*
* Fiasco src encoding:
* 63-48: reserved
* 47-32: its_idx
* 31- 0: device_id
*/
return ((l4_uint64_t)_idx << 32) | src;
}
l4_uint64_t translate_stream_id(l4_uint64_t src) const override
{
d_printf(DBG_ERR, "IORT: DMA translation requested for ITS (%u, 0x%llx)\n",
_idx, src);
return Translation_failed;
}
private:
unsigned _idx;
};
int
its_index(ACPI_TABLE_MADT *madt, UINT32 id)
{
unsigned count = 0;
for (unsigned offset = sizeof(ACPI_TABLE_MADT); offset < madt->Header.Length;)
{
auto *entry = ACPI_ADD_PTR(ACPI_SUBTABLE_HEADER, madt, offset);
offset += entry->Length;
if (entry->Type != ACPI_MADT_TYPE_GENERIC_TRANSLATOR)
continue;
auto *its = reinterpret_cast<ACPI_MADT_GENERIC_MSI_FRAME *>(entry);
if (its->MsiFrameId == id)
return count;
count++;
}
d_printf(DBG_ERR, "IORT: references unknown ITS %u\n", id);
return -1;
}
int
smmu_index(ACPI_TABLE_IORT *iort, ACPI_IORT_NODE *smmu_node)
{
unsigned count = 0;
for (UINT32 offset = iort->NodeOffset, i = 0;
offset < iort->Header.Length && i < iort->NodeCount;
i++)
{
auto *node = ACPI_ADD_PTR(ACPI_IORT_NODE, iort, offset);
offset += node->Length;
if (node->Type != ACPI_IORT_NODE_SMMU &&
node->Type != ACPI_IORT_NODE_SMMU_V3)
continue;
if (node == smmu_node)
return count;
count++;
}
d_printf(DBG_ERR, "IORT: references unknown SMMU\n");
return -1;
}
Iort_node *
Iort_node::parse_mappings(ACPI_TABLE_IORT *iort, ACPI_IORT_NODE *node,
ACPI_TABLE_MADT *madt)
{
switch (node->Type)
{
case ACPI_IORT_NODE_ITS_GROUP:
{
// We just use the first ITS in a group. Could be extended in the
// future to distribute MSI sources evenly in a group.
auto *its = ACPI_CAST_PTR(ACPI_IORT_ITS_GROUP, &node->NodeData);
if (its->ItsCount == 0)
{
d_printf(DBG_ERR, "IORT: no ITS in group!\n");
return nullptr;
}
int idx = its_index(madt, its->Identifiers[0]);
if (idx < 0)
return nullptr;
return new Its(idx);
}
case ACPI_IORT_NODE_SMMU:
case ACPI_IORT_NODE_SMMU_V3:
{
int idx = smmu_index(iort, node);
if (idx < 0)
return nullptr;
return new Smmu(idx, iort, node, madt);
}
default:
d_printf(DBG_WARN, "IORT: unexpected node type: %d\n", node->Type);
return nullptr;
}
}
/**
* IORT table helper.
*
* Arm64 ACPI based systems describe the relationship of PCI root complexes,
* SMMUs and ITSs by the "IO Remapping Table". See ARM DEN 0049E.
*/
class Iort : public Hw::Pci::Platform_adapter_if
{
public:
/**
* Parse IORT table and create translators for each PCI segment.
*
* Find all defined root complex nodes and traverse their dependency chain.
*/
Iort()
{
ACPI_TABLE_HEADER *iort_hdr;
ACPI_STATUS status = AcpiGetTable(ACPI_STRING(ACPI_SIG_IORT), 1, &iort_hdr);
if (ACPI_FAILURE(status))
return;
// At lease on the AVA platform we see this spec violation. Linux doesn't
// seem to care as well...
auto *iort = reinterpret_cast<ACPI_TABLE_IORT *>(iort_hdr);
if (iort->Header.Revision < 3)
d_printf(DBG_WARN,
"Firmware bug: IORT table too old: %d. Continuing anyway...\n",
iort->Header.Revision);
// We also need the MADT table. Fiasco uses the position of an ITS in the
// MADT as index.
ACPI_TABLE_HEADER *madt_hdr;
status = AcpiGetTable(ACPI_STRING(ACPI_SIG_MADT), 1, &madt_hdr);
if (ACPI_FAILURE(status))
return;
auto *madt = reinterpret_cast<ACPI_TABLE_MADT *>(madt_hdr);
for (UINT32 offset = iort->NodeOffset, i = 0;
offset < iort->Header.Length && i < iort->NodeCount;
i++)
{
auto *node = ACPI_ADD_PTR(ACPI_IORT_NODE, iort, offset);
offset += node->Length;
if (node->Type != ACPI_IORT_NODE_PCI_ROOT_COMPLEX)
continue;
auto *rc = ACPI_CAST_PTR(ACPI_IORT_ROOT_COMPLEX, &node->NodeData);
_pci_segments[rc->PciSegmentNumber] = new Root_complex(iort, node, madt);
}
}
int translate_msi_src(::Hw::Pci::If *dev, l4_uint64_t *si) override
{
Root_complex const *rc = find_root_complex(dev->segment_nr());
if (!rc)
return -L4_ENODEV;
// Start from a standard PCI requester ID according to Arm Base System
// Architecture.
l4_uint64_t src = (unsigned{dev->bus_nr()} << 8) | dev->devfn();
/*
* We don't care about DMA requester ID aliasing. The assumption is that
* there are no legacy bridges on Arm64 systems. If we ever have one, we
* will have a fun time here to figure out which is the correct alias or,
* even worse, support multiple aliases.
*/
src = rc->translate_device_id(src);
if (src == Iort_node::Translation_failed)
{
d_printf(DBG_ERR, "IORT: untranslatable MSI source: %02x:%02x.%d\n",
dev->bus_nr(), dev->device_nr(), dev->function_nr());
return -L4_ENODEV;
}
*si = src;
return 0;
}
int translate_dma_src(::Hw::Pci::Dma_requester_id rid, l4_uint64_t *si) const override
{
if (!rid)
return -L4_EINVAL;
Root_complex const *rc = find_root_complex(rid.segment());
if (!rc)
return -L4_ENODEV;
// Start from a standard PCI requester ID according to Arm Base System
// Architecture.
l4_uint64_t src = (unsigned{rid.bus()} << 8) | rid.devfn();
src = rc->translate_stream_id(src);
if (src == Iort_node::Translation_failed)
{
d_printf(DBG_ERR, "IORT: untranslatable DMA source: %04x:%02x:%02x.%u\n",
rid.segment().get(), rid.bus().get(), rid.dev().get(),
rid.fn().get());
return -L4_ENODEV;
}
*si = src;
return 0;
}
int map_msi_src(::Hw::Pci::If *dev, l4_uint64_t msi_addr_phys,
l4_uint64_t *msi_addr_iova) override
{
Dma_domain *d = dev->host()->dma_domain();
if (!d)
return -L4_ENODEV;
// Without DMA space we cannot map. This might be because we have no IOMMU
// or because the client did not yet attach the DMA space. We assume the
// former and just leave the address as-is.
if (!d->kern_dma_space())
{
*msi_addr_iova = msi_addr_phys;
return 0;
}
l4_addr_t virt = res_map_iomem(msi_addr_phys, 4);
if (!virt)
return -L4_ENOMEM;
auto it = _its_maps.find(msi_addr_phys);
if (it != _its_maps.end())
*msi_addr_iova = it->second;
else
{
*msi_addr_iova = _its_next_addr + (msi_addr_phys & (L4_PAGESIZE - 1));
_its_maps[msi_addr_phys] = *msi_addr_iova;
_its_next_addr += L4_PAGESIZE;
}
int res = l4_error(
d->kern_dma_space()->map(L4Re::This_task,
l4_fpage(l4_trunc_size(virt, L4_PAGESHIFT),
L4_PAGESHIFT, L4_FPAGE_RW),
l4_trunc_size(*msi_addr_iova, L4_PAGESHIFT)));
if (res < 0)
d_printf(DBG_ERR,
"error: map_msi_src failed: %d, phys=0x%llx, virt=0x%lx, iova=0x%llx\n",
res, msi_addr_phys, virt, *msi_addr_iova);
return res;
}
private:
Root_complex *find_root_complex(unsigned segment) const
{
auto it = _pci_segments.find(segment);
if (it == _pci_segments.end())
{
d_printf(DBG_WARN, "IORT: no translation for PCI segment %d.\n",
segment);
return nullptr;
}
return it->second;
}
// FIXME: this must be an address that is not in any PCI bridge window!
// FIXME: the region must be marked as reserved in the DMA address space!
// Nothing else should be mapped at this addresses. Otherwise MSIs
// won't work.
l4_uint64_t _its_next_addr = 0xf0000000;
std::map<l4_uint64_t, l4_uint64_t> _its_maps;
std::map<unsigned, Root_complex *> _pci_segments;
};
}
namespace Hw { namespace Acpi {
Hw::Pci::Platform_adapter_if *
setup_pci_platform()
{
static Iort iort_adapter;
return &iort_adapter;
}
} }

View File

@@ -0,0 +1,460 @@
/*
* Copyright (C) 2023-2024 Kernkonzept GmbH.
* Author(s): Jan Klötzke <jan.kloetzke@kernkonzept.com>
* Philipp Eppelt <philipp.eppelt@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "io_acpi.h"
#include "pci-if.h"
#include "pci-dev.h"
#include "pci-bridge.h"
#include "acpi_dmar.h"
#include <l4/cxx/minmax>
#include <vector>
#include <list>
#include <tuple>
#include <optional>
#include "main.h"
namespace Hw {
namespace Acpi {
class Vtd_iommu
{
struct Info
{
/// Range of a PCI subhierarchy with inclusive start and end.
struct Bus_range
{
Bus_range(l4_uint8_t s, l4_uint8_t e) : start_bus_nr(s), end_bus_nr(e) {}
l4_uint8_t start_bus_nr;
l4_uint8_t end_bus_nr;
};
Info(l4_uint8_t idx, l4_uint64_t addr, l4_uint16_t seg, l4_uint8_t flags)
: base_addr(addr), segment(seg), idx(idx), flags(flags)
{}
bool match_bus(l4_uint8_t b) const
{
for (Bus_range const &r : subhierarchies)
if (r.start_bus_nr <= b && b <= r.end_bus_nr)
return true;
return false;
}
void add_subhierarchy(l4_uint8_t start, l4_uint8_t end)
{ subhierarchies.emplace_back(start, end); }
bool pci_all() const
{ return flags & 1; }
l4_uint64_t base_addr = 0;
l4_uint16_t segment = 0;
l4_uint8_t idx = 0;
l4_uint8_t flags = 0;
std::vector<Bus_range> subhierarchies;
};
struct Dev_mmu
{
Dev_mmu(l4_uint16_t b, l4_uint8_t d, l4_uint8_t fn, Info const &mmu)
: bus(b), devfn(d << 3 | fn), iommu(mmu)
{}
l4_uint16_t bus;
l4_uint8_t devfn;
Info const &iommu;
bool match(l4_uint16_t seg, l4_uint8_t b, l4_uint8_t df) const
{
return seg == iommu.segment
&& b == bus
&& df == devfn;
}
l4_uint8_t iommu_idx() const
{ return iommu.idx; }
};
public:
static bool probe();
static int get_index(l4_uint16_t segment, l4_uint8_t bus, l4_uint8_t devfn);
private:
static void parse_drhd_entry(Dmar_drhd const &drhd, unsigned entry_num);
static void parse_drhd_dev_scope(Dev_scope_vect const &devs,
Info &iommu);
static std::optional<std::tuple<l4_uint8_t, l4_uint8_t, l4_uint8_t>>
find_pci_bdf(l4_uint16_t segment, l4_uint8_t bus,
Dmar_dev_scope::Path_entry const *path_it,
Dmar_dev_scope::Path_entry const *path_end);
static std::list<Info> _iommus;
static std::vector<Dev_mmu> _devs;
}; // class Vtd_iommu
std::list<Vtd_iommu::Info> Vtd_iommu::_iommus;
std::vector<Vtd_iommu::Dev_mmu> Vtd_iommu::_devs;
int Vtd_iommu::get_index(l4_uint16_t segment, l4_uint8_t bus, l4_uint8_t devfn)
{
// check explicit devices
for (auto const &d : _devs)
{
if (d.match(segment, bus, devfn))
return d.iommu_idx();
}
unsigned pci_all_idx = -1u;
for (auto const &info : _iommus)
{
if (info.segment != segment)
continue;
if (info.match_bus(bus))
return info.idx;
if (info.pci_all())
pci_all_idx = info.idx;
}
if (pci_all_idx != -1U)
return pci_all_idx;
return -L4_ENODEV;
}
std::optional<std::tuple<l4_uint8_t, l4_uint8_t, l4_uint8_t>>
Vtd_iommu::find_pci_bdf(l4_uint16_t segment, l4_uint8_t bus,
Dmar_dev_scope::Path_entry const *path_it,
Dmar_dev_scope::Path_entry const *path_end)
{
l4_uint8_t dev = 0, fn = 0;
for (; path_it != path_end; ++path_it)
{
dev = path_it->dev;
fn = path_it->func;
Hw::Pci::Dev *pdev = Hw::Pci::find_pci_device(segment, bus, dev, fn);
if (!pdev)
{
d_printf(DBG_ERR, "No PCI device found for %04x:%02x:%x.%x\n",
segment, bus, dev, fn);
return std::nullopt;
}
if (auto *bridge = dynamic_cast<Hw::Pci::Bridge_base *>(pdev))
bus = bridge->secondary;
else
// bus dev fn identifies a device.
d_printf(DBG_DEBUG, "Found PCI Endpoint: %04x:%02x:%x.%x\n", segment,
bus, dev, fn);
}
return std::make_tuple(bus, dev, fn);
}
void Vtd_iommu::parse_drhd_dev_scope(Dev_scope_vect const &devs,
Info &iommu)
{
for (Dmar_dev_scope const &dev_scope : devs)
{
d_printf(DBG_DEBUG,
"Dev scope:\n\ttype: %u, length %u, enum id: %x, start bus num: "
"0x%x, path length: %zi\n",
dev_scope.type, dev_scope.length, dev_scope.enum_id,
dev_scope.start_bus_nr, dev_scope.end() - dev_scope.begin());
for (auto const &p : dev_scope)
d_printf(DBG_DEBUG, "\tpath: %x.%x\n", p.dev, p.func);
l4_uint16_t segment = iommu.segment;
l4_uint8_t bus = dev_scope.start_bus_nr;
auto path_it = dev_scope.begin();
l4_uint8_t dev = path_it->dev;
l4_uint8_t fn = path_it->func;
unsigned path_length = (dev_scope.length - 6) / 2;
switch (dev_scope.type)
{
case Dmar_dev_scope::Pci_endpoint:
if (path_length > 1)
{
auto bdf = find_pci_bdf(segment, bus, path_it, dev_scope.end());
if (bdf)
std::tie(bus, dev, fn) = bdf.value();
else
// error case
break;
}
_devs.emplace_back(bus, dev, fn, iommu);
break;
case Dmar_dev_scope::Pci_subhierarchy:
{
if (path_length > 1)
d_printf(DBG_ERR,
"Warning: Unexpected path length of %i for PCI "
"sub-hierarchy.\n", path_length);
Hw::Pci::Bridge_base *bridge =
Hw::Pci::find_root_bridge(segment, bus);
if (!bridge)
{
d_printf(DBG_DEBUG,
"No root bridge for segment 0x%x and start bus nr 0x%x. "
"Search all devices.\n",
segment, bus);
bridge = dynamic_cast<Hw::Pci::Bridge_base *>(
Hw::Pci::find_pci_device(segment, bus, dev, fn));
}
if (!bridge)
{
d_printf(DBG_ERR, "No PCI bridge device found\n");
break;
}
iommu.add_subhierarchy(bus, bridge->subordinate);
}
break;
// Unhandled types:
// case Dmar_dev_scope::Io_apic:
// case Dmar_dev_scope::Hpet_msi:
// case Dmar_dev_scope::Acpi_namespace_device:
default:
break;
}
}
}
void
Vtd_iommu::parse_drhd_entry(Dmar_drhd const &drhd, unsigned entry_num)
{
d_printf(DBG_DEBUG,
"DRHD[%p]: type %u, length %u, flags 0x%x, size %u, segment %u, "
"addr 0x%llx\n",
&drhd, drhd.type, drhd.length, drhd.flags, drhd._rsvd,
drhd.segment, drhd.register_base);
Info &iommu = _iommus.emplace_back(entry_num, drhd.register_base,
drhd.segment, drhd.flags);
parse_drhd_dev_scope(drhd.devs(), iommu);
}
bool Vtd_iommu::probe()
{
ACPI_TABLE_HEADER *tblhdr;
if (ACPI_FAILURE(AcpiGetTable(ACPI_STRING(ACPI_SIG_DMAR), 0, &tblhdr)))
return false;
if (!tblhdr)
return false;
Acpi_dmar const *dmar_table = reinterpret_cast<Acpi_dmar const *>(tblhdr);
unsigned count = 0;
for (auto it = dmar_table->begin(); it != dmar_table->end(); ++it)
{
if (auto *drhd = it->cast<Dmar_drhd>())
{
parse_drhd_entry(*drhd, count);
// next IOMMU
++count;
}
}
return count > 0;
}
class Vtd_platform_adapter : public Hw::Pci::Platform_adapter_if
{
/**
* Intel VT-d interrupt remapping table entry format.
*
* This format is used by Fiasco as `source` parameter to the system Icu
* `msi_info()` call on x86. It it programmed directly into the IOMMU.
*/
struct Vtd_irte_src_id
{
l4_uint64_t v = 0;
Vtd_irte_src_id(l4_uint64_t v) : v(v) {}
CXX_BITFIELD_MEMBER(18, 19, svt, v);
enum Source_validation_type
{
Svt_none = 0,
Svt_requester_id = 1,
Svt_bus_range = 2,
};
CXX_BITFIELD_MEMBER(16, 17, sq, v);
// svt == Svt_requester_id
CXX_BITFIELD_MEMBER( 8, 15, bus, v);
CXX_BITFIELD_MEMBER( 3, 7, dev, v);
CXX_BITFIELD_MEMBER( 0, 2, fn, v);
CXX_BITFIELD_MEMBER( 0, 7, devfn, v);
// svt == Svt_bus_range
CXX_BITFIELD_MEMBER( 8, 15, start_bus, v);
CXX_BITFIELD_MEMBER( 0, 7, end_bus, v);
};
/**
* Intel VT-d dma source id.
*
* This format is used by Fiasco as `src_id` parameter to Iommu::bind() on
* x86.
*/
struct Vtd_dma_src_id
{
l4_uint64_t v = 0;
Vtd_dma_src_id(l4_uint64_t v) : v(v) {}
CXX_BITFIELD_MEMBER(32, 47, iommu_idx, v);
CXX_BITFIELD_MEMBER(18, 19, match, v);
enum Mode
{
Match_requester_id = 1,
};
// match == Match_requester_id
CXX_BITFIELD_MEMBER( 8, 15, bus, v);
CXX_BITFIELD_MEMBER( 3, 7, dev, v);
CXX_BITFIELD_MEMBER( 0, 2, fn, v);
CXX_BITFIELD_MEMBER( 0, 7, devfn, v);
};
public:
/**
* Translate PCI device into an opaque MSI source-ID.
*
* We have to deal with the additional problem of DMA aliases here.
* Fortunately the Intel VT-d interrupt remapping entry allows for enough
* flexibility to match common alias conditions.
*/
int translate_msi_src(Hw::Pci::If *dev, l4_uint64_t *si) override
{
// By default the exact requester ID is used
Vtd_irte_src_id id(0);
id.svt() = Vtd_irte_src_id::Svt_requester_id;
id.sq() = dev->phantomfn_bits();
id.bus() = dev->bus_nr();
id.devfn() = dev->devfn();
// Walk up the bus hierarchy to see if there are aliasing bridges. We don't
// need to care about the segment number because that will always stay the
// same in a bus hierarchy.
for (Hw::Pci::Bridge_if *bridge = dev->bridge();
bridge;
bridge = bridge->parent_bridge())
{
if (auto alias = bridge->dma_alias())
{
if (alias.is_rewrite())
{
// legacy PCI bridge
id.svt() = Vtd_irte_src_id::Svt_requester_id;
id.sq() = 0;
id.bus() = alias.bus();
id.devfn() = alias.devfn();
}
else if (alias.is_alias() && alias.bus() == id.bus())
{
// PCIe-to-PCI(-X) bridge
id.svt() = Vtd_irte_src_id::Svt_bus_range;
id.start_bus() = alias.bus();
id.end_bus() = alias.bus();
}
else
{
d_printf(DBG_WARN, "Cannot handle DMA alias: %s: 0x%x\n",
alias.as_cstr(), alias.addr);
return -L4_EINVAL;
}
}
}
*si = id.v;
return 0;
}
/**
* Translate DMA source ID.
*
* We always match the exact requester ID as this is the only thing that is
* supported by hardware.
*/
int translate_dma_src(Hw::Pci::Dma_requester_id rid, l4_uint64_t *si) const override
{
if (!rid)
return -L4_EINVAL;
Vtd_dma_src_id id(0);
id.match() = Vtd_dma_src_id::Match_requester_id;
id.bus() = rid.bus();
id.devfn() = rid.devfn();
int idx = Vtd_iommu::get_index(rid.segment(), rid.bus(), rid.devfn());
if (idx < 0)
{
d_printf(DBG_WARN,
"Device to IOMMU assignment for %04x:%02x:%x.%x not found. "
"Fail.\n",
rid.segment().get(), rid.bus().get(), rid.dev().get(),
rid.fn().get());
return idx;
}
else
id.iommu_idx() = idx;
*si = id.v;
return 0;
}
int map_msi_src(::Hw::Pci::If *, l4_uint64_t msi_addr_phys,
l4_uint64_t *msi_addr_iova) override
{
// MSI controller address is handled specially on Intel VT-d and requires
// no mapping in the device IOVA.
*msi_addr_iova = msi_addr_phys;
return 0;
}
};
Hw::Pci::Platform_adapter_if *
setup_pci_platform()
{
static Vtd_platform_adapter vtd_adapter;
return &vtd_adapter;
}
void
setup_iommus()
{
Vtd_iommu::probe();
}
}
}

View File

@@ -0,0 +1,9 @@
#include "arm_dma_device.h"
namespace Hw {
namespace {
static Device_factory_t<Arm_dma_device> __hw_pf_factory("Arm_dma_device");
}
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include "hw_device.h"
#include "main.h"
namespace Hw {
/**
* Hw::Device with specific properties for ARM IOMMUs.
*
* This class offers the following new properties for devices:
* - iommu ID of the IOMMU
* - sid stream ID of the device
*
* Only a single stream ID is supported.
*/
class Arm_dma_device : public Device, public Dma_src_feature
{
public:
Arm_dma_device(l4_umword_t uid, l4_uint32_t adr) : Device(uid, adr)
{ setup_device(); }
Arm_dma_device(l4_uint32_t adr) : Device(adr)
{ setup_device(); }
Arm_dma_device() : Device()
{ setup_device(); }
int enumerate_dma_src_ids(Dma_src_feature::Dma_src_id_cb cb) const override
{
/*
* src_id encoding:
* 63-48: reserved
* 47-32: smmu_idx
* 31- 0: stream_id
*/
l4_uint64_t smmu_idx = _iommu.val();
l4_uint64_t src_id = (smmu_idx << 32) | _sid.val();
return cb(src_id);
}
private:
void setup_device()
{
register_property("iommu", &_iommu);
register_property("sid", &_sid);
property("flags")->set(-1, DF_dma_supported);
add_feature(static_cast<Dma_src_feature *>(this));
}
Int_property _iommu;
Int_property _sid;
};
}

View File

@@ -0,0 +1,26 @@
/*
* (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace Hw {
class Device;
}
class Io_config
{
public:
virtual bool transparent_msi(Hw::Device *) const = 0;
virtual bool legacy_ide_resources(Hw::Device *) const = 0;
virtual bool expansion_rom(Hw::Device *) const = 0;
virtual int verbose() const = 0;
virtual ~Io_config() = 0;
static Io_config *cfg;
};
inline Io_config::~Io_config() {}

View File

@@ -0,0 +1,77 @@
/*
* (c) 2011 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "debug.h"
#include <cstdio>
#include <cstdarg>
static unsigned _debug_level = 1;
static unsigned _trace_mask;
void set_debug_level(unsigned level)
{
_debug_level = level;
}
bool dlevel(unsigned level)
{
return _debug_level >= level;
}
void d_printf(unsigned level, char const *fmt, ...)
{
if (_debug_level < level)
return;
va_list a;
va_start(a, fmt);
vprintf(fmt, a);
va_end(a);
}
/**
* \brief Set the mask for event tracing (See debug.h for possible trace
* events).
*/
void set_trace_mask(unsigned mask)
{
_trace_mask = mask;
}
/**
* \brief Trace an event.
*
* \param event Event to be traced
*/
void trace_event(unsigned event, char const *fmt, ...)
{
if ((_trace_mask & event) == 0)
return;
va_list a;
va_start(a, fmt);
vprintf(fmt, a);
va_end(a);
}
/**
* \brief Determine if an event was selected for tracing.
*
* \param event Event to be queried.
*
* \returns true if event was selected for tracing, false otherwise
*/
bool trace_event_enabled(unsigned event)
{
return (_trace_mask & event);
}
// Set the ACPICA debug flags
// This function is overridden in acpi/acpi.cc.
void __attribute__((weak)) acpi_set_debug_level(unsigned) {}

View File

@@ -0,0 +1,37 @@
/*
* (c) 2011 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
enum Debug_level
{
DBG_NONE = 0,
DBG_ERR,
DBG_WARN,
DBG_INFO,
DBG_DEBUG,
DBG_DEBUG2,
DBG_ALL
};
void set_debug_level(unsigned level);
void d_printf(unsigned level, char const *fmt, ...)
__attribute__((format(printf, 2, 3)));
bool dlevel(unsigned level);
enum Trace_events
{
TRACE_ACPI_EVENT = 0x1,
};
void set_trace_mask(unsigned mask);
void trace_event(unsigned event, char const *fmt, ...)
__attribute__((format(printf, 2, 3)));
bool trace_event_enabled(unsigned event);
void acpi_set_debug_level(unsigned level);

View File

@@ -0,0 +1,333 @@
/*
* (c) 2011 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "device.h"
#include "debug.h"
#include <typeinfo>
#include <cassert>
#include <set>
std::string
Generic_device::get_full_path() const
{
if (!parent())
return std::string("/") + name();
return parent()->get_full_path() + "/" + name();
}
// The resource setup works in two phases:
//
// (1) The REQUEST phase tries to map preconfigured child resources into
// provided resources of their parents.
// (2) Child resources which are not yet configured are configured in the
// ALLOCATE phase.
//
// The rationale behind this strategy is to try hard to not change any setup
// done by previous instances in the boot chain (BIOS, EFI, firmware, etc).
// Changing such an existing setup can lead to a hanging system if firmware
// (e.g. System Management Mode on x86 systems) continues to use the old setup.
bool
Device::alloc_child_resource(Resource *r, Device *cld)
{
bool found_as = false;
// The first run requires exact match between client and parent resource.
// The second run allows to assign a prefetchable MMIO client region to a
// non-prefetchable MMIO parent resource.
bool exact = true;
auto const *rl = resources();
while (true)
{
for (auto *br: *rl)
{
if (!br)
continue;
if (br->disabled())
continue;
if (!br->provided())
continue;
if (!br->compatible(r, exact))
continue;
if (parent() && !parent()->can_alloc_from_res(br))
continue;
found_as = true;
if (parent() && !parent()->resource_allocated(br))
{
br->provided()->assign(br, r);
d_printf(DBG_ALL, "assigned resource: ");
if (dlevel(DBG_ALL))
r->dump();
return true;
}
else if (br->provided()->alloc(br, this, r, cld, false))
{
r->enable();
d_printf(DBG_ALL, "allocated resource: ");
if (dlevel(DBG_ALL))
r->dump();
return true;
}
}
if (exact)
exact = false;
else
{
if (!found_as && parent())
return parent()->alloc_child_resource(r, cld);
d_printf(DBG_ERR, "ERROR: could not reserve resource\n");
if (dlevel(DBG_ERR))
r->dump();
r->disable();
return false;
}
}
}
void
Device::request_resource(Resource *r)
{
Device *p = parent();
// Are we the root device?
if (!p)
return;
if (r->empty())
return;
if (p->resource_allocated(r))
return; // already allocated
if (r->disabled())
return;
if (!p->request_child_resource(r, this) && r->fixed_addr())
{
d_printf(DBG_WARN, "warning: inconsistent fixed resource @ device: %s\n",
get_full_path().c_str());
if (dlevel(DBG_WARN))
{
dump(2);
r->dump(2);
}
}
}
void
Device::request_resources()
{
Resource_list const *rl = resources();
// Are we the root device?
if (!parent())
return;
for (Resource_list::const_iterator r = rl->begin();
r != rl->end(); ++r)
if (*r)
request_resource(*r);
}
void
Device::request_child_resources()
{
for (iterator dev = begin(0); dev != end(); ++dev)
{
// First, try to map all our resources of our child (dev) into
// provided resources of ourselves
(*dev)->request_resources();
// Second, recurse down to our child (dev)
(*dev)->request_child_resources();
}
}
// sorted set of (resource, device) pairs
namespace {
struct Res_dev
{
Resource *r;
Device *d;
Res_dev() = default;
Res_dev(Resource *r, Device *d) : r(r), d(d) {}
};
static bool res_cmp(Res_dev const &l, Res_dev const &r)
{ return l.r->alignment() > r.r->alignment(); }
typedef std::multiset<Res_dev, bool (*)(Res_dev const &l, Res_dev const &r)> UAD;
static bool _allocate_pending_resources(Device *dev, UAD *to_allocate)
{
Device *p = dev->parent();
assert (p);
auto const *rl = dev->resources();
for (auto *r: *rl)
{
if (!r)
continue;
if (r->empty())
continue;
if (p->resource_allocated(r))
continue;
if (r->fixed_addr())
continue;
if (0)
{
printf("unallocated resource: %s ", r->res_type_name());
r->dump(0);
}
to_allocate->insert(Res_dev(r, dev));
}
return true;
}
}
/**
* Allocate all unallocated child resources.
*
* The allocation of a resource includes its relocation so that PCI device
* resources are located inside the respective resource window of their PCI
* bridge. Allocated resources are fixed until the device is removed.
*
* It is not possible for IO clients to perform a resource relocation on a
* physical device as physical devices are accessible by clients only through
* respective proxy devices. The proxy device for PCI devices emulates accesses
* to the BARs.
*/
void
Device::allocate_pending_resources()
{
allocate_pending_child_resources();
Device *p = parent();
if (!p)
return;
UAD to_allocate(res_cmp);
_allocate_pending_resources(this, &to_allocate);
for (UAD::const_iterator i = to_allocate.begin(); i != to_allocate.end(); ++i)
p->alloc_child_resource((*i).r, this);
}
void
Device::allocate_pending_child_resources()
{
UAD to_allocate(res_cmp);
for (iterator dev = begin(0); dev != end(); ++dev)
{
dev->allocate_pending_child_resources();
_allocate_pending_resources(*dev, &to_allocate);
}
for (UAD::const_iterator i = to_allocate.begin(); i != to_allocate.end(); ++i)
alloc_child_resource((*i).r, (*i).d);
}
bool
Device::request_child_resource(Resource *r, Device *cld)
{
bool found_as = false;
// The first run requires exact match between client and parent resource.
// The second run allows to assign a prefetchable MMIO client region to a
// non-prefetchable MMIO parent resource.
bool exact = true;
auto const *rl = resources();
while (true)
{
// scan through all our resources and try to find a
// provided resource that is consumed by resource 'r'
for (auto *br: *rl)
{
if (!br)
continue;
if (br->disabled())
continue;
if (!br->provided())
continue;
if (!br->compatible(r, exact))
continue;
found_as = true;
if (br->provided()->request(br, this, r, cld))
return true;
}
if (exact)
exact = false;
else if (!found_as && parent())
{
// If there is no proper resource provided by
// ourselves that fits resource r, than try to
// go up the hierarchy to our parent node
if (0)
{
printf("try parent resource request...\n");
r->dump();
}
return parent()->request_child_resource(r, cld);
}
else
return false;
}
}
int
Generic_device::register_property(std::string const &n, Property *prop)
{
Property *&p = _properties[n];
if (p)
{
d_printf(DBG_ERR, "internal error: %s:"
"property %s is already registered\n", name(), n.c_str());
return -EEXIST;
}
p = prop;
return 0;
}
Property *
Generic_device::property(std::string const &name)
{
return _properties[name];
}

View File

@@ -0,0 +1,558 @@
/*
* (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <fnmatch.h>
#include <l4/sys/l4int.h>
#include <l4/vbus/vbus_types.h>
#include <l4/cxx/minmax>
#include <l4/cxx/string>
#include <l4/cxx/bitfield>
#include <l4/cxx/unique_ptr>
#include "resource.h"
#include "debug.h"
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
template<typename D> class Device_tree_mixin;
/**
* The device tree consists of nodes with links to their parent, child and
* sibling nodes. If there is no parent node, this node is a root node.
* After setup, there must only be one root node.
*
* The parent has only one pointer to a child node.
* Sibling nodes have the same parent node and the same depth in the tree.
* The siblings form a single-linked list via their next pointer.
* The head of this single-linked list is the child pointer in the parent node.
*
* The depth describes the number of parent nodes until the root node is
* reached.
* The root node has depth zero.
*/
template< typename D >
class Device_tree
{
friend class Device_tree_mixin<D>;
private:
D *_n; ///< next sibling node
D *_p; ///< parent node
D *_c; ///< child node
int _depth; ///< depth of this node
public:
Device_tree() : _n(0), _p(0), _c(0), _depth(0) {}
D *parent() const { return _p; }
D *children() const { return _c; }
D *next() const { return _n; }
int depth() const { return _depth; }
private:
/**
* Set \a p as parent device
*
* \param p Parent device
*/
void set_parent(D *p) { _p = p; }
void add_sibling(D *s)
{ _n = s; }
/**
* Add \a d as child device to \a self
*
* \param d Child device
* \param self Parent device
*/
void add_child(D *d, D *self)
{
if (d->parent())
{
d_printf(DBG_ERR, "warning: device %s already has a parent. Ignoring.\n",
d->name());
return;
}
for (iterator i = iterator(0, d, L4VBUS_MAX_DEPTH); i != iterator(); ++i)
i->set_depth(i->depth() + depth() + 1);
d->set_parent(self);
if (!_c)
_c = d;
else
{
D *p;
for (p = _c; p->next(); p = p->next())
;
p->add_sibling(d);
}
}
void set_depth(int d) { _depth = d; }
public:
class iterator
{
public:
/**
* Construct an iterator for a subtree of devices.
*
* \param p Root node of the subtree to iterate.
* \param c Node to start iterating from.
* \param depth Maximum depth of the iterable subtree relative to `p`.
*/
iterator(D *p, D *c, int depth = 0)
: _p(p), _c(c), _d(depth + (p ? p->depth() : 0))
{}
/**
* Construct an iterator for a subtree of devices with root node `p`.
*
* \param p Root node of the subtree to iterate.
* \param depth Maximum depth of the iterable subtree relative to `p`.
*
* \pre `p` must not be nullptr.
*/
iterator(D const *p, int depth = 0)
: _p(p), _c(p->children()), _d(depth + p->depth())
{}
/// Construct an invalid iterator.
/// At least `_c` has to be initialized corresponding to the `==` operator!
iterator()
: _c(0)
{}
bool operator == (iterator const &i) const
{
if (!_c && !i._c)
return true;
return _c == i._c && _p == i._p && _d == i._d;
}
bool operator != (iterator const &i) const
{ return !operator == (i); }
D *operator -> () const { return _c; }
D *operator * () const { return _c; }
/// Advance to next device; if there is none, return an invalid iterator.
iterator operator ++ ()
{
if (!_c)
return *this;
// This performs a limited-depth, depth-first search algorithm.
if (_d > _c->depth() && _c->children())
// go to a child if not at max depth and there are children
_c = _c->children();
else if (_c->next())
// go to the next sibling
_c = _c->next();
else if (_c == _p)
_c = 0;
else
{
for (D *x = _c->parent(); x && x != _p; x = x->parent())
if (x->next())
{
_c = x->next();
return *this;
}
_c = 0;
}
return *this;
}
iterator operator ++ (int)
{
iterator o = *this;
++(*this);
return o;
}
private:
D const *_p; ///< parent device
D *_c; ///< current device or end()
int _d; ///< max depth to iterate up to
};
};
template< typename D >
class Device_tree_mixin
{
friend class Device_tree<D>;
protected:
Device_tree<D> _dt;
public:
typedef typename Device_tree<D>::iterator iterator;
iterator begin(int depth = 0) const
{ return iterator(static_cast<D const*>(this), depth); }
static iterator end() { return iterator(); }
D *find_by_name(char const *name) const
{
for (iterator c = begin(0); c != end(); ++c)
if (strcmp((*c)->name(), name) == 0)
return *c;
return 0;
}
virtual void add_child(D *c) { _dt.add_child(c, static_cast<D*>(this)); }
virtual ~Device_tree_mixin() {}
private:
void set_depth(int d) { return _dt.set_depth(d); }
void set_parent(D *p) { _dt.set_parent(p); }
void add_sibling(D *s) { _dt.add_sibling(s); }
};
class Resource_container
{
public:
virtual Resource_list const *resources() const = 0;
virtual bool resource_allocated(Resource const *) const = 0;
virtual ~Resource_container() {}
};
class Device : public Resource_container
{
public:
/**
* Interpretation of the opaque `source` parameter of L4::Icu::msi_info()
* for an io vbus.
*
* Effectively it can be used in two modes. If is_dev_handle() is set,
* the value is interpreted as dev_handle() and is used to lookup the device
* on the System_bus. Otherwise it resembles a subset of the Intel VT-d
* interrupt remapping entry structure. It's solely supported function here
* is to lookup a PCI device by requester id (bus/device/function).
*/
struct Msi_src_info
{
l4_uint64_t v = 0;
Msi_src_info(l4_uint64_t v) : v(v) {}
CXX_BITFIELD_MEMBER(63, 63, is_dev_handle, v);
// is_dev_handle == 1
CXX_BITFIELD_MEMBER( 0, 62, dev_handle, v);
// is_dev_handle == 0
CXX_BITFIELD_MEMBER(18, 19, query, v);
enum Query
{
Query_none = 0,
Query_requester_id = 1,
};
// query == Query_requester_id
CXX_BITFIELD_MEMBER( 8, 15, bus, v);
CXX_BITFIELD_MEMBER( 3, 7, dev, v);
CXX_BITFIELD_MEMBER( 0, 2, fn, v);
};
virtual Device *parent() const = 0;
virtual Device *children() const = 0;
virtual Device *next() const = 0;
virtual int depth() const = 0;
bool request_child_resource(Resource *, Device *);
bool alloc_child_resource(Resource *, Device *);
void request_resource(Resource *r);
void request_resources();
void request_child_resources();
void allocate_pending_child_resources();
void allocate_pending_resources();
virtual bool can_alloc_from_res(Resource const *) { return true; }
virtual char const *name() const = 0;
virtual char const *hid() const = 0;
virtual void dump(int) const {};
typedef Device_tree<Device>::iterator iterator;
iterator begin(int depth = 0) const { return iterator(this, depth); }
static iterator end() { return iterator(); }
virtual int pm_suspend() = 0;
virtual int pm_resume() = 0;
virtual std::string get_full_path() const = 0;
};
class Property;
class Generic_device : public Device
{
private:
typedef std::map<std::string, Property *> Property_list;
Resource_list _resources;
Property_list _properties;
public:
//typedef gen_iterator<Generic_device> iterator;
Resource_list const *resources() const override { return &_resources; }
void add_resource(Resource *r)
{ _resources.push_back(r); }
void add_resource_rq(Resource *r)
{
add_resource(r);
request_resource(r);
}
virtual bool match_cid(cxx::String const &cid) const
{
if (hid() == nullptr)
return false;
auto cid_cstr = cxx::make_unique<char[]>(cid.len() + 1);
__builtin_memcpy(cid_cstr.get(), cid.start(), cid.len());
cid_cstr[cid.len()] = 0;
if (!fnmatch(cid_cstr.get(), hid(), 0))
return true;
return false;
}
char const *name() const override { return "(noname)"; }
char const *hid() const override { return 0; }
int pm_suspend() override { return 0; }
int pm_resume() override { return 0; }
std::string get_full_path() const override;
/**
* Register a property
*
* Register a property so that it can be set via Lua. The name of the
* property has to be unique. Hardware device drivers use the properties
* 'hid' and 'adr' by default.
*
* \param name Name of the property
* \param prop Property object which should be registered
* \return 0 if the property has been registered, -EEXIST if there already
* is a property with the same name
*/
int register_property(std::string const &name, Property *prop);
/**
* Retrieve property object
*
* Retrieve the property object which corresponds with \a name
*
* \param name Name of the requested property
* \return The corresponding property object if it exists, NULL otherwise
*/
Property *property(std::string const &name);
/**
* Verify that the property was set and return an error otherwise.
*
* Verify if the property has the value of `default_value`. If so, print a
* warning (if the ERR debug level is enabled) using the device name and
* return an error. Otherwise just return 0 (success).
*
* \param prop Property.
* \param prop_name Property name.
* \param default_value Default value to check for.
*
* \retval 0 Property has a non-default value.
* \retval -L4_EINVAL Property was not set and has the default value.
*/
template<typename P, typename V>
int assert_property(P const &prop, std::string const &prop_name,
V default_value = 0)
{
if (prop->val() == default_value)
{
d_printf(DBG_ERR, "error: %s: Property '%s' not set.\n",
name(), prop_name.c_str());
return -L4_EINVAL;
}
return 0;
}
};
/**
* Base class to define device driver properties
*
* This class is not intended to be used directly. Instead you should inherit
* from this class to define your own property type if necessary.
*/
class Property
{
public:
Property() = default;
Property &operator = (Property const &) = delete;
Property(Property const &) = delete;
virtual ~Property() = 0;
/**
* Set property value
*
* \param k Array index in the case of an array property (starting from 1)
* and -1 in the case of a single value property.
* \param val Property value
*/
virtual int set(int k, std::string const &str) = 0;
/**
* \copydoc set(int k, std::string const &val) = 0;
*/
virtual int set(int k, l4_int64_t val) = 0;
/**
* \copydoc set(int k, std::string const &val) = 0;
*/
virtual int set(int k, Generic_device *val) = 0;
/**
* \copydoc set(int k, std::string const &val) = 0;
*/
virtual int set(int k, Resource *val) = 0;
};
inline Property::~Property() {}
/// This class implements a string property
class String_property : public Property
{
private:
std::string _s;
public:
int set(int k, std::string const &str) override
{
if (k != -1)
return -EINVAL;
_s = str;
return 0;
}
int set(int, l4_int64_t) override { return -EINVAL; }
int set(int, Generic_device *) override { return -EINVAL; }
int set(int, Resource *) override { return -EINVAL; }
/// Read property value
std::string const &val() const { return _s; }
};
/**
* This class implements an integer property
*
* The value of this property is stored as a 64-bit signed integer
*/
class Int_property : public Property
{
private:
l4_int64_t _i = 0;
public:
Int_property() = default;
Int_property(l4_int64_t i) : _i(i)
{}
operator l4_int64_t () const { return _i; }
int set(int, std::string const &) override { return -EINVAL; }
int set(int k, l4_int64_t i) override
{
if (k != -1)
return -EINVAL;
_i = i;
return 0;
}
int set(int, Generic_device *) override { return -EINVAL; }
int set(int, Resource *) override { return -EINVAL; }
l4_int64_t val() const { return _i; }
};
/// This class implements a device reference property
template<typename DEVICE>
class Device_property : public Property
{
private:
DEVICE *_dev;
public:
Device_property(): _dev(0) {}
int set(int, std::string const &) override { return -EINVAL; }
int set(int, l4_int64_t) override { return -EINVAL; }
int set(int k, Generic_device *d) override
{
if (k != -1)
return -EINVAL;
_dev = dynamic_cast<DEVICE *>(d);
if (!_dev)
return -EINVAL;
return 0;
}
int set(int, Resource *) override { return -EINVAL; }
DEVICE *dev() { return _dev; }
};
/// This class implements a resource property
class Resource_property : public Property
{
private:
Resource *_res;
public:
Resource_property() : _res(0) {}
int set(int, std::string const &) override { return -EINVAL; }
int set(int, l4_int64_t) override { return -EINVAL; }
int set(int, Generic_device *) override { return -EINVAL; }
int set(int k, Resource *r) override
{
if (k != -1)
return -EINVAL;
_res = r;
return 0;
}
Resource *res() { return _res; }
};

View File

@@ -0,0 +1,200 @@
#include "dma_domain.h"
#include "debug.h"
#include <cassert>
#include <cstdio>
unsigned Dma_domain::_next_free_domain;
bool Dma_domain_if::_supports_remapping;
void
Dma_domain::add_to_group(Dma_domain_group *g)
{
if (!_v_domain && !g->_set)
{
Dma_domain_set *n = new Dma_domain_set();
g->assign_set(n);
add_to_set(n);
return;
}
if (_v_domain == g->_set)
return;
if (_v_domain && g->_set)
{
// domain is already in a group and the target group already contains
// other domains
g->merge(_v_domain);
return;
}
if (_v_domain)
{
g->assign_set(_v_domain);
return;
}
add_to_set(g->_set);
}
int
Dma_domain_if::set_dma_space(bool set, L4::Cap<L4Re::Dma_space> space)
{
if (0)
printf("%s:%d: %s : %s\n", __FILE__, __LINE__, __func__,
set ? "bind" : "unbind");
// FIXME: check trustworthiness of `space`
if (!set)
return 0; // FIXME: space->disassociate(_kern_dma_space);
if (!_supports_remapping)
{
d_printf(DBG_DEBUG2, "DMA: use CPU-phys addresses for DMA\n");
return space->associate(L4::Ipc::make_cap(_kern_dma_space, 0),
L4Re::Dma_space::Space_attrib::Phys_space);
}
if (_kern_dma_space && !_managed_kern_dma_space)
return -L4_EBUSY;
if (!_kern_dma_space)
{
d_printf(DBG_DEBUG2, "DMA: create kern DMA space for managed DMA\n");
int r = create_managed_kern_dma_space();
if (r < 0)
return r;
_managed_kern_dma_space = true;
}
d_printf(DBG_DEBUG2, "DMA: associate managed DMA space (cap=%lx)\n",
_kern_dma_space.cap());
return space->associate(L4::Ipc::make_cap_rws(_kern_dma_space),
L4Re::Dma_space::Space_attribs::None);
}
int
Dma_domain_set::create_managed_kern_dma_space()
{
assert (!_kern_dma_space);
if (_domains.empty())
return -L4_ENOENT;
L4::Cap<L4::Task> kds = L4::Cap<L4::Task>::Invalid;
for (auto d: _domains)
{
if (d->kern_dma_space() && !d->managed_kern_dma_space())
{
d_printf(DBG_ERR, "error: conflicting DMA remapping assignment "
"(unmanaged DMA domain in group)\n");
return -L4_EBUSY;
}
if (d->kern_dma_space() && kds && kds != d->kern_dma_space())
{
d_printf(DBG_ERR, "error: conflicting DMA remapping assignment "
"(conflicting DMA domain assignment)\n");
return -L4_EBUSY;
}
if (d->kern_dma_space())
kds = d->kern_dma_space();
}
if (kds)
d_printf(DBG_INFO, "reuse managed DMA domain for DMA domain group\n");
else
{
int r = _domains[0]->create_managed_kern_dma_space();
if (r < 0)
return r;
kds = _domains[0]->kern_dma_space();
}
for (auto d: _domains)
if (!d->kern_dma_space())
{
int r = d->set_managed_kern_dma_space(kds);
if (r < 0)
{
d_printf(DBG_ERR,
"Error: Domain_set: "
"Failed to create kernel-side DMA space for domain: %d\n", r);
return r;
}
}
int r = set_managed_kern_dma_space(kds);
if (r < 0)
{
d_printf(DBG_ERR,
"Error: Domain_set: "
"Failed to create kernel-side DMA space: %d\n", r);
return r;
}
return 0;
}
int
Dma_domain_set::set_dma_task(bool set, L4::Cap<L4::Task> dma_task)
{
if (managed_kern_dma_space())
return -L4_EBUSY;
if (set && kern_dma_space())
return -L4_EBUSY;
if (!set && !kern_dma_space())
return -L4_EINVAL;
static L4::Cap<L4::Task> const &me = L4Re::This_task;
if (!set && !me->cap_equal(kern_dma_space(), dma_task).label())
return -L4_EINVAL;
L4::Cap<L4::Task> kds = L4::Cap<L4::Task>::Invalid;
for (auto d: _domains)
{
if (d->managed_kern_dma_space())
{
d_printf(DBG_ERR, "error: conflicting DMA remapping assignment "
"(managed DMA domain in group)\n");
return -L4_EBUSY;
}
if (!set)
continue;
if (d->kern_dma_space() && kds && !me->cap_equal(kern_dma_space(), kds).label())
{
d_printf(DBG_ERR, "error: conflicting DMA remapping assignment "
"(conflicting DMA domain assignment)\n");
return -L4_EBUSY;
}
if (d->kern_dma_space())
kds = d->kern_dma_space();
}
for (auto d: _domains)
{
int res = 0;
if (set && !d->kern_dma_space())
res = d->set_dma_task(true, dma_task);
else if (!set && d->kern_dma_space())
res = d->set_dma_task(false, dma_task);
if (res < 0)
return res;
}
if (set)
_kern_dma_space = dma_task;
else
_kern_dma_space = L4::Cap<L4::Task>::Invalid;
return 0;
}

View File

@@ -0,0 +1,173 @@
#pragma once
#include "resource.h"
#include <l4/sys/task>
#include <l4/re/dma_space>
class Dma_domain_set;
class Dma_domain_group;
class Dma_domain_if
{
protected:
L4::Cap<L4::Task> _kern_dma_space = L4::Cap<L4::Task>::Invalid;
static bool _supports_remapping;
bool _managed_kern_dma_space = false;
public:
bool managed_kern_dma_space() const
{ return _managed_kern_dma_space; }
L4::Cap<L4::Task> kern_dma_space() const
{ return _kern_dma_space; }
virtual int set_managed_kern_dma_space(L4::Cap<L4::Task> s)
{
_kern_dma_space = s;
_managed_kern_dma_space = true;
return 0;
}
virtual int create_managed_kern_dma_space() = 0;
virtual int set_dma_space(bool set, L4::Cap<L4Re::Dma_space> dma_space);
virtual int set_dma_task(bool set, L4::Cap<L4::Task> dma_task) = 0;
virtual ~Dma_domain_if() = default;
};
class Dma_domain : public Resource, public Dma_domain_if
{
friend class Dma_domain_group;
private:
static unsigned _next_free_domain;
/**
* Virtual DMA domain that domain belongs to.
*
* Usually there is one virtual DMA domain per virtual bus, and all physical
* DMA domains that are assigned to that virtual bus belong to the same
* virtual DMA domain. If a physical DMA domain is shared among multiple
* virtual buses these buses also share their virtual DMA domain.
*/
Dma_domain_set *_v_domain = 0;
void add_to_set(Dma_domain_set *s);
public:
Dma_domain(Dma_domain const &) = delete;
Dma_domain &operator = (Dma_domain const &) = delete;
Dma_domain()
: Resource("DMAD", Dma_domain_res, _next_free_domain, _next_free_domain)
{ ++_next_free_domain; }
void add_to_group(Dma_domain_group *g);
};
class Dma_domain_set : public Dma_domain_if
{
friend class Dma_domain;
friend class Dma_domain_group;
private:
typedef std::vector<Dma_domain *> Domains;
typedef std::vector<Dma_domain_group *> Groups;
Domains _domains;
Groups _groups;
void add_group(Dma_domain_group *g)
{ _groups.push_back(g); }
bool rm_group(Dma_domain_group *g)
{
for (auto i = _groups.begin(); i != _groups.end(); ++i)
if (*i == g)
{
_groups.erase(i);
return true;
}
return false;
}
void add_domain(Dma_domain *d)
{ _domains.push_back(d); }
bool rm_domain(Dma_domain *d)
{
for (auto i = _domains.begin(); i != _domains.end(); ++i)
if (*i == d)
{
_domains.erase(i);
return true;
}
return false;
}
public:
int set_dma_task(bool, L4::Cap<L4::Task>) override;
int create_managed_kern_dma_space() override;
};
class Dma_domain_group
{
friend class Dma_domain;
private:
Dma_domain_set *_set = 0;
void assign(Dma_domain_group const &g)
{
if (_set == g._set)
return;
if (_set)
_set->rm_group(this);
_set = g._set;
if (_set)
_set->add_group(this);
}
void assign_set(Dma_domain_set *s)
{
s->add_group(this);
_set = s;
}
public:
Dma_domain_group() = default;
Dma_domain_group(Dma_domain_group const &) = delete;
Dma_domain_group &operator = (Dma_domain_group const &) = delete;
~Dma_domain_group()
{
if (!_set)
return;
_set->rm_group(this);
}
Dma_domain_if *operator -> () const { return _set; }
Dma_domain_if *get() const { return _set; }
private:
void merge(Dma_domain_set *s);
};
inline void
Dma_domain::add_to_set(Dma_domain_set *s)
{
s->add_domain(this);
_v_domain = s;
}
inline void
Dma_domain_group::merge(Dma_domain_set *s)
{
for (auto g: s->_groups)
g->assign_set(_set);
for (auto d: s->_domains)
d->add_to_set(_set);
delete s;
}

View File

@@ -0,0 +1,11 @@
config L4IO_SCU_IMX8QM
bool "NXP i.MX8QM SCU"
default y
help
Add support for i.MX8 System Controller Unit found on the NXP i.MX8QM.
config L4IO_IOMUXC_IMX8MP
bool "NXP i.MX8MP IOMUXC Driver"
default y
help
Add support for i.MX8 IOMUXC controller found on the NXP i.MX8MP.

View File

@@ -0,0 +1,16 @@
PKGDIR ?= ../../..
L4DIR ?= $(PKGDIR)/../../..
SUBDIRS = gpio
SUBDIRS-$(CONFIG_L4IO_ACPI) += acpi
SUBDIRS-$(CONFIG_L4IO_PCI) += pci
SUBDIRS += $(SUBDIRS-y)
SRC_CC-$(CONFIG_L4IO_PCI) += pcie_ecam.cc
SRC_CC-$(CONFIG_L4IO_PCI) += pcie_rcar3.cc cpg_rcar3.cc
SRC_CC-$(CONFIG_L4IO_PCI) += dwc_pcie_core.cc pcie_imx8.cc
SRC_CC-$(CONFIG_L4IO_SCU_IMX8QM) += scu_imx8qm.cc
SRC_CC-$(CONFIG_L4IO_IOMUXC_IMX8MP) += iomuxc.cc i2c-ctrl.cc
include $(PKGDIR)/server/src/lib_subdir.mk

View File

@@ -0,0 +1,16 @@
PKGDIR ?= ../../../..
L4DIR ?= $(PKGDIR)/../../..
SUBDIRS :=
SRC_CC := pci-root.cc \
button.cc \
SRC_CC-amd64 := ec.cc
SRC_CC-x86 := ec.cc
SRC_CC += $(SRC_CC-$(ARCH))
REQUIRES_LIBS = acpica
include $(PKGDIR)/server/src/lib_subdir.mk
WARNINGS += -Wno-unused-parameter

View File

@@ -0,0 +1,281 @@
/*
* (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "io_acpi.h"
#include "__acpi.h"
#include "debug.h"
#include "event_source.h"
#include <l4/re/event_enums.h>
#include <cassert>
namespace {
using namespace Hw;
using Hw::Device;
/**
* \brief Setup device as a wake source.
*
* \param device This device
* \param handle ACPI handle
*/
static void acpi_setup_device_wake(Device *device, ACPI_HANDLE handle)
{
Acpi_auto_buffer buffer;
buffer.Length = ACPI_ALLOCATE_BUFFER;
ACPI_STATUS status;
status = AcpiEvaluateObject(handle, ACPI_STRING("_PRW"), NULL, &buffer);
if (ACPI_FAILURE(status))
{
d_printf(DBG_ERR, "error: ACPI %s: could not evaluate _PRW object (%s), "
"not enabled as wake source\n",
device->name(), AcpiFormatException(status));
return;
}
ACPI_OBJECT *prw = (ACPI_OBJECT*)buffer.Pointer;
if (!prw || prw->Type != ACPI_TYPE_PACKAGE || prw->Package.Count < 2)
{
d_printf(DBG_ERR, "error: ACPI %s: has invalid _PRW object, "
"not enabled as wake source\n", device->name());
return;
}
ACPI_HANDLE gpe_device = NULL;
int gpe_number;
ACPI_OBJECT *gpe_info = &(prw->Package.Elements[0]);
switch (gpe_info->Type)
{
case ACPI_TYPE_PACKAGE:
if ((gpe_info->Package.Count < 2)
|| (gpe_info->Package.Elements[0].Type != ACPI_TYPE_LOCAL_REFERENCE)
|| (gpe_info->Package.Elements[1].Type != ACPI_TYPE_INTEGER))
{
d_printf(DBG_ERR, "error: ACPI %s: invalid GPE info, "
"not enabled as wake source\n", device->name());
return;
}
gpe_device = gpe_info->Package.Elements[0].Reference.Handle;
gpe_number = gpe_info->Package.Elements[1].Integer.Value;
break;
case ACPI_TYPE_INTEGER:
gpe_number = gpe_info->Integer.Value;
break;
default:
d_printf(DBG_ERR, "error: ACPI %s: invalid GPE info, "
"not enabled as wake source\n", device->name());
return;
}
status = AcpiSetupGpeForWake(handle, gpe_device, gpe_number);
if (ACPI_FAILURE(status))
{
d_printf(DBG_ERR, "error: ACPI %s: AcpiSetupGpeForWake failed: %s\n",
device->name(), AcpiFormatException(status));
return;
}
status = AcpiSetGpeWakeMask(gpe_device, gpe_number, ACPI_GPE_ENABLE);
if (ACPI_FAILURE(status))
{
d_printf(DBG_ERR, "error: ACPI %s: AcpiSetGpeWakeMask failed: %s\n",
device->name(), AcpiFormatException(status));
return;
}
status = AcpiEnableGpe(gpe_device, gpe_number);
if (ACPI_FAILURE(status))
{
d_printf(DBG_ERR, "error: ACPI %s: could not enable GPE: %s\n",
device->name(), AcpiFormatException(status));
return;
}
d_printf(DBG_DEBUG, "ACPI %s: configured as wake source\n",
device->name());
}
class Acpi_lid : public Acpi_dev
{
public:
Acpi_lid(ACPI_HANDLE hdl, Device *host);
void enable_notifications(Hw::Device *host) override;
void get_and_store_state(bool *closed, bool *duplicate);
void notify(unsigned type, unsigned event, unsigned value)
{ _host->notify(type, event, value); }
private:
Hw::Device *_host;
bool _generate_switch_events;
static void notification_handler(ACPI_HANDLE handle, UINT32 event,
void *ctxt);
};
Acpi_lid::Acpi_lid(ACPI_HANDLE hdl, Device *host)
: Acpi_dev(hdl), _host(host), _generate_switch_events(false)
{
Io::Event_source_infos *source_info = _host->get_event_infos(true);
L4Re::Event_stream_info *info = &source_info->info;
if (info->get_swbit(L4RE_SW_0))
{
d_printf(DBG_ERR, "error: ACPI %s: LID switch 0 already taken, "
"no LID events will be generated\n", host->name());
return;
}
info->set_swbit(L4RE_SW_0, true);
_generate_switch_events = true;
bool closed, duplicate;
get_and_store_state(&closed, &duplicate);
d_printf(DBG_DEBUG, "ACPI %s: initial state: %s\n", host->name(),
closed ? "closed": "open");
}
void
Acpi_lid::notification_handler(ACPI_HANDLE handle, UINT32 event,
void *ctxt)
{
Acpi_lid *lid = static_cast<Acpi_lid*>(ctxt);
(void)handle;
assert (handle == lid->handle());
if (event != 0x80)
{
// not a LID changed event, pass on
lid->notify(Acpi_dev::Acpi_event, event, 0);
return;
}
if (!lid->_generate_switch_events)
return;
// translate LID status changed into L4Re switch event for switch 0
bool closed = false, duplicate = false;
lid->get_and_store_state(&closed, &duplicate);
d_printf(DBG_DEBUG, "ACPI: LID %s\n", closed ? "closed" : "open");
if (!duplicate)
lid->notify(L4RE_EV_SW, L4RE_SW_0, closed ? 0 : 1);
}
void
Acpi_lid::enable_notifications(Hw::Device *d)
{
d_printf(DBG_DEBUG, "ACPI %s: enable notifications\n", d->name());
if (_have_notification_handler)
return;
ACPI_STATUS s = AcpiInstallNotifyHandler(handle(), ACPI_ALL_NOTIFY,
notification_handler, this);
if (ACPI_SUCCESS(s))
_have_notification_handler = true;
else
d_printf(DBG_ERR, "error: ACPI %s: cannot install notification handler: %s\n",
d->name(), AcpiFormatException(s));
}
/**
* \brief Get the LID state from ACPI and store it.
*
* Get the LID state from the ACPI and store it in the Hw::Device
* L4Re::Event_stream_state struct.
*
* \pre _generate_switch_events == true
* \param handle ACPI handle of this device
* \retval closed true if LID is closed, false if LID open
* \retval duplicate true if stored LID state matches measured LID state
*/
void
Acpi_lid::get_and_store_state(bool *closed, bool *duplicate)
{
l4_uint64_t value = 1;
Io::Event_source_infos *source_info = _host->get_event_infos();
L4Re::Event_stream_state *state = &source_info->state;
Acpi_buffer<ACPI_OBJECT> o;
ACPI_STATUS s = AcpiEvaluateObject(handle(), ACPI_STRING("_LID"), NULL, &o);
if (!ACPI_SUCCESS(s) || o.value.Type != ACPI_TYPE_INTEGER)
d_printf(DBG_ERR, "error: ACPI LID: could not read _LID: %s\n",
AcpiFormatException(s));
else
value = o.value.Integer.Value;
bool lid_closed = (value == 0);
*closed = lid_closed;
if (lid_closed != state->get_swbit(L4RE_SW_0))
{
d_printf(DBG_DEBUG2, "ACPI LID: duplicate LID event\n");
*duplicate = true;
return;
}
state->set_swbit(L4RE_SW_0, !lid_closed);
*duplicate = false;
}
class Acpi_lid_driver : public Acpi_device_driver
{
public:
Acpi_dev *probe(Device *device, ACPI_HANDLE handle,
ACPI_DEVICE_INFO const *) override
{
Acpi_lid *dev;
d_printf(DBG_DEBUG, "ACPI: found LID: %s\n", device->name());
dev = new Acpi_lid(handle, device);
dev->discover_crs(device);
device->add_feature(dev);
// LID and buttons are always wake sources therefore tell ACPICA about it
acpi_setup_device_wake(device, handle);
return dev;
}
};
class Acpi_button_driver : public Acpi_device_driver
{
public:
Acpi_dev *probe(Device *device, ACPI_HANDLE handle,
ACPI_DEVICE_INFO const *) override
{
Acpi_dev *dev;
d_printf(DBG_DEBUG, "ACPI: found ACPI button: %s\n", device->name());
dev = new Acpi_dev(handle);
dev->discover_crs(device);
device->add_feature(dev);
// LID and buttons are always wake sources therefore tell ACPICA about it
acpi_setup_device_wake(device, handle);
return dev;
}
};
static Acpi_lid_driver _acpi_lid_drv;
static Acpi_button_driver _acpi_button_drv;
struct Init
{
Init()
{
Acpi_device_driver::register_driver("PNP0C0C", &_acpi_button_drv);
Acpi_device_driver::register_driver("PNP0C0D", &_acpi_lid_drv);
Acpi_device_driver::register_driver("PNP0C0E", &_acpi_button_drv);
}
};
static Init init;
}

View File

@@ -0,0 +1,376 @@
#include "io_acpi.h"
#include "__acpi.h"
#include "debug.h"
#include <l4/cxx/bitfield>
#include <l4/util/port_io.h>
#include <l4/util/util.h>
#include <map>
namespace {
using namespace Hw;
using Hw::Device;
struct Acpi_ec : Acpi_dev
{
struct Flags
{
l4_uint32_t raw;
CXX_BITFIELD_MEMBER( 0, 0, as_handler_installed, raw);
CXX_BITFIELD_MEMBER( 1, 1, gpe_handler_installed, raw);
CXX_BITFIELD_MEMBER( 0, 1, handlers_installed, raw);
Flags() = default;
explicit Flags(l4_uint32_t v) : raw(v) {}
};
/**
* \brief The EC status register.
*/
struct Status
{
l4_uint8_t raw;
CXX_BITFIELD_MEMBER_UNSHIFTED(0, 0, out_buffer_full, raw);
CXX_BITFIELD_MEMBER_UNSHIFTED(1, 1, in_buffer_full, raw);
CXX_BITFIELD_MEMBER_UNSHIFTED(3, 3, command_issued, raw);
CXX_BITFIELD_MEMBER_UNSHIFTED(4, 4, burst_mode, raw);
CXX_BITFIELD_MEMBER_UNSHIFTED(5, 5, sci_pending, raw);
CXX_BITFIELD_MEMBER_UNSHIFTED(6, 6, smi_pending, raw);
Status() = default;
Status(l4_uint8_t v) : raw(v) {}
};
// EC Commands
enum Ec_command : l4_uint8_t
{
Ec_read = 0x80,
Ec_write = 0x81,
Ec_burst_enable = 0x82,
Ec_burst_disable = 0x83,
Ec_query = 0x84
};
Acpi_ec(ACPI_HANDLE hdl) : Acpi_dev(hdl), _flags(0), _data(0) {}
static bool discover_ecdt()
{
ACPI_TABLE_HEADER *ecdt_hdr;
ACPI_STATUS status = AcpiGetTable(ACPI_STRING(ACPI_SIG_ECDT), 1, &ecdt_hdr);
if (ACPI_FAILURE(status))
return false;
ACPI_TABLE_ECDT *ecdt = reinterpret_cast<ACPI_TABLE_ECDT *>(ecdt_hdr);
d_printf(DBG_DEBUG, "ACPI: EC via ECDT found\n");
Acpi_ec *ec = new Acpi_ec(ACPI_ROOT_OBJECT);
ec->_data = ecdt->Data.Address;
ec->_control = ecdt->Control.Address;
ec->_gpe = ecdt->Gpe;
ec->_need_lock = 0;
ec->_uid = ecdt->Uid;
AcpiGetHandle(ACPI_ROOT_OBJECT, ACPI_STRING(ecdt->Id), &ec->_obj);
_ecdt_ec = ec;
ec->install_handlers();
return true;
}
void setup_ec(Hw::Device *host)
{
Resource_list const &r = *host->resources();
if (r.size() < 2)
{
d_printf(DBG_ERR, "error: EC: error missing resources\n");
return;
}
if (r[0]->type() != Resource::Io_res || r[1]->type() != Resource::Io_res)
{
d_printf(DBG_ERR, "error: EC: resource type mismatch: types=%d,%d\n",
r[0]->type(), r[1]->type());
return;
}
if (!r[0]->start() || !r[1]->start())
{
d_printf(DBG_ERR, "error: EC: invalid ec ports=%x,%x\n",
(unsigned)r[0]->start(), (unsigned)r[1]->start());
return;
}
{
// handle ECs _GLK object
Acpi_buffer<ACPI_OBJECT> glk;
ACPI_STATUS status = AcpiEvaluateObject(handle(), ACPI_STRING("_GLK"), NULL, &glk);
if (ACPI_FAILURE(status) || glk.value.Type != ACPI_TYPE_INTEGER)
_need_lock = 0;
else
_need_lock = static_cast<bool>(glk.value.Integer.Value);
}
// if we are already initialized (may be via ECDT) we must skip the rest
if (_data)
{
return;
}
{
// handle the _GPE object
ACPI_STATUS status;
Acpi_buffer<ACPI_OBJECT> gpe;
status = AcpiEvaluateObject(handle(), ACPI_STRING("_GPE"), NULL, &gpe);
// never seen the package return value here...
if (ACPI_FAILURE(status) || gpe.value.Type != ACPI_TYPE_INTEGER)
{
d_printf(DBG_ERR, "error: EC: invalid result from _GPE: %d\n", status);
return;
}
_gpe = gpe.value.Integer.Value;
}
_data = r[0]->start();
_control = r[1]->start();
install_handlers();
Acpi_walk find_qr = [this](ACPI_HANDLE hdl, int)
{
Acpi_buffer<char [5]> name;
ACPI_STATUS s = AcpiGetName(hdl, ACPI_SINGLE_NAME, &name);
unsigned qval;
if (ACPI_SUCCESS(s) && sscanf(name.value, "_Q%x", &qval) == 1)
_query_handlers[qval] = hdl;
return AE_OK;
};
find_qr.walk(ACPI_TYPE_METHOD, handle(), 1);
AcpiEnableGpe(0, _gpe);
}
private:
friend struct Acpi_ec_drv;
typedef std::map<l4_uint8_t, ACPI_HANDLE> Handler_map;
Flags _flags;
Handler_map _query_handlers;
l4_uint16_t _data;
l4_uint16_t _control;
l4_uint32_t _gpe;
l4_uint32_t _uid;
bool _need_lock : 1;
static Acpi_ec *_ecdt_ec;
void write_cmd(l4_uint8_t cmd)
{ l4util_out8(cmd, _control); }
void write_data(l4_uint8_t data)
{ l4util_out8(data, _data); }
l4_uint8_t read_data()
{ return l4util_in8(_data); }
void wait_read()
{
while (!status().out_buffer_full())
;
}
void wait_write()
{
while (status().in_buffer_full())
;
}
ACPI_STATUS write(l4_uint8_t address, l4_uint8_t data)
{
if (!_data)
return AE_NOT_FOUND;
AcpiDisableGpe(NULL, _gpe);
write_cmd(Ec_write);
wait_write();
write_data(address);
wait_write();
write_data(data);
l4_sleep(1);
AcpiEnableGpe(NULL, _gpe);
return AE_OK;
}
ACPI_STATUS read(l4_uint8_t address, l4_uint8_t *data)
{
if (!_data)
return AE_NOT_FOUND;
AcpiDisableGpe(NULL, _gpe);
write_cmd(Ec_read);
wait_write();
write_data(address);
wait_read();
*data = read_data();
l4_sleep(1);
AcpiEnableGpe(NULL, _gpe);
return AE_OK;
}
Status status() const
{ return l4util_in8(_control); }
ACPI_STATUS as_handler(l4_uint32_t func, ACPI_PHYSICAL_ADDRESS addr,
l4_uint32_t width, UINT64 *_value)
{
ACPI_STATUS result = AE_OK;
unsigned bytes = width / 8;
l4_uint8_t *value = reinterpret_cast<l4_uint8_t*>(_value);
if ((addr > 0xFF) || !value)
return AE_BAD_PARAMETER;
if (func != ACPI_READ && func != ACPI_WRITE)
return AE_BAD_PARAMETER;
for (unsigned i = 0; i < bytes; ++i, ++addr, ++value)
result = (func == ACPI_READ)
? read(addr, value)
: write(addr, *value);
return result;
}
ACPI_STATUS gpe_handler(ACPI_HANDLE, l4_uint32_t)
{
if (status().sci_pending())
AcpiOsExecute(OSL_NOTIFY_HANDLER, _gpe_query, this);
return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE;
}
void gpe_query()
{
AcpiDisableGpe(NULL, _gpe);
write_cmd(Ec_query);
wait_read();
l4_uint8_t q = read_data();
AcpiEnableGpe(NULL, _gpe);
Handler_map::const_iterator i = _query_handlers.find(q);
if (i == _query_handlers.end())
return;
AcpiEvaluateObject(i->second, 0, 0, 0);
}
static void _gpe_query(void *ec)
{
static_cast<Acpi_ec*>(ec)->gpe_query();
}
static ACPI_STATUS _as_handler(l4_uint32_t func, ACPI_PHYSICAL_ADDRESS addr,
l4_uint32_t width, UINT64 *value,
void *ctxt, void *)
{
return static_cast<Acpi_ec*>(ctxt)->as_handler(func, addr, width, value);
}
static ACPI_STATUS _gpe_handler(ACPI_HANDLE handle, l4_uint32_t gpe, void *ctxt)
{
return static_cast<Acpi_ec*>(ctxt)->gpe_handler(handle, gpe);
}
void install_handlers()
{
if (_flags.handlers_installed())
return;
ACPI_STATUS status;
status = AcpiInstallAddressSpaceHandler(handle(), ACPI_ADR_SPACE_EC,
&_as_handler, 0, this);
if (ACPI_FAILURE(status))
{
d_printf(DBG_ERR, "error: failed to install EC address-space handler: %s\n",
AcpiFormatException(status));
return;
}
_flags.as_handler_installed() = true;
status = AcpiInstallGpeHandler(NULL, _gpe, ACPI_GPE_EDGE_TRIGGERED,
&_gpe_handler, this);
if (ACPI_FAILURE(status))
{
d_printf(DBG_ERR, "error: failed to install EC GPE handler: %s\n",
AcpiFormatException(status));
return;
}
_flags.gpe_handler_installed() = true;
}
};
Acpi_ec *Acpi_ec::_ecdt_ec;
struct Acpi_ec_drv : Acpi_device_driver
{
Acpi_dev *probe(Device *device, ACPI_HANDLE acpi_hdl,
ACPI_DEVICE_INFO const *) override
{
d_printf(DBG_DEBUG, "Found ACPI EC\n");
Acpi_ec *ec = 0;
if ( Acpi_ec::_ecdt_ec
&& ( Acpi_ec::_ecdt_ec->handle() == acpi_hdl
|| Acpi_ec::_ecdt_ec->handle() == ACPI_ROOT_OBJECT))
{
d_printf(DBG_DEBUG, "ACPI: Use EC from ECDT\n");
ec = Acpi_ec::_ecdt_ec;
Acpi_ec::_ecdt_ec = 0;
}
else
ec = new Acpi_ec(acpi_hdl);
ec->discover_crs(device);
ec->setup_ec(device);
device->add_feature(ec);
return ec;
};
};
static Acpi_ec_drv _acpi_ec_drv;
struct Init
{
Init()
{
Acpi_device_driver::register_driver("PNP0C09", &_acpi_ec_drv);
}
};
static Init init;
}
int acpi_ecdt_scan()
{
Acpi_ec::discover_ecdt();
return 0;
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright (C) 2010-2020, 2022, 2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam.lackorzynski@kernkonzept.com>,
* Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "io_acpi.h"
#include "__acpi.h"
#include "debug.h"
#include <pci-root.h>
#include <l4/cxx/list>
namespace {
using namespace Hw;
using Hw::Device;
static ACPI_STATUS
acpi_eval_int(ACPI_HANDLE hdl, ACPI_STRING path,
ACPI_OBJECT_LIST *args, unsigned long long *res)
{
Acpi_buffer<ACPI_OBJECT> r;
if (!res)
return AE_BAD_PARAMETER;
ACPI_STATUS s = AcpiEvaluateObjectTyped(hdl, path, args, &r,
ACPI_TYPE_INTEGER);
if (ACPI_FAILURE(s))
return s;
*res = r.value.Integer.Value;
return AE_OK;
}
static ACPI_STATUS
get_bus_range(ACPI_RESOURCE *res, void *ctxt)
{
int *bbn = (int *)ctxt;
int mi = 0, len = 0;
switch (res->Type)
{
case ACPI_RESOURCE_TYPE_ADDRESS16:
mi = res->Data.Address16.Address.Minimum;
len = res->Data.Address16.Address.AddressLength;
break;
case ACPI_RESOURCE_TYPE_ADDRESS32:
mi = res->Data.Address32.Address.Minimum;
len = res->Data.Address32.Address.AddressLength;
break;
case ACPI_RESOURCE_TYPE_ADDRESS64:
mi = res->Data.Address64.Address.Minimum;
len = res->Data.Address64.Address.AddressLength;
break;
default:
return AE_OK;
}
if (res->Data.Address.ResourceType != ACPI_BUS_NUMBER_RANGE)
return AE_OK;
bbn[0] = mi;
bbn[1] = len;
return AE_OK;
}
static ACPI_STATUS
get_bbn(ACPI_HANDLE hdl, int *bbn)
{
bbn[0] = -1;
ACPI_STATUS s;
s = AcpiWalkResources(hdl, ACPI_STRING("_CRS"), get_bus_range, bbn);
if (ACPI_FAILURE(s))
return s;
if (bbn[0] == -1)
return AE_ERROR;
return AE_OK;
}
struct Acpi_pci_root_drv : Acpi_device_driver
{
Acpi_dev *probe(Device *device, ACPI_HANDLE acpi_hdl,
ACPI_DEVICE_INFO const *info) override
{
d_printf(DBG_DEBUG, "Found PCI root bridge...\n");
// do this first so we have an Acpi_dev feature installed for 'device'
Acpi_dev *adev = Acpi_device_driver::probe(device, acpi_hdl, info);
unsigned long long seg = 0;
ACPI_STATUS s = acpi_eval_int(acpi_hdl, ACPI_STRING("_SEG"), NULL, &seg);
// _SEG is optional so use segment = 0 if not found
if (ACPI_FAILURE(s) && s != AE_NOT_FOUND)
{
d_printf(DBG_ERR,
"error: could not evaluate '_SEG' for PCI root bridge\n");
return adev;
}
Hw::Pci::Root_bridge *rb = Hw::Pci::root_bridge(seg);
if (!rb)
{
d_printf(DBG_ERR, "error: PCI root bridge for segment %d missing\n",
(int)seg);
return adev;
}
int bbn[2] = { -1, -1 };
s = get_bbn(acpi_hdl, bbn);
if (ACPI_FAILURE(s))
{
unsigned long long _bbn = 0;
s = acpi_eval_int(acpi_hdl, ACPI_STRING("_BBN"), NULL, &_bbn);
if (ACPI_FAILURE(s) && s != AE_NOT_FOUND)
{
d_printf(DBG_ERR,
"error: cannot evaluate '_BBN' for PCI root bridge\n");
return adev;
}
bbn[0] = _bbn;
bbn[1] = 256;
}
if (bbn[1] <= 0)
{
d_printf(DBG_INFO,
"%s: assigned PCI bus range is empty skip root bridge\n",
device->name());
return adev;
}
auto *rb2 = Hw::Pci::find_root_bridge(seg, bbn[0]);
if (rb2 && rb2->host())
{
d_printf(DBG_DEBUG, "%s: skip bridge for already existing bus: %d\n",
device->name(), bbn[0]);
return adev;
}
d_printf(DBG_DEBUG, "%s: PCI bus range %d-%d\n", device->name(), bbn[0],
bbn[0] + bbn[1] - 1);
if (rb->host())
{
// we found a second root bridge
// create a new root bridge instance
rb = acpi_create_pci_root_bridge(seg, bbn[0], device);
Hw::Pci::register_root_bridge(rb);
}
else
{
rb->set_host(device);
rb->secondary = bbn[0];
}
device->add_feature(rb);
return adev;
}
};
static Acpi_pci_root_drv pci_root_drv;
struct Init
{
Init()
{
Acpi_device_driver::register_driver(PCI_ROOT_HID_STRING, &pci_root_drv);
Acpi_device_driver::register_driver(PCI_EXPRESS_ROOT_HID_STRING, &pci_root_drv);
}
};
static Init init;
}

View File

@@ -0,0 +1,13 @@
/*
* Copyright (C) 2019, 2024 Kernkonzept GmbH.
* Author(s): Frank Mehnert <frank.mehnert@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "cpg_rcar3.h"
// Static constexpr data member declarations aren't definitions in C++11/14.
constexpr unsigned Rcar3_cpg::rmstpcr[Nr_modules];
constexpr unsigned Rcar3_cpg::smstpcr[Nr_modules];
constexpr unsigned Rcar3_cpg::mstpsr[Nr_modules];

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2019-2022, 2024 Kernkonzept GmbH.
* Author(s): Frank Mehnert <frank.mehnert@kernkonzept.com>
* Matthias Lange <matthias.lange@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/drivers/hw_mmio_register_block>
#include <l4/re/env>
#include <l4/re/rm>
#include <l4/util/util.h>
#include "debug.h"
#include "res.h"
/*
* Driver for the RCar-3 clock pulse generator (CPG). Documentation can be
* found at section 8 ("Clock Pulse Generator") in the Renesas RCar-3 hardware
* manual. This code is used to enable a hardware unit at the board. If such a
* unit is not enabled then it's invisible.
*/
class Rcar3_cpg
{
public:
Rcar3_cpg(l4_uint64_t base)
{
_base = res_map_iomem(base, 0x1000);
if (!_base)
{
d_printf(DBG_ERR, "error: rcar3_cpg: could not map CPG memory.\n");
throw -L4_ENOMEM;
}
_regs = new L4drivers::Mmio_register_block<32>(_base);
}
~Rcar3_cpg()
{
L4Re::Env::env()->rm()->detach(_base, 0);
}
/**
* Enable the clock for a certain hardware unit.
*
* \param n The number of the module register.
* \param bit The nth bit of the module register.
*
* \retval false Enabling the unit failed for some reason.
* \retval true Enabling the unit succeeded.
*
* The unit is described by the number of the module register and by the bit
* within the module register. The bits of all module register are documented
* in the Renesas RCar-3 hardware manual, see "Module Stop Status Register".
* If the function returns successfully then the hardware unit was indeed
* enabled.
*/
int enable_clock(unsigned n, unsigned bit)
{
if (n > Nr_modules - 1)
{
d_printf(DBG_ERR, "rcar3_cpg: invalid module %u.\n", n);
return -L4_EINVAL;
}
if (bit > 31)
{
d_printf(DBG_ERR, "rcar3_cpg: invalid bit %u.\n", bit);
return -L4_EINVAL;
}
unsigned mask = 1 << bit;
// assume Cpgwpcr.wpe=1
unsigned val = _regs[smstpcr[n]];
val &= ~mask;
_regs[Cpgwpr] = ~val;
_regs[smstpcr[n]] = val;
// The MSTPSRn register shows the status of the corresponding module which
// was enabled using the respective SMSTPCRn register.
for (unsigned i = 20; i > 0; --i)
{
if (!(_regs[mstpsr[n]] & mask))
return L4_EOK;
l4_sleep(5);
}
// Device not there or doesn't work.
return -L4_ENXIO;
}
private:
enum : unsigned
{
Cpgwpr = 0x900, //< CPG write protect register
Cpgwpcr = 0x904, //< CPG write protect control register
Nr_modules = 12, //< Number of module registers
//< (for Mstpsr, Rmstpcr, Smstpcr, Scmstpcr)
Smstpcr0 = 0x130, //< System module stop control register 0
Smstpcr1 = 0x134, //< System module stop control register 1
Smstpcr2 = 0x138, //< System module stop control register 2
Smstpcr3 = 0x13c, //< System module stop control register 3
Smstpcr4 = 0x140, //< System module stop control register 4
Smstpcr5 = 0x144, //< System module stop control register 5
Smstpcr6 = 0x148, //< System module stop control register 6
Smstpcr7 = 0x14c, //< System module stop control register 7
Smstpcr8 = 0x990, //< System module stop control register 8
Smstpcr9 = 0x994, //< System module stop control register 9
Smstpcr10 = 0x998, //< System module stop control register 10
Smstpcr11 = 0x99c, //< System module stop control register 11
Mstpsr0 = 0x030, //< Module stop status register 0
Mstpsr1 = 0x038, //< Module stop status register 1
Mstpsr2 = 0x040, //< Module stop status register 2
Mstpsr3 = 0x048, //< Module stop status register 3
Mstpsr4 = 0x04c, //< Module stop status register 4
Mstpsr5 = 0x03c, //< Module stop status register 5
Mstpsr6 = 0x1c0, //< Module stop status register 6
Mstpsr7 = 0x1c4, //< Module stop status register 7
Mstpsr8 = 0x9a0, //< Module stop status register 8
Mstpsr9 = 0x9a4, //< Module stop status register 9
Mstpsr10 = 0x9a8, //< Module stop status register 10
Mstpsr11 = 0x9ac, //< Module stop status register 11
Rmstpcr0 = 0x110, //< Realtime module stop control register 0
Rmstpcr1 = 0x114, //< Realtime module stop control register 1
Rmstpcr2 = 0x118, //< Realtime module stop control register 2
Rmstpcr3 = 0x11c, //< Realtime module stop control register 3
Rmstpcr4 = 0x120, //< Realtime module stop control register 4
Rmstpcr5 = 0x124, //< Realtime module stop control register 5
Rmstpcr6 = 0x128, //< Realtime module stop control register 6
Rmstpcr7 = 0x12c, //< Realtime module stop control register 7
Rmstpcr8 = 0x980, //< Realtime module stop control register 8
Rmstpcr9 = 0x984, //< Realtime module stop control register 9
Rmstpcr10 = 0x988, //< Realtime module stop control register 10
Rmstpcr11 = 0x98c, //< Realtime module stop control register 11
};
static constexpr unsigned rmstpcr[Nr_modules] =
{
Rmstpcr0, Rmstpcr1, Rmstpcr2, Rmstpcr3, Rmstpcr4, Rmstpcr5,
Rmstpcr6, Rmstpcr7, Rmstpcr8, Rmstpcr9, Rmstpcr10, Rmstpcr11,
};
static constexpr unsigned smstpcr[Nr_modules] =
{
Smstpcr0, Smstpcr1, Smstpcr2, Smstpcr3, Smstpcr4, Smstpcr5,
Smstpcr6, Smstpcr7, Smstpcr8, Smstpcr9, Smstpcr10, Smstpcr11
};
static constexpr unsigned mstpsr[Nr_modules] =
{
Mstpsr0, Mstpsr1, Mstpsr2, Mstpsr3, Mstpsr4, Mstpsr5,
Mstpsr6, Mstpsr7, Mstpsr8, Mstpsr9, Mstpsr10, Mstpsr11,
};
l4_addr_t _base;
L4drivers::Register_block<32> _regs;
};

View File

@@ -0,0 +1,261 @@
/*
* Copyright (C) 2017, 2022-2024 Kernkonzept GmbH.
* Author(s): Matthias Lange <matthias.lange@kernkonzept.com>
* Frank Mehnert <frank.mehnert@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/util/util.h>
#include "dwc_pcie_core.h"
#include "utils.h"
#include "resource_provider.h"
#include <errno.h>
#include <l4/drivers/hw_mmio_register_block>
#include <l4/re/error_helper>
#include <inttypes.h>
int
Dwc_pcie::host_init()
{
if ( assert_property(&_cfg_base, "cfg_base", ~0)
|| assert_property(&_cfg_size, "cfg_size", ~0)
|| assert_property(&_regs_base, "regs_base", ~0)
|| assert_property(&_regs_size, "regs_size", ~0)
|| assert_property(&_mem_base, "mem_base", ~0)
|| assert_property(&_mem_size, "mem_size", ~0))
return -L4_EINVAL;
if (_num_lanes > 16)
{
d_printf(DBG_ERR, "error: %s: invalid number of PCIe lanes: %lld\n",
name(), _num_lanes.val());
return -L4_EINVAL;
}
l4_addr_t va = res_map_iomem(_regs_base, _regs_size);
if (!va)
{
d_printf(DBG_ERR, "error: %s: could not map IP core memory.\n", name());
return -L4_ENOMEM;
}
_regs = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_cfg_base, _cfg_size);
if (!va)
{
d_printf(DBG_ERR, "error: %s: could not map config space memory.\n",
name());
return -L4_ENOMEM;
}
_cfg = new L4drivers::Mmio_register_block<32>(va);
return L4_EOK;
}
void
Dwc_pcie::set_iatu_region(unsigned index, l4_uint64_t base_addr,
l4_uint32_t size, l4_uint64_t target_addr,
unsigned tlp_type, unsigned dir)
{
// the default configuration of the PCIe core only has two viewports
if (index > 1)
return;
if (_cpu_fixup != ~0)
base_addr = base_addr + _cpu_fixup - _mem_base;
_regs[Iatu_viewport] = index | (dir & Dir_mask);
_regs[Iatu_lower_base] = base_addr & 0xffffffff;
_regs[Iatu_upper_base] = base_addr >> 32;
_regs[Iatu_limit] = (base_addr + size - 1) & 0xffffffff; // i.MX8: bits 12..31 ignored
_regs[Iatu_lower_target] = target_addr & 0xffffffff;
_regs[Iatu_upper_target] = target_addr >> 32;
_regs[Iatu_ctrl_1] = tlp_type & Type_mask;
_regs[Iatu_ctrl_2] = Region_enable;
for (unsigned i = 0; i < 10; ++i)
{
if ((_regs[Iatu_ctrl_2] & Region_enable) == Region_enable)
return;
l4_usleep(10000);
}
d_printf(DBG_ERR, "error: %s: ATU not enabled @index %u\n", name(), index);
#ifdef ARCH_MIPS
asm volatile ("sync" : : : "memory");
#endif
}
void
Dwc_pcie::setup_rc()
{
// enable writing to read-only registers
_regs[Misc_control_1].set(1U << 0);
// set number of lanes
unsigned lme;
switch (_num_lanes)
{
case 2: lme = Link_2_lanes; break;
case 4: lme = Link_4_lanes; break;
case 8: lme = Link_8_lanes; break;
case 16: lme = Link_16_lanes; break;
default: lme = Link_1_lanes; break;
}
_regs[Port_logic::Link_ctrl].modify(Mode_enable_mask,
lme << Mode_enable_shift);
_regs[Gen2].modify(Lane_enable_mask, _num_lanes << Lane_enable_shift);
// disable MSI for now
_regs[Msi_ctrl_lower_addr] = 0;
_regs[Msi_ctrl_upper_addr] = 0;
// setup BARs
_regs[Hw::Pci::Config::Bar_0] = 0x00000004;
_regs[Hw::Pci::Config::Bar_0+4] = 0x00000000;
// setup interrupt pins
_regs[Hw::Pci::Config::Irq_line].modify(0x0000ff00, 0x100);
// setup bus numbers (primary = 0, secondary = 1, subordinate = 1)
_regs[Hw::Pci::Config::Primary].modify(0x00ffffff, 0x010100);
// setup command register: Io, Memory, Master, Serr
_regs[Hw::Pci::Config::Command].modify(0x0000ffff, 0x107);
// TODO: Should we get the mem bus addr from a property?
l4_addr_t bus_addr = _mem_base;
set_iatu_region(Iatu_vp::Idx0, _mem_base, _mem_size, bus_addr, Tlp_type::Mem);
Resource *re = new Resource_provider(Resource::Mmio_res);
re->alignment(0xfffff);
re->start_end(_mem_base, _mem_base + _mem_size - 1);
re->set_id("MMIO");
add_resource_rq(re);
if (_regs[Iatu_ctrl_2] != Region_enable)
d_printf(DBG_INFO, "info: %s: iATU not enabled\n", name());
_regs[Hw::Pci::Config::Bar_0] = 0x00000000;
// set correct PCI class for RC
_regs[Hw::Pci::Config::Class_rev].modify(0xffff0000, 0x0604 << 16);
// enable directed speed change to automatically transition to Gen2 or Gen3
// speeds after link training
_regs[Gen2].set(1 << Speed_change_shift);
// disable writing to read-only registers
_regs[Misc_control_1].clear(1U << 0);
}
inline L4drivers::Register_block<32>
Dwc_pcie::cfg_regs(Cfg_addr addr)
{
if (addr.bus() == secondary)
return _regs;
uint32_t target = ((addr.bus() << 8) | addr.devfn()) << 16;
// TODO: add cfg type 1 access
set_iatu_region(Iatu_vp::Idx1, _cfg_base, _cfg_size, target, Tlp_type::Cfg0);
return _cfg;
}
int
Dwc_pcie::cfg_read(Cfg_addr addr, l4_uint32_t *value, Cfg_width w)
{
uint32_t v;
if (!device_valid(addr))
v = 0xffffffff;
else
v = cfg_regs(addr)[addr.reg() & ~3];
switch (w)
{
case Hw::Pci::Cfg_long:
*value = v;
break;
case Hw::Pci::Cfg_short:
*value = (v >> ((addr.reg() & 3) << 3)) & 0xffff;
break;
case Hw::Pci::Cfg_byte:
*value = (v >> ((addr.reg() & 3) << 3)) & 0xff;
break;
default:
d_printf(DBG_WARN, "%s: Invalid width %d in cfg_read!\n", name(), w);
return -EIO;
}
d_printf(DBG_ALL,
"%s: cfg_read addr=%02x:%02x.%x reg=%03x width=%2d-bit => %0*x\n",
name(), addr.bus(), addr.dev(), addr.fn(), addr.reg(), 8 << w,
2 << w, *value & cfg_o_to_mask(w));
return 0;
}
int
Dwc_pcie::cfg_write(Cfg_addr addr, l4_uint32_t value, Cfg_width w)
{
if (!device_valid(addr))
return 0;
auto r = cfg_regs(addr);
uint32_t mask, shift;
d_printf(DBG_ALL,
"%s: cfg_write addr=%02x:%02x.%x reg=%03x width=%2d-bit value=%0*x\n",
name(), addr.bus(), addr.dev(), addr.fn(), addr.reg(), 8 << w,
2 << w, value & cfg_o_to_mask(w));
switch (w)
{
case Hw::Pci::Cfg_long:
r[addr.reg() & ~3] = value;
return 0;
case Hw::Pci::Cfg_short:
shift = (addr.reg() & 3) << 3;
mask = 0xffff << shift;
break;
case Hw::Pci::Cfg_byte:
shift = (addr.reg() & 3) << 3;
mask = 0xff << shift;
break;
default:
d_printf(DBG_WARN, "%s: Invalid width %d in cfg_write!\n", name(), w);
return -EIO;
}
r[addr.reg() & ~3].modify(mask, (value << shift) & mask);
return 0;
}
bool
Dwc_pcie::device_valid(Cfg_addr addr)
{
// It is a specific property of the Designware PCIe core that on bus 0 the
// PCI-to-PCI bridge is connected. To avoid stalls for any access to bus > 0
// the link has to be up.
if (addr.bus() != 0)
if (!link_up())
return false;
// there is only the PCI-to-PCI bridge on 'our' bus and there are no other
// devices
if ((addr.bus() == secondary) && (addr.dev() > 0))
return false;
if ((addr.bus() > secondary) && (addr.dev() > 0))
return false;
return true;
}

View File

@@ -0,0 +1,272 @@
/*
* Copyright (C) 2017, 2022-2024 Kernkonzept GmbH.
* Author(s): Matthias Lange <matthias.lange@kernkonzept.com>
* Frank Mehnert <frank.mehnert@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/cxx/type_traits>
#include <l4/drivers/hw_mmio_register_block>
#include "hw_device.h"
#include <pci-root.h>
/**
* Designware PCIe core driver
*/
class Dwc_pcie :
public Hw::Device,
public Hw::Pci::Root_bridge
{
public:
template<typename ...ARGS>
Dwc_pcie(ARGS && ...args)
: Hw::Pci::Root_bridge(cxx::forward<ARGS>(args)...),
_regs_base(~0), _regs_size(~0), _cfg_base(~0), _cfg_size(~0),
_mem_base(~0), _mem_size(~0), _cpu_fixup(~0), _num_lanes(1)
{
// the set of mandatory properties
register_property("cfg_base", &_cfg_base);
register_property("cfg_size", &_cfg_size);
register_property("regs_base", &_regs_base);
register_property("regs_size", &_regs_size);
register_property("mem_base", &_mem_base);
register_property("mem_size", &_mem_size);
register_property("cpu_fixup", &_cpu_fixup);
// the set of optional properties, if not set defaults will be applied
register_property("lanes", &_num_lanes);
}
/**
* Map and initialize all required resources.
*
* \retval L4_EOK Success.
* \retval -L4_EINVAL One of the mandatory resources could not be
* initialized.
* \retval -L4_ENOMEM One of the resources could not be mapped.
*/
int host_init();
/**
* Definition of the port logic register offsets.
*
* The port logic registers are vendor-specific and are mainly used to
* configure the PCIe core. The registers reside in the application register
* section of the configuration space starting at 0x700.
*/
enum Port_logic
{
Port_logic = 0x700,
Link_ctrl = Port_logic + 0x10,
Debug0 = Port_logic + 0x28,
Debug1 = Port_logic + 0x2c,
Gen2 = Port_logic + 0x10c, // PCIE_LINK_WIDTH_SPEED_CONTROL
Msi_ctrl_lower_addr = Port_logic + 0x120,
Msi_ctrl_upper_addr = Port_logic + 0x124,
Misc_control_1 = Port_logic + 0x1bc,
Iatu_viewport = Port_logic + 0x200,
Iatu_ctrl_1 = Port_logic + 0x204,
Iatu_ctrl_2 = Port_logic + 0x208,
Iatu_lower_base = Port_logic + 0x20c,
Iatu_upper_base = Port_logic + 0x210,
Iatu_limit = Port_logic + 0x214,
Iatu_lower_target = Port_logic + 0x218,
Iatu_upper_target = Port_logic + 0x21c
};
/**
* Bit for the Link_ctrl register
*/
enum Pl_link_ctrl
{
Mode_enable_shift = 16,
Mode_enable_mask = 0x3f << Mode_enable_shift,
};
/**
* Constants for the Link Mode Enable bits in the Pl_link_ctrl register.
*/
enum Link_mode
{
Link_1_lanes = 0x1,
Link_2_lanes = 0x3,
Link_4_lanes = 0x7,
Link_8_lanes = 0xf,
Link_16_lanes = 0x1f,
};
/**
* Bits for the Gen2 control register
*/
enum Pl_gen2
{
/**
* Indicate the number of lanes to check and to ignore broken lanes.
*/
Lane_enable_shift = 8,
/**
* Mask for the number of lanes.
*
* DesignWare documents bits 8-16. i.MX8 documents bits 8-12. The Linux
* driver uses bits 8-12 in PORT_LOGIC_LINK_WIDTH_MASK. As bits 13-16 have
* other meanings on i.MX8 we use bits 8-12 to not break i.MX8.
*/
Lane_enable_mask = 0x1f << Lane_enable_shift,
/**
* Indicates to LTSSM whether to initiate a speed change.
*/
Speed_change_shift = 17,
};
/**
* The total number of available iATU regions (=viewports) is configuration
* dependent. A driver must not set a viewport greater than
* CX_ATU_NUM_[OUT,IN]BOUND_REGIONS-1. However, the default configuration
* of the core seems to always have at least 2 outbound viewports (see the
* Linux driver or e.g. the iMX6q TRM.
*/
enum Iatu_vp
{
Idx0 = 0, ///< Viewport 1 index
Idx1 = 1, ///< Viewport 2 index
Idx2 = 2, ///< Viewport 3 index
Idx3 = 3, ///< Viewport 4 index
Dir_mask = 1 << 31,
Outbound = 0 << 31,
Inbound = 1 << 31,
};
enum Iatu_ctrl_1_values
{
Type_mask = 0x1f,
};
enum Iatu_ctrl_2_values
{
Region_enable = 1UL << 31, ///< Enable address translation for the configured region
};
/**
* Definition of PCIe Transaction Layer Protocol (TLP) types
*/
enum Tlp_type
{
Mem = 0, ///< Memory read and write requests
Io = 2, ///< I/O read and write requests
Cfg0 = 4, ///< Config type 0 read and write requests
Cfg1 = 5, ///< Config type 1 read and write requests
};
/**
* Program the internal address translation unit (iATU).
*
* \param index The viewport index that should be programmed. See
* #Iatu_vp.
* \param base_addr The start address of the region that should be
* translated.
* \param size The size of the region that should be translated. It
* depends on the platform how many bits are considered.
* For instance, for i.MX8, only bits 0..11 are relevant.
* \param target_addr The start address of the translated address.
* \param tlp_type The access type of the transaction layer protocol. See
* #Tlp_type.
* \param dir Determine whether the region is an inbound or outbound
* region. This parameter is optional and defaults to
* #Iatu_vp::Outbound.
*
* The iATU registers are programmed through an index (viewport) which needs
* to be written into the Iatu_viewport register before accessing the other
* iATU registers.
*/
void set_iatu_region(unsigned index, l4_uint64_t base_addr, l4_uint32_t size,
l4_uint64_t target_addr, unsigned tlp_type,
unsigned dir = Outbound);
typedef Hw::Pci::Cfg_addr Cfg_addr;
typedef Hw::Pci::Cfg_width Cfg_width;
/**
* Configure and return a PCI config space register set.
*
* \param addr The address of the config space that should be setup.
*
* \return The config space register set for the given config address.
*/
L4drivers::Register_block<32> cfg_regs(Cfg_addr addr);
/**
* Perform a PCI config space read.
*
* \param addr The config space address to read from.
* \param[out] value This parameter returns the value read from addr.
* \param width The width of the config space access. See #Cfg_width.
*
* \retval 0 The access was completed successfully.
* \retval -EIO Invalid access width.
*/
int cfg_read(Cfg_addr addr, l4_uint32_t *value, Cfg_width) override;
/**
* Perform a PCI config space write.
*
* \param addr The config space address to write to.
* \param value The value to write to addr.
* \param width The width of the config space access. See #Cfg_width.
*
* \retval 0 The access was completed successfully.
* \retval -EIO Invalid access width.
*/
int cfg_write(Cfg_addr addr, l4_uint32_t value, Cfg_width) override;
/**
* Returns whether the PCIe link is running.
*
* \retval true PCIe link is running.
* \retval false PCIe link is not running.
*
* This function checks the Debug Register 1 from the vendor-specific
* registers in the PCIe extended configuration space. Bit 4 in this register
* reports whether LTSSM was able to bring the PHY link up. Bit 29 signals
* that LTSSM is still performing link training.
*
* This function can be overridden by a sub class.
*/
virtual bool link_up()
{
l4_uint32_t val = _regs[Port_logic::Debug1];
return ((val & (1 << 4)) && !(val & 1 << 29));
}
/**
* Setup the PCIe core as root complex (RC)
*/
void setup_rc();
protected:
L4drivers::Register_block<32> _regs; ///< The PCIe IP core registers
L4drivers::Register_block<32> _cfg; ///< The PCI config space region
Int_property _regs_base; ///< Base address of the PCIe core registers
Int_property _regs_size; ///< Size of the PCIe core register space
Int_property _cfg_base; ///< Base address of the PCI config space region
Int_property _cfg_size; ///< Size of the PCI config space region
Int_property _mem_base; ///< Base address of the memory region
Int_property _mem_size; ///< Size of the memory region
Int_property _cpu_fixup; ///< CPU fixup for accessing config space (optional)
Int_property _num_lanes; ///< Number of PCIe lanes
private:
/**
* Check whether the config space address belongs to a valid device
*
* \param addr Config space address of the device.
*
* \retval true The device is valid.
* \retval false The device is invalid and cannot be accessed.
*/
bool device_valid(Cfg_addr addr);
};

View File

@@ -0,0 +1,27 @@
comment "GPIO Drivers"
config L4IO_GPIO_BCM2835
bool "Broadcom BCM2835"
default y
help
Add support for Broadcom BCM2835 gpio chip
config L4IO_GPIO_OMAP
bool "OMAP35x, OMAP44x, OMAP54x"
default y
help
Add support for the OMAP3/4/5 GPIO chips (omap35x,omap44x,omap54x)
config L4IO_GPIO_QCOM
bool "GPIO Qualcomm TLMM"
default y
help
Add support for the Qualcomm "Top-Level Mode Multiplexer" (TLMM) GPIO
controller used in most Qualcomm Snapdragon chips (e.g. MSM8916).
config L4IO_GPIO_IMX
bool "GPIO IMX / IMX8QM"
default y
select L4IO_SCU_IMX8QM
help
Add support for the IMX / IMX8 GPIO chips.

View File

@@ -0,0 +1,10 @@
PKGDIR ?= ../../../..
L4DIR ?= $(PKGDIR)/../../..
SUBDIRS :=
SRC_CC-$(CONFIG_L4IO_GPIO_BCM2835) += bcm2835.cc
SRC_CC-$(CONFIG_L4IO_GPIO_OMAP) += omap.cc
SRC_CC-$(CONFIG_L4IO_GPIO_QCOM) += qcom.cc
SRC_CC-$(CONFIG_L4IO_GPIO_IMX) += imx.cc
include $(PKGDIR)/server/src/lib_subdir.mk

View File

@@ -0,0 +1,564 @@
#include <l4/drivers/hw_mmio_register_block>
#include <l4/util/util.h>
#include <l4/cxx/unique_ptr>
#include "main.h"
#include "irqs.h"
#include "debug.h"
#include "gpio"
#include "hw_device.h"
#include "hw_irqs.h"
#include "server.h"
#include "gpio_irq.h"
namespace {
/*
* Registers with one bit per pin and 3 registers
* of each kind.
*/
enum Regs
{
Set = 0x1c,
Clr = 0x28,
Lev = 0x34,
Eds = 0x40,
Ren = 0x4c,
Fen = 0x58,
Hen = 0x64,
Len = 0x70,
Aren = 0x7c,
Afen = 0x88,
Pudclk = 0x98,
};
class Gpio_irq_server;
class Gpio_irq_pin : public Gpio_irq_base_t<Gpio_irq_pin>
{
L4drivers::Register_block<32> _regs;
void write_reg_pin(unsigned reg, unsigned val)
{
if (val & 1)
_regs[reg].set(1 << pin());
else
_regs[reg].clear(1 << pin());
}
public:
Gpio_irq_pin(unsigned vpin, L4drivers::Register_block<32> const &regs)
: Gpio_irq_base_t<Gpio_irq_pin>(vpin), _regs(regs)
{}
void do_mask()
{
switch (mode())
{
case L4_IRQ_F_LEVEL_HIGH:
write_reg_pin(Hen, 0);
return;
case L4_IRQ_F_LEVEL_LOW:
write_reg_pin(Len, 0);
return;
case L4_IRQ_F_POS_EDGE:
write_reg_pin(Ren, 0);
return;
case L4_IRQ_F_NEG_EDGE:
write_reg_pin(Fen, 0);
return;
case L4_IRQ_F_BOTH_EDGE:
write_reg_pin(Ren, 0);
write_reg_pin(Fen, 0);
return;
}
}
void do_unmask()
{
switch (mode())
{
case L4_IRQ_F_LEVEL_HIGH:
write_reg_pin(Hen, 1);
return;
case L4_IRQ_F_LEVEL_LOW:
write_reg_pin(Len, 1);
return;
case L4_IRQ_F_POS_EDGE:
write_reg_pin(Ren, 1);
return;
case L4_IRQ_F_NEG_EDGE:
write_reg_pin(Fen, 1);
return;
case L4_IRQ_F_BOTH_EDGE:
write_reg_pin(Ren, 1);
write_reg_pin(Fen, 1);
return;
}
}
bool do_set_mode(unsigned mode)
{
// this operation touches multiple mmio registers and is thus
// not atomic, that's why we first mask the IRQ and if it was
// enabled we unmask it after we have changed the mode
// masking the Irq also clears the Irq mode on the BCM2835
if (enabled())
do_mask();
_mode = mode;
if (enabled())
do_unmask();
return true;
}
int clear() override
{
l4_uint32_t e = _regs[Eds] & (1UL << pin());
if (e)
_regs[Eds] = e;
return Io_irq_pin::clear() + (e >> pin());
}
};
class Gpio_irq_server : public Irq_demux_t<Gpio_irq_server>
{
l4_uint32_t _pins_mask;
L4drivers::Register_block<32> _regs;
public:
Gpio_irq_server(int irq, unsigned pins,
L4drivers::Register_block<32> const &regs)
: Irq_demux_t<Gpio_irq_server>(irq, 0, pins),
_pins_mask(pins >= 32 ? ~l4_uint32_t(0) : (1UL << pins) - 1),
_regs(regs)
{ enable(); }
void handle_irq()
{
l4_uint32_t eds = _regs[Eds];
if (L4_UNLIKELY(!eds))
{
// this usually never happens
enable();
return;
}
l4_uint32_t reset = eds;
l4_uint32_t pin_bit = 1, clear_hen = 0, clear_len = 0;
// mask all out-of-bounds pins for IRQ delivery
// however, we assume that this never happens
if (L4_UNLIKELY(eds & ~_pins_mask))
{
d_printf(DBG_ERR, "GPIO-BCM2835: IRQ for invalid pin(s): 0x%x, "
"maybe you use an invalid hardware config\n",
eds & ~_pins_mask);
eds &= _pins_mask;
}
for (unsigned pin = 0; eds; ++pin, pin_bit <<= 1, eds >>= 1)
if ((eds & 1) && _pins[pin])
{
switch (_pins[pin]->mode())
{
case L4_IRQ_F_LEVEL_HIGH: clear_hen |= pin_bit; break;
case L4_IRQ_F_LEVEL_LOW: clear_len |= pin_bit; break;
}
_pins[pin]->trigger();
}
// do the mask for level triggered IRQs
if (clear_hen)
_regs[Hen].clear(clear_hen);
if (clear_len)
_regs[Len].clear(clear_len);
_regs[Eds] = reset;
enable();
}
};
class Gpio_bcm2835_chip : public Hw::Gpio_device
{
private:
/*
* Registers with special layout
*/
enum Regs
{
Fsel = 0x0,
Pud = 0x94,
};
L4drivers::Register_block<32> _regs[2];
Gpio_irq_server *_irq_svr[2];
Int_property _nr_pins;
static unsigned _func_reg(unsigned pin)
{
/* There is room for 10 pins in each function select register,
* since each pin takes 3 bits */
return Fsel + (pin / 10) * (sizeof (l4_uint32_t));
}
static unsigned _func_reg_shift(unsigned pin)
{
/* There is room for 10 pins in each function select register,
* since each pin takes 3 bits */
return (pin % 10) * 3;
}
static unsigned _register(unsigned reg, unsigned pin)
{
/* There is room for 32 pins in each register,
* since each pin takes 1 bit */
return reg + (pin / 32) * (sizeof (l4_uint32_t));
}
static l4_uint32_t _pin_bit(unsigned pin)
{ return 1 << (pin & 31); }
static unsigned _pin_shift(unsigned pin)
{
/* There is room for 32 pins in each register,
* since each pin takes 1 bit */
return pin % 32;
}
unsigned _reg_offset_check(unsigned pin_offset) const
{
switch (pin_offset)
{
case 0:
return 0;
case 32:
if (_nr_pins <= 32)
throw -L4_ERANGE;
return 4;
default:
throw -L4_EINVAL;
}
}
public:
Gpio_bcm2835_chip() : _nr_pins(0)
{
add_cid("gpio");
add_cid("gpio-bcm2835");
register_property("pins", &_nr_pins);
}
unsigned nr_pins() const override { return _nr_pins; }
void setup(unsigned pin, unsigned mode, int value = 0) override;
void config_pull(unsigned pin, unsigned mode) override;
int get(unsigned pin) override;
void set(unsigned pin, int value) override;
void config_pad(unsigned pin, unsigned func, unsigned value) override;
void config_get(unsigned pin, unsigned func, unsigned *value) override;
Io_irq_pin *get_irq(unsigned pin) override;
void multi_setup(Pin_slice const &mask, unsigned mode,
unsigned outvalues = 0) override
{ generic_multi_setup(mask, mode, outvalues); }
void multi_config_pad(Pin_slice const &mask, unsigned func,
unsigned value = 0) override
{ generic_multi_config_pad(mask, func, value); }
void multi_set(Pin_slice const &mask, unsigned data) override;
unsigned multi_get(unsigned offset) override;
void init() override;
private:
enum
{
Func_in = 0,
Func_out = 1,
};
void config(unsigned pin, unsigned func)
{
if (func & ~0x7U)
{
d_printf(DBG_WARN, "Gpio_bcm2835_chip::config(func 0x%x): Wrong function\n",
func);
throw -L4_EINVAL;
}
unsigned shift = _func_reg_shift(pin);
_regs[0][_func_reg(pin)].modify(0x7U << shift, func << shift);
}
};
int
Gpio_bcm2835_chip::get(unsigned pin)
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
l4_uint32_t val = _regs[0][_register(Lev, pin)];
return (val >> _pin_shift(pin)) & 1;
}
unsigned
Gpio_bcm2835_chip::multi_get(unsigned offset)
{
return _regs[0][Lev + _reg_offset_check(offset)];
}
void
Gpio_bcm2835_chip::set(unsigned pin, int value)
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
unsigned reg_set = value ? Set : Clr;
_regs[0][_register(reg_set, pin)] = _pin_bit(pin);
}
void
Gpio_bcm2835_chip::multi_set(Pin_slice const &mask, unsigned data)
{
unsigned roffs = _reg_offset_check(mask.offset);
if (mask.mask & data)
_regs[0][Set + roffs] = (mask.mask & data);
if (mask.mask & ~data)
_regs[0][Clr + roffs] = (mask.mask & ~data);
}
void
Gpio_bcm2835_chip::setup(unsigned pin, unsigned mode, int value)
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
switch (mode)
{
case Input:
mode = Func_in;
break;
case Output:
mode = Func_out;
break;
case Irq:
// the BCM2835 GPIO does not have a dedicated Irq mode
// just set the function to Input
mode = Func_in;
break;
default:
// although setup is part of the generic Gpio API we allow
// hardware specific modes as well
mode &= 0x7;
break;
}
config(pin, mode);
if (mode == Func_out)
set(pin, value);
}
void
Gpio_bcm2835_chip::config_pull(unsigned pin, unsigned mode)
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
// NOTE: This function is not reentrant, uses the global
// PUD register.
switch (mode)
{
case Pull_none:
_regs[0][Pud] = 0;
break;
case Pull_up:
_regs[0][Pud] = 0x2;
break;
case Pull_down:
_regs[0][Pud] = 0x1;
break;
default:
d_printf(DBG_WARN, "warning: %s: invalid PUD mode for pin %u. "
"Ignoring.\n", name(), pin);
throw -L4_EINVAL;
}
/* We should wait at least 150 cycles according to the manual */
// FIXME: We usually MUST never sleep synchronously
l4_usleep(10);
_regs[0][_register(Pudclk, pin)] = _pin_bit(pin);
/* We should wait at least 150 cycles according to the manual */
// FIXME: We usually MUST never sleep synchronously
l4_usleep(10);
_regs[0][Pud] = 0;
_regs[0][_register(Pudclk, pin)] = 0;
}
void
Gpio_bcm2835_chip::config_pad(unsigned pin, unsigned reg, unsigned value)
{
d_printf(DBG_DEBUG2, "Gpio_bcm2835_chip::config_pad(%u, reg=%x, value=%x)\n",
pin, reg, value);
if (pin >= _nr_pins)
throw -L4_EINVAL;
switch (reg)
{
case Fsel:
config(pin, value);
break;
case Set:
case Clr:
case Lev:
case Eds:
case Ren:
case Fen:
case Hen:
case Len:
case Aren:
case Afen:
case Pudclk:
_regs[0][_register(reg, pin)].modify(_pin_bit(pin), value ? _pin_bit(pin) : 0);
break;
default:
case Pud: // cannot allow the global PUD register here (would need locks)
throw -L4_EINVAL;
}
}
void
Gpio_bcm2835_chip::config_get(unsigned pin, unsigned reg, unsigned *value)
{
d_printf(DBG_DEBUG2, "Gpio_bcm2835_chip::config_get(%u, reg=%x)\n",
pin, reg);
if (pin >= _nr_pins)
throw -L4_EINVAL;
switch (reg)
{
case Fsel:
*value = (_regs[0][_func_reg(pin)] >> _func_reg_shift(pin)) & 0x7;
return;
case Set:
case Clr:
case Lev:
case Eds:
case Ren:
case Fen:
case Hen:
case Len:
case Aren:
case Afen:
case Pudclk:
*value = (_regs[0][_register(reg, pin)] >> _pin_shift(pin)) & 1;
return;
default:
case Pud:
// cannot allow access to the global PUD register,
// it seems reading this register is useless anyway
throw -L4_EINVAL;
}
}
Io_irq_pin *
Gpio_bcm2835_chip::get_irq(unsigned pin)
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
unsigned svr = pin / 32;
if (!_irq_svr[svr])
return nullptr;
return _irq_svr[svr]->get_pin<Gpio_irq_pin>(pin % 32, _regs[svr]);
}
void
Gpio_bcm2835_chip::init()
{
Gpio_device::init();
Resource *regs = resources()->find("regs");
if (!regs || regs->type() != Resource::Mmio_res)
{
d_printf(DBG_ERR, "error: %s: no base address set for device: Gpio_bcm2835_chip\n"
" missing or wrong 'regs' resource\n"
" the chip will not work at all!\n", name());
return;
}
l4_uint64_t phys_base = regs->start();
l4_uint64_t size = regs->size();
if (size < 0xb4 || size > (1 << 20))
{
d_printf(DBG_ERR, "error: %s: invalid mmio size (%llx) for device: Gpio_bcm2835_chip\n"
" the chip will not work at all!\n", name(), size);
return;
}
if (_nr_pins <= 0)
{
d_printf(DBG_ERR, "error: %s: Gpio_bcm2835_chip configured for 0 pins\n"
" forgot to set 'pins' property in your config?\n",
name());
return;
}
l4_addr_t vbase = res_map_iomem(phys_base, size);
if (!vbase)
{
d_printf(DBG_ERR, "error: %s: cannot map registers for Gpio_bcm2835_chip\n"
" phys=%llx-%llx\n",
name(), phys_base, phys_base + size - 1);
return;
}
d_printf(DBG_DEBUG2, "%s: Gpio_bcm2835: mapped registers to %08lx\n",
name(), vbase);
_regs[0] = new L4drivers::Mmio_register_block<32>(vbase);
_regs[1] = new L4drivers::Mmio_register_block<32>(vbase + 4);
Resource *irq0 = resources()->find("int0");
if (irq0 && irq0->type() == Resource::Irq_res)
_irq_svr[0] = new Gpio_irq_server(irq0->start(),
cxx::min<unsigned>(32, _nr_pins), _regs[0]);
else
d_printf(DBG_WARN, "warning: %s: Gpio_bcm2835 no 'int0' configured\n"
" no IRQs for pins < 32\n", name());
if (_nr_pins <= 32)
return;
Resource *irq2 = resources()->find("int2");
if (irq2 && irq2->type() == Resource::Irq_res)
_irq_svr[1] = new Gpio_irq_server(irq2->start(),
cxx::min<unsigned>(32, _nr_pins - 32), _regs[1]);
else
d_printf(DBG_WARN, "warning: %s: Gpio_bcm2835 no 'int1' configured\n"
" no IRQs for pins 32-%u\n",
name(), (unsigned)_nr_pins - 1);
}
static Hw::Device_factory_t<Gpio_bcm2835_chip> __hw_pf_factory("Gpio_bcm2835_chip");
}

View File

@@ -0,0 +1,150 @@
/*
* (c) 2015 Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "irqs.h"
#include "debug.h"
#include "main.h"
#include "server.h"
#include <l4/sys/cxx/ipc_epiface>
#include <l4/cxx/unique_ptr>
#include <l4/sys/irq>
class Gpio_irq_base : public Io_irq_pin
{
private:
unsigned const _pin;
protected:
unsigned _mode = L4_IRQ_F_NONE;
unsigned _enabled = 0;
public:
explicit Gpio_irq_base(unsigned pin) : _pin(pin) {}
unsigned pin() const { return _pin; }
unsigned mode() const { return _mode; }
bool enabled() const { return _enabled; }
void trigger() { irq()->trigger(); }
int bind(Triggerable const &irq, unsigned) override
{
set_shareable(false);
if (_mode == L4_IRQ_F_NONE)
{
d_printf(DBG_ERR, "error: Gpio_irq_pin: invalid Irq mode.\n"
" Set Irq mode before bind.\n");
throw -L4_EINVAL;
}
Io_irq_pin::bind(irq, 0);
return 1;
}
int unbind(bool deleted) override
{
this->mask();
Io_irq_pin::unbind(deleted);
return 0;
}
};
template<typename IMPL>
class Gpio_irq_base_t : public Gpio_irq_base
{
public:
explicit Gpio_irq_base_t(unsigned pin) : Gpio_irq_base(pin) {}
int mask() override
{
_enabled = false;
static_cast<IMPL*>(this)->do_mask();
return 0;
}
int unmask() override
{
// make sure client has selected an Irq mode
// because GPIO irqs don't have a default mode
if (_mode == L4_IRQ_F_NONE)
d_printf(DBG_WARN, "warning: Gpio_irq_pin: No Irq mode set.\n"
" You will not receive any Irqs.\n");
_enabled = true;
static_cast<IMPL*>(this)->do_unmask();
return 0;
}
int set_mode(unsigned mode) override
{
if (mode == L4_IRQ_F_NONE || _mode == mode)
return _mode;
if (static_cast<IMPL*>(this)->do_set_mode(mode))
_mode = mode;
return _mode;
}
};
class Irq_demux
{
protected:
cxx::unique_ptr<Gpio_irq_base*[]> _pins;
unsigned _hw_irq_num;
public:
Irq_demux(unsigned hw_irq_num, unsigned mode, unsigned npins)
: _hw_irq_num(hw_irq_num)
{
_pins = cxx::make_unique<Gpio_irq_base*[]>(npins);
if (l4_error(system_icu()->icu->set_mode(_hw_irq_num, mode)) < 0)
{
d_printf(DBG_ERR, "error: Irq_demux: failed to set hw irq mode.\n");
return;
}
}
void disable()
{ system_icu()->icu->mask(_hw_irq_num); }
void enable()
{ system_icu()->icu->unmask(_hw_irq_num); }
template<typename PIN, typename ...ARGS>
Gpio_irq_base *get_pin(unsigned pin, ARGS &&...args)
{
if (_pins[pin])
return _pins[pin];
_pins[pin] = new PIN(pin, cxx::forward<ARGS>(args)...);
return _pins[pin];
}
};
template<typename IMPL>
class Irq_demux_t : public L4::Irqep_t<IMPL>, public Irq_demux
{
public:
Irq_demux_t(unsigned hw_irq_num, unsigned mode, unsigned npins)
: Irq_demux(hw_irq_num, mode, npins)
{
registry->register_irq_obj(this);
// FIXME: should test for unmask via ICU (result of bind ==1)
if (l4_error(system_icu()->icu->bind(_hw_irq_num, this->obj_cap())) < 0)
{
d_printf(DBG_ERR, "error: Irq_demux: failed to bind hw irq\n");
return;
}
}
};

View File

@@ -0,0 +1,448 @@
/*
* Copyright (C) 2021-2024 Kernkonzept GmbH.
* Author(s): Adam Lackorzynski <adam.lackorzynski@kernkonzept.com>
* Christian Pötzsch <christian.poetzsch@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "debug.h"
#include "irqs.h"
#include "gpio"
#include "hw_device.h"
#include "hw_irqs.h"
#include "gpio_irq.h"
#include "../scu_imx8qm.h"
#include <cstdio>
#include <l4/util/util.h>
#include <l4/drivers/hw_mmio_register_block>
#include "resource_provider.h"
namespace {
typedef L4drivers::Register_block<32> Chipregs;
enum
{
GPIO_DR = 0x00,
GPIO_GDIR = 0x04,
GPIO_PSR = 0x08,
GPIO_ICR1 = 0x0c,
GPIO_ICR2 = 0x10,
GPIO_IMR = 0x14,
GPIO_ISR = 0x18,
GPIO_EDGE_SEL = 0x1c,
};
class Irq_pin : public Gpio_irq_base_t<Irq_pin>
{
public:
Irq_pin(unsigned vpin, Chipregs const &regs)
: Gpio_irq_base_t<Irq_pin>(vpin), _regs(regs)
{}
void do_mask()
{
_regs[GPIO_IMR].clear(1 << pin());
}
void do_unmask()
{
_regs[GPIO_IMR].set(1 << pin());
}
bool do_set_mode(unsigned mode)
{
_mode = mode;
if (mode == L4_IRQ_F_BOTH_EDGE)
_regs[GPIO_EDGE_SEL].set(1 << pin());
else
{
_regs[GPIO_EDGE_SEL].clear(1 << pin());
unsigned r = pin() >= 16 ? GPIO_ICR2 : GPIO_ICR1;
unsigned p = (pin() % 16) * 2;
switch (mode)
{
case L4_IRQ_F_NEG_EDGE:
_regs[r].modify(3 << p, 3 << p);
break;
case L4_IRQ_F_POS_EDGE:
_regs[r].modify(3 << p, 2 << p);
break;
case L4_IRQ_F_LEVEL_HIGH:
_regs[r].modify(3 << p, 1 << p);
break;
case L4_IRQ_F_LEVEL_LOW:
_regs[r].modify(3 << p, 0 << p);
break;
}
}
return true;
}
private:
Chipregs _regs;
};
class Irq_server : public Irq_demux_t<Irq_server>
{
public:
Irq_server(int irq, unsigned flags, Chipregs const &regs)
: Irq_demux_t<Irq_server>(irq,
(flags & Resource::Irq_type_mask)
/ Resource::Irq_type_base,
32),
_regs(regs)
{
enable();
}
void handle_irq_both()
{
l4_uint32_t isr = _regs[GPIO_ISR] & _regs[GPIO_IMR];
while (isr)
{
unsigned p = ffs(isr) - 1;
Gpio_irq_base *po = _pins[p];
if (!po)
printf("Wrong pin %d got an interrupt\n", p);
else
po->trigger();
isr &= ~(1 << p);
}
}
void handle_irq()
{
handle_irq_both();
}
private:
Chipregs _regs;
};
// This is just forwarding interrupts of the second interrupt to the
// Irq_server handler
class Irq_server_secondary : public Irq_demux_t<Irq_server_secondary>
{
public:
Irq_server_secondary(int irq, unsigned flags, Irq_server *irq_svr)
: Irq_demux_t<Irq_server_secondary>(irq,
(flags & Resource::Irq_type_mask)
/ Resource::Irq_type_base,
0),
_irq_svr(irq_svr)
{
enable();
}
void handle_irq()
{
_irq_svr->handle_irq_both();
}
private:
Irq_server *_irq_svr;
};
class Gpio_imx_chip : public Hw::Gpio_device
{
public:
Gpio_imx_chip()
{
add_cid("gpio");
add_cid("gpio-imx35");
}
void init() override;
void request(unsigned) override {}
void free(unsigned) override {}
void setup(unsigned pin, unsigned mode, int outvalue = 0) override;
int get(unsigned pin) override;
void set(unsigned pin, int value) override;
void config_pad(unsigned pin, unsigned func, unsigned value) override;
void config_get(unsigned pin, unsigned func, unsigned *value) override;
Io_irq_pin *get_irq(unsigned pin) override;
void multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalues) override;
void multi_config_pad(Pin_slice const &mask, unsigned func, unsigned value) override;
void multi_set(Pin_slice const &mask, unsigned data) override
{
unsigned m = mask.mask << mask.offset;
_regs[GPIO_DR].modify(m, m & (data << mask.offset));
}
void config_pull(unsigned pin, unsigned mode) override;
unsigned multi_get(unsigned offset) override
{
return _regs[GPIO_DR] >> offset;
}
int set_power_state(unsigned, bool)
{
return -L4_ENOSYS;
}
unsigned nr_pins() const override
{
return 32;
}
private:
Chipregs _regs;
Irq_server *_irq_svr;
Irq_server_secondary *_irq_svr_secondary = nullptr;
};
void
Gpio_imx_chip::setup(unsigned pin, unsigned mode, int value)
{
printf("%s: setup(%d, mode=%u, value=%d)\n", name(), pin, mode, value);
switch (mode)
{
case Input:
_regs[GPIO_GDIR].clear(1 << pin);
break;
case Output:
_regs[GPIO_GDIR].set(1 << pin);
set(pin, value);
break;
default:
break;
}
}
int
Gpio_imx_chip::get(unsigned pin)
{
int v = (_regs[GPIO_DR] >> pin) & 1;
return v;
}
void
Gpio_imx_chip::set(unsigned pin, int value)
{
_regs[GPIO_DR].modify(1 << pin, (!!value) << pin);
}
void
Gpio_imx_chip::config_pad(unsigned pin, unsigned func, unsigned value)
{
switch (func)
{
case GPIO_DR:
case GPIO_GDIR:
_regs[func].modify(1 << pin, (value & 1) << pin);
break;
case GPIO_PSR:
throw -L4_EINVAL;
case GPIO_ICR1:
case GPIO_ICR2:
if (pin > 15)
throw -L4_EINVAL;
_regs[func].modify(3 << (pin * 2), (value & 3) << (pin * 2));
break;
case GPIO_IMR:
case GPIO_EDGE_SEL:
_regs[func].modify(1 << pin, (value & 1) << pin);
break;
case GPIO_ISR: // w1c -- write 1 to clear
_regs[func] = (value & 1) << pin;
if (pin >= 16 || !_irq_svr_secondary)
_irq_svr->enable();
else
_irq_svr_secondary->enable();
break;
default:
throw -L4_EINVAL;
}
}
static l4_uint32_t bitmask_blow_16_to_32(l4_uint32_t m)
{
unsigned m2 = 0;
for (unsigned i = 0; i < 16; ++i)
if ((1 << i) & m)
m2 |= 3 << (i * 2);
return m2;
}
void Gpio_imx_chip::multi_config_pad(Pin_slice const &mask,
unsigned func, unsigned value)
{
unsigned m = mask.mask << mask.offset;
unsigned v = value << mask.offset;
switch (func)
{
case GPIO_DR:
case GPIO_GDIR:
case GPIO_IMR:
case GPIO_EDGE_SEL:
_regs[func].modify(m, m & v);
break;
case GPIO_PSR:
throw -L4_EINVAL;
case GPIO_ICR1:
if (mask.offset)
throw -L4_EINVAL;
if (unsigned m2 = bitmask_blow_16_to_32(m))
_regs[func].modify(m2, m2 & v);
break;
case GPIO_ICR2:
if (mask.offset)
throw -L4_EINVAL;
if (unsigned m2 = bitmask_blow_16_to_32(m >> 16))
_regs[func].modify(m2, m2 & v);
break;
case GPIO_ISR: // w1c -- write 1 to clear
_regs[func] = m & v;
if (m & v & 0xffff || !_irq_svr_secondary)
_irq_svr->enable();
else if ((m & v) >> 16)
_irq_svr_secondary->enable();
break;
default:
throw -L4_EINVAL;
}
}
void
Gpio_imx_chip::config_get(unsigned pin, unsigned func, unsigned *value)
{
switch (func)
{
case GPIO_DR:
case GPIO_GDIR:
case GPIO_PSR:
*value = (_regs[func] >> pin) & 1;
break;
case GPIO_ICR1:
case GPIO_ICR2:
if (pin > 15)
throw -L4_EINVAL;
*value = _regs[func] & (3 << (pin * 2));
break;
case GPIO_IMR:
case GPIO_ISR:
case GPIO_EDGE_SEL:
*value = (_regs[func] >> pin) & 1;
break;
default:
throw -L4_EINVAL;
}
}
void Gpio_imx_chip::multi_setup(Pin_slice const &mask,
unsigned mode, unsigned outvalues)
{
d_printf(DBG_ERR,
"Not implemented: multi_setup(mask={0x%x,0x%08x}, mode=0x%x, outvalues=0x%x)\n",
mask.offset, mask.mask, mode, outvalues);
}
void
Gpio_imx_chip::config_pull(unsigned, unsigned)
{
// No such possibility with the GPIO
throw -L4_EINVAL;
}
Io_irq_pin *
Gpio_imx_chip::get_irq(unsigned pin)
{
if (pin >= nr_pins())
throw -L4_EINVAL;
Io_irq_pin *i = _irq_svr->get_pin<Irq_pin>(pin, _regs);
if (i)
i->set_mode(L4_IRQ_F_LEVEL_HIGH);
return i;
}
void Gpio_imx_chip::init()
{
Gpio_device::init();
d_printf(DBG_INFO, "%s: init() %p\n", name(), this);
Resource *regs = resources()->find("regs");
// TODO: also look for reg0 if 'regs' not found -- or look for some
// Mmio_res independent of name
if (!regs || regs->type() != Resource::Mmio_res)
{
d_printf(DBG_ERR, "error: %s: no base address set\n"
" missing or wrong 'regs' resource\n", name());
throw "gpio-imx init error";
}
l4_addr_t phys_base = regs->start();
l4_addr_t size = regs->size();
if (size < 0x20 || size > 0x4000)
{
d_printf(DBG_ERR, "error: %s: invalid mmio size (%lx).\n", name(), size);
throw "gpio-imx init error";
}
l4_addr_t vbase = res_map_iomem(phys_base, size);
if (!vbase)
{
d_printf(DBG_ERR, "error: %s: cannot map registers: phys=%lx-%lx",
name(), phys_base, phys_base + size - 1);
throw "gpio-imx init error";
}
d_printf(DBG_ERR, "%s: mapped %lx registers to %08lx\n",
name(), phys_base, vbase);
_regs = new L4drivers::Mmio_register_block<32>(vbase);
_regs[GPIO_IMR] = 0;
_regs[GPIO_ISR] = ~0;
Resource *irq = resources()->find("irq0");
// TODO: look for two IRQs independent of some name
if (!irq || irq->type() != Resource::Irq_res)
d_printf(DBG_WARN, "warning: %s: no 'irq0' configured\n"
" no IRQs available for pins 0-15\n", name());
_irq_svr = new Irq_server(irq->start(), irq->flags(), _regs);
irq = resources()->find("irq1");
if (!irq || irq->type() != Resource::Irq_res)
d_printf(DBG_WARN, "warning: %s: no 'irq1' configured\n"
" no IRQs available for pins 16-31\n", name());
else
_irq_svr_secondary = new Irq_server_secondary(irq->start(), irq->flags(),
_irq_svr);
// TODO use irq-type from resource in get_irq
d_printf(DBG_INFO, "gpio-imx driver ready\n");
}
class Gpio_imx8qm_chip : public Hw::Scu_device<Gpio_imx_chip>
{};
static Hw::Device_factory_t<Gpio_imx_chip> __hw_pf_factory("Gpio_imx_chip");
static Hw::Device_factory_t<Gpio_imx8qm_chip> __hw_pf_factory1("Gpio_imx8qm_chip");
}

View File

@@ -0,0 +1,699 @@
#include <l4/cxx/unique_ptr>
#include <l4/drivers/hw_mmio_register_block>
#include "main.h"
#include "irqs.h"
#include "debug.h"
#include "gpio"
#include "hw_device.h"
#include "hw_irqs.h"
#include "server.h"
#include "gpio_irq.h"
namespace {
struct Omap_gpio_base
{
enum : unsigned
{
Revision = 0x000, // GPIO_REVISION
Sysconfig = 0x010 // GPIO_SYSCONFIG
};
enum Pull_selection : unsigned
{
None = 0,
Down = 0,
Enable = 1,
Up = 2,
};
};
struct Omap3_gpio : Omap_gpio_base
{
// Multiplexing modes, Omap3 TRM, table 7-2
enum Pad_mode : unsigned
{
Gpio = 4,
Safe = 7
};
// Gpio chip register offsets, Omap3 TRM, table 24-7
// register name written as comment behind enum member definition
enum : unsigned
{
Irq_status = 0x018, // GPIO_IRQSTATUS1
Irq_enable = 0x01c, // GPIO_IRQENABLE1
Wkup_enable = 0x020, // GPIO_WAKEUPENABLE
Ctrl = 0x030, // GPIO_CTRL
Direction = 0x034, // GPIO_OE
Data_in = 0x038, // GPIO_DATAIN
Data_out = 0x03c, // GPIO_DATAOUT
Level_detect_low = 0x040, // GPIO_LEVELDETECT0
Level_detect_high = 0x044, // GPIO_LEVELDETECT1
Rising_detect = 0x048, // GPIO_RISINGDETECT
Falling_detect = 0x04c, // GPIO_FALLINGDETECT
Debounce_enable = 0x050, // GPIO_DEBOUNCENABLE
Debounce_time = 0x054, // GPIO_DEBOUNCINGTIME
Clr_irq_enable = 0x060, // GPIO_CLEARIRQENABLE1
Set_irq_enable = 0x064, // GPIO_SETIRQENABLE1
Clr_data_out = 0x090, // GPIO_CLRDATAOUT
Set_data_out = 0x094, // GPIO_SETDATAOUT
};
};
struct Omap4_gpio : Omap_gpio_base
{
// Multiplexing modes, Omap4 TRM, table 18-6
enum Pad_mode : unsigned
{
Gpio = 3,
Safe = 7
};
// Gpio chip register offsets, Omap4 TRM, table 25-18
// register name written as comment behind enum member definition
enum : unsigned
{
Irq_status = 0x02c, // GPIO_IRQSTATUS_0
Irq_enable = 0x034, // GPIO_IRQSTATUS_SET_0
Wkup_enable = 0x120, // GPIO_WAKEUPENABLE
Ctrl = 0x130, // GPIO_CTRL
Direction = 0x134, // GPIO_OE
Data_in = 0x138, // GPIO_DATAIN
Data_out = 0x13c, // GPIO_DATAOUT
Level_detect_low = 0x140, // GPIO_LEVELDETECT0
Level_detect_high = 0x144, // GPIO_LEVELDETECT1
Rising_detect = 0x148, // GPIO_RISINGDETECT
Falling_detect = 0x14c, // GPIO_FALLINGDETECT
Debounce_enable = 0x150, // GPIO_DEBOUNCENABLE
Debounce_time = 0x154, // GPIO_DEBOUNCINGTIME
Clr_irq_enable = 0x03c, // GPIO_IRQSTATUS_CLR_0
Set_irq_enable = 0x034, // GPIO_IRQSTATUS_SET_0
Clr_data_out = 0x190, // GPIO_CLRDATAOUT
Set_data_out = 0x194, // GPIO_SETDATAOUT
};
};
struct Omap5_gpio : Omap_gpio_base
{
// Multiplexing modes, Omap5 TRM, table 18-6
enum Pad_mode : unsigned
{
Gpio = 6,
Safe = 7
};
// Gpio chip register offsets, Omap5 TRM, table 25-18
// register name written as comment behind enum member definition
enum : unsigned
{
Irq_status = 0x02c, // GPIO_IRQSTATUS_0
Irq_enable = 0x034, // GPIO_IRQSTATUS_SET_0
Wkup_enable = 0x120, // GPIO_WAKEUPENABLE
Ctrl = 0x130, // GPIO_CTRL
Direction = 0x134, // GPIO_OE
Data_in = 0x138, // GPIO_DATAIN
Data_out = 0x13c, // GPIO_DATAOUT
Level_detect_low = 0x140, // GPIO_LEVELDETECT0
Level_detect_high = 0x144, // GPIO_LEVELDETECT1
Rising_detect = 0x148, // GPIO_RISINGDETECT
Falling_detect = 0x14c, // GPIO_FALLINGDETECT
Debounce_enable = 0x150, // GPIO_DEBOUNCENABLE
Debounce_time = 0x154, // GPIO_DEBOUNCINGTIME
Clr_irq_enable = 0x03c, // GPIO_IRQSTATUS_CLR_0
Set_irq_enable = 0x034, // GPIO_IRQSTATUS_SET_0
Clr_data_out = 0x190, // GPIO_CLRDATAOUT
Set_data_out = 0x194, // GPIO_SETDATAOUT
};
};
class Scm_omap : public Hw::Device
{
L4drivers::Register_block<16> _regs;
public:
Scm_omap() { add_cid("scm-omap"); }
void init() override
{
Hw::Device::init();
Resource *regs = resources()->find("regs");
if (!regs || regs->type() != Resource::Mmio_res)
{
d_printf(DBG_ERR, "error: %s: no base address set for device: Scm_omap\n"
" missing or wrong 'regs' resource\n"
" the SCM will not work at all!\n", name());
return;
}
l4_uint64_t phys_base = regs->start();
l4_uint64_t size = regs->size();
if (size < 0x100 || size > (1 << 12))
{
d_printf(DBG_ERR, "error: %s: invalid mmio size (%llx) for device: Scm_omap\n"
" the chip will not work at all!\n", name(), size);
return;
}
l4_addr_t vbase = res_map_iomem(phys_base, size);
if (!vbase)
{
d_printf(DBG_ERR, "error: %s: cannot map registers for Scm_omap\n"
" phys=%llx-%llx",
name(), phys_base, phys_base + size - 1);
return;
}
d_printf(DBG_DEBUG2, "%s: Scm_omap: mapped registers to %08lx\n",
name(), vbase);
_regs = new L4drivers::Mmio_register_block<16>(vbase);
}
void set_mode(l4_int32_t offset, unsigned mode)
{
if (offset < 0)
throw -L4_EINVAL;
_regs[offset].modify(0x7, mode & 0x7);
}
void set_pull(l4_int32_t offset, unsigned value)
{
if (offset < 0)
throw -L4_EINVAL;
// bits 3 (enable) and 4 (type) are for pull mode
// also enable bidirectional mode in bit 8
_regs[offset].modify(0x118, ((value & 0x3) << 3) | 0x100);
}
};
static Hw::Device_factory_t<Scm_omap> __hw_scm_factory("Scm_omap");
class Scm_property : public Property
{
private:
enum : int { Num_offsets = 32 };
Scm_omap *_scm;
l4_int32_t _offsets[Num_offsets];
public:
Scm_property()
{
// fill offsets with -1 to be save if user supplies less than 32 values
std::fill_n(_offsets, Num_offsets, -1);
}
int set(int, std::string const &) override { return -EINVAL; }
int set(int k, l4_int64_t i) override
{
// check for correct index, lua tables start with index 1
// and the first entry is defined to be the device reference
// we expect 32 values
if (k > (Num_offsets + 1) || k < 2)
return -EINVAL;
// assume the mmio size of the Scm to be <= 0x1000
if (i >= 0x1000)
return -EINVAL;
// do not forget to reduce the index, it is offset by 2
_offsets[k - 2] = i;
return 0;
}
/* the device reference has to be the first entry in the table
* note: lua tables start at index 1 */
int set(int k, Generic_device *d) override
{
if (k != 1)
return -EINVAL;
_scm = dynamic_cast<Scm_omap *>(d);
if (!_scm)
return -EINVAL;
return 0;
}
int set(int, Resource *) override { return -EINVAL; }
Scm_omap *dev() { return _scm; }
l4_int32_t offset(int index)
{
if (index < 0 || index >= Num_offsets)
return -ERANGE;
return _offsets[index];
}
};
template<typename IMPL>
class Omap_irq_base : public Gpio_irq_base_t<IMPL>
{
protected:
L4drivers::Register_block<32> _regs;
Omap_irq_base(unsigned pin, L4drivers::Register_block<32> const &regs)
: Gpio_irq_base_t<IMPL>(pin), _regs(regs)
{}
void write_reg_pin(unsigned reg, int value)
{
if (value & 1)
_regs[reg].set(1 << this->pin());
else
_regs[reg].clear(1 << this->pin());
}
};
template<class REGS>
class Gpio_irq_pin_t : public Omap_irq_base<Gpio_irq_pin_t<REGS>>
{
public:
Gpio_irq_pin_t(unsigned pin, L4drivers::Register_block<32> const &regs)
: Omap_irq_base<Gpio_irq_pin_t<REGS>>(pin, regs)
{}
void do_mask()
{
this->_regs[REGS::Clr_irq_enable] = 1 << this->pin();
}
void do_unmask()
{
this->_regs[REGS::Set_irq_enable] = 1 << this->pin();
}
bool do_set_mode(unsigned mode)
{
int values[4] = {0, 0, 0, 0};
switch(mode)
{
case L4_IRQ_F_LEVEL_HIGH:
values[3] = 1;
break;
case L4_IRQ_F_LEVEL_LOW:
values[2] = 1;
break;
case L4_IRQ_F_POS_EDGE:
values[0] = 1;
break;
case L4_IRQ_F_NEG_EDGE:
values[1] = 1;
break;
case L4_IRQ_F_BOTH_EDGE:
values[0] = 1;
values[1] = 1;
break;
default:
return false;
}
// this operation touches multiple mmio registers and is thus
// not atomic, that's why we first mask the IRQ and if it was
// enabled we unmask it after we have changed the mode
if (this->enabled())
do_mask();
this->_regs[REGS::Direction].set(1 << this->pin());
this->write_reg_pin(REGS::Rising_detect, values[0]);
this->write_reg_pin(REGS::Falling_detect, values[1]);
this->write_reg_pin(REGS::Level_detect_low, values[2]);
this->write_reg_pin(REGS::Level_detect_high, values[3]);
if (this->enabled())
do_unmask();
return true;
}
int clear() override
{
l4_uint32_t status = this->_regs[REGS::Irq_status] & (1UL << this->pin());
if (status)
this->_regs[REGS::Irq_status] = status;
return Io_irq_pin::clear() + (status >> this->pin());
}
};
template<class REGS>
class Gpio_irq_server_t : public Irq_demux_t<Gpio_irq_server_t<REGS>>
{
friend class Gpio_irq_pin_t<REGS>;
typedef Gpio_irq_pin_t<REGS> Gpio_irq_pin;
typedef Irq_demux_t<Gpio_irq_server_t<REGS>> Base;
L4drivers::Register_block<32> _regs;
public:
Gpio_irq_server_t(int irq, unsigned pins,
L4drivers::Register_block<32> const &regs)
: Base(irq, 0, pins), _regs(regs)
{
this->enable();
}
void handle_irq()
{
// I think it is sufficient to read irqstatus as we only use the first
// hw irq per chip
unsigned status = _regs[REGS::Irq_status];
unsigned reset = status;
if (!status) // usually never happens
{
this->enable();
return;
}
l4_uint32_t mask_irqs = 0, pin_bit = 1;
for (unsigned pin = 0; status; ++pin, status >>= 1, pin_bit <<= 1)
{
if (!(status & pin))
continue;
Gpio_irq_base *p = this->_pins[pin];
if (p)
{
switch (p->mode())
{
case L4_IRQ_F_LEVEL_HIGH: mask_irqs |= pin_bit; break;
case L4_IRQ_F_LEVEL_LOW: mask_irqs |= pin_bit; break;
}
p->trigger();
}
else
// this is strange as this would mean an unassigned IRQ is unmasked
mask_irqs |= pin_bit;
}
// do the mask for level triggered IRQs
if (mask_irqs)
_regs[REGS::Clr_irq_enable] = mask_irqs;
_regs[REGS::Irq_status] = reset;
this->enable();
}
};
template<class REGS>
class Gpio_omap_chip : public Hw::Gpio_device
{
private:
typedef Gpio_irq_server_t<REGS> Gpio_irq_server;
L4drivers::Register_block<32> _regs;
unsigned _nr_pins;
Gpio_irq_server *_irq_svr;
Scm_property _scm;
static l4_uint32_t _pin_bit(unsigned pin)
{ return 1 << (pin & 31); }
static unsigned _pin_shift(unsigned pin)
{ return pin % 32; }
unsigned _reg_offset_check(unsigned pin_offset) const
{
switch (pin_offset)
{
case 0:
return 0;
default:
throw -L4_EINVAL;
}
}
void config(unsigned pin, unsigned func)
{
if (_scm.dev())
_scm.dev()->set_mode(_scm.offset(pin), func);
}
public:
Gpio_omap_chip() : _nr_pins(32), _irq_svr(0)
{
add_cid("gpio");
add_cid("gpio-omap");
register_property("scm", &_scm);
}
unsigned nr_pins() const override { return _nr_pins; }
int get(unsigned pin) override
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
return (_regs[REGS::Data_out] >> _pin_shift(pin)) & 1;
}
void set(unsigned pin, int value) override
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
unsigned reg_set = value ? REGS::Set_data_out : REGS::Clr_data_out;
_regs[reg_set] = _pin_bit(pin);
}
unsigned multi_get(unsigned offset) override
{
_reg_offset_check(offset);
return _regs[REGS::Data_out];
}
void multi_set(Pin_slice const &mask, unsigned data) override
{
_reg_offset_check(mask.offset);
if (mask.mask & data)
_regs[REGS::Set_data_out] = (mask.mask & data);
if (mask.mask & ~data)
_regs[REGS::Clr_data_out] = (mask.mask & ~data);
}
void setup(unsigned pin, unsigned mode, int value) override
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
switch (mode)
{
case Input:
_regs[REGS::Direction].set(_pin_bit(pin));
_scm.dev()->set_mode(_scm.offset(pin), REGS::Pad_mode::Gpio);
return;
case Output:
_regs[REGS::Direction].clear(_pin_bit(pin));
_scm.dev()->set_mode(_scm.offset(pin), REGS::Pad_mode::Gpio);
set(pin, value);
return;
case Irq:
d_printf(DBG_WARN, "warning: Gpio_omap_chip: trying to setup pin as Irq\n"
" This mode is not supported. Setting mode to input\n"
" Use to_irq() to configure Irq\n");
_regs[REGS::Direction].set(_pin_bit(pin));
return;
default:
// although setup is part of the generic Gpio API we allow
// hardware specific modes as well
mode &= 0x7;
break;
}
config(pin, mode);
}
void config_pad(unsigned pin, unsigned reg, unsigned value) override
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
switch (reg)
{
case REGS::Irq_status: // hmm, allow user to reset the irq status?
case REGS::Irq_enable: // hmm, allow user to enable irq this way?
case REGS::Wkup_enable:
case REGS::Direction:
case REGS::Data_out:
case REGS::Level_detect_low: // hmm, allow user to configure IRQ this way?
case REGS::Level_detect_high: // hmm, allow user to configure IRQ this way?
case REGS::Rising_detect: // hmm, allow user to configure IRQ this way?
case REGS::Falling_detect: // hmm, allow user to configure IRQ this way?
case REGS::Debounce_enable:
case REGS::Clr_irq_enable: // hmm, allow user to disable IRQ this way?
case REGS::Clr_data_out:
case REGS::Set_data_out:
_regs[reg].modify(_pin_bit(pin), value ? _pin_bit(pin) : 0);
break;
default:
// cannot allow the following registers, they have security implications
// Sysconfig, Ctrl, Debounce_time
// the following registers are read-only
// Revision, Sysstatus (also security), Data_in
throw -L4_EINVAL;
}
}
void config_get(unsigned pin, unsigned reg, unsigned *value) override
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
switch (reg)
{
case REGS::Revision:
*value = _regs[REGS::Revision] & 0xff;
break;
case REGS::Sysconfig:
*value = _regs[REGS::Sysconfig] & 0x1f;
break;
case REGS::Ctrl:
*value = _regs[REGS::Ctrl] & 0x7;
break;
case REGS::Debounce_time:
*value = _regs[REGS::Debounce_time] & 0xff;
break;
case REGS::Irq_status:
case REGS::Irq_enable:
case REGS::Wkup_enable:
case REGS::Direction:
case REGS::Data_in:
case REGS::Data_out:
case REGS::Level_detect_low:
case REGS::Level_detect_high:
case REGS::Rising_detect:
case REGS::Falling_detect:
case REGS::Debounce_enable:
case REGS::Clr_irq_enable:
case REGS::Clr_data_out:
case REGS::Set_data_out:
*value = (_regs[reg] >> _pin_shift(pin)) & 1;
break;
default:
throw -L4_EINVAL;
}
}
void config_pull(unsigned pin, unsigned mode) override
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
switch (mode)
{
case Pull_none:
mode = REGS::Pull_selection::None;
break;
case Pull_up:
mode = REGS::Pull_selection::Up | REGS::Pull_selection::Enable;
break;
case Pull_down:
mode = REGS::Pull_selection::Down | REGS::Pull_selection::Enable;
break;
default:
d_printf(DBG_WARN, "warning: %s: invalid PUD mode for pin %u. "
"Ignoring.\n", name(), pin);
throw -L4_EINVAL;
}
if (_scm.dev())
_scm.dev()->set_pull(_scm.offset(pin), mode);
}
Io_irq_pin *get_irq(unsigned pin) override
{
if (pin >= _nr_pins)
throw -L4_EINVAL;
if (!_irq_svr)
return nullptr;
return _irq_svr->template get_pin<Gpio_irq_pin_t<REGS>>(pin, _regs);
}
void multi_config_pad(Pin_slice const &mask, unsigned func,
unsigned value) override
{ generic_multi_config_pad(mask, func, value); }
void multi_setup(Pin_slice const &mask, unsigned mode,
unsigned outvalues) override
{ generic_multi_setup(mask, mode, outvalues); }
void init() override
{
Gpio_device::init();
Resource *regs = resources()->find("regs");
if (!regs || regs->type() != Resource::Mmio_res)
{
d_printf(DBG_ERR, "error: %s: no base address set for device: Gpio_omap_chip\n"
" missing or wrong 'regs' resource\n"
" the chip will not work at all!\n", name());
return;
}
l4_uint64_t phys_base = regs->start();
l4_uint64_t size = regs->size();
if (size < 0x100 || size > (1 << 12))
{
d_printf(DBG_ERR, "error: %s: invalid mmio size (%llx) for device: Gpio_omap_chip\n"
" the chip will not work at all!\n",
name(), size);
return;
}
l4_addr_t vbase = res_map_iomem(phys_base, size);
if (!vbase)
{
d_printf(DBG_ERR, "error: %s: cannot map registers for Gpio_omap_chip\n"
" phys=%llx-%llx",
name(), phys_base, phys_base + size - 1);
return;
}
d_printf(DBG_DEBUG2, "%s: Gpio_omap_chip: mapped registers to %08lx\n",
name(), vbase);
_regs = new L4drivers::Mmio_register_block<32>(vbase);
Resource *irq = resources()->find("irq");
if (irq && irq->type() == Resource::Irq_res)
_irq_svr = new Gpio_irq_server(irq->start(), _nr_pins, _regs);
else
d_printf(DBG_WARN, "warning: %s: Gpio_omap_chip no irq configured\n",
name());
if (!_scm.dev())
d_printf(DBG_WARN, "warning: %s: no Scm for device: Gpio_omap_chip\n"
" 'scm' property in device tree not set?\n"
" Setting pin and PUD modes will be disabled\n",
name());
}
};
static Hw::Device_factory_t<Gpio_omap_chip<Omap3_gpio>>
__hw_gpio_omap35x_factory("Gpio_omap35x_chip");
static Hw::Device_factory_t<Gpio_omap_chip<Omap4_gpio>>
__hw_gpio_omap44x_factory("Gpio_omap44x_chip");
static Hw::Device_factory_t<Gpio_omap_chip<Omap5_gpio>>
__hw_gpio_omap54x_factory("Gpio_omap54x_chip");
}

View File

@@ -0,0 +1,366 @@
/*
* Copyright (C) 2021-2022 Stephan Gerhold <stephan@gerhold.net>
* Copyright (C) 2022-2023 Kernkonzept GmbH.
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* Driver for Qualcomm "Top-Level Mode Multiplexer" (TLMM) GPIO controller.
*
* \code{.lua}
* TLMM = Hw.Gpio_qcom_chip(function()
* compatible = {"qcom,msm8916-pinctrl", "qcom,tlmm"};
* Resource.reg0 = Res.mmio(0x01000000, 0x010fffff);
* Resource.irq0 = Res.irq(32+208, Io.Resource.Irq_type_level_high);
* Property.ngpios = 122; -- Total number of GPIOs
* Property.target_proc = 0x4; -- Target processor for IRQs
* Property.reg_gpio_size = 0x1000; -- Size of a single GPIO register group
* end);
* \endcode
*/
#include <l4/drivers/hw_mmio_register_block>
#include "debug.h"
#include "gpio"
#include "hw_device.h"
#include "irqs.h"
#include "server.h"
#include "gpio_irq.h"
namespace {
enum Regs
{
// Each GPIO is in a separate register group (size configured in _gpio_size)
TLMM_GPIO_CFG = 0x0,
TLMM_GPIO_IN_OUT = 0x4,
TLMM_GPIO_INTR_CFG = 0x8,
TLMM_GPIO_INTR_STATUS = 0xc,
};
class Gpio_irq_pin : public Gpio_irq_base_t<Gpio_irq_pin>
{
private:
enum Intr_cfg_regs {
INTR_ENABLE = 0x1 << 0,
INTR_POL_SHIFT = 1,
INTR_POL_ACTIVE_LOW = 0x0 << INTR_POL_SHIFT,
INTR_POL_ACTIVE_HIGH = 0x1 << INTR_POL_SHIFT,
INTR_DECT_SHIFT = 2,
INTR_DECT_LEVEL = 0x0 << INTR_DECT_SHIFT,
INTR_DECT_POS_EDGE = 0x1 << INTR_DECT_SHIFT,
INTR_DECT_NEG_EDGE = 0x2 << INTR_DECT_SHIFT,
INTR_DECT_BOTH_EDGE = 0x3 << INTR_DECT_SHIFT,
INTR_RAW_STATUS_EN = 0x1 << 4,
INTR_TARGET_PROC_SHIFT = 5,
INTR_TARGET_PROC_MASK = 0b111,
};
L4drivers::Register_block<32> _regs;
unsigned int _base;
unsigned int _target_proc;
void enable_intr(unsigned val)
{
val |= INTR_ENABLE | INTR_RAW_STATUS_EN | _target_proc;
_regs[_base + TLMM_GPIO_INTR_CFG] = val;
// Some interrupts seem to trigger once after enabling them
_regs[_base + TLMM_GPIO_INTR_STATUS] = 0;
}
public:
Gpio_irq_pin(unsigned pin, L4drivers::Register_block<32> const &regs,
unsigned base, unsigned target_proc)
: Gpio_irq_base_t<Gpio_irq_pin>(pin), _regs(regs), _base(base),
_target_proc((target_proc & INTR_TARGET_PROC_MASK) << INTR_TARGET_PROC_SHIFT)
{}
void do_mask()
{
_regs[_base + TLMM_GPIO_INTR_CFG].clear(INTR_ENABLE | INTR_RAW_STATUS_EN);
}
void do_unmask()
{
switch (mode())
{
case L4_IRQ_F_LEVEL_HIGH:
enable_intr(INTR_DECT_LEVEL | INTR_POL_ACTIVE_HIGH);
return;
case L4_IRQ_F_LEVEL_LOW:
enable_intr(INTR_DECT_LEVEL | INTR_POL_ACTIVE_LOW);
return;
case L4_IRQ_F_POS_EDGE:
enable_intr(INTR_DECT_POS_EDGE | INTR_POL_ACTIVE_HIGH);
return;
case L4_IRQ_F_NEG_EDGE:
enable_intr(INTR_DECT_NEG_EDGE | INTR_POL_ACTIVE_HIGH);
return;
case L4_IRQ_F_BOTH_EDGE:
enable_intr(INTR_DECT_BOTH_EDGE | INTR_POL_ACTIVE_HIGH);
return;
}
}
bool do_set_mode(unsigned mode)
{
_mode = mode;
if (enabled())
do_unmask();
return true;
}
bool handle_interrupt(bool mask_level)
{
// Check if the interupt is active
if (!_regs[_base + TLMM_GPIO_INTR_STATUS])
return false;
// Mask level-triggered IRQs to avoid triggering them again immediately
if (mask_level && mode() & L4_IRQ_F_LEVEL)
do_mask();
// Clear the interrupt
_regs[_base + TLMM_GPIO_INTR_STATUS] = 0;
return true;
}
int clear() override
{
return Io_irq_pin::clear() + handle_interrupt(false);
}
};
class Gpio_irq_server : public Irq_demux_t<Gpio_irq_server>
{
private:
unsigned _npins;
public:
Gpio_irq_server(unsigned irq, unsigned npins)
: Irq_demux_t<Gpio_irq_server>(irq, 0, npins), _npins(npins)
{ enable(); }
void handle_irq()
{
for (unsigned pin = 0; pin < _npins; pin++)
{
if (!_pins[pin] || !_pins[pin]->enabled())
continue;
if (dynamic_cast<Gpio_irq_pin*>(_pins[pin])->handle_interrupt(true))
_pins[pin]->trigger();
}
enable();
}
};
class Gpio_qcom_chip : public Hw::Gpio_device
{
private:
enum Gpio_cfg_regs
{
GPIO_PULL_SHIFT = 0,
GPIO_PULL_NONE = 0x0 << GPIO_PULL_SHIFT,
GPIO_PULL_DOWN = 0x1 << GPIO_PULL_SHIFT,
GPIO_PULL_KEEP = 0x2 << GPIO_PULL_SHIFT,
GPIO_PULL_UP = 0x3 << GPIO_PULL_SHIFT,
GPIO_PULL_MASK = 0x3 << GPIO_PULL_SHIFT,
GPIO_FUNC_SHIFT = 2,
GPIO_FUNC_MASK = 0xf << GPIO_FUNC_SHIFT,
GPIO_OE = 1 << 9,
};
enum Gpio_in_out_regs
{
GPIO_IN = 1 << 0,
GPIO_OUT = 1 << 1,
};
L4drivers::Register_block<32> _regs;
Gpio_irq_server *_irq_svr;
Int_property _ngpios;
Int_property _target_proc;
Int_property _reg_gpio_size;
unsigned pin_reg(unsigned reg, unsigned pin)
{ return reg + (pin * _reg_gpio_size); }
public:
Gpio_qcom_chip() : _target_proc(~0)
{
register_property("ngpios", &_ngpios);
register_property("target_proc", &_target_proc);
register_property("reg_gpio_size", &_reg_gpio_size);
}
unsigned nr_pins() const override { return _ngpios; }
void setup(unsigned pin, unsigned mode, int value = 0) override;
void config_pull(unsigned pin, unsigned mode) override;
int get(unsigned pin) override;
void set(unsigned pin, int value) override;
Io_irq_pin *get_irq(unsigned pin) override;
void init() override;
void multi_setup(Pin_slice const &mask, unsigned mode,
unsigned outvalues = 0) override
{ generic_multi_setup(mask, mode, outvalues); }
void multi_config_pad(Pin_slice const &mask, unsigned func,
unsigned value) override
{ generic_multi_config_pad(mask, func, value); }
void multi_set(Pin_slice const &mask, unsigned data) override
{ generic_multi_set(mask, data); }
unsigned multi_get(unsigned offset) override
{ return generic_multi_get(offset); }
// Platform-specific configuration is not supported at the moment
void config_get(unsigned, unsigned, unsigned *) override
{ throw -L4_EINVAL; }
void config_pad(unsigned, unsigned, unsigned) override
{ throw -L4_EINVAL; }
};
void
Gpio_qcom_chip::setup(unsigned pin, unsigned mode, int value)
{
if (pin >= nr_pins())
throw -L4_EINVAL;
switch (mode)
{
case Input:
case Irq: // No special IRQ mode
mode = 0;
break;
case Output:
mode = GPIO_OE;
break;
default:
// although setup is part of the generic Gpio API
// we allow hardware specific modes as well
mode = (mode << GPIO_FUNC_SHIFT) & GPIO_FUNC_MASK;
break;
}
_regs[pin_reg(TLMM_GPIO_CFG, pin)].modify(GPIO_OE | GPIO_FUNC_MASK, mode);
if (mode == GPIO_OE)
set(pin, value);
}
void
Gpio_qcom_chip::config_pull(unsigned pin, unsigned mode)
{
if (pin >= nr_pins())
throw -L4_EINVAL;
switch (mode)
{
case Pull_none:
mode = GPIO_PULL_NONE;
break;
case Pull_up:
mode = GPIO_PULL_UP;
break;
case Pull_down:
mode = GPIO_PULL_DOWN;
break;
default:
d_printf(DBG_WARN, "warning: %s: invalid PUD mode for pin %u. "
"Ignoring.\n", name(), pin);
throw -L4_EINVAL;
}
_regs[pin_reg(TLMM_GPIO_CFG, pin)].modify(GPIO_PULL_MASK, mode);
}
int
Gpio_qcom_chip::get(unsigned pin)
{
if (pin >= nr_pins())
throw -L4_EINVAL;
return _regs[pin_reg(TLMM_GPIO_IN_OUT, pin)] & GPIO_IN;
}
void
Gpio_qcom_chip::set(unsigned pin, int value)
{
if (pin >= nr_pins())
throw -L4_EINVAL;
_regs[pin_reg(TLMM_GPIO_IN_OUT, pin)] = value ? GPIO_OUT : 0;
}
Io_irq_pin *
Gpio_qcom_chip::get_irq(unsigned pin)
{
if (pin >= nr_pins())
throw -L4_EINVAL;
if (!_irq_svr)
return nullptr;
return _irq_svr->get_pin<Gpio_irq_pin>(pin, _regs, pin_reg(0, pin),
_target_proc);
}
void
Gpio_qcom_chip::init()
{
Gpio_device::init();
if ( assert_property(&_ngpios, "ngpios", 0)
|| assert_property(&_target_proc, "target_proc", ~0)
|| assert_property(&_reg_gpio_size, "reg_gpio_size", 0))
return;
Resource *regs = resources()->find("reg0");
if (!regs || regs->type() != Resource::Mmio_res)
{
d_printf(DBG_ERR, "error: %s: no base address set for device: Gpio_qcom_chip\n"
" missing or wrong 'regs' resource\n"
" the chip will not work at all!\n", name());
return;
}
l4_addr_t phys_base = regs->start();
l4_addr_t size = regs->size();
auto end = pin_reg(0, nr_pins());
if (size < end)
{
d_printf(DBG_ERR, "error: %s: invalid mmio size (%lx) for device: Gpio_qcom_chip\n"
" the chip will not work at all!\n", name(), size);
return;
}
l4_addr_t vbase = res_map_iomem(phys_base, size);
if (!vbase)
{
d_printf(DBG_ERR, "error: %s: cannot map registers for Gpio_qcom_chip\n"
" phys=%lx-%lx\n",
name(), phys_base, phys_base + size - 1);
return;
}
d_printf(DBG_DEBUG2, "%s: Gpio_qcom_chip: mapped registers to %08lx\n",
name(), vbase);
_regs = new L4drivers::Mmio_register_block<32>(vbase);
Resource *irq = resources()->find("irq0");
if (irq && irq->type() == Resource::Irq_res)
_irq_svr = new Gpio_irq_server(irq->start(), nr_pins());
else
d_printf(DBG_WARN, "warning: %s: Gpio_qcom_chip no irq configured\n", name());
}
static Hw::Device_factory_t<Gpio_qcom_chip> __hw_gpio_qcom_factory("Gpio_qcom_chip");
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Philipp Eppelt <philipp.eppelt@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "debug.h"
#include "hw_device.h"
#include "irqs.h"
#include "iomuxc.h"
namespace {
class Hw_i2c_ctrl : public Hw::Device
{
public:
void init() override;
};
void
Hw_i2c_ctrl::init()
{
set_name_if_empty("Hw_i2c_ctrl");
}
class I2c_imx8mp_ctrl : public Hw::Iomux_device<Hw_i2c_ctrl>
{
};
static Hw::Device_factory_t<I2c_imx8mp_ctrl> __hw_pf_factory("I2c_imx8mp_ctrl");
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Philipp Eppelt <philipp.eppelt@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "iomuxc.h"
namespace Hw {
void
Iomuxc_imx8mp::init()
{
if (_initialized)
return;
_initialized = true;
Hw::Device::init();
set_name_if_empty("Iomuxc_imx8mp");
Resource *reg = resources()->find("reg0");
if (!reg || reg->type() != Resource::Mmio_res)
{
d_printf(DBG_ERR, "error: %s: no base address set\n"
" missing or wrong 'reg0' resource\n", name());
throw "Iomuxc_imx8mp: missing or wrong reg0 resource.";
}
l4_addr_t phys_base = reg->start();
l4_addr_t size = reg->size();
if ((size & (size - 1)) != 0)
{
d_printf(DBG_ERR, "error: %s: unaligned MMIO resource size (0x%lx)\n",
name(), size);
throw "Iomuxc_imx8mp: unaligned MMIO resource size.";
}
l4_addr_t vbase = res_map_iomem(phys_base, size);
if (!vbase)
{
d_printf(DBG_ERR, "error: %s: cannot map registers (phys=[%lx, %lx]\n",
name(), phys_base, phys_base + size - 1);
throw "Iomuxc_imx8mp: failed to map MMIO registers.";
}
d_printf(DBG_INFO, "%s: mapped 0x%lx registers to 0x%08lx\n",
name(), phys_base, vbase);
_mregs = cxx::make_unique<Mem_regs>(vbase, size);
}
static Hw::Device_factory_t<Iomuxc_imx8mp> __hw_pf_factory("Iomuxc_imx8mp");
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Philipp Eppelt <philipp.eppelt@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "debug.h"
#include "hw_device.h"
#include "resource_provider.h"
#include <l4/cxx/unique_ptr>
#include <l4/drivers/hw_mmio_register_block>
#include <string>
#include <vector>
namespace Hw {
class Iomuxc_table_property : public Property
{
protected:
std::vector<unsigned> _table;
public:
int set(int, std::string const &) override { return -EINVAL; }
int set(int, Generic_device *) override { return -EINVAL; }
int set(int, Resource *) override { return -EINVAL; }
int set(int, l4_int64_t i) override
{
_table.push_back(static_cast<unsigned>(i));
return 0;
}
};
class Mem_regs
{
public:
Mem_regs(l4_addr_t base, l4_size_t size)
: _base(base), _size(size)
{}
l4_uint32_t get_reg(l4_addr_t offset) const
{
if (!check_access(offset))
return -1U;
return _base.read<l4_uint32_t>(offset);
}
bool set_reg(l4_addr_t offset, l4_uint32_t value)
{
if (!check_access(offset))
return false;
_base.write<l4_uint32_t>(value, offset);
return true;
}
private:
bool check_access(l4_addr_t offset) const
{
if (offset > _size)
return false;
if ((offset & 3) != 0)
return false;
return true;
}
L4drivers::Mmio_register_block<32> _base;
l4_size_t _size;
};
/**
* Description of a pad connection of the CPU.
*/
class Pad
{
public:
Pad(l4_addr_t mux_offs, l4_addr_t cfg_offs, l4_addr_t input_offs,
l4_uint32_t mux_val, l4_uint32_t cfg_val, l4_uint32_t input_val,
Mem_regs *regs)
: _mux_offs(mux_offs), _cfg_offs(cfg_offs), _input_offs(input_offs),
_mux_val(mux_val), _cfg_val(cfg_val), _input_val(input_val),
_regs(regs)
{}
bool config() { return configure(_mux_val, _cfg_val, _input_val); }
private:
bool configure(l4_uint32_t mux_val, l4_uint32_t cfg_val, l4_uint32_t input_val)
{
d_printf(DBG_DEBUG, "IOMUXC PAD: Write to mux @0x%lx value 0x%x\n",
_mux_offs, mux_val);
bool ret = _regs->set_reg(_mux_offs, mux_val);
if (!ret)
return ret;
d_printf(DBG_DEBUG, "IOMUXC PAD: Write to cfg @0x%lx value 0x%x\n",
_cfg_offs, cfg_val);
ret = _regs->set_reg(_cfg_offs, cfg_val);
if (!ret)
return ret;
d_printf(DBG_DEBUG, "IOMUXC PAD: Write to input @0x%lx value 0x%x\n",
_input_offs, input_val);
ret = _regs->set_reg(_input_offs, input_val);
return ret;
}
l4_addr_t _mux_offs;
l4_addr_t _cfg_offs;
l4_addr_t _input_offs;
l4_uint32_t _mux_val;
l4_uint32_t _cfg_val;
l4_uint32_t _input_val;
Mem_regs *_regs;
};
class Iomuxc_imx8mp : public Hw::Device
{
public:
Iomuxc_imx8mp()
{}
void init() override;
Mem_regs *mregs() const { return _mregs.get(); }
private:
bool _initialized = false;
cxx::unique_ptr<Mem_regs> _mregs;
};
template <typename DEV>
class Iomux_device : public DEV
{
/**
* Resource pads property
*/
class Pads_property : public Iomuxc_table_property
{
public:
size_t size() const
{ return _table.size() / 6; }
unsigned mux(size_t idx) const
{ return _table.at(idx * 6); }
unsigned cfg(size_t idx) const
{ return _table.at(idx * 6 + 1); }
unsigned input(size_t idx) const
{ return _table.at(idx * 6 + 2); }
unsigned mux_val(size_t idx) const
{ return _table.at(idx * 6 + 3); }
unsigned cfg_val(size_t idx) const
{ return _table.at(idx * 6 + 5); }
unsigned input_val(size_t idx) const
{ return _table.at(idx * 6 + 4); }
};
public:
Iomux_device()
{
this->register_property("iomuxc", &_iomuxc);
this->register_property("pads", &_pads);
}
void init() override
{
if (_iomuxc.dev() == nullptr)
{
d_printf(DBG_ERR, "error: %s: 'iomuxc' not set.\n", DEV::name());
throw "Iomuxc init error";
}
_iomuxc.dev()->init();
d_printf(DBG_DEBUG, "Table size: %zu\n", _pads.size());
for (unsigned i = 0; i < _pads.size(); ++i)
{
if (!pad_from_property(i).config())
d_printf(DBG_WARN, "warning: %s: pad %i failed to configure.\n",
DEV::name(), i);
}
DEV::init();
}
Pad pad_from_property(unsigned idx)
{
if (idx > _pads.size())
throw -L4_EINVAL;
return Pad(_pads.mux(idx), _pads.cfg(idx), _pads.input(idx),
_pads.mux_val(idx), _pads.cfg_val(idx), _pads.input_val(idx),
_iomuxc.dev()->mregs());
}
private:
Device_property<Hw::Iomuxc_imx8mp> _iomuxc;
Pads_property _pads;
};
} // namespace Hw

View File

@@ -0,0 +1,11 @@
PKGDIR ?= ../../../..
L4DIR ?= $(PKGDIR)/../../..
SUBDIRS :=
SRC_CC := ide.cc \
intel_gma500.cc \
intel_i915.cc \
marvell_91xx.cc \
intel_p2p_inhibitor.cc
include $(PKGDIR)/server/src/lib_subdir.mk

View File

@@ -0,0 +1,61 @@
/*
* (c) 2010-2020 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <alexander.warg@kernkonzept.com>
* Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "cfg.h"
#include "debug.h"
#include <pci-driver.h>
namespace {
using namespace Hw::Pci;
struct Pci_ide_drv : Driver
{
int probe(Dev *d) override
{
d_printf(DBG_DEBUG, "Found IDE device\n");
if (!Io_config::cfg->legacy_ide_resources(d->host()))
return 1;
unsigned io_flags = Resource::Io_res
| Resource::F_size_aligned
| Resource::F_hierarchical;
// IDE legacy IO interface
if (!(d->cfg.cls_rev & 0x100))
{
d->host()->add_resource_rq(new Resource(io_flags, 0x1f0, 0x1f7));
d->host()->add_resource_rq(new Resource(io_flags, 0x3f6, 0x3f6));
d->host()->add_resource_rq(
new Resource(Resource::Irq_res | Resource::Irq_type_raising_edge, 14, 14)
);
}
if (!(d->cfg.cls_rev & 0x400))
{
d->host()->add_resource_rq(new Resource(io_flags, 0x170, 0x177));
d->host()->add_resource_rq(new Resource(io_flags, 0x376, 0x376));
d->host()->add_resource_rq(
new Resource(Resource::Irq_res | Resource::Irq_type_raising_edge, 15, 15)
);
}
return 0;
};
};
static Pci_ide_drv _pci_ide_drv;
struct Init
{
Init()
{
_pci_ide_drv.register_driver_for_class(0x0101);
}
};
static Init init;
}

View File

@@ -0,0 +1,73 @@
/*
* (c) 2010-2020 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <alexander.warg@kernkonzept.com>
* Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "cfg.h"
#include "debug.h"
#include <pci-driver.h>
namespace {
using namespace Hw::Pci;
struct Pci_intel_gma500_drv : Driver
{
int probe(Dev *d) override
{
d_printf(DBG_DEBUG, "Found Intel gma500 device\n");
l4_uint32_t v;
// GC - Graphics Control
d->cfg_read(0x52, &v, Cfg_short);
unsigned gfx_mem_sz = 0;
// VGA disabled?
if (v & 2)
return 1;
switch ((v >> 4) & 7)
{
case 1: gfx_mem_sz = 1 << 20; break;
case 2: gfx_mem_sz = 4 << 20; break;
case 3: gfx_mem_sz = 8 << 20; break;
default: return 1;
}
// BSM - Base of Stolen Memory
d->cfg_read(0x5c, &v, Cfg_long);
v &= 0xfff00000;
unsigned flags = Resource::Mmio_res | Resource::Mem_type_rw
| Resource::F_prefetchable;
l4_addr_t end = v + gfx_mem_sz - 1;
Resource *res = new Resource(flags, v, end);
d->host()->add_resource(res);
Device *p;
for (p = d->host()->parent(); p && p->parent(); p = p->parent())
;
if (p)
p->request_child_resource(res, d->host());
return 0;
}
};
static Pci_intel_gma500_drv _pci_intel_gma500_drv;
struct Init
{
Init()
{
_pci_intel_gma500_drv.register_driver(0x8086, 0x8108);
}
};
static Init init;
}

View File

@@ -0,0 +1,122 @@
/*
* (c) 2014-2020 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "cfg.h"
#include "debug.h"
#include <pci-driver.h>
namespace {
using namespace Hw::Pci;
struct Pci_intel_i915_drv : Driver
{
struct Opregion_header
{
char const sign[16]; ///< Signature: "IntelGraphicsMem"
l4_uint32_t size; ///< Size of OpRegion incl header in KB
l4_uint32_t over; ///< OpRegion version
l4_uint32_t sver[8]; ///< System BIOS version
l4_uint32_t vver[4]; ///< Video BIOS version
l4_uint32_t gver[4]; ///< Graphics driver version
l4_uint32_t mbox; ///< Supported mailboxes
l4_uint32_t dmod; ///< Driver model
l4_uint32_t pcon; ///< Platform configuration
l4_uint32_t dver[8]; ///< GOP/Driver version
l4_uint32_t rm01[31]; ///< Reserved must be zero
} __attribute__((packed));
static_assert(sizeof(Opregion_header) == 256, "Wrong OpRegion header size.");
/// Validate the signature of the OpRegion.
static bool check_opregion_sig(char const *sig)
{ return 0 == memcmp(sig, "IntelGraphicsMem", 16); }
/// Map the OpRegion and check the signature; return nullptr on failure.
Opregion_header *map_opregion(l4_addr_t addr)
{
l4_addr_t base = res_map_iomem(addr, L4_PAGESIZE);
if (!base)
return nullptr;
Opregion_header *hdr = reinterpret_cast<Opregion_header *>(base);
if (!check_opregion_sig(hdr->sign))
return nullptr;
return hdr;
}
int probe(Dev *d) override
{
d_printf(DBG_INFO, "Found Intel i915 device\n");
l4_uint32_t v;
// PCI_ASLS
d->cfg_read(0xfc, &v, Cfg_long);
// According to the Intel IGD OpRegion specification the default value is
// 0h which means the BIOS has not placed a memory offset into this
// register
if (v == 0 || v == ~0U)
return 0;
d_printf(DBG_DEBUG, "Found Intel i915 GPU OpRegion at %x\n", v);
Opregion_header *hdr = map_opregion(v);
l4_uint32_t size = 0U;
if (hdr)
{
size = hdr->size;
d_printf(DBG_DEBUG, "i915 OpRegion: size 0x%x, version 0x%x\n", size,
hdr->over);
}
else
{
d_printf(DBG_WARN,
"i915: OpRegion header invalid. Probing failed.\n");
return 0;
}
unsigned flags = Resource::Mmio_res | Resource::Mem_type_rw
| Resource::F_prefetchable;
// the opregion address might not be page aligned... (0xdaf68018)
Resource *res = new Resource(flags, l4_trunc_page(v),
l4_round_page(v + size * 0x400) - 1);
d->host()->add_resource(res);
Device *p;
for (p = d->host()->parent(); p && p->parent(); p = p->parent())
;
if (p)
p->request_child_resource(res, d->host());
return 0;
};
};
static Pci_intel_i915_drv _pci_intel_i915_drv;
struct Init
{
Init()
{
_pci_intel_i915_drv.register_driver(0x8086, 0x0046);
_pci_intel_i915_drv.register_driver(0x8086, 0x0166);
_pci_intel_i915_drv.register_driver(0x8086, 0x0412);
_pci_intel_i915_drv.register_driver(0x8086, 0x0416);
_pci_intel_i915_drv.register_driver(0x8086, 0x1612);
_pci_intel_i915_drv.register_driver(0x8086, 0x1912);
_pci_intel_i915_drv.register_driver(0x8086, 0x1916);
_pci_intel_i915_drv.register_driver(0x8086, 0x5912); // "HD Graphics 630"
_pci_intel_i915_drv.register_driver(0x8086, 0x9bc8); // "UHD Graphics 630"
_pci_intel_i915_drv.register_driver(0x8086, 0x3ea0); // "UHD Graphics 620"
_pci_intel_i915_drv.register_driver(0x8086, 0x46d1); // "UHD Graphics"
}
};
static Init init;
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2019-2022, 2024 Kernkonzept GmbH.
* Author: Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* PCIe introduced a technology named PCI Access Control Services (PCI/ACS).
* The idea is to have a common interface to restrict communication between
* PCI devices.
*
* PCI ACS is optional, but some Intel PCHs without PCI ACS implement features
* that allow for similar isolation.
*
* The purpose of this driver is to enable these features for L4Re.
*
* Unfortunately Intel does not officially disclose which PCHs are supported.
* The most official list of supported PCI IDs is included in Redhat Bugzilla
* 1037684 (https://bugzilla.redhat.com/show_bug.cgi?id=1037684).
*
* The datasheet for 7-series/c216 PCH is here:
* https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/7-series-chipset-pch-datasheet.pdf
*
* The BSRP and UPDCR registers are documented here:
* https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/4th-gen-core-family-mobile-i-o-datasheet.pdf
* (page 325)
*/
#include <l4/drivers/hw_mmio_register_block>
#include "cfg.h"
#include "res.h"
#include "debug.h"
#include <pci-driver.h>
namespace {
using namespace Hw::Pci;
struct Intel_root_complex_p2p_inhibitor : Driver
{
enum Lpc_register
{
Rcba = 0xf0, ///< root complex base address register
Rcba_enable = 1,
Rcba_mask = 0xffffc000,
};
enum Chipset_register
{
Bspr = 0x1104, ///< backbone scratch pad register
Bspr_bpnpd = (1 << 8), ///< backbone peer non-posted disable
Bspr_bppd = (1 << 9), ///< backbone peer posted disable
Updcr = 0x1014, ///< upstream peer decode configuration register
Updcr_enable_mask = 0x3f, ///< peer decode enable bits
};
Intel_root_complex_p2p_inhibitor()
{
// this matches to LPC controller PCI IDs corresponding to the chipsets
// that are documented to support the feature in Redhat Bugzilla 1037684
// 5 series lpc controller
for (unsigned id = 0x3b00; id <= 0x3b16; ++id)
register_driver(0x8086, id);
// 6 series lpc controller
for (unsigned id = 0x1c40; id <= 0x1c5f; ++id)
register_driver(0x8086, id);
// 7 series chipset lpc controller
for (unsigned id = 0x1e40; id <= 0x1e5f; ++id)
register_driver(0x8086, id);
// c600/x79 lpc controller
for (unsigned id = 0x1d40; id <= 0x1d41; ++id)
register_driver(0x8086, id);
// 8 series lpc controller
for (unsigned id = 0x8c40; id <= 0x8c5f; ++id)
register_driver(0x8086, id);
// 9 series lpc controller
for (unsigned id = 0x9cc0; id <= 0x9cc6; ++id)
register_driver(0x8086, id);
}
/**
* This function configures the root complex such that requests are always
* routed through the root complex and that there are no
* peer-to-peer-requests going directly between root ports. This is the
* equivalent to Request Redirect, Completion Redirect and Upstream
* Forwarding in PCI ACS. For more information please consult the PCIe base
* specification (revision 3).
*/
int probe(Dev *d) override
{
d_printf(DBG_DEBUG, "Intel PCIe: disable P2P traffic.\n");
l4_uint32_t rcba;
d->cfg_read(Rcba, &rcba, Cfg_long);
if (!(rcba & Rcba_enable))
{
d_printf(DBG_DEBUG, "LPC root complex base not enabled\n");
return 0;
}
rcba &= Rcba_mask;
l4_addr_t vbase = res_map_iomem(rcba, 8192);
if (!vbase)
{
d_printf(DBG_ERR, "Could not map root complex base register space\n");
return 0;
}
auto regs = L4drivers::Mmio_register_block<32>(vbase);
l4_uint32_t bspr = regs.read<l4_uint32_t>(Bspr);
d_printf(DBG_DEBUG, "Bspr = %x\n", bspr);
bspr &= Bspr_bpnpd | Bspr_bppd;
// If Bspr_bppd (Backbone Peer Posted Disable) and Bspr_bpnpd (Backbone
// Peer Non-Posted Disable) are both set, we do not need to disable peer
// decodes on individual root ports (Updcr register)
if (bspr != (Bspr_bpnpd | Bspr_bppd))
{
l4_uint32_t updcr = regs.read<l4_uint32_t>(Updcr);
d_printf(DBG_DEBUG, "Disabling Updcr peer decodes\n");
// This setting disables peer requests on all root ports.
updcr &= ~ Updcr_enable_mask;
regs.write(updcr, Updcr);
}
// TODO unmap?
return 0;
}
};
static Intel_root_complex_p2p_inhibitor _intel_root_complex_p2p_inhibitor;
struct Intel_root_port_p2p_inhibitor : Driver
{
enum Pci_cfg_register
{
///< Miscellaneous Port Configuration Register, see datasheet page 851
Pci_mpc_register = 0xd8,
};
enum Mpc_register
{
Irbnce = (1 << 26), ///< Invalid Receive Bus Number Check Enable
};
Intel_root_port_p2p_inhibitor()
{
// PCI IDs taken from Redhat Bugzilla 1037684
// ibexpeak PCH
// https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/5-chipset-3400-chipset-datasheet.pdf
for (unsigned id = 0x3b42; id <= 0x3b51; ++id)
register_driver(0x8086, id);
// cougarpoint PCH
// intel 6 series
// https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/6-chipset-c200-chipset-datasheet.pdf
for (unsigned id = 0x1c10; id <= 0x1c1f; ++id)
register_driver(0x8086, id);
for (unsigned id = 0x1e10; id <= 0x1e1f; ++id)
register_driver(0x8086, id);
// lynxpoint-H PCH
// https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/7-series-chipset-pch-datasheet.pdf
for (unsigned id = 0x8c10; id <= 0x8c1f; ++id)
register_driver(0x8086, id);
// lynxpoint-LP PCH
// https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/8-series-chipset-pch-datasheet.pdf
// https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/4th-gen-core-family-mobile-i-o-datasheet.pdf
for (unsigned id = 0x9c10; id <= 0x9c1b; ++id)
register_driver(0x8086, id);
// wildcatpoint PCH
// series 9
for (unsigned id = 0x9c90; id <= 0x9c9b; ++id)
register_driver(0x8086, id);
// X79
for (unsigned id = 0x1d10; id <= 0x1d1e; id += 2)
register_driver(0x8086, id);
}
/**
* This function configures the PCI root port such that it implements the
* equivalent of PCI ACS source validation. For reference, please consult
* the PCIe base specification (revision 3).
*/
int probe(Dev *d) override
{
d_printf(DBG_DEBUG, "Intel PCIe: disable P2P traffic.\n");
l4_uint32_t mpc;
d->cfg_read(Pci_mpc_register, &mpc, Cfg_long);
if (!(mpc & Irbnce))
{
d_printf(DBG_DEBUG, "PCIe root port: Setting Irbnce\n");
mpc |= Irbnce;
d->cfg_write(Pci_mpc_register, mpc, Cfg_long);
}
return 0;
};
};
static Intel_root_port_p2p_inhibitor _intel_root_port_p2p_inhibitor;
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2016-2020, 2022, 2024 Kernkonzept GmbH.
* Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "cfg.h"
#include "debug.h"
#include <pci-driver.h>
namespace {
using namespace Hw::Pci;
struct Pci_marvell_91xx_quirk : Driver
{
int probe(Dev *d) override
{
d_printf(DBG_DEBUG,
"Found Marvell 91xx SATA controller: use PCI function aliasing quirk\n");
// These controllers initiate DMA and may be also MSI transactions
// using the wrong PCI function (function 1) to trigger the correct
// entries for DMA remapping and MSI remapping we simulate this by
// using 3 bits for the PCIe phantom function which allows the device
// to use all PCI function IDs for transactions.
//
// TODO: The 9123 controller also has an IDE controller as PCI
// function 1 which we should disable here, however the infrastructure
// for that is missing so the IO config file should make sure that
// the IDE function of the controller is never assigned to any virtual
// bus.
d->set_phantomfn_bits(3);
return 0;
}
};
static Pci_marvell_91xx_quirk _pci_marvell_sata_quirk;
struct Init
{
Init()
{
_pci_marvell_sata_quirk.register_driver(0x1b4b, 0x9123);
_pci_marvell_sata_quirk.register_driver(0x1b4b, 0x9170);
}
};
static Init init;
}

View File

@@ -0,0 +1,354 @@
/*
* Copyright (C) 2019-2024 Kernkonzept GmbH.
* Author(s): Frank Mehnert <frank.mehnert@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* Driver for the QEMU ECAM PCI controller emulation.
*
* The Linux device tree for that device looks like this:
*
* \code{.dts}
* pcie@10000000 {
* interrupt-map-mask = <0x1800 0x00 0x00 0x07>;
* interrupt-map = < 0x00 0x00 0x00 0x01 0x8001 0x00 0x00 0x00 0x03 0x04
* 0x00 0x00 0x00 0x02 0x8001 0x00 0x00 0x00 0x04 0x04
* 0x00 0x00 0x00 0x03 0x8001 0x00 0x00 0x00 0x05 0x04
* 0x00 0x00 0x00 0x04 0x8001 0x00 0x00 0x00 0x06 0x04
* 0x800 0x00 0x00 0x01 0x8001 0x00 0x00 0x00 0x04 0x04
* 0x800 0x00 0x00 0x02 0x8001 0x00 0x00 0x00 0x05 0x04
* 0x800 0x00 0x00 0x03 0x8001 0x00 0x00 0x00 0x06 0x04
* 0x800 0x00 0x00 0x04 0x8001 0x00 0x00 0x00 0x03 0x04
* 0x1000 0x00 0x00 0x01 0x8001 0x00 0x00 0x00 0x05 0x04
* 0x1000 0x00 0x00 0x02 0x8001 0x00 0x00 0x00 0x06 0x04
* 0x1000 0x00 0x00 0x03 0x8001 0x00 0x00 0x00 0x03 0x04
* 0x1000 0x00 0x00 0x04 0x8001 0x00 0x00 0x00 0x04 0x04
* 0x1800 0x00 0x00 0x01 0x8001 0x00 0x00 0x00 0x06 0x04
* 0x1800 0x00 0x00 0x02 0x8001 0x00 0x00 0x00 0x03 0x04
* 0x1800 0x00 0x00 0x03 0x8001 0x00 0x00 0x00 0x04 0x04
* 0x1800 0x00 0x00 0x04 0x8001 0x00 0x00 0x00 0x05 0x04>;
* #interrupt-cells = <0x01>;
* ranges = <0x1000000 0x00 0x00000000 0x00 0x3eff0000 0x00 0x00010000
* 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000
* 0x3000000 0x80 0x00000000 0x80 0x00000000 0x80 0x00000000>;
* reg = <0x40 0x10000000 0x00 0x10000000>;
* msi-parent = <0x8002>;
* dma-coherent;
* bus-range = <0x00 0xff>;
* linux,pci-domain = <0x00>;
* #size-cells = <0x02>;
* #address-cells = <0x03>;
* device_type = "pci";
* compatible = "pci-host-ecam-generic";
* };
* \endcode
*
* The ranges property can be interpreted as follows, see
* https://elinux.org/Device_Tree_Usage#PCI_Host_Bridge
*
* \verbatim
* 0x3000000 0x80 0x00000000 0x80 0x00000000 0x80 0x00000000>;
* phys.hi+phys.mid+phys.low region base region size
*
* phys.hi: npt000ss bbbbbbbb dddddfff rrrrrrrr
* n: relocation relocatable region flag (not relevant here)
* p: prefetchable (cacheable) region flag
* t: aliased address flags (not relevant here)
* s: space code (0=cfg space, 1=I/O space, 2=32-bit MMIO, 3=64-bit MMIO)
* b: PCI bus number
* d: PCI device number
* f: PCI function number
* r: register number (not relevant here)
* phys.mid+phys.log: PCI child address
* \endverbatim
*
* This entry should be converted into the following io.cfg code:
*
* \code{.lua}
* pciec0 = Io.Hw.Ecam_pcie_bridge(function()
* Property.regs_base = 0x10000000
* Property.regs_size = 0x2eff0000
* Property.cfg_base = 0x4010000000
* Property.cfg_size = 0x0010000000 -- 256 buses x 256 devs x 4KB
* Property.ioport_base = 0x3eff0000
* Property.ioport_size = 0x10000 -- 64KB (for port I/O access)
* Property.mmio_base = 0x10000000
* Property.mmio_size = 0x2eff0000 --~750MB (for 32-bit MMIO access)
* Property.mmio_base_64 = 0x8000000000
* Property.mmio_size_64 = 0x8000000000 -- 512GB (for 64-bit MMIO access)
* Property.int_a = 32 + 3
* Property.int_b = 32 + 4
* Property.int_c = 32 + 5
* Property.int_d = 32 + 6
* end)
* \endcode
*/
#include <l4/drivers/hw_mmio_register_block>
#include "hw_device.h"
#include <pci-root.h>
#include "resource_provider.h"
namespace {
class Ecam_pcie_bridge
: public Hw::Device,
public Hw::Pci::Root_bridge
{
public:
explicit Ecam_pcie_bridge(int segment = 0, unsigned bus_nr = 0)
: Hw::Device(0xffee0000), // just don't use the default 0xffffffff ID
Hw::Pci::Root_bridge(segment, bus_nr, this, nullptr)
{
// Use a default name as long as no name was set with the Lua script.
set_name_if_empty("pcie_ecam");
// the set of mandatory properties
register_property("regs_base", &_regs_base); // mandatory
register_property("regs_size", &_regs_size); // mandatory
register_property("cfg_base", &_cfg_base); // mandatory
register_property("cfg_size", &_cfg_size); // mandatory
register_property("ioport_base", &_ioport_base); // optional
register_property("ioport_size", &_ioport_size); // optional
register_property("mmio_base", &_mmio_base); // mandatory
register_property("mmio_size", &_mmio_size); // mandatory
register_property("mmio_base_64", &_mmio_base_64); // optional
register_property("mmio_size_64", &_mmio_size_64); // optional
register_property("int_a", &_int_map[0]); // optional
register_property("int_b", &_int_map[1]); // optional
register_property("int_c", &_int_map[2]); // optional
register_property("int_d", &_int_map[3]); // optional
}
typedef Hw::Pci::Cfg_addr Cfg_addr;
typedef Hw::Pci::Cfg_width Cfg_width;
void init() override;
int cfg_read(Cfg_addr addr, l4_uint32_t *value, Cfg_width) override;
int cfg_write(Cfg_addr addr, l4_uint32_t value, Cfg_width) override;
int int_map(int i) const { return _int_map[i]; }
private:
int host_init();
Int_property _regs_base{~0};
Int_property _regs_size{~0};
Int_property _cfg_base{~0};
Int_property _cfg_size{~0};
Int_property _ioport_base{~0};
Int_property _ioport_size{~0};
Int_property _mmio_base{~0};
Int_property _mmio_size{~0};
Int_property _mmio_base_64{~0};
Int_property _mmio_size_64{~0};
Int_property _int_map[4];
// PCI root bridge core memory. (Currently) not used.
L4drivers::Register_block<32> _regs;
// PCI root bridge config space.
L4drivers::Register_block<32> _cfg;
};
class Irq_router_rs : public Resource_space
{
public:
char const *res_type_name() const override
{ return "ECAM PCIe IRQ router"; }
bool request(Resource *parent, ::Device *pdev,
Resource *child, ::Device *cdev) override
{
auto *cd = dynamic_cast<Hw::Device *>(cdev);
if (!cd)
return false;
unsigned pin = child->start();
if (pin > 3)
return false;
auto *pd = dynamic_cast<Ecam_pcie_bridge *>(pdev);
if (!pd)
return false;
unsigned slot = (cd->adr() >> 16);
unsigned irq_nr = pd->int_map((pin + slot) & 3);
d_printf(DBG_DEBUG, "%s/%08x: Requesting IRQ%c at slot %d => IRQ %d\n",
cd->get_full_path().c_str(), cd->adr(),
(int)('A' + pin), slot, irq_nr);
child->del_flags(Resource::F_relative);
child->start(irq_nr);
child->del_flags(Resource::Irq_type_mask);
child->add_flags(Resource::Irq_type_base | Resource::Irq_type_level_high);
child->parent(parent);
return true;
}
bool alloc(Resource *, ::Device *, Resource *, ::Device *, bool) override
{ return false; }
void assign(Resource *, Resource *) override
{
d_printf(DBG_ERR, "internal error: cannot assign to Irq_router_rs\n");
}
bool adjust_children(Resource *) override
{
d_printf(DBG_ERR, "internal error: cannot adjust root Irq_router_rs\n");
return false;
}
};
int
Ecam_pcie_bridge::host_init()
{
// assert on mandatory properties
if ( assert_property(&_regs_base, "regs_base", ~0)
|| assert_property(&_regs_size, "regs_size", ~0)
|| assert_property(&_cfg_base, "cfg_base", ~0)
|| assert_property(&_cfg_size, "cfg_size", ~0)
|| assert_property(&_mmio_base, "mmio_base", ~0)
|| assert_property(&_mmio_size, "mmio_size", ~0))
return -L4_EINVAL;
l4_addr_t va = res_map_iomem(_regs_base, _regs_size);
if (!va)
{
d_printf(DBG_ERR, "error: %s: could not map core memory.\n", name());
return -L4_ENOMEM;
}
_regs = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_cfg_base, _cfg_size);
if (!va)
{
d_printf(DBG_ERR, "error: %s: could not map cfg memory.\n", name());
return -L4_ENOMEM;
}
_cfg = new L4drivers::Mmio_register_block<32>(va);
// Port I/O is not performed using dedicated instructions like in/out on
// non-x86 hosts. Instead, port I/O is emulated using an MMIO region. The
// resources as reflected by the above device tree on ARM64 contain such a
// region of 0x3eff0000..0x3f000000. Given that the I/O ports of separate
// devices are located on dedicated pages (which is currently not the case,
// see Resource_provider::min_align()) it would be possible to the map the
// required part of the port I/O memory to the client. But it was decided
// that on such architectures the actual memory access will be performed in
// the IO server and port I/O performed by the client forwarded to the IO
// server using IPC.
// This is currently not implemented, therefore access to port I/O resources
// of PCI devices will not work on non-x86 architectures.
if (_ioport_base != ~0 && (_ioport_base & 0xffffffff0000ULL))
d_printf(DBG_WARN, "%s: Base for I/O ports set to MMIO range (%08llx-%08llx)!\n",
name(), _ioport_base.val(), _ioport_base.val() + _ioport_size.val());
return 0;
}
int
Ecam_pcie_bridge::cfg_read(Cfg_addr addr, l4_uint32_t *value, Cfg_width width)
{
switch (width)
{
case Hw::Pci::Cfg_long:
*value = _cfg.r<32>(addr.addr());
break;
case Hw::Pci::Cfg_short:
*value = _cfg.r<16>(addr.addr());
break;
case Hw::Pci::Cfg_byte:
*value = _cfg.r<8>(addr.addr());
break;
default:
d_printf(DBG_WARN, "Invalid width %d!\n", width);
return -EIO;
}
d_printf(DBG_ALL,
"%s: cfg_read addr=%02x:%02x.%x reg=%03x width=%2d-bit => %0*x\n",
name(), addr.bus(), addr.dev(), addr.fn(), addr.reg(), 8 << width,
2 << width, *value & cfg_o_to_mask(width));
return 0;
}
int
Ecam_pcie_bridge::cfg_write(Cfg_addr addr, l4_uint32_t value, Cfg_width width)
{
d_printf(DBG_ALL,
"%s: cfg_write addr=%02x:%02x.%x reg=%03x width=%2d-bit value=%0*x\n",
name(), addr.bus(), addr.dev(), addr.fn(), addr.reg(), 8 << width,
2 << width, value & cfg_o_to_mask(width));
switch (width)
{
case Hw::Pci::Cfg_long:
_cfg.r<32>(addr.addr()) = value;
break;
case Hw::Pci::Cfg_short:
_cfg.r<16>(addr.addr()) = value;
break;
case Hw::Pci::Cfg_byte:
_cfg.r<8>(addr.addr()) = value;
break;
default:
d_printf(DBG_WARN, "Invalid width %d!\n", width);
return -EIO;
}
return 0;
}
void
Ecam_pcie_bridge::init()
{
if (host_init())
return;
Resource *mr;
// I/O ports are not supported on ARM anyway.
if (_ioport_base != ~0 && _ioport_size != ~0)
{
mr = new Resource_provider(Resource::Io_res);
mr->start_size(_ioport_base & 0xffff, _ioport_size);
mr->set_id("IO");
add_resource_rq(mr);
}
mr = new Resource_provider(Resource::Mmio_res | Resource::Mem_type_rw);
mr->start_size(_mmio_base, _mmio_size);
mr->set_id("MMIO");
add_resource_rq(mr);
if (_mmio_base_64 != ~0 && _mmio_size_64 != ~0)
{
mr = new Resource_provider(Resource::Mmio_res | Resource::Mem_type_rw
| Resource::F_width_64bit);
mr->start_size(_mmio_base_64, _mmio_size_64);
mr->set_id("MMIO");
add_resource_rq(mr);
}
auto *ir = new Hw::Pci::Irq_router_res<Irq_router_rs>();
ir->set_id("IRQR");
add_resource_rq(ir);
discover_bus(this, this);
Hw::Device::init();
}
static Hw::Device_factory_t<Ecam_pcie_bridge> f("Ecam_pcie_bridge");
}

View File

@@ -0,0 +1,557 @@
/*
* Copyright (C) 2022-2024 Kernkonzept GmbH.
* Author(s): Frank Mehnert <frank.mehnert@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* \file
* Driver for the PCIe controller on i.MX8 MQ boards.
*
* The Linux device tree for such devices looks like this:
* \code{.dts}
* pciea: pcie@0x5f000000 {
* compatible = "fsl,imx8qm-pcie","snps,dw-pcie";
* reg = <0x5f000000 0x10000>,
* <0x6ff00000 0x80000>,
* <0x5f080000 0xf0000>;
* reg-names = "dbi", "config", "hsio";
* #address-cells = <3>;
* #size-cells = <2>;
* device_type = "pci";
* bus-range = <0x00 0xff>;
* ranges = <0x81000000 0 0x00000000 0x6ff80000 0 0x00010000
* 0x82000000 0 0x60000000 0x60000000 0 0x0ff00000>;
* num-lanes = <1>;
* num-viewport = <4>;
* interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>,
* <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
* interrupt-names = "msi", "dma";
* #interrupt-cells = <1>;
* interrupt-map-mask = <0 0 0 0x7>;
* interrupt-map = <0 0 0 1 &gic 0 73 4>,
* <0 0 0 2 &gic 0 74 4>,
* <0 0 0 3 &gic 0 75 4>,
* <0 0 0 4 &gic 0 76 4>;
* clocks = <&pciea_lpcg 0>,
* <&pciea_lpcg 1>,
* <&pciea_lpcg 2>,
* <&phyx2_lpcg 0>,
* <&phyx2_crr0_lpcg 0>,
* <&pciea_crr2_lpcg 0>,
* <&misc_crr5_lpcg 0>;
* clock-names = "pcie", "pcie_bus", "pcie_inbound_axi",
* "pcie_phy", "phy_per", "pcie_per", "misc_per";
* power-domains = <&pd IMX_SC_R_PCIE_A>,
* <&pd IMX_SC_R_SERDES_0>,
* <&pd IMX_SC_R_HSIO_GPIO>;
* power-domain-names = "pcie", "pcie_phy", "hsio_gpio";
* fsl,max-link-speed = <3>;
* hsio-cfg = <PCIEAX1PCIEBX1SATA>;
* local-addr = <0x40000000>;
* ext_osc = <1>;
* pinctrl-0 = <&pinctrl_pciea>;
* reset-gpio = <&lsio_gpio3 14 GPIO_ACTIVE_LOW>;
* ...
* };
* ...
* pinctrl_pciea: pcieagrp {
* fsl,pins = <IMX8QM_SAI1_RXFS_LSIO_GPIO3_IO14 0x00000021>;
* };
* ...
* hsio_refa_clk: clock-hsio-refa {
* ...
* enable-gpios = <&lsio_gpio4 27 GPIO_ACTIVE_LOW>;
* };
* ...
* hsio_refb_clk: clock-hsio-refb {
* ...
* enable-gpios = <&lsio_gpio4 1 GPIO_ACTIVE_LOW>;
* };
* ...
* pciea_lpcg: clock-controller@5f050000 {
* compatible = "fsl,imx8qxp-lpcg";
* reg = <0x5f050000 0x10000>;
* #clock-cells = <1>;
* clocks = <&hsio_axi_clk>, <&hsio_axi_clk>, <&hsio_axi_clk>;
* bit-offset = <16 20 24>;
* clock-output-names = "hsio_pciea_mstr_axi_clk",
* "hsio_pciea_slv_axi_clk",
* "hsio_pciea_dbi_axi_clk";
* power-domains = <&pd IMX_SC_R_PCIE_A>;
* };
* ...
* phyx2_crr0_lpcg: clock-controller@5f0a0000 {
* compatible = "fsl,imx8qxp-lpcg";
* reg = <0x5f0a0000 0x10000>;
* #clock-cells = <1>;
* clocks = <&hsio_per_clk>;
* bit-offset = <16>;
* clock-output-names = "hsio_phyx2_per_clk";
* power-domains = <&pd IMX_SC_R_SERDES_0>;
* };
* ...
* pciea_crr2_lpcg: clock-controller@5f0c0000 {
* compatible = "fsl,imx8qxp-lpcg";
* reg = <0x5f0c0000 0x10000>;
* #clock-cells = <1>;
* clocks = <&hsio_per_clk>;
* bit-offset = <16>;
* clock-output-names = "hsio_pciea_per_clk";
* power-domains = <&pd IMX_SC_R_PCIE_A>;
* };
* ...
* misc_crr5_lpcg: clock-controller@5f0f0000 {
* compatible = "fsl,imx8qxp-lpcg";
* reg = <0x5f0f0000 0x10000>;
* #clock-cells = <1>;
* clocks = <&hsio_per_clk>;
* bit-offset = <16>;
* clock-output-names = "hsio_misc_per_clk";
* power-domains = <&pd IMX_SC_R_HSIO_GPIO>;
* };
* \endcode
*
* This entry should be converted into the following io.cfg code. Some of the
* information of the above device tree is directly implemented in init().
*
* \code{.lua}
* pcie0 = Io.Hw.Pcie_imx8qm(function()
* Property.scu = scu
* Property.gpio3 = gpio3 -- see reset-gpio
* Property.gpio4 = gpio4 -- see hsio_refa_clk
* Property.power = {
* 152, -- IMX_SC_R_PCIE_A (pciea and HSIO crr)
* 153, -- IMX_SC_R_SERDES_0 (pciea and HSIO crr)
* 172, -- IMX_SC_R_HSIO_GPIO (pciea and HSIO crr)
* 169, -- IMX_SC_R_PCIE_B (pcieb and HSIO crr)
* 171, -- IMX_SC_R_SERDES_1 (pcieb and HSIO crr)
* }
* Property.pads = {
* 129, 3, 0x00000021 -- pinctrl_pciea
* }
* Property.clks = {}
* Property.regs_base = 0x5f000000 -- 'dbi' regs entry
* Property.regs_size = 0x00010000
* Property.cfg_base = 0x6ff00000 -- 'config' regs entry
* Property.cfg_size = 0x00080000
* Property.mem_base = 0x60000000 -- MMIO range from 'ranges'
* Property.mem_size = 0x0ff00000
* Property.cpu_fixup = 0x40000000 -- see 'local-addr'
* Property.irq_pin_a = 32 + 73,
* Property.irq_pin_b = 32 + 74,
* Property.irq_pin_c = 32 + 75,
* Property.irq_pin_d = 32 + 76,
* Property.lanes = 1
* end)
* \endcode
*
* Note that the code below only handles the pciea controller properly. The
* pcieb controller would require adaptions, for example write Hsio_csr_phyx1,
* Hsio_csr_pcieb and Hsio_lpcg_pcieb.
*/
#include <l4/util/util.h>
#include "dwc_pcie_core.h"
#include "scu_imx8qm.h"
#include "gpio"
#include <l4/drivers/hw_mmio_register_block>
#include <cstdio>
#include <errno.h>
#include <inttypes.h>
#include <l4/sys/kdebug.h>
namespace {
class Pci_irq_router_rs : public Resource_space
{
public:
char const *res_type_name() const override
{ return "i.MX8 PCIe IRQ router"; }
bool request(Resource *parent, ::Device *, Resource *child, ::Device *cdev) override;
bool alloc(Resource *, ::Device *, Resource *, ::Device *, bool) override
{ return false; }
void assign(Resource *, Resource *) override
{
d_printf(DBG_ERR, "internal error: cannot assign to Pci_irq_router_rs\n");
}
bool adjust_children(Resource *) override
{
d_printf(DBG_ERR, "internal error: cannot adjust root Pci_irq_router_rs\n");
return false;
}
};
class Pcie_imx8_bridge : public Dwc_pcie
{
// _regs_base + 0x050000: LPCG_PCIEX2_0
L4drivers::Register_block<32> _hsio_lpcg_pciea;
// _regs_base + 0x080000: LPCG_PHYX2_0
L4drivers::Register_block<32> _hsio_lpcg_phyx2;
// _regs_base + 0x0a0000: LPCG_PHYX2_CRR0_0
L4drivers::Register_block<32> _hsio_lpcg_crr_0;
// _regs_base + 0x0c0000: LPCG_PCIEX2_CRR2_0
L4drivers::Register_block<32> _hsio_lpcg_crr_2;
// _regs_base + 0x0f0000: LPCG_MISC_CRR5_0
L4drivers::Register_block<32> _hsio_lpcg_crr_5;
// _regs_base + 0x110000: HSIO CRR PHYx2
L4drivers::Register_block<32> _hsio_csr_phyx2;
enum Hsio_csr_phyx2_regs
{
Phyx2_ctrl0 = 0x0,
Phyx2_stts0 = 0x4,
};
enum Phyx2_ctrl0_values
{
Phyx2_ctrl0_apb_rstn_0 = 1U << 0,
Phyx2_ctrl0_apb_rstn_1 = 1U << 1,
};
enum Phyx2_stts0_values
{
Phyx2_stts0_lane0_tx_pll_lock = 1U << 4,
};
// _regs_base + 0x130000: HSIO CRR for PCIex1
L4drivers::Register_block<32> _hsio_csr_pciea;
enum Hsio_csr_pciea_regs
{
Pciex1_ctrl0 = 0x0,
Pciex1_ctrl1 = 0x4,
Pciex1_ctrl2 = 0x8,
Pciex1_stts0 = 0xc,
};
enum Pciex1_ctrl0_values
{
Pciex1_ctrl0_ps_dev_id_shft = 0,
Pciex1_ctrl0_ps_dev_id_mask = 0xffffU << Pciex1_ctrl0_ps_dev_id_shft,
Pciex1_ctrl0_ps_rev_id_shft = 16,
Pciex1_ctrl0_ps_rev_id_mask = 0xffU << Pciex1_ctrl0_ps_rev_id_shft,
Pciex1_ctrl0_dev_type_shft = 24,
Pciex1_ctrl0_dev_type_mask = 0xfU << Pciex1_ctrl0_dev_type_shft,
Pciex1_ctrl0_dev_type_pcie_ep = 0U << Pciex1_ctrl0_dev_type_shft,
Pciex1_ctrl0_dev_type_lpcie_ep = 1U << Pciex1_ctrl0_dev_type_shft,
Pciex1_ctrl0_dev_type_root_port = 4U << Pciex1_ctrl0_dev_type_shft,
};
enum Pciex1_ctrl2_values
{
Pciex1_ctrl2_app_ltssm_enable = 1U << 4,
Pciex1_ctrl2_button_rst_n = 1U << 21,
Pciex1_ctrl2_perst_n = 1U << 22,
Pciex1_ctrl2_power_up_rst_n = 1U << 23,
};
enum Pciex1_stts0_values
{
Pciex_stts0_rm_req_cor_rst = 1U << 19,
};
// _regs_base + 0x160000: HSIO CRR MISC
L4drivers::Register_block<32> _hsio_csr_misc;
enum Hsio_csr_misc_regs
{
Misc_ctrl0 = 0x0,
Misc_stts0 = 0x4,
};
enum Misc_ctrl0_values
{
Misc_ctrl0_iob_rxena = 1U << 0,
Misc_ctrl0_iob_txena = 1U << 1,
Misc_ctrl0_phy_x1_epcs_sel = 1U << 12,
Misc_ctrl0_pcie_ab_select = 1U << 13,
Misc_ctrl0_clkreqn_out_0 = 1U << 23,
Misc_ctrl0_clkreqn_out_override = 1U << 25,
};
// For resetting PCIe at GPIO.
Device_property<Hw::Gpio_device> _gpio3;
Int_property _int_map[4];
enum Rc_regs
{
Pf0_pcie_cap = 0x70,
Lcr = Pf0_pcie_cap + 0xc, ///< Link capability register
};
/// PCIe spec 3.0: 7.8.6
enum Lcr_values
{
Pcie_cap_max_link_speed_mask = 0xf,
Pcie_cap_max_link_speed_gen1 = 1,
};
/// See 2.2.2. Each region is 64KiB (0x10000).
enum
{
Hsio_pcie0 = 0x000000,
Hsio_pcie1 = 0x010000,
Hsio_sata0 = 0x020000,
Hsio_lpcg_pciea = 0x050000,
Hsio_lpcg_pcieb = 0x060000,
Hsio_lpcg_sata = 0x070000,
Hsio_lpcg_phyx2 = 0x080000, // Linux: IMX8QM_PHYX2_LPCG_OFFSET
Hsio_lpcg_phyx1 = 0x090000,
Hsio_lpcg_crr_0 = 0x0a0000,
Hsio_lpcg_crr_1 = 0x0b0000,
Hsio_lpcg_crr_2 = 0x0c0000,
Hsio_lpcg_crr_3 = 0x0d0000,
Hsio_lpcg_crr_4 = 0x0e0000,
Hsio_lpcg_crr_5 = 0x0f0000,
Hsio_lpcg_gpio = 0x100000,
Hsio_csr_phyx2 = 0x110000, // Linux: IMX8QM_CSR_PHYX2_OFFSET
Hsio_csr_phyx1 = 0x120000, // Linux: IMX8QM_CSR_PHYX1_OFFSET, for pcieb
Hsio_csr_pciea = 0x130000, // Linux: IMX8QM_CSR_PCIEA_OFFSET
Hsio_csr_pcieb = 0x140000, // Linux: IMX8QM_CSR_PCIEB_OFFSET
Hsio_csr_sata = 0x150000,
Hsio_csr_misc = 0x160000, // Linux: IMX8QM_CSR_MISC_OFFSET
Hsio_gpio = 0x170000,
Hsio_phyx2_l0 = 0x180000,
Hsio_phyx2_l1 = 0x190000,
Hsio_phyx1 = 0x1a0000,
};
public:
explicit Pcie_imx8_bridge(int segment = 0, unsigned bus_nr = 0);
void init() override;
bool link_up() override
{ return _regs[Port_logic::Debug1] & (1 << 4); }
// configuration: PCIEAX1PCIEBX1SATA
void wait_for_pll_lock()
{
for (unsigned retries = 0; retries < 10; ++retries)
{
if (_hsio_csr_phyx2[Phyx2_stts0] & Phyx2_stts0_lane0_tx_pll_lock)
{
d_printf(DBG_INFO, "%s: PCIe PLL locked\n", name());
return;
}
l4_sleep(2);
}
d_printf(DBG_WARN, "%s: warning: PCIe PLL lock timeout!\n", name());
}
// check whether link is up and running
void wait_link_up()
{
for (unsigned retries = 0; retries < 10; ++retries)
{
if (link_up())
return;
l4_usleep(90000);
}
}
void assert_core_reset();
void deassert_core_reset();
int int_map(int i) const { return _int_map[i]; }
};
bool
Pci_irq_router_rs::request(Resource *parent, ::Device *pdev,
Resource *child, ::Device *cdev)
{
auto *cd = dynamic_cast<Hw::Device *>(cdev);
if (!cd)
return false;
unsigned pin = child->start();
if (pin > 3)
return false;
auto *pd = dynamic_cast<Pcie_imx8_bridge *>(pdev);
if (!pd)
return false;
unsigned slot = (cd->adr() >> 16);
unsigned irq_nr = pd->int_map((pin + slot) & 3);
d_printf(DBG_DEBUG, "%s: Requesting IRQ%c at slot %d => IRQ %d\n",
cd->get_full_path().c_str(), (int)('A' + pin), slot, irq_nr);
child->del_flags(Resource::F_relative);
child->start(irq_nr);
child->del_flags(Resource::Irq_type_mask);
child->add_flags(Resource::Irq_type_base | Resource::Irq_type_level_high);
child->parent(parent);
return true;
}
Pcie_imx8_bridge::Pcie_imx8_bridge(int segment, unsigned bus_nr)
: Dwc_pcie(segment, bus_nr, this, nullptr)
{
set_name_if_empty("pcie_imx8");
register_property("gpio3", &_gpio3); // mandatory
register_property("irq_pin_a", &_int_map[0]); // optional
register_property("irq_pin_b", &_int_map[1]); // optional
register_property("irq_pin_c", &_int_map[2]); // optional
register_property("irq_pin_d", &_int_map[3]); // optional
}
void
Pcie_imx8_bridge::init()
{
if (Dwc_pcie::host_init())
return;
if (_gpio3.dev() == nullptr)
{
d_printf(DBG_ERR, "%s: error: 'gpio3' not set.\n", name());
throw "Pcie_imx8_bridge init error";
}
l4_addr_t va = res_map_iomem(_regs_base + Hsio_lpcg_pciea, 0x10000);
_hsio_lpcg_pciea = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_regs_base + Hsio_lpcg_phyx2, 0x10000);
_hsio_lpcg_phyx2 = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_regs_base + Hsio_lpcg_crr_0, 0x10000);
_hsio_lpcg_crr_0 = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_regs_base + Hsio_lpcg_crr_2, 0x10000);
_hsio_lpcg_crr_2 = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_regs_base + Hsio_lpcg_crr_5, 0x10000);
_hsio_lpcg_crr_5 = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_regs_base + Hsio_csr_phyx2, 0x10000);
_hsio_csr_phyx2 = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_regs_base + Hsio_csr_pciea, 0x10000);
_hsio_csr_pciea = new L4drivers::Mmio_register_block<32>(va);
va = res_map_iomem(_regs_base + Hsio_csr_misc, 0x10000);
_hsio_csr_misc = new L4drivers::Mmio_register_block<32>(va);
assert_core_reset();
// imx8_pcie_init_phy -- PCIEAX1PCIEBX1SATA
_hsio_csr_phyx2[Phyx2_ctrl0].set(Phyx2_ctrl0_apb_rstn_0 // APB_RSTN_1=1
| Phyx2_ctrl0_apb_rstn_1); // APB_RSTN_0=1
_hsio_csr_misc[Misc_ctrl0].set(Misc_ctrl0_phy_x1_epcs_sel);
_hsio_csr_misc[Misc_ctrl0].set(Misc_ctrl0_pcie_ab_select);
// ext_osc=1
_hsio_csr_misc[Misc_ctrl0].set(Misc_ctrl0_iob_rxena);
_hsio_csr_misc[Misc_ctrl0].clear(Misc_ctrl0_iob_txena);
_hsio_csr_pciea[Pciex1_ctrl0].modify(Pciex1_ctrl0_dev_type_mask,
Pciex1_ctrl0_dev_type_root_port);
deassert_core_reset();
wait_for_pll_lock();
setup_rc();
// Now establish the PCIe link. Start with Gen1 operation mode.
_regs[Lcr].modify(Pcie_cap_max_link_speed_mask, Pcie_cap_max_link_speed_gen1);
// enable ltssm: APP_LTSSM_ENABLE=1
_hsio_csr_pciea[Pciex1_ctrl2].set(Pciex1_ctrl2_app_ltssm_enable);
wait_link_up();
// DEFAULT_GEN2_N_FTS=3 (number of fast training sequences during link
// training)
_regs[Gen2].modify(0, 3U << 0);
wait_link_up();
d_printf(DBG_WARN, "%s: Link %s\n", name(), link_up() ? "up" : "DOWN");
typedef Hw::Pci::Irq_router_res<Pci_irq_router_rs> Irq_res;
Irq_res *ir = new Irq_res();
ir->set_id("IRQR");
add_resource_rq(ir);
discover_bus(this, this);
Hw::Device::init();
}
void
Pcie_imx8_bridge::assert_core_reset()
{
// We could also do this with using some property mechanism.
// hsio_phyx2_pclk_0 / bits 0+1
_hsio_lpcg_phyx2[0x0].modify(3U << 0, 2U << 0);
l4_sleep(3);
// hsio_pciea_slv_axi_clk / bits 20+21 (see device tree pciea_lpcg clock 1)
_hsio_lpcg_pciea[0x0].modify(3U << 20, 2U << 20);
l4_sleep(3);
// hsio_pciea_mstr_axi_clk / bits 16+17 (see device tree pciea_lpcg clock 0)
_hsio_lpcg_pciea[0x0].modify(3U << 16, 2U << 16);
l4_sleep(3);
// hsio_pciea_dbi_axi_clk / bits 24+25 (see device tree pciea_lpcg clock 2)
_hsio_lpcg_pciea[0x0].modify(3U << 24, 2U << 24);
l4_sleep(3);
// hsio_pciea_per_clk / bits 16+17 (see device tree pciea_crr2_lpcg clock 0)
_hsio_lpcg_crr_2[0x0].modify(3U << 16, 2U << 16);
l4_sleep(3);
// hsio_phyx2_per_clk / bits 16+17 (see device tree phyx2_crr0_lpcg clock 0)
_hsio_lpcg_crr_0[0x0].modify(3U << 16, 2U << 16);
l4_sleep(3);
// hsio_misc_per_clk / bits 16+17 (see device tree misc_crr5_lpcg clock 0)
_hsio_lpcg_crr_5[0x0].modify(3U << 16, 2U << 16);
l4_sleep(3);
// CLKREQ_0=0
_hsio_csr_misc[Misc_ctrl0].clear(Misc_ctrl0_clkreqn_out_0);
// CLKREQ_OVERRIDE_EN_0=1
_hsio_csr_misc[Misc_ctrl0].set(Misc_ctrl0_clkreqn_out_override);
// BUTTON_RST_N=1
_hsio_csr_pciea[Pciex1_ctrl2].set(Pciex1_ctrl2_button_rst_n);
// PERST_N=1
_hsio_csr_pciea[Pciex1_ctrl2].set(Pciex1_ctrl2_perst_n);
// POWER_UP_RST_N=1
_hsio_csr_pciea[Pciex1_ctrl2].set(Pciex1_ctrl2_power_up_rst_n);
}
void
Pcie_imx8_bridge::deassert_core_reset()
{
for (unsigned retries = 0; retries < 10; ++retries)
{
if (!(_hsio_csr_pciea[Pciex1_stts0] & Pciex_stts0_rm_req_cor_rst))
break;
l4_sleep(2);
}
if (_hsio_csr_pciea[Pciex1_stts0] & Pciex_stts0_rm_req_cor_rst)
d_printf(DBG_INFO, "%s: PM_REQ_CORE_RST still set!\n", name());
wait_for_pll_lock();
// Reset phy
Hw::Gpio_chip *gpio3(dynamic_cast<Hw::Gpio_chip*>(_gpio3.dev()));
gpio3->setup(14, Hw::Gpio_chip::Fix_mode::Output, 0);
gpio3->set(14, 0); // reset-gpio: 14/GPIO_ACTIVE_LOW assert
l4_sleep(100);
gpio3->set(14, 1); // reset-gpio: 14/GPIO_ACTIVE_LOW de-assert
}
// This device requires access to the System Control Unit (SCU). The
// Scu_device::init() function performs the configured SCU operations
// (enabling power, pads, clks) before calling our init() function.
class Pcie_imx8qm : public Hw::Scu_device<Pcie_imx8_bridge>
{};
static Hw::Device_factory_t<Pcie_imx8qm> f("Pcie_imx8qm");
}

View File

@@ -0,0 +1,648 @@
/*
* Copyright (C) 2019-2024 Kernkonzept GmbH.
* Author(s): Frank Mehnert <frank.mehnert@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* Driver for the PCIe controller of RCar-3 boards.
*
* The Linux device tree for such devices looks like this:
*
* \code{.dts}
* pciec0: pcie@fe000000 {
* compatible = "renesas,pcie-r8a7795",
* "renesas,pcie-rcar-gen3";
* reg = <0 0xfe000000 0 0x80000>;
* #address-cells = <3>;
* #size-cells = <2>;
* bus-range = <0x00 0xff>;
* device_type = "pci";
* ranges = <0x01000000 0 0x00000000 0 0xfe100000 0 0x00100000
* 0x02000000 0 0xfe200000 0 0xfe200000 0 0x00200000
* 0x02000000 0 0x30000000 0 0x30000000 0 0x08000000
* 0x42000000 0 0x38000000 0 0x38000000 0 0x08000000>;
* dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 0 0x40000000>;
* interrupts = <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>,
* <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>,
* <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
* #interrupt-cells = <1>;
* interrupt-map-mask = <0 0 0 0>;
* interrupt-map = <0 0 0 0 &gic GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>;
* ...
* };
* \endcode
*
* This entry should be converted into the following io.cfg code:
*
* \code{.lua}
* pciec0 = Io.Hw.Rcar3_pcie_bridge(function()
* Property.regs_base = 0xfe000000; -- controller registers, see 'reg'
* Property.regs_size = 0x00080000;
* Property.mem_base_1 = 0xfe100000; -- 1st window, see 'ranges'
* Property.mem_size_1 = 0x00100000;
* Property.mem_base_2 = 0xfe200000; -- 2nd window, see 'ranges'
* Property.mem_size_2 = 0x00200000;
* Property.mem_base_3 = 0x30000000; -- 3rd window, see 'ranges'
* Property.mem_size_3 = 0x08000000;
* Property.mem_base_4 = 0x38000000; -- 4th window, see 'ranges'
* Property.mem_size_4 = 0x08000000;
* Property.irq = 32 + 116; -- 1st interrupt, see 'interrupts'
* end)
* \endcode
*
*/
#include "cpg_rcar3.h"
#include "hw_device.h"
#include <pci-root.h>
#include "resource_provider.h"
#include "pcie_rcar3_regs.h"
#include <l4/drivers/hw_mmio_register_block>
#include <l4/re/error_helper>
#include <l4/util/util.h>
#include <cstdint>
enum
{
// More investigation required.
Enable_msi = 0
};
namespace {
using namespace Pcie_rcar3_regs;
class Rcar3_pcie_bridge
: public Hw::Device,
public Hw::Pci::Root_bridge
{
public:
Rcar3_pcie_bridge(int segment = 0, unsigned bus_nr = 0)
: Hw::Device(),
Hw::Pci::Root_bridge(segment, bus_nr, this, nullptr)
{
// the set of mandatory properties
register_property("regs_base", &_regs_base);
register_property("regs_size", &_regs_size);
register_property("mem_base_1", &_mem_base_1);
register_property("mem_size_1", &_mem_size_1);
register_property("mem_base_2", &_mem_base_2);
register_property("mem_size_2", &_mem_size_2);
register_property("mem_base_3", &_mem_base_3);
register_property("mem_size_3", &_mem_size_3);
register_property("mem_base_4", &_mem_base_4);
register_property("mem_size_4", &_mem_size_4);
register_property("cpg_base", &_cpg_base);
register_property("cpg_reg_num", &_cpg_reg_num);
register_property("cpg_reg_bit", &_cpg_reg_bit);
register_property("irq", &_interrupt);
set_name("Rcar3 PCIe root bridge");
}
typedef Hw::Pci::Cfg_addr Cfg_addr;
typedef Hw::Pci::Cfg_width Cfg_width;
void init() override;
int cfg_read(Cfg_addr addr, l4_uint32_t *value, Cfg_width) override;
int cfg_write(Cfg_addr addr, l4_uint32_t value, Cfg_width) override;
int interrupt() const { return _interrupt; }
private:
int host_init();
int access_enable(Cfg_addr addr, Cfg_width width);
void access_disable(Cfg_addr addr);
void alloc_msi_page(void **virt, l4_addr_t *phys);
void init_msi();
Int_property _regs_base{~0}; // mandatory
Int_property _regs_size{~0}; // mandatory
Int_property _mem_base_1{~0}; // mandatory
Int_property _mem_size_1{~0}; // mandatory
Int_property _mem_base_2{~0}; // mandatory
Int_property _mem_size_2{~0}; // mandatory
Int_property _mem_base_3{~0}; // mandatory
Int_property _mem_size_3{~0}; // mandatory
Int_property _mem_base_4{~0}; // mandatory
Int_property _mem_size_4{~0}; // mandatory
Int_property _cpg_base{0xe6150000}; // optional
Int_property _cpg_reg_num{~0}; // optional
Int_property _cpg_reg_bit{~0}; // optional
Int_property _interrupt{~0}; // mandatory
// PCI root bridge core memory.
L4drivers::Register_block<32> _regs;
L4Re::Util::Unique_cap<L4Re::Dataspace> _ds_msi;
};
// return upper 32-bit part of a 64-bit value
static inline unsigned u64_hi(l4_uint64_t u64)
{ return u64 >> 32; }
// return lower 32-bit part of a 64-bit value
static inline unsigned u64_lo(l4_uint64_t u64)
{ return u64 & 0xffffffff; }
class Irq_router_rs : public Resource_space
{
public:
char const *res_type_name() const override
{ return "RCar3 PCIe IRQ router"; }
bool request(Resource *parent, ::Device *pdev,
Resource *child, ::Device *cdev) override
{
auto *cd = dynamic_cast<Hw::Device *>(cdev);
if (!cd)
return false;
unsigned pin = child->start();
auto *pd = dynamic_cast<Rcar3_pcie_bridge *>(pdev);
if (!pd)
return false;
int irq_nr = pd->interrupt();
if (irq_nr < 0)
return false;
d_printf(DBG_ERR, "%s/%08x Requesting IRQ%c => IRQ %d\n",
cd->get_full_path().c_str(), cd->adr(),
(int)('A' + pin), irq_nr);
child->del_flags(Resource::F_relative);
child->start(irq_nr);
child->del_flags(Resource::Irq_type_mask);
child->add_flags(Resource::Irq_type_base | Resource::Irq_type_level_high);
child->parent(parent);
return true;
}
bool alloc(Resource *, ::Device *, Resource *, ::Device *, bool) override
{ return false; }
void assign(Resource *, Resource *) override
{
d_printf(DBG_ERR, "internal error: cannot assign to Irq_router_rs\n");
}
bool adjust_children(Resource *) override
{
d_printf(DBG_ERR, "internal error: cannot adjust root Irq_router_rs\n");
return false;
}
};
int
Rcar3_pcie_bridge::host_init()
{
if ( assert_property(&_regs_base, "regs_base", ~0)
|| assert_property(&_regs_size, "regs_size", ~0)
|| assert_property(&_mem_base_1, "mem_base_1", ~0)
|| assert_property(&_mem_size_1, "mem_size_1", ~0)
|| assert_property(&_mem_base_2, "mem_base_2", ~0)
|| assert_property(&_mem_size_2, "mem_size_2", ~0)
|| assert_property(&_mem_base_3, "mem_base_3", ~0)
|| assert_property(&_mem_size_3, "mem_size_3", ~0)
|| assert_property(&_mem_base_4, "mem_base_4", ~0)
|| assert_property(&_mem_size_4, "mem_size_4", ~0)
|| assert_property(&_interrupt, "irq", ~0))
return -L4_EINVAL;
l4_addr_t va = res_map_iomem(_regs_base, _regs_size);
if (!va)
{
d_printf(DBG_ERR, "error: %s: could not map core memory.\n", name());
return -L4_ENOMEM;
}
_regs = new L4drivers::Mmio_register_block<32>(va);
Rcar3_cpg cpg(_cpg_base);
if (_cpg_reg_num == ~0 && _cpg_reg_bit == ~0)
{
_cpg_reg_num.set(-1, 3); // pcie0 or pcie1
if (_regs_base == 0xfe000000)
_cpg_reg_bit.set(-1, 19); // pcie0
else if (_regs_base == 0xee800000)
_cpg_reg_bit.set(-1, 18); // pcie1
else
{
d_printf(DBG_ERR, "error: unknown PCIe controller at %08llx -- fix CPG code!\n",
_regs_base.val());
return -L4_EINVAL;
}
}
int ret = cpg.enable_clock(_cpg_reg_num, _cpg_reg_bit);
if (ret != L4_EOK)
{
d_printf(DBG_ERR,
"error: %s: couldn't enable PCIe controller at CPG (%s)!\n",
name(), l4sys_errtostr(ret));
return ret;
}
// set PCIe inbound ranges
_regs[Pcie_prar0] = 0x40000000;
_regs[Pcie_lar0] = 0x40000000;
_regs[Pcie_lamr0] = Pcie_lamr_1gb // local address mask 1GiB
| Pcie_lamr_mmio // MMIO
| Pcie_lamr_laren // local address enable
| Pcie_lamr_mmio64bit // 64-bit addresses used
| Pcie_lamr_mmiopref; // prefetchable MMIO
// clear because of Pcie_lamr0.Pcie_lamr_mmio64bit=1
_regs[Pcie_prar1] = 0x00000000;
_regs[Pcie_lar1] = 0x00000000;
_regs[Pcie_lamr1] = 0x00000000;
// ====== BEGIN initialization ======
_regs[Pcie_tctlr] = Pcie_tctlr_initstrt;
// PCIe mode: Type 01 (root port)
_regs[Pcie_msr] = Pcie_msr_rootport;
// wait for PHY ready
for (unsigned i = 0; i < 20 && !(_regs[Pcie_physr] & 1); ++i)
l4_sleep(10);
if (!(_regs[Pcie_physr] & 1))
{
d_printf(DBG_ERR, "error: %s: PHY not ready!\n", name());
return -L4_ENXIO;
}
// ID setting register 1 (value reflected to Pciconf2)
_regs[Idsetr1] = 0x6040000;
// type01: secondary bus no: 1
_regs[Pciconf6].modify(0x0000ff00, 0x00000100);
// type01: subordinate bus no: 1
_regs[Pciconf6].modify(0x00ff0000, 0x00010000);
// PCIe capability list ID (0x10 fix)
_regs[Expcap0].modify(0x000000ff, Hw::Pci::Cap::Pcie);
// device port type: 4=root port of PCIe root complex
_regs[Expcap0].modify(0x00f00000, 0x00400000);
// root port => type01 layout
_regs[Pciconf3].modify(0x007f0000, 0x00010000);
// bit 20: Data link layer active reporting capable
_regs[Expcap3].modify(0x00100000, 0x00100000);
// physical slot number: 0 (actually "invalid" with RCar3)
_regs[Expcap5].modify(0xfff80000, 0x00000000);
// completion timer: 50ms
_regs[Tlctlr].modify(0x00003f00, 0x00003200);
// terminate cap list
_regs[Vccap0].modify(0xfff00000, 0x00000000);
if (Enable_msi)
{
// MSI: enable
_regs[Pcie_msitxr] = Pcie_msitxr_msie
| (0x1f << Pcie_msitxr_mmenum_shft);
}
// IO / mmio /prefetchable memory disabled
_regs[Pciconf7] = 0x000000f0; // no IO (base > limit)
_regs[Pciconf8] = 0x0000fff0; // no memory (base > limit)
_regs[Pciconf9] = 0x0000fff0; // no prefetchable memory (base > limit)
// Finish initialization -- establish PCIe link
// At this point the hardware sets the BARn registers based on PcielamrX.
_regs[Pcie_tctlr] = Pcie_tctlr_initdone;
// ====== DONE initialization ======
// setup resource window 0
_regs[Pcie_ptctlr0] = 0;
_regs[Pcie_pamr0] = (_mem_size_1 - 1) & ~0x7f;
_regs[Pcie_paur0] = u64_hi(_mem_base_1);
_regs[Pcie_palr0] = u64_lo(_mem_base_1);
_regs[Pcie_ptctlr0] = Pcie_ptctlr_pare | Pcie_ptctlr_spcio;
// setup resource window 1
_regs[Pcie_ptctlr1] = 0;
_regs[Pcie_pamr1] = (_mem_size_2 - 1) & ~0x7f;
_regs[Pcie_paur1] = u64_hi(_mem_base_2);
_regs[Pcie_palr1] = u64_lo(_mem_base_2);
_regs[Pcie_ptctlr1] = Pcie_ptctlr_pare | Pcie_ptctlr_spcmmio;
// setup resource window 2
_regs[Pcie_ptctlr2] = 0;
_regs[Pcie_pamr2] = (_mem_size_3 - 1) & ~0x7f;
_regs[Pcie_paur2] = u64_hi(_mem_base_3);
_regs[Pcie_palr2] = u64_lo(_mem_base_3);
_regs[Pcie_ptctlr2] = Pcie_ptctlr_pare | Pcie_ptctlr_spcmmio;
// setup resource window 3
_regs[Pcie_ptctlr3] = 0;
_regs[Pcie_pamr3] = (_mem_size_4 - 1) & ~0x7f;
_regs[Pcie_paur3] = u64_hi(_mem_base_4);
_regs[Pcie_palr3] = u64_lo(_mem_base_4);
_regs[Pcie_ptctlr3] = Pcie_ptctlr_pare | Pcie_ptctlr_spcmmio;
// MSI??
// _regs[Pciconf8] = 0x30403000;
// _regs[Pciconf9] = 0x0000fff0;
// wait until PCIe link is up
for (unsigned i = 0; i < 20; ++i)
{
if (_regs[Pcie_tstr] & Pcie_tstr_dllact)
{
d_printf(DBG_INFO, "%s: link up.\n", name());
// Remember, we are in root port mode:
// INTA enable (apparently INTB/INTC/INTD are not used)
_regs[Pcie_intxr].modify(0x0000ff00, 0x00000100);
// bit 15..8: interrupt pin but cannot be written to anything != 0
// bit 7..0: set interrupt line to 0x00: INTx interrupt reported to
// INTC(why INTC and not INTA?). The default value is 0xff
// which means that no interrupt is reported.
_regs[Pciconf15].modify(0x000000ff, 0x00000000);
return L4_EOK;
}
l4_sleep(10);
}
d_printf(DBG_INFO, "%s: link down.\n", name());
return -L4_ENXIO;
}
/**
* Helper for cfg_read()/cfg_write().
*/
int
Rcar3_pcie_bridge::access_enable(Cfg_addr addr, Cfg_width width)
{
unsigned reg = addr.reg() & ~3;
// crossing 32-bit addresses!
if (reg != ((addr.reg() + (1 << width) - 1) & ~3))
return -EIO;
// root device is the PCI bridge itself
if (addr.bus() == 0)
return 0;
_regs[Pcie_errfr].modify(0, 0); // clear errors
_regs[Pcie_car] = ((addr.bus() & 0xff) << 24)
| ((addr.dev() & 0x1f) << 19)
| ((addr.fn() & 7) << 16)
| reg;
if (addr.dev() != 0)
// type-1 access (bus+dev+fn+reg)
// Actually that makes only sense if a PCIe-to-PCIe bridge is plugged into
// the PCIe slot.
_regs[Pcie_cctlr] = Pcie_cctlr_ccie | Pcie_cctlr_type;
else
// type-0 access (only fn+reg)
_regs[Pcie_cctlr] = Pcie_cctlr_ccie;
// check errors for "unsupported request"
if (_regs[Pcie_errfr] & Pcie_errfr_rcvurcpl)
return -EIO;
// check for "master/target abort"
if (_regs[Pciconf1] & (Pciconf1_rma|Pciconf1_rta))
return -EIO;
return 0;
}
/**
* Helper for cfg_read()/cfg_write().
*/
void
Rcar3_pcie_bridge::access_disable(Cfg_addr addr)
{
if (addr.bus() != 0)
_regs[Pcie_cctlr] = 0;
}
void
Rcar3_pcie_bridge::alloc_msi_page(void **virt, l4_addr_t *phys)
{
_ds_msi = L4Re::Util::make_unique_cap<L4Re::Dataspace>();
L4Re::chksys(L4Re::Env::env()->mem_alloc()
->alloc(L4_PAGESIZE, _ds_msi.get(),
L4Re::Mem_alloc::Continuous));
auto d = L4Re::chkcap(L4Re::Util::make_unique_cap<L4Re::Dma_space>(),
"Allocate DMA space cap");
L4Re::chksys(L4Re::Env::env()->user_factory()->create(d.get()),
"Create DMA space");
L4Re::chksys(d->associate(L4::Ipc::Cap<L4::Task>(),
L4Re::Dma_space::Space_attrib::Phys_space),
"Associate DMA space for CPU physical");
l4_size_t phys_size;
L4Re::Dma_space::Dma_addr phys_ram = 0;
L4Re::chksys(d->map(L4::Ipc::make_cap_rw(_ds_msi.get()), 0, &phys_size,
L4Re::Dma_space::Attributes::None,
L4Re::Dma_space::Bidirectional, &phys_ram),
"Map dataspace to DMA space");
if (phys_ram < L4_PAGESIZE)
throw(L4::Out_of_memory("not really"));
*virt = 0;
L4Re::chksys(L4Re::Env::env()->rm()
->attach(virt, L4_PAGESIZE,
L4Re::Rm::F::Search_addr | L4Re::Rm::F::Eager_map
| L4Re::Rm::F::RW,
L4::Ipc::make_cap_rw(_ds_msi.get())),
"Attach dataspace");
*phys = phys_ram;
d_printf(DBG_INFO, "%s: alloc_msi_page: virt=%08lx phys=%08lx\n",
name(), (unsigned long)*virt, *phys);
}
void
Rcar3_pcie_bridge::init_msi()
{
void *virt;
l4_addr_t phys;
alloc_msi_page(&virt, &phys);
_regs[Pcie_msialr] = u64_lo(phys) | 1;
_regs[Pcie_msiaur] = u64_hi(phys);
_regs[Pcie_msiier] = 0xffffffff;
}
int
Rcar3_pcie_bridge::cfg_read(Cfg_addr addr, l4_uint32_t *value, Cfg_width width)
{
uint32_t v;
if (access_enable(addr, width) < 0)
v = 0xffffffff;
else
{
if (addr.bus() == 0)
{
if (addr.dev() != 0)
v = 0xffffffff;
else
v = _regs[Pciconf0 + (addr.reg() & ~3)];
}
else
v = _regs[Pcie_cdr];
}
access_disable(addr);
switch (width)
{
case Hw::Pci::Cfg_long:
*value = v;
break;
case Hw::Pci::Cfg_short:
*value = (v >> ((addr.reg() & 2) << 3)) & 0xffff;
break;
case Hw::Pci::Cfg_byte:
*value = (v >> ((addr.reg() & 3) << 3)) & 0xff;
break;
default:
return -EIO;
}
d_printf(DBG_ALL,
"%s: cfg_read addr=%02x:%02x.%x reg=%03x width=%2d-bit value=%0*x\n",
name(), addr.bus(), addr.dev(), addr.fn(), addr.reg(), 8 << width,
2 << width, *value & cfg_o_to_mask(width));
return 0;
}
int
Rcar3_pcie_bridge::cfg_write(Cfg_addr addr, l4_uint32_t value, Cfg_width width)
{
d_printf(DBG_ALL,
"%s: cfg_wrte addr=%02x:%02x.%x reg=%03x width=%2d-bit value=%0*x\n",
name(), addr.bus(), addr.dev(), addr.fn(), addr.reg(), 8 << width,
2 << width, value & cfg_o_to_mask(width));
if (access_enable(addr, width) < 0)
return -EIO;
if (addr.bus() == 0 && addr.dev() != 0)
return 0;
uint32_t mask, shift;
switch (width)
{
case Hw::Pci::Cfg_long:
shift = 0;
mask = 0xffffffff;
break;
case Hw::Pci::Cfg_short:
shift = (addr.reg() & 2) << 3;
mask = 0xffff << shift;
break;
case Hw::Pci::Cfg_byte:
shift = (addr.reg() & 3) << 3;
mask = 0xff << shift;
break;
default:
return -EIO;
}
if (addr.bus() == 0)
_regs[Pciconf0 + (addr.reg() & ~3)].modify(mask, (value << shift) & mask);
else
_regs[Pcie_cdr].modify(mask, (value << shift) & mask);
access_disable(addr);
return 0;
}
void
Rcar3_pcie_bridge::init()
{
if (host_init())
return;
d_printf(DBG_INFO, "%s: new device.\n", name());
if (Enable_msi)
{
try
{
init_msi();
}
// Errors during MSI initialization are fatal. This could change in the
// future.
catch (L4::Runtime_error &e)
{
if (e.extra_str() && e.extra_str()[0] != '\0')
dprintf(DBG_ERR, "%s: %s: %s\n", name(), e.extra_str(), e.str());
else
dprintf(DBG_ERR, "%s: %s\n", name(), e.str());
return;
}
}
Resource *mr;
// Ignore I/O ports in _mem_base_1 / _mem_size_1 as we don't support IO ports
// on ARM anyway (at least for now).
mr = new Resource_provider(Resource::Mmio_res | Resource::Mem_type_rw);
mr->start_size(_mem_base_2, _mem_size_2);
mr->alignment(0xfffff);
mr->set_id("MMIO");
add_resource_rq(mr);
mr = new Resource_provider(Resource::Mmio_res | Resource::Mem_type_rw);
mr->start_size(_mem_base_3, _mem_size_3);
mr->alignment(0xfffff);
mr->set_id("MMIO");
add_resource_rq(mr);
mr = new Resource_provider(Resource::Mmio_res | Resource::Mem_type_rw
| Resource::F_prefetchable);
mr->start_size(_mem_base_4, _mem_size_4);
mr->alignment(0xfffff);
mr->set_id("MMIO");
add_resource_rq(mr);
auto *ir = new Hw::Pci::Irq_router_res<Irq_router_rs>();
ir->set_id("IRQR");
add_resource_rq(ir);
discover_bus(this, this);
Hw::Device::init();
// Enable the bus master bit at the PCI host bridge.
l4_uint32_t cmd;
cfg_read(Cfg_addr(0, 0, 0, 0x04), &cmd, Hw::Pci::Cfg_short);
cmd |= Hw::Pci::Dev::CC_bus_master;
cfg_write(Cfg_addr(0, 0, 0, 0x04), cmd, Hw::Pci::Cfg_short);
}
static Hw::Device_factory_t<Rcar3_pcie_bridge> f("Rcar3_pcie_bridge");
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2019-2020, 2024 Kernkonzept GmbH.
* Author(s): Frank Mehnert <frank.mehnert@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
namespace Pcie_rcar3_regs {
enum
{
Pcie_car = 0x0010, // configuration transmission address
Pcie_cctlr = 0x0018, // configuration transmission control
Pcie_cctlr_ccie = 1U << 31,
Pcie_cctlr_type = 1 << 8,
Pcie_cdr = 0x0020, // configuration transmission data
Pcie_msr = 0x0028, // mode setting
Pcie_msr_endpoint = 0 << 0,
Pcie_msr_rootport = 1 << 0,
Pcie_intxr = 0x0400, // INTx register
Pcie_physr = 0x07f0, // physical layer status
Pcie_msitxr = 0x0840, // MSI transmission
Pcie_msitxr_msie = 1U << 31,
Pcie_msitxr_mmenum_shft = 16,
Pcie_tctlr = 0x2000, // transfer control register
Pcie_tctlr_initstrt = 0 << 0, // start initialization
Pcie_tctlr_initdone = 1 << 0, // configuration initialization done
Pcie_tstr = 0x2004, // transfer status register
Pcie_tstr_dllact = 1 << 0,
Pcie_intr = 0x2008, // interrupt register
Pcie_errfr = 0x2020, // error factor register
Pcie_errfr_rcvurcpl = 1 << 4,
Pcie_msialr = 0x2048, // MSI address lower register
Pcie_msiaur = 0x204c, // MSI address upper register
Pcie_msiier = 0x2050, // MSI interrupt enable register
Pcie_prar0 = 0x2080, // root port address register 0
Pcie_prar1 = 0x2084, // root port address register 1
Pcie_prar2 = 0x2088, // root port address register 2
Pcie_prar3 = 0x208c, // root port address register 3
Pcie_prar4 = 0x2090, // root port address register 4
Pcie_prar5 = 0x2094, // root port address register 5
Pcie_lar0 = 0x2200, // local address register 0
Pcie_lamr0 = 0x2208, // local address mask register 0
Pcie_lamr_16 = 0 << 4,
Pcie_lamr_32 = ((1 << 1) - 1) << 4,
Pcie_lamr_64 = ((1 << 2) - 1) << 4,
Pcie_lamr_128 = ((1 << 3) - 1) << 4,
Pcie_lamr_256 = ((1 << 4) - 1) << 4,
Pcie_lamr_512 = ((1 << 5) - 1) << 4,
Pcie_lamr_256mb = ((1 << 24) - 1) << 4,
Pcie_lamr_512mb = ((1 << 25) - 1) << 4,
Pcie_lamr_1gb = ((1 << 26) - 1) << 4,
Pcie_lamr_2gb = ((1 << 27) - 1) << 4,
Pcie_lamr_4gb = ((1 << 28) - 1) << 4,
Pcie_lamr_io = 1 << 0,
Pcie_lamr_mmio = 0 << 0,
Pcie_lamr_laren = 1 << 1,
Pcie_lamr_mmio32bit = 0 << 2,
Pcie_lamr_mmio64bit = 1 << 2,
Pcie_lamr_mmiopref = 1 << 3,
Pcie_lamr_mmionpref = 0 << 3,
Pcie_lamr_io16b = 1 << 3,
Pcie_lamr_io8b = 1 << 2,
Pcie_lamr_io4b = 0 << 2,
Pcie_lar1 = 0x2220, // local address register 1
Pcie_lamr1 = 0x2228, // local address mask register 1
Pcie_lar2 = 0x2240, // local address register 2
Pcie_lamr2 = 0x2248, // local address mask register 2
Pcie_lar3 = 0x2260, // local address register 3
Pcie_lamr3 = 0x2268, // local address mask register 3
Pcie_palr0 = 0x3400, // PCIEC address lower register 0
Pcie_paur0 = 0x3404, // PCIEC address upper register 0
Pcie_pamr0 = 0x3408, // PCIEC address mask register 0
Pcie_ptctlr0 = 0x340c, // PCIEC conversion control register 0
Pcie_ptctlr_pare = 1 << 31,
Pcie_ptctlr_spcio = 1 << 8,
Pcie_ptctlr_spcmmio = 0 << 8,
Pcie_palr1 = 0x3420, // PCIEC address lower register 1
Pcie_paur1 = 0x3424, // PCIEC address upper register 1
Pcie_pamr1 = 0x3428, // PCIEC address mask register 1
Pcie_ptctlr1 = 0x342c, // PCIEC conversion control register 1
Pcie_palr2 = 0x3440, // PCIEC address lower register 2
Pcie_paur2 = 0x3444, // PCIEC address upper register 2
Pcie_pamr2 = 0x3448, // PCIEC address mask register 2
Pcie_ptctlr2 = 0x344c, // PCIEC conversion control register 2
Pcie_palr3 = 0x3460, // PCIEC address lower register 3
Pcie_paur3 = 0x3464, // PCIEC address upper register 3
Pcie_pamr3 = 0x3468, // PCIEC address mask register 3
Pcie_ptctlr3 = 0x346c, // PCIEC conversion control register 3
// PCI configuration registers (of the root bridge). See also PCI spec
// "Configuration Space" but the RCar3 boards add more registers / bits.
Pciconf0 = 0x10000, // vendor ID + device ID
Pciconf1 = 0x10004, // command + status
Pciconf1_parerr = 1 << 31, // detected parity error
Pciconf1_err = 1 << 30, // signaled system error
Pciconf1_rma = 1 << 29, // received master abort
Pciconf1_rta = 1 << 28, // received target abort
Pciconf1_sta = 1 << 27, // signaled target abort
Pciconf1_mdpe = 1 << 24, // master data parity error
Pciconf1_fbtc = 1 << 23, // fast back-to-back transaction cpbl
Pciconf2 = 0x10008, // revision ID + class code
Pciconf3 = 0x1000c, // cache line size, latency timer etc
Pciconf4 = 0x10010, // base address register 0
Pciconf5 = 0x10014, // base address register 1
Pciconf6 = 0x10018, // primary/secondary/subordinate busnr
Pciconf7 = 0x1001c, // IO base/limit + secondary status
Pciconf8 = 0x10020, // memory base / limit
Pciconf9 = 0x10024, // prefetchable mem base / limit
Pciconf10 = 0x10028, // prefetchable mem base upper 32-bit
Pciconf11 = 0x1002c, // prefetchable mem limit upper 32-bit
Pciconf12 = 0x10030, // IO base/limit upper 16-bit
Pciconf13 = 0x10034, // capabilities pointer
Pciconf14 = 0x10038, // expansion ROM
Pciconf15 = 0x1003c, // int line + int pin + bridge control
// PCI power management
Pmcap0 = 0x10040,
Pmcap1 = 0x10044,
// MSI capability registers
Msicap0 = 0x10050,
Msicap1 = 0x10054,
Msicap2 = 0x10058,
Msicap3 = 0x1005c,
Msicap4 = 0x10060,
Msicap5 = 0x10064,
// PCIe capability registers
Expcap0 = 0x10070,
Expcap1 = 0x10074,
Expcap2 = 0x10078,
Expcap3 = 0x1007c,
Expcap4 = 0x10080,
Expcap5 = 0x10084,
Expcap6 = 0x10088,
Expcap7 = 0x1008c,
Expcap8 = 0x10090,
Expcap9 = 0x10094,
Expcap10 = 0x10098,
Expcap11 = 0x1009c,
Expcap12 = 0x100a0,
Expcap13 = 0x100a4,
Expcap14 = 0x100a8,
// VC capability registers
Vccap0 = 0x10100,
Vccap1 = 0x10104,
Vccap2 = 0x10108,
Vccap3 = 0x1010c,
Vccap4 = 0x10110,
Vccap5 = 0x10114,
Vccap6 = 0x10118,
// PCIEC link layer control registers
Idsetr0 = 0x11000, // ID setting register 0
Idsetr1 = 0x11004, // ID setting register 1
Tlctlr = 0x11048, // TL control register
};
} // namespace Pcie_rcar3_regs

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2021-2024 Kernkonzept GmbH.
* Author(s): Christian Pötzsch <christian.poetzsch@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "scu_imx8qm.h"
namespace Hw {
void Scu_imx8qm::init()
{
if (_initialized)
return;
_initialized = true;
Hw::Device::init();
set_name_if_empty("scu");
Resource *regs = resources()->find("regs");
if (!regs || regs->type() != Resource::Mmio_res)
{
d_printf(DBG_ERR, "error: %s: no base address set\n"
" missing or wrong 'regs' resource\n", name());
throw "scu init error";
}
l4_addr_t phys_base = regs->start();
l4_addr_t size = regs->size();
if (size < sizeof(l4_uint32_t) * 4)
{
d_printf(DBG_ERR, "error: %s: invalid mmio size (%lx)\n", name(), size);
throw "scu init error";
}
l4_addr_t vbase = res_map_iomem(phys_base, size);
if (!vbase)
{
d_printf(DBG_ERR, "error: %s: cannot map registers (phys=%lx-%lx)\n",
name(), phys_base, phys_base + size - 1);
throw "scu init error";
}
d_printf(DBG_INFO, "%s: mapped %lx registers to %08lx\n",
name(), phys_base, vbase);
_mu = cxx::make_unique<Mu>(vbase);
for (size_t i = 0; i < _sids.size(); ++i)
{
d_printf(DBG_INFO, "%s: scu smmu: rscs: %u sid: %u\n",
name(), _sids.rscs(i), _sids.sid(i));
rm_set_master_sid(_sids.rscs(i), _sids.sid(i));
}
}
static Hw::Device_factory_t<Scu_imx8qm> __hw_pf_factory("Scu_imx8qm");
}

View File

@@ -0,0 +1,338 @@
/*
* Copyright (C) 2021-2024 Kernkonzept GmbH.
* Author(s): Christian Pötzsch <christian.poetzsch@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "debug.h"
#include "hw_device.h"
#include "resource_provider.h"
#include <l4/cxx/unique_ptr>
#include <l4/drivers/hw_mmio_register_block>
namespace Hw {
class Scu_table_property : public Property
{
protected:
std::vector<unsigned> _table;
public:
int set(int, std::string const &) override { return -EINVAL; }
int set(int, Generic_device *) override { return -EINVAL; }
int set(int, Resource *) override { return -EINVAL; }
int set(int, l4_int64_t i) override
{
_table.push_back(static_cast<unsigned>(i));
return 0;
}
};
class Scu_imx8qm : public Hw::Device
{
public:
enum
{
Pm_pw_mode_off = 0U, /* Power off */
Pm_pw_mode_stby = 1U, /* Power in standby */
Pm_pw_mode_lp = 2U, /* Power in low-power */
Pm_pw_mode_on = 3U, /* Power on */
};
Scu_imx8qm()
{ register_property("sids", &_sids); }
void init() override;
int rm_set_master_sid(l4_uint16_t res, l4_uint16_t sid)
{
init(); // this may be called before init was called
Scu_msg msg(Svc_rm, Func_rm_set_master_sid, 2);
msg.data.r<16>(0) = res;
msg.data.r<16>(2) = sid;
scu_call(&msg, true);
return msg.data.r<8>(0);
}
int pm_set_resource_power_mode(l4_uint16_t res, l4_uint8_t mode)
{
init(); // this may be called before init was called
Scu_msg msg(Svc_pm, Func_pm_set_resource_power_mode, 2);
msg.data.r<16>(0) = res;
msg.data.r<8>(2) = mode;
scu_call(&msg, true);
return msg.data.r<8>(0);
}
int pm_clock_enable(l4_uint16_t res, l4_uint8_t clk, bool enable, bool autog)
{
init(); // this may be called before init was called
Scu_msg msg(Svc_pm, Func_pm_clock_enable, 3);
msg.data.r<16>(0) = res;
msg.data.r<8>(2) = clk;
msg.data.r<8>(3) = enable;
msg.data.r<8>(4) = autog;
scu_call(&msg, true);
return msg.data.r<8>(0);
}
int pad_set(l4_uint16_t pad, l4_uint32_t mux, l4_uint32_t conf)
{
init(); // this may be called before init was called
Scu_msg msg(Svc_pad, Func_pad_set, 3);
msg.data.r<32>(0) = (mux << 27) | conf;
msg.data.r<16>(4) = pad;
scu_call(&msg, true);
return msg.data.r<8>(0);
}
private:
/**
* Smmu sids property
*
* Pairs of rscs and sids to use, e.g:
*
* Property.sids = {
* 248, 1, -- IMX_SC_R_SDHC_0 // emmc
* 249, 1, -- IMX_SC_R_SDHC_1 // sdcard
* 250, 2, -- IMX_SC_R_SDHC_2 // wifi
* }
*/
class Sids_property : public Scu_table_property
{
public:
size_t size() const
{ return _table.size() / 2; }
int rscs(size_t idx) const
{ return _table.at(idx * 2); }
int sid(size_t idx) const
{ return _table.at(idx * 2 + 1); }
};
class Mu
{
enum
{
MU_SR_TE0_MASK1 = 1 << 23,
MU_SR_RF0_MASK1 = 1 << 27,
MU_ATR0_OFFSET1 = 0x0,
MU_ARR0_OFFSET1 = 0x10,
MU_ASR_OFFSET1 = 0x20,
MU_ACR_OFFSET1 = 0x24
};
public:
Mu(l4_addr_t vbase) : _mmio_mu(vbase), _mu(&_mmio_mu) { }
void write_mu(unsigned int regIndex, l4_uint32_t msg)
{
unsigned int mask = MU_SR_TE0_MASK1 >> regIndex;
// Wait for TX to be empty
while (!(_mu[MU_ASR_OFFSET1] & mask))
;
_mu[MU_ATR0_OFFSET1 + (regIndex * 4)] = msg;
}
void read_mu(unsigned int regIndex, l4_uint32_t *msg)
{
unsigned int mask = MU_SR_RF0_MASK1 >> regIndex;
// Wait for RX to be full
while (!(_mu[MU_ASR_OFFSET1] & mask))
;
*msg = _mu[MU_ARR0_OFFSET1 + (regIndex * 4)];
}
private:
L4drivers::Mmio_register_block<32> _mmio_mu;
L4drivers::Register_block<32> _mu;
};
enum
{
Svc_pm = 2U,
Svc_rm = 3U,
Svc_pad = 6U
};
enum
{
Func_pm_set_resource_power_mode = 3U,
Func_pm_clock_enable = 7U,
};
enum { Func_rm_set_master_sid = 11U };
enum { Func_pad_set = 15U };
struct Scu_msg
{
union
{
struct
{
l4_uint8_t ver;
l4_uint8_t size;
l4_uint8_t svc;
l4_uint8_t func;
} h;
l4_uint32_t hdr;
};
// data block
l4_uint32_t d[4];
L4drivers::Mmio_register_block<32> _data{reinterpret_cast<l4_addr_t>(d)};
// view on data block
L4drivers::Register_block<32> data{&_data};
explicit Scu_msg(l4_uint8_t svc, l4_uint8_t func, l4_uint8_t sz)
{
h.ver = 1;
h.svc = svc;
h.size = sz;
h.func = func;
}
};
void scu_call(Scu_msg *msg, bool has_result)
{
// Write header
_mu->write_mu(0, msg->hdr);
unsigned count = 1;
// Write data into the 4 channels
while (count < msg->h.size)
{
_mu->write_mu(count % 4 , msg->d[count - 1]);
count++;
}
if (has_result)
{
// Read header
_mu->read_mu(0, &msg->hdr);
count = 1;
// Read data from the 4 channels
while (count < msg->h.size)
{
_mu->read_mu(count % 4, &msg->d[count - 1]);
count++;
}
}
}
bool _initialized = false;
Sids_property _sids;
cxx::unique_ptr<Mu> _mu;
};
template<class DEV>
class Scu_device : public DEV
{
public:
/**
* Resource power property
*
* Rscs to use, e.g:
*
* Property.power = { 100 }; -- IMX_SC_R_I2C_4
*/
class Scu_power_property : public Scu_table_property
{
public:
size_t size() const
{ return _table.size(); }
unsigned pin(size_t idx) const
{ return _table.at(idx); }
};
/**
* Resource clocks property
*
* Pairs of rscs and clocks to use, e.g:
*
* Property.clks = { 100, 2 }; -- IMX_SC_R_I2C_4, IMX_SC_PM_CLK_PER
*/
class Scu_clks_property : public Scu_table_property
{
public:
size_t size() const
{ return _table.size() / 2; }
unsigned res(size_t idx) const
{ return _table.at(idx * 2); }
unsigned clk(size_t idx) const
{ return _table.at(idx * 2 + 1); }
};
/**
* Resource pads property
*
* Pairs of pads, mux and conf to use, e.g:
*
* Property.pads =
* { 169, 1, 0xc600004c, -- IMX8QM_ENET1_MDIO_DMA_I2C4_SDA
* 170, 1, 0xc600004c }; -- IMX8QM_ENET1_MDC_DMA_I2C4_SCL
*/
class Scu_pads_property : public Scu_table_property
{
public:
size_t size() const
{ return _table.size() / 3; }
unsigned pad(size_t idx) const
{ return _table.at(idx * 3); }
unsigned mux(size_t idx) const
{ return _table.at(idx * 3 + 1); }
unsigned conf(size_t idx) const
{ return _table.at(idx * 3 + 2); }
};
Scu_device()
{
this->register_property("scu", &_scu);
this->register_property("power", &_power);
this->register_property("pads", &_pads);
this->register_property("clks", &_clks);
}
void init() override
{
if (_scu.dev() == nullptr)
{
d_printf(DBG_ERR, "error: %s: 'scu' not set.\n", DEV::name());
throw "Scu_device init error";
}
for (size_t i = 0; i < _power.size(); ++i)
_scu.dev()->pm_set_resource_power_mode(_power.pin(i),
Hw::Scu_imx8qm::Pm_pw_mode_on);
for (size_t i = 0; i < _clks.size(); ++i)
_scu.dev()->pm_clock_enable(_clks.res(i), _clks.clk(i), true, false);
for (size_t i = 0; i < _pads.size(); ++i)
_scu.dev()->pad_set(_pads.pad(i), _pads.mux(i), _pads.conf(i));
DEV::init();
}
protected:
Device_property<Hw::Scu_imx8qm> _scu;
Scu_power_property _power;
Scu_pads_property _pads;
Scu_clks_property _clks;
};
}

View File

@@ -0,0 +1,22 @@
/*
* (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com>
* economic rights: Kerkonzept GmbH (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/re/event>
namespace Io {
struct Event_source_infos
{
L4Re::Event_stream_state state;
L4Re::Event_stream_info info;
};
}

View File

@@ -0,0 +1,256 @@
// vi:ft=cpp
#pragma once
#include "debug.h"
#include "hw_device.h"
#include "resource.h"
#include <l4/vbus/vbus_gpio.h>
class Io_irq_pin;
namespace Hw {
class Gpio_chip
{
public:
/// Modes used for setup()
enum Fix_mode
{
Input = L4VBUS_GPIO_SETUP_INPUT, ///< Configure as input pin
Output = L4VBUS_GPIO_SETUP_OUTPUT, ///< Configure as output pin
Irq = L4VBUS_GPIO_SETUP_IRQ, ///< Configure as IRQ source
};
/// Modes used for pull up / pull down (config_pull())
enum Pull_mode
{
Pull_none = L4VBUS_GPIO_PIN_PULL_NONE, ///< No pull up or pull down resistors
Pull_up = L4VBUS_GPIO_PIN_PULL_UP, ///< Enable pull up resistor
Pull_down = L4VBUS_GPIO_PIN_PULL_DOWN, ///< Enable pull down resistor
};
virtual void request(unsigned /*pin*/) {}
virtual void free(unsigned /*pin*/) {}
/**
* Request number of pins from GPIO chip
*
* \return Number of pins of this GPIO chip
*/
virtual unsigned nr_pins() const = 0;
/**
* Generic (platform independent) setup for a pin.
*
* \param pin The pin number to configure.
* \param mode The mode for the pin (see Fix_mode).
* \param value The value if the pin is configured as output.
*/
virtual void setup(unsigned pin, unsigned mode, int value = 0) = 0;
/**
* Generic (platform independet) function to set a pin's pull up/down mode
*
* \param pin The pin number to configure.
* \param mode The pull up/down mode (see Pull_mode).
*/
virtual void config_pull(unsigned pin, unsigned mode) = 0;
/**
* Set platform specific pad configuration.
*
* \param pin The pin to configure.
* \param func A platform specific sub-function of a pad to be configured
* \param value A platform specific value for the given sub-function.
*/
virtual void config_pad(unsigned pin, unsigned func, unsigned value) = 0;
/**
* Get platform specific pad configuration.
*
* \param pin The pin for which the pad configuration should be
* retrieved.
* \param func A platform specific sub-function of the pin to be
* configured.
* \param[out] value A platform specific value for the given sub-function.
*/
virtual void config_get(unsigned pin, unsigned func, unsigned *value) = 0;
/**
* Get the value of the given pin (generic API).
*
* \param pin The pin to read the value from.
*
* \return Pin value.
*/
virtual int get(unsigned pin) = 0;
/**
* Set the value of the given pin (generic API).
*
* \pre The pin has to be configured as output before using this function,
* otherwise this call will be ignored.
*
* \param pin The pin to write the value to.
* \param value The value to program for output (0 or 1).
*/
virtual void set(unsigned pin, int value) = 0;
/**
* Enable the pin to function as an IRQ and return the IRQ number that will
* be triggered.
*
* \param pin The pin to configure as IRQ.
*
* \return Io_irq_pin object.
*/
virtual Io_irq_pin *get_irq(unsigned pin) = 0;
struct Pin_slice
{
unsigned offset;
unsigned mask;
};
/**
* Setup multiple pins (generic API)
*
* \param mask The pins to actually set up.
* \param mode The mode for the pins (see Fix_mode).
* \param outvalue The value if configured as output.
*/
virtual void multi_setup(Pin_slice const &mask, unsigned mode,
unsigned outvalue = 0) = 0;
/**
* Configure multiple pads at once (platform specific API).
*
* \param mask The pads to configure.
* \param func The platform-specific sub-function to configure.
* \param value The platform-specific value to set for the sub-function.
*
* \see config_pad()
*/
virtual void multi_config_pad(Pin_slice const &mask, unsigned func,
unsigned value) = 0;
/**
* Set the value for multiple output pins at once.
*
* \param mask The pins that shall actually be set to the given values.
* \param data The bit-wise value for each pin to set (according to mask).
*/
virtual void multi_set(Pin_slice const &mask, unsigned data) = 0;
/**
* Get the value for all pins at once.
*/
virtual unsigned multi_get(unsigned offset) = 0;
void output(unsigned pin, int value)
{ setup(pin, Output, value); }
void input(unsigned pin)
{ setup(pin, Input); }
void multi_output(Pin_slice const &mask, unsigned data)
{ multi_setup(mask, mask.mask, data); }
void multi_input(Pin_slice const &mask)
{ multi_setup(mask, ~mask.mask); }
protected:
/**
* \copydoc multi_setup()
* \note Generic implementation that can be used by GPIO drivers as fallback
* if the hardware does not allow a more efficient implementation.
*/
void generic_multi_setup(Pin_slice const &mask, unsigned mode, unsigned outvalue = 0)
{
unsigned m = mask.mask;
for (unsigned pin = mask.offset; pin < nr_pins(); ++pin, m >>= 1, outvalue >>= 1)
if (m & 1)
setup(pin, mode, outvalue & 1);
}
/**
* \copydoc multi_config_pad()
* \note Generic implementation that can be used by GPIO drivers as fallback
* if the hardware does not allow a more efficient implementation.
*/
void generic_multi_config_pad(Pin_slice const &mask, unsigned func, unsigned value)
{
unsigned m = mask.mask;
for (unsigned pin = mask.offset; pin < nr_pins(); ++pin, m >>= 1)
if (m & 1)
config_pad(pin, func, value);
}
/**
* \copydoc multi_set()
* \note Generic implementation that can be used by GPIO drivers as fallback
* if the hardware does not allow a more efficient implementation.
*/
void generic_multi_set(Pin_slice const &mask, unsigned data)
{
unsigned m = mask.mask;
for (unsigned pin = mask.offset; pin < nr_pins(); ++pin, m >>= 1, data >>= 1)
if (m & 1)
set(pin, data & 1);
}
/**
* \copydoc multi_get()
* \note Generic implementation that can be used by GPIO drivers as fallback
* if the hardware does not allow a more efficient implementation.
*/
unsigned generic_multi_get(unsigned offset)
{
if (offset >= nr_pins())
throw -L4_EINVAL;
unsigned data = 0;
unsigned max_bits = sizeof(data) * 8;
unsigned bits = cxx::min(nr_pins() - offset, max_bits);
for (unsigned i = 0; i < bits; ++i)
data |= get(offset + i) << i;
return data;
}
};
class Gpio_device :
public Gpio_chip,
public Hw::Device
{
};
}
class Gpio_resource : public Resource
{
public:
explicit Gpio_resource(Hw::Device *dev, unsigned start, unsigned end)
: Resource(Gpio_res | F_relative, start, end),
_hw(dynamic_cast<Hw::Gpio_device*>(dev))
{
if (!_hw)
{
d_printf(DBG_ERR,
"ERROR: GPIO: failed to assign GPIO-device (%p: %s) to resource\n",
dev, dev ? dev->name() : "");
throw("ERROR: GPIO: failed to assign GPIO-device to resource");
}
}
void dump(int indent) const override;
Hw::Gpio_device *provider() const { return _hw; }
private:
Gpio_resource(Gpio_resource const &);
void operator = (Gpio_resource const &);
Hw::Gpio_device *_hw;
};

View File

@@ -0,0 +1,16 @@
#include "gpio"
void
Gpio_resource::dump(int indent) const
{
l4_uint64_t s, e;
s = start();
e = end();
printf("%*.s%s%c [%014llx-%014llx %llx] (dev=%p:%s)\n",
indent, " ",
"GPIO", provided() ? '*' : ' ',
s, e, (l4_uint64_t)size(),
_hw, _hw ? _hw->name() : "(NULL)");
}

View File

@@ -0,0 +1,271 @@
/*
* (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include "hw_device.h"
#include "cfg.h"
#include "debug.h"
namespace Hw {
bool
Device::setup()
{
// FIXME: check and initialize things we depend on
for (Feature_list::const_iterator d = _features.begin();
d != _features.end(); ++d)
(*d)->setup(this);
return true;
}
int
Device::pm_suspend()
{
if (pm_power_state() != Pm_online)
return 0;
for (Feature_list::const_iterator d = _features.begin();
d != _features.end(); ++d)
(*d)->pm_save_state(this);
pm_set_state(Pm_suspended);
return 0;
}
int
Device::pm_resume()
{
if (pm_power_state() != Pm_suspended)
return 0;
for (Feature_list::const_iterator d = _features.begin();
d != _features.end(); ++d)
(*d)->pm_restore_state(this);
pm_set_state(Pm_online);
return 0;
}
void
Device::setup_children()
{
for (Feature_list::const_iterator d = _features.begin();
d != _features.end(); ++d)
(*d)->setup_children(this);
}
int
Device::pm_init()
{
#if 0
printf("Hw::Device::plug(this=%p, name='%s', hid='%s')\n",
this, name(), hid());
#endif
allocate_pending_resources();
if (!setup())
{
pm_set_state(Pm_failed);
return -ENODEV;
}
_sta = Active;
setup_children();
pm_set_state(Pm_online);
return 0;
}
void
Device::init()
{
if (0)
printf("Hw::Device::plug(this=%p, name='%s', hid='%s')\n",
this, name(), hid());
if (_flags & DF_dma_supported)
if (!dma_domain() && parent())
parent()->dma_domain_for(this);
int r = pm_init();
if (r < 0)
{
d_printf(DBG_ERR, "error: failed to setup device: %s\n", name());
return;
}
for (iterator c = begin(0); c != end(); ++c)
(*c)->init();
}
void
Device::plugin()
{
init();
}
void
Device_factory::dump()
{
for (Name_map::const_iterator i = nm().begin(); i != nm().end(); ++i)
printf("HW: '%s'\n", (*i).first.c_str());
}
Device *
Device_factory::create(cxx::String const &name)
{
Name_map::const_iterator i = nm().find(std::string(name.start(), name.end()));
if (i != nm().end())
return i->second->create();
return 0;
}
bool
Device::resource_allocated(Resource const *r) const
{
return r->parent();
}
Device *
Device::get_child_dev_adr(l4_uint32_t adr, bool create)
{
for (Device *c = children(); c; c = c->next())
if (c->adr() == adr)
return c;
if (!create)
return 0;
Device *c = new Device(adr);
add_child(c);
return c;
}
Device *
Device::get_child_dev_uid(l4_umword_t uid, l4_uint32_t adr, bool create)
{
for (Device *c = children(); c; c = c->next())
if (c->uid() == uid)
return c;
if (!create)
return 0;
Device *c = new Device(uid, adr);
add_child(c);
return c;
}
bool
Device::match_cid(cxx::String const &cid) const
{
if (Generic_device::match_cid(cid))
return true;
for (Cid_list::const_iterator i = _cid.begin(); i != _cid.end(); ++i)
if (cid == (*i).c_str())
return true;
for (Feature_list::const_iterator i = _features.begin();
i != _features.end(); ++i)
if ((*i)->match_cid(cid))
return true;
return false;
}
void
Device::dump(int indent) const
{
printf("%*.s%s: %s%s\n", indent, " ", name(),
hid() && hid()[0] ? "hid=" : "", hid() ? hid() : "");
if (!_cid.empty())
{
bool first = true;
printf("%*.s compatible= { ", indent, " ");
for (auto const &i : _cid)
{
printf("%s\"%s\"", first ? "" : ", ", i.c_str());
first = false;
}
puts(" }");
}
if (Io_config::cfg->verbose() > 2)
{
for (Feature_list::const_iterator i = _features.begin();
i != _features.end(); ++i)
(*i)->dump(indent + 2);
if (!_clients.empty())
{
printf("%*.s Clients: ===== start ====\n", indent, " ");
for (auto i = _clients.begin(); i != _clients.end(); ++i)
(*i)->dump(indent + 4);
printf("%*.s Clients: ===== end ====\n", indent, " ");
}
}
}
void
Device::add_feature(Dev_feature *f)
{
_features.push_back(f);
Feature_manager_base::match(this, f);
if (!_clients.empty())
f->enable_notifications(this);
}
void
Device::check_conflicts() const
{
for (auto i = _clients.begin(); i != _clients.end(); ++i)
for (auto j = i; ++j != _clients.end();)
{
if (!(*j)->check_conflict(*i))
continue;
d_printf(DBG_WARN, "warning: conflicting virtual clients:\n"
" %s\n"
" %s\n",
(*j)->get_full_name().c_str(),
(*i)->get_full_name().c_str());
}
}
void
Device::add_client(Device_client *client)
{
bool first = _clients.empty();
_clients.push_front(client);
if (first)
for (auto i = _features.begin(); i != _features.end(); ++i)
(*i)->enable_notifications(this);
}
void
Device::notify(unsigned type, unsigned event, unsigned value)
{
for (auto i = _clients.begin(); i != _clients.end(); ++i)
(*i)->notify(type, event, value);
if (_clients.empty())
for (auto i = _features.begin(); i != _features.end(); ++i)
(*i)->disable_notifications(this);
}
namespace {
static Device_factory_t<Device> __hw_pf_factory("Device");
}
}

View File

@@ -0,0 +1,482 @@
/*
* (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include "device.h"
#include "event_source.h"
#include "pm.h"
#include "type_matcher.h"
#include "hw_device_client.h"
#include "dma_domain.h"
#include <l4/cxx/avl_map>
#include <cerrno>
#include <functional>
#include <string>
#include <vector>
#include <l4/cxx/unique_ptr>
#include <l4/re/dma_space>
namespace Hw {
class Device_factory;
class Device;
class Dma_domain_factory
{
public:
virtual Dma_domain *create(Hw::Device *dev) = 0;
virtual ~Dma_domain_factory() = default;
};
/** \brief A device feature extends a generic device with some extra
* functionality at runtime.
*
* A device feature is an extension of a generic device, adding some device
* specific or bus specific functionality.
*
* Features are for example things like being an PCI device or an ACPI device
* etc.
*
* Each device can be extended with an arbitrary number of features.
*/
class Dev_feature
{
public:
virtual ~Dev_feature() = 0;
virtual bool match_cid(cxx::String const &) const { return false; }
virtual void dump(int) const {}
virtual void setup(Hw::Device *) {}
virtual void setup_children(Hw::Device *) {}
virtual void pm_save_state(Hw::Device *) {}
virtual void pm_restore_state(Hw::Device *) {}
virtual void enable_notifications(Hw::Device *) {}
virtual void disable_notifications(Hw::Device *) {}
};
inline Dev_feature::~Dev_feature() {}
/** \brief A feature manager can be called every time a matching feature is
* added to a device.
*
* This class is not intended to be used directly, use Feature_manager instead.
*
* This base class is inherited by all feature managers that shall be tried
* whenever Device::add_feaure() is called.
*/
struct Feature_manager_base : Type_matcher<Feature_manager_base>
{
virtual bool do_match(Device *dev, Dev_feature *f) const = 0;
virtual ~Feature_manager_base() = 0;
Feature_manager_base(std::type_info const *type)
: Type_matcher<Feature_manager_base>(type) {}
};
inline Feature_manager_base::~Feature_manager_base() {}
/// A generic hardware device.
class Device :
public Generic_device,
public Device_tree_mixin<Device>,
public Pm
{
private:
unsigned long _ref_cnt = 0;
l4_umword_t _uid = reinterpret_cast<l4_umword_t>(this);
Int_property _adr = ~0;
Int_property _flags = 0;
public:
enum Status
{
Disabled,
Active
};
enum Device_flags
{
DF_dma_supported = 1 << 0,
DF_multi_vbus = 1 << 1,
};
unsigned long ref_count() const { return _ref_cnt; }
void inc_ref_count() { ++_ref_cnt; }
void dec_ref_count() { --_ref_cnt; }
Device(l4_umword_t uid, l4_uint32_t adr) : _uid(uid), _adr(adr)
{ register_properties(); }
explicit Device(l4_uint32_t adr) : _adr(adr)
{ register_properties(); }
Device()
{ register_properties(); }
l4_umword_t uid() const { return _uid; }
/**
* Get bus-specific 'address' of the device.
*
* Bus-specific representation of the device address. For PCI devices, 'adr'
* contains the device number (upper 16 bits) and the function number (lower
* 16 bits). The bus number is not part of 'adr' -- use the parent device to
* determine this information instead.
*/
l4_uint32_t adr() const { return _adr.val(); }
bool resource_allocated(Resource const *r) const override;
Device *get_child_dev_adr(l4_uint32_t adr, bool create = false);
Device *get_child_dev_uid(l4_umword_t uid, l4_uint32_t adr, bool create = false);
Device *parent() const override { return _dt.parent(); }
Device *children() const override { return _dt.children(); }
Device *next() const override { return _dt.next(); }
int depth() const override { return _dt.depth(); }
typedef Device_tree<Device>::iterator iterator;
using Device_tree_mixin<Device>::begin;
using Device_tree_mixin<Device>::end;
typedef std::vector<Dev_feature *> Feature_list;
Feature_list const *features() const { return &_features; }
void add_feature(Dev_feature *f);
template<typename T>
T *find_feature()
{
for (Feature_list::const_iterator i = _features.begin();
i != _features.end();
++i)
if (T *r = dynamic_cast<T*>(*i))
return r;
return 0;
}
void plugin();
bool setup();
void setup_children();
int pm_init() override;
int pm_suspend() override;
int pm_resume() override;
virtual void init();
Status status() const { return _sta; }
void dump(int indent) const override;
char const *name() const override { return _name.c_str(); }
/**
* Get the 'hardware interface description ID' of this hardware device.
*
* HIDs are not suitable for identifying a specific device as multiple
* devices can share the same HID.
*/
char const *hid() const override { return _hid.val().c_str(); }
void set_name(std::string const &name) { _name = name; }
bool set_name_if_empty(std::string const &name)
{
if (!_name.empty())
return false;
_name = name;
return true;
}
void set_hid(char const *hid) { _hid.set(-1, hid); }
void set_uid(l4_umword_t uid) { _uid = uid; }
void add_cid(char const *cid) { _cid.push_back(cid); }
bool match_cid(cxx::String const &cid) const override;
void add_client(Device_client *client);
void check_conflicts() const;
void notify(unsigned type, unsigned event, unsigned value);
Io::Event_source_infos const *get_event_infos() const
{ return _event_source_infos.get(); }
Io::Event_source_infos *get_event_infos(bool alloc = false)
{
if (!alloc || _event_source_infos.get())
return _event_source_infos.get();
_event_source_infos = cxx::make_unique<Io::Event_source_infos>();
return _event_source_infos.get();
}
/**
* \param dev The device for which the DMA domain is requested.
*/
Dma_domain *dma_domain_for(Device *dev)
{
Dma_domain *d = 0;
for (Device *c = this; c; c = c->parent())
{
if (c->_downstream_dma_domain)
{
d = c->_downstream_dma_domain;
break;
}
if (auto *f = c->_dma_domain_factory)
{
d = f->create(dev);
break;
}
}
if (dev && d)
{
dev->_dma_domain = d;
dev->add_resource_rq(d);
}
return d;
}
void set_dma_domain_factory(Dma_domain_factory *dma)
{ _dma_domain_factory = dma; }
Dma_domain *dma_domain()
{ return _dma_domain; }
void set_downstream_dma_domain(Dma_domain *dma)
{ _downstream_dma_domain = dma; }
/**
* Get the DMA domain that **must** be used for our child devices.
*
* If this function returns NULL the caller must descend the device tree down
* to the root and check until it finds a non-NULL secondary DMA domain.
*/
Dma_domain *downstream_dma_domain() const
{ return _downstream_dma_domain; }
bool is_multi_vbus_dev() const { return _flags & DF_multi_vbus; }
private:
typedef std::vector<std::string> Cid_list;
void register_properties()
{
register_property("hid", &_hid);
register_property("adr", &_adr);
register_property("flags", &_flags);
}
protected:
Status _sta = Disabled;
private:
std::string _name;
String_property _hid;
Cid_list _cid;
Feature_list _features;
Device_client::Client_list _clients;
cxx::unique_ptr<Io::Event_source_infos> _event_source_infos;
/// The DMA domain to be used for this device itself.
Dma_domain *_dma_domain = 0;
/// DMA domain that **must** be used for all children of this device.
Dma_domain *_downstream_dma_domain = 0;
Dma_domain_factory *_dma_domain_factory = 0;
};
/**
* Feature for DMA capable device that may be bound to an IOMMU.
*/
struct Dma_src_feature : public Dev_feature
{
public:
/**
* Callback for DMA source-IDs.
*
* \return Negative error value. Non-negative value on success.
*/
using Dma_src_id_cb = std::function<int(l4_uint64_t sid)>;
/**
* Enumerate IOMMU source-ID for device.
*
* The `cb` callback is invoked for each possible source-ID of the device.
* These source-IDs are used in L4::Iommu::bind() to bind the device to a DMA
* task.
*
* \return 0 to continue enumeration on downstream bridges/devices, >0 to
* stop enumeration because the bridge takes ownership of all
* transactions or <0 on errors.
*/
virtual int enumerate_dma_src_ids(Dma_src_id_cb cb) const = 0;
protected:
virtual ~Dma_src_feature() = default;
};
/**
* \brief A generic factory for creating hardware device objects of a specific
* type.
*
* \see Device_factory_t for more information.
*/
class Device_factory
{
public:
static void dump();
static Device *create(cxx::String const &name);
static void register_factory(char const *name, Device_factory *f)
{ nm()[name] = f; }
virtual Device *create() = 0;
virtual ~Device_factory() = 0;
protected:
typedef cxx::Avl_map<std::string, Device_factory *> Name_map;
static Name_map &nm()
{
static Name_map _factories;
return _factories;
}
};
inline Device_factory::~Device_factory() {}
/**
* \brief Template to fabricate a hardware device object of type \a HW_DEV.
*/
template< typename HW_DEV >
class Device_factory_t : public Device_factory
{
public:
/**
* \brief Create instantiate a factory for a given name.
*
* Those factories are used from the IO scripts to instantiate device nodes
* for a given device class.
*/
Device_factory_t(char const *name) { register_factory(name, this); }
Device *create() override { return new HW_DEV(); }
};
/**
* \brief Helper code for the Feature_manager.
*/
namespace __Feature_manager_helper {
template<typename T>
struct Tw { T *v; Tw() : v(0) {} };
template<typename ...Features>
struct Fm;
template<typename F1, typename ...Features>
struct Fm<F1, Features...> : Tw<F1>, Fm<Features...>
{
typedef F1 First_feature;
template<typename F>
F *get() const { return Tw<F>::v; }
template<typename F>
void set(F *f) { Tw<F>::v = f; }
bool have_all() const
{
if (!Tw<F1>::v)
return false;
return Fm<Features...>::have_all();
}
template<typename G>
bool add(G *g)
{
if (Tw<F1>::v)
return Fm<Features...>::add(g);
if (F1 *x = dynamic_cast<F1 *>(g))
{
Tw<F1>::v = x;
return true;
}
return Fm<Features...>::add(g);
}
};
template<>
struct Fm<>
{
bool have_all() const { return true; }
template<typename G>
bool add(G *) { return false; }
};
}
/**
* \brief A manager for a given combination of features (\a Features).
*
* To trigger an action for a specific set of features (\a Features) one needs
* to inherit from Feature_manager and provide a setup() method that gets
* called whenever a Device first has all of the features given in \a Features.
*/
template<typename ...Features>
struct Feature_manager : Feature_manager_base
{
/** \brief override this function with your action. */
virtual bool setup(Device *, Features *...) const = 0;
bool do_match(Device *dev, Dev_feature *_f) const override
{
if (!_f || !dev)
return false;
__Feature_manager_helper::Fm<Features...> h;
if (!h.add(_f))
return false;
for (Device::Feature_list::const_iterator i = dev->features()->begin();
!h.have_all() && i != dev->features()->end();
++i)
h.add(*i);
if (!h.have_all())
return false;
setup(dev, h.__Feature_manager_helper::template Tw<Features>::v...);
return false;
}
Feature_manager()
: Feature_manager_base(&typeid(typename __Feature_manager_helper::Fm<Features...>::First_feature))
{}
};
}

Some files were not shown because too many files have changed in this diff Show More