| commit 0335b5034b998e978bf9343da77246bcbad33981 |
| Author: Mark Salter <msalter@redhat.com> |
| Date: Wed Nov 19 10:32:11 2014 -0500 |
| |
| arm64: explicitly set noncoherent ops for _CCA handling |
| |
| Signed-off-by: Mark Salter <msalter@redhat.com> |
| |
| commit 4a2d43442e20a24b78594d12914706deddc119de |
| 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 b93f804830d9ef6d572dd6be9734108199141b87 |
| 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 2d76cb937a6c0010d1de181d0142f0822df5071d |
| 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 a438ff7be0140738b1224d5ade6345a9febad279 |
| 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 814b22167d35b18fc3de745277a2190ff0841585 |
| 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 b90d000000801b473ed4c69757d3be9e433b6c5e |
| 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 243e5c1dc198958ce862e39d33efc798a47b339a |
| 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 612eea3ae291b28f7ed50ccf50bd1685a2a7f753 |
| 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 3d509a508c6fd70eb0fb2f0e82d08d92cc96568c |
| Author: Mark Salter <msalter@redhat.com> |
| Date: Sat Nov 22 12:08:53 2014 -0500 |
| |
| DO NOT UPSTREAM - arm64: kvm: Change vgic resource size error to info |
| |
| From: Donald Dutile <ddutile@redhat.com> |
| |
| A new check was added to upstream to ensure a full |
| kernel page was allocated to the vgic. The check failed |
| kvm configuration if the condition wasn't met. An arm64 |
| kernel with 64K pagesize and certain early firmware will |
| fail this test. Change error to info & continue configuration |
| for now. |
| |
| Signed-off-by: Mark Salter <msalter@redhat.com> |
| |
| commit 2b5dd4609e947b418afbbeae95da7f34594bc048 |
| Author: Wei Huang <wei@redhat.com> |
| Date: Sat Nov 22 10:38:45 2014 -0500 |
| |
| 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> |
| [combined with subsequent patch to use acpi_disabled] |
| Signed-off-by: Mark Salter <msalter@redhat.com> |
| |
| commit 756fc31666a4a3094727da5274fc0705a05f13a3 |
| Author: Wei Huang <wei@redhat.com> |
| Date: Sat Nov 22 10:18:57 2014 -0500 |
| |
| 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> |
| [combined with subsequent patch to use acpi_disabled] |
| Signed-off-by: Mark Salter <msalter@redhat.com> |
| |
| commit b189108603f6db4a11e0c30050e840e8bb36f098 |
| Author: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
| Date: Tue Nov 18 21:52:34 2014 +0100 |
| |
| arm, arm64: KVM: handle potential incoherency of readonly memslots |
| |
| Upstream posting: |
| http://thread.gmane.org/gmane.comp.emulators.kvm.devel/129475/focus=129477 |
| |
| Readonly memslots are often used to implement emulation of ROMs and |
| NOR flashes, in which case the guest may legally map these regions as |
| uncached. |
| To deal with the incoherency associated with uncached guest mappings, |
| treat all readonly memslots as incoherent, and ensure that pages that |
| belong to regions tagged as such are flushed to DRAM before being passed |
| to the guest. |
| |
| Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
| |
| Acadia backport notes: |
| |
| - we have a few more conflicts here. The main thing is that our |
| kvm_arch_prepare_memory_region() is "empty" at the moment; in upstream |
| Ard introduced performance related, host device related code there, in |
| commit 8eef91239. |
| |
| What we certainly need to grab from commit 8eef91239 is the acquiring of |
| "kvm->mmu_lock" (which is visible in the upstream patch context too), |
| and the filtering against |
| |
| (change != KVM_MR_CREATE && change != KVM_MR_MOVE) |
| |
| near the top of the function (which is not visible in the upstream patch |
| context). (If (change == KVM_MR_DELETE), then the caller has set up |
| "memslot" in such a way that calling stage2_flush_memslot() on it is |
| invalid, and it would actually crash the host -- speaking from |
| experience. :)) |
| |
| - The hunk that seems to matter in practice, in my testing on Mustang, is |
| the "fault_ipa_uncached" assignment one (which affects the the |
| demand-paged case, ie. the coherent_cache_guest_page() function, |
| modified in the previous patch). |
| |
| The kvm_arch_prepare_memory_region() hunk exists for completeness of |
| implementation, and while it could certainly make a difference, I've |
| never seen it make one, in my testing. We should pick it up nonetheless. |
| |
| Signed-off-by: Laszlo Ersek <lersek@redhat.com> |
| |
| commit 8ab19d68b49c2f6a9f0e6226c51bf9b2fe553022 |
| Author: Laszlo Ersek <lersek@redhat.com> |
| Date: Tue Nov 18 21:52:33 2014 +0100 |
| |
| arm, arm64: KVM: allow forced dcache flush on page faults |
| |
| Upstream posting: |
| http://thread.gmane.org/gmane.comp.emulators.kvm.devel/129475/focus=129476 |
| |
| From: Laszlo Ersek <lersek@redhat.com> |
| |
| To allow handling of incoherent memslots in a subsequent patch, this |
| patch adds a paramater 'ipa_uncached' to cache_coherent_guest_page() |
| so that we can instruct it to flush the page's contents to DRAM even |
| if the guest has caching globally enabled. |
| |
| Signed-off-by: Laszlo Ersek <lersek@redhat.com> |
| Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
| |
| Acadia backport notes: |
| - minimal context conflict in user_mem_abort(): upstream passes |
| |
| pgprot_val(mem_type) == pgprot_val(PAGE_S2_DEVICE) |
| |
| as last parameter of stage2_set_pte(), while we do a direct comparison. |
| |
| (See upstream commit 3d08c629, "arm: kvm: STRICT_MM_TYPECHECKS fix for |
| user_mem_abort".) |
| |
| Signed-off-by: Laszlo Ersek <lersek@redhat.com> |
| |
| commit 1ac87393dff5d6fb10edfba84dfff89f57a7224a |
| Author: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
| Date: Tue Nov 18 21:52:32 2014 +0100 |
| |
| kvm: add a memslot flag for incoherent memory regions |
| |
| Upstream posting: |
| http://thread.gmane.org/gmane.comp.emulators.kvm.devel/129475 |
| |
| Memory regions may be incoherent with the caches, typically when the |
| guest has mapped a host system RAM backed memory region as uncached. |
| Add a flag KVM_MEMSLOT_INCOHERENT so that we can tag these memslots |
| and handle them appropriately when mapping them. |
| |
| Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> |
| Signed-off-by: Laszlo Ersek <lersek@redhat.com> |
| |
| commit 2a0a5cbf98c94e2906d9a357a63fbbb153488e1c |
| 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 807e95abb96e7868e1c8e863a92298937a69d437 |
| Author: Mark Salter <msalter@redhat.com> |
| Date: Tue Oct 7 12:54:08 2014 -0400 |
| |
| xgene acpi network - first cut |
| |
| commit bdecd2af5d4234ed50042ab28a21736edcec6d41 |
| 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 42b0d1b64cdf1c8d37e69ff7cff45852f7a16f65 |
| 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 c3f29cda7420da6a721d40d116f369cfc1533d38 |
| 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 4117b39d39f59d2497ceac1091ec54aa3056cb4f |
| 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 d9345c86ad290bb4ca98b37520f94fc8075b2b94 |
| 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 22cb8c44e198b7e3f3299324edbcaa1389016d52 |
| 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 c70e2807a8bacbdfed992b58ca24eb152a778a01 |
| 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 29257e751014d0d43f78bcfecd9a56a603096c95 |
| 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 08ddbc9678ce8465f17acc8e1b1b67442593d1b5 |
| 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 880614f83402ae8b408f33cb252505da0760f3e5 |
| 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 b5923f9dc379c1861a70d8836c7d9976d9521390 |
| 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 5c3c4fe36a05672113646f8fb839e4c91256ef5d |
| 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 8105c03352f32060c765837cbb7d619e075289d9 |
| 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 ce0e2672808ce2805d0aadfd12d94e2dd6be5ab9 |
| 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 e072a051ef3a5d612949dc22ca71e40dbe978ed1 |
| 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 e176d66bf1a8e613e501dae1fc6798e1a42b7062 |
| 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 e30b98eab5645fa42d372cc1be44e22db5f5e9b8 |
| 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 a8762c178234b62dce5e55df8de8528857a47bb7 |
| 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 a9240791b83abd372e4efb77f20444c27a56ebae |
| Author: Mark Salter <msalter@redhat.com> |
| Date: Tue Sep 30 17:19:24 2014 -0400 |
| |
| arm64: avoid need for console= to enable serial console |
| |
| Tell kernel to prefer one of the serial ports on platforms |
| pl011, 8250, or sbsa uarts. console= on command line will |
| override these assumed preferences. 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 6fee3b2bd2ad6349e7bb3082393bf6355e01ce6f |
| 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 0a0eea6d358d51ab381e2945c0e9db2f6cc06157 |
| 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 ff93b53f3ca8748529855d421bf79329086544cd |
| 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 5315cb5a532e900612ac1202507551761e8bd13c |
| 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 640f607f1fa10c7c0cd6025dde8883d02fc9b411 |
| 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 e8afbea7e3e11f37c234770d72725894f92de415 |
| Author: Mark Salter <msalter@redhat.com> |
| Date: Mon Aug 11 13:46:43 2014 -0400 |
| |
| xgene: add support for ACPI-probed serial port |
| |
| commit 3d0ad3e452a81a32842d85dbb88078b74582efb5 |
| 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 a42353df5a2f741e8d2e356c3f053aac8d3eff0e |
| 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 d81f088880b9d763a7006e40dff6bb526c534255 |
| 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 475b6ce1555e1146761b53c550f2ac019311739f |
| 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 cb6ebe40936adc5c3154abbec6f89ccb8a0536b7 |
| 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 b7fc0378e13207a53a3e8466ba6329cfbcaa0526 |
| 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 c5b655e6af273a61e7ec3653deb3550ae4f7abf1 |
| Author: Mark Salter <msalter@redhat.com> |
| Date: Wed Nov 19 10:08:29 2014 -0500 |
| |
| tty/sbsauart: make ttySBSA the active console device |
| |
| The sbsauart driver doesn't register itself as a console |
| until module_initcall time. This allows the virtual console |
| driver to register the active console if no console= is |
| given on the cmdline. This patch allows ttySBSA to take |
| over the active console device role from any existing |
| console device if no console= is given on the cmdline. |
| |
| Signed-off-by: Mark Salter <msalter@redhat.com> |
| |
| commit 6bee52af3d281d91b871f6876138fa51a41f0472 |
| 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 a66f13e7c17cf7283b9987da2349c0a5c204fa4b |
| 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 b00f36975b651c0afbddee49e84109694e610f43 |
| 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 4f6ca522fc13d8c13c844a2c2f9eafe091a336a9 |
| 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 91eaa3c5387ebcf698b070a4c21e39e5240699ba |
| 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 ef17919599275fd58edb255ecf69807653cdd763 |
| 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 7d78ff059af78cc5a80576314c38584834557fc1 |
| 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 8a387ea5071c9d8bdaf5305320130022ec1d4f7d |
| 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 7523c8b3b0d23629781c4581272c0647fa543af5 |
| 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 743691994f2fb03bc05f539a42ba6ccccb5d18b8 |
| 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 0c4d9983cb7b50351aad0aa32e8b6134adaabb0d |
| 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 cb6b980abe388e25e357f73c5780aa1cf1b2e337 |
| 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 74a72e003ae388f460294a0910a536aced8ce93c |
| 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 059facbd0f0664aaaf6d732ce02b2f56ea6ad98f |
| 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 7ec63267bb1630c62e5f7fd2eb75a9a31131c89a |
| 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 2f1dd91e7866f2b617af29e9973b88b2cc2e00d6 |
| 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 6941a3cced9ca3d29e6e9c8b0f43917526b74789 |
| 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 8764d6bd07827a2a29eaaa382fc397527ad1ba19 |
| 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 37144fd61ca68e9d542e875187739c982c8d3662 |
| 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 a4f035d2876b41b6f224321da6b6278de577d4c5 |
| 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 b3055f38d52f5be0103b436a1b04fbf3947aaa39 |
| 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 fff62dfc1d7ab6ad3d528b008413fd116d208150 |
| 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 997a0488adb3f525aedb678b584f2733f43e248e |
| 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 5b823d4bf52286f97dc76683b533ae5c08763897 |
| 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 f866806e1ca75a0efc62cda59559286faa7c9926 |
| 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 a68d368ceb495cabafe82cdd0b83ec1e271a7f9d |
| 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 246a31b3a8f065e831a6d8d9fd96f3a8e17dbdb9 |
| 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 ecd0f8e21514e8ac758ed16a188faaa4c5ef75c7 |
| 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 2899d6ea451eb0037427bbf430069f73cb76becc |
| 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 67b1122c25f0c7a9a126ebabcd2085ef99640c13 |
| 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 6a3c07e9aa03b7fbec14ab8bc21fce8590f12d83 |
| 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 7c0569a739d0f1dd8ffd709cf37c53bb42143687 |
| 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 7acf53bfb80c2823d233c14d33cfd2d119713b89 |
| 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 1971c19fb2a38a1d94cedada404760a917c36cd4 |
| 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 7652016ff98e614ed2e3abac19b996af02434293 |
| 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 2586f232bd388d2223f3c7bcba446b2bd25d8e3d |
| 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 16b52b24f3d7a4f1555e4233398172b32306c1e4 |
| 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 016f4b4f5cee9ddd8c243a36c220a65bdfb82dc8 |
| 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 ac627ea950e853f0a3f91607fb16cb9477f434d7 |
| 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 850ba08a3f4756a8340edadd4fdeccd881813ba5 |
| 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 2787807ca4f5f7df82a5c54312753b157e8c052e |
| 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 7bd0585d9ab62d9787c389d3b62141b76319e5f7 |
| 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 5a0edb2dbdf9327322ae57e8e16d162c2a371318 |
| 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/include/asm/kvm_mmu.h | 5 +- |
| arch/arm/kvm/mmu.c | 22 +- |
| 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/kvm_mmu.h | 5 +- |
| 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 | 51 +- |
| 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 | 112 ++++ |
| 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 | 69 +- |
| 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 | 167 +++++ |
| drivers/pnp/resource.c | 2 + |
| drivers/tty/Kconfig | 6 + |
| drivers/tty/Makefile | 1 + |
| drivers/tty/sbsauart.c | 358 +++++++++++ |
| 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/kvm_host.h | 1 + |
| 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 | 107 ++-- |
| virt/kvm/arm/vgic-v2.c | 86 ++- |
| virt/kvm/arm/vgic-v3.c | 8 +- |
| virt/kvm/arm/vgic.c | 30 +- |
| 125 files changed, 6843 insertions(+), 1117 deletions(-) |
| |
| diff --git a/Documentation/acpi/gpio-properties.txt b/Documentation/acpi/gpio-properties.txt |
| new file mode 100644 |
| index 0000000..ae36fcf |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h |
| index acb0d57..f867060 100644 |
| |
| |
| @@ -161,9 +161,10 @@ static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu) |
| } |
| |
| static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva, |
| - unsigned long size) |
| + unsigned long size, |
| + bool ipa_uncached) |
| { |
| - if (!vcpu_has_cache_enabled(vcpu)) |
| + if (!vcpu_has_cache_enabled(vcpu) || ipa_uncached) |
| kvm_flush_dcache_to_poc((void *)hva, size); |
| |
| /* |
| diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c |
| index 8664ff1..8fa2060 100644 |
| |
| |
| @@ -853,6 +853,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, |
| struct vm_area_struct *vma; |
| pfn_t pfn; |
| pgprot_t mem_type = PAGE_S2; |
| + bool fault_ipa_uncached; |
| |
| write_fault = kvm_is_write_fault(vcpu); |
| if (fault_status == FSC_PERM && !write_fault) { |
| @@ -919,6 +920,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, |
| if (!hugetlb && !force_pte) |
| hugetlb = transparent_hugepage_adjust(&pfn, &fault_ipa); |
| |
| + fault_ipa_uncached = memslot->flags & KVM_MEMSLOT_INCOHERENT; |
| + |
| if (hugetlb) { |
| pmd_t new_pmd = pfn_pmd(pfn, mem_type); |
| new_pmd = pmd_mkhuge(new_pmd); |
| @@ -926,7 +929,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, |
| kvm_set_s2pmd_writable(&new_pmd); |
| kvm_set_pfn_dirty(pfn); |
| } |
| - coherent_cache_guest_page(vcpu, hva & PMD_MASK, PMD_SIZE); |
| + coherent_cache_guest_page(vcpu, hva & PMD_MASK, PMD_SIZE, |
| + fault_ipa_uncached); |
| ret = stage2_set_pmd_huge(kvm, memcache, fault_ipa, &new_pmd); |
| } else { |
| pte_t new_pte = pfn_pte(pfn, mem_type); |
| @@ -934,7 +938,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, |
| kvm_set_s2pte_writable(&new_pte); |
| kvm_set_pfn_dirty(pfn); |
| } |
| - coherent_cache_guest_page(vcpu, hva, PAGE_SIZE); |
| + coherent_cache_guest_page(vcpu, hva, PAGE_SIZE, |
| + fault_ipa_uncached); |
| ret = stage2_set_pte(kvm, memcache, fault_ipa, &new_pte, |
| pgprot_val(mem_type) == pgprot_val(PAGE_S2_DEVICE)); |
| } |
| @@ -1245,6 +1250,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, |
| (KVM_PHYS_SIZE >> PAGE_SHIFT)) |
| return -EFAULT; |
| |
| + spin_lock(&kvm->mmu_lock); |
| + stage2_flush_memslot(kvm, memslot); |
| + spin_unlock(&kvm->mmu_lock); |
| + |
| /* |
| * A memory region could potentially cover multiple VMAs, and any holes |
| * between them, so iterate over all of them to find out if we can map |
| @@ -1310,6 +1319,15 @@ void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free, |
| int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, |
| unsigned long npages) |
| { |
| + /* |
| + * Readonly memslots are not incoherent with the caches by definition, |
| + * but in practice, they are used mostly to emulate ROMs or NOR flashes |
| + * that the guest may consider devices and hence map as uncached. |
| + * To prevent incoherency issues in these cases, tag all readonly |
| + * regions as incoherent. |
| + */ |
| + if (slot->flags & KVM_MEM_READONLY) |
| + slot->flags |= KVM_MEMSLOT_INCOHERENT; |
| return 0; |
| } |
| |
| diff --git a/arch/arm/mach-s3c24xx/h1940-bluetooth.c b/arch/arm/mach-s3c24xx/h1940-bluetooth.c |
| index b4d14b8..9c8b127 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 e048f61..e53fc8d 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h |
| index 0caf7a5..123b521 100644 |
| |
| |
| @@ -243,9 +243,10 @@ static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu) |
| } |
| |
| static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva, |
| - unsigned long size) |
| + unsigned long size, |
| + bool ipa_uncached) |
| { |
| - if (!vcpu_has_cache_enabled(vcpu)) |
| + if (!vcpu_has_cache_enabled(vcpu) || ipa_uncached) |
| kvm_flush_dcache_to_poc((void *)hva, size); |
| |
| if (!icache_is_aliasing()) { /* PIPT */ |
| diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h |
| index 872ba93..2f287a6 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 d18a449..8ce9b05 100644 |
| |
| |
| @@ -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 |
| |
| /* |
| * Calculate size of the kernel Image (same for original and copy). |
| diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c |
| index 95c49eb..f9de195 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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..914287d 100644 |
| |
| |
| @@ -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,7 @@ |
| #include <asm/memblock.h> |
| #include <asm/psci.h> |
| #include <asm/efi.h> |
| +#include <asm/acpi.h> |
| |
| unsigned int processor_id; |
| EXPORT_SYMBOL(processor_id); |
| @@ -116,12 +118,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 +318,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 +385,8 @@ void __init setup_arch(char **cmdline_p) |
| |
| early_ioremap_init(); |
| |
| + disable_acpi(); |
| + |
| parse_early_param(); |
| |
| /* |
| @@ -389,19 +398,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(); |
| early_ioremap_reset(); |
| |
| - 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 |
| |
| @@ -506,3 +523,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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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..cf890e3 100644 |
| |
| |
| @@ -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,116 @@ 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. For PCI devices, the _CCA is only a default |
| + * setting. Individual devices on a PCIe bus may set transaction |
| + * ordering and caching attributes individually. Such drivers will |
| + * also be resonsible for using the correct DMA ops for the cache |
| + * conherence used. |
| + * |
| + * PCI devices won't have a handle 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; |
| + struct dma_map_ops *ops; |
| + |
| + status = acpi_check_coherency(ACPI_HANDLE(dev), |
| + &coherent); |
| + if (ACPI_FAILURE(status) || coherent) |
| + ops = &coherent_swiotlb_dma_ops; |
| + else |
| + ops = &noncoherent_swiotlb_dma_ops; |
| + |
| + set_dma_ops(_dev, 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/pci/Makefile b/arch/arm64/pci/Makefile |
| new file mode 100644 |
| index 0000000..7038b51 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 7ba83ff..29aad5e 100644 |
| |
| |
| @@ -663,15 +663,20 @@ 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 in DT\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)) { |
| netdev_err(ndev, "Could not connect to PHY\n"); |
| return -ENODEV; |
| } |
| @@ -681,11 +686,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 +748,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 +766,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 1236696..f66598a 100644 |
| |
| |
| @@ -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; |
| @@ -875,7 +920,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) |
| @@ -936,7 +981,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; |
| @@ -984,6 +1029,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",}, |
| {}, |
| @@ -995,6 +1048,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 f9958fa..0e06cad 100644 |
| |
| |
| @@ -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 6cc3cf6..91c36a2 100644 |
| |
| |
| @@ -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> |
| @@ -2467,6 +2468,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, |
| @@ -2475,6 +2484,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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 2988fe1..9029d59c 100644 |
| |
| |
| @@ -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; |
| diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c |
| index 782e822..d952462 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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..0f44624 |
| |
| |
| @@ -0,0 +1,358 @@ |
| +/* |
| + * 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; |
| + /* if no console= on cmdline, make this the console device */ |
| + if (!console_set_on_cmdline) |
| + qtty->console.flags |= CON_CONSDEV; |
| + 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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/kvm_host.h b/include/linux/kvm_host.h |
| index a6059bd..e4d8f70 100644 |
| |
| |
| @@ -43,6 +43,7 @@ |
| * include/linux/kvm_h. |
| */ |
| #define KVM_MEMSLOT_INVALID (1UL << 16) |
| +#define KVM_MEMSLOT_INCOHERENT (1UL << 17) |
| |
| /* Two fragments for cross MMIO pages. */ |
| #define KVM_MAX_MMIO_FRAGMENTS 2 |
| diff --git a/include/linux/leds.h b/include/linux/leds.h |
| index a57611d..361101f 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 4c8ac5f..ea663d8 100644 |
| |
| |
| @@ -563,15 +563,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; |
| @@ -1326,6 +1317,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 */ |
| |
| /* |
| @@ -1427,6 +1428,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; } |
| @@ -1636,7 +1654,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 |
| |
| |
| @@ -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/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c |
| index 0f62326..2a47179 100644 |
| |
| |
| @@ -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..642dad4 100644 |
| |
| |
| @@ -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,91 @@ 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 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) |
| + 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..510049c 100644 |
| |
| |
| @@ -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) |
| { |
| @@ -222,11 +224,22 @@ int vgic_v2_probe(struct device_node *vgic_node, |
| } |
| |
| if (!PAGE_ALIGNED(resource_size(&vcpu_res))) { |
| +#if 0 |
| kvm_err("GICV size 0x%llx not a multiple of page size 0x%lx\n", |
| (unsigned long long)resource_size(&vcpu_res), |
| PAGE_SIZE); |
| ret = -ENXIO; |
| goto out_unmap; |
| +#else |
| + /* |
| + * The check fails for arm64 with 64K pagesize and certain firmware. |
| + * Ignore for now until firmware takes care of the problem. |
| + */ |
| + kvm_info("GICV size 0x%llx not a multiple of page size 0x%lx\n", |
| + (unsigned long long)resource_size(&vcpu_res), |
| + PAGE_SIZE); |
| + kvm_info("Update DT to assign GICV a multiple of kernel page size \n"); |
| +#endif |
| } |
| |
| vgic->vcpu_base = vcpu_res.start; |
| @@ -245,3 +258,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 |
| |
| |
| @@ -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 aacdb59..d972d63 100644 |
| |
| |
| @@ -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,8 +2429,8 @@ 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, }, |
| {}, |
| }; |
| |
| @@ -2438,20 +2440,26 @@ int kvm_vgic_hyp_init(void) |
| 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) { |
| + /* 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) { |