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/devicetree/bindings/pci/xgene-pci.txt b/Documentation/devicetree/bindings/pci/xgene-pci.txt new file mode 100644 index 0000000..1070b06 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/xgene-pci.txt @@ -0,0 +1,57 @@ +* AppliedMicro X-Gene PCIe interface + +Required properties: +- device_type: set to "pci" +- compatible: should contain "apm,xgene-pcie" to identify the core. +- reg: A list of physical base address and length for each set of controller + registers. Must contain an entry for each entry in the reg-names + property. +- reg-names: Must include the following entries: + "csr": controller configuration registers. + "cfg": pcie configuration space registers. +- #address-cells: set to <3> +- #size-cells: set to <2> +- ranges: ranges for the outbound memory, I/O regions. +- dma-ranges: ranges for the inbound memory regions. +- #interrupt-cells: set to <1> +- interrupt-map-mask and interrupt-map: standard PCI properties + to define the mapping of the PCIe interface to interrupt + numbers. +- clocks: from common clock binding: handle to pci clock. + +Optional properties: +- status: Either "ok" or "disabled". +- dma-coherent: Present if dma operations are coherent + +Example: + +SoC specific DT Entry: + + pcie0: pcie@1f2b0000 { + status = "disabled"; + device_type = "pci"; + compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */ + 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */ + reg-names = "csr", "cfg"; + ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */ + 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */ + dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 + 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1 + 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1 + 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1 + 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>; + dma-coherent; + clocks = <&pcie0clk 0>; + }; + + +Board specific DT Entry: + &pcie0 { + status = "ok"; + }; diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 10d51c2..9464c6d 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -165,7 +165,7 @@ multipliers 'Kilo', 'Mega', and 'Giga', equalling 2^10, 2^20, and 2^30 bytes respectively. Such letter suffixes can also be entirely omitted. - acpi= [HW,ACPI,X86] + acpi= [HW,ACPI,X86,ARM] Advanced Configuration and Power Interface Format: { force | off | strict | noirq | rsdt } force -- enable ACPI if default was off @@ -175,6 +175,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. strictly ACPI specification compliant. rsdt -- prefer RSDT over (default) XSDT copy_dsdt -- copy DSDT to memory + For ARM64, ONLY "acpi=off" is available. See also Documentation/power/runtime_pm.txt, pci=noacpi diff --git a/MAINTAINERS b/MAINTAINERS index f10ed39..a8c9b14 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6946,6 +6946,14 @@ L: linux-pci@vger.kernel.org S: Maintained F: drivers/pci/host/*spear* +PCI DRIVER FOR APPLIEDMICRO XGENE +M: Tanmay Inamdar +L: linux-pci@vger.kernel.org +L: linux-arm-kernel@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/pci/xgene-pci.txt +F: drivers/pci/host/pci-xgene.c + PCMCIA SUBSYSTEM P: Linux PCMCIA Team L: linux-pcmcia@lists.infradead.org diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index 3d23418..1805674 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -178,6 +178,7 @@ static inline void __iomem *__typesafe_io(unsigned long addr) /* PCI fixed i/o mapping */ #define PCI_IO_VIRT_BASE 0xfee00000 +#define PCI_IOBASE ((void __iomem *)PCI_IO_VIRT_BASE) #if defined(CONFIG_PCI) void pci_ioremap_set_mem_type(int mem_type); diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h index 5cc0b0f..03a08bb 100644 --- a/arch/arm/include/asm/kvm_mmu.h +++ b/arch/arm/include/asm/kvm_mmu.h @@ -21,6 +21,7 @@ #include #include +#include /* * We directly use the kernel VA for the HYP, as we can directly share @@ -178,6 +179,18 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva, void stage2_flush_vm(struct kvm *kvm); +static inline int kvm_get_phys_addr_shift(void) +{ + return KVM_PHYS_SHIFT; +} + + +static inline u32 get_vttbr_baddr_mask(void) +{ + return VTTBR_BADDR_MASK; +} + + #endif /* !__ASSEMBLY__ */ #endif /* __ARM_KVM_MMU_H__ */ diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index a99e0cd..d0fca8f 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,12 @@ static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1); static u8 kvm_next_vmid; static DEFINE_SPINLOCK(kvm_vmid_lock); +#ifdef CONFIG_ARM64 +static u64 vttbr_baddr_mask; +#else +static u32 vttbr_baddr_mask; +#endif + static bool vgic_present; static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu) @@ -429,8 +436,14 @@ static void update_vttbr(struct kvm *kvm) /* update vttbr to be used with the new vmid */ pgd_phys = virt_to_phys(kvm->arch.pgd); vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK; - kvm->arch.vttbr = pgd_phys & VTTBR_BADDR_MASK; - kvm->arch.vttbr |= vmid; + + /* + * If the VTTBR isn't aligned there is something wrong with the system + * or kernel. + */ + BUG_ON(pgd_phys & ~vttbr_baddr_mask); + + kvm->arch.vttbr = pgd_phys | vmid; spin_unlock(&kvm_vmid_lock); } @@ -1015,6 +1028,12 @@ int kvm_arch_init(void *opaque) } } + vttbr_baddr_mask = get_vttbr_baddr_mask(); + if (vttbr_baddr_mask == ~0) { + kvm_err("Cannot set vttbr_baddr_mask\n"); + return -EINVAL; + } + cpu_notifier_register_begin(); err = init_hyp_mode(); diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c index 05e1f73..c186a17 100644 --- a/arch/arm/mach-integrator/pci_v3.c +++ b/arch/arm/mach-integrator/pci_v3.c @@ -660,6 +660,7 @@ static void __init pci_v3_preinit(void) { unsigned long flags; unsigned int temp; + phys_addr_t io_address = pci_pio_to_address(io_mem.start); pcibios_min_mem = 0x00100000; @@ -701,7 +702,7 @@ static void __init pci_v3_preinit(void) /* * Setup window 2 - PCI IO */ - v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_mem.start) | + v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_address) | V3_LB_BASE_ENABLE); v3_writew(V3_LB_MAP2, v3_addr_to_lb_map2(0)); @@ -742,6 +743,7 @@ static void __init pci_v3_preinit(void) static void __init pci_v3_postinit(void) { unsigned int pci_cmd; + phys_addr_t io_address = pci_pio_to_address(io_mem.start); pci_cmd = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; @@ -758,7 +760,7 @@ static void __init pci_v3_postinit(void) "interrupt: %d\n", ret); #endif - register_isa_ports(non_mem.start, io_mem.start, 0); + register_isa_ports(non_mem.start, io_address, 0); } /* @@ -867,33 +869,32 @@ static int __init pci_v3_probe(struct platform_device *pdev) for_each_of_pci_range(&parser, &range) { if (!range.flags) { - of_pci_range_to_resource(&range, np, &conf_mem); + ret = of_pci_range_to_resource(&range, np, &conf_mem); conf_mem.name = "PCIv3 config"; } if (range.flags & IORESOURCE_IO) { - of_pci_range_to_resource(&range, np, &io_mem); + ret = of_pci_range_to_resource(&range, np, &io_mem); io_mem.name = "PCIv3 I/O"; } if ((range.flags & IORESOURCE_MEM) && !(range.flags & IORESOURCE_PREFETCH)) { non_mem_pci = range.pci_addr; non_mem_pci_sz = range.size; - of_pci_range_to_resource(&range, np, &non_mem); + ret = of_pci_range_to_resource(&range, np, &non_mem); non_mem.name = "PCIv3 non-prefetched mem"; } if ((range.flags & IORESOURCE_MEM) && (range.flags & IORESOURCE_PREFETCH)) { pre_mem_pci = range.pci_addr; pre_mem_pci_sz = range.size; - of_pci_range_to_resource(&range, np, &pre_mem); + ret = of_pci_range_to_resource(&range, np, &pre_mem); pre_mem.name = "PCIv3 prefetched mem"; } - } - if (!conf_mem.start || !io_mem.start || - !non_mem.start || !pre_mem.start) { - dev_err(&pdev->dev, "missing ranges in device node\n"); - return -EINVAL; + if (ret < 0) { + dev_err(&pdev->dev, "missing ranges in device node\n"); + return ret; + } } pci_v3.map_irq = of_irq_parse_and_map_pci; diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index fd4e81a..e57b91a 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1,5 +1,6 @@ config ARM64 def_bool y + select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_SG_CHAIN select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST @@ -81,7 +82,7 @@ config MMU def_bool y config NO_IOPORT_MAP - def_bool y + def_bool y if !PCI config STACKTRACE_SUPPORT def_bool y @@ -156,6 +157,26 @@ menu "Bus support" config ARM_AMBA bool +config PCI + bool "PCI support" + help + This feature enables support for PCI bus system. If you say Y + here, the kernel will include drivers and infrastructure code + to support PCI bus devices. + +config PCI_DOMAINS + def_bool PCI + +config PCI_DOMAINS_GENERIC + def_bool PCI + +config PCI_SYSCALL + def_bool PCI + +source "drivers/pci/Kconfig" +source "drivers/pci/pcie/Kconfig" +source "drivers/pci/hotplug/Kconfig" + endmenu menu "Kernel Features" @@ -235,6 +256,9 @@ config SMP If you don't know what to do here, say N. +config ARM_PARKING_PROTOCOL + def_bool y if SMP + config SCHED_MC bool "Multi-core scheduler support" depends on SMP @@ -421,6 +445,8 @@ source "drivers/Kconfig" source "drivers/firmware/Kconfig" +source "drivers/acpi/Kconfig" + source "fs/Kconfig" source "arch/arm64/kvm/Kconfig" diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index 2df5e5d..bddd4e3 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -50,6 +50,7 @@ core-y += arch/arm64/kernel/ arch/arm64/mm/ core-$(CONFIG_KVM) += arch/arm64/kvm/ core-$(CONFIG_XEN) += arch/arm64/xen/ core-$(CONFIG_CRYPTO) += arch/arm64/crypto/ +drivers-$(CONFIG_PCI) += arch/arm64/pci/ libs-y := arch/arm64/lib/ $(libs-y) libs-y += $(LIBGCC) libs-$(CONFIG_EFI_STUB) += drivers/firmware/efi/libstub/ diff --git a/arch/arm64/boot/dts/apm-mustang.dts b/arch/arm64/boot/dts/apm-mustang.dts index b2f5622..f649000 100644 --- a/arch/arm64/boot/dts/apm-mustang.dts +++ b/arch/arm64/boot/dts/apm-mustang.dts @@ -25,6 +25,14 @@ }; }; +&pcie0clk { + status = "ok"; +}; + +&pcie0 { + status = "ok"; +}; + &serial0 { status = "ok"; }; diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi index c0aceef..403197a 100644 --- a/arch/arm64/boot/dts/apm-storm.dtsi +++ b/arch/arm64/boot/dts/apm-storm.dtsi @@ -269,6 +269,171 @@ enable-mask = <0x2>; clock-output-names = "rtcclk"; }; + + pcie0clk: pcie0clk@1f2bc000 { + status = "disabled"; + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + reg = <0x0 0x1f2bc000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "pcie0clk"; + }; + + pcie1clk: pcie1clk@1f2cc000 { + status = "disabled"; + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + reg = <0x0 0x1f2cc000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "pcie1clk"; + }; + + pcie2clk: pcie2clk@1f2dc000 { + status = "disabled"; + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + reg = <0x0 0x1f2dc000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "pcie2clk"; + }; + + pcie3clk: pcie3clk@1f50c000 { + status = "disabled"; + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + reg = <0x0 0x1f50c000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "pcie3clk"; + }; + + pcie4clk: pcie4clk@1f51c000 { + status = "disabled"; + compatible = "apm,xgene-device-clock"; + #clock-cells = <1>; + clocks = <&socplldiv2 0>; + reg = <0x0 0x1f51c000 0x0 0x1000>; + reg-names = "csr-reg"; + clock-output-names = "pcie4clk"; + }; + }; + + pcie0: pcie@1f2b0000 { + status = "disabled"; + device_type = "pci"; + compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */ + 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */ + reg-names = "csr", "cfg"; + ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */ + 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */ + dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 + 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1 + 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1 + 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1 + 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>; + dma-coherent; + clocks = <&pcie0clk 0>; + }; + + pcie1: pcie@1f2c0000 { + status = "disabled"; + device_type = "pci"; + compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = < 0x00 0x1f2c0000 0x0 0x00010000 /* Controller registers */ + 0xd0 0xd0000000 0x0 0x00040000>; /* PCI config space */ + reg-names = "csr", "cfg"; + ranges = <0x01000000 0x0 0x00000000 0xd0 0x10000000 0x00 0x00010000 /* io */ + 0x02000000 0x0 0x80000000 0xd1 0x80000000 0x00 0x80000000>; /* mem */ + dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 + 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc8 0x1 + 0x0 0x0 0x0 0x2 &gic 0x0 0xc9 0x1 + 0x0 0x0 0x0 0x3 &gic 0x0 0xca 0x1 + 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>; + dma-coherent; + clocks = <&pcie1clk 0>; + }; + + pcie2: pcie@1f2d0000 { + status = "disabled"; + device_type = "pci"; + compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = < 0x00 0x1f2d0000 0x0 0x00010000 /* Controller registers */ + 0x90 0xd0000000 0x0 0x00040000>; /* PCI config space */ + reg-names = "csr", "cfg"; + ranges = <0x01000000 0x0 0x00000000 0x90 0x10000000 0x0 0x00010000 /* io */ + 0x02000000 0x0 0x80000000 0x91 0x80000000 0x0 0x80000000>; /* mem */ + dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 + 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xce 0x1 + 0x0 0x0 0x0 0x2 &gic 0x0 0xcf 0x1 + 0x0 0x0 0x0 0x3 &gic 0x0 0xd0 0x1 + 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>; + dma-coherent; + clocks = <&pcie2clk 0>; + }; + + pcie3: pcie@1f500000 { + status = "disabled"; + device_type = "pci"; + compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = < 0x00 0x1f500000 0x0 0x00010000 /* Controller registers */ + 0xa0 0xd0000000 0x0 0x00040000>; /* PCI config space */ + reg-names = "csr", "cfg"; + ranges = <0x01000000 0x0 0x00000000 0xa0 0x10000000 0x0 0x00010000 /* io */ + 0x02000000 0x0 0x80000000 0xa1 0x80000000 0x0 0x80000000>; /* mem */ + dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 + 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xd4 0x1 + 0x0 0x0 0x0 0x2 &gic 0x0 0xd5 0x1 + 0x0 0x0 0x0 0x3 &gic 0x0 0xd6 0x1 + 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>; + dma-coherent; + clocks = <&pcie3clk 0>; + }; + + pcie4: pcie@1f510000 { + status = "disabled"; + device_type = "pci"; + compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = < 0x00 0x1f510000 0x0 0x00010000 /* Controller registers */ + 0xc0 0xd0000000 0x0 0x00200000>; /* PCI config space */ + reg-names = "csr", "cfg"; + ranges = <0x01000000 0x0 0x00000000 0xc0 0x10000000 0x0 0x00010000 /* io */ + 0x02000000 0x0 0x80000000 0xc1 0x80000000 0x0 0x80000000>; /* mem */ + dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000 + 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xda 0x1 + 0x0 0x0 0x0 0x2 &gic 0x0 0xdb 0x1 + 0x0 0x0 0x0 0x3 &gic 0x0 0xdc 0x1 + 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>; + dma-coherent; + clocks = <&pcie4clk 0>; }; serial0: serial@1c020000 { diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild index 0b3fcf8..07cb417 100644 --- a/arch/arm64/include/asm/Kbuild +++ b/arch/arm64/include/asm/Kbuild @@ -29,6 +29,7 @@ generic-y += mman.h generic-y += msgbuf.h generic-y += mutex.h generic-y += pci.h +generic-y += pci-bridge.h generic-y += poll.h generic-y += preempt.h generic-y += resource.h diff --git a/arch/arm64/include/asm/acenv.h b/arch/arm64/include/asm/acenv.h new file mode 100644 index 0000000..b49166f --- /dev/null +++ b/arch/arm64/include/asm/acenv.h @@ -0,0 +1,18 @@ +/* + * ARM64 specific ACPICA environments and implementation + * + * Copyright (C) 2014, Linaro Ltd. + * Author: Hanjun Guo + * 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 d7b4b38..d149580 100644 --- a/arch/arm64/include/asm/cpu_ops.h +++ b/arch/arm64/include/asm/cpu_ops.h @@ -61,6 +61,7 @@ struct cpu_operations { }; extern const struct cpu_operations *cpu_ops[NR_CPUS]; +const struct cpu_operations *cpu_get_ops(const char *name); extern int __init cpu_read_ops(struct device_node *dn, int cpu); extern void __init cpu_read_bootcpu_ops(void); diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index 01d3aab..8186df6 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -114,7 +114,8 @@ typedef struct user_fpsimd_state elf_fpregset_t; */ #define elf_check_arch(x) ((x)->e_machine == EM_AARCH64) -#define elf_read_implies_exec(ex,stk) (stk != EXSTACK_DISABLE_X) +#define elf_read_implies_exec(ex,stk) (test_thread_flag(TIF_32BIT) \ + ? (stk == EXSTACK_ENABLE_X) : 0) #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE PAGE_SIZE diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index e0ecdcf..f998d90 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -121,7 +121,8 @@ static inline u64 __raw_readq(const volatile void __iomem *addr) /* * I/O port access primitives. */ -#define IO_SPACE_LIMIT 0xffff +#define arch_has_dev_port() (1) +#define IO_SPACE_LIMIT (SZ_32M - 1) #define PCI_IOBASE ((void __iomem *)(MODULES_VADDR - SZ_32M)) static inline u8 inb(unsigned long addr) diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index cc83520..ff4a4fa 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -95,7 +95,6 @@ /* TCR_EL2 Registers bits */ #define TCR_EL2_TBI (1 << 20) #define TCR_EL2_PS (7 << 16) -#define TCR_EL2_PS_40B (2 << 16) #define TCR_EL2_TG0 (1 << 14) #define TCR_EL2_SH0 (3 << 12) #define TCR_EL2_ORGN0 (3 << 10) @@ -104,8 +103,6 @@ #define TCR_EL2_MASK (TCR_EL2_TG0 | TCR_EL2_SH0 | \ TCR_EL2_ORGN0 | TCR_EL2_IRGN0 | TCR_EL2_T0SZ) -#define TCR_EL2_FLAGS (TCR_EL2_PS_40B) - /* VTCR_EL2 Registers bits */ #define VTCR_EL2_PS_MASK (7 << 16) #define VTCR_EL2_TG0_MASK (1 << 14) @@ -120,36 +117,28 @@ #define VTCR_EL2_SL0_MASK (3 << 6) #define VTCR_EL2_SL0_LVL1 (1 << 6) #define VTCR_EL2_T0SZ_MASK 0x3f -#define VTCR_EL2_T0SZ_40B 24 +#define VTCR_EL2_T0SZ(bits) (64 - (bits)) #ifdef CONFIG_ARM64_64K_PAGES /* * Stage2 translation configuration: - * 40bits output (PS = 2) - * 40bits input (T0SZ = 24) * 64kB pages (TG0 = 1) * 2 level page tables (SL = 1) */ #define VTCR_EL2_FLAGS (VTCR_EL2_TG0_64K | VTCR_EL2_SH0_INNER | \ VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \ - VTCR_EL2_SL0_LVL1 | VTCR_EL2_T0SZ_40B) -#define VTTBR_X (38 - VTCR_EL2_T0SZ_40B) + VTCR_EL2_SL0_LVL1) #else /* * Stage2 translation configuration: - * 40bits output (PS = 2) - * 40bits input (T0SZ = 24) * 4kB pages (TG0 = 0) * 3 level page tables (SL = 1) */ #define VTCR_EL2_FLAGS (VTCR_EL2_TG0_4K | VTCR_EL2_SH0_INNER | \ VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \ - VTCR_EL2_SL0_LVL1 | VTCR_EL2_T0SZ_40B) -#define VTTBR_X (37 - VTCR_EL2_T0SZ_40B) + VTCR_EL2_SL0_LVL1) #endif -#define VTTBR_BADDR_SHIFT (VTTBR_X - 1) -#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT) #define VTTBR_VMID_SHIFT (48LLU) #define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT) diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 8e138c7..1c70b2f 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -167,5 +167,80 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva, void stage2_flush_vm(struct kvm *kvm); +/* + * ARMv8 64K architecture limitations: + * 16 <= T0SZ <= 21 is valid under 3 level of translation tables + * 18 <= T0SZ <= 34 is valid under 2 level of translation tables + * 31 <= T0SZ <= 39 is valid under 1 level of transltaion tables + * + * ARMv8 4K architecture limitations: + * 16 <= T0SZ <= 24 is valid under 4 level of translation tables + * 21 <= T0SZ <= 33 is valid under 3 level of translation tables + * 30 <= T0SZ <= 39 is valid under 2 level of translation tables + * + * For 4K pages we only support 3 or 4 level, giving T0SZ a range of 16 to 33. + * For 64K pages we only support 2 or 3 level, giving T0SZ a range of 16 to 34. + * + * See Table D4-23 and Table D4-25 in ARM DDI 0487A.b to figure out + * the origin of the hardcoded values, 38 and 37. + */ + +#ifdef CONFIG_ARM64_64K_PAGES +static inline int t0sz_to_vttbr_x(int t0sz) +{ + if (t0sz < 16 || t0sz > 34) { + kvm_err("Cannot support %d-bit address space\n", 64 - t0sz); + return -EINVAL; + } + + return 38 - t0sz; +} +#else /* 4K pages */ +static inline int t0sz_to_vttbr_x(int t0sz) +{ + if (t0sz < 16 || t0sz > 33) { + kvm_err("Cannot support %d-bit address space\n", 64 - t0sz); + return -EINVAL; + } + return 37 - t0sz; +} +#endif +static inline int kvm_get_phys_addr_shift(void) +{ + int pa_range = read_cpuid(ID_AA64MMFR0_EL1) & 0xf; + + switch (pa_range) { + case 0: return 32; + case 1: return 36; + case 2: return 40; + case 3: return 42; + case 4: return 44; + case 5: return 48; + default: + BUG(); + return 0; + } +} + +/** + * get_vttbr_baddr_mask - get mask value for vttbr base address + * + * In ARMv8, vttbr_baddr_mask cannot be determined in compile time since the + * stage2 input address size depends on hardware capability. Thus, we first + * need to read ID_AA64MMFR0_EL1.PARange and then set vttbr_baddr_mask with + * consideration of both the granule size and the level of translation tables. + */ +static inline u64 get_vttbr_baddr_mask(void) +{ + int t0sz, vttbr_x; + + t0sz = VTCR_EL2_T0SZ(kvm_get_phys_addr_shift()); + vttbr_x = t0sz_to_vttbr_x(t0sz); + if (vttbr_x < 0) + return ~0; + return GENMASK_ULL(48, (vttbr_x - 1)); + +} + #endif /* __ASSEMBLY__ */ #endif /* __ARM64_KVM_MMU_H__ */ diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h new file mode 100644 index 0000000..872ba93 --- /dev/null +++ b/arch/arm64/include/asm/pci.h @@ -0,0 +1,37 @@ +#ifndef __ASM_PCI_H +#define __ASM_PCI_H +#ifdef __KERNEL__ + +#include +#include +#include + +#include +#include +#include + +#define PCIBIOS_MIN_IO 0x1000 +#define PCIBIOS_MIN_MEM 0 + +/* + * Set to 1 if the kernel should re-assign all PCI bus numbers + */ +#define pcibios_assign_all_busses() \ + (pci_has_flag(PCI_REASSIGN_ALL_BUS)) + +/* + * PCI address space differs from physical memory address space + */ +#define PCI_DMA_BUS_IS_PHYS (0) + +extern int isa_dma_bridge_buggy; + +#ifdef CONFIG_PCI +static inline int pci_proc_domain(struct pci_bus *bus) +{ + return 1; +} +#endif /* CONFIG_PCI */ + +#endif /* __KERNEL__ */ +#endif /* __ASM_PCI_H */ diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index ffe1ba0..a968523 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -296,6 +296,8 @@ static inline int has_transparent_hugepage(void) __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN) #define pgprot_writecombine(prot) \ __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN) +#define pgprot_device(prot) \ + __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN) #define __HAVE_PHYS_MEM_ACCESS_PROT struct file; extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h index e5312ea..2454bc5 100644 --- a/arch/arm64/include/asm/psci.h +++ b/arch/arm64/include/asm/psci.h @@ -14,6 +14,7 @@ #ifndef __ASM_PSCI_H #define __ASM_PSCI_H -int psci_init(void); +int psci_dt_init(void); +int psci_acpi_init(void); #endif /* __ASM_PSCI_H */ diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index a498f2c..2ebbd55 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h @@ -39,9 +39,10 @@ extern void show_ipi_list(struct seq_file *p, int prec); extern void handle_IPI(int ipinr, struct pt_regs *regs); /* - * Setup the set of possible CPUs (via set_cpu_possible) + * Discover the set of possible CPUs and determine their + * SMP operations. */ -extern void smp_init_cpus(void); +extern void of_smp_init_cpus(void); /* * Provide a function to raise an IPI cross call on CPUs in callmap. @@ -49,6 +50,11 @@ extern void smp_init_cpus(void); extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int)); /* + * Provide a function to signal a parked secondary CPU. + */ +extern void set_smp_boot_wakeup_call(void (*)(int cpu)); + +/* * Called from the secondary holding pen, this is the secondary CPU entry point. */ asmlinkage void secondary_start_kernel(void); diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index df7ef87..b0bad2e 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -21,7 +21,8 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o -arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o +arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o \ + smp_parking_protocol.o arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o @@ -29,6 +30,8 @@ arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o arm64-obj-$(CONFIG_KGDB) += kgdb.o arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o +arm64-obj-$(CONFIG_PCI) += pci.o +arm64-obj-$(CONFIG_ACPI) += acpi.o obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m) diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c new file mode 100644 index 0000000..5486426 --- /dev/null +++ b/arch/arm64/kernel/acpi.c @@ -0,0 +1,397 @@ +/* + * ARM64 Specific Low-Level ACPI Boot Support + * + * Copyright (C) 2013-2014, Linaro Ltd. + * Author: Al Stone + * 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-stub.c b/arch/arm64/kernel/efi-stub.c index 1317fef..d27dd98 100644 --- a/arch/arm64/kernel/efi-stub.c +++ b/arch/arm64/kernel/efi-stub.c @@ -28,20 +28,16 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, kernel_size = _edata - _text; if (*image_addr != (dram_base + TEXT_OFFSET)) { kernel_memsize = kernel_size + (_end - _edata); - status = efi_relocate_kernel(sys_table, image_addr, - kernel_size, kernel_memsize, - dram_base + TEXT_OFFSET, - PAGE_SIZE); + status = efi_low_alloc(sys_table, kernel_memsize + TEXT_OFFSET, + SZ_2M, reserve_addr); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Failed to relocate kernel\n"); return status; } - if (*image_addr != (dram_base + TEXT_OFFSET)) { - pr_efi_err(sys_table, "Failed to alloc kernel memory\n"); - efi_free(sys_table, kernel_memsize, *image_addr); - return EFI_LOAD_ERROR; - } - *image_size = kernel_memsize; + memcpy((void *)*reserve_addr + TEXT_OFFSET, (void *)*image_addr, + kernel_size); + *image_addr = *reserve_addr + TEXT_OFFSET; + *reserve_size = kernel_memsize + TEXT_OFFSET; } diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c index 03aaa99..6c4de44 100644 --- a/arch/arm64/kernel/efi.c +++ b/arch/arm64/kernel/efi.c @@ -479,3 +479,14 @@ err_unmap: return -1; } early_initcall(arm64_enter_virtual_mode); + +/* + * If nothing else is handling pm_power_off, use EFI + * + * This is called from a late_initcall after other mechanisms + * have had a chance to register a handler. + */ +bool efi_poweroff_required(void) +{ + return pm_power_off == NULL; +} diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 8730690..0a6e4f9 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -151,7 +151,7 @@ optional_header: .short 0x20b // PE32+ format .byte 0x02 // MajorLinkerVersion .byte 0x14 // MinorLinkerVersion - .long _edata - stext // SizeOfCode + .long _end - stext // SizeOfCode .long 0 // SizeOfInitializedData .long 0 // SizeOfUninitializedData .long efi_stub_entry - efi_head // AddressOfEntryPoint @@ -169,7 +169,7 @@ extra_header_fields: .short 0 // MinorSubsystemVersion .long 0 // Win32VersionValue - .long _edata - efi_head // SizeOfImage + .long _end - efi_head // SizeOfImage // Everything before the kernel image is considered part of the header .long stext - efi_head // SizeOfHeaders @@ -216,7 +216,7 @@ section_table: .byte 0 .byte 0 .byte 0 // end of 0 padding of section name - .long _edata - stext // VirtualSize + .long _end - stext // VirtualSize .long stext - efi_head // VirtualAddress .long _edata - stext // SizeOfRawData .long stext - efi_head // PointerToRawData diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c new file mode 100644 index 0000000..ce5836c --- /dev/null +++ b/arch/arm64/kernel/pci.c @@ -0,0 +1,70 @@ +/* + * Code borrowed from powerpc/kernel/pci-common.c + * + * Copyright (C) 2003 Anton Blanchard , IBM + * Copyright (C) 2014 ARM Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Called after each bus is probed, but before its children are examined + */ +void pcibios_fixup_bus(struct pci_bus *bus) +{ + /* nothing to do, expected to be removed in the future */ +} + +/* + * We don't have to worry about legacy ISA devices, so nothing to do here + */ +resource_size_t pcibios_align_resource(void *data, const struct resource *res, + resource_size_t size, resource_size_t align) +{ + return res->start; +} + +/* + * Try to assign the IRQ number from DT when adding a new device + */ +int pcibios_add_device(struct pci_dev *dev) +{ + dev->irq = of_irq_parse_and_map_pci(dev, 0, 0); + + return 0; +} + + +#ifdef CONFIG_PCI_DOMAINS_GENERIC +static bool dt_domain_found = false; + +void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) +{ + int domain = of_get_pci_domain_nr(parent->of_node); + + if (domain >= 0) { + dt_domain_found = true; + } else if (dt_domain_found == true) { + dev_err(parent, "Node %s is missing \"linux,pci-domain\" property in DT\n", + parent->of_node->full_name); + return; + } else { + domain = pci_get_new_domain_nr(); + } + + bus->domain_nr = domain; +} +#endif diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index baf5afb..a0fb686 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -1304,6 +1305,107 @@ static int __init register_pmu_driver(void) } device_initcall(register_pmu_driver); +#ifdef CONFIG_ACPI +struct acpi_pmu_irq { + int gsi; + int trigger; +}; + +static struct acpi_pmu_irq acpi_pmu_irqs[NR_CPUS] __initdata; + +static int __init +acpi_parse_pmu_irqs(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_interrupt *gic; + int cpu; + u64 mpidr; + + gic = (struct acpi_madt_generic_interrupt *)header; + if (BAD_MADT_ENTRY(gic, end)) + return -EINVAL; + + mpidr = gic->arm_mpidr & MPIDR_HWID_BITMASK; + + for_each_possible_cpu(cpu) { + if (cpu_logical_map(cpu) != mpidr) + continue; + + acpi_pmu_irqs[cpu].gsi = gic->performance_interrupt; + if (gic->flags & ACPI_MADT_PERFORMANCE_IRQ_MODE) + acpi_pmu_irqs[cpu].trigger = ACPI_EDGE_SENSITIVE; + else + acpi_pmu_irqs[cpu].trigger = ACPI_LEVEL_SENSITIVE; + return 0; + } + + return -EINVAL; +} + +static int __init pmu_acpi_init(void) +{ + struct platform_device *pdev; + struct acpi_pmu_irq *pirq = acpi_pmu_irqs; + struct resource *res, *r; + int err = -ENOMEM; + int i, count, irq; + + if (acpi_disabled) + return 0; + + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, + acpi_parse_pmu_irqs, num_possible_cpus()); + /* Must have irq for boot boot cpu, at least */ + if (count <= 0 || pirq->gsi == 0) + return -EINVAL; + + irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger, + ACPI_ACTIVE_HIGH); + + if (irq_is_percpu(irq)) + count = 1; + + pdev = platform_device_alloc("arm-pmu", -1); + if (!pdev) + return err; + + res = kcalloc(count, sizeof(*res), GFP_KERNEL); + if (!res) + goto err_free_device; + + for (i = 0, r = res; i < count; i++, pirq++, r++) { + if (i) + irq = acpi_register_gsi(NULL, pirq->gsi, pirq->trigger, + ACPI_ACTIVE_HIGH); + r->start = r->end = irq; + r->flags = IORESOURCE_IRQ; + if (pirq->trigger == ACPI_EDGE_SENSITIVE) + r->flags |= IORESOURCE_IRQ_HIGHEDGE; + else + r->flags |= IORESOURCE_IRQ_HIGHLEVEL; + } + + err = platform_device_add_resources(pdev, res, count); + if (err) + goto err_free_res; + + err = platform_device_add(pdev); + if (err) + goto err_free_res; + + return 0; + +err_free_res: + kfree(res); + +err_free_device: + platform_device_put(pdev); + return err; +} +arch_initcall(pmu_acpi_init); + +#endif /* ACPI */ + static struct pmu_hw_events *armpmu_get_cpu_events(void) { return this_cpu_ptr(&cpu_hw_events); diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 29d4869..c0427bc 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -182,6 +183,11 @@ void machine_restart(char *cmd) arm_pm_restart(reboot_mode, cmd); /* + * If all else fails, try EFI + */ + efi_reboot(reboot_mode, cmd); + + /* * Whoops - the architecture was unable to reboot. */ printk("Reboot failed -- System halted\n"); diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 5539547..15ba470 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -15,6 +15,7 @@ #define pr_fmt(fmt) "psci: " fmt +#include #include #include #include @@ -23,6 +24,7 @@ #include #include +#include #include #include #include @@ -231,6 +233,33 @@ static void psci_sys_poweroff(void) invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); } +static void psci_0_2_set_functions(void) +{ + pr_info("Using standard PSCI v0.2 function IDs\n"); + psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND; + psci_ops.cpu_suspend = psci_cpu_suspend; + + psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; + psci_ops.cpu_off = psci_cpu_off; + + psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON; + psci_ops.cpu_on = psci_cpu_on; + + psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE; + psci_ops.migrate = psci_migrate; + + psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO; + psci_ops.affinity_info = psci_affinity_info; + + psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] = + PSCI_0_2_FN_MIGRATE_INFO_TYPE; + psci_ops.migrate_info_type = psci_migrate_info_type; + + arm_pm_restart = psci_sys_reset; + + pm_power_off = psci_sys_poweroff; +} + /* * PSCI Function IDs for v0.2+ are well defined so use * standard values. @@ -264,29 +293,7 @@ static int __init psci_0_2_init(struct device_node *np) } } - pr_info("Using standard PSCI v0.2 function IDs\n"); - psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND; - psci_ops.cpu_suspend = psci_cpu_suspend; - - psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; - psci_ops.cpu_off = psci_cpu_off; - - psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON; - psci_ops.cpu_on = psci_cpu_on; - - psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE; - psci_ops.migrate = psci_migrate; - - psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO; - psci_ops.affinity_info = psci_affinity_info; - - psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] = - PSCI_0_2_FN_MIGRATE_INFO_TYPE; - psci_ops.migrate_info_type = psci_migrate_info_type; - - arm_pm_restart = psci_sys_reset; - - pm_power_off = psci_sys_poweroff; + psci_0_2_set_functions(); out_put_node: of_node_put(np); @@ -339,7 +346,7 @@ static const struct of_device_id psci_of_match[] __initconst = { {}, }; -int __init psci_init(void) +int __init psci_dt_init(void) { struct device_node *np; const struct of_device_id *matched_np; @@ -354,6 +361,29 @@ int __init psci_init(void) return init_fn(np); } +/* + * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's + * explicitly clarified in SBBR + */ +int __init psci_acpi_init(void) +{ + if (!acpi_psci_present()) { + pr_info("is not implemented in ACPI.\n"); + return -EOPNOTSUPP; + } + + pr_info("probing for conduit method from ACPI.\n"); + + if (acpi_psci_use_hvc()) + invoke_psci_fn = __invoke_psci_fn_hvc; + else + invoke_psci_fn = __invoke_psci_fn_smc; + + psci_0_2_set_functions(); + + return 0; +} + #ifdef CONFIG_SMP static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu) diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index edb146d..a793a20 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -43,6 +43,7 @@ #include #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); @@ -385,22 +390,34 @@ void __init setup_arch(char **cmdline_p) parse_early_param(); + if (acpi_disabled) + disable_acpi(); + efi_init(); arm64_memblock_init(); + /* Parse the ACPI tables for possible boot-time configuration */ + acpi_boot_table_init(); + paging_init(); request_standard_resources(); efi_idmap_init(); - unflatten_device_tree(); - - psci_init(); - cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; - cpu_read_bootcpu_ops(); + if (acpi_disabled) { + unflatten_device_tree(); + psci_dt_init(); + cpu_read_bootcpu_ops(); +#ifdef CONFIG_SMP + of_smp_init_cpus(); +#endif + } else { + psci_acpi_init(); + acpi_smp_init_cpus(); + } + #ifdef CONFIG_SMP - smp_init_cpus(); smp_build_mpidr_hash(); #endif @@ -413,6 +430,19 @@ void __init setup_arch(char **cmdline_p) #endif } +static int __init parse_kvm_acpi(char *arg) +{ + if (!arg) + return -EINVAL; + + if (strcmp(arg, "on") == 0) { + acadia_kvm_acpi = 1; + } + + return 0; +} +early_param("kvmacpi", parse_kvm_acpi); + static int __init arm64_device_init(void) { of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); @@ -505,3 +535,25 @@ const struct seq_operations cpuinfo_op = { .stop = c_stop, .show = c_show }; + +/* + * Temporary hack to avoid need for console= on command line + */ +static int __init arm64_console_setup(void) +{ + /* Allow cmdline to override our assumed preferences */ + if (console_set_on_cmdline) + return 0; + + if (IS_ENABLED(CONFIG_SBSAUART_TTY)) + add_preferred_console("ttySBSA", 0, "115200"); + + if (IS_ENABLED(CONFIG_SERIAL_AMBA_PL011)) + add_preferred_console("ttyAMA", 0, "115200"); + + if (IS_ENABLED(CONFIG_SERIAL_8250)) + add_preferred_console("ttyS", 0, "115200"); + + return 0; +} +early_initcall(arm64_console_setup); diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 4743397..4e390ac 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -321,7 +321,7 @@ void __init smp_prepare_boot_cpu(void) * cpu logical map array containing MPIDR values related to logical * cpus. Assumes that cpu_logical_map(0) has already been initialized. */ -void __init smp_init_cpus(void) +void __init of_smp_init_cpus(void) { struct device_node *dn = NULL; unsigned int i, cpu = 1; diff --git a/arch/arm64/kernel/smp_parking_protocol.c b/arch/arm64/kernel/smp_parking_protocol.c new file mode 100644 index 0000000..e1153ce --- /dev/null +++ b/arch/arm64/kernel/smp_parking_protocol.c @@ -0,0 +1,110 @@ +/* + * Parking Protocol SMP initialisation + * + * Based largely on spin-table method. + * + * Copyright (C) 2013 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#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/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c index 0347d38..4f93c67 100644 --- a/arch/arm64/kernel/smp_spin_table.c +++ b/arch/arm64/kernel/smp_spin_table.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -65,12 +66,21 @@ static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu) static int smp_spin_table_cpu_prepare(unsigned int cpu) { - void **release_addr; + __le64 __iomem *release_addr; if (!cpu_release_addr[cpu]) return -ENODEV; - release_addr = __va(cpu_release_addr[cpu]); + /* + * The cpu-release-addr may or may not be inside the linear mapping. + * As ioremap_cache will either give us a new mapping or reuse the + * existing linear mapping, we can use it to cover both cases. In + * either case the memory will be MT_NORMAL. + */ + release_addr = ioremap_cache(cpu_release_addr[cpu], + sizeof(*release_addr)); + if (!release_addr) + return -ENOMEM; /* * We write the release address as LE regardless of the native @@ -79,15 +89,17 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu) * boot-loader's endianess before jumping. This is mandated by * the boot protocol. */ - release_addr[0] = (void *) cpu_to_le64(__pa(secondary_holding_pen)); - - __flush_dcache_area(release_addr, sizeof(release_addr[0])); + writeq_relaxed(__pa(secondary_holding_pen), release_addr); + __flush_dcache_area((__force void *)release_addr, + sizeof(*release_addr)); /* * Send an event to wake up the secondary CPU. */ sev(); + iounmap(release_addr); + return 0; } diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index 1a7125c..42f9195 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -35,6 +35,7 @@ #include #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/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S index c319116..fa7e67e 100644 --- a/arch/arm64/kvm/hyp-init.S +++ b/arch/arm64/kvm/hyp-init.S @@ -63,17 +63,21 @@ __do_hyp_init: mrs x4, tcr_el1 ldr x5, =TCR_EL2_MASK and x4, x4, x5 - ldr x5, =TCR_EL2_FLAGS - orr x4, x4, x5 - msr tcr_el2, x4 - - ldr x4, =VTCR_EL2_FLAGS /* * Read the PARange bits from ID_AA64MMFR0_EL1 and set the PS bits in - * VTCR_EL2. + * TCR_EL2 and both PS bits and T0SZ bits in VTCR_EL2. */ mrs x5, ID_AA64MMFR0_EL1 bfi x4, x5, #16, #3 + msr tcr_el2, x4 + + ldr x4, =VTCR_EL2_FLAGS + bfi x4, x5, #16, #3 + and x5, x5, #0xf + adr x6, t0sz + add x6, x6, x5, lsl #2 + ldr w5, [x6] + orr x4, x4, x5 msr vtcr_el2, x4 mrs x4, mair_el1 @@ -113,6 +117,10 @@ target: /* We're now in the trampoline code, switch page tables */ /* Hello, World! */ eret + +t0sz: + .word VTCR_EL2_T0SZ(32), VTCR_EL2_T0SZ(36), VTCR_EL2_T0SZ(40) + .word VTCR_EL2_T0SZ(42), VTCR_EL2_T0SZ(44), VTCR_EL2_T0SZ(48) ENDPROC(__kvm_hyp_init) .ltorg diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 4164c5a..b864a24 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -23,10 +23,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include @@ -319,6 +322,63 @@ static int dma_bus_notifier(struct notifier_block *nb, if (of_property_read_bool(dev->of_node, "dma-coherent")) set_dma_ops(dev, &coherent_swiotlb_dma_ops); +#ifdef CONFIG_ACPI + else if (ACPI_HANDLE(dev)) { + acpi_status status; + int coherent; + + /* + * Kernel defaults to noncoherent ops but ACPI 5.1 spec says arm64 + * defaults to coherent. Set coherent ops if _CCA not found or _CCA + * found and non-zero. + */ + status = acpi_check_coherency(ACPI_HANDLE(dev), &coherent); + if (ACPI_FAILURE(status) || coherent) + set_dma_ops(dev, &coherent_swiotlb_dma_ops); + } +#endif + return NOTIFY_OK; +} + +static int dma_bus_notifier_pci(struct notifier_block *nb, + unsigned long event, void *_dev) +{ + struct device *dev = _dev; + + if (event != BUS_NOTIFY_ADD_DEVICE) + return NOTIFY_DONE; + + /* + * PCI devices won't have an of_node but the bridge will. + * Search up the device chain until we find an of_node + * to check. + */ + while (dev) { + if (dev->of_node) { + if (of_dma_is_coherent(dev->of_node)) + set_dma_ops(_dev, &coherent_swiotlb_dma_ops); + break; + } +#ifdef CONFIG_ACPI + if (ACPI_HANDLE(dev)) { + acpi_status status; + int coherent; + + /* + * Kernel defaults to noncoherent ops but ACPI 5.1 spec says arm64 + * defaults to coherent. Set coherent ops if _CCA not found or _CCA + * found and non-zero. + */ + status = acpi_check_coherency(ACPI_HANDLE(dev), &coherent); + if (ACPI_FAILURE(status) || coherent) { + set_dma_ops(dev, &coherent_swiotlb_dma_ops); + break; + } + } +#endif + dev = dev->parent; + } + return NOTIFY_OK; } @@ -330,6 +390,10 @@ static struct notifier_block amba_bus_nb = { .notifier_call = dma_bus_notifier, }; +static struct notifier_block pci_bus_nb = { + .notifier_call = dma_bus_notifier_pci, +}; + extern int swiotlb_late_init_with_default_size(size_t default_size); static int __init swiotlb_late_init(void) @@ -341,6 +405,7 @@ static int __init swiotlb_late_init(void) */ bus_register_notifier(&platform_bus_type, &platform_bus_nb); bus_register_notifier(&amba_bustype, &amba_bus_nb); + bus_register_notifier(&pci_bus_type, &pci_bus_nb); dma_ops = &noncoherent_swiotlb_dma_ops; diff --git a/arch/arm64/pci/Makefile b/arch/arm64/pci/Makefile new file mode 100644 index 0000000..b8d5dbd --- /dev/null +++ b/arch/arm64/pci/Makefile @@ -0,0 +1 @@ +obj-y += pci.o diff --git a/arch/arm64/pci/pci.c b/arch/arm64/pci/pci.c new file mode 100644 index 0000000..b03b0eb --- /dev/null +++ b/arch/arm64/pci/pci.c @@ -0,0 +1,28 @@ +#include +#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 += power.o acpi-y += event.o acpi-y += sysfs.o +acpi-y += property.o acpi-$(CONFIG_X86) += acpi_cmos_rtc.o acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-$(CONFIG_ACPI_NUMA) += numa.o diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c index 14cb6c0..5cd017c 100644 --- a/drivers/acpi/acpica/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -87,7 +87,9 @@ const char *acpi_gbl_io_decode[] = { const char *acpi_gbl_ll_decode[] = { "ActiveHigh", - "ActiveLow" + "ActiveLow", + "ActiveBoth", + "Reserved" }; const char *acpi_gbl_max_decode[] = { diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 8b67bd0..c412fdb 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -448,6 +448,9 @@ static int __init acpi_bus_init_irq(void) case ACPI_IRQ_MODEL_IOSAPIC: message = "IOSAPIC"; break; + case ACPI_IRQ_MODEL_GIC: + message = "GIC"; + break; case ACPI_IRQ_MODEL_PLATFORM: message = "platform specific model"; break; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4c5cf77..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 3abe9b2..c50757b 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -326,11 +326,11 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size) return NULL; } -#ifndef CONFIG_IA64 -#define should_use_kmap(pfn) page_is_ram(pfn) -#else +#if defined(CONFIG_IA64) || defined(CONFIG_ARM) || defined(CONFIG_ARM64) /* ioremap will take care of cache attributes */ #define should_use_kmap(pfn) 0 +#else +#define should_use_kmap(pfn) page_is_ram(pfn) #endif static void __iomem *acpi_map(acpi_physical_address pg_off, unsigned long pg_sz) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index e32321c..4007313 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -64,6 +64,38 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, return 0; } +/* + * On ARM platform, MPIDR value is the hardware ID as apic ID + * on Intel platforms + */ +static int map_gicc_mpidr(struct acpi_subtable_header *entry, + int device_declaration, u32 acpi_id, int *mpidr) +{ + struct acpi_madt_generic_interrupt *gicc = + container_of(entry, struct acpi_madt_generic_interrupt, header); + + if (!(gicc->flags & ACPI_MADT_ENABLED)) + return -ENODEV; + + /* In the GIC interrupt model, logical processors are + * required to have a Processor Device object in the DSDT, + * so we should check device_declaration here + */ + if (device_declaration && (gicc->uid == acpi_id)) { + /* + * Only bits [0:7] Aff0, bits [8:15] Aff1, bits [16:23] Aff2 + * and bits [32:39] Aff3 are meaningful, so pack the Affx + * fields into a single 32 bit identifier to accommodate the + * acpi processor drivers. + */ + *mpidr = ((gicc->arm_mpidr & 0xff00000000) >> 8) + | gicc->arm_mpidr; + return 0; + } + + return -EINVAL; +} + static int map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; @@ -99,6 +131,9 @@ static int map_madt_entry(int type, u32 acpi_id) } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { if (!map_lsapic_id(header, type, acpi_id, &apic_id)) break; + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + if (!map_gicc_mpidr(header, type, acpi_id, &apic_id)) + break; } entry += header->length; } @@ -131,6 +166,8 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) map_lsapic_id(header, type, acpi_id, &apic_id); } else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) { map_x2apic_id(header, type, acpi_id, &apic_id); + } else if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) { + map_gicc_mpidr(header, type, acpi_id, &apic_id); } exit: diff --git a/drivers/acpi/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 07c8c5a..aec9656 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -698,3 +698,29 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs) return false; } EXPORT_SYMBOL(acpi_check_dsm); + +/** + * acpi_check_coherency - check for memory coherency of a device + * @handle: ACPI device handle + * @val: Pointer to returned value + * + * Search a device and its parents for a _CCA method and return + * its value. + */ +acpi_status acpi_check_coherency(acpi_handle handle, int *val) +{ + unsigned long long data; + acpi_status status; + + do { + status = acpi_evaluate_integer(handle, "_CCA", NULL, &data); + if (!ACPI_FAILURE(status)) { + *val = data; + break; + } + status = acpi_get_parent(handle, &handle); + } while (!ACPI_FAILURE(status)); + + return status; +} +EXPORT_SYMBOL(acpi_check_coherency); diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index e1b9278..f2e6c9e 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -48,7 +48,7 @@ config ATA_VERBOSE_ERROR config ATA_ACPI bool "ATA ACPI Support" - depends on ACPI && PCI + depends on ACPI default y help This option adds support for ATA-related ACPI objects. diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index f61ddb9..3499bab 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -20,6 +20,9 @@ #include #include #include +#ifdef CONFIG_ATA_ACPI +#include +#endif #include "ahci.h" static const struct ata_port_info ahci_port_info = { @@ -87,6 +90,13 @@ static const struct of_device_id ahci_of_match[] = { }; MODULE_DEVICE_TABLE(of, ahci_of_match); +#ifdef CONFIG_ATA_ACPI +static const struct acpi_device_id ahci_acpi_match[] = { + { "AMDI0600", 0 }, /* AMD Seattle AHCI */ + { }, +}; +#endif + static struct platform_driver ahci_driver = { .probe = ahci_probe, .remove = ata_platform_remove_one, @@ -94,6 +104,9 @@ static struct platform_driver ahci_driver = { .name = "ahci", .owner = THIS_MODULE, .of_match_table = ahci_of_match, +#ifdef CONFIG_ATA_ACPI + .acpi_match_table = ACPI_PTR(ahci_acpi_match), +#endif .pm = &ahci_pm_ops, }, }; diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index f03aab1..b02ba9d 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -28,6 +28,7 @@ #include #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 @@ -495,11 +489,6 @@ static int xgene_ahci_probe(struct platform_device *pdev) return -ENODEV; } - if (xgene_ahci_is_memram_inited(ctx)) { - dev_info(dev, "skip clock and PHY initialization\n"); - goto skip_clk_phy; - } - /* Due to errata, HW requires full toggle transition */ rc = ahci_platform_enable_clks(hpriv); if (rc) @@ -512,7 +501,7 @@ static int xgene_ahci_probe(struct platform_device *pdev) /* Configure the host controller */ xgene_ahci_hw_init(hpriv); -skip_clk_phy: + hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_NCQ; rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info); @@ -527,6 +516,16 @@ disable_resources: return rc; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_ahci_acpi_match[] = { + { "APMC0D00", }, + { "APMC0D0D", }, + { "APMC0D09", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match); +#endif + static const struct of_device_id xgene_ahci_of_match[] = { {.compatible = "apm,xgene-ahci"}, {}, @@ -540,6 +539,7 @@ static struct platform_driver xgene_ahci_driver = { .name = "xgene-ahci", .owner = THIS_MODULE, .of_match_table = xgene_ahci_of_match, + .acpi_match_table = ACPI_PTR(xgene_ahci_acpi_match), }, }; diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 4aab26e..0d801cf 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 5163ec1..1bec05b 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; @@ -338,8 +340,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np) if (arch_timer_rate) return; - /* Try to determine the frequency from the device tree or CNTFRQ */ - if (of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) { + /* + * Try to determine the frequency from the device tree or CNTFRQ, + * if ACPI is enabled, get the frequency from CNTFRQ ONLY. + */ + if (!acpi_disabled || + of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) { if (cntbase) arch_timer_rate = readl_relaxed(cntbase + CNTFRQ); else @@ -635,20 +641,8 @@ static void __init arch_timer_common_init(void) arch_timer_arch_init(); } -static void __init arch_timer_init(struct device_node *np) +static void __init arch_timer_init(void) { - int i; - - if (arch_timers_present & ARCH_CP15_TIMER) { - pr_warn("arch_timer: multiple nodes in dt, skipping\n"); - return; - } - - arch_timers_present |= ARCH_CP15_TIMER; - for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++) - arch_timer_ppi[i] = irq_of_parse_and_map(np, i); - arch_timer_detect_rate(NULL, np); - /* * If HYP mode is available, we know that the physical timer * has been configured to be accessible from PL1. Use it, so @@ -667,13 +661,31 @@ static void __init arch_timer_init(struct device_node *np) } } - arch_timer_c3stop = !of_property_read_bool(np, "always-on"); - arch_timer_register(); arch_timer_common_init(); } -CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init); -CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init); + +static void __init arch_timer_of_init(struct device_node *np) +{ + int i; + + if (arch_timers_present & ARCH_CP15_TIMER) { + pr_warn("arch_timer: multiple nodes in dt, skipping\n"); + return; + } + + arch_timers_present |= ARCH_CP15_TIMER; + for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++) + arch_timer_ppi[i] = irq_of_parse_and_map(np, i); + + arch_timer_detect_rate(NULL, np); + + arch_timer_c3stop = !of_property_read_bool(np, "always-on"); + + arch_timer_init(); +} +CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init); +CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init); static void __init arch_timer_mem_init(struct device_node *np) { @@ -740,3 +752,71 @@ static void __init arch_timer_mem_init(struct device_node *np) } CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", arch_timer_mem_init); + +#ifdef CONFIG_ACPI +static int __init +map_generic_timer_interrupt(u32 interrupt, u32 flags) +{ + int trigger, polarity; + + if (!interrupt) + return 0; + + trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE + : ACPI_LEVEL_SENSITIVE; + + polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW + : ACPI_ACTIVE_HIGH; + + return acpi_register_gsi(NULL, interrupt, trigger, polarity); +} + +/* Initialize per-processor generic timer */ +static int __init arch_timer_acpi_init(struct acpi_table_header *table) +{ + struct acpi_table_gtdt *gtdt; + + if (arch_timers_present & ARCH_CP15_TIMER) { + pr_warn("arch_timer: already initialized, skipping\n"); + return -EINVAL; + } + + gtdt = container_of(table, struct acpi_table_gtdt, header); + + arch_timers_present |= ARCH_CP15_TIMER; + + arch_timer_ppi[PHYS_SECURE_PPI] = + map_generic_timer_interrupt(gtdt->secure_el1_interrupt, + gtdt->secure_el1_flags); + + arch_timer_ppi[PHYS_NONSECURE_PPI] = + map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt, + gtdt->non_secure_el1_flags); + + arch_timer_ppi[VIRT_PPI] = + map_generic_timer_interrupt(gtdt->virtual_timer_interrupt, + gtdt->virtual_timer_flags); + + arch_timer_ppi[HYP_PPI] = + map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt, + gtdt->non_secure_el2_flags); + + /* Get the frequency from CNTFRQ */ + arch_timer_detect_rate(NULL, NULL); + + /* Always-on capability */ + arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON); + + arch_timer_init(); + return 0; +} + +/* Initialize all the generic timers presented in GTDT */ +void __init acpi_generic_timer_init(void) +{ + if (acpi_disabled) + return; + + acpi_table_parse(ACPI_SIG_GTDT, arch_timer_acpi_init); +} +#endif diff --git a/drivers/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 687476f..b14c045 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -293,6 +293,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; }; @@ -306,13 +307,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; @@ -320,40 +332,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); @@ -362,8 +409,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 c68d037..344bc12 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1487,14 +1487,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; @@ -1695,6 +1717,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 a0698b4..d2da911 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -490,9 +490,19 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) isb(); } +#ifdef CONFIG_ARM_PARKING_PROTOCOL +static void gic_wakeup_parked_cpu(int cpu) +{ + gic_raise_softirq(cpumask_of(cpu), 0); +} +#endif + static void gic_smp_init(void) { set_smp_cross_call(gic_raise_softirq); +#ifdef CONFIG_ARM_PARKING_PROTOCOL + set_smp_boot_wakeup_call(gic_wakeup_parked_cpu); +#endif register_cpu_notifier(&gic_cpu_notifier); } diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index dda6dbc..5d9bdd3 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -33,12 +33,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -622,6 +624,13 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } + +#ifdef CONFIG_ARM_PARKING_PROTOCOL +static void gic_wakeup_parked_cpu(int cpu) +{ + gic_raise_softirq(cpumask_of(cpu), GIC_DIST_SOFTINT_NSATT); +} +#endif #endif #ifdef CONFIG_BL_SWITCHER @@ -977,6 +986,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, #ifdef CONFIG_SMP set_smp_cross_call(gic_raise_softirq); register_cpu_notifier(&gic_cpu_notifier); +#ifdef CONFIG_ARM_PARKING_PROTOCOL + set_smp_boot_wakeup_call(gic_wakeup_parked_cpu); +#endif #endif set_handle_irq(gic_handle_irq); } @@ -1029,3 +1041,107 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init); IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init); #endif + +#ifdef CONFIG_ACPI +static phys_addr_t dist_phy_base, cpu_phy_base; +static int cpu_base_assigned; + +static int __init +gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_interrupt *processor; + phys_addr_t gic_cpu_base; + + processor = (struct acpi_madt_generic_interrupt *)header; + + if (BAD_MADT_ENTRY(processor, end)) + return -EINVAL; + + /* + * There is no support for non-banked GICv1/2 register in ACPI spec. + * All CPU interface addresses have to be the same. + */ + gic_cpu_base = processor->base_address; + if (cpu_base_assigned && gic_cpu_base != cpu_phy_base) + return -EFAULT; + + cpu_phy_base = gic_cpu_base; + cpu_base_assigned = 1; + return 0; +} + +static int __init +gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_distributor *dist; + + dist = (struct acpi_madt_generic_distributor *)header; + + if (BAD_MADT_ENTRY(dist, end)) + return -EINVAL; + + dist_phy_base = dist->base_address; + return 0; +} + +int __init +gic_v2_acpi_init(struct acpi_table_header *table) +{ + void __iomem *cpu_base, *dist_base; + int count; + + /* Collect CPU base addresses */ + count = acpi_parse_entries(sizeof(struct acpi_table_madt), + gic_acpi_parse_madt_cpu, table, + ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0); + if (count < 0) { + pr_err("Error during GICC entries parsing\n"); + return -EFAULT; + } else if (!count) { + pr_err("No valid GICC entries exist\n"); + return -EINVAL; + } + + /* + * Find distributor base address. We expect one distributor entry since + * ACPI 5.1 spec neither support multi-GIC instances nor GIC cascade. + */ + count = acpi_parse_entries(sizeof(struct acpi_table_madt), + gic_acpi_parse_madt_distributor, table, + ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0); + if (count <= 0) { + pr_err("Error during GICD entries parsing\n"); + return -EFAULT; + } else if (!count) { + pr_err("No valid GICD entries exist\n"); + return -EINVAL; + } else if (count > 1) { + pr_err("More than one GICD entry detected\n"); + return -EINVAL; + } + + cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE); + if (!cpu_base) { + pr_err("Unable to map GICC registers\n"); + return -ENOMEM; + } + + dist_base = ioremap(dist_phy_base, ACPI_GICV2_DIST_MEM_SIZE); + if (!dist_base) { + pr_err("Unable to map GICD registers\n"); + iounmap(cpu_base); + return -ENOMEM; + } + + /* + * Initialize zero GIC instance (no multi-GIC support). Also, set GIC + * as default IRQ domain to allow for GSI registration and GSI to IRQ + * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()). + */ + gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL); + irq_set_default_host(gic_data[0].domain); + return 0; +} +#endif diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c index 0fe2f71..9106c6d 100644 --- a/drivers/irqchip/irqchip.c +++ b/drivers/irqchip/irqchip.c @@ -11,6 +11,7 @@ #include #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,13 +230,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 ea27383..fd61489 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -131,7 +131,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 @@ -154,7 +154,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 @@ -696,6 +696,18 @@ static int xgbe_read_mmd_regs(struct xgbe_prv_data *pdata, int prtad, else mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + if (XGBE_SEATTLE_A0) { + /* The PCS implementation has reversed the devices in + * package registers so we need to change 05 to 06 and + * 06 to 05 if being read (these registers are readonly + * so no need to do this in the write function) + */ + if ((mmd_address & 0xffff) == 0x05) + mmd_address = (mmd_address & ~0xffff) | 0x06; + else if ((mmd_address & 0xffff) == 0x06) + mmd_address = (mmd_address & ~0xffff) | 0x05; + } + /* The PCS registers are accessed using mmio. The underlying APB3 * management interface uses indirect addressing to access the MMD * register sets. This requires accessing of the PCS register in two diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index b26d758..ca7895c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -426,6 +426,9 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) hw_feat->rx_ch_cnt++; hw_feat->tx_ch_cnt++; + /* A0 does not support NUMTC, hardcode it for now */ + hw_feat->tc_cnt = XGBE_TC_CNT; + DBGPR("<--xgbe_get_all_hw_features\n"); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index bdf9cfa..7ab57fd 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" @@ -216,6 +217,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; @@ -223,8 +428,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"); @@ -240,6 +443,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); @@ -265,40 +469,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) @@ -309,23 +486,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); @@ -338,23 +508,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); @@ -532,10 +685,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); @@ -543,7 +708,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 6d2221e..095e70a 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" @@ -206,25 +207,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) */ @@ -253,12 +245,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; } @@ -284,8 +273,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"); @@ -301,9 +288,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 37e64cf..2d1e84b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c @@ -240,7 +240,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; @@ -261,7 +261,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 e9fe6e6..78deed9 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -173,6 +173,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 */ @@ -187,8 +193,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 @@ -571,6 +580,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 */ @@ -651,6 +661,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; @@ -660,7 +671,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 812d8d6..0fb2971 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -580,9 +580,11 @@ 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); @@ -648,15 +650,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; } @@ -666,11 +673,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; @@ -687,7 +735,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; } @@ -705,7 +753,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 e4222af..06f8252 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -739,6 +739,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; @@ -754,6 +790,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; @@ -765,6 +803,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; @@ -776,6 +816,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; @@ -783,7 +825,7 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) pdata->ring_cmd_addr = devm_ioremap_resource(dev, res); if (IS_ERR(pdata->ring_cmd_addr)) { dev_err(dev, "Unable to retrieve ENET Ring command region\n"); - return PTR_ERR(pdata->ring_cmd_addr); + return PTR_ERR(pdata->ring_cmd_addr); } ret = platform_get_irq(pdev, 0); @@ -797,22 +839,25 @@ 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, "Incorrect phy-connection-type in DTS\n"); return -EINVAL; } 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; @@ -824,7 +869,7 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) pdata->mcx_mac_csr_addr = base_addr + BLOCK_ETH_MAC_CSR_OFFSET; pdata->rx_buff_cnt = NUM_PKT_BUF; - return ret; + return 0; } static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) @@ -855,7 +900,7 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) dst_ring_num = xgene_enet_dst_ring_num(pdata->rx_ring); xgene_enet_cle_bypass(pdata, dst_ring_num, buf_pool->id); - return ret; + return 0; } static int xgene_enet_probe(struct platform_device *pdev) @@ -895,7 +940,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; @@ -936,6 +981,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",}, {}, @@ -947,6 +1000,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 0815866..60375cc 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 bcaa41a..ebad872 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -81,6 +81,7 @@ static const char version[] = #include #include #include +#include #include #include @@ -2408,6 +2409,14 @@ static struct dev_pm_ops smc_drv_pm_ops = { .resume = smc_drv_resume, }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id smc91x_acpi_match[] = { + { "LNRO0003", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, smc91x_acpi_match); +#endif + static struct platform_driver smc_driver = { .probe = smc_drv_probe, .remove = smc_drv_remove, @@ -2416,6 +2425,7 @@ static struct platform_driver smc_driver = { .owner = THIS_MODULE, .pm = &smc_drv_pm_ops, .of_match_table = of_match_ptr(smc91x_match), + .acpi_match_table = ACPI_PTR(smc91x_acpi_match), }, }; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 65de0ca..360f9a2 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 f3230ee..d852c6e 100644 --- a/drivers/net/phy/amd-xgbe-phy.c +++ b/drivers/net/phy/amd-xgbe-phy.c @@ -74,16 +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 @@ -118,77 +121,6 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100) #endif -/* SerDes integration register offsets */ -#define SIR0_KR_RT_1 0x002c -#define SIR0_STATUS 0x0040 -#define SIR1_SPEED 0x0000 - -/* SerDes integration register entry bit positions and sizes */ -#define SIR0_KR_RT_1_RESET_INDEX 11 -#define SIR0_KR_RT_1_RESET_WIDTH 1 -#define SIR0_STATUS_RX_READY_INDEX 0 -#define SIR0_STATUS_RX_READY_WIDTH 1 -#define SIR0_STATUS_TX_READY_INDEX 8 -#define SIR0_STATUS_TX_READY_WIDTH 1 -#define SIR1_SPEED_DATARATE_INDEX 4 -#define SIR1_SPEED_DATARATE_WIDTH 2 -#define SIR1_SPEED_PI_SPD_SEL_INDEX 12 -#define SIR1_SPEED_PI_SPD_SEL_WIDTH 4 -#define SIR1_SPEED_PLLSEL_INDEX 3 -#define SIR1_SPEED_PLLSEL_WIDTH 1 -#define SIR1_SPEED_RATECHANGE_INDEX 6 -#define SIR1_SPEED_RATECHANGE_WIDTH 1 -#define SIR1_SPEED_TXAMP_INDEX 8 -#define SIR1_SPEED_TXAMP_WIDTH 4 -#define SIR1_SPEED_WORDMODE_INDEX 0 -#define SIR1_SPEED_WORDMODE_WIDTH 3 - -#define SPEED_10000_CDR 0x7 -#define SPEED_10000_PLL 0x1 -#define SPEED_10000_RATE 0x0 -#define SPEED_10000_TXAMP 0xa -#define SPEED_10000_WORD 0x7 - -#define SPEED_2500_CDR 0x2 -#define SPEED_2500_PLL 0x0 -#define SPEED_2500_RATE 0x1 -#define SPEED_2500_TXAMP 0xf -#define SPEED_2500_WORD 0x1 - -#define SPEED_1000_CDR 0x2 -#define SPEED_1000_PLL 0x0 -#define SPEED_1000_RATE 0x3 -#define SPEED_1000_TXAMP 0xf -#define SPEED_1000_WORD 0x1 - - -/* SerDes RxTx register offsets */ -#define RXTX_REG20 0x0050 -#define RXTX_REG114 0x01c8 - -/* SerDes RxTx register entry bit positions and sizes */ -#define RXTX_REG20_BLWC_ENA_INDEX 2 -#define RXTX_REG20_BLWC_ENA_WIDTH 1 -#define RXTX_REG114_PQ_REG_INDEX 9 -#define RXTX_REG114_PQ_REG_WIDTH 7 - -#define RXTX_10000_BLWC 0 -#define RXTX_10000_PQ 0x1e - -#define RXTX_2500_BLWC 1 -#define RXTX_2500_PQ 0xa - -#define RXTX_1000_BLWC 1 -#define RXTX_1000_PQ 0xa - -/* Bit setting and getting macros - * The get macro will extract the current bit field value from within - * the variable - * - * The set macro will clear the current bit field value within the - * variable and then set the bit field of the variable to the - * specified value - */ #define GET_BITS(_var, _index, _width) \ (((_var) >> (_index)) & ((0x1 << (_width)) - 1)) @@ -198,71 +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 XSIR1_IOWRITE(_priv, _reg, _val) \ - iowrite16((_val), (_priv)->sir1_regs + _reg) - -#define XSIR1_IOWRITE_BITS(_priv, _reg, _field, _val) \ -do { \ - u16 reg_val = XSIR1_IOREAD((_priv), _reg); \ - SET_BITS(reg_val, \ - _reg##_##_field##_INDEX, \ - _reg##_##_field##_WIDTH, (_val)); \ - XSIR1_IOWRITE((_priv), _reg, reg_val); \ -} while (0) +#define XCMU_IOREAD(_priv, _reg) \ + ioread16((_priv)->cmu_regs + _reg) +#define XCMU_IOWRITE(_priv, _reg, _val) \ + iowrite16((_val), (_priv)->cmu_regs + _reg) -/* Macros for reading or writing SerDes RxTx registers - * The ioread macros will get bit fields or full values using the - * register definitions formed using the input names - * - * The iowrite macros will set bit fields or full values using the - * register definitions formed using the input names - */ #define XRXTX_IOREAD(_priv, _reg) \ ioread16((_priv)->rxtx_regs + _reg) @@ -283,6 +156,77 @@ do { \ XRXTX_IOWRITE((_priv), _reg, reg_val); \ } while (0) +/* SerDes CMU register offsets */ +#define CMU_REG15 0x003c +#define CMU_REG16 0x0040 + +/* SerDes CMU register entry bit positions and sizes */ +#define CMU_REG16_TX_RATE_CHANGE_BASE 15 +#define CMU_REG16_RX_RATE_CHANGE_BASE 14 +#define CMU_REG16_RATE_CHANGE_DECR 2 + + +/* SerDes RxTx register offsets */ +#define RXTX_REG2 0x0008 +#define RXTX_REG3 0x000c +#define RXTX_REG5 0x0014 +#define RXTX_REG6 0x0018 +#define RXTX_REG20 0x0050 +#define RXTX_REG53 0x00d4 +#define RXTX_REG114 0x01c8 +#define RXTX_REG115 0x01cc +#define RXTX_REG142 0x0238 + +/* SerDes RxTx register entry bit positions and sizes */ +#define RXTX_REG2_RESETB_INDEX 15 +#define RXTX_REG2_RESETB_WIDTH 1 +#define RXTX_REG3_TX_DATA_RATE_INDEX 14 +#define RXTX_REG3_TX_DATA_RATE_WIDTH 2 +#define RXTX_REG3_TX_WORD_MODE_INDEX 11 +#define RXTX_REG3_TX_WORD_MODE_WIDTH 3 +#define RXTX_REG5_TXAMP_CNTL_INDEX 7 +#define RXTX_REG5_TXAMP_CNTL_WIDTH 4 +#define RXTX_REG6_RX_DATA_RATE_INDEX 9 +#define RXTX_REG6_RX_DATA_RATE_WIDTH 2 +#define RXTX_REG6_RX_WORD_MODE_INDEX 11 +#define RXTX_REG6_RX_WORD_MODE_WIDTH 3 +#define RXTX_REG20_BLWC_ENA_INDEX 2 +#define RXTX_REG20_BLWC_ENA_WIDTH 1 +#define RXTX_REG53_RX_PLLSELECT_INDEX 15 +#define RXTX_REG53_RX_PLLSELECT_WIDTH 1 +#define RXTX_REG53_TX_PLLSELECT_INDEX 14 +#define RXTX_REG53_TX_PLLSELECT_WIDTH 1 +#define RXTX_REG53_PI_SPD_SEL_CDR_INDEX 10 +#define RXTX_REG53_PI_SPD_SEL_CDR_WIDTH 4 +#define RXTX_REG114_PQ_REG_INDEX 9 +#define RXTX_REG114_PQ_REG_WIDTH 7 +#define RXTX_REG115_FORCE_LAT_CAL_START_INDEX 2 +#define RXTX_REG115_FORCE_LAT_CAL_START_WIDTH 1 +#define RXTX_REG115_FORCE_SUM_CAL_START_INDEX 1 +#define RXTX_REG115_FORCE_SUM_CAL_START_WIDTH 1 +#define RXTX_REG142_SUM_CALIB_DONE_INDEX 15 +#define RXTX_REG142_SUM_CALIB_DONE_WIDTH 1 +#define RXTX_REG142_SUM_CALIB_ERR_INDEX 14 +#define RXTX_REG142_SUM_CALIB_ERR_WIDTH 1 +#define RXTX_REG142_LAT_CALIB_DONE_INDEX 11 +#define RXTX_REG142_LAT_CALIB_DONE_WIDTH 1 + +#define RXTX_FULL_RATE 0x0 +#define RXTX_HALF_RATE 0x1 +#define RXTX_FIFTH_RATE 0x3 +#define RXTX_66BIT_WORD 0x7 +#define RXTX_10BIT_WORD 0x1 +#define RXTX_10G_TX_AMP 0xa +#define RXTX_1G_TX_AMP 0xf +#define RXTX_10G_CDR 0x7 +#define RXTX_1G_CDR 0x2 +#define RXTX_10G_PLL 0x1 +#define RXTX_1G_PLL 0x0 +#define RXTX_10G_PQ 0x1e +#define RXTX_1G_PQ 0xa + + +DEFINE_SPINLOCK(cmu_lock); enum amd_xgbe_phy_an { AMD_XGBE_AN_READY = 0, @@ -309,30 +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; enum amd_xgbe_phy_mode mode; - unsigned int speed_set; /* Auto-negotiation state machine support */ struct mutex an_mutex; @@ -394,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) @@ -428,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; @@ -455,19 +418,30 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev) return ret; /* Set SerDes to 10G speed */ + spin_lock(&cmu_lock); + amd_xgbe_phy_serdes_start_ratechange(phydev); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR); + XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_DATA_RATE, RXTX_FULL_RATE); + XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_WORD_MODE, RXTX_66BIT_WORD); + + XRXTX_IOWRITE_BITS(priv, RXTX_REG5, TXAMP_CNTL, RXTX_10G_TX_AMP); + + XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_DATA_RATE, RXTX_FULL_RATE); + XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_WORD_MODE, RXTX_66BIT_WORD); - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ); + XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, 0); + + XRXTX_IOWRITE_BITS(priv, RXTX_REG53, RX_PLLSELECT, RXTX_10G_PLL); + XRXTX_IOWRITE_BITS(priv, RXTX_REG53, TX_PLLSELECT, RXTX_10G_PLL); + XRXTX_IOWRITE_BITS(priv, RXTX_REG53, PI_SPD_SEL_CDR, RXTX_10G_CDR); + + XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10G_PQ); amd_xgbe_phy_serdes_complete_ratechange(phydev); + spin_unlock(&cmu_lock); + priv->mode = AMD_XGBE_MODE_KR; return 0; @@ -505,19 +479,30 @@ static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev) return ret; /* Set SerDes to 2.5G speed */ + spin_lock(&cmu_lock); + amd_xgbe_phy_serdes_start_ratechange(phydev); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR); + XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_DATA_RATE, RXTX_HALF_RATE); + XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_WORD_MODE, RXTX_10BIT_WORD); + + XRXTX_IOWRITE_BITS(priv, RXTX_REG5, TXAMP_CNTL, RXTX_1G_TX_AMP); + + XRXTX_IOWRITE_BITS(priv, RXTX_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; @@ -555,19 +540,30 @@ static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev) return ret; /* Set SerDes to 1G speed */ + spin_lock(&cmu_lock); + amd_xgbe_phy_serdes_start_ratechange(phydev); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR); + XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_DATA_RATE, RXTX_FIFTH_RATE); + XRXTX_IOWRITE_BITS(priv, RXTX_REG3, TX_WORD_MODE, RXTX_10BIT_WORD); + + XRXTX_IOWRITE_BITS(priv, RXTX_REG5, TXAMP_CNTL, RXTX_1G_TX_AMP); + + XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_DATA_RATE, RXTX_FIFTH_RATE); + XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RX_WORD_MODE, RXTX_10BIT_WORD); - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_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; @@ -639,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; } @@ -1248,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) { @@ -1278,60 +1429,33 @@ 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_sir1; + goto err_resources; if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR) priv->mode = AMD_XGBE_MODE_KR; else @@ -1342,30 +1466,17 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev) priv->an_workqueue = create_singlethread_workqueue(wq_name); if (!priv->an_workqueue) { ret = -ENOMEM; - goto err_sir1; + goto err_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); @@ -1373,9 +1484,6 @@ err_priv: err_name: kfree(wq_name); -err_pdev: - of_dev_put(pdev); - return ret; } @@ -1392,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/address.c b/drivers/of/address.c index e371825..afdb782 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include /* Max address size we deal with */ @@ -293,6 +295,51 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, } EXPORT_SYMBOL_GPL(of_pci_range_parser_one); +/* + * of_pci_range_to_resource - Create a resource from an of_pci_range + * @range: the PCI range that describes the resource + * @np: device node where the range belongs to + * @res: pointer to a valid resource that will be updated to + * reflect the values contained in the range. + * + * Returns EINVAL if the range cannot be converted to resource. + * + * Note that if the range is an IO range, the resource will be converted + * using pci_address_to_pio() which can fail if it is called too early or + * if the range cannot be matched to any host bridge IO space (our case here). + * To guard against that we try to register the IO range first. + * If that fails we know that pci_address_to_pio() will do too. + */ +int of_pci_range_to_resource(struct of_pci_range *range, + struct device_node *np, struct resource *res) +{ + int err; + res->flags = range->flags; + res->parent = res->child = res->sibling = NULL; + res->name = np->full_name; + + if (res->flags & IORESOURCE_IO) { + unsigned long port; + err = pci_register_io_range(range->cpu_addr, range->size); + if (err) + goto invalid_range; + port = pci_address_to_pio(range->cpu_addr); + if (port == (unsigned long)-1) { + err = -EINVAL; + goto invalid_range; + } + res->start = port; + } else { + res->start = range->cpu_addr; + } + res->end = res->start + range->size - 1; + return 0; + +invalid_range: + res->start = (resource_size_t)OF_BAD_ADDR; + res->end = (resource_size_t)OF_BAD_ADDR; + return err; +} #endif /* CONFIG_PCI */ /* @@ -601,12 +648,119 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, } EXPORT_SYMBOL(of_get_address); +#ifdef PCI_IOBASE +struct io_range { + struct list_head list; + phys_addr_t start; + resource_size_t size; +}; + +static LIST_HEAD(io_range_list); +static DEFINE_SPINLOCK(io_range_lock); +#endif + +/* + * Record the PCI IO range (expressed as CPU physical address + size). + * Return a negative value if an error has occured, zero otherwise + */ +int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size) +{ + int err = 0; + +#ifdef PCI_IOBASE + struct io_range *range; + resource_size_t allocated_size = 0; + + /* check if the range hasn't been previously recorded */ + spin_lock(&io_range_lock); + list_for_each_entry(range, &io_range_list, list) { + if (addr >= range->start && addr + size <= range->start + size) { + /* range already registered, bail out */ + goto end_register; + } + allocated_size += range->size; + } + + /* range not registed yet, check for available space */ + if (allocated_size + size - 1 > IO_SPACE_LIMIT) { + /* if it's too big check if 64K space can be reserved */ + if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) { + err = -E2BIG; + goto end_register; + } + + size = SZ_64K; + pr_warn("Requested IO range too big, new size set to 64K\n"); + } + + /* add the range to the list */ + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) { + err = -ENOMEM; + goto end_register; + } + + range->start = addr; + range->size = size; + + list_add_tail(&range->list, &io_range_list); + +end_register: + spin_unlock(&io_range_lock); +#endif + + return err; +} + +phys_addr_t pci_pio_to_address(unsigned long pio) +{ + phys_addr_t address = (phys_addr_t)OF_BAD_ADDR; + +#ifdef PCI_IOBASE + struct io_range *range; + resource_size_t allocated_size = 0; + + if (pio > IO_SPACE_LIMIT) + return address; + + spin_lock(&io_range_lock); + list_for_each_entry(range, &io_range_list, list) { + if (pio >= allocated_size && pio < allocated_size + range->size) { + address = range->start + pio - allocated_size; + break; + } + allocated_size += range->size; + } + spin_unlock(&io_range_lock); +#endif + + return address; +} + unsigned long __weak pci_address_to_pio(phys_addr_t address) { +#ifdef PCI_IOBASE + struct io_range *res; + resource_size_t offset = 0; + unsigned long addr = -1; + + spin_lock(&io_range_lock); + list_for_each_entry(res, &io_range_list, list) { + if (address >= res->start && address < res->start + res->size) { + addr = res->start - address + offset; + break; + } + offset += res->size; + } + spin_unlock(&io_range_lock); + + return addr; +#else if (address > IO_SPACE_LIMIT) return (unsigned long)-1; return (unsigned long) address; +#endif } static int __of_address_to_resource(struct device_node *dev, diff --git a/drivers/of/base.c b/drivers/of/base.c index 293ed4b..270837b 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1247,6 +1247,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. @@ -2183,3 +2259,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/of/of_pci.c b/drivers/of/of_pci.c index 8481996..8882b46 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -1,7 +1,9 @@ #include #include #include +#include #include +#include static inline int __of_pci_pci_compare(struct device_node *node, unsigned int data) @@ -89,6 +91,146 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res) } EXPORT_SYMBOL_GPL(of_pci_parse_bus_range); +/** + * This function will try to obtain the host bridge domain number by + * finding a property called "linux,pci-domain" of the given device node. + * + * @node: device tree node with the domain information + * + * Returns the associated domain number from DT in the range [0-0xffff], or + * a negative value if the required property is not found. + */ +int of_get_pci_domain_nr(struct device_node *node) +{ + const __be32 *value; + int len; + u16 domain; + + value = of_get_property(node, "linux,pci-domain", &len); + if (!value || len < sizeof(*value)) + return -EINVAL; + + domain = (u16)be32_to_cpup(value); + + return domain; +} +EXPORT_SYMBOL_GPL(of_get_pci_domain_nr); + +#if defined(CONFIG_OF_ADDRESS) +/** + * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT + * @dev: device node of the host bridge having the range property + * @busno: bus number associated with the bridge root bus + * @bus_max: maximum number of buses for this bridge + * @resources: list where the range of resources will be added after DT parsing + * @io_base: pointer to a variable that will contain on return the physical + * address for the start of the I/O range. Can be NULL if the caller doesn't + * expect IO ranges to be present in the device tree. + * + * It is the caller's job to free the @resources list. + * + * This function will parse the "ranges" property of a PCI host bridge device + * node and setup the resource mapping based on its content. It is expected + * that the property conforms with the Power ePAPR document. + * + * It returns zero if the range parsing has been successful or a standard error + * value if it failed. + */ +int of_pci_get_host_bridge_resources(struct device_node *dev, + unsigned char busno, unsigned char bus_max, + struct list_head *resources, resource_size_t *io_base) +{ + struct resource *res; + struct resource *bus_range; + struct of_pci_range range; + struct of_pci_range_parser parser; + char range_type[4]; + int err; + + if (io_base) + *io_base = (resource_size_t)OF_BAD_ADDR; + + bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL); + if (!bus_range) + return -ENOMEM; + + pr_info("PCI host bridge %s ranges:\n", dev->full_name); + + err = of_pci_parse_bus_range(dev, bus_range); + if (err) { + bus_range->start = busno; + bus_range->end = bus_max; + bus_range->flags = IORESOURCE_BUS; + pr_info(" No bus range found for %s, using %pR\n", + dev->full_name, bus_range); + } else { + if (bus_range->end > bus_range->start + bus_max) + bus_range->end = bus_range->start + bus_max; + } + pci_add_resource(resources, bus_range); + + /* Check for ranges property */ + err = of_pci_range_parser_init(&parser, dev); + if (err) + goto parse_failed; + + pr_debug("Parsing ranges property...\n"); + for_each_of_pci_range(&parser, &range) { + /* Read next ranges element */ + if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO) + snprintf(range_type, 4, " IO"); + else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM) + snprintf(range_type, 4, "MEM"); + else + snprintf(range_type, 4, "err"); + pr_info(" %s %#010llx..%#010llx -> %#010llx\n", range_type, + range.cpu_addr, range.cpu_addr + range.size - 1, + range.pci_addr); + + /* + * If we failed translation or got a zero-sized region + * then skip this range + */ + if (range.cpu_addr == OF_BAD_ADDR || range.size == 0) + continue; + + res = kzalloc(sizeof(struct resource), GFP_KERNEL); + if (!res) { + err = -ENOMEM; + goto parse_failed; + } + + err = of_pci_range_to_resource(&range, dev, res); + if (err) + goto conversion_failed; + + if (resource_type(res) == IORESOURCE_IO) { + if (!io_base) { + pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n", + dev->full_name); + err = -EINVAL; + goto conversion_failed; + } + if (*io_base != (resource_size_t)OF_BAD_ADDR) + pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n", + dev->full_name); + *io_base = range.cpu_addr; + } + + pci_add_resource_offset(resources, res, res->start - range.pci_addr); + } + + return 0; + +conversion_failed: + kfree(res); +parse_failed: + pci_free_resource_list(resources); + return err; +} +EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); +#endif /* CONFIG_OF_ADDRESS */ + #ifdef CONFIG_PCI_MSI static LIST_HEAD(of_pci_msi_chip_list); diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 90f5cca..382fd3d 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -63,4 +63,14 @@ config PCIE_SPEAR13XX help Say Y here if you want PCIe support on SPEAr13XX SoCs. +config PCI_XGENE + bool "X-Gene PCIe controller" + depends on ARCH_XGENE + depends on OF + select PCIEPORTBUS + help + Say Y here if you want internal PCI support on APM X-Gene SoC. + There are 5 internal PCIe ports available. Each port is GEN3 capable + and have varied lanes from x1 to x8. + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index d0e88f1..845611f 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o +obj-$(CONFIG_PCI_XGENE) += pci-xgene.o diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 0fb0fdb..946935d 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -626,13 +626,14 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) { struct tegra_pcie *pcie = sys_to_pcie(sys); + phys_addr_t io_start = pci_pio_to_address(pcie->io.start); pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource_offset(&sys->resources, &pcie->prefetch, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); - pci_ioremap_io(nr * SZ_64K, pcie->io.start); + pci_ioremap_io(nr * SZ_64K, io_start); return 1; } @@ -737,6 +738,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) { u32 fpci_bar, size, axi_address; + phys_addr_t io_start = pci_pio_to_address(pcie->io.start); /* Bar 0: type 1 extended configuration space */ fpci_bar = 0xfe100000; @@ -749,7 +751,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) /* Bar 1: downstream IO bar */ fpci_bar = 0xfdfc0000; size = resource_size(&pcie->io); - axi_address = pcie->io.start; + axi_address = io_start; afi_writel(pcie, axi_address, AFI_AXI_BAR1_START); afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1); @@ -1520,7 +1522,9 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) } for_each_of_pci_range(&parser, &range) { - of_pci_range_to_resource(&range, np, &res); + err = of_pci_range_to_resource(&range, np, &res); + if (err < 0) + return err; switch (res.flags & IORESOURCE_TYPE_BITS) { case IORESOURCE_IO: diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c new file mode 100644 index 0000000..41c76b8 --- /dev/null +++ b/drivers/pci/host/pci-xgene.c @@ -0,0 +1,659 @@ +/** + * APM X-Gene PCIe Driver + * + * Copyright (c) 2014 Applied Micro Circuits Corporation. + * + * Author: Tanmay Inamdar . + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCIECORE_CTLANDSTATUS 0x50 +#define PIM1_1L 0x80 +#define IBAR2 0x98 +#define IR2MSK 0x9c +#define PIM2_1L 0xa0 +#define IBAR3L 0xb4 +#define IR3MSKL 0xbc +#define PIM3_1L 0xc4 +#define OMR1BARL 0x100 +#define OMR2BARL 0x118 +#define OMR3BARL 0x130 +#define CFGBARL 0x154 +#define CFGBARH 0x158 +#define CFGCTL 0x15c +#define RTDID 0x160 +#define BRIDGE_CFG_0 0x2000 +#define BRIDGE_CFG_4 0x2010 +#define BRIDGE_STATUS_0 0x2600 + +#define LINK_UP_MASK 0x00000100 +#define AXI_EP_CFG_ACCESS 0x10000 +#define EN_COHERENCY 0xF0000000 +#define EN_REG 0x00000001 +#define OB_LO_IO 0x00000002 +#define XGENE_PCIE_VENDORID 0x10E8 +#define XGENE_PCIE_DEVICEID 0xE004 +#define SZ_1T (SZ_1G*1024ULL) +#define PIPE_PHY_RATE_RD(src) ((0xc000 & (u32)(src)) >> 0xe) + +struct xgene_pcie_port { + struct device_node *node; + struct device *dev; + struct clk *clk; + void __iomem *csr_base; + void __iomem *cfg_base; + unsigned long cfg_addr; + bool link_up; +}; + +static inline u32 pcie_bar_low_val(u32 addr, u32 flags) +{ + return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags; +} + +/* PCIE Configuration Out/In */ +static inline void xgene_pcie_cfg_out32(void __iomem *addr, int offset, u32 val) +{ + writel(val, addr + offset); +} + +static inline void xgene_pcie_cfg_out16(void __iomem *addr, int offset, u16 val) +{ + u32 val32 = readl(addr + (offset & ~0x3)); + + switch (offset & 0x3) { + case 2: + val32 &= ~0xFFFF0000; + val32 |= (u32)val << 16; + break; + case 0: + default: + val32 &= ~0xFFFF; + val32 |= val; + break; + } + writel(val32, addr + (offset & ~0x3)); +} + +static inline void xgene_pcie_cfg_out8(void __iomem *addr, int offset, u8 val) +{ + u32 val32 = readl(addr + (offset & ~0x3)); + + switch (offset & 0x3) { + case 0: + val32 &= ~0xFF; + val32 |= val; + break; + case 1: + val32 &= ~0xFF00; + val32 |= (u32)val << 8; + break; + case 2: + val32 &= ~0xFF0000; + val32 |= (u32)val << 16; + break; + case 3: + default: + val32 &= ~0xFF000000; + val32 |= (u32)val << 24; + break; + } + writel(val32, addr + (offset & ~0x3)); +} + +static inline void xgene_pcie_cfg_in32(void __iomem *addr, int offset, u32 *val) +{ + *val = readl(addr + offset); +} + +static inline void xgene_pcie_cfg_in16(void __iomem *addr, int offset, u32 *val) +{ + *val = readl(addr + (offset & ~0x3)); + + switch (offset & 0x3) { + case 2: + *val >>= 16; + break; + } + + *val &= 0xFFFF; +} + +static inline void xgene_pcie_cfg_in8(void __iomem *addr, int offset, u32 *val) +{ + *val = readl(addr + (offset & ~0x3)); + + switch (offset & 0x3) { + case 3: + *val = *val >> 24; + break; + case 2: + *val = *val >> 16; + break; + case 1: + *val = *val >> 8; + break; + } + *val &= 0xFF; +} + +/* + * When the address bit [17:16] is 2'b01, the Configuration access will be + * treated as Type 1 and it will be forwarded to external PCIe device. + */ +static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus) +{ + struct xgene_pcie_port *port = bus->sysdata; + + if (bus->number >= (bus->primary + 1)) + return port->cfg_base + AXI_EP_CFG_ACCESS; + + return port->cfg_base; +} + +/* + * For Configuration request, RTDID register is used as Bus Number, + * Device Number and Function number of the header fields. + */ +static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn) +{ + struct xgene_pcie_port *port = bus->sysdata; + unsigned int b, d, f; + u32 rtdid_val = 0; + + b = bus->number; + d = PCI_SLOT(devfn); + f = PCI_FUNC(devfn); + + if (!pci_is_root_bus(bus)) + rtdid_val = (b << 8) | (d << 3) | f; + + writel(rtdid_val, port->csr_base + RTDID); + /* read the register back to ensure flush */ + readl(port->csr_base + RTDID); +} + +/* + * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as + * the translation between PCI bus to native BUS. Entire DDR region + * is mapped on to PCIe space using these registers, so that it is + * accessible for EP devices for DMA. The BAR0/1 of bridge should be + * hidden during enumeration to avoid the sizing and resource allocation + * by PCIe core. + */ +static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) +{ + if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) || + (offset == PCI_BASE_ADDRESS_1))) + return true; + + return false; +} + +static int xgene_pcie_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct xgene_pcie_port *port = bus->sysdata; + void __iomem *addr; + + if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (xgene_pcie_hide_rc_bars(bus, offset)) { + *val = 0; + return PCIBIOS_SUCCESSFUL; + } + + xgene_pcie_set_rtdid_reg(bus, devfn); + addr = xgene_pcie_get_cfg_base(bus); + switch (len) { + case 1: + xgene_pcie_cfg_in8(addr, offset, val); + break; + case 2: + xgene_pcie_cfg_in16(addr, offset, val); + break; + default: + xgene_pcie_cfg_in32(addr, offset, val); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int xgene_pcie_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct xgene_pcie_port *port = bus->sysdata; + void __iomem *addr; + + if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (xgene_pcie_hide_rc_bars(bus, offset)) + return PCIBIOS_SUCCESSFUL; + + xgene_pcie_set_rtdid_reg(bus, devfn); + addr = xgene_pcie_get_cfg_base(bus); + switch (len) { + case 1: + xgene_pcie_cfg_out8(addr, offset, (u8)val); + break; + case 2: + xgene_pcie_cfg_out16(addr, offset, (u16)val); + break; + default: + xgene_pcie_cfg_out32(addr, offset, val); + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops xgene_pcie_ops = { + .read = xgene_pcie_read_config, + .write = xgene_pcie_write_config +}; + +static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr, + u32 flags, u64 size) +{ + u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags; + u32 val32 = 0; + u32 val; + + val32 = readl(csr_base + addr); + val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16); + writel(val, csr_base + addr); + + val32 = readl(csr_base + addr + 0x04); + val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16); + writel(val, csr_base + addr + 0x04); + + val32 = readl(csr_base + addr + 0x04); + val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16); + writel(val, csr_base + addr + 0x04); + + val32 = readl(csr_base + addr + 0x08); + val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16); + writel(val, csr_base + addr + 0x08); + + return mask; +} + +static void xgene_pcie_linkup(struct xgene_pcie_port *port, + u32 *lanes, u32 *speed) +{ + void __iomem *csr_base = port->csr_base; + u32 val32; + + port->link_up = false; + val32 = readl(csr_base + PCIECORE_CTLANDSTATUS); + if (val32 & LINK_UP_MASK) { + port->link_up = true; + *speed = PIPE_PHY_RATE_RD(val32); + val32 = readl(csr_base + BRIDGE_STATUS_0); + *lanes = val32 >> 26; + } +} + +static int xgene_pcie_init_port(struct xgene_pcie_port *port) +{ + int rc; + + port->clk = clk_get(port->dev, NULL); + if (IS_ERR(port->clk)) { + dev_err(port->dev, "clock not available\n"); + return -ENODEV; + } + + rc = clk_prepare_enable(port->clk); + if (rc) { + dev_err(port->dev, "clock enable failed\n"); + return rc; + } + + return 0; +} + +static int xgene_pcie_map_reg(struct xgene_pcie_port *port, + struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); + port->csr_base = devm_ioremap_resource(port->dev, res); + if (IS_ERR(port->csr_base)) + return PTR_ERR(port->csr_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); + port->cfg_base = devm_ioremap_resource(port->dev, res); + if (IS_ERR(port->cfg_base)) + return PTR_ERR(port->cfg_base); + port->cfg_addr = res->start; + + return 0; +} + +static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port, + struct resource *res, u32 offset, + u64 cpu_addr, u64 pci_addr) +{ + void __iomem *base = port->csr_base + offset; + resource_size_t size = resource_size(res); + u64 restype = resource_type(res); + u64 mask = 0; + u32 min_size; + u32 flag = EN_REG; + + if (restype == IORESOURCE_MEM) { + min_size = SZ_128M; + } else { + min_size = 128; + flag |= OB_LO_IO; + } + + if (size >= min_size) + mask = ~(size - 1) | flag; + else + dev_warn(port->dev, "res size 0x%llx less than minimum 0x%x\n", + (u64)size, min_size); + + writel(lower_32_bits(cpu_addr), base); + writel(upper_32_bits(cpu_addr), base + 0x04); + writel(lower_32_bits(mask), base + 0x08); + writel(upper_32_bits(mask), base + 0x0c); + writel(lower_32_bits(pci_addr), base + 0x10); + writel(upper_32_bits(pci_addr), base + 0x14); +} + +static void xgene_pcie_setup_cfg_reg(void __iomem *csr_base, u64 addr) +{ + writel(lower_32_bits(addr), csr_base + CFGBARL); + writel(upper_32_bits(addr), csr_base + CFGBARH); + writel(EN_REG, csr_base + CFGCTL); +} + +static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, + struct list_head *res, + resource_size_t io_base) +{ + struct pci_host_bridge_window *window; + struct device *dev = port->dev; + int ret; + + list_for_each_entry(window, res, list) { + struct resource *res = window->res; + u64 restype = resource_type(res); + + dev_dbg(port->dev, "%pR\n", res); + + switch (restype) { + case IORESOURCE_IO: + xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base, + res->start - window->offset); + ret = pci_remap_iospace(res, io_base); + if (ret < 0) + return ret; + break; + case IORESOURCE_MEM: + xgene_pcie_setup_ob_reg(port, res, OMR1BARL, res->start, + res->start - window->offset); + break; + case IORESOURCE_BUS: + break; + default: + dev_err(dev, "invalid io resource - %pR\n", res); + return -EINVAL; + } + } + xgene_pcie_setup_cfg_reg(port->csr_base, port->cfg_addr); + + return 0; +} + +static void xgene_pcie_setup_pims(void *addr, u64 pim, u64 size) +{ + writel(lower_32_bits(pim), addr); + writel(upper_32_bits(pim) | EN_COHERENCY, addr + 0x04); + writel(lower_32_bits(size), addr + 0x10); + writel(upper_32_bits(size), addr + 0x14); +} + +/* + * X-Gene PCIe support maximum 3 inbound memory regions + * This function helps to select a region based on size of region + */ +static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size) +{ + if ((size > 4) && (size < SZ_16M) && !(*ib_reg_mask & (1 << 1))) { + *ib_reg_mask |= (1 << 1); + return 1; + } + + if ((size > SZ_1K) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 0))) { + *ib_reg_mask |= (1 << 0); + return 0; + } + + if ((size > SZ_1M) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 2))) { + *ib_reg_mask |= (1 << 2); + return 2; + } + + return -EINVAL; +} + +static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, + struct of_pci_range *range, u8 *ib_reg_mask) +{ + void __iomem *csr_base = port->csr_base; + void __iomem *cfg_base = port->cfg_base; + void *bar_addr; + void *pim_addr; + u64 cpu_addr = range->cpu_addr; + u64 pci_addr = range->pci_addr; + u64 size = range->size; + u64 mask = ~(size - 1) | EN_REG; + u32 flags = PCI_BASE_ADDRESS_MEM_TYPE_64; + u32 bar_low; + int region; + + region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size); + if (region < 0) { + dev_warn(port->dev, "invalid pcie dma-range config\n"); + return; + } + + if (range->flags & IORESOURCE_PREFETCH) + flags |= PCI_BASE_ADDRESS_MEM_PREFETCH; + + bar_low = pcie_bar_low_val((u32)cpu_addr, flags); + switch (region) { + case 0: + xgene_pcie_set_ib_mask(csr_base, BRIDGE_CFG_4, flags, size); + bar_addr = cfg_base + PCI_BASE_ADDRESS_0; + writel(bar_low, bar_addr); + writel(upper_32_bits(cpu_addr), bar_addr + 0x4); + pim_addr = csr_base + PIM1_1L; + break; + case 1: + bar_addr = csr_base + IBAR2; + writel(bar_low, bar_addr); + writel(lower_32_bits(mask), csr_base + IR2MSK); + pim_addr = csr_base + PIM2_1L; + break; + case 2: + bar_addr = csr_base + IBAR3L; + writel(bar_low, bar_addr); + writel(upper_32_bits(cpu_addr), bar_addr + 0x4); + writel(lower_32_bits(mask), csr_base + IR3MSKL); + writel(upper_32_bits(mask), csr_base + IR3MSKL + 0x4); + pim_addr = csr_base + PIM3_1L; + break; + } + + xgene_pcie_setup_pims(pim_addr, pci_addr, ~(size - 1)); +} + +static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node) +{ + const int na = 3, ns = 2; + int rlen; + + parser->node = node; + parser->pna = of_n_addr_cells(node); + parser->np = parser->pna + na + ns; + + parser->range = of_get_property(node, "dma-ranges", &rlen); + if (!parser->range) + return -ENOENT; + parser->end = parser->range + rlen / sizeof(__be32); + + return 0; +} + +static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port) +{ + struct device_node *np = port->node; + struct of_pci_range range; + struct of_pci_range_parser parser; + struct device *dev = port->dev; + u8 ib_reg_mask = 0; + + if (pci_dma_range_parser_init(&parser, np)) { + dev_err(dev, "missing dma-ranges property\n"); + return -EINVAL; + } + + /* Get the dma-ranges from DT */ + for_each_of_pci_range(&parser, &range) { + u64 end = range.cpu_addr + range.size - 1; + + dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", + range.flags, range.cpu_addr, end, range.pci_addr); + xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask); + } + return 0; +} + +/* clear bar configuration which was done by firmware */ +static void xgene_pcie_clear_config(struct xgene_pcie_port *port) +{ + int i; + + for (i = PIM1_1L; i <= CFGCTL; i += 4) + writel(0x0, port->csr_base + i); +} + +static int xgene_pcie_setup(struct xgene_pcie_port *port, + struct list_head *res, + resource_size_t io_base) +{ + u32 val, lanes = 0, speed = 0; + int ret; + + xgene_pcie_clear_config(port); + + /* setup the vendor and device IDs correctly */ + val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID; + writel(val, port->csr_base + BRIDGE_CFG_0); + + ret = xgene_pcie_map_ranges(port, res, io_base); + if (ret) + return ret; + + ret = xgene_pcie_parse_map_dma_ranges(port); + if (ret) + return ret; + + xgene_pcie_linkup(port, &lanes, &speed); + if (!port->link_up) + dev_info(port->dev, "(rc) link down\n"); + else + dev_info(port->dev, "(rc) x%d gen-%d link up\n", + lanes, speed + 1); + return 0; +} + +static int xgene_pcie_probe_bridge(struct platform_device *pdev) +{ + struct device_node *dn = pdev->dev.of_node; + struct xgene_pcie_port *port; + resource_size_t iobase = 0; + struct pci_bus *bus; + int ret; + LIST_HEAD(res); + + port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + port->node = of_node_get(pdev->dev.of_node); + port->dev = &pdev->dev; + + ret = xgene_pcie_map_reg(port, pdev); + if (ret) + return ret; + + ret = xgene_pcie_init_port(port); + if (ret) + return ret; + + ret = of_pci_get_host_bridge_resources(dn, 0, 0xff, &res, &iobase); + if (ret) + return ret; + + ret = xgene_pcie_setup(port, &res, iobase); + if (ret) + return ret; + + bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res); + if (!bus) + return -ENOMEM; + + platform_set_drvdata(pdev, port); + return 0; +} + +static const struct of_device_id xgene_pcie_match_table[] = { + {.compatible = "apm,xgene-pcie",}, + {}, +}; + +static struct platform_driver xgene_pcie_driver = { + .driver = { + .name = "xgene-pcie", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(xgene_pcie_match_table), + }, + .probe = xgene_pcie_probe_bridge, +}; +module_platform_driver(xgene_pcie_driver); + +MODULE_AUTHOR("Tanmay Inamdar "); +MODULE_DESCRIPTION("APM X-Gene PCIe driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 4884ee5..61158e0 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -323,6 +323,7 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie) /* Setup PCIe address space mappings for each resource */ resource_size_t size; + resource_size_t res_start; u32 mask; rcar_pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win)); @@ -335,8 +336,13 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie) mask = (roundup_pow_of_two(size) / SZ_128) - 1; rcar_pci_write_reg(pcie, mask << 7, PCIEPAMR(win)); - rcar_pci_write_reg(pcie, upper_32_bits(res->start), PCIEPARH(win)); - rcar_pci_write_reg(pcie, lower_32_bits(res->start), PCIEPARL(win)); + if (res->flags & IORESOURCE_IO) + res_start = pci_pio_to_address(res->start); + else + res_start = res->start; + + rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPARH(win)); + rcar_pci_write_reg(pcie, lower_32_bits(res_start), PCIEPARL(win)); /* First resource is for IO */ mask = PAR_ENABLE; @@ -363,9 +369,10 @@ static int rcar_pcie_setup(int nr, struct pci_sys_data *sys) rcar_pcie_setup_window(i, pcie); - if (res->flags & IORESOURCE_IO) - pci_ioremap_io(nr * SZ_64K, res->start); - else + if (res->flags & IORESOURCE_IO) { + phys_addr_t io_start = pci_pio_to_address(res->start); + pci_ioremap_io(nr * SZ_64K, io_start); + } else pci_add_resource(&sys->resources, res); } pci_add_resource(&sys->resources, &pcie->busn); @@ -935,8 +942,10 @@ static int rcar_pcie_probe(struct platform_device *pdev) } for_each_of_pci_range(&parser, &range) { - of_pci_range_to_resource(&range, pdev->dev.of_node, + err = of_pci_range_to_resource(&range, pdev->dev.of_node, &pcie->res[win++]); + if (err < 0) + return err; if (win > RCAR_PCI_MAX_RESOURCES) break; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 2c9ac70..6e994fc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2704,6 +2704,37 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) } EXPORT_SYMBOL(pci_request_regions_exclusive); +/** + * pci_remap_iospace - Remap the memory mapped I/O space + * @res: Resource describing the I/O space + * @phys_addr: physical address of range to be mapped + * + * Remap the memory mapped I/O space described by the @res + * and the CPU physical address @phys_addr into virtual address space. + * Only architectures that have memory mapped IO functions defined + * (and the PCI_IOBASE value defined) should call this function. + */ +int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr) +{ +#if defined(PCI_IOBASE) && defined(CONFIG_MMU) + unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start; + + if (!(res->flags & IORESOURCE_IO)) + return -EINVAL; + + if (res->end > IO_SPACE_LIMIT) + return -EINVAL; + + return ioremap_page_range(vaddr, vaddr + resource_size(res), phys_addr, + pgprot_device(PAGE_KERNEL)); +#else + /* this architecture does not have memory mapped I/O space, + so this function should never be called */ + WARN_ONCE(1, "This architecture does not support memory mapped I/O\n"); + return -ENODEV; +#endif +} + static void __pci_set_master(struct pci_dev *dev, bool enable) { u16 old_cmd, cmd; @@ -4406,6 +4437,15 @@ static void pci_no_domains(void) #endif } +#ifdef CONFIG_PCI_DOMAINS +static atomic_t __domain_nr = ATOMIC_INIT(-1); + +int pci_get_new_domain_nr(void) +{ + return atomic_inc_return(&__domain_nr); +} +#endif + /** * pci_ext_cfg_avail - can we access extended PCI config space? * diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 4170113..ff44f5a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -485,7 +485,7 @@ void pci_read_bridge_bases(struct pci_bus *child) } } -static struct pci_bus *pci_alloc_bus(void) +static struct pci_bus *pci_alloc_bus(struct pci_bus *parent) { struct pci_bus *b; @@ -500,6 +500,10 @@ static struct pci_bus *pci_alloc_bus(void) INIT_LIST_HEAD(&b->resources); b->max_bus_speed = PCI_SPEED_UNKNOWN; b->cur_bus_speed = PCI_SPEED_UNKNOWN; +#ifdef CONFIG_PCI_DOMAINS_GENERIC + if (parent) + b->domain_nr = parent->domain_nr; +#endif return b; } @@ -671,7 +675,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, /* * Allocate a new bus, and inherit stuff from the parent.. */ - child = pci_alloc_bus(); + child = pci_alloc_bus(parent); if (!child) return NULL; @@ -1751,13 +1755,14 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, char bus_addr[64]; char *fmt; - b = pci_alloc_bus(); + b = pci_alloc_bus(NULL); if (!b) return NULL; b->sysdata = sysdata; b->ops = ops; b->number = b->busn_res.start = bus; + pci_bus_assign_domain_nr(b, parent); b2 = pci_find_bus(pci_domain_nr(b), bus); if (b2) { /* If we already got to this bus through a different bridge, ignore it */ @@ -1936,6 +1941,9 @@ struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, if (!found) pci_bus_update_busn_res_end(b, max); + if (!pci_has_flag(PCI_PROBE_ONLY)) + pci_assign_unassigned_bus_resources(b); + pci_bus_add_devices(b); return b; } diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c index 782e822..d952462 100644 --- a/drivers/pnp/resource.c +++ b/drivers/pnp/resource.c @@ -313,6 +313,7 @@ static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci, progif = class & 0xff; class >>= 8; +#ifdef HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ if (class == PCI_CLASS_STORAGE_IDE) { /* * Unless both channels are native-PCI mode only, @@ -326,6 +327,7 @@ static int pci_dev_uses_irq(struct pnp_dev *pnp, struct pci_dev *pci, return 1; } } +#endif /* HAVE_ARCH_PCI_GET_LEGACY_IDE_IRQ */ return 0; } diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index b24aa01..50fe279 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -419,4 +419,10 @@ config DA_CONSOLE help This enables a console on a Dash channel. +config SBSAUART_TTY + tristate "SBSA UART TTY Driver" + help + Console and system TTY driver for the SBSA UART which is defined + in the Server Base System Architecure document for ARM64 servers. + endif # TTY diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 58ad1c0..c3211c0 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -29,5 +29,6 @@ obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_DA_TTY) += metag_da.o +obj-$(CONFIG_SBSAUART_TTY) += sbsauart.o obj-y += ipwireless/ diff --git a/drivers/tty/sbsauart.c b/drivers/tty/sbsauart.c new file mode 100644 index 0000000..402f168 --- /dev/null +++ b/drivers/tty/sbsauart.c @@ -0,0 +1,355 @@ +/* + * SBSA (Server Base System Architecture) Compatible UART driver + * + * Copyright (C) 2014 Linaro Ltd + * + * Author: Graeme Gregory + * + * 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 57d9df8..e075437 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -313,10 +313,18 @@ static int dw8250_probe_of(struct uart_port *p, static int dw8250_probe_acpi(struct uart_8250_port *up, struct dw8250_data *data) { + const struct acpi_device_id *id; struct uart_port *p = &up->port; dw8250_setup_port(up); + id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev); + if (!id) + return -ENODEV; + + if (!p->uartclk) + p->uartclk = (unsigned int)id->driver_data; + p->iotype = UPIO_MEM32; p->serial_in = dw8250_serial_in32; p->serial_out = dw8250_serial_out32; @@ -541,6 +549,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = { { "INT3435", 0 }, { "80860F0A", 0 }, { "8086228A", 0 }, + { "APMC0D08", 50000000}, { }, }; MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match); diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index c600ccf..10c4775 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -100,8 +100,7 @@ #include #include #include - - +#include /* The alignment to use between consumer and producer parts of vring. * Currently hardcoded to the page size. */ @@ -637,6 +636,14 @@ static struct of_device_id virtio_mmio_match[] = { }; MODULE_DEVICE_TABLE(of, virtio_mmio_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id virtio_mmio_acpi_match[] = { + { "LNRO0005", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match); +#endif + static struct platform_driver virtio_mmio_driver = { .probe = virtio_mmio_probe, .remove = virtio_mmio_remove, @@ -644,6 +651,7 @@ static struct platform_driver virtio_mmio_driver = { .name = "virtio-mmio", .owner = THIS_MODULE, .of_match_table = virtio_mmio_match, + .acpi_match_table = ACPI_PTR(virtio_mmio_acpi_match), }, }; diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h index c728113..f97804b 100644 --- a/include/acpi/acnames.h +++ b/include/acpi/acnames.h @@ -59,6 +59,10 @@ #define METHOD_NAME__PRS "_PRS" #define METHOD_NAME__PRT "_PRT" #define METHOD_NAME__PRW "_PRW" +#define METHOD_NAME__PS0 "_PS0" +#define METHOD_NAME__PS1 "_PS1" +#define METHOD_NAME__PS2 "_PS2" +#define METHOD_NAME__PS3 "_PS3" #define METHOD_NAME__REG "_REG" #define METHOD_NAME__SB_ "_SB_" #define METHOD_NAME__SEG "_SEG" diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 57ee052..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/acpi/acpixf.h b/include/acpi/acpixf.h index b7c89d4..dc9d037 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20140724 +#define ACPI_CA_VERSION 0x20140828 #include #include diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 7626bfe..29e7937 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -952,7 +952,8 @@ enum acpi_srat_type { ACPI_SRAT_TYPE_CPU_AFFINITY = 0, ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1, ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2, - ACPI_SRAT_TYPE_RESERVED = 3 /* 3 and greater are reserved */ + ACPI_SRAT_TYPE_GICC_AFFINITY = 3, + ACPI_SRAT_TYPE_RESERVED = 4 /* 4 and greater are reserved */ }; /* @@ -968,7 +969,7 @@ struct acpi_srat_cpu_affinity { u32 flags; u8 local_sapic_eid; u8 proximity_domain_hi[3]; - u32 reserved; /* Reserved, must be zero */ + u32 clock_domain; }; /* Flags */ @@ -1010,6 +1011,20 @@ struct acpi_srat_x2apic_cpu_affinity { #define ACPI_SRAT_CPU_ENABLED (1) /* 00: Use affinity structure */ +/* 3: GICC Affinity (ACPI 5.1) */ + +struct acpi_srat_gicc_affinity { + struct acpi_subtable_header header; + u32 proximity_domain; + u32 acpi_processor_uid; + u32 flags; + u32 clock_domain; +}; + +/* Flags for struct acpi_srat_gicc_affinity */ + +#define ACPI_SRAT_GICC_ENABLED (1) /* 00: Use affinity structure */ + /* Reset to default packing */ #pragma pack() diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 787bcc8..5480cb2 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -310,10 +310,15 @@ struct acpi_gtdt_timer_entry { u32 common_flags; }; +/* Flag Definitions: timer_flags and virtual_timer_flags above */ + +#define ACPI_GTDT_GT_IRQ_MODE (1) +#define ACPI_GTDT_GT_IRQ_POLARITY (1<<1) + /* Flag Definitions: common_flags above */ -#define ACPI_GTDT_GT_IS_SECURE_TIMER (1) -#define ACPI_GTDT_GT_ALWAYS_ON (1<<1) +#define ACPI_GTDT_GT_IS_SECURE_TIMER (1) +#define ACPI_GTDT_GT_ALWAYS_ON (1<<1) /* 1: SBSA Generic Watchdog Structure */ diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index 975e1cc..b8fdc57 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -331,7 +331,7 @@ static inline void iounmap(void __iomem *addr) #ifndef CONFIG_GENERIC_IOMAP static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) { - return (void __iomem *) port; + return PCI_IOBASE + (port & IO_SPACE_LIMIT); } static inline void ioport_unmap(void __iomem *p) diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 53b2acc..977e545 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -249,6 +249,10 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b) #define pgprot_writecombine pgprot_noncached #endif +#ifndef pgprot_device +#define pgprot_device pgprot_noncached +#endif + /* * When walking page tables, get the address of the next boundary, * or the end address of the range if that comes earlier. Although no diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 35b0c12..d98e96b 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -237,17 +237,19 @@ bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, #define irqchip_in_kernel(k) (!!((k)->arch.vgic.in_kernel)) #define vgic_initialized(k) ((k)->arch.vgic.ready) -int vgic_v2_probe(struct device_node *vgic_node, - const struct vgic_ops **ops, - const struct vgic_params **params); +int vgic_v2_dt_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params); +int vgic_v2_acpi_probe(const struct vgic_ops **ops, + const struct vgic_params **params); #ifdef CONFIG_ARM_GIC_V3 -int vgic_v3_probe(struct device_node *vgic_node, - const struct vgic_ops **ops, - const struct vgic_params **params); +int vgic_v3_dt_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params); #else -static inline int vgic_v3_probe(struct device_node *vgic_node, - const struct vgic_ops **ops, - const struct vgic_params **params) +static inline int vgic_v3_dt_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params) { return -ENODEV; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 807cbc4..ecb9e25 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); @@ -660,4 +662,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 45e2d8c..2b0f246 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -39,6 +39,8 @@ #define GIC_DIST_SGI_PENDING_CLEAR 0xf10 #define GIC_DIST_SGI_PENDING_SET 0xf20 +#define GIC_DIST_SOFTINT_NSATT 0x8000 + #define GICH_HCR 0x0 #define GICH_VTR 0x4 #define GICH_VMCR 0x8 diff --git a/include/linux/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 6c4363b..a9ee42d 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/of_address.h b/include/linux/of_address.h index fb7b722..7ebb877 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -23,17 +23,6 @@ struct of_pci_range { #define for_each_of_pci_range(parser, range) \ for (; of_pci_range_parser_one(parser, range);) -static inline void of_pci_range_to_resource(struct of_pci_range *range, - struct device_node *np, - struct resource *res) -{ - res->flags = range->flags; - res->start = range->cpu_addr; - res->end = range->cpu_addr + range->size - 1; - res->parent = res->child = res->sibling = NULL; - res->name = np->full_name; -} - /* Translate a DMA address from device space to CPU space */ extern u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr); @@ -55,7 +44,9 @@ extern void __iomem *of_iomap(struct device_node *device, int index); extern const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags); +extern int pci_register_io_range(phys_addr_t addr, resource_size_t size); extern unsigned long pci_address_to_pio(phys_addr_t addr); +extern phys_addr_t pci_pio_to_address(unsigned long pio); extern int of_pci_range_parser_init(struct of_pci_range_parser *parser, struct device_node *node); @@ -138,6 +129,9 @@ extern const __be32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, unsigned int *flags); extern int of_pci_address_to_resource(struct device_node *dev, int bar, struct resource *r); +extern int of_pci_range_to_resource(struct of_pci_range *range, + struct device_node *np, + struct resource *res); #else /* CONFIG_OF_ADDRESS && CONFIG_PCI */ static inline int of_pci_address_to_resource(struct device_node *dev, int bar, struct resource *r) @@ -153,4 +147,3 @@ static inline const __be32 *of_get_pci_address(struct device_node *dev, #endif /* CONFIG_OF_ADDRESS && CONFIG_PCI */ #endif /* __OF_ADDRESS_H */ - diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index dde3a4a..1fd207e 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -15,6 +15,7 @@ struct device_node *of_pci_find_child_device(struct device_node *parent, int of_pci_get_devfn(struct device_node *np); int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin); int of_pci_parse_bus_range(struct device_node *node, struct resource *res); +int of_get_pci_domain_nr(struct device_node *node); #else static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq) { @@ -43,6 +44,18 @@ of_pci_parse_bus_range(struct device_node *node, struct resource *res) { return -EINVAL; } + +static inline int +of_get_pci_domain_nr(struct device_node *node) +{ + return -1; +} +#endif + +#if defined(CONFIG_OF_ADDRESS) +int of_pci_get_host_bridge_resources(struct device_node *dev, + unsigned char busno, unsigned char bus_max, + struct list_head *resources, resource_size_t *io_base); #endif #if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI) diff --git a/include/linux/pci.h b/include/linux/pci.h index 96453f9..6d540b9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -457,6 +457,9 @@ struct pci_bus { unsigned char primary; /* number of primary bridge */ unsigned char max_bus_speed; /* enum pci_bus_speed */ unsigned char cur_bus_speed; /* enum pci_bus_speed */ +#ifdef CONFIG_PCI_DOMAINS_GENERIC + int domain_nr; +#endif char name[48]; @@ -559,15 +562,6 @@ struct pci_ops { int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); }; -/* - * ACPI needs to be able to access PCI config space before we've done a - * PCI bus scan and created pci_bus structures. - */ -int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, - int reg, int len, u32 *val); -int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, - int reg, int len, u32 val); - struct pci_bus_region { dma_addr_t start; dma_addr_t end; @@ -1103,6 +1097,9 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus, resource_size_t), void *alignf_data); + +int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr); + static inline dma_addr_t pci_bus_address(struct pci_dev *pdev, int bar) { struct pci_bus_region region; @@ -1288,17 +1285,47 @@ void pci_cfg_access_unlock(struct pci_dev *dev); */ #ifdef CONFIG_PCI_DOMAINS extern int pci_domains_supported; +int pci_get_new_domain_nr(void); #else enum { pci_domains_supported = 0 }; static inline int pci_domain_nr(struct pci_bus *bus) { return 0; } static inline int pci_proc_domain(struct pci_bus *bus) { return 0; } +static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } #endif /* CONFIG_PCI_DOMAINS */ +/* + * Generic implementation for PCI domain support. If your + * architecture does not need custom management of PCI + * domains then this implementation will be used + */ +#ifdef CONFIG_PCI_DOMAINS_GENERIC +static inline int pci_domain_nr(struct pci_bus *bus) +{ + return bus->domain_nr; +} +void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent); +#else +static inline void pci_bus_assign_domain_nr(struct pci_bus *bus, + struct device *parent) +{ +} +#endif + /* some architectures require additional setup to direct VGA traffic */ typedef int (*arch_set_vga_state_t)(struct pci_dev *pdev, bool decode, unsigned int command_bits, u32 flags); void pci_register_set_vga_state(arch_set_vga_state_t func); +/* + * ACPI needs to be able to access PCI config space before we've done a + * PCI bus scan and created pci_bus structures. + */ +int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, + int reg, int len, u32 *val); +int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, + int reg, int len, u32 val); +void pcibios_penalize_isa_irq(int irq, int active); + #else /* CONFIG_PCI is not enabled */ /* @@ -1400,8 +1427,26 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus, unsigned int devfn) { return NULL; } +static inline struct pci_bus *pci_find_bus(int domain, int busnr) +{ return NULL; } + +static inline int pci_bus_write_config_byte(struct pci_bus *bus, + unsigned int devfn, int where, u8 val) +{ return -ENOSYS; } + +static inline int raw_pci_read(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *val) +{ return -ENOSYS; } + +static inline int raw_pci_write(unsigned int domain, unsigned int bus, + unsigned int devfn, int reg, int len, u32 val) +{ return -ENOSYS; } + +static inline void pcibios_penalize_isa_irq(int irq, int active) { } + static inline int pci_domain_nr(struct pci_bus *bus) { return 0; } static inline struct pci_dev *pci_dev_get(struct pci_dev *dev) { return NULL; } +static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } #define dev_is_pci(d) (false) #define dev_is_pf(d) (false) @@ -1613,7 +1658,6 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state); int pcibios_add_device(struct pci_dev *dev); void pcibios_release_device(struct pci_dev *dev); -void pcibios_penalize_isa_irq(int irq, int active); #ifdef CONFIG_HIBERNATE_CALLBACKS extern struct dev_pm_ops pcibios_pm_ops; diff --git a/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/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h index e9441b9..1d3f39c 100644 --- a/tools/perf/arch/arm64/include/perf_regs.h +++ b/tools/perf/arch/arm64/include/perf_regs.h @@ -6,6 +6,8 @@ #include #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1) +#define PERF_REGS_MAX PERF_REG_ARM64_MAX + #define PERF_REG_IP PERF_REG_ARM64_PC #define PERF_REG_SP PERF_REG_ARM64_SP diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 22fa819..9cd5dbd 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -21,9 +21,11 @@ #include #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 416baed..53bdd33 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 @@ -177,7 +179,7 @@ static const struct vgic_ops vgic_v2_ops = { static struct vgic_params vgic_v2_params; /** - * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT + * vgic_v2_dt_probe - probe for a GICv2 compatible interrupt controller in DT * @node: pointer to the DT node * @ops: address of a pointer to the GICv2 operations * @params: address of a pointer to HW-specific parameters @@ -186,7 +188,7 @@ static struct vgic_params vgic_v2_params; * in *ops and the HW parameters in *params. Returns an error code * otherwise. */ -int vgic_v2_probe(struct device_node *vgic_node, +int vgic_v2_dt_probe(struct device_node *vgic_node, const struct vgic_ops **ops, const struct vgic_params **params) { @@ -263,3 +265,72 @@ out: of_node_put(vgic_node); return ret; } + +struct acpi_madt_generic_interrupt *vgic_acpi; +static void gic_get_acpi_header(struct acpi_subtable_header *header) +{ + vgic_acpi = (struct acpi_madt_generic_interrupt *)header; +} + +int vgic_v2_acpi_probe(const struct vgic_ops **ops, + const struct vgic_params **params) +{ + struct vgic_params *vgic = &vgic_v2_params; + int irq_mode, ret; + + /* MADT table */ + ret = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, + (acpi_tbl_entry_handler)gic_get_acpi_header, 0); + if (!ret) { + pr_err("Failed to get MADT VGIC CPU entry\n"); + ret = -ENODEV; + goto out; + } + + /* IRQ trigger mode */ + irq_mode = (vgic_acpi->flags & ACPI_MADT_VGIC_IRQ_MODE) ? + ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE; + /* According to GIC-400 manual, all PPIs are active-LOW, level + * sensative. We register IRQ as active-low. + */ + vgic->maint_irq = acpi_register_gsi(NULL, vgic_acpi->vgic_interrupt, + irq_mode, ACPI_ACTIVE_LOW); + if (!vgic->maint_irq) { + pr_err("Cannot register VGIC ACPI maintenance irq\n"); + ret = -ENXIO; + goto out; + } + + /* GICH resource */ + vgic->vctrl_base = ioremap(vgic_acpi->gich_base_address, SZ_8K); + if (!vgic->vctrl_base) { + pr_err("cannot ioremap GICH memory\n"); + ret = -ENOMEM; + goto out; + } + + vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR); + vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1; + + ret = create_hyp_io_mappings(vgic->vctrl_base, + vgic->vctrl_base + SZ_8K, + vgic_acpi->gich_base_address); + if (ret) { + kvm_err("Cannot map GICH into hyp\n"); + goto out; + } + + vgic->vcpu_base = vgic_acpi->gicv_base_address; + + kvm_info("GICH base=0x%llx, GICV base=0x%llx, IRQ=%d\n", + (unsigned long long)vgic_acpi->gich_base_address, + (unsigned long long)vgic_acpi->gicv_base_address, + vgic->maint_irq); + + vgic->type = VGIC_V2; + *ops = &vgic_v2_ops; + *params = vgic; + +out: + return ret; +} diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c index 1c2c8ee..8b56920 100644 --- a/virt/kvm/arm/vgic-v3.c +++ b/virt/kvm/arm/vgic-v3.c @@ -173,7 +173,7 @@ static const struct vgic_ops vgic_v3_ops = { static struct vgic_params vgic_v3_params; /** - * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT + * vgic_v3_dt_probe - probe for a GICv3 compatible interrupt controller in DT * @node: pointer to the DT node * @ops: address of a pointer to the GICv3 operations * @params: address of a pointer to HW-specific parameters @@ -182,9 +182,9 @@ static struct vgic_params vgic_v3_params; * in *ops and the HW parameters in *params. Returns an error code * otherwise. */ -int vgic_v3_probe(struct device_node *vgic_node, - const struct vgic_ops **ops, - const struct vgic_params **params) +int vgic_v3_dt_probe(struct device_node *vgic_node, + const struct vgic_ops **ops, + const struct vgic_params **params) { int ret = 0; u32 gicv_idx; diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c index 73eba79..ca98a3b 100644 --- a/virt/kvm/arm/vgic.c +++ b/virt/kvm/arm/vgic.c @@ -25,9 +25,11 @@ #include #include #include +#include #include +#include #include #include #include @@ -1549,31 +1551,39 @@ static struct notifier_block vgic_cpu_nb = { }; static const struct of_device_id vgic_ids[] = { - { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, }, - { .compatible = "arm,gic-v3", .data = vgic_v3_probe, }, + { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_dt_probe, }, + { .compatible = "arm,gic-v3", .data = vgic_v3_dt_probe, }, {}, }; +extern int acadia_kvm_acpi; + int kvm_vgic_hyp_init(void) { const struct of_device_id *matched_id; int (*vgic_probe)(struct device_node *,const struct vgic_ops **, const struct vgic_params **); struct device_node *vgic_node; - int ret; + int ret = -ENODEV; - vgic_node = of_find_matching_node_and_match(NULL, - vgic_ids, &matched_id); - if (!vgic_node) { - kvm_err("error: no compatible GIC node found\n"); - return -ENODEV; + /* probe VGIC */ + if (vgic_node = of_find_matching_node_and_match(NULL, + vgic_ids, &matched_id)) { + /* probe VGIC in DT */ + vgic_probe = matched_id->data; + ret = vgic_probe(vgic_node, &vgic_ops, &vgic); + } + else if (!acpi_disabled && acadia_kvm_acpi) { + /* probe VGIC in ACPI */ + ret = vgic_v2_acpi_probe(&vgic_ops, &vgic); } - vgic_probe = matched_id->data; - ret = vgic_probe(vgic_node, &vgic_ops, &vgic); - if (ret) + if (ret) { + kvm_err("error: no compatible GIC info found\n"); return ret; + } + /* configuration */ ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler, "vgic", kvm_get_running_vcpus()); if (ret) {