kernel/kernel-arm64.patch

8045 lines
248 KiB
Diff

commit 87257d3e584fad0b47e6304da54a1932f42b11bb
Author: Mark Salter <msalter@redhat.com>
Date: Tue Sep 30 17:19:24 2014 -0400
arm64: avoid need for console= to enable serial console
Tell kernel to prefer one of the serial ports on platforms
pl011, 8250, or sbsa uarts. console= on command line will
override these assumed preferences.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 56db24589d311ea3590527030ede007ec339e2d7
Author: Tom Lendacky <thomas.lendacky@amd.com>
Date: Tue Sep 9 23:33:17 2014 -0400
drivers: net: AMD Seattle XGBE PHY support for A0 silicon
This patch modifies the upstream AMD XGBE PHY driver to support
A0 Seattle silicon in currently shipping systems. The upstream
Linux driver is targetted for Seattle B0 silicon.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 75554bb2e3c433a47e172d81a0b59df58810dc01
Author: Tom Lendacky <thomas.lendacky@amd.com>
Date: Tue Sep 9 23:34:07 2014 -0400
drivers: net: AMD Seattle XGBE 10GbE support for A0 silicon
This patch modifies the upstream AMD 10GbE XGBE Ethernet driver to
support A0 Seattle silicon in currently shipping systems. The
upstream Linux driver is targetted for Seattle B0 silicon.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 41cb1b3c9e62256b8a4e92c50cd51b2a68d0c9c6
Author: Graeme Gregory <graeme.gregory@linaro.org>
Date: Fri Jul 26 17:55:02 2013 +0100
virtio-mmio: add ACPI probing
Added the match table and pointers for ACPI probing to the driver.
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
commit c06502fb4f00c6996c1f55cd342288508808c678
Author: Graeme Gregory <graeme.gregory@linaro.org>
Date: Wed Jul 24 11:29:48 2013 +0100
net: smc91x: add ACPI probing support.
Add device ID LINA0003 for this device and add the match table.
As its a platform device it needs no other code and will be probed in by
acpi_platform once device ID is added.
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
commit aad559613ff05a13f940129675659297e7125979
Author: Mark Salter <msalter@redhat.com>
Date: Tue Sep 23 12:48:48 2014 -0400
arm64/pci: add coherency inheritance for pci devices
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 8d2a4226d96ccae17fbc0ef7d7d9c5a07ad8b31f
Author: Mark Salter <msalter@redhat.com>
Date: Tue Sep 23 12:35:17 2014 -0400
arm64/acpi: make acpi disabled by default
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 0553f2b9fc94bfb0f9038003ad6f150cca196aad
Author: Wei Huang <wei@redhat.com>
Date: Thu Sep 18 20:32:03 2014 -0400
KVM: fix VTTBR_BADDR_MASK
The following is patch from AMD to fix VTTBR_BADDR_MASK. According to
AMD, this patch is required to enable KVM on Seattle.
The current VTTBR_BADDR_MASK only masks 39 bits, which is broken on current
systems. Rather than just add a bit it seems like a good time to also set
things at run-time instead of compile time to accomodate more hardware.
This patch sets TCR_EL2.PS, VTCR_EL2.T0SZ and vttbr_baddr_mask in runtime,
not compile time.
In ARMv8, EL2 physical address size (TCR_EL2.PS) and stage2 input address
size (VTCR_EL2.T0SZE) cannot be determined in compile time since they
depend on hardware capability.
According to Table D4-23 and Table D4-25 in ARM DDI 0487A.b document,
vttbr_x is calculated using different fixed values with consideration
of T0SZ, granule size and the level of translation tables. Therefore,
vttbr_baddr_mask should be determined dynamically.
Changes since v5:
Fixed declaration of vttbr_baddr_mask to not create multiple instances
Refactored return codes based on feedback
For 32 bit included kvm_arm.h in kvm_mmu.h to explictly pick up
VTTBR_BADDR_MASK
Changes since v4:
More minor cleanups from review
Moved some functions into headers
Added runtime check in kvm_alloc_stage2_pgd
Changes since v3:
Another rebase
Addressed minor comments from v2
Changes since v2:
Rebased on https://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git next branch
Changes since v1:
Rebased fix on Jungseok Lee's patch https://lkml.org/lkml/2014/5/12/189 to
provide better long term fix. Updated that patch to log error instead of
silently fail on unaligned vttbr.
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Christoffer Dall <christoffer.dall@linaro.org>
Cc: Sungjinn Chung <sungjinn.chung@samsung.com>
Signed-off-by: Jungseok Lee <jays.lee@samsung.com>
Signed-off-by: Joel Schopp <joel.schopp@amd.com>
Signed-off-by: Wei Huang <wei@redhat.com>
commit f53d1278fe445b7130f1ff76b2f453b453368284
Author: Wei Huang <wei@redhat.com>
Date: Thu Sep 18 20:02:57 2014 -0400
KVM/ACPI: Enable ACPI support for KVM virt GIC
This patches enables ACPI support for KVM virtual GIC. KVM parses
ACPI table for virt GIC related information when DT table is not
present. This is done by retrieving the information defined in
generic_interrupt entry of MADT table.
Note: Alexander Spyridakis from Virtual Open System posts a
_very_ similar patch to enable acpi-kvm. This patch borrows some
ideas from his patch.
Signed-off-by: Wei Huang <wei@redhat.com>
commit ba63e452ff5b09cc0314f94e163a51c3279b9ca7
Author: Wei Huang <wei@redhat.com>
Date: Thu Sep 18 20:02:56 2014 -0400
KVM/ACPI: Enable ACPI support for virt arch timer
This patches enables ACPI support for KVM virtual arch_timer. It
allows KVM to parse ACPI table for virt arch_timer PPI when DT table
is not present. This is done by retrieving the information from
arch_timer_ppi array in arm_arch_timer driver.
Signed-off-by: Wei Huang <wei@redhat.com>
commit dd3f6094c2142786f40a3bc4d69c60b430ecc675
Author: Wei Huang <wei@redhat.com>
Date: Thu Sep 18 20:02:55 2014 -0400
KVM/ACPI: Add kernel parameter kvmacpi to enable KVM ACPI support
This patch addes a new kernel parameter, kvmacpi, to turn on ACPI
support for KVM. Users can enable it using "kvmacpi=on" in command
line. When it is on, KVM will will parse ACPI tables to configure related
components. By default this option is off.
Note that DT will be probed first, no matter kvmacpi is ON or OFF.
This is because many platforms, such qemu/kvm, still supports
DT only. We still want to support Acadia kernel on such platforms.
Signed-off-by: Wei Huang <wei@redhat.com>
commit cdfc19f1fbe88c1610db790ad55318d55ab00ee9
Author: Mark Salter <msalter@redhat.com>
Date: Thu Sep 18 21:13:05 2014 -0400
arm64/pci: fix dma coherency inheritance for PCI devices
The default dma_ops for devices on arm64 systems are noncoherent in
nature and rely upon special operations and bounce buffers to
perform a device DMA operation to/from memory. Some drivers rely
upon coherent operations involving suitably capable hardware. In
this case, a "dma-coherent" property will exist on the corresponding
Device Tree node for the bridge device, or one of its ancestors.
This patch adds support for applying a DMA coherent dma_ops for
PCI devices in the case of such a property.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit c8a62324eba5718fb43a94a168de7a81787aa94d
Author: Mark Salter <msalter@redhat.com>
Date: Thu Sep 18 15:05:23 2014 -0400
arm64: add sev to parking protocol
Parking protocol wakes secondary cores with an interrupt.
This patch adds an additional sev() to send an event. This
is a temporary hack for APM Mustang board and not intended
for upstream.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit e5f4ba1223515c46f1875597e77d5c32a37829ee
Author: Mark Salter <msalter@redhat.com>
Date: Sun Sep 14 09:44:44 2014 -0400
Revert "ahci_xgene: Skip the PHY and clock initialization if already configured by the firmware."
This reverts commit 0bed13bebd6c99d097796d2ca6c4f10fb5b2eabc.
Temporarily revert for backwards compatibility with rh-0.12-1 firmware
commit aeff595a5d57264e5f01add5c43f584d88be6a92
Author: Mark Salter <msalter@redhat.com>
Date: Mon Aug 11 13:46:43 2014 -0400
xgene: add support for ACPI-probed serial port
commit 02429d239f5ae917d870a7611a9d838b7822df1a
Author: Mark Salter <msalter@redhat.com>
Date: Sat Aug 9 12:01:20 2014 -0400
sata/xgene: support acpi probing
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 774385f250ebb7448ca3eeb344a064ac989c4988
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Thu Aug 28 14:26:16 2014 -0400
ARM64 / ACPI: Introduce some PCI functions when PCI is enabled
Introduce some PCI functions to make ACPI can be compiled when
CONFIG_PCI is enabled, these functions should be revisited when
implemented on ARM64.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
[fixed up for 3.17-rc]
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 6f711c98b37f1b0a42c4a523d0380d47ed2f95b9
Author: Al Stone <ahs3@redhat.com>
Date: Thu Aug 28 13:14:16 2014 -0400
Fix arm64 compilation error in PNP code
Signed-off-by: Mark Salter <msalter@redhat.com>
commit f874873b7cb10f827bb7f8e08fa282878f740e77
Author: Bob Moore <robert.moore@intel.com>
Date: Tue Sep 2 08:27:40 2014 +0800
ACPICA: Update version to 20140828.
Version 20140828.
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
commit 26f9b7b90576cf808a50edf1ec86ceece9349c9f
Author: Bob Moore <robert.moore@intel.com>
Date: Tue Sep 2 08:27:27 2014 +0800
ACPICA: Disassembler: Fix for gpio_int interrupt polarity flags.
The field is actually 2 bits, not 1.
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
commit 6e1eddc48f6f9b948be4126dd38841f6f70da080
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Tue Sep 2 08:27:19 2014 +0800
ACPICA: Headers: Add GTDT flag definitions for the timer subtable.
Mostly by Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
commit 1091460efb5542ba87f40ef20daff44215587f26
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Tue Sep 2 08:27:12 2014 +0800
ACPICA: ACPI 5.1/Disassembler: Add GICC affinity subtable to SRAT table.
Update template for SRAT.
Add clock_domain to standard CPU affinity subtable.
Mostly by Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
commit 02ba5e067dbb83b0f481db821d05851e9623c401
Author: Bob Moore <robert.moore@intel.com>
Date: Tue Sep 2 08:27:05 2014 +0800
ACPICA: Add _PSx names to the METHOD_NAME list.
Will be used by iASL.
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
commit f2ecef6608a1f74b236df4f93da9b7b5aba4d3fd
Author: Mark Salter <msalter@redhat.com>
Date: Tue Sep 9 22:59:48 2014 -0400
arm64: add parking protocol support
This is a first-cut effort at parking protocol support. It is
very much a work in progress (as is the spec it is based on).
This code deviates from the current spec in a number of ways
to work around current firmware issues and issues with kernels
using 64K page sizes.
caveat utilitor
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 63e220c94f072f10bfae2e1ed375af9dbc017571
Author: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Date: Tue Sep 9 15:37:15 2014 -0500
ata: ahci_platform: Add ACPI support for AMD Seattle SATA controller
This patch adds ACPI support for non-PCI SATA contoller in ahci_platform driver.
It adds ACPI matching table in ahci_platform to support AMD Seattle SATA controller
with following ASL structure in DSDT:
Device (SATA0)
{
Name(_HID, "AMDI0600") // Seattle AHSATA
Name (_CCA, 1) // Cache-coherent controller
Name (_CRS, ResourceTemplate ()
{
Memory32Fixed (ReadWrite, 0xE0300000, 0x00010000)
Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive,,,) { 387 }
})
}
Since ATA driver should not require PCI support for ATA_ACPI,
this patch also removes dependency in the driver/ata/Kconfig.
Signed-off-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
commit d9d7224bd65fb3c1490f06d635b7aceb035acb1e
Author: Graeme Gregory <graeme.gregory@linaro.org>
Date: Wed Aug 13 13:47:18 2014 +0100
tty: SBSA compatible UART
This is a subset of pl011 UART which does not supprt DMA or baud rate
changing. It does, however, provide earlycon support (i.e., using
"earlycon=ttySBSA" on the kernel command line).
It is specified in the Server Base System Architecture document from
ARM.
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
commit 4c67296fce53fed671a78b698d4552636f499b7f
Author: Mark Salter <msalter@redhat.com>
Date: Thu Aug 14 13:17:37 2014 -0400
arm64: set dma coherency ops for ACPI probed devices
Search for a _CCA object and set the correct dma ops based
on device coherency attribute and architecture default.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 959c571815c440150b2f290bf3d13b2fbadbee70
Author: Mark Salter <msalter@redhat.com>
Date: Thu Aug 14 12:32:13 2014 -0400
acpi: add utility to test for device dma coherency
ACPI 5.1 adds a _CCA object to indicate memory coherency
of a bus master device. It is an integer with zero meaning
non-coherent and one meaning coherent. This attribute may
be inherited from a parent device. It may also be missing
entirely, in which case, an architecture-specific default
is assumed.
This patch adds a utility function to parse a device handle
(and its parents) for a _CCA object and return the coherency
attribute if found.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 0dbac48379d3aace2fd7468d83044116f176b4c9
Author: Mark Salter <msalter@redhat.com>
Date: Mon Sep 8 11:58:46 2014 -0400
acpi: fix acpi_os_ioremap for arm64
The acpi_os_ioremap() function may be used to map normal RAM or IO
regions. The current implementation simply uses ioremap_cache(). This
will work for some architectures, but arm64 ioremap_cache() cannot be
used to map IO regions which don't support caching. So for arm64, use
ioremap() for non-RAM regions.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit c9eab819c2107e0c95cf57233de4de5404851ab6
Author: Graeme Gregory <graeme.gregory@linaro.org>
Date: Mon Sep 8 10:36:44 2014 -0400
acpi: add arm to the platforms that use ioremap
Now with the base changes to the arm memory mapping it is safe
to convert to using ioremap to map in the tables.
Signed-off-by: Al Stone <al.stone@linaro.org>
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
commit 7e772a485f980b826a58ecd9c39fbe82085c55fa
Author: Mark Salter <msalter@redhat.com>
Date: Mon Sep 8 17:04:28 2014 -0400
acpi/arm64: NOT FOR UPSTREAM - remove EXPERT dependency
For convenience to keep existing configs working, remove
CONFIG_EXPERT dependency from ACPI for ARM64. This shouldn't
go upstream just yet.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit b42e8f7901e58b86a1cb3ffdf14cb2455fd91ede
Author: Graeme Gregory <graeme.gregory@linaro.org>
Date: Fri Sep 12 22:00:16 2014 +0800
Documentation: ACPI for ARM64
Add documentation for the guidelines of how to use ACPI
on ARM64.
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 57dc75b87d5663181e1c19802297e72e51a324ba
Author: Graeme Gregory <graeme.gregory@linaro.org>
Date: Fri Sep 12 22:00:15 2014 +0800
ARM64 / ACPI: Enable ARM64 in Kconfig
Add Kconfigs to build ACPI on ARM64, and make ACPI available on ARM64.
acpi_idle driver is x86/IA64 dependent now, so make CONFIG_ACPI_PROCESSOR
depend on X86 || IA64, and implement it on ARM64 in the future.
Reviewed-by: Grant Likely <grant.likely@linaro.org>
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
Signed-off-by: Al Stone <al.stone@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 106c5cb3caff13c91cb6056f88a1c0e710b8e0eb
Author: Al Stone <al.stone@linaro.org>
Date: Fri Sep 12 22:00:14 2014 +0800
ARM64 / ACPI: Select ACPI_REDUCED_HARDWARE_ONLY if ACPI is enabled on ARM64
ACPI reduced hardware mode is disabled by default, but ARM64
can only run properly in ACPI hardware reduced mode, so select
ACPI_REDUCED_HARDWARE_ONLY if ACPI is enabled on ARM64.
Reviewed-by: Grant Likely <grant.likely@linaro.org>
Signed-off-by: Al Stone <al.stone@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 73f4aca21985ace8989b6fc8af503940c469f1d7
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Fri Sep 12 22:00:13 2014 +0800
ARM64 / ACPI: Parse GTDT to initialize arch timer
Using the information presented by GTDT to initialize the arch
timer (not memory-mapped).
Originally-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 5efba15fb24c25139dd621a417f2b9cbe2e675f5
Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
Date: Fri Sep 12 22:00:12 2014 +0800
ARM64 / ACPI: Add GICv2 specific ACPI boot support
ACPI kernel uses MADT table for proper GIC initialization. It needs to
parse GIC related subtables, collect CPU interface and distributor
addresses and call driver initialization function (which is hardware
abstraction agnostic). In a similar way, FDT initialize GICv1/2.
NOTE: This commit allow to initialize GICv1/2 basic functionality.
GICv2 vitalization extension, GICv3/4 and ITS are considered as next
steps.
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit e060d9d74cecff59ea96d6124ffb1c9c044a4f9d
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Fri Sep 12 22:00:11 2014 +0800
ARM64 / ACPI: Introduce ACPI_IRQ_MODEL_GIC and register device's gsi
Introduce ACPI_IRQ_MODEL_GIC which is needed for ARM64 as GIC is
used, and then register device's gsi with the core IRQ subsystem.
acpi_register_gsi() is similar to DT based irq_of_parse_and_map(),
since gsi is unique in the system, so use hwirq number directly
for the mapping.
Originally-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit c25e8f66c630713107967076933b5f349655ea6a
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Fri Sep 12 22:00:10 2014 +0800
ACPI / processor: Make it possible to get CPU hardware ID via GICC
Introduce a new function map_gicc_mpidr() to allow MPIDRs to be obtained
from the GICC Structure introduced by ACPI 5.1.
MPIDR is the CPU hardware ID as local APIC ID on x86 platform, so we use
MPIDR not the GIC CPU interface ID to identify CPUs.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 5ce1c3ff91aa9d8012324518789363bb4ded33d4
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Fri Sep 12 22:00:09 2014 +0800
ARM64 / ACPI: Parse MADT for SMP initialization
MADT contains the information for MPIDR which is essential for
SMP initialization, parse the GIC cpu interface structures to
get the MPIDR value and map it to cpu_logical_map(), and add
enabled cpu with valid MPIDR into cpu_possible_map.
ACPI 5.1 only has two explicit methods to boot up SMP, PSCI and
Parking protocol, but the Parking protocol is only specified for
ARMv7 now, so make PSCI as the only way for the SMP boot protocol
before some updates for the ACPI spec or the Parking protocol spec.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
commit 8634cf0fcc8294c355d7cecb55da017ba9ff3ff7
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Fri Sep 12 22:00:08 2014 +0800
ACPI / table: Print GIC information when MADT is parsed
When MADT is parsed, print GIC information to make the boot
log look pretty.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
commit 05facef9c824235c82d8d9c2ae03fe7a729ceef8
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Fri Sep 12 22:00:07 2014 +0800
ARM64 / ACPI: Parse FADT table to get PSCI flags for PSCI init
There are two flags: PSCI_COMPLIANT and PSCI_USE_HVC. When set,
the former signals to the OS that the firmware is PSCI compliant.
The latter selects the appropriate conduit for PSCI calls by
toggling between Hypervisor Calls (HVC) and Secure Monitor Calls
(SMC).
FADT table contains such information, parse FADT to get the flags
for PSCI init. Since ACPI 5.1 doesn't support self defined PSCI
function IDs, which means that only PSCI 0.2+ is supported in ACPI.
At the same time, only ACPI 5.1 or higher verison supports PSCI,
and FADT Major.Minor version was introduced in ACPI 5.1, so we
will check the version and only parse FADT table with version >= 5.1.
If firmware provides ACPI tables with ACPI version less than 5.1,
OS will be messed up with those information and have no way to init
smp and GIC, so disable ACPI if we get an FADT table with version
less that 5.1.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
commit a13a8c748b6170a8a3f4876163ff74e460bd889f
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Fri Sep 12 22:00:06 2014 +0800
ARM64 / ACPI: Make PCI optional for ACPI on ARM64
As PCI for ARM64 is not ready, so introduce some stub functions
to make PCI optional for ACPI, and make ACPI core run without
CONFIG_PCI on ARM64.
Since ACPI on X86 and IA64 depends on PCI and this patch only makes
PCI optional for ARM64, it will not break anything on X86 and IA64.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit a6bf98355490142fc3f6e9c0839af128e326f16d
Author: Graeme Gregory <graeme.gregory@linaro.org>
Date: Fri Sep 12 22:00:05 2014 +0800
ARM64 / ACPI: If we chose to boot from acpi then disable FDT
If the early boot methods of acpi are happy that we have valid ACPI
tables and acpi=off has not been passed. Then do not unflat
devicetree effectively disabling further hardware probing from DT.
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit e3b9886532d1e13ddb68e4955ad776a8623f9766
Author: Al Stone <al.stone@linaro.org>
Date: Fri Sep 12 22:00:04 2014 +0800
ARM64 / ACPI: Introduce early_param for "acpi"
Introduce one early parameters "off" for "acpi" to disable ACPI on
ARM64.
This ensures the kernel uses the DT on a platform that provides both
ACPI tables and DT.
Signed-off-by: Al Stone <al.stone@linaro.org>
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit cf1cbe39385d417286a98e3abd7ea3456be2e88c
Author: Graeme Gregory <graeme.gregory@linaro.org>
Date: Fri Sep 12 22:00:03 2014 +0800
ARM64 / ACPI: Introduce sleep-arm.c
ACPI 5.1 does not currently support S states for ARM64 hardware but
ACPI code will call acpi_target_system_state() for device power
managment, so introduce sleep-arm.c to allow other drivers to function
until S states are defined.
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 90e5cfbaf183d0eb92f1977c24da20c4e0a2f6be
Author: Al Stone <al.stone@linaro.org>
Date: Fri Sep 12 22:00:02 2014 +0800
ARM64 / ACPI: Get RSDP and ACPI boot-time tables
As we want to get ACPI tables to parse and then use the information
for system initialization, we should get the RSDP (Root System
Description Pointer) first, it then locates Extended Root Description
Table (XSDT) which contains all the 64-bit physical address that
pointer to other boot-time tables.
Introduce acpi.c and its related head file in this patch to provide
fundamental needs of extern variables and functions for ACPI core,
and then get boot-time tables as needed.
- asm/acenv.h for arch specific ACPICA environments and
implementation, It is needed unconditionally by ACPI core;
- asm/acpi.h for arch specific variables and functions needed by
ACPI driver core;
- acpi.c for ARM64 related ACPI implementation for ACPI driver
core;
acpi_boot_table_init() is introduced to get RSDP and boot-time tables,
it will be called in setup_arch() before paging_init(), so we should
use eary_memremap() mechanism here to get the RSDP and all the table
pointers.
Signed-off-by: Al Stone <al.stone@linaro.org>
Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 2fe5d24887dea3f1bffabf04e364fa9f355ed553
Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
Date: Fri Sep 12 22:00:01 2014 +0800
ACPI / table: Count matched and successfully parsed entries without specifying max entries
It is very useful to traverse all available table entries without max
number of expected entries type. Current acpi_parse_entries()
implementation gives that feature but it does not count those entries,
it returns 0 instead, so fix it to count matched and successfully
entries and return it.
NOTE: This change has no impact to x86 and ia64 archs since existing code
checks for error occurrence only (acpi_parse_entries(...,0) < 0).
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 8c278c76231f0ba0110e755678eefdd6d077f5da
Author: Ashwin Chaugule <ashwin.chaugule@linaro.org>
Date: Fri Sep 12 22:00:00 2014 +0800
ACPI / table: Add new function to get table entries
The acpi_table_parse() function has a callback that
passes a pointer to a table_header. Add a new function
which takes this pointer and parses its entries. This
eliminates the need to re-traverse all the tables for
each call. e.g. as in acpi_table_parse_madt() which is
normally called after acpi_table_parse().
Signed-off-by: Ashwin Chaugule <ashwin.chaugule@linaro.org>
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit 651ab5ff6804fa0f516715b4d47399e587944de0
Author: Hanjun Guo <hanjun.guo@linaro.org>
Date: Fri Sep 12 21:59:59 2014 +0800
ARM64: Move the init of cpu_logical_map(0) before unflatten_device_tree()
It always make sense to initialize CPU0's logical map entry from the
hardware values, so move the initialization of cpu_logical_map(0)
before unflatten_device_tree() which is needed by ACPI code later.
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
commit b91246f1a7120de6de8fe630ed01e9a0d0415e88
Author: Tanmay Inamdar <tinamdar@apm.com>
Date: Fri Sep 26 14:08:27 2014 -0700
MAINTAINERS: entry for APM X-Gene PCIe host driver
Add entry for AppliedMicro X-Gene PCIe host driver.
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
commit d20b104083718058b599a5ca1a925d8c5243e2e1
Author: Tanmay Inamdar <tinamdar@apm.com>
Date: Fri Sep 26 14:08:26 2014 -0700
dt-bindings: pci: xgene pcie device tree bindings
This patch adds the bindings for X-Gene PCIe driver. The driver resides
under 'drivers/pci/host/pci-xgene.c' file.
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
commit 9c429c919b77a3a63ac2843e9e0e2e50e59d28f3
Author: Tanmay Inamdar <tinamdar@apm.com>
Date: Fri Sep 26 14:08:25 2014 -0700
arm64: dts: APM X-Gene PCIe device tree nodes
This patch adds the device tree nodes for APM X-Gene PCIe host controller and
PCIe clock interface. Since X-Gene SOC supports maximum 5 ports, 5 dts
nodes are added.
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
commit 3aa4f71932b67e648aff4ae0dc5ace7162d74c1c
Author: Tanmay Inamdar <tinamdar@apm.com>
Date: Fri Sep 26 14:08:24 2014 -0700
pci:host: APM X-Gene PCIe host controller driver
This patch adds the AppliedMicro X-Gene SOC PCIe host controller driver.
X-Gene PCIe controller supports maximum up to 8 lanes and GEN3 speed.
X-Gene SOC supports maximum 5 PCIe ports.
Reviewed-by: Liviu Dudau <Liviu.Dudau@arm.com>
Tested-by: Ming Lei <ming.lei@canonical.com>
Tested-by: Dann Frazier <dann.frazier@canonical.com>
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
commit f3c00bd4ae49b19923049f0f4f9d6a95eaa61b1e
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:14 2014 +0100
arm64: Add architectural support for PCI
Use the generic PCI domain and OF functions to provide support for PCI
on arm64.
[bhelgaas: Change comments to use generic PCI, not just PCIe. Nothing at
this level is PCIe-specific.]
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
commit 497220defa08534526ad7b20a9c7eb2d0d903ca4
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:13 2014 +0100
PCI: Add pci_remap_iospace() to map bus I/O resources
Add pci_remap_iospace() to map bus I/O resources into the CPU virtual
address space. Architectures with special needs may provide their own
version, but most should be able to use this one.
This function is useful for PCI host bridge drivers that need to map the
PCI I/O resources into virtual memory space.
[bhelgaas: phys_addr description, drop temporary "err" variable]
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
CC: Arnd Bergmann <arnd@arndb.de>
commit 7a46ace11cf09b3713dcdc2ef4a17130a738f856
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:12 2014 +0100
PCI: Assign unassigned bus resources in pci_scan_root_bus()
If the firmware has not assigned all the bus resources and we are not just
probing the PCI buses, it makes sense to assign the unassigned resources
in pci_scan_root_bus().
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: Arnd Bergmann <arnd@arndb.de>
CC: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
CC: Rob Herring <robh+dt@kernel.org>
commit fe33bc876010c3ef700c863099f2c3c1a5e2c18b
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Wed Sep 24 11:27:33 2014 -0600
of/pci: Add support for parsing PCI host bridge resources from DT
Provide a function to parse the PCI DT ranges that can be used to create a
pci_host_bridge structure together with its associated bus.
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
[make io_base parameter optional]
Signed-off-by: Robert Richter <rrichter@cavium.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: Arnd Bergmann <arnd@arndb.de>
CC: Grant Likely <grant.likely@linaro.org>
CC: Rob Herring <robh+dt@kernel.org>
CC: Catalin Marinas <catalin.marinas@arm.com>
commit 71094afda69ac873bb3d6486307e3048e21e912c
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:10 2014 +0100
of/pci: Add of_pci_get_domain_nr() and of_get_pci_domain_nr()
Add pci_get_new_domain_nr() to allocate a new domain number and
of_get_pci_domain_nr() to retrieve the PCI domain number of a given device
from DT. Host bridge drivers or architecture-specific code can choose to
implement their PCI domain number policy using these two functions.
Using of_get_pci_domain_nr() guarantees a stable PCI domain number on every
boot provided that all host bridge controllers are assigned a number in the
device tree using "linux,pci-domain" property. Mixing use of
pci_get_new_domain_nr() and of_get_pci_domain_nr() is not recommended as it
can lead to potentially conflicting domain numbers being assigned to root
buses behind different host bridges.
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: Arnd Bergmann <arnd@arndb.de>
CC: Grant Likely <grant.likely@linaro.org>
CC: Rob Herring <robh+dt@kernel.org>
CC: Catalin Marinas <catalin.marinas@arm.com>
commit 6b385455f18d15da6b5d60a3b67fdb846ae1a3b1
Author: Catalin Marinas <catalin.marinas@arm.com>
Date: Tue Sep 23 20:01:09 2014 +0100
PCI: Add generic domain handling
The handling of PCI domains (or PCI segments in ACPI speak) is usually a
straightforward affair but its implementation is currently left to the
architectural code, with pci_domain_nr(b) querying the value of the domain
associated with bus b.
This patch introduces CONFIG_PCI_DOMAINS_GENERIC as an option that can be
selected if an architecture wants a simple implementation where the value
of the domain associated with a bus is stored in struct pci_bus.
The architectures that select CONFIG_PCI_DOMAINS_GENERIC will then have to
implement pci_bus_assign_domain_nr() as a way of setting the domain number
associated with a root bus. All child buses except the root bus will
inherit the domain_nr value from their parent.
Signed-off-by: Catalin Marinas <Catalin.Marinas@arm.com>
[Renamed pci_set_domain_nr() to pci_bus_assign_domain_nr()]
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: Arnd Bergmann <arnd@arndb.de>
commit 04b36e0e1da5a96a58dec4f1b393090f00e5b635
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:08 2014 +0100
PCI: Create pci_host_bridge before root bus
Before 7b5436635800 ("PCI: add generic device into pci_host_bridge
struct"), the pci_host_bridge was created before the root bus. Revert the
order of creation as we are going to depend on the pci_host_bridge
structure to retrieve the domain number of the root bus.
[bhelgaas: changelog]
Tested-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Grant Likely <grant.likely@linaro.org>
commit 7569c510218798bd7e5216bf14a42c656f14e891
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:07 2014 +0100
of/pci: Fix the conversion of IO ranges into IO resources
The ranges property for a host bridge controller in DT describes the
mapping between the PCI bus address and the CPU physical address. The
resources framework however expects that the IO resources start at a pseudo
"port" address 0 (zero) and have a maximum size of IO_SPACE_LIMIT. The
conversion from PCI ranges to resources failed to take that into account,
returning a CPU physical address instead of a port number.
Also fix all the drivers that depend on the old behaviour by fetching the
CPU physical address based on the port number where it is being needed.
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
CC: Grant Likely <grant.likely@linaro.org>
CC: Rob Herring <robh+dt@kernel.org>
CC: Arnd Bergmann <arnd@arndb.de>
CC: Thierry Reding <thierry.reding@gmail.com>
CC: Simon Horman <horms@verge.net.au>
CC: Catalin Marinas <catalin.marinas@arm.com>
commit 1b381671c9dd2c82cd6f04bc1588d0fc4e1aea59
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:06 2014 +0100
of/pci: Move of_pci_range_to_resources() to of/address.c
We need to enhance of_pci_range_to_resources() enough that it won't make
sense for it to be inline anymore. Move it to drivers/of/address.c,
keeping it under #ifdef CONFIG_PCI.
[bhelgaas: drop extra detail from changelog, move def under CONFIG_PCI]
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: Grant Likely <grant.likely@linaro.org>
CC: Rob Herring <robh+dt@kernel.org>
CC: Arnd Bergmann <arnd@arndb.de>
CC: Catalin Marinas <catalin.marinas@arm.com>
commit eedd8f88d4895d5c6bc46cf3ddeb114a0fafb7c3
Author: Bjorn Helgaas <bhelgaas@google.com>
Date: Tue Sep 23 17:27:42 2014 -0600
of/pci: Define of_pci_range_to_resource() only when CONFIG_PCI=y
of_pci_range_to_resource() was previously defined always, but it's only
used by PCI code, so move the definition inside the CONFIG_OF_ADDRESS &&
CONFIG_PCI block.
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
commit 4ac73f8ded507537318717bb2b5b6c765db633cd
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:05 2014 +0100
ARM: Define PCI_IOBASE as the base of virtual PCI IO space
This is needed for calls into OF code that parses PCI ranges. It signals
support for memory mapped PCI I/O accesses that are described by device
trees.
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
CC: Russell King <linux@arm.linux.org.uk>
CC: Rob Herring <robh+dt@kernel.org>
commit ce7af33b6db857c95ac3b65fef37e589e839cc79
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:04 2014 +0100
of/pci: Add pci_register_io_range() and pci_pio_to_address()
Some architectures do not have a simple view of the PCI I/O space and
instead use a range of CPU addresses that map to bus addresses. For some
architectures these ranges will be expressed by OF bindings in a device
tree file.
This patch introduces a pci_register_io_range() helper function with a
generic implementation that can be used by such architectures to keep track
of the I/O ranges described by the PCI bindings. If the PCI_IOBASE macro
is not defined, that signals lack of support for PCI and we return an
error.
In order to retrieve the CPU address associated with an I/O port, a new
helper function pci_pio_to_address() is introduced. This will search in
the list of ranges registered with pci_register_io_range() and return the
CPU address that corresponds to the given port.
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
CC: Grant Likely <grant.likely@linaro.org>
CC: Arnd Bergmann <arnd@arndb.de>
commit c5d57f95901af49149ebf6dfc3d7518157e41bfa
Author: Liviu Dudau <Liviu.Dudau@arm.com>
Date: Tue Sep 23 20:01:03 2014 +0100
asm-generic/io.h: Fix ioport_map() for !CONFIG_GENERIC_IOMAP
The !CONFIG_GENERIC_IOMAP version of ioport_map() is wrong. It returns a
mapped, i.e., virtual, address that can start from zero and completely
ignores the PCI_IOBASE and IO_SPACE_LIMIT that most architectures that use
!CONFIG_GENERIC_MAP define.
Tested-by: Tanmay Inamdar <tinamdar@apm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
commit a65e51156bec2c8d690b924bbddf1a740309e543
Author: Mark Salter <msalter@redhat.com>
Date: Tue Jun 24 09:50:28 2014 -0400
arm64: use EFI as last resort for reboot and poweroff
Wire in support for EFI reboot and poweroff functions. We use these
only if no other mechanism has been registered with arm_pm_reboot
and/or pm_power_off respectively.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit c6b81122978c39e52021cc7308edafff88c8b87a
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Date: Wed Jul 30 11:59:04 2014 +0100
arm64/efi: efistub: don't abort if base of DRAM is occupied
If we cannot relocate the kernel Image to its preferred offset of base of DRAM
plus TEXT_OFFSET, instead relocate it to the lowest available 2 MB boundary plus
TEXT_OFFSET. We may lose a bit of memory at the low end, but we can still
proceed normally otherwise.
Acked-by: Mark Salter <msalter@redhat.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: Leif Lindholm <leif.lindholm@linaro.org>
Tested-by: Leif Lindholm <leif.lindholm@linaro.org>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
commit 81fd1d315f3c5b13f9dcf71cce51dfd3a10331c3
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Date: Wed Jul 30 11:59:03 2014 +0100
arm64/efi: efistub: cover entire static mem footprint in PE/COFF .text
The static memory footprint of a kernel Image at boot is larger than the
Image file itself. Things like .bss data and initial page tables are allocated
statically but populated dynamically so their content is not contained in the
Image file.
However, if EFI (or GRUB) has loaded the Image at precisely the desired offset
of base of DRAM + TEXT_OFFSET, the Image will be booted in place, and we have
to make sure that the allocation done by the PE/COFF loader is large enough.
Fix this by growing the PE/COFF .text section to cover the entire static
memory footprint. The part of the section that is not covered by the payload
will be zero initialised by the PE/COFF loader.
Acked-by: Mark Salter <msalter@redhat.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: Leif Lindholm <leif.lindholm@linaro.org>
Tested-by: Leif Lindholm <leif.lindholm@linaro.org>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
commit 8f5f73c2117c2dc4e8903d162023c34177327ae3
Author: Mark Rutland <mark.rutland@arm.com>
Date: Wed Jul 30 11:59:02 2014 +0100
arm64: spin-table: handle unmapped cpu-release-addrs
In certain cases the cpu-release-addr of a CPU may not fall in the
linear mapping (e.g. when the kernel is loaded above this address due to
the presence of other images in memory). This is problematic for the
spin-table code as it assumes that it can trivially convert a
cpu-release-addr to a valid VA in the linear map.
This patch modifies the spin-table code to use a temporary cached
mapping to write to a given cpu-release-addr, enabling us to support
addresses regardless of whether they are covered by the linear mapping.
Acked-by: Leif Lindholm <leif.lindholm@linaro.org>
Tested-by: Leif Lindholm <leif.lindholm@linaro.org>
Tested-by: Mark Salter <msalter@redhat.com>
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
[ardb: added (__force void *) cast]
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
commit b00c56bff1ffb6c77655479fb350ee3d5c8bcf63
Author: Mark Salter <msalter@redhat.com>
Date: Tue Jun 24 23:16:45 2014 -0400
perf: fix arm64 build error
I'm seeing the following build error on arm64:
In file included from util/event.c:3:0:
util/event.h:95:17: error: 'PERF_REGS_MAX' undeclared here (not in a function)
u64 cache_regs[PERF_REGS_MAX];
^
This patch adds a PEFF_REGS_MAX definition for arm64.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit f440dcb067bf1367719e8a41b96a2a83c1232690
Author: Mark Salter <msalter@redhat.com>
Date: Thu Jul 17 13:34:50 2014 -0400
ahci_xgene: add errata workaround for ATA_CMD_SMART
commit 2a0bdff6b958d1b2:
ahci_xgene: fix the dma state machine lockup for the IDENTIFY DEVICE PIO mode command.
added a workaround for X-Gene AHCI controller errata. This was done
for all ATA_CMD_ID_ATA commands. The errata also appears to affect
ATA_CMD_SMART commands as well. This was discovered when running
smartd or just smartctl -x. This patch adds a dma engine restart for
ATA_CMD_SMART commands which clears up the issues seen with smartd.
Signed-off-by: Mark Salter <msalter@redhat.com>
commit 93b44dc9e1d73d4864a9350888509792f25e4210
Author: Kyle McMartin <kmcmarti@redhat.com>
Date: Tue May 13 22:25:26 2014 -0400
arm64: don't set READ_IMPLIES_EXEC for EM_AARCH64 ELF objects
Message-id: <20140513222526.GC26038@redacted.bos.redhat.com>
Patchwork-id: 79789
O-Subject: [ACADIA PATCH] arm64: don't set READ_IMPLIES_EXEC for EM_AARCH64 ELF objects
Bugzilla: 1085528
BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1085528
Upstream: submitted soon
[Sadly this isn't (yet) sufficient... but it fixes at least one issue
here... cat /proc/$$/personality shows READ_IMPLIES_EXEC before. I'll
try to figure the rest out tomorrow.]
Currently, we're accidentally ending up with executable stacks on
AArch64 when the ABI says we shouldn't be, and relying on glibc to fix
things up for us when we're loaded. However, SELinux will deny us
mucking with the stack, and hit us with execmem AVCs.
The reason this is happening is somewhat complex:
fs/binfmt_elf.c:load_elf_binary()
- initializes executable_stack = EXSTACK_DEFAULT implying the
architecture should make up its mind.
- does a pile of loading goo
- runs through the program headers, looking for PT_GNU_STACK
and setting (or unsetting) executable_stack if it finds it.
This is our first problem, we won't generate these unless an
executable stack is explicitly requested.
- more ELF loading goo
- sets whether we're a compat task or not (TIF_32BIT) based on compat.h
- for compat reasons (pre-GNU_STACK) checks if the READ_IMPLIES_EXEC
flag should be set for ancient toolchains
Here's our second problem, we test if read_implies_exec based on
stk != EXSTACK_DISABLE_X, which is true since stk == EXSTACK_DEFAULT.
So we set current->personality |= READ_IMPLIES_EXEC like a broken
legacy toolchain would want.
- Now we call setup_arg_pages to set up the stack...
fs/exec.c:setup_arg_pages()
- lots of magic happens here
- vm_flags gets initialized to VM_STACK_FLAGS
Here's our third problem, VM_STACK_FLAGS on arm64 is
VM_DEFAULT_DATA_FLAG which tests READ_IMPLIES_EXEC and sets VM_EXEC
if it's true. So we end up with an executable stack mapping, since we
don't have executable_stack set (it's still EXSTACK_DEFAULT at this
point) to unset it anywhere.
Bang. execstack AVC when the program starts running.
The easiest way I can see to fix this is to test if we're a legacy task
and fix it up there. But that's not as simple as it sounds, because
the 32-bit ABI depends on what revision of the CPU we've enabled (not
that it matters since we're ARMv8...) Regardless, in the compat case,
set READ_IMPLIES_EXEC if we've found a GNU_STACK header which explicitly
requested it as in arch/arm/kernel/elf.c:arm_elf_read_implies_exec().
Signed-off-by: Kyle McMartin <kmcmarti@redhat.com>
Signed-off-by: Donald Dutile <ddutile@redhat.com>
Documentation/arm64/arm-acpi.txt | 218 +++++++
.../devicetree/bindings/pci/xgene-pci.txt | 57 ++
Documentation/kernel-parameters.txt | 3 +-
MAINTAINERS | 8 +
arch/arm/include/asm/io.h | 1 +
arch/arm/include/asm/kvm_mmu.h | 13 +
arch/arm/kvm/arm.c | 23 +-
arch/arm/mach-integrator/pci_v3.c | 23 +-
arch/arm64/Kconfig | 28 +-
arch/arm64/Makefile | 1 +
arch/arm64/boot/dts/apm-mustang.dts | 8 +
arch/arm64/boot/dts/apm-storm.dtsi | 165 ++++++
arch/arm64/include/asm/Kbuild | 1 +
arch/arm64/include/asm/acenv.h | 18 +
arch/arm64/include/asm/acpi.h | 99 ++++
arch/arm64/include/asm/cpu_ops.h | 1 +
arch/arm64/include/asm/elf.h | 3 +-
arch/arm64/include/asm/io.h | 3 +-
arch/arm64/include/asm/kvm_arm.h | 17 +-
arch/arm64/include/asm/kvm_mmu.h | 75 +++
arch/arm64/include/asm/pci.h | 37 ++
arch/arm64/include/asm/pgtable.h | 2 +
arch/arm64/include/asm/psci.h | 3 +-
arch/arm64/include/asm/smp.h | 10 +-
arch/arm64/kernel/Makefile | 5 +-
arch/arm64/kernel/acpi.c | 397 +++++++++++++
arch/arm64/kernel/cpu_ops.c | 8 +-
arch/arm64/kernel/efi-stub.c | 16 +-
arch/arm64/kernel/efi.c | 11 +
arch/arm64/kernel/head.S | 6 +-
arch/arm64/kernel/pci.c | 70 +++
arch/arm64/kernel/process.c | 6 +
arch/arm64/kernel/psci.c | 78 ++-
arch/arm64/kernel/setup.c | 64 +-
arch/arm64/kernel/smp.c | 2 +-
arch/arm64/kernel/smp_parking_protocol.c | 110 ++++
arch/arm64/kernel/smp_spin_table.c | 22 +-
arch/arm64/kernel/time.c | 7 +
arch/arm64/kvm/hyp-init.S | 20 +-
arch/arm64/mm/dma-mapping.c | 65 ++
arch/arm64/pci/Makefile | 1 +
arch/arm64/pci/pci.c | 28 +
drivers/acpi/Kconfig | 6 +-
drivers/acpi/Makefile | 6 +-
drivers/acpi/acpica/utresrc.c | 4 +-
drivers/acpi/bus.c | 3 +
drivers/acpi/internal.h | 5 +
drivers/acpi/osl.c | 6 +-
drivers/acpi/processor_core.c | 37 ++
drivers/acpi/sleep-arm.c | 28 +
drivers/acpi/tables.c | 115 +++-
drivers/acpi/utils.c | 26 +
drivers/ata/Kconfig | 2 +-
drivers/ata/ahci_platform.c | 13 +
drivers/ata/ahci_xgene.c | 30 +-
drivers/clocksource/arm_arch_timer.c | 120 +++-
drivers/irqchip/irq-gic-v3.c | 10 +
drivers/irqchip/irq-gic.c | 116 ++++
drivers/irqchip/irqchip.c | 3 +
drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 12 +
drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 3 +
drivers/net/ethernet/amd/xgbe/xgbe-main.c | 1 +
drivers/net/ethernet/amd/xgbe/xgbe.h | 3 +
drivers/net/ethernet/smsc/smc91x.c | 10 +
drivers/net/phy/amd-xgbe-phy.c | 408 +++++++------
drivers/of/address.c | 154 +++++
drivers/of/of_pci.c | 142 +++++
drivers/pci/host/Kconfig | 10 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pci-tegra.c | 10 +-
drivers/pci/host/pci-xgene.c | 659 +++++++++++++++++++++
drivers/pci/host/pcie-rcar.c | 21 +-
drivers/pci/pci.c | 40 ++
drivers/pci/probe.c | 46 +-
drivers/pnp/resource.c | 2 +
drivers/tty/Kconfig | 6 +
drivers/tty/Makefile | 1 +
drivers/tty/sbsauart.c | 355 +++++++++++
drivers/tty/serial/8250/8250_dw.c | 9 +
drivers/virtio/virtio_mmio.c | 12 +-
include/acpi/acnames.h | 4 +
include/acpi/acpi_bus.h | 2 +
include/acpi/acpi_io.h | 6 +
include/acpi/acpixf.h | 2 +-
include/acpi/actbl1.h | 19 +-
include/acpi/actbl3.h | 9 +-
include/asm-generic/io.h | 2 +-
include/asm-generic/pgtable.h | 4 +
include/kvm/arm_vgic.h | 20 +-
include/linux/acpi.h | 5 +
include/linux/clocksource.h | 6 +
include/linux/irqchip/arm-gic-acpi.h | 31 +
include/linux/irqchip/arm-gic.h | 2 +
include/linux/of_address.h | 17 +-
include/linux/of_pci.h | 13 +
include/linux/pci.h | 64 +-
tools/perf/arch/arm64/include/perf_regs.h | 2 +
virt/kvm/arm/arch_timer.c | 108 ++--
virt/kvm/arm/vgic-v2.c | 75 ++-
virt/kvm/arm/vgic-v3.c | 8 +-
virt/kvm/arm/vgic.c | 32 +-
101 files changed, 4112 insertions(+), 487 deletions(-)
diff --git a/Documentation/arm64/arm-acpi.txt b/Documentation/arm64/arm-acpi.txt
new file mode 100644
index 0000000..b7dc826
--- /dev/null
+++ b/Documentation/arm64/arm-acpi.txt
@@ -0,0 +1,218 @@
+ACPI on ARMv8 Servers
+---------------------
+
+ACPI can be used for ARMv8 general purpose servers designed to follow
+the SBSA specification (currently available to people with an ARM login at
+http://silver.arm.com).
+
+The kernel will implement minimum ACPI version is 5.1 + errata as released by
+the UEFI Forum, which is available at <http://www.uefi.org/acpi/specs>.
+
+If the machine does not meet the requirements of the SBSA, or cannot be
+described in the required ACPI specifications then it is likely that Device Tree
+(DT) is more suitable for the hardware.
+
+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).
+
+When booting using ACPI tables the /chosen node in DT will still be parsed
+to extract the kernel command line and initrd path. No other section of
+the DT will be used.
+
+Booting using ACPI tables
+-------------------------
+
+Currently, the only defined method to pass ACPI tables to the kernel on ARMv8
+is via the UEFI system configuration table.
+
+The UEFI implementation MUST set the ACPI_20_TABLE_GUID to point to the
+RSDP table (the table with the ACPI signature "RSD PTR ").
+
+The pointer to the RSDP table will be retrieved from EFI by the ACPI core.
+
+Processing of ACPI tables may be disabled by passing acpi=off on the kernel
+command line.
+
+DO use an XSDT; RSDTs are deprecated and should not be used on arm64. They
+only allow for 32-bit addresses.
+
+DO NOT use the 32-bit address fields in the FADT; they are deprecated. The
+64-bit alternatives MUST be used.
+
+The minimum set of tables MUST include RSDP, XSDT, FACS, FADT, DSDT, MADT
+and GTDT. If PCI is used the MCFG table MUST also be present.
+
+ACPI Detection
+--------------
+
+Drivers should determine their probe() type by checking for ACPI_HANDLE,
+or .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 are far simpler than the information provided via Device Tree. Drivers
+should take into account this simplicity and work with sensible defaults.
+
+On no account should a Device Tree attempt to be replicated in ASL using such
+constructs as Name(KEY0, "Value1") type constructs. Additional driver specific
+data should be represented with the appropriate _DSD (ACPI Section 6.2.5)
+structure. _DSM (ACPI Section 9.14.1) should only be used if _DSD cannot
+represent the data required.
+
+This data should be rare and not OS specific. For x86 ACPI has taken to
+identifying itself as Windows because it was found that only one path was
+routinely tested. For ARMv8 it would be preferable to have only one well
+tested path.
+
+_DSD covers more than the generic server case and care should be taken not to
+replicate highly specific embedded behaviour from DT into generic servers.
+
+Common _DSD bindings should be submitted to ASWG to be included in the
+document :-
+
+http://www.uefi.org/sites/default/files/resources/_DSD-implementation-guide-toplevel.htm
+
+If these bindings are mirrored from DT care should be taken to ensure they are
+reviewed as DT bindings before submission to limit divergance in bindings.
+
+Programmable Power Control Resources
+------------------------------------
+
+Programmable power control resources include such resources as voltage/current
+providers (regulators) and clock sources.
+
+For power control of these resources they should be represented with Power
+Resource Objects (ACPI Section 7.1). The ACPI core will then handle correctly
+enabling/disabling of resources as they are needed.
+
+The ACPI 5.1 specification does not contain any standard binding for these
+objects to enable programmable levels or rates so this should be avoided if
+possible and the resources set to appropriate levels by the firmware. If this is
+not possible then any manipulation should be abstracted in ASL.
+
+Each device in ACPI has D-states and these can be controlled through
+the optional methods _PS0..._PS3 where _PS0 is full on and _PS3 is full off.
+
+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/enabled in the _PS0 method should be disabled/de-allocated
+in the _PS3 method.
+
+Such code in _PS? methods will of course be very platform specific but
+should allow the driver to operate the device without special non-standard
+values being read from ASL. Further, abstracting the use of these resources
+allows hardware revisions without requiring updates to the kernel.
+
+Clocks
+------
+
+Like clocks that are part of the power resources there is no standard way
+to represent a clock tree in ACPI 5.1 in a similar manner to how it is
+described in DT.
+
+Devices affected by this include things like UARTs, SoC driven LCD displays,
+etc.
+
+The firmware (for example, UEFI) should initialize these clocks to fixed working
+values before the kernel is executed.
+
+Driver Recommendations
+----------------------
+
+DO NOT remove any FDT handling when adding ACPI support for a driver. Different
+systems may use the same device.
+
+DO try and keep complex sections of ACPI and DT functionality separate. This
+may mean a patch to break out some complex DT to another function before
+the patch to add ACPI. This may happen in other functions but is most likely
+in probe function. This gives a clearer flow of data for reviewing driver
+source.
+
+probe() :-
+
+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(stuct 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.
+
+module device tables :-
+
+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 following areas are not yet well defined for ARM in the current ACPI
+specification and are expected to be worked through in the UEFI ACPI
+Specification Working Group (ASWG) <http://www.uefi.org/workinggroups>.
+Participation in this group is open to all UEFI members.
+
+ - ACPI based CPU topology
+ - ACPI based Power management
+ - CPU idle control based on PSCI
+ - CPU performance control (CPPC)
+ - ACPI based SMMU
+ - ITS support for GIC in MADT
+
+No code shall be accepted into the kernel unless it complies with the released
+standards from UEFI ASWG. If there are features missing from ACPI to make it
+function on a platform, ECRs should be submitted to ASWG and go through the
+approval process.
diff --git a/Documentation/devicetree/bindings/pci/xgene-pci.txt b/Documentation/devicetree/bindings/pci/xgene-pci.txt
new file mode 100644
index 0000000..1070b06
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/xgene-pci.txt
@@ -0,0 +1,57 @@
+* AppliedMicro X-Gene PCIe interface
+
+Required properties:
+- device_type: set to "pci"
+- compatible: should contain "apm,xgene-pcie" to identify the core.
+- reg: A list of physical base address and length for each set of controller
+ registers. Must contain an entry for each entry in the reg-names
+ property.
+- reg-names: Must include the following entries:
+ "csr": controller configuration registers.
+ "cfg": pcie configuration space registers.
+- #address-cells: set to <3>
+- #size-cells: set to <2>
+- ranges: ranges for the outbound memory, I/O regions.
+- dma-ranges: ranges for the inbound memory regions.
+- #interrupt-cells: set to <1>
+- interrupt-map-mask and interrupt-map: standard PCI properties
+ to define the mapping of the PCIe interface to interrupt
+ numbers.
+- clocks: from common clock binding: handle to pci clock.
+
+Optional properties:
+- status: Either "ok" or "disabled".
+- dma-coherent: Present if dma operations are coherent
+
+Example:
+
+SoC specific DT Entry:
+
+ pcie0: pcie@1f2b0000 {
+ status = "disabled";
+ device_type = "pci";
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */
+ 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+ reg-names = "csr", "cfg";
+ ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */
+ 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+ dma-coherent;
+ clocks = <&pcie0clk 0>;
+ };
+
+
+Board specific DT Entry:
+ &pcie0 {
+ status = "ok";
+ };
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 10d51c2..9464c6d 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -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.
- acpi= [HW,ACPI,X86]
+ acpi= [HW,ACPI,X86,ARM]
Advanced Configuration and Power Interface
Format: { force | off | strict | noirq | rsdt }
force -- enable ACPI if default was off
@@ -175,6 +175,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
strictly ACPI specification compliant.
rsdt -- prefer RSDT over (default) XSDT
copy_dsdt -- copy DSDT to memory
+ For ARM64, ONLY "acpi=off" is available.
See also Documentation/power/runtime_pm.txt, pci=noacpi
diff --git a/MAINTAINERS b/MAINTAINERS
index 3705430..f6b49e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6940,6 +6940,14 @@ L: linux-pci@vger.kernel.org
S: Maintained
F: drivers/pci/host/*spear*
+PCI DRIVER FOR APPLIEDMICRO XGENE
+M: Tanmay Inamdar <tinamdar@apm.com>
+L: linux-pci@vger.kernel.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/xgene-pci.txt
+F: drivers/pci/host/pci-xgene.c
+
PCMCIA SUBSYSTEM
P: Linux PCMCIA Team
L: linux-pcmcia@lists.infradead.org
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index 3d23418..22b7529 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -178,6 +178,7 @@ static inline void __iomem *__typesafe_io(unsigned long addr)
/* PCI fixed i/o mapping */
#define PCI_IO_VIRT_BASE 0xfee00000
+#define PCI_IOBASE PCI_IO_VIRT_BASE
#if defined(CONFIG_PCI)
void pci_ioremap_set_mem_type(int mem_type);
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 5cc0b0f..03a08bb 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -21,6 +21,7 @@
#include <asm/memory.h>
#include <asm/page.h>
+#include <asm/kvm_arm.h>
/*
* We directly use the kernel VA for the HYP, as we can directly share
@@ -178,6 +179,18 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
void stage2_flush_vm(struct kvm *kvm);
+static inline int kvm_get_phys_addr_shift(void)
+{
+ return KVM_PHYS_SHIFT;
+}
+
+
+static inline u32 get_vttbr_baddr_mask(void)
+{
+ return VTTBR_BADDR_MASK;
+}
+
+
#endif /* !__ASSEMBLY__ */
#endif /* __ARM_KVM_MMU_H__ */
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index a99e0cd..d0fca8f 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -37,6 +37,7 @@
#include <asm/mman.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
+#include <asm/cputype.h>
#include <asm/virt.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_asm.h>
@@ -61,6 +62,12 @@ static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
static u8 kvm_next_vmid;
static DEFINE_SPINLOCK(kvm_vmid_lock);
+#ifdef CONFIG_ARM64
+static u64 vttbr_baddr_mask;
+#else
+static u32 vttbr_baddr_mask;
+#endif
+
static bool vgic_present;
static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
@@ -429,8 +436,14 @@ static void update_vttbr(struct kvm *kvm)
/* update vttbr to be used with the new vmid */
pgd_phys = virt_to_phys(kvm->arch.pgd);
vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK;
- kvm->arch.vttbr = pgd_phys & VTTBR_BADDR_MASK;
- kvm->arch.vttbr |= vmid;
+
+ /*
+ * If the VTTBR isn't aligned there is something wrong with the system
+ * or kernel.
+ */
+ BUG_ON(pgd_phys & ~vttbr_baddr_mask);
+
+ kvm->arch.vttbr = pgd_phys | vmid;
spin_unlock(&kvm_vmid_lock);
}
@@ -1015,6 +1028,12 @@ int kvm_arch_init(void *opaque)
}
}
+ vttbr_baddr_mask = get_vttbr_baddr_mask();
+ if (vttbr_baddr_mask == ~0) {
+ kvm_err("Cannot set vttbr_baddr_mask\n");
+ return -EINVAL;
+ }
+
cpu_notifier_register_begin();
err = init_hyp_mode();
diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c
index 05e1f73..c186a17 100644
--- a/arch/arm/mach-integrator/pci_v3.c
+++ b/arch/arm/mach-integrator/pci_v3.c
@@ -660,6 +660,7 @@ static void __init pci_v3_preinit(void)
{
unsigned long flags;
unsigned int temp;
+ phys_addr_t io_address = pci_pio_to_address(io_mem.start);
pcibios_min_mem = 0x00100000;
@@ -701,7 +702,7 @@ static void __init pci_v3_preinit(void)
/*
* Setup window 2 - PCI IO
*/
- v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_mem.start) |
+ v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_address) |
V3_LB_BASE_ENABLE);
v3_writew(V3_LB_MAP2, v3_addr_to_lb_map2(0));
@@ -742,6 +743,7 @@ static void __init pci_v3_preinit(void)
static void __init pci_v3_postinit(void)
{
unsigned int pci_cmd;
+ phys_addr_t io_address = pci_pio_to_address(io_mem.start);
pci_cmd = PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
@@ -758,7 +760,7 @@ static void __init pci_v3_postinit(void)
"interrupt: %d\n", ret);
#endif
- register_isa_ports(non_mem.start, io_mem.start, 0);
+ register_isa_ports(non_mem.start, io_address, 0);
}
/*
@@ -867,33 +869,32 @@ static int __init pci_v3_probe(struct platform_device *pdev)
for_each_of_pci_range(&parser, &range) {
if (!range.flags) {
- of_pci_range_to_resource(&range, np, &conf_mem);
+ ret = of_pci_range_to_resource(&range, np, &conf_mem);
conf_mem.name = "PCIv3 config";
}
if (range.flags & IORESOURCE_IO) {
- of_pci_range_to_resource(&range, np, &io_mem);
+ ret = of_pci_range_to_resource(&range, np, &io_mem);
io_mem.name = "PCIv3 I/O";
}
if ((range.flags & IORESOURCE_MEM) &&
!(range.flags & IORESOURCE_PREFETCH)) {
non_mem_pci = range.pci_addr;
non_mem_pci_sz = range.size;
- of_pci_range_to_resource(&range, np, &non_mem);
+ ret = of_pci_range_to_resource(&range, np, &non_mem);
non_mem.name = "PCIv3 non-prefetched mem";
}
if ((range.flags & IORESOURCE_MEM) &&
(range.flags & IORESOURCE_PREFETCH)) {
pre_mem_pci = range.pci_addr;
pre_mem_pci_sz = range.size;
- of_pci_range_to_resource(&range, np, &pre_mem);
+ ret = of_pci_range_to_resource(&range, np, &pre_mem);
pre_mem.name = "PCIv3 prefetched mem";
}
- }
- if (!conf_mem.start || !io_mem.start ||
- !non_mem.start || !pre_mem.start) {
- dev_err(&pdev->dev, "missing ranges in device node\n");
- return -EINVAL;
+ if (ret < 0) {
+ dev_err(&pdev->dev, "missing ranges in device node\n");
+ return ret;
+ }
}
pci_v3.map_irq = of_irq_parse_and_map_pci;
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index fd4e81a..e57b91a 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1,5 +1,6 @@
config ARM64
def_bool y
+ select ACPI_REDUCED_HARDWARE_ONLY if ACPI
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_SG_CHAIN
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
@@ -81,7 +82,7 @@ config MMU
def_bool y
config NO_IOPORT_MAP
- def_bool y
+ def_bool y if !PCI
config STACKTRACE_SUPPORT
def_bool y
@@ -156,6 +157,26 @@ menu "Bus support"
config ARM_AMBA
bool
+config PCI
+ bool "PCI support"
+ help
+ This feature enables support for PCI bus system. If you say Y
+ here, the kernel will include drivers and infrastructure code
+ to support PCI bus devices.
+
+config PCI_DOMAINS
+ def_bool PCI
+
+config PCI_DOMAINS_GENERIC
+ def_bool PCI
+
+config PCI_SYSCALL
+ def_bool PCI
+
+source "drivers/pci/Kconfig"
+source "drivers/pci/pcie/Kconfig"
+source "drivers/pci/hotplug/Kconfig"
+
endmenu
menu "Kernel Features"
@@ -235,6 +256,9 @@ config SMP
If you don't know what to do here, say N.
+config ARM_PARKING_PROTOCOL
+ def_bool y if SMP
+
config SCHED_MC
bool "Multi-core scheduler support"
depends on SMP
@@ -421,6 +445,8 @@ source "drivers/Kconfig"
source "drivers/firmware/Kconfig"
+source "drivers/acpi/Kconfig"
+
source "fs/Kconfig"
source "arch/arm64/kvm/Kconfig"
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 2df5e5d..bddd4e3 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -50,6 +50,7 @@ core-y += arch/arm64/kernel/ arch/arm64/mm/
core-$(CONFIG_KVM) += arch/arm64/kvm/
core-$(CONFIG_XEN) += arch/arm64/xen/
core-$(CONFIG_CRYPTO) += arch/arm64/crypto/
+drivers-$(CONFIG_PCI) += arch/arm64/pci/
libs-y := arch/arm64/lib/ $(libs-y)
libs-y += $(LIBGCC)
libs-$(CONFIG_EFI_STUB) += drivers/firmware/efi/libstub/
diff --git a/arch/arm64/boot/dts/apm-mustang.dts b/arch/arm64/boot/dts/apm-mustang.dts
index b2f5622..f649000 100644
--- a/arch/arm64/boot/dts/apm-mustang.dts
+++ b/arch/arm64/boot/dts/apm-mustang.dts
@@ -25,6 +25,14 @@
};
};
+&pcie0clk {
+ status = "ok";
+};
+
+&pcie0 {
+ status = "ok";
+};
+
&serial0 {
status = "ok";
};
diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi
index c0aceef..403197a 100644
--- a/arch/arm64/boot/dts/apm-storm.dtsi
+++ b/arch/arm64/boot/dts/apm-storm.dtsi
@@ -269,6 +269,171 @@
enable-mask = <0x2>;
clock-output-names = "rtcclk";
};
+
+ pcie0clk: pcie0clk@1f2bc000 {
+ status = "disabled";
+ compatible = "apm,xgene-device-clock";
+ #clock-cells = <1>;
+ clocks = <&socplldiv2 0>;
+ reg = <0x0 0x1f2bc000 0x0 0x1000>;
+ reg-names = "csr-reg";
+ clock-output-names = "pcie0clk";
+ };
+
+ pcie1clk: pcie1clk@1f2cc000 {
+ status = "disabled";
+ compatible = "apm,xgene-device-clock";
+ #clock-cells = <1>;
+ clocks = <&socplldiv2 0>;
+ reg = <0x0 0x1f2cc000 0x0 0x1000>;
+ reg-names = "csr-reg";
+ clock-output-names = "pcie1clk";
+ };
+
+ pcie2clk: pcie2clk@1f2dc000 {
+ status = "disabled";
+ compatible = "apm,xgene-device-clock";
+ #clock-cells = <1>;
+ clocks = <&socplldiv2 0>;
+ reg = <0x0 0x1f2dc000 0x0 0x1000>;
+ reg-names = "csr-reg";
+ clock-output-names = "pcie2clk";
+ };
+
+ pcie3clk: pcie3clk@1f50c000 {
+ status = "disabled";
+ compatible = "apm,xgene-device-clock";
+ #clock-cells = <1>;
+ clocks = <&socplldiv2 0>;
+ reg = <0x0 0x1f50c000 0x0 0x1000>;
+ reg-names = "csr-reg";
+ clock-output-names = "pcie3clk";
+ };
+
+ pcie4clk: pcie4clk@1f51c000 {
+ status = "disabled";
+ compatible = "apm,xgene-device-clock";
+ #clock-cells = <1>;
+ clocks = <&socplldiv2 0>;
+ reg = <0x0 0x1f51c000 0x0 0x1000>;
+ reg-names = "csr-reg";
+ clock-output-names = "pcie4clk";
+ };
+ };
+
+ pcie0: pcie@1f2b0000 {
+ status = "disabled";
+ device_type = "pci";
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */
+ 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+ reg-names = "csr", "cfg";
+ ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */
+ 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
+ dma-coherent;
+ clocks = <&pcie0clk 0>;
+ };
+
+ pcie1: pcie@1f2c0000 {
+ status = "disabled";
+ device_type = "pci";
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = < 0x00 0x1f2c0000 0x0 0x00010000 /* Controller registers */
+ 0xd0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+ reg-names = "csr", "cfg";
+ ranges = <0x01000000 0x0 0x00000000 0xd0 0x10000000 0x00 0x00010000 /* io */
+ 0x02000000 0x0 0x80000000 0xd1 0x80000000 0x00 0x80000000>; /* mem */
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc8 0x1
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xc9 0x1
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xca 0x1
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
+ dma-coherent;
+ clocks = <&pcie1clk 0>;
+ };
+
+ pcie2: pcie@1f2d0000 {
+ status = "disabled";
+ device_type = "pci";
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = < 0x00 0x1f2d0000 0x0 0x00010000 /* Controller registers */
+ 0x90 0xd0000000 0x0 0x00040000>; /* PCI config space */
+ reg-names = "csr", "cfg";
+ ranges = <0x01000000 0x0 0x00000000 0x90 0x10000000 0x0 0x00010000 /* io */
+ 0x02000000 0x0 0x80000000 0x91 0x80000000 0x0 0x80000000>; /* mem */
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xce 0x1
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xcf 0x1
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xd0 0x1
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
+ dma-coherent;
+ clocks = <&pcie2clk 0>;
+ };
+
+ pcie3: pcie@1f500000 {
+ status = "disabled";
+ device_type = "pci";
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = < 0x00 0x1f500000 0x0 0x00010000 /* Controller registers */
+ 0xa0 0xd0000000 0x0 0x00040000>; /* PCI config space */
+ reg-names = "csr", "cfg";
+ ranges = <0x01000000 0x0 0x00000000 0xa0 0x10000000 0x0 0x00010000 /* io */
+ 0x02000000 0x0 0x80000000 0xa1 0x80000000 0x0 0x80000000>; /* mem */
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xd4 0x1
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xd5 0x1
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xd6 0x1
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
+ dma-coherent;
+ clocks = <&pcie3clk 0>;
+ };
+
+ pcie4: pcie@1f510000 {
+ status = "disabled";
+ device_type = "pci";
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
+ #interrupt-cells = <1>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = < 0x00 0x1f510000 0x0 0x00010000 /* Controller registers */
+ 0xc0 0xd0000000 0x0 0x00200000>; /* PCI config space */
+ reg-names = "csr", "cfg";
+ ranges = <0x01000000 0x0 0x00000000 0xc0 0x10000000 0x0 0x00010000 /* io */
+ 0x02000000 0x0 0x80000000 0xc1 0x80000000 0x0 0x80000000>; /* mem */
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xda 0x1
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xdb 0x1
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xdc 0x1
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
+ dma-coherent;
+ clocks = <&pcie4clk 0>;
};
serial0: serial@1c020000 {
diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild
index 0b3fcf8..07cb417 100644
--- a/arch/arm64/include/asm/Kbuild
+++ b/arch/arm64/include/asm/Kbuild
@@ -29,6 +29,7 @@ generic-y += mman.h
generic-y += msgbuf.h
generic-y += mutex.h
generic-y += pci.h
+generic-y += pci-bridge.h
generic-y += poll.h
generic-y += preempt.h
generic-y += resource.h
diff --git a/arch/arm64/include/asm/acenv.h b/arch/arm64/include/asm/acenv.h
new file mode 100644
index 0000000..b49166f
--- /dev/null
+++ b/arch/arm64/include/asm/acenv.h
@@ -0,0 +1,18 @@
+/*
+ * 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 */
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
new file mode 100644
index 0000000..7f6cd91
--- /dev/null
+++ b/arch/arm64/include/asm/acpi.h
@@ -0,0 +1,99 @@
+/*
+ * 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 <asm/smp_plat.h>
+
+/* Basic configuration for ACPI */
+#ifdef CONFIG_ACPI
+#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;
+}
+
+/* MPIDR value provided in GICC structure is 64 bits, but
+ * the acpi processor driver use the 32 bits cpu hardware
+ * ID (apic_id on intel platform) everywhere, it is pretty
+ * hard to modify the acpi processor driver to accept the
+ * 64 bits MPIDR value, at the same time, only 32 bits of
+ * the MPIDR is used in the 64 bits MPIDR, just pack the
+ * Affx fields into a single 32 bit identifier to accommodate
+ * the acpi processor drivers.
+ */
+static inline u32 pack_mpidr_into_32_bits(u64 mpidr)
+{
+ /*
+ * Bits [0:7] Aff0;
+ * Bits [8:15] Aff1;
+ * Bits [16:23] Aff2;
+ * Bits [32:39] Aff3;
+ */
+ return (u32) ((mpidr & 0xff00000000) >> 8) | mpidr;
+}
+
+/*
+ * 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.
+ *
+ * cpu_logical_map(cpu) is the mapping of MPIDR and the logical cpu,
+ * and MPIDR is the cpu hardware ID we needed to pack.
+ */
+#define cpu_physical_id(cpu) pack_mpidr_into_32_bits(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_smp_init_cpus(void);
+
+extern int acpi_get_cpu_parked_address(int cpu, u64 *addr);
+
+#else
+
+static inline bool acpi_psci_present(void) { return false; }
+static inline bool acpi_psci_use_hvc(void) { return false; }
+static inline void acpi_smp_init_cpus(void) { }
+static inline int acpi_get_cpu_parked_address(int cpu, u64 *addr) { return -EOPNOTSUPP; }
+
+#endif /* CONFIG_ACPI */
+
+#endif /*_ASM_ACPI_H*/
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h
index d7b4b38..d149580 100644
--- a/arch/arm64/include/asm/cpu_ops.h
+++ b/arch/arm64/include/asm/cpu_ops.h
@@ -61,6 +61,7 @@ struct cpu_operations {
};
extern const struct cpu_operations *cpu_ops[NR_CPUS];
+const struct cpu_operations *cpu_get_ops(const char *name);
extern int __init cpu_read_ops(struct device_node *dn, int cpu);
extern void __init cpu_read_bootcpu_ops(void);
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index 01d3aab..8186df6 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -114,7 +114,8 @@ typedef struct user_fpsimd_state elf_fpregset_t;
*/
#define elf_check_arch(x) ((x)->e_machine == EM_AARCH64)
-#define elf_read_implies_exec(ex,stk) (stk != EXSTACK_DISABLE_X)
+#define elf_read_implies_exec(ex,stk) (test_thread_flag(TIF_32BIT) \
+ ? (stk == EXSTACK_ENABLE_X) : 0)
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE PAGE_SIZE
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index e0ecdcf..f998d90 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -121,7 +121,8 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
/*
* I/O port access primitives.
*/
-#define IO_SPACE_LIMIT 0xffff
+#define arch_has_dev_port() (1)
+#define IO_SPACE_LIMIT (SZ_32M - 1)
#define PCI_IOBASE ((void __iomem *)(MODULES_VADDR - SZ_32M))
static inline u8 inb(unsigned long addr)
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index cc83520..ff4a4fa 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -95,7 +95,6 @@
/* TCR_EL2 Registers bits */
#define TCR_EL2_TBI (1 << 20)
#define TCR_EL2_PS (7 << 16)
-#define TCR_EL2_PS_40B (2 << 16)
#define TCR_EL2_TG0 (1 << 14)
#define TCR_EL2_SH0 (3 << 12)
#define TCR_EL2_ORGN0 (3 << 10)
@@ -104,8 +103,6 @@
#define TCR_EL2_MASK (TCR_EL2_TG0 | TCR_EL2_SH0 | \
TCR_EL2_ORGN0 | TCR_EL2_IRGN0 | TCR_EL2_T0SZ)
-#define TCR_EL2_FLAGS (TCR_EL2_PS_40B)
-
/* VTCR_EL2 Registers bits */
#define VTCR_EL2_PS_MASK (7 << 16)
#define VTCR_EL2_TG0_MASK (1 << 14)
@@ -120,36 +117,28 @@
#define VTCR_EL2_SL0_MASK (3 << 6)
#define VTCR_EL2_SL0_LVL1 (1 << 6)
#define VTCR_EL2_T0SZ_MASK 0x3f
-#define VTCR_EL2_T0SZ_40B 24
+#define VTCR_EL2_T0SZ(bits) (64 - (bits))
#ifdef CONFIG_ARM64_64K_PAGES
/*
* Stage2 translation configuration:
- * 40bits output (PS = 2)
- * 40bits input (T0SZ = 24)
* 64kB pages (TG0 = 1)
* 2 level page tables (SL = 1)
*/
#define VTCR_EL2_FLAGS (VTCR_EL2_TG0_64K | VTCR_EL2_SH0_INNER | \
VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \
- VTCR_EL2_SL0_LVL1 | VTCR_EL2_T0SZ_40B)
-#define VTTBR_X (38 - VTCR_EL2_T0SZ_40B)
+ VTCR_EL2_SL0_LVL1)
#else
/*
* Stage2 translation configuration:
- * 40bits output (PS = 2)
- * 40bits input (T0SZ = 24)
* 4kB pages (TG0 = 0)
* 3 level page tables (SL = 1)
*/
#define VTCR_EL2_FLAGS (VTCR_EL2_TG0_4K | VTCR_EL2_SH0_INNER | \
VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \
- VTCR_EL2_SL0_LVL1 | VTCR_EL2_T0SZ_40B)
-#define VTTBR_X (37 - VTCR_EL2_T0SZ_40B)
+ VTCR_EL2_SL0_LVL1)
#endif
-#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
-#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
#define VTTBR_VMID_SHIFT (48LLU)
#define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT)
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 8e138c7..1c70b2f 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -167,5 +167,80 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
void stage2_flush_vm(struct kvm *kvm);
+/*
+ * ARMv8 64K architecture limitations:
+ * 16 <= T0SZ <= 21 is valid under 3 level of translation tables
+ * 18 <= T0SZ <= 34 is valid under 2 level of translation tables
+ * 31 <= T0SZ <= 39 is valid under 1 level of transltaion tables
+ *
+ * ARMv8 4K architecture limitations:
+ * 16 <= T0SZ <= 24 is valid under 4 level of translation tables
+ * 21 <= T0SZ <= 33 is valid under 3 level of translation tables
+ * 30 <= T0SZ <= 39 is valid under 2 level of translation tables
+ *
+ * For 4K pages we only support 3 or 4 level, giving T0SZ a range of 16 to 33.
+ * For 64K pages we only support 2 or 3 level, giving T0SZ a range of 16 to 34.
+ *
+ * See Table D4-23 and Table D4-25 in ARM DDI 0487A.b to figure out
+ * the origin of the hardcoded values, 38 and 37.
+ */
+
+#ifdef CONFIG_ARM64_64K_PAGES
+static inline int t0sz_to_vttbr_x(int t0sz)
+{
+ if (t0sz < 16 || t0sz > 34) {
+ kvm_err("Cannot support %d-bit address space\n", 64 - t0sz);
+ return -EINVAL;
+ }
+
+ return 38 - t0sz;
+}
+#else /* 4K pages */
+static inline int t0sz_to_vttbr_x(int t0sz)
+{
+ if (t0sz < 16 || t0sz > 33) {
+ kvm_err("Cannot support %d-bit address space\n", 64 - t0sz);
+ return -EINVAL;
+ }
+ return 37 - t0sz;
+}
+#endif
+static inline int kvm_get_phys_addr_shift(void)
+{
+ int pa_range = read_cpuid(ID_AA64MMFR0_EL1) & 0xf;
+
+ switch (pa_range) {
+ case 0: return 32;
+ case 1: return 36;
+ case 2: return 40;
+ case 3: return 42;
+ case 4: return 44;
+ case 5: return 48;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+/**
+ * get_vttbr_baddr_mask - get mask value for vttbr base address
+ *
+ * In ARMv8, vttbr_baddr_mask cannot be determined in compile time since the
+ * stage2 input address size depends on hardware capability. Thus, we first
+ * need to read ID_AA64MMFR0_EL1.PARange and then set vttbr_baddr_mask with
+ * consideration of both the granule size and the level of translation tables.
+ */
+static inline u64 get_vttbr_baddr_mask(void)
+{
+ int t0sz, vttbr_x;
+
+ t0sz = VTCR_EL2_T0SZ(kvm_get_phys_addr_shift());
+ vttbr_x = t0sz_to_vttbr_x(t0sz);
+ if (vttbr_x < 0)
+ return ~0;
+ return GENMASK_ULL(48, (vttbr_x - 1));
+
+}
+
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h
new file mode 100644
index 0000000..872ba93
--- /dev/null
+++ b/arch/arm64/include/asm/pci.h
@@ -0,0 +1,37 @@
+#ifndef __ASM_PCI_H
+#define __ASM_PCI_H
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm-generic/pci-bridge.h>
+#include <asm-generic/pci-dma-compat.h>
+
+#define PCIBIOS_MIN_IO 0x1000
+#define PCIBIOS_MIN_MEM 0
+
+/*
+ * Set to 1 if the kernel should re-assign all PCI bus numbers
+ */
+#define pcibios_assign_all_busses() \
+ (pci_has_flag(PCI_REASSIGN_ALL_BUS))
+
+/*
+ * PCI address space differs from physical memory address space
+ */
+#define PCI_DMA_BUS_IS_PHYS (0)
+
+extern int isa_dma_bridge_buggy;
+
+#ifdef CONFIG_PCI
+static inline int pci_proc_domain(struct pci_bus *bus)
+{
+ return 1;
+}
+#endif /* CONFIG_PCI */
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_PCI_H */
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index ffe1ba0..a968523 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -296,6 +296,8 @@ static inline int has_transparent_hugepage(void)
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)
#define pgprot_writecombine(prot) \
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
+#define pgprot_device(prot) \
+ __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN)
#define __HAVE_PHYS_MEM_ACCESS_PROT
struct file;
extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h
index e5312ea..2454bc5 100644
--- a/arch/arm64/include/asm/psci.h
+++ b/arch/arm64/include/asm/psci.h
@@ -14,6 +14,7 @@
#ifndef __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 */
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index a498f2c..2ebbd55 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -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);
/*
- * 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.
@@ -49,6 +50,11 @@ extern void smp_init_cpus(void);
extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int));
/*
+ * Provide a function to signal a parked secondary CPU.
+ */
+extern void set_smp_boot_wakeup_call(void (*)(int cpu));
+
+/*
* Called from the secondary holding pen, this is the secondary CPU entry point.
*/
asmlinkage void secondary_start_kernel(void);
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index df7ef87..b0bad2e 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -21,7 +21,8 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
sys_compat.o
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
-arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o
+arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o \
+ smp_parking_protocol.o
arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
@@ -29,6 +30,8 @@ arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
arm64-obj-$(CONFIG_KGDB) += kgdb.o
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
+arm64-obj-$(CONFIG_PCI) += pci.o
+arm64-obj-$(CONFIG_ACPI) += acpi.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
new file mode 100644
index 0000000..5486426
--- /dev/null
+++ b/arch/arm64/kernel/acpi.c
@@ -0,0 +1,397 @@
+/*
+ * 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/init.h>
+#include <linux/acpi.h>
+#include <linux/cpumask.h>
+#include <linux/memblock.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/bootmem.h>
+#include <linux/smp.h>
+#include <linux/irqchip/arm-gic-acpi.h>
+
+#include <asm/cputype.h>
+#include <asm/cpu_ops.h>
+
+#define ARM64_ACPI_DISABLED_DEFAULT 1
+
+int acpi_noirq; /* skip ACPI IRQ initialization */
+int acpi_disabled = ARM64_ACPI_DISABLED_DEFAULT;
+EXPORT_SYMBOL(acpi_disabled);
+
+int acpi_pci_disabled; /* skip ACPI PCI scan and IRQ initialization */
+EXPORT_SYMBOL(acpi_pci_disabled);
+
+static int enabled_cpus; /* Processors (GICC) with enabled flag in MADT */
+
+static char *boot_method;
+static u64 parked_address[NR_CPUS];
+
+/*
+ * Since we're on ARM, the default interrupt routing model
+ * clearly has to be GIC.
+ */
+enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_GIC;
+
+/*
+ * __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 (!phys || !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
+ * @mpidr: CPU's hardware id to register, MPIDR represented in MADT
+ * @enabled: this cpu is enabled or not
+ *
+ * Returns the logical cpu number which maps to MPIDR
+ */
+static int acpi_map_gic_cpu_interface(u64 mpidr, u64 parked_addr, u8 enabled)
+{
+ int cpu;
+
+ if (mpidr == INVALID_HWID) {
+ pr_info("Skip invalid cpu hardware ID\n");
+ return -EINVAL;
+ }
+
+ total_cpus++;
+ if (!enabled)
+ return -EINVAL;
+
+ 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 -EINVAL;
+ }
+
+ /* No need to check duplicate MPIDRs for the first CPU */
+ if (enabled_cpus) {
+ /*
+ * Duplicate MPIDRs are a recipe for disaster. Scan
+ * all initialized entries and check for
+ * duplicates. If any is found just ignore the CPU.
+ */
+ for_each_possible_cpu(cpu) {
+ if (cpu_logical_map(cpu) == mpidr) {
+ pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n",
+ mpidr);
+ return -EINVAL;
+ }
+ }
+
+ /* allocate a logical cpu id for the new comer */
+ cpu = cpumask_next_zero(-1, cpu_possible_mask);
+ } else {
+ /* First GICC entry must be BSP as ACPI spec said */
+ if (cpu_logical_map(0) != mpidr) {
+ pr_err("First GICC entry with MPIDR 0x%llx is not BSP\n",
+ mpidr);
+ return -EINVAL;
+ }
+
+ /*
+ * boot_cpu_init() already hold bit 0 in cpu_present_mask
+ * for BSP, no need to allocate again.
+ */
+ cpu = 0;
+ }
+
+ parked_address[cpu] = parked_addr;
+
+ /* CPU 0 was already initialized */
+ if (cpu) {
+ cpu_ops[cpu] = cpu_get_ops(boot_method);
+ if (!cpu_ops[cpu])
+ return -EINVAL;
+
+ if (cpu_ops[cpu]->cpu_init(NULL, cpu))
+ return -EOPNOTSUPP;
+
+ /* map the logical cpu id to cpu MPIDR */
+ cpu_logical_map(cpu) = mpidr;
+
+ set_cpu_possible(cpu, true);
+ } else {
+ /* get cpu0's ops, no need to return if ops is null */
+ cpu_ops[0] = cpu_get_ops(boot_method);
+ }
+
+ enabled_cpus++;
+ return cpu;
+}
+
+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->arm_mpidr & MPIDR_HWID_BITMASK,
+ processor->parked_address, processor->flags & ACPI_MADT_ENABLED);
+
+ return 0;
+}
+
+/* Parse GIC cpu interface entries in MADT for SMP init */
+void __init acpi_smp_init_cpus(void)
+{
+ int count;
+
+ /*
+ * 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;
+ }
+
+ /* Make boot-up look pretty */
+ pr_info("%d CPUs enabled, %d CPUs total\n", enabled_cpus, total_cpus);
+}
+
+int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
+{
+ *irq = irq_find_mapping(NULL, gsi);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
+
+/*
+ * success: return IRQ number (>0)
+ * failure: return =< 0
+ */
+int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
+{
+ unsigned int irq;
+ unsigned int irq_type;
+
+ /*
+ * ACPI have no bindings to indicate SPI or PPI, so we
+ * use different mappings from DT in ACPI.
+ *
+ * For FDT
+ * PPI interrupt: in the range [0, 15];
+ * SPI interrupt: in the range [0, 987];
+ *
+ * For ACPI, GSI should be unique so using
+ * the hwirq directly for the mapping:
+ * PPI interrupt: in the range [16, 31];
+ * SPI interrupt: in the range [32, 1019];
+ */
+
+ if (trigger == ACPI_EDGE_SENSITIVE &&
+ polarity == ACPI_ACTIVE_LOW)
+ irq_type = IRQ_TYPE_EDGE_FALLING;
+ else if (trigger == ACPI_EDGE_SENSITIVE &&
+ polarity == ACPI_ACTIVE_HIGH)
+ irq_type = IRQ_TYPE_EDGE_RISING;
+ else if (trigger == ACPI_LEVEL_SENSITIVE &&
+ polarity == ACPI_ACTIVE_LOW)
+ irq_type = IRQ_TYPE_LEVEL_LOW;
+ else if (trigger == ACPI_LEVEL_SENSITIVE &&
+ polarity == ACPI_ACTIVE_HIGH)
+ irq_type = IRQ_TYPE_LEVEL_HIGH;
+ else
+ irq_type = IRQ_TYPE_NONE;
+
+ /*
+ * Since only one GIC is supported in ACPI 5.0, we can
+ * create mapping refer to the default domain
+ */
+ irq = irq_create_mapping(NULL, gsi);
+ if (!irq)
+ return irq;
+
+ /* 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);
+
+void acpi_unregister_gsi(u32 gsi)
+{
+}
+EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
+
+static int __init acpi_parse_fadt(struct acpi_table_header *table)
+{
+ struct acpi_table_fadt *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 higher revision
+ * to get arm boot flags, or we will disable ACPI.
+ */
+ if (table->revision > 5 ||
+ (table->revision == 5 && fadt->minor_revision >= 1)) {
+ /*
+ * ACPI 5.1 only has two explicit methods to boot up SMP,
+ * PSCI and Parking protocol, but the Parking protocol is
+ * only specified for ARMv7 now, so make PSCI as the only
+ * way for the SMP boot protocol before some updates for
+ * the ACPI spec or the Parking protocol spec.
+ */
+ if (acpi_psci_present())
+ boot_method = "psci";
+ else if (IS_ENABLED(CONFIG_ARM_PARKING_PROTOCOL))
+ boot_method = "parking-protocol";
+
+ if (!boot_method)
+ pr_warn("has no boot support, will not bring up secondary CPUs\n");
+ return -EOPNOTSUPP;
+ }
+
+ pr_warn("Unsupported FADT revision %d.%d, should be 5.1+, will disable ACPI\n",
+ table->revision, fadt->minor_revision);
+ disable_acpi();
+
+ return -EINVAL;
+}
+
+/*
+ * 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 revisoin
+ *
+ * We can parse ACPI boot-time tables such as MADT after
+ * this function is called.
+ */
+void __init acpi_boot_table_init(void)
+{
+ /* If acpi_disabled, bail out */
+ if (acpi_disabled)
+ return;
+
+ /* Initialize the ACPI boot-time table parser. */
+ if (acpi_table_init()) {
+ disable_acpi();
+ return;
+ }
+
+ if (acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt))
+ pr_err("Can't find FADT or error happened during parsing FADT\n");
+}
+
+void __init acpi_gic_init(void)
+{
+ struct acpi_table_header *table;
+ acpi_status status;
+ acpi_size tbl_size;
+ int err;
+
+ 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);
+}
+
+/*
+ * Parked Address in ACPI GIC structure will be used as the CPU
+ * release address
+ */
+int acpi_get_cpu_parked_address(int cpu, u64 *addr)
+{
+ if (!addr || !parked_address[cpu])
+ return -EINVAL;
+
+ *addr = parked_address[cpu];
+
+ return 0;
+}
+
+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)
+ acpi_disabled = 1;
+ else if (strcmp(arg, "on") == 0)
+ acpi_disabled = 0;
+ else
+ return -EINVAL; /* Core will print when we return error */
+
+ return 0;
+}
+early_param("acpi", parse_acpi);
+
+int acpi_isa_irq_to_gsi(unsigned isa_irq, u32 *gsi)
+{
+ return -1;
+}
+
+int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base)
+{
+ /* TBD */
+ return -EINVAL;
+}
+EXPORT_SYMBOL(acpi_register_ioapic);
+
+int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base)
+{
+ /* TBD */
+ return -EINVAL;
+}
+EXPORT_SYMBOL(acpi_unregister_ioapic);
+
diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c
index cce9524..1d90f31 100644
--- a/arch/arm64/kernel/cpu_ops.c
+++ b/arch/arm64/kernel/cpu_ops.c
@@ -23,19 +23,23 @@
#include <linux/string.h>
extern const struct cpu_operations smp_spin_table_ops;
+extern const struct cpu_operations smp_parking_protocol_ops;
extern const struct cpu_operations cpu_psci_ops;
const struct cpu_operations *cpu_ops[NR_CPUS];
-static const struct cpu_operations *supported_cpu_ops[] __initconst = {
+static const struct cpu_operations *supported_cpu_ops[] = {
#ifdef CONFIG_SMP
&smp_spin_table_ops,
+#ifdef CONFIG_ARM_PARKING_PROTOCOL
+ &smp_parking_protocol_ops,
+#endif
#endif
&cpu_psci_ops,
NULL,
};
-static const struct cpu_operations * __init cpu_get_ops(const char *name)
+const struct cpu_operations *cpu_get_ops(const char *name)
{
const struct cpu_operations **ops = supported_cpu_ops;
diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c
index 1317fef..d27dd98 100644
--- a/arch/arm64/kernel/efi-stub.c
+++ b/arch/arm64/kernel/efi-stub.c
@@ -28,20 +28,16 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
kernel_size = _edata - _text;
if (*image_addr != (dram_base + TEXT_OFFSET)) {
kernel_memsize = kernel_size + (_end - _edata);
- status = efi_relocate_kernel(sys_table, image_addr,
- kernel_size, kernel_memsize,
- dram_base + TEXT_OFFSET,
- PAGE_SIZE);
+ status = efi_low_alloc(sys_table, kernel_memsize + TEXT_OFFSET,
+ SZ_2M, reserve_addr);
if (status != EFI_SUCCESS) {
pr_efi_err(sys_table, "Failed to relocate kernel\n");
return status;
}
- if (*image_addr != (dram_base + TEXT_OFFSET)) {
- pr_efi_err(sys_table, "Failed to alloc kernel memory\n");
- efi_free(sys_table, kernel_memsize, *image_addr);
- return EFI_LOAD_ERROR;
- }
- *image_size = kernel_memsize;
+ memcpy((void *)*reserve_addr + TEXT_OFFSET, (void *)*image_addr,
+ kernel_size);
+ *image_addr = *reserve_addr + TEXT_OFFSET;
+ *reserve_size = kernel_memsize + TEXT_OFFSET;
}
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index 03aaa99..6c4de44 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -479,3 +479,14 @@ err_unmap:
return -1;
}
early_initcall(arm64_enter_virtual_mode);
+
+/*
+ * If nothing else is handling pm_power_off, use EFI
+ *
+ * This is called from a late_initcall after other mechanisms
+ * have had a chance to register a handler.
+ */
+bool efi_poweroff_required(void)
+{
+ return pm_power_off == NULL;
+}
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 8730690..0a6e4f9 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -151,7 +151,7 @@ optional_header:
.short 0x20b // PE32+ format
.byte 0x02 // MajorLinkerVersion
.byte 0x14 // MinorLinkerVersion
- .long _edata - stext // SizeOfCode
+ .long _end - stext // SizeOfCode
.long 0 // SizeOfInitializedData
.long 0 // SizeOfUninitializedData
.long efi_stub_entry - efi_head // AddressOfEntryPoint
@@ -169,7 +169,7 @@ extra_header_fields:
.short 0 // MinorSubsystemVersion
.long 0 // Win32VersionValue
- .long _edata - efi_head // SizeOfImage
+ .long _end - efi_head // SizeOfImage
// Everything before the kernel image is considered part of the header
.long stext - efi_head // SizeOfHeaders
@@ -216,7 +216,7 @@ section_table:
.byte 0
.byte 0
.byte 0 // end of 0 padding of section name
- .long _edata - stext // VirtualSize
+ .long _end - stext // VirtualSize
.long stext - efi_head // VirtualAddress
.long _edata - stext // SizeOfRawData
.long stext - efi_head // PointerToRawData
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
new file mode 100644
index 0000000..ce5836c
--- /dev/null
+++ b/arch/arm64/kernel/pci.c
@@ -0,0 +1,70 @@
+/*
+ * Code borrowed from powerpc/kernel/pci-common.c
+ *
+ * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * 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/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+
+#include <asm/pci-bridge.h>
+
+/*
+ * Called after each bus is probed, but before its children are examined
+ */
+void pcibios_fixup_bus(struct pci_bus *bus)
+{
+ /* nothing to do, expected to be removed in the future */
+}
+
+/*
+ * We don't have to worry about legacy ISA devices, so nothing to do here
+ */
+resource_size_t pcibios_align_resource(void *data, const struct resource *res,
+ resource_size_t size, resource_size_t align)
+{
+ return res->start;
+}
+
+/*
+ * Try to assign the IRQ number from DT when adding a new device
+ */
+int pcibios_add_device(struct pci_dev *dev)
+{
+ dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+static bool dt_domain_found = false;
+
+void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
+{
+ int domain = of_get_pci_domain_nr(parent->of_node);
+
+ if (domain >= 0) {
+ dt_domain_found = true;
+ } else if (dt_domain_found == true) {
+ dev_err(parent, "Node %s is missing \"linux,pci-domain\" property in DT\n",
+ parent->of_node->full_name);
+ return;
+ } else {
+ domain = pci_get_new_domain_nr();
+ }
+
+ bus->domain_nr = domain;
+}
+#endif
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 29d4869..c0427bc 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -43,6 +43,7 @@
#include <linux/hw_breakpoint.h>
#include <linux/personality.h>
#include <linux/notifier.h>
+#include <linux/efi.h>
#include <asm/compat.h>
#include <asm/cacheflush.h>
@@ -182,6 +183,11 @@ void machine_restart(char *cmd)
arm_pm_restart(reboot_mode, cmd);
/*
+ * If all else fails, try EFI
+ */
+ efi_reboot(reboot_mode, cmd);
+
+ /*
* Whoops - the architecture was unable to reboot.
*/
printk("Reboot failed -- System halted\n");
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index 5539547..15ba470 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -15,6 +15,7 @@
#define pr_fmt(fmt) "psci: " fmt
+#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/smp.h>
@@ -23,6 +24,7 @@
#include <linux/delay.h>
#include <uapi/linux/psci.h>
+#include <asm/acpi.h>
#include <asm/compiler.h>
#include <asm/cpu_ops.h>
#include <asm/errno.h>
@@ -231,6 +233,33 @@ static void psci_sys_poweroff(void)
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
}
+static void psci_0_2_set_functions(void)
+{
+ pr_info("Using standard PSCI v0.2 function IDs\n");
+ psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;
+ psci_ops.cpu_suspend = psci_cpu_suspend;
+
+ psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
+ psci_ops.cpu_off = psci_cpu_off;
+
+ psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON;
+ psci_ops.cpu_on = psci_cpu_on;
+
+ psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;
+ psci_ops.migrate = psci_migrate;
+
+ psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO;
+ psci_ops.affinity_info = psci_affinity_info;
+
+ psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
+ PSCI_0_2_FN_MIGRATE_INFO_TYPE;
+ psci_ops.migrate_info_type = psci_migrate_info_type;
+
+ arm_pm_restart = psci_sys_reset;
+
+ pm_power_off = psci_sys_poweroff;
+}
+
/*
* PSCI Function IDs for v0.2+ are well defined so use
* standard values.
@@ -264,29 +293,7 @@ static int __init psci_0_2_init(struct device_node *np)
}
}
- pr_info("Using standard PSCI v0.2 function IDs\n");
- psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;
- psci_ops.cpu_suspend = psci_cpu_suspend;
-
- psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
- psci_ops.cpu_off = psci_cpu_off;
-
- psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON;
- psci_ops.cpu_on = psci_cpu_on;
-
- psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;
- psci_ops.migrate = psci_migrate;
-
- psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO;
- psci_ops.affinity_info = psci_affinity_info;
-
- psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
- PSCI_0_2_FN_MIGRATE_INFO_TYPE;
- psci_ops.migrate_info_type = psci_migrate_info_type;
-
- arm_pm_restart = psci_sys_reset;
-
- pm_power_off = psci_sys_poweroff;
+ psci_0_2_set_functions();
out_put_node:
of_node_put(np);
@@ -339,7 +346,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;
const struct of_device_id *matched_np;
@@ -354,6 +361,29 @@ int __init psci_init(void)
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;
+
+ psci_0_2_set_functions();
+
+ return 0;
+}
+
#ifdef CONFIG_SMP
static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu)
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index edb146d..a793a20 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -43,6 +43,7 @@
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <linux/efi.h>
+#include <linux/acpi.h>
#include <asm/fixmap.h>
#include <asm/cpu.h>
@@ -59,6 +60,10 @@
#include <asm/memblock.h>
#include <asm/psci.h>
#include <asm/efi.h>
+#include <asm/acpi.h>
+
+int acadia_kvm_acpi=0;
+EXPORT_SYMBOL(acadia_kvm_acpi);
unsigned int processor_id;
EXPORT_SYMBOL(processor_id);
@@ -385,22 +390,34 @@ void __init setup_arch(char **cmdline_p)
parse_early_param();
+ if (acpi_disabled)
+ disable_acpi();
+
efi_init();
arm64_memblock_init();
+ /* Parse the ACPI tables for possible boot-time configuration */
+ acpi_boot_table_init();
+
paging_init();
request_standard_resources();
efi_idmap_init();
- unflatten_device_tree();
-
- psci_init();
-
cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
- cpu_read_bootcpu_ops();
+ if (acpi_disabled) {
+ unflatten_device_tree();
+ psci_dt_init();
+ cpu_read_bootcpu_ops();
+#ifdef CONFIG_SMP
+ of_smp_init_cpus();
+#endif
+ } else {
+ psci_acpi_init();
+ acpi_smp_init_cpus();
+ }
+
#ifdef CONFIG_SMP
- smp_init_cpus();
smp_build_mpidr_hash();
#endif
@@ -413,6 +430,19 @@ void __init setup_arch(char **cmdline_p)
#endif
}
+static int __init parse_kvm_acpi(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+
+ if (strcmp(arg, "on") == 0) {
+ acadia_kvm_acpi = 1;
+ }
+
+ return 0;
+}
+early_param("kvmacpi", parse_kvm_acpi);
+
static int __init arm64_device_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
@@ -505,3 +535,25 @@ const struct seq_operations cpuinfo_op = {
.stop = c_stop,
.show = c_show
};
+
+/*
+ * Temporary hack to avoid need for console= on command line
+ */
+static int __init arm64_console_setup(void)
+{
+ /* Allow cmdline to override our assumed preferences */
+ if (console_set_on_cmdline)
+ return 0;
+
+ if (IS_ENABLED(CONFIG_SBSAUART_TTY))
+ add_preferred_console("ttySBSA", 0, "115200");
+
+ if (IS_ENABLED(CONFIG_SERIAL_AMBA_PL011))
+ add_preferred_console("ttyAMA", 0, "115200");
+
+ if (IS_ENABLED(CONFIG_SERIAL_8250))
+ add_preferred_console("ttyS", 0, "115200");
+
+ return 0;
+}
+early_initcall(arm64_console_setup);
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 4743397..4e390ac 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -321,7 +321,7 @@ void __init smp_prepare_boot_cpu(void)
* cpu logical map array containing MPIDR values related to logical
* 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;
unsigned int i, cpu = 1;
diff --git a/arch/arm64/kernel/smp_parking_protocol.c b/arch/arm64/kernel/smp_parking_protocol.c
new file mode 100644
index 0000000..e1153ce
--- /dev/null
+++ b/arch/arm64/kernel/smp_parking_protocol.c
@@ -0,0 +1,110 @@
+/*
+ * Parking Protocol SMP initialisation
+ *
+ * Based largely on spin-table method.
+ *
+ * Copyright (C) 2013 ARM Ltd.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+#include <linux/acpi.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cpu_ops.h>
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
+
+static phys_addr_t cpu_mailbox_addr[NR_CPUS];
+
+static void (*__smp_boot_wakeup)(int cpu);
+
+void set_smp_boot_wakeup_call(void (*fn)(int cpu))
+{
+ __smp_boot_wakeup = fn;
+}
+
+static int smp_parking_protocol_cpu_init(struct device_node *dn,
+ unsigned int cpu)
+{
+ /*
+ * Determine the mailbox address.
+ */
+ if (!acpi_get_cpu_parked_address(cpu, &cpu_mailbox_addr[cpu])) {
+ pr_info("%s: ACPI parked addr=%llx\n",
+ __func__, cpu_mailbox_addr[cpu]);
+ return 0;
+ }
+
+ pr_err("CPU %d: missing or invalid parking protocol mailbox\n", cpu);
+
+ return -1;
+}
+
+static int smp_parking_protocol_cpu_prepare(unsigned int cpu)
+{
+ return 0;
+}
+
+struct parking_protocol_mailbox {
+ __le32 cpu_id;
+ __le32 reserved;
+ __le64 entry_point;
+};
+
+static int smp_parking_protocol_cpu_boot(unsigned int cpu)
+{
+ struct parking_protocol_mailbox __iomem *mailbox;
+
+ if (!cpu_mailbox_addr[cpu] || !__smp_boot_wakeup)
+ return -ENODEV;
+
+ /*
+ * The mailbox may or may not be inside the linear mapping.
+ * As ioremap_cache will either give us a new mapping or reuse the
+ * existing linear mapping, we can use it to cover both cases. In
+ * either case the memory will be MT_NORMAL.
+ */
+ mailbox = ioremap_cache(cpu_mailbox_addr[cpu], sizeof(*mailbox));
+ if (!mailbox)
+ return -ENOMEM;
+
+ /*
+ * We write the entry point and cpu id as LE regardless of the
+ * native endianess of the kernel. Therefore, any boot-loaders
+ * that read this address need to convert this address to the
+ * Boot-Loader's endianess before jumping.
+ */
+ writeq(__pa(secondary_entry), &mailbox->entry_point);
+ writel(cpu, &mailbox->cpu_id);
+ __flush_dcache_area(mailbox, sizeof(*mailbox));
+ __smp_boot_wakeup(cpu);
+
+ /* temp hack for broken firmware */
+ sev();
+
+ iounmap(mailbox);
+
+ return 0;
+}
+
+const struct cpu_operations smp_parking_protocol_ops = {
+ .name = "parking-protocol",
+ .cpu_init = smp_parking_protocol_cpu_init,
+ .cpu_prepare = smp_parking_protocol_cpu_prepare,
+ .cpu_boot = smp_parking_protocol_cpu_boot,
+};
diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c
index 0347d38..4f93c67 100644
--- a/arch/arm64/kernel/smp_spin_table.c
+++ b/arch/arm64/kernel/smp_spin_table.c
@@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/of.h>
#include <linux/smp.h>
+#include <linux/types.h>
#include <asm/cacheflush.h>
#include <asm/cpu_ops.h>
@@ -65,12 +66,21 @@ static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu)
static int smp_spin_table_cpu_prepare(unsigned int cpu)
{
- void **release_addr;
+ __le64 __iomem *release_addr;
if (!cpu_release_addr[cpu])
return -ENODEV;
- release_addr = __va(cpu_release_addr[cpu]);
+ /*
+ * The cpu-release-addr may or may not be inside the linear mapping.
+ * As ioremap_cache will either give us a new mapping or reuse the
+ * existing linear mapping, we can use it to cover both cases. In
+ * either case the memory will be MT_NORMAL.
+ */
+ release_addr = ioremap_cache(cpu_release_addr[cpu],
+ sizeof(*release_addr));
+ if (!release_addr)
+ return -ENOMEM;
/*
* We write the release address as LE regardless of the native
@@ -79,15 +89,17 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu)
* boot-loader's endianess before jumping. This is mandated by
* the boot protocol.
*/
- release_addr[0] = (void *) cpu_to_le64(__pa(secondary_holding_pen));
-
- __flush_dcache_area(release_addr, sizeof(release_addr[0]));
+ writeq_relaxed(__pa(secondary_holding_pen), release_addr);
+ __flush_dcache_area((__force void *)release_addr,
+ sizeof(*release_addr));
/*
* Send an event to wake up the secondary CPU.
*/
sev();
+ iounmap(release_addr);
+
return 0;
}
diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
index 1a7125c..42f9195 100644
--- a/arch/arm64/kernel/time.c
+++ b/arch/arm64/kernel/time.c
@@ -35,6 +35,7 @@
#include <linux/delay.h>
#include <linux/clocksource.h>
#include <linux/clk-provider.h>
+#include <linux/acpi.h>
#include <clocksource/arm_arch_timer.h>
@@ -72,6 +73,12 @@ void __init time_init(void)
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();
if (!arch_timer_rate)
panic("Unable to initialise architected timer.\n");
diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
index c319116..fa7e67e 100644
--- a/arch/arm64/kvm/hyp-init.S
+++ b/arch/arm64/kvm/hyp-init.S
@@ -63,17 +63,21 @@ __do_hyp_init:
mrs x4, tcr_el1
ldr x5, =TCR_EL2_MASK
and x4, x4, x5
- ldr x5, =TCR_EL2_FLAGS
- orr x4, x4, x5
- msr tcr_el2, x4
-
- ldr x4, =VTCR_EL2_FLAGS
/*
* Read the PARange bits from ID_AA64MMFR0_EL1 and set the PS bits in
- * VTCR_EL2.
+ * TCR_EL2 and both PS bits and T0SZ bits in VTCR_EL2.
*/
mrs x5, ID_AA64MMFR0_EL1
bfi x4, x5, #16, #3
+ msr tcr_el2, x4
+
+ ldr x4, =VTCR_EL2_FLAGS
+ bfi x4, x5, #16, #3
+ and x5, x5, #0xf
+ adr x6, t0sz
+ add x6, x6, x5, lsl #2
+ ldr w5, [x6]
+ orr x4, x4, x5
msr vtcr_el2, x4
mrs x4, mair_el1
@@ -113,6 +117,10 @@ target: /* We're now in the trampoline code, switch page tables */
/* Hello, World! */
eret
+
+t0sz:
+ .word VTCR_EL2_T0SZ(32), VTCR_EL2_T0SZ(36), VTCR_EL2_T0SZ(40)
+ .word VTCR_EL2_T0SZ(42), VTCR_EL2_T0SZ(44), VTCR_EL2_T0SZ(48)
ENDPROC(__kvm_hyp_init)
.ltorg
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 4164c5a..b864a24 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -23,10 +23,13 @@
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/vmalloc.h>
#include <linux/swiotlb.h>
#include <linux/amba/bus.h>
+#include <linux/acpi.h>
+#include <linux/pci.h>
#include <asm/cacheflush.h>
@@ -319,6 +322,63 @@ static int dma_bus_notifier(struct notifier_block *nb,
if (of_property_read_bool(dev->of_node, "dma-coherent"))
set_dma_ops(dev, &coherent_swiotlb_dma_ops);
+#ifdef CONFIG_ACPI
+ else if (ACPI_HANDLE(dev)) {
+ acpi_status status;
+ int coherent;
+
+ /*
+ * Kernel defaults to noncoherent ops but ACPI 5.1 spec says arm64
+ * defaults to coherent. Set coherent ops if _CCA not found or _CCA
+ * found and non-zero.
+ */
+ status = acpi_check_coherency(ACPI_HANDLE(dev), &coherent);
+ if (ACPI_FAILURE(status) || coherent)
+ set_dma_ops(dev, &coherent_swiotlb_dma_ops);
+ }
+#endif
+ return NOTIFY_OK;
+}
+
+static int dma_bus_notifier_pci(struct notifier_block *nb,
+ unsigned long event, void *_dev)
+{
+ struct device *dev = _dev;
+
+ if (event != BUS_NOTIFY_ADD_DEVICE)
+ return NOTIFY_DONE;
+
+ /*
+ * PCI devices won't have an of_node but the bridge will.
+ * Search up the device chain until we find an of_node
+ * to check.
+ */
+ while (dev) {
+ if (dev->of_node) {
+ if (of_dma_is_coherent(dev->of_node))
+ set_dma_ops(_dev, &coherent_swiotlb_dma_ops);
+ break;
+ }
+#ifdef CONFIG_ACPI
+ if (ACPI_HANDLE(dev)) {
+ acpi_status status;
+ int coherent;
+
+ /*
+ * Kernel defaults to noncoherent ops but ACPI 5.1 spec says arm64
+ * defaults to coherent. Set coherent ops if _CCA not found or _CCA
+ * found and non-zero.
+ */
+ status = acpi_check_coherency(ACPI_HANDLE(dev), &coherent);
+ if (ACPI_FAILURE(status) || coherent) {
+ set_dma_ops(dev, &coherent_swiotlb_dma_ops);
+ break;
+ }
+ }
+#endif
+ dev = dev->parent;
+ }
+
return NOTIFY_OK;
}
@@ -330,6 +390,10 @@ static struct notifier_block amba_bus_nb = {
.notifier_call = dma_bus_notifier,
};
+static struct notifier_block pci_bus_nb = {
+ .notifier_call = dma_bus_notifier_pci,
+};
+
extern int swiotlb_late_init_with_default_size(size_t default_size);
static int __init swiotlb_late_init(void)
@@ -341,6 +405,7 @@ static int __init swiotlb_late_init(void)
*/
bus_register_notifier(&platform_bus_type, &platform_bus_nb);
bus_register_notifier(&amba_bustype, &amba_bus_nb);
+ bus_register_notifier(&pci_bus_type, &pci_bus_nb);
dma_ops = &noncoherent_swiotlb_dma_ops;
diff --git a/arch/arm64/pci/Makefile b/arch/arm64/pci/Makefile
new file mode 100644
index 0000000..b8d5dbd
--- /dev/null
+++ b/arch/arm64/pci/Makefile
@@ -0,0 +1 @@
+obj-y += pci.o
diff --git a/arch/arm64/pci/pci.c b/arch/arm64/pci/pci.c
new file mode 100644
index 0000000..b03b0eb
--- /dev/null
+++ b/arch/arm64/pci/pci.c
@@ -0,0 +1,28 @@
+#include <linux/acpi.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+/**
+ * raw_pci_read - Platform-specific PCI config space access.
+ *
+ * Default empty implementation. Replace with an architecture-specific setup
+ * routine, if necessary.
+ */
+int __weak raw_pci_read(unsigned int domain, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 *val)
+{
+ return -EINVAL;
+}
+
+int __weak raw_pci_write(unsigned int domain, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 val)
+{
+ return -EINVAL;
+}
+
+/* Root bridge scanning */
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+{
+ return NULL;
+}
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index d0f3265..3343080 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -5,8 +5,7 @@
menuconfig ACPI
bool "ACPI (Advanced Configuration and Power Interface) Support"
depends on !IA64_HP_SIM
- depends on IA64 || X86
- depends on PCI
+ depends on ((IA64 || X86) && PCI) || ARM64
select PNP
default y
help
@@ -163,6 +162,7 @@ config ACPI_PROCESSOR
tristate "Processor"
select THERMAL
select CPU_IDLE
+ depends on X86 || IA64
default y
help
This driver installs ACPI as the idle handler for Linux and uses
@@ -263,7 +263,7 @@ config ACPI_DEBUG
config ACPI_PCI_SLOT
bool "PCI slot detection driver"
- depends on SYSFS
+ depends on SYSFS && PCI
default n
help
This driver creates entries in /sys/bus/pci/slots/ for all PCI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 505d4d7..6f3a74d 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -23,7 +23,11 @@ acpi-y += nvs.o
# Power management related files
acpi-y += wakeup.o
+ifeq ($(ARCH), arm64)
+acpi-y += sleep-arm.o
+else # X86, IA64
acpi-y += sleep.o
+endif
acpi-y += device_pm.o
acpi-$(CONFIG_ACPI_SLEEP) += proc.o
@@ -39,7 +43,7 @@ acpi-y += processor_core.o
acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o
acpi-y += ec.o
acpi-$(CONFIG_ACPI_DOCK) += dock.o
-acpi-y += pci_root.o pci_link.o pci_irq.o
+acpi-$(CONFIG_PCI) += pci_root.o pci_link.o pci_irq.o
acpi-y += acpi_lpss.o
acpi-y += acpi_platform.o
acpi-y += acpi_pnp.o
diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c
index 14cb6c0..5cd017c 100644
--- a/drivers/acpi/acpica/utresrc.c
+++ b/drivers/acpi/acpica/utresrc.c
@@ -87,7 +87,9 @@ const char *acpi_gbl_io_decode[] = {
const char *acpi_gbl_ll_decode[] = {
"ActiveHigh",
- "ActiveLow"
+ "ActiveLow",
+ "ActiveBoth",
+ "Reserved"
};
const char *acpi_gbl_max_decode[] = {
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 8b67bd0..c412fdb 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -448,6 +448,9 @@ static int __init acpi_bus_init_irq(void)
case ACPI_IRQ_MODEL_IOSAPIC:
message = "IOSAPIC";
break;
+ case ACPI_IRQ_MODEL_GIC:
+ message = "GIC";
+ break;
case ACPI_IRQ_MODEL_PLATFORM:
message = "platform specific model";
break;
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 4c5cf77..e1e6487 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -26,8 +26,13 @@
acpi_status acpi_os_initialize1(void);
int init_acpi_device_notify(void);
int acpi_scan_init(void);
+#ifdef CONFIG_PCI
void acpi_pci_root_init(void);
void acpi_pci_link_init(void);
+#else
+static inline void acpi_pci_root_init(void) {}
+static inline void acpi_pci_link_init(void) {}
+#endif
void acpi_processor_init(void);
void acpi_platform_init(void);
void acpi_pnp_init(void);
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 3abe9b2..c50757b 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -326,11 +326,11 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size)
return NULL;
}
-#ifndef CONFIG_IA64
-#define should_use_kmap(pfn) page_is_ram(pfn)
-#else
+#if defined(CONFIG_IA64) || defined(CONFIG_ARM) || defined(CONFIG_ARM64)
/* ioremap will take care of cache attributes */
#define should_use_kmap(pfn) 0
+#else
+#define should_use_kmap(pfn) page_is_ram(pfn)
#endif
static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz)
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index e32321c..4007313 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -64,6 +64,38 @@ static int map_lsapic_id(struct acpi_subtable_header *entry,
return 0;
}
+/*
+ * On ARM platform, MPIDR value is the hardware ID as apic ID
+ * on Intel platforms
+ */
+static int map_gicc_mpidr(struct acpi_subtable_header *entry,
+ int device_declaration, u32 acpi_id, int *mpidr)
+{
+ struct acpi_madt_generic_interrupt *gicc =
+ container_of(entry, struct acpi_madt_generic_interrupt, header);
+
+ if (!(gicc->flags & ACPI_MADT_ENABLED))
+ return -ENODEV;
+
+ /* 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)) {
+ /*
+ * Only bits [0:7] Aff0, bits [8:15] Aff1, bits [16:23] Aff2
+ * and bits [32:39] Aff3 are meaningful, so pack the Affx
+ * fields into a single 32 bit identifier to accommodate the
+ * acpi processor drivers.
+ */
+ *mpidr = ((gicc->arm_mpidr & 0xff00000000) >> 8)
+ | gicc->arm_mpidr;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
static int map_madt_entry(int type, u32 acpi_id)
{
unsigned long madt_end, entry;
@@ -99,6 +131,9 @@ static int map_madt_entry(int type, u32 acpi_id)
} else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) {
if (!map_lsapic_id(header, type, acpi_id, &apic_id))
break;
+ } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
+ if (!map_gicc_mpidr(header, type, acpi_id, &apic_id))
+ break;
}
entry += header->length;
}
@@ -131,6 +166,8 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
map_lsapic_id(header, type, acpi_id, &apic_id);
} else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) {
map_x2apic_id(header, type, acpi_id, &apic_id);
+ } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
+ map_gicc_mpidr(header, type, acpi_id, &apic_id);
}
exit:
diff --git a/drivers/acpi/sleep-arm.c b/drivers/acpi/sleep-arm.c
new file mode 100644
index 0000000..54578ef
--- /dev/null
+++ b/drivers/acpi/sleep-arm.c
@@ -0,0 +1,28 @@
+/*
+ * ARM64 Specific Sleep Functionality
+ *
+ * Copyright (C) 2013-2014, Linaro Ltd.
+ * 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.
+ */
+
+#include <linux/acpi.h>
+
+/*
+ * Currently the ACPI 5.1 standard does not define S states in a
+ * manner which is usable for ARM64. These two stubs are sufficient
+ * that system initialises and device PM works.
+ */
+u32 acpi_target_system_state(void)
+{
+ return ACPI_STATE_S0;
+}
+EXPORT_SYMBOL_GPL(acpi_target_system_state);
+
+int __init acpi_sleep_init(void)
+{
+ return -ENOSYS;
+}
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index 6d5a6cd..47f36d4 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -183,6 +183,49 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
}
break;
+ case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
+ {
+ struct acpi_madt_generic_interrupt *p =
+ (struct acpi_madt_generic_interrupt *)header;
+ pr_info("GICC (acpi_id[0x%04x] address[%p] MPDIR[0x%llx] %s)\n",
+ p->uid, (void *)(unsigned long)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_info("GIC Distributor (gic_id[0x%04x] address[%p] gsi_base[%d])\n",
+ p->gic_id,
+ (void *)(unsigned long)p->base_address,
+ p->global_irq_base);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_GENERIC_MSI_FRAME:
+ {
+ struct acpi_madt_generic_msi_frame *p =
+ (struct acpi_madt_generic_msi_frame *)header;
+ pr_info("GIC MSI Frame (msi_fame_id[%d] address[%p])\n",
+ p->msi_frame_id,
+ (void *)(unsigned long)p->base_address);
+ }
+ break;
+
+ case ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR:
+ {
+ struct acpi_madt_generic_redistributor *p =
+ (struct acpi_madt_generic_redistributor *)header;
+ pr_info("GIC Redistributor (address[%p] region_size[0x%x])\n",
+ (void *)(unsigned long)p->base_address,
+ p->length);
+ }
+ break;
+
default:
pr_warn("Found unsupported MADT entry (type = 0x%x)\n",
header->type);
@@ -192,17 +235,14 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
int __init
-acpi_table_parse_entries(char *id,
- unsigned long table_size,
- int entry_id,
- acpi_tbl_entry_handler handler,
- unsigned int max_entries)
+acpi_parse_entries(unsigned long table_size,
+ acpi_tbl_entry_handler handler,
+ struct acpi_table_header *table_header,
+ int entry_id, unsigned int max_entries)
{
- struct acpi_table_header *table_header = NULL;
struct acpi_subtable_header *entry;
- unsigned int count = 0;
+ int count = 0;
unsigned long table_end;
- acpi_size tbl_size;
if (acpi_disabled)
return -ENODEV;
@@ -210,13 +250,11 @@ acpi_table_parse_entries(char *id,
if (!handler)
return -EINVAL;
- if (strncmp(id, ACPI_SIG_MADT, 4) == 0)
- acpi_get_table_with_size(id, acpi_apic_instance, &table_header, &tbl_size);
- else
- acpi_get_table_with_size(id, 0, &table_header, &tbl_size);
+ if (!table_size)
+ return -EINVAL;
if (!table_header) {
- pr_warn("%4.4s not present\n", id);
+ pr_warn("Table header not present\n");
return -ENODEV;
}
@@ -230,32 +268,67 @@ acpi_table_parse_entries(char *id,
while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) <
table_end) {
if (entry->type == entry_id
- && (!max_entries || count++ < max_entries))
+ && (!max_entries || count < max_entries)) {
if (handler(entry, table_end))
- goto err;
+ return -EINVAL;
+
+ count++;
+ }
/*
* If entry->length is 0, break from this loop to avoid
* infinite loop.
*/
if (entry->length == 0) {
- pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, entry_id);
- goto err;
+ pr_err("[0x%02x] Invalid zero length\n", entry_id);
+ return -EINVAL;
}
entry = (struct acpi_subtable_header *)
((unsigned long)entry + entry->length);
}
+
if (max_entries && count > max_entries) {
pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n",
- id, entry_id, count - max_entries, count);
+ table_header->signature, entry_id, count - max_entries,
+ count);
}
- early_acpi_os_unmap_memory((char *)table_header, tbl_size);
return count;
-err:
+}
+
+int __init
+acpi_table_parse_entries(char *id,
+ unsigned long table_size,
+ int entry_id,
+ acpi_tbl_entry_handler handler,
+ unsigned int max_entries)
+{
+ struct acpi_table_header *table_header = NULL;
+ acpi_size tbl_size;
+ int count;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ if (!handler)
+ return -EINVAL;
+
+ if (strncmp(id, ACPI_SIG_MADT, 4) == 0)
+ acpi_get_table_with_size(id, acpi_apic_instance, &table_header, &tbl_size);
+ else
+ acpi_get_table_with_size(id, 0, &table_header, &tbl_size);
+
+ if (!table_header) {
+ pr_warn("%4.4s not present\n", id);
+ return -ENODEV;
+ }
+
+ count = acpi_parse_entries(table_size, handler, table_header,
+ entry_id, max_entries);
+
early_acpi_os_unmap_memory((char *)table_header, tbl_size);
- return -EINVAL;
+ return count;
}
int __init
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 07c8c5a..aec9656 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -698,3 +698,29 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs)
return false;
}
EXPORT_SYMBOL(acpi_check_dsm);
+
+/**
+ * acpi_check_coherency - check for memory coherency of a device
+ * @handle: ACPI device handle
+ * @val: Pointer to returned value
+ *
+ * Search a device and its parents for a _CCA method and return
+ * its value.
+ */
+acpi_status acpi_check_coherency(acpi_handle handle, int *val)
+{
+ unsigned long long data;
+ acpi_status status;
+
+ do {
+ status = acpi_evaluate_integer(handle, "_CCA", NULL, &data);
+ if (!ACPI_FAILURE(status)) {
+ *val = data;
+ break;
+ }
+ status = acpi_get_parent(handle, &handle);
+ } while (!ACPI_FAILURE(status));
+
+ return status;
+}
+EXPORT_SYMBOL(acpi_check_coherency);
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index e1b9278..f2e6c9e 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -48,7 +48,7 @@ config ATA_VERBOSE_ERROR
config ATA_ACPI
bool "ATA ACPI Support"
- depends on ACPI && PCI
+ depends on ACPI
default y
help
This option adds support for ATA-related ACPI objects.
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index f61ddb9..3499bab 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -20,6 +20,9 @@
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
+#ifdef CONFIG_ATA_ACPI
+#include <linux/acpi.h>
+#endif
#include "ahci.h"
static const struct ata_port_info ahci_port_info = {
@@ -87,6 +90,13 @@ static const struct of_device_id ahci_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ahci_of_match);
+#ifdef CONFIG_ATA_ACPI
+static const struct acpi_device_id ahci_acpi_match[] = {
+ { "AMDI0600", 0 }, /* AMD Seattle AHCI */
+ { },
+};
+#endif
+
static struct platform_driver ahci_driver = {
.probe = ahci_probe,
.remove = ata_platform_remove_one,
@@ -94,6 +104,9 @@ static struct platform_driver ahci_driver = {
.name = "ahci",
.owner = THIS_MODULE,
.of_match_table = ahci_of_match,
+#ifdef CONFIG_ATA_ACPI
+ .acpi_match_table = ACPI_PTR(ahci_acpi_match),
+#endif
.pm = &ahci_pm_ops,
},
};
diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c
index f03aab1..b02ba9d 100644
--- a/drivers/ata/ahci_xgene.c
+++ b/drivers/ata/ahci_xgene.c
@@ -28,6 +28,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/phy/phy.h>
+#include <linux/acpi.h>
#include "ahci.h"
/* Max # of disk per a controller */
@@ -137,7 +138,8 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc)
struct xgene_ahci_context *ctx = hpriv->plat_data;
int rc = 0;
- if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA))
+ if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA ||
+ ctx->last_cmd[ap->port_no] == ATA_CMD_SMART))
xgene_ahci_restart_engine(ap);
rc = ahci_qc_issue(qc);
@@ -148,14 +150,6 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc)
return rc;
}
-static bool xgene_ahci_is_memram_inited(struct xgene_ahci_context *ctx)
-{
- void __iomem *diagcsr = ctx->csr_diag;
-
- return (readl(diagcsr + CFG_MEM_RAM_SHUTDOWN) == 0 &&
- readl(diagcsr + BLOCK_MEM_RDY) == 0xFFFFFFFF);
-}
-
/**
* xgene_ahci_read_id - Read ID data from the specified device
* @dev: device
@@ -495,11 +489,6 @@ static int xgene_ahci_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (xgene_ahci_is_memram_inited(ctx)) {
- dev_info(dev, "skip clock and PHY initialization\n");
- goto skip_clk_phy;
- }
-
/* Due to errata, HW requires full toggle transition */
rc = ahci_platform_enable_clks(hpriv);
if (rc)
@@ -512,7 +501,7 @@ static int xgene_ahci_probe(struct platform_device *pdev)
/* Configure the host controller */
xgene_ahci_hw_init(hpriv);
-skip_clk_phy:
+
hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_NCQ;
rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info);
@@ -527,6 +516,16 @@ disable_resources:
return rc;
}
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_ahci_acpi_match[] = {
+ { "APMC0D00", },
+ { "APMC0D0D", },
+ { "APMC0D09", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match);
+#endif
+
static const struct of_device_id xgene_ahci_of_match[] = {
{.compatible = "apm,xgene-ahci"},
{},
@@ -540,6 +539,7 @@ static struct platform_driver xgene_ahci_driver = {
.name = "xgene-ahci",
.owner = THIS_MODULE,
.of_match_table = xgene_ahci_of_match,
+ .acpi_match_table = ACPI_PTR(xgene_ahci_acpi_match),
},
};
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 5163ec1..1bec05b 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -21,6 +21,7 @@
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/sched_clock.h>
+#include <linux/acpi.h>
#include <asm/arch_timer.h>
#include <asm/virt.h>
@@ -61,7 +62,8 @@ enum ppi_nr {
MAX_TIMER_PPI
};
-static int arch_timer_ppi[MAX_TIMER_PPI];
+int arch_timer_ppi[MAX_TIMER_PPI];
+EXPORT_SYMBOL(arch_timer_ppi);
static struct clock_event_device __percpu *arch_timer_evt;
@@ -338,8 +340,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
if (arch_timer_rate)
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)
arch_timer_rate = readl_relaxed(cntbase + CNTFRQ);
else
@@ -635,20 +641,8 @@ static void __init arch_timer_common_init(void)
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 HYP mode is available, we know that the physical timer
* has been configured to be accessible from PL1. Use it, so
@@ -667,13 +661,31 @@ 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_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");
+
+ 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)
{
@@ -740,3 +752,71 @@ static void __init arch_timer_mem_init(struct device_node *np)
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
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
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index a0698b4..d2da911 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -490,9 +490,19 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
isb();
}
+#ifdef CONFIG_ARM_PARKING_PROTOCOL
+static void gic_wakeup_parked_cpu(int cpu)
+{
+ gic_raise_softirq(cpumask_of(cpu), 0);
+}
+#endif
+
static void gic_smp_init(void)
{
set_smp_cross_call(gic_raise_softirq);
+#ifdef CONFIG_ARM_PARKING_PROTOCOL
+ set_smp_boot_wakeup_call(gic_wakeup_parked_cpu);
+#endif
register_cpu_notifier(&gic_cpu_notifier);
}
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index dda6dbc..5d9bdd3 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -33,12 +33,14 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/acpi.h>
#include <linux/irqdomain.h>
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <linux/slab.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/arm-gic.h>
+#include <linux/irqchip/arm-gic-acpi.h>
#include <asm/cputype.h>
#include <asm/irq.h>
@@ -622,6 +624,13 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
+
+#ifdef CONFIG_ARM_PARKING_PROTOCOL
+static void gic_wakeup_parked_cpu(int cpu)
+{
+ gic_raise_softirq(cpumask_of(cpu), GIC_DIST_SOFTINT_NSATT);
+}
+#endif
#endif
#ifdef CONFIG_BL_SWITCHER
@@ -977,6 +986,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
#ifdef CONFIG_SMP
set_smp_cross_call(gic_raise_softirq);
register_cpu_notifier(&gic_cpu_notifier);
+#ifdef CONFIG_ARM_PARKING_PROTOCOL
+ set_smp_boot_wakeup_call(gic_wakeup_parked_cpu);
+#endif
#endif
set_handle_irq(gic_handle_irq);
}
@@ -1029,3 +1041,107 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
#endif
+
+#ifdef CONFIG_ACPI
+static phys_addr_t dist_phy_base, cpu_phy_base;
+static int cpu_base_assigned;
+
+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;
+
+ 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 -EFAULT;
+
+ 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(sizeof(struct acpi_table_madt),
+ gic_acpi_parse_madt_cpu, table,
+ ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0);
+ if (count < 0) {
+ pr_err("Error during GICC entries parsing\n");
+ return -EFAULT;
+ } else if (!count) {
+ 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(sizeof(struct acpi_table_madt),
+ gic_acpi_parse_madt_distributor, table,
+ ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0);
+ if (count <= 0) {
+ pr_err("Error during GICD entries parsing\n");
+ return -EFAULT;
+ } else if (!count) {
+ 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);
+ return 0;
+}
+#endif
diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c
index 0fe2f71..9106c6d 100644
--- a/drivers/irqchip/irqchip.c
+++ b/drivers/irqchip/irqchip.c
@@ -11,6 +11,7 @@
#include <linux/init.h>
#include <linux/of_irq.h>
#include <linux/irqchip.h>
+#include <linux/irqchip/arm-gic-acpi.h>
/*
* This special of_device_id is the sentinel at the end of the
@@ -26,4 +27,6 @@ extern struct of_device_id __irqchip_of_table[];
void __init irqchip_init(void)
{
of_irq_init(__irqchip_of_table);
+
+ acpi_gic_init();
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
index ea27383..d0d3ab5 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
@@ -696,6 +696,18 @@ static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
else
mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
+ if (XGBE_SEATTLE_A0) {
+ /* The PCS implementation has reversed the devices in
+ * package registers so we need to change 05 to 06 and
+ * 06 to 05 if being read (these registers are readonly
+ * so no need to do this in the write function)
+ */
+ if ((mmd_address & 0xffff) == 0x05)
+ mmd_address = (mmd_address & ~0xffff) | 0x06;
+ else if ((mmd_address & 0xffff) == 0x06)
+ mmd_address = (mmd_address & ~0xffff) | 0x05;
+ }
+
/* The PCS registers are accessed using mmio. The underlying APB3
* management interface uses indirect addressing to access the MMD
* register sets. This requires accessing of the PCS register in two
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index b26d758..ca7895c 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -426,6 +426,9 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata)
hw_feat->rx_ch_cnt++;
hw_feat->tx_ch_cnt++;
+ /* A0 does not support NUMTC, hardcode it for now */
+ hw_feat->tc_cnt = XGBE_TC_CNT;
+
DBGPR("<--xgbe_get_all_hw_features\n");
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
index bdf9cfa..ba53e41 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
@@ -533,6 +533,7 @@ static int xgbe_resume(struct device *dev)
#endif /* CONFIG_PM */
static const struct of_device_id xgbe_of_match[] = {
+ { .compatible = "amd,xgbe-seattle-v0a", },
{ .compatible = "amd,xgbe-seattle-v1a", },
{},
};
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index e9fe6e6..389bfec 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -187,8 +187,11 @@
#define XGBE_FIFO_SIZE_B(x) (x)
#define XGBE_FIFO_SIZE_KB(x) (x * 1024)
+#define XGBE_TC_CNT 2
#define XGBE_TC_MIN_QUANTUM 10
+#define XGBE_SEATTLE_A0 ((read_cpuid_id() & 0x00f0000f) == 0)
+
/* Helper macro for descriptor handling
* Always use XGBE_GET_DESC_DATA to access the descriptor data
* since the index is free-running and needs to be and-ed
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index bcaa41a..ebad872 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -81,6 +81,7 @@ static const char version[] =
#include <linux/workqueue.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/acpi.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -2408,6 +2409,14 @@ static struct dev_pm_ops smc_drv_pm_ops = {
.resume = smc_drv_resume,
};
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id smc91x_acpi_match[] = {
+ { "LNRO0003", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, smc91x_acpi_match);
+#endif
+
static struct platform_driver smc_driver = {
.probe = smc_drv_probe,
.remove = smc_drv_remove,
@@ -2416,6 +2425,7 @@ static struct platform_driver smc_driver = {
.owner = THIS_MODULE,
.pm = &smc_drv_pm_ops,
.of_match_table = of_match_ptr(smc91x_match),
+ .acpi_match_table = ACPI_PTR(smc91x_acpi_match),
},
};
diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c
index f3230ee..90145d9 100644
--- a/drivers/net/phy/amd-xgbe-phy.c
+++ b/drivers/net/phy/amd-xgbe-phy.c
@@ -78,12 +78,14 @@
MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_VERSION("1.0.0-a");
+MODULE_VERSION("0.0.0-a");
MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
-#define XGBE_PHY_ID 0x000162d0
+#define XGBE_PHY_ID 0x7996ced0
#define XGBE_PHY_MASK 0xfffffff0
+#define XGBE_PHY_SERDES_RETRY 32
+#define XGBE_PHY_CHANNEL_PROPERTY "amd,serdes-channel"
#define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set"
#define XGBE_AN_INT_CMPLT 0x01
@@ -118,77 +120,6 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
#endif
-/* SerDes integration register offsets */
-#define SIR0_KR_RT_1 0x002c
-#define SIR0_STATUS 0x0040
-#define SIR1_SPEED 0x0000
-
-/* SerDes integration register entry bit positions and sizes */
-#define SIR0_KR_RT_1_RESET_INDEX 11
-#define SIR0_KR_RT_1_RESET_WIDTH 1
-#define SIR0_STATUS_RX_READY_INDEX 0
-#define SIR0_STATUS_RX_READY_WIDTH 1
-#define SIR0_STATUS_TX_READY_INDEX 8
-#define SIR0_STATUS_TX_READY_WIDTH 1
-#define SIR1_SPEED_DATARATE_INDEX 4
-#define SIR1_SPEED_DATARATE_WIDTH 2
-#define SIR1_SPEED_PI_SPD_SEL_INDEX 12
-#define SIR1_SPEED_PI_SPD_SEL_WIDTH 4
-#define SIR1_SPEED_PLLSEL_INDEX 3
-#define SIR1_SPEED_PLLSEL_WIDTH 1
-#define SIR1_SPEED_RATECHANGE_INDEX 6
-#define SIR1_SPEED_RATECHANGE_WIDTH 1
-#define SIR1_SPEED_TXAMP_INDEX 8
-#define SIR1_SPEED_TXAMP_WIDTH 4
-#define SIR1_SPEED_WORDMODE_INDEX 0
-#define SIR1_SPEED_WORDMODE_WIDTH 3
-
-#define SPEED_10000_CDR 0x7
-#define SPEED_10000_PLL 0x1
-#define SPEED_10000_RATE 0x0
-#define SPEED_10000_TXAMP 0xa
-#define SPEED_10000_WORD 0x7
-
-#define SPEED_2500_CDR 0x2
-#define SPEED_2500_PLL 0x0
-#define SPEED_2500_RATE 0x1
-#define SPEED_2500_TXAMP 0xf
-#define SPEED_2500_WORD 0x1
-
-#define SPEED_1000_CDR 0x2
-#define SPEED_1000_PLL 0x0
-#define SPEED_1000_RATE 0x3
-#define SPEED_1000_TXAMP 0xf
-#define SPEED_1000_WORD 0x1
-
-
-/* SerDes RxTx register offsets */
-#define RXTX_REG20 0x0050
-#define RXTX_REG114 0x01c8
-
-/* SerDes RxTx register entry bit positions and sizes */
-#define RXTX_REG20_BLWC_ENA_INDEX 2
-#define RXTX_REG20_BLWC_ENA_WIDTH 1
-#define RXTX_REG114_PQ_REG_INDEX 9
-#define RXTX_REG114_PQ_REG_WIDTH 7
-
-#define RXTX_10000_BLWC 0
-#define RXTX_10000_PQ 0x1e
-
-#define RXTX_2500_BLWC 1
-#define RXTX_2500_PQ 0xa
-
-#define RXTX_1000_BLWC 1
-#define RXTX_1000_PQ 0xa
-
-/* Bit setting and getting macros
- * The get macro will extract the current bit field value from within
- * the variable
- *
- * The set macro will clear the current bit field value within the
- * variable and then set the bit field of the variable to the
- * specified value
- */
#define GET_BITS(_var, _index, _width) \
(((_var) >> (_index)) & ((0x1 << (_width)) - 1))
@@ -198,71 +129,12 @@ do { \
(_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index)); \
} while (0)
-#define XSIR_GET_BITS(_var, _prefix, _field) \
- GET_BITS((_var), \
- _prefix##_##_field##_INDEX, \
- _prefix##_##_field##_WIDTH)
-
-#define XSIR_SET_BITS(_var, _prefix, _field, _val) \
- SET_BITS((_var), \
- _prefix##_##_field##_INDEX, \
- _prefix##_##_field##_WIDTH, (_val))
+#define XCMU_IOREAD(_priv, _reg) \
+ ioread16((_priv)->cmu_regs + _reg)
-/* Macros for reading or writing SerDes integration registers
- * The ioread macros will get bit fields or full values using the
- * register definitions formed using the input names
- *
- * The iowrite macros will set bit fields or full values using the
- * register definitions formed using the input names
- */
-#define XSIR0_IOREAD(_priv, _reg) \
- ioread16((_priv)->sir0_regs + _reg)
+#define XCMU_IOWRITE(_priv, _reg, _val) \
+ iowrite16((_val), (_priv)->cmu_regs + _reg)
-#define XSIR0_IOREAD_BITS(_priv, _reg, _field) \
- GET_BITS(XSIR0_IOREAD((_priv), _reg), \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH)
-
-#define XSIR0_IOWRITE(_priv, _reg, _val) \
- iowrite16((_val), (_priv)->sir0_regs + _reg)
-
-#define XSIR0_IOWRITE_BITS(_priv, _reg, _field, _val) \
-do { \
- u16 reg_val = XSIR0_IOREAD((_priv), _reg); \
- SET_BITS(reg_val, \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH, (_val)); \
- XSIR0_IOWRITE((_priv), _reg, reg_val); \
-} while (0)
-
-#define XSIR1_IOREAD(_priv, _reg) \
- ioread16((_priv)->sir1_regs + _reg)
-
-#define XSIR1_IOREAD_BITS(_priv, _reg, _field) \
- GET_BITS(XSIR1_IOREAD((_priv), _reg), \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH)
-
-#define XSIR1_IOWRITE(_priv, _reg, _val) \
- iowrite16((_val), (_priv)->sir1_regs + _reg)
-
-#define XSIR1_IOWRITE_BITS(_priv, _reg, _field, _val) \
-do { \
- u16 reg_val = XSIR1_IOREAD((_priv), _reg); \
- SET_BITS(reg_val, \
- _reg##_##_field##_INDEX, \
- _reg##_##_field##_WIDTH, (_val)); \
- XSIR1_IOWRITE((_priv), _reg, reg_val); \
-} while (0)
-
-
-/* Macros for reading or writing SerDes RxTx registers
- * The ioread macros will get bit fields or full values using the
- * register definitions formed using the input names
- *
- * The iowrite macros will set bit fields or full values using the
- * register definitions formed using the input names
- */
#define XRXTX_IOREAD(_priv, _reg) \
ioread16((_priv)->rxtx_regs + _reg)
@@ -283,6 +155,77 @@ do { \
XRXTX_IOWRITE((_priv), _reg, reg_val); \
} while (0)
+/* SerDes CMU register offsets */
+#define CMU_REG15 0x003c
+#define CMU_REG16 0x0040
+
+/* SerDes CMU register entry bit positions and sizes */
+#define CMU_REG16_TX_RATE_CHANGE_BASE 15
+#define CMU_REG16_RX_RATE_CHANGE_BASE 14
+#define CMU_REG16_RATE_CHANGE_DECR 2
+
+
+/* SerDes RxTx register offsets */
+#define RXTX_REG2 0x0008
+#define RXTX_REG3 0x000c
+#define RXTX_REG5 0x0014
+#define RXTX_REG6 0x0018
+#define RXTX_REG20 0x0050
+#define RXTX_REG53 0x00d4
+#define RXTX_REG114 0x01c8
+#define RXTX_REG115 0x01cc
+#define RXTX_REG142 0x0238
+
+/* SerDes RxTx register entry bit positions and sizes */
+#define RXTX_REG2_RESETB_INDEX 15
+#define RXTX_REG2_RESETB_WIDTH 1
+#define RXTX_REG3_TX_DATA_RATE_INDEX 14
+#define RXTX_REG3_TX_DATA_RATE_WIDTH 2
+#define RXTX_REG3_TX_WORD_MODE_INDEX 11
+#define RXTX_REG3_TX_WORD_MODE_WIDTH 3
+#define RXTX_REG5_TXAMP_CNTL_INDEX 7
+#define RXTX_REG5_TXAMP_CNTL_WIDTH 4
+#define RXTX_REG6_RX_DATA_RATE_INDEX 9
+#define RXTX_REG6_RX_DATA_RATE_WIDTH 2
+#define RXTX_REG6_RX_WORD_MODE_INDEX 11
+#define RXTX_REG6_RX_WORD_MODE_WIDTH 3
+#define RXTX_REG20_BLWC_ENA_INDEX 2
+#define RXTX_REG20_BLWC_ENA_WIDTH 1
+#define RXTX_REG53_RX_PLLSELECT_INDEX 15
+#define RXTX_REG53_RX_PLLSELECT_WIDTH 1
+#define RXTX_REG53_TX_PLLSELECT_INDEX 14
+#define RXTX_REG53_TX_PLLSELECT_WIDTH 1
+#define RXTX_REG53_PI_SPD_SEL_CDR_INDEX 10
+#define RXTX_REG53_PI_SPD_SEL_CDR_WIDTH 4
+#define RXTX_REG114_PQ_REG_INDEX 9
+#define RXTX_REG114_PQ_REG_WIDTH 7
+#define RXTX_REG115_FORCE_LAT_CAL_START_INDEX 2
+#define RXTX_REG115_FORCE_LAT_CAL_START_WIDTH 1
+#define RXTX_REG115_FORCE_SUM_CAL_START_INDEX 1
+#define RXTX_REG115_FORCE_SUM_CAL_START_WIDTH 1
+#define RXTX_REG142_SUM_CALIB_DONE_INDEX 15
+#define RXTX_REG142_SUM_CALIB_DONE_WIDTH 1
+#define RXTX_REG142_SUM_CALIB_ERR_INDEX 14
+#define RXTX_REG142_SUM_CALIB_ERR_WIDTH 1
+#define RXTX_REG142_LAT_CALIB_DONE_INDEX 11
+#define RXTX_REG142_LAT_CALIB_DONE_WIDTH 1
+
+#define RXTX_FULL_RATE 0x0
+#define RXTX_HALF_RATE 0x1
+#define RXTX_FIFTH_RATE 0x3
+#define RXTX_66BIT_WORD 0x7
+#define RXTX_10BIT_WORD 0x1
+#define RXTX_10G_TX_AMP 0xa
+#define RXTX_1G_TX_AMP 0xf
+#define RXTX_10G_CDR 0x7
+#define RXTX_1G_CDR 0x2
+#define RXTX_10G_PLL 0x1
+#define RXTX_1G_PLL 0x0
+#define RXTX_10G_PQ 0x1e
+#define RXTX_1G_PQ 0xa
+
+
+DEFINE_SPINLOCK(cmu_lock);
enum amd_xgbe_phy_an {
AMD_XGBE_AN_READY = 0,
@@ -321,18 +264,18 @@ struct amd_xgbe_phy_priv {
/* SerDes related mmio resources */
struct resource *rxtx_res;
- struct resource *sir0_res;
- struct resource *sir1_res;
+ struct resource *cmu_res;
/* SerDes related mmio registers */
void __iomem *rxtx_regs; /* SerDes Rx/Tx CSRs */
- void __iomem *sir0_regs; /* SerDes integration registers (1/2) */
- void __iomem *sir1_regs; /* SerDes integration registers (2/2) */
+ void __iomem *cmu_regs; /* SerDes CMU CSRs */
+
+ unsigned int serdes_channel;
+ unsigned int speed_set;
/* Maintain link status for re-starting auto-negotiation */
unsigned int link;
enum amd_xgbe_phy_mode mode;
- unsigned int speed_set;
/* Auto-negotiation state machine support */
struct mutex an_mutex;
@@ -394,33 +337,51 @@ static int amd_xgbe_phy_pcs_power_cycle(struct phy_device *phydev)
static void amd_xgbe_phy_serdes_start_ratechange(struct phy_device *phydev)
{
struct amd_xgbe_phy_priv *priv = phydev->priv;
+ u16 val, mask;
+
+ /* Assert Rx and Tx ratechange in CMU_reg16 */
+ val = XCMU_IOREAD(priv, CMU_REG16);
- /* Assert Rx and Tx ratechange */
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 1);
+ mask = (1 << (CMU_REG16_TX_RATE_CHANGE_BASE -
+ (priv->serdes_channel * CMU_REG16_RATE_CHANGE_DECR))) |
+ (1 << (CMU_REG16_RX_RATE_CHANGE_BASE -
+ (priv->serdes_channel * CMU_REG16_RATE_CHANGE_DECR)));
+ val |= mask;
+
+ XCMU_IOWRITE(priv, CMU_REG16, val);
}
static void amd_xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev)
{
struct amd_xgbe_phy_priv *priv = phydev->priv;
+ u16 val, mask;
unsigned int wait;
- u16 status;
- /* Release Rx and Tx ratechange */
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 0);
+ /* Release Rx and Tx ratechange for proper channel in CMU_reg16 */
+ val = XCMU_IOREAD(priv, CMU_REG16);
+
+ mask = (1 << (CMU_REG16_TX_RATE_CHANGE_BASE -
+ (priv->serdes_channel * CMU_REG16_RATE_CHANGE_DECR))) |
+ (1 << (CMU_REG16_RX_RATE_CHANGE_BASE -
+ (priv->serdes_channel * CMU_REG16_RATE_CHANGE_DECR)));
+ val &= ~mask;
- /* Wait for Rx and Tx ready */
+ XCMU_IOWRITE(priv, CMU_REG16, val);
+
+ /* Wait for Rx and Tx ready in CMU_reg15 */
+ mask = (1 << priv->serdes_channel) |
+ (1 << (priv->serdes_channel + 8));
wait = XGBE_PHY_RATECHANGE_COUNT;
while (wait--) {
- usleep_range(50, 75);
+ udelay(50);
- status = XSIR0_IOREAD(priv, SIR0_STATUS);
- if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) &&
- XSIR_GET_BITS(status, SIR0_STATUS, TX_READY))
+ val = XCMU_IOREAD(priv, CMU_REG15);
+ if ((val & mask) == mask)
return;
}
netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n",
- status);
+ val);
}
static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
@@ -428,8 +389,8 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
struct amd_xgbe_phy_priv *priv = phydev->priv;
int ret;
- /* Enable KR training */
- ret = amd_xgbe_an_enable_kr_training(phydev);
+ /* Disable KR training */
+ ret = amd_xgbe_an_disable_kr_training(phydev);
if (ret < 0)
return ret;
@@ -455,19 +416,30 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
return ret;
/* Set SerDes to 10G speed */
+ spin_lock(&cmu_lock);
+
amd_xgbe_phy_serdes_start_ratechange(phydev);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_DATA_RATE, RXTX_FULL_RATE);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_WORD_MODE, RXTX_66BIT_WORD);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG5, TXAMP_CNTL, RXTX_10G_TX_AMP);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_DATA_RATE, RXTX_FULL_RATE);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_WORD_MODE, RXTX_66BIT_WORD);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, 0);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, RX_PLLSELECT, RXTX_10G_PLL);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, TX_PLLSELECT, RXTX_10G_PLL);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, PI_SPD_SEL_CDR, RXTX_10G_CDR);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10G_PQ);
amd_xgbe_phy_serdes_complete_ratechange(phydev);
+ spin_unlock(&cmu_lock);
+
priv->mode = AMD_XGBE_MODE_KR;
return 0;
@@ -505,19 +477,30 @@ static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev)
return ret;
/* Set SerDes to 2.5G speed */
+ spin_lock(&cmu_lock);
+
amd_xgbe_phy_serdes_start_ratechange(phydev);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_DATA_RATE, RXTX_HALF_RATE);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_WORD_MODE, RXTX_10BIT_WORD);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG5, TXAMP_CNTL, RXTX_1G_TX_AMP);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_2500_PQ);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_DATA_RATE, RXTX_HALF_RATE);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_WORD_MODE, RXTX_10BIT_WORD);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, 1);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, RX_PLLSELECT, RXTX_1G_PLL);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, TX_PLLSELECT, RXTX_1G_PLL);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, PI_SPD_SEL_CDR, RXTX_1G_CDR);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1G_PQ);
amd_xgbe_phy_serdes_complete_ratechange(phydev);
+ spin_unlock(&cmu_lock);
+
priv->mode = AMD_XGBE_MODE_KX;
return 0;
@@ -555,19 +538,30 @@ static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev)
return ret;
/* Set SerDes to 1G speed */
+ spin_lock(&cmu_lock);
+
amd_xgbe_phy_serdes_start_ratechange(phydev);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL);
- XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_DATA_RATE, RXTX_FIFTH_RATE);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_WORD_MODE, RXTX_10BIT_WORD);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG5, TXAMP_CNTL, RXTX_1G_TX_AMP);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC);
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_PQ);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_DATA_RATE, RXTX_FIFTH_RATE);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_WORD_MODE, RXTX_10BIT_WORD);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, 1);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, RX_PLLSELECT, RXTX_1G_PLL);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, TX_PLLSELECT, RXTX_1G_PLL);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG53, PI_SPD_SEL_CDR, RXTX_1G_CDR);
+
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1G_PQ);
amd_xgbe_phy_serdes_complete_ratechange(phydev);
+ spin_unlock(&cmu_lock);
+
priv->mode = AMD_XGBE_MODE_KX;
return 0;
@@ -639,13 +633,9 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
if (ret < 0)
return AMD_XGBE_AN_ERROR;
- XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1);
-
ret |= 0x01;
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
- XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 0);
-
return AMD_XGBE_AN_EVENT;
}
@@ -1291,21 +1281,33 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
goto err_priv;
}
- priv->sir0_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- priv->sir0_regs = devm_ioremap_resource(dev, priv->sir0_res);
- if (IS_ERR(priv->sir0_regs)) {
- dev_err(dev, "sir0 ioremap failed\n");
- ret = PTR_ERR(priv->sir0_regs);
+ /* All xgbe phy devices share the CMU registers so retrieve
+ * the resource and do the ioremap directly rather than
+ * the devm_ioremap_resource call
+ */
+ priv->cmu_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!priv->cmu_res) {
+ dev_err(dev, "cmu invalid resource\n");
+ ret = -EINVAL;
+ goto err_rxtx;
+ }
+ priv->cmu_regs = devm_ioremap_nocache(dev, priv->cmu_res->start,
+ resource_size(priv->cmu_res));
+ if (!priv->cmu_regs) {
+ dev_err(dev, "cmu ioremap failed\n");
+ ret = -ENOMEM;
goto err_rxtx;
}
- priv->sir1_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
- priv->sir1_regs = devm_ioremap_resource(dev, priv->sir1_res);
- if (IS_ERR(priv->sir1_regs)) {
- dev_err(dev, "sir1 ioremap failed\n");
- ret = PTR_ERR(priv->sir1_regs);
- goto err_sir0;
+ /* Get the device serdes channel property */
+ property = of_get_property(dev->of_node, XGBE_PHY_CHANNEL_PROPERTY,
+ NULL);
+ if (!property) {
+ dev_err(dev, "unable to obtain serdes_channel property\n");
+ ret = -EINVAL;
+ goto err_cmu;
}
+ priv->serdes_channel = be32_to_cpu(*property);
/* Get the device speed set property */
speed_set = 0;
@@ -1324,14 +1326,14 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
default:
dev_err(dev, "invalid amd,speed-set property\n");
ret = -EINVAL;
- goto err_sir1;
+ goto err_cmu;
}
priv->link = 1;
ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
if (ret < 0)
- goto err_sir1;
+ goto err_cmu;
if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
priv->mode = AMD_XGBE_MODE_KR;
else
@@ -1342,7 +1344,7 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
priv->an_workqueue = create_singlethread_workqueue(wq_name);
if (!priv->an_workqueue) {
ret = -ENOMEM;
- goto err_sir1;
+ goto err_cmu;
}
phydev->priv = priv;
@@ -1352,15 +1354,8 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
return 0;
-err_sir1:
- devm_iounmap(dev, priv->sir1_regs);
- devm_release_mem_region(dev, priv->sir1_res->start,
- resource_size(priv->sir1_res));
-
-err_sir0:
- devm_iounmap(dev, priv->sir0_regs);
- devm_release_mem_region(dev, priv->sir0_res->start,
- resource_size(priv->sir0_res));
+err_cmu:
+ devm_iounmap(dev, priv->cmu_regs);
err_rxtx:
devm_iounmap(dev, priv->rxtx_regs);
@@ -1392,14 +1387,7 @@ static void amd_xgbe_phy_remove(struct phy_device *phydev)
flush_workqueue(priv->an_workqueue);
destroy_workqueue(priv->an_workqueue);
- /* Release resources */
- devm_iounmap(dev, priv->sir1_regs);
- devm_release_mem_region(dev, priv->sir1_res->start,
- resource_size(priv->sir1_res));
-
- devm_iounmap(dev, priv->sir0_regs);
- devm_release_mem_region(dev, priv->sir0_res->start,
- resource_size(priv->sir0_res));
+ devm_iounmap(dev, priv->cmu_regs);
devm_iounmap(dev, priv->rxtx_regs);
devm_release_mem_region(dev, priv->rxtx_res->start,
diff --git a/drivers/of/address.c b/drivers/of/address.c
index e371825..afdb782 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/pci_regs.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
#include <linux/string.h>
/* Max address size we deal with */
@@ -293,6 +295,51 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
}
EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
+/*
+ * of_pci_range_to_resource - Create a resource from an of_pci_range
+ * @range: the PCI range that describes the resource
+ * @np: device node where the range belongs to
+ * @res: pointer to a valid resource that will be updated to
+ * reflect the values contained in the range.
+ *
+ * Returns EINVAL if the range cannot be converted to resource.
+ *
+ * Note that if the range is an IO range, the resource will be converted
+ * using pci_address_to_pio() which can fail if it is called too early or
+ * if the range cannot be matched to any host bridge IO space (our case here).
+ * To guard against that we try to register the IO range first.
+ * If that fails we know that pci_address_to_pio() will do too.
+ */
+int of_pci_range_to_resource(struct of_pci_range *range,
+ struct device_node *np, struct resource *res)
+{
+ int err;
+ res->flags = range->flags;
+ res->parent = res->child = res->sibling = NULL;
+ res->name = np->full_name;
+
+ if (res->flags & IORESOURCE_IO) {
+ unsigned long port;
+ err = pci_register_io_range(range->cpu_addr, range->size);
+ if (err)
+ goto invalid_range;
+ port = pci_address_to_pio(range->cpu_addr);
+ if (port == (unsigned long)-1) {
+ err = -EINVAL;
+ goto invalid_range;
+ }
+ res->start = port;
+ } else {
+ res->start = range->cpu_addr;
+ }
+ res->end = res->start + range->size - 1;
+ return 0;
+
+invalid_range:
+ res->start = (resource_size_t)OF_BAD_ADDR;
+ res->end = (resource_size_t)OF_BAD_ADDR;
+ return err;
+}
#endif /* CONFIG_PCI */
/*
@@ -601,12 +648,119 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
}
EXPORT_SYMBOL(of_get_address);
+#ifdef PCI_IOBASE
+struct io_range {
+ struct list_head list;
+ phys_addr_t start;
+ resource_size_t size;
+};
+
+static LIST_HEAD(io_range_list);
+static DEFINE_SPINLOCK(io_range_lock);
+#endif
+
+/*
+ * Record the PCI IO range (expressed as CPU physical address + size).
+ * Return a negative value if an error has occured, zero otherwise
+ */
+int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
+{
+ int err = 0;
+
+#ifdef PCI_IOBASE
+ struct io_range *range;
+ resource_size_t allocated_size = 0;
+
+ /* check if the range hasn't been previously recorded */
+ spin_lock(&io_range_lock);
+ list_for_each_entry(range, &io_range_list, list) {
+ if (addr >= range->start && addr + size <= range->start + size) {
+ /* range already registered, bail out */
+ goto end_register;
+ }
+ allocated_size += range->size;
+ }
+
+ /* range not registed yet, check for available space */
+ if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
+ /* if it's too big check if 64K space can be reserved */
+ if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
+ err = -E2BIG;
+ goto end_register;
+ }
+
+ size = SZ_64K;
+ pr_warn("Requested IO range too big, new size set to 64K\n");
+ }
+
+ /* add the range to the list */
+ range = kzalloc(sizeof(*range), GFP_KERNEL);
+ if (!range) {
+ err = -ENOMEM;
+ goto end_register;
+ }
+
+ range->start = addr;
+ range->size = size;
+
+ list_add_tail(&range->list, &io_range_list);
+
+end_register:
+ spin_unlock(&io_range_lock);
+#endif
+
+ return err;
+}
+
+phys_addr_t pci_pio_to_address(unsigned long pio)
+{
+ phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
+
+#ifdef PCI_IOBASE
+ struct io_range *range;
+ resource_size_t allocated_size = 0;
+
+ if (pio > IO_SPACE_LIMIT)
+ return address;
+
+ spin_lock(&io_range_lock);
+ list_for_each_entry(range, &io_range_list, list) {
+ if (pio >= allocated_size && pio < allocated_size + range->size) {
+ address = range->start + pio - allocated_size;
+ break;
+ }
+ allocated_size += range->size;
+ }
+ spin_unlock(&io_range_lock);
+#endif
+
+ return address;
+}
+
unsigned long __weak pci_address_to_pio(phys_addr_t address)
{
+#ifdef PCI_IOBASE
+ struct io_range *res;
+ resource_size_t offset = 0;
+ unsigned long addr = -1;
+
+ spin_lock(&io_range_lock);
+ list_for_each_entry(res, &io_range_list, list) {
+ if (address >= res->start && address < res->start + res->size) {
+ addr = res->start - address + offset;
+ break;
+ }
+ offset += res->size;
+ }
+ spin_unlock(&io_range_lock);
+
+ return addr;
+#else
if (address > IO_SPACE_LIMIT)
return (unsigned long)-1;
return (unsigned long) address;
+#endif
}
static int __of_address_to_resource(struct device_node *dev,
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 8481996..8882b46 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -1,7 +1,9 @@
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include <linux/of_pci.h>
+#include <linux/slab.h>
static inline int __of_pci_pci_compare(struct device_node *node,
unsigned int data)
@@ -89,6 +91,146 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res)
}
EXPORT_SYMBOL_GPL(of_pci_parse_bus_range);
+/**
+ * This function will try to obtain the host bridge domain number by
+ * finding a property called "linux,pci-domain" of the given device node.
+ *
+ * @node: device tree node with the domain information
+ *
+ * Returns the associated domain number from DT in the range [0-0xffff], or
+ * a negative value if the required property is not found.
+ */
+int of_get_pci_domain_nr(struct device_node *node)
+{
+ const __be32 *value;
+ int len;
+ u16 domain;
+
+ value = of_get_property(node, "linux,pci-domain", &len);
+ if (!value || len < sizeof(*value))
+ return -EINVAL;
+
+ domain = (u16)be32_to_cpup(value);
+
+ return domain;
+}
+EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
+
+#if defined(CONFIG_OF_ADDRESS)
+/**
+ * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT
+ * @dev: device node of the host bridge having the range property
+ * @busno: bus number associated with the bridge root bus
+ * @bus_max: maximum number of buses for this bridge
+ * @resources: list where the range of resources will be added after DT parsing
+ * @io_base: pointer to a variable that will contain on return the physical
+ * address for the start of the I/O range. Can be NULL if the caller doesn't
+ * expect IO ranges to be present in the device tree.
+ *
+ * It is the caller's job to free the @resources list.
+ *
+ * This function will parse the "ranges" property of a PCI host bridge device
+ * node and setup the resource mapping based on its content. It is expected
+ * that the property conforms with the Power ePAPR document.
+ *
+ * It returns zero if the range parsing has been successful or a standard error
+ * value if it failed.
+ */
+int of_pci_get_host_bridge_resources(struct device_node *dev,
+ unsigned char busno, unsigned char bus_max,
+ struct list_head *resources, resource_size_t *io_base)
+{
+ struct resource *res;
+ struct resource *bus_range;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ char range_type[4];
+ int err;
+
+ if (io_base)
+ *io_base = (resource_size_t)OF_BAD_ADDR;
+
+ bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
+ if (!bus_range)
+ return -ENOMEM;
+
+ pr_info("PCI host bridge %s ranges:\n", dev->full_name);
+
+ err = of_pci_parse_bus_range(dev, bus_range);
+ if (err) {
+ bus_range->start = busno;
+ bus_range->end = bus_max;
+ bus_range->flags = IORESOURCE_BUS;
+ pr_info(" No bus range found for %s, using %pR\n",
+ dev->full_name, bus_range);
+ } else {
+ if (bus_range->end > bus_range->start + bus_max)
+ bus_range->end = bus_range->start + bus_max;
+ }
+ pci_add_resource(resources, bus_range);
+
+ /* Check for ranges property */
+ err = of_pci_range_parser_init(&parser, dev);
+ if (err)
+ goto parse_failed;
+
+ pr_debug("Parsing ranges property...\n");
+ for_each_of_pci_range(&parser, &range) {
+ /* Read next ranges element */
+ if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
+ snprintf(range_type, 4, " IO");
+ else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
+ snprintf(range_type, 4, "MEM");
+ else
+ snprintf(range_type, 4, "err");
+ pr_info(" %s %#010llx..%#010llx -> %#010llx\n", range_type,
+ range.cpu_addr, range.cpu_addr + range.size - 1,
+ range.pci_addr);
+
+ /*
+ * If we failed translation or got a zero-sized region
+ * then skip this range
+ */
+ if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
+ continue;
+
+ res = kzalloc(sizeof(struct resource), GFP_KERNEL);
+ if (!res) {
+ err = -ENOMEM;
+ goto parse_failed;
+ }
+
+ err = of_pci_range_to_resource(&range, dev, res);
+ if (err)
+ goto conversion_failed;
+
+ if (resource_type(res) == IORESOURCE_IO) {
+ if (!io_base) {
+ pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
+ dev->full_name);
+ err = -EINVAL;
+ goto conversion_failed;
+ }
+ if (*io_base != (resource_size_t)OF_BAD_ADDR)
+ pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
+ dev->full_name);
+ *io_base = range.cpu_addr;
+ }
+
+ pci_add_resource_offset(resources, res, res->start - range.pci_addr);
+ }
+
+ return 0;
+
+conversion_failed:
+ kfree(res);
+parse_failed:
+ pci_free_resource_list(resources);
+ return err;
+}
+EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
+#endif /* CONFIG_OF_ADDRESS */
+
#ifdef CONFIG_PCI_MSI
static LIST_HEAD(of_pci_msi_chip_list);
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 90f5cca..382fd3d 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -63,4 +63,14 @@ config PCIE_SPEAR13XX
help
Say Y here if you want PCIe support on SPEAr13XX SoCs.
+config PCI_XGENE
+ bool "X-Gene PCIe controller"
+ depends on ARCH_XGENE
+ depends on OF
+ select PCIEPORTBUS
+ help
+ Say Y here if you want internal PCI support on APM X-Gene SoC.
+ There are 5 internal PCIe ports available. Each port is GEN3 capable
+ and have varied lanes from x1 to x8.
+
endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index d0e88f1..845611f 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 0fb0fdb..946935d 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -626,13 +626,14 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
{
struct tegra_pcie *pcie = sys_to_pcie(sys);
+ phys_addr_t io_start = pci_pio_to_address(pcie->io.start);
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
sys->mem_offset);
pci_add_resource(&sys->resources, &pcie->busn);
- pci_ioremap_io(nr * SZ_64K, pcie->io.start);
+ pci_ioremap_io(nr * SZ_64K, io_start);
return 1;
}
@@ -737,6 +738,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
{
u32 fpci_bar, size, axi_address;
+ phys_addr_t io_start = pci_pio_to_address(pcie->io.start);
/* Bar 0: type 1 extended configuration space */
fpci_bar = 0xfe100000;
@@ -749,7 +751,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
/* Bar 1: downstream IO bar */
fpci_bar = 0xfdfc0000;
size = resource_size(&pcie->io);
- axi_address = pcie->io.start;
+ axi_address = io_start;
afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
@@ -1520,7 +1522,9 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
}
for_each_of_pci_range(&parser, &range) {
- of_pci_range_to_resource(&range, np, &res);
+ err = of_pci_range_to_resource(&range, np, &res);
+ if (err < 0)
+ return err;
switch (res.flags & IORESOURCE_TYPE_BITS) {
case IORESOURCE_IO:
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
new file mode 100644
index 0000000..41c76b8
--- /dev/null
+++ b/drivers/pci/host/pci-xgene.c
@@ -0,0 +1,659 @@
+/**
+ * APM X-Gene PCIe Driver
+ *
+ * Copyright (c) 2014 Applied Micro Circuits Corporation.
+ *
+ * Author: Tanmay Inamdar <tinamdar@apm.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk-private.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define PCIECORE_CTLANDSTATUS 0x50
+#define PIM1_1L 0x80
+#define IBAR2 0x98
+#define IR2MSK 0x9c
+#define PIM2_1L 0xa0
+#define IBAR3L 0xb4
+#define IR3MSKL 0xbc
+#define PIM3_1L 0xc4
+#define OMR1BARL 0x100
+#define OMR2BARL 0x118
+#define OMR3BARL 0x130
+#define CFGBARL 0x154
+#define CFGBARH 0x158
+#define CFGCTL 0x15c
+#define RTDID 0x160
+#define BRIDGE_CFG_0 0x2000
+#define BRIDGE_CFG_4 0x2010
+#define BRIDGE_STATUS_0 0x2600
+
+#define LINK_UP_MASK 0x00000100
+#define AXI_EP_CFG_ACCESS 0x10000
+#define EN_COHERENCY 0xF0000000
+#define EN_REG 0x00000001
+#define OB_LO_IO 0x00000002
+#define XGENE_PCIE_VENDORID 0x10E8
+#define XGENE_PCIE_DEVICEID 0xE004
+#define SZ_1T (SZ_1G*1024ULL)
+#define PIPE_PHY_RATE_RD(src) ((0xc000 & (u32)(src)) >> 0xe)
+
+struct xgene_pcie_port {
+ struct device_node *node;
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *csr_base;
+ void __iomem *cfg_base;
+ unsigned long cfg_addr;
+ bool link_up;
+};
+
+static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
+{
+ return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
+}
+
+/* PCIE Configuration Out/In */
+static inline void xgene_pcie_cfg_out32(void __iomem *addr, int offset, u32 val)
+{
+ writel(val, addr + offset);
+}
+
+static inline void xgene_pcie_cfg_out16(void __iomem *addr, int offset, u16 val)
+{
+ u32 val32 = readl(addr + (offset & ~0x3));
+
+ switch (offset & 0x3) {
+ case 2:
+ val32 &= ~0xFFFF0000;
+ val32 |= (u32)val << 16;
+ break;
+ case 0:
+ default:
+ val32 &= ~0xFFFF;
+ val32 |= val;
+ break;
+ }
+ writel(val32, addr + (offset & ~0x3));
+}
+
+static inline void xgene_pcie_cfg_out8(void __iomem *addr, int offset, u8 val)
+{
+ u32 val32 = readl(addr + (offset & ~0x3));
+
+ switch (offset & 0x3) {
+ case 0:
+ val32 &= ~0xFF;
+ val32 |= val;
+ break;
+ case 1:
+ val32 &= ~0xFF00;
+ val32 |= (u32)val << 8;
+ break;
+ case 2:
+ val32 &= ~0xFF0000;
+ val32 |= (u32)val << 16;
+ break;
+ case 3:
+ default:
+ val32 &= ~0xFF000000;
+ val32 |= (u32)val << 24;
+ break;
+ }
+ writel(val32, addr + (offset & ~0x3));
+}
+
+static inline void xgene_pcie_cfg_in32(void __iomem *addr, int offset, u32 *val)
+{
+ *val = readl(addr + offset);
+}
+
+static inline void xgene_pcie_cfg_in16(void __iomem *addr, int offset, u32 *val)
+{
+ *val = readl(addr + (offset & ~0x3));
+
+ switch (offset & 0x3) {
+ case 2:
+ *val >>= 16;
+ break;
+ }
+
+ *val &= 0xFFFF;
+}
+
+static inline void xgene_pcie_cfg_in8(void __iomem *addr, int offset, u32 *val)
+{
+ *val = readl(addr + (offset & ~0x3));
+
+ switch (offset & 0x3) {
+ case 3:
+ *val = *val >> 24;
+ break;
+ case 2:
+ *val = *val >> 16;
+ break;
+ case 1:
+ *val = *val >> 8;
+ break;
+ }
+ *val &= 0xFF;
+}
+
+/*
+ * When the address bit [17:16] is 2'b01, the Configuration access will be
+ * treated as Type 1 and it will be forwarded to external PCIe device.
+ */
+static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
+{
+ struct xgene_pcie_port *port = bus->sysdata;
+
+ if (bus->number >= (bus->primary + 1))
+ return port->cfg_base + AXI_EP_CFG_ACCESS;
+
+ return port->cfg_base;
+}
+
+/*
+ * For Configuration request, RTDID register is used as Bus Number,
+ * Device Number and Function number of the header fields.
+ */
+static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
+{
+ struct xgene_pcie_port *port = bus->sysdata;
+ unsigned int b, d, f;
+ u32 rtdid_val = 0;
+
+ b = bus->number;
+ d = PCI_SLOT(devfn);
+ f = PCI_FUNC(devfn);
+
+ if (!pci_is_root_bus(bus))
+ rtdid_val = (b << 8) | (d << 3) | f;
+
+ writel(rtdid_val, port->csr_base + RTDID);
+ /* read the register back to ensure flush */
+ readl(port->csr_base + RTDID);
+}
+
+/*
+ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
+ * the translation between PCI bus to native BUS. Entire DDR region
+ * is mapped on to PCIe space using these registers, so that it is
+ * accessible for EP devices for DMA. The BAR0/1 of bridge should be
+ * hidden during enumeration to avoid the sizing and resource allocation
+ * by PCIe core.
+ */
+static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
+{
+ if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
+ (offset == PCI_BASE_ADDRESS_1)))
+ return true;
+
+ return false;
+}
+
+static int xgene_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 *val)
+{
+ struct xgene_pcie_port *port = bus->sysdata;
+ void __iomem *addr;
+
+ if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (xgene_pcie_hide_rc_bars(bus, offset)) {
+ *val = 0;
+ return PCIBIOS_SUCCESSFUL;
+ }
+
+ xgene_pcie_set_rtdid_reg(bus, devfn);
+ addr = xgene_pcie_get_cfg_base(bus);
+ switch (len) {
+ case 1:
+ xgene_pcie_cfg_in8(addr, offset, val);
+ break;
+ case 2:
+ xgene_pcie_cfg_in16(addr, offset, val);
+ break;
+ default:
+ xgene_pcie_cfg_in32(addr, offset, val);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int xgene_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 val)
+{
+ struct xgene_pcie_port *port = bus->sysdata;
+ void __iomem *addr;
+
+ if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (xgene_pcie_hide_rc_bars(bus, offset))
+ return PCIBIOS_SUCCESSFUL;
+
+ xgene_pcie_set_rtdid_reg(bus, devfn);
+ addr = xgene_pcie_get_cfg_base(bus);
+ switch (len) {
+ case 1:
+ xgene_pcie_cfg_out8(addr, offset, (u8)val);
+ break;
+ case 2:
+ xgene_pcie_cfg_out16(addr, offset, (u16)val);
+ break;
+ default:
+ xgene_pcie_cfg_out32(addr, offset, val);
+ break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops xgene_pcie_ops = {
+ .read = xgene_pcie_read_config,
+ .write = xgene_pcie_write_config
+};
+
+static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr,
+ u32 flags, u64 size)
+{
+ u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags;
+ u32 val32 = 0;
+ u32 val;
+
+ val32 = readl(csr_base + addr);
+ val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16);
+ writel(val, csr_base + addr);
+
+ val32 = readl(csr_base + addr + 0x04);
+ val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16);
+ writel(val, csr_base + addr + 0x04);
+
+ val32 = readl(csr_base + addr + 0x04);
+ val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16);
+ writel(val, csr_base + addr + 0x04);
+
+ val32 = readl(csr_base + addr + 0x08);
+ val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16);
+ writel(val, csr_base + addr + 0x08);
+
+ return mask;
+}
+
+static void xgene_pcie_linkup(struct xgene_pcie_port *port,
+ u32 *lanes, u32 *speed)
+{
+ void __iomem *csr_base = port->csr_base;
+ u32 val32;
+
+ port->link_up = false;
+ val32 = readl(csr_base + PCIECORE_CTLANDSTATUS);
+ if (val32 & LINK_UP_MASK) {
+ port->link_up = true;
+ *speed = PIPE_PHY_RATE_RD(val32);
+ val32 = readl(csr_base + BRIDGE_STATUS_0);
+ *lanes = val32 >> 26;
+ }
+}
+
+static int xgene_pcie_init_port(struct xgene_pcie_port *port)
+{
+ int rc;
+
+ port->clk = clk_get(port->dev, NULL);
+ if (IS_ERR(port->clk)) {
+ dev_err(port->dev, "clock not available\n");
+ return -ENODEV;
+ }
+
+ rc = clk_prepare_enable(port->clk);
+ if (rc) {
+ dev_err(port->dev, "clock enable failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int xgene_pcie_map_reg(struct xgene_pcie_port *port,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
+ port->csr_base = devm_ioremap_resource(port->dev, res);
+ if (IS_ERR(port->csr_base))
+ return PTR_ERR(port->csr_base);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+ port->cfg_base = devm_ioremap_resource(port->dev, res);
+ if (IS_ERR(port->cfg_base))
+ return PTR_ERR(port->cfg_base);
+ port->cfg_addr = res->start;
+
+ return 0;
+}
+
+static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port,
+ struct resource *res, u32 offset,
+ u64 cpu_addr, u64 pci_addr)
+{
+ void __iomem *base = port->csr_base + offset;
+ resource_size_t size = resource_size(res);
+ u64 restype = resource_type(res);
+ u64 mask = 0;
+ u32 min_size;
+ u32 flag = EN_REG;
+
+ if (restype == IORESOURCE_MEM) {
+ min_size = SZ_128M;
+ } else {
+ min_size = 128;
+ flag |= OB_LO_IO;
+ }
+
+ if (size >= min_size)
+ mask = ~(size - 1) | flag;
+ else
+ dev_warn(port->dev, "res size 0x%llx less than minimum 0x%x\n",
+ (u64)size, min_size);
+
+ writel(lower_32_bits(cpu_addr), base);
+ writel(upper_32_bits(cpu_addr), base + 0x04);
+ writel(lower_32_bits(mask), base + 0x08);
+ writel(upper_32_bits(mask), base + 0x0c);
+ writel(lower_32_bits(pci_addr), base + 0x10);
+ writel(upper_32_bits(pci_addr), base + 0x14);
+}
+
+static void xgene_pcie_setup_cfg_reg(void __iomem *csr_base, u64 addr)
+{
+ writel(lower_32_bits(addr), csr_base + CFGBARL);
+ writel(upper_32_bits(addr), csr_base + CFGBARH);
+ writel(EN_REG, csr_base + CFGCTL);
+}
+
+static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
+ struct list_head *res,
+ resource_size_t io_base)
+{
+ struct pci_host_bridge_window *window;
+ struct device *dev = port->dev;
+ int ret;
+
+ list_for_each_entry(window, res, list) {
+ struct resource *res = window->res;
+ u64 restype = resource_type(res);
+
+ dev_dbg(port->dev, "%pR\n", res);
+
+ switch (restype) {
+ case IORESOURCE_IO:
+ xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base,
+ res->start - window->offset);
+ ret = pci_remap_iospace(res, io_base);
+ if (ret < 0)
+ return ret;
+ break;
+ case IORESOURCE_MEM:
+ xgene_pcie_setup_ob_reg(port, res, OMR1BARL, res->start,
+ res->start - window->offset);
+ break;
+ case IORESOURCE_BUS:
+ break;
+ default:
+ dev_err(dev, "invalid io resource - %pR\n", res);
+ return -EINVAL;
+ }
+ }
+ xgene_pcie_setup_cfg_reg(port->csr_base, port->cfg_addr);
+
+ return 0;
+}
+
+static void xgene_pcie_setup_pims(void *addr, u64 pim, u64 size)
+{
+ writel(lower_32_bits(pim), addr);
+ writel(upper_32_bits(pim) | EN_COHERENCY, addr + 0x04);
+ writel(lower_32_bits(size), addr + 0x10);
+ writel(upper_32_bits(size), addr + 0x14);
+}
+
+/*
+ * X-Gene PCIe support maximum 3 inbound memory regions
+ * This function helps to select a region based on size of region
+ */
+static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size)
+{
+ if ((size > 4) && (size < SZ_16M) && !(*ib_reg_mask & (1 << 1))) {
+ *ib_reg_mask |= (1 << 1);
+ return 1;
+ }
+
+ if ((size > SZ_1K) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 0))) {
+ *ib_reg_mask |= (1 << 0);
+ return 0;
+ }
+
+ if ((size > SZ_1M) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 2))) {
+ *ib_reg_mask |= (1 << 2);
+ return 2;
+ }
+
+ return -EINVAL;
+}
+
+static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
+ struct of_pci_range *range, u8 *ib_reg_mask)
+{
+ void __iomem *csr_base = port->csr_base;
+ void __iomem *cfg_base = port->cfg_base;
+ void *bar_addr;
+ void *pim_addr;
+ u64 cpu_addr = range->cpu_addr;
+ u64 pci_addr = range->pci_addr;
+ u64 size = range->size;
+ u64 mask = ~(size - 1) | EN_REG;
+ u32 flags = PCI_BASE_ADDRESS_MEM_TYPE_64;
+ u32 bar_low;
+ int region;
+
+ region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size);
+ if (region < 0) {
+ dev_warn(port->dev, "invalid pcie dma-range config\n");
+ return;
+ }
+
+ if (range->flags & IORESOURCE_PREFETCH)
+ flags |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+ bar_low = pcie_bar_low_val((u32)cpu_addr, flags);
+ switch (region) {
+ case 0:
+ xgene_pcie_set_ib_mask(csr_base, BRIDGE_CFG_4, flags, size);
+ bar_addr = cfg_base + PCI_BASE_ADDRESS_0;
+ writel(bar_low, bar_addr);
+ writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
+ pim_addr = csr_base + PIM1_1L;
+ break;
+ case 1:
+ bar_addr = csr_base + IBAR2;
+ writel(bar_low, bar_addr);
+ writel(lower_32_bits(mask), csr_base + IR2MSK);
+ pim_addr = csr_base + PIM2_1L;
+ break;
+ case 2:
+ bar_addr = csr_base + IBAR3L;
+ writel(bar_low, bar_addr);
+ writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
+ writel(lower_32_bits(mask), csr_base + IR3MSKL);
+ writel(upper_32_bits(mask), csr_base + IR3MSKL + 0x4);
+ pim_addr = csr_base + PIM3_1L;
+ break;
+ }
+
+ xgene_pcie_setup_pims(pim_addr, pci_addr, ~(size - 1));
+}
+
+static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
+ struct device_node *node)
+{
+ const int na = 3, ns = 2;
+ int rlen;
+
+ parser->node = node;
+ parser->pna = of_n_addr_cells(node);
+ parser->np = parser->pna + na + ns;
+
+ parser->range = of_get_property(node, "dma-ranges", &rlen);
+ if (!parser->range)
+ return -ENOENT;
+ parser->end = parser->range + rlen / sizeof(__be32);
+
+ return 0;
+}
+
+static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port)
+{
+ struct device_node *np = port->node;
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ struct device *dev = port->dev;
+ u8 ib_reg_mask = 0;
+
+ if (pci_dma_range_parser_init(&parser, np)) {
+ dev_err(dev, "missing dma-ranges property\n");
+ return -EINVAL;
+ }
+
+ /* Get the dma-ranges from DT */
+ for_each_of_pci_range(&parser, &range) {
+ u64 end = range.cpu_addr + range.size - 1;
+
+ dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
+ range.flags, range.cpu_addr, end, range.pci_addr);
+ xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask);
+ }
+ return 0;
+}
+
+/* clear bar configuration which was done by firmware */
+static void xgene_pcie_clear_config(struct xgene_pcie_port *port)
+{
+ int i;
+
+ for (i = PIM1_1L; i <= CFGCTL; i += 4)
+ writel(0x0, port->csr_base + i);
+}
+
+static int xgene_pcie_setup(struct xgene_pcie_port *port,
+ struct list_head *res,
+ resource_size_t io_base)
+{
+ u32 val, lanes = 0, speed = 0;
+ int ret;
+
+ xgene_pcie_clear_config(port);
+
+ /* setup the vendor and device IDs correctly */
+ val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID;
+ writel(val, port->csr_base + BRIDGE_CFG_0);
+
+ ret = xgene_pcie_map_ranges(port, res, io_base);
+ if (ret)
+ return ret;
+
+ ret = xgene_pcie_parse_map_dma_ranges(port);
+ if (ret)
+ return ret;
+
+ xgene_pcie_linkup(port, &lanes, &speed);
+ if (!port->link_up)
+ dev_info(port->dev, "(rc) link down\n");
+ else
+ dev_info(port->dev, "(rc) x%d gen-%d link up\n",
+ lanes, speed + 1);
+ return 0;
+}
+
+static int xgene_pcie_probe_bridge(struct platform_device *pdev)
+{
+ struct device_node *dn = pdev->dev.of_node;
+ struct xgene_pcie_port *port;
+ resource_size_t iobase = 0;
+ struct pci_bus *bus;
+ int ret;
+ LIST_HEAD(res);
+
+ port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+ port->node = of_node_get(pdev->dev.of_node);
+ port->dev = &pdev->dev;
+
+ ret = xgene_pcie_map_reg(port, pdev);
+ if (ret)
+ return ret;
+
+ ret = xgene_pcie_init_port(port);
+ if (ret)
+ return ret;
+
+ ret = of_pci_get_host_bridge_resources(dn, 0, 0xff, &res, &iobase);
+ if (ret)
+ return ret;
+
+ ret = xgene_pcie_setup(port, &res, iobase);
+ if (ret)
+ return ret;
+
+ bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res);
+ if (!bus)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, port);
+ return 0;
+}
+
+static const struct of_device_id xgene_pcie_match_table[] = {
+ {.compatible = "apm,xgene-pcie",},
+ {},
+};
+
+static struct platform_driver xgene_pcie_driver = {
+ .driver = {
+ .name = "xgene-pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(xgene_pcie_match_table),
+ },
+ .probe = xgene_pcie_probe_bridge,
+};
+module_platform_driver(xgene_pcie_driver);
+
+MODULE_AUTHOR("Tanmay Inamdar <tinamdar@apm.com>");
+MODULE_DESCRIPTION("APM X-Gene PCIe driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
index 4884ee5..61158e0 100644
--- a/drivers/pci/host/pcie-rcar.c
+++ b/drivers/pci/host/pcie-rcar.c
@@ -323,6 +323,7 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
/* Setup PCIe address space mappings for each resource */
resource_size_t size;
+ resource_size_t res_start;
u32 mask;
rcar_pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win));
@@ -335,8 +336,13 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
mask = (roundup_pow_of_two(size) / SZ_128) - 1;
rcar_pci_write_reg(pcie, mask << 7, PCIEPAMR(win));
- rcar_pci_write_reg(pcie, upper_32_bits(res->start), PCIEPARH(win));
- rcar_pci_write_reg(pcie, lower_32_bits(res->start), PCIEPARL(win));
+ if (res->flags & IORESOURCE_IO)
+ res_start = pci_pio_to_address(res->start);
+ else
+ res_start = res->start;
+
+ rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPARH(win));
+ rcar_pci_write_reg(pcie, lower_32_bits(res_start), PCIEPARL(win));
/* First resource is for IO */
mask = PAR_ENABLE;
@@ -363,9 +369,10 @@ static int rcar_pcie_setup(int nr, struct pci_sys_data *sys)
rcar_pcie_setup_window(i, pcie);
- if (res->flags & IORESOURCE_IO)
- pci_ioremap_io(nr * SZ_64K, res->start);
- else
+ if (res->flags & IORESOURCE_IO) {
+ phys_addr_t io_start = pci_pio_to_address(res->start);
+ pci_ioremap_io(nr * SZ_64K, io_start);
+ } else
pci_add_resource(&sys->resources, res);
}
pci_add_resource(&sys->resources, &pcie->busn);
@@ -935,8 +942,10 @@ static int rcar_pcie_probe(struct platform_device *pdev)
}
for_each_of_pci_range(&parser, &range) {
- of_pci_range_to_resource(&range, pdev->dev.of_node,
+ err = of_pci_range_to_resource(&range, pdev->dev.of_node,
&pcie->res[win++]);
+ if (err < 0)
+ return err;
if (win > RCAR_PCI_MAX_RESOURCES)
break;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 2c9ac70..6e994fc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2704,6 +2704,37 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
}
EXPORT_SYMBOL(pci_request_regions_exclusive);
+/**
+ * pci_remap_iospace - Remap the memory mapped I/O space
+ * @res: Resource describing the I/O space
+ * @phys_addr: physical address of range to be mapped
+ *
+ * Remap the memory mapped I/O space described by the @res
+ * and the CPU physical address @phys_addr into virtual address space.
+ * Only architectures that have memory mapped IO functions defined
+ * (and the PCI_IOBASE value defined) should call this function.
+ */
+int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
+{
+#if defined(PCI_IOBASE) && defined(CONFIG_MMU)
+ unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start;
+
+ if (!(res->flags & IORESOURCE_IO))
+ return -EINVAL;
+
+ if (res->end > IO_SPACE_LIMIT)
+ return -EINVAL;
+
+ return ioremap_page_range(vaddr, vaddr + resource_size(res), phys_addr,
+ pgprot_device(PAGE_KERNEL));
+#else
+ /* this architecture does not have memory mapped I/O space,
+ so this function should never be called */
+ WARN_ONCE(1, "This architecture does not support memory mapped I/O\n");
+ return -ENODEV;
+#endif
+}
+
static void __pci_set_master(struct pci_dev *dev, bool enable)
{
u16 old_cmd, cmd;
@@ -4406,6 +4437,15 @@ static void pci_no_domains(void)
#endif
}
+#ifdef CONFIG_PCI_DOMAINS
+static atomic_t __domain_nr = ATOMIC_INIT(-1);
+
+int pci_get_new_domain_nr(void)
+{
+ return atomic_inc_return(&__domain_nr);
+}
+#endif
+
/**
* pci_ext_cfg_avail - can we access extended PCI config space?
*
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 4170113..c3cec34 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -485,7 +485,7 @@ void pci_read_bridge_bases(struct pci_bus *child)
}
}
-static struct pci_bus *pci_alloc_bus(void)
+static struct pci_bus *pci_alloc_bus(struct pci_bus *parent)
{
struct pci_bus *b;
@@ -500,6 +500,10 @@ static struct pci_bus *pci_alloc_bus(void)
INIT_LIST_HEAD(&b->resources);
b->max_bus_speed = PCI_SPEED_UNKNOWN;
b->cur_bus_speed = PCI_SPEED_UNKNOWN;
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+ if (parent)
+ b->domain_nr = parent->domain_nr;
+#endif
return b;
}
@@ -515,7 +519,7 @@ static void pci_release_host_bridge_dev(struct device *dev)
kfree(bridge);
}
-static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
+static struct pci_host_bridge *pci_alloc_host_bridge(void)
{
struct pci_host_bridge *bridge;
@@ -524,7 +528,8 @@ static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b)
return NULL;
INIT_LIST_HEAD(&bridge->windows);
- bridge->bus = b;
+ bridge->dev.release = pci_release_host_bridge_dev;
+
return bridge;
}
@@ -671,7 +676,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
/*
* Allocate a new bus, and inherit stuff from the parent..
*/
- child = pci_alloc_bus();
+ child = pci_alloc_bus(parent);
if (!child)
return NULL;
@@ -1751,37 +1756,37 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
char bus_addr[64];
char *fmt;
- b = pci_alloc_bus();
- if (!b)
+ bridge = pci_alloc_host_bridge();
+ if (!bridge)
return NULL;
+ bridge->dev.parent = parent;
+
+ b = pci_alloc_bus(NULL);
+ if (!b)
+ goto err_out;
+
b->sysdata = sysdata;
b->ops = ops;
b->number = b->busn_res.start = bus;
+ pci_bus_assign_domain_nr(b, parent);
b2 = pci_find_bus(pci_domain_nr(b), bus);
if (b2) {
/* If we already got to this bus through a different bridge, ignore it */
dev_dbg(&b2->dev, "bus already known\n");
- goto err_out;
+ goto err_bus_out;
}
- bridge = pci_alloc_host_bridge(b);
- if (!bridge)
- goto err_out;
-
- bridge->dev.parent = parent;
- bridge->dev.release = pci_release_host_bridge_dev;
+ bridge->bus = b;
dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
error = pcibios_root_bridge_prepare(bridge);
- if (error) {
- kfree(bridge);
+ if (error)
goto err_out;
- }
error = device_register(&bridge->dev);
if (error) {
put_device(&bridge->dev);
- goto err_out;
+ goto err_bus_out;
}
b->bridge = get_device(&bridge->dev);
device_enable_async_suspend(b->bridge);
@@ -1838,8 +1843,10 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
class_dev_reg_err:
put_device(&bridge->dev);
device_unregister(&bridge->dev);
-err_out:
+err_bus_out:
kfree(b);
+err_out:
+ kfree(bridge);
return NULL;
}
@@ -1936,6 +1943,9 @@ struct pci_bus *pci_scan_root_bus(struct device *parent, int bus,
if (!found)
pci_bus_update_busn_res_end(b, max);
+ if (!pci_has_flag(PCI_PROBE_ONLY))
+ pci_assign_unassigned_bus_resources(b);
+
pci_bus_add_devices(b);
return b;
}
diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c
index 782e822..d952462 100644
--- a/drivers/pnp/resource.c
+++ b/drivers/pnp/resource.c
@@ -313,6 +313,7 @@ static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci,
progif = class & 0xff;
class >>= 8;
+#ifdef HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ
if (class == PCI_CLASS_STORAGE_IDE) {
/*
* Unless both channels are native-PCI mode only,
@@ -326,6 +327,7 @@ static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci,
return 1;
}
}
+#endif /* HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ */
return 0;
}
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index b24aa01..50fe279 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -419,4 +419,10 @@ config DA_CONSOLE
help
This enables a console on a Dash channel.
+config SBSAUART_TTY
+ tristate "SBSA UART TTY Driver"
+ help
+ Console and system TTY driver for the SBSA UART which is defined
+ in the Server Base System Architecure document for ARM64 servers.
+
endif # TTY
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 58ad1c0..c3211c0 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -29,5 +29,6 @@ obj-$(CONFIG_SYNCLINK) += synclink.o
obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o
obj-$(CONFIG_DA_TTY) += metag_da.o
+obj-$(CONFIG_SBSAUART_TTY) += sbsauart.o
obj-y += ipwireless/
diff --git a/drivers/tty/sbsauart.c b/drivers/tty/sbsauart.c
new file mode 100644
index 0000000..402f168
--- /dev/null
+++ b/drivers/tty/sbsauart.c
@@ -0,0 +1,355 @@
+/*
+ * SBSA (Server Base System Architecture) Compatible UART driver
+ *
+ * Copyright (C) 2014 Linaro Ltd
+ *
+ * Author: Graeme Gregory <graeme.gregory@linaro.org>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/amba/serial.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+struct sbsa_tty {
+ struct tty_port port;
+ spinlock_t lock;
+ void __iomem *base;
+ u32 irq;
+ int opencount;
+ struct console console;
+};
+
+static struct tty_driver *sbsa_tty_driver;
+static struct sbsa_tty *sbsa_tty;
+
+#define SBSAUART_CHAR_MASK 0xFF
+
+static void sbsa_raw_putc(struct uart_port *port, int c)
+{
+ while (readw(port->membase + UART01x_FR) & UART01x_FR_TXFF)
+ ;
+ writew(c & 0xFF, port->membase + UART01x_DR);
+}
+
+static void sbsa_uart_early_write(struct console *con, const char *buf,
+ unsigned count)
+{
+ struct earlycon_device *dev = con->data;
+
+ uart_console_write(&dev->port, buf, count, sbsa_raw_putc);
+}
+
+static int __init sbsa_uart_early_console_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->con->write = sbsa_uart_early_write;
+ return 0;
+}
+EARLYCON_DECLARE(sbsauart, sbsa_uart_early_console_setup);
+
+static void sbsa_tty_do_write(const char *buf, unsigned count)
+{
+ unsigned long irq_flags;
+ struct sbsa_tty *qtty = sbsa_tty;
+ void __iomem *base = qtty->base;
+ unsigned n;
+
+ spin_lock_irqsave(&qtty->lock, irq_flags);
+ for (n = 0; n < count; n++) {
+ while (readw(base + UART01x_FR) & UART01x_FR_TXFF)
+ ;
+ writew(buf[n], base + UART01x_DR);
+ }
+ spin_unlock_irqrestore(&qtty->lock, irq_flags);
+}
+
+static void sbsauart_fifo_to_tty(struct sbsa_tty *qtty)
+{
+ void __iomem *base = qtty->base;
+ unsigned int flag, max_count = 32;
+ u16 status, ch;
+
+ while (max_count--) {
+ status = readw(base + UART01x_FR);
+ if (status & UART01x_FR_RXFE)
+ break;
+
+ /* Take chars from the FIFO and update status */
+ ch = readw(base + UART01x_DR);
+ flag = TTY_NORMAL;
+
+ if (ch & UART011_DR_BE)
+ flag = TTY_BREAK;
+ else if (ch & UART011_DR_PE)
+ flag = TTY_PARITY;
+ else if (ch & UART011_DR_FE)
+ flag = TTY_FRAME;
+ else if (ch & UART011_DR_OE)
+ flag = TTY_OVERRUN;
+
+ ch &= SBSAUART_CHAR_MASK;
+
+ tty_insert_flip_char(&qtty->port, ch, flag);
+ }
+
+ tty_schedule_flip(&qtty->port);
+
+ /* Clear the RX IRQ */
+ writew(UART011_RXIC | UART011_RXIC, base + UART011_ICR);
+}
+
+static irqreturn_t sbsa_tty_interrupt(int irq, void *dev_id)
+{
+ struct sbsa_tty *qtty = sbsa_tty;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&qtty->lock, irq_flags);
+ sbsauart_fifo_to_tty(qtty);
+ spin_unlock_irqrestore(&qtty->lock, irq_flags);
+
+ return IRQ_HANDLED;
+}
+
+static int sbsa_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ struct sbsa_tty *qtty = sbsa_tty;
+
+ return tty_port_open(&qtty->port, tty, filp);
+}
+
+static void sbsa_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ tty_port_close(tty->port, tty, filp);
+}
+
+static void sbsa_tty_hangup(struct tty_struct *tty)
+{
+ tty_port_hangup(tty->port);
+}
+
+static int sbsa_tty_write(struct tty_struct *tty, const unsigned char *buf,
+ int count)
+{
+ sbsa_tty_do_write(buf, count);
+ return count;
+}
+
+static int sbsa_tty_write_room(struct tty_struct *tty)
+{
+ return 32;
+}
+
+static void sbsa_tty_console_write(struct console *co, const char *b,
+ unsigned count)
+{
+ sbsa_tty_do_write(b, count);
+
+ if (b[count - 1] == '\n')
+ sbsa_tty_do_write("\r", 1);
+}
+
+static struct tty_driver *sbsa_tty_console_device(struct console *c,
+ int *index)
+{
+ *index = c->index;
+ return sbsa_tty_driver;
+}
+
+static int sbsa_tty_console_setup(struct console *co, char *options)
+{
+ if ((unsigned)co->index > 0)
+ return -ENODEV;
+ if (sbsa_tty->base == NULL)
+ return -ENODEV;
+ return 0;
+}
+
+static struct tty_port_operations sbsa_port_ops = {
+};
+
+static const struct tty_operations sbsa_tty_ops = {
+ .open = sbsa_tty_open,
+ .close = sbsa_tty_close,
+ .hangup = sbsa_tty_hangup,
+ .write = sbsa_tty_write,
+ .write_room = sbsa_tty_write_room,
+};
+
+static int sbsa_tty_create_driver(void)
+{
+ int ret;
+ struct tty_driver *tty;
+
+ sbsa_tty = kzalloc(sizeof(*sbsa_tty), GFP_KERNEL);
+ if (sbsa_tty == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_sbsa_tty_failed;
+ }
+ tty = alloc_tty_driver(1);
+ if (tty == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_tty_driver_failed;
+ }
+ tty->driver_name = "sbsauart";
+ tty->name = "ttySBSA";
+ tty->type = TTY_DRIVER_TYPE_SERIAL;
+ tty->subtype = SERIAL_TYPE_NORMAL;
+ tty->init_termios = tty_std_termios;
+ tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(tty, &sbsa_tty_ops);
+ ret = tty_register_driver(tty);
+ if (ret)
+ goto err_tty_register_driver_failed;
+
+ sbsa_tty_driver = tty;
+ return 0;
+
+err_tty_register_driver_failed:
+ put_tty_driver(tty);
+err_alloc_tty_driver_failed:
+ kfree(sbsa_tty);
+ sbsa_tty = NULL;
+err_alloc_sbsa_tty_failed:
+ return ret;
+}
+
+static void sbsa_tty_delete_driver(void)
+{
+ tty_unregister_driver(sbsa_tty_driver);
+ put_tty_driver(sbsa_tty_driver);
+ sbsa_tty_driver = NULL;
+ kfree(sbsa_tty);
+ sbsa_tty = NULL;
+}
+
+static int sbsa_tty_probe(struct platform_device *pdev)
+{
+ struct sbsa_tty *qtty;
+ int ret = -EINVAL;
+ int i;
+ struct resource *r;
+ struct device *ttydev;
+ void __iomem *base;
+ u32 irq;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL)
+ return -EINVAL;
+
+ base = ioremap(r->start, r->end - r->start);
+ if (base == NULL)
+ pr_err("sbsa_tty: unable to remap base\n");
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (r == NULL)
+ goto err_unmap;
+
+ irq = r->start;
+
+ if (pdev->id > 0)
+ goto err_unmap;
+
+ ret = sbsa_tty_create_driver();
+ if (ret)
+ goto err_unmap;
+
+ qtty = sbsa_tty;
+ spin_lock_init(&qtty->lock);
+ tty_port_init(&qtty->port);
+ qtty->port.ops = &sbsa_port_ops;
+ qtty->base = base;
+ qtty->irq = irq;
+
+ /* Clear and Mask all IRQs */
+ writew(0, base + UART011_IMSC);
+ writew(0xFFFF, base + UART011_ICR);
+
+ ret = request_irq(irq, sbsa_tty_interrupt, IRQF_SHARED,
+ "sbsa_tty", pdev);
+ if (ret)
+ goto err_request_irq_failed;
+
+ /* Unmask the RX IRQ */
+ writew(UART011_RXIM | UART011_RTIM, base + UART011_IMSC);
+
+ ttydev = tty_port_register_device(&qtty->port, sbsa_tty_driver,
+ 0, &pdev->dev);
+ if (IS_ERR(ttydev)) {
+ ret = PTR_ERR(ttydev);
+ goto err_tty_register_device_failed;
+ }
+
+ strcpy(qtty->console.name, "ttySBSA");
+ qtty->console.write = sbsa_tty_console_write;
+ qtty->console.device = sbsa_tty_console_device;
+ qtty->console.setup = sbsa_tty_console_setup;
+ qtty->console.flags = CON_PRINTBUFFER;
+ qtty->console.index = pdev->id;
+ register_console(&qtty->console);
+
+ return 0;
+
+ tty_unregister_device(sbsa_tty_driver, i);
+err_tty_register_device_failed:
+ free_irq(irq, pdev);
+err_request_irq_failed:
+ sbsa_tty_delete_driver();
+err_unmap:
+ iounmap(base);
+ return ret;
+}
+
+static int sbsa_tty_remove(struct platform_device *pdev)
+{
+ struct sbsa_tty *qtty;
+
+ qtty = sbsa_tty;
+ unregister_console(&qtty->console);
+ tty_unregister_device(sbsa_tty_driver, pdev->id);
+ iounmap(qtty->base);
+ qtty->base = 0;
+ free_irq(qtty->irq, pdev);
+ sbsa_tty_delete_driver();
+ return 0;
+}
+
+static const struct acpi_device_id sbsa_acpi_match[] = {
+ { "ARMH0011", 0 },
+ { }
+};
+
+static struct platform_driver sbsa_tty_platform_driver = {
+ .probe = sbsa_tty_probe,
+ .remove = sbsa_tty_remove,
+ .driver = {
+ .name = "sbsa_tty",
+ .acpi_match_table = ACPI_PTR(sbsa_acpi_match),
+ }
+};
+
+module_platform_driver(sbsa_tty_platform_driver);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 57d9df8..e075437 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -313,10 +313,18 @@ static int dw8250_probe_of(struct uart_port *p,
static int dw8250_probe_acpi(struct uart_8250_port *up,
struct dw8250_data *data)
{
+ const struct acpi_device_id *id;
struct uart_port *p = &up->port;
dw8250_setup_port(up);
+ id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
+ if (!id)
+ return -ENODEV;
+
+ if (!p->uartclk)
+ p->uartclk = (unsigned int)id->driver_data;
+
p->iotype = UPIO_MEM32;
p->serial_in = dw8250_serial_in32;
p->serial_out = dw8250_serial_out32;
@@ -541,6 +549,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
{ "INT3435", 0 },
{ "80860F0A", 0 },
{ "8086228A", 0 },
+ { "APMC0D08", 50000000},
{ },
};
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index c600ccf..10c4775 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -100,8 +100,7 @@
#include <linux/virtio_config.h>
#include <linux/virtio_mmio.h>
#include <linux/virtio_ring.h>
-
-
+#include <linux/acpi.h>
/* The alignment to use between consumer and producer parts of vring.
* Currently hardcoded to the page size. */
@@ -637,6 +636,14 @@ static struct of_device_id virtio_mmio_match[] = {
};
MODULE_DEVICE_TABLE(of, virtio_mmio_match);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id virtio_mmio_acpi_match[] = {
+ { "LNRO0005", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match);
+#endif
+
static struct platform_driver virtio_mmio_driver = {
.probe = virtio_mmio_probe,
.remove = virtio_mmio_remove,
@@ -644,6 +651,7 @@ static struct platform_driver virtio_mmio_driver = {
.name = "virtio-mmio",
.owner = THIS_MODULE,
.of_match_table = virtio_mmio_match,
+ .acpi_match_table = ACPI_PTR(virtio_mmio_acpi_match),
},
};
diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h
index c728113..f97804b 100644
--- a/include/acpi/acnames.h
+++ b/include/acpi/acnames.h
@@ -59,6 +59,10 @@
#define METHOD_NAME__PRS "_PRS"
#define METHOD_NAME__PRT "_PRT"
#define METHOD_NAME__PRW "_PRW"
+#define METHOD_NAME__PS0 "_PS0"
+#define METHOD_NAME__PS1 "_PS1"
+#define METHOD_NAME__PS2 "_PS2"
+#define METHOD_NAME__PS3 "_PS3"
#define METHOD_NAME__REG "_REG"
#define METHOD_NAME__SB_ "_SB_"
#define METHOD_NAME__SEG "_SEG"
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 57ee052..a483e61 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -68,6 +68,8 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs);
union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid,
int rev, int func, union acpi_object *argv4);
+acpi_status acpi_check_coherency(acpi_handle handle, int *val);
+
static inline union acpi_object *
acpi_evaluate_dsm_typed(acpi_handle handle, const u8 *uuid, int rev, int func,
union acpi_object *argv4, acpi_object_type type)
diff --git a/include/acpi/acpi_io.h b/include/acpi/acpi_io.h
index 444671e..9d573db 100644
--- a/include/acpi/acpi_io.h
+++ b/include/acpi/acpi_io.h
@@ -1,11 +1,17 @@
#ifndef _ACPI_IO_H_
#define _ACPI_IO_H_
+#include <linux/mm.h>
#include <linux/io.h>
static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys,
acpi_size size)
{
+#ifdef CONFIG_ARM64
+ if (!page_is_ram(phys >> PAGE_SHIFT))
+ return ioremap(phys, size);
+#endif
+
return ioremap_cache(phys, size);
}
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index b7c89d4..dc9d037 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -46,7 +46,7 @@
/* Current ACPICA subsystem version in YYYYMMDD format */
-#define ACPI_CA_VERSION 0x20140724
+#define ACPI_CA_VERSION 0x20140828
#include <acpi/acconfig.h>
#include <acpi/actypes.h>
diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h
index 7626bfe..29e7937 100644
--- a/include/acpi/actbl1.h
+++ b/include/acpi/actbl1.h
@@ -952,7 +952,8 @@ enum acpi_srat_type {
ACPI_SRAT_TYPE_CPU_AFFINITY = 0,
ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1,
ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2,
- ACPI_SRAT_TYPE_RESERVED = 3 /* 3 and greater are reserved */
+ ACPI_SRAT_TYPE_GICC_AFFINITY = 3,
+ ACPI_SRAT_TYPE_RESERVED = 4 /* 4 and greater are reserved */
};
/*
@@ -968,7 +969,7 @@ struct acpi_srat_cpu_affinity {
u32 flags;
u8 local_sapic_eid;
u8 proximity_domain_hi[3];
- u32 reserved; /* Reserved, must be zero */
+ u32 clock_domain;
};
/* Flags */
@@ -1010,6 +1011,20 @@ struct acpi_srat_x2apic_cpu_affinity {
#define ACPI_SRAT_CPU_ENABLED (1) /* 00: Use affinity structure */
+/* 3: GICC Affinity (ACPI 5.1) */
+
+struct acpi_srat_gicc_affinity {
+ struct acpi_subtable_header header;
+ u32 proximity_domain;
+ u32 acpi_processor_uid;
+ u32 flags;
+ u32 clock_domain;
+};
+
+/* Flags for struct acpi_srat_gicc_affinity */
+
+#define ACPI_SRAT_GICC_ENABLED (1) /* 00: Use affinity structure */
+
/* Reset to default packing */
#pragma pack()
diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h
index 787bcc8..5480cb2 100644
--- a/include/acpi/actbl3.h
+++ b/include/acpi/actbl3.h
@@ -310,10 +310,15 @@ struct acpi_gtdt_timer_entry {
u32 common_flags;
};
+/* Flag Definitions: timer_flags and virtual_timer_flags above */
+
+#define ACPI_GTDT_GT_IRQ_MODE (1)
+#define ACPI_GTDT_GT_IRQ_POLARITY (1<<1)
+
/* Flag Definitions: common_flags above */
-#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
-#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
+#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
+#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
/* 1: SBSA Generic Watchdog Structure */
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 975e1cc..2e2161b 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -331,7 +331,7 @@ static inline void iounmap(void __iomem *addr)
#ifndef CONFIG_GENERIC_IOMAP
static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
{
- return (void __iomem *) port;
+ return (void __iomem *)(PCI_IOBASE + (port & IO_SPACE_LIMIT));
}
static inline void ioport_unmap(void __iomem *p)
diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h
index 53b2acc..977e545 100644
--- a/include/asm-generic/pgtable.h
+++ b/include/asm-generic/pgtable.h
@@ -249,6 +249,10 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b)
#define pgprot_writecombine pgprot_noncached
#endif
+#ifndef pgprot_device
+#define pgprot_device pgprot_noncached
+#endif
+
/*
* When walking page tables, get the address of the next boundary,
* or the end address of the range if that comes earlier. Although no
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 35b0c12..d98e96b 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -237,17 +237,19 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run,
#define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel))
#define vgic_initialized(k) ((k)->arch.vgic.ready)
-int vgic_v2_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params);
+int vgic_v2_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params);
+int vgic_v2_acpi_probe(const struct vgic_ops **ops,
+ const struct vgic_params **params);
#ifdef CONFIG_ARM_GIC_V3
-int vgic_v3_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params);
+int vgic_v3_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params);
#else
-static inline int vgic_v3_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params)
+static inline int vgic_v3_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
{
return -ENODEV;
}
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 807cbc4..4615eb1 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -71,6 +71,7 @@ enum acpi_irq_model_id {
ACPI_IRQ_MODEL_IOAPIC,
ACPI_IRQ_MODEL_IOSAPIC,
ACPI_IRQ_MODEL_PLATFORM,
+ ACPI_IRQ_MODEL_GIC,
ACPI_IRQ_MODEL_COUNT
};
@@ -123,6 +124,10 @@ int acpi_numa_init (void);
int acpi_table_init (void);
int acpi_table_parse(char *id, acpi_tbl_table_handler handler);
+int __init acpi_parse_entries(unsigned long table_size,
+ acpi_tbl_entry_handler handler,
+ struct acpi_table_header *table_header,
+ int entry_id, unsigned int max_entries);
int __init acpi_table_parse_entries(char *id, unsigned long table_size,
int entry_id,
acpi_tbl_entry_handler handler,
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 653f0e2..5839f98 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -346,4 +346,10 @@ extern void clocksource_of_init(void);
static inline void clocksource_of_init(void) {}
#endif
+#ifdef CONFIG_ACPI
+void acpi_generic_timer_init(void);
+#else
+static inline void acpi_generic_timer_init(void) {}
+#endif
+
#endif /* _LINUX_CLOCKSOURCE_H */
diff --git a/include/linux/irqchip/arm-gic-acpi.h b/include/linux/irqchip/arm-gic-acpi.h
new file mode 100644
index 0000000..ad5b577
--- /dev/null
+++ b/include/linux/irqchip/arm-gic-acpi.h
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+void acpi_gic_init(void);
+int gic_v2_acpi_init(struct acpi_table_header *table);
+#else
+static inline void acpi_gic_init(void) { }
+#endif
+
+#endif /* ARM_GIC_ACPI_H_ */
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index 45e2d8c..2b0f246 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -39,6 +39,8 @@
#define GIC_DIST_SGI_PENDING_CLEAR 0xf10
#define GIC_DIST_SGI_PENDING_SET 0xf20
+#define GIC_DIST_SOFTINT_NSATT 0x8000
+
#define GICH_HCR 0x0
#define GICH_VTR 0x4
#define GICH_VMCR 0x8
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index fb7b722..7ebb877 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -23,17 +23,6 @@ struct of_pci_range {
#define for_each_of_pci_range(parser, range) \
for (; of_pci_range_parser_one(parser, range);)
-static inline void of_pci_range_to_resource(struct of_pci_range *range,
- struct device_node *np,
- struct resource *res)
-{
- res->flags = range->flags;
- res->start = range->cpu_addr;
- res->end = range->cpu_addr + range->size - 1;
- res->parent = res->child = res->sibling = NULL;
- res->name = np->full_name;
-}
-
/* Translate a DMA address from device space to CPU space */
extern u64 of_translate_dma_address(struct device_node *dev,
const __be32 *in_addr);
@@ -55,7 +44,9 @@ extern void __iomem *of_iomap(struct device_node *device, int index);
extern const __be32 *of_get_address(struct device_node *dev, int index,
u64 *size, unsigned int *flags);
+extern int pci_register_io_range(phys_addr_t addr, resource_size_t size);
extern unsigned long pci_address_to_pio(phys_addr_t addr);
+extern phys_addr_t pci_pio_to_address(unsigned long pio);
extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
struct device_node *node);
@@ -138,6 +129,9 @@ extern const __be32 *of_get_pci_address(struct device_node *dev, int bar_no,
u64 *size, unsigned int *flags);
extern int of_pci_address_to_resource(struct device_node *dev, int bar,
struct resource *r);
+extern int of_pci_range_to_resource(struct of_pci_range *range,
+ struct device_node *np,
+ struct resource *res);
#else /* CONFIG_OF_ADDRESS && CONFIG_PCI */
static inline int of_pci_address_to_resource(struct device_node *dev, int bar,
struct resource *r)
@@ -153,4 +147,3 @@ static inline const __be32 *of_get_pci_address(struct device_node *dev,
#endif /* CONFIG_OF_ADDRESS && CONFIG_PCI */
#endif /* __OF_ADDRESS_H */
-
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index dde3a4a..1fd207e 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -15,6 +15,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent,
int of_pci_get_devfn(struct device_node *np);
int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
+int of_get_pci_domain_nr(struct device_node *node);
#else
static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
{
@@ -43,6 +44,18 @@ of_pci_parse_bus_range(struct device_node *node, struct resource *res)
{
return -EINVAL;
}
+
+static inline int
+of_get_pci_domain_nr(struct device_node *node)
+{
+ return -1;
+}
+#endif
+
+#if defined(CONFIG_OF_ADDRESS)
+int of_pci_get_host_bridge_resources(struct device_node *dev,
+ unsigned char busno, unsigned char bus_max,
+ struct list_head *resources, resource_size_t *io_base);
#endif
#if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI)
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 96453f9..6d540b9 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -457,6 +457,9 @@ struct pci_bus {
unsigned char primary; /* number of primary bridge */
unsigned char max_bus_speed; /* enum pci_bus_speed */
unsigned char cur_bus_speed; /* enum pci_bus_speed */
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+ int domain_nr;
+#endif
char name[48];
@@ -559,15 +562,6 @@ struct pci_ops {
int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
};
-/*
- * ACPI needs to be able to access PCI config space before we've done a
- * PCI bus scan and created pci_bus structures.
- */
-int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
- int reg, int len, u32 *val);
-int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
- int reg, int len, u32 val);
-
struct pci_bus_region {
dma_addr_t start;
dma_addr_t end;
@@ -1103,6 +1097,9 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
resource_size_t),
void *alignf_data);
+
+int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
+
static inline dma_addr_t pci_bus_address(struct pci_dev *pdev, int bar)
{
struct pci_bus_region region;
@@ -1288,17 +1285,47 @@ void pci_cfg_access_unlock(struct pci_dev *dev);
*/
#ifdef CONFIG_PCI_DOMAINS
extern int pci_domains_supported;
+int pci_get_new_domain_nr(void);
#else
enum { pci_domains_supported = 0 };
static inline int pci_domain_nr(struct pci_bus *bus) { return 0; }
static inline int pci_proc_domain(struct pci_bus *bus) { return 0; }
+static inline int pci_get_new_domain_nr(void) { return -ENOSYS; }
#endif /* CONFIG_PCI_DOMAINS */
+/*
+ * Generic implementation for PCI domain support. If your
+ * architecture does not need custom management of PCI
+ * domains then this implementation will be used
+ */
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+static inline int pci_domain_nr(struct pci_bus *bus)
+{
+ return bus->domain_nr;
+}
+void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent);
+#else
+static inline void pci_bus_assign_domain_nr(struct pci_bus *bus,
+ struct device *parent)
+{
+}
+#endif
+
/* some architectures require additional setup to direct VGA traffic */
typedef int (*arch_set_vga_state_t)(struct pci_dev *pdev, bool decode,
unsigned int command_bits, u32 flags);
void pci_register_set_vga_state(arch_set_vga_state_t func);
+/*
+ * ACPI needs to be able to access PCI config space before we've done a
+ * PCI bus scan and created pci_bus structures.
+ */
+int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
+ int reg, int len, u32 *val);
+int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
+ int reg, int len, u32 val);
+void pcibios_penalize_isa_irq(int irq, int active);
+
#else /* CONFIG_PCI is not enabled */
/*
@@ -1400,8 +1427,26 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus,
unsigned int devfn)
{ return NULL; }
+static inline struct pci_bus *pci_find_bus(int domain, int busnr)
+{ return NULL; }
+
+static inline int pci_bus_write_config_byte(struct pci_bus *bus,
+ unsigned int devfn, int where, u8 val)
+{ return -ENOSYS; }
+
+static inline int raw_pci_read(unsigned int domain, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 *val)
+{ return -ENOSYS; }
+
+static inline int raw_pci_write(unsigned int domain, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 val)
+{ return -ENOSYS; }
+
+static inline void pcibios_penalize_isa_irq(int irq, int active) { }
+
static inline int pci_domain_nr(struct pci_bus *bus) { return 0; }
static inline struct pci_dev *pci_dev_get(struct pci_dev *dev) { return NULL; }
+static inline int pci_get_new_domain_nr(void) { return -ENOSYS; }
#define dev_is_pci(d) (false)
#define dev_is_pf(d) (false)
@@ -1613,7 +1658,6 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev,
enum pcie_reset_state state);
int pcibios_add_device(struct pci_dev *dev);
void pcibios_release_device(struct pci_dev *dev);
-void pcibios_penalize_isa_irq(int irq, int active);
#ifdef CONFIG_HIBERNATE_CALLBACKS
extern struct dev_pm_ops pcibios_pm_ops;
diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h
index e9441b9..1d3f39c 100644
--- a/tools/perf/arch/arm64/include/perf_regs.h
+++ b/tools/perf/arch/arm64/include/perf_regs.h
@@ -6,6 +6,8 @@
#include <asm/perf_regs.h>
#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1)
+#define PERF_REGS_MAX PERF_REG_ARM64_MAX
+
#define PERF_REG_IP PERF_REG_ARM64_PC
#define PERF_REG_SP PERF_REG_ARM64_SP
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 22fa819..9cd5dbd 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -21,9 +21,11 @@
#include <linux/kvm.h>
#include <linux/kvm_host.h>
#include <linux/interrupt.h>
+#include <linux/acpi.h>
#include <clocksource/arm_arch_timer.h>
#include <asm/arch_timer.h>
+#include <asm/acpi.h>
#include <kvm/arm_vgic.h>
#include <kvm/arm_arch_timer.h>
@@ -244,60 +246,92 @@ static const struct of_device_id arch_timer_of_match[] = {
{},
};
-int kvm_timer_hyp_init(void)
+static int kvm_timer_ppi_parse_dt(unsigned int *ppi)
{
struct device_node *np;
- unsigned int ppi;
- int err;
-
- timecounter = arch_timer_get_timecounter();
- if (!timecounter)
- return -ENODEV;
np = of_find_matching_node(NULL, arch_timer_of_match);
if (!np) {
- kvm_err("kvm_arch_timer: can't find DT node\n");
return -ENODEV;
}
- ppi = irq_of_parse_and_map(np, 2);
- if (!ppi) {
- kvm_err("kvm_arch_timer: no virtual timer interrupt\n");
- err = -EINVAL;
- goto out;
+ *ppi = irq_of_parse_and_map(np, 2);
+ if (*ppi == 0) {
+ of_node_put(np);
+ return -EINVAL;
}
- err = request_percpu_irq(ppi, kvm_arch_timer_handler,
- "kvm guest timer", kvm_get_running_vcpus());
- if (err) {
- kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
- ppi, err);
- goto out;
- }
+ return 0;
+}
- host_vtimer_irq = ppi;
+extern int acadia_kvm_acpi;
+extern int arch_timer_ppi[];
- err = __register_cpu_notifier(&kvm_timer_cpu_nb);
- if (err) {
- kvm_err("Cannot register timer CPU notifier\n");
- goto out_free;
- }
+static int kvm_timer_ppi_parse_acpi(unsigned int *ppi)
- wqueue = create_singlethread_workqueue("kvm_arch_timer");
- if (!wqueue) {
- err = -ENOMEM;
- goto out_free;
- }
+{
+ /* retrieve VIRT_PPI info */
+ *ppi = arch_timer_ppi[2];
- kvm_info("%s IRQ%d\n", np->name, ppi);
- on_each_cpu(kvm_timer_init_interrupt, NULL, 1);
+ if (*ppi == 0)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+int kvm_timer_hyp_init(void)
+{
+ unsigned int ppi;
+ int err;
+
+ timecounter = arch_timer_get_timecounter();
+ if (!timecounter)
+ return -ENODEV;
+
+ /* PPI DT parsing */
+ err = kvm_timer_ppi_parse_dt(&ppi);
- goto out;
+ /* if DT parsing fails, try ACPI next */
+ if (err && !acpi_disabled && acadia_kvm_acpi )
+ err = kvm_timer_ppi_parse_acpi(&ppi);
+
+ if (err) {
+ kvm_err("kvm_timer_hyp_init: can't find virtual timer info or "
+ "config virtual timer interrupt\n");
+ return err;
+ }
+
+ /* configure IRQ handler */
+ err = request_percpu_irq(ppi, kvm_arch_timer_handler,
+ "kvm guest timer", kvm_get_running_vcpus());
+ if (err) {
+ kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
+ ppi, err);
+ goto out;
+ }
+
+ host_vtimer_irq = ppi;
+
+ err = __register_cpu_notifier(&kvm_timer_cpu_nb);
+ if (err) {
+ kvm_err("Cannot register timer CPU notifier\n");
+ goto out_free;
+ }
+
+ wqueue = create_singlethread_workqueue("kvm_arch_timer");
+ if (!wqueue) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ kvm_info("timer IRQ%d\n", ppi);
+ on_each_cpu(kvm_timer_init_interrupt, NULL, 1);
+
+ goto out;
out_free:
- free_percpu_irq(ppi, kvm_get_running_vcpus());
+ free_percpu_irq(ppi, kvm_get_running_vcpus());
out:
- of_node_put(np);
- return err;
+ return err;
}
void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu)
diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c
index 416baed..53bdd33 100644
--- a/virt/kvm/arm/vgic-v2.c
+++ b/virt/kvm/arm/vgic-v2.c
@@ -19,6 +19,7 @@
#include <linux/kvm.h>
#include <linux/kvm_host.h>
#include <linux/interrupt.h>
+#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -26,6 +27,7 @@
#include <linux/irqchip/arm-gic.h>
+#include <asm/acpi.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_mmu.h>
@@ -177,7 +179,7 @@ static const struct vgic_ops vgic_v2_ops = {
static struct vgic_params vgic_v2_params;
/**
- * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
+ * vgic_v2_dt_probe - probe for a GICv2 compatible interrupt controller in DT
* @node: pointer to the DT node
* @ops: address of a pointer to the GICv2 operations
* @params: address of a pointer to HW-specific parameters
@@ -186,7 +188,7 @@ static struct vgic_params vgic_v2_params;
* in *ops and the HW parameters in *params. Returns an error code
* otherwise.
*/
-int vgic_v2_probe(struct device_node *vgic_node,
+int vgic_v2_dt_probe(struct device_node *vgic_node,
const struct vgic_ops **ops,
const struct vgic_params **params)
{
@@ -263,3 +265,72 @@ out:
of_node_put(vgic_node);
return ret;
}
+
+struct acpi_madt_generic_interrupt *vgic_acpi;
+static void gic_get_acpi_header(struct acpi_subtable_header *header)
+{
+ vgic_acpi = (struct acpi_madt_generic_interrupt *)header;
+}
+
+int vgic_v2_acpi_probe(const struct vgic_ops **ops,
+ const struct vgic_params **params)
+{
+ struct vgic_params *vgic = &vgic_v2_params;
+ int irq_mode, ret;
+
+ /* MADT table */
+ ret = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ (acpi_tbl_entry_handler)gic_get_acpi_header, 0);
+ if (!ret) {
+ pr_err("Failed to get MADT VGIC CPU entry\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* IRQ trigger mode */
+ irq_mode = (vgic_acpi->flags & ACPI_MADT_VGIC_IRQ_MODE) ?
+ ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE;
+ /* According to GIC-400 manual, all PPIs are active-LOW, level
+ * sensative. We register IRQ as active-low.
+ */
+ vgic->maint_irq = acpi_register_gsi(NULL, vgic_acpi->vgic_interrupt,
+ irq_mode, ACPI_ACTIVE_LOW);
+ if (!vgic->maint_irq) {
+ pr_err("Cannot register VGIC ACPI maintenance irq\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ /* GICH resource */
+ vgic->vctrl_base = ioremap(vgic_acpi->gich_base_address, SZ_8K);
+ if (!vgic->vctrl_base) {
+ pr_err("cannot ioremap GICH memory\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR);
+ vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1;
+
+ ret = create_hyp_io_mappings(vgic->vctrl_base,
+ vgic->vctrl_base + SZ_8K,
+ vgic_acpi->gich_base_address);
+ if (ret) {
+ kvm_err("Cannot map GICH into hyp\n");
+ goto out;
+ }
+
+ vgic->vcpu_base = vgic_acpi->gicv_base_address;
+
+ kvm_info("GICH base=0x%llx, GICV base=0x%llx, IRQ=%d\n",
+ (unsigned long long)vgic_acpi->gich_base_address,
+ (unsigned long long)vgic_acpi->gicv_base_address,
+ vgic->maint_irq);
+
+ vgic->type = VGIC_V2;
+ *ops = &vgic_v2_ops;
+ *params = vgic;
+
+out:
+ return ret;
+}
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
index 1c2c8ee..8b56920 100644
--- a/virt/kvm/arm/vgic-v3.c
+++ b/virt/kvm/arm/vgic-v3.c
@@ -173,7 +173,7 @@ static const struct vgic_ops vgic_v3_ops = {
static struct vgic_params vgic_v3_params;
/**
- * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT
+ * vgic_v3_dt_probe - probe for a GICv3 compatible interrupt controller in DT
* @node: pointer to the DT node
* @ops: address of a pointer to the GICv3 operations
* @params: address of a pointer to HW-specific parameters
@@ -182,9 +182,9 @@ static struct vgic_params vgic_v3_params;
* in *ops and the HW parameters in *params. Returns an error code
* otherwise.
*/
-int vgic_v3_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params)
+int vgic_v3_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
{
int ret = 0;
u32 gicv_idx;
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index 73eba79..ca98a3b 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -25,9 +25,11 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/uaccess.h>
+#include <linux/acpi.h>
#include <linux/irqchip/arm-gic.h>
+#include <asm/acpi.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_mmu.h>
@@ -1549,31 +1551,39 @@ static struct notifier_block vgic_cpu_nb = {
};
static const struct of_device_id vgic_ids[] = {
- { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, },
- { .compatible = "arm,gic-v3", .data = vgic_v3_probe, },
+ { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_dt_probe, },
+ { .compatible = "arm,gic-v3", .data = vgic_v3_dt_probe, },
{},
};
+extern int acadia_kvm_acpi;
+
int kvm_vgic_hyp_init(void)
{
const struct of_device_id *matched_id;
int (*vgic_probe)(struct device_node *,const struct vgic_ops **,
const struct vgic_params **);
struct device_node *vgic_node;
- int ret;
+ int ret = -ENODEV;
- vgic_node = of_find_matching_node_and_match(NULL,
- vgic_ids, &matched_id);
- if (!vgic_node) {
- kvm_err("error: no compatible GIC node found\n");
- return -ENODEV;
+ /* probe VGIC */
+ if (vgic_node = of_find_matching_node_and_match(NULL,
+ vgic_ids, &matched_id)) {
+ /* probe VGIC in DT */
+ vgic_probe = matched_id->data;
+ ret = vgic_probe(vgic_node, &vgic_ops, &vgic);
+ }
+ else if (!acpi_disabled && acadia_kvm_acpi) {
+ /* probe VGIC in ACPI */
+ ret = vgic_v2_acpi_probe(&vgic_ops, &vgic);
}
- vgic_probe = matched_id->data;
- ret = vgic_probe(vgic_node, &vgic_ops, &vgic);
- if (ret)
+ if (ret) {
+ kvm_err("error: no compatible GIC info found\n");
return ret;
+ }
+ /* configuration */
ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler,
"vgic", kvm_get_running_vcpus());
if (ret) {