diff mbox series

[v3,2/7] ACPI: scan: Extract CSI-2 connection graph from _CRS

Message ID 2927980.e9J7NaK4W3@kreacher
State New
Headers show
Series None | expand

Commit Message

Rafael J. Wysocki Nov. 6, 2023, 4:09 p.m. UTC
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Find ACPI CSI-2 resource descriptors defined since ACPI 6.4 (for
CSI-2 and camera configuration) in _CRS for all device objects in
the given scope of the ACPI namespace that have them, identify the
corresponding "remote endpoint" device objects for them and
allocate memory for software nodes needed to create a DT-like data
structure representing the CSI-2 connection graph for drivers.

The code needed to populate these software nodes will be added by
subsequent change sets.

Link: https://uefi.org/specs/ACPI/6.5/06_Device_Configuration.html#camera-serial-interface-csi-2-connection-resource-descriptor
Co-developed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---

v2 -> v3:
   * Change the name of the new file to mipi-disco-img.c
   * Combine check_*_overflow() computations in alloc_crs_csi2_swnodes()

---
 drivers/acpi/Makefile         |    2 
 drivers/acpi/internal.h       |    8 +
 drivers/acpi/mipi-disco-img.c |  292 ++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c           |   48 +++++-
 include/acpi/acpi_bus.h       |   18 ++
 5 files changed, 358 insertions(+), 10 deletions(-)
 create mode 100644 drivers/acpi/mipi-disco-img.c

Comments

Miao Wang Nov. 21, 2024, 9:57 p.m. UTC | #1
2023年11月7日 00:09,Rafael J. Wysocki <rjw@rjwysocki.net> 写道:
> 
>  +static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
>   				      struct acpi_device **adev_p)
>   {
>   	struct acpi_device *device = acpi_fetch_acpi_dev(handle);
>  @@ -2054,9 +2059,25 @@ static acpi_status acpi_bus_check_add(ac
>   		if (acpi_device_should_be_hidden(handle))
>   			return AE_OK;
>   
>  -		/* Bail out if there are dependencies. */
>  -		if (acpi_scan_check_dep(handle, check_dep) > 0)
>  -			return AE_CTRL_DEPTH;
>  +		if (first_pass) {
>  +			acpi_mipi_check_crs_csi2(handle);
>  +
>  +			/* Bail out if there are dependencies. */
>  +			if (acpi_scan_check_dep(handle) > 0) {
>  +				/*
>  +				 * The entire CSI-2 connection graph needs to be
>  +				 * extracted before any drivers or scan handlers
>  +				 * are bound to struct device objects, so scan
>  +				 * _CRS CSI-2 resource descriptors for all
>  +				 * devices below the current handle.
>  +				 */
>  +				acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
>  +						    ACPI_UINT32_MAX,
>  +						    acpi_scan_check_crs_csi2_cb,
>  +						    NULL, NULL, NULL);
>  +				return AE_CTRL_DEPTH;
>  +			}
>  +		}
>   
>   		fallthrough;
>   	case ACPI_TYPE_ANY:	/* for ACPI_ROOT_OBJECT */
> 

Hi, I'd like to report some issues caused by this patch. Correct me if I'm wrong
since I'm not expert on ACPI. Before this patch, properties of ACPI devices with
_DEP relationship declared were evaluated after the initialization of the
depending devices, i.e. the execution of handler->attach(). With this patch,
_CRS of all the ACPI devices are evaluated to check if the device contains a
CSI2 resource, regardless of the declaration of _DEP relationship. The
evaluation of _CRS is even before _STA and other methods indicating the status
of the device.

This has caused some issues in certain scenarios, where the DSDT contains legacy
devices, which requires reading IO ports to form the result of its _CRS. An
example of such a legacy device is declared in the DSDT is as below:

    Device (LPT)
    {
        Name (_HID, EisaId ("PNP0400") /* Standard LPT Parallel Port */)
        Name (_UID, 0x02)  // _UID: Unique ID
        Name (_DDN, "LPT1")  // _DDN: DOS Device Name
        Name (_DEP, Package (0x01)  // _DEP: Dependencies
        {
            \_SB.PCI0
        })
        OperationRegion (ITE1, SystemIO, 0x2E, 0x02)
        Field (ITE1, ByteAcc, NoLock, Preserve) {INDX, 8,   DATA, 8}
        IndexField (INDX, DATA, ByteAcc, NoLock, Preserve)
        {
            // Omitting some declarations of fields
            IOAH,   8,
            IOAL,   8,
        }
        Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
        {
            Name (BUF0, ResourceTemplate ()
            {
                IO (Decode16,
                    0x0378,             // Range Minimum
                    0x0378,             // Range Maximum
                    0x01,               // Alignment
                    0x08,               // Length
                    _Y01)
                IRQ (Level, ActiveLow, Exclusive, _Y02)
                    {7}
            })
            CreateByteField (BUF0, \LPT._CRS._Y01._MIN, IOLO)
            CreateByteField (BUF0, 0x03, IOHI)
            CreateByteField (BUF0, \LPT._CRS._Y01._MAX, IORL)
            CreateByteField (BUF0, 0x05, IORH)
            CreateByteField (BUF0, \LPT._CRS._Y01._LEN, LEN1)
            CreateByteField (BUF0, \LPT._CRS._Y02._INT, IRQL) 
            
            IOLO = IOAL /* \LPT_.IOAL */
            IORL = IOAL /* \LPT_.IOAL */
            IOHI = IOAH /* \LPT_.IOAH */
            IORH = IOAH /* \LPT_.IOAH */
            // Omitting some assignments and calculations
            
            Return (BUF0)
        }
    }

On non-x86 platforms, IO ports are implemented using MMIO. The memory is
remapped in the initialization of the PCI root, using its QWordIO resource
declared in its _CRS, in acpi_pci_root_remap_iospace(). Before the
initialization of the PCI root, reads and writes of the IO ports will result in
accessing memory region which is not mapped.

Before this patch, adding a _DEP of Package(){PCI0} to these legacy devices will
postpone the initialization of these devices after the PCI root, solving the
above problem. After this patch, however, the evaluation of _CRS regardless of
_DEP declarations causes accessing IO ports before they are ready.

I've checked the ACPI spec, and it states that ``OSPM must guarantee that the
following operation regions are always accessible: SystemIO operation regions"
and ``Since the region types above are permanently available, no _REG methods
are required, nor will OSPM evaluate any _REG methods that appear in the same
scope as the operation region declaration(s) of these types". It seems that
from the view of the AML codes in the DSDT, it can never know when the IO ports
are ready, and it is impossible for Linux on non-x86 platforms to ensure IO
ports are always accessible.

I wonder if there would be a better solution to this problem.

Cheers,

Miao Wang
Rafael J. Wysocki Dec. 3, 2024, 7:44 p.m. UTC | #2
Hi,

Sorry for the delay.

On Thu, Nov 21, 2024 at 10:57 PM Miao Wang <shankerwangmiao@gmail.com> wrote:
>
> 2023年11月7日 00:09,Rafael J. Wysocki <rjw@rjwysocki.net> 写道:
> >
> >  +static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
> >                                     struct acpi_device **adev_p)
> >   {
> >       struct acpi_device *device = acpi_fetch_acpi_dev(handle);
> >  @@ -2054,9 +2059,25 @@ static acpi_status acpi_bus_check_add(ac
> >               if (acpi_device_should_be_hidden(handle))
> >                       return AE_OK;
> >
> >  -            /* Bail out if there are dependencies. */
> >  -            if (acpi_scan_check_dep(handle, check_dep) > 0)
> >  -                    return AE_CTRL_DEPTH;
> >  +            if (first_pass) {
> >  +                    acpi_mipi_check_crs_csi2(handle);
> >  +
> >  +                    /* Bail out if there are dependencies. */
> >  +                    if (acpi_scan_check_dep(handle) > 0) {
> >  +                            /*
> >  +                             * The entire CSI-2 connection graph needs to be
> >  +                             * extracted before any drivers or scan handlers
> >  +                             * are bound to struct device objects, so scan
> >  +                             * _CRS CSI-2 resource descriptors for all
> >  +                             * devices below the current handle.
> >  +                             */
> >  +                            acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
> >  +                                                ACPI_UINT32_MAX,
> >  +                                                acpi_scan_check_crs_csi2_cb,
> >  +                                                NULL, NULL, NULL);
> >  +                            return AE_CTRL_DEPTH;
> >  +                    }
> >  +            }
> >
> >               fallthrough;
> >       case ACPI_TYPE_ANY:     /* for ACPI_ROOT_OBJECT */
> >
>
> Hi, I'd like to report some issues caused by this patch. Correct me if I'm wrong
> since I'm not expert on ACPI. Before this patch, properties of ACPI devices with
> _DEP relationship declared were evaluated after the initialization of the
> depending devices, i.e. the execution of handler->attach(). With this patch,
> _CRS of all the ACPI devices are evaluated to check if the device contains a
> CSI2 resource, regardless of the declaration of _DEP relationship.

Yes, but the _DEP information is not relevant for whether or not _CRS
may be evaluated at all.

> The evaluation of _CRS is even before _STA

Not really.  _STA has already been evaluated by
acpi_ns_init_one_device() for all devices at this point.

> and other methods indicating the status of the device.

No, but it doesn't matter, at least by the spec.  Had the device been
disabled, _CRS would have been expected to work anyway:

"If a device is disabled, then _CRS returns a valid resource template
for the device, but the actual resource assignments in the return byte
stream are ignored." (ACPI 6.5, Section 6.2.2. _CRS (Current Resource
Settings)).

That said, adding a device status check before the _CRS evaluation in
acpi_bus_check_add() is not inconceivable.

> This has caused some issues in certain scenarios, where the DSDT contains legacy
> devices, which requires reading IO ports to form the result of its _CRS. An
> example of such a legacy device is declared in the DSDT is as below:
>
>     Device (LPT)
>     {
>         Name (_HID, EisaId ("PNP0400") /* Standard LPT Parallel Port */)
>         Name (_UID, 0x02)  // _UID: Unique ID
>         Name (_DDN, "LPT1")  // _DDN: DOS Device Name
>         Name (_DEP, Package (0x01)  // _DEP: Dependencies
>         {
>             \_SB.PCI0
>         })
>         OperationRegion (ITE1, SystemIO, 0x2E, 0x02)
>         Field (ITE1, ByteAcc, NoLock, Preserve) {INDX, 8,   DATA, 8}
>         IndexField (INDX, DATA, ByteAcc, NoLock, Preserve)
>         {
>             // Omitting some declarations of fields
>             IOAH,   8,
>             IOAL,   8,
>         }
>         Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
>         {
>             Name (BUF0, ResourceTemplate ()
>             {
>                 IO (Decode16,
>                     0x0378,             // Range Minimum
>                     0x0378,             // Range Maximum
>                     0x01,               // Alignment
>                     0x08,               // Length
>                     _Y01)
>                 IRQ (Level, ActiveLow, Exclusive, _Y02)
>                     {7}
>             })
>             CreateByteField (BUF0, \LPT._CRS._Y01._MIN, IOLO)
>             CreateByteField (BUF0, 0x03, IOHI)
>             CreateByteField (BUF0, \LPT._CRS._Y01._MAX, IORL)
>             CreateByteField (BUF0, 0x05, IORH)
>             CreateByteField (BUF0, \LPT._CRS._Y01._LEN, LEN1)
>             CreateByteField (BUF0, \LPT._CRS._Y02._INT, IRQL)
>
>             IOLO = IOAL /* \LPT_.IOAL */
>             IORL = IOAL /* \LPT_.IOAL */
>             IOHI = IOAH /* \LPT_.IOAH */
>             IORH = IOAH /* \LPT_.IOAH */
>             // Omitting some assignments and calculations
>
>             Return (BUF0)
>         }
>     }
>
> On non-x86 platforms, IO ports are implemented using MMIO. The memory is
> remapped in the initialization of the PCI root, using its QWordIO resource
> declared in its _CRS, in acpi_pci_root_remap_iospace(). Before the
> initialization of the PCI root, reads and writes of the IO ports will result in
> accessing memory region which is not mapped.

That's not really straightforward.

> Before this patch, adding a _DEP of Package(){PCI0} to these legacy devices will
> postpone the initialization of these devices after the PCI root, solving the
> above problem. After this patch, however, the evaluation of _CRS regardless of
> _DEP declarations causes accessing IO ports before they are ready.

Well, before this patch, Linux simply did not have to evaluate _CRS
during device discovery.

Nowhere in the spec it is stated that _CRS cannot be evaluated for a
given device before enumerating all devices listed by its _DEP object.
As it is currently defined, _DEP is only about operation region
dependencies, not even about general device enumeration ordering (even
though it is used for enforcing specific enumeration ordering in the
field because OSes tend to enumerate devices in the order following
from _DEP as a matter of fact).

> I've checked the ACPI spec, and it states that ``OSPM must guarantee that the
> following operation regions are always accessible: SystemIO operation regions"

Which to me is clearly at odds with the SystemIO implementation
description above.

> and ``Since the region types above are permanently available, no _REG methods
> are required, nor will OSPM evaluate any _REG methods that appear in the same
> scope as the operation region declaration(s) of these types". It seems that
> from the view of the AML codes in the DSDT, it can never know when the IO ports
> are ready, and it is impossible for Linux on non-x86 platforms to ensure IO
> ports are always accessible.

They aren't always accessible anyhow.  They become accessible after
enumerating the PCI host bridge and they aren't accessible earlier.

> I wonder if there would be a better solution to this problem.

Well, it is a bit unfortunate that it took 6 kernel release cycles to
realize that there was a problem.  Had this been reported earlier,
there would have been more options on the table.

At this point, the functionality related to CSI-2 connection graphs
simply needs to be there, the only option for avoiding _CRS evaluation
in acpi_bus_check_add() would be to somehow defer the enumeration of
all devices using CSI-2 connections until the host bridge is
enumerated.  Alternatively, the enumeration of the PCI host bridge
might be pushed forward, but that would require addressing at least a
few enumeration ordering issues.  None of these would be a small
change and backporting any of that would have been a considerable
effort.

One of the things worth considering is whether or not CSI-2 is
relevant for the architectures that rely on the old behavior related
to _DEP.  If not at all, a Kconfig option could be added to disable
CSI-2 graphs enumeration on those architectures and that would make
the problem go away.

Also I'm wondering if the firmware could be updated to survive a mere
evaluation of _CRS if the hardware is not ready.

Thanks!
Miao Wang Dec. 5, 2024, 7:23 a.m. UTC | #3
> 2024年12月4日 03:44,Rafael J. Wysocki <rafael@kernel.org> 写道:
> 
> Hi,
> 
> Sorry for the delay.
> 
> On Thu, Nov 21, 2024 at 10:57 PM Miao Wang <shankerwangmiao@gmail.com> wrote:
>> 
>> 2023年11月7日 00:09,Rafael J. Wysocki <rjw@rjwysocki.net> 写道:
>>> 
>>> +static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
>>>                                    struct acpi_device **adev_p)
>>>  {
>>>      struct acpi_device *device = acpi_fetch_acpi_dev(handle);
>>> @@ -2054,9 +2059,25 @@ static acpi_status acpi_bus_check_add(ac
>>>              if (acpi_device_should_be_hidden(handle))
>>>                      return AE_OK;
>>> 
>>> -            /* Bail out if there are dependencies. */
>>> -            if (acpi_scan_check_dep(handle, check_dep) > 0)
>>> -                    return AE_CTRL_DEPTH;
>>> +            if (first_pass) {
>>> +                    acpi_mipi_check_crs_csi2(handle);
>>> +
>>> +                    /* Bail out if there are dependencies. */
>>> +                    if (acpi_scan_check_dep(handle) > 0) {
>>> +                            /*
>>> +                             * The entire CSI-2 connection graph needs to be
>>> +                             * extracted before any drivers or scan handlers
>>> +                             * are bound to struct device objects, so scan
>>> +                             * _CRS CSI-2 resource descriptors for all
>>> +                             * devices below the current handle.
>>> +                             */
>>> +                            acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
>>> +                                                ACPI_UINT32_MAX,
>>> +                                                acpi_scan_check_crs_csi2_cb,
>>> +                                                NULL, NULL, NULL);
>>> +                            return AE_CTRL_DEPTH;
>>> +                    }
>>> +            }
>>> 
>>>              fallthrough;
>>>      case ACPI_TYPE_ANY:     /* for ACPI_ROOT_OBJECT */
>>> 
>> 
>> Hi, I'd like to report some issues caused by this patch. Correct me if I'm wrong
>> since I'm not expert on ACPI. Before this patch, properties of ACPI devices with
>> _DEP relationship declared were evaluated after the initialization of the
>> depending devices, i.e. the execution of handler->attach(). With this patch,
>> _CRS of all the ACPI devices are evaluated to check if the device contains a
>> CSI2 resource, regardless of the declaration of _DEP relationship.
> 
> Yes, but the _DEP information is not relevant for whether or not _CRS
> may be evaluated at all.
> 
>> The evaluation of _CRS is even before _STA
> 
> Not really.  _STA has already been evaluated by
> acpi_ns_init_one_device() for all devices at this point.

I quickly scanned the code in acpi_ns_init_one_device(), and it seems
that it an object does not have _INI method, then the _STA will not be
evaluated here.

> 
>> and other methods indicating the status of the device.
> 
> No, but it doesn't matter, at least by the spec.  Had the device been
> disabled, _CRS would have been expected to work anyway:
> 
> "If a device is disabled, then _CRS returns a valid resource template
> for the device, but the actual resource assignments in the return byte
> stream are ignored." (ACPI 6.5, Section 6.2.2. _CRS (Current Resource
> Settings)).
> 
> That said, adding a device status check before the _CRS evaluation in
> acpi_bus_check_add() is not inconceivable.
> 
>> This has caused some issues in certain scenarios, where the DSDT contains legacy
>> devices, which requires reading IO ports to form the result of its _CRS. An
>> example of such a legacy device is declared in the DSDT is as below:
>> 
>>    Device (LPT)
>>    {
>>        Name (_HID, EisaId ("PNP0400") /* Standard LPT Parallel Port */)
>>        Name (_UID, 0x02)  // _UID: Unique ID
>>        Name (_DDN, "LPT1")  // _DDN: DOS Device Name
>>        Name (_DEP, Package (0x01)  // _DEP: Dependencies
>>        {
>>            \_SB.PCI0
>>        })
>>        OperationRegion (ITE1, SystemIO, 0x2E, 0x02)
>>        Field (ITE1, ByteAcc, NoLock, Preserve) {INDX, 8,   DATA, 8}
>>        IndexField (INDX, DATA, ByteAcc, NoLock, Preserve)
>>        {
>>            // Omitting some declarations of fields
>>            IOAH,   8,
>>            IOAL,   8,
>>        }
>>        Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
>>        {
>>            Name (BUF0, ResourceTemplate ()
>>            {
>>                IO (Decode16,
>>                    0x0378,             // Range Minimum
>>                    0x0378,             // Range Maximum
>>                    0x01,               // Alignment
>>                    0x08,               // Length
>>                    _Y01)
>>                IRQ (Level, ActiveLow, Exclusive, _Y02)
>>                    {7}
>>            })
>>            CreateByteField (BUF0, \LPT._CRS._Y01._MIN, IOLO)
>>            CreateByteField (BUF0, 0x03, IOHI)
>>            CreateByteField (BUF0, \LPT._CRS._Y01._MAX, IORL)
>>            CreateByteField (BUF0, 0x05, IORH)
>>            CreateByteField (BUF0, \LPT._CRS._Y01._LEN, LEN1)
>>            CreateByteField (BUF0, \LPT._CRS._Y02._INT, IRQL)
>> 
>>            IOLO = IOAL /* \LPT_.IOAL */
>>            IORL = IOAL /* \LPT_.IOAL */
>>            IOHI = IOAH /* \LPT_.IOAH */
>>            IORH = IOAH /* \LPT_.IOAH */
>>            // Omitting some assignments and calculations
>> 
>>            Return (BUF0)
>>        }
>>    }
>> 
>> On non-x86 platforms, IO ports are implemented using MMIO. The memory is
>> remapped in the initialization of the PCI root, using its QWordIO resource
>> declared in its _CRS, in acpi_pci_root_remap_iospace(). Before the
>> initialization of the PCI root, reads and writes of the IO ports will result in
>> accessing memory region which is not mapped.
> 
> That's not really straightforward.
> 
>> Before this patch, adding a _DEP of Package(){PCI0} to these legacy devices will
>> postpone the initialization of these devices after the PCI root, solving the
>> above problem. After this patch, however, the evaluation of _CRS regardless of
>> _DEP declarations causes accessing IO ports before they are ready.
> 
> Well, before this patch, Linux simply did not have to evaluate _CRS
> during device discovery.
> 
> Nowhere in the spec it is stated that _CRS cannot be evaluated for a
> given device before enumerating all devices listed by its _DEP object.
> As it is currently defined, _DEP is only about operation region
> dependencies, not even about general device enumeration ordering (even
> though it is used for enforcing specific enumeration ordering in the
> field because OSes tend to enumerate devices in the order following
> from _DEP as a matter of fact).
> 
>> I've checked the ACPI spec, and it states that ``OSPM must guarantee that the
>> following operation regions are always accessible: SystemIO operation regions"
> 
> Which to me is clearly at odds with the SystemIO implementation
> description above.
> 
>> and ``Since the region types above are permanently available, no _REG methods
>> are required, nor will OSPM evaluate any _REG methods that appear in the same
>> scope as the operation region declaration(s) of these types". It seems that
>> from the view of the AML codes in the DSDT, it can never know when the IO ports
>> are ready, and it is impossible for Linux on non-x86 platforms to ensure IO
>> ports are always accessible.
> 
> They aren't always accessible anyhow.  They become accessible after
> enumerating the PCI host bridge and they aren't accessible earlier.
> 
>> I wonder if there would be a better solution to this problem.
> 
> Well, it is a bit unfortunate that it took 6 kernel release cycles to
> realize that there was a problem.  Had this been reported earlier,
> there would have been more options on the table.

Yeah, since it is rare to keep a legacy device on non-X86 platforms,
and as a result, this issue is not discovered until now.

> 
> At this point, the functionality related to CSI-2 connection graphs
> simply needs to be there, the only option for avoiding _CRS evaluation
> in acpi_bus_check_add() would be to somehow defer the enumeration of
> all devices using CSI-2 connections until the host bridge is
> enumerated.  Alternatively, the enumeration of the PCI host bridge
> might be pushed forward, but that would require addressing at least a
> few enumeration ordering issues.  None of these would be a small
> change and backporting any of that would have been a considerable
> effort.
> 
> One of the things worth considering is whether or not CSI-2 is
> relevant for the architectures that rely on the old behavior related
> to _DEP.  If not at all, a Kconfig option could be added to disable
> CSI-2 graphs enumeration on those architectures and that would make
> the problem go away.

I'm not familiar with ACPI. However, in my personal point of view, it
seems that the root cause of the issue is in the ACPI standard, which
assumes SystemIO operation regions are always available, which is not
the case on non-X86 platforms. Also, there is no mechanism for the DSDT
code to know whether SystemIO operation regions are available, and it
is thus impossible to return ``a valid resource template" in the _CRS
method when SystemIO operation regions are unavailable. So it is not
the architectures that want to rely on the old behavior related to
_DEP, but all non-x86 architectures with ACPI support may have no
choice but to rely on this to postpone possible IO port operations after
the initialization of the PCI host bridge. However, on non-x86
architectures, there might be limited number of machines with devices
using legacy IO operations, so this issue is currently not so significant.

The documents for CSI-2 give me an impression that it is related to a
certain kind of camera, so I don't think it is good to remove this for
specific architectures.

> Also I'm wondering if the firmware could be updated to survive a mere
> evaluation of _CRS if the hardware is not ready.

If we only consider the current ACPI standard, I cannot come up with
a solution on the firmware side, as discussed before. But utilizing
(or abusing?) the current Linux implementation details, I managed to
work this around in this way:

Device (LPT)
{
    //...

    Name (STAD, Zero)
    Name (_DEP, Package (0x01)
    {
        \_SB.PCI0
    })

    Method (_STA, 0, NotSerialized)
    {
        STAD = One
        // other codes
    }
    
    Method (_CRS, 0, NotSerialized)
    {
        Name (BUF0,/*....*/)

        // Initialize BUF0

        If (STAD)
        {
            // do actual IO operations and fill BUF0 with actual
            // numbers
        }
        
        Return (BUF0)
    }
}

This work around is because I noticed the _STA method is called after
the depending PCI0 is initialized, so set a flag in the _STA method to
mark the availability of the IO port operations.

Maybe we can prioritize the initialization of the PCI host bridge to
fully eliminate this issue?

Cheers,

Miao Wang
Rafael J. Wysocki Dec. 5, 2024, 10:54 a.m. UTC | #4
On Thu, Dec 5, 2024 at 8:24 AM Miao Wang <shankerwangmiao@gmail.com> wrote:
>
>
> > 2024年12月4日 03:44,Rafael J. Wysocki <rafael@kernel.org> 写道:
> >
> > Hi,
> >
> > Sorry for the delay.
> >
> > On Thu, Nov 21, 2024 at 10:57 PM Miao Wang <shankerwangmiao@gmail.com> wrote:
> >>
> >> 2023年11月7日 00:09,Rafael J. Wysocki <rjw@rjwysocki.net> 写道:
> >>>
> >>> +static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
> >>>                                    struct acpi_device **adev_p)
> >>>  {
> >>>      struct acpi_device *device = acpi_fetch_acpi_dev(handle);
> >>> @@ -2054,9 +2059,25 @@ static acpi_status acpi_bus_check_add(ac
> >>>              if (acpi_device_should_be_hidden(handle))
> >>>                      return AE_OK;
> >>>
> >>> -            /* Bail out if there are dependencies. */
> >>> -            if (acpi_scan_check_dep(handle, check_dep) > 0)
> >>> -                    return AE_CTRL_DEPTH;
> >>> +            if (first_pass) {
> >>> +                    acpi_mipi_check_crs_csi2(handle);
> >>> +
> >>> +                    /* Bail out if there are dependencies. */
> >>> +                    if (acpi_scan_check_dep(handle) > 0) {
> >>> +                            /*
> >>> +                             * The entire CSI-2 connection graph needs to be
> >>> +                             * extracted before any drivers or scan handlers
> >>> +                             * are bound to struct device objects, so scan
> >>> +                             * _CRS CSI-2 resource descriptors for all
> >>> +                             * devices below the current handle.
> >>> +                             */
> >>> +                            acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
> >>> +                                                ACPI_UINT32_MAX,
> >>> +                                                acpi_scan_check_crs_csi2_cb,
> >>> +                                                NULL, NULL, NULL);
> >>> +                            return AE_CTRL_DEPTH;
> >>> +                    }
> >>> +            }
> >>>
> >>>              fallthrough;
> >>>      case ACPI_TYPE_ANY:     /* for ACPI_ROOT_OBJECT */
> >>>
> >>
> >> Hi, I'd like to report some issues caused by this patch. Correct me if I'm wrong
> >> since I'm not expert on ACPI. Before this patch, properties of ACPI devices with
> >> _DEP relationship declared were evaluated after the initialization of the
> >> depending devices, i.e. the execution of handler->attach(). With this patch,
> >> _CRS of all the ACPI devices are evaluated to check if the device contains a
> >> CSI2 resource, regardless of the declaration of _DEP relationship.

[cut]

>
> Maybe we can prioritize the initialization of the PCI host bridge to
> fully eliminate this issue?

The problem with this is that the current code requires struct
acpi_device objects to be present for all PCI devices that have
corresponding objects in the ACPI Namespace at the time when the host
bridge is initialized because that causes the PCI bus to be scanned
for devices and struct acpi_device objects are looked up from there.

To make this work, the "ACPI companion lookup" code needs to be
changed and that would be kind of a heavy lifting and it may introduce
some unexpected enumeration ordering issues.

Alternatively, the PCI host bridge could be initialized early, but
without scanning the PCI bus which would be scanned at the time when
all of the struct acpi_device objects are present.  It looks like this
could be made work, but it would require some investigation and code
refactoring.
Miao Wang Dec. 8, 2024, 1:53 p.m. UTC | #5
> 2024年12月5日 18:54,Rafael J. Wysocki <rafael@kernel.org> 写道:
> 
>> 
>> Maybe we can prioritize the initialization of the PCI host bridge to
>> fully eliminate this issue?
> 
> The problem with this is that the current code requires struct
> acpi_device objects to be present for all PCI devices that have
> corresponding objects in the ACPI Namespace at the time when the host
> bridge is initialized because that causes the PCI bus to be scanned
> for devices and struct acpi_device objects are looked up from there.
> 
> To make this work, the "ACPI companion lookup" code needs to be
> changed and that would be kind of a heavy lifting and it may introduce
> some unexpected enumeration ordering issues.
> 
> Alternatively, the PCI host bridge could be initialized early, but
> without scanning the PCI bus which would be scanned at the time when
> all of the struct acpi_device objects are present.  It looks like this
> could be made work, but it would require some investigation and code
> refactoring.

Thanks again for your explanation on this. I understand that it may need
lots of work. I think it would be better if the fix would become as minor
as possible, because it would be easier to backport and more importantly,
the case where legacy IO device is used on non-x86 ACPI-enabled
architectures is really rare.

I'm currently running out of better ideas, due to my limited experience
on this part of code. I suppose maybe we can scan the ACPI device tree
in three passes, adding one in the beginning to pick out the PCI Host.
But we should deal with the case where evaluating the _CRS of the PCI
Host itself requires reading IO ports, since it seems that the
specification does not forbid this...

If you are familiar with someone in the UEFI forum, maybe this problem
can be raised to them and a clarification can be requested.

Cheers,

Miao Wang
diff mbox series

Patch

Index: linux-pm/drivers/acpi/Makefile
===================================================================
--- linux-pm.orig/drivers/acpi/Makefile
+++ linux-pm/drivers/acpi/Makefile
@@ -37,7 +37,7 @@  acpi-$(CONFIG_ACPI_SLEEP)	+= proc.o
 # ACPI Bus and Device Drivers
 #
 acpi-y				+= bus.o glue.o
-acpi-y				+= scan.o
+acpi-y				+= scan.o mipi-disco-img.o
 acpi-y				+= resource.o
 acpi-y				+= acpi_processor.o
 acpi-y				+= processor_core.o
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -281,4 +281,12 @@  void acpi_init_lpit(void);
 static inline void acpi_init_lpit(void) { }
 #endif
 
+/*--------------------------------------------------------------------------
+		ACPI _CRS CSI-2 and MIPI DisCo for Imaging
+  -------------------------------------------------------------------------- */
+
+void acpi_mipi_check_crs_csi2(acpi_handle handle);
+void acpi_mipi_scan_crs_csi2(void);
+void acpi_mipi_crs_csi2_cleanup(void);
+
 #endif /* _ACPI_INTERNAL_H_ */
Index: linux-pm/drivers/acpi/mipi-disco-img.c
===================================================================
--- /dev/null
+++ linux-pm/drivers/acpi/mipi-disco-img.c
@@ -0,0 +1,292 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MIPI DisCo for Imaging support.
+ *
+ * Copyright (C) 2023 Intel Corporation
+ *
+ * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in
+ * Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource
+ * Descriptor" of ACPI 6.5.
+ *
+ * The implementation looks for the information in the ACPI namespace (CSI-2
+ * resource descriptors in _CRS) and constructs software nodes compatible with
+ * Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2
+ * connection graph.
+ */
+
+#include <linux/acpi.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "internal.h"
+
+static LIST_HEAD(acpi_mipi_crs_csi2_list);
+
+static void acpi_mipi_data_tag(acpi_handle handle, void *context)
+{
+}
+
+/* Connection data extracted from one _CRS CSI-2 resource descriptor. */
+struct crs_csi2_connection {
+	struct list_head entry;
+	struct acpi_resource_csi2_serialbus csi2_data;
+	acpi_handle remote_handle;
+	char remote_name[];
+};
+
+/* Data extracted from _CRS CSI-2 resource descriptors for one device. */
+struct crs_csi2 {
+	struct list_head entry;
+	acpi_handle handle;
+	struct acpi_device_software_nodes *swnodes;
+	struct list_head connections;
+	u32 port_count;
+};
+
+struct csi2_resources_walk_data {
+	acpi_handle handle;
+	struct list_head connections;
+};
+
+static acpi_status parse_csi2_resource(struct acpi_resource *res, void *context)
+{
+	struct csi2_resources_walk_data *crwd = context;
+	struct acpi_resource_csi2_serialbus *csi2_res;
+	struct acpi_resource_source *csi2_res_src;
+	u16 csi2_res_src_length;
+	struct crs_csi2_connection *conn;
+	acpi_handle remote_handle;
+
+	if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+		return AE_OK;
+
+	csi2_res = &res->data.csi2_serial_bus;
+
+	if (csi2_res->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2)
+		return AE_OK;
+
+	csi2_res_src = &csi2_res->resource_source;
+	if (ACPI_FAILURE(acpi_get_handle(NULL, csi2_res_src->string_ptr,
+					 &remote_handle))) {
+		acpi_handle_debug(crwd->handle,
+				  "unable to find resource source\n");
+		return AE_OK;
+	}
+	csi2_res_src_length = csi2_res_src->string_length;
+	if (!csi2_res_src_length) {
+		acpi_handle_debug(crwd->handle,
+				  "invalid resource source string length\n");
+		return AE_OK;
+	}
+
+	conn = kmalloc(struct_size(conn, remote_name, csi2_res_src_length + 1),
+		       GFP_KERNEL);
+	if (!conn)
+		return AE_OK;
+
+	conn->csi2_data = *csi2_res;
+	strscpy(conn->remote_name, csi2_res_src->string_ptr, csi2_res_src_length);
+	conn->csi2_data.resource_source.string_ptr = conn->remote_name;
+	conn->remote_handle = remote_handle;
+
+	list_add(&conn->entry, &crwd->connections);
+
+	return AE_OK;
+}
+
+static struct crs_csi2 *acpi_mipi_add_crs_csi2(acpi_handle handle,
+					       struct list_head *list)
+{
+	struct crs_csi2 *csi2;
+
+	csi2 = kzalloc(sizeof(*csi2), GFP_KERNEL);
+	if (!csi2)
+		return NULL;
+
+	csi2->handle = handle;
+	INIT_LIST_HEAD(&csi2->connections);
+	csi2->port_count = 1;
+
+	if (ACPI_FAILURE(acpi_attach_data(handle, acpi_mipi_data_tag, csi2))) {
+		kfree(csi2);
+		return NULL;
+	}
+
+	list_add(&csi2->entry, list);
+
+	return csi2;
+}
+
+static struct crs_csi2 *acpi_mipi_get_crs_csi2(acpi_handle handle)
+{
+	struct crs_csi2 *csi2;
+
+	if (ACPI_FAILURE(acpi_get_data_full(handle, acpi_mipi_data_tag,
+					    (void **)&csi2, NULL)))
+		return NULL;
+
+	return csi2;
+}
+
+static void csi_csr2_release_connections(struct list_head *list)
+{
+	struct crs_csi2_connection *conn, *conn_tmp;
+
+	list_for_each_entry_safe(conn, conn_tmp, list, entry) {
+		list_del(&conn->entry);
+		kfree(conn);
+	}
+}
+
+static void acpi_mipi_del_crs_csi2(struct crs_csi2 *csi2)
+{
+	list_del(&csi2->entry);
+	acpi_detach_data(csi2->handle, acpi_mipi_data_tag);
+	kfree(csi2->swnodes);
+	csi_csr2_release_connections(&csi2->connections);
+	kfree(csi2);
+}
+
+/**
+ * acpi_mipi_check_crs_csi2 - Look for devices with _CRS CSI-2 resources
+ * @handle: ACPI namespace walk starting point.
+ *
+ * Find all devices with _CRS CSI-2 resource descriptors in the ACPI namespace
+ * branch starting at @handle and collect them into a list.
+ */
+void acpi_mipi_check_crs_csi2(acpi_handle handle)
+{
+	struct csi2_resources_walk_data crwd = {
+		.handle = handle,
+		.connections = LIST_HEAD_INIT(crwd.connections),
+	};
+	struct crs_csi2 *csi2;
+
+	/*
+	 * Avoid allocating _CRS CSI-2 objects for devices without any CSI-2
+	 * resource descriptions in _CRS to reduce overhead.
+	 */
+	acpi_walk_resources(handle, METHOD_NAME__CRS, parse_csi2_resource, &crwd);
+	if (list_empty(&crwd.connections))
+		return;
+
+	/*
+	 * Create a _CRS CSI-2 entry to store the extracted connection
+	 * information and add it to the global list.
+	 */
+	csi2 = acpi_mipi_add_crs_csi2(handle, &acpi_mipi_crs_csi2_list);
+	if (!csi2) {
+		csi_csr2_release_connections(&crwd.connections);
+		return; /* Nothing really can be done about this. */
+	}
+
+	list_replace(&crwd.connections, &csi2->connections);
+}
+
+#define NO_CSI2_PORT (UINT_MAX - 1)
+
+static void alloc_crs_csi2_swnodes(struct crs_csi2 *csi2)
+{
+	size_t port_count = csi2->port_count;
+	struct acpi_device_software_nodes *swnodes;
+	size_t alloc_size;
+	unsigned int i;
+
+	/*
+	 * Allocate memory for ports, node pointers (number of nodes +
+	 * 1 (guardian), nodes (root + number of ports * 2 (because for
+	 * every port there is an endpoint)).
+	 */
+	if (check_mul_overflow(sizeof(*swnodes->ports) +
+			       sizeof(*swnodes->nodes) * 2 +
+			       sizeof(*swnodes->nodeptrs) * 2,
+			       port_count, &alloc_size) ||
+	    check_add_overflow(sizeof(*swnodes) +
+			       sizeof(*swnodes->nodes) +
+			       sizeof(*swnodes->nodeptrs) * 2,
+			       alloc_size, &alloc_size)) {
+		acpi_handle_info(csi2->handle,
+				 "too many _CRS CSI-2 resource handles (%zu)",
+				 port_count);
+		return;
+	}
+
+	swnodes = kmalloc(alloc_size, GFP_KERNEL);
+	if (!swnodes)
+		return;
+
+	swnodes->ports = (struct acpi_device_software_node_port *)(swnodes + 1);
+	swnodes->nodes = (struct software_node *)(swnodes->ports + port_count);
+	swnodes->nodeptrs = (const struct software_node **)(swnodes->nodes + 1 +
+				2 * port_count);
+	swnodes->num_ports = port_count;
+
+	for (i = 0; i < 2 * port_count + 1; i++)
+		swnodes->nodeptrs[i] = &swnodes->nodes[i];
+
+	swnodes->nodeptrs[i] = NULL;
+
+	for (i = 0; i < port_count; i++)
+		swnodes->ports[i].port_nr = NO_CSI2_PORT;
+
+	csi2->swnodes = swnodes;
+}
+
+/**
+ * acpi_mipi_scan_crs_csi2 - Create ACPI _CRS CSI-2 software nodes
+ *
+ * Note that this function must be called before any struct acpi_device objects
+ * are bound to any ACPI drivers or scan handlers, so it cannot assume the
+ * existence of struct acpi_device objects for every device present in the ACPI
+ * namespace.
+ *
+ * acpi_scan_lock in scan.c must be held when calling this function.
+ */
+void acpi_mipi_scan_crs_csi2(void)
+{
+	struct crs_csi2 *csi2;
+	LIST_HEAD(aux_list);
+
+	/* Count references to each ACPI handle in the CSI-2 connection graph. */
+	list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) {
+		struct crs_csi2_connection *conn;
+
+		list_for_each_entry(conn, &csi2->connections, entry) {
+			struct crs_csi2 *remote_csi2;
+
+			csi2->port_count++;
+
+			remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle);
+			if (remote_csi2) {
+				remote_csi2->port_count++;
+				continue;
+			}
+			/*
+			 * The remote endpoint has no _CRS CSI-2 list entry yet,
+			 * so create one for it and add it to the list.
+			 */
+			acpi_mipi_add_crs_csi2(conn->remote_handle, &aux_list);
+		}
+	}
+	list_splice(&aux_list, &acpi_mipi_crs_csi2_list);
+
+	/* Allocate softwware nodes for representing the CSI-2 information. */
+	list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
+		alloc_crs_csi2_swnodes(csi2);
+}
+
+/**
+ * acpi_mipi_crs_csi2_cleanup - Free _CRS CSI-2 temporary data
+ */
+void acpi_mipi_crs_csi2_cleanup(void)
+{
+	struct crs_csi2 *csi2, *csi2_tmp;
+
+	list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry)
+		acpi_mipi_del_crs_csi2(csi2);
+}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -366,6 +366,24 @@  struct acpi_device_data {
 
 struct acpi_gpio_mapping;
 
+struct acpi_device_software_node_port {
+	unsigned int port_nr;
+};
+
+/**
+ * struct acpi_device_software_nodes - Software nodes for an ACPI device
+ * @nodes: Software nodes for root as well as ports and endpoints.
+ * @nodeprts: Array of software node pointers, for (un)registering them.
+ * @ports: Information related to each port and endpoint within a port.
+ * @num_ports: The number of ports.
+ */
+struct acpi_device_software_nodes {
+	struct software_node *nodes;
+	const struct software_node **nodeptrs;
+	struct acpi_device_software_node_port *ports;
+	unsigned int num_ports;
+};
+
 /* Device */
 struct acpi_device {
 	u32 pld_crc;
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -1976,7 +1976,7 @@  static void acpi_scan_init_hotplug(struc
 	}
 }
 
-static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
+static u32 acpi_scan_check_dep(acpi_handle handle)
 {
 	struct acpi_handle_list dep_devices;
 	acpi_status status;
@@ -1989,8 +1989,7 @@  static u32 acpi_scan_check_dep(acpi_hand
 	 * 2. ACPI nodes describing USB ports.
 	 * Still, checking for _HID catches more then just these cases ...
 	 */
-	if (!check_dep || !acpi_has_method(handle, "_DEP") ||
-	    !acpi_has_method(handle, "_HID"))
+	if (!acpi_has_method(handle, "_DEP") || !acpi_has_method(handle, "_HID"))
 		return 0;
 
 	status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices);
@@ -2036,7 +2035,13 @@  static u32 acpi_scan_check_dep(acpi_hand
 	return count;
 }
 
-static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
+static acpi_status acpi_scan_check_crs_csi2_cb(acpi_handle handle, u32 a, void *b, void **c)
+{
+	acpi_mipi_check_crs_csi2(handle);
+	return AE_OK;
+}
+
+static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
 				      struct acpi_device **adev_p)
 {
 	struct acpi_device *device = acpi_fetch_acpi_dev(handle);
@@ -2054,9 +2059,25 @@  static acpi_status acpi_bus_check_add(ac
 		if (acpi_device_should_be_hidden(handle))
 			return AE_OK;
 
-		/* Bail out if there are dependencies. */
-		if (acpi_scan_check_dep(handle, check_dep) > 0)
-			return AE_CTRL_DEPTH;
+		if (first_pass) {
+			acpi_mipi_check_crs_csi2(handle);
+
+			/* Bail out if there are dependencies. */
+			if (acpi_scan_check_dep(handle) > 0) {
+				/*
+				 * The entire CSI-2 connection graph needs to be
+				 * extracted before any drivers or scan handlers
+				 * are bound to struct device objects, so scan
+				 * _CRS CSI-2 resource descriptors for all
+				 * devices below the current handle.
+				 */
+				acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+						    ACPI_UINT32_MAX,
+						    acpi_scan_check_crs_csi2_cb,
+						    NULL, NULL, NULL);
+				return AE_CTRL_DEPTH;
+			}
+		}
 
 		fallthrough;
 	case ACPI_TYPE_ANY:	/* for ACPI_ROOT_OBJECT */
@@ -2079,10 +2100,10 @@  static acpi_status acpi_bus_check_add(ac
 	}
 
 	/*
-	 * If check_dep is true at this point, the device has no dependencies,
+	 * If first_pass is true at this point, the device has no dependencies,
 	 * or the creation of the device object would have been postponed above.
 	 */
-	acpi_add_single_object(&device, handle, type, !check_dep);
+	acpi_add_single_object(&device, handle, type, !first_pass);
 	if (!device)
 		return AE_CTRL_DEPTH;
 
@@ -2494,12 +2515,21 @@  int acpi_bus_scan(acpi_handle handle)
 	if (!device)
 		return -ENODEV;
 
+	/*
+	 * Allocate ACPI _CRS CSI-2 software nodes using information extracted
+	 * from the _CRS CSI-2 resource descriptors during the ACPI namespace
+	 * walk above.
+	 */
+	acpi_mipi_scan_crs_csi2();
+
 	acpi_bus_attach(device, (void *)true);
 
 	/* Pass 2: Enumerate all of the remaining devices. */
 
 	acpi_scan_postponed();
 
+	acpi_mipi_crs_csi2_cleanup();
+
 	return 0;
 }
 EXPORT_SYMBOL(acpi_bus_scan);