force serial consoles to be the primary console device instead of defaulting to tty0. No changes to drivers outside of ARM-land. also update kernel-arm64.patch.
11272 lines
323 KiB
Diff
11272 lines
323 KiB
Diff
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", <VALUE1>},
|
|
+ Package () {"name2", <VALUE2>}
|
|
+ }
|
|
+ })
|
|
+
|
|
+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 <http://www.uefi.org/acpi/specs>.
|
|
+
|
|
+If the machine does not meet the requirements of the SBSA, or cannot be
|
|
+described in the required ACPI specifications then it is likely that Device Tree
|
|
+(DT) is more suitable for the hardware.
|
|
+
|
|
+Relationship with Device Tree
|
|
+-----------------------------
|
|
+
|
|
+ACPI support in drivers and subsystems for ARMv8 should never be mutually
|
|
+exclusive with DT support at compile time.
|
|
+
|
|
+At boot time the kernel will only use one description method depending on
|
|
+parameters passed from the bootloader (including kernel bootargs).
|
|
+
|
|
+Regardless of whether DT or ACPI is used, the kernel must always be capable
|
|
+of booting with either scheme (in kernels with both schemes enabled at compile
|
|
+time).
|
|
+
|
|
+When booting using ACPI tables the /chosen node in DT will still be parsed
|
|
+to extract the kernel command line and initrd path. No other section of
|
|
+the DT will be used.
|
|
+
|
|
+Booting using ACPI tables
|
|
+-------------------------
|
|
+
|
|
+Currently, the only defined method to pass ACPI tables to the kernel on ARMv8
|
|
+is via the UEFI system configuration table.
|
|
+
|
|
+The UEFI implementation MUST set the ACPI_20_TABLE_GUID to point to the
|
|
+RSDP table (the table with the ACPI signature "RSD PTR ").
|
|
+
|
|
+The pointer to the RSDP table will be retrieved from EFI by the ACPI core.
|
|
+
|
|
+Processing of ACPI tables may be disabled by passing acpi=off on the kernel
|
|
+command line.
|
|
+
|
|
+DO use an XSDT; RSDTs are deprecated and should not be used on arm64. They
|
|
+only allow for 32-bit addresses.
|
|
+
|
|
+DO NOT use the 32-bit address fields in the FADT; they are deprecated. The
|
|
+64-bit alternatives MUST be used.
|
|
+
|
|
+The minimum set of tables MUST include RSDP, XSDT, FACS, FADT, DSDT, MADT
|
|
+and GTDT. If PCI is used the MCFG table MUST also be present.
|
|
+
|
|
+ACPI Detection
|
|
+--------------
|
|
+
|
|
+Drivers should determine their probe() type by checking for ACPI_HANDLE,
|
|
+or .of_node, or other information in the device structure. This is
|
|
+detailed further in the "Driver Recommendations" section.
|
|
+
|
|
+In non-driver code If the presence of ACPI needs to be detected at runtime,
|
|
+then check the value of acpi_disabled. If CONFIG_ACPI is not set,
|
|
+acpi_disabled will always be 1.
|
|
+
|
|
+Device Enumeration
|
|
+------------------
|
|
+
|
|
+Device descriptions in ACPI should use standard recognized ACPI interfaces.
|
|
+These are far simpler than the information provided via Device Tree. Drivers
|
|
+should take into account this simplicity and work with sensible defaults.
|
|
+
|
|
+On no account should a Device Tree attempt to be replicated in ASL using such
|
|
+constructs as Name(KEY0, "Value1") type constructs. Additional driver specific
|
|
+data should be represented with the appropriate _DSD (ACPI Section 6.2.5)
|
|
+structure. _DSM (ACPI Section 9.14.1) should only be used if _DSD cannot
|
|
+represent the data required.
|
|
+
|
|
+This data should be rare and not OS specific. For x86 ACPI has taken to
|
|
+identifying itself as Windows because it was found that only one path was
|
|
+routinely tested. For ARMv8 it would be preferable to have only one well
|
|
+tested path.
|
|
+
|
|
+_DSD covers more than the generic server case and care should be taken not to
|
|
+replicate highly specific embedded behaviour from DT into generic servers.
|
|
+
|
|
+Common _DSD bindings should be submitted to ASWG to be included in the
|
|
+document :-
|
|
+
|
|
+http://www.uefi.org/sites/default/files/resources/_DSD-implementation-guide-toplevel.htm
|
|
+
|
|
+If these bindings are mirrored from DT care should be taken to ensure they are
|
|
+reviewed as DT bindings before submission to limit divergance in bindings.
|
|
+
|
|
+Programmable Power Control Resources
|
|
+------------------------------------
|
|
+
|
|
+Programmable power control resources include such resources as voltage/current
|
|
+providers (regulators) and clock sources.
|
|
+
|
|
+For power control of these resources they should be represented with Power
|
|
+Resource Objects (ACPI Section 7.1). The ACPI core will then handle correctly
|
|
+enabling/disabling of resources as they are needed.
|
|
+
|
|
+The ACPI 5.1 specification does not contain any standard binding for these
|
|
+objects to enable programmable levels or rates so this should be avoided if
|
|
+possible and the resources set to appropriate levels by the firmware. If this is
|
|
+not possible then any manipulation should be abstracted in ASL.
|
|
+
|
|
+Each device in ACPI has D-states and these can be controlled through
|
|
+the optional methods _PS0..._PS3 where _PS0 is full on and _PS3 is full off.
|
|
+
|
|
+If either _PS0 or _PS3 is implemented, then the other method must also be
|
|
+implemented.
|
|
+
|
|
+If a device requires usage or setup of a power resource when on, the ASL
|
|
+should organize that it is allocated/enabled using the _PS0 method.
|
|
+
|
|
+Resources allocated/enabled in the _PS0 method should be disabled/de-allocated
|
|
+in the _PS3 method.
|
|
+
|
|
+Such code in _PS? methods will of course be very platform specific but
|
|
+should allow the driver to operate the device without special non-standard
|
|
+values being read from ASL. Further, abstracting the use of these resources
|
|
+allows hardware revisions without requiring updates to the kernel.
|
|
+
|
|
+Clocks
|
|
+------
|
|
+
|
|
+Like clocks that are part of the power resources there is no standard way
|
|
+to represent a clock tree in ACPI 5.1 in a similar manner to how it is
|
|
+described in DT.
|
|
+
|
|
+Devices affected by this include things like UARTs, SoC driven LCD displays,
|
|
+etc.
|
|
+
|
|
+The firmware (for example, UEFI) should initialize these clocks to fixed working
|
|
+values before the kernel is executed.
|
|
+
|
|
+Driver Recommendations
|
|
+----------------------
|
|
+
|
|
+DO NOT remove any FDT handling when adding ACPI support for a driver. Different
|
|
+systems may use the same device.
|
|
+
|
|
+DO try and keep complex sections of ACPI and DT functionality separate. This
|
|
+may mean a patch to break out some complex DT to another function before
|
|
+the patch to add ACPI. This may happen in other functions but is most likely
|
|
+in probe function. This gives a clearer flow of data for reviewing driver
|
|
+source.
|
|
+
|
|
+probe() :-
|
|
+
|
|
+static int device_probe_dt(struct platform_device *pdev)
|
|
+{
|
|
+ /* DT specific functionality */
|
|
+ ...
|
|
+}
|
|
+
|
|
+static int device_probe_acpi(struct platform_device *pdev)
|
|
+{
|
|
+ /* ACPI specific functionality */
|
|
+ ...
|
|
+}
|
|
+
|
|
+static int device_probe(stuct platform_device *pdev)
|
|
+{
|
|
+ ...
|
|
+ struct device_node node = pdev->dev.of_node;
|
|
+ ...
|
|
+
|
|
+ if (node)
|
|
+ ret = device_probe_dt(pdev);
|
|
+ else if (ACPI_HANDLE(&pdev->dev))
|
|
+ ret = device_probe_acpi(pdev);
|
|
+ else
|
|
+ /* other initialization */
|
|
+ ...
|
|
+ /* Continue with any generic probe operations */
|
|
+ ...
|
|
+}
|
|
+
|
|
+DO keep the MODULE_DEVICE_TABLE entries together in the driver to make it clear
|
|
+the different names the driver is probed for, both from DT and from ACPI.
|
|
+
|
|
+module device tables :-
|
|
+
|
|
+static struct of_device_id virtio_mmio_match[] = {
|
|
+ { .compatible = "virtio,mmio", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, virtio_mmio_match);
|
|
+
|
|
+static const struct acpi_device_id virtio_mmio_acpi_match[] = {
|
|
+ { "LNRO0005", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match);
|
|
+
|
|
+ASWG
|
|
+----
|
|
+
|
|
+The following areas are not yet well defined for ARM in the current ACPI
|
|
+specification and are expected to be worked through in the UEFI ACPI
|
|
+Specification Working Group (ASWG) <http://www.uefi.org/workinggroups>.
|
|
+Participation in this group is open to all UEFI members.
|
|
+
|
|
+ - ACPI based CPU topology
|
|
+ - ACPI based Power management
|
|
+ - CPU idle control based on PSCI
|
|
+ - CPU performance control (CPPC)
|
|
+ - ACPI based SMMU
|
|
+ - ITS support for GIC in MADT
|
|
+
|
|
+No code shall be accepted into the kernel unless it complies with the released
|
|
+standards from UEFI ASWG. If there are features missing from ACPI to make it
|
|
+function on a platform, ECRs should be submitted to ASWG and go through the
|
|
+approval process.
|
|
diff --git a/Documentation/devicetree/bindings/pci/xgene-pci.txt b/Documentation/devicetree/bindings/pci/xgene-pci.txt
|
|
new file mode 100644
|
|
index 0000000..1070b06
|
|
--- /dev/null
|
|
+++ b/Documentation/devicetree/bindings/pci/xgene-pci.txt
|
|
@@ -0,0 +1,57 @@
|
|
+* AppliedMicro X-Gene PCIe interface
|
|
+
|
|
+Required properties:
|
|
+- device_type: set to "pci"
|
|
+- compatible: should contain "apm,xgene-pcie" to identify the core.
|
|
+- reg: A list of physical base address and length for each set of controller
|
|
+ registers. Must contain an entry for each entry in the reg-names
|
|
+ property.
|
|
+- reg-names: Must include the following entries:
|
|
+ "csr": controller configuration registers.
|
|
+ "cfg": pcie configuration space registers.
|
|
+- #address-cells: set to <3>
|
|
+- #size-cells: set to <2>
|
|
+- ranges: ranges for the outbound memory, I/O regions.
|
|
+- dma-ranges: ranges for the inbound memory regions.
|
|
+- #interrupt-cells: set to <1>
|
|
+- interrupt-map-mask and interrupt-map: standard PCI properties
|
|
+ to define the mapping of the PCIe interface to interrupt
|
|
+ numbers.
|
|
+- clocks: from common clock binding: handle to pci clock.
|
|
+
|
|
+Optional properties:
|
|
+- status: Either "ok" or "disabled".
|
|
+- dma-coherent: Present if dma operations are coherent
|
|
+
|
|
+Example:
|
|
+
|
|
+SoC specific DT Entry:
|
|
+
|
|
+ pcie0: pcie@1f2b0000 {
|
|
+ status = "disabled";
|
|
+ device_type = "pci";
|
|
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
|
+ #interrupt-cells = <1>;
|
|
+ #size-cells = <2>;
|
|
+ #address-cells = <3>;
|
|
+ reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */
|
|
+ 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
|
+ reg-names = "csr", "cfg";
|
|
+ ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */
|
|
+ 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
|
|
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
|
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
|
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
|
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
|
|
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
|
|
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
|
|
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
|
|
+ dma-coherent;
|
|
+ clocks = <&pcie0clk 0>;
|
|
+ };
|
|
+
|
|
+
|
|
+Board specific DT Entry:
|
|
+ &pcie0 {
|
|
+ status = "ok";
|
|
+ };
|
|
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
|
|
index 10d51c2..9464c6d 100644
|
|
--- a/Documentation/kernel-parameters.txt
|
|
+++ b/Documentation/kernel-parameters.txt
|
|
@@ -165,7 +165,7 @@ multipliers 'Kilo', 'Mega', and 'Giga', equalling 2^10, 2^20, and 2^30
|
|
bytes respectively. Such letter suffixes can also be entirely omitted.
|
|
|
|
|
|
- acpi= [HW,ACPI,X86]
|
|
+ acpi= [HW,ACPI,X86,ARM]
|
|
Advanced Configuration and Power Interface
|
|
Format: { force | off | strict | noirq | rsdt }
|
|
force -- enable ACPI if default was off
|
|
@@ -175,6 +175,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|
strictly ACPI specification compliant.
|
|
rsdt -- prefer RSDT over (default) XSDT
|
|
copy_dsdt -- copy DSDT to memory
|
|
+ For ARM64, ONLY "acpi=off" is available.
|
|
|
|
See also Documentation/power/runtime_pm.txt, pci=noacpi
|
|
|
|
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
index 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 <tinamdar@apm.com>
|
|
+L: linux-pci@vger.kernel.org
|
|
+L: linux-arm-kernel@lists.infradead.org
|
|
+S: Maintained
|
|
+F: Documentation/devicetree/bindings/pci/xgene-pci.txt
|
|
+F: drivers/pci/host/pci-xgene.c
|
|
+
|
|
PCMCIA SUBSYSTEM
|
|
P: Linux PCMCIA Team
|
|
L: linux-pcmcia@lists.infradead.org
|
|
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
|
|
index 3d23418..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 <asm/memory.h>
|
|
#include <asm/page.h>
|
|
+#include <asm/kvm_arm.h>
|
|
|
|
/*
|
|
* We directly use the kernel VA for the HYP, as we can directly share
|
|
@@ -178,6 +179,18 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
|
|
|
|
void stage2_flush_vm(struct kvm *kvm);
|
|
|
|
+static inline int kvm_get_phys_addr_shift(void)
|
|
+{
|
|
+ return KVM_PHYS_SHIFT;
|
|
+}
|
|
+
|
|
+
|
|
+static inline u32 get_vttbr_baddr_mask(void)
|
|
+{
|
|
+ return VTTBR_BADDR_MASK;
|
|
+}
|
|
+
|
|
+
|
|
#endif /* !__ASSEMBLY__ */
|
|
|
|
#endif /* __ARM_KVM_MMU_H__ */
|
|
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
|
|
index a99e0cd..d0fca8f 100644
|
|
--- a/arch/arm/kvm/arm.c
|
|
+++ b/arch/arm/kvm/arm.c
|
|
@@ -37,6 +37,7 @@
|
|
#include <asm/mman.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/cacheflush.h>
|
|
+#include <asm/cputype.h>
|
|
#include <asm/virt.h>
|
|
#include <asm/kvm_arm.h>
|
|
#include <asm/kvm_asm.h>
|
|
@@ -61,6 +62,12 @@ static atomic64_t kvm_vmid_gen = ATOMIC64_INIT(1);
|
|
static u8 kvm_next_vmid;
|
|
static DEFINE_SPINLOCK(kvm_vmid_lock);
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+static u64 vttbr_baddr_mask;
|
|
+#else
|
|
+static u32 vttbr_baddr_mask;
|
|
+#endif
|
|
+
|
|
static bool vgic_present;
|
|
|
|
static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu)
|
|
@@ -429,8 +436,14 @@ static void update_vttbr(struct kvm *kvm)
|
|
/* update vttbr to be used with the new vmid */
|
|
pgd_phys = virt_to_phys(kvm->arch.pgd);
|
|
vmid = ((u64)(kvm->arch.vmid) << VTTBR_VMID_SHIFT) & VTTBR_VMID_MASK;
|
|
- kvm->arch.vttbr = pgd_phys & VTTBR_BADDR_MASK;
|
|
- kvm->arch.vttbr |= vmid;
|
|
+
|
|
+ /*
|
|
+ * If the VTTBR isn't aligned there is something wrong with the system
|
|
+ * or kernel.
|
|
+ */
|
|
+ BUG_ON(pgd_phys & ~vttbr_baddr_mask);
|
|
+
|
|
+ kvm->arch.vttbr = pgd_phys | vmid;
|
|
|
|
spin_unlock(&kvm_vmid_lock);
|
|
}
|
|
@@ -1015,6 +1028,12 @@ int kvm_arch_init(void *opaque)
|
|
}
|
|
}
|
|
|
|
+ vttbr_baddr_mask = get_vttbr_baddr_mask();
|
|
+ if (vttbr_baddr_mask == ~0) {
|
|
+ kvm_err("Cannot set vttbr_baddr_mask\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
cpu_notifier_register_begin();
|
|
|
|
err = init_hyp_mode();
|
|
diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c
|
|
index 05e1f73..c186a17 100644
|
|
--- a/arch/arm/mach-integrator/pci_v3.c
|
|
+++ b/arch/arm/mach-integrator/pci_v3.c
|
|
@@ -660,6 +660,7 @@ static void __init pci_v3_preinit(void)
|
|
{
|
|
unsigned long flags;
|
|
unsigned int temp;
|
|
+ phys_addr_t io_address = pci_pio_to_address(io_mem.start);
|
|
|
|
pcibios_min_mem = 0x00100000;
|
|
|
|
@@ -701,7 +702,7 @@ static void __init pci_v3_preinit(void)
|
|
/*
|
|
* Setup window 2 - PCI IO
|
|
*/
|
|
- v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_mem.start) |
|
|
+ v3_writel(V3_LB_BASE2, v3_addr_to_lb_base2(io_address) |
|
|
V3_LB_BASE_ENABLE);
|
|
v3_writew(V3_LB_MAP2, v3_addr_to_lb_map2(0));
|
|
|
|
@@ -742,6 +743,7 @@ static void __init pci_v3_preinit(void)
|
|
static void __init pci_v3_postinit(void)
|
|
{
|
|
unsigned int pci_cmd;
|
|
+ phys_addr_t io_address = pci_pio_to_address(io_mem.start);
|
|
|
|
pci_cmd = PCI_COMMAND_MEMORY |
|
|
PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
|
|
@@ -758,7 +760,7 @@ static void __init pci_v3_postinit(void)
|
|
"interrupt: %d\n", ret);
|
|
#endif
|
|
|
|
- register_isa_ports(non_mem.start, io_mem.start, 0);
|
|
+ register_isa_ports(non_mem.start, io_address, 0);
|
|
}
|
|
|
|
/*
|
|
@@ -867,33 +869,32 @@ static int __init pci_v3_probe(struct platform_device *pdev)
|
|
|
|
for_each_of_pci_range(&parser, &range) {
|
|
if (!range.flags) {
|
|
- of_pci_range_to_resource(&range, np, &conf_mem);
|
|
+ ret = of_pci_range_to_resource(&range, np, &conf_mem);
|
|
conf_mem.name = "PCIv3 config";
|
|
}
|
|
if (range.flags & IORESOURCE_IO) {
|
|
- of_pci_range_to_resource(&range, np, &io_mem);
|
|
+ ret = of_pci_range_to_resource(&range, np, &io_mem);
|
|
io_mem.name = "PCIv3 I/O";
|
|
}
|
|
if ((range.flags & IORESOURCE_MEM) &&
|
|
!(range.flags & IORESOURCE_PREFETCH)) {
|
|
non_mem_pci = range.pci_addr;
|
|
non_mem_pci_sz = range.size;
|
|
- of_pci_range_to_resource(&range, np, &non_mem);
|
|
+ ret = of_pci_range_to_resource(&range, np, &non_mem);
|
|
non_mem.name = "PCIv3 non-prefetched mem";
|
|
}
|
|
if ((range.flags & IORESOURCE_MEM) &&
|
|
(range.flags & IORESOURCE_PREFETCH)) {
|
|
pre_mem_pci = range.pci_addr;
|
|
pre_mem_pci_sz = range.size;
|
|
- of_pci_range_to_resource(&range, np, &pre_mem);
|
|
+ ret = of_pci_range_to_resource(&range, np, &pre_mem);
|
|
pre_mem.name = "PCIv3 prefetched mem";
|
|
}
|
|
- }
|
|
|
|
- if (!conf_mem.start || !io_mem.start ||
|
|
- !non_mem.start || !pre_mem.start) {
|
|
- dev_err(&pdev->dev, "missing ranges in device node\n");
|
|
- return -EINVAL;
|
|
+ if (ret < 0) {
|
|
+ dev_err(&pdev->dev, "missing ranges in device node\n");
|
|
+ return ret;
|
|
+ }
|
|
}
|
|
|
|
pci_v3.map_irq = of_irq_parse_and_map_pci;
|
|
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
|
|
index fd4e81a..e57b91a 100644
|
|
--- a/arch/arm64/Kconfig
|
|
+++ b/arch/arm64/Kconfig
|
|
@@ -1,5 +1,6 @@
|
|
config ARM64
|
|
def_bool y
|
|
+ select ACPI_REDUCED_HARDWARE_ONLY if ACPI
|
|
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
|
|
select ARCH_HAS_SG_CHAIN
|
|
select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
|
|
@@ -81,7 +82,7 @@ config MMU
|
|
def_bool y
|
|
|
|
config NO_IOPORT_MAP
|
|
- def_bool y
|
|
+ def_bool y if !PCI
|
|
|
|
config STACKTRACE_SUPPORT
|
|
def_bool y
|
|
@@ -156,6 +157,26 @@ menu "Bus support"
|
|
config ARM_AMBA
|
|
bool
|
|
|
|
+config PCI
|
|
+ bool "PCI support"
|
|
+ help
|
|
+ This feature enables support for PCI bus system. If you say Y
|
|
+ here, the kernel will include drivers and infrastructure code
|
|
+ to support PCI bus devices.
|
|
+
|
|
+config PCI_DOMAINS
|
|
+ def_bool PCI
|
|
+
|
|
+config PCI_DOMAINS_GENERIC
|
|
+ def_bool PCI
|
|
+
|
|
+config PCI_SYSCALL
|
|
+ def_bool PCI
|
|
+
|
|
+source "drivers/pci/Kconfig"
|
|
+source "drivers/pci/pcie/Kconfig"
|
|
+source "drivers/pci/hotplug/Kconfig"
|
|
+
|
|
endmenu
|
|
|
|
menu "Kernel Features"
|
|
@@ -235,6 +256,9 @@ config SMP
|
|
|
|
If you don't know what to do here, say N.
|
|
|
|
+config ARM_PARKING_PROTOCOL
|
|
+ def_bool y if SMP
|
|
+
|
|
config SCHED_MC
|
|
bool "Multi-core scheduler support"
|
|
depends on SMP
|
|
@@ -421,6 +445,8 @@ source "drivers/Kconfig"
|
|
|
|
source "drivers/firmware/Kconfig"
|
|
|
|
+source "drivers/acpi/Kconfig"
|
|
+
|
|
source "fs/Kconfig"
|
|
|
|
source "arch/arm64/kvm/Kconfig"
|
|
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
|
|
index 2df5e5d..bddd4e3 100644
|
|
--- a/arch/arm64/Makefile
|
|
+++ b/arch/arm64/Makefile
|
|
@@ -50,6 +50,7 @@ core-y += arch/arm64/kernel/ arch/arm64/mm/
|
|
core-$(CONFIG_KVM) += arch/arm64/kvm/
|
|
core-$(CONFIG_XEN) += arch/arm64/xen/
|
|
core-$(CONFIG_CRYPTO) += arch/arm64/crypto/
|
|
+drivers-$(CONFIG_PCI) += arch/arm64/pci/
|
|
libs-y := arch/arm64/lib/ $(libs-y)
|
|
libs-y += $(LIBGCC)
|
|
libs-$(CONFIG_EFI_STUB) += drivers/firmware/efi/libstub/
|
|
diff --git a/arch/arm64/boot/dts/apm-mustang.dts b/arch/arm64/boot/dts/apm-mustang.dts
|
|
index b2f5622..f649000 100644
|
|
--- a/arch/arm64/boot/dts/apm-mustang.dts
|
|
+++ b/arch/arm64/boot/dts/apm-mustang.dts
|
|
@@ -25,6 +25,14 @@
|
|
};
|
|
};
|
|
|
|
+&pcie0clk {
|
|
+ status = "ok";
|
|
+};
|
|
+
|
|
+&pcie0 {
|
|
+ status = "ok";
|
|
+};
|
|
+
|
|
&serial0 {
|
|
status = "ok";
|
|
};
|
|
diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi
|
|
index c0aceef..403197a 100644
|
|
--- a/arch/arm64/boot/dts/apm-storm.dtsi
|
|
+++ b/arch/arm64/boot/dts/apm-storm.dtsi
|
|
@@ -269,6 +269,171 @@
|
|
enable-mask = <0x2>;
|
|
clock-output-names = "rtcclk";
|
|
};
|
|
+
|
|
+ pcie0clk: pcie0clk@1f2bc000 {
|
|
+ status = "disabled";
|
|
+ compatible = "apm,xgene-device-clock";
|
|
+ #clock-cells = <1>;
|
|
+ clocks = <&socplldiv2 0>;
|
|
+ reg = <0x0 0x1f2bc000 0x0 0x1000>;
|
|
+ reg-names = "csr-reg";
|
|
+ clock-output-names = "pcie0clk";
|
|
+ };
|
|
+
|
|
+ pcie1clk: pcie1clk@1f2cc000 {
|
|
+ status = "disabled";
|
|
+ compatible = "apm,xgene-device-clock";
|
|
+ #clock-cells = <1>;
|
|
+ clocks = <&socplldiv2 0>;
|
|
+ reg = <0x0 0x1f2cc000 0x0 0x1000>;
|
|
+ reg-names = "csr-reg";
|
|
+ clock-output-names = "pcie1clk";
|
|
+ };
|
|
+
|
|
+ pcie2clk: pcie2clk@1f2dc000 {
|
|
+ status = "disabled";
|
|
+ compatible = "apm,xgene-device-clock";
|
|
+ #clock-cells = <1>;
|
|
+ clocks = <&socplldiv2 0>;
|
|
+ reg = <0x0 0x1f2dc000 0x0 0x1000>;
|
|
+ reg-names = "csr-reg";
|
|
+ clock-output-names = "pcie2clk";
|
|
+ };
|
|
+
|
|
+ pcie3clk: pcie3clk@1f50c000 {
|
|
+ status = "disabled";
|
|
+ compatible = "apm,xgene-device-clock";
|
|
+ #clock-cells = <1>;
|
|
+ clocks = <&socplldiv2 0>;
|
|
+ reg = <0x0 0x1f50c000 0x0 0x1000>;
|
|
+ reg-names = "csr-reg";
|
|
+ clock-output-names = "pcie3clk";
|
|
+ };
|
|
+
|
|
+ pcie4clk: pcie4clk@1f51c000 {
|
|
+ status = "disabled";
|
|
+ compatible = "apm,xgene-device-clock";
|
|
+ #clock-cells = <1>;
|
|
+ clocks = <&socplldiv2 0>;
|
|
+ reg = <0x0 0x1f51c000 0x0 0x1000>;
|
|
+ reg-names = "csr-reg";
|
|
+ clock-output-names = "pcie4clk";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ pcie0: pcie@1f2b0000 {
|
|
+ status = "disabled";
|
|
+ device_type = "pci";
|
|
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
|
+ #interrupt-cells = <1>;
|
|
+ #size-cells = <2>;
|
|
+ #address-cells = <3>;
|
|
+ reg = < 0x00 0x1f2b0000 0x0 0x00010000 /* Controller registers */
|
|
+ 0xe0 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
|
+ reg-names = "csr", "cfg";
|
|
+ ranges = <0x01000000 0x00 0x00000000 0xe0 0x10000000 0x00 0x00010000 /* io */
|
|
+ 0x02000000 0x00 0x80000000 0xe1 0x80000000 0x00 0x80000000>; /* mem */
|
|
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
|
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
|
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
|
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc2 0x1
|
|
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xc3 0x1
|
|
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xc4 0x1
|
|
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xc5 0x1>;
|
|
+ dma-coherent;
|
|
+ clocks = <&pcie0clk 0>;
|
|
+ };
|
|
+
|
|
+ pcie1: pcie@1f2c0000 {
|
|
+ status = "disabled";
|
|
+ device_type = "pci";
|
|
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
|
+ #interrupt-cells = <1>;
|
|
+ #size-cells = <2>;
|
|
+ #address-cells = <3>;
|
|
+ reg = < 0x00 0x1f2c0000 0x0 0x00010000 /* Controller registers */
|
|
+ 0xd0 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
|
+ reg-names = "csr", "cfg";
|
|
+ ranges = <0x01000000 0x0 0x00000000 0xd0 0x10000000 0x00 0x00010000 /* io */
|
|
+ 0x02000000 0x0 0x80000000 0xd1 0x80000000 0x00 0x80000000>; /* mem */
|
|
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
|
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
|
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
|
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xc8 0x1
|
|
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xc9 0x1
|
|
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xca 0x1
|
|
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xcb 0x1>;
|
|
+ dma-coherent;
|
|
+ clocks = <&pcie1clk 0>;
|
|
+ };
|
|
+
|
|
+ pcie2: pcie@1f2d0000 {
|
|
+ status = "disabled";
|
|
+ device_type = "pci";
|
|
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
|
+ #interrupt-cells = <1>;
|
|
+ #size-cells = <2>;
|
|
+ #address-cells = <3>;
|
|
+ reg = < 0x00 0x1f2d0000 0x0 0x00010000 /* Controller registers */
|
|
+ 0x90 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
|
+ reg-names = "csr", "cfg";
|
|
+ ranges = <0x01000000 0x0 0x00000000 0x90 0x10000000 0x0 0x00010000 /* io */
|
|
+ 0x02000000 0x0 0x80000000 0x91 0x80000000 0x0 0x80000000>; /* mem */
|
|
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
|
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
|
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
|
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xce 0x1
|
|
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xcf 0x1
|
|
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xd0 0x1
|
|
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xd1 0x1>;
|
|
+ dma-coherent;
|
|
+ clocks = <&pcie2clk 0>;
|
|
+ };
|
|
+
|
|
+ pcie3: pcie@1f500000 {
|
|
+ status = "disabled";
|
|
+ device_type = "pci";
|
|
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
|
+ #interrupt-cells = <1>;
|
|
+ #size-cells = <2>;
|
|
+ #address-cells = <3>;
|
|
+ reg = < 0x00 0x1f500000 0x0 0x00010000 /* Controller registers */
|
|
+ 0xa0 0xd0000000 0x0 0x00040000>; /* PCI config space */
|
|
+ reg-names = "csr", "cfg";
|
|
+ ranges = <0x01000000 0x0 0x00000000 0xa0 0x10000000 0x0 0x00010000 /* io */
|
|
+ 0x02000000 0x0 0x80000000 0xa1 0x80000000 0x0 0x80000000>; /* mem */
|
|
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
|
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
|
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
|
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xd4 0x1
|
|
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xd5 0x1
|
|
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xd6 0x1
|
|
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xd7 0x1>;
|
|
+ dma-coherent;
|
|
+ clocks = <&pcie3clk 0>;
|
|
+ };
|
|
+
|
|
+ pcie4: pcie@1f510000 {
|
|
+ status = "disabled";
|
|
+ device_type = "pci";
|
|
+ compatible = "apm,xgene-storm-pcie", "apm,xgene-pcie";
|
|
+ #interrupt-cells = <1>;
|
|
+ #size-cells = <2>;
|
|
+ #address-cells = <3>;
|
|
+ reg = < 0x00 0x1f510000 0x0 0x00010000 /* Controller registers */
|
|
+ 0xc0 0xd0000000 0x0 0x00200000>; /* PCI config space */
|
|
+ reg-names = "csr", "cfg";
|
|
+ ranges = <0x01000000 0x0 0x00000000 0xc0 0x10000000 0x0 0x00010000 /* io */
|
|
+ 0x02000000 0x0 0x80000000 0xc1 0x80000000 0x0 0x80000000>; /* mem */
|
|
+ dma-ranges = <0x42000000 0x80 0x00000000 0x80 0x00000000 0x00 0x80000000
|
|
+ 0x42000000 0x00 0x00000000 0x00 0x00000000 0x80 0x00000000>;
|
|
+ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
|
+ interrupt-map = <0x0 0x0 0x0 0x1 &gic 0x0 0xda 0x1
|
|
+ 0x0 0x0 0x0 0x2 &gic 0x0 0xdb 0x1
|
|
+ 0x0 0x0 0x0 0x3 &gic 0x0 0xdc 0x1
|
|
+ 0x0 0x0 0x0 0x4 &gic 0x0 0xdd 0x1>;
|
|
+ dma-coherent;
|
|
+ clocks = <&pcie4clk 0>;
|
|
};
|
|
|
|
serial0: serial@1c020000 {
|
|
diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild
|
|
index 0b3fcf8..07cb417 100644
|
|
--- a/arch/arm64/include/asm/Kbuild
|
|
+++ b/arch/arm64/include/asm/Kbuild
|
|
@@ -29,6 +29,7 @@ generic-y += mman.h
|
|
generic-y += msgbuf.h
|
|
generic-y += mutex.h
|
|
generic-y += pci.h
|
|
+generic-y += pci-bridge.h
|
|
generic-y += poll.h
|
|
generic-y += preempt.h
|
|
generic-y += resource.h
|
|
diff --git a/arch/arm64/include/asm/acenv.h b/arch/arm64/include/asm/acenv.h
|
|
new file mode 100644
|
|
index 0000000..b49166f
|
|
--- /dev/null
|
|
+++ b/arch/arm64/include/asm/acenv.h
|
|
@@ -0,0 +1,18 @@
|
|
+/*
|
|
+ * ARM64 specific ACPICA environments and implementation
|
|
+ *
|
|
+ * Copyright (C) 2014, Linaro Ltd.
|
|
+ * Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
+ * Author: Graeme Gregory <graeme.gregory@linaro.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#ifndef _ASM_ACENV_H
|
|
+#define _ASM_ACENV_H
|
|
+
|
|
+/* It is required unconditionally by ACPI core, update it when needed. */
|
|
+
|
|
+#endif /* _ASM_ACENV_H */
|
|
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
|
|
new file mode 100644
|
|
index 0000000..7f6cd91
|
|
--- /dev/null
|
|
+++ b/arch/arm64/include/asm/acpi.h
|
|
@@ -0,0 +1,99 @@
|
|
+/*
|
|
+ * Copyright (C) 2013-2014, Linaro Ltd.
|
|
+ * Author: Al Stone <al.stone@linaro.org>
|
|
+ * Author: Graeme Gregory <graeme.gregory@linaro.org>
|
|
+ * Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation;
|
|
+ */
|
|
+
|
|
+#ifndef _ASM_ACPI_H
|
|
+#define _ASM_ACPI_H
|
|
+
|
|
+#include <asm/smp_plat.h>
|
|
+
|
|
+/* Basic configuration for ACPI */
|
|
+#ifdef CONFIG_ACPI
|
|
+#define acpi_strict 1 /* No out-of-spec workarounds on ARM64 */
|
|
+extern int acpi_disabled;
|
|
+extern int acpi_noirq;
|
|
+extern int acpi_pci_disabled;
|
|
+
|
|
+/* 1 to indicate PSCI 0.2+ is implemented */
|
|
+static inline bool acpi_psci_present(void)
|
|
+{
|
|
+ return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_COMPLIANT;
|
|
+}
|
|
+
|
|
+/* 1 to indicate HVC must be used instead of SMC as the PSCI conduit */
|
|
+static inline bool acpi_psci_use_hvc(void)
|
|
+{
|
|
+ return acpi_gbl_FADT.arm_boot_flags & ACPI_FADT_PSCI_USE_HVC;
|
|
+}
|
|
+
|
|
+static inline void disable_acpi(void)
|
|
+{
|
|
+ acpi_disabled = 1;
|
|
+ acpi_pci_disabled = 1;
|
|
+ acpi_noirq = 1;
|
|
+}
|
|
+
|
|
+/* MPIDR value provided in GICC structure is 64 bits, but
|
|
+ * the acpi processor driver use the 32 bits cpu hardware
|
|
+ * ID (apic_id on intel platform) everywhere, it is pretty
|
|
+ * hard to modify the acpi processor driver to accept the
|
|
+ * 64 bits MPIDR value, at the same time, only 32 bits of
|
|
+ * the MPIDR is used in the 64 bits MPIDR, just pack the
|
|
+ * Affx fields into a single 32 bit identifier to accommodate
|
|
+ * the acpi processor drivers.
|
|
+ */
|
|
+static inline u32 pack_mpidr_into_32_bits(u64 mpidr)
|
|
+{
|
|
+ /*
|
|
+ * Bits [0:7] Aff0;
|
|
+ * Bits [8:15] Aff1;
|
|
+ * Bits [16:23] Aff2;
|
|
+ * Bits [32:39] Aff3;
|
|
+ */
|
|
+ return (u32) ((mpidr & 0xff00000000) >> 8) | mpidr;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * The ACPI processor driver for ACPI core code needs this macro
|
|
+ * to find out this cpu was already mapped (mapping from CPU hardware
|
|
+ * ID to CPU logical ID) or not.
|
|
+ *
|
|
+ * cpu_logical_map(cpu) is the mapping of MPIDR and the logical cpu,
|
|
+ * and MPIDR is the cpu hardware ID we needed to pack.
|
|
+ */
|
|
+#define cpu_physical_id(cpu) pack_mpidr_into_32_bits(cpu_logical_map(cpu))
|
|
+
|
|
+/*
|
|
+ * It's used from ACPI core in kdump to boot UP system with SMP kernel,
|
|
+ * with this check the ACPI core will not override the CPU index
|
|
+ * obtained from GICC with 0 and not print some error message as well.
|
|
+ * Since MADT must provide at least one GICC structure for GIC
|
|
+ * initialization, CPU will be always available in MADT on ARM64.
|
|
+ */
|
|
+static inline bool acpi_has_cpu_in_madt(void)
|
|
+{
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static inline void arch_fix_phys_package_id(int num, u32 slot) { }
|
|
+void __init acpi_smp_init_cpus(void);
|
|
+
|
|
+extern int acpi_get_cpu_parked_address(int cpu, u64 *addr);
|
|
+
|
|
+#else
|
|
+
|
|
+static inline bool acpi_psci_present(void) { return false; }
|
|
+static inline bool acpi_psci_use_hvc(void) { return false; }
|
|
+static inline void acpi_smp_init_cpus(void) { }
|
|
+static inline int acpi_get_cpu_parked_address(int cpu, u64 *addr) { return -EOPNOTSUPP; }
|
|
+
|
|
+#endif /* CONFIG_ACPI */
|
|
+
|
|
+#endif /*_ASM_ACPI_H*/
|
|
diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h
|
|
index d7b4b38..d149580 100644
|
|
--- a/arch/arm64/include/asm/cpu_ops.h
|
|
+++ b/arch/arm64/include/asm/cpu_ops.h
|
|
@@ -61,6 +61,7 @@ struct cpu_operations {
|
|
};
|
|
|
|
extern const struct cpu_operations *cpu_ops[NR_CPUS];
|
|
+const struct cpu_operations *cpu_get_ops(const char *name);
|
|
extern int __init cpu_read_ops(struct device_node *dn, int cpu);
|
|
extern void __init cpu_read_bootcpu_ops(void);
|
|
|
|
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
|
|
index 01d3aab..8186df6 100644
|
|
--- a/arch/arm64/include/asm/elf.h
|
|
+++ b/arch/arm64/include/asm/elf.h
|
|
@@ -114,7 +114,8 @@ typedef struct user_fpsimd_state elf_fpregset_t;
|
|
*/
|
|
#define elf_check_arch(x) ((x)->e_machine == EM_AARCH64)
|
|
|
|
-#define elf_read_implies_exec(ex,stk) (stk != EXSTACK_DISABLE_X)
|
|
+#define elf_read_implies_exec(ex,stk) (test_thread_flag(TIF_32BIT) \
|
|
+ ? (stk == EXSTACK_ENABLE_X) : 0)
|
|
|
|
#define CORE_DUMP_USE_REGSET
|
|
#define ELF_EXEC_PAGESIZE PAGE_SIZE
|
|
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
|
|
index e0ecdcf..f998d90 100644
|
|
--- a/arch/arm64/include/asm/io.h
|
|
+++ b/arch/arm64/include/asm/io.h
|
|
@@ -121,7 +121,8 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)
|
|
/*
|
|
* I/O port access primitives.
|
|
*/
|
|
-#define IO_SPACE_LIMIT 0xffff
|
|
+#define arch_has_dev_port() (1)
|
|
+#define IO_SPACE_LIMIT (SZ_32M - 1)
|
|
#define PCI_IOBASE ((void __iomem *)(MODULES_VADDR - SZ_32M))
|
|
|
|
static inline u8 inb(unsigned long addr)
|
|
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
|
|
index cc83520..ff4a4fa 100644
|
|
--- a/arch/arm64/include/asm/kvm_arm.h
|
|
+++ b/arch/arm64/include/asm/kvm_arm.h
|
|
@@ -95,7 +95,6 @@
|
|
/* TCR_EL2 Registers bits */
|
|
#define TCR_EL2_TBI (1 << 20)
|
|
#define TCR_EL2_PS (7 << 16)
|
|
-#define TCR_EL2_PS_40B (2 << 16)
|
|
#define TCR_EL2_TG0 (1 << 14)
|
|
#define TCR_EL2_SH0 (3 << 12)
|
|
#define TCR_EL2_ORGN0 (3 << 10)
|
|
@@ -104,8 +103,6 @@
|
|
#define TCR_EL2_MASK (TCR_EL2_TG0 | TCR_EL2_SH0 | \
|
|
TCR_EL2_ORGN0 | TCR_EL2_IRGN0 | TCR_EL2_T0SZ)
|
|
|
|
-#define TCR_EL2_FLAGS (TCR_EL2_PS_40B)
|
|
-
|
|
/* VTCR_EL2 Registers bits */
|
|
#define VTCR_EL2_PS_MASK (7 << 16)
|
|
#define VTCR_EL2_TG0_MASK (1 << 14)
|
|
@@ -120,36 +117,28 @@
|
|
#define VTCR_EL2_SL0_MASK (3 << 6)
|
|
#define VTCR_EL2_SL0_LVL1 (1 << 6)
|
|
#define VTCR_EL2_T0SZ_MASK 0x3f
|
|
-#define VTCR_EL2_T0SZ_40B 24
|
|
+#define VTCR_EL2_T0SZ(bits) (64 - (bits))
|
|
|
|
#ifdef CONFIG_ARM64_64K_PAGES
|
|
/*
|
|
* Stage2 translation configuration:
|
|
- * 40bits output (PS = 2)
|
|
- * 40bits input (T0SZ = 24)
|
|
* 64kB pages (TG0 = 1)
|
|
* 2 level page tables (SL = 1)
|
|
*/
|
|
#define VTCR_EL2_FLAGS (VTCR_EL2_TG0_64K | VTCR_EL2_SH0_INNER | \
|
|
VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \
|
|
- VTCR_EL2_SL0_LVL1 | VTCR_EL2_T0SZ_40B)
|
|
-#define VTTBR_X (38 - VTCR_EL2_T0SZ_40B)
|
|
+ VTCR_EL2_SL0_LVL1)
|
|
#else
|
|
/*
|
|
* Stage2 translation configuration:
|
|
- * 40bits output (PS = 2)
|
|
- * 40bits input (T0SZ = 24)
|
|
* 4kB pages (TG0 = 0)
|
|
* 3 level page tables (SL = 1)
|
|
*/
|
|
#define VTCR_EL2_FLAGS (VTCR_EL2_TG0_4K | VTCR_EL2_SH0_INNER | \
|
|
VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \
|
|
- VTCR_EL2_SL0_LVL1 | VTCR_EL2_T0SZ_40B)
|
|
-#define VTTBR_X (37 - VTCR_EL2_T0SZ_40B)
|
|
+ VTCR_EL2_SL0_LVL1)
|
|
#endif
|
|
|
|
-#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
|
|
-#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
|
|
#define VTTBR_VMID_SHIFT (48LLU)
|
|
#define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT)
|
|
|
|
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
|
|
index 8e138c7..1c70b2f 100644
|
|
--- a/arch/arm64/include/asm/kvm_mmu.h
|
|
+++ b/arch/arm64/include/asm/kvm_mmu.h
|
|
@@ -167,5 +167,80 @@ static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva,
|
|
|
|
void stage2_flush_vm(struct kvm *kvm);
|
|
|
|
+/*
|
|
+ * ARMv8 64K architecture limitations:
|
|
+ * 16 <= T0SZ <= 21 is valid under 3 level of translation tables
|
|
+ * 18 <= T0SZ <= 34 is valid under 2 level of translation tables
|
|
+ * 31 <= T0SZ <= 39 is valid under 1 level of transltaion tables
|
|
+ *
|
|
+ * ARMv8 4K architecture limitations:
|
|
+ * 16 <= T0SZ <= 24 is valid under 4 level of translation tables
|
|
+ * 21 <= T0SZ <= 33 is valid under 3 level of translation tables
|
|
+ * 30 <= T0SZ <= 39 is valid under 2 level of translation tables
|
|
+ *
|
|
+ * For 4K pages we only support 3 or 4 level, giving T0SZ a range of 16 to 33.
|
|
+ * For 64K pages we only support 2 or 3 level, giving T0SZ a range of 16 to 34.
|
|
+ *
|
|
+ * See Table D4-23 and Table D4-25 in ARM DDI 0487A.b to figure out
|
|
+ * the origin of the hardcoded values, 38 and 37.
|
|
+ */
|
|
+
|
|
+#ifdef CONFIG_ARM64_64K_PAGES
|
|
+static inline int t0sz_to_vttbr_x(int t0sz)
|
|
+{
|
|
+ if (t0sz < 16 || t0sz > 34) {
|
|
+ kvm_err("Cannot support %d-bit address space\n", 64 - t0sz);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 38 - t0sz;
|
|
+}
|
|
+#else /* 4K pages */
|
|
+static inline int t0sz_to_vttbr_x(int t0sz)
|
|
+{
|
|
+ if (t0sz < 16 || t0sz > 33) {
|
|
+ kvm_err("Cannot support %d-bit address space\n", 64 - t0sz);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ return 37 - t0sz;
|
|
+}
|
|
+#endif
|
|
+static inline int kvm_get_phys_addr_shift(void)
|
|
+{
|
|
+ int pa_range = read_cpuid(ID_AA64MMFR0_EL1) & 0xf;
|
|
+
|
|
+ switch (pa_range) {
|
|
+ case 0: return 32;
|
|
+ case 1: return 36;
|
|
+ case 2: return 40;
|
|
+ case 3: return 42;
|
|
+ case 4: return 44;
|
|
+ case 5: return 48;
|
|
+ default:
|
|
+ BUG();
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+/**
|
|
+ * get_vttbr_baddr_mask - get mask value for vttbr base address
|
|
+ *
|
|
+ * In ARMv8, vttbr_baddr_mask cannot be determined in compile time since the
|
|
+ * stage2 input address size depends on hardware capability. Thus, we first
|
|
+ * need to read ID_AA64MMFR0_EL1.PARange and then set vttbr_baddr_mask with
|
|
+ * consideration of both the granule size and the level of translation tables.
|
|
+ */
|
|
+static inline u64 get_vttbr_baddr_mask(void)
|
|
+{
|
|
+ int t0sz, vttbr_x;
|
|
+
|
|
+ t0sz = VTCR_EL2_T0SZ(kvm_get_phys_addr_shift());
|
|
+ vttbr_x = t0sz_to_vttbr_x(t0sz);
|
|
+ if (vttbr_x < 0)
|
|
+ return ~0;
|
|
+ return GENMASK_ULL(48, (vttbr_x - 1));
|
|
+
|
|
+}
|
|
+
|
|
#endif /* __ASSEMBLY__ */
|
|
#endif /* __ARM64_KVM_MMU_H__ */
|
|
diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h
|
|
new file mode 100644
|
|
index 0000000..872ba93
|
|
--- /dev/null
|
|
+++ b/arch/arm64/include/asm/pci.h
|
|
@@ -0,0 +1,37 @@
|
|
+#ifndef __ASM_PCI_H
|
|
+#define __ASM_PCI_H
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/dma-mapping.h>
|
|
+
|
|
+#include <asm/io.h>
|
|
+#include <asm-generic/pci-bridge.h>
|
|
+#include <asm-generic/pci-dma-compat.h>
|
|
+
|
|
+#define PCIBIOS_MIN_IO 0x1000
|
|
+#define PCIBIOS_MIN_MEM 0
|
|
+
|
|
+/*
|
|
+ * Set to 1 if the kernel should re-assign all PCI bus numbers
|
|
+ */
|
|
+#define pcibios_assign_all_busses() \
|
|
+ (pci_has_flag(PCI_REASSIGN_ALL_BUS))
|
|
+
|
|
+/*
|
|
+ * PCI address space differs from physical memory address space
|
|
+ */
|
|
+#define PCI_DMA_BUS_IS_PHYS (0)
|
|
+
|
|
+extern int isa_dma_bridge_buggy;
|
|
+
|
|
+#ifdef CONFIG_PCI
|
|
+static inline int pci_proc_domain(struct pci_bus *bus)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
+#endif /* CONFIG_PCI */
|
|
+
|
|
+#endif /* __KERNEL__ */
|
|
+#endif /* __ASM_PCI_H */
|
|
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
|
|
index ffe1ba0..a968523 100644
|
|
--- a/arch/arm64/include/asm/pgtable.h
|
|
+++ b/arch/arm64/include/asm/pgtable.h
|
|
@@ -296,6 +296,8 @@ static inline int has_transparent_hugepage(void)
|
|
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)
|
|
#define pgprot_writecombine(prot) \
|
|
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
|
|
+#define pgprot_device(prot) \
|
|
+ __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN)
|
|
#define __HAVE_PHYS_MEM_ACCESS_PROT
|
|
struct file;
|
|
extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
|
|
diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h
|
|
index e5312ea..2454bc5 100644
|
|
--- a/arch/arm64/include/asm/psci.h
|
|
+++ b/arch/arm64/include/asm/psci.h
|
|
@@ -14,6 +14,7 @@
|
|
#ifndef __ASM_PSCI_H
|
|
#define __ASM_PSCI_H
|
|
|
|
-int psci_init(void);
|
|
+int psci_dt_init(void);
|
|
+int psci_acpi_init(void);
|
|
|
|
#endif /* __ASM_PSCI_H */
|
|
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
|
|
index a498f2c..2ebbd55 100644
|
|
--- a/arch/arm64/include/asm/smp.h
|
|
+++ b/arch/arm64/include/asm/smp.h
|
|
@@ -39,9 +39,10 @@ extern void show_ipi_list(struct seq_file *p, int prec);
|
|
extern void handle_IPI(int ipinr, struct pt_regs *regs);
|
|
|
|
/*
|
|
- * Setup the set of possible CPUs (via set_cpu_possible)
|
|
+ * Discover the set of possible CPUs and determine their
|
|
+ * SMP operations.
|
|
*/
|
|
-extern void smp_init_cpus(void);
|
|
+extern void of_smp_init_cpus(void);
|
|
|
|
/*
|
|
* Provide a function to raise an IPI cross call on CPUs in callmap.
|
|
@@ -49,6 +50,11 @@ extern void smp_init_cpus(void);
|
|
extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int));
|
|
|
|
/*
|
|
+ * Provide a function to signal a parked secondary CPU.
|
|
+ */
|
|
+extern void set_smp_boot_wakeup_call(void (*)(int cpu));
|
|
+
|
|
+/*
|
|
* Called from the secondary holding pen, this is the secondary CPU entry point.
|
|
*/
|
|
asmlinkage void secondary_start_kernel(void);
|
|
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
|
|
index df7ef87..b0bad2e 100644
|
|
--- a/arch/arm64/kernel/Makefile
|
|
+++ b/arch/arm64/kernel/Makefile
|
|
@@ -21,7 +21,8 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
|
|
sys_compat.o
|
|
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
|
|
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
|
|
-arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o
|
|
+arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o \
|
|
+ smp_parking_protocol.o
|
|
arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
|
|
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
|
|
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
|
|
@@ -29,6 +30,8 @@ arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o
|
|
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
|
|
arm64-obj-$(CONFIG_KGDB) += kgdb.o
|
|
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
|
|
+arm64-obj-$(CONFIG_PCI) += pci.o
|
|
+arm64-obj-$(CONFIG_ACPI) += acpi.o
|
|
|
|
obj-y += $(arm64-obj-y) vdso/
|
|
obj-m += $(arm64-obj-m)
|
|
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
|
|
new file mode 100644
|
|
index 0000000..5486426
|
|
--- /dev/null
|
|
+++ b/arch/arm64/kernel/acpi.c
|
|
@@ -0,0 +1,397 @@
|
|
+/*
|
|
+ * ARM64 Specific Low-Level ACPI Boot Support
|
|
+ *
|
|
+ * Copyright (C) 2013-2014, Linaro Ltd.
|
|
+ * Author: Al Stone <al.stone@linaro.org>
|
|
+ * Author: Graeme Gregory <graeme.gregory@linaro.org>
|
|
+ * Author: Hanjun Guo <hanjun.guo@linaro.org>
|
|
+ * Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
|
|
+ * Author: Naresh Bhat <naresh.bhat@linaro.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) "ACPI: " fmt
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/cpumask.h>
|
|
+#include <linux/memblock.h>
|
|
+#include <linux/irq.h>
|
|
+#include <linux/irqdomain.h>
|
|
+#include <linux/bootmem.h>
|
|
+#include <linux/smp.h>
|
|
+#include <linux/irqchip/arm-gic-acpi.h>
|
|
+
|
|
+#include <asm/cputype.h>
|
|
+#include <asm/cpu_ops.h>
|
|
+
|
|
+#define ARM64_ACPI_DISABLED_DEFAULT 1
|
|
+
|
|
+int acpi_noirq; /* skip ACPI IRQ initialization */
|
|
+int acpi_disabled = ARM64_ACPI_DISABLED_DEFAULT;
|
|
+EXPORT_SYMBOL(acpi_disabled);
|
|
+
|
|
+int acpi_pci_disabled; /* skip ACPI PCI scan and IRQ initialization */
|
|
+EXPORT_SYMBOL(acpi_pci_disabled);
|
|
+
|
|
+static int enabled_cpus; /* Processors (GICC) with enabled flag in MADT */
|
|
+
|
|
+static char *boot_method;
|
|
+static u64 parked_address[NR_CPUS];
|
|
+
|
|
+/*
|
|
+ * Since we're on ARM, the default interrupt routing model
|
|
+ * clearly has to be GIC.
|
|
+ */
|
|
+enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_GIC;
|
|
+
|
|
+/*
|
|
+ * __acpi_map_table() will be called before page_init(), so early_ioremap()
|
|
+ * or early_memremap() should be called here to for ACPI table mapping.
|
|
+ */
|
|
+char *__init __acpi_map_table(unsigned long phys, unsigned long size)
|
|
+{
|
|
+ if (!phys || !size)
|
|
+ return NULL;
|
|
+
|
|
+ return early_memremap(phys, size);
|
|
+}
|
|
+
|
|
+void __init __acpi_unmap_table(char *map, unsigned long size)
|
|
+{
|
|
+ if (!map || !size)
|
|
+ return;
|
|
+
|
|
+ early_memunmap(map, size);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * acpi_map_gic_cpu_interface - generates a logical cpu number
|
|
+ * and map to MPIDR represented by GICC structure
|
|
+ * @mpidr: CPU's hardware id to register, MPIDR represented in MADT
|
|
+ * @enabled: this cpu is enabled or not
|
|
+ *
|
|
+ * Returns the logical cpu number which maps to MPIDR
|
|
+ */
|
|
+static int acpi_map_gic_cpu_interface(u64 mpidr, u64 parked_addr, u8 enabled)
|
|
+{
|
|
+ int cpu;
|
|
+
|
|
+ if (mpidr == INVALID_HWID) {
|
|
+ pr_info("Skip invalid cpu hardware ID\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ total_cpus++;
|
|
+ if (!enabled)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (enabled_cpus >= NR_CPUS) {
|
|
+ pr_warn("NR_CPUS limit of %d reached, Processor %d/0x%llx ignored.\n",
|
|
+ NR_CPUS, total_cpus, mpidr);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* No need to check duplicate MPIDRs for the first CPU */
|
|
+ if (enabled_cpus) {
|
|
+ /*
|
|
+ * Duplicate MPIDRs are a recipe for disaster. Scan
|
|
+ * all initialized entries and check for
|
|
+ * duplicates. If any is found just ignore the CPU.
|
|
+ */
|
|
+ for_each_possible_cpu(cpu) {
|
|
+ if (cpu_logical_map(cpu) == mpidr) {
|
|
+ pr_err("Firmware bug, duplicate CPU MPIDR: 0x%llx in MADT\n",
|
|
+ mpidr);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* allocate a logical cpu id for the new comer */
|
|
+ cpu = cpumask_next_zero(-1, cpu_possible_mask);
|
|
+ } else {
|
|
+ /* First GICC entry must be BSP as ACPI spec said */
|
|
+ if (cpu_logical_map(0) != mpidr) {
|
|
+ pr_err("First GICC entry with MPIDR 0x%llx is not BSP\n",
|
|
+ mpidr);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * boot_cpu_init() already hold bit 0 in cpu_present_mask
|
|
+ * for BSP, no need to allocate again.
|
|
+ */
|
|
+ cpu = 0;
|
|
+ }
|
|
+
|
|
+ parked_address[cpu] = parked_addr;
|
|
+
|
|
+ /* CPU 0 was already initialized */
|
|
+ if (cpu) {
|
|
+ cpu_ops[cpu] = cpu_get_ops(boot_method);
|
|
+ if (!cpu_ops[cpu])
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (cpu_ops[cpu]->cpu_init(NULL, cpu))
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ /* map the logical cpu id to cpu MPIDR */
|
|
+ cpu_logical_map(cpu) = mpidr;
|
|
+
|
|
+ set_cpu_possible(cpu, true);
|
|
+ } else {
|
|
+ /* get cpu0's ops, no need to return if ops is null */
|
|
+ cpu_ops[0] = cpu_get_ops(boot_method);
|
|
+ }
|
|
+
|
|
+ enabled_cpus++;
|
|
+ return cpu;
|
|
+}
|
|
+
|
|
+static int __init
|
|
+acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header,
|
|
+ const unsigned long end)
|
|
+{
|
|
+ struct acpi_madt_generic_interrupt *processor;
|
|
+
|
|
+ processor = (struct acpi_madt_generic_interrupt *)header;
|
|
+
|
|
+ if (BAD_MADT_ENTRY(processor, end))
|
|
+ return -EINVAL;
|
|
+
|
|
+ acpi_table_print_madt_entry(header);
|
|
+
|
|
+ acpi_map_gic_cpu_interface(processor->arm_mpidr & MPIDR_HWID_BITMASK,
|
|
+ processor->parked_address, processor->flags & ACPI_MADT_ENABLED);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Parse GIC cpu interface entries in MADT for SMP init */
|
|
+void __init acpi_smp_init_cpus(void)
|
|
+{
|
|
+ int count;
|
|
+
|
|
+ /*
|
|
+ * do a partial walk of MADT to determine how many CPUs
|
|
+ * we have including disabled CPUs, and get information
|
|
+ * we need for SMP init
|
|
+ */
|
|
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
|
|
+ acpi_parse_gic_cpu_interface, 0);
|
|
+
|
|
+ if (!count) {
|
|
+ pr_err("No GIC CPU interface entries present\n");
|
|
+ return;
|
|
+ } else if (count < 0) {
|
|
+ pr_err("Error parsing GIC CPU interface entry\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Make boot-up look pretty */
|
|
+ pr_info("%d CPUs enabled, %d CPUs total\n", enabled_cpus, total_cpus);
|
|
+}
|
|
+
|
|
+int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
|
|
+{
|
|
+ *irq = irq_find_mapping(NULL, gsi);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
|
|
+
|
|
+/*
|
|
+ * success: return IRQ number (>0)
|
|
+ * failure: return =< 0
|
|
+ */
|
|
+int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
|
|
+{
|
|
+ unsigned int irq;
|
|
+ unsigned int irq_type;
|
|
+
|
|
+ /*
|
|
+ * ACPI have no bindings to indicate SPI or PPI, so we
|
|
+ * use different mappings from DT in ACPI.
|
|
+ *
|
|
+ * For FDT
|
|
+ * PPI interrupt: in the range [0, 15];
|
|
+ * SPI interrupt: in the range [0, 987];
|
|
+ *
|
|
+ * For ACPI, GSI should be unique so using
|
|
+ * the hwirq directly for the mapping:
|
|
+ * PPI interrupt: in the range [16, 31];
|
|
+ * SPI interrupt: in the range [32, 1019];
|
|
+ */
|
|
+
|
|
+ if (trigger == ACPI_EDGE_SENSITIVE &&
|
|
+ polarity == ACPI_ACTIVE_LOW)
|
|
+ irq_type = IRQ_TYPE_EDGE_FALLING;
|
|
+ else if (trigger == ACPI_EDGE_SENSITIVE &&
|
|
+ polarity == ACPI_ACTIVE_HIGH)
|
|
+ irq_type = IRQ_TYPE_EDGE_RISING;
|
|
+ else if (trigger == ACPI_LEVEL_SENSITIVE &&
|
|
+ polarity == ACPI_ACTIVE_LOW)
|
|
+ irq_type = IRQ_TYPE_LEVEL_LOW;
|
|
+ else if (trigger == ACPI_LEVEL_SENSITIVE &&
|
|
+ polarity == ACPI_ACTIVE_HIGH)
|
|
+ irq_type = IRQ_TYPE_LEVEL_HIGH;
|
|
+ else
|
|
+ irq_type = IRQ_TYPE_NONE;
|
|
+
|
|
+ /*
|
|
+ * Since only one GIC is supported in ACPI 5.0, we can
|
|
+ * create mapping refer to the default domain
|
|
+ */
|
|
+ irq = irq_create_mapping(NULL, gsi);
|
|
+ if (!irq)
|
|
+ return irq;
|
|
+
|
|
+ /* Set irq type if specified and different than the current one */
|
|
+ if (irq_type != IRQ_TYPE_NONE &&
|
|
+ irq_type != irq_get_trigger_type(irq))
|
|
+ irq_set_irq_type(irq, irq_type);
|
|
+ return irq;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_register_gsi);
|
|
+
|
|
+void acpi_unregister_gsi(u32 gsi)
|
|
+{
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
|
|
+
|
|
+static int __init acpi_parse_fadt(struct acpi_table_header *table)
|
|
+{
|
|
+ struct acpi_table_fadt *fadt = (struct acpi_table_fadt *)table;
|
|
+
|
|
+ /*
|
|
+ * Revision in table header is the FADT Major revision,
|
|
+ * and there is a minor revision of FADT which was introduced
|
|
+ * by ACPI 5.1, we only deal with ACPI 5.1 or higher revision
|
|
+ * to get arm boot flags, or we will disable ACPI.
|
|
+ */
|
|
+ if (table->revision > 5 ||
|
|
+ (table->revision == 5 && fadt->minor_revision >= 1)) {
|
|
+ /*
|
|
+ * ACPI 5.1 only has two explicit methods to boot up SMP,
|
|
+ * PSCI and Parking protocol, but the Parking protocol is
|
|
+ * only specified for ARMv7 now, so make PSCI as the only
|
|
+ * way for the SMP boot protocol before some updates for
|
|
+ * the ACPI spec or the Parking protocol spec.
|
|
+ */
|
|
+ if (acpi_psci_present())
|
|
+ boot_method = "psci";
|
|
+ else if (IS_ENABLED(CONFIG_ARM_PARKING_PROTOCOL))
|
|
+ boot_method = "parking-protocol";
|
|
+
|
|
+ if (!boot_method)
|
|
+ pr_warn("has no boot support, will not bring up secondary CPUs\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ pr_warn("Unsupported FADT revision %d.%d, should be 5.1+, will disable ACPI\n",
|
|
+ table->revision, fadt->minor_revision);
|
|
+ disable_acpi();
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * acpi_boot_table_init() called from setup_arch(), always.
|
|
+ * 1. find RSDP and get its address, and then find XSDT
|
|
+ * 2. extract all tables and checksums them all
|
|
+ * 3. check ACPI FADT revisoin
|
|
+ *
|
|
+ * We can parse ACPI boot-time tables such as MADT after
|
|
+ * this function is called.
|
|
+ */
|
|
+void __init acpi_boot_table_init(void)
|
|
+{
|
|
+ /* If acpi_disabled, bail out */
|
|
+ if (acpi_disabled)
|
|
+ return;
|
|
+
|
|
+ /* Initialize the ACPI boot-time table parser. */
|
|
+ if (acpi_table_init()) {
|
|
+ disable_acpi();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt))
|
|
+ pr_err("Can't find FADT or error happened during parsing FADT\n");
|
|
+}
|
|
+
|
|
+void __init acpi_gic_init(void)
|
|
+{
|
|
+ struct acpi_table_header *table;
|
|
+ acpi_status status;
|
|
+ acpi_size tbl_size;
|
|
+ int err;
|
|
+
|
|
+ status = acpi_get_table_with_size(ACPI_SIG_MADT, 0, &table, &tbl_size);
|
|
+ if (ACPI_FAILURE(status)) {
|
|
+ const char *msg = acpi_format_exception(status);
|
|
+
|
|
+ pr_err("Failed to get MADT table, %s\n", msg);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ err = gic_v2_acpi_init(table);
|
|
+ if (err)
|
|
+ pr_err("Failed to initialize GIC IRQ controller");
|
|
+
|
|
+ early_acpi_os_unmap_memory((char *)table, tbl_size);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Parked Address in ACPI GIC structure will be used as the CPU
|
|
+ * release address
|
|
+ */
|
|
+int acpi_get_cpu_parked_address(int cpu, u64 *addr)
|
|
+{
|
|
+ if (!addr || !parked_address[cpu])
|
|
+ return -EINVAL;
|
|
+
|
|
+ *addr = parked_address[cpu];
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __init parse_acpi(char *arg)
|
|
+{
|
|
+ if (!arg)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* "acpi=off" disables both ACPI table parsing and interpreter */
|
|
+ if (strcmp(arg, "off") == 0)
|
|
+ acpi_disabled = 1;
|
|
+ else if (strcmp(arg, "on") == 0)
|
|
+ acpi_disabled = 0;
|
|
+ else
|
|
+ return -EINVAL; /* Core will print when we return error */
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+early_param("acpi", parse_acpi);
|
|
+
|
|
+int acpi_isa_irq_to_gsi(unsigned isa_irq, u32 *gsi)
|
|
+{
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base)
|
|
+{
|
|
+ /* TBD */
|
|
+ return -EINVAL;
|
|
+}
|
|
+EXPORT_SYMBOL(acpi_register_ioapic);
|
|
+
|
|
+int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base)
|
|
+{
|
|
+ /* TBD */
|
|
+ return -EINVAL;
|
|
+}
|
|
+EXPORT_SYMBOL(acpi_unregister_ioapic);
|
|
+
|
|
diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c
|
|
index cce9524..1d90f31 100644
|
|
--- a/arch/arm64/kernel/cpu_ops.c
|
|
+++ b/arch/arm64/kernel/cpu_ops.c
|
|
@@ -23,19 +23,23 @@
|
|
#include <linux/string.h>
|
|
|
|
extern const struct cpu_operations smp_spin_table_ops;
|
|
+extern const struct cpu_operations smp_parking_protocol_ops;
|
|
extern const struct cpu_operations cpu_psci_ops;
|
|
|
|
const struct cpu_operations *cpu_ops[NR_CPUS];
|
|
|
|
-static const struct cpu_operations *supported_cpu_ops[] __initconst = {
|
|
+static const struct cpu_operations *supported_cpu_ops[] = {
|
|
#ifdef CONFIG_SMP
|
|
&smp_spin_table_ops,
|
|
+#ifdef CONFIG_ARM_PARKING_PROTOCOL
|
|
+ &smp_parking_protocol_ops,
|
|
+#endif
|
|
#endif
|
|
&cpu_psci_ops,
|
|
NULL,
|
|
};
|
|
|
|
-static const struct cpu_operations * __init cpu_get_ops(const char *name)
|
|
+const struct cpu_operations *cpu_get_ops(const char *name)
|
|
{
|
|
const struct cpu_operations **ops = supported_cpu_ops;
|
|
|
|
diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c
|
|
index 1317fef..d27dd98 100644
|
|
--- a/arch/arm64/kernel/efi-stub.c
|
|
+++ b/arch/arm64/kernel/efi-stub.c
|
|
@@ -28,20 +28,16 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
|
|
kernel_size = _edata - _text;
|
|
if (*image_addr != (dram_base + TEXT_OFFSET)) {
|
|
kernel_memsize = kernel_size + (_end - _edata);
|
|
- status = efi_relocate_kernel(sys_table, image_addr,
|
|
- kernel_size, kernel_memsize,
|
|
- dram_base + TEXT_OFFSET,
|
|
- PAGE_SIZE);
|
|
+ status = efi_low_alloc(sys_table, kernel_memsize + TEXT_OFFSET,
|
|
+ SZ_2M, reserve_addr);
|
|
if (status != EFI_SUCCESS) {
|
|
pr_efi_err(sys_table, "Failed to relocate kernel\n");
|
|
return status;
|
|
}
|
|
- if (*image_addr != (dram_base + TEXT_OFFSET)) {
|
|
- pr_efi_err(sys_table, "Failed to alloc kernel memory\n");
|
|
- efi_free(sys_table, kernel_memsize, *image_addr);
|
|
- return EFI_LOAD_ERROR;
|
|
- }
|
|
- *image_size = kernel_memsize;
|
|
+ memcpy((void *)*reserve_addr + TEXT_OFFSET, (void *)*image_addr,
|
|
+ kernel_size);
|
|
+ *image_addr = *reserve_addr + TEXT_OFFSET;
|
|
+ *reserve_size = kernel_memsize + TEXT_OFFSET;
|
|
}
|
|
|
|
|
|
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
|
|
index 03aaa99..6c4de44 100644
|
|
--- a/arch/arm64/kernel/efi.c
|
|
+++ b/arch/arm64/kernel/efi.c
|
|
@@ -479,3 +479,14 @@ err_unmap:
|
|
return -1;
|
|
}
|
|
early_initcall(arm64_enter_virtual_mode);
|
|
+
|
|
+/*
|
|
+ * If nothing else is handling pm_power_off, use EFI
|
|
+ *
|
|
+ * This is called from a late_initcall after other mechanisms
|
|
+ * have had a chance to register a handler.
|
|
+ */
|
|
+bool efi_poweroff_required(void)
|
|
+{
|
|
+ return pm_power_off == NULL;
|
|
+}
|
|
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
|
|
index 8730690..0a6e4f9 100644
|
|
--- a/arch/arm64/kernel/head.S
|
|
+++ b/arch/arm64/kernel/head.S
|
|
@@ -151,7 +151,7 @@ optional_header:
|
|
.short 0x20b // PE32+ format
|
|
.byte 0x02 // MajorLinkerVersion
|
|
.byte 0x14 // MinorLinkerVersion
|
|
- .long _edata - stext // SizeOfCode
|
|
+ .long _end - stext // SizeOfCode
|
|
.long 0 // SizeOfInitializedData
|
|
.long 0 // SizeOfUninitializedData
|
|
.long efi_stub_entry - efi_head // AddressOfEntryPoint
|
|
@@ -169,7 +169,7 @@ extra_header_fields:
|
|
.short 0 // MinorSubsystemVersion
|
|
.long 0 // Win32VersionValue
|
|
|
|
- .long _edata - efi_head // SizeOfImage
|
|
+ .long _end - efi_head // SizeOfImage
|
|
|
|
// Everything before the kernel image is considered part of the header
|
|
.long stext - efi_head // SizeOfHeaders
|
|
@@ -216,7 +216,7 @@ section_table:
|
|
.byte 0
|
|
.byte 0
|
|
.byte 0 // end of 0 padding of section name
|
|
- .long _edata - stext // VirtualSize
|
|
+ .long _end - stext // VirtualSize
|
|
.long stext - efi_head // VirtualAddress
|
|
.long _edata - stext // SizeOfRawData
|
|
.long stext - efi_head // PointerToRawData
|
|
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
|
|
new file mode 100644
|
|
index 0000000..ce5836c
|
|
--- /dev/null
|
|
+++ b/arch/arm64/kernel/pci.c
|
|
@@ -0,0 +1,70 @@
|
|
+/*
|
|
+ * Code borrowed from powerpc/kernel/pci-common.c
|
|
+ *
|
|
+ * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM
|
|
+ * Copyright (C) 2014 ARM Ltd.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License
|
|
+ * version 2 as published by the Free Software Foundation.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/of_pci.h>
|
|
+#include <linux/of_platform.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#include <asm/pci-bridge.h>
|
|
+
|
|
+/*
|
|
+ * Called after each bus is probed, but before its children are examined
|
|
+ */
|
|
+void pcibios_fixup_bus(struct pci_bus *bus)
|
|
+{
|
|
+ /* nothing to do, expected to be removed in the future */
|
|
+}
|
|
+
|
|
+/*
|
|
+ * We don't have to worry about legacy ISA devices, so nothing to do here
|
|
+ */
|
|
+resource_size_t pcibios_align_resource(void *data, const struct resource *res,
|
|
+ resource_size_t size, resource_size_t align)
|
|
+{
|
|
+ return res->start;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Try to assign the IRQ number from DT when adding a new device
|
|
+ */
|
|
+int pcibios_add_device(struct pci_dev *dev)
|
|
+{
|
|
+ dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
|
|
+static bool dt_domain_found = false;
|
|
+
|
|
+void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
|
|
+{
|
|
+ int domain = of_get_pci_domain_nr(parent->of_node);
|
|
+
|
|
+ if (domain >= 0) {
|
|
+ dt_domain_found = true;
|
|
+ } else if (dt_domain_found == true) {
|
|
+ dev_err(parent, "Node %s is missing \"linux,pci-domain\" property in DT\n",
|
|
+ parent->of_node->full_name);
|
|
+ return;
|
|
+ } else {
|
|
+ domain = pci_get_new_domain_nr();
|
|
+ }
|
|
+
|
|
+ bus->domain_nr = domain;
|
|
+}
|
|
+#endif
|
|
diff --git a/arch/arm64/kernel/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 <linux/platform_device.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/uaccess.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include <asm/cputype.h>
|
|
#include <asm/irq.h>
|
|
@@ -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 <linux/hw_breakpoint.h>
|
|
#include <linux/personality.h>
|
|
#include <linux/notifier.h>
|
|
+#include <linux/efi.h>
|
|
|
|
#include <asm/compat.h>
|
|
#include <asm/cacheflush.h>
|
|
@@ -182,6 +183,11 @@ void machine_restart(char *cmd)
|
|
arm_pm_restart(reboot_mode, cmd);
|
|
|
|
/*
|
|
+ * If all else fails, try EFI
|
|
+ */
|
|
+ efi_reboot(reboot_mode, cmd);
|
|
+
|
|
+ /*
|
|
* Whoops - the architecture was unable to reboot.
|
|
*/
|
|
printk("Reboot failed -- System halted\n");
|
|
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
|
|
index 5539547..15ba470 100644
|
|
--- a/arch/arm64/kernel/psci.c
|
|
+++ b/arch/arm64/kernel/psci.c
|
|
@@ -15,6 +15,7 @@
|
|
|
|
#define pr_fmt(fmt) "psci: " fmt
|
|
|
|
+#include <linux/acpi.h>
|
|
#include <linux/init.h>
|
|
#include <linux/of.h>
|
|
#include <linux/smp.h>
|
|
@@ -23,6 +24,7 @@
|
|
#include <linux/delay.h>
|
|
#include <uapi/linux/psci.h>
|
|
|
|
+#include <asm/acpi.h>
|
|
#include <asm/compiler.h>
|
|
#include <asm/cpu_ops.h>
|
|
#include <asm/errno.h>
|
|
@@ -231,6 +233,33 @@ static void psci_sys_poweroff(void)
|
|
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
|
|
}
|
|
|
|
+static void psci_0_2_set_functions(void)
|
|
+{
|
|
+ pr_info("Using standard PSCI v0.2 function IDs\n");
|
|
+ psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;
|
|
+ psci_ops.cpu_suspend = psci_cpu_suspend;
|
|
+
|
|
+ psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
|
|
+ psci_ops.cpu_off = psci_cpu_off;
|
|
+
|
|
+ psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON;
|
|
+ psci_ops.cpu_on = psci_cpu_on;
|
|
+
|
|
+ psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;
|
|
+ psci_ops.migrate = psci_migrate;
|
|
+
|
|
+ psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO;
|
|
+ psci_ops.affinity_info = psci_affinity_info;
|
|
+
|
|
+ psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
|
|
+ PSCI_0_2_FN_MIGRATE_INFO_TYPE;
|
|
+ psci_ops.migrate_info_type = psci_migrate_info_type;
|
|
+
|
|
+ arm_pm_restart = psci_sys_reset;
|
|
+
|
|
+ pm_power_off = psci_sys_poweroff;
|
|
+}
|
|
+
|
|
/*
|
|
* PSCI Function IDs for v0.2+ are well defined so use
|
|
* standard values.
|
|
@@ -264,29 +293,7 @@ static int __init psci_0_2_init(struct device_node *np)
|
|
}
|
|
}
|
|
|
|
- pr_info("Using standard PSCI v0.2 function IDs\n");
|
|
- psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND;
|
|
- psci_ops.cpu_suspend = psci_cpu_suspend;
|
|
-
|
|
- psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
|
|
- psci_ops.cpu_off = psci_cpu_off;
|
|
-
|
|
- psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON;
|
|
- psci_ops.cpu_on = psci_cpu_on;
|
|
-
|
|
- psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE;
|
|
- psci_ops.migrate = psci_migrate;
|
|
-
|
|
- psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO;
|
|
- psci_ops.affinity_info = psci_affinity_info;
|
|
-
|
|
- psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] =
|
|
- PSCI_0_2_FN_MIGRATE_INFO_TYPE;
|
|
- psci_ops.migrate_info_type = psci_migrate_info_type;
|
|
-
|
|
- arm_pm_restart = psci_sys_reset;
|
|
-
|
|
- pm_power_off = psci_sys_poweroff;
|
|
+ psci_0_2_set_functions();
|
|
|
|
out_put_node:
|
|
of_node_put(np);
|
|
@@ -339,7 +346,7 @@ static const struct of_device_id psci_of_match[] __initconst = {
|
|
{},
|
|
};
|
|
|
|
-int __init psci_init(void)
|
|
+int __init psci_dt_init(void)
|
|
{
|
|
struct device_node *np;
|
|
const struct of_device_id *matched_np;
|
|
@@ -354,6 +361,29 @@ int __init psci_init(void)
|
|
return init_fn(np);
|
|
}
|
|
|
|
+/*
|
|
+ * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's
|
|
+ * explicitly clarified in SBBR
|
|
+ */
|
|
+int __init psci_acpi_init(void)
|
|
+{
|
|
+ if (!acpi_psci_present()) {
|
|
+ pr_info("is not implemented in ACPI.\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ pr_info("probing for conduit method from ACPI.\n");
|
|
+
|
|
+ if (acpi_psci_use_hvc())
|
|
+ invoke_psci_fn = __invoke_psci_fn_hvc;
|
|
+ else
|
|
+ invoke_psci_fn = __invoke_psci_fn_smc;
|
|
+
|
|
+ psci_0_2_set_functions();
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
#ifdef CONFIG_SMP
|
|
|
|
static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu)
|
|
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
|
|
index edb146d..a793a20 100644
|
|
--- a/arch/arm64/kernel/setup.c
|
|
+++ b/arch/arm64/kernel/setup.c
|
|
@@ -43,6 +43,7 @@
|
|
#include <linux/of_fdt.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/efi.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include <asm/fixmap.h>
|
|
#include <asm/cpu.h>
|
|
@@ -59,6 +60,10 @@
|
|
#include <asm/memblock.h>
|
|
#include <asm/psci.h>
|
|
#include <asm/efi.h>
|
|
+#include <asm/acpi.h>
|
|
+
|
|
+int acadia_kvm_acpi=0;
|
|
+EXPORT_SYMBOL(acadia_kvm_acpi);
|
|
|
|
unsigned int processor_id;
|
|
EXPORT_SYMBOL(processor_id);
|
|
@@ -385,22 +390,34 @@ void __init setup_arch(char **cmdline_p)
|
|
|
|
parse_early_param();
|
|
|
|
+ if (acpi_disabled)
|
|
+ disable_acpi();
|
|
+
|
|
efi_init();
|
|
arm64_memblock_init();
|
|
|
|
+ /* Parse the ACPI tables for possible boot-time configuration */
|
|
+ acpi_boot_table_init();
|
|
+
|
|
paging_init();
|
|
request_standard_resources();
|
|
|
|
efi_idmap_init();
|
|
|
|
- unflatten_device_tree();
|
|
-
|
|
- psci_init();
|
|
-
|
|
cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
|
|
- cpu_read_bootcpu_ops();
|
|
+ if (acpi_disabled) {
|
|
+ unflatten_device_tree();
|
|
+ psci_dt_init();
|
|
+ cpu_read_bootcpu_ops();
|
|
+#ifdef CONFIG_SMP
|
|
+ of_smp_init_cpus();
|
|
+#endif
|
|
+ } else {
|
|
+ psci_acpi_init();
|
|
+ acpi_smp_init_cpus();
|
|
+ }
|
|
+
|
|
#ifdef CONFIG_SMP
|
|
- smp_init_cpus();
|
|
smp_build_mpidr_hash();
|
|
#endif
|
|
|
|
@@ -413,6 +430,19 @@ void __init setup_arch(char **cmdline_p)
|
|
#endif
|
|
}
|
|
|
|
+static int __init parse_kvm_acpi(char *arg)
|
|
+{
|
|
+ if (!arg)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (strcmp(arg, "on") == 0) {
|
|
+ acadia_kvm_acpi = 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+early_param("kvmacpi", parse_kvm_acpi);
|
|
+
|
|
static int __init arm64_device_init(void)
|
|
{
|
|
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
|
@@ -505,3 +535,25 @@ const struct seq_operations cpuinfo_op = {
|
|
.stop = c_stop,
|
|
.show = c_show
|
|
};
|
|
+
|
|
+/*
|
|
+ * Temporary hack to avoid need for console= on command line
|
|
+ */
|
|
+static int __init arm64_console_setup(void)
|
|
+{
|
|
+ /* Allow cmdline to override our assumed preferences */
|
|
+ if (console_set_on_cmdline)
|
|
+ return 0;
|
|
+
|
|
+ if (IS_ENABLED(CONFIG_SBSAUART_TTY))
|
|
+ add_preferred_console("ttySBSA", 0, "115200");
|
|
+
|
|
+ if (IS_ENABLED(CONFIG_SERIAL_AMBA_PL011))
|
|
+ add_preferred_console("ttyAMA", 0, "115200");
|
|
+
|
|
+ if (IS_ENABLED(CONFIG_SERIAL_8250))
|
|
+ add_preferred_console("ttyS", 0, "115200");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+early_initcall(arm64_console_setup);
|
|
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
|
|
index 4743397..4e390ac 100644
|
|
--- a/arch/arm64/kernel/smp.c
|
|
+++ b/arch/arm64/kernel/smp.c
|
|
@@ -321,7 +321,7 @@ void __init smp_prepare_boot_cpu(void)
|
|
* cpu logical map array containing MPIDR values related to logical
|
|
* cpus. Assumes that cpu_logical_map(0) has already been initialized.
|
|
*/
|
|
-void __init smp_init_cpus(void)
|
|
+void __init of_smp_init_cpus(void)
|
|
{
|
|
struct device_node *dn = NULL;
|
|
unsigned int i, cpu = 1;
|
|
diff --git a/arch/arm64/kernel/smp_parking_protocol.c b/arch/arm64/kernel/smp_parking_protocol.c
|
|
new file mode 100644
|
|
index 0000000..e1153ce
|
|
--- /dev/null
|
|
+++ b/arch/arm64/kernel/smp_parking_protocol.c
|
|
@@ -0,0 +1,110 @@
|
|
+/*
|
|
+ * Parking Protocol SMP initialisation
|
|
+ *
|
|
+ * Based largely on spin-table method.
|
|
+ *
|
|
+ * Copyright (C) 2013 ARM Ltd.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+#include <linux/delay.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/smp.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/acpi.h>
|
|
+
|
|
+#include <asm/cacheflush.h>
|
|
+#include <asm/cpu_ops.h>
|
|
+#include <asm/cputype.h>
|
|
+#include <asm/smp_plat.h>
|
|
+
|
|
+static phys_addr_t cpu_mailbox_addr[NR_CPUS];
|
|
+
|
|
+static void (*__smp_boot_wakeup)(int cpu);
|
|
+
|
|
+void set_smp_boot_wakeup_call(void (*fn)(int cpu))
|
|
+{
|
|
+ __smp_boot_wakeup = fn;
|
|
+}
|
|
+
|
|
+static int smp_parking_protocol_cpu_init(struct device_node *dn,
|
|
+ unsigned int cpu)
|
|
+{
|
|
+ /*
|
|
+ * Determine the mailbox address.
|
|
+ */
|
|
+ if (!acpi_get_cpu_parked_address(cpu, &cpu_mailbox_addr[cpu])) {
|
|
+ pr_info("%s: ACPI parked addr=%llx\n",
|
|
+ __func__, cpu_mailbox_addr[cpu]);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ pr_err("CPU %d: missing or invalid parking protocol mailbox\n", cpu);
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static int smp_parking_protocol_cpu_prepare(unsigned int cpu)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct parking_protocol_mailbox {
|
|
+ __le32 cpu_id;
|
|
+ __le32 reserved;
|
|
+ __le64 entry_point;
|
|
+};
|
|
+
|
|
+static int smp_parking_protocol_cpu_boot(unsigned int cpu)
|
|
+{
|
|
+ struct parking_protocol_mailbox __iomem *mailbox;
|
|
+
|
|
+ if (!cpu_mailbox_addr[cpu] || !__smp_boot_wakeup)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /*
|
|
+ * The mailbox may or may not be inside the linear mapping.
|
|
+ * As ioremap_cache will either give us a new mapping or reuse the
|
|
+ * existing linear mapping, we can use it to cover both cases. In
|
|
+ * either case the memory will be MT_NORMAL.
|
|
+ */
|
|
+ mailbox = ioremap_cache(cpu_mailbox_addr[cpu], sizeof(*mailbox));
|
|
+ if (!mailbox)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /*
|
|
+ * We write the entry point and cpu id as LE regardless of the
|
|
+ * native endianess of the kernel. Therefore, any boot-loaders
|
|
+ * that read this address need to convert this address to the
|
|
+ * Boot-Loader's endianess before jumping.
|
|
+ */
|
|
+ writeq(__pa(secondary_entry), &mailbox->entry_point);
|
|
+ writel(cpu, &mailbox->cpu_id);
|
|
+ __flush_dcache_area(mailbox, sizeof(*mailbox));
|
|
+ __smp_boot_wakeup(cpu);
|
|
+
|
|
+ /* temp hack for broken firmware */
|
|
+ sev();
|
|
+
|
|
+ iounmap(mailbox);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+const struct cpu_operations smp_parking_protocol_ops = {
|
|
+ .name = "parking-protocol",
|
|
+ .cpu_init = smp_parking_protocol_cpu_init,
|
|
+ .cpu_prepare = smp_parking_protocol_cpu_prepare,
|
|
+ .cpu_boot = smp_parking_protocol_cpu_boot,
|
|
+};
|
|
diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c
|
|
index 0347d38..4f93c67 100644
|
|
--- a/arch/arm64/kernel/smp_spin_table.c
|
|
+++ b/arch/arm64/kernel/smp_spin_table.c
|
|
@@ -20,6 +20,7 @@
|
|
#include <linux/init.h>
|
|
#include <linux/of.h>
|
|
#include <linux/smp.h>
|
|
+#include <linux/types.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/cpu_ops.h>
|
|
@@ -65,12 +66,21 @@ static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu)
|
|
|
|
static int smp_spin_table_cpu_prepare(unsigned int cpu)
|
|
{
|
|
- void **release_addr;
|
|
+ __le64 __iomem *release_addr;
|
|
|
|
if (!cpu_release_addr[cpu])
|
|
return -ENODEV;
|
|
|
|
- release_addr = __va(cpu_release_addr[cpu]);
|
|
+ /*
|
|
+ * The cpu-release-addr may or may not be inside the linear mapping.
|
|
+ * As ioremap_cache will either give us a new mapping or reuse the
|
|
+ * existing linear mapping, we can use it to cover both cases. In
|
|
+ * either case the memory will be MT_NORMAL.
|
|
+ */
|
|
+ release_addr = ioremap_cache(cpu_release_addr[cpu],
|
|
+ sizeof(*release_addr));
|
|
+ if (!release_addr)
|
|
+ return -ENOMEM;
|
|
|
|
/*
|
|
* We write the release address as LE regardless of the native
|
|
@@ -79,15 +89,17 @@ static int smp_spin_table_cpu_prepare(unsigned int cpu)
|
|
* boot-loader's endianess before jumping. This is mandated by
|
|
* the boot protocol.
|
|
*/
|
|
- release_addr[0] = (void *) cpu_to_le64(__pa(secondary_holding_pen));
|
|
-
|
|
- __flush_dcache_area(release_addr, sizeof(release_addr[0]));
|
|
+ writeq_relaxed(__pa(secondary_holding_pen), release_addr);
|
|
+ __flush_dcache_area((__force void *)release_addr,
|
|
+ sizeof(*release_addr));
|
|
|
|
/*
|
|
* Send an event to wake up the secondary CPU.
|
|
*/
|
|
sev();
|
|
|
|
+ iounmap(release_addr);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
|
|
index 1a7125c..42f9195 100644
|
|
--- a/arch/arm64/kernel/time.c
|
|
+++ b/arch/arm64/kernel/time.c
|
|
@@ -35,6 +35,7 @@
|
|
#include <linux/delay.h>
|
|
#include <linux/clocksource.h>
|
|
#include <linux/clk-provider.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include <clocksource/arm_arch_timer.h>
|
|
|
|
@@ -72,6 +73,12 @@ void __init time_init(void)
|
|
|
|
tick_setup_hrtimer_broadcast();
|
|
|
|
+ /*
|
|
+ * Since ACPI or FDT will only one be available in the system,
|
|
+ * we can use acpi_generic_timer_init() here safely
|
|
+ */
|
|
+ acpi_generic_timer_init();
|
|
+
|
|
arch_timer_rate = arch_timer_get_rate();
|
|
if (!arch_timer_rate)
|
|
panic("Unable to initialise architected timer.\n");
|
|
diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
|
|
index c319116..fa7e67e 100644
|
|
--- a/arch/arm64/kvm/hyp-init.S
|
|
+++ b/arch/arm64/kvm/hyp-init.S
|
|
@@ -63,17 +63,21 @@ __do_hyp_init:
|
|
mrs x4, tcr_el1
|
|
ldr x5, =TCR_EL2_MASK
|
|
and x4, x4, x5
|
|
- ldr x5, =TCR_EL2_FLAGS
|
|
- orr x4, x4, x5
|
|
- msr tcr_el2, x4
|
|
-
|
|
- ldr x4, =VTCR_EL2_FLAGS
|
|
/*
|
|
* Read the PARange bits from ID_AA64MMFR0_EL1 and set the PS bits in
|
|
- * VTCR_EL2.
|
|
+ * TCR_EL2 and both PS bits and T0SZ bits in VTCR_EL2.
|
|
*/
|
|
mrs x5, ID_AA64MMFR0_EL1
|
|
bfi x4, x5, #16, #3
|
|
+ msr tcr_el2, x4
|
|
+
|
|
+ ldr x4, =VTCR_EL2_FLAGS
|
|
+ bfi x4, x5, #16, #3
|
|
+ and x5, x5, #0xf
|
|
+ adr x6, t0sz
|
|
+ add x6, x6, x5, lsl #2
|
|
+ ldr w5, [x6]
|
|
+ orr x4, x4, x5
|
|
msr vtcr_el2, x4
|
|
|
|
mrs x4, mair_el1
|
|
@@ -113,6 +117,10 @@ target: /* We're now in the trampoline code, switch page tables */
|
|
|
|
/* Hello, World! */
|
|
eret
|
|
+
|
|
+t0sz:
|
|
+ .word VTCR_EL2_T0SZ(32), VTCR_EL2_T0SZ(36), VTCR_EL2_T0SZ(40)
|
|
+ .word VTCR_EL2_T0SZ(42), VTCR_EL2_T0SZ(44), VTCR_EL2_T0SZ(48)
|
|
ENDPROC(__kvm_hyp_init)
|
|
|
|
.ltorg
|
|
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
|
|
index 4164c5a..b864a24 100644
|
|
--- a/arch/arm64/mm/dma-mapping.c
|
|
+++ b/arch/arm64/mm/dma-mapping.c
|
|
@@ -23,10 +23,13 @@
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/dma-contiguous.h>
|
|
#include <linux/of.h>
|
|
+#include <linux/of_address.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/swiotlb.h>
|
|
#include <linux/amba/bus.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/pci.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
|
|
@@ -319,6 +322,63 @@ static int dma_bus_notifier(struct notifier_block *nb,
|
|
if (of_property_read_bool(dev->of_node, "dma-coherent"))
|
|
set_dma_ops(dev, &coherent_swiotlb_dma_ops);
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+ else if (ACPI_HANDLE(dev)) {
|
|
+ acpi_status status;
|
|
+ int coherent;
|
|
+
|
|
+ /*
|
|
+ * Kernel defaults to noncoherent ops but ACPI 5.1 spec says arm64
|
|
+ * defaults to coherent. Set coherent ops if _CCA not found or _CCA
|
|
+ * found and non-zero.
|
|
+ */
|
|
+ status = acpi_check_coherency(ACPI_HANDLE(dev), &coherent);
|
|
+ if (ACPI_FAILURE(status) || coherent)
|
|
+ set_dma_ops(dev, &coherent_swiotlb_dma_ops);
|
|
+ }
|
|
+#endif
|
|
+ return NOTIFY_OK;
|
|
+}
|
|
+
|
|
+static int dma_bus_notifier_pci(struct notifier_block *nb,
|
|
+ unsigned long event, void *_dev)
|
|
+{
|
|
+ struct device *dev = _dev;
|
|
+
|
|
+ if (event != BUS_NOTIFY_ADD_DEVICE)
|
|
+ return NOTIFY_DONE;
|
|
+
|
|
+ /*
|
|
+ * PCI devices won't have an of_node but the bridge will.
|
|
+ * Search up the device chain until we find an of_node
|
|
+ * to check.
|
|
+ */
|
|
+ while (dev) {
|
|
+ if (dev->of_node) {
|
|
+ if (of_dma_is_coherent(dev->of_node))
|
|
+ set_dma_ops(_dev, &coherent_swiotlb_dma_ops);
|
|
+ break;
|
|
+ }
|
|
+#ifdef CONFIG_ACPI
|
|
+ if (ACPI_HANDLE(dev)) {
|
|
+ acpi_status status;
|
|
+ int coherent;
|
|
+
|
|
+ /*
|
|
+ * Kernel defaults to noncoherent ops but ACPI 5.1 spec says arm64
|
|
+ * defaults to coherent. Set coherent ops if _CCA not found or _CCA
|
|
+ * found and non-zero.
|
|
+ */
|
|
+ status = acpi_check_coherency(ACPI_HANDLE(dev), &coherent);
|
|
+ if (ACPI_FAILURE(status) || coherent) {
|
|
+ set_dma_ops(dev, &coherent_swiotlb_dma_ops);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ dev = dev->parent;
|
|
+ }
|
|
+
|
|
return NOTIFY_OK;
|
|
}
|
|
|
|
@@ -330,6 +390,10 @@ static struct notifier_block amba_bus_nb = {
|
|
.notifier_call = dma_bus_notifier,
|
|
};
|
|
|
|
+static struct notifier_block pci_bus_nb = {
|
|
+ .notifier_call = dma_bus_notifier_pci,
|
|
+};
|
|
+
|
|
extern int swiotlb_late_init_with_default_size(size_t default_size);
|
|
|
|
static int __init swiotlb_late_init(void)
|
|
@@ -341,6 +405,7 @@ static int __init swiotlb_late_init(void)
|
|
*/
|
|
bus_register_notifier(&platform_bus_type, &platform_bus_nb);
|
|
bus_register_notifier(&amba_bustype, &amba_bus_nb);
|
|
+ bus_register_notifier(&pci_bus_type, &pci_bus_nb);
|
|
|
|
dma_ops = &noncoherent_swiotlb_dma_ops;
|
|
|
|
diff --git a/arch/arm64/pci/Makefile b/arch/arm64/pci/Makefile
|
|
new file mode 100644
|
|
index 0000000..b8d5dbd
|
|
--- /dev/null
|
|
+++ b/arch/arm64/pci/Makefile
|
|
@@ -0,0 +1 @@
|
|
+obj-y += pci.o
|
|
diff --git a/arch/arm64/pci/pci.c b/arch/arm64/pci/pci.c
|
|
new file mode 100644
|
|
index 0000000..b03b0eb
|
|
--- /dev/null
|
|
+++ b/arch/arm64/pci/pci.c
|
|
@@ -0,0 +1,28 @@
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/pci.h>
|
|
+
|
|
+/**
|
|
+ * raw_pci_read - Platform-specific PCI config space access.
|
|
+ *
|
|
+ * Default empty implementation. Replace with an architecture-specific setup
|
|
+ * routine, if necessary.
|
|
+ */
|
|
+int __weak raw_pci_read(unsigned int domain, unsigned int bus,
|
|
+ unsigned int devfn, int reg, int len, u32 *val)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+int __weak raw_pci_write(unsigned int domain, unsigned int bus,
|
|
+ unsigned int devfn, int reg, int len, u32 val)
|
|
+{
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+/* Root bridge scanning */
|
|
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
|
|
index d0f3265..3343080 100644
|
|
--- a/drivers/acpi/Kconfig
|
|
+++ b/drivers/acpi/Kconfig
|
|
@@ -5,8 +5,7 @@
|
|
menuconfig ACPI
|
|
bool "ACPI (Advanced Configuration and Power Interface) Support"
|
|
depends on !IA64_HP_SIM
|
|
- depends on IA64 || X86
|
|
- depends on PCI
|
|
+ depends on ((IA64 || X86) && PCI) || ARM64
|
|
select PNP
|
|
default y
|
|
help
|
|
@@ -163,6 +162,7 @@ config ACPI_PROCESSOR
|
|
tristate "Processor"
|
|
select THERMAL
|
|
select CPU_IDLE
|
|
+ depends on X86 || IA64
|
|
default y
|
|
help
|
|
This driver installs ACPI as the idle handler for Linux and uses
|
|
@@ -263,7 +263,7 @@ config ACPI_DEBUG
|
|
|
|
config ACPI_PCI_SLOT
|
|
bool "PCI slot detection driver"
|
|
- depends on SYSFS
|
|
+ depends on SYSFS && PCI
|
|
default n
|
|
help
|
|
This driver creates entries in /sys/bus/pci/slots/ for all PCI
|
|
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
|
|
index 505d4d7..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 <mika.westerberg@linux.intel.com>
|
|
+ * Darren Hart <dvhart@linux.intel.com>
|
|
+ * Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/export.h>
|
|
+
|
|
+#include "internal.h"
|
|
+
|
|
+/* ACPI _DSD device properties UUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
|
|
+static const u8 prp_uuid[16] = {
|
|
+ 0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
|
|
+ 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01
|
|
+};
|
|
+
|
|
+static bool acpi_property_value_ok(const union acpi_object *value)
|
|
+{
|
|
+ int j;
|
|
+
|
|
+ /*
|
|
+ * The value must be an integer, a string, a reference, or a package
|
|
+ * whose every element must be an integer, a string, or a reference.
|
|
+ */
|
|
+ switch (value->type) {
|
|
+ case ACPI_TYPE_INTEGER:
|
|
+ case ACPI_TYPE_STRING:
|
|
+ case ACPI_TYPE_LOCAL_REFERENCE:
|
|
+ return true;
|
|
+
|
|
+ case ACPI_TYPE_PACKAGE:
|
|
+ for (j = 0; j < value->package.count; j++)
|
|
+ switch (value->package.elements[j].type) {
|
|
+ case ACPI_TYPE_INTEGER:
|
|
+ case ACPI_TYPE_STRING:
|
|
+ case ACPI_TYPE_LOCAL_REFERENCE:
|
|
+ continue;
|
|
+
|
|
+ default:
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static bool acpi_properties_format_valid(const union acpi_object *properties)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < properties->package.count; i++) {
|
|
+ const union acpi_object *property;
|
|
+
|
|
+ property = &properties->package.elements[i];
|
|
+ /*
|
|
+ * Only two elements allowed, the first one must be a string and
|
|
+ * the second one has to satisfy certain conditions.
|
|
+ */
|
|
+ if (property->package.count != 2
|
|
+ || property->package.elements[0].type != ACPI_TYPE_STRING
|
|
+ || !acpi_property_value_ok(&property->package.elements[1]))
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static void acpi_init_of_compatible(struct acpi_device *adev)
|
|
+{
|
|
+ const union acpi_object *of_compatible;
|
|
+ struct acpi_hardware_id *hwid;
|
|
+ bool acpi_of = false;
|
|
+
|
|
+ /*
|
|
+ * 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 <graeme.gregory@linaro.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <linux/acpi.h>
|
|
+
|
|
+/*
|
|
+ * Currently the ACPI 5.1 standard does not define S states in a
|
|
+ * manner which is usable for ARM64. These two stubs are sufficient
|
|
+ * that system initialises and device PM works.
|
|
+ */
|
|
+u32 acpi_target_system_state(void)
|
|
+{
|
|
+ return ACPI_STATE_S0;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(acpi_target_system_state);
|
|
+
|
|
+int __init acpi_sleep_init(void)
|
|
+{
|
|
+ return -ENOSYS;
|
|
+}
|
|
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
|
|
index 6d5a6cd..47f36d4 100644
|
|
--- a/drivers/acpi/tables.c
|
|
+++ b/drivers/acpi/tables.c
|
|
@@ -183,6 +183,49 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
|
|
}
|
|
break;
|
|
|
|
+ case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
|
|
+ {
|
|
+ struct acpi_madt_generic_interrupt *p =
|
|
+ (struct acpi_madt_generic_interrupt *)header;
|
|
+ pr_info("GICC (acpi_id[0x%04x] address[%p] MPDIR[0x%llx] %s)\n",
|
|
+ p->uid, (void *)(unsigned long)p->base_address,
|
|
+ p->arm_mpidr,
|
|
+ (p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
|
|
+
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
|
|
+ {
|
|
+ struct acpi_madt_generic_distributor *p =
|
|
+ (struct acpi_madt_generic_distributor *)header;
|
|
+ pr_info("GIC Distributor (gic_id[0x%04x] address[%p] gsi_base[%d])\n",
|
|
+ p->gic_id,
|
|
+ (void *)(unsigned long)p->base_address,
|
|
+ p->global_irq_base);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case ACPI_MADT_TYPE_GENERIC_MSI_FRAME:
|
|
+ {
|
|
+ struct acpi_madt_generic_msi_frame *p =
|
|
+ (struct acpi_madt_generic_msi_frame *)header;
|
|
+ pr_info("GIC MSI Frame (msi_fame_id[%d] address[%p])\n",
|
|
+ p->msi_frame_id,
|
|
+ (void *)(unsigned long)p->base_address);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR:
|
|
+ {
|
|
+ struct acpi_madt_generic_redistributor *p =
|
|
+ (struct acpi_madt_generic_redistributor *)header;
|
|
+ pr_info("GIC Redistributor (address[%p] region_size[0x%x])\n",
|
|
+ (void *)(unsigned long)p->base_address,
|
|
+ p->length);
|
|
+ }
|
|
+ break;
|
|
+
|
|
default:
|
|
pr_warn("Found unsupported MADT entry (type = 0x%x)\n",
|
|
header->type);
|
|
@@ -192,17 +235,14 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
|
|
|
|
|
|
int __init
|
|
-acpi_table_parse_entries(char *id,
|
|
- unsigned long table_size,
|
|
- int entry_id,
|
|
- acpi_tbl_entry_handler handler,
|
|
- unsigned int max_entries)
|
|
+acpi_parse_entries(unsigned long table_size,
|
|
+ acpi_tbl_entry_handler handler,
|
|
+ struct acpi_table_header *table_header,
|
|
+ int entry_id, unsigned int max_entries)
|
|
{
|
|
- struct acpi_table_header *table_header = NULL;
|
|
struct acpi_subtable_header *entry;
|
|
- unsigned int count = 0;
|
|
+ int count = 0;
|
|
unsigned long table_end;
|
|
- acpi_size tbl_size;
|
|
|
|
if (acpi_disabled)
|
|
return -ENODEV;
|
|
@@ -210,13 +250,11 @@ acpi_table_parse_entries(char *id,
|
|
if (!handler)
|
|
return -EINVAL;
|
|
|
|
- if (strncmp(id, ACPI_SIG_MADT, 4) == 0)
|
|
- acpi_get_table_with_size(id, acpi_apic_instance, &table_header, &tbl_size);
|
|
- else
|
|
- acpi_get_table_with_size(id, 0, &table_header, &tbl_size);
|
|
+ if (!table_size)
|
|
+ return -EINVAL;
|
|
|
|
if (!table_header) {
|
|
- pr_warn("%4.4s not present\n", id);
|
|
+ pr_warn("Table header not present\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
@@ -230,32 +268,67 @@ acpi_table_parse_entries(char *id,
|
|
while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) <
|
|
table_end) {
|
|
if (entry->type == entry_id
|
|
- && (!max_entries || count++ < max_entries))
|
|
+ && (!max_entries || count < max_entries)) {
|
|
if (handler(entry, table_end))
|
|
- goto err;
|
|
+ return -EINVAL;
|
|
+
|
|
+ count++;
|
|
+ }
|
|
|
|
/*
|
|
* If entry->length is 0, break from this loop to avoid
|
|
* infinite loop.
|
|
*/
|
|
if (entry->length == 0) {
|
|
- pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, entry_id);
|
|
- goto err;
|
|
+ pr_err("[0x%02x] Invalid zero length\n", entry_id);
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
entry = (struct acpi_subtable_header *)
|
|
((unsigned long)entry + entry->length);
|
|
}
|
|
+
|
|
if (max_entries && count > max_entries) {
|
|
pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n",
|
|
- id, entry_id, count - max_entries, count);
|
|
+ table_header->signature, entry_id, count - max_entries,
|
|
+ count);
|
|
}
|
|
|
|
- early_acpi_os_unmap_memory((char *)table_header, tbl_size);
|
|
return count;
|
|
-err:
|
|
+}
|
|
+
|
|
+int __init
|
|
+acpi_table_parse_entries(char *id,
|
|
+ unsigned long table_size,
|
|
+ int entry_id,
|
|
+ acpi_tbl_entry_handler handler,
|
|
+ unsigned int max_entries)
|
|
+{
|
|
+ struct acpi_table_header *table_header = NULL;
|
|
+ acpi_size tbl_size;
|
|
+ int count;
|
|
+
|
|
+ if (acpi_disabled)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (!handler)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (strncmp(id, ACPI_SIG_MADT, 4) == 0)
|
|
+ acpi_get_table_with_size(id, acpi_apic_instance, &table_header, &tbl_size);
|
|
+ else
|
|
+ acpi_get_table_with_size(id, 0, &table_header, &tbl_size);
|
|
+
|
|
+ if (!table_header) {
|
|
+ pr_warn("%4.4s not present\n", id);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ count = acpi_parse_entries(table_size, handler, table_header,
|
|
+ entry_id, max_entries);
|
|
+
|
|
early_acpi_os_unmap_memory((char *)table_header, tbl_size);
|
|
- return -EINVAL;
|
|
+ return count;
|
|
}
|
|
|
|
int __init
|
|
diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
|
|
index 07c8c5a..aec9656 100644
|
|
--- a/drivers/acpi/utils.c
|
|
+++ b/drivers/acpi/utils.c
|
|
@@ -698,3 +698,29 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs)
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL(acpi_check_dsm);
|
|
+
|
|
+/**
|
|
+ * acpi_check_coherency - check for memory coherency of a device
|
|
+ * @handle: ACPI device handle
|
|
+ * @val: Pointer to returned value
|
|
+ *
|
|
+ * Search a device and its parents for a _CCA method and return
|
|
+ * its value.
|
|
+ */
|
|
+acpi_status acpi_check_coherency(acpi_handle handle, int *val)
|
|
+{
|
|
+ unsigned long long data;
|
|
+ acpi_status status;
|
|
+
|
|
+ do {
|
|
+ status = acpi_evaluate_integer(handle, "_CCA", NULL, &data);
|
|
+ if (!ACPI_FAILURE(status)) {
|
|
+ *val = data;
|
|
+ break;
|
|
+ }
|
|
+ status = acpi_get_parent(handle, &handle);
|
|
+ } while (!ACPI_FAILURE(status));
|
|
+
|
|
+ return status;
|
|
+}
|
|
+EXPORT_SYMBOL(acpi_check_coherency);
|
|
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
|
|
index e1b9278..f2e6c9e 100644
|
|
--- a/drivers/ata/Kconfig
|
|
+++ b/drivers/ata/Kconfig
|
|
@@ -48,7 +48,7 @@ config ATA_VERBOSE_ERROR
|
|
|
|
config ATA_ACPI
|
|
bool "ATA ACPI Support"
|
|
- depends on ACPI && PCI
|
|
+ depends on ACPI
|
|
default y
|
|
help
|
|
This option adds support for ATA-related ACPI objects.
|
|
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
|
|
index f61ddb9..3499bab 100644
|
|
--- a/drivers/ata/ahci_platform.c
|
|
+++ b/drivers/ata/ahci_platform.c
|
|
@@ -20,6 +20,9 @@
|
|
#include <linux/platform_device.h>
|
|
#include <linux/libata.h>
|
|
#include <linux/ahci_platform.h>
|
|
+#ifdef CONFIG_ATA_ACPI
|
|
+#include <linux/acpi.h>
|
|
+#endif
|
|
#include "ahci.h"
|
|
|
|
static const struct ata_port_info ahci_port_info = {
|
|
@@ -87,6 +90,13 @@ static const struct of_device_id ahci_of_match[] = {
|
|
};
|
|
MODULE_DEVICE_TABLE(of, ahci_of_match);
|
|
|
|
+#ifdef CONFIG_ATA_ACPI
|
|
+static const struct acpi_device_id ahci_acpi_match[] = {
|
|
+ { "AMDI0600", 0 }, /* AMD Seattle AHCI */
|
|
+ { },
|
|
+};
|
|
+#endif
|
|
+
|
|
static struct platform_driver ahci_driver = {
|
|
.probe = ahci_probe,
|
|
.remove = ata_platform_remove_one,
|
|
@@ -94,6 +104,9 @@ static struct platform_driver ahci_driver = {
|
|
.name = "ahci",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = ahci_of_match,
|
|
+#ifdef CONFIG_ATA_ACPI
|
|
+ .acpi_match_table = ACPI_PTR(ahci_acpi_match),
|
|
+#endif
|
|
.pm = &ahci_pm_ops,
|
|
},
|
|
};
|
|
diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c
|
|
index f03aab1..b02ba9d 100644
|
|
--- a/drivers/ata/ahci_xgene.c
|
|
+++ b/drivers/ata/ahci_xgene.c
|
|
@@ -28,6 +28,7 @@
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/phy/phy.h>
|
|
+#include <linux/acpi.h>
|
|
#include "ahci.h"
|
|
|
|
/* Max # of disk per a controller */
|
|
@@ -137,7 +138,8 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc)
|
|
struct xgene_ahci_context *ctx = hpriv->plat_data;
|
|
int rc = 0;
|
|
|
|
- if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA))
|
|
+ if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA ||
|
|
+ ctx->last_cmd[ap->port_no] == ATA_CMD_SMART))
|
|
xgene_ahci_restart_engine(ap);
|
|
|
|
rc = ahci_qc_issue(qc);
|
|
@@ -148,14 +150,6 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc)
|
|
return rc;
|
|
}
|
|
|
|
-static bool xgene_ahci_is_memram_inited(struct xgene_ahci_context *ctx)
|
|
-{
|
|
- void __iomem *diagcsr = ctx->csr_diag;
|
|
-
|
|
- return (readl(diagcsr + CFG_MEM_RAM_SHUTDOWN) == 0 &&
|
|
- readl(diagcsr + BLOCK_MEM_RDY) == 0xFFFFFFFF);
|
|
-}
|
|
-
|
|
/**
|
|
* xgene_ahci_read_id - Read ID data from the specified device
|
|
* @dev: device
|
|
@@ -495,11 +489,6 @@ static int xgene_ahci_probe(struct platform_device *pdev)
|
|
return -ENODEV;
|
|
}
|
|
|
|
- if (xgene_ahci_is_memram_inited(ctx)) {
|
|
- dev_info(dev, "skip clock and PHY initialization\n");
|
|
- goto skip_clk_phy;
|
|
- }
|
|
-
|
|
/* Due to errata, HW requires full toggle transition */
|
|
rc = ahci_platform_enable_clks(hpriv);
|
|
if (rc)
|
|
@@ -512,7 +501,7 @@ static int xgene_ahci_probe(struct platform_device *pdev)
|
|
|
|
/* Configure the host controller */
|
|
xgene_ahci_hw_init(hpriv);
|
|
-skip_clk_phy:
|
|
+
|
|
hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_NCQ;
|
|
|
|
rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info);
|
|
@@ -527,6 +516,16 @@ disable_resources:
|
|
return rc;
|
|
}
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+static const struct acpi_device_id xgene_ahci_acpi_match[] = {
|
|
+ { "APMC0D00", },
|
|
+ { "APMC0D0D", },
|
|
+ { "APMC0D09", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match);
|
|
+#endif
|
|
+
|
|
static const struct of_device_id xgene_ahci_of_match[] = {
|
|
{.compatible = "apm,xgene-ahci"},
|
|
{},
|
|
@@ -540,6 +539,7 @@ static struct platform_driver xgene_ahci_driver = {
|
|
.name = "xgene-ahci",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = xgene_ahci_of_match,
|
|
+ .acpi_match_table = ACPI_PTR(xgene_ahci_acpi_match),
|
|
},
|
|
};
|
|
|
|
diff --git a/drivers/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 <rafael.j.wysocki@intel.com>
|
|
+ * Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#include <linux/property.h>
|
|
+#include <linux/export.h>
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/of.h>
|
|
+
|
|
+/**
|
|
+ * device_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 <linux/io.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched_clock.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include <asm/arch_timer.h>
|
|
#include <asm/virt.h>
|
|
@@ -61,7 +62,8 @@ enum ppi_nr {
|
|
MAX_TIMER_PPI
|
|
};
|
|
|
|
-static int arch_timer_ppi[MAX_TIMER_PPI];
|
|
+int arch_timer_ppi[MAX_TIMER_PPI];
|
|
+EXPORT_SYMBOL(arch_timer_ppi);
|
|
|
|
static struct clock_event_device __percpu *arch_timer_evt;
|
|
|
|
@@ -338,8 +340,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
|
|
if (arch_timer_rate)
|
|
return;
|
|
|
|
- /* Try to determine the frequency from the device tree or CNTFRQ */
|
|
- if (of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) {
|
|
+ /*
|
|
+ * Try to determine the frequency from the device tree or CNTFRQ,
|
|
+ * if ACPI is enabled, get the frequency from CNTFRQ ONLY.
|
|
+ */
|
|
+ if (!acpi_disabled ||
|
|
+ of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) {
|
|
if (cntbase)
|
|
arch_timer_rate = readl_relaxed(cntbase + CNTFRQ);
|
|
else
|
|
@@ -635,20 +641,8 @@ static void __init arch_timer_common_init(void)
|
|
arch_timer_arch_init();
|
|
}
|
|
|
|
-static void __init arch_timer_init(struct device_node *np)
|
|
+static void __init arch_timer_init(void)
|
|
{
|
|
- int i;
|
|
-
|
|
- if (arch_timers_present & ARCH_CP15_TIMER) {
|
|
- pr_warn("arch_timer: multiple nodes in dt, skipping\n");
|
|
- return;
|
|
- }
|
|
-
|
|
- arch_timers_present |= ARCH_CP15_TIMER;
|
|
- for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
|
|
- arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
|
|
- arch_timer_detect_rate(NULL, np);
|
|
-
|
|
/*
|
|
* If HYP mode is available, we know that the physical timer
|
|
* has been configured to be accessible from PL1. Use it, so
|
|
@@ -667,13 +661,31 @@ static void __init arch_timer_init(struct device_node *np)
|
|
}
|
|
}
|
|
|
|
- arch_timer_c3stop = !of_property_read_bool(np, "always-on");
|
|
-
|
|
arch_timer_register();
|
|
arch_timer_common_init();
|
|
}
|
|
-CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init);
|
|
-CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init);
|
|
+
|
|
+static void __init arch_timer_of_init(struct device_node *np)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (arch_timers_present & ARCH_CP15_TIMER) {
|
|
+ pr_warn("arch_timer: multiple nodes in dt, skipping\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ arch_timers_present |= ARCH_CP15_TIMER;
|
|
+ for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
|
|
+ arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
|
|
+
|
|
+ arch_timer_detect_rate(NULL, np);
|
|
+
|
|
+ arch_timer_c3stop = !of_property_read_bool(np, "always-on");
|
|
+
|
|
+ arch_timer_init();
|
|
+}
|
|
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
|
|
+CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
|
|
|
|
static void __init arch_timer_mem_init(struct device_node *np)
|
|
{
|
|
@@ -740,3 +752,71 @@ static void __init arch_timer_mem_init(struct device_node *np)
|
|
}
|
|
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
|
|
arch_timer_mem_init);
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+static int __init
|
|
+map_generic_timer_interrupt(u32 interrupt, u32 flags)
|
|
+{
|
|
+ int trigger, polarity;
|
|
+
|
|
+ if (!interrupt)
|
|
+ return 0;
|
|
+
|
|
+ trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
|
|
+ : ACPI_LEVEL_SENSITIVE;
|
|
+
|
|
+ polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
|
|
+ : ACPI_ACTIVE_HIGH;
|
|
+
|
|
+ return acpi_register_gsi(NULL, interrupt, trigger, polarity);
|
|
+}
|
|
+
|
|
+/* Initialize per-processor generic timer */
|
|
+static int __init arch_timer_acpi_init(struct acpi_table_header *table)
|
|
+{
|
|
+ struct acpi_table_gtdt *gtdt;
|
|
+
|
|
+ if (arch_timers_present & ARCH_CP15_TIMER) {
|
|
+ pr_warn("arch_timer: already initialized, skipping\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ gtdt = container_of(table, struct acpi_table_gtdt, header);
|
|
+
|
|
+ arch_timers_present |= ARCH_CP15_TIMER;
|
|
+
|
|
+ arch_timer_ppi[PHYS_SECURE_PPI] =
|
|
+ map_generic_timer_interrupt(gtdt->secure_el1_interrupt,
|
|
+ gtdt->secure_el1_flags);
|
|
+
|
|
+ arch_timer_ppi[PHYS_NONSECURE_PPI] =
|
|
+ map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt,
|
|
+ gtdt->non_secure_el1_flags);
|
|
+
|
|
+ arch_timer_ppi[VIRT_PPI] =
|
|
+ map_generic_timer_interrupt(gtdt->virtual_timer_interrupt,
|
|
+ gtdt->virtual_timer_flags);
|
|
+
|
|
+ arch_timer_ppi[HYP_PPI] =
|
|
+ map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt,
|
|
+ gtdt->non_secure_el2_flags);
|
|
+
|
|
+ /* Get the frequency from CNTFRQ */
|
|
+ arch_timer_detect_rate(NULL, NULL);
|
|
+
|
|
+ /* Always-on capability */
|
|
+ arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
|
|
+
|
|
+ arch_timer_init();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Initialize all the generic timers presented in GTDT */
|
|
+void __init acpi_generic_timer_init(void)
|
|
+{
|
|
+ if (acpi_disabled)
|
|
+ return;
|
|
+
|
|
+ acpi_table_parse(ACPI_SIG_GTDT, arch_timer_acpi_init);
|
|
+}
|
|
+#endif
|
|
diff --git a/drivers/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 <linux/gpio.h>
|
|
|
|
-static DEFINE_SPINLOCK(gpio_lock);
|
|
-
|
|
-#define CGEN (0x00)
|
|
-#define CGIO (0x04)
|
|
-#define CGLV (0x08)
|
|
-
|
|
-#define RGEN (0x20)
|
|
-#define RGIO (0x24)
|
|
-#define RGLV (0x28)
|
|
-
|
|
-static unsigned short gpio_ba;
|
|
-
|
|
-static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num)
|
|
-{
|
|
- u8 curr_dirs;
|
|
- unsigned short offset, bit;
|
|
-
|
|
- spin_lock(&gpio_lock);
|
|
-
|
|
- offset = CGIO + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
-
|
|
- curr_dirs = inb(gpio_ba + offset);
|
|
-
|
|
- if (!(curr_dirs & (1 << bit)))
|
|
- outb(curr_dirs | (1 << bit), gpio_ba + offset);
|
|
+#define GEN 0x00
|
|
+#define GIO 0x04
|
|
+#define GLV 0x08
|
|
+
|
|
+struct sch_gpio {
|
|
+ struct gpio_chip chip;
|
|
+ spinlock_t lock;
|
|
+ unsigned short iobase;
|
|
+ unsigned short core_base;
|
|
+ unsigned short resume_base;
|
|
+};
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
- return 0;
|
|
-}
|
|
+#define to_sch_gpio(c) container_of(c, struct sch_gpio, chip)
|
|
|
|
-static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
|
|
+static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio,
|
|
+ unsigned reg)
|
|
{
|
|
- int res;
|
|
- unsigned short offset, bit;
|
|
+ unsigned base = 0;
|
|
|
|
- offset = CGLV + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ if (gpio >= sch->resume_base) {
|
|
+ gpio -= sch->resume_base;
|
|
+ base += 0x20;
|
|
+ }
|
|
|
|
- res = !!(inb(gpio_ba + offset) & (1 << bit));
|
|
- return res;
|
|
+ return base + reg + gpio / 8;
|
|
}
|
|
|
|
-static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
|
|
+static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
|
|
{
|
|
- u8 curr_vals;
|
|
- unsigned short offset, bit;
|
|
-
|
|
- spin_lock(&gpio_lock);
|
|
-
|
|
- offset = CGLV + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
-
|
|
- curr_vals = inb(gpio_ba + offset);
|
|
-
|
|
- if (val)
|
|
- outb(curr_vals | (1 << bit), gpio_ba + offset);
|
|
- else
|
|
- outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
|
|
- spin_unlock(&gpio_lock);
|
|
+ if (gpio >= sch->resume_base)
|
|
+ gpio -= sch->resume_base;
|
|
+ return gpio % 8;
|
|
}
|
|
|
|
-static int sch_gpio_core_direction_out(struct gpio_chip *gc,
|
|
- unsigned gpio_num, int val)
|
|
+static void sch_gpio_enable(struct sch_gpio *sch, unsigned gpio)
|
|
{
|
|
- u8 curr_dirs;
|
|
unsigned short offset, bit;
|
|
+ u8 enable;
|
|
|
|
- spin_lock(&gpio_lock);
|
|
+ spin_lock(&sch->lock);
|
|
|
|
- offset = CGIO + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
-
|
|
- curr_dirs = inb(gpio_ba + offset);
|
|
- if (curr_dirs & (1 << bit))
|
|
- outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
|
|
+ offset = sch_gpio_offset(sch, gpio, GEN);
|
|
+ bit = sch_gpio_bit(sch, gpio);
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
+ enable = inb(sch->iobase + offset);
|
|
+ if (!(enable & (1 << bit)))
|
|
+ outb(enable | (1 << bit), sch->iobase + offset);
|
|
|
|
- /*
|
|
- * according to the datasheet, writing to the level register has no
|
|
- * effect when GPIO is programmed as input.
|
|
- * Actually the the level register is read-only when configured as input.
|
|
- * Thus presetting the output level before switching to output is _NOT_ possible.
|
|
- * Hence we set the level after configuring the GPIO as output.
|
|
- * But we cannot prevent a short low pulse if direction is set to high
|
|
- * and an external pull-up is connected.
|
|
- */
|
|
- sch_gpio_core_set(gc, gpio_num, val);
|
|
- return 0;
|
|
+ spin_unlock(&sch->lock);
|
|
}
|
|
|
|
-static struct gpio_chip sch_gpio_core = {
|
|
- .label = "sch_gpio_core",
|
|
- .owner = THIS_MODULE,
|
|
- .direction_input = sch_gpio_core_direction_in,
|
|
- .get = sch_gpio_core_get,
|
|
- .direction_output = sch_gpio_core_direction_out,
|
|
- .set = sch_gpio_core_set,
|
|
-};
|
|
-
|
|
-static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
|
|
- unsigned gpio_num)
|
|
+static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
|
|
{
|
|
+ struct sch_gpio *sch = to_sch_gpio(gc);
|
|
u8 curr_dirs;
|
|
unsigned short offset, bit;
|
|
|
|
- spin_lock(&gpio_lock);
|
|
+ spin_lock(&sch->lock);
|
|
|
|
- offset = RGIO + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ offset = sch_gpio_offset(sch, gpio_num, GIO);
|
|
+ bit = sch_gpio_bit(sch, gpio_num);
|
|
|
|
- curr_dirs = inb(gpio_ba + offset);
|
|
+ curr_dirs = inb(sch->iobase + offset);
|
|
|
|
if (!(curr_dirs & (1 << bit)))
|
|
- outb(curr_dirs | (1 << bit), gpio_ba + offset);
|
|
+ outb(curr_dirs | (1 << bit), sch->iobase + offset);
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
+ spin_unlock(&sch->lock);
|
|
return 0;
|
|
}
|
|
|
|
-static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
|
|
+static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
|
|
{
|
|
+ struct sch_gpio *sch = to_sch_gpio(gc);
|
|
+ int res;
|
|
unsigned short offset, bit;
|
|
|
|
- offset = RGLV + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ offset = sch_gpio_offset(sch, gpio_num, GLV);
|
|
+ bit = sch_gpio_bit(sch, gpio_num);
|
|
+
|
|
+ res = !!(inb(sch->iobase + offset) & (1 << bit));
|
|
|
|
- return !!(inb(gpio_ba + offset) & (1 << bit));
|
|
+ return res;
|
|
}
|
|
|
|
-static void sch_gpio_resume_set(struct gpio_chip *gc,
|
|
- unsigned gpio_num, int val)
|
|
+static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
|
|
{
|
|
+ struct sch_gpio *sch = to_sch_gpio(gc);
|
|
u8 curr_vals;
|
|
unsigned short offset, bit;
|
|
|
|
- spin_lock(&gpio_lock);
|
|
+ spin_lock(&sch->lock);
|
|
|
|
- offset = RGLV + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ offset = sch_gpio_offset(sch, gpio_num, GLV);
|
|
+ bit = sch_gpio_bit(sch, gpio_num);
|
|
|
|
- curr_vals = inb(gpio_ba + offset);
|
|
+ curr_vals = inb(sch->iobase + offset);
|
|
|
|
if (val)
|
|
- outb(curr_vals | (1 << bit), gpio_ba + offset);
|
|
+ outb(curr_vals | (1 << bit), sch->iobase + offset);
|
|
else
|
|
- outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
|
|
+ outb((curr_vals & ~(1 << bit)), sch->iobase + offset);
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
+ spin_unlock(&sch->lock);
|
|
}
|
|
|
|
-static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
|
|
- unsigned gpio_num, int val)
|
|
+static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
|
|
+ int val)
|
|
{
|
|
+ struct sch_gpio *sch = to_sch_gpio(gc);
|
|
u8 curr_dirs;
|
|
unsigned short offset, bit;
|
|
|
|
- offset = RGIO + gpio_num / 8;
|
|
- bit = gpio_num % 8;
|
|
+ spin_lock(&sch->lock);
|
|
|
|
- spin_lock(&gpio_lock);
|
|
+ offset = sch_gpio_offset(sch, gpio_num, GIO);
|
|
+ bit = sch_gpio_bit(sch, gpio_num);
|
|
|
|
- curr_dirs = inb(gpio_ba + offset);
|
|
+ curr_dirs = inb(sch->iobase + offset);
|
|
if (curr_dirs & (1 << bit))
|
|
- outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
|
|
+ outb(curr_dirs & ~(1 << bit), sch->iobase + offset);
|
|
|
|
- spin_unlock(&gpio_lock);
|
|
+ spin_unlock(&sch->lock);
|
|
|
|
/*
|
|
- * according to the datasheet, writing to the level register has no
|
|
- * effect when GPIO is programmed as input.
|
|
- * Actually the the level register is read-only when configured as input.
|
|
- * Thus presetting the output level before switching to output is _NOT_ possible.
|
|
- * Hence we set the level after configuring the GPIO as output.
|
|
- * But we cannot prevent a short low pulse if direction is set to high
|
|
- * and an external pull-up is connected.
|
|
- */
|
|
- sch_gpio_resume_set(gc, gpio_num, val);
|
|
+ * according to the datasheet, writing to the level register has no
|
|
+ * effect when GPIO is programmed as input.
|
|
+ * Actually the the level register is read-only when configured as input.
|
|
+ * Thus presetting the output level before switching to output is _NOT_ possible.
|
|
+ * Hence we set the level after configuring the GPIO as output.
|
|
+ * But we cannot prevent a short low pulse if direction is set to high
|
|
+ * and an external pull-up is connected.
|
|
+ */
|
|
+ sch_gpio_set(gc, gpio_num, val);
|
|
return 0;
|
|
}
|
|
|
|
-static struct gpio_chip sch_gpio_resume = {
|
|
- .label = "sch_gpio_resume",
|
|
+static struct gpio_chip sch_gpio_chip = {
|
|
+ .label = "sch_gpio",
|
|
.owner = THIS_MODULE,
|
|
- .direction_input = sch_gpio_resume_direction_in,
|
|
- .get = sch_gpio_resume_get,
|
|
- .direction_output = sch_gpio_resume_direction_out,
|
|
- .set = sch_gpio_resume_set,
|
|
+ .direction_input = sch_gpio_direction_in,
|
|
+ .get = sch_gpio_get,
|
|
+ .direction_output = sch_gpio_direction_out,
|
|
+ .set = sch_gpio_set,
|
|
};
|
|
|
|
static int sch_gpio_probe(struct platform_device *pdev)
|
|
{
|
|
+ struct sch_gpio *sch;
|
|
struct resource *res;
|
|
- int err, id;
|
|
|
|
- id = pdev->id;
|
|
- if (!id)
|
|
- return -ENODEV;
|
|
+ sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL);
|
|
+ if (!sch)
|
|
+ return -ENOMEM;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
|
if (!res)
|
|
return -EBUSY;
|
|
|
|
- if (!request_region(res->start, resource_size(res), pdev->name))
|
|
+ if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
|
|
+ pdev->name))
|
|
return -EBUSY;
|
|
|
|
- gpio_ba = res->start;
|
|
+ spin_lock_init(&sch->lock);
|
|
+ sch->iobase = res->start;
|
|
+ sch->chip = sch_gpio_chip;
|
|
+ sch->chip.label = dev_name(&pdev->dev);
|
|
+ sch->chip.dev = &pdev->dev;
|
|
|
|
- switch (id) {
|
|
+ switch (pdev->id) {
|
|
case PCI_DEVICE_ID_INTEL_SCH_LPC:
|
|
- sch_gpio_core.base = 0;
|
|
- sch_gpio_core.ngpio = 10;
|
|
- sch_gpio_resume.base = 10;
|
|
- sch_gpio_resume.ngpio = 4;
|
|
+ sch->core_base = 0;
|
|
+ sch->resume_base = 10;
|
|
+ sch->chip.ngpio = 14;
|
|
+
|
|
/*
|
|
* GPIO[6:0] enabled by default
|
|
* GPIO7 is configured by the CMC as SLPIOVR
|
|
* Enable GPIO[9:8] core powered gpios explicitly
|
|
*/
|
|
- outb(0x3, gpio_ba + CGEN + 1);
|
|
+ sch_gpio_enable(sch, 8);
|
|
+ sch_gpio_enable(sch, 9);
|
|
/*
|
|
* SUS_GPIO[2:0] enabled by default
|
|
* Enable SUS_GPIO3 resume powered gpio explicitly
|
|
*/
|
|
- outb(0x8, gpio_ba + RGEN);
|
|
+ sch_gpio_enable(sch, 13);
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_INTEL_ITC_LPC:
|
|
- sch_gpio_core.base = 0;
|
|
- sch_gpio_core.ngpio = 5;
|
|
- sch_gpio_resume.base = 5;
|
|
- sch_gpio_resume.ngpio = 9;
|
|
+ sch->core_base = 0;
|
|
+ sch->resume_base = 5;
|
|
+ sch->chip.ngpio = 14;
|
|
break;
|
|
|
|
case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
|
|
- sch_gpio_core.base = 0;
|
|
- sch_gpio_core.ngpio = 21;
|
|
- sch_gpio_resume.base = 21;
|
|
- sch_gpio_resume.ngpio = 9;
|
|
+ sch->core_base = 0;
|
|
+ sch->resume_base = 21;
|
|
+ sch->chip.ngpio = 30;
|
|
break;
|
|
|
|
default:
|
|
- err = -ENODEV;
|
|
- goto err_sch_gpio_core;
|
|
+ return -ENODEV;
|
|
}
|
|
|
|
- sch_gpio_core.dev = &pdev->dev;
|
|
- sch_gpio_resume.dev = &pdev->dev;
|
|
-
|
|
- err = gpiochip_add(&sch_gpio_core);
|
|
- if (err < 0)
|
|
- goto err_sch_gpio_core;
|
|
+ platform_set_drvdata(pdev, sch);
|
|
|
|
- err = gpiochip_add(&sch_gpio_resume);
|
|
- if (err < 0)
|
|
- goto err_sch_gpio_resume;
|
|
-
|
|
- return 0;
|
|
-
|
|
-err_sch_gpio_resume:
|
|
- gpiochip_remove(&sch_gpio_core);
|
|
-
|
|
-err_sch_gpio_core:
|
|
- release_region(res->start, resource_size(res));
|
|
- gpio_ba = 0;
|
|
-
|
|
- return err;
|
|
+ return gpiochip_add(&sch->chip);
|
|
}
|
|
|
|
static int sch_gpio_remove(struct platform_device *pdev)
|
|
{
|
|
- struct resource *res;
|
|
- if (gpio_ba) {
|
|
-
|
|
- gpiochip_remove(&sch_gpio_core);
|
|
- gpiochip_remove(&sch_gpio_resume);
|
|
-
|
|
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
|
-
|
|
- release_region(res->start, resource_size(res));
|
|
- gpio_ba = 0;
|
|
- }
|
|
+ struct sch_gpio *sch = platform_get_drvdata(pdev);
|
|
|
|
+ gpiochip_remove(&sch->chip);
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
|
|
index 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 <linux/ioport.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/gpio.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
#include <linux/gpio_keys.h>
|
|
-#include <linux/of.h>
|
|
-#include <linux/of_platform.h>
|
|
-#include <linux/of_gpio.h>
|
|
+#include <linux/property.h>
|
|
|
|
#define DRV_NAME "gpio-keys-polled"
|
|
|
|
@@ -51,15 +50,14 @@ static void gpio_keys_polled_check_state(struct input_dev *input,
|
|
int state;
|
|
|
|
if (bdata->can_sleep)
|
|
- state = !!gpio_get_value_cansleep(button->gpio);
|
|
+ state = !!gpiod_get_value_cansleep(button->gpiod);
|
|
else
|
|
- state = !!gpio_get_value(button->gpio);
|
|
+ state = !!gpiod_get_value(button->gpiod);
|
|
|
|
if (state != bdata->last_state) {
|
|
unsigned int type = button->type ?: EV_KEY;
|
|
|
|
- input_event(input, type, button->code,
|
|
- !!(state ^ button->active_low));
|
|
+ input_event(input, type, button->code, state);
|
|
input_sync(input);
|
|
bdata->count = 0;
|
|
bdata->last_state = state;
|
|
@@ -102,21 +100,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 <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
+#include <linux/acpi.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/irqchip/chained_irq.h>
|
|
#include <linux/irqchip/arm-gic.h>
|
|
+#include <linux/irqchip/arm-gic-acpi.h>
|
|
|
|
#include <asm/cputype.h>
|
|
#include <asm/irq.h>
|
|
@@ -622,6 +624,13 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
|
|
|
|
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
|
|
}
|
|
+
|
|
+#ifdef CONFIG_ARM_PARKING_PROTOCOL
|
|
+static void gic_wakeup_parked_cpu(int cpu)
|
|
+{
|
|
+ gic_raise_softirq(cpumask_of(cpu), GIC_DIST_SOFTINT_NSATT);
|
|
+}
|
|
+#endif
|
|
#endif
|
|
|
|
#ifdef CONFIG_BL_SWITCHER
|
|
@@ -977,6 +986,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
|
|
#ifdef CONFIG_SMP
|
|
set_smp_cross_call(gic_raise_softirq);
|
|
register_cpu_notifier(&gic_cpu_notifier);
|
|
+#ifdef CONFIG_ARM_PARKING_PROTOCOL
|
|
+ set_smp_boot_wakeup_call(gic_wakeup_parked_cpu);
|
|
+#endif
|
|
#endif
|
|
set_handle_irq(gic_handle_irq);
|
|
}
|
|
@@ -1029,3 +1041,107 @@ IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
|
|
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
|
|
|
|
#endif
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+static phys_addr_t dist_phy_base, cpu_phy_base;
|
|
+static int cpu_base_assigned;
|
|
+
|
|
+static int __init
|
|
+gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header,
|
|
+ const unsigned long end)
|
|
+{
|
|
+ struct acpi_madt_generic_interrupt *processor;
|
|
+ phys_addr_t gic_cpu_base;
|
|
+
|
|
+ processor = (struct acpi_madt_generic_interrupt *)header;
|
|
+
|
|
+ if (BAD_MADT_ENTRY(processor, end))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /*
|
|
+ * There is no support for non-banked GICv1/2 register in ACPI spec.
|
|
+ * All CPU interface addresses have to be the same.
|
|
+ */
|
|
+ gic_cpu_base = processor->base_address;
|
|
+ if (cpu_base_assigned && gic_cpu_base != cpu_phy_base)
|
|
+ return -EFAULT;
|
|
+
|
|
+ cpu_phy_base = gic_cpu_base;
|
|
+ cpu_base_assigned = 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __init
|
|
+gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header,
|
|
+ const unsigned long end)
|
|
+{
|
|
+ struct acpi_madt_generic_distributor *dist;
|
|
+
|
|
+ dist = (struct acpi_madt_generic_distributor *)header;
|
|
+
|
|
+ if (BAD_MADT_ENTRY(dist, end))
|
|
+ return -EINVAL;
|
|
+
|
|
+ dist_phy_base = dist->base_address;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int __init
|
|
+gic_v2_acpi_init(struct acpi_table_header *table)
|
|
+{
|
|
+ void __iomem *cpu_base, *dist_base;
|
|
+ int count;
|
|
+
|
|
+ /* Collect CPU base addresses */
|
|
+ count = acpi_parse_entries(sizeof(struct acpi_table_madt),
|
|
+ gic_acpi_parse_madt_cpu, table,
|
|
+ ACPI_MADT_TYPE_GENERIC_INTERRUPT, 0);
|
|
+ if (count < 0) {
|
|
+ pr_err("Error during GICC entries parsing\n");
|
|
+ return -EFAULT;
|
|
+ } else if (!count) {
|
|
+ pr_err("No valid GICC entries exist\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Find distributor base address. We expect one distributor entry since
|
|
+ * ACPI 5.1 spec neither support multi-GIC instances nor GIC cascade.
|
|
+ */
|
|
+ count = acpi_parse_entries(sizeof(struct acpi_table_madt),
|
|
+ gic_acpi_parse_madt_distributor, table,
|
|
+ ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, 0);
|
|
+ if (count <= 0) {
|
|
+ pr_err("Error during GICD entries parsing\n");
|
|
+ return -EFAULT;
|
|
+ } else if (!count) {
|
|
+ pr_err("No valid GICD entries exist\n");
|
|
+ return -EINVAL;
|
|
+ } else if (count > 1) {
|
|
+ pr_err("More than one GICD entry detected\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE);
|
|
+ if (!cpu_base) {
|
|
+ pr_err("Unable to map GICC registers\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ dist_base = ioremap(dist_phy_base, ACPI_GICV2_DIST_MEM_SIZE);
|
|
+ if (!dist_base) {
|
|
+ pr_err("Unable to map GICD registers\n");
|
|
+ iounmap(cpu_base);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Initialize zero GIC instance (no multi-GIC support). Also, set GIC
|
|
+ * as default IRQ domain to allow for GSI registration and GSI to IRQ
|
|
+ * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()).
|
|
+ */
|
|
+ gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL);
|
|
+ irq_set_default_host(gic_data[0].domain);
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c
|
|
index 0fe2f71..9106c6d 100644
|
|
--- a/drivers/irqchip/irqchip.c
|
|
+++ b/drivers/irqchip/irqchip.c
|
|
@@ -11,6 +11,7 @@
|
|
#include <linux/init.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/irqchip.h>
|
|
+#include <linux/irqchip/arm-gic-acpi.h>
|
|
|
|
/*
|
|
* This special of_device_id is the sentinel at the end of the
|
|
@@ -26,4 +27,6 @@ extern struct of_device_id __irqchip_of_table[];
|
|
void __init irqchip_init(void)
|
|
{
|
|
of_irq_init(__irqchip_of_table);
|
|
+
|
|
+ acpi_gic_init();
|
|
}
|
|
diff --git a/drivers/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 <linux/kernel.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/gpio.h>
|
|
+#include <linux/gpio/consumer.h>
|
|
#include <linux/leds.h>
|
|
-#include <linux/of.h>
|
|
-#include <linux/of_platform.h>
|
|
-#include <linux/of_gpio.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/module.h>
|
|
#include <linux/err.h>
|
|
+#include <linux/property.h>
|
|
|
|
struct gpio_led_data {
|
|
struct led_classdev cdev;
|
|
- unsigned gpio;
|
|
+ struct gpio_desc *gpiod;
|
|
struct work_struct work;
|
|
u8 new_level;
|
|
u8 can_sleep;
|
|
- u8 active_low;
|
|
u8 blinking;
|
|
int (*platform_gpio_blink_set)(unsigned gpio, int state,
|
|
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 <linux/spi/spi.h>
|
|
#include <linux/spi/eeprom.h>
|
|
-#include <linux/of.h>
|
|
+#include <linux/property.h>
|
|
|
|
/*
|
|
* NOTE: this is an *EEPROM* driver. The vagaries of product naming
|
|
@@ -301,35 +301,33 @@ static ssize_t at25_mem_write(struct memory_accessor *mem, const char *buf,
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
-static int at25_np_to_chip(struct device *dev,
|
|
- struct device_node *np,
|
|
- struct spi_eeprom *chip)
|
|
+static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
|
|
{
|
|
u32 val;
|
|
|
|
memset(chip, 0, sizeof(*chip));
|
|
- strncpy(chip->name, np->name, sizeof(chip->name));
|
|
+ strncpy(chip->name, "at25", sizeof(chip->name));
|
|
|
|
- if (of_property_read_u32(np, "size", &val) == 0 ||
|
|
- of_property_read_u32(np, "at25,byte-len", &val) == 0) {
|
|
+ if (device_property_read_u32(dev, "size", &val) == 0 ||
|
|
+ device_property_read_u32(dev, "at25,byte-len", &val) == 0) {
|
|
chip->byte_len = val;
|
|
} else {
|
|
dev_err(dev, "Error: missing \"size\" property\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
- if (of_property_read_u32(np, "pagesize", &val) == 0 ||
|
|
- of_property_read_u32(np, "at25,page-size", &val) == 0) {
|
|
+ if (device_property_read_u32(dev, "pagesize", &val) == 0 ||
|
|
+ device_property_read_u32(dev, "at25,page-size", &val) == 0) {
|
|
chip->page_size = (u16)val;
|
|
} else {
|
|
dev_err(dev, "Error: missing \"pagesize\" property\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
- if (of_property_read_u32(np, "at25,addr-mode", &val) == 0) {
|
|
+ if (device_property_read_u32(dev, "at25,addr-mode", &val) == 0) {
|
|
chip->flags = (u16)val;
|
|
} else {
|
|
- if (of_property_read_u32(np, "address-width", &val)) {
|
|
+ if (device_property_read_u32(dev, "address-width", &val)) {
|
|
dev_err(dev,
|
|
"Error: missing \"address-width\" property\n");
|
|
return -ENODEV;
|
|
@@ -350,7 +348,7 @@ static int at25_np_to_chip(struct device *dev,
|
|
val);
|
|
return -ENODEV;
|
|
}
|
|
- if (of_find_property(np, "read-only", NULL))
|
|
+ if (!device_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 <linux/of.h>
|
|
#include <linux/of_net.h>
|
|
#include <linux/clk.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#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 <linux/mdio.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/of.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#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 <linux/prefetch.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/phy.h>
|
|
+#include <linux/acpi.h>
|
|
#include "xgene_enet_hw.h"
|
|
|
|
#define XGENE_DRV_VERSION "v1.0"
|
|
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
|
|
index bcaa41a..ebad872 100644
|
|
--- a/drivers/net/ethernet/smsc/smc91x.c
|
|
+++ b/drivers/net/ethernet/smsc/smc91x.c
|
|
@@ -81,6 +81,7 @@ static const char version[] =
|
|
#include <linux/workqueue.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
@@ -2408,6 +2409,14 @@ static struct dev_pm_ops smc_drv_pm_ops = {
|
|
.resume = smc_drv_resume,
|
|
};
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+static const struct acpi_device_id smc91x_acpi_match[] = {
|
|
+ { "LNRO0003", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(acpi, smc91x_acpi_match);
|
|
+#endif
|
|
+
|
|
static struct platform_driver smc_driver = {
|
|
.probe = smc_drv_probe,
|
|
.remove = smc_drv_remove,
|
|
@@ -2416,6 +2425,7 @@ static struct platform_driver smc_driver = {
|
|
.owner = THIS_MODULE,
|
|
.pm = &smc_drv_pm_ops,
|
|
.of_match_table = of_match_ptr(smc91x_match),
|
|
+ .acpi_match_table = ACPI_PTR(smc91x_acpi_match),
|
|
},
|
|
};
|
|
|
|
diff --git a/drivers/net/phy/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 <linux/of_platform.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/uaccess.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
|
|
MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
-MODULE_VERSION("1.0.0-a");
|
|
+MODULE_VERSION("0.0.0-a");
|
|
MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
|
|
|
|
-#define XGBE_PHY_ID 0x000162d0
|
|
+#define XGBE_PHY_ID 0x7996ced0
|
|
#define XGBE_PHY_MASK 0xfffffff0
|
|
|
|
+#define XGBE_PHY_SERDES_RETRY 32
|
|
+#define XGBE_PHY_CHANNEL_PROPERTY "amd,serdes-channel"
|
|
#define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set"
|
|
|
|
#define XGBE_AN_INT_CMPLT 0x01
|
|
@@ -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 <linux/module.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/pci_regs.h>
|
|
+#include <linux/sizes.h>
|
|
+#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
|
|
/* Max address size we deal with */
|
|
@@ -293,6 +295,51 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
|
|
}
|
|
EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
|
|
|
|
+/*
|
|
+ * of_pci_range_to_resource - Create a resource from an of_pci_range
|
|
+ * @range: the PCI range that describes the resource
|
|
+ * @np: device node where the range belongs to
|
|
+ * @res: pointer to a valid resource that will be updated to
|
|
+ * reflect the values contained in the range.
|
|
+ *
|
|
+ * Returns EINVAL if the range cannot be converted to resource.
|
|
+ *
|
|
+ * Note that if the range is an IO range, the resource will be converted
|
|
+ * using pci_address_to_pio() which can fail if it is called too early or
|
|
+ * if the range cannot be matched to any host bridge IO space (our case here).
|
|
+ * To guard against that we try to register the IO range first.
|
|
+ * If that fails we know that pci_address_to_pio() will do too.
|
|
+ */
|
|
+int of_pci_range_to_resource(struct of_pci_range *range,
|
|
+ struct device_node *np, struct resource *res)
|
|
+{
|
|
+ int err;
|
|
+ res->flags = range->flags;
|
|
+ res->parent = res->child = res->sibling = NULL;
|
|
+ res->name = np->full_name;
|
|
+
|
|
+ if (res->flags & IORESOURCE_IO) {
|
|
+ unsigned long port;
|
|
+ err = pci_register_io_range(range->cpu_addr, range->size);
|
|
+ if (err)
|
|
+ goto invalid_range;
|
|
+ port = pci_address_to_pio(range->cpu_addr);
|
|
+ if (port == (unsigned long)-1) {
|
|
+ err = -EINVAL;
|
|
+ goto invalid_range;
|
|
+ }
|
|
+ res->start = port;
|
|
+ } else {
|
|
+ res->start = range->cpu_addr;
|
|
+ }
|
|
+ res->end = res->start + range->size - 1;
|
|
+ return 0;
|
|
+
|
|
+invalid_range:
|
|
+ res->start = (resource_size_t)OF_BAD_ADDR;
|
|
+ res->end = (resource_size_t)OF_BAD_ADDR;
|
|
+ return err;
|
|
+}
|
|
#endif /* CONFIG_PCI */
|
|
|
|
/*
|
|
@@ -601,12 +648,119 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
|
|
}
|
|
EXPORT_SYMBOL(of_get_address);
|
|
|
|
+#ifdef PCI_IOBASE
|
|
+struct io_range {
|
|
+ struct list_head list;
|
|
+ phys_addr_t start;
|
|
+ resource_size_t size;
|
|
+};
|
|
+
|
|
+static LIST_HEAD(io_range_list);
|
|
+static DEFINE_SPINLOCK(io_range_lock);
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Record the PCI IO range (expressed as CPU physical address + size).
|
|
+ * Return a negative value if an error has occured, zero otherwise
|
|
+ */
|
|
+int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
|
|
+{
|
|
+ int err = 0;
|
|
+
|
|
+#ifdef PCI_IOBASE
|
|
+ struct io_range *range;
|
|
+ resource_size_t allocated_size = 0;
|
|
+
|
|
+ /* check if the range hasn't been previously recorded */
|
|
+ spin_lock(&io_range_lock);
|
|
+ list_for_each_entry(range, &io_range_list, list) {
|
|
+ if (addr >= range->start && addr + size <= range->start + size) {
|
|
+ /* range already registered, bail out */
|
|
+ goto end_register;
|
|
+ }
|
|
+ allocated_size += range->size;
|
|
+ }
|
|
+
|
|
+ /* range not registed yet, check for available space */
|
|
+ if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
|
|
+ /* if it's too big check if 64K space can be reserved */
|
|
+ if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
|
|
+ err = -E2BIG;
|
|
+ goto end_register;
|
|
+ }
|
|
+
|
|
+ size = SZ_64K;
|
|
+ pr_warn("Requested IO range too big, new size set to 64K\n");
|
|
+ }
|
|
+
|
|
+ /* add the range to the list */
|
|
+ range = kzalloc(sizeof(*range), GFP_KERNEL);
|
|
+ if (!range) {
|
|
+ err = -ENOMEM;
|
|
+ goto end_register;
|
|
+ }
|
|
+
|
|
+ range->start = addr;
|
|
+ range->size = size;
|
|
+
|
|
+ list_add_tail(&range->list, &io_range_list);
|
|
+
|
|
+end_register:
|
|
+ spin_unlock(&io_range_lock);
|
|
+#endif
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+phys_addr_t pci_pio_to_address(unsigned long pio)
|
|
+{
|
|
+ phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
|
|
+
|
|
+#ifdef PCI_IOBASE
|
|
+ struct io_range *range;
|
|
+ resource_size_t allocated_size = 0;
|
|
+
|
|
+ if (pio > IO_SPACE_LIMIT)
|
|
+ return address;
|
|
+
|
|
+ spin_lock(&io_range_lock);
|
|
+ list_for_each_entry(range, &io_range_list, list) {
|
|
+ if (pio >= allocated_size && pio < allocated_size + range->size) {
|
|
+ address = range->start + pio - allocated_size;
|
|
+ break;
|
|
+ }
|
|
+ allocated_size += range->size;
|
|
+ }
|
|
+ spin_unlock(&io_range_lock);
|
|
+#endif
|
|
+
|
|
+ return address;
|
|
+}
|
|
+
|
|
unsigned long __weak pci_address_to_pio(phys_addr_t address)
|
|
{
|
|
+#ifdef PCI_IOBASE
|
|
+ struct io_range *res;
|
|
+ resource_size_t offset = 0;
|
|
+ unsigned long addr = -1;
|
|
+
|
|
+ spin_lock(&io_range_lock);
|
|
+ list_for_each_entry(res, &io_range_list, list) {
|
|
+ if (address >= res->start && address < res->start + res->size) {
|
|
+ addr = res->start - address + offset;
|
|
+ break;
|
|
+ }
|
|
+ offset += res->size;
|
|
+ }
|
|
+ spin_unlock(&io_range_lock);
|
|
+
|
|
+ return addr;
|
|
+#else
|
|
if (address > IO_SPACE_LIMIT)
|
|
return (unsigned long)-1;
|
|
|
|
return (unsigned long) address;
|
|
+#endif
|
|
}
|
|
|
|
static int __of_address_to_resource(struct device_node *dev,
|
|
diff --git a/drivers/of/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 <linux/kernel.h>
|
|
#include <linux/export.h>
|
|
#include <linux/of.h>
|
|
+#include <linux/of_address.h>
|
|
#include <linux/of_pci.h>
|
|
+#include <linux/slab.h>
|
|
|
|
static inline int __of_pci_pci_compare(struct device_node *node,
|
|
unsigned int data)
|
|
@@ -89,6 +91,146 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res)
|
|
}
|
|
EXPORT_SYMBOL_GPL(of_pci_parse_bus_range);
|
|
|
|
+/**
|
|
+ * This function will try to obtain the host bridge domain number by
|
|
+ * finding a property called "linux,pci-domain" of the given device node.
|
|
+ *
|
|
+ * @node: device tree node with the domain information
|
|
+ *
|
|
+ * Returns the associated domain number from DT in the range [0-0xffff], or
|
|
+ * a negative value if the required property is not found.
|
|
+ */
|
|
+int of_get_pci_domain_nr(struct device_node *node)
|
|
+{
|
|
+ const __be32 *value;
|
|
+ int len;
|
|
+ u16 domain;
|
|
+
|
|
+ value = of_get_property(node, "linux,pci-domain", &len);
|
|
+ if (!value || len < sizeof(*value))
|
|
+ return -EINVAL;
|
|
+
|
|
+ domain = (u16)be32_to_cpup(value);
|
|
+
|
|
+ return domain;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(of_get_pci_domain_nr);
|
|
+
|
|
+#if defined(CONFIG_OF_ADDRESS)
|
|
+/**
|
|
+ * of_pci_get_host_bridge_resources - Parse PCI host bridge resources from DT
|
|
+ * @dev: device node of the host bridge having the range property
|
|
+ * @busno: bus number associated with the bridge root bus
|
|
+ * @bus_max: maximum number of buses for this bridge
|
|
+ * @resources: list where the range of resources will be added after DT parsing
|
|
+ * @io_base: pointer to a variable that will contain on return the physical
|
|
+ * address for the start of the I/O range. Can be NULL if the caller doesn't
|
|
+ * expect IO ranges to be present in the device tree.
|
|
+ *
|
|
+ * It is the caller's job to free the @resources list.
|
|
+ *
|
|
+ * This function will parse the "ranges" property of a PCI host bridge device
|
|
+ * node and setup the resource mapping based on its content. It is expected
|
|
+ * that the property conforms with the Power ePAPR document.
|
|
+ *
|
|
+ * It returns zero if the range parsing has been successful or a standard error
|
|
+ * value if it failed.
|
|
+ */
|
|
+int of_pci_get_host_bridge_resources(struct device_node *dev,
|
|
+ unsigned char busno, unsigned char bus_max,
|
|
+ struct list_head *resources, resource_size_t *io_base)
|
|
+{
|
|
+ struct resource *res;
|
|
+ struct resource *bus_range;
|
|
+ struct of_pci_range range;
|
|
+ struct of_pci_range_parser parser;
|
|
+ char range_type[4];
|
|
+ int err;
|
|
+
|
|
+ if (io_base)
|
|
+ *io_base = (resource_size_t)OF_BAD_ADDR;
|
|
+
|
|
+ bus_range = kzalloc(sizeof(*bus_range), GFP_KERNEL);
|
|
+ if (!bus_range)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ pr_info("PCI host bridge %s ranges:\n", dev->full_name);
|
|
+
|
|
+ err = of_pci_parse_bus_range(dev, bus_range);
|
|
+ if (err) {
|
|
+ bus_range->start = busno;
|
|
+ bus_range->end = bus_max;
|
|
+ bus_range->flags = IORESOURCE_BUS;
|
|
+ pr_info(" No bus range found for %s, using %pR\n",
|
|
+ dev->full_name, bus_range);
|
|
+ } else {
|
|
+ if (bus_range->end > bus_range->start + bus_max)
|
|
+ bus_range->end = bus_range->start + bus_max;
|
|
+ }
|
|
+ pci_add_resource(resources, bus_range);
|
|
+
|
|
+ /* Check for ranges property */
|
|
+ err = of_pci_range_parser_init(&parser, dev);
|
|
+ if (err)
|
|
+ goto parse_failed;
|
|
+
|
|
+ pr_debug("Parsing ranges property...\n");
|
|
+ for_each_of_pci_range(&parser, &range) {
|
|
+ /* Read next ranges element */
|
|
+ if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_IO)
|
|
+ snprintf(range_type, 4, " IO");
|
|
+ else if ((range.flags & IORESOURCE_TYPE_BITS) == IORESOURCE_MEM)
|
|
+ snprintf(range_type, 4, "MEM");
|
|
+ else
|
|
+ snprintf(range_type, 4, "err");
|
|
+ pr_info(" %s %#010llx..%#010llx -> %#010llx\n", range_type,
|
|
+ range.cpu_addr, range.cpu_addr + range.size - 1,
|
|
+ range.pci_addr);
|
|
+
|
|
+ /*
|
|
+ * If we failed translation or got a zero-sized region
|
|
+ * then skip this range
|
|
+ */
|
|
+ if (range.cpu_addr == OF_BAD_ADDR || range.size == 0)
|
|
+ continue;
|
|
+
|
|
+ res = kzalloc(sizeof(struct resource), GFP_KERNEL);
|
|
+ if (!res) {
|
|
+ err = -ENOMEM;
|
|
+ goto parse_failed;
|
|
+ }
|
|
+
|
|
+ err = of_pci_range_to_resource(&range, dev, res);
|
|
+ if (err)
|
|
+ goto conversion_failed;
|
|
+
|
|
+ if (resource_type(res) == IORESOURCE_IO) {
|
|
+ if (!io_base) {
|
|
+ pr_err("I/O range found for %s. Please provide an io_base pointer to save CPU base address\n",
|
|
+ dev->full_name);
|
|
+ err = -EINVAL;
|
|
+ goto conversion_failed;
|
|
+ }
|
|
+ if (*io_base != (resource_size_t)OF_BAD_ADDR)
|
|
+ pr_warn("More than one I/O resource converted for %s. CPU base address for old range lost!\n",
|
|
+ dev->full_name);
|
|
+ *io_base = range.cpu_addr;
|
|
+ }
|
|
+
|
|
+ pci_add_resource_offset(resources, res, res->start - range.pci_addr);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+conversion_failed:
|
|
+ kfree(res);
|
|
+parse_failed:
|
|
+ pci_free_resource_list(resources);
|
|
+ return err;
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
|
|
+#endif /* CONFIG_OF_ADDRESS */
|
|
+
|
|
#ifdef CONFIG_PCI_MSI
|
|
|
|
static LIST_HEAD(of_pci_msi_chip_list);
|
|
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
|
|
index 90f5cca..382fd3d 100644
|
|
--- a/drivers/pci/host/Kconfig
|
|
+++ b/drivers/pci/host/Kconfig
|
|
@@ -63,4 +63,14 @@ config PCIE_SPEAR13XX
|
|
help
|
|
Say Y here if you want PCIe support on SPEAr13XX SoCs.
|
|
|
|
+config PCI_XGENE
|
|
+ bool "X-Gene PCIe controller"
|
|
+ depends on ARCH_XGENE
|
|
+ depends on OF
|
|
+ select PCIEPORTBUS
|
|
+ help
|
|
+ Say Y here if you want internal PCI support on APM X-Gene SoC.
|
|
+ There are 5 internal PCIe ports available. Each port is GEN3 capable
|
|
+ and have varied lanes from x1 to x8.
|
|
+
|
|
endmenu
|
|
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
|
|
index d0e88f1..845611f 100644
|
|
--- a/drivers/pci/host/Makefile
|
|
+++ b/drivers/pci/host/Makefile
|
|
@@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
|
|
obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
|
|
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
|
|
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
|
|
+obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
|
|
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
|
|
index 0fb0fdb..946935d 100644
|
|
--- a/drivers/pci/host/pci-tegra.c
|
|
+++ b/drivers/pci/host/pci-tegra.c
|
|
@@ -626,13 +626,14 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
|
|
static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
|
|
{
|
|
struct tegra_pcie *pcie = sys_to_pcie(sys);
|
|
+ phys_addr_t io_start = pci_pio_to_address(pcie->io.start);
|
|
|
|
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
|
|
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
|
|
sys->mem_offset);
|
|
pci_add_resource(&sys->resources, &pcie->busn);
|
|
|
|
- pci_ioremap_io(nr * SZ_64K, pcie->io.start);
|
|
+ pci_ioremap_io(nr * SZ_64K, io_start);
|
|
|
|
return 1;
|
|
}
|
|
@@ -737,6 +738,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
|
|
static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
|
|
{
|
|
u32 fpci_bar, size, axi_address;
|
|
+ phys_addr_t io_start = pci_pio_to_address(pcie->io.start);
|
|
|
|
/* Bar 0: type 1 extended configuration space */
|
|
fpci_bar = 0xfe100000;
|
|
@@ -749,7 +751,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
|
|
/* Bar 1: downstream IO bar */
|
|
fpci_bar = 0xfdfc0000;
|
|
size = resource_size(&pcie->io);
|
|
- axi_address = pcie->io.start;
|
|
+ axi_address = io_start;
|
|
afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
|
|
afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
|
|
afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
|
|
@@ -1520,7 +1522,9 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
|
|
}
|
|
|
|
for_each_of_pci_range(&parser, &range) {
|
|
- of_pci_range_to_resource(&range, np, &res);
|
|
+ err = of_pci_range_to_resource(&range, np, &res);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
|
|
switch (res.flags & IORESOURCE_TYPE_BITS) {
|
|
case IORESOURCE_IO:
|
|
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
|
|
new file mode 100644
|
|
index 0000000..41c76b8
|
|
--- /dev/null
|
|
+++ b/drivers/pci/host/pci-xgene.c
|
|
@@ -0,0 +1,659 @@
|
|
+/**
|
|
+ * APM X-Gene PCIe Driver
|
|
+ *
|
|
+ * Copyright (c) 2014 Applied Micro Circuits Corporation.
|
|
+ *
|
|
+ * Author: Tanmay Inamdar <tinamdar@apm.com>.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU General Public License as published by the
|
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
|
+ * option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ */
|
|
+#include <linux/clk-private.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/jiffies.h>
|
|
+#include <linux/memblock.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_irq.h>
|
|
+#include <linux/of_pci.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/slab.h>
|
|
+
|
|
+#define PCIECORE_CTLANDSTATUS 0x50
|
|
+#define PIM1_1L 0x80
|
|
+#define IBAR2 0x98
|
|
+#define IR2MSK 0x9c
|
|
+#define PIM2_1L 0xa0
|
|
+#define IBAR3L 0xb4
|
|
+#define IR3MSKL 0xbc
|
|
+#define PIM3_1L 0xc4
|
|
+#define OMR1BARL 0x100
|
|
+#define OMR2BARL 0x118
|
|
+#define OMR3BARL 0x130
|
|
+#define CFGBARL 0x154
|
|
+#define CFGBARH 0x158
|
|
+#define CFGCTL 0x15c
|
|
+#define RTDID 0x160
|
|
+#define BRIDGE_CFG_0 0x2000
|
|
+#define BRIDGE_CFG_4 0x2010
|
|
+#define BRIDGE_STATUS_0 0x2600
|
|
+
|
|
+#define LINK_UP_MASK 0x00000100
|
|
+#define AXI_EP_CFG_ACCESS 0x10000
|
|
+#define EN_COHERENCY 0xF0000000
|
|
+#define EN_REG 0x00000001
|
|
+#define OB_LO_IO 0x00000002
|
|
+#define XGENE_PCIE_VENDORID 0x10E8
|
|
+#define XGENE_PCIE_DEVICEID 0xE004
|
|
+#define SZ_1T (SZ_1G*1024ULL)
|
|
+#define PIPE_PHY_RATE_RD(src) ((0xc000 & (u32)(src)) >> 0xe)
|
|
+
|
|
+struct xgene_pcie_port {
|
|
+ struct device_node *node;
|
|
+ struct device *dev;
|
|
+ struct clk *clk;
|
|
+ void __iomem *csr_base;
|
|
+ void __iomem *cfg_base;
|
|
+ unsigned long cfg_addr;
|
|
+ bool link_up;
|
|
+};
|
|
+
|
|
+static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
|
|
+{
|
|
+ return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
|
|
+}
|
|
+
|
|
+/* PCIE Configuration Out/In */
|
|
+static inline void xgene_pcie_cfg_out32(void __iomem *addr, int offset, u32 val)
|
|
+{
|
|
+ writel(val, addr + offset);
|
|
+}
|
|
+
|
|
+static inline void xgene_pcie_cfg_out16(void __iomem *addr, int offset, u16 val)
|
|
+{
|
|
+ u32 val32 = readl(addr + (offset & ~0x3));
|
|
+
|
|
+ switch (offset & 0x3) {
|
|
+ case 2:
|
|
+ val32 &= ~0xFFFF0000;
|
|
+ val32 |= (u32)val << 16;
|
|
+ break;
|
|
+ case 0:
|
|
+ default:
|
|
+ val32 &= ~0xFFFF;
|
|
+ val32 |= val;
|
|
+ break;
|
|
+ }
|
|
+ writel(val32, addr + (offset & ~0x3));
|
|
+}
|
|
+
|
|
+static inline void xgene_pcie_cfg_out8(void __iomem *addr, int offset, u8 val)
|
|
+{
|
|
+ u32 val32 = readl(addr + (offset & ~0x3));
|
|
+
|
|
+ switch (offset & 0x3) {
|
|
+ case 0:
|
|
+ val32 &= ~0xFF;
|
|
+ val32 |= val;
|
|
+ break;
|
|
+ case 1:
|
|
+ val32 &= ~0xFF00;
|
|
+ val32 |= (u32)val << 8;
|
|
+ break;
|
|
+ case 2:
|
|
+ val32 &= ~0xFF0000;
|
|
+ val32 |= (u32)val << 16;
|
|
+ break;
|
|
+ case 3:
|
|
+ default:
|
|
+ val32 &= ~0xFF000000;
|
|
+ val32 |= (u32)val << 24;
|
|
+ break;
|
|
+ }
|
|
+ writel(val32, addr + (offset & ~0x3));
|
|
+}
|
|
+
|
|
+static inline void xgene_pcie_cfg_in32(void __iomem *addr, int offset, u32 *val)
|
|
+{
|
|
+ *val = readl(addr + offset);
|
|
+}
|
|
+
|
|
+static inline void xgene_pcie_cfg_in16(void __iomem *addr, int offset, u32 *val)
|
|
+{
|
|
+ *val = readl(addr + (offset & ~0x3));
|
|
+
|
|
+ switch (offset & 0x3) {
|
|
+ case 2:
|
|
+ *val >>= 16;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ *val &= 0xFFFF;
|
|
+}
|
|
+
|
|
+static inline void xgene_pcie_cfg_in8(void __iomem *addr, int offset, u32 *val)
|
|
+{
|
|
+ *val = readl(addr + (offset & ~0x3));
|
|
+
|
|
+ switch (offset & 0x3) {
|
|
+ case 3:
|
|
+ *val = *val >> 24;
|
|
+ break;
|
|
+ case 2:
|
|
+ *val = *val >> 16;
|
|
+ break;
|
|
+ case 1:
|
|
+ *val = *val >> 8;
|
|
+ break;
|
|
+ }
|
|
+ *val &= 0xFF;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * When the address bit [17:16] is 2'b01, the Configuration access will be
|
|
+ * treated as Type 1 and it will be forwarded to external PCIe device.
|
|
+ */
|
|
+static void __iomem *xgene_pcie_get_cfg_base(struct pci_bus *bus)
|
|
+{
|
|
+ struct xgene_pcie_port *port = bus->sysdata;
|
|
+
|
|
+ if (bus->number >= (bus->primary + 1))
|
|
+ return port->cfg_base + AXI_EP_CFG_ACCESS;
|
|
+
|
|
+ return port->cfg_base;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * For Configuration request, RTDID register is used as Bus Number,
|
|
+ * Device Number and Function number of the header fields.
|
|
+ */
|
|
+static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
|
|
+{
|
|
+ struct xgene_pcie_port *port = bus->sysdata;
|
|
+ unsigned int b, d, f;
|
|
+ u32 rtdid_val = 0;
|
|
+
|
|
+ b = bus->number;
|
|
+ d = PCI_SLOT(devfn);
|
|
+ f = PCI_FUNC(devfn);
|
|
+
|
|
+ if (!pci_is_root_bus(bus))
|
|
+ rtdid_val = (b << 8) | (d << 3) | f;
|
|
+
|
|
+ writel(rtdid_val, port->csr_base + RTDID);
|
|
+ /* read the register back to ensure flush */
|
|
+ readl(port->csr_base + RTDID);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * X-Gene PCIe port uses BAR0-BAR1 of RC's configuration space as
|
|
+ * the translation between PCI bus to native BUS. Entire DDR region
|
|
+ * is mapped on to PCIe space using these registers, so that it is
|
|
+ * accessible for EP devices for DMA. The BAR0/1 of bridge should be
|
|
+ * hidden during enumeration to avoid the sizing and resource allocation
|
|
+ * by PCIe core.
|
|
+ */
|
|
+static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset)
|
|
+{
|
|
+ if (pci_is_root_bus(bus) && ((offset == PCI_BASE_ADDRESS_0) ||
|
|
+ (offset == PCI_BASE_ADDRESS_1)))
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static int xgene_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
|
|
+ int offset, int len, u32 *val)
|
|
+{
|
|
+ struct xgene_pcie_port *port = bus->sysdata;
|
|
+ void __iomem *addr;
|
|
+
|
|
+ if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up)
|
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
|
+
|
|
+ if (xgene_pcie_hide_rc_bars(bus, offset)) {
|
|
+ *val = 0;
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
+ }
|
|
+
|
|
+ xgene_pcie_set_rtdid_reg(bus, devfn);
|
|
+ addr = xgene_pcie_get_cfg_base(bus);
|
|
+ switch (len) {
|
|
+ case 1:
|
|
+ xgene_pcie_cfg_in8(addr, offset, val);
|
|
+ break;
|
|
+ case 2:
|
|
+ xgene_pcie_cfg_in16(addr, offset, val);
|
|
+ break;
|
|
+ default:
|
|
+ xgene_pcie_cfg_in32(addr, offset, val);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
+}
|
|
+
|
|
+static int xgene_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
|
|
+ int offset, int len, u32 val)
|
|
+{
|
|
+ struct xgene_pcie_port *port = bus->sysdata;
|
|
+ void __iomem *addr;
|
|
+
|
|
+ if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up)
|
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
|
+
|
|
+ if (xgene_pcie_hide_rc_bars(bus, offset))
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
+
|
|
+ xgene_pcie_set_rtdid_reg(bus, devfn);
|
|
+ addr = xgene_pcie_get_cfg_base(bus);
|
|
+ switch (len) {
|
|
+ case 1:
|
|
+ xgene_pcie_cfg_out8(addr, offset, (u8)val);
|
|
+ break;
|
|
+ case 2:
|
|
+ xgene_pcie_cfg_out16(addr, offset, (u16)val);
|
|
+ break;
|
|
+ default:
|
|
+ xgene_pcie_cfg_out32(addr, offset, val);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
+}
|
|
+
|
|
+static struct pci_ops xgene_pcie_ops = {
|
|
+ .read = xgene_pcie_read_config,
|
|
+ .write = xgene_pcie_write_config
|
|
+};
|
|
+
|
|
+static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr,
|
|
+ u32 flags, u64 size)
|
|
+{
|
|
+ u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags;
|
|
+ u32 val32 = 0;
|
|
+ u32 val;
|
|
+
|
|
+ val32 = readl(csr_base + addr);
|
|
+ val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16);
|
|
+ writel(val, csr_base + addr);
|
|
+
|
|
+ val32 = readl(csr_base + addr + 0x04);
|
|
+ val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16);
|
|
+ writel(val, csr_base + addr + 0x04);
|
|
+
|
|
+ val32 = readl(csr_base + addr + 0x04);
|
|
+ val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16);
|
|
+ writel(val, csr_base + addr + 0x04);
|
|
+
|
|
+ val32 = readl(csr_base + addr + 0x08);
|
|
+ val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16);
|
|
+ writel(val, csr_base + addr + 0x08);
|
|
+
|
|
+ return mask;
|
|
+}
|
|
+
|
|
+static void xgene_pcie_linkup(struct xgene_pcie_port *port,
|
|
+ u32 *lanes, u32 *speed)
|
|
+{
|
|
+ void __iomem *csr_base = port->csr_base;
|
|
+ u32 val32;
|
|
+
|
|
+ port->link_up = false;
|
|
+ val32 = readl(csr_base + PCIECORE_CTLANDSTATUS);
|
|
+ if (val32 & LINK_UP_MASK) {
|
|
+ port->link_up = true;
|
|
+ *speed = PIPE_PHY_RATE_RD(val32);
|
|
+ val32 = readl(csr_base + BRIDGE_STATUS_0);
|
|
+ *lanes = val32 >> 26;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int xgene_pcie_init_port(struct xgene_pcie_port *port)
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ port->clk = clk_get(port->dev, NULL);
|
|
+ if (IS_ERR(port->clk)) {
|
|
+ dev_err(port->dev, "clock not available\n");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ rc = clk_prepare_enable(port->clk);
|
|
+ if (rc) {
|
|
+ dev_err(port->dev, "clock enable failed\n");
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int xgene_pcie_map_reg(struct xgene_pcie_port *port,
|
|
+ struct platform_device *pdev)
|
|
+{
|
|
+ struct resource *res;
|
|
+
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
|
|
+ port->csr_base = devm_ioremap_resource(port->dev, res);
|
|
+ if (IS_ERR(port->csr_base))
|
|
+ return PTR_ERR(port->csr_base);
|
|
+
|
|
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
|
|
+ port->cfg_base = devm_ioremap_resource(port->dev, res);
|
|
+ if (IS_ERR(port->cfg_base))
|
|
+ return PTR_ERR(port->cfg_base);
|
|
+ port->cfg_addr = res->start;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port,
|
|
+ struct resource *res, u32 offset,
|
|
+ u64 cpu_addr, u64 pci_addr)
|
|
+{
|
|
+ void __iomem *base = port->csr_base + offset;
|
|
+ resource_size_t size = resource_size(res);
|
|
+ u64 restype = resource_type(res);
|
|
+ u64 mask = 0;
|
|
+ u32 min_size;
|
|
+ u32 flag = EN_REG;
|
|
+
|
|
+ if (restype == IORESOURCE_MEM) {
|
|
+ min_size = SZ_128M;
|
|
+ } else {
|
|
+ min_size = 128;
|
|
+ flag |= OB_LO_IO;
|
|
+ }
|
|
+
|
|
+ if (size >= min_size)
|
|
+ mask = ~(size - 1) | flag;
|
|
+ else
|
|
+ dev_warn(port->dev, "res size 0x%llx less than minimum 0x%x\n",
|
|
+ (u64)size, min_size);
|
|
+
|
|
+ writel(lower_32_bits(cpu_addr), base);
|
|
+ writel(upper_32_bits(cpu_addr), base + 0x04);
|
|
+ writel(lower_32_bits(mask), base + 0x08);
|
|
+ writel(upper_32_bits(mask), base + 0x0c);
|
|
+ writel(lower_32_bits(pci_addr), base + 0x10);
|
|
+ writel(upper_32_bits(pci_addr), base + 0x14);
|
|
+}
|
|
+
|
|
+static void xgene_pcie_setup_cfg_reg(void __iomem *csr_base, u64 addr)
|
|
+{
|
|
+ writel(lower_32_bits(addr), csr_base + CFGBARL);
|
|
+ writel(upper_32_bits(addr), csr_base + CFGBARH);
|
|
+ writel(EN_REG, csr_base + CFGCTL);
|
|
+}
|
|
+
|
|
+static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
|
|
+ struct list_head *res,
|
|
+ resource_size_t io_base)
|
|
+{
|
|
+ struct pci_host_bridge_window *window;
|
|
+ struct device *dev = port->dev;
|
|
+ int ret;
|
|
+
|
|
+ list_for_each_entry(window, res, list) {
|
|
+ struct resource *res = window->res;
|
|
+ u64 restype = resource_type(res);
|
|
+
|
|
+ dev_dbg(port->dev, "%pR\n", res);
|
|
+
|
|
+ switch (restype) {
|
|
+ case IORESOURCE_IO:
|
|
+ xgene_pcie_setup_ob_reg(port, res, OMR3BARL, io_base,
|
|
+ res->start - window->offset);
|
|
+ ret = pci_remap_iospace(res, io_base);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ break;
|
|
+ case IORESOURCE_MEM:
|
|
+ xgene_pcie_setup_ob_reg(port, res, OMR1BARL, res->start,
|
|
+ res->start - window->offset);
|
|
+ break;
|
|
+ case IORESOURCE_BUS:
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(dev, "invalid io resource - %pR\n", res);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+ xgene_pcie_setup_cfg_reg(port->csr_base, port->cfg_addr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void xgene_pcie_setup_pims(void *addr, u64 pim, u64 size)
|
|
+{
|
|
+ writel(lower_32_bits(pim), addr);
|
|
+ writel(upper_32_bits(pim) | EN_COHERENCY, addr + 0x04);
|
|
+ writel(lower_32_bits(size), addr + 0x10);
|
|
+ writel(upper_32_bits(size), addr + 0x14);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * X-Gene PCIe support maximum 3 inbound memory regions
|
|
+ * This function helps to select a region based on size of region
|
|
+ */
|
|
+static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size)
|
|
+{
|
|
+ if ((size > 4) && (size < SZ_16M) && !(*ib_reg_mask & (1 << 1))) {
|
|
+ *ib_reg_mask |= (1 << 1);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if ((size > SZ_1K) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 0))) {
|
|
+ *ib_reg_mask |= (1 << 0);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if ((size > SZ_1M) && (size < SZ_1T) && !(*ib_reg_mask & (1 << 2))) {
|
|
+ *ib_reg_mask |= (1 << 2);
|
|
+ return 2;
|
|
+ }
|
|
+
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
|
|
+ struct of_pci_range *range, u8 *ib_reg_mask)
|
|
+{
|
|
+ void __iomem *csr_base = port->csr_base;
|
|
+ void __iomem *cfg_base = port->cfg_base;
|
|
+ void *bar_addr;
|
|
+ void *pim_addr;
|
|
+ u64 cpu_addr = range->cpu_addr;
|
|
+ u64 pci_addr = range->pci_addr;
|
|
+ u64 size = range->size;
|
|
+ u64 mask = ~(size - 1) | EN_REG;
|
|
+ u32 flags = PCI_BASE_ADDRESS_MEM_TYPE_64;
|
|
+ u32 bar_low;
|
|
+ int region;
|
|
+
|
|
+ region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size);
|
|
+ if (region < 0) {
|
|
+ dev_warn(port->dev, "invalid pcie dma-range config\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (range->flags & IORESOURCE_PREFETCH)
|
|
+ flags |= PCI_BASE_ADDRESS_MEM_PREFETCH;
|
|
+
|
|
+ bar_low = pcie_bar_low_val((u32)cpu_addr, flags);
|
|
+ switch (region) {
|
|
+ case 0:
|
|
+ xgene_pcie_set_ib_mask(csr_base, BRIDGE_CFG_4, flags, size);
|
|
+ bar_addr = cfg_base + PCI_BASE_ADDRESS_0;
|
|
+ writel(bar_low, bar_addr);
|
|
+ writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
|
|
+ pim_addr = csr_base + PIM1_1L;
|
|
+ break;
|
|
+ case 1:
|
|
+ bar_addr = csr_base + IBAR2;
|
|
+ writel(bar_low, bar_addr);
|
|
+ writel(lower_32_bits(mask), csr_base + IR2MSK);
|
|
+ pim_addr = csr_base + PIM2_1L;
|
|
+ break;
|
|
+ case 2:
|
|
+ bar_addr = csr_base + IBAR3L;
|
|
+ writel(bar_low, bar_addr);
|
|
+ writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
|
|
+ writel(lower_32_bits(mask), csr_base + IR3MSKL);
|
|
+ writel(upper_32_bits(mask), csr_base + IR3MSKL + 0x4);
|
|
+ pim_addr = csr_base + PIM3_1L;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ xgene_pcie_setup_pims(pim_addr, pci_addr, ~(size - 1));
|
|
+}
|
|
+
|
|
+static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
|
|
+ struct device_node *node)
|
|
+{
|
|
+ const int na = 3, ns = 2;
|
|
+ int rlen;
|
|
+
|
|
+ parser->node = node;
|
|
+ parser->pna = of_n_addr_cells(node);
|
|
+ parser->np = parser->pna + na + ns;
|
|
+
|
|
+ parser->range = of_get_property(node, "dma-ranges", &rlen);
|
|
+ if (!parser->range)
|
|
+ return -ENOENT;
|
|
+ parser->end = parser->range + rlen / sizeof(__be32);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port)
|
|
+{
|
|
+ struct device_node *np = port->node;
|
|
+ struct of_pci_range range;
|
|
+ struct of_pci_range_parser parser;
|
|
+ struct device *dev = port->dev;
|
|
+ u8 ib_reg_mask = 0;
|
|
+
|
|
+ if (pci_dma_range_parser_init(&parser, np)) {
|
|
+ dev_err(dev, "missing dma-ranges property\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Get the dma-ranges from DT */
|
|
+ for_each_of_pci_range(&parser, &range) {
|
|
+ u64 end = range.cpu_addr + range.size - 1;
|
|
+
|
|
+ dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
|
|
+ range.flags, range.cpu_addr, end, range.pci_addr);
|
|
+ xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* clear bar configuration which was done by firmware */
|
|
+static void xgene_pcie_clear_config(struct xgene_pcie_port *port)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = PIM1_1L; i <= CFGCTL; i += 4)
|
|
+ writel(0x0, port->csr_base + i);
|
|
+}
|
|
+
|
|
+static int xgene_pcie_setup(struct xgene_pcie_port *port,
|
|
+ struct list_head *res,
|
|
+ resource_size_t io_base)
|
|
+{
|
|
+ u32 val, lanes = 0, speed = 0;
|
|
+ int ret;
|
|
+
|
|
+ xgene_pcie_clear_config(port);
|
|
+
|
|
+ /* setup the vendor and device IDs correctly */
|
|
+ val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID;
|
|
+ writel(val, port->csr_base + BRIDGE_CFG_0);
|
|
+
|
|
+ ret = xgene_pcie_map_ranges(port, res, io_base);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = xgene_pcie_parse_map_dma_ranges(port);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ xgene_pcie_linkup(port, &lanes, &speed);
|
|
+ if (!port->link_up)
|
|
+ dev_info(port->dev, "(rc) link down\n");
|
|
+ else
|
|
+ dev_info(port->dev, "(rc) x%d gen-%d link up\n",
|
|
+ lanes, speed + 1);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int xgene_pcie_probe_bridge(struct platform_device *pdev)
|
|
+{
|
|
+ struct device_node *dn = pdev->dev.of_node;
|
|
+ struct xgene_pcie_port *port;
|
|
+ resource_size_t iobase = 0;
|
|
+ struct pci_bus *bus;
|
|
+ int ret;
|
|
+ LIST_HEAD(res);
|
|
+
|
|
+ port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
|
|
+ if (!port)
|
|
+ return -ENOMEM;
|
|
+ port->node = of_node_get(pdev->dev.of_node);
|
|
+ port->dev = &pdev->dev;
|
|
+
|
|
+ ret = xgene_pcie_map_reg(port, pdev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = xgene_pcie_init_port(port);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = of_pci_get_host_bridge_resources(dn, 0, 0xff, &res, &iobase);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = xgene_pcie_setup(port, &res, iobase);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res);
|
|
+ if (!bus)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ platform_set_drvdata(pdev, port);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id xgene_pcie_match_table[] = {
|
|
+ {.compatible = "apm,xgene-pcie",},
|
|
+ {},
|
|
+};
|
|
+
|
|
+static struct platform_driver xgene_pcie_driver = {
|
|
+ .driver = {
|
|
+ .name = "xgene-pcie",
|
|
+ .owner = THIS_MODULE,
|
|
+ .of_match_table = of_match_ptr(xgene_pcie_match_table),
|
|
+ },
|
|
+ .probe = xgene_pcie_probe_bridge,
|
|
+};
|
|
+module_platform_driver(xgene_pcie_driver);
|
|
+
|
|
+MODULE_AUTHOR("Tanmay Inamdar <tinamdar@apm.com>");
|
|
+MODULE_DESCRIPTION("APM X-Gene PCIe driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c
|
|
index 4884ee5..61158e0 100644
|
|
--- a/drivers/pci/host/pcie-rcar.c
|
|
+++ b/drivers/pci/host/pcie-rcar.c
|
|
@@ -323,6 +323,7 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
|
|
|
|
/* Setup PCIe address space mappings for each resource */
|
|
resource_size_t size;
|
|
+ resource_size_t res_start;
|
|
u32 mask;
|
|
|
|
rcar_pci_write_reg(pcie, 0x00000000, PCIEPTCTLR(win));
|
|
@@ -335,8 +336,13 @@ static void rcar_pcie_setup_window(int win, struct rcar_pcie *pcie)
|
|
mask = (roundup_pow_of_two(size) / SZ_128) - 1;
|
|
rcar_pci_write_reg(pcie, mask << 7, PCIEPAMR(win));
|
|
|
|
- rcar_pci_write_reg(pcie, upper_32_bits(res->start), PCIEPARH(win));
|
|
- rcar_pci_write_reg(pcie, lower_32_bits(res->start), PCIEPARL(win));
|
|
+ if (res->flags & IORESOURCE_IO)
|
|
+ res_start = pci_pio_to_address(res->start);
|
|
+ else
|
|
+ res_start = res->start;
|
|
+
|
|
+ rcar_pci_write_reg(pcie, upper_32_bits(res_start), PCIEPARH(win));
|
|
+ rcar_pci_write_reg(pcie, lower_32_bits(res_start), PCIEPARL(win));
|
|
|
|
/* First resource is for IO */
|
|
mask = PAR_ENABLE;
|
|
@@ -363,9 +369,10 @@ static int rcar_pcie_setup(int nr, struct pci_sys_data *sys)
|
|
|
|
rcar_pcie_setup_window(i, pcie);
|
|
|
|
- if (res->flags & IORESOURCE_IO)
|
|
- pci_ioremap_io(nr * SZ_64K, res->start);
|
|
- else
|
|
+ if (res->flags & IORESOURCE_IO) {
|
|
+ phys_addr_t io_start = pci_pio_to_address(res->start);
|
|
+ pci_ioremap_io(nr * SZ_64K, io_start);
|
|
+ } else
|
|
pci_add_resource(&sys->resources, res);
|
|
}
|
|
pci_add_resource(&sys->resources, &pcie->busn);
|
|
@@ -935,8 +942,10 @@ static int rcar_pcie_probe(struct platform_device *pdev)
|
|
}
|
|
|
|
for_each_of_pci_range(&parser, &range) {
|
|
- of_pci_range_to_resource(&range, pdev->dev.of_node,
|
|
+ err = of_pci_range_to_resource(&range, pdev->dev.of_node,
|
|
&pcie->res[win++]);
|
|
+ if (err < 0)
|
|
+ return err;
|
|
|
|
if (win > RCAR_PCI_MAX_RESOURCES)
|
|
break;
|
|
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
|
|
index 2c9ac70..6e994fc 100644
|
|
--- a/drivers/pci/pci.c
|
|
+++ b/drivers/pci/pci.c
|
|
@@ -2704,6 +2704,37 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
|
|
}
|
|
EXPORT_SYMBOL(pci_request_regions_exclusive);
|
|
|
|
+/**
|
|
+ * pci_remap_iospace - Remap the memory mapped I/O space
|
|
+ * @res: Resource describing the I/O space
|
|
+ * @phys_addr: physical address of range to be mapped
|
|
+ *
|
|
+ * Remap the memory mapped I/O space described by the @res
|
|
+ * and the CPU physical address @phys_addr into virtual address space.
|
|
+ * Only architectures that have memory mapped IO functions defined
|
|
+ * (and the PCI_IOBASE value defined) should call this function.
|
|
+ */
|
|
+int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
|
|
+{
|
|
+#if defined(PCI_IOBASE) && defined(CONFIG_MMU)
|
|
+ unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start;
|
|
+
|
|
+ if (!(res->flags & IORESOURCE_IO))
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (res->end > IO_SPACE_LIMIT)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return ioremap_page_range(vaddr, vaddr + resource_size(res), phys_addr,
|
|
+ pgprot_device(PAGE_KERNEL));
|
|
+#else
|
|
+ /* this architecture does not have memory mapped I/O space,
|
|
+ so this function should never be called */
|
|
+ WARN_ONCE(1, "This architecture does not support memory mapped I/O\n");
|
|
+ return -ENODEV;
|
|
+#endif
|
|
+}
|
|
+
|
|
static void __pci_set_master(struct pci_dev *dev, bool enable)
|
|
{
|
|
u16 old_cmd, cmd;
|
|
@@ -4406,6 +4437,15 @@ static void pci_no_domains(void)
|
|
#endif
|
|
}
|
|
|
|
+#ifdef CONFIG_PCI_DOMAINS
|
|
+static atomic_t __domain_nr = ATOMIC_INIT(-1);
|
|
+
|
|
+int pci_get_new_domain_nr(void)
|
|
+{
|
|
+ return atomic_inc_return(&__domain_nr);
|
|
+}
|
|
+#endif
|
|
+
|
|
/**
|
|
* pci_ext_cfg_avail - can we access extended PCI config space?
|
|
*
|
|
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
|
|
index 4170113..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 <graeme.gregory@linaro.org>
|
|
+ *
|
|
+ * This software is licensed under the terms of the GNU General Public
|
|
+ * License version 2, as published by the Free Software Foundation, and
|
|
+ * may be copied, distributed, and modified under those terms.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/amba/serial.h>
|
|
+#include <linux/console.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/serial_core.h>
|
|
+#include <linux/tty.h>
|
|
+#include <linux/tty_flip.h>
|
|
+
|
|
+struct sbsa_tty {
|
|
+ struct tty_port port;
|
|
+ spinlock_t lock;
|
|
+ void __iomem *base;
|
|
+ u32 irq;
|
|
+ int opencount;
|
|
+ struct console console;
|
|
+};
|
|
+
|
|
+static struct tty_driver *sbsa_tty_driver;
|
|
+static struct sbsa_tty *sbsa_tty;
|
|
+
|
|
+#define SBSAUART_CHAR_MASK 0xFF
|
|
+
|
|
+static void sbsa_raw_putc(struct uart_port *port, int c)
|
|
+{
|
|
+ while (readw(port->membase + UART01x_FR) & UART01x_FR_TXFF)
|
|
+ ;
|
|
+ writew(c & 0xFF, port->membase + UART01x_DR);
|
|
+}
|
|
+
|
|
+static void sbsa_uart_early_write(struct console *con, const char *buf,
|
|
+ unsigned count)
|
|
+{
|
|
+ struct earlycon_device *dev = con->data;
|
|
+
|
|
+ uart_console_write(&dev->port, buf, count, sbsa_raw_putc);
|
|
+}
|
|
+
|
|
+static int __init sbsa_uart_early_console_setup(struct earlycon_device *device,
|
|
+ const char *opt)
|
|
+{
|
|
+ if (!device->port.membase)
|
|
+ return -ENODEV;
|
|
+
|
|
+ device->con->write = sbsa_uart_early_write;
|
|
+ return 0;
|
|
+}
|
|
+EARLYCON_DECLARE(sbsauart, sbsa_uart_early_console_setup);
|
|
+
|
|
+static void sbsa_tty_do_write(const char *buf, unsigned count)
|
|
+{
|
|
+ unsigned long irq_flags;
|
|
+ struct sbsa_tty *qtty = sbsa_tty;
|
|
+ void __iomem *base = qtty->base;
|
|
+ unsigned n;
|
|
+
|
|
+ spin_lock_irqsave(&qtty->lock, irq_flags);
|
|
+ for (n = 0; n < count; n++) {
|
|
+ while (readw(base + UART01x_FR) & UART01x_FR_TXFF)
|
|
+ ;
|
|
+ writew(buf[n], base + UART01x_DR);
|
|
+ }
|
|
+ spin_unlock_irqrestore(&qtty->lock, irq_flags);
|
|
+}
|
|
+
|
|
+static void sbsauart_fifo_to_tty(struct sbsa_tty *qtty)
|
|
+{
|
|
+ void __iomem *base = qtty->base;
|
|
+ unsigned int flag, max_count = 32;
|
|
+ u16 status, ch;
|
|
+
|
|
+ while (max_count--) {
|
|
+ status = readw(base + UART01x_FR);
|
|
+ if (status & UART01x_FR_RXFE)
|
|
+ break;
|
|
+
|
|
+ /* Take chars from the FIFO and update status */
|
|
+ ch = readw(base + UART01x_DR);
|
|
+ flag = TTY_NORMAL;
|
|
+
|
|
+ if (ch & UART011_DR_BE)
|
|
+ flag = TTY_BREAK;
|
|
+ else if (ch & UART011_DR_PE)
|
|
+ flag = TTY_PARITY;
|
|
+ else if (ch & UART011_DR_FE)
|
|
+ flag = TTY_FRAME;
|
|
+ else if (ch & UART011_DR_OE)
|
|
+ flag = TTY_OVERRUN;
|
|
+
|
|
+ ch &= SBSAUART_CHAR_MASK;
|
|
+
|
|
+ tty_insert_flip_char(&qtty->port, ch, flag);
|
|
+ }
|
|
+
|
|
+ tty_schedule_flip(&qtty->port);
|
|
+
|
|
+ /* Clear the RX IRQ */
|
|
+ writew(UART011_RXIC | UART011_RXIC, base + UART011_ICR);
|
|
+}
|
|
+
|
|
+static irqreturn_t sbsa_tty_interrupt(int irq, void *dev_id)
|
|
+{
|
|
+ struct sbsa_tty *qtty = sbsa_tty;
|
|
+ unsigned long irq_flags;
|
|
+
|
|
+ spin_lock_irqsave(&qtty->lock, irq_flags);
|
|
+ sbsauart_fifo_to_tty(qtty);
|
|
+ spin_unlock_irqrestore(&qtty->lock, irq_flags);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int sbsa_tty_open(struct tty_struct *tty, struct file *filp)
|
|
+{
|
|
+ struct sbsa_tty *qtty = sbsa_tty;
|
|
+
|
|
+ return tty_port_open(&qtty->port, tty, filp);
|
|
+}
|
|
+
|
|
+static void sbsa_tty_close(struct tty_struct *tty, struct file *filp)
|
|
+{
|
|
+ tty_port_close(tty->port, tty, filp);
|
|
+}
|
|
+
|
|
+static void sbsa_tty_hangup(struct tty_struct *tty)
|
|
+{
|
|
+ tty_port_hangup(tty->port);
|
|
+}
|
|
+
|
|
+static int sbsa_tty_write(struct tty_struct *tty, const unsigned char *buf,
|
|
+ int count)
|
|
+{
|
|
+ sbsa_tty_do_write(buf, count);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static int sbsa_tty_write_room(struct tty_struct *tty)
|
|
+{
|
|
+ return 32;
|
|
+}
|
|
+
|
|
+static void sbsa_tty_console_write(struct console *co, const char *b,
|
|
+ unsigned count)
|
|
+{
|
|
+ sbsa_tty_do_write(b, count);
|
|
+
|
|
+ if (b[count - 1] == '\n')
|
|
+ sbsa_tty_do_write("\r", 1);
|
|
+}
|
|
+
|
|
+static struct tty_driver *sbsa_tty_console_device(struct console *c,
|
|
+ int *index)
|
|
+{
|
|
+ *index = c->index;
|
|
+ return sbsa_tty_driver;
|
|
+}
|
|
+
|
|
+static int sbsa_tty_console_setup(struct console *co, char *options)
|
|
+{
|
|
+ if ((unsigned)co->index > 0)
|
|
+ return -ENODEV;
|
|
+ if (sbsa_tty->base == NULL)
|
|
+ return -ENODEV;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct tty_port_operations sbsa_port_ops = {
|
|
+};
|
|
+
|
|
+static const struct tty_operations sbsa_tty_ops = {
|
|
+ .open = sbsa_tty_open,
|
|
+ .close = sbsa_tty_close,
|
|
+ .hangup = sbsa_tty_hangup,
|
|
+ .write = sbsa_tty_write,
|
|
+ .write_room = sbsa_tty_write_room,
|
|
+};
|
|
+
|
|
+static int sbsa_tty_create_driver(void)
|
|
+{
|
|
+ int ret;
|
|
+ struct tty_driver *tty;
|
|
+
|
|
+ sbsa_tty = kzalloc(sizeof(*sbsa_tty), GFP_KERNEL);
|
|
+ if (sbsa_tty == NULL) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_alloc_sbsa_tty_failed;
|
|
+ }
|
|
+ tty = alloc_tty_driver(1);
|
|
+ if (tty == NULL) {
|
|
+ ret = -ENOMEM;
|
|
+ goto err_alloc_tty_driver_failed;
|
|
+ }
|
|
+ tty->driver_name = "sbsauart";
|
|
+ tty->name = "ttySBSA";
|
|
+ tty->type = TTY_DRIVER_TYPE_SERIAL;
|
|
+ tty->subtype = SERIAL_TYPE_NORMAL;
|
|
+ tty->init_termios = tty_std_termios;
|
|
+ tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
|
|
+ TTY_DRIVER_DYNAMIC_DEV;
|
|
+ tty_set_operations(tty, &sbsa_tty_ops);
|
|
+ ret = tty_register_driver(tty);
|
|
+ if (ret)
|
|
+ goto err_tty_register_driver_failed;
|
|
+
|
|
+ sbsa_tty_driver = tty;
|
|
+ return 0;
|
|
+
|
|
+err_tty_register_driver_failed:
|
|
+ put_tty_driver(tty);
|
|
+err_alloc_tty_driver_failed:
|
|
+ kfree(sbsa_tty);
|
|
+ sbsa_tty = NULL;
|
|
+err_alloc_sbsa_tty_failed:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void sbsa_tty_delete_driver(void)
|
|
+{
|
|
+ tty_unregister_driver(sbsa_tty_driver);
|
|
+ put_tty_driver(sbsa_tty_driver);
|
|
+ sbsa_tty_driver = NULL;
|
|
+ kfree(sbsa_tty);
|
|
+ sbsa_tty = NULL;
|
|
+}
|
|
+
|
|
+static int sbsa_tty_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct sbsa_tty *qtty;
|
|
+ int ret = -EINVAL;
|
|
+ int i;
|
|
+ struct resource *r;
|
|
+ struct device *ttydev;
|
|
+ void __iomem *base;
|
|
+ u32 irq;
|
|
+
|
|
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ if (r == NULL)
|
|
+ return -EINVAL;
|
|
+
|
|
+ base = ioremap(r->start, r->end - r->start);
|
|
+ if (base == NULL)
|
|
+ pr_err("sbsa_tty: unable to remap base\n");
|
|
+
|
|
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
+ if (r == NULL)
|
|
+ goto err_unmap;
|
|
+
|
|
+ irq = r->start;
|
|
+
|
|
+ if (pdev->id > 0)
|
|
+ goto err_unmap;
|
|
+
|
|
+ ret = sbsa_tty_create_driver();
|
|
+ if (ret)
|
|
+ goto err_unmap;
|
|
+
|
|
+ qtty = sbsa_tty;
|
|
+ spin_lock_init(&qtty->lock);
|
|
+ tty_port_init(&qtty->port);
|
|
+ qtty->port.ops = &sbsa_port_ops;
|
|
+ qtty->base = base;
|
|
+ qtty->irq = irq;
|
|
+
|
|
+ /* Clear and Mask all IRQs */
|
|
+ writew(0, base + UART011_IMSC);
|
|
+ writew(0xFFFF, base + UART011_ICR);
|
|
+
|
|
+ ret = request_irq(irq, sbsa_tty_interrupt, IRQF_SHARED,
|
|
+ "sbsa_tty", pdev);
|
|
+ if (ret)
|
|
+ goto err_request_irq_failed;
|
|
+
|
|
+ /* Unmask the RX IRQ */
|
|
+ writew(UART011_RXIM | UART011_RTIM, base + UART011_IMSC);
|
|
+
|
|
+ ttydev = tty_port_register_device(&qtty->port, sbsa_tty_driver,
|
|
+ 0, &pdev->dev);
|
|
+ if (IS_ERR(ttydev)) {
|
|
+ ret = PTR_ERR(ttydev);
|
|
+ goto err_tty_register_device_failed;
|
|
+ }
|
|
+
|
|
+ strcpy(qtty->console.name, "ttySBSA");
|
|
+ qtty->console.write = sbsa_tty_console_write;
|
|
+ qtty->console.device = sbsa_tty_console_device;
|
|
+ qtty->console.setup = sbsa_tty_console_setup;
|
|
+ qtty->console.flags = CON_PRINTBUFFER;
|
|
+ qtty->console.index = pdev->id;
|
|
+ register_console(&qtty->console);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+ tty_unregister_device(sbsa_tty_driver, i);
|
|
+err_tty_register_device_failed:
|
|
+ free_irq(irq, pdev);
|
|
+err_request_irq_failed:
|
|
+ sbsa_tty_delete_driver();
|
|
+err_unmap:
|
|
+ iounmap(base);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int sbsa_tty_remove(struct platform_device *pdev)
|
|
+{
|
|
+ struct sbsa_tty *qtty;
|
|
+
|
|
+ qtty = sbsa_tty;
|
|
+ unregister_console(&qtty->console);
|
|
+ tty_unregister_device(sbsa_tty_driver, pdev->id);
|
|
+ iounmap(qtty->base);
|
|
+ qtty->base = 0;
|
|
+ free_irq(qtty->irq, pdev);
|
|
+ sbsa_tty_delete_driver();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct acpi_device_id sbsa_acpi_match[] = {
|
|
+ { "ARMH0011", 0 },
|
|
+ { }
|
|
+};
|
|
+
|
|
+static struct platform_driver sbsa_tty_platform_driver = {
|
|
+ .probe = sbsa_tty_probe,
|
|
+ .remove = sbsa_tty_remove,
|
|
+ .driver = {
|
|
+ .name = "sbsa_tty",
|
|
+ .acpi_match_table = ACPI_PTR(sbsa_acpi_match),
|
|
+ }
|
|
+};
|
|
+
|
|
+module_platform_driver(sbsa_tty_platform_driver);
|
|
+
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
|
|
index 57d9df8..e075437 100644
|
|
--- a/drivers/tty/serial/8250/8250_dw.c
|
|
+++ b/drivers/tty/serial/8250/8250_dw.c
|
|
@@ -313,10 +313,18 @@ static int dw8250_probe_of(struct uart_port *p,
|
|
static int dw8250_probe_acpi(struct uart_8250_port *up,
|
|
struct dw8250_data *data)
|
|
{
|
|
+ const struct acpi_device_id *id;
|
|
struct uart_port *p = &up->port;
|
|
|
|
dw8250_setup_port(up);
|
|
|
|
+ id = acpi_match_device(p->dev->driver->acpi_match_table, p->dev);
|
|
+ if (!id)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (!p->uartclk)
|
|
+ p->uartclk = (unsigned int)id->driver_data;
|
|
+
|
|
p->iotype = UPIO_MEM32;
|
|
p->serial_in = dw8250_serial_in32;
|
|
p->serial_out = dw8250_serial_out32;
|
|
@@ -541,6 +549,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = {
|
|
{ "INT3435", 0 },
|
|
{ "80860F0A", 0 },
|
|
{ "8086228A", 0 },
|
|
+ { "APMC0D08", 50000000},
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, dw8250_acpi_match);
|
|
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
|
|
index c600ccf..10c4775 100644
|
|
--- a/drivers/virtio/virtio_mmio.c
|
|
+++ b/drivers/virtio/virtio_mmio.c
|
|
@@ -100,8 +100,7 @@
|
|
#include <linux/virtio_config.h>
|
|
#include <linux/virtio_mmio.h>
|
|
#include <linux/virtio_ring.h>
|
|
-
|
|
-
|
|
+#include <linux/acpi.h>
|
|
|
|
/* The alignment to use between consumer and producer parts of vring.
|
|
* Currently hardcoded to the page size. */
|
|
@@ -637,6 +636,14 @@ static struct of_device_id virtio_mmio_match[] = {
|
|
};
|
|
MODULE_DEVICE_TABLE(of, virtio_mmio_match);
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
+static const struct acpi_device_id virtio_mmio_acpi_match[] = {
|
|
+ { "LNRO0005", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match);
|
|
+#endif
|
|
+
|
|
static struct platform_driver virtio_mmio_driver = {
|
|
.probe = virtio_mmio_probe,
|
|
.remove = virtio_mmio_remove,
|
|
@@ -644,6 +651,7 @@ static struct platform_driver virtio_mmio_driver = {
|
|
.name = "virtio-mmio",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = virtio_mmio_match,
|
|
+ .acpi_match_table = ACPI_PTR(virtio_mmio_acpi_match),
|
|
},
|
|
};
|
|
|
|
diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h
|
|
index c728113..f97804b 100644
|
|
--- a/include/acpi/acnames.h
|
|
+++ b/include/acpi/acnames.h
|
|
@@ -59,6 +59,10 @@
|
|
#define METHOD_NAME__PRS "_PRS"
|
|
#define METHOD_NAME__PRT "_PRT"
|
|
#define METHOD_NAME__PRW "_PRW"
|
|
+#define METHOD_NAME__PS0 "_PS0"
|
|
+#define METHOD_NAME__PS1 "_PS1"
|
|
+#define METHOD_NAME__PS2 "_PS2"
|
|
+#define METHOD_NAME__PS3 "_PS3"
|
|
#define METHOD_NAME__REG "_REG"
|
|
#define METHOD_NAME__SB_ "_SB_"
|
|
#define METHOD_NAME__SEG "_SEG"
|
|
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
|
|
index 57ee052..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 <linux/mm.h>
|
|
#include <linux/io.h>
|
|
|
|
static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys,
|
|
acpi_size size)
|
|
{
|
|
+#ifdef CONFIG_ARM64
|
|
+ if (!page_is_ram(phys >> PAGE_SHIFT))
|
|
+ return ioremap(phys, size);
|
|
+#endif
|
|
+
|
|
return ioremap_cache(phys, size);
|
|
}
|
|
|
|
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
|
|
index b7c89d4..dc9d037 100644
|
|
--- a/include/acpi/acpixf.h
|
|
+++ b/include/acpi/acpixf.h
|
|
@@ -46,7 +46,7 @@
|
|
|
|
/* Current ACPICA subsystem version in YYYYMMDD format */
|
|
|
|
-#define ACPI_CA_VERSION 0x20140724
|
|
+#define ACPI_CA_VERSION 0x20140828
|
|
|
|
#include <acpi/acconfig.h>
|
|
#include <acpi/actypes.h>
|
|
diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h
|
|
index 7626bfe..29e7937 100644
|
|
--- a/include/acpi/actbl1.h
|
|
+++ b/include/acpi/actbl1.h
|
|
@@ -952,7 +952,8 @@ enum acpi_srat_type {
|
|
ACPI_SRAT_TYPE_CPU_AFFINITY = 0,
|
|
ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1,
|
|
ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2,
|
|
- ACPI_SRAT_TYPE_RESERVED = 3 /* 3 and greater are reserved */
|
|
+ ACPI_SRAT_TYPE_GICC_AFFINITY = 3,
|
|
+ ACPI_SRAT_TYPE_RESERVED = 4 /* 4 and greater are reserved */
|
|
};
|
|
|
|
/*
|
|
@@ -968,7 +969,7 @@ struct acpi_srat_cpu_affinity {
|
|
u32 flags;
|
|
u8 local_sapic_eid;
|
|
u8 proximity_domain_hi[3];
|
|
- u32 reserved; /* Reserved, must be zero */
|
|
+ u32 clock_domain;
|
|
};
|
|
|
|
/* Flags */
|
|
@@ -1010,6 +1011,20 @@ struct acpi_srat_x2apic_cpu_affinity {
|
|
|
|
#define ACPI_SRAT_CPU_ENABLED (1) /* 00: Use affinity structure */
|
|
|
|
+/* 3: GICC Affinity (ACPI 5.1) */
|
|
+
|
|
+struct acpi_srat_gicc_affinity {
|
|
+ struct acpi_subtable_header header;
|
|
+ u32 proximity_domain;
|
|
+ u32 acpi_processor_uid;
|
|
+ u32 flags;
|
|
+ u32 clock_domain;
|
|
+};
|
|
+
|
|
+/* Flags for struct acpi_srat_gicc_affinity */
|
|
+
|
|
+#define ACPI_SRAT_GICC_ENABLED (1) /* 00: Use affinity structure */
|
|
+
|
|
/* Reset to default packing */
|
|
|
|
#pragma pack()
|
|
diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h
|
|
index 787bcc8..5480cb2 100644
|
|
--- a/include/acpi/actbl3.h
|
|
+++ b/include/acpi/actbl3.h
|
|
@@ -310,10 +310,15 @@ struct acpi_gtdt_timer_entry {
|
|
u32 common_flags;
|
|
};
|
|
|
|
+/* Flag Definitions: timer_flags and virtual_timer_flags above */
|
|
+
|
|
+#define ACPI_GTDT_GT_IRQ_MODE (1)
|
|
+#define ACPI_GTDT_GT_IRQ_POLARITY (1<<1)
|
|
+
|
|
/* Flag Definitions: common_flags above */
|
|
|
|
-#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
|
|
-#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
|
|
+#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
|
|
+#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
|
|
|
|
/* 1: SBSA Generic Watchdog Structure */
|
|
|
|
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
|
|
index 975e1cc..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 <linux/errno.h>
|
|
#include <linux/ioport.h> /* for struct resource */
|
|
#include <linux/device.h>
|
|
+#include <linux/property.h>
|
|
|
|
#ifndef _LINUX
|
|
#define _LINUX
|
|
@@ -71,6 +72,7 @@ enum acpi_irq_model_id {
|
|
ACPI_IRQ_MODEL_IOAPIC,
|
|
ACPI_IRQ_MODEL_IOSAPIC,
|
|
ACPI_IRQ_MODEL_PLATFORM,
|
|
+ ACPI_IRQ_MODEL_GIC,
|
|
ACPI_IRQ_MODEL_COUNT
|
|
};
|
|
|
|
@@ -123,6 +125,10 @@ int acpi_numa_init (void);
|
|
|
|
int acpi_table_init (void);
|
|
int acpi_table_parse(char *id, acpi_tbl_table_handler handler);
|
|
+int __init acpi_parse_entries(unsigned long table_size,
|
|
+ acpi_tbl_entry_handler handler,
|
|
+ struct acpi_table_header *table_header,
|
|
+ int entry_id, unsigned int max_entries);
|
|
int __init acpi_table_parse_entries(char *id, unsigned long table_size,
|
|
int entry_id,
|
|
acpi_tbl_entry_handler handler,
|
|
@@ -423,12 +429,8 @@ extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *),
|
|
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
|
|
const struct device *dev);
|
|
|
|
-static inline bool acpi_driver_match_device(struct device *dev,
|
|
- const struct device_driver *drv)
|
|
-{
|
|
- return !!acpi_match_device(drv->acpi_match_table, dev);
|
|
-}
|
|
-
|
|
+extern bool acpi_driver_match_device(struct device *dev,
|
|
+ const struct device_driver *drv);
|
|
int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
|
|
int acpi_device_modalias(struct device *, char *, int);
|
|
|
|
@@ -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 <tomasz.nowicki@linaro.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#ifndef ARM_GIC_ACPI_H_
|
|
+#define ARM_GIC_ACPI_H_
|
|
+
|
|
+#ifdef CONFIG_ACPI
|
|
+
|
|
+/*
|
|
+ * Hard code here, we can not get memory size from MADT (but FDT does),
|
|
+ * Actually no need to do that, because this size can be inferred
|
|
+ * from GIC spec.
|
|
+ */
|
|
+#define ACPI_GICV2_DIST_MEM_SIZE (SZ_4K)
|
|
+#define ACPI_GIC_CPU_IF_MEM_SIZE (SZ_8K)
|
|
+
|
|
+struct acpi_table_header;
|
|
+
|
|
+void acpi_gic_init(void);
|
|
+int gic_v2_acpi_init(struct acpi_table_header *table);
|
|
+#else
|
|
+static inline void acpi_gic_init(void) { }
|
|
+#endif
|
|
+
|
|
+#endif /* ARM_GIC_ACPI_H_ */
|
|
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
|
|
index 45e2d8c..2b0f246 100644
|
|
--- a/include/linux/irqchip/arm-gic.h
|
|
+++ b/include/linux/irqchip/arm-gic.h
|
|
@@ -39,6 +39,8 @@
|
|
#define GIC_DIST_SGI_PENDING_CLEAR 0xf10
|
|
#define GIC_DIST_SGI_PENDING_SET 0xf20
|
|
|
|
+#define GIC_DIST_SOFTINT_NSATT 0x8000
|
|
+
|
|
#define GICH_HCR 0x0
|
|
#define GICH_VTR 0x4
|
|
#define GICH_VMCR 0x8
|
|
diff --git a/include/linux/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 <linux/spinlock.h>
|
|
#include <linux/topology.h>
|
|
#include <linux/notifier.h>
|
|
+#include <linux/property.h>
|
|
|
|
#include <asm/byteorder.h>
|
|
#include <asm/errno.h>
|
|
@@ -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 <rafael.j.wysocki@intel.com>
|
|
+ * Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_PROPERTY_H_
|
|
+#define _LINUX_PROPERTY_H_
|
|
+
|
|
+#include <linux/device.h>
|
|
+
|
|
+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 <asm/perf_regs.h>
|
|
|
|
#define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1)
|
|
+#define PERF_REGS_MAX PERF_REG_ARM64_MAX
|
|
+
|
|
#define PERF_REG_IP PERF_REG_ARM64_PC
|
|
#define PERF_REG_SP PERF_REG_ARM64_SP
|
|
|
|
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
|
|
index 22fa819..9cd5dbd 100644
|
|
--- a/virt/kvm/arm/arch_timer.c
|
|
+++ b/virt/kvm/arm/arch_timer.c
|
|
@@ -21,9 +21,11 @@
|
|
#include <linux/kvm.h>
|
|
#include <linux/kvm_host.h>
|
|
#include <linux/interrupt.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include <clocksource/arm_arch_timer.h>
|
|
#include <asm/arch_timer.h>
|
|
+#include <asm/acpi.h>
|
|
|
|
#include <kvm/arm_vgic.h>
|
|
#include <kvm/arm_arch_timer.h>
|
|
@@ -244,60 +246,92 @@ static const struct of_device_id arch_timer_of_match[] = {
|
|
{},
|
|
};
|
|
|
|
-int kvm_timer_hyp_init(void)
|
|
+static int kvm_timer_ppi_parse_dt(unsigned int *ppi)
|
|
{
|
|
struct device_node *np;
|
|
- unsigned int ppi;
|
|
- int err;
|
|
-
|
|
- timecounter = arch_timer_get_timecounter();
|
|
- if (!timecounter)
|
|
- return -ENODEV;
|
|
|
|
np = of_find_matching_node(NULL, arch_timer_of_match);
|
|
if (!np) {
|
|
- kvm_err("kvm_arch_timer: can't find DT node\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
- ppi = irq_of_parse_and_map(np, 2);
|
|
- if (!ppi) {
|
|
- kvm_err("kvm_arch_timer: no virtual timer interrupt\n");
|
|
- err = -EINVAL;
|
|
- goto out;
|
|
+ *ppi = irq_of_parse_and_map(np, 2);
|
|
+ if (*ppi == 0) {
|
|
+ of_node_put(np);
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
- err = request_percpu_irq(ppi, kvm_arch_timer_handler,
|
|
- "kvm guest timer", kvm_get_running_vcpus());
|
|
- if (err) {
|
|
- kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
|
|
- ppi, err);
|
|
- goto out;
|
|
- }
|
|
+ return 0;
|
|
+}
|
|
|
|
- host_vtimer_irq = ppi;
|
|
+extern int acadia_kvm_acpi;
|
|
+extern int arch_timer_ppi[];
|
|
|
|
- err = __register_cpu_notifier(&kvm_timer_cpu_nb);
|
|
- if (err) {
|
|
- kvm_err("Cannot register timer CPU notifier\n");
|
|
- goto out_free;
|
|
- }
|
|
+static int kvm_timer_ppi_parse_acpi(unsigned int *ppi)
|
|
|
|
- wqueue = create_singlethread_workqueue("kvm_arch_timer");
|
|
- if (!wqueue) {
|
|
- err = -ENOMEM;
|
|
- goto out_free;
|
|
- }
|
|
+{
|
|
+ /* retrieve VIRT_PPI info */
|
|
+ *ppi = arch_timer_ppi[2];
|
|
|
|
- kvm_info("%s IRQ%d\n", np->name, ppi);
|
|
- on_each_cpu(kvm_timer_init_interrupt, NULL, 1);
|
|
+ if (*ppi == 0)
|
|
+ return -EINVAL;
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int kvm_timer_hyp_init(void)
|
|
+{
|
|
+ unsigned int ppi;
|
|
+ int err;
|
|
+
|
|
+ timecounter = arch_timer_get_timecounter();
|
|
+ if (!timecounter)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* PPI DT parsing */
|
|
+ err = kvm_timer_ppi_parse_dt(&ppi);
|
|
|
|
- goto out;
|
|
+ /* if DT parsing fails, try ACPI next */
|
|
+ if (err && !acpi_disabled && acadia_kvm_acpi )
|
|
+ err = kvm_timer_ppi_parse_acpi(&ppi);
|
|
+
|
|
+ if (err) {
|
|
+ kvm_err("kvm_timer_hyp_init: can't find virtual timer info or "
|
|
+ "config virtual timer interrupt\n");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* configure IRQ handler */
|
|
+ err = request_percpu_irq(ppi, kvm_arch_timer_handler,
|
|
+ "kvm guest timer", kvm_get_running_vcpus());
|
|
+ if (err) {
|
|
+ kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n",
|
|
+ ppi, err);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ host_vtimer_irq = ppi;
|
|
+
|
|
+ err = __register_cpu_notifier(&kvm_timer_cpu_nb);
|
|
+ if (err) {
|
|
+ kvm_err("Cannot register timer CPU notifier\n");
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ wqueue = create_singlethread_workqueue("kvm_arch_timer");
|
|
+ if (!wqueue) {
|
|
+ err = -ENOMEM;
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ kvm_info("timer IRQ%d\n", ppi);
|
|
+ on_each_cpu(kvm_timer_init_interrupt, NULL, 1);
|
|
+
|
|
+ goto out;
|
|
out_free:
|
|
- free_percpu_irq(ppi, kvm_get_running_vcpus());
|
|
+ free_percpu_irq(ppi, kvm_get_running_vcpus());
|
|
out:
|
|
- of_node_put(np);
|
|
- return err;
|
|
+ return err;
|
|
}
|
|
|
|
void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu)
|
|
diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c
|
|
index 416baed..53bdd33 100644
|
|
--- a/virt/kvm/arm/vgic-v2.c
|
|
+++ b/virt/kvm/arm/vgic-v2.c
|
|
@@ -19,6 +19,7 @@
|
|
#include <linux/kvm.h>
|
|
#include <linux/kvm_host.h>
|
|
#include <linux/interrupt.h>
|
|
+#include <linux/acpi.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
@@ -26,6 +27,7 @@
|
|
|
|
#include <linux/irqchip/arm-gic.h>
|
|
|
|
+#include <asm/acpi.h>
|
|
#include <asm/kvm_emulate.h>
|
|
#include <asm/kvm_arm.h>
|
|
#include <asm/kvm_mmu.h>
|
|
@@ -177,7 +179,7 @@ static const struct vgic_ops vgic_v2_ops = {
|
|
static struct vgic_params vgic_v2_params;
|
|
|
|
/**
|
|
- * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
|
|
+ * vgic_v2_dt_probe - probe for a GICv2 compatible interrupt controller in DT
|
|
* @node: pointer to the DT node
|
|
* @ops: address of a pointer to the GICv2 operations
|
|
* @params: address of a pointer to HW-specific parameters
|
|
@@ -186,7 +188,7 @@ static struct vgic_params vgic_v2_params;
|
|
* in *ops and the HW parameters in *params. Returns an error code
|
|
* otherwise.
|
|
*/
|
|
-int vgic_v2_probe(struct device_node *vgic_node,
|
|
+int vgic_v2_dt_probe(struct device_node *vgic_node,
|
|
const struct vgic_ops **ops,
|
|
const struct vgic_params **params)
|
|
{
|
|
@@ -263,3 +265,72 @@ out:
|
|
of_node_put(vgic_node);
|
|
return ret;
|
|
}
|
|
+
|
|
+struct acpi_madt_generic_interrupt *vgic_acpi;
|
|
+static void gic_get_acpi_header(struct acpi_subtable_header *header)
|
|
+{
|
|
+ vgic_acpi = (struct acpi_madt_generic_interrupt *)header;
|
|
+}
|
|
+
|
|
+int vgic_v2_acpi_probe(const struct vgic_ops **ops,
|
|
+ const struct vgic_params **params)
|
|
+{
|
|
+ struct vgic_params *vgic = &vgic_v2_params;
|
|
+ int irq_mode, ret;
|
|
+
|
|
+ /* MADT table */
|
|
+ ret = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
|
|
+ (acpi_tbl_entry_handler)gic_get_acpi_header, 0);
|
|
+ if (!ret) {
|
|
+ pr_err("Failed to get MADT VGIC CPU entry\n");
|
|
+ ret = -ENODEV;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* IRQ trigger mode */
|
|
+ irq_mode = (vgic_acpi->flags & ACPI_MADT_VGIC_IRQ_MODE) ?
|
|
+ ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE;
|
|
+ /* According to GIC-400 manual, all PPIs are active-LOW, level
|
|
+ * sensative. We register IRQ as active-low.
|
|
+ */
|
|
+ vgic->maint_irq = acpi_register_gsi(NULL, vgic_acpi->vgic_interrupt,
|
|
+ irq_mode, ACPI_ACTIVE_LOW);
|
|
+ if (!vgic->maint_irq) {
|
|
+ pr_err("Cannot register VGIC ACPI maintenance irq\n");
|
|
+ ret = -ENXIO;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* GICH resource */
|
|
+ vgic->vctrl_base = ioremap(vgic_acpi->gich_base_address, SZ_8K);
|
|
+ if (!vgic->vctrl_base) {
|
|
+ pr_err("cannot ioremap GICH memory\n");
|
|
+ ret = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR);
|
|
+ vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1;
|
|
+
|
|
+ ret = create_hyp_io_mappings(vgic->vctrl_base,
|
|
+ vgic->vctrl_base + SZ_8K,
|
|
+ vgic_acpi->gich_base_address);
|
|
+ if (ret) {
|
|
+ kvm_err("Cannot map GICH into hyp\n");
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ vgic->vcpu_base = vgic_acpi->gicv_base_address;
|
|
+
|
|
+ kvm_info("GICH base=0x%llx, GICV base=0x%llx, IRQ=%d\n",
|
|
+ (unsigned long long)vgic_acpi->gich_base_address,
|
|
+ (unsigned long long)vgic_acpi->gicv_base_address,
|
|
+ vgic->maint_irq);
|
|
+
|
|
+ vgic->type = VGIC_V2;
|
|
+ *ops = &vgic_v2_ops;
|
|
+ *params = vgic;
|
|
+
|
|
+out:
|
|
+ return ret;
|
|
+}
|
|
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
|
|
index 1c2c8ee..8b56920 100644
|
|
--- a/virt/kvm/arm/vgic-v3.c
|
|
+++ b/virt/kvm/arm/vgic-v3.c
|
|
@@ -173,7 +173,7 @@ static const struct vgic_ops vgic_v3_ops = {
|
|
static struct vgic_params vgic_v3_params;
|
|
|
|
/**
|
|
- * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT
|
|
+ * vgic_v3_dt_probe - probe for a GICv3 compatible interrupt controller in DT
|
|
* @node: pointer to the DT node
|
|
* @ops: address of a pointer to the GICv3 operations
|
|
* @params: address of a pointer to HW-specific parameters
|
|
@@ -182,9 +182,9 @@ static struct vgic_params vgic_v3_params;
|
|
* in *ops and the HW parameters in *params. Returns an error code
|
|
* otherwise.
|
|
*/
|
|
-int vgic_v3_probe(struct device_node *vgic_node,
|
|
- const struct vgic_ops **ops,
|
|
- const struct vgic_params **params)
|
|
+int vgic_v3_dt_probe(struct device_node *vgic_node,
|
|
+ const struct vgic_ops **ops,
|
|
+ const struct vgic_params **params)
|
|
{
|
|
int ret = 0;
|
|
u32 gicv_idx;
|
|
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
|
|
index 73eba79..ca98a3b 100644
|
|
--- a/virt/kvm/arm/vgic.c
|
|
+++ b/virt/kvm/arm/vgic.c
|
|
@@ -25,9 +25,11 @@
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/uaccess.h>
|
|
+#include <linux/acpi.h>
|
|
|
|
#include <linux/irqchip/arm-gic.h>
|
|
|
|
+#include <asm/acpi.h>
|
|
#include <asm/kvm_emulate.h>
|
|
#include <asm/kvm_arm.h>
|
|
#include <asm/kvm_mmu.h>
|
|
@@ -1549,31 +1551,39 @@ static struct notifier_block vgic_cpu_nb = {
|
|
};
|
|
|
|
static const struct of_device_id vgic_ids[] = {
|
|
- { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, },
|
|
- { .compatible = "arm,gic-v3", .data = vgic_v3_probe, },
|
|
+ { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_dt_probe, },
|
|
+ { .compatible = "arm,gic-v3", .data = vgic_v3_dt_probe, },
|
|
{},
|
|
};
|
|
|
|
+extern int acadia_kvm_acpi;
|
|
+
|
|
int kvm_vgic_hyp_init(void)
|
|
{
|
|
const struct of_device_id *matched_id;
|
|
int (*vgic_probe)(struct device_node *,const struct vgic_ops **,
|
|
const struct vgic_params **);
|
|
struct device_node *vgic_node;
|
|
- int ret;
|
|
+ int ret = -ENODEV;
|
|
|
|
- vgic_node = of_find_matching_node_and_match(NULL,
|
|
- vgic_ids, &matched_id);
|
|
- if (!vgic_node) {
|
|
- kvm_err("error: no compatible GIC node found\n");
|
|
- return -ENODEV;
|
|
+ /* probe VGIC */
|
|
+ if (vgic_node = of_find_matching_node_and_match(NULL,
|
|
+ vgic_ids, &matched_id)) {
|
|
+ /* probe VGIC in DT */
|
|
+ vgic_probe = matched_id->data;
|
|
+ ret = vgic_probe(vgic_node, &vgic_ops, &vgic);
|
|
+ }
|
|
+ else if (!acpi_disabled && acadia_kvm_acpi) {
|
|
+ /* probe VGIC in ACPI */
|
|
+ ret = vgic_v2_acpi_probe(&vgic_ops, &vgic);
|
|
}
|
|
|
|
- vgic_probe = matched_id->data;
|
|
- ret = vgic_probe(vgic_node, &vgic_ops, &vgic);
|
|
- if (ret)
|
|
+ if (ret) {
|
|
+ kvm_err("error: no compatible GIC info found\n");
|
|
return ret;
|
|
+ }
|
|
|
|
+ /* configuration */
|
|
ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler,
|
|
"vgic", kvm_get_running_vcpus());
|
|
if (ret) {
|