Commit 836ee487 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux

Pull initial ACPI support for arm64 from Will Deacon:
 "This series introduces preliminary ACPI 5.1 support to the arm64
  kernel using the "hardware reduced" profile.  We don't support any
  peripherals yet, so it's fairly limited in scope:

   - MEMORY init (UEFI)

   - ACPI discovery (RSDP via UEFI)

   - CPU init (FADT)

   - GIC init (MADT)

   - SMP boot (MADT + PSCI)

   - ACPI Kconfig options (dependent on EXPERT)

  ACPI for arm64 has been in development for a while now and hardware
  has been available that can boot with either FDT or ACPI tables.  This
  has been made possible by both changes to the ACPI spec to cater for
  ARM-based machines (known as "hardware-reduced" in ACPI parlance) but
  also a Linaro-driven effort to get this supported on top of the Linux
  kernel.  This pull request is the result of that work.

  These changes allow us to initialise the CPUs, interrupt controller,
  and timers via ACPI tables, with memory information and cmdline coming
  from EFI.  We don't support a hybrid ACPI/FDT scheme.  Of course,
  there is still plenty of work to do (a serial console would be nice!)
  but I expect that to happen on a per-driver basis after this core
  series has been merged.

  Anyway, the diff stat here is fairly horrible, but splitting this up
  and merging it via all the different subsystems would have been
  extremely painful.  Instead, we've got all the relevant Acks in place
  and I've not seen anything other than trivial (Kconfig) conflicts in
  -next (for completeness, I've included my resolution below).  Nearly
  half of the insertions fall under Documentation/.

  So, we'll see how this goes.  Right now, it all depends on EXPERT and
  I fully expect people to use FDT by default for the immediate future"

* tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: (31 commits)
  ARM64 / ACPI: make acpi_map_gic_cpu_interface() as void function
  ARM64 / ACPI: Ignore the return error value of acpi_map_gic_cpu_interface()
  ARM64 / ACPI: fix usage of acpi_map_gic_cpu_interface
  ARM64: kernel: acpi: honour acpi=force command line parameter
  ARM64: kernel: acpi: refactor ACPI tables init and checks
  ARM64: kernel: psci: let ACPI probe PSCI version
  ARM64: kernel: psci: factor out probe function
  ACPI: move arm64 GSI IRQ model to generic GSI IRQ layer
  ARM64 / ACPI: Don't unflatten device tree if acpi=force is passed
  ARM64 / ACPI: additions of ACPI documentation for arm64
  Documentation: ACPI for ARM64
  ARM64 / ACPI: Enable ARM64 in Kconfig
  XEN / ACPI: Make XEN ACPI depend on X86
  ARM64 / ACPI: Select ACPI_REDUCED_HARDWARE_ONLY if ACPI is enabled on ARM64
  clocksource / arch_timer: Parse GTDT to initialize arch timer
  irqchip: Add GICv2 specific ACPI boot support
  ARM64 / ACPI: Introduce ACPI_IRQ_MODEL_GIC and register device's gsi
  ACPI / processor: Make it possible to get CPU hardware ID via GICC
  ACPI / processor: Introduce phys_cpuid_t for CPU hardware ID
  ARM64 / ACPI: Parse MADT for SMP initialization
  ...
parents fb65d872 7676fa70
ACPI Tables
-----------
The expectations of individual ACPI tables are discussed in the list that
follows.
If a section number is used, it refers to a section number in the ACPI
specification where the object is defined. If "Signature Reserved" is used,
the table signature (the first four bytes of the table) is the only portion
of the table recognized by the specification, and the actual table is defined
outside of the UEFI Forum (see Section 5.2.6 of the specification).
For ACPI on arm64, tables also fall into the following categories:
-- Required: DSDT, FADT, GTDT, MADT, MCFG, RSDP, SPCR, XSDT
-- Recommended: BERT, EINJ, ERST, HEST, SSDT
-- Optional: BGRT, CPEP, CSRT, DRTM, ECDT, FACS, FPDT, MCHI, MPST,
MSCT, RASF, SBST, SLIT, SPMI, SRAT, TCPA, TPM2, UEFI
-- Not supported: BOOT, DBG2, DBGP, DMAR, ETDT, HPET, IBFT, IVRS,
LPIT, MSDM, RSDT, SLIC, WAET, WDAT, WDRT, WPBT
Table Usage for ARMv8 Linux
----- ----------------------------------------------------------------
BERT Section 18.3 (signature == "BERT")
== Boot Error Record Table ==
Must be supplied if RAS support is provided by the platform. It
is recommended this table be supplied.
BOOT Signature Reserved (signature == "BOOT")
== simple BOOT flag table ==
Microsoft only table, will not be supported.
BGRT Section 5.2.22 (signature == "BGRT")
== Boot Graphics Resource Table ==
Optional, not currently supported, with no real use-case for an
ARM server.
CPEP Section 5.2.18 (signature == "CPEP")
== Corrected Platform Error Polling table ==
Optional, not currently supported, and not recommended until such
time as ARM-compatible hardware is available, and the specification
suitably modified.
CSRT Signature Reserved (signature == "CSRT")
== Core System Resources Table ==
Optional, not currently supported.
DBG2 Signature Reserved (signature == "DBG2")
== DeBuG port table 2 ==
Microsoft only table, will not be supported.
DBGP Signature Reserved (signature == "DBGP")
== DeBuG Port table ==
Microsoft only table, will not be supported.
DSDT Section 5.2.11.1 (signature == "DSDT")
== Differentiated System Description Table ==
A DSDT is required; see also SSDT.
ACPI tables contain only one DSDT but can contain one or more SSDTs,
which are optional. Each SSDT can only add to the ACPI namespace,
but cannot modify or replace anything in the DSDT.
DMAR Signature Reserved (signature == "DMAR")
== DMA Remapping table ==
x86 only table, will not be supported.
DRTM Signature Reserved (signature == "DRTM")
== Dynamic Root of Trust for Measurement table ==
Optional, not currently supported.
ECDT Section 5.2.16 (signature == "ECDT")
== Embedded Controller Description Table ==
Optional, not currently supported, but could be used on ARM if and
only if one uses the GPE_BIT field to represent an IRQ number, since
there are no GPE blocks defined in hardware reduced mode. This would
need to be modified in the ACPI specification.
EINJ Section 18.6 (signature == "EINJ")
== Error Injection table ==
This table is very useful for testing platform response to error
conditions; it allows one to inject an error into the system as
if it had actually occurred. However, this table should not be
shipped with a production system; it should be dynamically loaded
and executed with the ACPICA tools only during testing.
ERST Section 18.5 (signature == "ERST")
== Error Record Serialization Table ==
On a platform supports RAS, this table must be supplied if it is not
UEFI-based; if it is UEFI-based, this table may be supplied. When this
table is not present, UEFI run time service will be utilized to save
and retrieve hardware error information to and from a persistent store.
ETDT Signature Reserved (signature == "ETDT")
== Event Timer Description Table ==
Obsolete table, will not be supported.
FACS Section 5.2.10 (signature == "FACS")
== Firmware ACPI Control Structure ==
It is unlikely that this table will be terribly useful. If it is
provided, the Global Lock will NOT be used since it is not part of
the hardware reduced profile, and only 64-bit address fields will
be considered valid.
FADT Section 5.2.9 (signature == "FACP")
== Fixed ACPI Description Table ==
Required for arm64.
The HW_REDUCED_ACPI flag must be set. All of the fields that are
to be ignored when HW_REDUCED_ACPI is set are expected to be set to
zero.
If an FACS table is provided, the X_FIRMWARE_CTRL field is to be
used, not FIRMWARE_CTRL.
If PSCI is used (as is recommended), make sure that ARM_BOOT_ARCH is
filled in properly -- that the PSCI_COMPLIANT flag is set and that
PSCI_USE_HVC is set or unset as needed (see table 5-37).
For the DSDT that is also required, the X_DSDT field is to be used,
not the DSDT field.
FPDT Section 5.2.23 (signature == "FPDT")
== Firmware Performance Data Table ==
Optional, not currently supported.
GTDT Section 5.2.24 (signature == "GTDT")
== Generic Timer Description Table ==
Required for arm64.
HEST Section 18.3.2 (signature == "HEST")
== Hardware Error Source Table ==
Until further error source types are defined, use only types 6 (AER
Root Port), 7 (AER Endpoint), 8 (AER Bridge), or 9 (Generic Hardware
Error Source). Firmware first error handling is possible if and only
if Trusted Firmware is being used on arm64.
Must be supplied if RAS support is provided by the platform. It
is recommended this table be supplied.
HPET Signature Reserved (signature == "HPET")
== High Precision Event timer Table ==
x86 only table, will not be supported.
IBFT Signature Reserved (signature == "IBFT")
== iSCSI Boot Firmware Table ==
Microsoft defined table, support TBD.
IVRS Signature Reserved (signature == "IVRS")
== I/O Virtualization Reporting Structure ==
x86_64 (AMD) only table, will not be supported.
LPIT Signature Reserved (signature == "LPIT")
== Low Power Idle Table ==
x86 only table as of ACPI 5.1; future versions have been adapted for
use with ARM and will be recommended in order to support ACPI power
management.
MADT Section 5.2.12 (signature == "APIC")
== Multiple APIC Description Table ==
Required for arm64. Only the GIC interrupt controller structures
should be used (types 0xA - 0xE).
MCFG Signature Reserved (signature == "MCFG")
== Memory-mapped ConFiGuration space ==
If the platform supports PCI/PCIe, an MCFG table is required.
MCHI Signature Reserved (signature == "MCHI")
== Management Controller Host Interface table ==
Optional, not currently supported.
MPST Section 5.2.21 (signature == "MPST")
== Memory Power State Table ==
Optional, not currently supported.
MSDM Signature Reserved (signature == "MSDM")
== Microsoft Data Management table ==
Microsoft only table, will not be supported.
MSCT Section 5.2.19 (signature == "MSCT")
== Maximum System Characteristic Table ==
Optional, not currently supported.
RASF Section 5.2.20 (signature == "RASF")
== RAS Feature table ==
Optional, not currently supported.
RSDP Section 5.2.5 (signature == "RSD PTR")
== Root System Description PoinTeR ==
Required for arm64.
RSDT Section 5.2.7 (signature == "RSDT")
== Root System Description Table ==
Since this table can only provide 32-bit addresses, it is deprecated
on arm64, and will not be used.
SBST Section 5.2.14 (signature == "SBST")
== Smart Battery Subsystem Table ==
Optional, not currently supported.
SLIC Signature Reserved (signature == "SLIC")
== Software LIcensing table ==
Microsoft only table, will not be supported.
SLIT Section 5.2.17 (signature == "SLIT")
== System Locality distance Information Table ==
Optional in general, but required for NUMA systems.
SPCR Signature Reserved (signature == "SPCR")
== Serial Port Console Redirection table ==
Required for arm64.
SPMI Signature Reserved (signature == "SPMI")
== Server Platform Management Interface table ==
Optional, not currently supported.
SRAT Section 5.2.16 (signature == "SRAT")
== System Resource Affinity Table ==
Optional, but if used, only the GICC Affinity structures are read.
To support NUMA, this table is required.
SSDT Section 5.2.11.2 (signature == "SSDT")
== Secondary System Description Table ==
These tables are a continuation of the DSDT; these are recommended
for use with devices that can be added to a running system, but can
also serve the purpose of dividing up device descriptions into more
manageable pieces.
An SSDT can only ADD to the ACPI namespace. It cannot modify or
replace existing device descriptions already in the namespace.
These tables are optional, however. ACPI tables should contain only
one DSDT but can contain many SSDTs.
TCPA Signature Reserved (signature == "TCPA")
== Trusted Computing Platform Alliance table ==
Optional, not currently supported, and may need changes to fully
interoperate with arm64.
TPM2 Signature Reserved (signature == "TPM2")
== Trusted Platform Module 2 table ==
Optional, not currently supported, and may need changes to fully
interoperate with arm64.
UEFI Signature Reserved (signature == "UEFI")
== UEFI ACPI data table ==
Optional, not currently supported. No known use case for arm64,
at present.
WAET Signature Reserved (signature == "WAET")
== Windows ACPI Emulated devices Table ==
Microsoft only table, will not be supported.
WDAT Signature Reserved (signature == "WDAT")
== Watch Dog Action Table ==
Microsoft only table, will not be supported.
WDRT Signature Reserved (signature == "WDRT")
== Watch Dog Resource Table ==
Microsoft only table, will not be supported.
WPBT Signature Reserved (signature == "WPBT")
== Windows Platform Binary Table ==
Microsoft only table, will not be supported.
XSDT Section 5.2.8 (signature == "XSDT")
== eXtended System Description Table ==
Required for arm64.
ACPI Objects
------------
The expectations on individual ACPI objects are discussed in the list that
follows:
Name Section Usage for ARMv8 Linux
---- ------------ -------------------------------------------------
_ADR 6.1.1 Use as needed.
_BBN 6.5.5 Use as needed; PCI-specific.
_BDN 6.5.3 Optional; not likely to be used on arm64.
_CCA 6.2.17 This method should be defined for all bus masters
on arm64. While cache coherency is assumed, making
it explicit ensures the kernel will set up DMA as
it should.
_CDM 6.2.1 Optional, to be used only for processor devices.
_CID 6.1.2 Use as needed.
_CLS 6.1.3 Use as needed.
_CRS 6.2.2 Required on arm64.
_DCK 6.5.2 Optional; not likely to be used on arm64.
_DDN 6.1.4 This field can be used for a device name. However,
it is meant for DOS device names (e.g., COM1), so be
careful of its use across OSes.
_DEP 6.5.8 Use as needed.
_DIS 6.2.3 Optional, for power management use.
_DLM 5.7.5 Optional.
_DMA 6.2.4 Optional.
_DSD 6.2.5 To be used with caution. If this object is used, try
to use it within the constraints already defined by the
Device Properties UUID. Only in rare circumstances
should it be necessary to create a new _DSD UUID.
In either case, submit the _DSD definition along with
any driver patches for discussion, especially when
device properties are used. A driver will not be
considered complete without a corresponding _DSD
description. Once approved by kernel maintainers,
the UUID or device properties must then be registered
with the UEFI Forum; this may cause some iteration as
more than one OS will be registering entries.
_DSM Do not use this method. It is not standardized, the
return values are not well documented, and it is
currently a frequent source of error.
_DSW 7.2.1 Use as needed; power management specific.
_EDL 6.3.1 Optional.
_EJD 6.3.2 Optional.
_EJx 6.3.3 Optional.
_FIX 6.2.7 x86 specific, not used on arm64.
\_GL 5.7.1 This object is not to be used in hardware reduced
mode, and therefore should not be used on arm64.
_GLK 6.5.7 This object requires a global lock be defined; there
is no global lock on arm64 since it runs in hardware
reduced mode. Hence, do not use this object on arm64.
\_GPE 5.3.1 This namespace is for x86 use only. Do not use it
on arm64.
_GSB 6.2.7 Optional.
_HID 6.1.5 Use as needed. This is the primary object to use in
device probing, though _CID and _CLS may also be used.
_HPP 6.2.8 Optional, PCI specific.
_HPX 6.2.9 Optional, PCI specific.
_HRV 6.1.6 Optional, use as needed to clarify device behavior; in
some cases, this may be easier to use than _DSD.
_INI 6.5.1 Not required, but can be useful in setting up devices
when UEFI leaves them in a state that may not be what
the driver expects before it starts probing.
_IRC 7.2.15 Use as needed; power management specific.
_LCK 6.3.4 Optional.
_MAT 6.2.10 Optional; see also the MADT.
_MLS 6.1.7 Optional, but highly recommended for use in
internationalization.
_OFF 7.1.2 It is recommended to define this method for any device
that can be turned on or off.
_ON 7.1.3 It is recommended to define this method for any device
that can be turned on or off.
\_OS 5.7.3 This method will return "Linux" by default (this is
the value of the macro ACPI_OS_NAME on Linux). The
command line parameter acpi_os=<string> can be used
to set it to some other value.
_OSC 6.2.11 This method can be a global method in ACPI (i.e.,
\_SB._OSC), or it may be associated with a specific
device (e.g., \_SB.DEV0._OSC), or both. When used
as a global method, only capabilities published in
the ACPI specification are allowed. When used as
a device-specific method, the process described for
using _DSD MUST be used to create an _OSC definition;
out-of-process use of _OSC is not allowed. That is,
submit the device-specific _OSC usage description as
part of the kernel driver submission, get it approved
by the kernel community, then register it with the
UEFI Forum.
\_OSI 5.7.2 Deprecated on ARM64. Any invocation of this method
will print a warning on the console and return false.
That is, as far as ACPI firmware is concerned, _OSI
cannot be used to determine what sort of system is
being used or what functionality is provided. The
_OSC method is to be used instead.
_OST 6.3.5 Optional.
_PDC 8.4.1 Deprecated, do not use on arm64.
\_PIC 5.8.1 The method should not be used. On arm64, the only
interrupt model available is GIC.
_PLD 6.1.8 Optional.
\_PR 5.3.1 This namespace is for x86 use only on legacy systems.
Do not use it on arm64.
_PRS 6.2.12 Optional.
_PRT 6.2.13 Required as part of the definition of all PCI root
devices.
_PRW 7.2.13 Use as needed; power management specific.
_PRx 7.2.8-11 Use as needed; power management specific. If _PR0 is
defined, _PR3 must also be defined.
_PSC 7.2.6 Use as needed; power management specific.
_PSE 7.2.7 Use as needed; power management specific.
_PSW 7.2.14 Use as needed; power management specific.
_PSx 7.2.2-5 Use as needed; power management specific. If _PS0 is
defined, _PS3 must also be defined. If clocks or
regulators need adjusting to be consistent with power
usage, change them in these methods.
\_PTS 7.3.1 Use as needed; power management specific.
_PXM 6.2.14 Optional.
_REG 6.5.4 Use as needed.
\_REV 5.7.4 Always returns the latest version of ACPI supported.
_RMV 6.3.6 Optional.
\_SB 5.3.1 Required on arm64; all devices must be defined in this
namespace.
_SEG 6.5.6 Use as needed; PCI-specific.
\_SI 5.3.1, Optional.
9.1
_SLI 6.2.15 Optional; recommended when SLIT table is in use.
_STA 6.3.7, It is recommended to define this method for any device
7.1.4 that can be turned on or off.
_SRS 6.2.16 Optional; see also _PRS.
_STR 6.1.10 Recommended for conveying device names to end users;
this is preferred over using _DDN.
_SUB 6.1.9 Use as needed; _HID or _CID are preferred.
_SUN 6.1.11 Optional.
\_Sx 7.3.2 Use as needed; power management specific.
_SxD 7.2.16-19 Use as needed; power management specific.
_SxW 7.2.20-24 Use as needed; power management specific.
_SWS 7.3.3 Use as needed; power management specific; this may
require specification changes for use on arm64.
\_TTS 7.3.4 Use as needed; power management specific.
\_TZ 5.3.1 Optional.
_UID 6.1.12 Recommended for distinguishing devices of the same
class; define it if at all possible.
\_WAK 7.3.5 Use as needed; power management specific.
ACPI Event Model
----------------
Do not use GPE block devices; these are not supported in the hardware reduced
profile used by arm64. Since there are no GPE blocks defined for use on ARM
platforms, GPIO-signaled interrupts should be used for creating system events.
ACPI Processor Control
----------------------
Section 8 of the ACPI specification is currently undergoing change that
should be completed in the 6.0 version of the specification. Processor
performance control will be handled differently for arm64 at that point
in time. Processor aggregator devices (section 8.5) will not be used,
for example, but another similar mechanism instead.
While UEFI constrains what we can say until the release of 6.0, it is
recommended that CPPC (8.4.5) be used as the primary model. This will
still be useful into the future. C-states and P-states will still be
provided, but most of the current design work appears to favor CPPC.
Further, it is essential that the ARMv8 SoC provide a fully functional
implementation of PSCI; this will be the only mechanism supported by ACPI
to control CPU power state (including secondary CPU booting).
More details will be provided on the release of the ACPI 6.0 specification.
ACPI System Address Map Interfaces
----------------------------------
In Section 15 of the ACPI specification, several methods are mentioned as
possible mechanisms for conveying memory resource information to the kernel.
For arm64, we will only support UEFI for booting with ACPI, hence the UEFI
GetMemoryMap() boot service is the only mechanism that will be used.
ACPI Platform Error Interfaces (APEI)
-------------------------------------
The APEI tables supported are described above.
APEI requires the equivalent of an SCI and an NMI on ARMv8. The SCI is used
to notify the OSPM of errors that have occurred but can be corrected and the
system can continue correct operation, even if possibly degraded. The NMI is
used to indicate fatal errors that cannot be corrected, and require immediate
attention.
Since there is no direct equivalent of the x86 SCI or NMI, arm64 handles
these slightly differently. The SCI is handled as a normal GPIO-signaled
interrupt; given that these are corrected (or correctable) errors being
reported, this is sufficient. The NMI is emulated as the highest priority
GPIO-signaled interrupt possible. This implies some caution must be used
since there could be interrupts at higher privilege levels or even interrupts
at the same priority as the emulated NMI. In Linux, this should not be the
case but one should be aware it could happen.
ACPI Objects Not Supported on ARM64
-----------------------------------
While this may change in the future, there are several classes of objects
that can be defined, but are not currently of general interest to ARM servers.
These are not supported:
-- Section 9.2: ambient light sensor devices
-- Section 9.3: battery devices
-- Section 9.4: lids (e.g., laptop lids)
-- Section 9.8.2: IDE controllers
-- Section 9.9: floppy controllers
-- Section 9.10: GPE block devices
-- Section 9.15: PC/AT RTC/CMOS devices
-- Section 9.16: user presence detection devices
-- Section 9.17: I/O APIC devices; all GICs must be enumerable via MADT
-- Section 9.18: time and alarm devices (see 9.15)
ACPI Objects Not Yet Implemented
--------------------------------
While these objects have x86 equivalents, and they do make some sense in ARM
servers, there is either no hardware available at present, or in some cases
there may not yet be a non-ARM implementation. Hence, they are currently not
implemented though that may change in the future.
Not yet implemented are:
-- Section 10: power source and power meter devices
-- Section 11: thermal management
-- Section 12: embedded controllers interface
-- Section 13: SMBus interfaces
-- Section 17: NUMA support (prototypes have been submitted for
review)
ACPI on ARMv8 Servers
---------------------
ACPI can be used for ARMv8 general purpose servers designed to follow
the ARM SBSA (Server Base System Architecture) [0] and SBBR (Server
Base Boot Requirements) [1] specifications. Please note that the SBBR
can be retrieved simply by visiting [1], but the SBSA is currently only
available to those with an ARM login due to ARM IP licensing concerns.
The ARMv8 kernel implements the reduced hardware model of ACPI version
5.1 or later. Links to the specification and all external documents
it refers to are managed by the UEFI Forum. The specification is
available at http://www.uefi.org/specifications and documents referenced
by the specification can be found via http://www.uefi.org/acpi.
If an ARMv8 system does not meet the requirements of the SBSA and SBBR,
or cannot be described using the mechanisms defined in the required ACPI
specifications, then ACPI may not be a good fit for the hardware.
While the documents mentioned above set out the requirements for building
industry-standard ARMv8 servers, they also apply to more than one operating
system. The purpose of this document is to describe the interaction between
ACPI and Linux only, on an ARMv8 system -- that is, what Linux expects of
ACPI and what ACPI can expect of Linux.
Why ACPI on ARM?
----------------
Before examining the details of the interface between ACPI and Linux, it is
useful to understand why ACPI is being used. Several technologies already
exist in Linux for describing non-enumerable hardware, after all. In this
section we summarize a blog post [2] from Grant Likely that outlines the
reasoning behind ACPI on ARMv8 servers. Actually, we snitch a good portion
of the summary text almost directly, to be honest.
The short form of the rationale for ACPI on ARM is:
-- ACPI’s bytecode (AML) allows the platform to encode hardware behavior,
while DT explicitly does not support this. For hardware vendors, being
able to encode behavior is a key tool used in supporting operating
system releases on new hardware.
-- ACPI’s OSPM defines a power management model that constrains what the
platform is allowed to do into a specific model, while still providing
flexibility in hardware design.
-- In the enterprise server environment, ACPI has established bindings (such
as for RAS) which are currently used in production systems. DT does not.
Such bindings could be defined in DT at some point, but doing so means ARM
and x86 would end up using completely different code paths in both firmware
and the kernel.
-- Choosing a single interface to describe the abstraction between a platform
and an OS is important. Hardware vendors would not be required to implement
both DT and ACPI if they want to support multiple operating systems. And,
agreeing on a single interface instead of being fragmented into per OS
interfaces makes for better interoperability overall.
-- The new ACPI governance process works well and Linux is now at the same
table as hardware vendors and other OS vendors. In fact, there is no
longer any reason to feel that ACPI is only belongs to Windows or that
Linux is in any way secondary to Microsoft in this arena. The move of
ACPI governance into the UEFI forum has significantly opened up the
specification development process, and currently, a large portion of the
changes being made to ACPI is being driven by Linux.
Key to the use of ACPI is the support model. For servers in general, the
responsibility for hardware behaviour cannot solely be the domain of the
kernel, but rather must be split between the platform and the kernel, in
order to allow for orderly change over time. ACPI frees the OS from needing
to understand all the minute details of the hardware so that the OS doesn’t
need to be ported to each and every device individually. It allows the
hardware vendors to take responsibility for power management behaviour without
depending on an OS release cycle which is not under their control.
ACPI is also important because hardware and OS vendors have already worked
out the mechanisms for supporting a general purpose computing ecosystem. The
infrastructure is in place, the bindings are in place, and the processes are
in place. DT does exactly what Linux needs it to when working with vertically
integrated devices, but there are no good processes for supporting what the
server vendors need. Linux could potentially get there with DT, but doing so
really just duplicates something that already works. ACPI already does what
the hardware vendors need, Microsoft won’t collaborate on DT, and hardware
vendors would still end up providing two completely separate firmware
interfaces -- one for Linux and one for Windows.
Kernel Compatibility
--------------------
One of the primary motivations for ACPI is standardization, and using that
to provide backward compatibility for Linux kernels. In the server market,
software and hardware are often used for long periods. ACPI allows the
kernel and firmware to agree on a consistent abstraction that can be
maintained over time, even as hardware or software change. As long as the
abstraction is supported, systems can be updated without necessarily having
to replace the kernel.
When a Linux driver or subsystem is first implemented using ACPI, it by
definition ends up requiring a specific version of the ACPI specification
-- it's baseline. ACPI firmware must continue to work, even though it may
not be optimal, with the earliest kernel version that first provides support
for that baseline version of ACPI. There may be a need for additional drivers,
but adding new functionality (e.g., CPU power management) should not break
older kernel versions. Further, ACPI firmware must also work with the most
recent version of the kernel.
Relationship with Device Tree
-----------------------------
ACPI support in drivers and subsystems for ARMv8 should never be mutually
exclusive with DT support at compile time.
At boot time the kernel will only use one description method depending on
parameters passed from the bootloader (including kernel bootargs).
Regardless of whether DT or ACPI is used, the kernel must always be capable
of booting with either scheme (in kernels with both schemes enabled at compile
time).
Booting using ACPI tables
-------------------------
The only defined method for passing ACPI tables to the kernel on ARMv8
is via the UEFI system configuration table. Just so it is explicit, this
means that ACPI is only supported on platforms that boot via UEFI.
When an ARMv8 system boots, it can either have DT information, ACPI tables,
or in some very unusual cases, both. If no command line parameters are used,
the kernel will try to use DT for device enumeration; if there is no DT
present, the kernel will try to use ACPI tables, but only if they are present.
In neither is available, the kernel will not boot. If acpi=force is used
on the command line, the kernel will attempt to use ACPI tables first, but
fall back to DT if there are no ACPI tables present. The basic idea is that
the kernel will not fail to boot unless it absolutely has no other choice.
Processing of ACPI tables may be disabled by passing acpi=off on the kernel
command line; this is the default behavior.
In order for the kernel to load and use ACPI tables, the UEFI implementation
MUST set the ACPI_20_TABLE_GUID to point to the RSDP table (the table with
the ACPI signature "RSD PTR "). If this pointer is incorrect and acpi=force
is used, the kernel will disable ACPI and try to use DT to boot instead; the
kernel has, in effect, determined that ACPI tables are not present at that
point.
If the pointer to the RSDP table is correct, the table will be mapped into
the kernel by the ACPI core, using the address provided by UEFI.
The ACPI core will then locate and map in all other ACPI tables provided by
using the addresses in the RSDP table to find the XSDT (eXtended System
Description Table). The XSDT in turn provides the addresses to all other
ACPI tables provided by the system firmware; the ACPI core will then traverse
this table and map in the tables listed.
The ACPI core will ignore any provided RSDT (Root System Description Table).
RSDTs have been deprecated and are ignored on arm64 since they only allow
for 32-bit addresses.
Further, the ACPI core will only use the 64-bit address fields in the FADT
(Fixed ACPI Description Table). Any 32-bit address fields in the FADT will
be ignored on arm64.
Hardware reduced mode (see Section 4.1 of the ACPI 5.1 specification) will
be enforced by the ACPI core on arm64. Doing so allows the ACPI core to
run less complex code since it no longer has to provide support for legacy
hardware from other architectures. Any fields that are not to be used for
hardware reduced mode must be set to zero.
For the ACPI core to operate properly, and in turn provide the information
the kernel needs to configure devices, it expects to find the following
tables (all section numbers refer to the ACPI 5.1 specfication):
-- RSDP (Root System Description Pointer), section 5.2.5
-- XSDT (eXtended System Description Table), section 5.2.8
-- FADT (Fixed ACPI Description Table), section 5.2.9
-- DSDT (Differentiated System Description Table), section
5.2.11.1
-- MADT (Multiple APIC Description Table), section 5.2.12
-- GTDT (Generic Timer Description Table), section 5.2.24
-- If PCI is supported, the MCFG (Memory mapped ConFiGuration
Table), section 5.2.6, specifically Table 5-31.
If the above tables are not all present, the kernel may or may not be
able to boot properly since it may not be able to configure all of the
devices available.
ACPI Detection
--------------
Drivers should determine their probe() type by checking for a null
value for ACPI_HANDLE, or checking .of_node, or other information in
the device structure. This is detailed further in the "Driver
Recommendations" section.
In non-driver code, if the presence of ACPI needs to be detected at
runtime, then check the value of acpi_disabled. If CONFIG_ACPI is not
set, acpi_disabled will always be 1.
Device Enumeration
------------------
Device descriptions in ACPI should use standard recognized ACPI interfaces.
These may contain less information than is typically provided via a Device
Tree description for the same device. This is also one of the reasons that
ACPI can be useful -- the driver takes into account that it may have less
detailed information about the device and uses sensible defaults instead.
If done properly in the driver, the hardware can change and improve over
time without the driver having to change at all.
Clocks provide an excellent example. In DT, clocks need to be specified
and the drivers need to take them into account. In ACPI, the assumption
is that UEFI will leave the device in a reasonable default state, including
any clock settings. If for some reason the driver needs to change a clock
value, this can be done in an ACPI method; all the driver needs to do is
invoke the method and not concern itself with what the method needs to do
to change the clock. Changing the hardware can then take place over time
by changing what the ACPI method does, and not the driver.
In DT, the parameters needed by the driver to set up clocks as in the example
above are known as "bindings"; in ACPI, these are known as "Device Properties"
and provided to a driver via the _DSD object.
ACPI tables are described with a formal language called ASL, the ACPI
Source Language (section 19 of the specification). This means that there
are always multiple ways to describe the same thing -- including device
properties. For example, device properties could use an ASL construct
that looks like this: Name(KEY0, "value0"). An ACPI device driver would
then retrieve the value of the property by evaluating the KEY0 object.
However, using Name() this way has multiple problems: (1) ACPI limits
names ("KEY0") to four characters unlike DT; (2) there is no industry
wide registry that maintains a list of names, minimzing re-use; (3)
there is also no registry for the definition of property values ("value0"),
again making re-use difficult; and (4) how does one maintain backward
compatibility as new hardware comes out? The _DSD method was created
to solve precisely these sorts of problems; Linux drivers should ALWAYS
use the _DSD method for device properties and nothing else.
The _DSM object (ACPI Section 9.14.1) could also be used for conveying
device properties to a driver. Linux drivers should only expect it to
be used if _DSD cannot represent the data required, and there is no way
to create a new UUID for the _DSD object. Note that there is even less
regulation of the use of _DSM than there is of _DSD. Drivers that depend
on the contents of _DSM objects will be more difficult to maintain over
time because of this; as of this writing, the use of _DSM is the cause
of quite a few firmware problems and is not recommended.
Drivers should look for device properties in the _DSD object ONLY; the _DSD
object is described in the ACPI specification section 6.2.5, but this only
describes how to define the structure of an object returned via _DSD, and
how specific data structures are defined by specific UUIDs. Linux should
only use the _DSD Device Properties UUID [5]:
-- UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301
-- http://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf
The UEFI Forum provides a mechanism for registering device properties [4]
so that they may be used across all operating systems supporting ACPI.
Device properties that have not been registered with the UEFI Forum should
not be used.
Before creating new device properties, check to be sure that they have not
been defined before and either registered in the Linux kernel documentation
as DT bindings, or the UEFI Forum as device properties. While we do not want
to simply move all DT bindings into ACPI device properties, we can learn from
what has been previously defined.
If it is necessary to define a new device property, or if it makes sense to
synthesize the definition of a binding so it can be used in any firmware,
both DT bindings and ACPI device properties for device drivers have review
processes. Use them both. When the driver itself is submitted for review
to the Linux mailing lists, the device property definitions needed must be
submitted at the same time. A driver that supports ACPI and uses device
properties will not be considered complete without their definitions. Once
the device property has been accepted by the Linux community, it must be
registered with the UEFI Forum [4], which will review it again for consistency
within the registry. This may require iteration. The UEFI Forum, though,
will always be the canonical site for device property definitions.
It may make sense to provide notice to the UEFI Forum that there is the
intent to register a previously unused device property name as a means of
reserving the name for later use. Other operating system vendors will
also be submitting registration requests and this may help smooth the
process.
Once registration and review have been completed, the kernel provides an
interface for looking up device properties in a manner independent of
whether DT or ACPI is being used. This API should be used [6]; it can
eliminate some duplication of code paths in driver probing functions and
discourage divergence between DT bindings and ACPI device properties.
Programmable Power Control Resources
------------------------------------
Programmable power control resources include such resources as voltage/current
providers (regulators) and clock sources.
With ACPI, the kernel clock and regulator framework is not expected to be used
at all.
The kernel assumes that power control of these resources is represented with
Power Resource Objects (ACPI section 7.1). The ACPI core will then handle
correctly enabling and disabling resources as they are needed. In order to
get that to work, ACPI assumes each device has defined D-states and that these
can be controlled through the optional ACPI methods _PS0, _PS1, _PS2, and _PS3;
in ACPI, _PS0 is the method to invoke to turn a device full on, and _PS3 is for
turning a device full off.
There are two options for using those Power Resources. They can:
-- be managed in a _PSx method which gets called on entry to power
state Dx.
-- be declared separately as power resources with their own _ON and _OFF
methods. They are then tied back to D-states for a particular device
via _PRx which specifies which power resources a device needs to be on
while in Dx. Kernel then tracks number of devices using a power resource
and calls _ON/_OFF as needed.
The kernel ACPI code will also assume that the _PSx methods follow the normal
ACPI rules for such methods:
-- If either _PS0 or _PS3 is implemented, then the other method must also
be implemented.
-- If a device requires usage or setup of a power resource when on, the ASL
should organize that it is allocated/enabled using the _PS0 method.
-- Resources allocated or enabled in the _PS0 method should be disabled
or de-allocated in the _PS3 method.
-- Firmware will leave the resources in a reasonable state before handing
over control to the kernel.
Such code in _PSx methods will of course be very platform specific. But,
this allows the driver to abstract out the interface for operating the device
and avoid having to read special non-standard values from ACPI tables. Further,
abstracting the use of these resources allows the hardware to change over time
without requiring updates to the driver.
Clocks
------
ACPI makes the assumption that clocks are initialized by the firmware --
UEFI, in this case -- to some working value before control is handed over
to the kernel. This has implications for devices such as UARTs, or SoC-driven
LCD displays, for example.
When the kernel boots, the clocks are assumed to be set to reasonable
working values. If for some reason the frequency needs to change -- e.g.,
throttling for power management -- the device driver should expect that
process to be abstracted out into some ACPI method that can be invoked
(please see the ACPI specification for further recommendations on standard
methods to be expected). The only exceptions to this are CPU clocks where
CPPC provides a much richer interface than ACPI methods. If the clocks
are not set, there is no direct way for Linux to control them.
If an SoC vendor wants to provide fine-grained control of the system clocks,
they could do so by providing ACPI methods that could be invoked by Linux
drivers. However, this is NOT recommended and Linux drivers should NOT use
such methods, even if they are provided. Such methods are not currently
standardized in the ACPI specification, and using them could tie a kernel
to a very specific SoC, or tie an SoC to a very specific version of the
kernel, both of which we are trying to avoid.
Driver Recommendations
----------------------
DO NOT remove any DT handling when adding ACPI support for a driver. The
same device may be used on many different systems.
DO try to structure the driver so that it is data-driven. That is, set up
a struct containing internal per-device state based on defaults and whatever
else must be discovered by the driver probe function. Then, have the rest
of the driver operate off of the contents of that struct. Doing so should
allow most divergence between ACPI and DT functionality to be kept local to
the probe function instead of being scattered throughout the driver. For
example:
static int device_probe_dt(struct platform_device *pdev)
{
/* DT specific functionality */
...
}
static int device_probe_acpi(struct platform_device *pdev)
{
/* ACPI specific functionality */
...
}
static int device_probe(struct platform_device *pdev)
{
...
struct device_node node = pdev->dev.of_node;
...
if (node)
ret = device_probe_dt(pdev);
else if (ACPI_HANDLE(&pdev->dev))
ret = device_probe_acpi(pdev);
else
/* other initialization */
...
/* Continue with any generic probe operations */
...
}
DO keep the MODULE_DEVICE_TABLE entries together in the driver to make it
clear the different names the driver is probed for, both from DT and from
ACPI:
static struct of_device_id virtio_mmio_match[] = {
{ .compatible = "virtio,mmio", },
{ }
};
MODULE_DEVICE_TABLE(of, virtio_mmio_match);
static const struct acpi_device_id virtio_mmio_acpi_match[] = {
{ "LNRO0005", },
{ }
};
MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match);
ASWG
----
The ACPI specification changes regularly. During the year 2014, for instance,
version 5.1 was released and version 6.0 substantially completed, with most of
the changes being driven by ARM-specific requirements. Proposed changes are
presented and discussed in the ASWG (ACPI Specification Working Group) which
is a part of the UEFI Forum.
Participation in this group is open to all UEFI members. Please see
http://www.uefi.org/workinggroup for details on group membership.
It is the intent of the ARMv8 ACPI kernel code to follow the ACPI specification
as closely as possible, and to only implement functionality that complies with
the released standards from UEFI ASWG. As a practical matter, there will be
vendors that provide bad ACPI tables or violate the standards in some way.
If this is because of errors, quirks and fixups may be necessary, but will
be avoided if possible. If there are features missing from ACPI that preclude
it from being used on a platform, ECRs (Engineering Change Requests) should be
submitted to ASWG and go through the normal approval process; for those that
are not UEFI members, many other members of the Linux community are and would
likely be willing to assist in submitting ECRs.
Linux Code
----------
Individual items specific to Linux on ARM, contained in the the Linux
source code, are in the list that follows:
ACPI_OS_NAME This macro defines the string to be returned when
an ACPI method invokes the _OS method. On ARM64
systems, this macro will be "Linux" by default.
The command line parameter acpi_os=<string>
can be used to set it to some other value. The
default value for other architectures is "Microsoft
Windows NT", for example.
ACPI Objects
------------
Detailed expectations for ACPI tables and object are listed in the file
Documentation/arm64/acpi_object_usage.txt.
References
----------
[0] http://silver.arm.com -- document ARM-DEN-0029, or newer
"Server Base System Architecture", version 2.3, dated 27 Mar 2014
[1] http://infocenter.arm.com/help/topic/com.arm.doc.den0044a/Server_Base_Boot_Requirements.pdf
Document ARM-DEN-0044A, or newer: "Server Base Boot Requirements, System
Software on ARM Platforms", dated 16 Aug 2014
[2] http://www.secretlab.ca/archives/151, 10 Jan 2015, Copyright (c) 2015,
Linaro Ltd., written by Grant Likely. A copy of the verbatim text (apart
from formatting) is also in Documentation/arm64/why_use_acpi.txt.
[3] AMD ACPI for Seattle platform documentation:
http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Seattle_ACPI_Guide.pdf
[4] http://www.uefi.org/acpi -- please see the link for the "ACPI _DSD Device
Property Registry Instructions"
[5] http://www.uefi.org/acpi -- please see the link for the "_DSD (Device
Specific Data) Implementation Guide"
[6] Kernel code for the unified device property interface can be found in
include/linux/property.h and drivers/base/property.c.
Authors
-------
Al Stone <al.stone@linaro.org>
Graeme Gregory <graeme.gregory@linaro.org>
Hanjun Guo <hanjun.guo@linaro.org>
Grant Likely <grant.likely@linaro.org>, for the "Why ACPI on ARM?" section
...@@ -165,7 +165,7 @@ multipliers 'Kilo', 'Mega', and 'Giga', equalling 2^10, 2^20, and 2^30 ...@@ -165,7 +165,7 @@ multipliers 'Kilo', 'Mega', and 'Giga', equalling 2^10, 2^20, and 2^30
bytes respectively. Such letter suffixes can also be entirely omitted. bytes respectively. Such letter suffixes can also be entirely omitted.
acpi= [HW,ACPI,X86] acpi= [HW,ACPI,X86,ARM64]
Advanced Configuration and Power Interface Advanced Configuration and Power Interface
Format: { force | off | strict | noirq | rsdt } Format: { force | off | strict | noirq | rsdt }
force -- enable ACPI if default was off force -- enable ACPI if default was off
...@@ -175,6 +175,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -175,6 +175,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
strictly ACPI specification compliant. strictly ACPI specification compliant.
rsdt -- prefer RSDT over (default) XSDT rsdt -- prefer RSDT over (default) XSDT
copy_dsdt -- copy DSDT to memory copy_dsdt -- copy DSDT to memory
For ARM64, ONLY "acpi=off" or "acpi=force" are available
See also Documentation/power/runtime_pm.txt, pci=noacpi See also Documentation/power/runtime_pm.txt, pci=noacpi
......
config ARM64 config ARM64
def_bool y def_bool y
select ACPI_GENERIC_GSI if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_GCOV_PROFILE_ALL
...@@ -758,6 +760,8 @@ source "drivers/Kconfig" ...@@ -758,6 +760,8 @@ source "drivers/Kconfig"
source "drivers/firmware/Kconfig" source "drivers/firmware/Kconfig"
source "drivers/acpi/Kconfig"
source "fs/Kconfig" source "fs/Kconfig"
source "arch/arm64/kvm/Kconfig" source "arch/arm64/kvm/Kconfig"
......
/*
* ARM64 specific ACPICA environments and implementation
*
* Copyright (C) 2014, Linaro Ltd.
* Author: Hanjun Guo <hanjun.guo@linaro.org>
* Author: Graeme Gregory <graeme.gregory@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ASM_ACENV_H
#define _ASM_ACENV_H
/* It is required unconditionally by ACPI core, update it when needed. */
#endif /* _ASM_ACENV_H */
/*
* Copyright (C) 2013-2014, Linaro Ltd.
* Author: Al Stone <al.stone@linaro.org>
* Author: Graeme Gregory <graeme.gregory@linaro.org>
* Author: Hanjun Guo <hanjun.guo@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*/
#ifndef _ASM_ACPI_H
#define _ASM_ACPI_H
#include <linux/mm.h>
#include <linux/irqchip/arm-gic-acpi.h>
#include <asm/cputype.h>
#include <asm/smp_plat.h>
/* Basic configuration for ACPI */
#ifdef CONFIG_ACPI
/* ACPI table mapping after acpi_gbl_permanent_mmap is set */
static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys,
acpi_size size)
{
if (!page_is_ram(phys >> PAGE_SHIFT))
return ioremap(phys, size);
return ioremap_cache(phys, size);
}
#define acpi_os_ioremap acpi_os_ioremap
typedef u64 phys_cpuid_t;
#define PHYS_CPUID_INVALID INVALID_HWID
#define acpi_strict 1 /* No out-of-spec workarounds on ARM64 */
extern int acpi_disabled;
extern int acpi_noirq;
extern int acpi_pci_disabled;
/* 1 to indicate PSCI 0.2+ is implemented */
static inline bool acpi_psci_present(void)
{
return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT;
}
/* 1 to indicate HVC must be used instead of SMC as the PSCI conduit */
static inline bool acpi_psci_use_hvc(void)
{
return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_USE_HVC;
}
static inline void disable_acpi(void)
{
acpi_disabled = 1;
acpi_pci_disabled = 1;
acpi_noirq = 1;
}
static inline void enable_acpi(void)
{
acpi_disabled = 0;
acpi_pci_disabled = 0;
acpi_noirq = 0;
}
/*
* The ACPI processor driver for ACPI core code needs this macro
* to find out this cpu was already mapped (mapping from CPU hardware
* ID to CPU logical ID) or not.
*/
#define cpu_physical_id(cpu) cpu_logical_map(cpu)
/*
* It's used from ACPI core in kdump to boot UP system with SMP kernel,
* with this check the ACPI core will not override the CPU index
* obtained from GICC with 0 and not print some error message as well.
* Since MADT must provide at least one GICC structure for GIC
* initialization, CPU will be always available in MADT on ARM64.
*/
static inline bool acpi_has_cpu_in_madt(void)
{
return true;
}
static inline void arch_fix_phys_package_id(int num, u32 slot) { }
void __init acpi_init_cpus(void);
#else
static inline bool acpi_psci_present(void) { return false; }
static inline bool acpi_psci_use_hvc(void) { return false; }
static inline void acpi_init_cpus(void) { }
#endif /* CONFIG_ACPI */
#endif /*_ASM_ACPI_H*/
...@@ -66,5 +66,6 @@ struct cpu_operations { ...@@ -66,5 +66,6 @@ struct cpu_operations {
extern const struct cpu_operations *cpu_ops[NR_CPUS]; extern const struct cpu_operations *cpu_ops[NR_CPUS];
int __init cpu_read_ops(struct device_node *dn, int cpu); int __init cpu_read_ops(struct device_node *dn, int cpu);
void __init cpu_read_bootcpu_ops(void); void __init cpu_read_bootcpu_ops(void);
const struct cpu_operations *cpu_get_ops(const char *name);
#endif /* ifndef __ASM_CPU_OPS_H */ #endif /* ifndef __ASM_CPU_OPS_H */
...@@ -62,6 +62,9 @@ void __init early_fixmap_init(void); ...@@ -62,6 +62,9 @@ void __init early_fixmap_init(void);
#define __early_set_fixmap __set_fixmap #define __early_set_fixmap __set_fixmap
#define __late_set_fixmap __set_fixmap
#define __late_clear_fixmap(idx) __set_fixmap((idx), 0, FIXMAP_PAGE_CLEAR)
extern void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot); extern void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot);
#include <asm-generic/fixmap.h> #include <asm-generic/fixmap.h>
......
#ifndef __ASM_IRQ_H #ifndef __ASM_IRQ_H
#define __ASM_IRQ_H #define __ASM_IRQ_H
#include <linux/irqchip/arm-gic-acpi.h>
#include <asm-generic/irq.h> #include <asm-generic/irq.h>
struct pt_regs; struct pt_regs;
...@@ -8,4 +10,15 @@ struct pt_regs; ...@@ -8,4 +10,15 @@ struct pt_regs;
extern void migrate_irqs(void); extern void migrate_irqs(void);
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
static inline void acpi_irq_init(void)
{
/*
* Hardcode ACPI IRQ chip initialization to GICv2 for now.
* Proper irqchip infrastructure will be implemented along with
* incoming GICv2m|GICv3|ITS bits.
*/
acpi_gic_init();
}
#define acpi_irq_init acpi_irq_init
#endif #endif
...@@ -27,6 +27,12 @@ ...@@ -27,6 +27,12 @@
extern int isa_dma_bridge_buggy; extern int isa_dma_bridge_buggy;
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
{
/* no legacy IRQ on arm64 */
return -ENODEV;
}
static inline int pci_proc_domain(struct pci_bus *bus) static inline int pci_proc_domain(struct pci_bus *bus)
{ {
return 1; return 1;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#ifndef __ASM_PSCI_H #ifndef __ASM_PSCI_H
#define __ASM_PSCI_H #define __ASM_PSCI_H
int psci_init(void); int psci_dt_init(void);
int psci_acpi_init(void);
#endif /* __ASM_PSCI_H */ #endif /* __ASM_PSCI_H */
...@@ -39,9 +39,10 @@ extern void show_ipi_list(struct seq_file *p, int prec); ...@@ -39,9 +39,10 @@ extern void show_ipi_list(struct seq_file *p, int prec);
extern void handle_IPI(int ipinr, struct pt_regs *regs); extern void handle_IPI(int ipinr, struct pt_regs *regs);
/* /*
* Setup the set of possible CPUs (via set_cpu_possible) * Discover the set of possible CPUs and determine their
* SMP operations.
*/ */
extern void smp_init_cpus(void); extern void of_smp_init_cpus(void);
/* /*
* Provide a function to raise an IPI cross call on CPUs in callmap. * Provide a function to raise an IPI cross call on CPUs in callmap.
......
...@@ -35,6 +35,7 @@ arm64-obj-$(CONFIG_KGDB) += kgdb.o ...@@ -35,6 +35,7 @@ arm64-obj-$(CONFIG_KGDB) += kgdb.o
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
arm64-obj-$(CONFIG_PCI) += pci.o arm64-obj-$(CONFIG_PCI) += pci.o
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
arm64-obj-$(CONFIG_ACPI) += acpi.o
obj-y += $(arm64-obj-y) vdso/ obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m) obj-m += $(arm64-obj-m)
......
/*
* ARM64 Specific Low-Level ACPI Boot Support
*
* Copyright (C) 2013-2014, Linaro Ltd.
* Author: Al Stone <al.stone@linaro.org>
* Author: Graeme Gregory <graeme.gregory@linaro.org>
* Author: Hanjun Guo <hanjun.guo@linaro.org>
* Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
* Author: Naresh Bhat <naresh.bhat@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) "ACPI: " fmt
#include <linux/acpi.h>
#include <linux/bootmem.h>
#include <linux/cpumask.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/smp.h>
#include <asm/cputype.h>
#include <asm/cpu_ops.h>
#include <asm/smp_plat.h>
int acpi_noirq = 1; /* skip ACPI IRQ initialization */
int acpi_disabled = 1;
EXPORT_SYMBOL(acpi_disabled);
int acpi_pci_disabled = 1; /* skip ACPI PCI scan and IRQ initialization */
EXPORT_SYMBOL(acpi_pci_disabled);
/* Processors with enabled flag and sane MPIDR */
static int enabled_cpus;
/* Boot CPU is valid or not in MADT */
static bool bootcpu_valid __initdata;
static bool param_acpi_off __initdata;
static bool param_acpi_force __initdata;
static int __init parse_acpi(char *arg)
{
if (!arg)
return -EINVAL;
/* "acpi=off" disables both ACPI table parsing and interpreter */
if (strcmp(arg, "off") == 0)
param_acpi_off = true;
else if (strcmp(arg, "force") == 0) /* force ACPI to be enabled */
param_acpi_force = true;
else
return -EINVAL; /* Core will print when we return error */
return 0;
}
early_param("acpi", parse_acpi);
static int __init dt_scan_depth1_nodes(unsigned long node,
const char *uname, int depth,
void *data)
{
/*
* Return 1 as soon as we encounter a node at depth 1 that is
* not the /chosen node.
*/
if (depth == 1 && (strcmp(uname, "chosen") != 0))
return 1;
return 0;
}
/*
* __acpi_map_table() will be called before page_init(), so early_ioremap()
* or early_memremap() should be called here to for ACPI table mapping.
*/
char *__init __acpi_map_table(unsigned long phys, unsigned long size)
{
if (!size)
return NULL;
return early_memremap(phys, size);
}
void __init __acpi_unmap_table(char *map, unsigned long size)
{
if (!map || !size)
return;
early_memunmap(map, size);
}
/**
* acpi_map_gic_cpu_interface - generates a logical cpu number
* and map to MPIDR represented by GICC structure
*/
static void __init
acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
{
int i;
u64 mpidr = processor->arm_mpidr & MPIDR_HWID_BITMASK;
bool enabled = !!(processor->flags & ACPI_MADT_ENABLED);
if (mpidr == INVALID_HWID) {
pr_info("Skip MADT cpu entry with invalid MPIDR\n");
return;
}
total_cpus++;
if (!enabled)
return;
if (enabled_cpus >= NR_CPUS) {
pr_warn("NR_CPUS limit of %d reached, Processor %d/0x%llx ignored.\n",
NR_CPUS, total_cpus, mpidr);
return;
}
/* Check if GICC structure of boot CPU is available in the MADT */
if (cpu_logical_map(0) == mpidr) {
if (bootcpu_valid) {
pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n",
mpidr);
return;
}
bootcpu_valid = true;
}
/*
* Duplicate MPIDRs are a recipe for disaster. Scan
* all initialized entries and check for
* duplicates. If any is found just ignore the CPU.
*/
for (i = 1; i < enabled_cpus; i++) {
if (cpu_logical_map(i) == mpidr) {
pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n",
mpidr);
return;
}
}
if (!acpi_psci_present())
return;
cpu_ops[enabled_cpus] = cpu_get_ops("psci");
/* CPU 0 was already initialized */
if (enabled_cpus) {
if (!cpu_ops[enabled_cpus])
return;
if (cpu_ops[enabled_cpus]->cpu_init(NULL, enabled_cpus))
return;
/* map the logical cpu id to cpu MPIDR */
cpu_logical_map(enabled_cpus) = mpidr;
}
enabled_cpus++;
}
static int __init
acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header,
const unsigned long end)
{
struct acpi_madt_generic_interrupt *processor;
processor = (struct acpi_madt_generic_interrupt *)header;
if (BAD_MADT_ENTRY(processor, end))
return -EINVAL;
acpi_table_print_madt_entry(header);
acpi_map_gic_cpu_interface(processor);
return 0;
}
/* Parse GIC cpu interface entries in MADT for SMP init */
void __init acpi_init_cpus(void)
{
int count, i;
/*
* do a partial walk of MADT to determine how many CPUs
* we have including disabled CPUs, and get information
* we need for SMP init
*/
count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
acpi_parse_gic_cpu_interface, 0);
if (!count) {
pr_err("No GIC CPU interface entries present\n");
return;
} else if (count < 0) {
pr_err("Error parsing GIC CPU interface entry\n");
return;
}
if (!bootcpu_valid) {
pr_err("MADT missing boot CPU MPIDR, not enabling secondaries\n");
return;
}
for (i = 0; i < enabled_cpus; i++)
set_cpu_possible(i, true);
/* Make boot-up look pretty */
pr_info("%d CPUs enabled, %d CPUs total\n", enabled_cpus, total_cpus);
}
/*
* acpi_fadt_sanity_check() - Check FADT presence and carry out sanity
* checks on it
*
* Return 0 on success, <0 on failure
*/
static int __init acpi_fadt_sanity_check(void)
{
struct acpi_table_header *table;
struct acpi_table_fadt *fadt;
acpi_status status;
acpi_size tbl_size;
int ret = 0;
/*
* FADT is required on arm64; retrieve it to check its presence
* and carry out revision and ACPI HW reduced compliancy tests
*/
status = acpi_get_table_with_size(ACPI_SIG_FADT, 0, &table, &tbl_size);
if (ACPI_FAILURE(status)) {
const char *msg = acpi_format_exception(status);
pr_err("Failed to get FADT table, %s\n", msg);
return -ENODEV;
}
fadt = (struct acpi_table_fadt *)table;
/*
* Revision in table header is the FADT Major revision, and there
* is a minor revision of FADT which was introduced by ACPI 5.1,
* we only deal with ACPI 5.1 or newer revision to get GIC and SMP
* boot protocol configuration data.
*/
if (table->revision < 5 ||
(table->revision == 5 && fadt->minor_revision < 1)) {
pr_err("Unsupported FADT revision %d.%d, should be 5.1+\n",
table->revision, fadt->minor_revision);
ret = -EINVAL;
goto out;
}
if (!(fadt->flags & ACPI_FADT_HW_REDUCED)) {
pr_err("FADT not ACPI hardware reduced compliant\n");
ret = -EINVAL;
}
out:
/*
* acpi_get_table_with_size() creates FADT table mapping that
* should be released after parsing and before resuming boot
*/
early_acpi_os_unmap_memory(table, tbl_size);
return ret;
}
/*
* acpi_boot_table_init() called from setup_arch(), always.
* 1. find RSDP and get its address, and then find XSDT
* 2. extract all tables and checksums them all
* 3. check ACPI FADT revision
* 4. check ACPI FADT HW reduced flag
*
* We can parse ACPI boot-time tables such as MADT after
* this function is called.
*
* On return ACPI is enabled if either:
*
* - ACPI tables are initialized and sanity checks passed
* - acpi=force was passed in the command line and ACPI was not disabled
* explicitly through acpi=off command line parameter
*
* ACPI is disabled on function return otherwise
*/
void __init acpi_boot_table_init(void)
{
/*
* Enable ACPI instead of device tree unless
* - ACPI has been disabled explicitly (acpi=off), or
* - the device tree is not empty (it has more than just a /chosen node)
* and ACPI has not been force enabled (acpi=force)
*/
if (param_acpi_off ||
(!param_acpi_force && of_scan_flat_dt(dt_scan_depth1_nodes, NULL)))
return;
/*
* ACPI is disabled at this point. Enable it in order to parse
* the ACPI tables and carry out sanity checks
*/
enable_acpi();
/*
* If ACPI tables are initialized and FADT sanity checks passed,
* leave ACPI enabled and carry on booting; otherwise disable ACPI
* on initialization error.
* If acpi=force was passed on the command line it forces ACPI
* to be enabled even if its initialization failed.
*/
if (acpi_table_init() || acpi_fadt_sanity_check()) {
pr_err("Failed to init ACPI tables\n");
if (!param_acpi_force)
disable_acpi();
}
}
void __init acpi_gic_init(void)
{
struct acpi_table_header *table;
acpi_status status;
acpi_size tbl_size;
int err;
if (acpi_disabled)
return;
status = acpi_get_table_with_size(ACPI_SIG_MADT, 0, &table, &tbl_size);
if (ACPI_FAILURE(status)) {
const char *msg = acpi_format_exception(status);
pr_err("Failed to get MADT table, %s\n", msg);
return;
}
err = gic_v2_acpi_init(table);
if (err)
pr_err("Failed to initialize GIC IRQ controller");
early_acpi_os_unmap_memory((char *)table, tbl_size);
}
...@@ -35,7 +35,7 @@ static const struct cpu_operations *supported_cpu_ops[] __initconst = { ...@@ -35,7 +35,7 @@ static const struct cpu_operations *supported_cpu_ops[] __initconst = {
NULL, NULL,
}; };
static const struct cpu_operations * __init cpu_get_ops(const char *name) const struct cpu_operations * __init cpu_get_ops(const char *name)
{ {
const struct cpu_operations **ops = supported_cpu_ops; const struct cpu_operations **ops = supported_cpu_ops;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* *
*/ */
#include <linux/acpi.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -46,3 +47,27 @@ int pcibios_add_device(struct pci_dev *dev) ...@@ -46,3 +47,27 @@ int pcibios_add_device(struct pci_dev *dev)
return 0; return 0;
} }
/*
* raw_pci_read/write - Platform-specific PCI config space access.
*/
int raw_pci_read(unsigned int domain, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *val)
{
return -ENXIO;
}
int raw_pci_write(unsigned int domain, unsigned int bus,
unsigned int devfn, int reg, int len, u32 val)
{
return -ENXIO;
}
#ifdef CONFIG_ACPI
/* Root bridge scanning */
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
{
/* TODO: Should be revisited when implementing PCI on ACPI */
return NULL;
}
#endif
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#define pr_fmt(fmt) "psci: " fmt #define pr_fmt(fmt) "psci: " fmt
#include <linux/acpi.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/smp.h> #include <linux/smp.h>
...@@ -24,6 +25,7 @@ ...@@ -24,6 +25,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <uapi/linux/psci.h> #include <uapi/linux/psci.h>
#include <asm/acpi.h>
#include <asm/compiler.h> #include <asm/compiler.h>
#include <asm/cpu_ops.h> #include <asm/cpu_ops.h>
#include <asm/errno.h> #include <asm/errno.h>
...@@ -273,39 +275,8 @@ static void psci_sys_poweroff(void) ...@@ -273,39 +275,8 @@ static void psci_sys_poweroff(void)
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
} }
/* static void __init psci_0_2_set_functions(void)
* PSCI Function IDs for v0.2+ are well defined so use
* standard values.
*/
static int __init psci_0_2_init(struct device_node *np)
{ {
int err, ver;
err = get_set_conduit_method(np);
if (err)
goto out_put_node;
ver = psci_get_version();
if (ver == PSCI_RET_NOT_SUPPORTED) {
/* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */
pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
err = -EOPNOTSUPP;
goto out_put_node;
} else {
pr_info("PSCIv%d.%d detected in firmware.\n",
PSCI_VERSION_MAJOR(ver),
PSCI_VERSION_MINOR(ver));
if (PSCI_VERSION_MAJOR(ver) == 0 &&
PSCI_VERSION_MINOR(ver) < 2) {
err = -EINVAL;
pr_err("Conflicting PSCI version detected.\n");
goto out_put_node;
}
}
pr_info("Using standard PSCI v0.2 function IDs\n"); pr_info("Using standard PSCI v0.2 function IDs\n");
psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND; psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;
psci_ops.cpu_suspend = psci_cpu_suspend; psci_ops.cpu_suspend = psci_cpu_suspend;
...@@ -329,6 +300,60 @@ static int __init psci_0_2_init(struct device_node *np) ...@@ -329,6 +300,60 @@ static int __init psci_0_2_init(struct device_node *np)
arm_pm_restart = psci_sys_reset; arm_pm_restart = psci_sys_reset;
pm_power_off = psci_sys_poweroff; pm_power_off = psci_sys_poweroff;
}
/*
* Probe function for PSCI firmware versions >= 0.2
*/
static int __init psci_probe(void)
{
int ver = psci_get_version();
if (ver == PSCI_RET_NOT_SUPPORTED) {
/*
* PSCI versions >=0.2 mandates implementation of
* PSCI_VERSION.
*/
pr_err("PSCI firmware does not comply with the v0.2 spec.\n");
return -EOPNOTSUPP;
} else {
pr_info("PSCIv%d.%d detected in firmware.\n",
PSCI_VERSION_MAJOR(ver),
PSCI_VERSION_MINOR(ver));
if (PSCI_VERSION_MAJOR(ver) == 0 &&
PSCI_VERSION_MINOR(ver) < 2) {
pr_err("Conflicting PSCI version detected.\n");
return -EINVAL;
}
}
psci_0_2_set_functions();
return 0;
}
/*
* PSCI init function for PSCI versions >=0.2
*
* Probe based on PSCI PSCI_VERSION function
*/
static int __init psci_0_2_init(struct device_node *np)
{
int err;
err = get_set_conduit_method(np);
if (err)
goto out_put_node;
/*
* Starting with v0.2, the PSCI specification introduced a call
* (PSCI_VERSION) that allows probing the firmware version, so
* that PSCI function IDs and version specific initialization
* can be carried out according to the specific version reported
* by firmware
*/
err = psci_probe();
out_put_node: out_put_node:
of_node_put(np); of_node_put(np);
...@@ -381,7 +406,7 @@ static const struct of_device_id psci_of_match[] __initconst = { ...@@ -381,7 +406,7 @@ static const struct of_device_id psci_of_match[] __initconst = {
{}, {},
}; };
int __init psci_init(void) int __init psci_dt_init(void)
{ {
struct device_node *np; struct device_node *np;
const struct of_device_id *matched_np; const struct of_device_id *matched_np;
...@@ -396,6 +421,27 @@ int __init psci_init(void) ...@@ -396,6 +421,27 @@ int __init psci_init(void)
return init_fn(np); return init_fn(np);
} }
/*
* We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's
* explicitly clarified in SBBR
*/
int __init psci_acpi_init(void)
{
if (!acpi_psci_present()) {
pr_info("is not implemented in ACPI.\n");
return -EOPNOTSUPP;
}
pr_info("probing for conduit method from ACPI.\n");
if (acpi_psci_use_hvc())
invoke_psci_fn = __invoke_psci_fn_hvc;
else
invoke_psci_fn = __invoke_psci_fn_smc;
return psci_probe();
}
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu) static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/acpi.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/stddef.h> #include <linux/stddef.h>
...@@ -46,6 +47,7 @@ ...@@ -46,6 +47,7 @@
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <asm/acpi.h>
#include <asm/fixmap.h> #include <asm/fixmap.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/cputype.h> #include <asm/cputype.h>
...@@ -395,18 +397,27 @@ void __init setup_arch(char **cmdline_p) ...@@ -395,18 +397,27 @@ void __init setup_arch(char **cmdline_p)
efi_init(); efi_init();
arm64_memblock_init(); arm64_memblock_init();
/* Parse the ACPI tables for possible boot-time configuration */
acpi_boot_table_init();
paging_init(); paging_init();
request_standard_resources(); request_standard_resources();
early_ioremap_reset(); early_ioremap_reset();
if (acpi_disabled) {
unflatten_device_tree(); unflatten_device_tree();
psci_dt_init();
psci_init();
cpu_read_bootcpu_ops(); cpu_read_bootcpu_ops();
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
smp_init_cpus(); of_smp_init_cpus();
#endif
} else {
psci_acpi_init();
acpi_init_cpus();
}
#ifdef CONFIG_SMP
smp_build_mpidr_hash(); smp_build_mpidr_hash();
#endif #endif
......
...@@ -323,7 +323,7 @@ void __init smp_prepare_boot_cpu(void) ...@@ -323,7 +323,7 @@ void __init smp_prepare_boot_cpu(void)
* cpu logical map array containing MPIDR values related to logical * cpu logical map array containing MPIDR values related to logical
* cpus. Assumes that cpu_logical_map(0) has already been initialized. * cpus. Assumes that cpu_logical_map(0) has already been initialized.
*/ */
void __init smp_init_cpus(void) void __init of_smp_init_cpus(void)
{ {
struct device_node *dn = NULL; struct device_node *dn = NULL;
unsigned int i, cpu = 1; unsigned int i, cpu = 1;
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/acpi.h>
#include <clocksource/arm_arch_timer.h> #include <clocksource/arm_arch_timer.h>
...@@ -72,6 +73,12 @@ void __init time_init(void) ...@@ -72,6 +73,12 @@ void __init time_init(void)
tick_setup_hrtimer_broadcast(); tick_setup_hrtimer_broadcast();
/*
* Since ACPI or FDT will only one be available in the system,
* we can use acpi_generic_timer_init() here safely
*/
acpi_generic_timer_init();
arch_timer_rate = arch_timer_get_rate(); arch_timer_rate = arch_timer_get_rate();
if (!arch_timer_rate) if (!arch_timer_rate)
panic("Unable to initialise architected timer.\n"); panic("Unable to initialise architected timer.\n");
......
...@@ -15,6 +15,7 @@ config IA64 ...@@ -15,6 +15,7 @@ config IA64
select ARCH_MIGHT_HAVE_PC_SERIO select ARCH_MIGHT_HAVE_PC_SERIO
select PCI if (!IA64_HP_SIM) select PCI if (!IA64_HP_SIM)
select ACPI if (!IA64_HP_SIM) select ACPI if (!IA64_HP_SIM)
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI
select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_UNSTABLE_SCHED_CLOCK
select HAVE_IDE select HAVE_IDE
......
...@@ -887,7 +887,7 @@ static int _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu) ...@@ -887,7 +887,7 @@ static int _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu)
} }
/* wrapper to silence section mismatch warning */ /* wrapper to silence section mismatch warning */
int __ref acpi_map_cpu(acpi_handle handle, int physid, int *pcpu) int __ref acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu)
{ {
return _acpi_map_lsapic(handle, physid, pcpu); return _acpi_map_lsapic(handle, physid, pcpu);
} }
......
...@@ -22,6 +22,7 @@ config X86_64 ...@@ -22,6 +22,7 @@ config X86_64
### Arch settings ### Arch settings
config X86 config X86
def_bool y def_bool y
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI
select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
select ARCH_HAS_FAST_MULTIPLIER select ARCH_HAS_FAST_MULTIPLIER
......
...@@ -757,7 +757,7 @@ static int _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu) ...@@ -757,7 +757,7 @@ static int _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu)
} }
/* wrapper to silence section mismatch warning */ /* wrapper to silence section mismatch warning */
int __ref acpi_map_cpu(acpi_handle handle, int physid, int *pcpu) int __ref acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu)
{ {
return _acpi_map_lsapic(handle, physid, pcpu); return _acpi_map_lsapic(handle, physid, pcpu);
} }
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
menuconfig ACPI menuconfig ACPI
bool "ACPI (Advanced Configuration and Power Interface) Support" bool "ACPI (Advanced Configuration and Power Interface) Support"
depends on !IA64_HP_SIM depends on !IA64_HP_SIM
depends on IA64 || X86 depends on IA64 || X86 || (ARM64 && EXPERT)
depends on PCI depends on PCI
select PNP select PNP
default y default y
...@@ -48,9 +48,16 @@ config ACPI_LEGACY_TABLES_LOOKUP ...@@ -48,9 +48,16 @@ config ACPI_LEGACY_TABLES_LOOKUP
config ARCH_MIGHT_HAVE_ACPI_PDC config ARCH_MIGHT_HAVE_ACPI_PDC
bool bool
config ACPI_GENERIC_GSI
bool
config ACPI_SYSTEM_POWER_STATES_SUPPORT
bool
config ACPI_SLEEP config ACPI_SLEEP
bool bool
depends on SUSPEND || HIBERNATION depends on SUSPEND || HIBERNATION
depends on ACPI_SYSTEM_POWER_STATES_SUPPORT
default y default y
config ACPI_PROCFS_POWER config ACPI_PROCFS_POWER
...@@ -163,6 +170,7 @@ config ACPI_PROCESSOR ...@@ -163,6 +170,7 @@ config ACPI_PROCESSOR
tristate "Processor" tristate "Processor"
select THERMAL select THERMAL
select CPU_IDLE select CPU_IDLE
depends on X86 || IA64
default y default y
help help
This driver installs ACPI as the idle handler for Linux and uses This driver installs ACPI as the idle handler for Linux and uses
......
...@@ -23,7 +23,7 @@ acpi-y += nvs.o ...@@ -23,7 +23,7 @@ acpi-y += nvs.o
# Power management related files # Power management related files
acpi-y += wakeup.o acpi-y += wakeup.o
acpi-y += sleep.o acpi-$(CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT) += sleep.o
acpi-y += device_pm.o acpi-y += device_pm.o
acpi-$(CONFIG_ACPI_SLEEP) += proc.o acpi-$(CONFIG_ACPI_SLEEP) += proc.o
...@@ -56,6 +56,7 @@ ifdef CONFIG_ACPI_VIDEO ...@@ -56,6 +56,7 @@ ifdef CONFIG_ACPI_VIDEO
acpi-y += video_detect.o acpi-y += video_detect.o
endif endif
acpi-y += acpi_lpat.o acpi-y += acpi_lpat.o
acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o
# These are (potentially) separate modules # These are (potentially) separate modules
......
...@@ -170,7 +170,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) ...@@ -170,7 +170,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr)
acpi_status status; acpi_status status;
int ret; int ret;
if (pr->phys_id == -1) if (pr->phys_id == PHYS_CPUID_INVALID)
return -ENODEV; return -ENODEV;
status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta);
...@@ -215,7 +215,8 @@ static int acpi_processor_get_info(struct acpi_device *device) ...@@ -215,7 +215,8 @@ static int acpi_processor_get_info(struct acpi_device *device)
union acpi_object object = { 0 }; union acpi_object object = { 0 };
struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
struct acpi_processor *pr = acpi_driver_data(device); struct acpi_processor *pr = acpi_driver_data(device);
int phys_id, cpu_index, device_declaration = 0; phys_cpuid_t phys_id;
int cpu_index, device_declaration = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
static int cpu0_initialized; static int cpu0_initialized;
unsigned long long value; unsigned long long value;
...@@ -263,7 +264,7 @@ static int acpi_processor_get_info(struct acpi_device *device) ...@@ -263,7 +264,7 @@ static int acpi_processor_get_info(struct acpi_device *device)
} }
phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id); phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id);
if (phys_id < 0) if (phys_id == PHYS_CPUID_INVALID)
acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n"); acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n");
pr->phys_id = phys_id; pr->phys_id = phys_id;
......
...@@ -448,6 +448,9 @@ static int __init acpi_bus_init_irq(void) ...@@ -448,6 +448,9 @@ static int __init acpi_bus_init_irq(void)
case ACPI_IRQ_MODEL_IOSAPIC: case ACPI_IRQ_MODEL_IOSAPIC:
message = "IOSAPIC"; message = "IOSAPIC";
break; break;
case ACPI_IRQ_MODEL_GIC:
message = "GIC";
break;
case ACPI_IRQ_MODEL_PLATFORM: case ACPI_IRQ_MODEL_PLATFORM:
message = "platform specific model"; message = "platform specific model";
break; break;
......
/*
* ACPI GSI IRQ layer
*
* Copyright (C) 2015 ARM Ltd.
* Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/acpi.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
enum acpi_irq_model_id acpi_irq_model;
static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity)
{
switch (polarity) {
case ACPI_ACTIVE_LOW:
return trigger == ACPI_EDGE_SENSITIVE ?
IRQ_TYPE_EDGE_FALLING :
IRQ_TYPE_LEVEL_LOW;
case ACPI_ACTIVE_HIGH:
return trigger == ACPI_EDGE_SENSITIVE ?
IRQ_TYPE_EDGE_RISING :
IRQ_TYPE_LEVEL_HIGH;
case ACPI_ACTIVE_BOTH:
if (trigger == ACPI_EDGE_SENSITIVE)
return IRQ_TYPE_EDGE_BOTH;
default:
return IRQ_TYPE_NONE;
}
}
/**
* acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
* @gsi: GSI IRQ number to map
* @irq: pointer where linux IRQ number is stored
*
* irq location updated with irq value [>0 on success, 0 on failure]
*
* Returns: linux IRQ number on success (>0)
* -EINVAL on failure
*/
int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
{
/*
* Only default domain is supported at present, always find
* the mapping corresponding to default domain by passing NULL
* as irq_domain parameter
*/
*irq = irq_find_mapping(NULL, gsi);
/*
* *irq == 0 means no mapping, that should
* be reported as a failure
*/
return (*irq > 0) ? *irq : -EINVAL;
}
EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
/**
* acpi_register_gsi() - Map a GSI to a linux IRQ number
* @dev: device for which IRQ has to be mapped
* @gsi: GSI IRQ number
* @trigger: trigger type of the GSI number to be mapped
* @polarity: polarity of the GSI to be mapped
*
* Returns: a valid linux IRQ number on success
* -EINVAL on failure
*/
int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
int polarity)
{
unsigned int irq;
unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity);
/*
* There is no way at present to look-up the IRQ domain on ACPI,
* hence always create mapping referring to the default domain
* by passing NULL as irq_domain parameter
*/
irq = irq_create_mapping(NULL, gsi);
if (!irq)
return -EINVAL;
/* Set irq type if specified and different than the current one */
if (irq_type != IRQ_TYPE_NONE &&
irq_type != irq_get_trigger_type(irq))
irq_set_irq_type(irq, irq_type);
return irq;
}
EXPORT_SYMBOL_GPL(acpi_register_gsi);
/**
* acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping
* @gsi: GSI IRQ number
*/
void acpi_unregister_gsi(u32 gsi)
{
int irq = irq_find_mapping(NULL, gsi);
irq_dispose_mapping(irq);
}
EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
...@@ -161,7 +161,11 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); ...@@ -161,7 +161,11 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
/*-------------------------------------------------------------------------- /*--------------------------------------------------------------------------
Suspend/Resume Suspend/Resume
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT
extern int acpi_sleep_init(void); extern int acpi_sleep_init(void);
#else
static inline int acpi_sleep_init(void) { return -ENXIO; }
#endif
#ifdef CONFIG_ACPI_SLEEP #ifdef CONFIG_ACPI_SLEEP
int acpi_sleep_proc_init(void); int acpi_sleep_proc_init(void);
......
...@@ -336,11 +336,11 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size) ...@@ -336,11 +336,11 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size)
return NULL; return NULL;
} }
#ifndef CONFIG_IA64 #if defined(CONFIG_IA64) || defined(CONFIG_ARM64)
#define should_use_kmap(pfn) page_is_ram(pfn)
#else
/* ioremap will take care of cache attributes */ /* ioremap will take care of cache attributes */
#define should_use_kmap(pfn) 0 #define should_use_kmap(pfn) 0
#else
#define should_use_kmap(pfn) page_is_ram(pfn)
#endif #endif
static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz) static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz)
......
...@@ -32,7 +32,7 @@ static struct acpi_table_madt *get_madt_table(void) ...@@ -32,7 +32,7 @@ static struct acpi_table_madt *get_madt_table(void)
} }
static int map_lapic_id(struct acpi_subtable_header *entry, static int map_lapic_id(struct acpi_subtable_header *entry,
u32 acpi_id, int *apic_id) u32 acpi_id, phys_cpuid_t *apic_id)
{ {
struct acpi_madt_local_apic *lapic = struct acpi_madt_local_apic *lapic =
container_of(entry, struct acpi_madt_local_apic, header); container_of(entry, struct acpi_madt_local_apic, header);
...@@ -48,7 +48,7 @@ static int map_lapic_id(struct acpi_subtable_header *entry, ...@@ -48,7 +48,7 @@ static int map_lapic_id(struct acpi_subtable_header *entry,
} }
static int map_x2apic_id(struct acpi_subtable_header *entry, static int map_x2apic_id(struct acpi_subtable_header *entry,
int device_declaration, u32 acpi_id, int *apic_id) int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
{ {
struct acpi_madt_local_x2apic *apic = struct acpi_madt_local_x2apic *apic =
container_of(entry, struct acpi_madt_local_x2apic, header); container_of(entry, struct acpi_madt_local_x2apic, header);
...@@ -65,7 +65,7 @@ static int map_x2apic_id(struct acpi_subtable_header *entry, ...@@ -65,7 +65,7 @@ static int map_x2apic_id(struct acpi_subtable_header *entry,
} }
static int map_lsapic_id(struct acpi_subtable_header *entry, static int map_lsapic_id(struct acpi_subtable_header *entry,
int device_declaration, u32 acpi_id, int *apic_id) int device_declaration, u32 acpi_id, phys_cpuid_t *apic_id)
{ {
struct acpi_madt_local_sapic *lsapic = struct acpi_madt_local_sapic *lsapic =
container_of(entry, struct acpi_madt_local_sapic, header); container_of(entry, struct acpi_madt_local_sapic, header);
...@@ -83,10 +83,35 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, ...@@ -83,10 +83,35 @@ static int map_lsapic_id(struct acpi_subtable_header *entry,
return 0; return 0;
} }
static int map_madt_entry(int type, u32 acpi_id) /*
* Retrieve the ARM CPU physical identifier (MPIDR)
*/
static int map_gicc_mpidr(struct acpi_subtable_header *entry,
int device_declaration, u32 acpi_id, phys_cpuid_t *mpidr)
{
struct acpi_madt_generic_interrupt *gicc =
container_of(entry, struct acpi_madt_generic_interrupt, header);
if (!(gicc->flags & ACPI_MADT_ENABLED))
return -ENODEV;
/* device_declaration means Device object in DSDT, in the
* GIC interrupt model, logical processors are required to
* have a Processor Device object in the DSDT, so we should
* check device_declaration here
*/
if (device_declaration && (gicc->uid == acpi_id)) {
*mpidr = gicc->arm_mpidr;
return 0;
}
return -EINVAL;
}
static phys_cpuid_t map_madt_entry(int type, u32 acpi_id)
{ {
unsigned long madt_end, entry; unsigned long madt_end, entry;
int phys_id = -1; /* CPU hardware ID */ phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */
struct acpi_table_madt *madt; struct acpi_table_madt *madt;
madt = get_madt_table(); madt = get_madt_table();
...@@ -111,18 +136,21 @@ static int map_madt_entry(int type, u32 acpi_id) ...@@ -111,18 +136,21 @@ static int map_madt_entry(int type, u32 acpi_id)
} else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
if (!map_lsapic_id(header, type, acpi_id, &phys_id)) if (!map_lsapic_id(header, type, acpi_id, &phys_id))
break; break;
} else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
if (!map_gicc_mpidr(header, type, acpi_id, &phys_id))
break;
} }
entry += header->length; entry += header->length;
} }
return phys_id; return phys_id;
} }
static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
{ {
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj; union acpi_object *obj;
struct acpi_subtable_header *header; struct acpi_subtable_header *header;
int phys_id = -1; phys_cpuid_t phys_id = PHYS_CPUID_INVALID;
if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
goto exit; goto exit;
...@@ -143,33 +171,35 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) ...@@ -143,33 +171,35 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
map_lsapic_id(header, type, acpi_id, &phys_id); map_lsapic_id(header, type, acpi_id, &phys_id);
else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC)
map_x2apic_id(header, type, acpi_id, &phys_id); map_x2apic_id(header, type, acpi_id, &phys_id);
else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT)
map_gicc_mpidr(header, type, acpi_id, &phys_id);
exit: exit:
kfree(buffer.pointer); kfree(buffer.pointer);
return phys_id; return phys_id;
} }
int acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
{ {
int phys_id; phys_cpuid_t phys_id;
phys_id = map_mat_entry(handle, type, acpi_id); phys_id = map_mat_entry(handle, type, acpi_id);
if (phys_id == -1) if (phys_id == PHYS_CPUID_INVALID)
phys_id = map_madt_entry(type, acpi_id); phys_id = map_madt_entry(type, acpi_id);
return phys_id; return phys_id;
} }
int acpi_map_cpuid(int phys_id, u32 acpi_id) int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id)
{ {
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
int i; int i;
#endif #endif
if (phys_id == -1) { if (phys_id == PHYS_CPUID_INVALID) {
/* /*
* On UP processor, there is no _MAT or MADT table. * On UP processor, there is no _MAT or MADT table.
* So above phys_id is always set to -1. * So above phys_id is always set to PHYS_CPUID_INVALID.
* *
* BIOS may define multiple CPU handles even for UP processor. * BIOS may define multiple CPU handles even for UP processor.
* For example, * For example,
...@@ -190,7 +220,7 @@ int acpi_map_cpuid(int phys_id, u32 acpi_id) ...@@ -190,7 +220,7 @@ int acpi_map_cpuid(int phys_id, u32 acpi_id)
if (nr_cpu_ids <= 1 && acpi_id == 0) if (nr_cpu_ids <= 1 && acpi_id == 0)
return acpi_id; return acpi_id;
else else
return phys_id; return -1;
} }
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
...@@ -208,7 +238,7 @@ int acpi_map_cpuid(int phys_id, u32 acpi_id) ...@@ -208,7 +238,7 @@ int acpi_map_cpuid(int phys_id, u32 acpi_id)
int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
{ {
int phys_id; phys_cpuid_t phys_id;
phys_id = acpi_get_phys_id(handle, type, acpi_id); phys_id = acpi_get_phys_id(handle, type, acpi_id);
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
* *
*/ */
/* Uncomment next line to get verbose printout */
/* #define DEBUG */
#define pr_fmt(fmt) "ACPI: " fmt #define pr_fmt(fmt) "ACPI: " fmt
#include <linux/init.h> #include <linux/init.h>
...@@ -61,7 +63,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) ...@@ -61,7 +63,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
{ {
struct acpi_madt_local_apic *p = struct acpi_madt_local_apic *p =
(struct acpi_madt_local_apic *)header; (struct acpi_madt_local_apic *)header;
pr_info("LAPIC (acpi_id[0x%02x] lapic_id[0x%02x] %s)\n", pr_debug("LAPIC (acpi_id[0x%02x] lapic_id[0x%02x] %s)\n",
p->processor_id, p->id, p->processor_id, p->id,
(p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
} }
...@@ -71,7 +73,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) ...@@ -71,7 +73,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
{ {
struct acpi_madt_local_x2apic *p = struct acpi_madt_local_x2apic *p =
(struct acpi_madt_local_x2apic *)header; (struct acpi_madt_local_x2apic *)header;
pr_info("X2APIC (apic_id[0x%02x] uid[0x%02x] %s)\n", pr_debug("X2APIC (apic_id[0x%02x] uid[0x%02x] %s)\n",
p->local_apic_id, p->uid, p->local_apic_id, p->uid,
(p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
} }
...@@ -81,7 +83,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) ...@@ -81,7 +83,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
{ {
struct acpi_madt_io_apic *p = struct acpi_madt_io_apic *p =
(struct acpi_madt_io_apic *)header; (struct acpi_madt_io_apic *)header;
pr_info("IOAPIC (id[0x%02x] address[0x%08x] gsi_base[%d])\n", pr_debug("IOAPIC (id[0x%02x] address[0x%08x] gsi_base[%d])\n",
p->id, p->address, p->global_irq_base); p->id, p->address, p->global_irq_base);
} }
break; break;
...@@ -155,7 +157,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) ...@@ -155,7 +157,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
{ {
struct acpi_madt_io_sapic *p = struct acpi_madt_io_sapic *p =
(struct acpi_madt_io_sapic *)header; (struct acpi_madt_io_sapic *)header;
pr_info("IOSAPIC (id[0x%x] address[%p] gsi_base[%d])\n", pr_debug("IOSAPIC (id[0x%x] address[%p] gsi_base[%d])\n",
p->id, (void *)(unsigned long)p->address, p->id, (void *)(unsigned long)p->address,
p->global_irq_base); p->global_irq_base);
} }
...@@ -165,7 +167,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) ...@@ -165,7 +167,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
{ {
struct acpi_madt_local_sapic *p = struct acpi_madt_local_sapic *p =
(struct acpi_madt_local_sapic *)header; (struct acpi_madt_local_sapic *)header;
pr_info("LSAPIC (acpi_id[0x%02x] lsapic_id[0x%02x] lsapic_eid[0x%02x] %s)\n", pr_debug("LSAPIC (acpi_id[0x%02x] lsapic_id[0x%02x] lsapic_eid[0x%02x] %s)\n",
p->processor_id, p->id, p->eid, p->processor_id, p->id, p->eid,
(p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); (p->lapic_flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
} }
...@@ -183,6 +185,28 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) ...@@ -183,6 +185,28 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
} }
break; break;
case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
{
struct acpi_madt_generic_interrupt *p =
(struct acpi_madt_generic_interrupt *)header;
pr_debug("GICC (acpi_id[0x%04x] address[%llx] MPIDR[0x%llx] %s)\n",
p->uid, p->base_address,
p->arm_mpidr,
(p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
}
break;
case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
{
struct acpi_madt_generic_distributor *p =
(struct acpi_madt_generic_distributor *)header;
pr_debug("GIC Distributor (gic_id[0x%04x] address[%llx] gsi_base[%d])\n",
p->gic_id, p->base_address,
p->global_irq_base);
}
break;
default: default:
pr_warn("Found unsupported MADT entry (type = 0x%x)\n", pr_warn("Found unsupported MADT entry (type = 0x%x)\n",
header->type); header->type);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sched_clock.h> #include <linux/sched_clock.h>
#include <linux/acpi.h>
#include <asm/arch_timer.h> #include <asm/arch_timer.h>
#include <asm/virt.h> #include <asm/virt.h>
...@@ -371,8 +372,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np) ...@@ -371,8 +372,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
if (arch_timer_rate) if (arch_timer_rate)
return; return;
/* Try to determine the frequency from the device tree or CNTFRQ */ /*
if (of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) { * Try to determine the frequency from the device tree or CNTFRQ,
* if ACPI is enabled, get the frequency from CNTFRQ ONLY.
*/
if (!acpi_disabled ||
of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) {
if (cntbase) if (cntbase)
arch_timer_rate = readl_relaxed(cntbase + CNTFRQ); arch_timer_rate = readl_relaxed(cntbase + CNTFRQ);
else else
...@@ -691,28 +696,8 @@ static void __init arch_timer_common_init(void) ...@@ -691,28 +696,8 @@ static void __init arch_timer_common_init(void)
arch_timer_arch_init(); arch_timer_arch_init();
} }
static void __init arch_timer_init(struct device_node *np) static void __init arch_timer_init(void)
{ {
int i;
if (arch_timers_present & ARCH_CP15_TIMER) {
pr_warn("arch_timer: multiple nodes in dt, skipping\n");
return;
}
arch_timers_present |= ARCH_CP15_TIMER;
for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
arch_timer_detect_rate(NULL, np);
/*
* If we cannot rely on firmware initializing the timer registers then
* we should use the physical timers instead.
*/
if (IS_ENABLED(CONFIG_ARM) &&
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
arch_timer_use_virtual = false;
/* /*
* If HYP mode is available, we know that the physical timer * If HYP mode is available, we know that the physical timer
* has been configured to be accessible from PL1. Use it, so * has been configured to be accessible from PL1. Use it, so
...@@ -731,13 +716,39 @@ static void __init arch_timer_init(struct device_node *np) ...@@ -731,13 +716,39 @@ static void __init arch_timer_init(struct device_node *np)
} }
} }
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
arch_timer_register(); arch_timer_register();
arch_timer_common_init(); arch_timer_common_init();
} }
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init);
CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init); static void __init arch_timer_of_init(struct device_node *np)
{
int i;
if (arch_timers_present & ARCH_CP15_TIMER) {
pr_warn("arch_timer: multiple nodes in dt, skipping\n");
return;
}
arch_timers_present |= ARCH_CP15_TIMER;
for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
arch_timer_detect_rate(NULL, np);
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
/*
* If we cannot rely on firmware initializing the timer registers then
* we should use the physical timers instead.
*/
if (IS_ENABLED(CONFIG_ARM) &&
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
arch_timer_use_virtual = false;
arch_timer_init();
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
static void __init arch_timer_mem_init(struct device_node *np) static void __init arch_timer_mem_init(struct device_node *np)
{ {
...@@ -804,3 +815,70 @@ static void __init arch_timer_mem_init(struct device_node *np) ...@@ -804,3 +815,70 @@ static void __init arch_timer_mem_init(struct device_node *np)
} }
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
arch_timer_mem_init); arch_timer_mem_init);
#ifdef CONFIG_ACPI
static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
{
int trigger, polarity;
if (!interrupt)
return 0;
trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
: ACPI_LEVEL_SENSITIVE;
polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
: ACPI_ACTIVE_HIGH;
return acpi_register_gsi(NULL, interrupt, trigger, polarity);
}
/* Initialize per-processor generic timer */
static int __init arch_timer_acpi_init(struct acpi_table_header *table)
{
struct acpi_table_gtdt *gtdt;
if (arch_timers_present & ARCH_CP15_TIMER) {
pr_warn("arch_timer: already initialized, skipping\n");
return -EINVAL;
}
gtdt = container_of(table, struct acpi_table_gtdt, header);
arch_timers_present |= ARCH_CP15_TIMER;
arch_timer_ppi[PHYS_SECURE_PPI] =
map_generic_timer_interrupt(gtdt->secure_el1_interrupt,
gtdt->secure_el1_flags);
arch_timer_ppi[PHYS_NONSECURE_PPI] =
map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt,
gtdt->non_secure_el1_flags);
arch_timer_ppi[VIRT_PPI] =
map_generic_timer_interrupt(gtdt->virtual_timer_interrupt,
gtdt->virtual_timer_flags);
arch_timer_ppi[HYP_PPI] =
map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt,
gtdt->non_secure_el2_flags);
/* Get the frequency from CNTFRQ */
arch_timer_detect_rate(NULL, NULL);
/* Always-on capability */
arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
arch_timer_init();
return 0;
}
/* Initialize all the generic timers presented in GTDT */
void __init acpi_generic_timer_init(void)
{
if (acpi_disabled)
return;
acpi_table_parse(ACPI_SIG_GTDT, arch_timer_acpi_init);
}
#endif
...@@ -33,12 +33,14 @@ ...@@ -33,12 +33,14 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/acpi.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h> #include <linux/irqchip/arm-gic.h>
#include <linux/irqchip/arm-gic-acpi.h>
#include <asm/cputype.h> #include <asm/cputype.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -1107,3 +1109,105 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); ...@@ -1107,3 +1109,105 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
#endif #endif
#ifdef CONFIG_ACPI
static phys_addr_t dist_phy_base, cpu_phy_base __initdata;
static int __init
gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
const unsigned long end)
{
struct acpi_madt_generic_interrupt *processor;
phys_addr_t gic_cpu_base;
static int cpu_base_assigned;
processor = (struct acpi_madt_generic_interrupt *)header;
if (BAD_MADT_ENTRY(processor, end))
return -EINVAL;
/*
* There is no support for non-banked GICv1/2 register in ACPI spec.
* All CPU interface addresses have to be the same.
*/
gic_cpu_base = processor->base_address;
if (cpu_base_assigned && gic_cpu_base != cpu_phy_base)
return -EINVAL;
cpu_phy_base = gic_cpu_base;
cpu_base_assigned = 1;
return 0;
}
static int __init
gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header,
const unsigned long end)
{
struct acpi_madt_generic_distributor *dist;
dist = (struct acpi_madt_generic_distributor *)header;
if (BAD_MADT_ENTRY(dist, end))
return -EINVAL;
dist_phy_base = dist->base_address;
return 0;
}
int __init
gic_v2_acpi_init(struct acpi_table_header *table)
{
void __iomem *cpu_base, *dist_base;
int count;
/* Collect CPU base addresses */
count = acpi_parse_entries(ACPI_SIG_MADT,
sizeof(struct acpi_table_madt),
gic_acpi_parse_madt_cpu, table,
ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0);
if (count <= 0) {
pr_err("No valid GICC entries exist\n");
return -EINVAL;
}
/*
* Find distributor base address. We expect one distributor entry since
* ACPI 5.1 spec neither support multi-GIC instances nor GIC cascade.
*/
count = acpi_parse_entries(ACPI_SIG_MADT,
sizeof(struct acpi_table_madt),
gic_acpi_parse_madt_distributor, table,
ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0);
if (count <= 0) {
pr_err("No valid GICD entries exist\n");
return -EINVAL;
} else if (count > 1) {
pr_err("More than one GICD entry detected\n");
return -EINVAL;
}
cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE);
if (!cpu_base) {
pr_err("Unable to map GICC registers\n");
return -ENOMEM;
}
dist_base = ioremap(dist_phy_base, ACPI_GICV2_DIST_MEM_SIZE);
if (!dist_base) {
pr_err("Unable to map GICD registers\n");
iounmap(cpu_base);
return -ENOMEM;
}
/*
* Initialize zero GIC instance (no multi-GIC support). Also, set GIC
* as default IRQ domain to allow for GSI registration and GSI to IRQ
* number translation (see acpi_register_gsi() and acpi_gsi_to_irq()).
*/
gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL);
irq_set_default_host(gic_data[0].domain);
acpi_irq_model = ACPI_IRQ_MODEL_GIC;
return 0;
}
#endif
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
* warranty of any kind, whether express or implied. * warranty of any kind, whether express or implied.
*/ */
#include <linux/acpi_irq.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/irqchip.h> #include <linux/irqchip.h>
...@@ -26,4 +27,6 @@ extern struct of_device_id __irqchip_of_table[]; ...@@ -26,4 +27,6 @@ extern struct of_device_id __irqchip_of_table[];
void __init irqchip_init(void) void __init irqchip_init(void)
{ {
of_irq_init(__irqchip_of_table); of_irq_init(__irqchip_of_table);
acpi_irq_init();
} }
...@@ -276,4 +276,8 @@ config XEN_AUTO_XLATE ...@@ -276,4 +276,8 @@ config XEN_AUTO_XLATE
help help
Support for auto-translated physmap guests. Support for auto-translated physmap guests.
config XEN_ACPI
def_bool y
depends on X86 && ACPI
endmenu endmenu
...@@ -13,7 +13,7 @@ CFLAGS_efi.o += -fshort-wchar ...@@ -13,7 +13,7 @@ CFLAGS_efi.o += -fshort-wchar
dom0-$(CONFIG_PCI) += pci.o dom0-$(CONFIG_PCI) += pci.o
dom0-$(CONFIG_USB_SUPPORT) += dbgp.o dom0-$(CONFIG_USB_SUPPORT) += dbgp.o
dom0-$(CONFIG_ACPI) += acpi.o $(xen-pad-y) dom0-$(CONFIG_XEN_ACPI) += acpi.o $(xen-pad-y)
xen-pad-$(CONFIG_X86) += xen-acpi-pad.o xen-pad-$(CONFIG_X86) += xen-acpi-pad.o
dom0-$(CONFIG_X86) += pcpu.o dom0-$(CONFIG_X86) += pcpu.o
obj-$(CONFIG_XEN_DOM0) += $(dom0-y) obj-$(CONFIG_XEN_DOM0) += $(dom0-y)
......
...@@ -3,11 +3,15 @@ ...@@ -3,11 +3,15 @@
#include <linux/io.h> #include <linux/io.h>
#include <asm/acpi.h>
#ifndef acpi_os_ioremap
static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys,
acpi_size size) acpi_size size)
{ {
return ioremap_cache(phys, size); return ioremap_cache(phys, size);
} }
#endif
void __iomem *__init_refok void __iomem *__init_refok
acpi_os_map_iomem(acpi_physical_address phys, acpi_size size); acpi_os_map_iomem(acpi_physical_address phys, acpi_size size);
......
...@@ -196,7 +196,7 @@ struct acpi_processor_flags { ...@@ -196,7 +196,7 @@ struct acpi_processor_flags {
struct acpi_processor { struct acpi_processor {
acpi_handle handle; acpi_handle handle;
u32 acpi_id; u32 acpi_id;
u32 phys_id; /* CPU hardware ID such as APIC ID for x86 */ phys_cpuid_t phys_id; /* CPU hardware ID such as APIC ID for x86 */
u32 id; /* CPU logical ID allocated by OS */ u32 id; /* CPU logical ID allocated by OS */
u32 pblk; u32 pblk;
int performance_platform_limit; int performance_platform_limit;
...@@ -310,8 +310,8 @@ static inline int acpi_processor_get_bios_limit(int cpu, unsigned int *limit) ...@@ -310,8 +310,8 @@ static inline int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)
#endif /* CONFIG_CPU_FREQ */ #endif /* CONFIG_CPU_FREQ */
/* in processor_core.c */ /* in processor_core.c */
int acpi_get_phys_id(acpi_handle, int type, u32 acpi_id); phys_cpuid_t acpi_get_phys_id(acpi_handle, int type, u32 acpi_id);
int acpi_map_cpuid(int phys_id, u32 acpi_id); int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id);
int acpi_get_cpuid(acpi_handle, int type, u32 acpi_id); int acpi_get_cpuid(acpi_handle, int type, u32 acpi_id);
/* in processor_pdc.c */ /* in processor_pdc.c */
......
...@@ -79,6 +79,7 @@ enum acpi_irq_model_id { ...@@ -79,6 +79,7 @@ enum acpi_irq_model_id {
ACPI_IRQ_MODEL_IOAPIC, ACPI_IRQ_MODEL_IOAPIC,
ACPI_IRQ_MODEL_IOSAPIC, ACPI_IRQ_MODEL_IOSAPIC,
ACPI_IRQ_MODEL_PLATFORM, ACPI_IRQ_MODEL_PLATFORM,
ACPI_IRQ_MODEL_GIC,
ACPI_IRQ_MODEL_COUNT ACPI_IRQ_MODEL_COUNT
}; };
...@@ -152,9 +153,14 @@ void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa); ...@@ -152,9 +153,14 @@ void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa);
int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma); int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma);
void acpi_numa_arch_fixup(void); void acpi_numa_arch_fixup(void);
#ifndef PHYS_CPUID_INVALID
typedef u32 phys_cpuid_t;
#define PHYS_CPUID_INVALID (phys_cpuid_t)(-1)
#endif
#ifdef CONFIG_ACPI_HOTPLUG_CPU #ifdef CONFIG_ACPI_HOTPLUG_CPU
/* Arch dependent functions for cpu hotplug support */ /* Arch dependent functions for cpu hotplug support */
int acpi_map_cpu(acpi_handle handle, int physid, int *pcpu); int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu);
int acpi_unmap_cpu(int cpu); int acpi_unmap_cpu(int cpu);
#endif /* CONFIG_ACPI_HOTPLUG_CPU */ #endif /* CONFIG_ACPI_HOTPLUG_CPU */
......
#ifndef _LINUX_ACPI_IRQ_H
#define _LINUX_ACPI_IRQ_H
#include <linux/irq.h>
#ifndef acpi_irq_init
static inline void acpi_irq_init(void) { }
#endif
#endif /* _LINUX_ACPI_IRQ_H */
...@@ -253,4 +253,10 @@ extern void clocksource_of_init(void); ...@@ -253,4 +253,10 @@ extern void clocksource_of_init(void);
static inline void clocksource_of_init(void) {} static inline void clocksource_of_init(void) {}
#endif #endif
#ifdef CONFIG_ACPI
void acpi_generic_timer_init(void);
#else
static inline void acpi_generic_timer_init(void) { }
#endif
#endif /* _LINUX_CLOCKSOURCE_H */ #endif /* _LINUX_CLOCKSOURCE_H */
/*
* Copyright (C) 2014, Linaro Ltd.
* Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARM_GIC_ACPI_H_
#define ARM_GIC_ACPI_H_
#ifdef CONFIG_ACPI
/*
* Hard code here, we can not get memory size from MADT (but FDT does),
* Actually no need to do that, because this size can be inferred
* from GIC spec.
*/
#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K)
#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K)
struct acpi_table_header;
int gic_v2_acpi_init(struct acpi_table_header *table);
void acpi_gic_init(void);
#else
static inline void acpi_gic_init(void) { }
#endif
#endif /* ARM_GIC_ACPI_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment