commit fb3aa1c1644a73bc36a47a6844c3ee6414787e9e Author: Mark Salter Date: Sun Oct 19 11:11:08 2014 -0400 clocksource: arm_arch_timer: fix system hang Arm allows for two possible architectural clock sources. One memory mapped and the other coprocessor based. If both timers exist, then the driver waits for both to be probed before registering a clocksource. Commit c387f07e6205 ("clocksource: arm_arch_timer: Discard unavailable timers correctly") attempted to fix a hang occurring when one of the two possible timers had a device node, but was disabled. In that case, the second probe would never occur and the system would hang without a clocksource being registered. Unfortunately, incorrect logic in that commit made things worse such that a hang would occur unless both timers had a device node and were enabled. This patch fixes the logic so that we don't wait to probe a second timer unless it exists and is enabled. Signed-off-by: Mark Salter commit 798bcf9c5057f0acd54154d09abf6fc4fc044e41 Author: Mark Salter Date: Tue Oct 14 13:51:54 2014 -0400 arm64: [NOT FOR UPSTREAM] 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 commit 4b7a879d4fe95e935a06bd5209026a2278251ce6 Author: Mark Salter 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 commit 0e83bbc67aa2ced7b0f62bc9cdbf8026f3a83ea7 Author: Wei Huang Date: Thu Sep 18 20:02:57 2014 -0400 KVM/ACPI: Enable ACPI support for KVM virt GIC This patches enables ACPI support for KVM virtual GIC. KVM parses ACPI table for virt GIC related information when DT table is not present. This is done by retrieving the information defined in generic_interrupt entry of MADT table. Note: Alexander Spyridakis from Virtual Open System posts a _very_ similar patch to enable acpi-kvm. This patch borrows some ideas from his patch. Signed-off-by: Wei Huang commit b01e346d3156935d05aada065b69dbd4e8e3c38e Author: Wei Huang Date: Thu Sep 18 20:02:56 2014 -0400 KVM/ACPI: Enable ACPI support for virt arch timer This patches enables ACPI support for KVM virtual arch_timer. It allows KVM to parse ACPI table for virt arch_timer PPI when DT table is not present. This is done by retrieving the information from arch_timer_ppi array in arm_arch_timer driver. Signed-off-by: Wei Huang commit 1f7fbdb23b3f616344bc6ab42d133528508b4183 Author: Wei Huang Date: Thu Sep 18 20:02:55 2014 -0400 KVM/ACPI: Add kernel parameter kvmacpi to enable KVM ACPI support This patch addes a new kernel parameter, kvmacpi, to turn on ACPI support for KVM. Users can enable it using "kvmacpi=on" in command line. When it is on, KVM will will parse ACPI tables to configure related components. By default this option is off. Note that DT will be probed first, no matter kvmacpi is ON or OFF. This is because many platforms, such qemu/kvm, still supports DT only. We still want to support Acadia kernel on such platforms. Signed-off-by: Wei Huang commit f1e1479c450a58b7fc68fec33c39b050dcc4b13d Author: Al Stone Date: Thu Sep 18 15:46:38 2014 -0600 DO NOT UPSTREAM -- patch to work around bad MAC of 0x0 Signed-off-by: Al Stone commit b97981d8eaeef88d34b68d6bf6fae3088fce76f6 Author: Tom Lendacky 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 commit dd2c9edb72ff2e36152bae3bdf4f849b155c6178 Author: Mark Salter Date: Tue Oct 7 12:54:08 2014 -0400 xgene acpi network - first cut commit 34f6f9c9a449fce5038a79ef74d85c0cd8aa983d Author: Mika Westerberg Date: Wed Oct 1 04:22:27 2014 +0200 misc: at25: Add ACPI probing support Add support for matching using DT compatible string from ACPI _DSD. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki commit a17b2b5016d76e752f706b5f6b04ffae8ec1728e Author: Mika Westerberg Date: Wed Oct 1 04:21:18 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. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki commit 66442ae93d72f35bf046a6001403fdb89fbcfbdf Author: Mika Westerberg Date: Wed Oct 1 04:20:43 2014 +0200 input: gpio_keys_polled - Add ACPI probing support Allow the driver to probe from ACPI namespace. Signed-off-by: Aaron Lu Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki commit 7f06c354929e2325f200de26e47189a13100647e Author: Aaron Lu Date: Wed Oct 1 04:20:05 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 Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki commit 1fb6d8d9606826cf1dfdac8a75bc5e3f68e4d0de Author: Aaron Lu Date: Wed Oct 1 04:17:46 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 Signed-off-by: Mika Westerberg Acked-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Rafael J. Wysocki commit 8cd8efd79a3f822db3ffc2f30f7646584b69d99f Author: Max Eliaser Date: Wed Oct 1 04:17:02 2014 +0200 leds: leds-gpio: Add ACPI probing support This allows the driver to probe from ACPI namespace. Signed-off-by: Max Eliaser Signed-off-by: Mika Westerberg Acked-by: Bryan Wu Signed-off-by: Rafael J. Wysocki commit 14144259abecb65edd35b9d5e717688800628e27 Author: Max Eliaser Date: Wed Oct 1 04:16:25 2014 +0200 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. Signed-off-by: Max Eliaser Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki commit 998f66211496063e464918fcdb4e40e99fcf2501 Author: Mika Westerberg Date: Wed Oct 1 04:15:41 2014 +0200 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 Acked-by: Alexandre Courbot Acked-by: Bryan Wu Signed-off-by: Rafael J. Wysocki commit ecf8d19a6019f60c567615c46bec6f4501c03c99 Author: Mika Westerberg Date: Wed Oct 1 04:15:01 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 Acked-by: Linus Walleij Signed-off-by: Rafael J. Wysocki commit f87218916f4a9bcd2acdf8db1a4dbf8db5592036 Author: Mika Westerberg Date: Wed Oct 1 04:14:20 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 Signed-off-by: Rafael J. Wysocki commit 2edae8da3e10c8b9d679637693e9d26a0cb9440f Author: Mika Westerberg Date: Wed Oct 1 04:12:41 2014 +0200 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 Acked-by: Linus Walleij Signed-off-by: Rafael J. Wysocki commit 01164a93badc2df11a1f1bfdf7d46e3253a0a6ec Author: Mika Westerberg Date: Wed Oct 1 04:11:20 2014 +0200 ACPI: Document ACPI device specific properties This document describes the data format and interfaces of ACPI device specific properties. Signed-off-by: Mika Westerberg Signed-off-by: Darren Hart Signed-off-by: Rafael J. Wysocki commit 1ea48df9784142befa0cdb6cb79f7fd405a7a103 Author: Mika Westerberg Date: Wed Oct 1 04:10:40 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 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 Signed-off-by: Rafael J. Wysocki commit f15380516841e837f8e20ed18c0039e052bb6187 Author: Rafael J. Wysocki Date: Wed Oct 1 04:10:03 2014 +0200 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. Three general helper functions, device_get_property(), device_read_property() and device_read_property_array() are provided. The first one allows the raw value of a given device property to be accessed. The remaining two allow the value of a numeric or string property and multiple numeric or string values of one array property to be acquired, respectively. Static inline wrappers are also provided for the various property data types that can be passed to device_read_property() or device_read_property_array() for extra type checking. In addition to that, new generic routines are provided for retrieving properties from device description objects in the platform firmware in case a device driver needs/wants to access properties of a child object of a given device object. There are cases in which there is no struct device representation of such child objects and this additional API is useful then. Again, three functions are provided, device_get_child_property(), device_read_child_property(), device_read_child_property_array(), in analogy with device_get_property(), device_read_property() and device_read_property_array() described above, respectively, along with static inline wrappers for all of the propery data types that can be used. For all of them, the first argument is a struct device pointer to the parent device object and the second argument is a (void *) pointer to the child description provided by the platform firmware (either ACPI or FDT). Finally, device_for_each_child_node() is added for iterating over the children of the device description object associated with a given device. The interface covers both ACPI and Device Trees. This change set includes material from Mika Westerberg and Aaron Lu. Signed-off-by: Aaron Lu Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki commit 6b0beaa05b29a299e62293fa4ea4dad0053f31b1 Author: Mika Westerberg Date: Wed Oct 1 04:08:56 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", }, Package () {"name2", }, ... } }) 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 Reviewed-by: Josh Triplett Signed-off-by: Darren Hart Signed-off-by: Rafael J. Wysocki Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki commit ca68950ecf22403e9d0cacdcd86fb4c245de6b9f Author: Mark Salter Date: Tue Sep 30 17:19:24 2014 -0400 arm64: avoid need for console= to enable serial console Tell kernel to prefer one of the serial ports on platforms pl011, 8250, or sbsa uarts. console= on command line will override these assumed preferences. Signed-off-by: Mark Salter commit 6194557254f1ed05638237a64f45f2ec08aa2523 Author: Tom Lendacky 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 commit 0c9b242dd24b3ddf2e87209cb07c4bc7f5da21c4 Author: Tom Lendacky 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 commit 7697f77962035de040c847f4c331eb91dc1c7c91 Author: Graeme Gregory 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 commit 3025ec9f5711f2773be702464bf3150aa914bd66 Author: Graeme Gregory 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 commit a41a9bd6569e079f0572d4b106ffa95d2c16ad06 Author: Mark Salter 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 commit 6e86c887dd05557830a38aa8fd93fab03a409b69 Author: Mark Salter 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 81bcad4ccaf54357a6e3d51a135e07a67ce122ee Author: Mark Salter Date: Mon Aug 11 13:46:43 2014 -0400 xgene: add support for ACPI-probed serial port commit d99f65d46bcc6a28262ff18506b0f2abd10d789e Author: Mark Salter Date: Sat Aug 9 12:01:20 2014 -0400 sata/xgene: support acpi probing Signed-off-by: Mark Salter commit 977b4da81b8b80d02f54f2fc799fb292b22ade12 Author: Mark Salter 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 commit b0be9a425fd397fbed0b720a310e82d5d422f0da Author: Mark Salter Date: Tue Sep 23 12:35:17 2014 -0400 arm64/acpi: make acpi disabled by default Signed-off-by: Mark Salter commit 2dd589faf326c07b981093f73a9c7448ddf4afbf Author: Hanjun Guo 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 [fixed up for 3.17-rc] Signed-off-by: Mark Salter commit 1857c8d68b23fb57f6f6c64d09e3ce183244d399 Author: Al Stone Date: Thu Aug 28 13:14:16 2014 -0400 Fix arm64 compilation error in PNP code Signed-off-by: Mark Salter commit 4529b4dbbaa8732c742300f3abe8db83e08670e8 Author: Suravee Suthikulpanit 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 commit 2bf2a4a102bbe2abf7b2306e89d4d59fb7ade8d2 Author: Graeme Gregory 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 commit 5c62121c6de8268b06d563596953ce71d292733a Author: Mark Salter 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 commit c4f70201c3e20f95c1a96929cf8e524a82dddf81 Author: Graeme Gregory 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 Signed-off-by: Graeme Gregory commit e632bb50e732757dd8ac56f94785b86c300585ba Author: Mark Salter 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 commit 51ba60203fe3cb844f1b56389dc840ecf0264817 Author: Graeme Gregory Date: Fri Sep 12 22:00:16 2014 +0800 Documentation: ACPI for ARM64 Add documentation for the guidelines of how to use ACPI on ARM64. Signed-off-by: Graeme Gregory Signed-off-by: Hanjun Guo commit 49c2936aebfdc0d6fb6f8a2ed85295e4370afac7 Author: Graeme Gregory Date: Fri Sep 12 22:00:15 2014 +0800 ARM64 / ACPI: Enable ARM64 in Kconfig Add Kconfigs to build ACPI on ARM64, and make ACPI available on ARM64. acpi_idle driver is x86/IA64 dependent now, so make CONFIG_ACPI_PROCESSOR depend on X86 || IA64, and implement it on ARM64 in the future. Reviewed-by: Grant Likely Signed-off-by: Graeme Gregory Signed-off-by: Al Stone Signed-off-by: Hanjun Guo commit 4a1bcc17290231af1cfbef33aa6ec77aca609389 Author: Al Stone Date: Fri Sep 12 22:00:14 2014 +0800 ARM64 / ACPI: Select ACPI_REDUCED_HARDWARE_ONLY if ACPI is enabled on ARM64 ACPI reduced hardware mode is disabled by default, but ARM64 can only run properly in ACPI hardware reduced mode, so select ACPI_REDUCED_HARDWARE_ONLY if ACPI is enabled on ARM64. Reviewed-by: Grant Likely Signed-off-by: Al Stone Signed-off-by: Hanjun Guo commit 2863c8dd559eaa85dbec982045ae8201bdd0216e Author: Hanjun Guo Date: Fri Sep 12 22:00:13 2014 +0800 ARM64 / ACPI: Parse GTDT to initialize arch timer Using the information presented by GTDT to initialize the arch timer (not memory-mapped). Originally-by: Amit Daniel Kachhap Signed-off-by: Hanjun Guo commit 75398d7dfd0a9bb67c70568bbc49e79d43681516 Author: Tomasz Nowicki Date: Fri Sep 12 22:00:12 2014 +0800 ARM64 / ACPI: Add GICv2 specific ACPI boot support ACPI kernel uses MADT table for proper GIC initialization. It needs to parse GIC related subtables, collect CPU interface and distributor addresses and call driver initialization function (which is hardware abstraction agnostic). In a similar way, FDT initialize GICv1/2. NOTE: This commit allow to initialize GICv1/2 basic functionality. GICv2 vitalization extension, GICv3/4 and ITS are considered as next steps. Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo commit 325e4412a370d58213bd1cd9de5a5f104dd2bab4 Author: Hanjun Guo Date: Fri Sep 12 22:00:11 2014 +0800 ARM64 / ACPI: Introduce ACPI_IRQ_MODEL_GIC and register device's gsi Introduce ACPI_IRQ_MODEL_GIC which is needed for ARM64 as GIC is used, and then register device's gsi with the core IRQ subsystem. acpi_register_gsi() is similar to DT based irq_of_parse_and_map(), since gsi is unique in the system, so use hwirq number directly for the mapping. Originally-by: Amit Daniel Kachhap Signed-off-by: Hanjun Guo commit 4537275d3b1611bb1102eadfce56af2d36d1c25b Author: Hanjun Guo Date: Fri Sep 12 22:00:10 2014 +0800 ACPI / processor: Make it possible to get CPU hardware ID via GICC Introduce a new function map_gicc_mpidr() to allow MPIDRs to be obtained from the GICC Structure introduced by ACPI 5.1. MPIDR is the CPU hardware ID as local APIC ID on x86 platform, so we use MPIDR not the GIC CPU interface ID to identify CPUs. Signed-off-by: Hanjun Guo commit 5ae8a2bbb06f1833c6494fc7a5f30931cc4b5d9a Author: Hanjun Guo Date: Fri Sep 12 22:00:09 2014 +0800 ARM64 / ACPI: Parse MADT for SMP initialization MADT contains the information for MPIDR which is essential for SMP initialization, parse the GIC cpu interface structures to get the MPIDR value and map it to cpu_logical_map(), and add enabled cpu with valid MPIDR into cpu_possible_map. ACPI 5.1 only has two explicit methods to boot up SMP, PSCI and Parking protocol, but the Parking protocol is only specified for ARMv7 now, so make PSCI as the only way for the SMP boot protocol before some updates for the ACPI spec or the Parking protocol spec. Signed-off-by: Hanjun Guo Signed-off-by: Tomasz Nowicki commit 4a3aed9caf1780e72030a7efc32d28a32d3ed603 Author: Hanjun Guo Date: Fri Sep 12 22:00:08 2014 +0800 ACPI / table: Print GIC information when MADT is parsed When MADT is parsed, print GIC information to make the boot log look pretty. Signed-off-by: Hanjun Guo Signed-off-by: Tomasz Nowicki commit d9f88c9f5385ac2ae7c87ca0ee63e8e77a37ca09 Author: Hanjun Guo Date: Fri Sep 12 22:00:07 2014 +0800 ARM64 / ACPI: Parse FADT table to get PSCI flags for PSCI init There are two flags: PSCI_COMPLIANT and PSCI_USE_HVC. When set, the former signals to the OS that the firmware is PSCI compliant. The latter selects the appropriate conduit for PSCI calls by toggling between Hypervisor Calls (HVC) and Secure Monitor Calls (SMC). FADT table contains such information, parse FADT to get the flags for PSCI init. Since ACPI 5.1 doesn't support self defined PSCI function IDs, which means that only PSCI 0.2+ is supported in ACPI. At the same time, only ACPI 5.1 or higher verison supports PSCI, and FADT Major.Minor version was introduced in ACPI 5.1, so we will check the version and only parse FADT table with version >= 5.1. If firmware provides ACPI tables with ACPI version less than 5.1, OS will be messed up with those information and have no way to init smp and GIC, so disable ACPI if we get an FADT table with version less that 5.1. Signed-off-by: Hanjun Guo Signed-off-by: Graeme Gregory Signed-off-by: Tomasz Nowicki commit f5b32d09a98c2d5353915d2bcc3d7fa3151b6890 Author: Hanjun Guo Date: Fri Sep 12 22:00:06 2014 +0800 ARM64 / ACPI: Make PCI optional for ACPI on ARM64 As PCI for ARM64 is not ready, so introduce some stub functions to make PCI optional for ACPI, and make ACPI core run without CONFIG_PCI on ARM64. Since ACPI on X86 and IA64 depends on PCI and this patch only makes PCI optional for ARM64, it will not break anything on X86 and IA64. Signed-off-by: Hanjun Guo commit 445abae9b9e86d9bd81b3cbffc5d20e8cb375ab0 Author: Graeme Gregory Date: Fri Sep 12 22:00:05 2014 +0800 ARM64 / ACPI: If we chose to boot from acpi then disable FDT If the early boot methods of acpi are happy that we have valid ACPI tables and acpi=off has not been passed. Then do not unflat devicetree effectively disabling further hardware probing from DT. Signed-off-by: Graeme Gregory Signed-off-by: Hanjun Guo commit fad554282770ff7d0a0b002bb2c3d289ad5cc187 Author: Al Stone Date: Fri Sep 12 22:00:04 2014 +0800 ARM64 / ACPI: Introduce early_param for "acpi" Introduce one early parameters "off" for "acpi" to disable ACPI on ARM64. This ensures the kernel uses the DT on a platform that provides both ACPI tables and DT. Signed-off-by: Al Stone Signed-off-by: Graeme Gregory Signed-off-by: Hanjun Guo commit ed545c4a15e82373747e2ca4dd44c313a5cbfdbc Author: Graeme Gregory Date: Fri Sep 12 22:00:03 2014 +0800 ARM64 / ACPI: Introduce sleep-arm.c ACPI 5.1 does not currently support S states for ARM64 hardware but ACPI code will call acpi_target_system_state() for device power managment, so introduce sleep-arm.c to allow other drivers to function until S states are defined. Signed-off-by: Graeme Gregory Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo commit f6b5641dd6b5bf915ee9c18bb0f44f07b858bfc2 Author: Al Stone Date: Fri Sep 12 22:00:02 2014 +0800 ARM64 / ACPI: Get RSDP and ACPI boot-time tables As we want to get ACPI tables to parse and then use the information for system initialization, we should get the RSDP (Root System Description Pointer) first, it then locates Extended Root Description Table (XSDT) which contains all the 64-bit physical address that pointer to other boot-time tables. Introduce acpi.c and its related head file in this patch to provide fundamental needs of extern variables and functions for ACPI core, and then get boot-time tables as needed. - asm/acenv.h for arch specific ACPICA environments and implementation, It is needed unconditionally by ACPI core; - asm/acpi.h for arch specific variables and functions needed by ACPI driver core; - acpi.c for ARM64 related ACPI implementation for ACPI driver core; acpi_boot_table_init() is introduced to get RSDP and boot-time tables, it will be called in setup_arch() before paging_init(), so we should use eary_memremap() mechanism here to get the RSDP and all the table pointers. Signed-off-by: Al Stone Signed-off-by: Graeme Gregory Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo commit 7a5a04722c2527962e4b1ebc979e185a64ed11b4 Author: Tomasz Nowicki Date: Fri Sep 12 22:00:01 2014 +0800 ACPI / table: Count matched and successfully parsed entries without specifying max entries It is very useful to traverse all available table entries without max number of expected entries type. Current acpi_parse_entries() implementation gives that feature but it does not count those entries, it returns 0 instead, so fix it to count matched and successfully entries and return it. NOTE: This change has no impact to x86 and ia64 archs since existing code checks for error occurrence only (acpi_parse_entries(...,0) < 0). Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo commit 80eb7126770fd6b5c595e4733fb3194bda75f25b Author: Ashwin Chaugule Date: Fri Sep 12 22:00:00 2014 +0800 ACPI / table: Add new function to get table entries The acpi_table_parse() function has a callback that passes a pointer to a table_header. Add a new function which takes this pointer and parses its entries. This eliminates the need to re-traverse all the tables for each call. e.g. as in acpi_table_parse_madt() which is normally called after acpi_table_parse(). Signed-off-by: Ashwin Chaugule Signed-off-by: Tomasz Nowicki Signed-off-by: Hanjun Guo commit 36633ceecab0b57354030ba4e796c3eccb0084a8 Author: Hanjun Guo Date: Fri Sep 12 21:59:59 2014 +0800 ARM64: Move the init of cpu_logical_map(0) before unflatten_device_tree() It always make sense to initialize CPU0's logical map entry from the hardware values, so move the initialization of cpu_logical_map(0) before unflatten_device_tree() which is needed by ACPI code later. Signed-off-by: Hanjun Guo commit 1029a72c660016d7b566f9b91156068f4a86ccb7 Author: Mark Salter Date: Tue Jun 24 09:50:28 2014 -0400 arm64: use EFI as last resort for reboot and poweroff Wire in support for EFI reboot and poweroff functions. We use these only if no other mechanism has been registered with arm_pm_reboot and/or pm_power_off respectively. Signed-off-by: Mark Salter commit 0fdae79847bdc64aee1a765c0bb5ee99ed95eb0e Author: Mark Salter 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 commit 6e00476b0a33370988b3fee8551c0a8d98206bd2 Author: Kyle McMartin 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 Signed-off-by: Donald Dutile Documentation/acpi/properties.txt | 410 ++++++++++++ Documentation/arm64/arm-acpi.txt | 218 +++++++ Documentation/kernel-parameters.txt | 3 +- arch/arm64/Kconfig | 6 + arch/arm64/Makefile | 1 + arch/arm64/include/asm/acenv.h | 18 + arch/arm64/include/asm/acpi.h | 99 +++ arch/arm64/include/asm/cpu_ops.h | 1 + arch/arm64/include/asm/elf.h | 3 +- arch/arm64/include/asm/psci.h | 3 +- arch/arm64/include/asm/smp.h | 10 +- arch/arm64/kernel/Makefile | 4 +- arch/arm64/kernel/acpi.c | 397 ++++++++++++ arch/arm64/kernel/cpu_ops.c | 8 +- arch/arm64/kernel/efi.c | 11 + arch/arm64/kernel/process.c | 6 + arch/arm64/kernel/psci.c | 78 ++- arch/arm64/kernel/setup.c | 64 +- arch/arm64/kernel/smp.c | 2 +- arch/arm64/kernel/smp_parking_protocol.c | 110 ++++ arch/arm64/kernel/time.c | 7 + arch/arm64/mm/dma-mapping.c | 103 +++ arch/arm64/pci/Makefile | 1 + arch/arm64/pci/pci.c | 28 + 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 | 586 +++++++++++++++++ drivers/acpi/scan.c | 93 ++- 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 | 235 +++++++ drivers/clocksource/arm_arch_timer.c | 131 +++- drivers/gpio/devres.c | 34 + drivers/gpio/gpio-sch.c | 293 ++++----- drivers/gpio/gpiolib-acpi.c | 78 ++- drivers/gpio/gpiolib.c | 86 ++- drivers/gpio/gpiolib.h | 7 +- drivers/input/keyboard/gpio_keys_polled.c | 163 ++--- drivers/irqchip/irq-gic-v3.c | 10 + drivers/irqchip/irq-gic.c | 116 ++++ drivers/irqchip/irqchip.c | 3 + drivers/leds/leds-gpio.c | 185 +++--- drivers/misc/eeprom/at25.c | 41 +- 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 | 294 +++++++-- drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 20 +- drivers/net/ethernet/amd/xgbe/xgbe-ptp.c | 4 +- drivers/net/ethernet/amd/xgbe/xgbe.h | 13 + drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 77 ++- drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 68 +- drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 1 + drivers/net/ethernet/smsc/smc91x.c | 10 + drivers/net/phy/Kconfig | 2 +- drivers/net/phy/amd-xgbe-phy.c | 777 ++++++++++++----------- drivers/of/base.c | 186 ++++++ drivers/pnp/resource.c | 2 + drivers/tty/Kconfig | 6 + drivers/tty/Makefile | 1 + drivers/tty/sbsauart.c | 355 +++++++++++ drivers/tty/serial/8250/8250_dw.c | 9 + drivers/virtio/virtio_mmio.c | 12 +- include/acpi/acpi_bus.h | 10 + include/acpi/acpi_io.h | 6 + include/kvm/arm_vgic.h | 20 +- include/linux/acpi.h | 95 ++- include/linux/clocksource.h | 6 + include/linux/gpio/consumer.h | 5 + include/linux/gpio_keys.h | 3 + include/linux/irqchip/arm-gic-acpi.h | 31 + include/linux/irqchip/arm-gic.h | 2 + include/linux/leds.h | 1 + include/linux/of.h | 37 ++ include/linux/pci.h | 37 +- include/linux/property.h | 207 ++++++ virt/kvm/arm/arch_timer.c | 108 ++-- virt/kvm/arm/vgic-v2.c | 75 ++- virt/kvm/arm/vgic-v3.c | 8 +- virt/kvm/arm/vgic.c | 32 +- 89 files changed, 5425 insertions(+), 1054 deletions(-) diff --git a/Documentation/acpi/properties.txt b/Documentation/acpi/properties.txt new file mode 100644 index 0000000..13a93c5 --- /dev/null +++ b/Documentation/acpi/properties.txt @@ -0,0 +1,410 @@ +ACPI device properties +====================== +This document describes the format and interfaces of ACPI device +properties as specified in "Device Properties UUID For _DSD" available +here: + +http://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf + +1. Introduction +--------------- +In systems that use ACPI and want to take advantage of device specific +properties, there needs to be a standard way to return and extract +name-value pairs for a given ACPI device. + +An ACPI device that wants to export its properties must implement a +static name called _DSD that takes no arguments and returns a package of +packages: + + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () {"name1", }, + Package () {"name2", } + } + }) + +The UUID identifies contents of the following package. In case of ACPI +device properties it is daffd814-6eba-4d8c-8a91-bc9bbf4aa301. + +In each returned package, the first item is the name and must be a string. +The corresponding value can be a string, integer, reference, or package. If +a package it may only contain strings, integers, and references. + +An example device where we might need properties is a device that uses +GPIOs. In addition to the GpioIo/GpioInt resources the driver needs to +know which GPIO is used for which purpose. + +To solve this we add the following ACPI device properties to the device: + + Device (DEV0) + { + Name (_CRS, ResourceTemplate () { + GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly, + "\\_SB.PCI0.LPC", 0, ResourceConsumer) {0} + GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly, + "\\_SB.PCI0.LPC", 0, ResourceConsumer) {1} + ... + }) + + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () {"reset-gpio", {^DEV0, 0, 0, 0}}, + Package () {"shutdown-gpio", {^DEV0, 1, 0, 0}}, + } + }) + } + +Now the device driver can reference the GPIOs using names instead of +using indexes. + +If there is an existing Device Tree binding for a device, it is expected +that the same bindings are used with ACPI properties, so that the driver +dealing with the device needs only minor modifications if any. + +2. Formal definition of properties +---------------------------------- +The following chapters define the currently supported properties. For +these there exists a helper function that can be used to extract the +property value. + +2.1 Integer types +----------------- +ACPI integers are always 64-bit. However, for drivers the full range is +typically not needed so we provide a set of functions which convert the +64-bit integer to a smaller Linux integer type. + +An integer property looks like this: + + Package () {"i2c-sda-hold-time-ns", 300}, + Package () {"clock-frequency", 400000}, + +To read a property value, use a unified property accessor as shown +below: + + u32 val; + int ret; + + ret = device_property_read_u32(dev, "clock-frequency", &val); + if (ret) + /* Handle error */ + +The function returns 0 if the property is copied to 'val' or negative +errno if something went wrong (or the property does not exist). + +2.2 Integer arrays +------------------ +An integer array is a package holding only integers. Arrays can be used to +represent different things like Linux input key codes to GPIO mappings, pin +control settings, dma request lines, etc. + +An integer array looks like this: + + Package () { + "max8952,dvs-mode-microvolt", + Package () { + 1250000, + 1200000, + 1050000, + 950000, + } + } + +The above array property can be accessed like: + + u32 voltages[4]; + int ret; + + ret = device_property_read_u32_array(dev, "max8952,dvs-mode-microvolt", + voltages, ARRAY_SIZE(voltages)); + if (ret) + /* Handle error */ + + +All functions copy the resulting values cast to a requested type to the +caller supplied array. If you pass NULL in the value pointer ('voltages' in +this case), the function returns number of items in the array. This can be +useful if caller does not know size of the array beforehand. + +2.3 Strings +----------- +String properties can be used to describe many things like labels for GPIO +buttons, compability ids, etc. + +A string property looks like this: + + Package () {"pwm-names", "backlight"}, + Package () {"label", "Status-LED"}, + +You can use device_property_read_string() to extract strings: + + const char *val; + int ret; + + ret = device_property_read_string(dev, "label", &val); + if (ret) + /* Handle error */ + +Note that the function does not copy the returned string but instead the +value is modified to point to the string property itself. + +The memory is owned by the associated ACPI device object and released +when it is removed. The user need not free the associated memory. + +2.4 String arrays +----------------- +String arrays can be useful in describing a list of labels, names for +DMA channels, etc. + +A string array property looks like this: + + Package () {"dma-names", Package () {"tx", "rx", "rx-tx"}}, + Package () {"clock-output-names", Package () {"pll", "pll-switched"}}, + +And these can be read in similar way that the integer arrrays: + + const char *dma_names[3]; + int ret; + + ret = device_property_read_string_array(dev, "dma-names", dma_names, + ARRAY_SIZE(dma_names)); + if (ret) + /* Handle error */ + +The memory management rules follow what is specified for single strings. +Specifically the returned pointers should be treated as constant and not to +be freed. That is done automatically when the correspondig ACPI device +object is released. + +2.5 Object references +--------------------- +An ACPI object reference is used to refer to some object in the +namespace. For example, if a device has dependencies with some other +object, an object reference can be used. + +An object reference looks like this: + + Package () {"dev0", \_SB.DEV0}, + +At the time of writing this, there is no unified device_property_* accessor +for references so one needs to use the following ACPI helper function: + + int acpi_dev_get_property_reference(struct acpi_device *adev, + const char *name, + const char *size_prop, int index, + struct acpi_reference_args *args); + +The referenced ACPI device is returned in args->adev if found. + +In addition to simple object references it is also possible to have object +references with arguments. These are represented in ASL as follows: + + Device (\_SB.PCI0.PWM) + { + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () {"#pwm-cells", 2} + } + }) + } + + Device (\_SB.PCI0.BL) + { + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () { + "pwms", + Package () { + \_SB.PCI0.PWM, 0, 5000000, + \_SB.PCI0.PWM, 1, 4500000, + } + } + } + }) + } + +In the above example, the referenced device declares a property that +returns the number of expected arguments (here it is "#pwm-cells"). If +no such property is given we assume that all the integers following the +reference are arguments. + +In the above example PWM device expects 2 additional arguments. This +will be validated by the ACPI property core. + +The additional arguments must be integers. Nothing else is supported. + +It is possible, as in the above example, to have multiple references +with varying number of integer arguments. It is up to the referenced +device to declare how many arguments it expects. The 'index' parameter +selects which reference is returned. + +One can use acpi_dev_get_property_reference() as well to extract the +information in additional parameters: + + struct acpi_reference_args args; + struct acpi_device *adev = /* this will point to the BL device */ + int ret; + + /* extract the first reference */ + acpi_dev_get_property_reference(adev, "pwms", "#pwm-cells", 0, &args); + + BUG_ON(args.nargs != 2); + BUG_ON(args.args[0] != 0); + BUG_ON(args.args[1] != 5000000); + + /* extract the second reference */ + acpi_dev_get_property_reference(adev, "pwms", "#pwm-cells", 1, &args); + + BUG_ON(args.nargs != 2); + BUG_ON(args.args[0] != 1); + BUG_ON(args.args[1] != 4500000); + +In addition to arguments, args.adev now points to the ACPI device that +corresponds to \_SB.PCI0.PWM. + +It is intended that this function is not used directly but instead +subsystems like pwm implement their ACPI support on top of this function +in such way that it is hidden from the client drivers, such as via +pwm_get(). + +3. Device property hierarchies +------------------------------ +Devices are organized in a tree within the Linux kernel. It follows that +the configuration data would also be hierarchical. In order to reach +equivalence with Device Tree, the ACPI mechanism must also provide some +sort of tree-like representation. Fortunately, the ACPI namespace is +already such a structure. + +For example, we could have the following device in ACPI namespace. The +KEYS device is much like gpio_keys_polled.c in that it includes "pseudo" +devices for each GPIO: + + Device (KEYS) + { + Name (_CRS, ResourceTemplate () { + GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly, + "\\_SB.PCI0.LPC", 0, ResourceConsumer) {0} + GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly, + "\\_SB.PCI0.LPC", 0, ResourceConsumer) {1} + ... + }) + + // "pseudo" devices declared under the parent device + Device (BTN0) { + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () {"label", "minnow_btn0"} + Package () {"gpios", Package () {^KEYS, 0, 0, 1}} + } + }) + } + + Device (BTN1) { + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () {"label", "minnow_btn1"} + Package () {"gpios", Package () {^KEYS, 1, 0, 1}} + } + }) + } + } + +We can extract the above in gpio_keys_polled.c like: + + static int gpio_keys_polled_create_button(struct device *dev, void *child, + void *data) + { + struct button_data *bdata = data; + const char *label = NULL; + + /* + * We need to use device_child_ variant here to access + * properties of the child. + */ + device_child_property_read_string(dev, child, "label", &label); + /* and so on */ + } + + static void gpio_keys_polled_probe(struct device *dev) + { + /* Properties for the KEYS device itself */ + device_property_read(dev, ...); + + /* + * Iterate over button devices and extract their + * firmware configuration. + */ + ret = device_for_each_child_node(dev, gpio_keys_polled_create_button, + &bdata); + if (ret) + /* Handle error */ + } + +Note that you still need proper error handling which is omitted in the +above example. + +4. Existing Device Tree enabled drivers +--------------------------------------- +At the time of writing this, there are ~250 existing DT enabled drivers. +Allocating _HID/_CID for each would not be feasible. To make sure that +those drivers can still be used on ACPI systems, we provide an +alternative way to get these matched. + +There is a special _HID "PRP0001" which means that use the DT bindings +for matching this device to a driver. The driver needs to have +.of_match_table filled in even when !CONFIG_OF. + +An example device would be leds that can be controlled via GPIOs. This +is represented as "leds-gpio" device and looks like this in the ACPI +namespace: + + Device (LEDS) + { + Name (_DSD, Package () { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () {"compatible", Package () {"gpio-leds"}}, + } + }) + ... + } + +In order to get the existing drivers/leds/leds-gpio.c bound to this +device, we take advantage of "PRP0001": + + /* Following already exists in the driver */ + static const struct of_device_id of_gpio_leds_match[] = { + { .compatible = "gpio-leds", }, + {}, + }; + MODULE_DEVICE_TABLE(of, of_gpio_leds_match); + + /* This we add to the driver to get it probed */ + static const struct acpi_device_id acpi_gpio_leds_match[] = { + { "PRP0001" }, /* Device Tree shoehorned into ACPI */ + {}, + }; + MODULE_DEVICE_TABLE(acpi, acpi_gpio_leds_match); + + static struct platform_driver gpio_led_driver = { + .driver = { + /* + * No of_match_ptr() here because we want this + * table to be visible even when !CONFIG_OF to + * match against "compatible" in _DSD. + */ + .of_match_table = of_gpio_leds_match, + .acpi_match_table = acpi_gpio_leds_match, + }, + }; + +Once ACPI core sees "PRP0001" and that the device has "compatible" +property it will do the match using .of_match_table instead. + +It is preferred that new devices get a proper _HID allocated for them +instead of inventing new DT "compatible" devices. diff --git a/Documentation/arm64/arm-acpi.txt b/Documentation/arm64/arm-acpi.txt new file mode 100644 index 0000000..b7dc826 --- /dev/null +++ b/Documentation/arm64/arm-acpi.txt @@ -0,0 +1,218 @@ +ACPI on ARMv8 Servers +--------------------- + +ACPI can be used for ARMv8 general purpose servers designed to follow +the SBSA specification (currently available to people with an ARM login at +http://silver.arm.com). + +The kernel will implement minimum ACPI version is 5.1 + errata as released by +the UEFI Forum, which is available at . + +If the machine does not meet the requirements of the SBSA, or cannot be +described in the required ACPI specifications then it is likely that Device Tree +(DT) is more suitable for the hardware. + +Relationship with Device Tree +----------------------------- + +ACPI support in drivers and subsystems for ARMv8 should never be mutually +exclusive with DT support at compile time. + +At boot time the kernel will only use one description method depending on +parameters passed from the bootloader (including kernel bootargs). + +Regardless of whether DT or ACPI is used, the kernel must always be capable +of booting with either scheme (in kernels with both schemes enabled at compile +time). + +When booting using ACPI tables the /chosen node in DT will still be parsed +to extract the kernel command line and initrd path. No other section of +the DT will be used. + +Booting using ACPI tables +------------------------- + +Currently, the only defined method to pass ACPI tables to the kernel on ARMv8 +is via the UEFI system configuration table. + +The UEFI implementation MUST set the ACPI_20_TABLE_GUID to point to the +RSDP table (the table with the ACPI signature "RSD PTR "). + +The pointer to the RSDP table will be retrieved from EFI by the ACPI core. + +Processing of ACPI tables may be disabled by passing acpi=off on the kernel +command line. + +DO use an XSDT; RSDTs are deprecated and should not be used on arm64. They +only allow for 32-bit addresses. + +DO NOT use the 32-bit address fields in the FADT; they are deprecated. The +64-bit alternatives MUST be used. + +The minimum set of tables MUST include RSDP, XSDT, FACS, FADT, DSDT, MADT +and GTDT. If PCI is used the MCFG table MUST also be present. + +ACPI Detection +-------------- + +Drivers should determine their probe() type by checking for ACPI_HANDLE, +or .of_node, or other information in the device structure. This is +detailed further in the "Driver Recommendations" section. + +In non-driver code If the presence of ACPI needs to be detected at runtime, +then check the value of acpi_disabled. If CONFIG_ACPI is not set, +acpi_disabled will always be 1. + +Device Enumeration +------------------ + +Device descriptions in ACPI should use standard recognized ACPI interfaces. +These are far simpler than the information provided via Device Tree. Drivers +should take into account this simplicity and work with sensible defaults. + +On no account should a Device Tree attempt to be replicated in ASL using such +constructs as Name(KEY0, "Value1") type constructs. Additional driver specific +data should be represented with the appropriate _DSD (ACPI Section 6.2.5) +structure. _DSM (ACPI Section 9.14.1) should only be used if _DSD cannot +represent the data required. + +This data should be rare and not OS specific. For x86 ACPI has taken to +identifying itself as Windows because it was found that only one path was +routinely tested. For ARMv8 it would be preferable to have only one well +tested path. + +_DSD covers more than the generic server case and care should be taken not to +replicate highly specific embedded behaviour from DT into generic servers. + +Common _DSD bindings should be submitted to ASWG to be included in the +document :- + +http://www.uefi.org/sites/default/files/resources/_DSD-implementation-guide-toplevel.htm + +If these bindings are mirrored from DT care should be taken to ensure they are +reviewed as DT bindings before submission to limit divergance in bindings. + +Programmable Power Control Resources +------------------------------------ + +Programmable power control resources include such resources as voltage/current +providers (regulators) and clock sources. + +For power control of these resources they should be represented with Power +Resource Objects (ACPI Section 7.1). The ACPI core will then handle correctly +enabling/disabling of resources as they are needed. + +The ACPI 5.1 specification does not contain any standard binding for these +objects to enable programmable levels or rates so this should be avoided if +possible and the resources set to appropriate levels by the firmware. If this is +not possible then any manipulation should be abstracted in ASL. + +Each device in ACPI has D-states and these can be controlled through +the optional methods _PS0..._PS3 where _PS0 is full on and _PS3 is full off. + +If either _PS0 or _PS3 is implemented, then the other method must also be +implemented. + +If a device requires usage or setup of a power resource when on, the ASL +should organize that it is allocated/enabled using the _PS0 method. + +Resources allocated/enabled in the _PS0 method should be disabled/de-allocated +in the _PS3 method. + +Such code in _PS? methods will of course be very platform specific but +should allow the driver to operate the device without special non-standard +values being read from ASL. Further, abstracting the use of these resources +allows hardware revisions without requiring updates to the kernel. + +Clocks +------ + +Like clocks that are part of the power resources there is no standard way +to represent a clock tree in ACPI 5.1 in a similar manner to how it is +described in DT. + +Devices affected by this include things like UARTs, SoC driven LCD displays, +etc. + +The firmware (for example, UEFI) should initialize these clocks to fixed working +values before the kernel is executed. + +Driver Recommendations +---------------------- + +DO NOT remove any FDT handling when adding ACPI support for a driver. Different +systems may use the same device. + +DO try and keep complex sections of ACPI and DT functionality separate. This +may mean a patch to break out some complex DT to another function before +the patch to add ACPI. This may happen in other functions but is most likely +in probe function. This gives a clearer flow of data for reviewing driver +source. + +probe() :- + +static int device_probe_dt(struct platform_device *pdev) +{ + /* DT specific functionality */ + ... +} + +static int device_probe_acpi(struct platform_device *pdev) +{ + /* ACPI specific functionality */ + ... +} + +static int device_probe(stuct platform_device *pdev) +{ + ... + struct device_node node = pdev->dev.of_node; + ... + + if (node) + ret = device_probe_dt(pdev); + else if (ACPI_HANDLE(&pdev->dev)) + ret = device_probe_acpi(pdev); + else + /* other initialization */ + ... + /* Continue with any generic probe operations */ + ... +} + +DO keep the MODULE_DEVICE_TABLE entries together in the driver to make it clear +the different names the driver is probed for, both from DT and from ACPI. + +module device tables :- + +static struct of_device_id virtio_mmio_match[] = { + { .compatible = "virtio,mmio", }, + { } +}; +MODULE_DEVICE_TABLE(of, virtio_mmio_match); + +static const struct acpi_device_id virtio_mmio_acpi_match[] = { + { "LNRO0005", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match); + +ASWG +---- + +The following areas are not yet well defined for ARM in the current ACPI +specification and are expected to be worked through in the UEFI ACPI +Specification Working Group (ASWG) . +Participation in this group is open to all UEFI members. + + - ACPI based CPU topology + - ACPI based Power management + - CPU idle control based on PSCI + - CPU performance control (CPPC) + - ACPI based SMMU + - ITS support for GIC in MADT + +No code shall be accepted into the kernel unless it complies with the released +standards from UEFI ASWG. If there are features missing from ACPI to make it +function on a platform, ECRs should be submitted to ASWG and go through the +approval process. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7dbe5ec..bb69b63 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -165,7 +165,7 @@ multipliers 'Kilo', 'Mega', and 'Giga', equalling 2^10, 2^20, and 2^30 bytes respectively. Such letter suffixes can also be entirely omitted. - acpi= [HW,ACPI,X86] + acpi= [HW,ACPI,X86,ARM] Advanced Configuration and Power Interface Format: { force | off | strict | noirq | rsdt } force -- enable ACPI if default was off @@ -175,6 +175,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. strictly ACPI specification compliant. rsdt -- prefer RSDT over (default) XSDT copy_dsdt -- copy DSDT to memory + For ARM64, ONLY "acpi=off" is available. See also Documentation/power/runtime_pm.txt, pci=noacpi diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index ac9afde..14423f3 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1,5 +1,6 @@ config ARM64 def_bool y + select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ARCH_BINFMT_ELF_RANDOMIZE_PIE select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_SG_CHAIN @@ -267,6 +268,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 @@ -453,6 +457,8 @@ source "drivers/Kconfig" source "drivers/firmware/Kconfig" +source "drivers/acpi/Kconfig" + source "fs/Kconfig" source "arch/arm64/kvm/Kconfig" diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 20901ff..983d72a 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -49,6 +49,7 @@ core-$(CONFIG_NET) += arch/arm64/net/ core-$(CONFIG_KVM) += arch/arm64/kvm/ core-$(CONFIG_XEN) += arch/arm64/xen/ core-$(CONFIG_CRYPTO) += arch/arm64/crypto/ +drivers-$(CONFIG_PCI) += arch/arm64/pci/ libs-y := arch/arm64/lib/ $(libs-y) libs-y += $(LIBGCC) libs-$(CONFIG_EFI_STUB) += drivers/firmware/efi/libstub/ diff --git a/arch/arm64/include/asm/acenv.h b/arch/arm64/include/asm/acenv.h new file mode 100644 index 0000000..b49166f --- /dev/null +++ b/arch/arm64/include/asm/acenv.h @@ -0,0 +1,18 @@ +/* + * ARM64 specific ACPICA environments and implementation + * + * Copyright (C) 2014, Linaro Ltd. + * Author: Hanjun Guo + * Author: Graeme Gregory + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ASM_ACENV_H +#define _ASM_ACENV_H + +/* It is required unconditionally by ACPI core, update it when needed. */ + +#endif /* _ASM_ACENV_H */ diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h new file mode 100644 index 0000000..7f6cd91 --- /dev/null +++ b/arch/arm64/include/asm/acpi.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2013-2014, Linaro Ltd. + * Author: Al Stone + * Author: Graeme Gregory + * Author: Hanjun Guo + * + * 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 + +/* Basic configuration for ACPI */ +#ifdef CONFIG_ACPI +#define acpi_strict 1 /* No out-of-spec workarounds on ARM64 */ +extern int acpi_disabled; +extern int acpi_noirq; +extern int acpi_pci_disabled; + +/* 1 to indicate PSCI 0.2+ is implemented */ +static inline bool acpi_psci_present(void) +{ + return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT; +} + +/* 1 to indicate HVC must be used instead of SMC as the PSCI conduit */ +static inline bool acpi_psci_use_hvc(void) +{ + return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_USE_HVC; +} + +static inline void disable_acpi(void) +{ + acpi_disabled = 1; + acpi_pci_disabled = 1; + acpi_noirq = 1; +} + +/* MPIDR value provided in GICC structure is 64 bits, but + * the acpi processor driver use the 32 bits cpu hardware + * ID (apic_id on intel platform) everywhere, it is pretty + * hard to modify the acpi processor driver to accept the + * 64 bits MPIDR value, at the same time, only 32 bits of + * the MPIDR is used in the 64 bits MPIDR, just pack the + * Affx fields into a single 32 bit identifier to accommodate + * the acpi processor drivers. + */ +static inline u32 pack_mpidr_into_32_bits(u64 mpidr) +{ + /* + * Bits [0:7] Aff0; + * Bits [8:15] Aff1; + * Bits [16:23] Aff2; + * Bits [32:39] Aff3; + */ + return (u32) ((mpidr & 0xff00000000) >> 8) | mpidr; +} + +/* + * The ACPI processor driver for ACPI core code needs this macro + * to find out this cpu was already mapped (mapping from CPU hardware + * ID to CPU logical ID) or not. + * + * cpu_logical_map(cpu) is the mapping of MPIDR and the logical cpu, + * and MPIDR is the cpu hardware ID we needed to pack. + */ +#define cpu_physical_id(cpu) pack_mpidr_into_32_bits(cpu_logical_map(cpu)) + +/* + * It's used from ACPI core in kdump to boot UP system with SMP kernel, + * with this check the ACPI core will not override the CPU index + * obtained from GICC with 0 and not print some error message as well. + * Since MADT must provide at least one GICC structure for GIC + * initialization, CPU will be always available in MADT on ARM64. + */ +static inline bool acpi_has_cpu_in_madt(void) +{ + return true; +} + +static inline void arch_fix_phys_package_id(int num, u32 slot) { } +void __init acpi_smp_init_cpus(void); + +extern int acpi_get_cpu_parked_address(int cpu, u64 *addr); + +#else + +static inline bool acpi_psci_present(void) { return false; } +static inline bool acpi_psci_use_hvc(void) { return false; } +static inline void acpi_smp_init_cpus(void) { } +static inline int acpi_get_cpu_parked_address(int cpu, u64 *addr) { return -EOPNOTSUPP; } + +#endif /* CONFIG_ACPI */ + +#endif /*_ASM_ACPI_H*/ diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h index 6f8e2ef..978f567 100644 --- a/arch/arm64/include/asm/cpu_ops.h +++ b/arch/arm64/include/asm/cpu_ops.h @@ -64,6 +64,7 @@ struct cpu_operations { }; extern const struct cpu_operations *cpu_ops[NR_CPUS]; +const struct cpu_operations *cpu_get_ops(const char *name); int __init cpu_read_ops(struct device_node *dn, int cpu); void __init cpu_read_bootcpu_ops(void); diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index 01d3aab..8186df6 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -114,7 +114,8 @@ typedef struct user_fpsimd_state elf_fpregset_t; */ #define elf_check_arch(x) ((x)->e_machine == EM_AARCH64) -#define elf_read_implies_exec(ex,stk) (stk != EXSTACK_DISABLE_X) +#define elf_read_implies_exec(ex,stk) (test_thread_flag(TIF_32BIT) \ + ? (stk == EXSTACK_ENABLE_X) : 0) #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE PAGE_SIZE diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h index e5312ea..2454bc5 100644 --- a/arch/arm64/include/asm/psci.h +++ b/arch/arm64/include/asm/psci.h @@ -14,6 +14,7 @@ #ifndef __ASM_PSCI_H #define __ASM_PSCI_H -int psci_init(void); +int psci_dt_init(void); +int psci_acpi_init(void); #endif /* __ASM_PSCI_H */ diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index 780f82c..3411561 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h @@ -39,9 +39,10 @@ extern void show_ipi_list(struct seq_file *p, int prec); extern void handle_IPI(int ipinr, struct pt_regs *regs); /* - * Setup the set of possible CPUs (via set_cpu_possible) + * Discover the set of possible CPUs and determine their + * SMP operations. */ -extern void smp_init_cpus(void); +extern void of_smp_init_cpus(void); /* * Provide a function to raise an IPI cross call on CPUs in callmap. @@ -51,6 +52,11 @@ extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int)); extern void (*__smp_cross_call)(const struct cpumask *, unsigned int); /* + * Provide a function to signal a parked secondary CPU. + */ +extern void set_smp_boot_wakeup_call(void (*)(int cpu)); + +/* * Called from the secondary holding pen, this is the secondary CPU entry point. */ asmlinkage void secondary_start_kernel(void); diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 5bd029b..f4ba4fe 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -21,7 +21,8 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o -arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o +arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o \ + smp_parking_protocol.o arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o @@ -31,6 +32,7 @@ arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o arm64-obj-$(CONFIG_KGDB) += kgdb.o arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o arm64-obj-$(CONFIG_PCI) += pci.o +arm64-obj-$(CONFIG_ACPI) += acpi.o obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m) diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c new file mode 100644 index 0000000..5486426 --- /dev/null +++ b/arch/arm64/kernel/acpi.c @@ -0,0 +1,397 @@ +/* + * ARM64 Specific Low-Level ACPI Boot Support + * + * Copyright (C) 2013-2014, Linaro Ltd. + * Author: Al Stone + * Author: Graeme Gregory + * Author: Hanjun Guo + * Author: Tomasz Nowicki + * Author: Naresh Bhat + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ARM64_ACPI_DISABLED_DEFAULT 1 + +int acpi_noirq; /* skip ACPI IRQ initialization */ +int acpi_disabled = ARM64_ACPI_DISABLED_DEFAULT; +EXPORT_SYMBOL(acpi_disabled); + +int acpi_pci_disabled; /* skip ACPI PCI scan and IRQ initialization */ +EXPORT_SYMBOL(acpi_pci_disabled); + +static int enabled_cpus; /* Processors (GICC) with enabled flag in MADT */ + +static char *boot_method; +static u64 parked_address[NR_CPUS]; + +/* + * Since we're on ARM, the default interrupt routing model + * clearly has to be GIC. + */ +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_GIC; + +/* + * __acpi_map_table() will be called before page_init(), so early_ioremap() + * or early_memremap() should be called here to for ACPI table mapping. + */ +char *__init __acpi_map_table(unsigned long phys, unsigned long size) +{ + if (!phys || !size) + return NULL; + + return early_memremap(phys, size); +} + +void __init __acpi_unmap_table(char *map, unsigned long size) +{ + if (!map || !size) + return; + + early_memunmap(map, size); +} + +/** + * acpi_map_gic_cpu_interface - generates a logical cpu number + * and map to MPIDR represented by GICC structure + * @mpidr: CPU's hardware id to register, MPIDR represented in MADT + * @enabled: this cpu is enabled or not + * + * Returns the logical cpu number which maps to MPIDR + */ +static int acpi_map_gic_cpu_interface(u64 mpidr, u64 parked_addr, u8 enabled) +{ + int cpu; + + if (mpidr == INVALID_HWID) { + pr_info("Skip invalid cpu hardware ID\n"); + return -EINVAL; + } + + total_cpus++; + if (!enabled) + return -EINVAL; + + if (enabled_cpus >= NR_CPUS) { + pr_warn("NR_CPUS limit of %d reached, Processor %d/0x%llx ignored.\n", + NR_CPUS, total_cpus, mpidr); + return -EINVAL; + } + + /* No need to check duplicate MPIDRs for the first CPU */ + if (enabled_cpus) { + /* + * Duplicate MPIDRs are a recipe for disaster. Scan + * all initialized entries and check for + * duplicates. If any is found just ignore the CPU. + */ + for_each_possible_cpu(cpu) { + if (cpu_logical_map(cpu) == mpidr) { + pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n", + mpidr); + return -EINVAL; + } + } + + /* allocate a logical cpu id for the new comer */ + cpu = cpumask_next_zero(-1, cpu_possible_mask); + } else { + /* First GICC entry must be BSP as ACPI spec said */ + if (cpu_logical_map(0) != mpidr) { + pr_err("First GICC entry with MPIDR 0x%llx is not BSP\n", + mpidr); + return -EINVAL; + } + + /* + * boot_cpu_init() already hold bit 0 in cpu_present_mask + * for BSP, no need to allocate again. + */ + cpu = 0; + } + + parked_address[cpu] = parked_addr; + + /* CPU 0 was already initialized */ + if (cpu) { + cpu_ops[cpu] = cpu_get_ops(boot_method); + if (!cpu_ops[cpu]) + return -EINVAL; + + if (cpu_ops[cpu]->cpu_init(NULL, cpu)) + return -EOPNOTSUPP; + + /* map the logical cpu id to cpu MPIDR */ + cpu_logical_map(cpu) = mpidr; + + set_cpu_possible(cpu, true); + } else { + /* get cpu0's ops, no need to return if ops is null */ + cpu_ops[0] = cpu_get_ops(boot_method); + } + + enabled_cpus++; + return cpu; +} + +static int __init +acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_interrupt *processor; + + processor = (struct acpi_madt_generic_interrupt *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + acpi_table_print_madt_entry(header); + + acpi_map_gic_cpu_interface(processor->arm_mpidr & MPIDR_HWID_BITMASK, + processor->parked_address, processor->flags & ACPI_MADT_ENABLED); + + return 0; +} + +/* Parse GIC cpu interface entries in MADT for SMP init */ +void __init acpi_smp_init_cpus(void) +{ + int count; + + /* + * do a partial walk of MADT to determine how many CPUs + * we have including disabled CPUs, and get information + * we need for SMP init + */ + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, + acpi_parse_gic_cpu_interface, 0); + + if (!count) { + pr_err("No GIC CPU interface entries present\n"); + return; + } else if (count < 0) { + pr_err("Error parsing GIC CPU interface entry\n"); + return; + } + + /* Make boot-up look pretty */ + pr_info("%d CPUs enabled, %d CPUs total\n", enabled_cpus, total_cpus); +} + +int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) +{ + *irq = irq_find_mapping(NULL, gsi); + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); + +/* + * success: return IRQ number (>0) + * failure: return =< 0 + */ +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity) +{ + unsigned int irq; + unsigned int irq_type; + + /* + * ACPI have no bindings to indicate SPI or PPI, so we + * use different mappings from DT in ACPI. + * + * For FDT + * PPI interrupt: in the range [0, 15]; + * SPI interrupt: in the range [0, 987]; + * + * For ACPI, GSI should be unique so using + * the hwirq directly for the mapping: + * PPI interrupt: in the range [16, 31]; + * SPI interrupt: in the range [32, 1019]; + */ + + if (trigger == ACPI_EDGE_SENSITIVE && + polarity == ACPI_ACTIVE_LOW) + irq_type = IRQ_TYPE_EDGE_FALLING; + else if (trigger == ACPI_EDGE_SENSITIVE && + polarity == ACPI_ACTIVE_HIGH) + irq_type = IRQ_TYPE_EDGE_RISING; + else if (trigger == ACPI_LEVEL_SENSITIVE && + polarity == ACPI_ACTIVE_LOW) + irq_type = IRQ_TYPE_LEVEL_LOW; + else if (trigger == ACPI_LEVEL_SENSITIVE && + polarity == ACPI_ACTIVE_HIGH) + irq_type = IRQ_TYPE_LEVEL_HIGH; + else + irq_type = IRQ_TYPE_NONE; + + /* + * Since only one GIC is supported in ACPI 5.0, we can + * create mapping refer to the default domain + */ + irq = irq_create_mapping(NULL, gsi); + if (!irq) + return irq; + + /* Set irq type if specified and different than the current one */ + if (irq_type != IRQ_TYPE_NONE && + irq_type != irq_get_trigger_type(irq)) + irq_set_irq_type(irq, irq_type); + return irq; +} +EXPORT_SYMBOL_GPL(acpi_register_gsi); + +void acpi_unregister_gsi(u32 gsi) +{ +} +EXPORT_SYMBOL_GPL(acpi_unregister_gsi); + +static int __init acpi_parse_fadt(struct acpi_table_header *table) +{ + struct acpi_table_fadt *fadt = (struct acpi_table_fadt *)table; + + /* + * Revision in table header is the FADT Major revision, + * and there is a minor revision of FADT which was introduced + * by ACPI 5.1, we only deal with ACPI 5.1 or higher revision + * to get arm boot flags, or we will disable ACPI. + */ + if (table->revision > 5 || + (table->revision == 5 && fadt->minor_revision >= 1)) { + /* + * ACPI 5.1 only has two explicit methods to boot up SMP, + * PSCI and Parking protocol, but the Parking protocol is + * only specified for ARMv7 now, so make PSCI as the only + * way for the SMP boot protocol before some updates for + * the ACPI spec or the Parking protocol spec. + */ + if (acpi_psci_present()) + boot_method = "psci"; + else if (IS_ENABLED(CONFIG_ARM_PARKING_PROTOCOL)) + boot_method = "parking-protocol"; + + if (!boot_method) + pr_warn("has no boot support, will not bring up secondary CPUs\n"); + return -EOPNOTSUPP; + } + + pr_warn("Unsupported FADT revision %d.%d, should be 5.1+, will disable ACPI\n", + table->revision, fadt->minor_revision); + disable_acpi(); + + return -EINVAL; +} + +/* + * acpi_boot_table_init() called from setup_arch(), always. + * 1. find RSDP and get its address, and then find XSDT + * 2. extract all tables and checksums them all + * 3. check ACPI FADT revisoin + * + * We can parse ACPI boot-time tables such as MADT after + * this function is called. + */ +void __init acpi_boot_table_init(void) +{ + /* If acpi_disabled, bail out */ + if (acpi_disabled) + return; + + /* Initialize the ACPI boot-time table parser. */ + if (acpi_table_init()) { + disable_acpi(); + return; + } + + if (acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt)) + pr_err("Can't find FADT or error happened during parsing FADT\n"); +} + +void __init acpi_gic_init(void) +{ + struct acpi_table_header *table; + acpi_status status; + acpi_size tbl_size; + int err; + + status = acpi_get_table_with_size(ACPI_SIG_MADT, 0, &table, &tbl_size); + if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + + pr_err("Failed to get MADT table, %s\n", msg); + return; + } + + err = gic_v2_acpi_init(table); + if (err) + pr_err("Failed to initialize GIC IRQ controller"); + + early_acpi_os_unmap_memory((char *)table, tbl_size); +} + +/* + * Parked Address in ACPI GIC structure will be used as the CPU + * release address + */ +int acpi_get_cpu_parked_address(int cpu, u64 *addr) +{ + if (!addr || !parked_address[cpu]) + return -EINVAL; + + *addr = parked_address[cpu]; + + return 0; +} + +static int __init parse_acpi(char *arg) +{ + if (!arg) + return -EINVAL; + + /* "acpi=off" disables both ACPI table parsing and interpreter */ + if (strcmp(arg, "off") == 0) + acpi_disabled = 1; + else if (strcmp(arg, "on") == 0) + acpi_disabled = 0; + else + return -EINVAL; /* Core will print when we return error */ + + return 0; +} +early_param("acpi", parse_acpi); + +int acpi_isa_irq_to_gsi(unsigned isa_irq, u32 *gsi) +{ + return -1; +} + +int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base) +{ + /* TBD */ + return -EINVAL; +} +EXPORT_SYMBOL(acpi_register_ioapic); + +int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base) +{ + /* TBD */ + return -EINVAL; +} +EXPORT_SYMBOL(acpi_unregister_ioapic); + diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c index cce9524..1d90f31 100644 --- a/arch/arm64/kernel/cpu_ops.c +++ b/arch/arm64/kernel/cpu_ops.c @@ -23,19 +23,23 @@ #include 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.c b/arch/arm64/kernel/efi.c index 03aaa99..6c4de44 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -479,3 +479,14 @@ err_unmap: return -1; } early_initcall(arm64_enter_virtual_mode); + +/* + * If nothing else is handling pm_power_off, use EFI + * + * This is called from a late_initcall after other mechanisms + * have had a chance to register a handler. + */ +bool efi_poweroff_required(void) +{ + return pm_power_off == NULL; +} diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index c3065db..99edd1f 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -157,6 +158,11 @@ void machine_restart(char *cmd) do_kernel_restart(cmd); /* + * If all else fails, try EFI + */ + efi_reboot(reboot_mode, cmd); + + /* * Whoops - the architecture was unable to reboot. */ printk("Reboot failed -- System halted\n"); diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 866c1c8..f8a981e 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -15,6 +15,7 @@ #define pr_fmt(fmt) "psci: " fmt +#include #include #include #include @@ -24,6 +25,7 @@ #include #include +#include #include #include #include @@ -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..c1144a1 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,10 @@ #include #include #include +#include + +int acadia_kvm_acpi=0; +EXPORT_SYMBOL(acadia_kvm_acpi); unsigned int processor_id; EXPORT_SYMBOL(processor_id); @@ -386,22 +391,34 @@ void __init setup_arch(char **cmdline_p) */ local_async_enable(); + if (acpi_disabled) + disable_acpi(); + efi_init(); arm64_memblock_init(); + /* Parse the ACPI tables for possible boot-time configuration */ + acpi_boot_table_init(); + paging_init(); request_standard_resources(); efi_idmap_init(); - unflatten_device_tree(); - - psci_init(); - cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; - cpu_read_bootcpu_ops(); + if (acpi_disabled) { + unflatten_device_tree(); + psci_dt_init(); + cpu_read_bootcpu_ops(); +#ifdef CONFIG_SMP + of_smp_init_cpus(); +#endif + } else { + psci_acpi_init(); + acpi_smp_init_cpus(); + } + #ifdef CONFIG_SMP - smp_init_cpus(); smp_build_mpidr_hash(); #endif @@ -414,6 +431,19 @@ void __init setup_arch(char **cmdline_p) #endif } +static int __init parse_kvm_acpi(char *arg) +{ + if (!arg) + return -EINVAL; + + if (strcmp(arg, "on") == 0) { + acadia_kvm_acpi = 1; + } + + return 0; +} +early_param("kvmacpi", parse_kvm_acpi); + static int __init arm64_device_init(void) { of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); @@ -506,3 +536,25 @@ const struct seq_operations cpuinfo_op = { .stop = c_stop, .show = c_show }; + +/* + * Temporary hack to avoid need for console= on command line + */ +static int __init arm64_console_setup(void) +{ + /* Allow cmdline to override our assumed preferences */ + if (console_set_on_cmdline) + return 0; + + if (IS_ENABLED(CONFIG_SBSAUART_TTY)) + add_preferred_console("ttySBSA", 0, "115200"); + + if (IS_ENABLED(CONFIG_SERIAL_AMBA_PL011)) + add_preferred_console("ttyAMA", 0, "115200"); + + if (IS_ENABLED(CONFIG_SERIAL_8250)) + add_preferred_console("ttyS", 0, "115200"); + + return 0; +} +early_initcall(arm64_console_setup); diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index b06d1d9..2988829 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -321,7 +321,7 @@ void __init smp_prepare_boot_cpu(void) * cpu logical map array containing MPIDR values related to logical * cpus. Assumes that cpu_logical_map(0) has already been initialized. */ -void __init smp_init_cpus(void) +void __init of_smp_init_cpus(void) { struct device_node *dn = NULL; unsigned int i, cpu = 1; diff --git a/arch/arm64/kernel/smp_parking_protocol.c b/arch/arm64/kernel/smp_parking_protocol.c new file mode 100644 index 0000000..e1153ce --- /dev/null +++ b/arch/arm64/kernel/smp_parking_protocol.c @@ -0,0 +1,110 @@ +/* + * Parking Protocol SMP initialisation + * + * Based largely on spin-table method. + * + * Copyright (C) 2013 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static phys_addr_t cpu_mailbox_addr[NR_CPUS]; + +static void (*__smp_boot_wakeup)(int cpu); + +void set_smp_boot_wakeup_call(void (*fn)(int cpu)) +{ + __smp_boot_wakeup = fn; +} + +static int smp_parking_protocol_cpu_init(struct device_node *dn, + unsigned int cpu) +{ + /* + * Determine the mailbox address. + */ + if (!acpi_get_cpu_parked_address(cpu, &cpu_mailbox_addr[cpu])) { + pr_info("%s: ACPI parked addr=%llx\n", + __func__, cpu_mailbox_addr[cpu]); + return 0; + } + + pr_err("CPU %d: missing or invalid parking protocol mailbox\n", cpu); + + return -1; +} + +static int smp_parking_protocol_cpu_prepare(unsigned int cpu) +{ + return 0; +} + +struct parking_protocol_mailbox { + __le32 cpu_id; + __le32 reserved; + __le64 entry_point; +}; + +static int smp_parking_protocol_cpu_boot(unsigned int cpu) +{ + struct parking_protocol_mailbox __iomem *mailbox; + + if (!cpu_mailbox_addr[cpu] || !__smp_boot_wakeup) + return -ENODEV; + + /* + * The mailbox may or may not be inside the linear mapping. + * As ioremap_cache will either give us a new mapping or reuse the + * existing linear mapping, we can use it to cover both cases. In + * either case the memory will be MT_NORMAL. + */ + mailbox = ioremap_cache(cpu_mailbox_addr[cpu], sizeof(*mailbox)); + if (!mailbox) + return -ENOMEM; + + /* + * We write the entry point and cpu id as LE regardless of the + * native endianess of the kernel. Therefore, any boot-loaders + * that read this address need to convert this address to the + * Boot-Loader's endianess before jumping. + */ + writeq(__pa(secondary_entry), &mailbox->entry_point); + writel(cpu, &mailbox->cpu_id); + __flush_dcache_area(mailbox, sizeof(*mailbox)); + __smp_boot_wakeup(cpu); + + /* temp hack for broken firmware */ + sev(); + + iounmap(mailbox); + + return 0; +} + +const struct cpu_operations smp_parking_protocol_ops = { + .name = "parking-protocol", + .cpu_init = smp_parking_protocol_cpu_init, + .cpu_prepare = smp_parking_protocol_cpu_prepare, + .cpu_boot = smp_parking_protocol_cpu_boot, +}; diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index 1a7125c..42f9195 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -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/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index d920942..fda70ab 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -23,8 +23,14 @@ #include #include #include +#include +#include +#include #include #include +#include +#include +#include #include @@ -423,10 +429,107 @@ out: return -ENOMEM; } +#ifdef CONFIG_PCI +static void arm64_of_set_dma_ops(void *_dev) +{ + struct device *dev = _dev; + + /* + * PCI devices won't have an ACPI handle but the bridge will. + * Search up the device chain until we find an of_node + * to check. + */ + while (dev) { + if (dev->of_node) { + if (of_dma_is_coherent(dev->of_node)) + set_dma_ops(_dev, &coherent_swiotlb_dma_ops); + break; + } + dev = dev->parent; + } +} +#else +static inline arm64_of_set_dma_ops(void *_dev) {} +#endif + + +#ifdef CONFIG_ACPI +static void arm64_acpi_set_dma_ops(void *_dev) +{ + struct device *dev = _dev; + + /* + * Kernel defaults to noncoherent ops but ACPI 5.1 spec says arm64 + * defaults to coherent. Set coherent ops if _CCA not found or _CCA + * found and non-zero. + * + * PCI devices won't have an of_node but the bridge will. + * Search up the device chain until we find an ACPI handle + * to check. + */ + while (dev) { + if (ACPI_HANDLE(dev)) { + acpi_status status; + int coherent; + status = acpi_check_coherency(ACPI_HANDLE(dev), + &coherent); + if (ACPI_FAILURE(status) || coherent) + set_dma_ops(dev, &coherent_swiotlb_dma_ops); + break; + } + dev = dev->parent; + } +} +#else +static inline arm64_acpi_set_dma_ops(void *_dev) {} +#endif + +static int dma_bus_notifier(struct notifier_block *nb, + unsigned long event, void *_dev) +{ + if (event != BUS_NOTIFY_ADD_DEVICE) + return NOTIFY_DONE; + + if (acpi_disabled) + arm64_of_set_dma_ops(_dev); + else + arm64_acpi_set_dma_ops(_dev); + + return NOTIFY_OK; +} + +#ifdef CONFIG_ACPI +static struct notifier_block platform_bus_nb = { + .notifier_call = dma_bus_notifier, +}; + +static struct notifier_block amba_bus_nb = { + .notifier_call = dma_bus_notifier, +}; +#endif + +#ifdef CONFIG_PCI +static struct notifier_block pci_bus_nb = { + .notifier_call = dma_bus_notifier, +}; +#endif + static int __init swiotlb_late_init(void) { size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT); + /* + * These must be registered before of_platform_populate(). + */ +#ifdef CONFIG_ACPI + bus_register_notifier(&platform_bus_type, &platform_bus_nb); + bus_register_notifier(&amba_bustype, &amba_bus_nb); +#endif + +#ifdef CONFIG_PCI + bus_register_notifier(&pci_bus_type, &pci_bus_nb); +#endif + dma_ops = &noncoherent_swiotlb_dma_ops; return swiotlb_late_init_with_default_size(swiotlb_size); diff --git a/arch/arm64/pci/Makefile b/arch/arm64/pci/Makefile new file mode 100644 index 0000000..b8d5dbd --- /dev/null +++ b/arch/arm64/pci/Makefile @@ -0,0 +1 @@ +obj-y += pci.o diff --git a/arch/arm64/pci/pci.c b/arch/arm64/pci/pci.c new file mode 100644 index 0000000..b03b0eb --- /dev/null +++ b/arch/arm64/pci/pci.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +/** + * raw_pci_read - Platform-specific PCI config space access. + * + * Default empty implementation. Replace with an architecture-specific setup + * routine, if necessary. + */ +int __weak raw_pci_read(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) +{ + return -EINVAL; +} + +int __weak raw_pci_write(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) +{ + return -EINVAL; +} + +/* Root bridge scanning */ +struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) +{ + return NULL; +} diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index d0f3265..3343080 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -5,8 +5,7 @@ menuconfig ACPI bool "ACPI (Advanced Configuration and Power Interface) Support" depends on !IA64_HP_SIM - depends on IA64 || X86 - depends on PCI + depends on ((IA64 || X86) && PCI) || ARM64 select PNP default y help @@ -163,6 +162,7 @@ config ACPI_PROCESSOR tristate "Processor" select THERMAL select CPU_IDLE + depends on X86 || IA64 default y help This driver installs ACPI as the idle handler for Linux and uses @@ -263,7 +263,7 @@ config ACPI_DEBUG config ACPI_PCI_SLOT bool "PCI slot detection driver" - depends on SYSFS + depends on SYSFS && PCI default n help This driver creates entries in /sys/bus/pci/slots/ for all PCI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 505d4d7..252d0ff 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -23,7 +23,11 @@ acpi-y += nvs.o # Power management related files acpi-y += wakeup.o +ifeq ($(ARCH), arm64) +acpi-y += sleep-arm.o +else # X86, IA64 acpi-y += sleep.o +endif acpi-y += device_pm.o acpi-$(CONFIG_ACPI_SLEEP) += proc.o @@ -39,13 +43,14 @@ 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 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 diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 8b67bd0..c412fdb 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -448,6 +448,9 @@ static int __init acpi_bus_init_irq(void) case ACPI_IRQ_MODEL_IOSAPIC: message = "IOSAPIC"; break; + case ACPI_IRQ_MODEL_GIC: + message = "GIC"; + break; case ACPI_IRQ_MODEL_PLATFORM: message = "platform specific model"; break; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4c5cf77..926ca5c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -26,8 +26,13 @@ acpi_status acpi_os_initialize1(void); int init_acpi_device_notify(void); int acpi_scan_init(void); +#ifdef CONFIG_PCI void acpi_pci_root_init(void); void acpi_pci_link_init(void); +#else +static inline void acpi_pci_root_init(void) {} +static inline void acpi_pci_link_init(void) {} +#endif void acpi_processor_init(void); void acpi_platform_init(void); void acpi_pnp_init(void); @@ -181,4 +186,10 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev); bool acpi_osi_is_win8(void); #endif +/*-------------------------------------------------------------------------- + Device properties + -------------------------------------------------------------------------- */ +void acpi_init_properties(struct acpi_device *adev); +void acpi_free_properties(struct acpi_device *adev); + #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 9964f70..5c480d5 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -336,11 +336,11 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size) return NULL; } -#ifndef CONFIG_IA64 -#define should_use_kmap(pfn) page_is_ram(pfn) -#else +#if defined(CONFIG_IA64) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) /* ioremap will take care of cache attributes */ #define should_use_kmap(pfn) 0 +#else +#define should_use_kmap(pfn) page_is_ram(pfn) #endif static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index ef58f46..5c84e0d 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -64,6 +64,38 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, return 0; } +/* + * On ARM platform, MPIDR value is the hardware ID as apic ID + * on Intel platforms + */ +static int map_gicc_mpidr(struct acpi_subtable_header *entry, + int device_declaration, u32 acpi_id, int *mpidr) +{ + struct acpi_madt_generic_interrupt *gicc = + container_of(entry, struct acpi_madt_generic_interrupt, header); + + if (!(gicc->flags & ACPI_MADT_ENABLED)) + return -ENODEV; + + /* In the GIC interrupt model, logical processors are + * required to have a Processor Device object in the DSDT, + * so we should check device_declaration here + */ + if (device_declaration && (gicc->uid == acpi_id)) { + /* + * Only bits [0:7] Aff0, bits [8:15] Aff1, bits [16:23] Aff2 + * and bits [32:39] Aff3 are meaningful, so pack the Affx + * fields into a single 32 bit identifier to accommodate the + * acpi processor drivers. + */ + *mpidr = ((gicc->arm_mpidr & 0xff00000000) >> 8) + | gicc->arm_mpidr; + return 0; + } + + return -EINVAL; +} + static int map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; @@ -99,6 +131,9 @@ static int map_madt_entry(int type, u32 acpi_id) } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { if (!map_lsapic_id(header, type, acpi_id, &apic_id)) break; + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + if (!map_gicc_mpidr(header, type, acpi_id, &apic_id)) + break; } entry += header->length; } @@ -131,6 +166,8 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) map_lsapic_id(header, type, acpi_id, &apic_id); } else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) { map_x2apic_id(header, type, acpi_id, &apic_id); + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + map_gicc_mpidr(header, type, acpi_id, &apic_id); } exit: diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c new file mode 100644 index 0000000..ff53eb8 --- /dev/null +++ b/drivers/acpi/property.c @@ -0,0 +1,586 @@ +/* + * ACPI device specific properties support. + * + * Copyright (C) 2014, Intel Corporation + * All rights reserved. + * + * Authors: Mika Westerberg + * Darren Hart + * Rafael J. Wysocki + * + * 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 +#include +#include + +#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; + + /* + * 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; + + if (acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING, + &of_compatible)) { + 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 + * @size_prop: Name of the "size" property in referenced object + * @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 (@size_prop is not %NULL) follow the + * reference and check whether or not there is an integer property @size_prop + * under the target object and if so, whether or not its value matches the + * number of arguments that follow the reference. 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, + const char *size_prop, 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 (size_prop || 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; + + if (size_prop) { + const union acpi_object *prop; + + /* + * Find out how many arguments the refenced object + * expects by reading its size_prop property. + */ + ret = acpi_dev_get_property(device, size_prop, + ACPI_TYPE_INTEGER, &prop); + if (ret) + return ret; + + nargs = prop->integer.value; + if (nargs > MAX_ACPI_REFERENCE_ARGS + || element + nargs > end) + return -EPROTO; + + /* + * Skip to the start of the arguments and verify + * that they all are in fact integers. + */ + for (i = 0; i < nargs; i++) + if (element[i].type != ACPI_TYPE_INTEGER) + return -EPROTO; + } else { + /* 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(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val) +{ + const union acpi_object *obj; + int ret = -EINVAL; + + 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; + } + 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_array(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; + + ret = acpi_dev_get_property_array(adev, propname, ACPI_TYPE_ANY, &obj); + if (ret) + return ret; + + if (!val) + return obj->package.count; + + if (nval > obj->package.count) + nval = obj->package.count; + + 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; +} + +int acpi_for_each_child_node(struct device *dev, + int (*fn)(struct device *dev, void *child, void *data), + void *data) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_device *child; + int ret = 0; + + if (!adev) + return -EINVAL; + + list_for_each_entry(child, &adev->children, node) { + ret = fn(dev, child, data); + if (ret) + break; + } + return ret; +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ae44d86..4da55d8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -124,17 +124,43 @@ 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; + /* + * If the device has PRP0001 we expose DT compatible modalias + * instead. + */ + if (acpi_dev->data.of_compatible) { + const union acpi_object *of_compatible, *obj; + int i; + + len = snprintf(modalias, size, "of:Nprp0001Tacpi"); + + of_compatible = acpi_dev->data.of_compatible; + for (i = 0; i < of_compatible->package.count; i++) { + obj = &of_compatible->package.elements[i]; - 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; + 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'; @@ -864,6 +890,51 @@ int acpi_match_device_ids(struct acpi_device *device, } EXPORT_SYMBOL(acpi_match_device_ids); +/* Performs match for special "PRP0001" shoehorn ACPI ID */ +static bool acpi_of_driver_match_device(struct device *dev, + const struct device_driver *drv) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + const union acpi_object *of_compatible; + int i; + + /* + * If the ACPI device does not have corresponding compatible + * property or the driver in question does not have DT matching + * table we consider the match succesful (matches the ACPI ID). + */ + of_compatible = adev->data.of_compatible; + if (!drv->of_match_table || !of_compatible) + return true; + + /* Now we can look for the driver DT compatible strings */ + for (i = 0; i < of_compatible->package.count; i++) { + const struct of_device_id *id; + const union acpi_object *obj; + + obj = &of_compatible->package.elements[i]; + + 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) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(drv->acpi_match_table, dev); + if (!id) + return false; + + return acpi_of_driver_match_device(dev, drv); +} +EXPORT_SYMBOL_GPL(acpi_driver_match_device); + static void acpi_free_power_resources_lists(struct acpi_device *device) { int i; @@ -884,6 +955,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); @@ -1888,6 +1960,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_set_device_status(device, sta); acpi_device_get_busid(device); acpi_set_pnp_ids(handle, &device->pnp, type); + acpi_init_properties(device); acpi_bus_get_flags(device); device->flags.match_driver = false; device->flags.initialized = true; diff --git a/drivers/acpi/sleep-arm.c b/drivers/acpi/sleep-arm.c new file mode 100644 index 0000000..54578ef --- /dev/null +++ b/drivers/acpi/sleep-arm.c @@ -0,0 +1,28 @@ +/* + * ARM64 Specific Sleep Functionality + * + * Copyright (C) 2013-2014, Linaro Ltd. + * Author: Graeme Gregory + * + * 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 + +/* + * Currently the ACPI 5.1 standard does not define S states in a + * manner which is usable for ARM64. These two stubs are sufficient + * that system initialises and device PM works. + */ +u32 acpi_target_system_state(void) +{ + return ACPI_STATE_S0; +} +EXPORT_SYMBOL_GPL(acpi_target_system_state); + +int __init acpi_sleep_init(void) +{ + return -ENOSYS; +} diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 6d5a6cd..47f36d4 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -183,6 +183,49 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) } break; + case ACPI_MADT_TYPE_GENERIC_INTERRUPT: + { + struct acpi_madt_generic_interrupt *p = + (struct acpi_madt_generic_interrupt *)header; + pr_info("GICC (acpi_id[0x%04x] address[%p] MPDIR[0x%llx] %s)\n", + p->uid, (void *)(unsigned long)p->base_address, + p->arm_mpidr, + (p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled"); + + } + break; + + case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR: + { + struct acpi_madt_generic_distributor *p = + (struct acpi_madt_generic_distributor *)header; + pr_info("GIC Distributor (gic_id[0x%04x] address[%p] gsi_base[%d])\n", + p->gic_id, + (void *)(unsigned long)p->base_address, + p->global_irq_base); + } + break; + + case ACPI_MADT_TYPE_GENERIC_MSI_FRAME: + { + struct acpi_madt_generic_msi_frame *p = + (struct acpi_madt_generic_msi_frame *)header; + pr_info("GIC MSI Frame (msi_fame_id[%d] address[%p])\n", + p->msi_frame_id, + (void *)(unsigned long)p->base_address); + } + break; + + case ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR: + { + struct acpi_madt_generic_redistributor *p = + (struct acpi_madt_generic_redistributor *)header; + pr_info("GIC Redistributor (address[%p] region_size[0x%x])\n", + (void *)(unsigned long)p->base_address, + p->length); + } + break; + default: pr_warn("Found unsupported MADT entry (type = 0x%x)\n", header->type); @@ -192,17 +235,14 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) int __init -acpi_table_parse_entries(char *id, - unsigned long table_size, - int entry_id, - acpi_tbl_entry_handler handler, - unsigned int max_entries) +acpi_parse_entries(unsigned long table_size, + acpi_tbl_entry_handler handler, + struct acpi_table_header *table_header, + int entry_id, unsigned int max_entries) { - struct acpi_table_header *table_header = NULL; struct acpi_subtable_header *entry; - unsigned int count = 0; + int count = 0; unsigned long table_end; - acpi_size tbl_size; if (acpi_disabled) return -ENODEV; @@ -210,13 +250,11 @@ acpi_table_parse_entries(char *id, if (!handler) return -EINVAL; - if (strncmp(id, ACPI_SIG_MADT, 4) == 0) - acpi_get_table_with_size(id, acpi_apic_instance, &table_header, &tbl_size); - else - acpi_get_table_with_size(id, 0, &table_header, &tbl_size); + if (!table_size) + return -EINVAL; if (!table_header) { - pr_warn("%4.4s not present\n", id); + pr_warn("Table header not present\n"); return -ENODEV; } @@ -230,32 +268,67 @@ acpi_table_parse_entries(char *id, while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) < table_end) { if (entry->type == entry_id - && (!max_entries || count++ < max_entries)) + && (!max_entries || count < max_entries)) { if (handler(entry, table_end)) - goto err; + return -EINVAL; + + count++; + } /* * If entry->length is 0, break from this loop to avoid * infinite loop. */ if (entry->length == 0) { - pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, entry_id); - goto err; + pr_err("[0x%02x] Invalid zero length\n", entry_id); + return -EINVAL; } entry = (struct acpi_subtable_header *) ((unsigned long)entry + entry->length); } + if (max_entries && count > max_entries) { pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", - id, entry_id, count - max_entries, count); + table_header->signature, entry_id, count - max_entries, + count); } - early_acpi_os_unmap_memory((char *)table_header, tbl_size); return count; -err: +} + +int __init +acpi_table_parse_entries(char *id, + unsigned long table_size, + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries) +{ + struct acpi_table_header *table_header = NULL; + acpi_size tbl_size; + int count; + + if (acpi_disabled) + return -ENODEV; + + if (!handler) + return -EINVAL; + + if (strncmp(id, ACPI_SIG_MADT, 4) == 0) + acpi_get_table_with_size(id, acpi_apic_instance, &table_header, &tbl_size); + else + acpi_get_table_with_size(id, 0, &table_header, &tbl_size); + + if (!table_header) { + pr_warn("%4.4s not present\n", id); + return -ENODEV; + } + + count = acpi_parse_entries(table_size, handler, table_header, + entry_id, max_entries); + early_acpi_os_unmap_memory((char *)table_header, tbl_size); - return -EINVAL; + return count; } int __init diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 834f35c..b163f73 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -697,3 +697,29 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs) return false; } EXPORT_SYMBOL(acpi_check_dsm); + +/** + * acpi_check_coherency - check for memory coherency of a device + * @handle: ACPI device handle + * @val: Pointer to returned value + * + * Search a device and its parents for a _CCA method and return + * its value. + */ +acpi_status acpi_check_coherency(acpi_handle handle, int *val) +{ + unsigned long long data; + acpi_status status; + + do { + status = acpi_evaluate_integer(handle, "_CCA", NULL, &data); + if (!ACPI_FAILURE(status)) { + *val = data; + break; + } + status = acpi_get_parent(handle, &handle); + } while (!ACPI_FAILURE(status)); + + return status; +} +EXPORT_SYMBOL(acpi_check_coherency); diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index cd4cccb..edb00c6 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -48,7 +48,7 @@ config ATA_VERBOSE_ERROR config ATA_ACPI bool "ATA ACPI Support" - depends on ACPI && PCI + depends on ACPI default y help This option adds support for ATA-related ACPI objects. diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 06f1d59..df2ea85 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -20,6 +20,9 @@ #include #include #include +#ifdef CONFIG_ATA_ACPI +#include +#endif #include "ahci.h" static const struct ata_port_info ahci_port_info = { @@ -71,6 +74,13 @@ static const struct of_device_id ahci_of_match[] = { }; MODULE_DEVICE_TABLE(of, ahci_of_match); +#ifdef CONFIG_ATA_ACPI +static const struct acpi_device_id ahci_acpi_match[] = { + { "AMDI0600", 0 }, /* AMD Seattle AHCI */ + { }, +}; +#endif + static struct platform_driver ahci_driver = { .probe = ahci_probe, .remove = ata_platform_remove_one, @@ -78,6 +88,9 @@ static struct platform_driver ahci_driver = { .name = "ahci", .owner = THIS_MODULE, .of_match_table = ahci_of_match, +#ifdef CONFIG_ATA_ACPI + .acpi_match_table = ACPI_PTR(ahci_acpi_match), +#endif .pm = &ahci_pm_ops, }, }; diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index 0f8538f..2d8103a 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "ahci.h" /* Max # of disk per a controller */ @@ -137,7 +138,8 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) struct xgene_ahci_context *ctx = hpriv->plat_data; int rc = 0; - if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA)) + if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA || + ctx->last_cmd[ap->port_no] == ATA_CMD_SMART)) xgene_ahci_restart_engine(ap); rc = ahci_qc_issue(qc); @@ -148,14 +150,6 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) return rc; } -static bool xgene_ahci_is_memram_inited(struct xgene_ahci_context *ctx) -{ - void __iomem *diagcsr = ctx->csr_diag; - - return (readl(diagcsr + CFG_MEM_RAM_SHUTDOWN) == 0 && - readl(diagcsr + BLOCK_MEM_RDY) == 0xFFFFFFFF); -} - /** * xgene_ahci_read_id - Read ID data from the specified device * @dev: device @@ -501,11 +495,6 @@ static int xgene_ahci_probe(struct platform_device *pdev) return -ENODEV; } - if (xgene_ahci_is_memram_inited(ctx)) { - dev_info(dev, "skip clock and PHY initialization\n"); - goto skip_clk_phy; - } - /* Due to errata, HW requires full toggle transition */ rc = ahci_platform_enable_clks(hpriv); if (rc) @@ -518,7 +507,7 @@ static int xgene_ahci_probe(struct platform_device *pdev) /* Configure the host controller */ xgene_ahci_hw_init(hpriv); -skip_clk_phy: + hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_NCQ; rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info); @@ -533,6 +522,16 @@ disable_resources: return rc; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_ahci_acpi_match[] = { + { "APMC0D00", }, + { "APMC0D0D", }, + { "APMC0D09", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match); +#endif + static const struct of_device_id xgene_ahci_of_match[] = { {.compatible = "apm,xgene-ahci"}, {}, @@ -546,6 +545,7 @@ static struct platform_driver xgene_ahci_driver = { .name = "xgene-ahci", .owner = THIS_MODULE, .of_match_table = xgene_ahci_of_match, + .acpi_match_table = ACPI_PTR(xgene_ahci_acpi_match), }, }; diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 6922cd6..53c3fe1 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -4,7 +4,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o \ - topology.o container.o + topology.o container.o property.o obj-$(CONFIG_DEVTMPFS) += devtmpfs.o obj-$(CONFIG_DMA_CMA) += dma-contiguous.o obj-y += power/ diff --git a/drivers/base/property.c b/drivers/base/property.c new file mode 100644 index 0000000..7bf5708 --- /dev/null +++ b/drivers/base/property.c @@ -0,0 +1,235 @@ +/* + * property.c - Unified device property interface. + * + * Copyright (C) 2014, Intel Corporation + * Authors: Rafael J. Wysocki + * Mika Westerberg + * + * 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 +#include +#include +#include + +/** + * device_get_property - return a raw property of a device + * @dev: Device get the property of + * @propname: Name of the property + * @valptr: The raw property value is stored here + * + * Function reads property @propname from the device firmware description and + * stores the raw value into @valptr if found. Otherwise returns a negative + * errno as specified below. + * + * Return: %0 if the property was found (success), + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not exist. + */ +int device_get_property(struct device *dev, const char *propname, void **valptr) +{ + if (IS_ENABLED(CONFIG_OF) && dev->of_node) + return of_dev_prop_get(dev->of_node, propname, valptr); + + return acpi_dev_prop_get(ACPI_COMPANION(dev), propname, valptr); +} +EXPORT_SYMBOL_GPL(device_get_property); + +/** + * device_get_child_property - return a raw property of a device's child + * @dev: Parent device + * @child: Child to get a property of + * @propname: Name of the property + * @valptr: The raw property value is stored here + * + * Function reads property @propname from the firmware description of @child and + * stores the raw value into @valptr if found. Otherwise returns a negative + * errno as specified below. + * + * Return: %0 if the property was found (success), + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not exist. + */ +int device_get_child_property(struct device *dev, void *child, + const char *propname, void **valptr) +{ + if (!child) + return -EINVAL; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) + return of_dev_prop_get(child, propname, valptr); + else if (ACPI_COMPANION(dev)) + return acpi_dev_prop_get(child, propname, valptr); + + return -ENODATA; +} +EXPORT_SYMBOL_GPL(device_get_child_property); + +/** + * device_read_property - read a typed property of a device + * @dev: Device to get the property of + * @propname: Name of the property + * @proptype: Type 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 of type + * @proptype. + * + * Return: %0 if the property was found (success), + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not exist, + * %-EPROTO if the property type does not match @proptype, + * %-EOVERFLOW if the property value is out of bounds of @proptype. + */ +int device_read_property(struct device *dev, const char *propname, + enum dev_prop_type proptype, void *val) +{ + if (IS_ENABLED(CONFIG_OF) && dev->of_node) + return of_dev_prop_read(dev->of_node, propname, proptype, val); + + return acpi_dev_prop_read(ACPI_COMPANION(dev), propname, proptype, val); +} +EXPORT_SYMBOL_GPL(device_read_property); + +/** + * device_read_child_property - read a typed property of a device's child + * @dev: Parent device + * @child: Child to read a property of + * @propname: Name of the property + * @proptype: Type of the property + * @val: The value is stored here + * + * Function reads property @propname from the firmware description of @child and + * stores the value into @val if found. The value is checked to be of type + * @proptype. + * + * Return: %0 if the property was found (success), + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not exist, + * %-EPROTO if the property type does not match @proptype, + * %-EOVERFLOW if the property value is out of bounds of @proptype. + */ +int device_read_child_property(struct device *dev, void *child, + const char *propname, enum dev_prop_type proptype, + void *val) +{ + if (!child) + return -EINVAL; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) + return of_dev_prop_read(child, propname, proptype, val); + else if (ACPI_COMPANION(dev)) + return acpi_dev_prop_read(child, propname, proptype, val); + + return -ENODATA; +} +EXPORT_SYMBOL_GPL(device_read_child_property); + +/** + * device_read_property_array - read an array property of a device + * @dev: Device to get the property of + * @propname: Name of the property + * @proptype: Type of the property + * @val: The values are stored here + * @nval: Size of the @val array + * + * Function reads an array of properties with @propname from the device + * firmware description and stores them to @val if found. All the values + * in the array must be of type @proptype. + * + * Return: %0 if the property was found (success), + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not exist, + * %-EPROTO if the property type does not match @proptype, + * %-EOVERFLOW if the property value is out of bounds of @proptype. + */ +int device_read_property_array(struct device *dev, const char *propname, + enum dev_prop_type proptype, void *val, + size_t nval) +{ + if (IS_ENABLED(CONFIG_OF) && dev->of_node) + return of_dev_prop_read_array(dev->of_node, propname, proptype, + val, nval); + + return acpi_dev_prop_read_array(ACPI_COMPANION(dev), propname, proptype, + val, nval); +} +EXPORT_SYMBOL_GPL(device_read_property_array); + +/** + * device_read_child_property_array - read an array property of a device's child + * @dev: Parent device + * @child: Child to get the property of + * @propname: Name of the property + * @proptype: Type of the property + * @val: The values are stored here + * @nval: Size of the @val array + * + * Function reads an array of properties with @propname from the firmware + * description of @child and stores them to @val if found. All the values + * in the array must be of type @proptype. + * + * Return: %0 if the property was found (success), + * %-EINVAL if given arguments are not valid, + * %-ENODATA if the property does not exist, + * %-EPROTO if the property type does not match @proptype, + * %-EOVERFLOW if the property value is out of bounds of @proptype. + */ +int device_read_child_property_array(struct device *dev, void *child, + const char *propname, + enum dev_prop_type proptype, void *val, + size_t nval) +{ + if (!child) + return -EINVAL; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) + return of_dev_prop_read_array(child, propname, proptype, + val, nval); + else if (ACPI_COMPANION(dev)) + return acpi_dev_prop_read_array(child, propname, proptype, + val, nval); + + return -ENODATA; +} +EXPORT_SYMBOL_GPL(device_read_child_property_array); + +/** + * device_for_each_child_node - execute function for each child node of device + * @dev: Device to run the function for + * @fn: Function to run + * @data: Additional data to pass to the function + */ +int device_for_each_child_node(struct device *dev, + int (*fn)(struct device *dev, void *child, void *data), + void *data) +{ + if (IS_ENABLED(CONFIG_OF) && dev->of_node) + return of_for_each_child_node(dev, fn, data); + + return acpi_for_each_child_node(dev, fn, data); +} +EXPORT_SYMBOL_GPL(device_for_each_child_node); + +static int increment_count(struct device *dev, void *child, void *data) +{ + *((unsigned int *)data) += 1; + return 0; +} + +/** + * 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) +{ + unsigned int count = 0; + + device_for_each_child_node(dev, increment_count, &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 2133f9d..b73392b 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -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 @@ -660,13 +666,14 @@ static bool __init arch_timer_probed(int type, const struct of_device_id *matches) { struct device_node *dn; - bool probed = false; + bool probed = true; dn = of_find_matching_node(NULL, matches); - if (dn && of_device_is_available(dn) && (arch_timers_present & type)) - probed = true; - of_node_put(dn); - + if (dn) { + if (of_device_is_available(dn) && !(arch_timers_present & type)) + probed = false; + of_node_put(dn); + } return probed; } @@ -687,20 +694,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 +714,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 +805,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/gpio/devres.c b/drivers/gpio/devres.c index 954b9f6..a1f7e55 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -109,6 +109,40 @@ struct gpio_desc *__must_check __devm_gpiod_get_index(struct device *dev, EXPORT_SYMBOL(__devm_gpiod_get_index); /** + * devm_get_named_gpiod_from_child - managed dev_get_named_gpiod_from_child() + * @dev: GPIO consumer + * @child: firmware node (child of @dev) + * @propname: name of the firmware property + * @index: index of the GPIO in the property value in case of many + * + * GPIO descriptors returned from this function are automatically disposed on + * driver detach. + */ +struct gpio_desc *devm_get_named_gpiod_from_child(struct device *dev, void *child, + const char *propname, int index) +{ + 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 = dev_get_named_gpiod_from_child(dev, child, propname, index); + if (IS_ERR(desc)) { + devres_free(dr); + return desc; + } + + *dr = desc; + devres_add(dev, dr); + + return desc; +} +EXPORT_SYMBOL(devm_get_named_gpiod_from_child); + +/** * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() * @dev: GPIO consumer * @con_id: function within the GPIO consumer diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index 41e91d7..99720c8 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -29,290 +29,221 @@ #include -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..8aa6ca4 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -290,6 +290,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; + int pin_index; struct gpio_desc *desc; int n; }; @@ -303,13 +304,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 +329,75 @@ 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, NULL, + index, &args); + if (ret) + 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 +406,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..0fa5f79 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1505,14 +1505,36 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags) { + static const char * const suffixes[] = { "gpios", "gpio" }; + struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_gpio_info info; struct gpio_desc *desc; + char propname[32]; + int i; - desc = acpi_get_gpiod_by_index(dev, idx, &info); - if (IS_ERR(desc)) - return desc; + /* Try first from _DSD */ + for (i = 0; i < ARRAY_SIZE(suffixes); i++) { + if (con_id && strcmp(con_id, "gpios")) { + snprintf(propname, sizeof(propname), "%s-%s", + con_id, suffixes[i]); + } else { + snprintf(propname, sizeof(propname), "%s", + suffixes[i]); + } + + desc = acpi_get_gpiod_by_index(adev, propname, 0, &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,62 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev, EXPORT_SYMBOL_GPL(__gpiod_get_index); /** + * dev_get_named_gpiod_from_child - obtain a GPIO from firmware node + * @dev: parent device + * @child: firmware node (child of @dev) + * @propname: name of the firmware property + * @idx: index of the GPIO in the property value in case of many + * + * This function can be used for drivers that get their configuration + * from firmware in such a way that some properties are described as child + * nodes for the parent device in DT or ACPI. + * + * 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 *dev_get_named_gpiod_from_child(struct device *dev, void *child, + const char *propname, int index) +{ + struct gpio_desc *desc = ERR_PTR(-ENODEV); + bool active_low = false; + int ret; + + if (!child) + return ERR_PTR(-EINVAL); + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + enum of_gpio_flags flags; + + desc = of_get_named_gpiod_flags(child, propname, index, &flags); + if (!IS_ERR(desc)) + active_low = flags & OF_GPIO_ACTIVE_LOW; + } else if (ACPI_COMPANION(dev)) { + struct acpi_gpio_info info; + + desc = acpi_get_gpiod_by_index(child, propname, index, &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(dev_get_named_gpiod_from_child); + +/** * gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO * function * @dev: GPIO consumer, can be NULL for system-global GPIOs diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 9db2b6a..e3a5211 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -34,7 +34,8 @@ void acpi_gpiochip_remove(struct gpio_chip *chip); void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); -struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index, +struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, + const char *propname, int index, struct acpi_gpio_info *info); #else static inline void acpi_gpiochip_add(struct gpio_chip *chip) { } @@ -47,8 +48,8 @@ static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } static inline struct gpio_desc * -acpi_get_gpiod_by_index(struct device *dev, int index, - struct acpi_gpio_info *info) +acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, + int index, struct acpi_gpio_info *info) { return ERR_PTR(-ENOSYS); } diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 432d363..3563850 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -23,10 +23,9 @@ #include #include #include +#include #include -#include -#include -#include +#include #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,57 @@ static void gpio_keys_polled_close(struct input_polled_dev *dev) pdata->disable(bdev->dev); } -#ifdef CONFIG_OF +static int gpio_keys_polled_get_button(struct device *dev, void *child, + void *data) +{ + struct gpio_keys_platform_data *pdata = data; + struct gpio_keys_button *button; + struct gpio_desc *desc; + + desc = devm_get_named_gpiod_from_child(dev, child, "gpios", 0); + if (IS_ERR(desc)) { + int err = PTR_ERR(desc); + + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get gpio flags, error: %d\n", + err); + return err; + } + + button = &pdata->buttons[pdata->nbuttons++]; + button->gpiod = desc; + + if (device_child_property_read_u32(dev, child, "linux,code", + &button->code)) { + dev_err(dev, "Button without keycode: %d\n", + pdata->nbuttons - 1); + return -EINVAL; + } + + device_child_property_read_string(dev, child, "label", &button->desc); + + if (device_child_property_read_u32(dev, child, "linux,input-type", + &button->type)) + button->type = EV_KEY; + + button->wakeup = !device_get_child_property(dev, child, + "gpio-key,wakeup", NULL); + + if (device_child_property_read_u32(dev, child, "debounce-interval", + &button->debounce_interval)) + button->debounce_interval = 5; + + return 0; +} + 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; 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,54 +160,14 @@ 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_get_property(dev, "autorepeat", NULL); + 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; - - 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; - if (error != -EPROBE_DEFER) - dev_err(dev, - "Failed to get gpio flags, error: %d\n", - error); - return ERR_PTR(error); - } - - button = &pdata->buttons[i++]; - - button->gpio = gpio; - button->active_low = flags & OF_GPIO_ACTIVE_LOW; - - if (of_property_read_u32(pp, "linux,code", &button->code)) { - dev_err(dev, "Button without keycode: 0x%x\n", - button->gpio); - return ERR_PTR(-EINVAL); - } - - button->desc = of_get_property(pp, "label", NULL); - - if (of_property_read_u32(pp, "linux,input-type", &button->type)) - button->type = EV_KEY; - - button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL); - - if (of_property_read_u32(pp, "debounce-interval", - &button->debounce_interval)) - button->debounce_interval = 5; - } + error = device_for_each_child_node(dev, gpio_keys_polled_get_button, + pdata); + if (error) + return ERR_PTR(error); if (pdata->nbuttons == 0) return ERR_PTR(-EINVAL); @@ -187,14 +181,11 @@ 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 const struct acpi_device_id gpio_keys_polled_acpi_match[] = { + { "PRP0001" }, /* Device Tree shoehorned into ACPI */ + { }, +}; +MODULE_DEVICE_TABLE(acpi, gpio_keys_polled_acpi_match); static int gpio_keys_polled_probe(struct platform_device *pdev) { @@ -259,7 +250,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 +257,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 +314,8 @@ 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, + .acpi_match_table = gpio_keys_polled_acpi_match, }, }; module_platform_driver(gpio_keys_polled_driver); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index aa17ae8..d330dab 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -506,9 +506,19 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) isb(); } +#ifdef CONFIG_ARM_PARKING_PROTOCOL +static void gic_wakeup_parked_cpu(int cpu) +{ + gic_raise_softirq(cpumask_of(cpu), 0); +} +#endif + static void gic_smp_init(void) { set_smp_cross_call(gic_raise_softirq); +#ifdef CONFIG_ARM_PARKING_PROTOCOL + set_smp_boot_wakeup_call(gic_wakeup_parked_cpu); +#endif register_cpu_notifier(&gic_cpu_notifier); } diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 38493ff..26e6773 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -33,12 +33,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -641,6 +643,13 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } + +#ifdef CONFIG_ARM_PARKING_PROTOCOL +static void gic_wakeup_parked_cpu(int cpu) +{ + gic_raise_softirq(cpumask_of(cpu), GIC_DIST_SOFTINT_NSATT); +} +#endif #endif #ifdef CONFIG_BL_SWITCHER @@ -996,6 +1005,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, #ifdef CONFIG_SMP set_smp_cross_call(gic_raise_softirq); register_cpu_notifier(&gic_cpu_notifier); +#ifdef CONFIG_ARM_PARKING_PROTOCOL + set_smp_boot_wakeup_call(gic_wakeup_parked_cpu); +#endif #endif set_handle_irq(gic_handle_irq); } @@ -1048,3 +1060,107 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); #endif + +#ifdef CONFIG_ACPI +static phys_addr_t dist_phy_base, cpu_phy_base; +static int cpu_base_assigned; + +static int __init +gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_interrupt *processor; + phys_addr_t gic_cpu_base; + + processor = (struct acpi_madt_generic_interrupt *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + /* + * There is no support for non-banked GICv1/2 register in ACPI spec. + * All CPU interface addresses have to be the same. + */ + gic_cpu_base = processor->base_address; + if (cpu_base_assigned && gic_cpu_base != cpu_phy_base) + return -EFAULT; + + cpu_phy_base = gic_cpu_base; + cpu_base_assigned = 1; + return 0; +} + +static int __init +gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_distributor *dist; + + dist = (struct acpi_madt_generic_distributor *)header; + + if (BAD_MADT_ENTRY(dist, end)) + return -EINVAL; + + dist_phy_base = dist->base_address; + return 0; +} + +int __init +gic_v2_acpi_init(struct acpi_table_header *table) +{ + void __iomem *cpu_base, *dist_base; + int count; + + /* Collect CPU base addresses */ + count = acpi_parse_entries(sizeof(struct acpi_table_madt), + gic_acpi_parse_madt_cpu, table, + ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0); + if (count < 0) { + pr_err("Error during GICC entries parsing\n"); + return -EFAULT; + } else if (!count) { + pr_err("No valid GICC entries exist\n"); + return -EINVAL; + } + + /* + * Find distributor base address. We expect one distributor entry since + * ACPI 5.1 spec neither support multi-GIC instances nor GIC cascade. + */ + count = acpi_parse_entries(sizeof(struct acpi_table_madt), + gic_acpi_parse_madt_distributor, table, + ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0); + if (count <= 0) { + pr_err("Error during GICD entries parsing\n"); + return -EFAULT; + } else if (!count) { + pr_err("No valid GICD entries exist\n"); + return -EINVAL; + } else if (count > 1) { + pr_err("More than one GICD entry detected\n"); + return -EINVAL; + } + + cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); + if (!cpu_base) { + pr_err("Unable to map GICC registers\n"); + return -ENOMEM; + } + + dist_base = ioremap(dist_phy_base, ACPI_GICV2_DIST_MEM_SIZE); + if (!dist_base) { + pr_err("Unable to map GICD registers\n"); + iounmap(cpu_base); + return -ENOMEM; + } + + /* + * Initialize zero GIC instance (no multi-GIC support). Also, set GIC + * as default IRQ domain to allow for GSI registration and GSI to IRQ + * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()). + */ + gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL); + irq_set_default_host(gic_data[0].domain); + return 0; +} +#endif diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index 0fe2f71..9106c6d 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -11,6 +11,7 @@ #include #include #include +#include /* * 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 57ff20f..681efd5 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -13,22 +13,20 @@ #include #include +#include #include #include #include -#include -#include -#include #include +#include #include #include 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, unsigned long *delay_on, unsigned long *delay_off); @@ -40,12 +38,16 @@ 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); + int gpio = desc_to_gpio(led_dat->gpiod); + int level = led_dat->new_level; + + if (gpiod_is_active_low(led_dat->gpiod)) + level = !level; + + led_dat->platform_gpio_blink_set(gpio, 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 +62,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 +71,16 @@ 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, - NULL, NULL); + int gpio = desc_to_gpio(led_dat->gpiod); + + if (gpiod_is_active_low(led_dat->gpiod)) + level = !level; + + led_dat->platform_gpio_blink_set(gpio, level, NULL, + NULL); led_dat->blinking = 0; } else - gpio_set_value(led_dat->gpio, level); + gpiod_set_value(led_dat->gpiod, level); } } @@ -85,9 +89,10 @@ static int gpio_blink_set(struct led_classdev *led_cdev, { struct gpio_led_data *led_dat = container_of(led_cdev, struct gpio_led_data, cdev); + int gpio = desc_to_gpio(led_dat->gpiod); led_dat->blinking = 1; - return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK, + return led_dat->platform_gpio_blink_set(gpio, GPIO_LED_BLINK, delay_on, delay_off); } @@ -97,24 +102,33 @@ static int create_gpio_led(const struct gpio_led *template, { int ret, state; - led_dat->gpio = -1; + if (!template->gpiod) { + 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; + } - ret = devm_gpio_request(parent, template->gpio, template->name); - if (ret < 0) - return ret; + if (template->active_low) + flags |= GPIOF_ACTIVE_LOW; + + 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->gpiod = template->gpiod; + led_dat->can_sleep = gpiod_cansleep(template->gpiod); led_dat->blinking = 0; if (blink_set) { led_dat->platform_gpio_blink_set = blink_set; @@ -122,30 +136,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,65 +169,59 @@ 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 int gpio_leds_create_led(struct device *dev, void *child, void *data) +{ + struct gpio_leds_priv *priv = data; + struct gpio_led led = {}; + const char *state = NULL; + + led.gpiod = devm_get_named_gpiod_from_child(dev, child, "gpios", 0); + if (IS_ERR(led.gpiod)) + return PTR_ERR(led.gpiod); + + device_child_property_read_string(dev, child, "label", &led.name); + device_child_property_read_string(dev, child, "linux,default-trigger", + &led.default_trigger); + + device_child_property_read_string(dev, child, "linux,default_state", + &state); + if (state) { + if (!strcmp(state, "keep")) + led.default_state = LEDS_GPIO_DEFSTATE_KEEP; + else if (!strcmp(state, "on")) + led.default_state = LEDS_GPIO_DEFSTATE_ON; + else + led.default_state = LEDS_GPIO_DEFSTATE_OFF; + } + + if (!device_get_child_property(dev, child, "retain-state-suspended", NULL)) + led.retain_state_suspended = 1; + + return create_gpio_led(&led, &priv->leds[priv->num_leds++], dev, NULL); +} + +static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node, *child; struct gpio_leds_priv *priv; - int count, ret; + int ret, count; - /* 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(&pdev->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); if (!priv) return ERR_PTR(-ENOMEM); - for_each_available_child_of_node(np, 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) { - if (!strcmp(state, "keep")) - led.default_state = LEDS_GPIO_DEFSTATE_KEEP; - else if (!strcmp(state, "on")) - led.default_state = LEDS_GPIO_DEFSTATE_ON; - else - led.default_state = LEDS_GPIO_DEFSTATE_OFF; - } - - if (of_get_property(child, "retain-state-suspended", NULL)) - led.retain_state_suspended = 1; - - ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], - &pdev->dev, NULL); - if (ret < 0) { - of_node_put(child); - goto err; - } + ret = device_for_each_child_node(&pdev->dev, gpio_leds_create_led, priv); + if (ret) { + for (count = priv->num_leds - 2; count >= 0; count--) + delete_gpio_led(&priv->leds[count]); + return ERR_PTR(ret); } return priv; - -err: - for (count = priv->num_leds - 2; count >= 0; count--) - delete_gpio_led(&priv->leds[count]); - return ERR_PTR(-ENODEV); } static const struct of_device_id of_gpio_leds_match[] = { @@ -228,12 +228,13 @@ 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 const struct acpi_device_id acpi_gpio_leds_match[] = { + { "PRP0001" }, /* Device Tree shoehorned into ACPI */ + {}, +}; + +MODULE_DEVICE_TABLE(acpi, acpi_gpio_leds_match); static int gpio_led_probe(struct platform_device *pdev) { @@ -263,7 +265,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); } @@ -290,7 +292,8 @@ 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, + .acpi_match_table = acpi_gpio_leds_match, }, }; diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 634f729..1a760cd 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -18,7 +18,7 @@ #include #include -#include +#include /* * 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_get_property(dev, "read-only", NULL)) 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; @@ -467,11 +459,18 @@ static const struct of_device_id at25_of_match[] = { }; MODULE_DEVICE_TABLE(of, at25_of_match); +static const struct acpi_device_id at25_acpi_match[] = { + { "PRP0001" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, at25_acpi_match); + static struct spi_driver at25_driver = { .driver = { .name = "at25", .owner = THIS_MODULE, .of_match_table = at25_of_match, + .acpi_match_table = at25_acpi_match, }, .probe = at25_probe, .remove = at25_remove, diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 8319c99..6feb6ef3 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -179,7 +179,7 @@ config SUNLANCE config AMD_XGBE tristate "AMD 10GbE Ethernet driver" - depends on OF_NET + depends on OF_NET || ACPI select PHYLIB select AMD_XGBE_PHY select BITREVERSE diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 9da3a03..a34cad2 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -130,7 +130,7 @@ static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata, DBGPR("-->xgbe_usec_to_riwt\n"); - rate = clk_get_rate(pdata->sysclk); + rate = pdata->sysclk_rate; /* * Convert the input usec value to the watchdog timer value. Each @@ -153,7 +153,7 @@ static unsigned int xgbe_riwt_to_usec(struct xgbe_prv_data *pdata, DBGPR("-->xgbe_riwt_to_usec\n"); - rate = clk_get_rate(pdata->sysclk); + rate = pdata->sysclk_rate; /* * Convert the input watchdog timer value to the usec value. Each @@ -695,6 +695,18 @@ static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad, else mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + if (XGBE_SEATTLE_A0) { + /* The PCS implementation has reversed the devices in + * package registers so we need to change 05 to 06 and + * 06 to 05 if being read (these registers are readonly + * so no need to do this in the write function) + */ + if ((mmd_address & 0xffff) == 0x05) + mmd_address = (mmd_address & ~0xffff) | 0x06; + else if ((mmd_address & 0xffff) == 0x06) + mmd_address = (mmd_address & ~0xffff) | 0x05; + } + /* The PCS registers are accessed using mmio. The underlying APB3 * management interface uses indirect addressing to access the MMD * register sets. This requires accessing of the PCS register in two diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 2955499..423ae3d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -425,6 +425,9 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) hw_feat->rx_ch_cnt++; hw_feat->tx_ch_cnt++; + /* A0 does not support NUMTC, hardcode it for now */ + hw_feat->tc_cnt = XGBE_TC_CNT; + DBGPR("<--xgbe_get_all_hw_features\n"); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index f5a8fa0..db29dec 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -124,6 +124,7 @@ #include #include #include +#include #include "xgbe.h" #include "xgbe-common.h" @@ -215,6 +216,210 @@ 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); +#if 0 + return -EINVAL; +#else + dev_err(dev, "invalid MAC address, using random address\n"); + eth_random_addr(pdata->mac_addr); +#endif + } + + /* 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 +427,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 +442,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 +468,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 +485,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 +507,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 +684,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 +707,12 @@ static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume); static struct platform_driver xgbe_driver = { .driver = { .name = "amd-xgbe", +#ifdef CONFIG_ACPI + .acpi_match_table = xgbe_acpi_match, +#endif +#ifdef CONFIG_OF .of_match_table = xgbe_of_match, +#endif .pm = &xgbe_pm_ops, }, .probe = xgbe_probe, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 363b210..5d2c89b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -119,6 +119,7 @@ #include #include #include +#include #include "xgbe.h" #include "xgbe-common.h" @@ -205,25 +206,16 @@ void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) int xgbe_mdio_register(struct xgbe_prv_data *pdata) { - struct device_node *phy_node; struct mii_bus *mii; struct phy_device *phydev; int ret = 0; DBGPR("-->xgbe_mdio_register\n"); - /* Retrieve the phy-handle */ - phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0); - if (!phy_node) { - dev_err(pdata->dev, "unable to parse phy-handle\n"); - return -EINVAL; - } - mii = mdiobus_alloc(); if (mii == NULL) { dev_err(pdata->dev, "mdiobus_alloc failed\n"); - ret = -ENOMEM; - goto err_node_get; + return -ENOMEM; } /* Register on the MDIO bus (don't probe any PHYs) */ @@ -252,12 +244,9 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS])); - of_node_get(phy_node); - phydev->dev.of_node = phy_node; ret = phy_device_register(phydev); if (ret) { dev_err(pdata->dev, "phy_device_register failed\n"); - of_node_put(phy_node); goto err_phy_device; } @@ -283,8 +272,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) pdata->phydev = phydev; - of_node_put(phy_node); - DBGPHY_REGS(pdata); DBGPR("<--xgbe_mdio_register\n"); @@ -300,9 +287,6 @@ err_mdiobus_register: err_mdiobus_alloc: mdiobus_free(mii); -err_node_get: - of_node_put(phy_node); - return ret; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c index a1bf9d1c..fa67203 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c @@ -239,7 +239,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata) snprintf(info->name, sizeof(info->name), "%s", netdev_name(pdata->netdev)); info->owner = THIS_MODULE; - info->max_adj = clk_get_rate(pdata->ptpclk); + info->max_adj = pdata->ptpclk_rate; info->adjfreq = xgbe_adjfreq; info->adjtime = xgbe_adjtime; info->gettime = xgbe_gettime; @@ -260,7 +260,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata) */ dividend = 50000000; dividend <<= 32; - pdata->tstamp_addend = div_u64(dividend, clk_get_rate(pdata->ptpclk)); + pdata->tstamp_addend = div_u64(dividend, pdata->ptpclk_rate); /* Setup the timecounter */ cc->read = xgbe_cc_read; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 789957d..59498eb 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -172,6 +172,12 @@ #define XGBE_DMA_CLOCK "dma_clk" #define XGBE_PTP_CLOCK "ptp_clk" +/* ACPI property names */ +#define XGBE_ACPI_MAC_ADDR "mac-address" +#define XGBE_ACPI_PHY_MODE "phy-mode" +#define XGBE_ACPI_DMA_FREQ "amd,dma-freq" +#define XGBE_ACPI_PTP_FREQ "amd,ptp-freq" + /* Timestamp support - values based on 50MHz PTP clock * 50MHz => 20 nsec */ @@ -186,8 +192,11 @@ #define XGBE_FIFO_SIZE_B(x) (x) #define XGBE_FIFO_SIZE_KB(x) (x * 1024) +#define XGBE_TC_CNT 2 #define XGBE_TC_MIN_QUANTUM 10 +#define XGBE_SEATTLE_A0 ((read_cpuid_id() & 0x00f0000f) == 0) + /* Helper macro for descriptor handling * Always use XGBE_GET_DESC_DATA to access the descriptor data * since the index is free-running and needs to be and-ed @@ -569,6 +578,7 @@ struct xgbe_hw_features { struct xgbe_prv_data { struct net_device *netdev; struct platform_device *pdev; + struct acpi_device *adev; struct device *dev; /* XGMAC/XPCS related mmio registers */ @@ -649,6 +659,7 @@ struct xgbe_prv_data { unsigned int phy_rx_pause; /* Netdev related settings */ + unsigned char mac_addr[MAX_ADDR_LEN]; netdev_features_t netdev_features; struct napi_struct napi; struct xgbe_mmc_stats mmc_stats; @@ -658,7 +669,9 @@ struct xgbe_prv_data { /* Device clocks */ struct clk *sysclk; + unsigned long sysclk_rate; struct clk *ptpclk; + unsigned long ptpclk_rate; /* Timestamp support */ spinlock_t tstamp_lock; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 63ea194..bb059b4 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -579,9 +579,11 @@ static void xgene_enet_reset(struct xgene_enet_pdata *pdata) { u32 val; - clk_prepare_enable(pdata->clk); - clk_disable_unprepare(pdata->clk); - clk_prepare_enable(pdata->clk); + if (pdata->clk) { + clk_prepare_enable(pdata->clk); + clk_disable_unprepare(pdata->clk); + clk_prepare_enable(pdata->clk); + } xgene_enet_ecc_init(pdata); xgene_enet_config_ring_if_assoc(pdata); @@ -647,15 +649,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; } @@ -665,11 +672,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; @@ -686,7 +734,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; } @@ -704,7 +752,10 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) mdio_bus->priv = pdata; mdio_bus->parent = &ndev->dev; - ret = of_mdiobus_register(mdio_bus, mdio_np); + if (dev->of_node) + ret = of_mdiobus_register(mdio_bus, mdio_np); + else + ret = xgene_acpi_mdiobus_register(pdata, mdio_bus); if (ret) { netdev_err(ndev, "Failed to register MDIO bus\n"); mdiobus_free(mdio_bus); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 3c208cc..6370ff4 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -746,6 +746,42 @@ static const struct net_device_ops xgene_ndev_ops = { .ndo_set_mac_address = xgene_enet_set_mac_address, }; +#ifdef CONFIG_ACPI +static int acpi_get_mac_address(struct device *dev, + unsigned char *addr) +{ + int ret; + + ret = device_property_read_u8_array(dev, "mac-address", addr, 6); + if (ret) + return 0; + + return 6; +} + +static int acpi_get_phy_mode(struct device *dev) +{ + int i, ret, phy_mode; + char *modestr; + + ret = device_property_read_string(dev, "phy-mode", &modestr); + if (ret) + return -1; + + phy_mode = -1; + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(modestr, phy_modes(i))) { + phy_mode = i; + break; + } + } + return phy_mode; +} +#else +#define acpi_get_mac_address(a, b, c) 0 +#define acpi_get_phy_mode(a) -1 +#endif + static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) { struct platform_device *pdev; @@ -761,6 +797,8 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) ndev = pdata->ndev; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "enet_csr"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "Resource enet_csr not defined\n"); return -ENODEV; @@ -772,6 +810,8 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ring_csr"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { dev_err(dev, "Resource ring_csr not defined\n"); return -ENODEV; @@ -783,6 +823,8 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ring_cmd"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); if (!res) { dev_err(dev, "Resource ring_cmd not defined\n"); return -ENODEV; @@ -804,11 +846,13 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) mac = of_get_mac_address(dev->of_node); if (mac) memcpy(ndev->dev_addr, mac, ndev->addr_len); - else + else if (!acpi_get_mac_address(dev, ndev->dev_addr)) eth_hw_addr_random(ndev); memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); pdata->phy_mode = of_get_phy_mode(pdev->dev.of_node); + if (pdata->phy_mode < 0) + pdata->phy_mode = acpi_get_phy_mode(dev); if (pdata->phy_mode < 0) { dev_err(dev, "Unable to get phy-connection-type\n"); return pdata->phy_mode; @@ -821,11 +865,12 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } pdata->clk = devm_clk_get(&pdev->dev, NULL); - ret = IS_ERR(pdata->clk); if (IS_ERR(pdata->clk)) { - dev_err(&pdev->dev, "can't get clock\n"); - ret = PTR_ERR(pdata->clk); - return ret; + /* + * Not necessarily an error. Firmware may have + * set up the clock already. + */ + pdata->clk = NULL; } base_addr = pdata->base_addr; @@ -873,7 +918,7 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) pdata->port_ops->cle_bypass(pdata, dst_ring_num, buf_pool->id); pdata->mac_ops->init(pdata); - return ret; + return 0; } static void xgene_enet_setup_ops(struct xgene_enet_pdata *pdata) @@ -934,7 +979,7 @@ static int xgene_enet_probe(struct platform_device *pdev) goto err; } - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (ret) { netdev_err(ndev, "No usable DMA configuration\n"); goto err; @@ -981,6 +1026,14 @@ static int xgene_enet_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_enet_acpi_match[] = { + { "APMC0D05", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match); +#endif + static struct of_device_id xgene_enet_match[] = { {.compatible = "apm,xgene-enet",}, {}, @@ -992,6 +1045,7 @@ static struct platform_driver xgene_enet_driver = { .driver = { .name = "xgene-enet", .of_match_table = xgene_enet_match, + .acpi_match_table = ACPI_PTR(xgene_enet_acpi_match), }, .probe = xgene_enet_probe, .remove = xgene_enet_remove, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index 874e5a0..8b7e2cf 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -31,6 +31,7 @@ #include #include #include +#include #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 5e94d00..a6131a9 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -81,6 +81,7 @@ static const char version[] = #include #include #include +#include #include #include @@ -2405,6 +2406,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, @@ -2413,6 +2422,7 @@ static struct platform_driver smc_driver = { .owner = THIS_MODULE, .pm = &smc_drv_pm_ops, .of_match_table = of_match_ptr(smc91x_match), + .acpi_match_table = ACPI_PTR(smc91x_acpi_match), }, }; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 75472cf7..bacafe2 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -26,7 +26,7 @@ config AMD_PHY config AMD_XGBE_PHY tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs" - depends on OF + depends on OF || ACPI ---help--- Currently supports the AMD 10GbE PHY diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c index c456559..d852c6e 100644 --- a/drivers/net/phy/amd-xgbe-phy.c +++ b/drivers/net/phy/amd-xgbe-phy.c @@ -74,15 +74,19 @@ #include #include #include +#include + MODULE_AUTHOR("Tom Lendacky "); 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 2305dc0..43999a8 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1250,6 +1250,39 @@ int of_property_read_u64(const struct device_node *np, const char *propname, EXPORT_SYMBOL_GPL(of_property_read_u64); /** + * of_property_read_u64_array - Find and read an array of 64 bit integers + * from a property. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_values: pointer to return value, modified only if return value is 0. + * @sz: number of array elements to read + * + * Search for a property in a device node and read 64-bit value(s) from + * it. Returns 0 on success, -EINVAL if the property does not exist, + * -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + * + * The out_values is modified only if a valid u64 value can be decoded. + */ +int of_property_read_u64_array(const struct device_node *np, + const char *propname, u64 *out_values, + size_t sz) +{ + const __be32 *val = of_find_property_value_of_size(np, propname, + (sz * sizeof(*out_values))); + + if (IS_ERR(val)) + return PTR_ERR(val); + + while (sz--) { + *out_values++ = of_read_number(val, 2); + val += 2; + } + return 0; +} + +/** * of_property_read_string - Find and read a string from a property * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. @@ -1397,6 +1430,49 @@ int of_property_count_strings(struct device_node *np, const char *propname) } EXPORT_SYMBOL_GPL(of_property_count_strings); +/** + * of_property_read_string_array - Find and read an array of strings + * from a multiple strings property. + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_string: pointer to null terminated return string, modified only if + * return value is 0. + * @sz: number of array elements to read + * + * Search for a property in a device tree node and retrieve a list of + * terminated string value (pointer to data, not a copy) in that property. + * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if + * property does not have a value, and -EOVERFLOW if the string is not + * null-terminated within the length of the property data. + * + * The out_string pointer is modified only if a valid string can be decoded. + */ +int of_property_read_string_array(struct device_node *np, const char *propname, + char **output, size_t sz) +{ + struct property *prop = of_find_property(np, propname, NULL); + int i = 0; + size_t l = 0, total = 0; + char *p; + + if (!prop) + return -EINVAL; + + if (!prop->value) + return -ENODATA; + + if (strnlen(prop->value, prop->length) >= prop->length) + return -EOVERFLOW; + + p = prop->value; + + for (i = 0; total < prop->length; total += l, p += l) { + output[i++] = p; + l = strlen(p) + 1; + } + return 0; +} + void of_print_phandle_args(const char *msg, const struct of_phandle_args *args) { int i; @@ -2186,3 +2262,113 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node) return of_get_next_parent(np); } EXPORT_SYMBOL(of_graph_get_remote_port); + +int of_dev_prop_get(struct device_node *dn, const char *propname, void **valptr) +{ + struct property *pp = of_find_property(dn, propname, NULL); + + if (!pp) + return -ENODATA; + + if (valptr) + *valptr = pp->value; + return 0; +} + +int of_dev_prop_read(struct device_node *dn, const char *propname, + enum dev_prop_type proptype, void *val) +{ + void *value; + int ret = of_dev_prop_get(dn, propname, &value); + + if (ret) + return ret; + + if (proptype >= DEV_PROP_U8 && proptype <= DEV_PROP_U64) { + switch (proptype) { + case DEV_PROP_U8: { + *(u8 *)val = *(u8 *)value; + break; + } + case DEV_PROP_U16: + *(u16 *)val = *(u16 *)value; + break; + case DEV_PROP_U32: + *(u32 *)val = *(u32 *)value; + break; + default: + *(u64 *)val = *(u64 *)value; + break; + } + } else if (proptype == DEV_PROP_STRING) { + *(char **)val = value; + } + return ret; + +} + +int of_dev_prop_read_array(struct device_node *dn, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval) +{ + int ret, elem_size; + + if (!val) { + switch (proptype) { + case DEV_PROP_U8: + elem_size = sizeof(u8); + break; + case DEV_PROP_U16: + elem_size = sizeof(u16); + break; + case DEV_PROP_U32: + elem_size = sizeof(u32); + break; + case DEV_PROP_U64: + elem_size = sizeof(u64); + break; + case DEV_PROP_STRING: + return of_property_count_strings(dn, propname); + default: + return -EINVAL; + } + return of_property_count_elems_of_size(dn, propname, elem_size); + } + + switch (proptype) { + case DEV_PROP_U8: + ret = of_property_read_u8_array(dn, propname, (u8 *)val, nval); + break; + case DEV_PROP_U16: + ret = of_property_read_u16_array(dn, propname, (u16 *)val, nval); + break; + case DEV_PROP_U32: + ret = of_property_read_u32_array(dn, propname, (u32 *)val, nval); + break; + case DEV_PROP_U64: + ret = of_property_read_u64_array(dn, propname, (u64 *)val, nval); + break; + case DEV_PROP_STRING: + ret = of_property_read_string_array(dn, propname, + (char **)val, nval); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +int of_for_each_child_node(struct device *dev, + int (*fn)(struct device *dev, void *child, void *data), + void *data) +{ + struct device_node *child; + int ret = 0; + + for_each_child_of_node(dev->of_node, child) { + ret = fn(dev, child, data); + if (ret) + break; + } + return ret; +} diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index 782e822..d952462 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -313,6 +313,7 @@ static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci, progif = class & 0xff; class >>= 8; +#ifdef HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ if (class == PCI_CLASS_STORAGE_IDE) { /* * Unless both channels are native-PCI mode only, @@ -326,6 +327,7 @@ static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci, return 1; } } +#endif /* HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ */ return 0; } diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index b24aa01..50fe279 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -419,4 +419,10 @@ config DA_CONSOLE help This enables a console on a Dash channel. +config SBSAUART_TTY + tristate "SBSA UART TTY Driver" + help + Console and system TTY driver for the SBSA UART which is defined + in the Server Base System Architecure document for ARM64 servers. + endif # TTY diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 58ad1c0..c3211c0 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -29,5 +29,6 @@ obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_DA_TTY) += metag_da.o +obj-$(CONFIG_SBSAUART_TTY) += sbsauart.o obj-y += ipwireless/ diff --git a/drivers/tty/sbsauart.c b/drivers/tty/sbsauart.c new file mode 100644 index 0000000..402f168 --- /dev/null +++ b/drivers/tty/sbsauart.c @@ -0,0 +1,355 @@ +/* + * SBSA (Server Base System Architecture) Compatible UART driver + * + * Copyright (C) 2014 Linaro Ltd + * + * Author: Graeme Gregory + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct sbsa_tty { + struct tty_port port; + spinlock_t lock; + void __iomem *base; + u32 irq; + int opencount; + struct console console; +}; + +static struct tty_driver *sbsa_tty_driver; +static struct sbsa_tty *sbsa_tty; + +#define SBSAUART_CHAR_MASK 0xFF + +static void sbsa_raw_putc(struct uart_port *port, int c) +{ + while (readw(port->membase + UART01x_FR) & UART01x_FR_TXFF) + ; + writew(c & 0xFF, port->membase + UART01x_DR); +} + +static void sbsa_uart_early_write(struct console *con, const char *buf, + unsigned count) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, buf, count, sbsa_raw_putc); +} + +static int __init sbsa_uart_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = sbsa_uart_early_write; + return 0; +} +EARLYCON_DECLARE(sbsauart, sbsa_uart_early_console_setup); + +static void sbsa_tty_do_write(const char *buf, unsigned count) +{ + unsigned long irq_flags; + struct sbsa_tty *qtty = sbsa_tty; + void __iomem *base = qtty->base; + unsigned n; + + spin_lock_irqsave(&qtty->lock, irq_flags); + for (n = 0; n < count; n++) { + while (readw(base + UART01x_FR) & UART01x_FR_TXFF) + ; + writew(buf[n], base + UART01x_DR); + } + spin_unlock_irqrestore(&qtty->lock, irq_flags); +} + +static void sbsauart_fifo_to_tty(struct sbsa_tty *qtty) +{ + void __iomem *base = qtty->base; + unsigned int flag, max_count = 32; + u16 status, ch; + + while (max_count--) { + status = readw(base + UART01x_FR); + if (status & UART01x_FR_RXFE) + break; + + /* Take chars from the FIFO and update status */ + ch = readw(base + UART01x_DR); + flag = TTY_NORMAL; + + if (ch & UART011_DR_BE) + flag = TTY_BREAK; + else if (ch & UART011_DR_PE) + flag = TTY_PARITY; + else if (ch & UART011_DR_FE) + flag = TTY_FRAME; + else if (ch & UART011_DR_OE) + flag = TTY_OVERRUN; + + ch &= SBSAUART_CHAR_MASK; + + tty_insert_flip_char(&qtty->port, ch, flag); + } + + tty_schedule_flip(&qtty->port); + + /* Clear the RX IRQ */ + writew(UART011_RXIC | UART011_RXIC, base + UART011_ICR); +} + +static irqreturn_t sbsa_tty_interrupt(int irq, void *dev_id) +{ + struct sbsa_tty *qtty = sbsa_tty; + unsigned long irq_flags; + + spin_lock_irqsave(&qtty->lock, irq_flags); + sbsauart_fifo_to_tty(qtty); + spin_unlock_irqrestore(&qtty->lock, irq_flags); + + return IRQ_HANDLED; +} + +static int sbsa_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct sbsa_tty *qtty = sbsa_tty; + + return tty_port_open(&qtty->port, tty, filp); +} + +static void sbsa_tty_close(struct tty_struct *tty, struct file *filp) +{ + tty_port_close(tty->port, tty, filp); +} + +static void sbsa_tty_hangup(struct tty_struct *tty) +{ + tty_port_hangup(tty->port); +} + +static int sbsa_tty_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + sbsa_tty_do_write(buf, count); + return count; +} + +static int sbsa_tty_write_room(struct tty_struct *tty) +{ + return 32; +} + +static void sbsa_tty_console_write(struct console *co, const char *b, + unsigned count) +{ + sbsa_tty_do_write(b, count); + + if (b[count - 1] == '\n') + sbsa_tty_do_write("\r", 1); +} + +static struct tty_driver *sbsa_tty_console_device(struct console *c, + int *index) +{ + *index = c->index; + return sbsa_tty_driver; +} + +static int sbsa_tty_console_setup(struct console *co, char *options) +{ + if ((unsigned)co->index > 0) + return -ENODEV; + if (sbsa_tty->base == NULL) + return -ENODEV; + return 0; +} + +static struct tty_port_operations sbsa_port_ops = { +}; + +static const struct tty_operations sbsa_tty_ops = { + .open = sbsa_tty_open, + .close = sbsa_tty_close, + .hangup = sbsa_tty_hangup, + .write = sbsa_tty_write, + .write_room = sbsa_tty_write_room, +}; + +static int sbsa_tty_create_driver(void) +{ + int ret; + struct tty_driver *tty; + + sbsa_tty = kzalloc(sizeof(*sbsa_tty), GFP_KERNEL); + if (sbsa_tty == NULL) { + ret = -ENOMEM; + goto err_alloc_sbsa_tty_failed; + } + tty = alloc_tty_driver(1); + if (tty == NULL) { + ret = -ENOMEM; + goto err_alloc_tty_driver_failed; + } + tty->driver_name = "sbsauart"; + tty->name = "ttySBSA"; + tty->type = TTY_DRIVER_TYPE_SERIAL; + tty->subtype = SERIAL_TYPE_NORMAL; + tty->init_termios = tty_std_termios; + tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(tty, &sbsa_tty_ops); + ret = tty_register_driver(tty); + if (ret) + goto err_tty_register_driver_failed; + + sbsa_tty_driver = tty; + return 0; + +err_tty_register_driver_failed: + put_tty_driver(tty); +err_alloc_tty_driver_failed: + kfree(sbsa_tty); + sbsa_tty = NULL; +err_alloc_sbsa_tty_failed: + return ret; +} + +static void sbsa_tty_delete_driver(void) +{ + tty_unregister_driver(sbsa_tty_driver); + put_tty_driver(sbsa_tty_driver); + sbsa_tty_driver = NULL; + kfree(sbsa_tty); + sbsa_tty = NULL; +} + +static int sbsa_tty_probe(struct platform_device *pdev) +{ + struct sbsa_tty *qtty; + int ret = -EINVAL; + int i; + struct resource *r; + struct device *ttydev; + void __iomem *base; + u32 irq; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) + return -EINVAL; + + base = ioremap(r->start, r->end - r->start); + if (base == NULL) + pr_err("sbsa_tty: unable to remap base\n"); + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (r == NULL) + goto err_unmap; + + irq = r->start; + + if (pdev->id > 0) + goto err_unmap; + + ret = sbsa_tty_create_driver(); + if (ret) + goto err_unmap; + + qtty = sbsa_tty; + spin_lock_init(&qtty->lock); + tty_port_init(&qtty->port); + qtty->port.ops = &sbsa_port_ops; + qtty->base = base; + qtty->irq = irq; + + /* Clear and Mask all IRQs */ + writew(0, base + UART011_IMSC); + writew(0xFFFF, base + UART011_ICR); + + ret = request_irq(irq, sbsa_tty_interrupt, IRQF_SHARED, + "sbsa_tty", pdev); + if (ret) + goto err_request_irq_failed; + + /* Unmask the RX IRQ */ + writew(UART011_RXIM | UART011_RTIM, base + UART011_IMSC); + + ttydev = tty_port_register_device(&qtty->port, sbsa_tty_driver, + 0, &pdev->dev); + if (IS_ERR(ttydev)) { + ret = PTR_ERR(ttydev); + goto err_tty_register_device_failed; + } + + strcpy(qtty->console.name, "ttySBSA"); + qtty->console.write = sbsa_tty_console_write; + qtty->console.device = sbsa_tty_console_device; + qtty->console.setup = sbsa_tty_console_setup; + qtty->console.flags = CON_PRINTBUFFER; + qtty->console.index = pdev->id; + register_console(&qtty->console); + + return 0; + + tty_unregister_device(sbsa_tty_driver, i); +err_tty_register_device_failed: + free_irq(irq, pdev); +err_request_irq_failed: + sbsa_tty_delete_driver(); +err_unmap: + iounmap(base); + return ret; +} + +static int sbsa_tty_remove(struct platform_device *pdev) +{ + struct sbsa_tty *qtty; + + qtty = sbsa_tty; + unregister_console(&qtty->console); + tty_unregister_device(sbsa_tty_driver, pdev->id); + iounmap(qtty->base); + qtty->base = 0; + free_irq(qtty->irq, pdev); + sbsa_tty_delete_driver(); + return 0; +} + +static const struct acpi_device_id sbsa_acpi_match[] = { + { "ARMH0011", 0 }, + { } +}; + +static struct platform_driver sbsa_tty_platform_driver = { + .probe = sbsa_tty_probe, + .remove = sbsa_tty_remove, + .driver = { + .name = "sbsa_tty", + .acpi_match_table = ACPI_PTR(sbsa_acpi_match), + } +}; + +module_platform_driver(sbsa_tty_platform_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index beea6ca..7038a2d 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -310,10 +310,18 @@ static int dw8250_probe_of(struct uart_port *p, static int dw8250_probe_acpi(struct uart_8250_port *up, struct dw8250_data *data) { + const struct acpi_device_id *id; struct uart_port *p = &up->port; dw8250_setup_port(up); + id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); + if (!id) + return -ENODEV; + + if (!p->uartclk) + p->uartclk = (unsigned int)id->driver_data; + p->iotype = UPIO_MEM32; p->serial_in = dw8250_serial_in32; p->serial_out = dw8250_serial_out32; @@ -536,6 +544,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = { { "INT3435", 0 }, { "80860F0A", 0 }, { "8086228A", 0 }, + { "APMC0D08", 50000000}, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index ef9a165..9f1939c 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -100,8 +100,7 @@ #include #include #include - - +#include /* 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/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 57ee052..a1ef42f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -68,6 +68,8 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs); union acpi_object *acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, int rev, int func, union acpi_object *argv4); +acpi_status acpi_check_coherency(acpi_handle handle, int *val); + static inline union acpi_object * acpi_evaluate_dsm_typed(acpi_handle handle, const u8 *uuid, int rev, int func, union acpi_object *argv4, acpi_object_type type) @@ -337,6 +339,13 @@ 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; +}; + /* Device */ struct acpi_device { int device_type; @@ -353,6 +362,7 @@ 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; diff --git a/include/acpi/acpi_io.h b/include/acpi/acpi_io.h index 444671e..9d573db 100644 --- a/include/acpi/acpi_io.h +++ b/include/acpi/acpi_io.h @@ -1,11 +1,17 @@ #ifndef _ACPI_IO_H_ #define _ACPI_IO_H_ +#include #include 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/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 206dcc3..660dbfc 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -289,17 +289,19 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, #define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel)) #define vgic_initialized(k) ((k)->arch.vgic.ready) -int vgic_v2_probe(struct device_node *vgic_node, - const struct vgic_ops **ops, - const struct vgic_params **params); +int vgic_v2_dt_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params); +int vgic_v2_acpi_probe(const struct vgic_ops **ops, + const struct vgic_params **params); #ifdef CONFIG_ARM_GIC_V3 -int vgic_v3_probe(struct device_node *vgic_node, - const struct vgic_ops **ops, - const struct vgic_params **params); +int vgic_v3_dt_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params); #else -static inline int vgic_v3_probe(struct device_node *vgic_node, - const struct vgic_ops **ops, - const struct vgic_params **params) +static inline int vgic_v3_dt_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params) { return -ENODEV; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index b7926bb..6c22abf 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -28,6 +28,7 @@ #include #include /* for struct resource */ #include +#include #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); @@ -658,4 +660,85 @@ do { \ #endif #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, + const char *cells_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(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val); +int acpi_dev_prop_read_array(struct acpi_device *adev, const char *propname, + enum dev_prop_type proptype, void *val, + size_t nval); +int acpi_for_each_child_node(struct device *dev, + int (*fn)(struct device *dev, void *child, void *data), + void *data); +#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(struct acpi_device *adev, + const char *propname, + enum dev_prop_type proptype, void *val) +{ + return -ENXIO; +} + +static inline int acpi_dev_prop_read_array(struct acpi_device *adev, + const char *propname, + enum dev_prop_type proptype, + void *val, size_t nval) +{ + return -ENXIO; +} + +static inline int acpi_for_each_child_node(struct device *dev, + int (*fn)(struct device *dev, void *child, void *data), + void *data) +{ + return -ENXIO; +} + +#endif + #endif /*_LINUX_ACPI_H*/ diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 653f0e2..5839f98 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -346,4 +346,10 @@ extern void clocksource_of_init(void); static inline void clocksource_of_init(void) {} #endif +#ifdef CONFIG_ACPI +void acpi_generic_timer_init(void); +#else +static inline void acpi_generic_timer_init(void) {} +#endif + #endif /* _LINUX_CLOCKSOURCE_H */ diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 12f146f..033d2fd 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -94,6 +94,11 @@ 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 gpio_desc *dev_get_named_gpiod_from_child(struct device *dev, void *child, + const char *propname, int index); +struct gpio_desc *devm_get_named_gpiod_from_child(struct device *dev, void *child, + const char *propname, int index); #else /* CONFIG_GPIOLIB */ static inline struct gpio_desc *__must_check __gpiod_get(struct device *dev, diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index 8b62246..ee2d8c6 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -2,6 +2,7 @@ #define _GPIO_KEYS_H struct device; +struct gpio_desc; /** * struct gpio_keys_button - configuration parameters @@ -17,6 +18,7 @@ struct device; * disable button via sysfs * @value: axis value for %EV_ABS * @irq: Irq number in case of interrupt keys + * @gpiod: GPIO descriptor */ struct gpio_keys_button { unsigned int code; @@ -29,6 +31,7 @@ struct gpio_keys_button { bool can_disable; int value; unsigned int irq; + struct gpio_desc *gpiod; }; /** diff --git a/include/linux/irqchip/arm-gic-acpi.h b/include/linux/irqchip/arm-gic-acpi.h new file mode 100644 index 0000000..ad5b577 --- /dev/null +++ b/include/linux/irqchip/arm-gic-acpi.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014, Linaro Ltd. + * Author: Tomasz Nowicki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ARM_GIC_ACPI_H_ +#define ARM_GIC_ACPI_H_ + +#ifdef CONFIG_ACPI + +/* + * Hard code here, we can not get memory size from MADT (but FDT does), + * Actually no need to do that, because this size can be inferred + * from GIC spec. + */ +#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K) +#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K) + +struct acpi_table_header; + +void acpi_gic_init(void); +int gic_v2_acpi_init(struct acpi_table_header *table); +#else +static inline void acpi_gic_init(void) { } +#endif + +#endif /* ARM_GIC_ACPI_H_ */ diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 13eed92..dc9cb5f 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -55,6 +55,8 @@ (GICD_INT_DEF_PRI << 8) |\ GICD_INT_DEF_PRI) +#define GIC_DIST_SOFTINT_NSATT 0x8000 + #define GICH_HCR 0x0 #define GICH_VTR 0x4 #define GICH_VMCR 0x8 diff --git a/include/linux/leds.h b/include/linux/leds.h index e436864..879a113 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -246,6 +246,7 @@ struct led_platform_data { struct gpio_led { const char *name; const char *default_trigger; + struct gpio_desc *gpiod; unsigned gpio; unsigned active_low : 1; unsigned retain_state_suspended : 1; diff --git a/include/linux/of.h b/include/linux/of.h index 6545e7a..9962b70 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -355,6 +356,15 @@ const char *of_prop_next_string(struct property *prop, const char *cur); bool of_console_check(struct device_node *dn, char *name, int index); +int of_dev_prop_get(struct device_node *dn, const char *propname, void **valptr); +int of_dev_prop_read(struct device_node *dn, const char *propname, + enum dev_prop_type proptype, void *val); +int of_dev_prop_read_array(struct device_node *dn, const char *propname, + enum dev_prop_type proptype, void *val, size_t nval); +int of_for_each_child_node(struct device *dev, + int (*fn)(struct device *dev, void *child, void *data), + void *data); + #else /* CONFIG_OF */ static inline const char* of_node_full_name(const struct device_node *np) @@ -582,6 +592,33 @@ static inline const char *of_prop_next_string(struct property *prop, return NULL; } +static inline int of_dev_prop_get(struct device_node *dn, const char *propname, + void **valptr) +{ + return -ENXIO; +} + +static inline int of_dev_prop_read(struct device_node *dn, const char *propname, + enum dev_prop_type proptype, void *val) +{ + return -ENXIO; +} + +static inline int of_dev_prop_read_array(struct device_node *dn, + const char *propname, + enum dev_prop_type proptype, + void *val, size_t nval) +{ + return -ENXIO; +} + +static inline int of_for_each_child_node(struct device *dev, + int (*fn)(struct device *dev, void *child, void *data), + void *data) +{ + return -ENXIO; +} + #define of_match_ptr(_ptr) NULL #define of_match_node(_matches, _node) NULL #endif /* CONFIG_OF */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 5be8db4..6afba72 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -562,15 +562,6 @@ struct pci_ops { int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); }; -/* - * ACPI needs to be able to access PCI config space before we've done a - * PCI bus scan and created pci_bus structures. - */ -int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, - int reg, int len, u32 *val); -int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, - int reg, int len, u32 val); - struct pci_bus_region { dma_addr_t start; dma_addr_t end; @@ -1325,6 +1316,16 @@ typedef int (*arch_set_vga_state_t)(struct pci_dev *pdev, bool decode, unsigned int command_bits, u32 flags); void pci_register_set_vga_state(arch_set_vga_state_t func); +/* + * ACPI needs to be able to access PCI config space before we've done a + * PCI bus scan and created pci_bus structures. + */ +int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, + int reg, int len, u32 *val); +int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, + int reg, int len, u32 val); +void pcibios_penalize_isa_irq(int irq, int active); + #else /* CONFIG_PCI is not enabled */ /* @@ -1426,6 +1427,23 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, unsigned int devfn) { return NULL; } +static inline struct pci_bus *pci_find_bus(int domain, int busnr) +{ return NULL; } + +static inline int pci_bus_write_config_byte(struct pci_bus *bus, + unsigned int devfn, int where, u8 val) +{ return -ENOSYS; } + +static inline int raw_pci_read(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) +{ return -ENOSYS; } + +static inline int raw_pci_write(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) +{ return -ENOSYS; } + +static inline void pcibios_penalize_isa_irq(int irq, int active) { } + static inline int pci_domain_nr(struct pci_bus *bus) { return 0; } static inline struct pci_dev *pci_dev_get(struct pci_dev *dev) { return NULL; } static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } @@ -1635,7 +1653,6 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state); int pcibios_add_device(struct pci_dev *dev); void pcibios_release_device(struct pci_dev *dev); -void pcibios_penalize_isa_irq(int irq, int active); #ifdef CONFIG_HIBERNATE_CALLBACKS extern struct dev_pm_ops pcibios_pm_ops; diff --git a/include/linux/property.h b/include/linux/property.h new file mode 100644 index 0000000..1a42878 --- /dev/null +++ b/include/linux/property.h @@ -0,0 +1,207 @@ +/* + * property.h - Unified device property interface. + * + * Copyright (C) 2014, Intel Corporation + * Authors: Rafael J. Wysocki + * Mika Westerberg + * + * 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 + +enum dev_prop_type { + DEV_PROP_U8, + DEV_PROP_U16, + DEV_PROP_U32, + DEV_PROP_U64, + DEV_PROP_STRING, + DEV_PROP_MAX, +}; + +int device_get_property(struct device *dev, const char *propname, + void **valptr); +int device_read_property(struct device *dev, const char *propname, + enum dev_prop_type proptype, void *val); +int device_read_property_array(struct device *dev, const char *propname, + enum dev_prop_type proptype, void *val, + size_t nval); +int device_get_child_property(struct device *dev, void *child, + const char *propname, void **valptr); +int device_read_child_property(struct device *dev, void *child, + const char *propname, + enum dev_prop_type proptype, void *val); +int device_read_child_property_array(struct device *dev, void *child, + const char *propname, + enum dev_prop_type proptype, void *val, + size_t nval); +int device_for_each_child_node(struct device *dev, + int (*fn)(struct device *dev, void *child, void *data), + void *data); +unsigned int device_get_child_node_count(struct device *dev); + +static inline int device_property_read_u8(struct device *dev, + const char *propname, u8 *out_value) +{ + return device_read_property(dev, propname, DEV_PROP_U8, out_value); +} + +static inline int device_property_read_u16(struct device *dev, + const char *propname, u16 *out_value) +{ + return device_read_property(dev, propname, DEV_PROP_U16, out_value); +} + +static inline int device_property_read_u32(struct device *dev, + const char *propname, u32 *out_value) +{ + return device_read_property(dev, propname, DEV_PROP_U32, out_value); +} + +static inline int device_property_read_u64(struct device *dev, + const char *propname, u64 *out_value) +{ + return device_read_property(dev, propname, DEV_PROP_U64, out_value); +} + +static inline int device_property_read_u8_array(struct device *dev, + const char *propname, + u8 *val, size_t nval) +{ + return device_read_property_array(dev, propname, DEV_PROP_U8, val, + nval); +} + +static inline int device_property_read_u16_array(struct device *dev, + const char *propname, + u16 *val, size_t nval) +{ + return device_read_property_array(dev, propname, DEV_PROP_U16, val, + nval); +} + +static inline int device_property_read_u32_array(struct device *dev, + const char *propname, + u32 *val, size_t nval) +{ + return device_read_property_array(dev, propname, DEV_PROP_U32, val, + nval); +} + +static inline int device_property_read_u64_array(struct device *dev, + const char *propname, + u64 *val, size_t nval) +{ + return device_read_property_array(dev, propname, DEV_PROP_U64, val, + nval); +} + +static inline int device_property_read_string(struct device *dev, + const char *propname, + const char **out_string) +{ + return device_read_property(dev, propname, DEV_PROP_STRING, out_string); +} + +static inline int device_property_read_string_array(struct device *dev, + const char *propname, + const char **out_strings, + size_t nstrings) +{ + return device_read_property_array(dev, propname, DEV_PROP_STRING, + out_strings, nstrings); +} + +static inline int device_child_property_read_u8(struct device *dev, void *child, + const char *propname, + u8 *out_value) +{ + return device_read_child_property(dev, child, propname, DEV_PROP_U8, + out_value); +} + +static inline int device_child_property_read_u16(struct device *dev, void *child, + const char *propname, + u16 *out_value) +{ + return device_read_child_property(dev, child, propname, DEV_PROP_U16, + out_value); +} + +static inline int device_child_property_read_u32(struct device *dev, void *child, + const char *propname, + u32 *out_value) +{ + return device_read_child_property(dev, child, propname, DEV_PROP_U32, + out_value); +} + +static inline int device_child_property_read_u64(struct device *dev, void *child, + const char *propname, + u64 *out_value) +{ + return device_read_child_property(dev, child, propname, DEV_PROP_U64, + out_value); +} + +static inline int device_child_property_read_u8_array(struct device *dev, + void *child, + const char *propname, + u8 *val, size_t nval) +{ + return device_read_child_property_array(dev, child, propname, + DEV_PROP_U8, val, nval); +} + +static inline int device_child_property_read_u16_array(struct device *dev, + void *child, + const char *propname, + u16 *val, size_t nval) +{ + return device_read_child_property_array(dev, child, propname, + DEV_PROP_U16, val, nval); +} + +static inline int device_child_property_read_u32_array(struct device *dev, + void *child, + const char *propname, + u32 *val, size_t nval) +{ + return device_read_child_property_array(dev, child, propname, + DEV_PROP_U32, val, nval); +} + +static inline int device_child_property_read_u64_array(struct device *dev, + void *child, + const char *propname, + u64 *val, size_t nval) +{ + return device_read_child_property_array(dev, child, propname, + DEV_PROP_U64, val, nval); +} + +static inline int device_child_property_read_string(struct device *dev, + void *child, + const char *propname, + const char **out_string) +{ + return device_read_child_property(dev, child, propname, DEV_PROP_STRING, + out_string); +} + +static inline int device_child_property_read_string_array(struct device *dev, + void *child, + const char *propname, + const char **out_strings, + size_t nstrings) +{ + return device_read_child_property_array(dev, child, propname, + DEV_PROP_STRING, + out_strings, nstrings); +} +#endif /* _LINUX_PROPERTY_H_ */ diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 22fa819..9cd5dbd 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -21,9 +21,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -244,60 +246,92 @@ static const struct of_device_id arch_timer_of_match[] = { {}, }; -int kvm_timer_hyp_init(void) +static int kvm_timer_ppi_parse_dt(unsigned int *ppi) { struct device_node *np; - unsigned int ppi; - int err; - - timecounter = arch_timer_get_timecounter(); - if (!timecounter) - return -ENODEV; np = of_find_matching_node(NULL, arch_timer_of_match); if (!np) { - kvm_err("kvm_arch_timer: can't find DT node\n"); return -ENODEV; } - ppi = irq_of_parse_and_map(np, 2); - if (!ppi) { - kvm_err("kvm_arch_timer: no virtual timer interrupt\n"); - err = -EINVAL; - goto out; + *ppi = irq_of_parse_and_map(np, 2); + if (*ppi == 0) { + of_node_put(np); + return -EINVAL; } - err = request_percpu_irq(ppi, kvm_arch_timer_handler, - "kvm guest timer", kvm_get_running_vcpus()); - if (err) { - kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n", - ppi, err); - goto out; - } + return 0; +} - host_vtimer_irq = ppi; +extern int acadia_kvm_acpi; +extern int arch_timer_ppi[]; - err = __register_cpu_notifier(&kvm_timer_cpu_nb); - if (err) { - kvm_err("Cannot register timer CPU notifier\n"); - goto out_free; - } +static int kvm_timer_ppi_parse_acpi(unsigned int *ppi) - wqueue = create_singlethread_workqueue("kvm_arch_timer"); - if (!wqueue) { - err = -ENOMEM; - goto out_free; - } +{ + /* retrieve VIRT_PPI info */ + *ppi = arch_timer_ppi[2]; - kvm_info("%s IRQ%d\n", np->name, ppi); - on_each_cpu(kvm_timer_init_interrupt, NULL, 1); + if (*ppi == 0) + return -EINVAL; + else + return 0; +} + +int kvm_timer_hyp_init(void) +{ + unsigned int ppi; + int err; + + timecounter = arch_timer_get_timecounter(); + if (!timecounter) + return -ENODEV; + + /* PPI DT parsing */ + err = kvm_timer_ppi_parse_dt(&ppi); - goto out; + /* if DT parsing fails, try ACPI next */ + if (err && !acpi_disabled && acadia_kvm_acpi ) + err = kvm_timer_ppi_parse_acpi(&ppi); + + if (err) { + kvm_err("kvm_timer_hyp_init: can't find virtual timer info or " + "config virtual timer interrupt\n"); + return err; + } + + /* configure IRQ handler */ + err = request_percpu_irq(ppi, kvm_arch_timer_handler, + "kvm guest timer", kvm_get_running_vcpus()); + if (err) { + kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n", + ppi, err); + goto out; + } + + host_vtimer_irq = ppi; + + err = __register_cpu_notifier(&kvm_timer_cpu_nb); + if (err) { + kvm_err("Cannot register timer CPU notifier\n"); + goto out_free; + } + + wqueue = create_singlethread_workqueue("kvm_arch_timer"); + if (!wqueue) { + err = -ENOMEM; + goto out_free; + } + + kvm_info("timer IRQ%d\n", ppi); + on_each_cpu(kvm_timer_init_interrupt, NULL, 1); + + goto out; out_free: - free_percpu_irq(ppi, kvm_get_running_vcpus()); + free_percpu_irq(ppi, kvm_get_running_vcpus()); out: - of_node_put(np); - return err; + return err; } void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c index 2935405..196f49e 100644 --- a/virt/kvm/arm/vgic-v2.c +++ b/virt/kvm/arm/vgic-v2.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include +#include #include #include #include @@ -159,7 +161,7 @@ static const struct vgic_ops vgic_v2_ops = { static struct vgic_params vgic_v2_params; /** - * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT + * vgic_v2_dt_probe - probe for a GICv2 compatible interrupt controller in DT * @node: pointer to the DT node * @ops: address of a pointer to the GICv2 operations * @params: address of a pointer to HW-specific parameters @@ -168,7 +170,7 @@ static struct vgic_params vgic_v2_params; * in *ops and the HW parameters in *params. Returns an error code * otherwise. */ -int vgic_v2_probe(struct device_node *vgic_node, +int vgic_v2_dt_probe(struct device_node *vgic_node, const struct vgic_ops **ops, const struct vgic_params **params) { @@ -245,3 +247,72 @@ out: of_node_put(vgic_node); return ret; } + +struct acpi_madt_generic_interrupt *vgic_acpi; +static void gic_get_acpi_header(struct acpi_subtable_header *header) +{ + vgic_acpi = (struct acpi_madt_generic_interrupt *)header; +} + +int vgic_v2_acpi_probe(const struct vgic_ops **ops, + const struct vgic_params **params) +{ + struct vgic_params *vgic = &vgic_v2_params; + int irq_mode, ret; + + /* MADT table */ + ret = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, + (acpi_tbl_entry_handler)gic_get_acpi_header, 0); + if (!ret) { + pr_err("Failed to get MADT VGIC CPU entry\n"); + ret = -ENODEV; + goto out; + } + + /* IRQ trigger mode */ + irq_mode = (vgic_acpi->flags & ACPI_MADT_VGIC_IRQ_MODE) ? + ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE; + /* According to GIC-400 manual, all PPIs are active-LOW, level + * sensative. We register IRQ as active-low. + */ + vgic->maint_irq = acpi_register_gsi(NULL, vgic_acpi->vgic_interrupt, + irq_mode, ACPI_ACTIVE_LOW); + if (!vgic->maint_irq) { + pr_err("Cannot register VGIC ACPI maintenance irq\n"); + ret = -ENXIO; + goto out; + } + + /* GICH resource */ + vgic->vctrl_base = ioremap(vgic_acpi->gich_base_address, SZ_8K); + if (!vgic->vctrl_base) { + pr_err("cannot ioremap GICH memory\n"); + ret = -ENOMEM; + goto out; + } + + vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR); + vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1; + + ret = create_hyp_io_mappings(vgic->vctrl_base, + vgic->vctrl_base + SZ_8K, + vgic_acpi->gich_base_address); + if (ret) { + kvm_err("Cannot map GICH into hyp\n"); + goto out; + } + + vgic->vcpu_base = vgic_acpi->gicv_base_address; + + kvm_info("GICH base=0x%llx, GICV base=0x%llx, IRQ=%d\n", + (unsigned long long)vgic_acpi->gich_base_address, + (unsigned long long)vgic_acpi->gicv_base_address, + vgic->maint_irq); + + vgic->type = VGIC_V2; + *ops = &vgic_v2_ops; + *params = vgic; + +out: + return ret; +} diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c index 1c2c8ee..8b56920 100644 --- a/virt/kvm/arm/vgic-v3.c +++ b/virt/kvm/arm/vgic-v3.c @@ -173,7 +173,7 @@ static const struct vgic_ops vgic_v3_ops = { static struct vgic_params vgic_v3_params; /** - * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT + * vgic_v3_dt_probe - probe for a GICv3 compatible interrupt controller in DT * @node: pointer to the DT node * @ops: address of a pointer to the GICv3 operations * @params: address of a pointer to HW-specific parameters @@ -182,9 +182,9 @@ static struct vgic_params vgic_v3_params; * in *ops and the HW parameters in *params. Returns an error code * otherwise. */ -int vgic_v3_probe(struct device_node *vgic_node, - const struct vgic_ops **ops, - const struct vgic_params **params) +int vgic_v3_dt_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params) { int ret = 0; u32 gicv_idx; diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 3aaca49..f6e9922 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -25,9 +25,11 @@ #include #include #include +#include #include +#include #include #include #include @@ -2427,31 +2429,39 @@ static struct notifier_block vgic_cpu_nb = { }; static const struct of_device_id vgic_ids[] = { - { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, }, - { .compatible = "arm,gic-v3", .data = vgic_v3_probe, }, + { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_dt_probe, }, + { .compatible = "arm,gic-v3", .data = vgic_v3_dt_probe, }, {}, }; +extern int acadia_kvm_acpi; + int kvm_vgic_hyp_init(void) { const struct of_device_id *matched_id; const int (*vgic_probe)(struct device_node *,const struct vgic_ops **, const struct vgic_params **); struct device_node *vgic_node; - int ret; + int ret = -ENODEV; - vgic_node = of_find_matching_node_and_match(NULL, - vgic_ids, &matched_id); - if (!vgic_node) { - kvm_err("error: no compatible GIC node found\n"); - return -ENODEV; + /* probe VGIC */ + if (vgic_node = of_find_matching_node_and_match(NULL, + vgic_ids, &matched_id)) { + /* probe VGIC in DT */ + vgic_probe = matched_id->data; + ret = vgic_probe(vgic_node, &vgic_ops, &vgic); + } + else if (!acpi_disabled && acadia_kvm_acpi) { + /* probe VGIC in ACPI */ + ret = vgic_v2_acpi_probe(&vgic_ops, &vgic); } - vgic_probe = matched_id->data; - ret = vgic_probe(vgic_node, &vgic_ops, &vgic); - if (ret) + if (ret) { + kvm_err("error: no compatible GIC info found\n"); return ret; + } + /* configuration */ ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler, "vgic", kvm_get_running_vcpus()); if (ret) {