13198 lines
405 KiB
Diff
13198 lines
405 KiB
Diff
commit ccdf75caa6a1165b8199930983596ef64cf09bd3
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Mon Nov 10 17:09:29 2014 -0500
|
|
|
|
DO NOT UPSTREAM - pci/xgene: Provide fixup for ACPI MCFG support
|
|
|
|
Xgene doesn't decode bus bits of mmconfig region and only
|
|
supports devfn 0 of bus 0. For other buses/devices, some
|
|
internal registers need to be poked. This patch provides
|
|
a fixup to support ACPI MCFG tables. This is a horrible
|
|
hack allowing the hardware to be used for PCI testing, but
|
|
it is not intended to be a long term patch.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit 4421965a8ea7caae3f50760abce1094e0186c05e
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Mon Nov 10 17:33:18 2014 -0500
|
|
|
|
DO NOT UPSTREAM - provide hook for MCFG fixups
|
|
|
|
This is a temprary mechanism needed by at least one early
|
|
arm64 hardware platform with broken MCFG support. This is
|
|
not intended for upstream and will go away as soon as newer
|
|
hardware with fully compliant ECAM becomes available.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit dcaeb407c91845739a64fb719a8c8d34474371e8
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Mon Nov 10 17:30:25 2014 -0500
|
|
|
|
arm64/pci/acpi: initial support for ACPI probing of PCI
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit 68c8ee5c1ab7b899401750cc359912efbeb40676
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Mon Nov 10 17:23:57 2014 -0500
|
|
|
|
arm64/acpi/pci: add support for parsing MCFG table
|
|
|
|
Add support for parsing MCFG table and provide functions to read/write
|
|
PCI configuration space based on the parsed info. This provides the
|
|
low-level raw_pci_read/raw_pci_write functionality.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit fada05b28e93c24e8932539d94a679ce55ff332f
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Mon Nov 10 16:42:14 2014 -0500
|
|
|
|
DO NOT UPSTREAM - pci/xgene: workaround CRS issue
|
|
|
|
CRS is not behaving properly for some reason. Until this
|
|
gets diagnosed properly, pretend not to support it in order
|
|
to prevent hangs in 3.18 kernel.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit e917d30f3febf6d88cc5001ad236ca367a211cef
|
|
Author: Duc Dang <dhdang@apm.com>
|
|
Date: Thu Nov 6 17:14:18 2014 -0800
|
|
|
|
PCI: X-Gene: assign resource to bus before adding new devices
|
|
|
|
X-Gene PCIE driver currently depends on Liviu Dudau's patch
|
|
https://lkml.org/lkml/2014/9/30/166 in order to assign resource
|
|
to root bus and endpoint devices. The patch was dropped because
|
|
it will break x86, powerpc and probably others. So X-Gene PCIE
|
|
host functionality is currently broken.
|
|
|
|
This patch adds function calls to create and scan root_bus as well
|
|
as assign unassigned bus resource (similar to Liviu Dudau's patch
|
|
above). This will help resolve the dependency to Liviu Dudau's patch
|
|
and make X-Gene PCIE work in latest open-source kernel.
|
|
|
|
Signed-off-by: Duc Dang <dhdang@apm.com>
|
|
Signed-off-by: Tanmay Inamdar <tinamdar@apm.com>
|
|
|
|
commit 6581c0591cafe4fa3b0bd9a25a84a5bf830209fd
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Mon Nov 10 16:31:05 2014 -0500
|
|
|
|
iommu/arm-smmu: fix NULL dereference with ACPI PCI devices
|
|
|
|
Fix a NULL dereference in find_mmu_master which occurs when
|
|
booting with ACPI. In that case, PCI bridges with not have
|
|
an of_node. Add a check for NULL of_node and bail out if that
|
|
is the case.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit e2992498e4b2aad3ceac52bfb6faccfd18332236
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Mon Nov 10 21:35:11 2014 -0500
|
|
|
|
DO NOT UPSTREAM - arm64: fix dma_ops for ACPI and PCI devices
|
|
|
|
Commit 2189064795dc3fb4101e5:
|
|
|
|
arm64: Implement set_arch_dma_coherent_ops() to replace bus notifiers
|
|
|
|
removed the bus notifiers from dma-mapping.c. This patch
|
|
adds the notifier back for ACPI and PCI devices until a
|
|
better permanent solution is worked out.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit ceb5533cf5a6b68b68dc4b8ad61f5671576bdaf2
|
|
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 21fef5d50bb3d4465c6b644bc451c1d219967a81
|
|
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 3357db72822a2c9bc012528c6be3cf861fb3f35c
|
|
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 dc62803a26a34bb3190c9c1b5e3639ca6ebbf788
|
|
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 18099d49d96bfac5096b8ad1d3d5c6d14f92e0a1
|
|
Author: Tom Lendacky <thomas.lendacky@amd.com>
|
|
Date: Mon Sep 15 17:02:52 2014 -0600
|
|
|
|
amd-xgbe: AMD 10GbE driver APCI support for A0
|
|
|
|
This patch provides ACPI support for the AMD 10GbE device driver
|
|
and AMD 10GbE phy driver.
|
|
|
|
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
|
|
|
|
commit 7f208a9dffd58379f2e42e226052386e93d4b2dd
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Tue Oct 7 12:54:08 2014 -0400
|
|
|
|
xgene acpi network - first cut
|
|
|
|
commit e3ce87d3414a845f8cbf71f0d80f3f3136995ce6
|
|
Author: Geert Uytterhoeven <geert+renesas@glider.be>
|
|
Date: Thu Nov 6 12:23:23 2014 +0100
|
|
|
|
leds: leds-gpio: Fix legacy GPIO number case
|
|
|
|
In the legacy case, led_dat->gpiod is initialized correctly, but
|
|
overwritten later by template->gpiod, which is NULL, causing leds-gpio
|
|
to fail with:
|
|
|
|
gpiod_direction_output: invalid GPIO
|
|
leds-gpio: probe of leds-gpio failed with error -22
|
|
|
|
Move the initialization of led_dat->gpiod from template->gpiod up, and
|
|
always use led_dat->gpiod later, to fix this.
|
|
|
|
Fixes: 5c51277a9ababfa4 (leds: leds-gpio: Add support for GPIO descriptors)
|
|
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
|
|
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit f22bf3eef963c9fc4e58e9e96dc7e9d243042b5a
|
|
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
Date: Wed Nov 5 00:29:07 2014 +0100
|
|
|
|
ACPI / property: Drop size_prop from acpi_dev_get_property_reference()
|
|
|
|
The size_prop argument of the recently added function
|
|
acpi_dev_get_property_reference() is not used by the only current
|
|
caller of that function and is very unlikely to be used at any time
|
|
going forward.
|
|
|
|
Namely, for a property whose value is a list of items each containing
|
|
a references to a device object possibly accompanied by some integers,
|
|
the number of items in the list can always be computed as the number
|
|
of elements of type ACPI_TYPE_LOCAL_REFERENCE in the property package.
|
|
Thus it should never be necessary to provide an additional "cells"
|
|
property with a value equal to the number of items in that list. It
|
|
also should never be necessary to provide a "cells" property specifying
|
|
how many integers are supposed to be following each reference.
|
|
|
|
For this reason, drop the size_prop argument from
|
|
acpi_dev_get_property_reference() and update its caller accordingly.
|
|
|
|
Link: http://marc.info/?l=linux-kernel&m=141511255610556&w=2
|
|
Suggested-by: Grant Likely <grant.likely@linaro.org>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 1bb8c08673f0ca7951c0583665d2496e19368a56
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Fri Oct 31 13:40:58 2014 +0200
|
|
|
|
leds: leds-gpio: Convert gpio_blink_set() to use GPIO descriptors
|
|
|
|
Commit 21f2aae91e902aad ("leds: leds-gpio: Add support for GPIO
|
|
descriptors") already converted most of the driver to use GPIO descriptors.
|
|
What is still missing is the platform specific hook gpio_blink_set() and
|
|
board files which pass legacy GPIO numbers to this driver in platform data.
|
|
|
|
In this patch we handle the former and convert gpio_blink_set() to take
|
|
GPIO descriptor instead. In order to do this we convert the existing four
|
|
users to accept GPIO descriptor and translate it to legacy GPIO number in
|
|
the platform code. This effectively "pushes" legacy GPIO number usage from
|
|
the driver to platforms.
|
|
|
|
Also add comment to the remaining block describing that it is legacy code
|
|
path and we are getting rid of it eventually.
|
|
|
|
Suggested-by: Linus Walleij <linus.walleij@linaro.org>
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Andrew Lunn <andrew@lunn.ch>
|
|
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
|
|
Acked-by: Alexandre Courbot <acourbot@nvidia.com>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 1b8f83503e4b9b7168c5d81cabce5b66e4063607
|
|
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
Date: Mon Nov 3 23:39:57 2014 +0100
|
|
|
|
ACPI / GPIO: Document ACPI GPIO mappings API
|
|
|
|
Document the previously introduced method that can be used by device
|
|
drivers to provide the GPIO subsystem with mappings between GPIO names
|
|
(connection IDs) and GpioIo()/GpioInt() resources in _CRS.
|
|
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
|
|
commit 49b5f3126e722dd0266d22a2866bb056ede2cf0a
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Mon Oct 27 12:15:14 2014 +0200
|
|
|
|
net: rfkill: gpio: Add default GPIO driver mappings for ACPI
|
|
|
|
The driver uses devm_gpiod_get_index(..., index) so that the index refers
|
|
directly to the GpioIo resource under the ACPI device. The problem with
|
|
this is that if the ordering changes we get wrong GPIOs.
|
|
|
|
With ACPI 5.1 _DSD we can now use names instead to reference GPIOs
|
|
analogous to Device Tree. However, we still have systems out there that do
|
|
not provide _DSD at all. These systems must be supported as well.
|
|
|
|
Luckily we now have acpi_dev_add_driver_gpios() that can be used to provide
|
|
mappings for systems where _DSD is not provided and still take advantage of
|
|
_DSD if it exists.
|
|
|
|
This patch changes the driver to create default GPIO mappings if we are
|
|
running on ACPI system.
|
|
|
|
While there we can drop the indices completely and use devm_gpiod_get()
|
|
with name instead.
|
|
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Reviewed-by: Johannes Berg <johannes@sipsolutions.net>
|
|
Acked-by: John W. Linville <linville@tuxdriver.com>
|
|
Acked-by: Linus Walleij <linus.walleij@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit c9accb22ace8c3318e75fc0bbbca91336dab270d
|
|
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
Date: Mon Nov 3 23:39:41 2014 +0100
|
|
|
|
ACPI / GPIO: Driver GPIO mappings for ACPI GPIOs
|
|
|
|
Provide a way for device drivers using GPIOs described by ACPI
|
|
GpioIo resources in _CRS to tell the GPIO subsystem what names
|
|
(connection IDs) to associate with specific GPIO pins defined
|
|
in there.
|
|
|
|
To do that, a driver needs to define a mapping table as a
|
|
NULL-terminated array of struct acpi_gpio_mapping objects
|
|
that each contain a name, a pointer to an array of line data
|
|
(struct acpi_gpio_params) objects and the size of that array.
|
|
|
|
Each struct acpi_gpio_params object consists of three fields,
|
|
crs_entry_index, line_index, active_low, representing the index of
|
|
the target GpioIo()/GpioInt() resource in _CRS starting from zero,
|
|
the index of the target line in that resource starting from zero,
|
|
and the active-low flag for that line, respectively.
|
|
|
|
Next, the mapping table needs to be passed as the second
|
|
argument to acpi_dev_add_driver_gpios() that will register it with
|
|
the ACPI device object pointed to by its first argument. That
|
|
should be done in the driver's .probe() routine.
|
|
|
|
On removal, the driver should unregister its GPIO mapping table
|
|
by calling acpi_dev_remove_driver_gpios() on the ACPI device
|
|
object where that table was previously registered.
|
|
|
|
Included are fixes from Mika Westerberg.
|
|
|
|
Acked-by: Alexandre Courbot <acourbot@nvidia.com>
|
|
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 872e3713eb734e43d3001a6ef957cb67ba2349a3
|
|
Author: Aaron Lu <aaron.lu@intel.com>
|
|
Date: Tue Oct 21 13:34:00 2014 +0200
|
|
|
|
input: gpio_keys_polled: Make use of device property API
|
|
|
|
Make use of device property API in this driver so that both OF based
|
|
system and ACPI based system can use this driver.
|
|
|
|
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit ac8ab45966dc9e98cdb44d0ca833c25772eb4745
|
|
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
Date: Mon Oct 27 23:30:10 2014 +0100
|
|
|
|
leds: leds-gpio: Make use of device property API
|
|
|
|
Make use of device property API in this driver so that both OF and ACPI
|
|
based system can use the same driver.
|
|
|
|
This change contains material from Max Eliaser and Mika Westerberg.
|
|
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Bryan Wu <cooloney@gmail.com>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit d48dca7d09f36535c2f6dba340e796b54aa6667d
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Tue Oct 21 13:33:59 2014 +0200
|
|
|
|
gpio: Support for unified device properties interface
|
|
|
|
Some drivers need to deal with only firmware representation of its
|
|
GPIOs. An example would be a GPIO button array driver where each button
|
|
is described as a separate firmware node in device tree. Typically these
|
|
child nodes do not have physical representation in the Linux device
|
|
model.
|
|
|
|
In order to help device drivers to handle such firmware child nodes we
|
|
add dev[m]_get_named_gpiod_from_child() that takes a child firmware
|
|
node pointer as its second argument (the first one is the parent device
|
|
itself), finds the GPIO using whatever is the underlying firmware
|
|
method, and requests the GPIO properly.
|
|
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Alexandre Courbot <acourbot@nvidia.com>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit fcca136c38aa18deec3ddd3b872ea49e6d082e5a
|
|
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
Date: Tue Nov 4 14:03:59 2014 +0100
|
|
|
|
Driver core: Unified interface for firmware node properties
|
|
|
|
Add new generic routines are provided for retrieving properties from
|
|
device description objects in the platform firmware in case there are
|
|
no struct device objects for them (either those objects have not been
|
|
created yet or they do not exist at all).
|
|
|
|
The following functions are provided:
|
|
|
|
fwnode_property_present()
|
|
fwnode_property_read_u8()
|
|
fwnode_property_read_u16()
|
|
fwnode_property_read_u32()
|
|
fwnode_property_read_u64()
|
|
fwnode_property_read_string()
|
|
fwnode_property_read_u8_array()
|
|
fwnode_property_read_u16_array()
|
|
fwnode_property_read_u32_array()
|
|
fwnode_property_read_u64_array()
|
|
fwnode_property_read_string_array()
|
|
|
|
in analogy with the corresponding functions for struct device added
|
|
previously. For all of them, the first argument is a pointer to struct
|
|
fwnode_handle (new type) that allows a device description object
|
|
(depending on what platform firmware interface is in use) to be
|
|
obtained.
|
|
|
|
Add a new macro device_for_each_child_node() for iterating over the
|
|
children of the device description object associated with a given
|
|
device and a new function device_get_child_node_count() returning the
|
|
number of a given device's child nodes.
|
|
|
|
The interface covers both ACPI and Device Trees.
|
|
|
|
Suggested-by: Grant Likely <grant.likely@linaro.org>
|
|
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 4ac0bb216349cce71aea75f6156d44c59fc18086
|
|
Author: Aaron Lu <aaron.lu@intel.com>
|
|
Date: Tue Oct 21 23:30:25 2014 +0200
|
|
|
|
input: gpio_keys_polled: Add support for GPIO descriptors
|
|
|
|
GPIO descriptors are the preferred way over legacy GPIO numbers
|
|
nowadays. Convert the driver to use GPIO descriptors internally but
|
|
still allow passing legacy GPIO numbers from platform data to support
|
|
existing platforms.
|
|
|
|
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Alexandre Courbot <acourbot@nvidia.com>
|
|
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
|
|
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 17907d91fb1bd542b9503cc5223a070039b8d031
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Mon Oct 27 23:29:32 2014 +0100
|
|
|
|
leds: leds-gpio: Add support for GPIO descriptors
|
|
|
|
GPIO descriptors are the preferred way over legacy GPIO numbers
|
|
nowadays. Convert the driver to use GPIO descriptors internally but
|
|
still allow passing legacy GPIO numbers from platform data to support
|
|
existing platforms.
|
|
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Alexandre Courbot <acourbot@nvidia.com>
|
|
Acked-by: Bryan Wu <cooloney@gmail.com>
|
|
Acked-by: Arnd Bergmann <arnd@arndb.de>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 2781ab92fd33f2270c7b5e1628db0a180ed3ab03
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Tue Oct 21 13:33:56 2014 +0200
|
|
|
|
gpio: sch: Consolidate core and resume banks
|
|
|
|
This is actually a single device with two sets of identical registers,
|
|
which just happen to start from a different offset. Instead of having
|
|
separate GPIO chips created we consolidate them to be single GPIO chip.
|
|
|
|
In addition having a single GPIO chip allows us to handle ACPI GPIO
|
|
translation in the core in a more generic way, since the two GPIO chips
|
|
share the same parent ACPI device.
|
|
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Linus Walleij <linus.walleij@linaro.org>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 732feab25f90c338e9ef36457a3591f322f53264
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Wed Oct 29 15:41:01 2014 +0100
|
|
|
|
gpio / ACPI: Add support for _DSD device properties
|
|
|
|
With release of ACPI 5.1 and _DSD method we can finally name GPIOs (and
|
|
other things as well) returned by _CRS. Previously we were only able to
|
|
use integer index to find the corresponding GPIO, which is pretty error
|
|
prone if the order changes.
|
|
|
|
With _DSD we can now query GPIOs using name instead of an integer index,
|
|
like the below example shows:
|
|
|
|
// Bluetooth device with reset and shutdown GPIOs
|
|
Device (BTH)
|
|
{
|
|
Name (_HID, ...)
|
|
|
|
Name (_CRS, ResourceTemplate ()
|
|
{
|
|
GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly,
|
|
"\\_SB.GPO0", 0, ResourceConsumer) {15}
|
|
GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly,
|
|
"\\_SB.GPO0", 0, ResourceConsumer) {27, 31}
|
|
})
|
|
|
|
Name (_DSD, Package ()
|
|
{
|
|
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
|
|
Package ()
|
|
{
|
|
Package () {"reset-gpio", Package() {^BTH, 1, 1, 0 }},
|
|
Package () {"shutdown-gpio", Package() {^BTH, 0, 0, 0 }},
|
|
}
|
|
})
|
|
}
|
|
|
|
The format of the supported GPIO property is:
|
|
|
|
Package () { "name", Package () { ref, index, pin, active_low }}
|
|
|
|
ref - The device that has _CRS containing GpioIo()/GpioInt() resources,
|
|
typically this is the device itself (BTH in our case).
|
|
index - Index of the GpioIo()/GpioInt() resource in _CRS starting from zero.
|
|
pin - Pin in the GpioIo()/GpioInt() resource. Typically this is zero.
|
|
active_low - If 1 the GPIO is marked as active_low.
|
|
|
|
Since ACPI GpioIo() resource does not have field saying whether it is
|
|
active low or high, the "active_low" argument can be used here. Setting
|
|
it to 1 marks the GPIO as active low.
|
|
|
|
In our Bluetooth example the "reset-gpio" refers to the second GpioIo()
|
|
resource, second pin in that resource with the GPIO number of 31.
|
|
|
|
This patch implements necessary support to gpiolib for extracting GPIOs
|
|
using _DSD device properties.
|
|
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Linus Walleij <linus.walleij@linaro.org>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 6b23a29f06a006a48d7fb94eef42452552144fb9
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Tue Oct 21 13:33:56 2014 +0200
|
|
|
|
misc: at25: Make use of device property API
|
|
|
|
Make use of device property API in this driver so that both DT and ACPI
|
|
based systems can use this driver.
|
|
|
|
In addition we hard-code the name of the chip to be "at25" for the
|
|
reason that there is no common mechanism to fetch name of the firmware
|
|
node. The only existing user (arch/arm/boot/dts/phy3250.dts) uses the
|
|
same name so it should continue to work.
|
|
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit c901445ec7d1f37d9fd2250dd4390f395b954ef3
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Tue Oct 21 13:33:56 2014 +0200
|
|
|
|
ACPI: Allow drivers to match using Device Tree compatible property
|
|
|
|
We have lots of existing Device Tree enabled drivers and allocating
|
|
separate _HID for each is not feasible. Instead we allocate special _HID
|
|
"PRP0001" that means that the match should be done using Device Tree
|
|
compatible property using driver's .of_match_table instead if the driver
|
|
is missing .acpi_match_table.
|
|
|
|
If there is a need to distinguish from where the device is enumerated
|
|
(DT/ACPI) driver can check dev->of_node or ACPI_COMPATION(dev).
|
|
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 7eace032b0201dfb110586080dd0628ec48c0d5c
|
|
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
Date: Tue Nov 4 01:28:56 2014 +0100
|
|
|
|
Driver core: Unified device properties interface for platform firmware
|
|
|
|
Add a uniform interface by which device drivers can request device
|
|
properties from the platform firmware by providing a property name
|
|
and the corresponding data type. The purpose of it is to help to
|
|
write portable code that won't depend on any particular platform
|
|
firmware interface.
|
|
|
|
The following general helper functions are added:
|
|
|
|
device_property_present()
|
|
device_property_read_u8()
|
|
device_property_read_u16()
|
|
device_property_read_u32()
|
|
device_property_read_u64()
|
|
device_property_read_string()
|
|
device_property_read_u8_array()
|
|
device_property_read_u16_array()
|
|
device_property_read_u32_array()
|
|
device_property_read_u64_array()
|
|
device_property_read_string_array()
|
|
|
|
The first one allows the caller to check if the given property is
|
|
present. The next 5 of them allow single-valued properties of
|
|
various types to be retrieved in a uniform way. The remaining 5 are
|
|
for reading properties with multiple values (arrays of either numbers
|
|
or strings).
|
|
|
|
The interface covers both ACPI and Device Trees.
|
|
|
|
This change set includes material from Mika Westerberg and Aaron Lu.
|
|
|
|
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit c2d6966e876bb42c76d03d3eb207bfa754657d8b
|
|
Author: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Date: Tue Oct 21 13:33:55 2014 +0200
|
|
|
|
ACPI: Add support for device specific properties
|
|
|
|
Device Tree is used in many embedded systems to describe the system
|
|
configuration to the OS. It supports attaching properties or name-value
|
|
pairs to the devices it describe. With these properties one can pass
|
|
additional information to the drivers that would not be available
|
|
otherwise.
|
|
|
|
ACPI is another configuration mechanism (among other things) typically
|
|
seen, but not limited to, x86 machines. ACPI allows passing arbitrary
|
|
data from methods but there has not been mechanism equivalent to Device
|
|
Tree until the introduction of _DSD in the recent publication of the
|
|
ACPI 5.1 specification.
|
|
|
|
In order to facilitate ACPI usage in systems where Device Tree is
|
|
typically used, it would be beneficial to standardize a way to retrieve
|
|
Device Tree style properties from ACPI devices, which is what we do in
|
|
this patch.
|
|
|
|
If a given device described in ACPI namespace wants to export properties it
|
|
must implement _DSD method (Device Specific Data, introduced with ACPI 5.1)
|
|
that returns the properties in a package of packages. For example:
|
|
|
|
Name (_DSD, Package () {
|
|
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
|
|
Package () {
|
|
Package () {"name1", <VALUE1>},
|
|
Package () {"name2", <VALUE2>},
|
|
...
|
|
}
|
|
})
|
|
|
|
The UUID reserved for properties is daffd814-6eba-4d8c-8a91-bc9bbf4aa301
|
|
and is documented in the ACPI 5.1 companion document called "_DSD
|
|
Implementation Guide" [1], [2].
|
|
|
|
We add several helper functions that can be used to extract these
|
|
properties and convert them to different Linux data types.
|
|
|
|
The ultimate goal is that we only have one device property API that
|
|
retrieves the requested properties from Device Tree or from ACPI
|
|
transparent to the caller.
|
|
|
|
[1] http://www.uefi.org/sites/default/files/resources/_DSD-implementation-guide-toplevel.htm
|
|
[2] http://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf
|
|
|
|
Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Reviewed-by: Josh Triplett <josh@joshtriplett.org>
|
|
Reviewed-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
|
|
commit 3f675aef809d3f82bf1aa0700065ddf798d1eb86
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Tue Sep 30 17:19:24 2014 -0400
|
|
|
|
[NOT FOR UPSTREAM] 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. This is just a hack to
|
|
get the behavior we want from SPCR table support. Once SPCR
|
|
is supported, we can drop this.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit 20d7a3414d18cea0ed8982a5c538e7548bc3bb0a
|
|
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 286f94e27659e7eb45e4b54b4e3c0f9a3b9f9fd4
|
|
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 7e7eade04b5fe90bb30a68aa52f0881b354e7d1d
|
|
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 a1b37e75322dfbd359c86e51ee624511811c93b6
|
|
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 29fe31c2a0e6ce57695c1341351c2579d159e446
|
|
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 0875f7e4b6b078859a3765aac54c345980b2748c
|
|
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 31bf2a5577ffb5b318bb9f5d80a9f26224674521
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Mon Aug 11 13:46:43 2014 -0400
|
|
|
|
xgene: add support for ACPI-probed serial port
|
|
|
|
commit f099d9ceac69d7fda70e5d3ee8200f8585f4ff88
|
|
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 7020f9dda064514d58da5701a351d4773a1d274f
|
|
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 17b60c76da131d5fb9c401ec43404c911fe57f39
|
|
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 fc47393ae2927cb1069c2e15a7b142998702b05d
|
|
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 7100a707311becfd1cbfc26ad8298cc290877db5
|
|
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 397bc93ab853400f7146b8be14bd6cc044df9830
|
|
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 1e6ba230a5e0c03916b0134d846204e64c477972
|
|
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 b3ae15f9e484bab186a5ed697bb25f0244b5aa50
|
|
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 4b29bf89ba430787509cb06a6aa38fafeb910b56
|
|
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 58bb9299b1b2a87bd8002ee1bd6eb90138cc4f30
|
|
Author: Graeme Gregory <graeme.gregory@linaro.org>
|
|
Date: Fri Oct 17 21:37:14 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: Al Stone <al.stone@linaro.org>
|
|
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
|
|
|
|
commit 47e5e8a11d7d0f4930c2e98671a973f3edac313f
|
|
Author: Graeme Gregory <graeme.gregory@linaro.org>
|
|
Date: Fri Oct 17 21:37:13 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 3738397b4a8e91102db2af9ad29919a19425b3f0
|
|
Author: Al Stone <al.stone@linaro.org>
|
|
Date: Fri Oct 17 21:37:12 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 77476629347ec5b72b13e0f3d2cf71604286298c
|
|
Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Date: Fri Oct 17 21:37:11 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 1386d5f6353e7a13d34df5302bc7e4bd1c6aae28
|
|
Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
|
|
Date: Fri Oct 17 21:37:10 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 5213320ccc37374bbf6facc7f7e3c9246bc933c3
|
|
Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Date: Fri Oct 17 21:37:09 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 d1c5a9cdd33f04d4ca5f8ef1b8aed16552fc4f2d
|
|
Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Date: Fri Oct 17 21:37:08 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 9db9a0235e80b8a80af1a000fac4127333b2a2e6
|
|
Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Date: Fri Oct 17 21:37:07 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 1a5a392de06147924052458a6c2fd182dcd4d509
|
|
Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Date: Fri Oct 17 21:37:06 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:
|
|
|
|
ACPI: GICC (acpi_id[0x0000] address[00000000e112f000] MPDIR[0x0] enabled)
|
|
ACPI: GICC (acpi_id[0x0001] address[00000000e112f000] MPDIR[0x1] enabled)
|
|
...
|
|
ACPI: GICC (acpi_id[0x0201] address[00000000e112f000] MPDIR[0x201] enabled)
|
|
|
|
These information will be very helpful to bring up early systems to
|
|
see if acpi_id and MPIDR are matched or not as spec defined.
|
|
|
|
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
|
|
|
|
commit dbb10ccb37b11731a79bc004da46e482a7152be4
|
|
Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Date: Fri Oct 17 21:37:05 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 f367d6c84f50ad7e1944ef6ec7aca8b01fd82051
|
|
Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
Date: Fri Oct 17 21:37:04 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 2926fd3502c857db06ab41caa8f628089287d6f4
|
|
Author: Graeme Gregory <graeme.gregory@linaro.org>
|
|
Date: Fri Oct 17 21:37:03 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=force has 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 d40d81475b41044935ee4eb455a58308e0f070ac
|
|
Author: Al Stone <al.stone@linaro.org>
|
|
Date: Fri Oct 17 21:37:02 2014 +0800
|
|
|
|
ARM64 / ACPI: Introduce early_param for "acpi" and pass acpi=force to enable ACPI
|
|
|
|
Introduce one early parameters "off" and "force" for "acpi", acpi=off
|
|
will be the default behavior for ARM64, so introduce acpi=force to
|
|
enable ACPI on ARM64.
|
|
|
|
Disable ACPI before early parameters parsed, and enable it to pass
|
|
"acpi=force" if people want use ACPI on ARM64. This ensures DT be
|
|
the prefer one if ACPI table and DT both are provided at this moment.
|
|
|
|
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 19ddd869d20be9d05e37a3e867f3c1c379bc0a07
|
|
Author: Graeme Gregory <graeme.gregory@linaro.org>
|
|
Date: Fri Oct 17 21:37:01 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 b2247b541995418caafcfc9f12e57b444fdee75d
|
|
Author: Al Stone <al.stone@linaro.org>
|
|
Date: Fri Oct 17 21:37:00 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 ff7fee61b5c51bc24b358a6521ee9ac0c81c4ae6
|
|
Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
|
|
Date: Fri Oct 17 21:36:59 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).
|
|
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
Signed-off-by: Tomasz Nowicki <tomasz.nowicki@linaro.org>
|
|
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
|
|
|
|
commit 77a4d1f4423591c4f0bed7d580afb7fc7cacb082
|
|
Author: Ashwin Chaugule <ashwin.chaugule@linaro.org>
|
|
Date: Fri Oct 17 21:36:58 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().
|
|
|
|
Acked-by: Grant Likely <grant.likely@linaro.org>
|
|
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 17a565aba814f8cb39d27afb0dbf3834be83ac41
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Sat Nov 8 22:25:48 2014 -0500
|
|
|
|
arm64: use UEFI for reboot
|
|
|
|
Wire in support for UEFI reboot. We want UEFI reboot to have
|
|
highest priority for capsule support.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit c66bae4dfb9dbf577e4692fd034cb80703f687a6
|
|
Author: Mark Salter <msalter@redhat.com>
|
|
Date: Sat Nov 8 15:25:41 2014 -0500
|
|
|
|
arm64: use UEFI as last resort for poweroff
|
|
|
|
Wire in support for poweroff via UEFI.
|
|
|
|
Signed-off-by: Mark Salter <msalter@redhat.com>
|
|
|
|
commit ddc5d1b9a52d9c17fd975fd6ee22be7d6ce5e496
|
|
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 8675c7685ea64e0fda9cebbed08e3f30a71589cf
|
|
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>
|
|
|
|
commit ff46d52dc804aeff783ff55b4b6dc489da20bc11
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Fri Nov 7 14:12:34 2014 +0000
|
|
|
|
arm64: kvm: eliminate literal pool entries
|
|
|
|
Replace two instances of 'ldr xN, =(constant)' in the world switch
|
|
hot path with 'mov' instructions.
|
|
|
|
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
|
|
Acked-by: Christoffer Dall <christoffer.dall@linaro.org>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit 6ddb6f9d2fe1ecab0b812b3adc4ba4bc0cf19ad8
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Fri Nov 7 14:12:33 2014 +0000
|
|
|
|
arm64: ftrace: eliminate literal pool entries
|
|
|
|
Replace ldr xN, =<symbol> with adrp/add or adrp/ldr [as appropriate]
|
|
in the implementation of _mcount(), which may be called very often.
|
|
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit 35939bd2c847d742ab82049b2cc79c086e0e7130
|
|
Author: Mark Rutland <mark.rutland@arm.com>
|
|
Date: Tue Nov 4 10:50:16 2014 +0000
|
|
|
|
arm64: log physical ID of boot CPU
|
|
|
|
In certain debugging scenarios it's useful to know the physical ID (i.e.
|
|
the MPIDR_EL1.Aff* fields) of the boot CPU, but we don't currently log
|
|
this as we do for 32-bit ARM kernels.
|
|
|
|
This patch makes the kernel log the physical ID of the boot CPU early in
|
|
the boot process. The CPU logical map initialisation is folded in to
|
|
smp_setup_processor_id (which contrary to its name is also called by UP
|
|
kernels). This is called before setup_arch, so should not adversely
|
|
affect existing cpu_logical_map users.
|
|
|
|
Acked-by: Sudeep Holla <sudeep.holla@arm.com>
|
|
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
|
|
Acked-by: Lorenzo Pieralisis <lorenzo.pieralisi@arm.com>
|
|
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit aea6f516118cedb6f9e8d2ab23ab5a09c275e68d
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Mon Nov 3 16:50:01 2014 +0000
|
|
|
|
arm64/crypto: use crypto instructions to generate AES key schedule
|
|
|
|
This patch implements the AES key schedule generation using ARMv8
|
|
Crypto Instructions. It replaces the table based C implementation
|
|
in aes_generic.ko, which means we can drop the dependency on that
|
|
module.
|
|
|
|
Tested-by: Steve Capper <steve.capper@linaro.org>
|
|
Acked-by: Steve Capper <steve.capper@linaro.org>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit 0844cddfa0ad22093ef94fdf203fe50cb92222db
|
|
Author: Geoff Levand <geoff@infradead.org>
|
|
Date: Fri Oct 31 23:06:47 2014 +0000
|
|
|
|
arm64/kvm: Fix assembler compatibility of macros
|
|
|
|
Some of the macros defined in kvm_arm.h are useful in assembly files, but are
|
|
not compatible with the assembler. Change any C language integer constant
|
|
definitions using appended U, UL, or ULL to the UL() preprocessor macro. Also,
|
|
add a preprocessor include of the asm/memory.h file which defines the UL()
|
|
macro.
|
|
|
|
Fixes build errors like these when using kvm_arm.h in assembly
|
|
source files:
|
|
|
|
Error: unexpected characters following instruction at operand 3 -- `and x0,x1,#((1U<<25)-1)'
|
|
|
|
Acked-by: Mark Rutland <mark.rutland@arm.com>
|
|
Signed-off-by: Geoff Levand <geoff@infradead.org>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit 94141ed36a5e0a2159deadfcc29a504b55bb352d
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Tue Oct 28 12:24:20 2014 +0000
|
|
|
|
arm64/dt: add machine name to kernel call stack dump output
|
|
|
|
This installs the machine name as recorded by setup_machine_fdt()
|
|
as dump stack arch description. This results in the string to be
|
|
included in call stack dumps, as is shown here:
|
|
|
|
...
|
|
Bad mode in Synchronous Abort handler detected, code 0x84000005
|
|
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.18.0-rc2+ #548
|
|
> Hardware name: linux,dummy-virt (DT)
|
|
task: ffffffc07c870000 ti: ffffffc07c878000 task.ti: ffffffc07c878000
|
|
PC is at 0x0
|
|
...
|
|
|
|
Note that systems that support DMI/SMBIOS may override this later.
|
|
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit a3b3b6027369ce53251bb381c9b87031bbd75464
|
|
Author: Steve Capper <steve.capper@linaro.org>
|
|
Date: Fri Oct 24 13:22:20 2014 +0100
|
|
|
|
arm64: xchg: Implement cmpxchg_double
|
|
|
|
The arm64 architecture has the ability to exclusively load and store
|
|
a pair of registers from an address (ldxp/stxp). Also the SLUB can take
|
|
advantage of a cmpxchg_double implementation to avoid taking some
|
|
locks.
|
|
|
|
This patch provides an implementation of cmpxchg_double for 64-bit
|
|
pairs, and activates the logic required for the SLUB to use these
|
|
functions (HAVE_ALIGNED_STRUCT_PAGE and HAVE_CMPXCHG_DOUBLE).
|
|
|
|
Also definitions of this_cpu_cmpxchg_8 and this_cpu_cmpxchg_double_8
|
|
are wired up to cmpxchg_local and cmpxchg_double_local (rather than the
|
|
stock implementations that perform non-atomic operations with
|
|
interrupts disabled) as they are used by the SLUB.
|
|
|
|
On a Juno platform running on only the A57s I get quite a noticeable
|
|
performance improvement with 5 runs of hackbench on v3.17:
|
|
|
|
Baseline | With Patch
|
|
-----------------+-----------
|
|
Mean 119.2312 | 106.1782
|
|
StdDev 0.4919 | 0.4494
|
|
|
|
(times taken to complete `./hackbench 100 process 1000', in seconds)
|
|
|
|
Signed-off-by: Steve Capper <steve.capper@linaro.org>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit e21d5b44cf03b31feac7854a5953d17ef94d76d9
|
|
Author: Joonwoo Park <joonwoop@codeaurora.org>
|
|
Date: Tue Oct 21 01:59:03 2014 +0100
|
|
|
|
arm64: optimize memcpy_{from,to}io() and memset_io()
|
|
|
|
Optimize memcpy_{from,to}io() and memset_io() by transferring in 64 bit
|
|
as much as possible with minimized barrier usage. This simplest
|
|
optimization brings faster throughput compare to current byte-by-byte read
|
|
and write with barrier in the loop. Code's skeleton is taken from the
|
|
powerpc.
|
|
|
|
Link: http://lkml.kernel.org/p/20141020133304.GH23751@e104818-lin.cambridge.arm.com
|
|
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
|
|
Reviewed-by: Trilok Soni <tsoni@codeaurora.org>
|
|
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit c8f2e7e08d50c4adb77a0fac4a2871c100ef2f51
|
|
Author: Min-Hua Chen <orca.chen@gmail.com>
|
|
Date: Thu Oct 9 16:53:10 2014 +0100
|
|
|
|
arm64: fix data type for physical address
|
|
|
|
Use phys_addr_t for physical address in alloc_init_pud. Although
|
|
phys_addr_t and unsigned long are 64 bit in arm64, it is better
|
|
to use phys_addr_t to describe physical addresses.
|
|
|
|
Signed-off-by: Min-Hua Chen <orca.chen@gmail.com>
|
|
Signed-off-by: Will Deacon <will.deacon@arm.com>
|
|
|
|
commit 674f32f5ef38c93af7a273a725a0a89b4b8a38a1
|
|
Author: Mark Rutland <mark.rutland@arm.com>
|
|
Date: Thu Oct 23 16:33:33 2014 +0100
|
|
|
|
efi: efi-stub: notify on DTB absence
|
|
|
|
In the absence of a DTB configuration table, the EFI stub will happily
|
|
continue attempting to boot a kernel, despite the fact that this kernel
|
|
may not function without a description of the hardware. In this case, as
|
|
with a typo'd "dtb=" option (e.g. "dbt=") or many other possible
|
|
failures, the only output seen by the user will be the rather terse
|
|
output from the EFI stub:
|
|
|
|
EFI stub: Booting Linux Kernel...
|
|
|
|
To aid those attempting to debug such failures, this patch adds a notice
|
|
when no DTB is found, making the output more helpful:
|
|
|
|
EFI stub: Booting Linux Kernel...
|
|
EFI stub: Generating empty DTB
|
|
|
|
Additionally, a positive acknowledgement is added when a user-specified
|
|
DTB is in use:
|
|
|
|
EFI stub: Booting Linux Kernel...
|
|
EFI stub: Using DTB from command line
|
|
|
|
Similarly, a positive acknowledgement is added when a DTB from a
|
|
configuration table is in use:
|
|
|
|
EFI stub: Booting Linux Kernel...
|
|
EFI stub: Using DTB from configuration table
|
|
|
|
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
|
|
Acked-by: Leif Lindholm <leif.lindholm@linaro.org>
|
|
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Acked-by: Roy Franz <roy.franz@linaro.org>
|
|
Acked-by: Matt Fleming <matt.fleming@intel.com>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit 872cf856a3fc4cff5190af2f79cfc3ac410eae17
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Wed Oct 15 09:36:50 2014 +0200
|
|
|
|
arm64: dmi: set DMI string as dump stack arch description
|
|
|
|
This sets the DMI string, containing system type, serial number,
|
|
firmware version etc. as dump stack arch description, so that oopses
|
|
and other kernel stack dumps automatically have this information
|
|
included, if available.
|
|
|
|
Tested-by: Leif Lindholm <leif.lindholm@linaro.org>
|
|
Acked-by: Leif Lindholm <leif.lindholm@linaro.org>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit 2a2978d3748379bb9a0889c866b210f288c45160
|
|
Author: Yi Li <yi.li@linaro.org>
|
|
Date: Sat Oct 4 23:46:43 2014 +0800
|
|
|
|
arm64: dmi: Add SMBIOS/DMI support
|
|
|
|
SMBIOS is important for server hardware vendors. It implements a spec for
|
|
providing descriptive information about the platform. Things like serial
|
|
numbers, physical layout of the ports, build configuration data, and the like.
|
|
|
|
Signed-off-by: Yi Li <yi.li@linaro.org>
|
|
Tested-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
|
|
Tested-by: Leif Lindholm <leif.lindholm@linaro.org>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit c0f06f02f228ca722a2b850363c342f63e6214a6
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Tue Oct 14 16:41:27 2014 +0200
|
|
|
|
dmi: add support for SMBIOS 3.0 64-bit entry point
|
|
|
|
The DMTF SMBIOS reference spec v3.0.0 defines a new 64-bit entry point,
|
|
which enables support for SMBIOS structure tables residing at a physical
|
|
offset over 4 GB. This is especially important for upcoming arm64
|
|
platforms whose system RAM resides entirely above the 4 GB boundary.
|
|
|
|
For the UEFI case, this code attempts to detect the new SMBIOS 3.0
|
|
header magic at the offset passed in the SMBIOS3_TABLE_GUID UEFI
|
|
configuration table. If this configuration table is not provided, or
|
|
if we fail to parse the header, we fall back to using the legacy
|
|
SMBIOS_TABLE_GUID configuration table. This is in line with the spec,
|
|
that allows both configuration tables to be provided, but mandates that
|
|
they must point to the same structure table, unless the version pointed
|
|
to by the 64-bit entry point is a superset of the 32-bit one.
|
|
|
|
For the non-UEFI case, the detection logic is modified to look for the
|
|
SMBIOS 3.0 header magic before it looks for the legacy header magic.
|
|
|
|
Note that this patch is based on version 3.0.0d [draft] of the
|
|
specification, which is expected not to deviate from the final version
|
|
in ways that would affect the correctness of this implementation.
|
|
|
|
Tested-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
|
|
Acked-by: Leif Lindholm <leif.lindholm@linaro.org>
|
|
Tested-by: Leif Lindholm <leif.lindholm@linaro.org>
|
|
Cc: Andrew Morton <akpm@linux-foundation.org>
|
|
Cc: Tony Luck <tony.luck@intel.com>
|
|
Acked-by: Matt Fleming <matt.fleming@intel.com>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit 4ebf40f890ebad9458abc56e00d4814bc881fb9d
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Tue Oct 14 16:34:47 2014 +0200
|
|
|
|
efi: dmi: add support for SMBIOS 3.0 UEFI configuration table
|
|
|
|
This adds support to the UEFI side for detecting the presence of
|
|
a SMBIOS 3.0 64-bit entry point. This allows the actual SMBIOS
|
|
structure table to reside at a physical offset over 4 GB, which
|
|
cannot be supported by the legacy SMBIOS 32-bit entry point.
|
|
|
|
Since the firmware can legally provide both entry points, store
|
|
the SMBIOS 3.0 entry point in a separate variable, and let the
|
|
DMI decoding layer decide which one will be used.
|
|
|
|
Tested-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
|
|
Acked-by: Leif Lindholm <leif.lindholm@linaro.org>
|
|
Acked-by: Matt Fleming <matt.fleming@intel.com>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit 90c66a6923311d477c68543f945f543558b91eec
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Fri Oct 17 12:44:11 2014 +0200
|
|
|
|
arm64/efi: drop redundant set_bit(EFI_CONFIG_TABLES)
|
|
|
|
The EFI_CONFIG_TABLES bit already gets set by efi_config_init(),
|
|
so there is no reason to set it again after this function returns
|
|
successfully.
|
|
|
|
Acked-by: Will Deacon <will.deacon@arm.com>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit bbc705f61c1aebddb5bf80f75964b6da40c55ca9
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Mon Oct 20 15:31:57 2014 +0200
|
|
|
|
arm64/efi: invert UEFI memory region reservation logic
|
|
|
|
Instead of reserving the memory regions based on which types we know
|
|
need to be reserved, consider only regions of the following types as
|
|
free for general use by the OS:
|
|
|
|
EFI_LOADER_CODE
|
|
EFI_LOADER_DATA
|
|
EFI_BOOT_SERVICES_CODE
|
|
EFI_BOOT_SERVICES_DATA
|
|
EFI_CONVENTIONAL_MEMORY
|
|
|
|
Note that this also fixes a problem with the original code, which would
|
|
misidentify a EFI_RUNTIME_SERVICES_DATA region as not reserved if it
|
|
does not have the EFI_MEMORY_RUNTIME attribute set. However, it is
|
|
perfectly legal for the firmware not to request a virtual mapping for
|
|
EFI_RUNTIME_SERVICES_DATA regions that contain configuration tables, in
|
|
which case the EFI_MEMORY_RUNTIME attribute would not be set.
|
|
|
|
Acked-by: Roy Franz <roy.franz@linaro.org>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit d6e4e04244cb7b4670759bd83679ead21df55a8a
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Fri Oct 10 18:42:55 2014 +0200
|
|
|
|
arm64/efi: set PE/COFF file alignment to 512 bytes
|
|
|
|
Change our PE/COFF header to use the minimum file alignment of
|
|
512 bytes (0x200), as mandated by the PE/COFF spec v8.3
|
|
|
|
Also update the linker script so that the Image file itself is also a
|
|
round multiple of FileAlignment.
|
|
|
|
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
|
|
Acked-by: Roy Franz <roy.franz@linaro.org>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit e9e7422d7035c64a16771d47dcd9f907adc5070d
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Fri Oct 10 11:25:24 2014 +0200
|
|
|
|
arm64/efi: set PE/COFF section alignment to 4 KB
|
|
|
|
Position independent AArch64 code needs to be linked and loaded at the
|
|
same relative offset from a 4 KB boundary, or adrp/add and adrp/ldr
|
|
pairs will not work correctly. (This is how PC relative symbol
|
|
references with a 4 GB reach are emitted)
|
|
|
|
We need to declare this in the PE/COFF header, otherwise the PE/COFF
|
|
loader may load the Image and invoke the stub at an offset which
|
|
violates this rule.
|
|
|
|
Reviewed-by: Roy Franz <roy.franz@linaro.org>
|
|
Acked-by: Mark Rutland <mark.rutland@arm.com>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
commit 2d2346a11e6ac9be30ac5590a7ec4b47bda35870
|
|
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
Date: Wed Oct 8 16:11:27 2014 +0200
|
|
|
|
arm64/efi: efistub: jump to 'stext' directly, not through the header
|
|
|
|
After the EFI stub has done its business, it jumps into the kernel by
|
|
branching to offset #0 of the loaded Image, which is where it expects
|
|
to find the header containing a 'branch to stext' instruction.
|
|
|
|
However, the UEFI spec 2.1.1 states the following regarding PE/COFF
|
|
image loading:
|
|
"A UEFI image is loaded into memory through the LoadImage() Boot
|
|
Service. This service loads an image with a PE32+ format into memory.
|
|
This PE32+ loader is required to load all sections of the PE32+ image
|
|
into memory."
|
|
|
|
In other words, it is /not/ required to load parts of the image that are
|
|
not covered by a PE/COFF section, so it may not have loaded the header
|
|
at the expected offset, as it is not covered by any PE/COFF section.
|
|
|
|
So instead, jump to 'stext' directly, which is at the base of the
|
|
PE/COFF .text section, by supplying a symbol 'stext_offset' to
|
|
efi-entry.o which contains the relative offset of stext into the Image.
|
|
Also replace other open coded calculations of the same value with a
|
|
reference to 'stext_offset'
|
|
|
|
Acked-by: Mark Rutland <mark.rutland@arm.com>
|
|
Acked-by: Roy Franz <roy.franz@linaro.org>
|
|
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
|
|
|
Documentation/acpi/gpio-properties.txt | 96 +++
|
|
Documentation/arm64/arm-acpi.txt | 323 ++++++++++
|
|
Documentation/gpio/consumer.txt | 18 +
|
|
Documentation/kernel-parameters.txt | 3 +-
|
|
arch/arm/mach-s3c24xx/h1940-bluetooth.c | 4 +-
|
|
arch/arm/mach-s3c24xx/h1940.h | 4 +-
|
|
arch/arm/mach-s3c24xx/mach-h1940.c | 3 +-
|
|
arch/arm/mach-s3c24xx/mach-rx1950.c | 3 +-
|
|
arch/arm/plat-orion/gpio.c | 3 +-
|
|
arch/arm/plat-orion/include/plat/orion-gpio.h | 5 +-
|
|
arch/arm64/Kconfig | 22 +
|
|
arch/arm64/Makefile | 1 +
|
|
arch/arm64/crypto/Kconfig | 5 +-
|
|
arch/arm64/crypto/aes-ce-ccm-glue.c | 4 +-
|
|
arch/arm64/crypto/aes-ce-cipher.c | 112 +++-
|
|
arch/arm64/crypto/aes-ce-setkey.h | 5 +
|
|
arch/arm64/crypto/aes-glue.c | 18 +-
|
|
arch/arm64/include/asm/acenv.h | 18 +
|
|
arch/arm64/include/asm/acpi.h | 102 +++
|
|
arch/arm64/include/asm/cmpxchg.h | 71 +++
|
|
arch/arm64/include/asm/cpu_ops.h | 1 +
|
|
arch/arm64/include/asm/dmi.h | 31 +
|
|
arch/arm64/include/asm/elf.h | 3 +-
|
|
arch/arm64/include/asm/kvm_arm.h | 21 +-
|
|
arch/arm64/include/asm/pci.h | 51 ++
|
|
arch/arm64/include/asm/psci.h | 3 +-
|
|
arch/arm64/include/asm/smp.h | 10 +-
|
|
arch/arm64/kernel/Makefile | 4 +-
|
|
arch/arm64/kernel/acpi.c | 398 ++++++++++++
|
|
arch/arm64/kernel/cpu_ops.c | 8 +-
|
|
arch/arm64/kernel/efi-entry.S | 3 +-
|
|
arch/arm64/kernel/efi.c | 74 ++-
|
|
arch/arm64/kernel/entry-ftrace.S | 21 +-
|
|
arch/arm64/kernel/head.S | 24 +-
|
|
arch/arm64/kernel/io.c | 66 +-
|
|
arch/arm64/kernel/pci.c | 97 ++-
|
|
arch/arm64/kernel/psci.c | 78 ++-
|
|
arch/arm64/kernel/setup.c | 67 +-
|
|
arch/arm64/kernel/smp.c | 2 +-
|
|
arch/arm64/kernel/smp_parking_protocol.c | 110 ++++
|
|
arch/arm64/kernel/time.c | 7 +
|
|
arch/arm64/kernel/vmlinux.lds.S | 17 +
|
|
arch/arm64/kvm/hyp.S | 4 +-
|
|
arch/arm64/mm/dma-mapping.c | 103 +++
|
|
arch/arm64/mm/mmu.c | 2 +-
|
|
arch/arm64/pci/Makefile | 2 +
|
|
arch/arm64/pci/mmconfig.c | 292 +++++++++
|
|
arch/arm64/pci/pci.c | 461 ++++++++++++++
|
|
drivers/acpi/Kconfig | 6 +-
|
|
drivers/acpi/Makefile | 7 +-
|
|
drivers/acpi/bus.c | 3 +
|
|
drivers/acpi/internal.h | 11 +
|
|
drivers/acpi/osl.c | 6 +-
|
|
drivers/acpi/processor_core.c | 37 ++
|
|
drivers/acpi/property.c | 551 ++++++++++++++++
|
|
drivers/acpi/scan.c | 129 +++-
|
|
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/base/Makefile | 2 +-
|
|
drivers/base/property.c | 431 +++++++++++++
|
|
drivers/clocksource/arm_arch_timer.c | 120 +++-
|
|
drivers/firmware/dmi_scan.c | 79 ++-
|
|
drivers/firmware/efi/efi.c | 4 +
|
|
drivers/firmware/efi/libstub/arm-stub.c | 11 +-
|
|
drivers/gpio/devres.c | 32 +
|
|
drivers/gpio/gpio-sch.c | 293 ++++-----
|
|
drivers/gpio/gpiolib-acpi.c | 117 +++-
|
|
drivers/gpio/gpiolib.c | 85 ++-
|
|
drivers/gpio/gpiolib.h | 7 +-
|
|
drivers/input/keyboard/gpio_keys_polled.c | 112 ++--
|
|
drivers/iommu/arm-smmu.c | 8 +-
|
|
drivers/irqchip/irq-gic-v3.c | 10 +
|
|
drivers/irqchip/irq-gic.c | 116 ++++
|
|
drivers/irqchip/irqchip.c | 3 +
|
|
drivers/leds/leds-gpio.c | 140 ++--
|
|
drivers/misc/eeprom/at25.c | 34 +-
|
|
drivers/net/ethernet/amd/Kconfig | 2 +-
|
|
drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 16 +-
|
|
drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 3 +
|
|
drivers/net/ethernet/amd/xgbe/xgbe-main.c | 289 +++++++--
|
|
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 20 +-
|
|
drivers/net/ethernet/amd/xgbe/xgbe-ptp.c | 4 +-
|
|
drivers/net/ethernet/amd/xgbe/xgbe.h | 13 +
|
|
drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 77 ++-
|
|
drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 68 +-
|
|
drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 1 +
|
|
drivers/net/ethernet/smsc/smc91x.c | 10 +
|
|
drivers/net/phy/Kconfig | 2 +-
|
|
drivers/net/phy/amd-xgbe-phy.c | 777 ++++++++++++-----------
|
|
drivers/of/base.c | 33 +
|
|
drivers/pci/host/pci-xgene.c | 174 ++++-
|
|
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 +-
|
|
drivers/xen/efi.c | 1 +
|
|
include/acpi/acpi_bus.h | 30 +
|
|
include/acpi/acpi_io.h | 6 +
|
|
include/asm-generic/vmlinux.lds.h | 7 +
|
|
include/kvm/arm_vgic.h | 20 +-
|
|
include/linux/acpi.h | 141 +++-
|
|
include/linux/clocksource.h | 6 +
|
|
include/linux/efi.h | 6 +-
|
|
include/linux/gpio/consumer.h | 7 +
|
|
include/linux/gpio_keys.h | 3 +
|
|
include/linux/irqchip/arm-gic-acpi.h | 31 +
|
|
include/linux/irqchip/arm-gic.h | 2 +
|
|
include/linux/leds.h | 3 +-
|
|
include/linux/of.h | 34 +
|
|
include/linux/pci.h | 37 +-
|
|
include/linux/property.h | 143 +++++
|
|
net/rfkill/rfkill-gpio.c | 18 +-
|
|
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 +-
|
|
123 files changed, 6825 insertions(+), 1116 deletions(-)
|
|
|
|
diff --git a/Documentation/acpi/gpio-properties.txt b/Documentation/acpi/gpio-properties.txt
|
|
new file mode 100644
|
|
index 0000000..ae36fcf
|
|
--- /dev/null
|
|
+++ b/Documentation/acpi/gpio-properties.txt
|
|
@@ -0,0 +1,96 @@
|
|
+_DSD Device Properties Related to GPIO
|
|
+--------------------------------------
|
|
+
|
|
+With the release of ACPI 5.1 and the _DSD configuration objecte names
|
|
+can finally be given to GPIOs (and other things as well) returned by
|
|
+_CRS. Previously, we were only able to use an integer index to find
|
|
+the corresponding GPIO, which is pretty error prone (it depends on
|
|
+the _CRS output ordering, for example).
|
|
+
|
|
+With _DSD we can now query GPIOs using a name instead of an integer
|
|
+index, like the ASL example below shows:
|
|
+
|
|
+ // Bluetooth device with reset and shutdown GPIOs
|
|
+ Device (BTH)
|
|
+ {
|
|
+ Name (_HID, ...)
|
|
+
|
|
+ Name (_CRS, ResourceTemplate ()
|
|
+ {
|
|
+ GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly,
|
|
+ "\\_SB.GPO0", 0, ResourceConsumer) {15}
|
|
+ GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly,
|
|
+ "\\_SB.GPO0", 0, ResourceConsumer) {27, 31}
|
|
+ })
|
|
+
|
|
+ Name (_DSD, Package ()
|
|
+ {
|
|
+ ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
|
|
+ Package ()
|
|
+ {
|
|
+ Package () {"reset-gpio", Package() {^BTH, 1, 1, 0 }},
|
|
+ Package () {"shutdown-gpio", Package() {^BTH, 0, 0, 0 }},
|
|
+ }
|
|
+ })
|
|
+ }
|
|
+
|
|
+The format of the supported GPIO property is:
|
|
+
|
|
+ Package () { "name", Package () { ref, index, pin, active_low }}
|
|
+
|
|
+ ref - The device that has _CRS containing GpioIo()/GpioInt() resources,
|
|
+ typically this is the device itself (BTH in our case).
|
|
+ index - Index of the GpioIo()/GpioInt() resource in _CRS starting from zero.
|
|
+ pin - Pin in the GpioIo()/GpioInt() resource. Typically this is zero.
|
|
+ active_low - If 1 the GPIO is marked as active_low.
|
|
+
|
|
+Since ACPI GpioIo() resource does not have a field saying whether it is
|
|
+active low or high, the "active_low" argument can be used here. Setting
|
|
+it to 1 marks the GPIO as active low.
|
|
+
|
|
+In our Bluetooth example the "reset-gpio" refers to the second GpioIo()
|
|
+resource, second pin in that resource with the GPIO number of 31.
|
|
+
|
|
+ACPI GPIO Mappings Provided by Drivers
|
|
+--------------------------------------
|
|
+
|
|
+There are systems in which the ACPI tables do not contain _DSD but provide _CRS
|
|
+with GpioIo()/GpioInt() resources and device drivers still need to work with
|
|
+them.
|
|
+
|
|
+In those cases ACPI device identification objects, _HID, _CID, _CLS, _SUB, _HRV,
|
|
+available to the driver can be used to identify the device and that is supposed
|
|
+to be sufficient to determine the meaning and purpose of all of the GPIO lines
|
|
+listed by the GpioIo()/GpioInt() resources returned by _CRS. In other words,
|
|
+the driver is supposed to know what to use the GpioIo()/GpioInt() resources for
|
|
+once it has identified the device. Having done that, it can simply assign names
|
|
+to the GPIO lines it is going to use and provide the GPIO subsystem with a
|
|
+mapping between those names and the ACPI GPIO resources corresponding to them.
|
|
+
|
|
+To do that, the driver needs to define a mapping table as a NULL-terminated
|
|
+array of struct acpi_gpio_mapping objects that each contain a name, a pointer
|
|
+to an array of line data (struct acpi_gpio_params) objects and the size of that
|
|
+array. Each struct acpi_gpio_params object consists of three fields,
|
|
+crs_entry_index, line_index, active_low, representing the index of the target
|
|
+GpioIo()/GpioInt() resource in _CRS starting from zero, the index of the target
|
|
+line in that resource starting from zero, and the active-low flag for that line,
|
|
+respectively, in analogy with the _DSD GPIO property format specified above.
|
|
+
|
|
+For the example Bluetooth device discussed previously the data structures in
|
|
+question would look like this:
|
|
+
|
|
+static const struct acpi_gpio_params reset_gpio = { 1, 1, false };
|
|
+static const struct acpi_gpio_params shutdown_gpio = { 0, 0, false };
|
|
+
|
|
+static const struct acpi_gpio_mapping bluetooth_acpi_gpios[] = {
|
|
+ { "reset-gpio", &reset_gpio, 1 },
|
|
+ { "shutdown-gpio", &shutdown_gpio, 1 },
|
|
+ { },
|
|
+};
|
|
+
|
|
+Next, the mapping table needs to be passed as the second argument to
|
|
+acpi_dev_add_driver_gpios() that will register it with the ACPI device object
|
|
+pointed to by its first argument. That should be done in the driver's .probe()
|
|
+routine. On removal, the driver should unregister its GPIO mapping table by
|
|
+calling acpi_dev_remove_driver_gpios() on the ACPI device object where that
|
|
+table was previously registered.
|
|
diff --git a/Documentation/arm64/arm-acpi.txt b/Documentation/arm64/arm-acpi.txt
|
|
new file mode 100644
|
|
index 0000000..17cf96d
|
|
--- /dev/null
|
|
+++ b/Documentation/arm64/arm-acpi.txt
|
|
@@ -0,0 +1,323 @@
|
|
+ACPI on ARMv8 Servers
|
|
+---------------------
|
|
+ACPI can be used for ARMv8 general purpose servers designed to follow
|
|
+the ARM SBSA (Server Base System Architecture) specification, currently
|
|
+available to those with an ARM login at http://silver.arm.com.
|
|
+
|
|
+The ARMv8 kernel implements the reduced hardware model of ACPI version
|
|
+5.1 and its corresponding errata. Links to the specification and all
|
|
+external documents it refers to are managed by the UEFI Forum. The
|
|
+specification is available at http://www.uefi.org/specifications and
|
|
+external documents can be found via http://www.uefi.org/acpi.
|
|
+
|
|
+If an ARMv8 system does not meet the requirements of the SBSA, or cannot
|
|
+be described using the mechanisms defined in the required ACPI specifications,
|
|
+then it is likely that Device Tree (DT) is more suitable than ACPI 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
|
|
+-------------------------
|
|
+The only defined method for passing ACPI tables to the kernel on ARMv8
|
|
+is via the UEFI system configuration table.
|
|
+
|
|
+Processing of ACPI tables may be disabled by passing acpi=off on the kernel
|
|
+command line; this is the default behavior. If acpi=force is used, the kernel
|
|
+will ONLY use device configuration information contained in the ACPI tables.
|
|
+
|
|
+In order for the kernel to load and use ACPI tables, the UEFI implementation
|
|
+MUST set the ACPI_20_TABLE_GUID to point to the RSDP table (the table with
|
|
+the ACPI signature "RSD PTR "). If this pointer is incorrect and acpi=force
|
|
+is used, the kernel will disable ACPI and try to use DT to boot.
|
|
+
|
|
+If the pointer to the RSDP table is correct, the table will be mapped into
|
|
+the kernel by the ACPI core, using the address provided by UEFI.
|
|
+
|
|
+The ACPI core will then locate and map in all other ACPI tables provided by
|
|
+using the addresses in the RSDP table to find the XSDT (eXtended System
|
|
+Description Table). The XSDT in turn provides the addresses to all other
|
|
+ACPI tables provided by the system firmware; the ACPI core will then traverse
|
|
+this table and map in the tables listed.
|
|
+
|
|
+The ACPI core will ignore any provided RSDT (Root System Description Table).
|
|
+RSDTs have been deprecated and are ignored on arm64 since they only allow
|
|
+for 32-bit addresses.
|
|
+
|
|
+Further, the ACPI core will only use the 64-bit address fields in the FADT
|
|
+(Fixed ACPI Description Table). Any 32-bit address fields in the FADT will
|
|
+be ignored on arm64.
|
|
+
|
|
+Hardware reduced mode (see Section 4.1 of the ACPI 5.1 specification) will
|
|
+be enforced by the ACPI core on arm64. Doing so allows the ACPI core to
|
|
+run less complex code since it no longer has to provide support for legacy
|
|
+hardware from other architectures.
|
|
+
|
|
+For the ACPI core to operate properly, and in turn provide the information
|
|
+the kernel needs to configure devices, it expects to find the following
|
|
+tables (all section numbers refer to the ACPI 5.1 specfication):
|
|
+
|
|
+ -- RSDP (Root System Description Pointer), section 5.2.5
|
|
+
|
|
+ -- XSDT (eXtended System Description Table), section 5.2.8
|
|
+
|
|
+ -- FACS (Firmware ACPI Control Structure), section 5.2.10
|
|
+
|
|
+ -- FADT (Fixed ACPI Description Table), section 5.2.9
|
|
+
|
|
+ -- DSDT (Differentiated System Description Table), section
|
|
+ 5.2.11.1
|
|
+
|
|
+ -- MADT (Multiple APIC Description Table), section 5.2.12
|
|
+
|
|
+ -- GTDT (Generic Timer Description Table), section 5.2.24
|
|
+
|
|
+ -- If PCI is supported, the MCFG (Memory mapped ConFiGuration
|
|
+ Table), section 5.2.6, specifically Table 5-31.
|
|
+
|
|
+If the above tables are not all present, the kernel may or may not be
|
|
+able to boot properly since it may not be able to configure all of the
|
|
+devices available.
|
|
+
|
|
+
|
|
+ACPI Detection
|
|
+--------------
|
|
+Drivers should determine their probe() type by checking for a null
|
|
+value for ACPI_HANDLE, or checking .of_node, or other information in
|
|
+the device structure. This is detailed further in the "Driver
|
|
+Recommendations" section.
|
|
+
|
|
+In non-driver code, if the presence of ACPI needs to be detected at
|
|
+runtime, then check the value of acpi_disabled. If CONFIG_ACPI is not
|
|
+set, acpi_disabled will always be 1.
|
|
+
|
|
+
|
|
+Device Enumeration
|
|
+------------------
|
|
+Device descriptions in ACPI should use standard recognized ACPI interfaces.
|
|
+These can contain less information than is typically provided via a Device
|
|
+Tree description for the same device. This is also one of the reasons that
|
|
+ACPI can be useful -- the driver takes into account that it may have less
|
|
+detailed information about the device and uses sensible defaults instead.
|
|
+If done properly in the driver, the hardware can change and improve over
|
|
+time without the driver having to change at all.
|
|
+
|
|
+Clocks provide an excellent example. In DT, clocks need to be specified
|
|
+and the drivers need to take them into account. In ACPI, the assumption
|
|
+is that UEFI will leave the device in a reasonable default state, including
|
|
+any clock settings. If for some reason the driver needs to change a clock
|
|
+value, this can be done in an ACPI method; all the driver needs to do is
|
|
+invoke the method and not concern itself with what the method needs to do
|
|
+to change the clock. Changing the hardware can then take place over time
|
|
+by changing what the ACPI method does, and not the driver.
|
|
+
|
|
+ACPI drivers should only look at one specific ASL object -- the _DSD object
|
|
+-- for device driver parameters (known in DT as "bindings", or "Device
|
|
+Properties" in ACPI). Not all DT bindings will be recognized. The UEFI
|
|
+Forum provides a mechanism for registering such bindings [URL TBD by ASWG]
|
|
+so that they may be used on any operating system supporting ACPI. Device
|
|
+properties that have not been registered with the UEFI Forum should not be
|
|
+used.
|
|
+
|
|
+Drivers should look for device properties in the _DSD object ONLY; the _DSD
|
|
+object is described in the ACPI specification section 6.2.5, but more
|
|
+specifically, use the _DSD Device Properties UUID:
|
|
+
|
|
+ -- UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301
|
|
+
|
|
+ -- http://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf)
|
|
+
|
|
+The kernel has an interface for looking up device properties in a manner
|
|
+independent of whether DT or ACPI is being used and that interface should
|
|
+be used; it can eliminate some duplication of code paths in driver probing
|
|
+functions and discourage divergence between DT bindings and ACPI device
|
|
+properties.
|
|
+
|
|
+ACPI tables are described with a formal language called ASL, the ACPI
|
|
+Source Language (section 19 of the specification). This means that there
|
|
+are always multiple ways to describe the same thing -- including device
|
|
+properties. For example, device properties could use an ASL construct
|
|
+that looks like this: Name(KEY0, "value0"). An ACPI device driver would
|
|
+then retrieve the value of the property by evaluating the KEY0 object.
|
|
+However, using Name() this way has multiple problems: (1) ACPI limits
|
|
+names ("KEY0") to four characters unlike DT; (2) there is no industry
|
|
+wide registry that maintains a list of names, minimzing re-use; (3)
|
|
+there is also no registry for the definition of property values ("value0"),
|
|
+again making re-use difficult; and (4) how does one maintain backward
|
|
+compatibility as new hardware comes out? The _DSD method was created
|
|
+to solve precisely these sorts of problems; Linux drivers should ALWAYS
|
|
+use the _DSD method for device properties and nothing else.
|
|
+
|
|
+The _DSM object (ACPI Section 9.14.1) could also be used for conveying
|
|
+device properties to a driver. Linux drivers should only expect it to
|
|
+be used if _DSD cannot represent the data required, and there is no way
|
|
+to create a new UUID for the _DSD object. Note that there is even less
|
|
+regulation of the use of _DSM than there is of _DSD. Drivers that depend
|
|
+on the contents of _DSM objects will be more difficult to maintain over
|
|
+time because of this.
|
|
+
|
|
+The _DSD object is a very flexible mechanism in ACPI, as are the registered
|
|
+Device Properties. This flexibility allows _DSD to cover more than just the
|
|
+generic server case and care should be taken in device drivers not to expect
|
|
+it to replicate highly specific embedded behaviour from DT.
|
|
+
|
|
+Both DT bindings and ACPI device properties for device drivers have review
|
|
+processes. Use them. And, before creating new device properties, check to
|
|
+be sure that they have not been defined before and either registered in the
|
|
+Linux kernel documentation or the UEFI Forum. If the device drivers supports
|
|
+ACPI and DT, please make sure the device properties are consistent in both
|
|
+places.
|
|
+
|
|
+
|
|
+Programmable Power Control Resources
|
|
+------------------------------------
|
|
+Programmable power control resources include such resources as voltage/current
|
|
+providers (regulators) and clock sources.
|
|
+
|
|
+The kernel assumes that power control of these resources is represented with
|
|
+Power Resource Objects (ACPI section 7.1). The ACPI core will then handle
|
|
+correctly enabling and disabling resources as they are needed. In order to
|
|
+get that to work, ACPI assumes each device has defined D-states and that these
|
|
+can be controlled through the optional ACPI methods _PS0, _PS1, _PS2, and _PS3;
|
|
+in ACPI, _PS0 is the method to invoke to turn a device full on, and _PS3 is for
|
|
+turning a device full off.
|
|
+
|
|
+The kernel ACPI code will also assume that the _PS? methods follow the normal
|
|
+ACPI rules for such methods:
|
|
+
|
|
+ -- If either _PS0 or _PS3 is implemented, then the other method must also
|
|
+ be implemented.
|
|
+
|
|
+ -- If a device requires usage or setup of a power resource when on, the ASL
|
|
+ should organize that it is allocated/enabled using the _PS0 method.
|
|
+
|
|
+ -- Resources allocated or enabled in the _PS0 method should be disabled
|
|
+ or de-allocated in the _PS3 method.
|
|
+
|
|
+ -- Firmware will leave the resources in a reasonable state before handing
|
|
+ over control to the kernel.
|
|
+
|
|
+Such code in _PS? methods will of course be very platform specific. But,
|
|
+this allows the driver to abstract out the interface for operating the device
|
|
+and avoid having to read special non-standard values from ACPI tables. Further,
|
|
+abstracting the use of these resources allows the hardware to change over time
|
|
+without requiring updates to the driver.
|
|
+
|
|
+
|
|
+Clocks
|
|
+------
|
|
+ACPI makes the assumption that clocks are initialized by the firmware --
|
|
+UEFI, in this case -- to some working value before control is handed over
|
|
+to the kernel. This has implications for devices such as UARTs, or SoC
|
|
+driven LCD displays, for example.
|
|
+
|
|
+When the kernel boots, the clock is assumed to be set to reasonable
|
|
+working value. If for some reason the frequency needs to change -- e.g.,
|
|
+throttling for power management -- the device driver should expect that
|
|
+process to be abstracted out into some ACPI method that can be invoked
|
|
+(please see the ACPI specification for further recommendations on standard
|
|
+methods to be expected). If is not, there is no direct way for ACPI to
|
|
+control the clocks.
|
|
+
|
|
+
|
|
+Driver Recommendations
|
|
+----------------------
|
|
+DO NOT remove any DT handling when adding ACPI support for a driver. The
|
|
+same device may be used on many different systems.
|
|
+
|
|
+DO try to structure the driver so that it is data driven. That is, set up
|
|
+a struct containing internal per-device state based on defaults and whatever
|
|
+else must be discovered by the driver probe function. Then, have the rest
|
|
+of the driver operate off of the contents of that struct. Doing so should
|
|
+allow most divergence between ACPI and DT functionality to be kept local to
|
|
+the probe function instead of being scattered throughout the driver. For
|
|
+example:
|
|
+
|
|
+static int device_probe_dt(struct platform_device *pdev)
|
|
+{
|
|
+ /* DT specific functionality */
|
|
+ ...
|
|
+}
|
|
+
|
|
+static int device_probe_acpi(struct platform_device *pdev)
|
|
+{
|
|
+ /* ACPI specific functionality */
|
|
+ ...
|
|
+}
|
|
+
|
|
+static int device_probe(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:
|
|
+
|
|
+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 fully defined for ARM in the 5.1 version
|
|
+of the ACPI specification and are expected to be worked through in the
|
|
+UEFI ACPI Specification Working Group (ASWG):
|
|
+
|
|
+ -- 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
|
|
+
|
|
+Participation in this group is open to all UEFI members. Please see
|
|
+http://www.uefi.org/workinggroup for details on group membership.
|
|
+
|
|
+It is the intent of the ARMv8 ACPI kernel code to follow the ACPI specification
|
|
+as closely as possible, and to only implement functionality that complies with
|
|
+the released standards from UEFI ASWG. As a practical matter, there will be
|
|
+vendors that provide bad ACPI tables or violate the standards in some way.
|
|
+If this is because of errors, quirks and fixups may be necessary, but will
|
|
+be avoided if possible. If there are features missing from ACPI that preclude
|
|
+it from being used on a platform, ECRs (Engineering Change Requests) should be
|
|
+submitted to ASWG and go through the normal approval process; for those that
|
|
+are not UEFI members, many other members of the Linux community are and would
|
|
+likely be willing to assist in submitting ECRs.
|
|
diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt
|
|
index 6ce5441..859918d 100644
|
|
--- a/Documentation/gpio/consumer.txt
|
|
+++ b/Documentation/gpio/consumer.txt
|
|
@@ -219,6 +219,24 @@ part of the IRQ interface, e.g. IRQF_TRIGGER_FALLING, as are system wakeup
|
|
capabilities.
|
|
|
|
|
|
+GPIOs and ACPI
|
|
+==============
|
|
+
|
|
+On ACPI systems, GPIOs are described by GpioIo()/GpioInt() resources listed by
|
|
+the _CRS configuration objects of devices. Those resources do not provide
|
|
+connection IDs (names) for GPIOs, so it is necessary to use an additional
|
|
+mechanism for this purpose.
|
|
+
|
|
+Systems compliant with ACPI 5.1 or newer may provide a _DSD configuration object
|
|
+which, among other things, may be used to provide connection IDs for specific
|
|
+GPIOs described by the GpioIo()/GpioInt() resources in _CRS. If that is the
|
|
+case, it will be handled by the GPIO subsystem automatically. However, if the
|
|
+_DSD is not present, the mappings between GpioIo()/GpioInt() resources and GPIO
|
|
+connection IDs need to be provided by device drivers.
|
|
+
|
|
+For details refer to Documentation/acpi/gpio-properties.txt
|
|
+
|
|
+
|
|
Interacting With the Legacy GPIO Subsystem
|
|
==========================================
|
|
Many kernel subsystems still handle GPIOs using the legacy integer-based
|
|
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
|
|
index 479f332..6187d9b 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,ARM64]
|
|
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" or "acpi=force" are available
|
|
|
|
See also Documentation/power/runtime_pm.txt, pci=noacpi
|
|
|
|
diff --git a/arch/arm/mach-s3c24xx/h1940-bluetooth.c b/arch/arm/mach-s3c24xx/h1940-bluetooth.c
|
|
index b4d14b8..9c8b127 100644
|
|
--- a/arch/arm/mach-s3c24xx/h1940-bluetooth.c
|
|
+++ b/arch/arm/mach-s3c24xx/h1940-bluetooth.c
|
|
@@ -41,7 +41,7 @@ static void h1940bt_enable(int on)
|
|
mdelay(10);
|
|
gpio_set_value(S3C2410_GPH(1), 0);
|
|
|
|
- h1940_led_blink_set(-EINVAL, GPIO_LED_BLINK, NULL, NULL);
|
|
+ h1940_led_blink_set(NULL, GPIO_LED_BLINK, NULL, NULL);
|
|
}
|
|
else {
|
|
gpio_set_value(S3C2410_GPH(1), 1);
|
|
@@ -50,7 +50,7 @@ static void h1940bt_enable(int on)
|
|
mdelay(10);
|
|
gpio_set_value(H1940_LATCH_BLUETOOTH_POWER, 0);
|
|
|
|
- h1940_led_blink_set(-EINVAL, GPIO_LED_NO_BLINK_LOW, NULL, NULL);
|
|
+ h1940_led_blink_set(NULL, GPIO_LED_NO_BLINK_LOW, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
diff --git a/arch/arm/mach-s3c24xx/h1940.h b/arch/arm/mach-s3c24xx/h1940.h
|
|
index 2950cc4..596d9f6 100644
|
|
--- a/arch/arm/mach-s3c24xx/h1940.h
|
|
+++ b/arch/arm/mach-s3c24xx/h1940.h
|
|
@@ -19,8 +19,10 @@
|
|
#define H1940_SUSPEND_RESUMEAT (0x30081000)
|
|
#define H1940_SUSPEND_CHECK (0x30080000)
|
|
|
|
+struct gpio_desc;
|
|
+
|
|
extern void h1940_pm_return(void);
|
|
-extern int h1940_led_blink_set(unsigned gpio, int state,
|
|
+extern int h1940_led_blink_set(struct gpio_desc *desc, int state,
|
|
unsigned long *delay_on,
|
|
unsigned long *delay_off);
|
|
|
|
diff --git a/arch/arm/mach-s3c24xx/mach-h1940.c b/arch/arm/mach-s3c24xx/mach-h1940.c
|
|
index d35ddc1..d40d4f5 100644
|
|
--- a/arch/arm/mach-s3c24xx/mach-h1940.c
|
|
+++ b/arch/arm/mach-s3c24xx/mach-h1940.c
|
|
@@ -359,10 +359,11 @@ static struct platform_device h1940_battery = {
|
|
|
|
static DEFINE_SPINLOCK(h1940_blink_spin);
|
|
|
|
-int h1940_led_blink_set(unsigned gpio, int state,
|
|
+int h1940_led_blink_set(struct gpio_desc *desc, int state,
|
|
unsigned long *delay_on, unsigned long *delay_off)
|
|
{
|
|
int blink_gpio, check_gpio1, check_gpio2;
|
|
+ int gpio = desc ? desc_to_gpio(desc) : -EINVAL;
|
|
|
|
switch (gpio) {
|
|
case H1940_LATCH_LED_GREEN:
|
|
diff --git a/arch/arm/mach-s3c24xx/mach-rx1950.c b/arch/arm/mach-s3c24xx/mach-rx1950.c
|
|
index c3f2682..1d35ff3 100644
|
|
--- a/arch/arm/mach-s3c24xx/mach-rx1950.c
|
|
+++ b/arch/arm/mach-s3c24xx/mach-rx1950.c
|
|
@@ -250,9 +250,10 @@ static void rx1950_disable_charger(void)
|
|
|
|
static DEFINE_SPINLOCK(rx1950_blink_spin);
|
|
|
|
-static int rx1950_led_blink_set(unsigned gpio, int state,
|
|
+static int rx1950_led_blink_set(struct gpio_desc *desc, int state,
|
|
unsigned long *delay_on, unsigned long *delay_off)
|
|
{
|
|
+ int gpio = desc_to_gpio(desc);
|
|
int blink_gpio, check_gpio;
|
|
|
|
switch (gpio) {
|
|
diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c
|
|
index b61a3bc..b357053 100644
|
|
--- a/arch/arm/plat-orion/gpio.c
|
|
+++ b/arch/arm/plat-orion/gpio.c
|
|
@@ -306,9 +306,10 @@ EXPORT_SYMBOL(orion_gpio_set_blink);
|
|
|
|
#define ORION_BLINK_HALF_PERIOD 100 /* ms */
|
|
|
|
-int orion_gpio_led_blink_set(unsigned gpio, int state,
|
|
+int orion_gpio_led_blink_set(struct gpio_desc *desc, int state,
|
|
unsigned long *delay_on, unsigned long *delay_off)
|
|
{
|
|
+ unsigned gpio = desc_to_gpio(desc);
|
|
|
|
if (delay_on && delay_off && !*delay_on && !*delay_off)
|
|
*delay_on = *delay_off = ORION_BLINK_HALF_PERIOD;
|
|
diff --git a/arch/arm/plat-orion/include/plat/orion-gpio.h b/arch/arm/plat-orion/include/plat/orion-gpio.h
|
|
index e763988..e856b07 100644
|
|
--- a/arch/arm/plat-orion/include/plat/orion-gpio.h
|
|
+++ b/arch/arm/plat-orion/include/plat/orion-gpio.h
|
|
@@ -14,12 +14,15 @@
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/irqdomain.h>
|
|
+
|
|
+struct gpio_desc;
|
|
+
|
|
/*
|
|
* Orion-specific GPIO API extensions.
|
|
*/
|
|
void orion_gpio_set_unused(unsigned pin);
|
|
void orion_gpio_set_blink(unsigned pin, int blink);
|
|
-int orion_gpio_led_blink_set(unsigned gpio, int state,
|
|
+int orion_gpio_led_blink_set(struct gpio_desc *desc, int state,
|
|
unsigned long *delay_on, unsigned long *delay_off);
|
|
|
|
#define GPIO_INPUT_OK (1 << 0)
|
|
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
|
|
index 9532f8d..80a82ac 100644
|
|
--- a/arch/arm64/Kconfig
|
|
+++ b/arch/arm64/Kconfig
|
|
@@ -4,6 +4,7 @@ config ARM64
|
|
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
|
|
select ARCH_HAS_SG_CHAIN
|
|
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
|
|
+ select ACPI_REDUCED_HARDWARE_ONLY if ACPI
|
|
select ARCH_USE_CMPXCHG_LOCKREF
|
|
select ARCH_SUPPORTS_ATOMIC_RMW
|
|
select ARCH_WANT_OPTIONAL_GPIOLIB
|
|
@@ -34,6 +35,7 @@ config ARM64
|
|
select GENERIC_TIME_VSYSCALL
|
|
select HANDLE_DOMAIN_IRQ
|
|
select HARDIRQS_SW_RESEND
|
|
+ select HAVE_ALIGNED_STRUCT_PAGE if SLUB
|
|
select HAVE_ARCH_AUDITSYSCALL
|
|
select HAVE_ARCH_JUMP_LABEL
|
|
select HAVE_ARCH_KGDB
|
|
@@ -41,6 +43,7 @@ config ARM64
|
|
select HAVE_BPF_JIT
|
|
select HAVE_C_RECORDMCOUNT
|
|
select HAVE_CC_STACKPROTECTOR
|
|
+ select HAVE_CMPXCHG_DOUBLE
|
|
select HAVE_DEBUG_BUGVERBOSE
|
|
select HAVE_DEBUG_KMEMLEAK
|
|
select HAVE_DMA_API_DEBUG
|
|
@@ -185,6 +188,9 @@ config PCI_DOMAINS_GENERIC
|
|
config PCI_SYSCALL
|
|
def_bool PCI
|
|
|
|
+config PCI_MMCONFIG
|
|
+ def_bool y if PCI && ACPI
|
|
+
|
|
source "drivers/pci/Kconfig"
|
|
source "drivers/pci/pcie/Kconfig"
|
|
source "drivers/pci/hotplug/Kconfig"
|
|
@@ -268,6 +274,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
|
|
@@ -401,6 +410,17 @@ config EFI
|
|
allow the kernel to be booted as an EFI application. This
|
|
is only useful on systems that have UEFI firmware.
|
|
|
|
+config DMI
|
|
+ bool "Enable support for SMBIOS (DMI) tables"
|
|
+ depends on EFI
|
|
+ default y
|
|
+ help
|
|
+ This enables SMBIOS/DMI feature for systems.
|
|
+
|
|
+ This option is only useful on systems that have UEFI firmware.
|
|
+ However, even with this option, the resultant kernel should
|
|
+ continue to boot on existing non-UEFI platforms.
|
|
+
|
|
endmenu
|
|
|
|
menu "Userspace binary formats"
|
|
@@ -454,6 +474,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 20901ff..983d72a 100644
|
|
--- a/arch/arm64/Makefile
|
|
+++ b/arch/arm64/Makefile
|
|
@@ -49,6 +49,7 @@ core-$(CONFIG_NET) += arch/arm64/net/
|
|
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/crypto/Kconfig b/arch/arm64/crypto/Kconfig
|
|
index 5562652..a38b02c 100644
|
|
--- a/arch/arm64/crypto/Kconfig
|
|
+++ b/arch/arm64/crypto/Kconfig
|
|
@@ -27,20 +27,19 @@ config CRYPTO_AES_ARM64_CE
|
|
tristate "AES core cipher using ARMv8 Crypto Extensions"
|
|
depends on ARM64 && KERNEL_MODE_NEON
|
|
select CRYPTO_ALGAPI
|
|
- select CRYPTO_AES
|
|
|
|
config CRYPTO_AES_ARM64_CE_CCM
|
|
tristate "AES in CCM mode using ARMv8 Crypto Extensions"
|
|
depends on ARM64 && KERNEL_MODE_NEON
|
|
select CRYPTO_ALGAPI
|
|
- select CRYPTO_AES
|
|
+ select CRYPTO_AES_ARM64_CE
|
|
select CRYPTO_AEAD
|
|
|
|
config CRYPTO_AES_ARM64_CE_BLK
|
|
tristate "AES in ECB/CBC/CTR/XTS modes using ARMv8 Crypto Extensions"
|
|
depends on ARM64 && KERNEL_MODE_NEON
|
|
select CRYPTO_BLKCIPHER
|
|
- select CRYPTO_AES
|
|
+ select CRYPTO_AES_ARM64_CE
|
|
select CRYPTO_ABLK_HELPER
|
|
|
|
config CRYPTO_AES_ARM64_NEON_BLK
|
|
diff --git a/arch/arm64/crypto/aes-ce-ccm-glue.c b/arch/arm64/crypto/aes-ce-ccm-glue.c
|
|
index 9e6cdde..0ac73b8 100644
|
|
--- a/arch/arm64/crypto/aes-ce-ccm-glue.c
|
|
+++ b/arch/arm64/crypto/aes-ce-ccm-glue.c
|
|
@@ -16,6 +16,8 @@
|
|
#include <linux/crypto.h>
|
|
#include <linux/module.h>
|
|
|
|
+#include "aes-ce-setkey.h"
|
|
+
|
|
static int num_rounds(struct crypto_aes_ctx *ctx)
|
|
{
|
|
/*
|
|
@@ -48,7 +50,7 @@ static int ccm_setkey(struct crypto_aead *tfm, const u8 *in_key,
|
|
struct crypto_aes_ctx *ctx = crypto_aead_ctx(tfm);
|
|
int ret;
|
|
|
|
- ret = crypto_aes_expand_key(ctx, in_key, key_len);
|
|
+ ret = ce_aes_expandkey(ctx, in_key, key_len);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-cipher.c
|
|
index 2075e1a..ce47792 100644
|
|
--- a/arch/arm64/crypto/aes-ce-cipher.c
|
|
+++ b/arch/arm64/crypto/aes-ce-cipher.c
|
|
@@ -14,6 +14,8 @@
|
|
#include <linux/crypto.h>
|
|
#include <linux/module.h>
|
|
|
|
+#include "aes-ce-setkey.h"
|
|
+
|
|
MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions");
|
|
MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
|
|
MODULE_LICENSE("GPL v2");
|
|
@@ -124,6 +126,114 @@ static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
|
|
kernel_neon_end();
|
|
}
|
|
|
|
+/*
|
|
+ * aes_sub() - use the aese instruction to perform the AES sbox substitution
|
|
+ * on each byte in 'input'
|
|
+ */
|
|
+static u32 aes_sub(u32 input)
|
|
+{
|
|
+ u32 ret;
|
|
+
|
|
+ __asm__("dup v1.4s, %w[in] ;"
|
|
+ "movi v0.16b, #0 ;"
|
|
+ "aese v0.16b, v1.16b ;"
|
|
+ "umov %w[out], v0.4s[0] ;"
|
|
+
|
|
+ : [out] "=r"(ret)
|
|
+ : [in] "r"(input)
|
|
+ : "v0","v1");
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,
|
|
+ unsigned int key_len)
|
|
+{
|
|
+ /*
|
|
+ * The AES key schedule round constants
|
|
+ */
|
|
+ static u8 const rcon[] = {
|
|
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
|
|
+ };
|
|
+
|
|
+ u32 kwords = key_len / sizeof(u32);
|
|
+ struct aes_block *key_enc, *key_dec;
|
|
+ int i, j;
|
|
+
|
|
+ if (key_len != AES_KEYSIZE_128 &&
|
|
+ key_len != AES_KEYSIZE_192 &&
|
|
+ key_len != AES_KEYSIZE_256)
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(ctx->key_enc, in_key, key_len);
|
|
+ ctx->key_length = key_len;
|
|
+
|
|
+ kernel_neon_begin_partial(2);
|
|
+ for (i = 0; i < sizeof(rcon); i++) {
|
|
+ u32 *rki = ctx->key_enc + (i * kwords);
|
|
+ u32 *rko = rki + kwords;
|
|
+
|
|
+ rko[0] = ror32(aes_sub(rki[kwords - 1]), 8) ^ rcon[i] ^ rki[0];
|
|
+ rko[1] = rko[0] ^ rki[1];
|
|
+ rko[2] = rko[1] ^ rki[2];
|
|
+ rko[3] = rko[2] ^ rki[3];
|
|
+
|
|
+ if (key_len == AES_KEYSIZE_192) {
|
|
+ if (i >= 7)
|
|
+ break;
|
|
+ rko[4] = rko[3] ^ rki[4];
|
|
+ rko[5] = rko[4] ^ rki[5];
|
|
+ } else if (key_len == AES_KEYSIZE_256) {
|
|
+ if (i >= 6)
|
|
+ break;
|
|
+ rko[4] = aes_sub(rko[3]) ^ rki[4];
|
|
+ rko[5] = rko[4] ^ rki[5];
|
|
+ rko[6] = rko[5] ^ rki[6];
|
|
+ rko[7] = rko[6] ^ rki[7];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Generate the decryption keys for the Equivalent Inverse Cipher.
|
|
+ * This involves reversing the order of the round keys, and applying
|
|
+ * the Inverse Mix Columns transformation on all but the first and
|
|
+ * the last one.
|
|
+ */
|
|
+ key_enc = (struct aes_block *)ctx->key_enc;
|
|
+ key_dec = (struct aes_block *)ctx->key_dec;
|
|
+ j = num_rounds(ctx);
|
|
+
|
|
+ key_dec[0] = key_enc[j];
|
|
+ for (i = 1, j--; j > 0; i++, j--)
|
|
+ __asm__("ld1 {v0.16b}, %[in] ;"
|
|
+ "aesimc v1.16b, v0.16b ;"
|
|
+ "st1 {v1.16b}, %[out] ;"
|
|
+
|
|
+ : [out] "=Q"(key_dec[i])
|
|
+ : [in] "Q"(key_enc[j])
|
|
+ : "v0","v1");
|
|
+ key_dec[i] = key_enc[0];
|
|
+
|
|
+ kernel_neon_end();
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(ce_aes_expandkey);
|
|
+
|
|
+int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,
|
|
+ unsigned int key_len)
|
|
+{
|
|
+ struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
|
|
+ int ret;
|
|
+
|
|
+ ret = ce_aes_expandkey(ctx, in_key, key_len);
|
|
+ if (!ret)
|
|
+ return 0;
|
|
+
|
|
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
|
|
+ return -EINVAL;
|
|
+}
|
|
+EXPORT_SYMBOL(ce_aes_setkey);
|
|
+
|
|
static struct crypto_alg aes_alg = {
|
|
.cra_name = "aes",
|
|
.cra_driver_name = "aes-ce",
|
|
@@ -135,7 +245,7 @@ static struct crypto_alg aes_alg = {
|
|
.cra_cipher = {
|
|
.cia_min_keysize = AES_MIN_KEY_SIZE,
|
|
.cia_max_keysize = AES_MAX_KEY_SIZE,
|
|
- .cia_setkey = crypto_aes_set_key,
|
|
+ .cia_setkey = ce_aes_setkey,
|
|
.cia_encrypt = aes_cipher_encrypt,
|
|
.cia_decrypt = aes_cipher_decrypt
|
|
}
|
|
diff --git a/arch/arm64/crypto/aes-ce-setkey.h b/arch/arm64/crypto/aes-ce-setkey.h
|
|
new file mode 100644
|
|
index 0000000..f08a647
|
|
--- /dev/null
|
|
+++ b/arch/arm64/crypto/aes-ce-setkey.h
|
|
@@ -0,0 +1,5 @@
|
|
+
|
|
+int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,
|
|
+ unsigned int key_len);
|
|
+int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,
|
|
+ unsigned int key_len);
|
|
diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c
|
|
index 79cd911..801aae3 100644
|
|
--- a/arch/arm64/crypto/aes-glue.c
|
|
+++ b/arch/arm64/crypto/aes-glue.c
|
|
@@ -16,9 +16,13 @@
|
|
#include <linux/module.h>
|
|
#include <linux/cpufeature.h>
|
|
|
|
+#include "aes-ce-setkey.h"
|
|
+
|
|
#ifdef USE_V8_CRYPTO_EXTENSIONS
|
|
#define MODE "ce"
|
|
#define PRIO 300
|
|
+#define aes_setkey ce_aes_setkey
|
|
+#define aes_expandkey ce_aes_expandkey
|
|
#define aes_ecb_encrypt ce_aes_ecb_encrypt
|
|
#define aes_ecb_decrypt ce_aes_ecb_decrypt
|
|
#define aes_cbc_encrypt ce_aes_cbc_encrypt
|
|
@@ -30,6 +34,8 @@ MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS using ARMv8 Crypto Extensions");
|
|
#else
|
|
#define MODE "neon"
|
|
#define PRIO 200
|
|
+#define aes_setkey crypto_aes_set_key
|
|
+#define aes_expandkey crypto_aes_expand_key
|
|
#define aes_ecb_encrypt neon_aes_ecb_encrypt
|
|
#define aes_ecb_decrypt neon_aes_ecb_decrypt
|
|
#define aes_cbc_encrypt neon_aes_cbc_encrypt
|
|
@@ -79,10 +85,10 @@ static int xts_set_key(struct crypto_tfm *tfm, const u8 *in_key,
|
|
struct crypto_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm);
|
|
int ret;
|
|
|
|
- ret = crypto_aes_expand_key(&ctx->key1, in_key, key_len / 2);
|
|
+ ret = aes_expandkey(&ctx->key1, in_key, key_len / 2);
|
|
if (!ret)
|
|
- ret = crypto_aes_expand_key(&ctx->key2, &in_key[key_len / 2],
|
|
- key_len / 2);
|
|
+ ret = aes_expandkey(&ctx->key2, &in_key[key_len / 2],
|
|
+ key_len / 2);
|
|
if (!ret)
|
|
return 0;
|
|
|
|
@@ -288,7 +294,7 @@ static struct crypto_alg aes_algs[] = { {
|
|
.min_keysize = AES_MIN_KEY_SIZE,
|
|
.max_keysize = AES_MAX_KEY_SIZE,
|
|
.ivsize = AES_BLOCK_SIZE,
|
|
- .setkey = crypto_aes_set_key,
|
|
+ .setkey = aes_setkey,
|
|
.encrypt = ecb_encrypt,
|
|
.decrypt = ecb_decrypt,
|
|
},
|
|
@@ -306,7 +312,7 @@ static struct crypto_alg aes_algs[] = { {
|
|
.min_keysize = AES_MIN_KEY_SIZE,
|
|
.max_keysize = AES_MAX_KEY_SIZE,
|
|
.ivsize = AES_BLOCK_SIZE,
|
|
- .setkey = crypto_aes_set_key,
|
|
+ .setkey = aes_setkey,
|
|
.encrypt = cbc_encrypt,
|
|
.decrypt = cbc_decrypt,
|
|
},
|
|
@@ -324,7 +330,7 @@ static struct crypto_alg aes_algs[] = { {
|
|
.min_keysize = AES_MIN_KEY_SIZE,
|
|
.max_keysize = AES_MAX_KEY_SIZE,
|
|
.ivsize = AES_BLOCK_SIZE,
|
|
- .setkey = crypto_aes_set_key,
|
|
+ .setkey = aes_setkey,
|
|
.encrypt = ctr_encrypt,
|
|
.decrypt = ctr_encrypt,
|
|
},
|
|
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..6e692f4
|
|
--- /dev/null
|
|
+++ b/arch/arm64/include/asm/acpi.h
|
|
@@ -0,0 +1,102 @@
|
|
+/*
|
|
+ * 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;
|
|
+}
|
|
+
|
|
+static inline void enable_acpi(void)
|
|
+{
|
|
+ acpi_disabled = 0;
|
|
+ acpi_pci_disabled = 0;
|
|
+ acpi_noirq = 0;
|
|
+}
|
|
+
|
|
+/* MPIDR value provided in GICC structure is 64 bits, but the
|
|
+ * existing apic_id (CPU hardware ID) using in acpi processor
|
|
+ * driver is 32-bit, to conform to the same datatype we need
|
|
+ * to repack the GICC structure MPIDR.
|
|
+ *
|
|
+ * Only 32 bits of MPIDR are used:
|
|
+ *
|
|
+ * Bits [0:7] Aff0;
|
|
+ * Bits [8:15] Aff1;
|
|
+ * Bits [16:23] Aff2;
|
|
+ * Bits [32:39] Aff3;
|
|
+ */
|
|
+static inline u32 pack_mpidr(u64 mpidr)
|
|
+{
|
|
+ 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(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 void disable_acpi(void) { }
|
|
+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/cmpxchg.h b/arch/arm64/include/asm/cmpxchg.h
|
|
index ddb9d78..89e397b 100644
|
|
--- a/arch/arm64/include/asm/cmpxchg.h
|
|
+++ b/arch/arm64/include/asm/cmpxchg.h
|
|
@@ -19,6 +19,7 @@
|
|
#define __ASM_CMPXCHG_H
|
|
|
|
#include <linux/bug.h>
|
|
+#include <linux/mmdebug.h>
|
|
|
|
#include <asm/barrier.h>
|
|
|
|
@@ -152,6 +153,51 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
|
|
return oldval;
|
|
}
|
|
|
|
+#define system_has_cmpxchg_double() 1
|
|
+
|
|
+static inline int __cmpxchg_double(volatile void *ptr1, volatile void *ptr2,
|
|
+ unsigned long old1, unsigned long old2,
|
|
+ unsigned long new1, unsigned long new2, int size)
|
|
+{
|
|
+ unsigned long loop, lost;
|
|
+
|
|
+ switch (size) {
|
|
+ case 8:
|
|
+ VM_BUG_ON((unsigned long *)ptr2 - (unsigned long *)ptr1 != 1);
|
|
+ do {
|
|
+ asm volatile("// __cmpxchg_double8\n"
|
|
+ " ldxp %0, %1, %2\n"
|
|
+ " eor %0, %0, %3\n"
|
|
+ " eor %1, %1, %4\n"
|
|
+ " orr %1, %0, %1\n"
|
|
+ " mov %w0, #0\n"
|
|
+ " cbnz %1, 1f\n"
|
|
+ " stxp %w0, %5, %6, %2\n"
|
|
+ "1:\n"
|
|
+ : "=&r"(loop), "=&r"(lost), "+Q" (*(u64 *)ptr1)
|
|
+ : "r" (old1), "r"(old2), "r"(new1), "r"(new2));
|
|
+ } while (loop);
|
|
+ break;
|
|
+ default:
|
|
+ BUILD_BUG();
|
|
+ }
|
|
+
|
|
+ return !lost;
|
|
+}
|
|
+
|
|
+static inline int __cmpxchg_double_mb(volatile void *ptr1, volatile void *ptr2,
|
|
+ unsigned long old1, unsigned long old2,
|
|
+ unsigned long new1, unsigned long new2, int size)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ smp_mb();
|
|
+ ret = __cmpxchg_double(ptr1, ptr2, old1, old2, new1, new2, size);
|
|
+ smp_mb();
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old,
|
|
unsigned long new, int size)
|
|
{
|
|
@@ -182,6 +228,31 @@ static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old,
|
|
__ret; \
|
|
})
|
|
|
|
+#define cmpxchg_double(ptr1, ptr2, o1, o2, n1, n2) \
|
|
+({\
|
|
+ int __ret;\
|
|
+ __ret = __cmpxchg_double_mb((ptr1), (ptr2), (unsigned long)(o1), \
|
|
+ (unsigned long)(o2), (unsigned long)(n1), \
|
|
+ (unsigned long)(n2), sizeof(*(ptr1)));\
|
|
+ __ret; \
|
|
+})
|
|
+
|
|
+#define cmpxchg_double_local(ptr1, ptr2, o1, o2, n1, n2) \
|
|
+({\
|
|
+ int __ret;\
|
|
+ __ret = __cmpxchg_double((ptr1), (ptr2), (unsigned long)(o1), \
|
|
+ (unsigned long)(o2), (unsigned long)(n1), \
|
|
+ (unsigned long)(n2), sizeof(*(ptr1)));\
|
|
+ __ret; \
|
|
+})
|
|
+
|
|
+#define this_cpu_cmpxchg_8(ptr, o, n) \
|
|
+ cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
|
|
+
|
|
+#define this_cpu_cmpxchg_double_8(ptr1, ptr2, o1, o2, n1, n2) \
|
|
+ cmpxchg_double_local(raw_cpu_ptr(&(ptr1)), raw_cpu_ptr(&(ptr2)), \
|
|
+ o1, o2, n1, n2)
|
|
+
|
|
#define cmpxchg64(ptr,o,n) cmpxchg((ptr),(o),(n))
|
|
#define cmpxchg64_local(ptr,o,n) cmpxchg_local((ptr),(o),(n))
|
|
|
|
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h
|
|
index 6f8e2ef..978f567 100644
|
|
--- a/arch/arm64/include/asm/cpu_ops.h
|
|
+++ b/arch/arm64/include/asm/cpu_ops.h
|
|
@@ -64,6 +64,7 @@ struct cpu_operations {
|
|
};
|
|
|
|
extern const struct cpu_operations *cpu_ops[NR_CPUS];
|
|
+const struct cpu_operations *cpu_get_ops(const char *name);
|
|
int __init cpu_read_ops(struct device_node *dn, int cpu);
|
|
void __init cpu_read_bootcpu_ops(void);
|
|
|
|
diff --git a/arch/arm64/include/asm/dmi.h b/arch/arm64/include/asm/dmi.h
|
|
new file mode 100644
|
|
index 0000000..69d37d8
|
|
--- /dev/null
|
|
+++ b/arch/arm64/include/asm/dmi.h
|
|
@@ -0,0 +1,31 @@
|
|
+/*
|
|
+ * arch/arm64/include/asm/dmi.h
|
|
+ *
|
|
+ * Copyright (C) 2013 Linaro Limited.
|
|
+ * Written by: Yi Li (yi.li@linaro.org)
|
|
+ *
|
|
+ * based on arch/ia64/include/asm/dmi.h
|
|
+ *
|
|
+ * This file is subject to the terms and conditions of the GNU General Public
|
|
+ * License. See the file "COPYING" in the main directory of this archive
|
|
+ * for more details.
|
|
+ */
|
|
+
|
|
+#ifndef __ASM_DMI_H
|
|
+#define __ASM_DMI_H
|
|
+
|
|
+#include <linux/io.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+/*
|
|
+ * According to section 2.3.6 of the UEFI spec, the firmware should not
|
|
+ * request a virtual mapping for configuration tables such as SMBIOS.
|
|
+ * This means we have to map them before use.
|
|
+ */
|
|
+#define dmi_early_remap(x, l) ioremap_cache(x, l)
|
|
+#define dmi_early_unmap(x, l) iounmap(x)
|
|
+#define dmi_remap(x, l) ioremap_cache(x, l)
|
|
+#define dmi_unmap(x) iounmap(x)
|
|
+#define dmi_alloc(l) kzalloc(l, GFP_KERNEL)
|
|
+
|
|
+#endif
|
|
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
|
|
index 1f65be3..c0f89a0 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/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
|
|
index 7fd3e27..8afb863 100644
|
|
--- a/arch/arm64/include/asm/kvm_arm.h
|
|
+++ b/arch/arm64/include/asm/kvm_arm.h
|
|
@@ -18,6 +18,7 @@
|
|
#ifndef __ARM64_KVM_ARM_H__
|
|
#define __ARM64_KVM_ARM_H__
|
|
|
|
+#include <asm/memory.h>
|
|
#include <asm/types.h>
|
|
|
|
/* Hyp Configuration Register (HCR) bits */
|
|
@@ -160,9 +161,9 @@
|
|
#endif
|
|
|
|
#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
|
|
-#define VTTBR_BADDR_MASK (((1LLU << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
|
|
-#define VTTBR_VMID_SHIFT (48LLU)
|
|
-#define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT)
|
|
+#define VTTBR_BADDR_MASK (((UL(1) << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
|
|
+#define VTTBR_VMID_SHIFT (UL(48))
|
|
+#define VTTBR_VMID_MASK (UL(0xFF) << VTTBR_VMID_SHIFT)
|
|
|
|
/* Hyp System Trap Register */
|
|
#define HSTR_EL2_TTEE (1 << 16)
|
|
@@ -185,13 +186,13 @@
|
|
|
|
/* Exception Syndrome Register (ESR) bits */
|
|
#define ESR_EL2_EC_SHIFT (26)
|
|
-#define ESR_EL2_EC (0x3fU << ESR_EL2_EC_SHIFT)
|
|
-#define ESR_EL2_IL (1U << 25)
|
|
+#define ESR_EL2_EC (UL(0x3f) << ESR_EL2_EC_SHIFT)
|
|
+#define ESR_EL2_IL (UL(1) << 25)
|
|
#define ESR_EL2_ISS (ESR_EL2_IL - 1)
|
|
#define ESR_EL2_ISV_SHIFT (24)
|
|
-#define ESR_EL2_ISV (1U << ESR_EL2_ISV_SHIFT)
|
|
+#define ESR_EL2_ISV (UL(1) << ESR_EL2_ISV_SHIFT)
|
|
#define ESR_EL2_SAS_SHIFT (22)
|
|
-#define ESR_EL2_SAS (3U << ESR_EL2_SAS_SHIFT)
|
|
+#define ESR_EL2_SAS (UL(3) << ESR_EL2_SAS_SHIFT)
|
|
#define ESR_EL2_SSE (1 << 21)
|
|
#define ESR_EL2_SRT_SHIFT (16)
|
|
#define ESR_EL2_SRT_MASK (0x1f << ESR_EL2_SRT_SHIFT)
|
|
@@ -205,16 +206,16 @@
|
|
#define ESR_EL2_FSC_TYPE (0x3c)
|
|
|
|
#define ESR_EL2_CV_SHIFT (24)
|
|
-#define ESR_EL2_CV (1U << ESR_EL2_CV_SHIFT)
|
|
+#define ESR_EL2_CV (UL(1) << ESR_EL2_CV_SHIFT)
|
|
#define ESR_EL2_COND_SHIFT (20)
|
|
-#define ESR_EL2_COND (0xfU << ESR_EL2_COND_SHIFT)
|
|
+#define ESR_EL2_COND (UL(0xf) << ESR_EL2_COND_SHIFT)
|
|
|
|
|
|
#define FSC_FAULT (0x04)
|
|
#define FSC_PERM (0x0c)
|
|
|
|
/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
|
|
-#define HPFAR_MASK (~0xFUL)
|
|
+#define HPFAR_MASK (~UL(0xf))
|
|
|
|
#define ESR_EL2_EC_UNKNOWN (0x00)
|
|
#define ESR_EL2_EC_WFI (0x01)
|
|
diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h
|
|
index 872ba93..2f287a6 100644
|
|
--- a/arch/arm64/include/asm/pci.h
|
|
+++ b/arch/arm64/include/asm/pci.h
|
|
@@ -33,5 +33,56 @@ static inline int pci_proc_domain(struct pci_bus *bus)
|
|
}
|
|
#endif /* CONFIG_PCI */
|
|
|
|
+/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
|
|
+#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
|
|
+
|
|
+#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20)
|
|
+
|
|
+struct acpi_device;
|
|
+
|
|
+struct pci_sysdata {
|
|
+ int domain; /* PCI domain */
|
|
+ int node; /* NUMA node */
|
|
+ struct acpi_device *companion; /* ACPI companion device */
|
|
+ void *iommu; /* IOMMU private data */
|
|
+};
|
|
+
|
|
+struct acpi_pci_root;
|
|
+struct pci_mmcfg_region;
|
|
+
|
|
+typedef int (*acpi_mcfg_fixup_t)(struct acpi_pci_root *root,
|
|
+ struct pci_mmcfg_region *cfg);
|
|
+
|
|
+struct pci_mmcfg_region {
|
|
+ struct list_head list;
|
|
+ struct resource res;
|
|
+ int (*read)(struct pci_mmcfg_region *cfg, unsigned int bus,
|
|
+ unsigned int devfn, int reg, int len, u32 *value);
|
|
+ int (*write)(struct pci_mmcfg_region *cfg, unsigned int bus,
|
|
+ unsigned int devfn, int reg, int len, u32 value);
|
|
+ acpi_mcfg_fixup_t fixup;
|
|
+ void *data;
|
|
+ u64 address;
|
|
+ char __iomem *virt;
|
|
+ u16 segment;
|
|
+ u8 start_bus;
|
|
+ u8 end_bus;
|
|
+ char name[PCI_MMCFG_RESOURCE_NAME_LEN];
|
|
+};
|
|
+
|
|
+struct acpi_mcfg_fixup {
|
|
+ char oem_id[7];
|
|
+ char oem_table_id[9];
|
|
+ acpi_mcfg_fixup_t hook;
|
|
+};
|
|
+
|
|
+/* Designate a routine to fix up buggy MCFG */
|
|
+#define DECLARE_ACPI_MCFG_FIXUP(oem_id, table_id, hook) \
|
|
+ static const struct acpi_mcfg_fixup __acpi_fixup_##hook __used \
|
|
+ __attribute__((__section__(".acpi_fixup_mcfg"), aligned((sizeof(void *))))) \
|
|
+ = { {oem_id}, {table_id}, hook };
|
|
+
|
|
+extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
|
|
+
|
|
#endif /* __KERNEL__ */
|
|
#endif /* __ASM_PCI_H */
|
|
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 780f82c..3411561 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.
|
|
@@ -51,6 +52,11 @@ extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int));
|
|
extern void (*__smp_cross_call)(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 5bd029b..f4ba4fe 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
|
|
@@ -31,6 +32,7 @@ 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..06a96be
|
|
--- /dev/null
|
|
+++ b/arch/arm64/kernel/acpi.c
|
|
@@ -0,0 +1,398 @@
|
|
+/*
|
|
+ * 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>
|
|
+
|
|
+int acpi_noirq; /* skip ACPI IRQ initialization */
|
|
+int acpi_disabled;
|
|
+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 MADT cpu entry with invalid MPIDR\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
|
|
+ * in section 5.2.12.15
|
|
+ */
|
|
+ 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 newer 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("No boot method, 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)
|
|
+ disable_acpi();
|
|
+ else if (strcmp(arg, "force") == 0) /* force ACPI to be enabled */
|
|
+ enable_acpi();
|
|
+ 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-entry.S b/arch/arm64/kernel/efi-entry.S
|
|
index 619b1dd..a0016d3 100644
|
|
--- a/arch/arm64/kernel/efi-entry.S
|
|
+++ b/arch/arm64/kernel/efi-entry.S
|
|
@@ -61,7 +61,8 @@ ENTRY(efi_stub_entry)
|
|
*/
|
|
mov x20, x0 // DTB address
|
|
ldr x0, [sp, #16] // relocated _text address
|
|
- mov x21, x0
|
|
+ ldr x21, =stext_offset
|
|
+ add x21, x0, x21
|
|
|
|
/*
|
|
* Flush dcache covering current runtime addresses
|
|
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
|
|
index 95c49eb..f9de195 100644
|
|
--- a/arch/arm64/kernel/efi.c
|
|
+++ b/arch/arm64/kernel/efi.c
|
|
@@ -11,6 +11,7 @@
|
|
*
|
|
*/
|
|
|
|
+#include <linux/dmi.h>
|
|
#include <linux/efi.h>
|
|
#include <linux/export.h>
|
|
#include <linux/memblock.h>
|
|
@@ -112,8 +113,6 @@ static int __init uefi_init(void)
|
|
efi.systab->hdr.revision & 0xffff, vendor);
|
|
|
|
retval = efi_config_init(NULL);
|
|
- if (retval == 0)
|
|
- set_bit(EFI_CONFIG_TABLES, &efi.flags);
|
|
|
|
out:
|
|
early_memunmap(efi.systab, sizeof(efi_system_table_t));
|
|
@@ -125,17 +124,17 @@ out:
|
|
*/
|
|
static __init int is_reserve_region(efi_memory_desc_t *md)
|
|
{
|
|
- if (!is_normal_ram(md))
|
|
+ switch (md->type) {
|
|
+ case EFI_LOADER_CODE:
|
|
+ case EFI_LOADER_DATA:
|
|
+ case EFI_BOOT_SERVICES_CODE:
|
|
+ case EFI_BOOT_SERVICES_DATA:
|
|
+ case EFI_CONVENTIONAL_MEMORY:
|
|
return 0;
|
|
-
|
|
- if (md->attribute & EFI_MEMORY_RUNTIME)
|
|
- return 1;
|
|
-
|
|
- if (md->type == EFI_ACPI_RECLAIM_MEMORY ||
|
|
- md->type == EFI_RESERVED_TYPE)
|
|
- return 1;
|
|
-
|
|
- return 0;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return is_normal_ram(md);
|
|
}
|
|
|
|
static __init void reserve_regions(void)
|
|
@@ -471,3 +470,54 @@ err_unmap:
|
|
return -1;
|
|
}
|
|
early_initcall(arm64_enter_virtual_mode);
|
|
+
|
|
+static int __init arm64_dmi_init(void)
|
|
+{
|
|
+ /*
|
|
+ * On arm64, DMI depends on UEFI, and dmi_scan_machine() needs to
|
|
+ * be called early because dmi_id_init(), which is an arch_initcall
|
|
+ * itself, depends on dmi_scan_machine() having been called already.
|
|
+ */
|
|
+ dmi_scan_machine();
|
|
+ if (dmi_available)
|
|
+ dmi_set_dump_stack_arch_desc();
|
|
+ return 0;
|
|
+}
|
|
+core_initcall(arm64_dmi_init);
|
|
+
|
|
+/*
|
|
+ * If nothing else is handling pm_power_off, use EFI
|
|
+ *
|
|
+ * When Guenter Roeck's power-off handler call chain patches land,
|
|
+ * we just need to return true unconditionally.
|
|
+ */
|
|
+bool efi_poweroff_required(void)
|
|
+{
|
|
+ return pm_power_off == NULL;
|
|
+}
|
|
+
|
|
+static int arm64_efi_restart(struct notifier_block *this,
|
|
+ unsigned long mode, void *cmd)
|
|
+{
|
|
+ efi_reboot(reboot_mode, cmd);
|
|
+ return NOTIFY_DONE;
|
|
+}
|
|
+
|
|
+static struct notifier_block arm64_efi_restart_nb = {
|
|
+ .notifier_call = arm64_efi_restart,
|
|
+ .priority = INT_MAX,
|
|
+};
|
|
+
|
|
+static int __init arm64_register_efi_restart(void)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (efi_enabled(EFI_RUNTIME_SERVICES)) {
|
|
+ ret = register_restart_handler(&arm64_efi_restart_nb);
|
|
+ if (ret)
|
|
+ pr_err("%s: cannot register restart handler, %d\n",
|
|
+ __func__, ret);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+late_initcall(arm64_register_efi_restart);
|
|
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
|
|
index 38e704e..08cafc5 100644
|
|
--- a/arch/arm64/kernel/entry-ftrace.S
|
|
+++ b/arch/arm64/kernel/entry-ftrace.S
|
|
@@ -98,8 +98,8 @@
|
|
ENTRY(_mcount)
|
|
mcount_enter
|
|
|
|
- ldr x0, =ftrace_trace_function
|
|
- ldr x2, [x0]
|
|
+ adrp x0, ftrace_trace_function
|
|
+ ldr x2, [x0, #:lo12:ftrace_trace_function]
|
|
adr x0, ftrace_stub
|
|
cmp x0, x2 // if (ftrace_trace_function
|
|
b.eq skip_ftrace_call // != ftrace_stub) {
|
|
@@ -115,14 +115,15 @@ skip_ftrace_call: // return;
|
|
mcount_exit // return;
|
|
// }
|
|
skip_ftrace_call:
|
|
- ldr x1, =ftrace_graph_return
|
|
- ldr x2, [x1] // if ((ftrace_graph_return
|
|
- cmp x0, x2 // != ftrace_stub)
|
|
- b.ne ftrace_graph_caller
|
|
-
|
|
- ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry
|
|
- ldr x2, [x1] // != ftrace_graph_entry_stub))
|
|
- ldr x0, =ftrace_graph_entry_stub
|
|
+ adrp x1, ftrace_graph_return
|
|
+ ldr x2, [x1, #:lo12:ftrace_graph_return]
|
|
+ cmp x0, x2 // if ((ftrace_graph_return
|
|
+ b.ne ftrace_graph_caller // != ftrace_stub)
|
|
+
|
|
+ adrp x1, ftrace_graph_entry // || (ftrace_graph_entry
|
|
+ adrp x0, ftrace_graph_entry_stub // != ftrace_graph_entry_stub))
|
|
+ ldr x2, [x1, #:lo12:ftrace_graph_entry]
|
|
+ add x0, x0, #:lo12:ftrace_graph_entry_stub
|
|
cmp x0, x2
|
|
b.ne ftrace_graph_caller // ftrace_graph_caller();
|
|
|
|
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
|
|
index 0a6e4f9..5a76e3a 100644
|
|
--- a/arch/arm64/kernel/head.S
|
|
+++ b/arch/arm64/kernel/head.S
|
|
@@ -132,6 +132,8 @@ efi_head:
|
|
#endif
|
|
|
|
#ifdef CONFIG_EFI
|
|
+ .globl stext_offset
|
|
+ .set stext_offset, stext - efi_head
|
|
.align 3
|
|
pe_header:
|
|
.ascii "PE"
|
|
@@ -155,12 +157,12 @@ optional_header:
|
|
.long 0 // SizeOfInitializedData
|
|
.long 0 // SizeOfUninitializedData
|
|
.long efi_stub_entry - efi_head // AddressOfEntryPoint
|
|
- .long stext - efi_head // BaseOfCode
|
|
+ .long stext_offset // BaseOfCode
|
|
|
|
extra_header_fields:
|
|
.quad 0 // ImageBase
|
|
- .long 0x20 // SectionAlignment
|
|
- .long 0x8 // FileAlignment
|
|
+ .long 0x1000 // SectionAlignment
|
|
+ .long PECOFF_FILE_ALIGNMENT // FileAlignment
|
|
.short 0 // MajorOperatingSystemVersion
|
|
.short 0 // MinorOperatingSystemVersion
|
|
.short 0 // MajorImageVersion
|
|
@@ -172,7 +174,7 @@ extra_header_fields:
|
|
.long _end - efi_head // SizeOfImage
|
|
|
|
// Everything before the kernel image is considered part of the header
|
|
- .long stext - efi_head // SizeOfHeaders
|
|
+ .long stext_offset // SizeOfHeaders
|
|
.long 0 // CheckSum
|
|
.short 0xa // Subsystem (EFI application)
|
|
.short 0 // DllCharacteristics
|
|
@@ -217,16 +219,24 @@ section_table:
|
|
.byte 0
|
|
.byte 0 // end of 0 padding of section name
|
|
.long _end - stext // VirtualSize
|
|
- .long stext - efi_head // VirtualAddress
|
|
+ .long stext_offset // VirtualAddress
|
|
.long _edata - stext // SizeOfRawData
|
|
- .long stext - efi_head // PointerToRawData
|
|
+ .long stext_offset // PointerToRawData
|
|
|
|
.long 0 // PointerToRelocations (0 for executables)
|
|
.long 0 // PointerToLineNumbers (0 for executables)
|
|
.short 0 // NumberOfRelocations (0 for executables)
|
|
.short 0 // NumberOfLineNumbers (0 for executables)
|
|
.long 0xe0500020 // Characteristics (section flags)
|
|
- .align 5
|
|
+
|
|
+ /*
|
|
+ * EFI will load stext onwards at the 4k section alignment
|
|
+ * described in the PE/COFF header. To ensure that instruction
|
|
+ * sequences using an adrp and a :lo12: immediate will function
|
|
+ * correctly at this alignment, we must ensure that stext is
|
|
+ * placed at a 4k boundary in the Image to begin with.
|
|
+ */
|
|
+ .align 12
|
|
#endif
|
|
|
|
ENTRY(stext)
|
|
diff --git a/arch/arm64/kernel/io.c b/arch/arm64/kernel/io.c
|
|
index 7d37ead..354be2a 100644
|
|
--- a/arch/arm64/kernel/io.c
|
|
+++ b/arch/arm64/kernel/io.c
|
|
@@ -25,12 +25,26 @@
|
|
*/
|
|
void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
|
|
{
|
|
- unsigned char *t = to;
|
|
- while (count) {
|
|
+ while (count && (!IS_ALIGNED((unsigned long)from, 8) ||
|
|
+ !IS_ALIGNED((unsigned long)to, 8))) {
|
|
+ *(u8 *)to = __raw_readb(from);
|
|
+ from++;
|
|
+ to++;
|
|
count--;
|
|
- *t = readb(from);
|
|
- t++;
|
|
+ }
|
|
+
|
|
+ while (count >= 8) {
|
|
+ *(u64 *)to = __raw_readq(from);
|
|
+ from += 8;
|
|
+ to += 8;
|
|
+ count -= 8;
|
|
+ }
|
|
+
|
|
+ while (count) {
|
|
+ *(u8 *)to = __raw_readb(from);
|
|
from++;
|
|
+ to++;
|
|
+ count--;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(__memcpy_fromio);
|
|
@@ -40,12 +54,26 @@ EXPORT_SYMBOL(__memcpy_fromio);
|
|
*/
|
|
void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
|
|
{
|
|
- const unsigned char *f = from;
|
|
- while (count) {
|
|
+ while (count && (!IS_ALIGNED((unsigned long)to, 8) ||
|
|
+ !IS_ALIGNED((unsigned long)from, 8))) {
|
|
+ __raw_writeb(*(volatile u8 *)from, to);
|
|
+ from++;
|
|
+ to++;
|
|
count--;
|
|
- writeb(*f, to);
|
|
- f++;
|
|
+ }
|
|
+
|
|
+ while (count >= 8) {
|
|
+ __raw_writeq(*(volatile u64 *)from, to);
|
|
+ from += 8;
|
|
+ to += 8;
|
|
+ count -= 8;
|
|
+ }
|
|
+
|
|
+ while (count) {
|
|
+ __raw_writeb(*(volatile u8 *)from, to);
|
|
+ from++;
|
|
to++;
|
|
+ count--;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(__memcpy_toio);
|
|
@@ -55,10 +83,28 @@ EXPORT_SYMBOL(__memcpy_toio);
|
|
*/
|
|
void __memset_io(volatile void __iomem *dst, int c, size_t count)
|
|
{
|
|
- while (count) {
|
|
+ u64 qc = (u8)c;
|
|
+
|
|
+ qc |= qc << 8;
|
|
+ qc |= qc << 16;
|
|
+ qc |= qc << 32;
|
|
+
|
|
+ while (count && !IS_ALIGNED((unsigned long)dst, 8)) {
|
|
+ __raw_writeb(c, dst);
|
|
+ dst++;
|
|
count--;
|
|
- writeb(c, dst);
|
|
+ }
|
|
+
|
|
+ while (count >= 8) {
|
|
+ __raw_writeq(qc, dst);
|
|
+ dst += 8;
|
|
+ count -= 8;
|
|
+ }
|
|
+
|
|
+ while (count) {
|
|
+ __raw_writeb(c, dst);
|
|
dst++;
|
|
+ count--;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(__memset_io);
|
|
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
|
|
index ce5836c..978cd21 100644
|
|
--- a/arch/arm64/kernel/pci.c
|
|
+++ b/arch/arm64/kernel/pci.c
|
|
@@ -17,6 +17,8 @@
|
|
#include <linux/of_pci.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/slab.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/pci-acpi.h>
|
|
|
|
#include <asm/pci-bridge.h>
|
|
|
|
@@ -37,34 +39,99 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
|
|
return res->start;
|
|
}
|
|
|
|
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
|
|
+{
|
|
+ struct pci_sysdata *sd;
|
|
+
|
|
+ if (!acpi_disabled) {
|
|
+ sd = bridge->bus->sysdata;
|
|
+ ACPI_COMPANION_SET(&bridge->dev, sd->companion);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/*
|
|
* 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);
|
|
+ if (acpi_disabled)
|
|
+ dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
+void pcibios_add_bus(struct pci_bus *bus)
|
|
+{
|
|
+ if (!acpi_disabled)
|
|
+ acpi_pci_add_bus(bus);
|
|
+}
|
|
|
|
-#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
|
-static bool dt_domain_found = false;
|
|
+void pcibios_remove_bus(struct pci_bus *bus)
|
|
+{
|
|
+ if (!acpi_disabled)
|
|
+ acpi_pci_remove_bus(bus);
|
|
+}
|
|
+
|
|
+int pcibios_enable_irq(struct pci_dev *dev)
|
|
+{
|
|
+ if (!acpi_disabled && !pci_dev_msi_enabled(dev))
|
|
+ acpi_pci_irq_enable(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int pcibios_disable_irq(struct pci_dev *dev)
|
|
+{
|
|
+ if (!acpi_disabled && !pci_dev_msi_enabled(dev))
|
|
+ acpi_pci_irq_disable(dev);
|
|
+ return 0;
|
|
+}
|
|
|
|
+int pcibios_enable_device(struct pci_dev *dev, int bars)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ err = pci_enable_resources(dev, bars);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
+
|
|
+ if (!pci_dev_msi_enabled(dev))
|
|
+ return pcibios_enable_irq(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
|
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();
|
|
- }
|
|
+ int domain = -1;
|
|
|
|
- bus->domain_nr = domain;
|
|
+ if (acpi_disabled)
|
|
+ domain = of_get_pci_domain_nr(parent->of_node);
|
|
+ else {
|
|
+ struct pci_sysdata *sd = bus->sysdata;
|
|
+
|
|
+ domain = sd->domain;
|
|
+ }
|
|
+ if (domain >= 0)
|
|
+ bus->domain_nr = domain;
|
|
}
|
|
#endif
|
|
+
|
|
+static int __init pcibios_assign_resources(void)
|
|
+{
|
|
+ struct pci_bus *root_bus;
|
|
+
|
|
+ if (acpi_disabled)
|
|
+ return 0;
|
|
+
|
|
+ list_for_each_entry(root_bus, &pci_root_buses, node) {
|
|
+ pcibios_resource_survey_bus(root_bus);
|
|
+ pci_assign_unassigned_root_bus_resources(root_bus);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+/*
|
|
+ * fs_initcall comes after subsys_initcall, so we know acpi scan
|
|
+ * has run.
|
|
+ */
|
|
+fs_initcall(pcibios_assign_resources);
|
|
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
|
|
index 663da77..2d0deda 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>
|
|
@@ -24,6 +25,7 @@
|
|
#include <linux/slab.h>
|
|
#include <uapi/linux/psci.h>
|
|
|
|
+#include <asm/acpi.h>
|
|
#include <asm/compiler.h>
|
|
#include <asm/cpu_ops.h>
|
|
#include <asm/errno.h>
|
|
@@ -304,6 +306,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.
|
|
@@ -337,29 +366,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);
|
|
@@ -412,7 +419,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;
|
|
@@ -427,6 +434,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 2437196..4c7029d 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);
|
|
@@ -116,12 +121,16 @@ void __init early_print(const char *str, ...)
|
|
|
|
void __init smp_setup_processor_id(void)
|
|
{
|
|
+ u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
|
|
+ cpu_logical_map(0) = mpidr;
|
|
+
|
|
/*
|
|
* clear __my_cpu_offset on boot CPU to avoid hang caused by
|
|
* using percpu variable early, for example, lockdep will
|
|
* access percpu variable inside lock_release
|
|
*/
|
|
set_my_cpu_offset(0);
|
|
+ pr_info("Booting Linux on physical CPU 0x%lx\n", (unsigned long)mpidr);
|
|
}
|
|
|
|
bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
|
|
@@ -312,6 +321,7 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys)
|
|
}
|
|
|
|
machine_name = of_flat_dt_get_machine_name();
|
|
+ dump_stack_set_arch_desc("%s (DT)", machine_name);
|
|
}
|
|
|
|
/*
|
|
@@ -378,6 +388,8 @@ void __init setup_arch(char **cmdline_p)
|
|
|
|
early_ioremap_init();
|
|
|
|
+ disable_acpi();
|
|
+
|
|
parse_early_param();
|
|
|
|
/*
|
|
@@ -389,19 +401,27 @@ void __init setup_arch(char **cmdline_p)
|
|
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();
|
|
+ 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();
|
|
+ }
|
|
|
|
- cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
|
|
- cpu_read_bootcpu_ops();
|
|
#ifdef CONFIG_SMP
|
|
- smp_init_cpus();
|
|
smp_build_mpidr_hash();
|
|
#endif
|
|
|
|
@@ -414,6 +434,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);
|
|
@@ -506,3 +539,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 b06d1d9..2988829 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/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/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
|
|
index edf8715..4596f46 100644
|
|
--- a/arch/arm64/kernel/vmlinux.lds.S
|
|
+++ b/arch/arm64/kernel/vmlinux.lds.S
|
|
@@ -32,6 +32,22 @@ jiffies = jiffies_64;
|
|
*(.hyp.text) \
|
|
VMLINUX_SYMBOL(__hyp_text_end) = .;
|
|
|
|
+/*
|
|
+ * The size of the PE/COFF section that covers the kernel image, which
|
|
+ * runs from stext to _edata, must be a round multiple of the PE/COFF
|
|
+ * FileAlignment, which we set to its minimum value of 0x200. 'stext'
|
|
+ * itself is 4 KB aligned, so padding out _edata to a 0x200 aligned
|
|
+ * boundary should be sufficient.
|
|
+ */
|
|
+PECOFF_FILE_ALIGNMENT = 0x200;
|
|
+
|
|
+#ifdef CONFIG_EFI
|
|
+#define PECOFF_EDATA_PADDING \
|
|
+ .pecoff_edata_padding : { BYTE(0); . = ALIGN(PECOFF_FILE_ALIGNMENT); }
|
|
+#else
|
|
+#define PECOFF_EDATA_PADDING
|
|
+#endif
|
|
+
|
|
SECTIONS
|
|
{
|
|
/*
|
|
@@ -103,6 +119,7 @@ SECTIONS
|
|
_data = .;
|
|
_sdata = .;
|
|
RW_DATA_SECTION(64, PAGE_SIZE, THREAD_SIZE)
|
|
+ PECOFF_EDATA_PADDING
|
|
_edata = .;
|
|
|
|
BSS_SECTION(0, 0, 0)
|
|
diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S
|
|
index b72aa9f..fbe909f 100644
|
|
--- a/arch/arm64/kvm/hyp.S
|
|
+++ b/arch/arm64/kvm/hyp.S
|
|
@@ -761,10 +761,10 @@
|
|
.macro activate_traps
|
|
ldr x2, [x0, #VCPU_HCR_EL2]
|
|
msr hcr_el2, x2
|
|
- ldr x2, =(CPTR_EL2_TTA)
|
|
+ mov x2, #CPTR_EL2_TTA
|
|
msr cptr_el2, x2
|
|
|
|
- ldr x2, =(1 << 15) // Trap CP15 Cr=15
|
|
+ mov x2, #(1 << 15) // Trap CP15 Cr=15
|
|
msr hstr_el2, x2
|
|
|
|
mrs x2, mdcr_el2
|
|
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
|
|
index d920942..705a9ce 100644
|
|
--- a/arch/arm64/mm/dma-mapping.c
|
|
+++ b/arch/arm64/mm/dma-mapping.c
|
|
@@ -23,8 +23,14 @@
|
|
#include <linux/genalloc.h>
|
|
#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>
|
|
|
|
@@ -423,10 +429,107 @@ out:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
+#ifdef CONFIG_PCI
|
|
+static void arm64_of_set_dma_ops(void *_dev)
|
|
+{
|
|
+ struct device *dev = _dev;
|
|
+
|
|
+ /*
|
|
+ * PCI devices won't have an ACPI handle 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;
|
|
+ }
|
|
+ dev = dev->parent;
|
|
+ }
|
|
+}
|
|
+#else
|
|
+static inline arm64_of_set_dma_ops(void *_dev) {}
|
|
+#endif
|
|
+
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+static void arm64_acpi_set_dma_ops(void *_dev)
|
|
+{
|
|
+ struct device *dev = _dev;
|
|
+
|
|
+ /*
|
|
+ * 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.
|
|
+ *
|
|
+ * PCI devices won't have an of_node but the bridge will.
|
|
+ * Search up the device chain until we find an ACPI handle
|
|
+ * to check.
|
|
+ */
|
|
+ while (dev) {
|
|
+ if (ACPI_HANDLE(dev)) {
|
|
+ acpi_status status;
|
|
+ int coherent;
|
|
+ status = acpi_check_coherency(ACPI_HANDLE(dev),
|
|
+ &coherent);
|
|
+ if (ACPI_FAILURE(status) || coherent)
|
|
+ set_dma_ops(_dev, &coherent_swiotlb_dma_ops);
|
|
+ break;
|
|
+ }
|
|
+ dev = dev->parent;
|
|
+ }
|
|
+}
|
|
+#else
|
|
+static inline arm64_acpi_set_dma_ops(void *_dev) {}
|
|
+#endif
|
|
+
|
|
+static int dma_bus_notifier(struct notifier_block *nb,
|
|
+ unsigned long event, void *_dev)
|
|
+{
|
|
+ if (event != BUS_NOTIFY_ADD_DEVICE)
|
|
+ return NOTIFY_DONE;
|
|
+
|
|
+ if (acpi_disabled)
|
|
+ arm64_of_set_dma_ops(_dev);
|
|
+ else
|
|
+ arm64_acpi_set_dma_ops(_dev);
|
|
+
|
|
+ return NOTIFY_OK;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+static struct notifier_block platform_bus_nb = {
|
|
+ .notifier_call = dma_bus_notifier,
|
|
+};
|
|
+
|
|
+static struct notifier_block amba_bus_nb = {
|
|
+ .notifier_call = dma_bus_notifier,
|
|
+};
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_PCI
|
|
+static struct notifier_block pci_bus_nb = {
|
|
+ .notifier_call = dma_bus_notifier,
|
|
+};
|
|
+#endif
|
|
+
|
|
static int __init swiotlb_late_init(void)
|
|
{
|
|
size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT);
|
|
|
|
+ /*
|
|
+ * These must be registered before of_platform_populate().
|
|
+ */
|
|
+#ifdef CONFIG_ACPI
|
|
+ bus_register_notifier(&platform_bus_type, &platform_bus_nb);
|
|
+ bus_register_notifier(&amba_bustype, &amba_bus_nb);
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_PCI
|
|
+ bus_register_notifier(&pci_bus_type, &pci_bus_nb);
|
|
+#endif
|
|
+
|
|
dma_ops = &noncoherent_swiotlb_dma_ops;
|
|
|
|
return swiotlb_late_init_with_default_size(swiotlb_size);
|
|
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
|
|
index 0bf90d2..f4f8b50 100644
|
|
--- a/arch/arm64/mm/mmu.c
|
|
+++ b/arch/arm64/mm/mmu.c
|
|
@@ -202,7 +202,7 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
|
|
}
|
|
|
|
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
|
|
- unsigned long end, unsigned long phys,
|
|
+ unsigned long end, phys_addr_t phys,
|
|
int map_io)
|
|
{
|
|
pud_t *pud;
|
|
diff --git a/arch/arm64/pci/Makefile b/arch/arm64/pci/Makefile
|
|
new file mode 100644
|
|
index 0000000..7038b51
|
|
--- /dev/null
|
|
+++ b/arch/arm64/pci/Makefile
|
|
@@ -0,0 +1,2 @@
|
|
+obj-y += pci.o
|
|
+obj-$(CONFIG_ACPI) += mmconfig.o
|
|
diff --git a/arch/arm64/pci/mmconfig.c b/arch/arm64/pci/mmconfig.c
|
|
new file mode 100644
|
|
index 0000000..e83e0d5
|
|
--- /dev/null
|
|
+++ b/arch/arm64/pci/mmconfig.c
|
|
@@ -0,0 +1,292 @@
|
|
+/*
|
|
+ * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
|
|
+ *
|
|
+ * Borrowed heavily from x86
|
|
+ */
|
|
+
|
|
+#include <linux/pci.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/bitmap.h>
|
|
+#include <linux/dmi.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/rculist.h>
|
|
+#include <linux/rcupdate.h>
|
|
+
|
|
+#define PREFIX "PCI: "
|
|
+
|
|
+/* Indicate if the mmcfg resources have been placed into the resource table. */
|
|
+static bool pci_mmcfg_running_state;
|
|
+static bool pci_mmcfg_arch_init_failed;
|
|
+static DEFINE_MUTEX(pci_mmcfg_lock);
|
|
+
|
|
+LIST_HEAD(pci_mmcfg_list);
|
|
+
|
|
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg;
|
|
+
|
|
+ list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
|
|
+ if (cfg->segment == segment &&
|
|
+ cfg->start_bus <= bus && bus <= cfg->end_bus)
|
|
+ return cfg;
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg)
|
|
+{
|
|
+ void __iomem *addr;
|
|
+ u64 start, size;
|
|
+ int num_buses;
|
|
+
|
|
+ start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
|
|
+ num_buses = cfg->end_bus - cfg->start_bus + 1;
|
|
+ size = PCI_MMCFG_BUS_OFFSET(num_buses);
|
|
+ addr = ioremap_nocache(start, size);
|
|
+ if (addr)
|
|
+ addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
|
|
+ return addr;
|
|
+}
|
|
+
|
|
+void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
|
|
+{
|
|
+ if (cfg && cfg->virt) {
|
|
+ iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
|
|
+ cfg->virt = NULL;
|
|
+ }
|
|
+}
|
|
+
|
|
+void __init pci_mmcfg_arch_free(void)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg;
|
|
+
|
|
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
|
|
+ pci_mmcfg_arch_unmap(cfg);
|
|
+}
|
|
+
|
|
+int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
|
|
+{
|
|
+ cfg->virt = mcfg_ioremap(cfg);
|
|
+ if (!cfg->virt) {
|
|
+ pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
|
|
+{
|
|
+ if (cfg->res.parent)
|
|
+ release_resource(&cfg->res);
|
|
+ list_del(&cfg->list);
|
|
+ kfree(cfg);
|
|
+}
|
|
+
|
|
+static void __init free_all_mmcfg(void)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg, *tmp;
|
|
+
|
|
+ pci_mmcfg_arch_free();
|
|
+ list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
|
|
+ pci_mmconfig_remove(cfg);
|
|
+}
|
|
+
|
|
+static void list_add_sorted(struct pci_mmcfg_region *new)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg;
|
|
+
|
|
+ /* keep list sorted by segment and starting bus number */
|
|
+ list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
|
|
+ if (cfg->segment > new->segment ||
|
|
+ (cfg->segment == new->segment &&
|
|
+ cfg->start_bus >= new->start_bus)) {
|
|
+ list_add_tail_rcu(&new->list, &cfg->list);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ list_add_tail_rcu(&new->list, &pci_mmcfg_list);
|
|
+}
|
|
+
|
|
+static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
|
|
+ int end, u64 addr)
|
|
+{
|
|
+ struct pci_mmcfg_region *new;
|
|
+ struct resource *res;
|
|
+
|
|
+ if (addr == 0)
|
|
+ return NULL;
|
|
+
|
|
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
|
|
+ if (!new)
|
|
+ return NULL;
|
|
+
|
|
+ new->address = addr;
|
|
+ new->segment = segment;
|
|
+ new->start_bus = start;
|
|
+ new->end_bus = end;
|
|
+
|
|
+ res = &new->res;
|
|
+ res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
|
|
+ res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
|
|
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
|
+ snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
|
|
+ "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
|
|
+ res->name = new->name;
|
|
+
|
|
+ return new;
|
|
+}
|
|
+
|
|
+static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
|
|
+ int end, u64 addr)
|
|
+{
|
|
+ struct pci_mmcfg_region *new;
|
|
+
|
|
+ new = pci_mmconfig_alloc(segment, start, end, addr);
|
|
+ if (new) {
|
|
+ mutex_lock(&pci_mmcfg_lock);
|
|
+ list_add_sorted(new);
|
|
+ mutex_unlock(&pci_mmcfg_lock);
|
|
+
|
|
+ pr_info(PREFIX
|
|
+ "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
|
|
+ "(base %#lx)\n",
|
|
+ segment, start, end, &new->res, (unsigned long)addr);
|
|
+ }
|
|
+
|
|
+ return new;
|
|
+}
|
|
+
|
|
+extern struct acpi_mcfg_fixup __start_acpi_mcfg_fixups[];
|
|
+extern struct acpi_mcfg_fixup __end_acpi_mcfg_fixups[];
|
|
+
|
|
+static int __init pci_parse_mcfg(struct acpi_table_header *header)
|
|
+{
|
|
+ struct acpi_table_mcfg *mcfg;
|
|
+ struct acpi_mcfg_allocation *cfg_table, *cfg;
|
|
+ struct acpi_mcfg_fixup *fixup;
|
|
+ struct pci_mmcfg_region *new;
|
|
+ unsigned long i;
|
|
+ int entries;
|
|
+
|
|
+ if (!header)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mcfg = (struct acpi_table_mcfg *)header;
|
|
+
|
|
+ /* how many config structures do we have */
|
|
+ free_all_mmcfg();
|
|
+ entries = 0;
|
|
+ i = header->length - sizeof(struct acpi_table_mcfg);
|
|
+ while (i >= sizeof(struct acpi_mcfg_allocation)) {
|
|
+ entries++;
|
|
+ i -= sizeof(struct acpi_mcfg_allocation);
|
|
+ }
|
|
+ if (entries == 0) {
|
|
+ pr_err(PREFIX "MMCONFIG has no entries\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ fixup = __start_acpi_mcfg_fixups;
|
|
+ while (fixup < __end_acpi_mcfg_fixups) {
|
|
+ if (!strncmp(fixup->oem_id, header->oem_id, 6) &&
|
|
+ !strncmp(fixup->oem_table_id, header->oem_table_id, 8))
|
|
+ break;
|
|
+ ++fixup;
|
|
+ }
|
|
+
|
|
+ cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
|
|
+ for (i = 0; i < entries; i++) {
|
|
+ cfg = &cfg_table[i];
|
|
+
|
|
+ new = pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
|
|
+ cfg->end_bus_number, cfg->address);
|
|
+ if (!new) {
|
|
+ pr_warn(PREFIX "no memory for MCFG entries\n");
|
|
+ free_all_mmcfg();
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ if (fixup < __end_acpi_mcfg_fixups)
|
|
+ new->fixup = fixup->hook;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int __init pci_mmcfg_arch_init(void)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg;
|
|
+
|
|
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
|
|
+ if (pci_mmcfg_arch_map(cfg)) {
|
|
+ pci_mmcfg_arch_free();
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static void __init __pci_mmcfg_init(int early)
|
|
+{
|
|
+ if (list_empty(&pci_mmcfg_list)) {
|
|
+ pr_info("No MCFG table found!\n");
|
|
+ pci_mmcfg_arch_init_failed = true;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!pci_mmcfg_arch_init()) {
|
|
+ pr_info("pci_mmcfg_arch_init failed!\n");
|
|
+ free_all_mmcfg();
|
|
+ pci_mmcfg_arch_init_failed = true;
|
|
+ }
|
|
+}
|
|
+
|
|
+void __init pci_mmcfg_early_init(void)
|
|
+{
|
|
+ acpi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
|
|
+
|
|
+ __pci_mmcfg_init(1);
|
|
+}
|
|
+
|
|
+static int __init pci_mmcfg_init(void)
|
|
+{
|
|
+ pci_mmcfg_early_init();
|
|
+ return 0;
|
|
+}
|
|
+arch_initcall(pci_mmcfg_init);
|
|
+
|
|
+void __init pci_mmcfg_late_init(void)
|
|
+{
|
|
+ /* MMCONFIG hasn't been enabled yet, try again */
|
|
+ if (pci_mmcfg_arch_init_failed) {
|
|
+ acpi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
|
|
+ __pci_mmcfg_init(0);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int __init pci_mmcfg_late_insert_resources(void)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg;
|
|
+
|
|
+ pci_mmcfg_running_state = true;
|
|
+
|
|
+ /*
|
|
+ * Attempt to insert the mmcfg resources but not with the busy flag
|
|
+ * marked so it won't cause request errors when __request_region is
|
|
+ * called.
|
|
+ */
|
|
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
|
|
+ if (!cfg->res.parent)
|
|
+ insert_resource(&iomem_resource, &cfg->res);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Perform MMCONFIG resource insertion after PCI initialization to allow for
|
|
+ * misprogrammed MCFG tables that state larger sizes but actually conflict
|
|
+ * with other system resources.
|
|
+ */
|
|
+late_initcall(pci_mmcfg_late_insert_resources);
|
|
diff --git a/arch/arm64/pci/pci.c b/arch/arm64/pci/pci.c
|
|
new file mode 100644
|
|
index 0000000..0166475
|
|
--- /dev/null
|
|
+++ b/arch/arm64/pci/pci.c
|
|
@@ -0,0 +1,461 @@
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/pci.h>
|
|
+
|
|
+struct pci_root_info {
|
|
+ struct acpi_device *bridge;
|
|
+ char name[16];
|
|
+ unsigned int res_num;
|
|
+ struct resource *res;
|
|
+ resource_size_t *res_offset;
|
|
+ struct pci_sysdata sd;
|
|
+ u16 segment;
|
|
+ u8 start_bus;
|
|
+ u8 end_bus;
|
|
+};
|
|
+
|
|
+static char __iomem *pci_dev_base(struct pci_mmcfg_region *cfg,
|
|
+ unsigned int bus, unsigned int devfn)
|
|
+{
|
|
+ return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
|
|
+}
|
|
+
|
|
+static int __raw_pci_read(struct pci_mmcfg_region *cfg, unsigned int bus,
|
|
+ unsigned int devfn, int reg, int len, u32 *value)
|
|
+{
|
|
+ char __iomem *addr = pci_dev_base(cfg, bus, devfn) + (reg & ~3);
|
|
+ int shift = (reg & 3) * 8;
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(addr) >> shift;
|
|
+ switch (len) {
|
|
+ case 1:
|
|
+ *value = v & 0xff;
|
|
+ break;
|
|
+ case 2:
|
|
+ *value = v & 0xffff;
|
|
+ break;
|
|
+ case 4:
|
|
+ *value = v;
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __raw_pci_write(struct pci_mmcfg_region *cfg, unsigned int bus,
|
|
+ unsigned int devfn, int reg, int len, u32 value)
|
|
+{
|
|
+ char __iomem *addr = pci_dev_base(cfg, bus, devfn) + (reg & ~3);
|
|
+ int mask = 0, shift = (reg & 3) * 8;
|
|
+ u32 v;
|
|
+
|
|
+ switch (len) {
|
|
+ case 1:
|
|
+ mask = 0xff << shift;
|
|
+ break;
|
|
+ case 2:
|
|
+ mask = 0xffff << shift;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (mask) {
|
|
+ v = readl(addr) & ~mask;
|
|
+ writel(v | (value << shift), addr);
|
|
+ } else
|
|
+ writel(value, addr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * raw_pci_read/write - Platform-specific PCI config space access.
|
|
+ */
|
|
+int raw_pci_read(unsigned int domain, unsigned int bus,
|
|
+ unsigned int devfn, int reg, int len, u32 *val)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg;
|
|
+ int ret;
|
|
+
|
|
+ if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
|
|
+err: *val = -1;
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ rcu_read_lock();
|
|
+ cfg = pci_mmconfig_lookup(domain, bus);
|
|
+ if (!cfg || !cfg->virt) {
|
|
+ rcu_read_unlock();
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ if (cfg->read)
|
|
+ ret = (*cfg->read)(cfg, bus, devfn, reg, len, val);
|
|
+ else
|
|
+ ret = __raw_pci_read(cfg, bus, devfn, reg, len, val);
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int raw_pci_write(unsigned int domain, unsigned int bus,
|
|
+ unsigned int devfn, int reg, int len, u32 val)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg;
|
|
+ int ret;
|
|
+
|
|
+ if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
|
|
+ return -EINVAL;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ cfg = pci_mmconfig_lookup(domain, bus);
|
|
+ if (!cfg || !cfg->virt) {
|
|
+ rcu_read_unlock();
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (cfg->write)
|
|
+ ret = (*cfg->write)(cfg, bus, devfn, reg, len, val);
|
|
+ else
|
|
+ ret = __raw_pci_write(cfg, bus, devfn, reg, len, val);
|
|
+
|
|
+ rcu_read_unlock();
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+static int pci_read(struct pci_bus *bus, unsigned int devfn, int where,
|
|
+ int size, u32 *value)
|
|
+{
|
|
+ return raw_pci_read(pci_domain_nr(bus), bus->number,
|
|
+ devfn, where, size, value);
|
|
+}
|
|
+
|
|
+static int pci_write(struct pci_bus *bus, unsigned int devfn, int where,
|
|
+ int size, u32 value)
|
|
+{
|
|
+ return raw_pci_write(pci_domain_nr(bus), bus->number,
|
|
+ devfn, where, size, value);
|
|
+}
|
|
+
|
|
+struct pci_ops pci_root_ops = {
|
|
+ .read = pci_read,
|
|
+ .write = pci_write,
|
|
+};
|
|
+
|
|
+static acpi_status resource_to_addr(struct acpi_resource *resource,
|
|
+ struct acpi_resource_address64 *addr)
|
|
+{
|
|
+ acpi_status status;
|
|
+
|
|
+ memset(addr, 0, sizeof(*addr));
|
|
+ switch (resource->type) {
|
|
+ case ACPI_RESOURCE_TYPE_ADDRESS16:
|
|
+ case ACPI_RESOURCE_TYPE_ADDRESS32:
|
|
+ case ACPI_RESOURCE_TYPE_ADDRESS64:
|
|
+ status = acpi_resource_to_address64(resource, addr);
|
|
+ if (ACPI_SUCCESS(status) &&
|
|
+ (addr->resource_type == ACPI_MEMORY_RANGE ||
|
|
+ addr->resource_type == ACPI_IO_RANGE) &&
|
|
+ addr->address_length > 0) {
|
|
+ return AE_OK;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ return AE_ERROR;
|
|
+}
|
|
+
|
|
+static acpi_status count_resource(struct acpi_resource *acpi_res, void *data)
|
|
+{
|
|
+ struct pci_root_info *info = data;
|
|
+ struct acpi_resource_address64 addr;
|
|
+ acpi_status status;
|
|
+
|
|
+ status = resource_to_addr(acpi_res, &addr);
|
|
+ if (ACPI_SUCCESS(status))
|
|
+ info->res_num++;
|
|
+ return AE_OK;
|
|
+}
|
|
+
|
|
+static acpi_status setup_resource(struct acpi_resource *acpi_res, void *data)
|
|
+{
|
|
+ struct pci_root_info *info = data;
|
|
+ struct resource *res;
|
|
+ struct acpi_resource_address64 addr;
|
|
+ acpi_status status;
|
|
+ unsigned long flags;
|
|
+ u64 start, end;
|
|
+
|
|
+ status = resource_to_addr(acpi_res, &addr);
|
|
+ if (!ACPI_SUCCESS(status))
|
|
+ return AE_OK;
|
|
+
|
|
+ if (addr.resource_type == ACPI_MEMORY_RANGE) {
|
|
+ flags = IORESOURCE_MEM;
|
|
+ if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
|
|
+ flags |= IORESOURCE_PREFETCH;
|
|
+ } else if (addr.resource_type == ACPI_IO_RANGE) {
|
|
+ flags = IORESOURCE_IO;
|
|
+ } else
|
|
+ return AE_OK;
|
|
+
|
|
+ start = addr.minimum + addr.translation_offset;
|
|
+ end = addr.maximum + addr.translation_offset;
|
|
+
|
|
+ res = &info->res[info->res_num];
|
|
+ res->name = info->name;
|
|
+ res->flags = flags;
|
|
+ res->start = start;
|
|
+ res->end = end;
|
|
+
|
|
+ if (flags & IORESOURCE_IO) {
|
|
+ unsigned long port;
|
|
+ int err;
|
|
+
|
|
+ err = pci_register_io_range(start, addr.address_length);
|
|
+ if (err)
|
|
+ return AE_OK;
|
|
+
|
|
+ port = pci_address_to_pio(start);
|
|
+ if (port == (unsigned long)-1) {
|
|
+ res->start = -1;
|
|
+ res->end = -1;
|
|
+ return AE_OK;
|
|
+ }
|
|
+
|
|
+ res->start = port;
|
|
+ res->end = res->start + addr.address_length - 1;
|
|
+
|
|
+ if (pci_remap_iospace(res, start) < 0)
|
|
+ return AE_OK;
|
|
+
|
|
+ info->res_offset[info->res_num] = 0;
|
|
+ } else
|
|
+ info->res_offset[info->res_num] = addr.translation_offset;
|
|
+
|
|
+ info->res_num++;
|
|
+
|
|
+ return AE_OK;
|
|
+}
|
|
+
|
|
+static void coalesce_windows(struct pci_root_info *info, unsigned long type)
|
|
+{
|
|
+ int i, j;
|
|
+ struct resource *res1, *res2;
|
|
+
|
|
+ for (i = 0; i < info->res_num; i++) {
|
|
+ res1 = &info->res[i];
|
|
+ if (!(res1->flags & type))
|
|
+ continue;
|
|
+
|
|
+ for (j = i + 1; j < info->res_num; j++) {
|
|
+ res2 = &info->res[j];
|
|
+ if (!(res2->flags & type))
|
|
+ continue;
|
|
+
|
|
+ /*
|
|
+ * I don't like throwing away windows because then
|
|
+ * our resources no longer match the ACPI _CRS, but
|
|
+ * the kernel resource tree doesn't allow overlaps.
|
|
+ */
|
|
+ if (resource_overlaps(res1, res2)) {
|
|
+ res2->start = min(res1->start, res2->start);
|
|
+ res2->end = max(res1->end, res2->end);
|
|
+ dev_info(&info->bridge->dev,
|
|
+ "host bridge window expanded to %pR; %pR ignored\n",
|
|
+ res2, res1);
|
|
+ res1->flags = 0;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void add_resources(struct pci_root_info *info,
|
|
+ struct list_head *resources)
|
|
+{
|
|
+ int i;
|
|
+ struct resource *res, *root, *conflict;
|
|
+
|
|
+ coalesce_windows(info, IORESOURCE_MEM);
|
|
+ coalesce_windows(info, IORESOURCE_IO);
|
|
+
|
|
+ for (i = 0; i < info->res_num; i++) {
|
|
+ res = &info->res[i];
|
|
+
|
|
+ if (res->flags & IORESOURCE_MEM)
|
|
+ root = &iomem_resource;
|
|
+ else if (res->flags & IORESOURCE_IO)
|
|
+ root = &ioport_resource;
|
|
+ else
|
|
+ continue;
|
|
+
|
|
+ conflict = insert_resource_conflict(root, res);
|
|
+ if (conflict)
|
|
+ dev_info(&info->bridge->dev,
|
|
+ "ignoring host bridge window %pR (conflicts with %s %pR)\n",
|
|
+ res, conflict->name, conflict);
|
|
+ else
|
|
+ pci_add_resource_offset(resources, res,
|
|
+ info->res_offset[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void free_pci_root_info_res(struct pci_root_info *info)
|
|
+{
|
|
+ kfree(info->res);
|
|
+ info->res = NULL;
|
|
+ kfree(info->res_offset);
|
|
+ info->res_offset = NULL;
|
|
+ info->res_num = 0;
|
|
+}
|
|
+
|
|
+static void __release_pci_root_info(struct pci_root_info *info)
|
|
+{
|
|
+ int i;
|
|
+ struct resource *res;
|
|
+
|
|
+ for (i = 0; i < info->res_num; i++) {
|
|
+ res = &info->res[i];
|
|
+
|
|
+ if (!res->parent)
|
|
+ continue;
|
|
+
|
|
+ if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO)))
|
|
+ continue;
|
|
+
|
|
+ release_resource(res);
|
|
+ }
|
|
+
|
|
+ free_pci_root_info_res(info);
|
|
+
|
|
+ kfree(info);
|
|
+}
|
|
+
|
|
+static void release_pci_root_info(struct pci_host_bridge *bridge)
|
|
+{
|
|
+ struct pci_root_info *info = bridge->release_data;
|
|
+
|
|
+ __release_pci_root_info(info);
|
|
+}
|
|
+
|
|
+static void probe_pci_root_info(struct pci_root_info *info,
|
|
+ struct acpi_device *device,
|
|
+ int busnum, int domain)
|
|
+{
|
|
+ size_t size;
|
|
+
|
|
+ sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum);
|
|
+ info->bridge = device;
|
|
+
|
|
+ info->res_num = 0;
|
|
+ acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource,
|
|
+ info);
|
|
+ if (!info->res_num)
|
|
+ return;
|
|
+
|
|
+ size = sizeof(*info->res) * info->res_num;
|
|
+ info->res = kzalloc_node(size, GFP_KERNEL, info->sd.node);
|
|
+ if (!info->res) {
|
|
+ info->res_num = 0;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ size = sizeof(*info->res_offset) * info->res_num;
|
|
+ info->res_num = 0;
|
|
+ info->res_offset = kzalloc_node(size, GFP_KERNEL, info->sd.node);
|
|
+ if (!info->res_offset) {
|
|
+ kfree(info->res);
|
|
+ info->res = NULL;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource,
|
|
+ info);
|
|
+}
|
|
+
|
|
+/* Root bridge scanning */
|
|
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
|
|
+{
|
|
+ struct acpi_device *device = root->device;
|
|
+ struct pci_mmcfg_region *mcfg;
|
|
+ struct pci_root_info *info;
|
|
+ int domain = root->segment;
|
|
+ int busnum = root->secondary.start;
|
|
+ LIST_HEAD(resources);
|
|
+ struct pci_bus *bus;
|
|
+ struct pci_sysdata *sd;
|
|
+ int node;
|
|
+
|
|
+ /* we need mmconfig */
|
|
+ mcfg = pci_mmconfig_lookup(domain, busnum);
|
|
+ if (!mcfg) {
|
|
+ pr_err("pci_bus %04x:%02x has no MCFG table\n",
|
|
+ domain, busnum);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* temporary hack */
|
|
+ if (mcfg->fixup)
|
|
+ (*mcfg->fixup)(root, mcfg);
|
|
+
|
|
+ if (domain && !pci_domains_supported) {
|
|
+ pr_warn("PCI %04x:%02x: multiple domains not supported.\n",
|
|
+ domain, busnum);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ node = NUMA_NO_NODE;
|
|
+
|
|
+ info = kzalloc_node(sizeof(*info), GFP_KERNEL, node);
|
|
+ if (!info) {
|
|
+ pr_warn("PCI %04x:%02x: ignored (out of memory)\n",
|
|
+ domain, busnum);
|
|
+ return NULL;
|
|
+ }
|
|
+ info->segment = domain;
|
|
+ info->start_bus = busnum;
|
|
+ info->end_bus = root->secondary.end;
|
|
+
|
|
+ sd = &info->sd;
|
|
+ sd->domain = domain;
|
|
+ sd->node = node;
|
|
+ sd->companion = device;
|
|
+
|
|
+ probe_pci_root_info(info, device, busnum, domain);
|
|
+
|
|
+ /* insert busn res at first */
|
|
+ pci_add_resource(&resources, &root->secondary);
|
|
+
|
|
+ /* then _CRS resources */
|
|
+ add_resources(info, &resources);
|
|
+
|
|
+ bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd, &resources);
|
|
+ if (bus) {
|
|
+ pci_scan_child_bus(bus);
|
|
+ pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge),
|
|
+ release_pci_root_info, info);
|
|
+ } else {
|
|
+ pci_free_resource_list(&resources);
|
|
+ __release_pci_root_info(info);
|
|
+ }
|
|
+
|
|
+ /* After the PCI-E bus has been walked and all devices discovered,
|
|
+ * configure any settings of the fabric that might be necessary.
|
|
+ */
|
|
+ if (bus) {
|
|
+ struct pci_bus *child;
|
|
+
|
|
+ list_for_each_entry(child, &bus->children, node)
|
|
+ pcie_bus_configure_settings(child);
|
|
+ }
|
|
+
|
|
+ if (bus && node != NUMA_NO_NODE)
|
|
+ dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node);
|
|
+
|
|
+ return bus;
|
|
+}
|
|
+
|
|
+#endif /* CONFIG_ACPI */
|
|
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
|
|
index b23fe37..555e226 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 c3b2fcb..5a21476 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
|
|
@@ -47,6 +51,7 @@ acpi-y += int340x_thermal.o
|
|
acpi-y += power.o
|
|
acpi-y += event.o
|
|
acpi-y += sysfs.o
|
|
+acpi-y += property.o
|
|
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
|
|
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
|
|
acpi-$(CONFIG_ACPI_NUMA) += numa.o
|
|
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 447f6d6..c5ff8ba 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);
|
|
@@ -173,4 +178,10 @@ static inline void suspend_nvs_restore(void) {}
|
|
bool acpi_osi_is_win8(void);
|
|
#endif
|
|
|
|
+/*--------------------------------------------------------------------------
|
|
+ Device properties
|
|
+ -------------------------------------------------------------------------- */
|
|
+void acpi_init_properties(struct acpi_device *adev);
|
|
+void acpi_free_properties(struct acpi_device *adev);
|
|
+
|
|
#endif /* _ACPI_INTERNAL_H_ */
|
|
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
|
|
index 9964f70..5c480d5 100644
|
|
--- a/drivers/acpi/osl.c
|
|
+++ b/drivers/acpi/osl.c
|
|
@@ -336,11 +336,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 ef58f46..5c84e0d 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/property.c b/drivers/acpi/property.c
|
|
new file mode 100644
|
|
index 0000000..0d08373
|
|
--- /dev/null
|
|
+++ b/drivers/acpi/property.c
|
|
@@ -0,0 +1,551 @@
|
|
+/*
|
|
+ * ACPI device specific properties support.
|
|
+ *
|
|
+ * Copyright (C) 2014, Intel Corporation
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
+ * Darren Hart <dvhart@linux.intel.com>
|
|
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/export.h>
|
|
+
|
|
+#include "internal.h"
|
|
+
|
|
+/* ACPI _DSD device properties UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
|
|
+static const u8 prp_uuid[16] = {
|
|
+ 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
|
|
+ 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01
|
|
+};
|
|
+
|
|
+static bool acpi_property_value_ok(const union acpi_object *value)
|
|
+{
|
|
+ int j;
|
|
+
|
|
+ /*
|
|
+ * The value must be an integer, a string, a reference, or a package
|
|
+ * whose every element must be an integer, a string, or a reference.
|
|
+ */
|
|
+ switch (value->type) {
|
|
+ case ACPI_TYPE_INTEGER:
|
|
+ case ACPI_TYPE_STRING:
|
|
+ case ACPI_TYPE_LOCAL_REFERENCE:
|
|
+ return true;
|
|
+
|
|
+ case ACPI_TYPE_PACKAGE:
|
|
+ for (j = 0; j < value->package.count; j++)
|
|
+ switch (value->package.elements[j].type) {
|
|
+ case ACPI_TYPE_INTEGER:
|
|
+ case ACPI_TYPE_STRING:
|
|
+ case ACPI_TYPE_LOCAL_REFERENCE:
|
|
+ continue;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static bool acpi_properties_format_valid(const union acpi_object *properties)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < properties->package.count; i++) {
|
|
+ const union acpi_object *property;
|
|
+
|
|
+ property = &properties->package.elements[i];
|
|
+ /*
|
|
+ * Only two elements allowed, the first one must be a string and
|
|
+ * the second one has to satisfy certain conditions.
|
|
+ */
|
|
+ if (property->package.count != 2
|
|
+ || property->package.elements[0].type != ACPI_TYPE_STRING
|
|
+ || !acpi_property_value_ok(&property->package.elements[1]))
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void acpi_init_of_compatible(struct acpi_device *adev)
|
|
+{
|
|
+ const union acpi_object *of_compatible;
|
|
+ struct acpi_hardware_id *hwid;
|
|
+ bool acpi_of = false;
|
|
+ int ret;
|
|
+
|
|
+ /*
|
|
+ * Check if the special PRP0001 ACPI ID is present and in that
|
|
+ * case we fill in Device Tree compatible properties for this
|
|
+ * device.
|
|
+ */
|
|
+ list_for_each_entry(hwid, &adev->pnp.ids, list) {
|
|
+ if (!strcmp(hwid->id, "PRP0001")) {
|
|
+ acpi_of = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!acpi_of)
|
|
+ return;
|
|
+
|
|
+ ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING,
|
|
+ &of_compatible);
|
|
+ if (ret) {
|
|
+ ret = acpi_dev_get_property(adev, "compatible",
|
|
+ ACPI_TYPE_STRING, &of_compatible);
|
|
+ if (ret) {
|
|
+ acpi_handle_warn(adev->handle,
|
|
+ "PRP0001 requires compatible property\n");
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ adev->data.of_compatible = of_compatible;
|
|
+}
|
|
+
|
|
+void acpi_init_properties(struct acpi_device *adev)
|
|
+{
|
|
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
|
|
+ const union acpi_object *desc;
|
|
+ acpi_status status;
|
|
+ int i;
|
|
+
|
|
+ status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf,
|
|
+ ACPI_TYPE_PACKAGE);
|
|
+ if (ACPI_FAILURE(status))
|
|
+ return;
|
|
+
|
|
+ desc = buf.pointer;
|
|
+ if (desc->package.count % 2)
|
|
+ goto fail;
|
|
+
|
|
+ /* Look for the device properties UUID. */
|
|
+ for (i = 0; i < desc->package.count; i += 2) {
|
|
+ const union acpi_object *uuid, *properties;
|
|
+
|
|
+ uuid = &desc->package.elements[i];
|
|
+ properties = &desc->package.elements[i + 1];
|
|
+
|
|
+ /*
|
|
+ * The first element must be a UUID and the second one must be
|
|
+ * a package.
|
|
+ */
|
|
+ if (uuid->type != ACPI_TYPE_BUFFER || uuid->buffer.length != 16
|
|
+ || properties->type != ACPI_TYPE_PACKAGE)
|
|
+ break;
|
|
+
|
|
+ if (memcmp(uuid->buffer.pointer, prp_uuid, sizeof(prp_uuid)))
|
|
+ continue;
|
|
+
|
|
+ /*
|
|
+ * We found the matching UUID. Now validate the format of the
|
|
+ * package immediately following it.
|
|
+ */
|
|
+ if (!acpi_properties_format_valid(properties))
|
|
+ break;
|
|
+
|
|
+ adev->data.pointer = buf.pointer;
|
|
+ adev->data.properties = properties;
|
|
+
|
|
+ acpi_init_of_compatible(adev);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ fail:
|
|
+ dev_warn(&adev->dev, "Returned _DSD data is not valid, skipping\n");
|
|
+ ACPI_FREE(buf.pointer);
|
|
+}
|
|
+
|
|
+void acpi_free_properties(struct acpi_device *adev)
|
|
+{
|
|
+ ACPI_FREE((void *)adev->data.pointer);
|
|
+ adev->data.of_compatible = NULL;
|
|
+ adev->data.pointer = NULL;
|
|
+ adev->data.properties = NULL;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * acpi_dev_get_property - return an ACPI property with given name
|
|
+ * @adev: ACPI device to get property
|
|
+ * @name: Name of the property
|
|
+ * @type: Expected property type
|
|
+ * @obj: Location to store the property value (if not %NULL)
|
|
+ *
|
|
+ * Look up a property with @name and store a pointer to the resulting ACPI
|
|
+ * object at the location pointed to by @obj if found.
|
|
+ *
|
|
+ * Callers must not attempt to free the returned objects. These objects will be
|
|
+ * freed by the ACPI core automatically during the removal of @adev.
|
|
+ *
|
|
+ * Return: %0 if property with @name has been found (success),
|
|
+ * %-EINVAL if the arguments are invalid,
|
|
+ * %-ENODATA if the property doesn't exist,
|
|
+ * %-EPROTO if the property value type doesn't match @type.
|
|
+ */
|
|
+int acpi_dev_get_property(struct acpi_device *adev, const char *name,
|
|
+ acpi_object_type type, const union acpi_object **obj)
|
|
+{
|
|
+ const union acpi_object *properties;
|
|
+ int i;
|
|
+
|
|
+ if (!adev || !name)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (!adev->data.pointer || !adev->data.properties)
|
|
+ return -ENODATA;
|
|
+
|
|
+ properties = adev->data.properties;
|
|
+ for (i = 0; i < properties->package.count; i++) {
|
|
+ const union acpi_object *propname, *propvalue;
|
|
+ const union acpi_object *property;
|
|
+
|
|
+ property = &properties->package.elements[i];
|
|
+
|
|
+ propname = &property->package.elements[0];
|
|
+ propvalue = &property->package.elements[1];
|
|
+
|
|
+ if (!strcmp(name, propname->string.pointer)) {
|
|
+ if (type != ACPI_TYPE_ANY && propvalue->type != type)
|
|
+ return -EPROTO;
|
|
+ else if (obj)
|
|
+ *obj = propvalue;
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ return -ENODATA;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_get_property);
|
|
+
|
|
+/**
|
|
+ * acpi_dev_get_property_array - return an ACPI array property with given name
|
|
+ * @adev: ACPI device to get property
|
|
+ * @name: Name of the property
|
|
+ * @type: Expected type of array elements
|
|
+ * @obj: Location to store a pointer to the property value (if not NULL)
|
|
+ *
|
|
+ * Look up an array property with @name and store a pointer to the resulting
|
|
+ * ACPI object at the location pointed to by @obj if found.
|
|
+ *
|
|
+ * Callers must not attempt to free the returned objects. Those objects will be
|
|
+ * freed by the ACPI core automatically during the removal of @adev.
|
|
+ *
|
|
+ * Return: %0 if array property (package) with @name has been found (success),
|
|
+ * %-EINVAL if the arguments are invalid,
|
|
+ * %-ENODATA if the property doesn't exist,
|
|
+ * %-EPROTO if the property is not a package or the type of its elements
|
|
+ * doesn't match @type.
|
|
+ */
|
|
+int acpi_dev_get_property_array(struct acpi_device *adev, const char *name,
|
|
+ acpi_object_type type,
|
|
+ const union acpi_object **obj)
|
|
+{
|
|
+ const union acpi_object *prop;
|
|
+ int ret, i;
|
|
+
|
|
+ ret = acpi_dev_get_property(adev, name, ACPI_TYPE_PACKAGE, &prop);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (type != ACPI_TYPE_ANY) {
|
|
+ /* Check that all elements are of correct type. */
|
|
+ for (i = 0; i < prop->package.count; i++)
|
|
+ if (prop->package.elements[i].type != type)
|
|
+ return -EPROTO;
|
|
+ }
|
|
+ if (obj)
|
|
+ *obj = prop;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_array);
|
|
+
|
|
+/**
|
|
+ * acpi_dev_get_property_reference - returns handle to the referenced object
|
|
+ * @adev: ACPI device to get property
|
|
+ * @name: Name of the property
|
|
+ * @index: Index of the reference to return
|
|
+ * @args: Location to store the returned reference with optional arguments
|
|
+ *
|
|
+ * Find property with @name, verifify that it is a package containing at least
|
|
+ * one object reference and if so, store the ACPI device object pointer to the
|
|
+ * target object in @args->adev. If the reference includes arguments, store
|
|
+ * them in the @args->args[] array.
|
|
+ *
|
|
+ * If there's more than one reference in the property value package, @index is
|
|
+ * used to select the one to return.
|
|
+ *
|
|
+ * Return: %0 on success, negative error code on failure.
|
|
+ */
|
|
+int acpi_dev_get_property_reference(struct acpi_device *adev,
|
|
+ const char *name, size_t index,
|
|
+ struct acpi_reference_args *args)
|
|
+{
|
|
+ const union acpi_object *element, *end;
|
|
+ const union acpi_object *obj;
|
|
+ struct acpi_device *device;
|
|
+ int ret, idx = 0;
|
|
+
|
|
+ ret = acpi_dev_get_property(adev, name, ACPI_TYPE_ANY, &obj);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * The simplest case is when the value is a single reference. Just
|
|
+ * return that reference then.
|
|
+ */
|
|
+ if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) {
|
|
+ if (index)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = acpi_bus_get_device(obj->reference.handle, &device);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ args->adev = device;
|
|
+ args->nargs = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * If it is not a single reference, then it is a package of
|
|
+ * references followed by number of ints as follows:
|
|
+ *
|
|
+ * Package () { REF, INT, REF, INT, INT }
|
|
+ *
|
|
+ * The index argument is then used to determine which reference
|
|
+ * the caller wants (along with the arguments).
|
|
+ */
|
|
+ if (obj->type != ACPI_TYPE_PACKAGE || index >= obj->package.count)
|
|
+ return -EPROTO;
|
|
+
|
|
+ element = obj->package.elements;
|
|
+ end = element + obj->package.count;
|
|
+
|
|
+ while (element < end) {
|
|
+ u32 nargs, i;
|
|
+
|
|
+ if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
|
|
+ return -EPROTO;
|
|
+
|
|
+ ret = acpi_bus_get_device(element->reference.handle, &device);
|
|
+ if (ret)
|
|
+ return -ENODEV;
|
|
+
|
|
+ element++;
|
|
+ nargs = 0;
|
|
+
|
|
+ /* assume following integer elements are all args */
|
|
+ for (i = 0; element + i < end; i++) {
|
|
+ int type = element[i].type;
|
|
+
|
|
+ if (type == ACPI_TYPE_INTEGER)
|
|
+ nargs++;
|
|
+ else if (type == ACPI_TYPE_LOCAL_REFERENCE)
|
|
+ break;
|
|
+ else
|
|
+ return -EPROTO;
|
|
+ }
|
|
+
|
|
+ if (idx++ == index) {
|
|
+ args->adev = device;
|
|
+ args->nargs = nargs;
|
|
+ for (i = 0; i < nargs; i++)
|
|
+ args->args[i] = element[i].integer.value;
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ element += nargs;
|
|
+ }
|
|
+
|
|
+ return -EPROTO;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_get_property_reference);
|
|
+
|
|
+int acpi_dev_prop_get(struct acpi_device *adev, const char *propname,
|
|
+ void **valptr)
|
|
+{
|
|
+ return acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY,
|
|
+ (const union acpi_object **)valptr);
|
|
+}
|
|
+
|
|
+int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname,
|
|
+ enum dev_prop_type proptype, void *val)
|
|
+{
|
|
+ const union acpi_object *obj;
|
|
+ int ret;
|
|
+
|
|
+ if (!val)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) {
|
|
+ ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_INTEGER, &obj);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ switch (proptype) {
|
|
+ case DEV_PROP_U8:
|
|
+ if (obj->integer.value > U8_MAX)
|
|
+ return -EOVERFLOW;
|
|
+ *(u8 *)val = obj->integer.value;
|
|
+ break;
|
|
+ case DEV_PROP_U16:
|
|
+ if (obj->integer.value > U16_MAX)
|
|
+ return -EOVERFLOW;
|
|
+ *(u16 *)val = obj->integer.value;
|
|
+ break;
|
|
+ case DEV_PROP_U32:
|
|
+ if (obj->integer.value > U32_MAX)
|
|
+ return -EOVERFLOW;
|
|
+ *(u32 *)val = obj->integer.value;
|
|
+ break;
|
|
+ default:
|
|
+ *(u64 *)val = obj->integer.value;
|
|
+ break;
|
|
+ }
|
|
+ } else if (proptype == DEV_PROP_STRING) {
|
|
+ ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_STRING, &obj);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ *(char **)val = obj->string.pointer;
|
|
+ } else {
|
|
+ ret = -EINVAL;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int acpi_copy_property_array_u8(const union acpi_object *items, u8 *val,
|
|
+ size_t nval)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < nval; i++) {
|
|
+ if (items[i].type != ACPI_TYPE_INTEGER)
|
|
+ return -EPROTO;
|
|
+ if (items[i].integer.value > U8_MAX)
|
|
+ return -EOVERFLOW;
|
|
+
|
|
+ val[i] = items[i].integer.value;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int acpi_copy_property_array_u16(const union acpi_object *items,
|
|
+ u16 *val, size_t nval)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < nval; i++) {
|
|
+ if (items[i].type != ACPI_TYPE_INTEGER)
|
|
+ return -EPROTO;
|
|
+ if (items[i].integer.value > U16_MAX)
|
|
+ return -EOVERFLOW;
|
|
+
|
|
+ val[i] = items[i].integer.value;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int acpi_copy_property_array_u32(const union acpi_object *items,
|
|
+ u32 *val, size_t nval)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < nval; i++) {
|
|
+ if (items[i].type != ACPI_TYPE_INTEGER)
|
|
+ return -EPROTO;
|
|
+ if (items[i].integer.value > U32_MAX)
|
|
+ return -EOVERFLOW;
|
|
+
|
|
+ val[i] = items[i].integer.value;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int acpi_copy_property_array_u64(const union acpi_object *items,
|
|
+ u64 *val, size_t nval)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < nval; i++) {
|
|
+ if (items[i].type != ACPI_TYPE_INTEGER)
|
|
+ return -EPROTO;
|
|
+
|
|
+ val[i] = items[i].integer.value;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int acpi_copy_property_array_string(const union acpi_object *items,
|
|
+ char **val, size_t nval)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < nval; i++) {
|
|
+ if (items[i].type != ACPI_TYPE_STRING)
|
|
+ return -EPROTO;
|
|
+
|
|
+ val[i] = items[i].string.pointer;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int acpi_dev_prop_read(struct acpi_device *adev, const char *propname,
|
|
+ enum dev_prop_type proptype, void *val, size_t nval)
|
|
+{
|
|
+ const union acpi_object *obj;
|
|
+ const union acpi_object *items;
|
|
+ int ret;
|
|
+
|
|
+ if (val && nval == 1) {
|
|
+ ret = acpi_dev_prop_read_single(adev, propname, proptype, val);
|
|
+ if (!ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = acpi_dev_get_property_array(adev, propname, ACPI_TYPE_ANY, &obj);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (!val)
|
|
+ return obj->package.count;
|
|
+ else if (nval <= 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (nval > obj->package.count)
|
|
+ return -EOVERFLOW;
|
|
+
|
|
+ items = obj->package.elements;
|
|
+ switch (proptype) {
|
|
+ case DEV_PROP_U8:
|
|
+ ret = acpi_copy_property_array_u8(items, (u8 *)val, nval);
|
|
+ break;
|
|
+ case DEV_PROP_U16:
|
|
+ ret = acpi_copy_property_array_u16(items, (u16 *)val, nval);
|
|
+ break;
|
|
+ case DEV_PROP_U32:
|
|
+ ret = acpi_copy_property_array_u32(items, (u32 *)val, nval);
|
|
+ break;
|
|
+ case DEV_PROP_U64:
|
|
+ ret = acpi_copy_property_array_u64(items, (u64 *)val, nval);
|
|
+ break;
|
|
+ case DEV_PROP_STRING:
|
|
+ ret = acpi_copy_property_array_string(items, (char **)val, nval);
|
|
+ break;
|
|
+ default:
|
|
+ ret = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
|
|
index 0476e90..9cb5cca 100644
|
|
--- a/drivers/acpi/scan.c
|
|
+++ b/drivers/acpi/scan.c
|
|
@@ -124,17 +124,56 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
|
|
if (list_empty(&acpi_dev->pnp.ids))
|
|
return 0;
|
|
|
|
- len = snprintf(modalias, size, "acpi:");
|
|
- size -= len;
|
|
-
|
|
- list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
|
|
- count = snprintf(&modalias[len], size, "%s:", id->id);
|
|
- if (count < 0)
|
|
- return -EINVAL;
|
|
- if (count >= size)
|
|
- return -ENOMEM;
|
|
- len += count;
|
|
- size -= count;
|
|
+ /*
|
|
+ * If the device has PRP0001 we expose DT compatible modalias
|
|
+ * instead in form of of:NnameTCcompatible.
|
|
+ */
|
|
+ if (acpi_dev->data.of_compatible) {
|
|
+ struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
|
|
+ const union acpi_object *of_compatible, *obj;
|
|
+ int i, nval;
|
|
+ char *c;
|
|
+
|
|
+ acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
|
|
+ /* DT strings are all in lower case */
|
|
+ for (c = buf.pointer; *c != '\0'; c++)
|
|
+ *c = tolower(*c);
|
|
+
|
|
+ len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);
|
|
+ ACPI_FREE(buf.pointer);
|
|
+
|
|
+ of_compatible = acpi_dev->data.of_compatible;
|
|
+ if (of_compatible->type == ACPI_TYPE_PACKAGE) {
|
|
+ nval = of_compatible->package.count;
|
|
+ obj = of_compatible->package.elements;
|
|
+ } else { /* Must be ACPI_TYPE_STRING. */
|
|
+ nval = 1;
|
|
+ obj = of_compatible;
|
|
+ }
|
|
+ for (i = 0; i < nval; i++, obj++) {
|
|
+ count = snprintf(&modalias[len], size, "C%s",
|
|
+ obj->string.pointer);
|
|
+ if (count < 0)
|
|
+ return -EINVAL;
|
|
+ if (count >= size)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ len += count;
|
|
+ size -= count;
|
|
+ }
|
|
+ } else {
|
|
+ len = snprintf(modalias, size, "acpi:");
|
|
+ size -= len;
|
|
+
|
|
+ list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
|
|
+ count = snprintf(&modalias[len], size, "%s:", id->id);
|
|
+ if (count < 0)
|
|
+ return -EINVAL;
|
|
+ if (count >= size)
|
|
+ return -ENOMEM;
|
|
+ len += count;
|
|
+ size -= count;
|
|
+ }
|
|
}
|
|
|
|
modalias[len] = '\0';
|
|
@@ -902,6 +941,51 @@ int acpi_match_device_ids(struct acpi_device *device,
|
|
}
|
|
EXPORT_SYMBOL(acpi_match_device_ids);
|
|
|
|
+/* Performs match against special "PRP0001" shoehorn ACPI ID */
|
|
+static bool acpi_of_driver_match_device(struct device *dev,
|
|
+ const struct device_driver *drv)
|
|
+{
|
|
+ const union acpi_object *of_compatible, *obj;
|
|
+ struct acpi_device *adev;
|
|
+ int i, nval;
|
|
+
|
|
+ adev = ACPI_COMPANION(dev);
|
|
+ if (!adev)
|
|
+ return false;
|
|
+
|
|
+ of_compatible = adev->data.of_compatible;
|
|
+ if (!drv->of_match_table || !of_compatible)
|
|
+ return false;
|
|
+
|
|
+ if (of_compatible->type == ACPI_TYPE_PACKAGE) {
|
|
+ nval = of_compatible->package.count;
|
|
+ obj = of_compatible->package.elements;
|
|
+ } else { /* Must be ACPI_TYPE_STRING. */
|
|
+ nval = 1;
|
|
+ obj = of_compatible;
|
|
+ }
|
|
+ /* Now we can look for the driver DT compatible strings */
|
|
+ for (i = 0; i < nval; i++, obj++) {
|
|
+ const struct of_device_id *id;
|
|
+
|
|
+ for (id = drv->of_match_table; id->compatible[0]; id++)
|
|
+ if (!strcasecmp(obj->string.pointer, id->compatible))
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool acpi_driver_match_device(struct device *dev,
|
|
+ const struct device_driver *drv)
|
|
+{
|
|
+ if (!drv->acpi_match_table)
|
|
+ return acpi_of_driver_match_device(dev, drv);
|
|
+
|
|
+ return !!acpi_match_device(drv->acpi_match_table, dev);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_driver_match_device);
|
|
+
|
|
static void acpi_free_power_resources_lists(struct acpi_device *device)
|
|
{
|
|
int i;
|
|
@@ -922,6 +1006,7 @@ static void acpi_device_release(struct device *dev)
|
|
{
|
|
struct acpi_device *acpi_dev = to_acpi_device(dev);
|
|
|
|
+ acpi_free_properties(acpi_dev);
|
|
acpi_free_pnp_ids(&acpi_dev->pnp);
|
|
acpi_free_power_resources_lists(acpi_dev);
|
|
kfree(acpi_dev);
|
|
@@ -1304,6 +1389,26 @@ int acpi_device_add(struct acpi_device *device,
|
|
return result;
|
|
}
|
|
|
|
+struct acpi_device *acpi_get_next_child(struct device *dev,
|
|
+ struct acpi_device *child)
|
|
+{
|
|
+ struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
+ struct list_head *head, *next;
|
|
+
|
|
+ if (!adev)
|
|
+ return NULL;
|
|
+
|
|
+ head = &adev->children;
|
|
+ if (list_empty(head))
|
|
+ return NULL;
|
|
+
|
|
+ if (!child)
|
|
+ return list_first_entry(head, struct acpi_device, node);
|
|
+
|
|
+ next = child->node.next;
|
|
+ return next == head ? NULL : list_entry(next, struct acpi_device, node);
|
|
+}
|
|
+
|
|
/* --------------------------------------------------------------------------
|
|
Driver Management
|
|
-------------------------------------------------------------------------- */
|
|
@@ -1923,9 +2028,11 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
|
|
device->device_type = type;
|
|
device->handle = handle;
|
|
device->parent = acpi_bus_get_parent(handle);
|
|
+ device->fwnode.type = FWNODE_ACPI;
|
|
acpi_set_device_status(device, sta);
|
|
acpi_device_get_busid(device);
|
|
acpi_set_pnp_ids(handle, &device->pnp, type);
|
|
+ acpi_init_properties(device);
|
|
acpi_bus_get_flags(device);
|
|
device->flags.match_driver = false;
|
|
device->flags.initialized = true;
|
|
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 371ac12..af325a7 100644
|
|
--- a/drivers/acpi/utils.c
|
|
+++ b/drivers/acpi/utils.c
|
|
@@ -723,3 +723,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 cd4cccb..edb00c6 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 06f1d59..df2ea85 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 = {
|
|
@@ -71,6 +74,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,
|
|
@@ -78,6 +88,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 0f8538f..2d8103a 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
|
|
@@ -501,11 +495,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)
|
|
@@ -518,7 +507,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);
|
|
@@ -533,6 +522,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"},
|
|
{},
|
|
@@ -546,6 +545,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/base/Makefile b/drivers/base/Makefile
|
|
index 6922cd6..53c3fe1 100644
|
|
--- a/drivers/base/Makefile
|
|
+++ b/drivers/base/Makefile
|
|
@@ -4,7 +4,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
|
|
driver.o class.o platform.o \
|
|
cpu.o firmware.o init.o map.o devres.o \
|
|
attribute_container.o transport_class.o \
|
|
- topology.o container.o
|
|
+ topology.o container.o property.o
|
|
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
|
|
obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
|
|
obj-y += power/
|
|
diff --git a/drivers/base/property.c b/drivers/base/property.c
|
|
new file mode 100644
|
|
index 0000000..c458458
|
|
--- /dev/null
|
|
+++ b/drivers/base/property.c
|
|
@@ -0,0 +1,431 @@
|
|
+/*
|
|
+ * property.c - Unified device property interface.
|
|
+ *
|
|
+ * Copyright (C) 2014, Intel Corporation
|
|
+ * Authors: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
+ * Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <linux/property.h>
|
|
+#include <linux/export.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/of.h>
|
|
+
|
|
+/**
|
|
+ * device_property_present - check if a property of a device is present
|
|
+ * @dev: Device whose property is being checked
|
|
+ * @propname: Name of the property
|
|
+ *
|
|
+ * Check if property @propname is present in the device firmware description.
|
|
+ */
|
|
+bool device_property_present(struct device *dev, const char *propname)
|
|
+{
|
|
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node)
|
|
+ return of_property_read_bool(dev->of_node, propname);
|
|
+
|
|
+ return !acpi_dev_prop_get(ACPI_COMPANION(dev), propname, NULL);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_property_present);
|
|
+
|
|
+/**
|
|
+ * fwnode_property_present - check if a property of a firmware node is present
|
|
+ * @fwnode: Firmware node whose property to check
|
|
+ * @propname: Name of the property
|
|
+ */
|
|
+bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname)
|
|
+{
|
|
+ if (is_of_node(fwnode))
|
|
+ return of_property_read_bool(of_node(fwnode), propname);
|
|
+ else if (is_acpi_node(fwnode))
|
|
+ return !acpi_dev_prop_get(acpi_node(fwnode), propname, NULL);
|
|
+
|
|
+ return false;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_property_present);
|
|
+
|
|
+#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
|
|
+ (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
|
|
+ : of_property_count_elems_of_size((node), (propname), sizeof(type))
|
|
+
|
|
+#define DEV_PROP_READ_ARRAY(_dev_, _propname_, _type_, _proptype_, _val_, _nval_) \
|
|
+ IS_ENABLED(CONFIG_OF) && _dev_->of_node ? \
|
|
+ (OF_DEV_PROP_READ_ARRAY(_dev_->of_node, _propname_, _type_, \
|
|
+ _val_, _nval_)) : \
|
|
+ acpi_dev_prop_read(ACPI_COMPANION(_dev_), _propname_, \
|
|
+ _proptype_, _val_, _nval_)
|
|
+
|
|
+/**
|
|
+ * device_property_read_u8_array - return a u8 array property of a device
|
|
+ * @dev: Device to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Function reads an array of u8 properties with @propname from the device
|
|
+ * firmware description and stores them to @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of numbers,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected.
|
|
+ */
|
|
+int device_property_read_u8_array(struct device *dev, const char *propname,
|
|
+ u8 *val, size_t nval)
|
|
+{
|
|
+ return DEV_PROP_READ_ARRAY(dev, propname, u8, DEV_PROP_U8, val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_property_read_u8_array);
|
|
+
|
|
+/**
|
|
+ * device_property_read_u16_array - return a u16 array property of a device
|
|
+ * @dev: Device to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Function reads an array of u16 properties with @propname from the device
|
|
+ * firmware description and stores them to @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of numbers,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected.
|
|
+ */
|
|
+int device_property_read_u16_array(struct device *dev, const char *propname,
|
|
+ u16 *val, size_t nval)
|
|
+{
|
|
+ return DEV_PROP_READ_ARRAY(dev, propname, u16, DEV_PROP_U16, val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_property_read_u16_array);
|
|
+
|
|
+/**
|
|
+ * device_property_read_u32_array - return a u32 array property of a device
|
|
+ * @dev: Device to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Function reads an array of u32 properties with @propname from the device
|
|
+ * firmware description and stores them to @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of numbers,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected.
|
|
+ */
|
|
+int device_property_read_u32_array(struct device *dev, const char *propname,
|
|
+ u32 *val, size_t nval)
|
|
+{
|
|
+ return DEV_PROP_READ_ARRAY(dev, propname, u32, DEV_PROP_U32, val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_property_read_u32_array);
|
|
+
|
|
+/**
|
|
+ * device_property_read_u64_array - return a u64 array property of a device
|
|
+ * @dev: Device to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Function reads an array of u64 properties with @propname from the device
|
|
+ * firmware description and stores them to @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of numbers,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected.
|
|
+ */
|
|
+int device_property_read_u64_array(struct device *dev, const char *propname,
|
|
+ u64 *val, size_t nval)
|
|
+{
|
|
+ return DEV_PROP_READ_ARRAY(dev, propname, u64, DEV_PROP_U64, val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_property_read_u64_array);
|
|
+
|
|
+/**
|
|
+ * device_property_read_string_array - return a string array property of device
|
|
+ * @dev: Device to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Function reads an array of string properties with @propname from the device
|
|
+ * firmware description and stores them to @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO or %-EILSEQ if the property is not an array of strings,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected.
|
|
+ */
|
|
+int device_property_read_string_array(struct device *dev, const char *propname,
|
|
+ const char **val, size_t nval)
|
|
+{
|
|
+ return IS_ENABLED(CONFIG_OF) && dev->of_node ?
|
|
+ of_property_read_string_array(dev->of_node, propname, val, nval) :
|
|
+ acpi_dev_prop_read(ACPI_COMPANION(dev), propname,
|
|
+ DEV_PROP_STRING, val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_property_read_string_array);
|
|
+
|
|
+/**
|
|
+ * device_property_read_string - return a string property of a device
|
|
+ * @dev: Device to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The value is stored here
|
|
+ *
|
|
+ * Function reads property @propname from the device firmware description and
|
|
+ * stores the value into @val if found. The value is checked to be a string.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO or %-EILSEQ if the property type is not a string.
|
|
+ */
|
|
+int device_property_read_string(struct device *dev, const char *propname,
|
|
+ const char **val)
|
|
+{
|
|
+ return IS_ENABLED(CONFIG_OF) && dev->of_node ?
|
|
+ of_property_read_string(dev->of_node, propname, val) :
|
|
+ acpi_dev_prop_read(ACPI_COMPANION(dev), propname,
|
|
+ DEV_PROP_STRING, val, 1);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_property_read_string);
|
|
+
|
|
+#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
|
|
+({ \
|
|
+ int _ret_; \
|
|
+ if (is_of_node(_fwnode_)) \
|
|
+ _ret_ = OF_DEV_PROP_READ_ARRAY(of_node(_fwnode_), _propname_, \
|
|
+ _type_, _val_, _nval_); \
|
|
+ else if (is_acpi_node(_fwnode_)) \
|
|
+ _ret_ = acpi_dev_prop_read(acpi_node(_fwnode_), _propname_, \
|
|
+ _proptype_, _val_, _nval_); \
|
|
+ else \
|
|
+ _ret_ = -ENXIO; \
|
|
+ _ret_; \
|
|
+})
|
|
+
|
|
+/**
|
|
+ * fwnode_property_read_u8_array - return a u8 array property of firmware node
|
|
+ * @fwnode: Firmware node to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Read an array of u8 properties with @propname from @fwnode and stores them to
|
|
+ * @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of numbers,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected,
|
|
+ * %-ENXIO if no suitable firmware interface is present.
|
|
+ */
|
|
+int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u8 *val, size_t nval)
|
|
+{
|
|
+ return FWNODE_PROP_READ_ARRAY(fwnode, propname, u8, DEV_PROP_U8,
|
|
+ val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_property_read_u8_array);
|
|
+
|
|
+/**
|
|
+ * fwnode_property_read_u16_array - return a u16 array property of firmware node
|
|
+ * @fwnode: Firmware node to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Read an array of u16 properties with @propname from @fwnode and store them to
|
|
+ * @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of numbers,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected,
|
|
+ * %-ENXIO if no suitable firmware interface is present.
|
|
+ */
|
|
+int fwnode_property_read_u16_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u16 *val, size_t nval)
|
|
+{
|
|
+ return FWNODE_PROP_READ_ARRAY(fwnode, propname, u16, DEV_PROP_U16,
|
|
+ val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_property_read_u16_array);
|
|
+
|
|
+/**
|
|
+ * fwnode_property_read_u32_array - return a u32 array property of firmware node
|
|
+ * @fwnode: Firmware node to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Read an array of u32 properties with @propname from @fwnode store them to
|
|
+ * @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of numbers,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected,
|
|
+ * %-ENXIO if no suitable firmware interface is present.
|
|
+ */
|
|
+int fwnode_property_read_u32_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u32 *val, size_t nval)
|
|
+{
|
|
+ return FWNODE_PROP_READ_ARRAY(fwnode, propname, u32, DEV_PROP_U32,
|
|
+ val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_property_read_u32_array);
|
|
+
|
|
+/**
|
|
+ * fwnode_property_read_u64_array - return a u64 array property firmware node
|
|
+ * @fwnode: Firmware node to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Read an array of u64 properties with @propname from @fwnode and store them to
|
|
+ * @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of numbers,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected,
|
|
+ * %-ENXIO if no suitable firmware interface is present.
|
|
+ */
|
|
+int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u64 *val, size_t nval)
|
|
+{
|
|
+ return FWNODE_PROP_READ_ARRAY(fwnode, propname, u64, DEV_PROP_U64,
|
|
+ val, nval);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array);
|
|
+
|
|
+/**
|
|
+ * fwnode_property_read_string_array - return string array property of a node
|
|
+ * @fwnode: Firmware node to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The values are stored here
|
|
+ * @nval: Size of the @val array
|
|
+ *
|
|
+ * Read an string list property @propname from the given firmware node and store
|
|
+ * them to @val if found.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO if the property is not an array of strings,
|
|
+ * %-EOVERFLOW if the size of the property is not as expected,
|
|
+ * %-ENXIO if no suitable firmware interface is present.
|
|
+ */
|
|
+int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, const char **val,
|
|
+ size_t nval)
|
|
+{
|
|
+ if (is_of_node(fwnode))
|
|
+ return of_property_read_string_array(of_node(fwnode), propname,
|
|
+ val, nval);
|
|
+ else if (is_acpi_node(fwnode))
|
|
+ return acpi_dev_prop_read(acpi_node(fwnode), propname,
|
|
+ DEV_PROP_STRING, val, nval);
|
|
+
|
|
+ return -ENXIO;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
|
|
+
|
|
+/**
|
|
+ * fwnode_property_read_string - return a string property of a firmware node
|
|
+ * @fwnode: Firmware node to get the property of
|
|
+ * @propname: Name of the property
|
|
+ * @val: The value is stored here
|
|
+ *
|
|
+ * Read property @propname from the given firmware node and store the value into
|
|
+ * @val if found. The value is checked to be a string.
|
|
+ *
|
|
+ * Return: %0 if the property was found (success),
|
|
+ * %-EINVAL if given arguments are not valid,
|
|
+ * %-ENODATA if the property does not have a value,
|
|
+ * %-EPROTO or %-EILSEQ if the property is not a string,
|
|
+ * %-ENXIO if no suitable firmware interface is present.
|
|
+ */
|
|
+int fwnode_property_read_string(struct fwnode_handle *fwnode,
|
|
+ const char *propname, const char **val)
|
|
+{
|
|
+ if (is_of_node(fwnode))
|
|
+ return of_property_read_string(of_node(fwnode),propname, val);
|
|
+ else if (is_acpi_node(fwnode))
|
|
+ return acpi_dev_prop_read(acpi_node(fwnode), propname,
|
|
+ DEV_PROP_STRING, val, 1);
|
|
+
|
|
+ return -ENXIO;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_property_read_string);
|
|
+
|
|
+/**
|
|
+ * device_get_next_child_node - Return the next child node handle for a device
|
|
+ * @dev: Device to find the next child node for.
|
|
+ * @child: Handle to one of the device's child nodes or a null handle.
|
|
+ */
|
|
+struct fwnode_handle *device_get_next_child_node(struct device *dev,
|
|
+ struct fwnode_handle *child)
|
|
+{
|
|
+ if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
|
|
+ struct device_node *node;
|
|
+
|
|
+ node = of_get_next_available_child(dev->of_node, of_node(child));
|
|
+ if (node)
|
|
+ return &node->fwnode;
|
|
+ } else if (IS_ENABLED(CONFIG_ACPI)) {
|
|
+ struct acpi_device *node;
|
|
+
|
|
+ node = acpi_get_next_child(dev, acpi_node(child));
|
|
+ if (node)
|
|
+ return acpi_fwnode_handle(node);
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_get_next_child_node);
|
|
+
|
|
+/**
|
|
+ * fwnode_handle_put - Drop reference to a device node
|
|
+ * @fwnode: Pointer to the device node to drop the reference to.
|
|
+ *
|
|
+ * This has to be used when terminating device_for_each_child_node() iteration
|
|
+ * with break or return to prevent stale device node references from being left
|
|
+ * behind.
|
|
+ */
|
|
+void fwnode_handle_put(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ if (is_of_node(fwnode))
|
|
+ of_node_put(of_node(fwnode));
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_handle_put);
|
|
+
|
|
+/**
|
|
+ * device_get_child_node_count - return the number of child nodes for device
|
|
+ * @dev: Device to cound the child nodes for
|
|
+ */
|
|
+unsigned int device_get_child_node_count(struct device *dev)
|
|
+{
|
|
+ struct fwnode_handle *child;
|
|
+ unsigned int count = 0;
|
|
+
|
|
+ device_for_each_child_node(dev, child)
|
|
+ count++;
|
|
+
|
|
+ return count;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(device_get_child_node_count);
|
|
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
|
|
index 43005d4..c9411e6 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;
|
|
|
|
@@ -370,8 +372,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
|
|
@@ -687,20 +693,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
|
|
@@ -719,13 +713,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)
|
|
{
|
|
@@ -792,3 +804,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/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
|
|
index 17afc51..c5f7b4e 100644
|
|
--- a/drivers/firmware/dmi_scan.c
|
|
+++ b/drivers/firmware/dmi_scan.c
|
|
@@ -93,6 +93,12 @@ static void dmi_table(u8 *buf, int len, int num,
|
|
const struct dmi_header *dm = (const struct dmi_header *)data;
|
|
|
|
/*
|
|
+ * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
|
|
+ */
|
|
+ if (dm->type == DMI_ENTRY_END_OF_TABLE)
|
|
+ break;
|
|
+
|
|
+ /*
|
|
* We want to know the total length (formatted area and
|
|
* strings) before decoding to make sure we won't run off the
|
|
* table in dmi_decode or dmi_string
|
|
@@ -107,7 +113,7 @@ static void dmi_table(u8 *buf, int len, int num,
|
|
}
|
|
}
|
|
|
|
-static u32 dmi_base;
|
|
+static phys_addr_t dmi_base;
|
|
static u16 dmi_len;
|
|
static u16 dmi_num;
|
|
|
|
@@ -467,7 +473,7 @@ static int __init dmi_present(const u8 *buf)
|
|
|
|
if (memcmp(buf, "_SM_", 4) == 0 &&
|
|
buf[5] < 32 && dmi_checksum(buf, buf[5])) {
|
|
- smbios_ver = (buf[6] << 8) + buf[7];
|
|
+ smbios_ver = get_unaligned_be16(buf + 6);
|
|
|
|
/* Some BIOS report weird SMBIOS version, fix that up */
|
|
switch (smbios_ver) {
|
|
@@ -489,10 +495,9 @@ static int __init dmi_present(const u8 *buf)
|
|
buf += 16;
|
|
|
|
if (memcmp(buf, "_DMI_", 5) == 0 && dmi_checksum(buf, 15)) {
|
|
- dmi_num = (buf[13] << 8) | buf[12];
|
|
- dmi_len = (buf[7] << 8) | buf[6];
|
|
- dmi_base = (buf[11] << 24) | (buf[10] << 16) |
|
|
- (buf[9] << 8) | buf[8];
|
|
+ dmi_num = get_unaligned_le16(buf + 12);
|
|
+ dmi_len = get_unaligned_le16(buf + 6);
|
|
+ dmi_base = get_unaligned_le32(buf + 8);
|
|
|
|
if (dmi_walk_early(dmi_decode) == 0) {
|
|
if (smbios_ver) {
|
|
@@ -514,12 +519,72 @@ static int __init dmi_present(const u8 *buf)
|
|
return 1;
|
|
}
|
|
|
|
+/*
|
|
+ * Check for the SMBIOS 3.0 64-bit entry point signature. Unlike the legacy
|
|
+ * 32-bit entry point, there is no embedded DMI header (_DMI_) in here.
|
|
+ */
|
|
+static int __init dmi_smbios3_present(const u8 *buf)
|
|
+{
|
|
+ if (memcmp(buf, "_SM3_", 5) == 0 &&
|
|
+ buf[6] < 32 && dmi_checksum(buf, buf[6])) {
|
|
+ dmi_ver = get_unaligned_be16(buf + 7);
|
|
+ dmi_len = get_unaligned_le32(buf + 12);
|
|
+ dmi_base = get_unaligned_le64(buf + 16);
|
|
+
|
|
+ /*
|
|
+ * The 64-bit SMBIOS 3.0 entry point no longer has a field
|
|
+ * containing the number of structures present in the table.
|
|
+ * Instead, it defines the table size as a maximum size, and
|
|
+ * relies on the end-of-table structure type (#127) to be used
|
|
+ * to signal the end of the table.
|
|
+ * So let's define dmi_num as an upper bound as well: each
|
|
+ * structure has a 4 byte header, so dmi_len / 4 is an upper
|
|
+ * bound for the number of structures in the table.
|
|
+ */
|
|
+ dmi_num = dmi_len / 4;
|
|
+
|
|
+ if (dmi_walk_early(dmi_decode) == 0) {
|
|
+ pr_info("SMBIOS %d.%d present.\n",
|
|
+ dmi_ver >> 8, dmi_ver & 0xFF);
|
|
+ dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string));
|
|
+ pr_debug("DMI: %s\n", dmi_ids_string);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
void __init dmi_scan_machine(void)
|
|
{
|
|
char __iomem *p, *q;
|
|
char buf[32];
|
|
|
|
if (efi_enabled(EFI_CONFIG_TABLES)) {
|
|
+ /*
|
|
+ * According to the DMTF SMBIOS reference spec v3.0.0, it is
|
|
+ * allowed to define both the 64-bit entry point (smbios3) and
|
|
+ * the 32-bit entry point (smbios), in which case they should
|
|
+ * either both point to the same SMBIOS structure table, or the
|
|
+ * table pointed to by the 64-bit entry point should contain a
|
|
+ * superset of the table contents pointed to by the 32-bit entry
|
|
+ * point (section 5.2)
|
|
+ * This implies that the 64-bit entry point should have
|
|
+ * precedence if it is defined and supported by the OS. If we
|
|
+ * have the 64-bit entry point, but fail to decode it, fall
|
|
+ * back to the legacy one (if available)
|
|
+ */
|
|
+ if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) {
|
|
+ p = dmi_early_remap(efi.smbios3, 32);
|
|
+ if (p == NULL)
|
|
+ goto error;
|
|
+ memcpy_fromio(buf, p, 32);
|
|
+ dmi_early_unmap(p, 32);
|
|
+
|
|
+ if (!dmi_smbios3_present(buf)) {
|
|
+ dmi_available = 1;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
if (efi.smbios == EFI_INVALID_TABLE_ADDR)
|
|
goto error;
|
|
|
|
@@ -552,7 +617,7 @@ void __init dmi_scan_machine(void)
|
|
memset(buf, 0, 16);
|
|
for (q = p; q < p + 0x10000; q += 16) {
|
|
memcpy_fromio(buf + 16, q, 16);
|
|
- if (!dmi_present(buf)) {
|
|
+ if (!dmi_smbios3_present(buf) || !dmi_present(buf)) {
|
|
dmi_available = 1;
|
|
dmi_early_unmap(p, 0x10000);
|
|
goto out;
|
|
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
|
|
index 8590099..9035c1b 100644
|
|
--- a/drivers/firmware/efi/efi.c
|
|
+++ b/drivers/firmware/efi/efi.c
|
|
@@ -30,6 +30,7 @@ struct efi __read_mostly efi = {
|
|
.acpi = EFI_INVALID_TABLE_ADDR,
|
|
.acpi20 = EFI_INVALID_TABLE_ADDR,
|
|
.smbios = EFI_INVALID_TABLE_ADDR,
|
|
+ .smbios3 = EFI_INVALID_TABLE_ADDR,
|
|
.sal_systab = EFI_INVALID_TABLE_ADDR,
|
|
.boot_info = EFI_INVALID_TABLE_ADDR,
|
|
.hcdp = EFI_INVALID_TABLE_ADDR,
|
|
@@ -86,6 +87,8 @@ static ssize_t systab_show(struct kobject *kobj,
|
|
str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
|
|
if (efi.smbios != EFI_INVALID_TABLE_ADDR)
|
|
str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
|
|
+ if (efi.smbios3 != EFI_INVALID_TABLE_ADDR)
|
|
+ str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3);
|
|
if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
|
|
str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
|
|
if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
|
|
@@ -260,6 +263,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
|
|
{MPS_TABLE_GUID, "MPS", &efi.mps},
|
|
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
|
|
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
|
|
+ {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
|
|
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
|
|
{NULL_GUID, NULL, NULL},
|
|
};
|
|
diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c
|
|
index 75ee059..eb48a1a 100644
|
|
--- a/drivers/firmware/efi/libstub/arm-stub.c
|
|
+++ b/drivers/firmware/efi/libstub/arm-stub.c
|
|
@@ -247,9 +247,18 @@ unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table,
|
|
goto fail_free_cmdline;
|
|
}
|
|
}
|
|
- if (!fdt_addr)
|
|
+
|
|
+ if (fdt_addr) {
|
|
+ pr_efi(sys_table, "Using DTB from command line\n");
|
|
+ } else {
|
|
/* Look for a device tree configuration table entry. */
|
|
fdt_addr = (uintptr_t)get_fdt(sys_table);
|
|
+ if (fdt_addr)
|
|
+ pr_efi(sys_table, "Using DTB from configuration table\n");
|
|
+ }
|
|
+
|
|
+ if (!fdt_addr)
|
|
+ pr_efi(sys_table, "Generating empty DTB\n");
|
|
|
|
status = handle_cmdline_files(sys_table, image, cmdline_ptr,
|
|
"initrd=", dram_base + SZ_512M,
|
|
diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c
|
|
index 954b9f6..13dbd3d 100644
|
|
--- a/drivers/gpio/devres.c
|
|
+++ b/drivers/gpio/devres.c
|
|
@@ -109,6 +109,38 @@ struct gpio_desc *__must_check __devm_gpiod_get_index(struct device *dev,
|
|
EXPORT_SYMBOL(__devm_gpiod_get_index);
|
|
|
|
/**
|
|
+ * devm_get_gpiod_from_child - get a GPIO descriptor from a device's child node
|
|
+ * @dev: GPIO consumer
|
|
+ * @child: firmware node (child of @dev)
|
|
+ *
|
|
+ * GPIO descriptors returned from this function are automatically disposed on
|
|
+ * driver detach.
|
|
+ */
|
|
+struct gpio_desc *devm_get_gpiod_from_child(struct device *dev,
|
|
+ struct fwnode_handle *child)
|
|
+{
|
|
+ struct gpio_desc **dr;
|
|
+ struct gpio_desc *desc;
|
|
+
|
|
+ dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *),
|
|
+ GFP_KERNEL);
|
|
+ if (!dr)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ desc = fwnode_get_named_gpiod(child, "gpios");
|
|
+ if (IS_ERR(desc)) {
|
|
+ devres_free(dr);
|
|
+ return desc;
|
|
+ }
|
|
+
|
|
+ *dr = desc;
|
|
+ devres_add(dev, dr);
|
|
+
|
|
+ return desc;
|
|
+}
|
|
+EXPORT_SYMBOL(devm_get_gpiod_from_child);
|
|
+
|
|
+/**
|
|
* devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional()
|
|
* @dev: GPIO consumer
|
|
* @con_id: function within the GPIO consumer
|
|
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
|
|
index 41e91d7..99720c8 100644
|
|
--- a/drivers/gpio/gpio-sch.c
|
|
+++ b/drivers/gpio/gpio-sch.c
|
|
@@ -29,290 +29,221 @@
|
|
|
|
#include <linux/gpio.h>
|
|
|
|
-static DEFINE_SPINLOCK(gpio_lock);
|
|
-
|
|
-#define CGEN (0x00)
|
|
-#define CGIO (0x04)
|
|
-#define CGLV (0x08)
|
|
-
|
|
-#define RGEN (0x20)
|
|
-#define RGIO (0x24)
|
|
-#define RGLV (0x28)
|
|
-
|
|
-static unsigned short gpio_ba;
|
|
-
|
|
-static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num)
|
|
-{
|
|
- u8 curr_dirs;
|
|
- unsigned short offset, bit;
|
|
-
|
|
- spin_lock(&gpio_lock);
|
|
-
|
|
- offset = CGIO + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
-
|
|
- curr_dirs = inb(gpio_ba + offset);
|
|
-
|
|
- if (!(curr_dirs & (1 << bit)))
|
|
- outb(curr_dirs | (1 << bit), gpio_ba + offset);
|
|
+#define GEN 0x00
|
|
+#define GIO 0x04
|
|
+#define GLV 0x08
|
|
+
|
|
+struct sch_gpio {
|
|
+ struct gpio_chip chip;
|
|
+ spinlock_t lock;
|
|
+ unsigned short iobase;
|
|
+ unsigned short core_base;
|
|
+ unsigned short resume_base;
|
|
+};
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
- return 0;
|
|
-}
|
|
+#define to_sch_gpio(c) container_of(c, struct sch_gpio, chip)
|
|
|
|
-static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
|
|
+static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio,
|
|
+ unsigned reg)
|
|
{
|
|
- int res;
|
|
- unsigned short offset, bit;
|
|
+ unsigned base = 0;
|
|
|
|
- offset = CGLV + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ if (gpio >= sch->resume_base) {
|
|
+ gpio -= sch->resume_base;
|
|
+ base += 0x20;
|
|
+ }
|
|
|
|
- res = !!(inb(gpio_ba + offset) & (1 << bit));
|
|
- return res;
|
|
+ return base + reg + gpio / 8;
|
|
}
|
|
|
|
-static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
|
|
+static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
|
|
{
|
|
- u8 curr_vals;
|
|
- unsigned short offset, bit;
|
|
-
|
|
- spin_lock(&gpio_lock);
|
|
-
|
|
- offset = CGLV + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
-
|
|
- curr_vals = inb(gpio_ba + offset);
|
|
-
|
|
- if (val)
|
|
- outb(curr_vals | (1 << bit), gpio_ba + offset);
|
|
- else
|
|
- outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
|
|
- spin_unlock(&gpio_lock);
|
|
+ if (gpio >= sch->resume_base)
|
|
+ gpio -= sch->resume_base;
|
|
+ return gpio % 8;
|
|
}
|
|
|
|
-static int sch_gpio_core_direction_out(struct gpio_chip *gc,
|
|
- unsigned gpio_num, int val)
|
|
+static void sch_gpio_enable(struct sch_gpio *sch, unsigned gpio)
|
|
{
|
|
- u8 curr_dirs;
|
|
unsigned short offset, bit;
|
|
+ u8 enable;
|
|
|
|
- spin_lock(&gpio_lock);
|
|
+ spin_lock(&sch->lock);
|
|
|
|
- offset = CGIO + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
-
|
|
- curr_dirs = inb(gpio_ba + offset);
|
|
- if (curr_dirs & (1 << bit))
|
|
- outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
|
|
+ offset = sch_gpio_offset(sch, gpio, GEN);
|
|
+ bit = sch_gpio_bit(sch, gpio);
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
+ enable = inb(sch->iobase + offset);
|
|
+ if (!(enable & (1 << bit)))
|
|
+ outb(enable | (1 << bit), sch->iobase + offset);
|
|
|
|
- /*
|
|
- * according to the datasheet, writing to the level register has no
|
|
- * effect when GPIO is programmed as input.
|
|
- * Actually the the level register is read-only when configured as input.
|
|
- * Thus presetting the output level before switching to output is _NOT_ possible.
|
|
- * Hence we set the level after configuring the GPIO as output.
|
|
- * But we cannot prevent a short low pulse if direction is set to high
|
|
- * and an external pull-up is connected.
|
|
- */
|
|
- sch_gpio_core_set(gc, gpio_num, val);
|
|
- return 0;
|
|
+ spin_unlock(&sch->lock);
|
|
}
|
|
|
|
-static struct gpio_chip sch_gpio_core = {
|
|
- .label = "sch_gpio_core",
|
|
- .owner = THIS_MODULE,
|
|
- .direction_input = sch_gpio_core_direction_in,
|
|
- .get = sch_gpio_core_get,
|
|
- .direction_output = sch_gpio_core_direction_out,
|
|
- .set = sch_gpio_core_set,
|
|
-};
|
|
-
|
|
-static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
|
|
- unsigned gpio_num)
|
|
+static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
|
|
{
|
|
+ struct sch_gpio *sch = to_sch_gpio(gc);
|
|
u8 curr_dirs;
|
|
unsigned short offset, bit;
|
|
|
|
- spin_lock(&gpio_lock);
|
|
+ spin_lock(&sch->lock);
|
|
|
|
- offset = RGIO + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ offset = sch_gpio_offset(sch, gpio_num, GIO);
|
|
+ bit = sch_gpio_bit(sch, gpio_num);
|
|
|
|
- curr_dirs = inb(gpio_ba + offset);
|
|
+ curr_dirs = inb(sch->iobase + offset);
|
|
|
|
if (!(curr_dirs & (1 << bit)))
|
|
- outb(curr_dirs | (1 << bit), gpio_ba + offset);
|
|
+ outb(curr_dirs | (1 << bit), sch->iobase + offset);
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
+ spin_unlock(&sch->lock);
|
|
return 0;
|
|
}
|
|
|
|
-static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
|
|
+static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
|
|
{
|
|
+ struct sch_gpio *sch = to_sch_gpio(gc);
|
|
+ int res;
|
|
unsigned short offset, bit;
|
|
|
|
- offset = RGLV + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ offset = sch_gpio_offset(sch, gpio_num, GLV);
|
|
+ bit = sch_gpio_bit(sch, gpio_num);
|
|
+
|
|
+ res = !!(inb(sch->iobase + offset) & (1 << bit));
|
|
|
|
- return !!(inb(gpio_ba + offset) & (1 << bit));
|
|
+ return res;
|
|
}
|
|
|
|
-static void sch_gpio_resume_set(struct gpio_chip *gc,
|
|
- unsigned gpio_num, int val)
|
|
+static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
|
|
{
|
|
+ struct sch_gpio *sch = to_sch_gpio(gc);
|
|
u8 curr_vals;
|
|
unsigned short offset, bit;
|
|
|
|
- spin_lock(&gpio_lock);
|
|
+ spin_lock(&sch->lock);
|
|
|
|
- offset = RGLV + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ offset = sch_gpio_offset(sch, gpio_num, GLV);
|
|
+ bit = sch_gpio_bit(sch, gpio_num);
|
|
|
|
- curr_vals = inb(gpio_ba + offset);
|
|
+ curr_vals = inb(sch->iobase + offset);
|
|
|
|
if (val)
|
|
- outb(curr_vals | (1 << bit), gpio_ba + offset);
|
|
+ outb(curr_vals | (1 << bit), sch->iobase + offset);
|
|
else
|
|
- outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
|
|
+ outb((curr_vals & ~(1 << bit)), sch->iobase + offset);
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
+ spin_unlock(&sch->lock);
|
|
}
|
|
|
|
-static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
|
|
- unsigned gpio_num, int val)
|
|
+static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
|
|
+ int val)
|
|
{
|
|
+ struct sch_gpio *sch = to_sch_gpio(gc);
|
|
u8 curr_dirs;
|
|
unsigned short offset, bit;
|
|
|
|
- offset = RGIO + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ spin_lock(&sch->lock);
|
|
|
|
- spin_lock(&gpio_lock);
|
|
+ offset = sch_gpio_offset(sch, gpio_num, GIO);
|
|
+ bit = sch_gpio_bit(sch, gpio_num);
|
|
|
|
- curr_dirs = inb(gpio_ba + offset);
|
|
+ curr_dirs = inb(sch->iobase + offset);
|
|
if (curr_dirs & (1 << bit))
|
|
- outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
|
|
+ outb(curr_dirs & ~(1 << bit), sch->iobase + offset);
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
+ spin_unlock(&sch->lock);
|
|
|
|
/*
|
|
- * according to the datasheet, writing to the level register has no
|
|
- * effect when GPIO is programmed as input.
|
|
- * Actually the the level register is read-only when configured as input.
|
|
- * Thus presetting the output level before switching to output is _NOT_ possible.
|
|
- * Hence we set the level after configuring the GPIO as output.
|
|
- * But we cannot prevent a short low pulse if direction is set to high
|
|
- * and an external pull-up is connected.
|
|
- */
|
|
- sch_gpio_resume_set(gc, gpio_num, val);
|
|
+ * according to the datasheet, writing to the level register has no
|
|
+ * effect when GPIO is programmed as input.
|
|
+ * Actually the the level register is read-only when configured as input.
|
|
+ * Thus presetting the output level before switching to output is _NOT_ possible.
|
|
+ * Hence we set the level after configuring the GPIO as output.
|
|
+ * But we cannot prevent a short low pulse if direction is set to high
|
|
+ * and an external pull-up is connected.
|
|
+ */
|
|
+ sch_gpio_set(gc, gpio_num, val);
|
|
return 0;
|
|
}
|
|
|
|
-static struct gpio_chip sch_gpio_resume = {
|
|
- .label = "sch_gpio_resume",
|
|
+static struct gpio_chip sch_gpio_chip = {
|
|
+ .label = "sch_gpio",
|
|
.owner = THIS_MODULE,
|
|
- .direction_input = sch_gpio_resume_direction_in,
|
|
- .get = sch_gpio_resume_get,
|
|
- .direction_output = sch_gpio_resume_direction_out,
|
|
- .set = sch_gpio_resume_set,
|
|
+ .direction_input = sch_gpio_direction_in,
|
|
+ .get = sch_gpio_get,
|
|
+ .direction_output = sch_gpio_direction_out,
|
|
+ .set = sch_gpio_set,
|
|
};
|
|
|
|
static int sch_gpio_probe(struct platform_device *pdev)
|
|
{
|
|
+ struct sch_gpio *sch;
|
|
struct resource *res;
|
|
- int err, id;
|
|
|
|
- id = pdev->id;
|
|
- if (!id)
|
|
- return -ENODEV;
|
|
+ sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL);
|
|
+ if (!sch)
|
|
+ return -ENOMEM;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
|
if (!res)
|
|
return -EBUSY;
|
|
|
|
- if (!request_region(res->start, resource_size(res), pdev->name))
|
|
+ if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
|
|
+ pdev->name))
|
|
return -EBUSY;
|
|
|
|
- gpio_ba = res->start;
|
|
+ spin_lock_init(&sch->lock);
|
|
+ sch->iobase = res->start;
|
|
+ sch->chip = sch_gpio_chip;
|
|
+ sch->chip.label = dev_name(&pdev->dev);
|
|
+ sch->chip.dev = &pdev->dev;
|
|
|
|
- switch (id) {
|
|
+ switch (pdev->id) {
|
|
case PCI_DEVICE_ID_INTEL_SCH_LPC:
|
|
- sch_gpio_core.base = 0;
|
|
- sch_gpio_core.ngpio = 10;
|
|
- sch_gpio_resume.base = 10;
|
|
- sch_gpio_resume.ngpio = 4;
|
|
+ sch->core_base = 0;
|
|
+ sch->resume_base = 10;
|
|
+ sch->chip.ngpio = 14;
|
|
+
|
|
/*
|
|
* GPIO[6:0] enabled by default
|
|
* GPIO7 is configured by the CMC as SLPIOVR
|
|
* Enable GPIO[9:8] core powered gpios explicitly
|
|
*/
|
|
- outb(0x3, gpio_ba + CGEN + 1);
|
|
+ sch_gpio_enable(sch, 8);
|
|
+ sch_gpio_enable(sch, 9);
|
|
/*
|
|
* SUS_GPIO[2:0] enabled by default
|
|
* Enable SUS_GPIO3 resume powered gpio explicitly
|
|
*/
|
|
- outb(0x8, gpio_ba + RGEN);
|
|
+ sch_gpio_enable(sch, 13);
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_INTEL_ITC_LPC:
|
|
- sch_gpio_core.base = 0;
|
|
- sch_gpio_core.ngpio = 5;
|
|
- sch_gpio_resume.base = 5;
|
|
- sch_gpio_resume.ngpio = 9;
|
|
+ sch->core_base = 0;
|
|
+ sch->resume_base = 5;
|
|
+ sch->chip.ngpio = 14;
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
|
|
- sch_gpio_core.base = 0;
|
|
- sch_gpio_core.ngpio = 21;
|
|
- sch_gpio_resume.base = 21;
|
|
- sch_gpio_resume.ngpio = 9;
|
|
+ sch->core_base = 0;
|
|
+ sch->resume_base = 21;
|
|
+ sch->chip.ngpio = 30;
|
|
break;
|
|
|
|
default:
|
|
- err = -ENODEV;
|
|
- goto err_sch_gpio_core;
|
|
+ return -ENODEV;
|
|
}
|
|
|
|
- sch_gpio_core.dev = &pdev->dev;
|
|
- sch_gpio_resume.dev = &pdev->dev;
|
|
-
|
|
- err = gpiochip_add(&sch_gpio_core);
|
|
- if (err < 0)
|
|
- goto err_sch_gpio_core;
|
|
+ platform_set_drvdata(pdev, sch);
|
|
|
|
- err = gpiochip_add(&sch_gpio_resume);
|
|
- if (err < 0)
|
|
- goto err_sch_gpio_resume;
|
|
-
|
|
- return 0;
|
|
-
|
|
-err_sch_gpio_resume:
|
|
- gpiochip_remove(&sch_gpio_core);
|
|
-
|
|
-err_sch_gpio_core:
|
|
- release_region(res->start, resource_size(res));
|
|
- gpio_ba = 0;
|
|
-
|
|
- return err;
|
|
+ return gpiochip_add(&sch->chip);
|
|
}
|
|
|
|
static int sch_gpio_remove(struct platform_device *pdev)
|
|
{
|
|
- struct resource *res;
|
|
- if (gpio_ba) {
|
|
-
|
|
- gpiochip_remove(&sch_gpio_core);
|
|
- gpiochip_remove(&sch_gpio_resume);
|
|
-
|
|
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
|
-
|
|
- release_region(res->start, resource_size(res));
|
|
- gpio_ba = 0;
|
|
- }
|
|
+ struct sch_gpio *sch = platform_get_drvdata(pdev);
|
|
|
|
+ gpiochip_remove(&sch->chip);
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
|
|
index 05c6275..ba98bb5 100644
|
|
--- a/drivers/gpio/gpiolib-acpi.c
|
|
+++ b/drivers/gpio/gpiolib-acpi.c
|
|
@@ -287,9 +287,45 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
|
|
}
|
|
}
|
|
|
|
+int acpi_dev_add_driver_gpios(struct acpi_device *adev,
|
|
+ const struct acpi_gpio_mapping *gpios)
|
|
+{
|
|
+ if (adev && gpios) {
|
|
+ adev->driver_gpios = gpios;
|
|
+ return 0;
|
|
+ }
|
|
+ return -EINVAL;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios);
|
|
+
|
|
+static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
|
|
+ const char *name, int index,
|
|
+ struct acpi_reference_args *args)
|
|
+{
|
|
+ const struct acpi_gpio_mapping *gm;
|
|
+
|
|
+ if (!adev->driver_gpios)
|
|
+ return false;
|
|
+
|
|
+ for (gm = adev->driver_gpios; gm->name; gm++)
|
|
+ if (!strcmp(name, gm->name) && gm->data && index < gm->size) {
|
|
+ const struct acpi_gpio_params *par = gm->data + index;
|
|
+
|
|
+ args->adev = adev;
|
|
+ args->args[0] = par->crs_entry_index;
|
|
+ args->args[1] = par->line_index;
|
|
+ args->args[2] = par->active_low;
|
|
+ args->nargs = 3;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
struct acpi_gpio_lookup {
|
|
struct acpi_gpio_info info;
|
|
int index;
|
|
+ int pin_index;
|
|
struct gpio_desc *desc;
|
|
int n;
|
|
};
|
|
@@ -303,13 +339,24 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data)
|
|
|
|
if (lookup->n++ == lookup->index && !lookup->desc) {
|
|
const struct acpi_resource_gpio *agpio = &ares->data.gpio;
|
|
+ int pin_index = lookup->pin_index;
|
|
+
|
|
+ if (pin_index >= agpio->pin_table_length)
|
|
+ return 1;
|
|
|
|
lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
|
|
- agpio->pin_table[0]);
|
|
+ agpio->pin_table[pin_index]);
|
|
lookup->info.gpioint =
|
|
agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
|
|
- lookup->info.active_low =
|
|
- agpio->polarity == ACPI_ACTIVE_LOW;
|
|
+
|
|
+ /*
|
|
+ * ActiveLow is only specified for GpioInt resource. If
|
|
+ * GpioIo is used then the only way to set the flag is
|
|
+ * to use _DSD "gpios" property.
|
|
+ */
|
|
+ if (lookup->info.gpioint)
|
|
+ lookup->info.active_low =
|
|
+ agpio->polarity == ACPI_ACTIVE_LOW;
|
|
}
|
|
|
|
return 1;
|
|
@@ -317,40 +364,79 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data)
|
|
|
|
/**
|
|
* acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources
|
|
- * @dev: pointer to a device to get GPIO from
|
|
+ * @adev: pointer to a ACPI device to get GPIO from
|
|
+ * @propname: Property name of the GPIO (optional)
|
|
* @index: index of GpioIo/GpioInt resource (starting from %0)
|
|
* @info: info pointer to fill in (optional)
|
|
*
|
|
- * Function goes through ACPI resources for @dev and based on @index looks
|
|
+ * Function goes through ACPI resources for @adev and based on @index looks
|
|
* up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor,
|
|
* and returns it. @index matches GpioIo/GpioInt resources only so if there
|
|
* are total %3 GPIO resources, the index goes from %0 to %2.
|
|
*
|
|
+ * If @propname is specified the GPIO is looked using device property. In
|
|
+ * that case @index is used to select the GPIO entry in the property value
|
|
+ * (in case of multiple).
|
|
+ *
|
|
* If the GPIO cannot be translated or there is an error an ERR_PTR is
|
|
* returned.
|
|
*
|
|
* Note: if the GPIO resource has multiple entries in the pin list, this
|
|
* function only returns the first.
|
|
*/
|
|
-struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index,
|
|
+struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
|
+ const char *propname, int index,
|
|
struct acpi_gpio_info *info)
|
|
{
|
|
struct acpi_gpio_lookup lookup;
|
|
struct list_head resource_list;
|
|
- struct acpi_device *adev;
|
|
- acpi_handle handle;
|
|
+ bool active_low = false;
|
|
int ret;
|
|
|
|
- if (!dev)
|
|
- return ERR_PTR(-EINVAL);
|
|
-
|
|
- handle = ACPI_HANDLE(dev);
|
|
- if (!handle || acpi_bus_get_device(handle, &adev))
|
|
+ if (!adev)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
memset(&lookup, 0, sizeof(lookup));
|
|
lookup.index = index;
|
|
|
|
+ if (propname) {
|
|
+ struct acpi_reference_args args;
|
|
+
|
|
+ dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname);
|
|
+
|
|
+ memset(&args, 0, sizeof(args));
|
|
+ ret = acpi_dev_get_property_reference(adev, propname,
|
|
+ index, &args);
|
|
+ if (ret) {
|
|
+ bool found = acpi_get_driver_gpio_data(adev, propname,
|
|
+ index, &args);
|
|
+ if (!found)
|
|
+ return ERR_PTR(ret);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * The property was found and resolved so need to
|
|
+ * lookup the GPIO based on returned args instead.
|
|
+ */
|
|
+ adev = args.adev;
|
|
+ if (args.nargs >= 2) {
|
|
+ lookup.index = args.args[0];
|
|
+ lookup.pin_index = args.args[1];
|
|
+ /*
|
|
+ * 3rd argument, if present is used to
|
|
+ * specify active_low.
|
|
+ */
|
|
+ if (args.nargs >= 3)
|
|
+ active_low = !!args.args[2];
|
|
+ }
|
|
+
|
|
+ dev_dbg(&adev->dev, "GPIO: _DSD returned %s %zd %llu %llu %llu\n",
|
|
+ dev_name(&adev->dev), args.nargs,
|
|
+ args.args[0], args.args[1], args.args[2]);
|
|
+ } else {
|
|
+ dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index);
|
|
+ }
|
|
+
|
|
INIT_LIST_HEAD(&resource_list);
|
|
ret = acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio,
|
|
&lookup);
|
|
@@ -359,8 +445,11 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index,
|
|
|
|
acpi_dev_free_resource_list(&resource_list);
|
|
|
|
- if (lookup.desc && info)
|
|
+ if (lookup.desc && info) {
|
|
*info = lookup.info;
|
|
+ if (active_low)
|
|
+ info->active_low = active_low;
|
|
+ }
|
|
|
|
return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT);
|
|
}
|
|
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
|
|
index e8e98ca..58659db 100644
|
|
--- a/drivers/gpio/gpiolib.c
|
|
+++ b/drivers/gpio/gpiolib.c
|
|
@@ -1505,14 +1505,36 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
|
|
unsigned int idx,
|
|
enum gpio_lookup_flags *flags)
|
|
{
|
|
+ static const char * const suffixes[] = { "gpios", "gpio" };
|
|
+ struct acpi_device *adev = ACPI_COMPANION(dev);
|
|
struct acpi_gpio_info info;
|
|
struct gpio_desc *desc;
|
|
+ char propname[32];
|
|
+ int i;
|
|
|
|
- desc = acpi_get_gpiod_by_index(dev, idx, &info);
|
|
- if (IS_ERR(desc))
|
|
- return desc;
|
|
+ /* Try first from _DSD */
|
|
+ for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
|
|
+ if (con_id && strcmp(con_id, "gpios")) {
|
|
+ snprintf(propname, sizeof(propname), "%s-%s",
|
|
+ con_id, suffixes[i]);
|
|
+ } else {
|
|
+ snprintf(propname, sizeof(propname), "%s",
|
|
+ suffixes[i]);
|
|
+ }
|
|
+
|
|
+ desc = acpi_get_gpiod_by_index(adev, propname, idx, &info);
|
|
+ if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
|
|
+ break;
|
|
+ }
|
|
|
|
- if (info.gpioint && info.active_low)
|
|
+ /* Then from plain _CRS GPIOs */
|
|
+ if (IS_ERR(desc)) {
|
|
+ desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
|
|
+ if (IS_ERR(desc))
|
|
+ return desc;
|
|
+ }
|
|
+
|
|
+ if (info.active_low)
|
|
*flags |= GPIO_ACTIVE_LOW;
|
|
|
|
return desc;
|
|
@@ -1713,6 +1735,61 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
|
|
EXPORT_SYMBOL_GPL(__gpiod_get_index);
|
|
|
|
/**
|
|
+ * fwnode_get_named_gpiod - obtain a GPIO from firmware node
|
|
+ * @fwnode: handle of the firmware node
|
|
+ * @propname: name of the firmware property representing the GPIO
|
|
+ *
|
|
+ * This function can be used for drivers that get their configuration
|
|
+ * from firmware.
|
|
+ *
|
|
+ * Function properly finds the corresponding GPIO using whatever is the
|
|
+ * underlying firmware interface and then makes sure that the GPIO
|
|
+ * descriptor is requested before it is returned to the caller.
|
|
+ *
|
|
+ * In case of error an ERR_PTR() is returned.
|
|
+ */
|
|
+struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
|
+ const char *propname)
|
|
+{
|
|
+ struct gpio_desc *desc = ERR_PTR(-ENODEV);
|
|
+ bool active_low = false;
|
|
+ int ret;
|
|
+
|
|
+ if (!fwnode)
|
|
+ return ERR_PTR(-EINVAL);
|
|
+
|
|
+ if (is_of_node(fwnode)) {
|
|
+ enum of_gpio_flags flags;
|
|
+
|
|
+ desc = of_get_named_gpiod_flags(of_node(fwnode), propname, 0,
|
|
+ &flags);
|
|
+ if (!IS_ERR(desc))
|
|
+ active_low = flags & OF_GPIO_ACTIVE_LOW;
|
|
+ } else if (is_acpi_node(fwnode)) {
|
|
+ struct acpi_gpio_info info;
|
|
+
|
|
+ desc = acpi_get_gpiod_by_index(acpi_node(fwnode), propname, 0,
|
|
+ &info);
|
|
+ if (!IS_ERR(desc))
|
|
+ active_low = info.active_low;
|
|
+ }
|
|
+
|
|
+ if (IS_ERR(desc))
|
|
+ return desc;
|
|
+
|
|
+ ret = gpiod_request(desc, NULL);
|
|
+ if (ret)
|
|
+ return ERR_PTR(ret);
|
|
+
|
|
+ /* Only value flag can be set from both DT and ACPI is active_low */
|
|
+ if (active_low)
|
|
+ set_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
|
+
|
|
+ return desc;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
|
|
+
|
|
+/**
|
|
* gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO
|
|
* function
|
|
* @dev: GPIO consumer, can be NULL for system-global GPIOs
|
|
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
|
|
index 9db2b6a..e3a5211 100644
|
|
--- a/drivers/gpio/gpiolib.h
|
|
+++ b/drivers/gpio/gpiolib.h
|
|
@@ -34,7 +34,8 @@ void acpi_gpiochip_remove(struct gpio_chip *chip);
|
|
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
|
|
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
|
|
|
|
-struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index,
|
|
+struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
|
+ const char *propname, int index,
|
|
struct acpi_gpio_info *info);
|
|
#else
|
|
static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
|
|
@@ -47,8 +48,8 @@ static inline void
|
|
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
|
|
|
|
static inline struct gpio_desc *
|
|
-acpi_get_gpiod_by_index(struct device *dev, int index,
|
|
- struct acpi_gpio_info *info)
|
|
+acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname,
|
|
+ int index, struct acpi_gpio_info *info)
|
|
{
|
|
return ERR_PTR(-ENOSYS);
|
|
}
|
|
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
|
|
index 432d363..c9c1c8c 100644
|
|
--- a/drivers/input/keyboard/gpio_keys_polled.c
|
|
+++ b/drivers/input/keyboard/gpio_keys_polled.c
|
|
@@ -23,10 +23,9 @@
|
|
#include <linux/ioport.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/gpio.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
#include <linux/gpio_keys.h>
|
|
-#include <linux/of.h>
|
|
-#include <linux/of_platform.h>
|
|
-#include <linux/of_gpio.h>
|
|
+#include <linux/property.h>
|
|
|
|
#define DRV_NAME "gpio-keys-polled"
|
|
|
|
@@ -51,15 +50,14 @@ static void gpio_keys_polled_check_state(struct input_dev *input,
|
|
int state;
|
|
|
|
if (bdata->can_sleep)
|
|
- state = !!gpio_get_value_cansleep(button->gpio);
|
|
+ state = !!gpiod_get_value_cansleep(button->gpiod);
|
|
else
|
|
- state = !!gpio_get_value(button->gpio);
|
|
+ state = !!gpiod_get_value(button->gpiod);
|
|
|
|
if (state != bdata->last_state) {
|
|
unsigned int type = button->type ?: EV_KEY;
|
|
|
|
- input_event(input, type, button->code,
|
|
- !!(state ^ button->active_low));
|
|
+ input_event(input, type, button->code, state);
|
|
input_sync(input);
|
|
bdata->count = 0;
|
|
bdata->last_state = state;
|
|
@@ -102,21 +100,15 @@ static void gpio_keys_polled_close(struct input_polled_dev *dev)
|
|
pdata->disable(bdev->dev);
|
|
}
|
|
|
|
-#ifdef CONFIG_OF
|
|
static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device *dev)
|
|
{
|
|
- struct device_node *node, *pp;
|
|
struct gpio_keys_platform_data *pdata;
|
|
struct gpio_keys_button *button;
|
|
+ struct fwnode_handle *child;
|
|
int error;
|
|
int nbuttons;
|
|
- int i;
|
|
-
|
|
- node = dev->of_node;
|
|
- if (!node)
|
|
- return NULL;
|
|
|
|
- nbuttons = of_get_child_count(node);
|
|
+ nbuttons = device_get_child_node_count(dev);
|
|
if (nbuttons == 0)
|
|
return NULL;
|
|
|
|
@@ -126,52 +118,44 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
|
|
- pdata->nbuttons = nbuttons;
|
|
|
|
- pdata->rep = !!of_get_property(node, "autorepeat", NULL);
|
|
- of_property_read_u32(node, "poll-interval", &pdata->poll_interval);
|
|
+ pdata->rep = device_property_present(dev, "autorepeat");
|
|
+ device_property_read_u32(dev, "poll-interval", &pdata->poll_interval);
|
|
|
|
- i = 0;
|
|
- for_each_child_of_node(node, pp) {
|
|
- int gpio;
|
|
- enum of_gpio_flags flags;
|
|
+ device_for_each_child_node(dev, child) {
|
|
+ struct gpio_desc *desc;
|
|
|
|
- if (!of_find_property(pp, "gpios", NULL)) {
|
|
- pdata->nbuttons--;
|
|
- dev_warn(dev, "Found button without gpios\n");
|
|
- continue;
|
|
- }
|
|
-
|
|
- gpio = of_get_gpio_flags(pp, 0, &flags);
|
|
- if (gpio < 0) {
|
|
- error = gpio;
|
|
+ desc = devm_get_gpiod_from_child(dev, child);
|
|
+ if (IS_ERR(desc)) {
|
|
+ error = PTR_ERR(desc);
|
|
if (error != -EPROBE_DEFER)
|
|
dev_err(dev,
|
|
"Failed to get gpio flags, error: %d\n",
|
|
error);
|
|
+ fwnode_handle_put(child);
|
|
return ERR_PTR(error);
|
|
}
|
|
|
|
- button = &pdata->buttons[i++];
|
|
-
|
|
- button->gpio = gpio;
|
|
- button->active_low = flags & OF_GPIO_ACTIVE_LOW;
|
|
+ button = &pdata->buttons[pdata->nbuttons++];
|
|
+ button->gpiod = desc;
|
|
|
|
- if (of_property_read_u32(pp, "linux,code", &button->code)) {
|
|
- dev_err(dev, "Button without keycode: 0x%x\n",
|
|
- button->gpio);
|
|
+ if (fwnode_property_read_u32(child, "linux,code", &button->code)) {
|
|
+ dev_err(dev, "Button without keycode: %d\n",
|
|
+ pdata->nbuttons - 1);
|
|
+ fwnode_handle_put(child);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
- button->desc = of_get_property(pp, "label", NULL);
|
|
+ fwnode_property_read_string(child, "label", &button->desc);
|
|
|
|
- if (of_property_read_u32(pp, "linux,input-type", &button->type))
|
|
+ if (fwnode_property_read_u32(child, "linux,input-type",
|
|
+ &button->type))
|
|
button->type = EV_KEY;
|
|
|
|
- button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
|
|
+ button->wakeup = fwnode_property_present(child, "gpio-key,wakeup");
|
|
|
|
- if (of_property_read_u32(pp, "debounce-interval",
|
|
- &button->debounce_interval))
|
|
+ if (fwnode_property_read_u32(child, "debounce-interval",
|
|
+ &button->debounce_interval))
|
|
button->debounce_interval = 5;
|
|
}
|
|
|
|
@@ -187,15 +171,6 @@ static const struct of_device_id gpio_keys_polled_of_match[] = {
|
|
};
|
|
MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match);
|
|
|
|
-#else
|
|
-
|
|
-static inline struct gpio_keys_platform_data *
|
|
-gpio_keys_polled_get_devtree_pdata(struct device *dev)
|
|
-{
|
|
- return NULL;
|
|
-}
|
|
-#endif
|
|
-
|
|
static int gpio_keys_polled_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
@@ -259,7 +234,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
|
|
for (i = 0; i < pdata->nbuttons; i++) {
|
|
struct gpio_keys_button *button = &pdata->buttons[i];
|
|
struct gpio_keys_button_data *bdata = &bdev->data[i];
|
|
- unsigned int gpio = button->gpio;
|
|
unsigned int type = button->type ?: EV_KEY;
|
|
|
|
if (button->wakeup) {
|
|
@@ -267,15 +241,31 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
|
|
return -EINVAL;
|
|
}
|
|
|
|
- error = devm_gpio_request_one(&pdev->dev, gpio, GPIOF_IN,
|
|
- button->desc ? : DRV_NAME);
|
|
- if (error) {
|
|
- dev_err(dev, "unable to claim gpio %u, err=%d\n",
|
|
- gpio, error);
|
|
- return error;
|
|
+ /*
|
|
+ * Legacy GPIO number so request the GPIO here and
|
|
+ * convert it to descriptor.
|
|
+ */
|
|
+ if (!button->gpiod && gpio_is_valid(button->gpio)) {
|
|
+ unsigned flags = 0;
|
|
+
|
|
+ if (button->active_low)
|
|
+ flags |= GPIOF_ACTIVE_LOW;
|
|
+
|
|
+ error = devm_gpio_request_one(&pdev->dev, button->gpio,
|
|
+ flags, button->desc ? : DRV_NAME);
|
|
+ if (error) {
|
|
+ dev_err(dev, "unable to claim gpio %u, err=%d\n",
|
|
+ button->gpio, error);
|
|
+ return error;
|
|
+ }
|
|
+
|
|
+ button->gpiod = gpio_to_desc(button->gpio);
|
|
}
|
|
|
|
- bdata->can_sleep = gpio_cansleep(gpio);
|
|
+ if (IS_ERR(button->gpiod))
|
|
+ return PTR_ERR(button->gpiod);
|
|
+
|
|
+ bdata->can_sleep = gpiod_cansleep(button->gpiod);
|
|
bdata->last_state = -1;
|
|
bdata->threshold = DIV_ROUND_UP(button->debounce_interval,
|
|
pdata->poll_interval);
|
|
@@ -308,7 +298,7 @@ static struct platform_driver gpio_keys_polled_driver = {
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
.owner = THIS_MODULE,
|
|
- .of_match_table = of_match_ptr(gpio_keys_polled_of_match),
|
|
+ .of_match_table = gpio_keys_polled_of_match,
|
|
},
|
|
};
|
|
module_platform_driver(gpio_keys_polled_driver);
|
|
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
|
|
index 60558f7..3b92862 100644
|
|
--- a/drivers/iommu/arm-smmu.c
|
|
+++ b/drivers/iommu/arm-smmu.c
|
|
@@ -444,7 +444,10 @@ static struct device_node *dev_get_dev_node(struct device *dev)
|
|
|
|
while (!pci_is_root_bus(bus))
|
|
bus = bus->parent;
|
|
- return bus->bridge->parent->of_node;
|
|
+ if (bus->bridge->parent)
|
|
+ return bus->bridge->parent->of_node;
|
|
+ else
|
|
+ return NULL;
|
|
}
|
|
|
|
return dev->of_node;
|
|
@@ -560,6 +563,9 @@ static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
|
|
struct arm_smmu_master *master = NULL;
|
|
struct device_node *dev_node = dev_get_dev_node(dev);
|
|
|
|
+ if (!dev_node)
|
|
+ return NULL;
|
|
+
|
|
spin_lock(&arm_smmu_devices_lock);
|
|
list_for_each_entry(smmu, &arm_smmu_devices, list) {
|
|
master = find_smmu_master(smmu, dev_node);
|
|
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
|
|
index aa17ae8..d330dab 100644
|
|
--- a/drivers/irqchip/irq-gic-v3.c
|
|
+++ b/drivers/irqchip/irq-gic-v3.c
|
|
@@ -506,9 +506,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 38493ff..26e6773 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>
|
|
@@ -641,6 +643,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
|
|
@@ -996,6 +1005,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);
|
|
}
|
|
@@ -1048,3 +1060,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/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
|
|
index b4518c8..b3c5d9d 100644
|
|
--- a/drivers/leds/leds-gpio.c
|
|
+++ b/drivers/leds/leds-gpio.c
|
|
@@ -12,25 +12,23 @@
|
|
*/
|
|
#include <linux/err.h>
|
|
#include <linux/gpio.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/leds.h>
|
|
#include <linux/module.h>
|
|
-#include <linux/of.h>
|
|
-#include <linux/of_gpio.h>
|
|
-#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
+#include <linux/property.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
struct gpio_led_data {
|
|
struct led_classdev cdev;
|
|
- unsigned gpio;
|
|
+ struct gpio_desc *gpiod;
|
|
struct work_struct work;
|
|
u8 new_level;
|
|
u8 can_sleep;
|
|
- u8 active_low;
|
|
u8 blinking;
|
|
- int (*platform_gpio_blink_set)(unsigned gpio, int state,
|
|
+ int (*platform_gpio_blink_set)(struct gpio_desc *desc, int state,
|
|
unsigned long *delay_on, unsigned long *delay_off);
|
|
};
|
|
|
|
@@ -40,12 +38,11 @@ static void gpio_led_work(struct work_struct *work)
|
|
container_of(work, struct gpio_led_data, work);
|
|
|
|
if (led_dat->blinking) {
|
|
- led_dat->platform_gpio_blink_set(led_dat->gpio,
|
|
- led_dat->new_level,
|
|
- NULL, NULL);
|
|
+ led_dat->platform_gpio_blink_set(led_dat->gpiod,
|
|
+ led_dat->new_level, NULL, NULL);
|
|
led_dat->blinking = 0;
|
|
} else
|
|
- gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
|
|
+ gpiod_set_value_cansleep(led_dat->gpiod, led_dat->new_level);
|
|
}
|
|
|
|
static void gpio_led_set(struct led_classdev *led_cdev,
|
|
@@ -60,9 +57,6 @@ static void gpio_led_set(struct led_classdev *led_cdev,
|
|
else
|
|
level = 1;
|
|
|
|
- if (led_dat->active_low)
|
|
- level = !level;
|
|
-
|
|
/* Setting GPIOs with I2C/etc requires a task context, and we don't
|
|
* seem to have a reliable way to know if we're already in one; so
|
|
* let's just assume the worst.
|
|
@@ -72,11 +66,11 @@ static void gpio_led_set(struct led_classdev *led_cdev,
|
|
schedule_work(&led_dat->work);
|
|
} else {
|
|
if (led_dat->blinking) {
|
|
- led_dat->platform_gpio_blink_set(led_dat->gpio, level,
|
|
+ led_dat->platform_gpio_blink_set(led_dat->gpiod, level,
|
|
NULL, NULL);
|
|
led_dat->blinking = 0;
|
|
} else
|
|
- gpio_set_value(led_dat->gpio, level);
|
|
+ gpiod_set_value(led_dat->gpiod, level);
|
|
}
|
|
}
|
|
|
|
@@ -87,34 +81,49 @@ static int gpio_blink_set(struct led_classdev *led_cdev,
|
|
container_of(led_cdev, struct gpio_led_data, cdev);
|
|
|
|
led_dat->blinking = 1;
|
|
- return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK,
|
|
+ return led_dat->platform_gpio_blink_set(led_dat->gpiod, GPIO_LED_BLINK,
|
|
delay_on, delay_off);
|
|
}
|
|
|
|
static int create_gpio_led(const struct gpio_led *template,
|
|
struct gpio_led_data *led_dat, struct device *parent,
|
|
- int (*blink_set)(unsigned, int, unsigned long *, unsigned long *))
|
|
+ int (*blink_set)(struct gpio_desc *, int, unsigned long *,
|
|
+ unsigned long *))
|
|
{
|
|
int ret, state;
|
|
|
|
- led_dat->gpio = -1;
|
|
+ led_dat->gpiod = template->gpiod;
|
|
+ if (!led_dat->gpiod) {
|
|
+ /*
|
|
+ * This is the legacy code path for platform code that
|
|
+ * still uses GPIO numbers. Ultimately we would like to get
|
|
+ * rid of this block completely.
|
|
+ */
|
|
+ unsigned long flags = 0;
|
|
+
|
|
+ /* skip leds that aren't available */
|
|
+ if (!gpio_is_valid(template->gpio)) {
|
|
+ dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
|
|
+ template->gpio, template->name);
|
|
+ return 0;
|
|
+ }
|
|
|
|
- /* skip leds that aren't available */
|
|
- if (!gpio_is_valid(template->gpio)) {
|
|
- dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n",
|
|
- template->gpio, template->name);
|
|
- return 0;
|
|
- }
|
|
+ if (template->active_low)
|
|
+ flags |= GPIOF_ACTIVE_LOW;
|
|
|
|
- ret = devm_gpio_request(parent, template->gpio, template->name);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
+ ret = devm_gpio_request_one(parent, template->gpio, flags,
|
|
+ template->name);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ led_dat->gpiod = gpio_to_desc(template->gpio);
|
|
+ if (IS_ERR(led_dat->gpiod))
|
|
+ return PTR_ERR(led_dat->gpiod);
|
|
+ }
|
|
|
|
led_dat->cdev.name = template->name;
|
|
led_dat->cdev.default_trigger = template->default_trigger;
|
|
- led_dat->gpio = template->gpio;
|
|
- led_dat->can_sleep = gpio_cansleep(template->gpio);
|
|
- led_dat->active_low = template->active_low;
|
|
+ led_dat->can_sleep = gpiod_cansleep(led_dat->gpiod);
|
|
led_dat->blinking = 0;
|
|
if (blink_set) {
|
|
led_dat->platform_gpio_blink_set = blink_set;
|
|
@@ -122,30 +131,24 @@ static int create_gpio_led(const struct gpio_led *template,
|
|
}
|
|
led_dat->cdev.brightness_set = gpio_led_set;
|
|
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
|
|
- state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low;
|
|
+ state = !!gpiod_get_value_cansleep(led_dat->gpiod);
|
|
else
|
|
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
|
|
led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
|
|
if (!template->retain_state_suspended)
|
|
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
|
|
|
|
- ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
|
|
+ ret = gpiod_direction_output(led_dat->gpiod, state);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
INIT_WORK(&led_dat->work, gpio_led_work);
|
|
|
|
- ret = led_classdev_register(parent, &led_dat->cdev);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
-
|
|
- return 0;
|
|
+ return led_classdev_register(parent, &led_dat->cdev);
|
|
}
|
|
|
|
static void delete_gpio_led(struct gpio_led_data *led)
|
|
{
|
|
- if (!gpio_is_valid(led->gpio))
|
|
- return;
|
|
led_classdev_unregister(&led->cdev);
|
|
cancel_work_sync(&led->work);
|
|
}
|
|
@@ -161,40 +164,37 @@ static inline int sizeof_gpio_leds_priv(int num_leds)
|
|
(sizeof(struct gpio_led_data) * num_leds);
|
|
}
|
|
|
|
-/* Code to create from OpenFirmware platform devices */
|
|
-#ifdef CONFIG_OF_GPIO
|
|
-static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
|
|
+static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
|
|
{
|
|
- struct device_node *np = pdev->dev.of_node, *child;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct fwnode_handle *child;
|
|
struct gpio_leds_priv *priv;
|
|
int count, ret;
|
|
|
|
- /* count LEDs in this device, so we know how much to allocate */
|
|
- count = of_get_available_child_count(np);
|
|
+ count = device_get_child_node_count(dev);
|
|
if (!count)
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
- for_each_available_child_of_node(np, child)
|
|
- if (of_get_gpio(child, 0) == -EPROBE_DEFER)
|
|
- return ERR_PTR(-EPROBE_DEFER);
|
|
-
|
|
- priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count),
|
|
- GFP_KERNEL);
|
|
+ priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);
|
|
if (!priv)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
- for_each_available_child_of_node(np, child) {
|
|
+ device_for_each_child_node(dev, child) {
|
|
struct gpio_led led = {};
|
|
- enum of_gpio_flags flags;
|
|
- const char *state;
|
|
-
|
|
- led.gpio = of_get_gpio_flags(child, 0, &flags);
|
|
- led.active_low = flags & OF_GPIO_ACTIVE_LOW;
|
|
- led.name = of_get_property(child, "label", NULL) ? : child->name;
|
|
- led.default_trigger =
|
|
- of_get_property(child, "linux,default-trigger", NULL);
|
|
- state = of_get_property(child, "default-state", NULL);
|
|
- if (state) {
|
|
+ const char *state = NULL;
|
|
+
|
|
+ led.gpiod = devm_get_gpiod_from_child(dev, child);
|
|
+ if (IS_ERR(led.gpiod)) {
|
|
+ fwnode_handle_put(child);
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ fwnode_property_read_string(child, "label", &led.name);
|
|
+ fwnode_property_read_string(child, "linux,default-trigger",
|
|
+ &led.default_trigger);
|
|
+
|
|
+ if (!fwnode_property_read_string(child, "linux,default_state",
|
|
+ &state)) {
|
|
if (!strcmp(state, "keep"))
|
|
led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
|
|
else if (!strcmp(state, "on"))
|
|
@@ -203,13 +203,13 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
|
|
led.default_state = LEDS_GPIO_DEFSTATE_OFF;
|
|
}
|
|
|
|
- if (of_get_property(child, "retain-state-suspended", NULL))
|
|
+ if (fwnode_property_present(child, "retain-state-suspended"))
|
|
led.retain_state_suspended = 1;
|
|
|
|
ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
|
|
- &pdev->dev, NULL);
|
|
+ dev, NULL);
|
|
if (ret < 0) {
|
|
- of_node_put(child);
|
|
+ fwnode_handle_put(child);
|
|
goto err;
|
|
}
|
|
}
|
|
@@ -228,12 +228,6 @@ static const struct of_device_id of_gpio_leds_match[] = {
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, of_gpio_leds_match);
|
|
-#else /* CONFIG_OF_GPIO */
|
|
-static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev)
|
|
-{
|
|
- return ERR_PTR(-ENODEV);
|
|
-}
|
|
-#endif /* CONFIG_OF_GPIO */
|
|
|
|
static int gpio_led_probe(struct platform_device *pdev)
|
|
{
|
|
@@ -261,7 +255,7 @@ static int gpio_led_probe(struct platform_device *pdev)
|
|
}
|
|
}
|
|
} else {
|
|
- priv = gpio_leds_create_of(pdev);
|
|
+ priv = gpio_leds_create(pdev);
|
|
if (IS_ERR(priv))
|
|
return PTR_ERR(priv);
|
|
}
|
|
@@ -288,7 +282,7 @@ static struct platform_driver gpio_led_driver = {
|
|
.driver = {
|
|
.name = "leds-gpio",
|
|
.owner = THIS_MODULE,
|
|
- .of_match_table = of_match_ptr(of_gpio_leds_match),
|
|
+ .of_match_table = of_gpio_leds_match,
|
|
},
|
|
};
|
|
|
|
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
|
|
index 634f729..0a1af93 100644
|
|
--- a/drivers/misc/eeprom/at25.c
|
|
+++ b/drivers/misc/eeprom/at25.c
|
|
@@ -18,7 +18,7 @@
|
|
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/spi/eeprom.h>
|
|
-#include <linux/of.h>
|
|
+#include <linux/property.h>
|
|
|
|
/*
|
|
* NOTE: this is an *EEPROM* driver. The vagaries of product naming
|
|
@@ -301,35 +301,33 @@ static ssize_t at25_mem_write(struct memory_accessor *mem, const char *buf,
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
-static int at25_np_to_chip(struct device *dev,
|
|
- struct device_node *np,
|
|
- struct spi_eeprom *chip)
|
|
+static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
|
|
{
|
|
u32 val;
|
|
|
|
memset(chip, 0, sizeof(*chip));
|
|
- strncpy(chip->name, np->name, sizeof(chip->name));
|
|
+ strncpy(chip->name, "at25", sizeof(chip->name));
|
|
|
|
- if (of_property_read_u32(np, "size", &val) == 0 ||
|
|
- of_property_read_u32(np, "at25,byte-len", &val) == 0) {
|
|
+ if (device_property_read_u32(dev, "size", &val) == 0 ||
|
|
+ device_property_read_u32(dev, "at25,byte-len", &val) == 0) {
|
|
chip->byte_len = val;
|
|
} else {
|
|
dev_err(dev, "Error: missing \"size\" property\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
- if (of_property_read_u32(np, "pagesize", &val) == 0 ||
|
|
- of_property_read_u32(np, "at25,page-size", &val) == 0) {
|
|
+ if (device_property_read_u32(dev, "pagesize", &val) == 0 ||
|
|
+ device_property_read_u32(dev, "at25,page-size", &val) == 0) {
|
|
chip->page_size = (u16)val;
|
|
} else {
|
|
dev_err(dev, "Error: missing \"pagesize\" property\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
- if (of_property_read_u32(np, "at25,addr-mode", &val) == 0) {
|
|
+ if (device_property_read_u32(dev, "at25,addr-mode", &val) == 0) {
|
|
chip->flags = (u16)val;
|
|
} else {
|
|
- if (of_property_read_u32(np, "address-width", &val)) {
|
|
+ if (device_property_read_u32(dev, "address-width", &val)) {
|
|
dev_err(dev,
|
|
"Error: missing \"address-width\" property\n");
|
|
return -ENODEV;
|
|
@@ -350,7 +348,7 @@ static int at25_np_to_chip(struct device *dev,
|
|
val);
|
|
return -ENODEV;
|
|
}
|
|
- if (of_find_property(np, "read-only", NULL))
|
|
+ if (device_property_present(dev, "read-only"))
|
|
chip->flags |= EE_READONLY;
|
|
}
|
|
return 0;
|
|
@@ -360,21 +358,15 @@ static int at25_probe(struct spi_device *spi)
|
|
{
|
|
struct at25_data *at25 = NULL;
|
|
struct spi_eeprom chip;
|
|
- struct device_node *np = spi->dev.of_node;
|
|
int err;
|
|
int sr;
|
|
int addrlen;
|
|
|
|
/* Chip description */
|
|
if (!spi->dev.platform_data) {
|
|
- if (np) {
|
|
- err = at25_np_to_chip(&spi->dev, np, &chip);
|
|
- if (err)
|
|
- return err;
|
|
- } else {
|
|
- dev_err(&spi->dev, "Error: no chip description\n");
|
|
- return -ENODEV;
|
|
- }
|
|
+ err = at25_fw_to_chip(&spi->dev, &chip);
|
|
+ if (err)
|
|
+ return err;
|
|
} else
|
|
chip = *(struct spi_eeprom *)spi->dev.platform_data;
|
|
|
|
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
|
|
index 8319c99..6feb6ef3 100644
|
|
--- a/drivers/net/ethernet/amd/Kconfig
|
|
+++ b/drivers/net/ethernet/amd/Kconfig
|
|
@@ -179,7 +179,7 @@ config SUNLANCE
|
|
|
|
config AMD_XGBE
|
|
tristate "AMD 10GbE Ethernet driver"
|
|
- depends on OF_NET
|
|
+ depends on OF_NET || ACPI
|
|
select PHYLIB
|
|
select AMD_XGBE_PHY
|
|
select BITREVERSE
|
|
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
|
|
index 9da3a03..a34cad2 100644
|
|
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
|
|
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
|
|
@@ -130,7 +130,7 @@ static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata,
|
|
|
|
DBGPR("-->xgbe_usec_to_riwt\n");
|
|
|
|
- rate = clk_get_rate(pdata->sysclk);
|
|
+ rate = pdata->sysclk_rate;
|
|
|
|
/*
|
|
* Convert the input usec value to the watchdog timer value. Each
|
|
@@ -153,7 +153,7 @@ static unsigned int xgbe_riwt_to_usec(struct xgbe_prv_data *pdata,
|
|
|
|
DBGPR("-->xgbe_riwt_to_usec\n");
|
|
|
|
- rate = clk_get_rate(pdata->sysclk);
|
|
+ rate = pdata->sysclk_rate;
|
|
|
|
/*
|
|
* Convert the input watchdog timer value to the usec value. Each
|
|
@@ -695,6 +695,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 2349ea9..a04b18b 100644
|
|
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
|
|
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
|
|
@@ -425,6 +425,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 f5a8fa0..338c0ed 100644
|
|
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
|
|
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
|
|
@@ -124,6 +124,7 @@
|
|
#include <linux/of.h>
|
|
#include <linux/of_net.h>
|
|
#include <linux/clk.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include "xgbe.h"
|
|
#include "xgbe-common.h"
|
|
@@ -215,6 +216,205 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata)
|
|
xgbe_init_function_ptrs_desc(&pdata->desc_if);
|
|
}
|
|
|
|
+static int xgbe_map_resources(struct xgbe_prv_data *pdata)
|
|
+{
|
|
+ struct platform_device *pdev = pdata->pdev;
|
|
+ struct device *dev = pdata->dev;
|
|
+ struct resource *res;
|
|
+
|
|
+ /* Obtain the mmio areas for the device */
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ pdata->xgmac_regs = devm_ioremap_resource(dev, res);
|
|
+ if (IS_ERR(pdata->xgmac_regs)) {
|
|
+ dev_err(dev, "xgmac ioremap failed\n");
|
|
+ return PTR_ERR(pdata->xgmac_regs);
|
|
+ }
|
|
+ DBGPR(" xgmac_regs = %p\n", pdata->xgmac_regs);
|
|
+
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
+ pdata->xpcs_regs = devm_ioremap_resource(dev, res);
|
|
+ if (IS_ERR(pdata->xpcs_regs)) {
|
|
+ dev_err(dev, "xpcs ioremap failed\n");
|
|
+ return PTR_ERR(pdata->xpcs_regs);
|
|
+ }
|
|
+ DBGPR(" xpcs_regs = %p\n", pdata->xpcs_regs);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
|
|
+{
|
|
+ struct acpi_device *adev = pdata->adev;
|
|
+ struct device *dev = pdata->dev;
|
|
+ const union acpi_object *property;
|
|
+ acpi_status status;
|
|
+ u64 cca;
|
|
+ unsigned int i;
|
|
+ int ret;
|
|
+
|
|
+ /* Map the memory resources */
|
|
+ ret = xgbe_map_resources(pdata);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Obtain the system clock setting */
|
|
+ ret = acpi_dev_get_property(adev, XGBE_ACPI_DMA_FREQ, ACPI_TYPE_INTEGER,
|
|
+ &property);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "unable to obtain %s acpi property\n",
|
|
+ XGBE_ACPI_DMA_FREQ);
|
|
+ return ret;
|
|
+ }
|
|
+ pdata->sysclk_rate = property->integer.value;
|
|
+
|
|
+ /* Obtain the PTP clock setting */
|
|
+ ret = acpi_dev_get_property(adev, XGBE_ACPI_PTP_FREQ, ACPI_TYPE_INTEGER,
|
|
+ &property);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "unable to obtain %s acpi property\n",
|
|
+ XGBE_ACPI_PTP_FREQ);
|
|
+ return ret;
|
|
+ }
|
|
+ pdata->ptpclk_rate = property->integer.value;
|
|
+
|
|
+ /* Retrieve the MAC address */
|
|
+ ret = acpi_dev_get_property_array(adev, XGBE_ACPI_MAC_ADDR,
|
|
+ ACPI_TYPE_INTEGER, &property);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "unable to obtain %s acpi property\n",
|
|
+ XGBE_ACPI_MAC_ADDR);
|
|
+ return ret;
|
|
+ }
|
|
+ if (property->package.count != 6) {
|
|
+ dev_err(dev, "invalid %s acpi property\n",
|
|
+ XGBE_ACPI_MAC_ADDR);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ for (i = 0; i < property->package.count; i++) {
|
|
+ union acpi_object *obj = &property->package.elements[i];
|
|
+
|
|
+ pdata->mac_addr[i] = (u8)obj->integer.value;
|
|
+ }
|
|
+ if (!is_valid_ether_addr(pdata->mac_addr)) {
|
|
+ dev_err(dev, "invalid %s acpi property\n",
|
|
+ XGBE_ACPI_MAC_ADDR);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Retrieve the PHY mode - it must be "xgmii" */
|
|
+ ret = acpi_dev_get_property(adev, XGBE_ACPI_PHY_MODE, ACPI_TYPE_STRING,
|
|
+ &property);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "unable to obtain %s acpi property\n",
|
|
+ XGBE_ACPI_PHY_MODE);
|
|
+ return ret;
|
|
+ }
|
|
+ if (strcmp(property->string.pointer,
|
|
+ phy_modes(PHY_INTERFACE_MODE_XGMII))) {
|
|
+ dev_err(dev, "invalid %s acpi property\n",
|
|
+ XGBE_ACPI_PHY_MODE);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ pdata->phy_mode = PHY_INTERFACE_MODE_XGMII;
|
|
+
|
|
+#ifndef METHOD_NAME__CCA
|
|
+#define METHOD_NAME__CCA "_CCA"
|
|
+#endif
|
|
+ /* Set the device cache coherency values */
|
|
+ if (acpi_has_method(adev->handle, METHOD_NAME__CCA)) {
|
|
+ status = acpi_evaluate_integer(adev->handle, METHOD_NAME__CCA,
|
|
+ NULL, &cca);
|
|
+ if (ACPI_FAILURE(status)) {
|
|
+ dev_err(dev, "error obtaining acpi _CCA method\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else {
|
|
+ cca = 0;
|
|
+ }
|
|
+
|
|
+ if (cca) {
|
|
+ pdata->axdomain = XGBE_DMA_OS_AXDOMAIN;
|
|
+ pdata->arcache = XGBE_DMA_OS_ARCACHE;
|
|
+ pdata->awcache = XGBE_DMA_OS_AWCACHE;
|
|
+ } else {
|
|
+ pdata->axdomain = XGBE_DMA_SYS_AXDOMAIN;
|
|
+ pdata->arcache = XGBE_DMA_SYS_ARCACHE;
|
|
+ pdata->awcache = XGBE_DMA_SYS_AWCACHE;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#else /* CONFIG_ACPI */
|
|
+static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
+#endif /* CONFIG_ACPI */
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
+static int xgbe_of_support(struct xgbe_prv_data *pdata)
|
|
+{
|
|
+ struct device *dev = pdata->dev;
|
|
+ const u8 *mac_addr;
|
|
+ int ret;
|
|
+
|
|
+ /* Map the memory resources */
|
|
+ ret = xgbe_map_resources(pdata);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Obtain the system clock setting */
|
|
+ pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK);
|
|
+ if (IS_ERR(pdata->sysclk)) {
|
|
+ dev_err(dev, "dma devm_clk_get failed\n");
|
|
+ return PTR_ERR(pdata->sysclk);
|
|
+ }
|
|
+ pdata->sysclk_rate = clk_get_rate(pdata->sysclk);
|
|
+
|
|
+ /* Obtain the PTP clock setting */
|
|
+ pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK);
|
|
+ if (IS_ERR(pdata->ptpclk)) {
|
|
+ dev_err(dev, "ptp devm_clk_get failed\n");
|
|
+ return PTR_ERR(pdata->ptpclk);
|
|
+ }
|
|
+ pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk);
|
|
+
|
|
+ /* Retrieve the MAC address */
|
|
+ mac_addr = of_get_mac_address(dev->of_node);
|
|
+ if (!mac_addr) {
|
|
+ dev_err(dev, "invalid mac address for this device\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ memcpy(pdata->mac_addr, mac_addr, ETH_ALEN);
|
|
+
|
|
+ /* Retrieve the PHY mode - it must be "xgmii" */
|
|
+ pdata->phy_mode = of_get_phy_mode(dev->of_node);
|
|
+ if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
|
|
+ dev_err(dev, "invalid phy-mode specified for this device\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Set the device cache coherency values */
|
|
+ if (of_property_read_bool(dev->of_node, "dma-coherent")) {
|
|
+ pdata->axdomain = XGBE_DMA_OS_AXDOMAIN;
|
|
+ pdata->arcache = XGBE_DMA_OS_ARCACHE;
|
|
+ pdata->awcache = XGBE_DMA_OS_AWCACHE;
|
|
+ } else {
|
|
+ pdata->axdomain = XGBE_DMA_SYS_AXDOMAIN;
|
|
+ pdata->arcache = XGBE_DMA_SYS_ARCACHE;
|
|
+ pdata->awcache = XGBE_DMA_SYS_AWCACHE;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#else /* CONFIG_OF */
|
|
+static int xgbe_of_support(struct xgbe_prv_data *pdata)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
+#endif /*CONFIG_OF */
|
|
+
|
|
static int xgbe_probe(struct platform_device *pdev)
|
|
{
|
|
struct xgbe_prv_data *pdata;
|
|
@@ -222,8 +422,6 @@ static int xgbe_probe(struct platform_device *pdev)
|
|
struct xgbe_desc_if *desc_if;
|
|
struct net_device *netdev;
|
|
struct device *dev = &pdev->dev;
|
|
- struct resource *res;
|
|
- const u8 *mac_addr;
|
|
int ret;
|
|
|
|
DBGPR("--> xgbe_probe\n");
|
|
@@ -239,6 +437,7 @@ static int xgbe_probe(struct platform_device *pdev)
|
|
pdata = netdev_priv(netdev);
|
|
pdata->netdev = netdev;
|
|
pdata->pdev = pdev;
|
|
+ pdata->adev = ACPI_COMPANION(dev);
|
|
pdata->dev = dev;
|
|
platform_set_drvdata(pdev, netdev);
|
|
|
|
@@ -264,40 +463,13 @@ static int xgbe_probe(struct platform_device *pdev)
|
|
goto err_io;
|
|
}
|
|
|
|
- /* Obtain the system clock setting */
|
|
- pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK);
|
|
- if (IS_ERR(pdata->sysclk)) {
|
|
- dev_err(dev, "dma devm_clk_get failed\n");
|
|
- ret = PTR_ERR(pdata->sysclk);
|
|
- goto err_io;
|
|
- }
|
|
-
|
|
- /* Obtain the PTP clock setting */
|
|
- pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK);
|
|
- if (IS_ERR(pdata->ptpclk)) {
|
|
- dev_err(dev, "ptp devm_clk_get failed\n");
|
|
- ret = PTR_ERR(pdata->ptpclk);
|
|
- goto err_io;
|
|
- }
|
|
-
|
|
- /* Obtain the mmio areas for the device */
|
|
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
- pdata->xgmac_regs = devm_ioremap_resource(dev, res);
|
|
- if (IS_ERR(pdata->xgmac_regs)) {
|
|
- dev_err(dev, "xgmac ioremap failed\n");
|
|
- ret = PTR_ERR(pdata->xgmac_regs);
|
|
- goto err_io;
|
|
- }
|
|
- DBGPR(" xgmac_regs = %p\n", pdata->xgmac_regs);
|
|
-
|
|
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
- pdata->xpcs_regs = devm_ioremap_resource(dev, res);
|
|
- if (IS_ERR(pdata->xpcs_regs)) {
|
|
- dev_err(dev, "xpcs ioremap failed\n");
|
|
- ret = PTR_ERR(pdata->xpcs_regs);
|
|
+ /* Obtain device settings */
|
|
+ if (pdata->adev && !acpi_disabled)
|
|
+ ret = xgbe_acpi_support(pdata);
|
|
+ else
|
|
+ ret = xgbe_of_support(pdata);
|
|
+ if (ret)
|
|
goto err_io;
|
|
- }
|
|
- DBGPR(" xpcs_regs = %p\n", pdata->xpcs_regs);
|
|
|
|
/* Set the DMA mask */
|
|
if (!dev->dma_mask)
|
|
@@ -308,23 +480,16 @@ static int xgbe_probe(struct platform_device *pdev)
|
|
goto err_io;
|
|
}
|
|
|
|
- if (of_property_read_bool(dev->of_node, "dma-coherent")) {
|
|
- pdata->axdomain = XGBE_DMA_OS_AXDOMAIN;
|
|
- pdata->arcache = XGBE_DMA_OS_ARCACHE;
|
|
- pdata->awcache = XGBE_DMA_OS_AWCACHE;
|
|
- } else {
|
|
- pdata->axdomain = XGBE_DMA_SYS_AXDOMAIN;
|
|
- pdata->arcache = XGBE_DMA_SYS_ARCACHE;
|
|
- pdata->awcache = XGBE_DMA_SYS_AWCACHE;
|
|
- }
|
|
-
|
|
+ /* Get the device interrupt */
|
|
ret = platform_get_irq(pdev, 0);
|
|
if (ret < 0) {
|
|
dev_err(dev, "platform_get_irq failed\n");
|
|
goto err_io;
|
|
}
|
|
+
|
|
netdev->irq = ret;
|
|
netdev->base_addr = (unsigned long)pdata->xgmac_regs;
|
|
+ memcpy(netdev->dev_addr, pdata->mac_addr, netdev->addr_len);
|
|
|
|
/* Set all the function pointers */
|
|
xgbe_init_all_fptrs(pdata);
|
|
@@ -337,23 +502,6 @@ static int xgbe_probe(struct platform_device *pdev)
|
|
/* Populate the hardware features */
|
|
xgbe_get_all_hw_features(pdata);
|
|
|
|
- /* Retrieve the MAC address */
|
|
- mac_addr = of_get_mac_address(dev->of_node);
|
|
- if (!mac_addr) {
|
|
- dev_err(dev, "invalid mac address for this device\n");
|
|
- ret = -EINVAL;
|
|
- goto err_io;
|
|
- }
|
|
- memcpy(netdev->dev_addr, mac_addr, netdev->addr_len);
|
|
-
|
|
- /* Retrieve the PHY mode - it must be "xgmii" */
|
|
- pdata->phy_mode = of_get_phy_mode(dev->of_node);
|
|
- if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
|
|
- dev_err(dev, "invalid phy-mode specified for this device\n");
|
|
- ret = -EINVAL;
|
|
- goto err_io;
|
|
- }
|
|
-
|
|
/* Set default configuration data */
|
|
xgbe_default_config(pdata);
|
|
|
|
@@ -531,10 +679,22 @@ static int xgbe_resume(struct device *dev)
|
|
}
|
|
#endif /* CONFIG_PM */
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+static const struct acpi_device_id xgbe_acpi_match[] = {
|
|
+ { "AMDI8000", 0 },
|
|
+ {},
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match);
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
static const struct of_device_id xgbe_of_match[] = {
|
|
+ { .compatible = "amd,xgbe-seattle-v0a", },
|
|
{ .compatible = "amd,xgbe-seattle-v1a", },
|
|
{},
|
|
};
|
|
+#endif
|
|
|
|
MODULE_DEVICE_TABLE(of, xgbe_of_match);
|
|
static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume);
|
|
@@ -542,7 +702,12 @@ static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume);
|
|
static struct platform_driver xgbe_driver = {
|
|
.driver = {
|
|
.name = "amd-xgbe",
|
|
+#ifdef CONFIG_ACPI
|
|
+ .acpi_match_table = xgbe_acpi_match,
|
|
+#endif
|
|
+#ifdef CONFIG_OF
|
|
.of_match_table = xgbe_of_match,
|
|
+#endif
|
|
.pm = &xgbe_pm_ops,
|
|
},
|
|
.probe = xgbe_probe,
|
|
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
|
|
index 363b210..5d2c89b 100644
|
|
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
|
|
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
|
|
@@ -119,6 +119,7 @@
|
|
#include <linux/mdio.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/of.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include "xgbe.h"
|
|
#include "xgbe-common.h"
|
|
@@ -205,25 +206,16 @@ void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
|
|
|
|
int xgbe_mdio_register(struct xgbe_prv_data *pdata)
|
|
{
|
|
- struct device_node *phy_node;
|
|
struct mii_bus *mii;
|
|
struct phy_device *phydev;
|
|
int ret = 0;
|
|
|
|
DBGPR("-->xgbe_mdio_register\n");
|
|
|
|
- /* Retrieve the phy-handle */
|
|
- phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0);
|
|
- if (!phy_node) {
|
|
- dev_err(pdata->dev, "unable to parse phy-handle\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
mii = mdiobus_alloc();
|
|
if (mii == NULL) {
|
|
dev_err(pdata->dev, "mdiobus_alloc failed\n");
|
|
- ret = -ENOMEM;
|
|
- goto err_node_get;
|
|
+ return -ENOMEM;
|
|
}
|
|
|
|
/* Register on the MDIO bus (don't probe any PHYs) */
|
|
@@ -252,12 +244,9 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
|
|
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
|
|
MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS]));
|
|
|
|
- of_node_get(phy_node);
|
|
- phydev->dev.of_node = phy_node;
|
|
ret = phy_device_register(phydev);
|
|
if (ret) {
|
|
dev_err(pdata->dev, "phy_device_register failed\n");
|
|
- of_node_put(phy_node);
|
|
goto err_phy_device;
|
|
}
|
|
|
|
@@ -283,8 +272,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
|
|
|
|
pdata->phydev = phydev;
|
|
|
|
- of_node_put(phy_node);
|
|
-
|
|
DBGPHY_REGS(pdata);
|
|
|
|
DBGPR("<--xgbe_mdio_register\n");
|
|
@@ -300,9 +287,6 @@ err_mdiobus_register:
|
|
err_mdiobus_alloc:
|
|
mdiobus_free(mii);
|
|
|
|
-err_node_get:
|
|
- of_node_put(phy_node);
|
|
-
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
|
|
index a1bf9d1c..fa67203 100644
|
|
--- a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
|
|
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c
|
|
@@ -239,7 +239,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata)
|
|
snprintf(info->name, sizeof(info->name), "%s",
|
|
netdev_name(pdata->netdev));
|
|
info->owner = THIS_MODULE;
|
|
- info->max_adj = clk_get_rate(pdata->ptpclk);
|
|
+ info->max_adj = pdata->ptpclk_rate;
|
|
info->adjfreq = xgbe_adjfreq;
|
|
info->adjtime = xgbe_adjtime;
|
|
info->gettime = xgbe_gettime;
|
|
@@ -260,7 +260,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata)
|
|
*/
|
|
dividend = 50000000;
|
|
dividend <<= 32;
|
|
- pdata->tstamp_addend = div_u64(dividend, clk_get_rate(pdata->ptpclk));
|
|
+ pdata->tstamp_addend = div_u64(dividend, pdata->ptpclk_rate);
|
|
|
|
/* Setup the timecounter */
|
|
cc->read = xgbe_cc_read;
|
|
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
|
|
index 789957d..59498eb 100644
|
|
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
|
|
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
|
|
@@ -172,6 +172,12 @@
|
|
#define XGBE_DMA_CLOCK "dma_clk"
|
|
#define XGBE_PTP_CLOCK "ptp_clk"
|
|
|
|
+/* ACPI property names */
|
|
+#define XGBE_ACPI_MAC_ADDR "mac-address"
|
|
+#define XGBE_ACPI_PHY_MODE "phy-mode"
|
|
+#define XGBE_ACPI_DMA_FREQ "amd,dma-freq"
|
|
+#define XGBE_ACPI_PTP_FREQ "amd,ptp-freq"
|
|
+
|
|
/* Timestamp support - values based on 50MHz PTP clock
|
|
* 50MHz => 20 nsec
|
|
*/
|
|
@@ -186,8 +192,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
|
|
@@ -569,6 +578,7 @@ struct xgbe_hw_features {
|
|
struct xgbe_prv_data {
|
|
struct net_device *netdev;
|
|
struct platform_device *pdev;
|
|
+ struct acpi_device *adev;
|
|
struct device *dev;
|
|
|
|
/* XGMAC/XPCS related mmio registers */
|
|
@@ -649,6 +659,7 @@ struct xgbe_prv_data {
|
|
unsigned int phy_rx_pause;
|
|
|
|
/* Netdev related settings */
|
|
+ unsigned char mac_addr[MAX_ADDR_LEN];
|
|
netdev_features_t netdev_features;
|
|
struct napi_struct napi;
|
|
struct xgbe_mmc_stats mmc_stats;
|
|
@@ -658,7 +669,9 @@ struct xgbe_prv_data {
|
|
|
|
/* Device clocks */
|
|
struct clk *sysclk;
|
|
+ unsigned long sysclk_rate;
|
|
struct clk *ptpclk;
|
|
+ unsigned long ptpclk_rate;
|
|
|
|
/* Timestamp support */
|
|
spinlock_t tstamp_lock;
|
|
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
|
|
index 7ba83ffb08ac..3abbbd492a2c 100644
|
|
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
|
|
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
|
|
@@ -593,9 +593,11 @@ static int xgene_enet_reset(struct xgene_enet_pdata *pdata)
|
|
if (!xgene_ring_mgr_init(pdata))
|
|
return -ENODEV;
|
|
|
|
- clk_prepare_enable(pdata->clk);
|
|
- clk_disable_unprepare(pdata->clk);
|
|
- clk_prepare_enable(pdata->clk);
|
|
+ if (pdata->clk) {
|
|
+ clk_prepare_enable(pdata->clk);
|
|
+ clk_disable_unprepare(pdata->clk);
|
|
+ clk_prepare_enable(pdata->clk);
|
|
+ }
|
|
xgene_enet_ecc_init(pdata);
|
|
xgene_enet_config_ring_if_assoc(pdata);
|
|
|
|
@@ -663,15 +665,21 @@ static int xgene_enet_phy_connect(struct net_device *ndev)
|
|
struct phy_device *phy_dev;
|
|
struct device *dev = &pdata->pdev->dev;
|
|
|
|
- phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0);
|
|
- if (!phy_np) {
|
|
- netdev_dbg(ndev, "No phy-handle found\n");
|
|
- return -ENODEV;
|
|
+ if (dev->of_node) {
|
|
+ phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0);
|
|
+ if (!phy_np) {
|
|
+ netdev_dbg(ndev, "No phy-handle found\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ pdata->phy_dev = of_phy_find_device(phy_np);
|
|
}
|
|
|
|
- phy_dev = of_phy_connect(ndev, phy_np, &xgene_enet_adjust_link,
|
|
- 0, pdata->phy_mode);
|
|
- if (!phy_dev) {
|
|
+ phy_dev = pdata->phy_dev;
|
|
+
|
|
+ if (phy_dev == NULL ||
|
|
+ phy_connect_direct(ndev, phy_dev, &xgene_enet_adjust_link,
|
|
+ pdata->phy_mode)) {
|
|
+ 0, pdata->phy_mode);
|
|
netdev_err(ndev, "Could not connect to PHY\n");
|
|
return -ENODEV;
|
|
}
|
|
@@ -681,11 +689,52 @@ static int xgene_enet_phy_connect(struct net_device *ndev)
|
|
~SUPPORTED_100baseT_Half &
|
|
~SUPPORTED_1000baseT_Half;
|
|
phy_dev->advertising = phy_dev->supported;
|
|
- pdata->phy_dev = phy_dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+static int xgene_acpi_mdiobus_register(struct xgene_enet_pdata *pdata,
|
|
+ struct mii_bus *mdio)
|
|
+{
|
|
+ struct device *dev = &pdata->pdev->dev;
|
|
+ struct phy_device *phy;
|
|
+ int i, ret;
|
|
+ u32 phy_id;
|
|
+
|
|
+ /* Mask out all PHYs from auto probing. */
|
|
+ mdio->phy_mask = ~0;
|
|
+
|
|
+ /* Clear all the IRQ properties */
|
|
+ if (mdio->irq)
|
|
+ for (i = 0; i < PHY_MAX_ADDR; i++)
|
|
+ mdio->irq[i] = PHY_POLL;
|
|
+
|
|
+ /* Register the MDIO bus */
|
|
+ ret = mdiobus_register(mdio);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = device_property_read_u32(dev, "phy-channel", &phy_id);
|
|
+ if (ret)
|
|
+ return -EINVAL;
|
|
+
|
|
+ phy = get_phy_device(mdio, phy_id, true);
|
|
+ if (!phy || IS_ERR(phy))
|
|
+ return -EIO;
|
|
+
|
|
+ ret = phy_device_register(phy);
|
|
+ if (ret)
|
|
+ phy_device_free(phy);
|
|
+ else
|
|
+ pdata->phy_dev = phy;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#else
|
|
+#define xgene_acpi_mdiobus_register(a, b) -1
|
|
+#endif
|
|
+
|
|
int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata)
|
|
{
|
|
struct net_device *ndev = pdata->ndev;
|
|
@@ -702,7 +751,7 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata)
|
|
}
|
|
}
|
|
|
|
- if (!mdio_np) {
|
|
+ if (dev->of_node && !mdio_np) {
|
|
netdev_dbg(ndev, "No mdio node in the dts\n");
|
|
return -ENXIO;
|
|
}
|
|
@@ -720,7 +769,10 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata)
|
|
mdio_bus->priv = pdata;
|
|
mdio_bus->parent = &ndev->dev;
|
|
|
|
- ret = of_mdiobus_register(mdio_bus, mdio_np);
|
|
+ if (dev->of_node)
|
|
+ ret = of_mdiobus_register(mdio_bus, mdio_np);
|
|
+ else
|
|
+ ret = xgene_acpi_mdiobus_register(pdata, mdio_bus);
|
|
if (ret) {
|
|
netdev_err(ndev, "Failed to register MDIO bus\n");
|
|
mdiobus_free(mdio_bus);
|
|
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
|
|
index 3c208cc..6370ff4 100644
|
|
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
|
|
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c
|
|
@@ -746,6 +746,42 @@ static const struct net_device_ops xgene_ndev_ops = {
|
|
.ndo_set_mac_address = xgene_enet_set_mac_address,
|
|
};
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+static int acpi_get_mac_address(struct device *dev,
|
|
+ unsigned char *addr)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = device_property_read_u8_array(dev, "mac-address", addr, 6);
|
|
+ if (ret)
|
|
+ return 0;
|
|
+
|
|
+ return 6;
|
|
+}
|
|
+
|
|
+static int acpi_get_phy_mode(struct device *dev)
|
|
+{
|
|
+ int i, ret, phy_mode;
|
|
+ char *modestr;
|
|
+
|
|
+ ret = device_property_read_string(dev, "phy-mode", &modestr);
|
|
+ if (ret)
|
|
+ return -1;
|
|
+
|
|
+ phy_mode = -1;
|
|
+ for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) {
|
|
+ if (!strcasecmp(modestr, phy_modes(i))) {
|
|
+ phy_mode = i;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return phy_mode;
|
|
+}
|
|
+#else
|
|
+#define acpi_get_mac_address(a, b, c) 0
|
|
+#define acpi_get_phy_mode(a) -1
|
|
+#endif
|
|
+
|
|
static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
|
|
{
|
|
struct platform_device *pdev;
|
|
@@ -761,6 +797,8 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
|
|
ndev = pdata->ndev;
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "enet_csr");
|
|
+ if (!res)
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
dev_err(dev, "Resource enet_csr not defined\n");
|
|
return -ENODEV;
|
|
@@ -772,6 +810,8 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
|
|
}
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ring_csr");
|
|
+ if (!res)
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
if (!res) {
|
|
dev_err(dev, "Resource ring_csr not defined\n");
|
|
return -ENODEV;
|
|
@@ -783,6 +823,8 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
|
|
}
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ring_cmd");
|
|
+ if (!res)
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
|
if (!res) {
|
|
dev_err(dev, "Resource ring_cmd not defined\n");
|
|
return -ENODEV;
|
|
@@ -804,11 +846,13 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
|
|
mac = of_get_mac_address(dev->of_node);
|
|
if (mac)
|
|
memcpy(ndev->dev_addr, mac, ndev->addr_len);
|
|
- else
|
|
+ else if (!acpi_get_mac_address(dev, ndev->dev_addr))
|
|
eth_hw_addr_random(ndev);
|
|
memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len);
|
|
|
|
pdata->phy_mode = of_get_phy_mode(pdev->dev.of_node);
|
|
+ if (pdata->phy_mode < 0)
|
|
+ pdata->phy_mode = acpi_get_phy_mode(dev);
|
|
if (pdata->phy_mode < 0) {
|
|
dev_err(dev, "Unable to get phy-connection-type\n");
|
|
return pdata->phy_mode;
|
|
@@ -821,11 +865,12 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata)
|
|
}
|
|
|
|
pdata->clk = devm_clk_get(&pdev->dev, NULL);
|
|
- ret = IS_ERR(pdata->clk);
|
|
if (IS_ERR(pdata->clk)) {
|
|
- dev_err(&pdev->dev, "can't get clock\n");
|
|
- ret = PTR_ERR(pdata->clk);
|
|
- return ret;
|
|
+ /*
|
|
+ * Not necessarily an error. Firmware may have
|
|
+ * set up the clock already.
|
|
+ */
|
|
+ pdata->clk = NULL;
|
|
}
|
|
|
|
base_addr = pdata->base_addr;
|
|
@@ -873,7 +918,7 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata)
|
|
pdata->port_ops->cle_bypass(pdata, dst_ring_num, buf_pool->id);
|
|
pdata->mac_ops->init(pdata);
|
|
|
|
- return ret;
|
|
+ return 0;
|
|
}
|
|
|
|
static void xgene_enet_setup_ops(struct xgene_enet_pdata *pdata)
|
|
@@ -934,7 +979,7 @@ static int xgene_enet_probe(struct platform_device *pdev)
|
|
goto err;
|
|
}
|
|
|
|
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
|
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
|
if (ret) {
|
|
netdev_err(ndev, "No usable DMA configuration\n");
|
|
goto err;
|
|
@@ -981,6 +1026,14 @@ static int xgene_enet_remove(struct platform_device *pdev)
|
|
return 0;
|
|
}
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+static const struct acpi_device_id xgene_enet_acpi_match[] = {
|
|
+ { "APMC0D05", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match);
|
|
+#endif
|
|
+
|
|
static struct of_device_id xgene_enet_match[] = {
|
|
{.compatible = "apm,xgene-enet",},
|
|
{},
|
|
@@ -992,6 +1045,7 @@ static struct platform_driver xgene_enet_driver = {
|
|
.driver = {
|
|
.name = "xgene-enet",
|
|
.of_match_table = xgene_enet_match,
|
|
+ .acpi_match_table = ACPI_PTR(xgene_enet_acpi_match),
|
|
},
|
|
.probe = xgene_enet_probe,
|
|
.remove = xgene_enet_remove,
|
|
diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
|
|
index 874e5a0..8b7e2cf 100644
|
|
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
|
|
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h
|
|
@@ -31,6 +31,7 @@
|
|
#include <linux/prefetch.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/phy.h>
|
|
+#include <linux/acpi.h>
|
|
#include "xgene_enet_hw.h"
|
|
|
|
#define XGENE_DRV_VERSION "v1.0"
|
|
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
|
|
index 2c62208..98e22544 100644
|
|
--- a/drivers/net/ethernet/smsc/smc91x.c
|
|
+++ b/drivers/net/ethernet/smsc/smc91x.c
|
|
@@ -82,6 +82,7 @@ static const char version[] =
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_gpio.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
@@ -2463,6 +2464,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,
|
|
@@ -2471,6 +2480,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/Kconfig b/drivers/net/phy/Kconfig
|
|
index 75472cf7..bacafe2 100644
|
|
--- a/drivers/net/phy/Kconfig
|
|
+++ b/drivers/net/phy/Kconfig
|
|
@@ -26,7 +26,7 @@ config AMD_PHY
|
|
|
|
config AMD_XGBE_PHY
|
|
tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs"
|
|
- depends on OF
|
|
+ depends on OF || ACPI
|
|
---help---
|
|
Currently supports the AMD 10GbE PHY
|
|
|
|
diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c
|
|
index c456559..d852c6e 100644
|
|
--- a/drivers/net/phy/amd-xgbe-phy.c
|
|
+++ b/drivers/net/phy/amd-xgbe-phy.c
|
|
@@ -74,15 +74,19 @@
|
|
#include <linux/of_platform.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/uaccess.h>
|
|
+#include <linux/acpi.h>
|
|
+
|
|
|
|
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
|
|
@@ -99,11 +103,9 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
|
|
#ifndef MDIO_PMA_10GBR_PMD_CTRL
|
|
#define MDIO_PMA_10GBR_PMD_CTRL 0x0096
|
|
#endif
|
|
-
|
|
#ifndef MDIO_PMA_10GBR_FEC_CTRL
|
|
#define MDIO_PMA_10GBR_FEC_CTRL 0x00ab
|
|
#endif
|
|
-
|
|
#ifndef MDIO_AN_XNP
|
|
#define MDIO_AN_XNP 0x0016
|
|
#endif
|
|
@@ -111,93 +113,14 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
|
|
#ifndef MDIO_AN_INTMASK
|
|
#define MDIO_AN_INTMASK 0x8001
|
|
#endif
|
|
-
|
|
#ifndef MDIO_AN_INT
|
|
#define MDIO_AN_INT 0x8002
|
|
#endif
|
|
|
|
-#ifndef MDIO_AN_KR_CTRL
|
|
-#define MDIO_AN_KR_CTRL 0x8003
|
|
-#endif
|
|
-
|
|
#ifndef MDIO_CTRL1_SPEED1G
|
|
#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
|
|
#endif
|
|
|
|
-#ifndef MDIO_KR_CTRL_PDETECT
|
|
-#define MDIO_KR_CTRL_PDETECT 0x01
|
|
-#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))
|
|
|
|
@@ -207,70 +130,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))
|
|
-
|
|
-/* 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 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 XCMU_IOREAD(_priv, _reg) \
|
|
+ ioread16((_priv)->cmu_regs + _reg)
|
|
|
|
-#define XSIR1_IOWRITE(_priv, _reg, _val) \
|
|
- iowrite16((_val), (_priv)->sir1_regs + _reg)
|
|
+#define XCMU_IOWRITE(_priv, _reg, _val) \
|
|
+ iowrite16((_val), (_priv)->cmu_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)
|
|
|
|
@@ -291,6 +156,78 @@ 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,
|
|
AMD_XGBE_AN_START,
|
|
@@ -316,29 +253,31 @@ enum amd_xgbe_phy_mode {
|
|
};
|
|
|
|
enum amd_xgbe_phy_speedset {
|
|
- AMD_XGBE_PHY_SPEEDSET_1000_10000,
|
|
+ AMD_XGBE_PHY_SPEEDSET_1000_10000 = 0,
|
|
AMD_XGBE_PHY_SPEEDSET_2500_10000,
|
|
};
|
|
|
|
struct amd_xgbe_phy_priv {
|
|
struct platform_device *pdev;
|
|
+ struct acpi_device *adev;
|
|
struct device *dev;
|
|
|
|
struct phy_device *phydev;
|
|
|
|
/* 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;
|
|
- unsigned int speed_set;
|
|
+ enum amd_xgbe_phy_mode mode;
|
|
|
|
/* Auto-negotiation state machine support */
|
|
struct mutex an_mutex;
|
|
@@ -348,7 +287,6 @@ struct amd_xgbe_phy_priv {
|
|
enum amd_xgbe_phy_rx kx_state;
|
|
struct work_struct an_work;
|
|
struct workqueue_struct *an_workqueue;
|
|
- unsigned int parallel_detect;
|
|
};
|
|
|
|
static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
|
|
@@ -401,33 +339,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)
|
|
@@ -435,8 +391,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;
|
|
|
|
@@ -462,19 +418,32 @@ 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_REG20, BLWC_ENA, RXTX_10000_BLWC);
|
|
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ);
|
|
+ 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, 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;
|
|
}
|
|
|
|
@@ -510,19 +479,32 @@ 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_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, RXTX_2500_BLWC);
|
|
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_2500_PQ);
|
|
+ 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;
|
|
}
|
|
|
|
@@ -558,47 +540,33 @@ 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_REG20, BLWC_ENA, RXTX_1000_BLWC);
|
|
- XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_PQ);
|
|
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG5, TXAMP_CNTL, RXTX_1G_TX_AMP);
|
|
|
|
- amd_xgbe_phy_serdes_complete_ratechange(phydev);
|
|
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_DATA_RATE, RXTX_FIFTH_RATE);
|
|
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_WORD_MODE, RXTX_10BIT_WORD);
|
|
|
|
- return 0;
|
|
-}
|
|
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, 1);
|
|
|
|
-static int amd_xgbe_phy_cur_mode(struct phy_device *phydev,
|
|
- enum amd_xgbe_phy_mode *mode)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
+ 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);
|
|
|
|
- if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
|
|
- *mode = AMD_XGBE_MODE_KR;
|
|
- else
|
|
- *mode = AMD_XGBE_MODE_KX;
|
|
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1G_PQ);
|
|
|
|
- return 0;
|
|
-}
|
|
+ amd_xgbe_phy_serdes_complete_ratechange(phydev);
|
|
|
|
-static bool amd_xgbe_phy_in_kr_mode(struct phy_device *phydev)
|
|
-{
|
|
- enum amd_xgbe_phy_mode mode;
|
|
+ spin_unlock(&cmu_lock);
|
|
|
|
- if (amd_xgbe_phy_cur_mode(phydev, &mode))
|
|
- return false;
|
|
+ priv->mode = AMD_XGBE_MODE_KX;
|
|
|
|
- return (mode == AMD_XGBE_MODE_KR);
|
|
+ return 0;
|
|
}
|
|
|
|
static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
|
|
@@ -607,7 +575,7 @@ static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
|
|
int ret;
|
|
|
|
/* If we are in KR switch to KX, and vice-versa */
|
|
- if (amd_xgbe_phy_in_kr_mode(phydev)) {
|
|
+ if (priv->mode == AMD_XGBE_MODE_KR) {
|
|
if (priv->speed_set == AMD_XGBE_PHY_SPEEDSET_1000_10000)
|
|
ret = amd_xgbe_phy_gmii_mode(phydev);
|
|
else
|
|
@@ -619,20 +587,15 @@ static int amd_xgbe_phy_switch_mode(struct phy_device *phydev)
|
|
return ret;
|
|
}
|
|
|
|
-static int amd_xgbe_phy_set_mode(struct phy_device *phydev,
|
|
- enum amd_xgbe_phy_mode mode)
|
|
+static enum amd_xgbe_phy_an amd_xgbe_an_switch_mode(struct phy_device *phydev)
|
|
{
|
|
- enum amd_xgbe_phy_mode cur_mode;
|
|
int ret;
|
|
|
|
- ret = amd_xgbe_phy_cur_mode(phydev, &cur_mode);
|
|
- if (ret)
|
|
- return ret;
|
|
-
|
|
- if (mode != cur_mode)
|
|
- ret = amd_xgbe_phy_switch_mode(phydev);
|
|
+ ret = amd_xgbe_phy_switch_mode(phydev);
|
|
+ if (ret < 0)
|
|
+ return AMD_XGBE_AN_ERROR;
|
|
|
|
- return ret;
|
|
+ return AMD_XGBE_AN_START;
|
|
}
|
|
|
|
static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
|
|
@@ -643,8 +606,8 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
|
|
|
|
*state = AMD_XGBE_RX_COMPLETE;
|
|
|
|
- /* If we're not in KR mode then we're done */
|
|
- if (!amd_xgbe_phy_in_kr_mode(phydev))
|
|
+ /* If we're in KX mode then we're done */
|
|
+ if (priv->mode == AMD_XGBE_MODE_KX)
|
|
return AMD_XGBE_AN_EVENT;
|
|
|
|
/* Enable/Disable FEC */
|
|
@@ -672,13 +635,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;
|
|
}
|
|
|
|
@@ -702,6 +661,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
|
|
static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
|
|
enum amd_xgbe_phy_rx *state)
|
|
{
|
|
+ struct amd_xgbe_phy_priv *priv = phydev->priv;
|
|
unsigned int link_support;
|
|
int ret, ad_reg, lp_reg;
|
|
|
|
@@ -711,9 +671,9 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
|
|
return AMD_XGBE_AN_ERROR;
|
|
|
|
/* Check for a supported mode, otherwise restart in a different one */
|
|
- link_support = amd_xgbe_phy_in_kr_mode(phydev) ? 0x80 : 0x20;
|
|
+ link_support = (priv->mode == AMD_XGBE_MODE_KR) ? 0x80 : 0x20;
|
|
if (!(ret & link_support))
|
|
- return AMD_XGBE_AN_INCOMPAT_LINK;
|
|
+ return amd_xgbe_an_switch_mode(phydev);
|
|
|
|
/* Check Extended Next Page support */
|
|
ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
|
|
@@ -754,7 +714,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
|
|
int ret;
|
|
|
|
/* Be sure we aren't looping trying to negotiate */
|
|
- if (amd_xgbe_phy_in_kr_mode(phydev)) {
|
|
+ if (priv->mode == AMD_XGBE_MODE_KR) {
|
|
if (priv->kr_state != AMD_XGBE_RX_READY)
|
|
return AMD_XGBE_AN_NO_LINK;
|
|
priv->kr_state = AMD_XGBE_RX_BPA;
|
|
@@ -817,13 +777,6 @@ static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
|
|
/* Enable and start auto-negotiation */
|
|
phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
|
|
|
|
- ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_KR_CTRL);
|
|
- if (ret < 0)
|
|
- return AMD_XGBE_AN_ERROR;
|
|
-
|
|
- ret |= MDIO_KR_CTRL_PDETECT;
|
|
- phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_KR_CTRL, ret);
|
|
-
|
|
ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
|
|
if (ret < 0)
|
|
return AMD_XGBE_AN_ERROR;
|
|
@@ -864,8 +817,8 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
|
|
enum amd_xgbe_phy_rx *state;
|
|
int ret;
|
|
|
|
- state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state
|
|
- : &priv->kx_state;
|
|
+ state = (priv->mode == AMD_XGBE_MODE_KR) ? &priv->kr_state
|
|
+ : &priv->kx_state;
|
|
|
|
switch (*state) {
|
|
case AMD_XGBE_RX_BPA:
|
|
@@ -885,13 +838,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
|
|
|
|
static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev)
|
|
{
|
|
- int ret;
|
|
-
|
|
- ret = amd_xgbe_phy_switch_mode(phydev);
|
|
- if (ret)
|
|
- return AMD_XGBE_AN_ERROR;
|
|
-
|
|
- return AMD_XGBE_AN_START;
|
|
+ return amd_xgbe_an_switch_mode(phydev);
|
|
}
|
|
|
|
static void amd_xgbe_an_state_machine(struct work_struct *work)
|
|
@@ -904,10 +851,6 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
|
|
int sleep;
|
|
unsigned int an_supported = 0;
|
|
|
|
- /* Start in KX mode */
|
|
- if (amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX))
|
|
- priv->an_state = AMD_XGBE_AN_ERROR;
|
|
-
|
|
while (1) {
|
|
mutex_lock(&priv->an_mutex);
|
|
|
|
@@ -915,9 +858,8 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
|
|
|
|
switch (priv->an_state) {
|
|
case AMD_XGBE_AN_START:
|
|
- an_supported = 0;
|
|
- priv->parallel_detect = 0;
|
|
priv->an_state = amd_xgbe_an_start(phydev);
|
|
+ an_supported = 0;
|
|
break;
|
|
|
|
case AMD_XGBE_AN_EVENT:
|
|
@@ -934,7 +876,6 @@ static void amd_xgbe_an_state_machine(struct work_struct *work)
|
|
break;
|
|
|
|
case AMD_XGBE_AN_COMPLETE:
|
|
- priv->parallel_detect = an_supported ? 0 : 1;
|
|
netdev_info(phydev->attached_dev, "%s successful\n",
|
|
an_supported ? "Auto negotiation"
|
|
: "Parallel detection");
|
|
@@ -1069,6 +1010,7 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
|
|
{
|
|
struct amd_xgbe_phy_priv *priv = phydev->priv;
|
|
u32 mmd_mask = phydev->c45_ids.devices_in_package;
|
|
+ int ret;
|
|
|
|
if (phydev->autoneg != AUTONEG_ENABLE)
|
|
return amd_xgbe_phy_setup_forced(phydev);
|
|
@@ -1077,6 +1019,11 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
|
|
if (!(mmd_mask & MDIO_DEVS_AN))
|
|
return -EINVAL;
|
|
|
|
+ /* Get the current speed mode */
|
|
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
/* Start/Restart the auto-negotiation state machine */
|
|
mutex_lock(&priv->an_mutex);
|
|
priv->an_result = AMD_XGBE_AN_READY;
|
|
@@ -1166,14 +1113,18 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
|
|
{
|
|
struct amd_xgbe_phy_priv *priv = phydev->priv;
|
|
u32 mmd_mask = phydev->c45_ids.devices_in_package;
|
|
- int ret, ad_ret, lp_ret;
|
|
+ int ret, mode, ad_ret, lp_ret;
|
|
|
|
ret = amd_xgbe_phy_update_link(phydev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
- if ((phydev->autoneg == AUTONEG_ENABLE) &&
|
|
- !priv->parallel_detect) {
|
|
+ mode = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
|
|
+ if (mode < 0)
|
|
+ return mode;
|
|
+ mode &= MDIO_PCS_CTRL2_TYPE;
|
|
+
|
|
+ if (phydev->autoneg == AUTONEG_ENABLE) {
|
|
if (!(mmd_mask & MDIO_DEVS_AN))
|
|
return -EINVAL;
|
|
|
|
@@ -1204,39 +1155,40 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
|
|
ad_ret &= lp_ret;
|
|
if (ad_ret & 0x80) {
|
|
phydev->speed = SPEED_10000;
|
|
- ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR);
|
|
- if (ret)
|
|
- return ret;
|
|
+ if (mode != MDIO_PCS_CTRL2_10GBR) {
|
|
+ ret = amd_xgbe_phy_xgmii_mode(phydev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
} else {
|
|
- switch (priv->speed_set) {
|
|
- case AMD_XGBE_PHY_SPEEDSET_1000_10000:
|
|
- phydev->speed = SPEED_1000;
|
|
- break;
|
|
+ int (*mode_fcn)(struct phy_device *);
|
|
|
|
- case AMD_XGBE_PHY_SPEEDSET_2500_10000:
|
|
+ if (priv->speed_set ==
|
|
+ AMD_XGBE_PHY_SPEEDSET_1000_10000) {
|
|
+ phydev->speed = SPEED_1000;
|
|
+ mode_fcn = amd_xgbe_phy_gmii_mode;
|
|
+ } else {
|
|
phydev->speed = SPEED_2500;
|
|
- break;
|
|
+ mode_fcn = amd_xgbe_phy_gmii_2500_mode;
|
|
}
|
|
|
|
- ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX);
|
|
- if (ret)
|
|
- return ret;
|
|
+ if (mode == MDIO_PCS_CTRL2_10GBR) {
|
|
+ ret = mode_fcn(phydev);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ }
|
|
}
|
|
|
|
phydev->duplex = DUPLEX_FULL;
|
|
} else {
|
|
- if (amd_xgbe_phy_in_kr_mode(phydev)) {
|
|
+ if (mode == MDIO_PCS_CTRL2_10GBR) {
|
|
phydev->speed = SPEED_10000;
|
|
} else {
|
|
- switch (priv->speed_set) {
|
|
- case AMD_XGBE_PHY_SPEEDSET_1000_10000:
|
|
+ if (priv->speed_set ==
|
|
+ AMD_XGBE_PHY_SPEEDSET_1000_10000)
|
|
phydev->speed = SPEED_1000;
|
|
- break;
|
|
-
|
|
- case AMD_XGBE_PHY_SPEEDSET_2500_10000:
|
|
+ else
|
|
phydev->speed = SPEED_2500;
|
|
- break;
|
|
- }
|
|
}
|
|
phydev->duplex = DUPLEX_FULL;
|
|
phydev->pause = 0;
|
|
@@ -1288,29 +1240,188 @@ unlock:
|
|
return ret;
|
|
}
|
|
|
|
+static int amd_xgbe_phy_map_resources(struct amd_xgbe_phy_priv *priv,
|
|
+ struct platform_device *phy_pdev,
|
|
+ unsigned int phy_resnum)
|
|
+{
|
|
+ struct device *dev = priv->dev;
|
|
+ int ret;
|
|
+
|
|
+ /* Get the device mmio areas */
|
|
+ priv->rxtx_res = platform_get_resource(phy_pdev, IORESOURCE_MEM,
|
|
+ phy_resnum++);
|
|
+ priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res);
|
|
+ if (IS_ERR(priv->rxtx_regs)) {
|
|
+ dev_err(dev, "rxtx ioremap failed\n");
|
|
+ return PTR_ERR(priv->rxtx_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(phy_pdev, IORESOURCE_MEM,
|
|
+ phy_resnum++);
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_rxtx:
|
|
+ devm_iounmap(dev, priv->rxtx_regs);
|
|
+ devm_release_mem_region(dev, priv->rxtx_res->start,
|
|
+ resource_size(priv->rxtx_res));
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void amd_xgbe_phy_unmap_resources(struct amd_xgbe_phy_priv *priv)
|
|
+{
|
|
+ struct device *dev = priv->dev;
|
|
+
|
|
+ devm_iounmap(dev, priv->cmu_regs);
|
|
+
|
|
+ devm_iounmap(dev, priv->rxtx_regs);
|
|
+ devm_release_mem_region(dev, priv->rxtx_res->start,
|
|
+ resource_size(priv->rxtx_res));
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+static int amd_xgbe_phy_acpi_support(struct amd_xgbe_phy_priv *priv)
|
|
+{
|
|
+ struct platform_device *phy_pdev = priv->pdev;
|
|
+ struct acpi_device *adev = priv->adev;
|
|
+ struct device *dev = priv->dev;
|
|
+ const union acpi_object *property;
|
|
+ int ret;
|
|
+
|
|
+ /* Map the memory resources */
|
|
+ ret = amd_xgbe_phy_map_resources(priv, phy_pdev, 2);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Get the device serdes channel property */
|
|
+ ret = acpi_dev_get_property(adev, XGBE_PHY_CHANNEL_PROPERTY,
|
|
+ ACPI_TYPE_INTEGER, &property);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "unable to obtain %s acpi property\n",
|
|
+ XGBE_PHY_CHANNEL_PROPERTY);
|
|
+ goto err_resources;
|
|
+ }
|
|
+ priv->serdes_channel = property->integer.value;
|
|
+
|
|
+ /* Get the device speed set property */
|
|
+ ret = acpi_dev_get_property(adev, XGBE_PHY_SPEEDSET_PROPERTY,
|
|
+ ACPI_TYPE_INTEGER, &property);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "unable to obtain %s acpi property\n",
|
|
+ XGBE_PHY_SPEEDSET_PROPERTY);
|
|
+ goto err_resources;
|
|
+ }
|
|
+ priv->speed_set = property->integer.value;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_resources:
|
|
+ amd_xgbe_phy_unmap_resources(priv);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#else /* CONFIG_ACPI */
|
|
+static int amd_xgbe_phy_acpi_support(struct amd_xgbe_phy_priv *priv)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
+#endif /* CONFIG_ACPI */
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
+static int amd_xgbe_phy_of_support(struct amd_xgbe_phy_priv *priv)
|
|
+{
|
|
+ struct platform_device *phy_pdev;
|
|
+ struct device_node *bus_node;
|
|
+ struct device_node *phy_node;
|
|
+ struct device *dev = priv->dev;
|
|
+ const __be32 *property;
|
|
+ int ret;
|
|
+
|
|
+ bus_node = priv->dev->of_node;
|
|
+ phy_node = of_parse_phandle(bus_node, "phy-handle", 0);
|
|
+ if (!phy_node) {
|
|
+ dev_err(dev, "unable to parse phy-handle\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ phy_pdev = of_find_device_by_node(phy_node);
|
|
+ if (!phy_pdev) {
|
|
+ dev_err(dev, "unable to obtain phy device\n");
|
|
+ ret = -EINVAL;
|
|
+ goto err_put;
|
|
+ }
|
|
+
|
|
+ /* Map the memory resources */
|
|
+ ret = amd_xgbe_phy_map_resources(priv, phy_pdev, 0);
|
|
+ if (ret)
|
|
+ goto err_put;
|
|
+
|
|
+ /* Get the device serdes channel property */
|
|
+ property = of_get_property(phy_node, XGBE_PHY_CHANNEL_PROPERTY, NULL);
|
|
+ if (!property) {
|
|
+ dev_err(dev, "unable to obtain %s property\n",
|
|
+ XGBE_PHY_CHANNEL_PROPERTY);
|
|
+ ret = -EINVAL;
|
|
+ goto err_resources;
|
|
+ }
|
|
+ priv->serdes_channel = be32_to_cpu(*property);
|
|
+
|
|
+ /* Get the device speed set property */
|
|
+ property = of_get_property(phy_node, XGBE_PHY_SPEEDSET_PROPERTY, NULL);
|
|
+ if (property)
|
|
+ priv->speed_set = be32_to_cpu(*property);
|
|
+
|
|
+ of_node_put(phy_node);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_resources:
|
|
+ amd_xgbe_phy_unmap_resources(priv);
|
|
+
|
|
+err_put:
|
|
+ of_node_put(phy_node);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#else /* CONFIG_OF */
|
|
+static int amd_xgbe_phy_of_support(struct amd_xgbe_phy_priv *priv)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
+#endif /* CONFIG_OF */
|
|
+
|
|
static int amd_xgbe_phy_probe(struct phy_device *phydev)
|
|
{
|
|
struct amd_xgbe_phy_priv *priv;
|
|
- struct platform_device *pdev;
|
|
struct device *dev;
|
|
char *wq_name;
|
|
- const __be32 *property;
|
|
- unsigned int speed_set;
|
|
int ret;
|
|
|
|
- if (!phydev->dev.of_node)
|
|
+ if (!phydev->bus || !phydev->bus->parent)
|
|
return -EINVAL;
|
|
|
|
- pdev = of_find_device_by_node(phydev->dev.of_node);
|
|
- if (!pdev)
|
|
- return -EINVAL;
|
|
- dev = &pdev->dev;
|
|
+ dev = phydev->bus->parent;
|
|
|
|
wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", phydev->bus->name);
|
|
- if (!wq_name) {
|
|
- ret = -ENOMEM;
|
|
- goto err_pdev;
|
|
- }
|
|
+ if (!wq_name)
|
|
+ return -ENOMEM;
|
|
|
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
|
if (!priv) {
|
|
@@ -1318,86 +1429,54 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
|
|
goto err_name;
|
|
}
|
|
|
|
- priv->pdev = pdev;
|
|
+ priv->pdev = to_platform_device(dev);
|
|
+ priv->adev = ACPI_COMPANION(dev);
|
|
priv->dev = dev;
|
|
priv->phydev = phydev;
|
|
|
|
- /* Get the device mmio areas */
|
|
- priv->rxtx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
- priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res);
|
|
- if (IS_ERR(priv->rxtx_regs)) {
|
|
- dev_err(dev, "rxtx ioremap failed\n");
|
|
- ret = PTR_ERR(priv->rxtx_regs);
|
|
+ if (priv->adev && !acpi_disabled)
|
|
+ ret = amd_xgbe_phy_acpi_support(priv);
|
|
+ else
|
|
+ ret = amd_xgbe_phy_of_support(priv);
|
|
+ if (ret)
|
|
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);
|
|
- 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 speed set property */
|
|
- speed_set = 0;
|
|
- property = of_get_property(dev->of_node, XGBE_PHY_SPEEDSET_PROPERTY,
|
|
- NULL);
|
|
- if (property)
|
|
- speed_set = be32_to_cpu(*property);
|
|
-
|
|
- switch (speed_set) {
|
|
- case 0:
|
|
- priv->speed_set = AMD_XGBE_PHY_SPEEDSET_1000_10000;
|
|
- break;
|
|
- case 1:
|
|
- priv->speed_set = AMD_XGBE_PHY_SPEEDSET_2500_10000;
|
|
+ switch (priv->speed_set) {
|
|
+ case AMD_XGBE_PHY_SPEEDSET_1000_10000:
|
|
+ case AMD_XGBE_PHY_SPEEDSET_2500_10000:
|
|
break;
|
|
default:
|
|
dev_err(dev, "invalid amd,speed-set property\n");
|
|
ret = -EINVAL;
|
|
- goto err_sir1;
|
|
+ goto err_resources;
|
|
}
|
|
|
|
priv->link = 1;
|
|
|
|
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2);
|
|
+ if (ret < 0)
|
|
+ goto err_resources;
|
|
+ if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
|
|
+ priv->mode = AMD_XGBE_MODE_KR;
|
|
+ else
|
|
+ priv->mode = AMD_XGBE_MODE_KX;
|
|
+
|
|
mutex_init(&priv->an_mutex);
|
|
INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine);
|
|
priv->an_workqueue = create_singlethread_workqueue(wq_name);
|
|
if (!priv->an_workqueue) {
|
|
ret = -ENOMEM;
|
|
- goto err_sir1;
|
|
+ goto err_resources;
|
|
}
|
|
|
|
phydev->priv = priv;
|
|
|
|
kfree(wq_name);
|
|
- of_dev_put(pdev);
|
|
|
|
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_rxtx:
|
|
- devm_iounmap(dev, priv->rxtx_regs);
|
|
- devm_release_mem_region(dev, priv->rxtx_res->start,
|
|
- resource_size(priv->rxtx_res));
|
|
+err_resources:
|
|
+ amd_xgbe_phy_unmap_resources(priv);
|
|
|
|
err_priv:
|
|
devm_kfree(dev, priv);
|
|
@@ -1405,9 +1484,6 @@ err_priv:
|
|
err_name:
|
|
kfree(wq_name);
|
|
|
|
-err_pdev:
|
|
- of_dev_put(pdev);
|
|
-
|
|
return ret;
|
|
}
|
|
|
|
@@ -1424,18 +1500,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->rxtx_regs);
|
|
- devm_release_mem_region(dev, priv->rxtx_res->start,
|
|
- resource_size(priv->rxtx_res));
|
|
+ amd_xgbe_phy_unmap_resources(priv);
|
|
|
|
devm_kfree(dev, priv);
|
|
}
|
|
diff --git a/drivers/of/base.c b/drivers/of/base.c
|
|
index 3823edf..4c2ccde 100644
|
|
--- a/drivers/of/base.c
|
|
+++ b/drivers/of/base.c
|
|
@@ -1250,6 +1250,39 @@ int of_property_read_u64(const struct device_node *np, const char *propname,
|
|
EXPORT_SYMBOL_GPL(of_property_read_u64);
|
|
|
|
/**
|
|
+ * of_property_read_u64_array - Find and read an array of 64 bit integers
|
|
+ * from a property.
|
|
+ *
|
|
+ * @np: device node from which the property value is to be read.
|
|
+ * @propname: name of the property to be searched.
|
|
+ * @out_values: pointer to return value, modified only if return value is 0.
|
|
+ * @sz: number of array elements to read
|
|
+ *
|
|
+ * Search for a property in a device node and read 64-bit value(s) from
|
|
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
|
|
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
|
|
+ * property data isn't large enough.
|
|
+ *
|
|
+ * The out_values is modified only if a valid u64 value can be decoded.
|
|
+ */
|
|
+int of_property_read_u64_array(const struct device_node *np,
|
|
+ const char *propname, u64 *out_values,
|
|
+ size_t sz)
|
|
+{
|
|
+ const __be32 *val = of_find_property_value_of_size(np, propname,
|
|
+ (sz * sizeof(*out_values)));
|
|
+
|
|
+ if (IS_ERR(val))
|
|
+ return PTR_ERR(val);
|
|
+
|
|
+ while (sz--) {
|
|
+ *out_values++ = of_read_number(val, 2);
|
|
+ val += 2;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
* of_property_read_string - Find and read a string from a property
|
|
* @np: device node from which the property value is to be read.
|
|
* @propname: name of the property to be searched.
|
|
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
|
|
index 9ecabfa..9029d59c 100644
|
|
--- a/drivers/pci/host/pci-xgene.c
|
|
+++ b/drivers/pci/host/pci-xgene.c
|
|
@@ -29,6 +29,7 @@
|
|
#include <linux/pci.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#define PCIECORE_CTLANDSTATUS 0x50
|
|
#define PIM1_1L 0x80
|
|
@@ -235,6 +236,13 @@ static int xgene_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
|
|
break;
|
|
case 2:
|
|
xgene_pcie_cfg_in16(addr, offset, val);
|
|
+ /* FIXME.
|
|
+ * Something wrong with Configuration Request Retry Status
|
|
+ * on this hw. Pretend it isn't supported until the problem
|
|
+ * gets sorted out properly.
|
|
+ */
|
|
+ if (pci_is_root_bus(bus) && offset == (0x40 + PCI_EXP_RTCAP))
|
|
+ *val &= ~PCI_EXP_RTCAP_CRSVIS;
|
|
break;
|
|
default:
|
|
xgene_pcie_cfg_in32(addr, offset, val);
|
|
@@ -600,6 +608,165 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
|
|
return 0;
|
|
}
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+struct xgene_mcfg_info {
|
|
+ void __iomem *csr_base;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * 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 *__get_cfg_base(struct pci_mmcfg_region *cfg,
|
|
+ unsigned int bus)
|
|
+{
|
|
+ if (bus > cfg->start_bus)
|
|
+ return cfg->virt + AXI_EP_CFG_ACCESS;
|
|
+
|
|
+ return cfg->virt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * For Configuration request, RTDID register is used as Bus Number,
|
|
+ * Device Number and Function number of the header fields.
|
|
+ */
|
|
+static void __set_rtdid_reg(struct pci_mmcfg_region *cfg,
|
|
+ unsigned int bus, unsigned int devfn)
|
|
+{
|
|
+ struct xgene_mcfg_info *info = cfg->data;
|
|
+ unsigned int b, d, f;
|
|
+ u32 rtdid_val = 0;
|
|
+
|
|
+ b = bus;
|
|
+ d = PCI_SLOT(devfn);
|
|
+ f = PCI_FUNC(devfn);
|
|
+
|
|
+ if (bus != cfg->start_bus)
|
|
+ rtdid_val = (b << 8) | (d << 3) | f;
|
|
+
|
|
+ writel(rtdid_val, info->csr_base + RTDID);
|
|
+ /* read the register back to ensure flush */
|
|
+ readl(info->csr_base + RTDID);
|
|
+}
|
|
+
|
|
+static int xgene_raw_pci_read(struct pci_mmcfg_region *cfg, unsigned int bus,
|
|
+ unsigned int devfn, int offset, int len, u32 *val)
|
|
+{
|
|
+ void __iomem *addr;
|
|
+
|
|
+ if (bus == cfg->start_bus) {
|
|
+ if (devfn != 0) {
|
|
+ *val = 0xffffffff;
|
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
|
+ }
|
|
+
|
|
+ /* see xgene_pcie_hide_rc_bars() above */
|
|
+ if (offset == PCI_BASE_ADDRESS_0 ||
|
|
+ offset == PCI_BASE_ADDRESS_1) {
|
|
+ *val = 0;
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ __set_rtdid_reg(cfg, bus, devfn);
|
|
+ addr = __get_cfg_base(cfg, bus);
|
|
+ switch (len) {
|
|
+ case 1:
|
|
+ xgene_pcie_cfg_in8(addr, offset, val);
|
|
+ break;
|
|
+ case 2:
|
|
+ xgene_pcie_cfg_in16(addr, offset, val);
|
|
+ /* FIXME.
|
|
+ * Something wrong with Configuration Request Retry Status
|
|
+ * on this hw. Pretend it isn't supported until the problem
|
|
+ * gets sorted out properly.
|
|
+ */
|
|
+ if (bus == cfg->start_bus && offset == (0x40 + PCI_EXP_RTCAP))
|
|
+ *val &= ~PCI_EXP_RTCAP_CRSVIS;
|
|
+ break;
|
|
+ default:
|
|
+ xgene_pcie_cfg_in32(addr, offset, val);
|
|
+ break;
|
|
+ }
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
+}
|
|
+
|
|
+static int xgene_raw_pci_write(struct pci_mmcfg_region *cfg, unsigned int bus,
|
|
+ unsigned int devfn, int offset, int len, u32 val)
|
|
+{
|
|
+ void __iomem *addr;
|
|
+
|
|
+ if (bus == cfg->start_bus && devfn != 0)
|
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
|
+
|
|
+ __set_rtdid_reg(cfg, bus, devfn);
|
|
+ addr = __get_cfg_base(cfg, 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 acpi_status find_csr_base(struct acpi_resource *acpi_res, void *data)
|
|
+{
|
|
+ struct pci_mmcfg_region *cfg = data;
|
|
+ struct xgene_mcfg_info *info = cfg->data;
|
|
+ struct acpi_resource_fixed_memory32 *fixed32;
|
|
+
|
|
+ if (acpi_res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
|
|
+ fixed32 = &acpi_res->data.fixed_memory32;
|
|
+ info->csr_base = ioremap(fixed32->address,
|
|
+ fixed32->address_length);
|
|
+ return AE_CTRL_TERMINATE;
|
|
+ }
|
|
+ return AE_OK;
|
|
+}
|
|
+
|
|
+static int xgene_mcfg_fixup(struct acpi_pci_root *root,
|
|
+ struct pci_mmcfg_region *cfg)
|
|
+{
|
|
+ struct acpi_device *device = root->device;
|
|
+ struct xgene_mcfg_info *info;
|
|
+
|
|
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
|
|
+ if (info == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ cfg->data = info;
|
|
+
|
|
+ acpi_walk_resources(device->handle, METHOD_NAME__CRS,
|
|
+ find_csr_base, cfg);
|
|
+
|
|
+ if (!info->csr_base) {
|
|
+ kfree(info);
|
|
+ cfg->data = NULL;
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ cfg->read = xgene_raw_pci_read;
|
|
+ cfg->write = xgene_raw_pci_write;
|
|
+
|
|
+ /* actual last bus reachable through this mmconfig */
|
|
+ cfg->end_bus = root->secondary.end;
|
|
+
|
|
+ /* firmware should have done this */
|
|
+ xgene_raw_pci_write(cfg, cfg->start_bus, 0, PCI_PRIMARY_BUS, 4,
|
|
+ cfg->start_bus | ((cfg->start_bus + 1) << 8) |
|
|
+ (cfg->end_bus << 16));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+DECLARE_ACPI_MCFG_FIXUP("APM ", "XGENE ", xgene_mcfg_fixup);
|
|
+#endif /* CONFIG_ACPI */
|
|
+
|
|
static int xgene_pcie_probe_bridge(struct platform_device *pdev)
|
|
{
|
|
struct device_node *dn = pdev->dev.of_node;
|
|
@@ -631,10 +798,15 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
|
|
if (ret)
|
|
return ret;
|
|
|
|
- bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res);
|
|
+ bus = pci_create_root_bus(&pdev->dev, 0,
|
|
+ &xgene_pcie_ops, port, &res);
|
|
if (!bus)
|
|
return -ENOMEM;
|
|
|
|
+ pci_scan_child_bus(bus);
|
|
+ pci_assign_unassigned_bus_resources(bus);
|
|
+ pci_bus_add_devices(bus);
|
|
+
|
|
platform_set_drvdata(pdev, port);
|
|
return 0;
|
|
}
|
|
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 beea6ca..7038a2d 100644
|
|
--- a/drivers/tty/serial/8250/8250_dw.c
|
|
+++ b/drivers/tty/serial/8250/8250_dw.c
|
|
@@ -310,10 +310,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;
|
|
@@ -536,6 +544,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 ef9a165..9f1939c 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. */
|
|
@@ -634,6 +633,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,
|
|
@@ -641,6 +648,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/drivers/xen/efi.c b/drivers/xen/efi.c
|
|
index 1f850c9..f745db2 100644
|
|
--- a/drivers/xen/efi.c
|
|
+++ b/drivers/xen/efi.c
|
|
@@ -294,6 +294,7 @@ static const struct efi efi_xen __initconst = {
|
|
.acpi = EFI_INVALID_TABLE_ADDR,
|
|
.acpi20 = EFI_INVALID_TABLE_ADDR,
|
|
.smbios = EFI_INVALID_TABLE_ADDR,
|
|
+ .smbios3 = EFI_INVALID_TABLE_ADDR,
|
|
.sal_systab = EFI_INVALID_TABLE_ADDR,
|
|
.boot_info = EFI_INVALID_TABLE_ADDR,
|
|
.hcdp = EFI_INVALID_TABLE_ADDR,
|
|
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
|
|
index f34a083..04d02fc 100644
|
|
--- a/include/acpi/acpi_bus.h
|
|
+++ b/include/acpi/acpi_bus.h
|
|
@@ -27,6 +27,7 @@
|
|
#define __ACPI_BUS_H__
|
|
|
|
#include <linux/device.h>
|
|
+#include <linux/property.h>
|
|
|
|
/* TBD: Make dynamic */
|
|
#define ACPI_MAX_HANDLES 10
|
|
@@ -68,6 +69,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)
|
|
@@ -337,10 +340,20 @@ struct acpi_device_physical_node {
|
|
bool put_online:1;
|
|
};
|
|
|
|
+/* ACPI Device Specific Data (_DSD) */
|
|
+struct acpi_device_data {
|
|
+ const union acpi_object *pointer;
|
|
+ const union acpi_object *properties;
|
|
+ const union acpi_object *of_compatible;
|
|
+};
|
|
+
|
|
+struct acpi_gpio_mapping;
|
|
+
|
|
/* Device */
|
|
struct acpi_device {
|
|
int device_type;
|
|
acpi_handle handle; /* no handle for fixed hardware */
|
|
+ struct fwnode_handle fwnode;
|
|
struct acpi_device *parent;
|
|
struct list_head children;
|
|
struct list_head node;
|
|
@@ -353,9 +366,11 @@ struct acpi_device {
|
|
struct acpi_device_wakeup wakeup;
|
|
struct acpi_device_perf performance;
|
|
struct acpi_device_dir dir;
|
|
+ struct acpi_device_data data;
|
|
struct acpi_scan_handler *handler;
|
|
struct acpi_hotplug_context *hp;
|
|
struct acpi_driver *driver;
|
|
+ const struct acpi_gpio_mapping *driver_gpios;
|
|
void *driver_data;
|
|
struct device dev;
|
|
unsigned int physical_node_count;
|
|
@@ -364,6 +379,21 @@ struct acpi_device {
|
|
void (*remove)(struct acpi_device *);
|
|
};
|
|
|
|
+static inline bool is_acpi_node(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ return fwnode && fwnode->type == FWNODE_ACPI;
|
|
+}
|
|
+
|
|
+static inline struct acpi_device *acpi_node(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ return fwnode ? container_of(fwnode, struct acpi_device, fwnode) : NULL;
|
|
+}
|
|
+
|
|
+static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
|
|
+{
|
|
+ return &adev->fwnode;
|
|
+}
|
|
+
|
|
static inline void *acpi_driver_data(struct acpi_device *d)
|
|
{
|
|
return d->driver_data;
|
|
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/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
|
|
index aa70cbd..1261fef 100644
|
|
--- a/include/asm-generic/vmlinux.lds.h
|
|
+++ b/include/asm-generic/vmlinux.lds.h
|
|
@@ -275,6 +275,13 @@
|
|
VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \
|
|
} \
|
|
\
|
|
+ /* ACPI quirks */ \
|
|
+ .acpi_fixup : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) { \
|
|
+ VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .; \
|
|
+ *(.acpi_fixup_mcfg) \
|
|
+ VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .; \
|
|
+ } \
|
|
+ \
|
|
/* Built-in firmware blobs */ \
|
|
.builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \
|
|
VMLINUX_SYMBOL(__start_builtin_fw) = .; \
|
|
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
|
|
index 206dcc3..660dbfc 100644
|
|
--- a/include/kvm/arm_vgic.h
|
|
+++ b/include/kvm/arm_vgic.h
|
|
@@ -289,17 +289,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 407a12f..de81de3 100644
|
|
--- a/include/linux/acpi.h
|
|
+++ b/include/linux/acpi.h
|
|
@@ -28,6 +28,7 @@
|
|
#include <linux/errno.h>
|
|
#include <linux/ioport.h> /* for struct resource */
|
|
#include <linux/device.h>
|
|
+#include <linux/property.h>
|
|
|
|
#ifndef _LINUX
|
|
#define _LINUX
|
|
@@ -71,6 +72,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 +125,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,
|
|
@@ -423,12 +429,8 @@ extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *),
|
|
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
|
|
const struct device *dev);
|
|
|
|
-static inline bool acpi_driver_match_device(struct device *dev,
|
|
- const struct device_driver *drv)
|
|
-{
|
|
- return !!acpi_match_device(drv->acpi_match_table, dev);
|
|
-}
|
|
-
|
|
+extern bool acpi_driver_match_device(struct device *dev,
|
|
+ const struct device_driver *drv);
|
|
int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
|
|
int acpi_device_modalias(struct device *, char *, int);
|
|
|
|
@@ -443,6 +445,23 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *);
|
|
#define ACPI_COMPANION_SET(dev, adev) do { } while (0)
|
|
#define ACPI_HANDLE(dev) (NULL)
|
|
|
|
+struct fwnode_handle;
|
|
+
|
|
+static inline bool is_acpi_node(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static inline struct acpi_device *acpi_node(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
static inline const char *acpi_dev_name(struct acpi_device *adev)
|
|
{
|
|
return NULL;
|
|
@@ -659,4 +678,114 @@ do { \
|
|
#endif
|
|
#endif
|
|
|
|
+struct acpi_gpio_params {
|
|
+ unsigned int crs_entry_index;
|
|
+ unsigned int line_index;
|
|
+ bool active_low;
|
|
+};
|
|
+
|
|
+struct acpi_gpio_mapping {
|
|
+ const char *name;
|
|
+ const struct acpi_gpio_params *data;
|
|
+ unsigned int size;
|
|
+};
|
|
+
|
|
+#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
|
|
+int acpi_dev_add_driver_gpios(struct acpi_device *adev,
|
|
+ const struct acpi_gpio_mapping *gpios);
|
|
+
|
|
+static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev)
|
|
+{
|
|
+ if (adev)
|
|
+ adev->driver_gpios = NULL;
|
|
+}
|
|
+#else
|
|
+static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev,
|
|
+ const struct acpi_gpio_mapping *gpios)
|
|
+{
|
|
+ return -ENXIO;
|
|
+}
|
|
+static inline void acpi_dev_remove_driver_gpios(struct acpi_device *adev) {}
|
|
+#endif
|
|
+
|
|
+/* Device properties */
|
|
+
|
|
+#define MAX_ACPI_REFERENCE_ARGS 8
|
|
+struct acpi_reference_args {
|
|
+ struct acpi_device *adev;
|
|
+ size_t nargs;
|
|
+ u64 args[MAX_ACPI_REFERENCE_ARGS];
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+int acpi_dev_get_property(struct acpi_device *adev, const char *name,
|
|
+ acpi_object_type type, const union acpi_object **obj);
|
|
+int acpi_dev_get_property_array(struct acpi_device *adev, const char *name,
|
|
+ acpi_object_type type,
|
|
+ const union acpi_object **obj);
|
|
+int acpi_dev_get_property_reference(struct acpi_device *adev,
|
|
+ const char *name, size_t index,
|
|
+ struct acpi_reference_args *args);
|
|
+
|
|
+int acpi_dev_prop_get(struct acpi_device *adev, const char *propname,
|
|
+ void **valptr);
|
|
+int acpi_dev_prop_read_single(struct acpi_device *adev, const char *propname,
|
|
+ enum dev_prop_type proptype, void *val);
|
|
+int acpi_dev_prop_read(struct acpi_device *adev, const char *propname,
|
|
+ enum dev_prop_type proptype, void *val, size_t nval);
|
|
+
|
|
+struct acpi_device *acpi_get_next_child(struct device *dev,
|
|
+ struct acpi_device *child);
|
|
+#else
|
|
+static inline int acpi_dev_get_property(struct acpi_device *adev,
|
|
+ const char *name, acpi_object_type type,
|
|
+ const union acpi_object **obj)
|
|
+{
|
|
+ return -ENXIO;
|
|
+}
|
|
+static inline int acpi_dev_get_property_array(struct acpi_device *adev,
|
|
+ const char *name,
|
|
+ acpi_object_type type,
|
|
+ const union acpi_object **obj)
|
|
+{
|
|
+ return -ENXIO;
|
|
+}
|
|
+static inline int acpi_dev_get_property_reference(struct acpi_device *adev,
|
|
+ const char *name, const char *cells_name,
|
|
+ size_t index, struct acpi_reference_args *args)
|
|
+{
|
|
+ return -ENXIO;
|
|
+}
|
|
+
|
|
+static inline int acpi_dev_prop_get(struct acpi_device *adev,
|
|
+ const char *propname,
|
|
+ void **valptr)
|
|
+{
|
|
+ return -ENXIO;
|
|
+}
|
|
+
|
|
+static inline int acpi_dev_prop_read_single(struct acpi_device *adev,
|
|
+ const char *propname,
|
|
+ enum dev_prop_type proptype,
|
|
+ void *val)
|
|
+{
|
|
+ return -ENXIO;
|
|
+}
|
|
+
|
|
+static inline int acpi_dev_prop_read(struct acpi_device *adev,
|
|
+ const char *propname,
|
|
+ enum dev_prop_type proptype,
|
|
+ void *val, size_t nval)
|
|
+{
|
|
+ return -ENXIO;
|
|
+}
|
|
+
|
|
+static inline struct acpi_device *acpi_get_next_child(struct device *dev,
|
|
+ struct acpi_device *child)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
#endif /*_LINUX_ACPI_H*/
|
|
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
|
|
index abcafaa..4f5caa1 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/efi.h b/include/linux/efi.h
|
|
index 0949f9c..0238d61 100644
|
|
--- a/include/linux/efi.h
|
|
+++ b/include/linux/efi.h
|
|
@@ -547,6 +547,9 @@ void efi_native_runtime_setup(void);
|
|
#define SMBIOS_TABLE_GUID \
|
|
EFI_GUID( 0xeb9d2d31, 0x2d88, 0x11d3, 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d )
|
|
|
|
+#define SMBIOS3_TABLE_GUID \
|
|
+ EFI_GUID( 0xf2fd1544, 0x9794, 0x4a2c, 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94 )
|
|
+
|
|
#define SAL_SYSTEM_TABLE_GUID \
|
|
EFI_GUID( 0xeb9d2d32, 0x2d88, 0x11d3, 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d )
|
|
|
|
@@ -810,7 +813,8 @@ extern struct efi {
|
|
unsigned long mps; /* MPS table */
|
|
unsigned long acpi; /* ACPI table (IA64 ext 0.71) */
|
|
unsigned long acpi20; /* ACPI table (ACPI 2.0) */
|
|
- unsigned long smbios; /* SM BIOS table */
|
|
+ unsigned long smbios; /* SMBIOS table (32 bit entry point) */
|
|
+ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */
|
|
unsigned long sal_systab; /* SAL system table */
|
|
unsigned long boot_info; /* boot info table */
|
|
unsigned long hcdp; /* HCDP table */
|
|
diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h
|
|
index 12f146f..00b1b70 100644
|
|
--- a/include/linux/gpio/consumer.h
|
|
+++ b/include/linux/gpio/consumer.h
|
|
@@ -94,6 +94,13 @@ int gpiod_to_irq(const struct gpio_desc *desc);
|
|
struct gpio_desc *gpio_to_desc(unsigned gpio);
|
|
int desc_to_gpio(const struct gpio_desc *desc);
|
|
|
|
+/* Child properties interface */
|
|
+struct fwnode_handle;
|
|
+
|
|
+struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
|
+ const char *propname);
|
|
+struct gpio_desc *devm_get_gpiod_from_child(struct device *dev,
|
|
+ struct fwnode_handle *child);
|
|
#else /* CONFIG_GPIOLIB */
|
|
|
|
static inline struct gpio_desc *__must_check __gpiod_get(struct device *dev,
|
|
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
|
|
index 8b62246..ee2d8c6 100644
|
|
--- a/include/linux/gpio_keys.h
|
|
+++ b/include/linux/gpio_keys.h
|
|
@@ -2,6 +2,7 @@
|
|
#define _GPIO_KEYS_H
|
|
|
|
struct device;
|
|
+struct gpio_desc;
|
|
|
|
/**
|
|
* struct gpio_keys_button - configuration parameters
|
|
@@ -17,6 +18,7 @@ struct device;
|
|
* disable button via sysfs
|
|
* @value: axis value for %EV_ABS
|
|
* @irq: Irq number in case of interrupt keys
|
|
+ * @gpiod: GPIO descriptor
|
|
*/
|
|
struct gpio_keys_button {
|
|
unsigned int code;
|
|
@@ -29,6 +31,7 @@ struct gpio_keys_button {
|
|
bool can_disable;
|
|
int value;
|
|
unsigned int irq;
|
|
+ struct gpio_desc *gpiod;
|
|
};
|
|
|
|
/**
|
|
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 13eed92..dc9cb5f 100644
|
|
--- a/include/linux/irqchip/arm-gic.h
|
|
+++ b/include/linux/irqchip/arm-gic.h
|
|
@@ -55,6 +55,8 @@
|
|
(GICD_INT_DEF_PRI << 8) |\
|
|
GICD_INT_DEF_PRI)
|
|
|
|
+#define GIC_DIST_SOFTINT_NSATT 0x8000
|
|
+
|
|
#define GICH_HCR 0x0
|
|
#define GICH_VTR 0x4
|
|
#define GICH_VMCR 0x8
|
|
diff --git a/include/linux/leds.h b/include/linux/leds.h
|
|
index a57611d..361101f 100644
|
|
--- a/include/linux/leds.h
|
|
+++ b/include/linux/leds.h
|
|
@@ -261,6 +261,7 @@ struct gpio_led {
|
|
unsigned retain_state_suspended : 1;
|
|
unsigned default_state : 2;
|
|
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
|
|
+ struct gpio_desc *gpiod;
|
|
};
|
|
#define LEDS_GPIO_DEFSTATE_OFF 0
|
|
#define LEDS_GPIO_DEFSTATE_ON 1
|
|
@@ -273,7 +274,7 @@ struct gpio_led_platform_data {
|
|
#define GPIO_LED_NO_BLINK_LOW 0 /* No blink GPIO state low */
|
|
#define GPIO_LED_NO_BLINK_HIGH 1 /* No blink GPIO state high */
|
|
#define GPIO_LED_BLINK 2 /* Please, blink */
|
|
- int (*gpio_blink_set)(unsigned gpio, int state,
|
|
+ int (*gpio_blink_set)(struct gpio_desc *desc, int state,
|
|
unsigned long *delay_on,
|
|
unsigned long *delay_off);
|
|
};
|
|
diff --git a/include/linux/of.h b/include/linux/of.h
|
|
index 29f0adc..cf79be1 100644
|
|
--- a/include/linux/of.h
|
|
+++ b/include/linux/of.h
|
|
@@ -23,6 +23,7 @@
|
|
#include <linux/spinlock.h>
|
|
#include <linux/topology.h>
|
|
#include <linux/notifier.h>
|
|
+#include <linux/property.h>
|
|
|
|
#include <asm/byteorder.h>
|
|
#include <asm/errno.h>
|
|
@@ -49,6 +50,7 @@ struct device_node {
|
|
const char *type;
|
|
phandle phandle;
|
|
const char *full_name;
|
|
+ struct fwnode_handle fwnode;
|
|
|
|
struct property *properties;
|
|
struct property *deadprops; /* removed properties */
|
|
@@ -79,6 +81,7 @@ extern struct kobj_type of_node_ktype;
|
|
static inline void of_node_init(struct device_node *node)
|
|
{
|
|
kobject_init(&node->kobj, &of_node_ktype);
|
|
+ node->fwnode.type = FWNODE_OF;
|
|
}
|
|
|
|
/* true when node is initialized */
|
|
@@ -114,6 +117,16 @@ extern struct device_node *of_aliases;
|
|
extern struct device_node *of_stdout;
|
|
extern raw_spinlock_t devtree_lock;
|
|
|
|
+static inline bool is_of_node(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ return fwnode && fwnode->type == FWNODE_OF;
|
|
+}
|
|
+
|
|
+static inline struct device_node *of_node(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ return fwnode ? container_of(fwnode, struct device_node, fwnode) : NULL;
|
|
+}
|
|
+
|
|
static inline bool of_have_populated_dt(void)
|
|
{
|
|
return of_allnodes != NULL;
|
|
@@ -263,6 +276,10 @@ extern int of_property_read_u32_array(const struct device_node *np,
|
|
size_t sz);
|
|
extern int of_property_read_u64(const struct device_node *np,
|
|
const char *propname, u64 *out_value);
|
|
+extern int of_property_read_u64_array(const struct device_node *np,
|
|
+ const char *propname,
|
|
+ u64 *out_values,
|
|
+ size_t sz);
|
|
|
|
extern int of_property_read_string(struct device_node *np,
|
|
const char *propname,
|
|
@@ -355,6 +372,16 @@ bool of_console_check(struct device_node *dn, char *name, int index);
|
|
|
|
#else /* CONFIG_OF */
|
|
|
|
+static inline bool is_of_node(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static inline struct device_node *of_node(struct fwnode_handle *fwnode)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
static inline const char* of_node_full_name(const struct device_node *np)
|
|
{
|
|
return "<no-node>";
|
|
@@ -477,6 +504,13 @@ static inline int of_property_read_u32_array(const struct device_node *np,
|
|
return -ENOSYS;
|
|
}
|
|
|
|
+static inline int of_property_read_u64_array(const struct device_node *np,
|
|
+ const char *propname,
|
|
+ u64 *out_values, size_t sz)
|
|
+{
|
|
+ return -ENOSYS;
|
|
+}
|
|
+
|
|
static inline int of_property_read_string(struct device_node *np,
|
|
const char *propname,
|
|
const char **out_string)
|
|
diff --git a/include/linux/pci.h b/include/linux/pci.h
|
|
index 5be8db4..6afba72 100644
|
|
--- a/include/linux/pci.h
|
|
+++ b/include/linux/pci.h
|
|
@@ -562,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;
|
|
@@ -1325,6 +1316,16 @@ 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 */
|
|
|
|
/*
|
|
@@ -1426,6 +1427,23 @@ 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; }
|
|
@@ -1635,7 +1653,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/include/linux/property.h b/include/linux/property.h
|
|
new file mode 100644
|
|
index 0000000..a6a3d98
|
|
--- /dev/null
|
|
+++ b/include/linux/property.h
|
|
@@ -0,0 +1,143 @@
|
|
+/*
|
|
+ * property.h - Unified device property interface.
|
|
+ *
|
|
+ * Copyright (C) 2014, Intel Corporation
|
|
+ * Authors: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
+ * Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_PROPERTY_H_
|
|
+#define _LINUX_PROPERTY_H_
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+struct device;
|
|
+
|
|
+enum dev_prop_type {
|
|
+ DEV_PROP_U8,
|
|
+ DEV_PROP_U16,
|
|
+ DEV_PROP_U32,
|
|
+ DEV_PROP_U64,
|
|
+ DEV_PROP_STRING,
|
|
+ DEV_PROP_MAX,
|
|
+};
|
|
+
|
|
+bool device_property_present(struct device *dev, const char *propname);
|
|
+int device_property_read_u8_array(struct device *dev, const char *propname,
|
|
+ u8 *val, size_t nval);
|
|
+int device_property_read_u16_array(struct device *dev, const char *propname,
|
|
+ u16 *val, size_t nval);
|
|
+int device_property_read_u32_array(struct device *dev, const char *propname,
|
|
+ u32 *val, size_t nval);
|
|
+int device_property_read_u64_array(struct device *dev, const char *propname,
|
|
+ u64 *val, size_t nval);
|
|
+int device_property_read_string_array(struct device *dev, const char *propname,
|
|
+ const char **val, size_t nval);
|
|
+int device_property_read_string(struct device *dev, const char *propname,
|
|
+ const char **val);
|
|
+
|
|
+enum fwnode_type {
|
|
+ FWNODE_INVALID = 0,
|
|
+ FWNODE_OF,
|
|
+ FWNODE_ACPI,
|
|
+};
|
|
+
|
|
+struct fwnode_handle {
|
|
+ enum fwnode_type type;
|
|
+};
|
|
+
|
|
+bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname);
|
|
+int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u8 *val,
|
|
+ size_t nval);
|
|
+int fwnode_property_read_u16_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u16 *val,
|
|
+ size_t nval);
|
|
+int fwnode_property_read_u32_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u32 *val,
|
|
+ size_t nval);
|
|
+int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u64 *val,
|
|
+ size_t nval);
|
|
+int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
|
|
+ const char *propname, const char **val,
|
|
+ size_t nval);
|
|
+int fwnode_property_read_string(struct fwnode_handle *fwnode,
|
|
+ const char *propname, const char **val);
|
|
+
|
|
+struct fwnode_handle *device_get_next_child_node(struct device *dev,
|
|
+ struct fwnode_handle *child);
|
|
+
|
|
+#define device_for_each_child_node(dev, child) \
|
|
+ for (child = device_get_next_child_node(dev, NULL); child; \
|
|
+ child = device_get_next_child_node(dev, child))
|
|
+
|
|
+void fwnode_handle_put(struct fwnode_handle *fwnode);
|
|
+
|
|
+unsigned int device_get_child_node_count(struct device *dev);
|
|
+
|
|
+static inline bool device_property_read_bool(struct device *dev,
|
|
+ const char *propname)
|
|
+{
|
|
+ return device_property_present(dev, propname);
|
|
+}
|
|
+
|
|
+static inline int device_property_read_u8(struct device *dev,
|
|
+ const char *propname, u8 *val)
|
|
+{
|
|
+ return device_property_read_u8_array(dev, propname, val, 1);
|
|
+}
|
|
+
|
|
+static inline int device_property_read_u16(struct device *dev,
|
|
+ const char *propname, u16 *val)
|
|
+{
|
|
+ return device_property_read_u16_array(dev, propname, val, 1);
|
|
+}
|
|
+
|
|
+static inline int device_property_read_u32(struct device *dev,
|
|
+ const char *propname, u32 *val)
|
|
+{
|
|
+ return device_property_read_u32_array(dev, propname, val, 1);
|
|
+}
|
|
+
|
|
+static inline int device_property_read_u64(struct device *dev,
|
|
+ const char *propname, u64 *val)
|
|
+{
|
|
+ return device_property_read_u64_array(dev, propname, val, 1);
|
|
+}
|
|
+
|
|
+static inline bool fwnode_property_read_bool(struct fwnode_handle *fwnode,
|
|
+ const char *propname)
|
|
+{
|
|
+ return fwnode_property_present(fwnode, propname);
|
|
+}
|
|
+
|
|
+static inline int fwnode_property_read_u8(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u8 *val)
|
|
+{
|
|
+ return fwnode_property_read_u8_array(fwnode, propname, val, 1);
|
|
+}
|
|
+
|
|
+static inline int fwnode_property_read_u16(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u16 *val)
|
|
+{
|
|
+ return fwnode_property_read_u16_array(fwnode, propname, val, 1);
|
|
+}
|
|
+
|
|
+static inline int fwnode_property_read_u32(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u32 *val)
|
|
+{
|
|
+ return fwnode_property_read_u32_array(fwnode, propname, val, 1);
|
|
+}
|
|
+
|
|
+static inline int fwnode_property_read_u64(struct fwnode_handle *fwnode,
|
|
+ const char *propname, u64 *val)
|
|
+{
|
|
+ return fwnode_property_read_u64_array(fwnode, propname, val, 1);
|
|
+}
|
|
+
|
|
+#endif /* _LINUX_PROPERTY_H_ */
|
|
diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c
|
|
index 654c901..48da2c5 100644
|
|
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
|
|
index 0f62326..2a47179 100644
|
|
--- a/net/rfkill/rfkill-gpio.c
|
|
+++ b/net/rfkill/rfkill-gpio.c
|
|
@@ -63,6 +63,15 @@ static const struct rfkill_ops rfkill_gpio_ops = {
|
|
.set_block = rfkill_gpio_set_power,
|
|
};
|
|
|
|
+static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
|
|
+static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false };
|
|
+
|
|
+static const struct acpi_gpio_mapping acpi_rfkill_default_gpios[] = {
|
|
+ { "reset-gpios", &reset_gpios, 1 },
|
|
+ { "shutdown-gpios", &shutdown_gpios, 1 },
|
|
+ { },
|
|
+};
|
|
+
|
|
static int rfkill_gpio_acpi_probe(struct device *dev,
|
|
struct rfkill_gpio_data *rfkill)
|
|
{
|
|
@@ -75,7 +84,8 @@ static int rfkill_gpio_acpi_probe(struct device *dev,
|
|
rfkill->name = dev_name(dev);
|
|
rfkill->type = (unsigned)id->driver_data;
|
|
|
|
- return 0;
|
|
+ return acpi_dev_add_driver_gpios(ACPI_COMPANION(dev),
|
|
+ acpi_rfkill_default_gpios);
|
|
}
|
|
|
|
static int rfkill_gpio_probe(struct platform_device *pdev)
|
|
@@ -102,7 +112,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
|
|
|
|
rfkill->clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
|
- gpio = devm_gpiod_get_index(&pdev->dev, "reset", 0);
|
|
+ gpio = devm_gpiod_get(&pdev->dev, "reset");
|
|
if (!IS_ERR(gpio)) {
|
|
ret = gpiod_direction_output(gpio, 0);
|
|
if (ret)
|
|
@@ -110,7 +120,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
|
|
rfkill->reset_gpio = gpio;
|
|
}
|
|
|
|
- gpio = devm_gpiod_get_index(&pdev->dev, "shutdown", 1);
|
|
+ gpio = devm_gpiod_get(&pdev->dev, "shutdown");
|
|
if (!IS_ERR(gpio)) {
|
|
ret = gpiod_direction_output(gpio, 0);
|
|
if (ret)
|
|
@@ -150,6 +160,8 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
|
|
rfkill_unregister(rfkill->rfkill_dev);
|
|
rfkill_destroy(rfkill->rfkill_dev);
|
|
|
|
+ acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev));
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
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 2935405..196f49e 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>
|
|
@@ -159,7 +161,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
|
|
@@ -168,7 +170,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)
|
|
{
|
|
@@ -245,3 +247,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 3aaca49..f6e9922 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>
|
|
@@ -2427,31 +2429,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;
|
|
const 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) {
|