diff options
917 files changed, 33727 insertions, 13621 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-acpi b/Documentation/ABI/testing/sysfs-bus-acpi new file mode 100644 index 000000000000..7fa9cbc75344 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-acpi @@ -0,0 +1,58 @@ +What: /sys/bus/acpi/devices/.../path +Date: December 2006 +Contact: Rafael J. Wysocki <rjw@rjwysocki.net> +Description: + This attribute indicates the full path of ACPI namespace + object associated with the device object. For example, + \_SB_.PCI0. + This file is not present for device objects representing + fixed ACPI hardware features (like power and sleep + buttons). + +What: /sys/bus/acpi/devices/.../modalias +Date: July 2007 +Contact: Rafael J. Wysocki <rjw@rjwysocki.net> +Description: + This attribute indicates the PNP IDs of the device object. + That is acpi:HHHHHHHH:[CCCCCCC:]. Where each HHHHHHHH or + CCCCCCCC contains device object's PNPID (_HID or _CID). + +What: /sys/bus/acpi/devices/.../hid +Date: April 2005 +Contact: Rafael J. Wysocki <rjw@rjwysocki.net> +Description: + This attribute indicates the hardware ID (_HID) of the + device object. For example, PNP0103. + This file is present for device objects having the _HID + control method. + +What: /sys/bus/acpi/devices/.../description +Date: October 2012 +Contact: Rafael J. Wysocki <rjw@rjwysocki.net> +Description: + This attribute contains the output of the device object's + _STR control method, if present. + +What: /sys/bus/acpi/devices/.../adr +Date: October 2012 +Contact: Rafael J. Wysocki <rjw@rjwysocki.net> +Description: + This attribute contains the output of the device object's + _ADR control method, which is present for ACPI device + objects representing devices having standard enumeration + algorithms, such as PCI. + +What: /sys/bus/acpi/devices/.../uid +Date: October 2012 +Contact: Rafael J. Wysocki <rjw@rjwysocki.net> +Description: + This attribute contains the output of the device object's + _UID control method, if present. + +What: /sys/bus/acpi/devices/.../eject +Date: December 2006 +Contact: Rafael J. Wysocki <rjw@rjwysocki.net> +Description: + Writing 1 to this attribute will trigger hot removal of + this device object. This file exists for every device + object that has _EJ0 method. diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 0ba6ea2f89d9..ee39acacf6f8 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -78,3 +78,23 @@ Contact: Nishanth Menon <nm@ti.com> Description: The /sys/class/devfreq/.../available_governors shows currently available governors in the system. + +What: /sys/class/devfreq/.../min_freq +Date: January 2013 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/class/devfreq/.../min_freq shows and stores + the minimum frequency requested by users. It is 0 if + the user does not care. min_freq overrides the + frequency requested by governors. + +What: /sys/class/devfreq/.../max_freq +Date: January 2013 +Contact: MyungJoo Ham <myungjoo.ham@samsung.com> +Description: + The /sys/class/devfreq/.../max_freq shows and stores + the maximum frequency requested by users. It is 0 if + the user does not care. max_freq overrides the + frequency requested by governors and min_freq. + The max_freq overrides min_freq because max_freq may be + used to throttle devices to avoid overheating. diff --git a/Documentation/ABI/testing/sysfs-devices-online b/Documentation/ABI/testing/sysfs-devices-online new file mode 100644 index 000000000000..f990026c0740 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-online @@ -0,0 +1,20 @@ +What: /sys/devices/.../online +Date: April 2013 +Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com> +Description: + The /sys/devices/.../online attribute is only present for + devices whose bus types provide .online() and .offline() + callbacks. The number read from it (0 or 1) reflects the value + of the device's 'offline' field. If that number is 1 and '0' + (or 'n', or 'N') is written to this file, the device bus type's + .offline() callback is executed for the device and (if + successful) its 'offline' field is updated accordingly. In + turn, if that number is 0 and '1' (or 'y', or 'Y') is written to + this file, the device bus type's .online() callback is executed + for the device and (if successful) its 'offline' field is + updated as appropriate. + + After a successful execution of the bus type's .offline() + callback the device cannot be used for any purpose until either + it is removed (i.e. device_del() is called for it), or its bus + type's .online() is exeucted successfully. diff --git a/Documentation/ABI/testing/sysfs-devices-sun b/Documentation/ABI/testing/sysfs-devices-sun index 86be9848a77e..625ce4b63758 100644 --- a/Documentation/ABI/testing/sysfs-devices-sun +++ b/Documentation/ABI/testing/sysfs-devices-sun @@ -1,4 +1,4 @@ -Whatt: /sys/devices/.../sun +What: /sys/devices/.../sun Date: October 2012 Contact: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Description: diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index 2447698aed41..468e4d48f884 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -144,6 +144,21 @@ Description: Discover and change clock speed of CPUs to learn how to control the knobs. +What: /sys/devices/system/cpu/cpu#/cpufreq/freqdomain_cpus +Date: June 2013 +Contact: cpufreq@vger.kernel.org +Description: Discover CPUs in the same CPU frequency coordination domain + + freqdomain_cpus is the list of CPUs (online+offline) that share + the same clock/freq domain (possibly at the hardware level). + That information may be hidden from the cpufreq core and the + value of related_cpus may be different from freqdomain_cpus. This + attribute is useful for user space DVFS controllers to get better + power/performance results for platforms using acpi-cpufreq. + + This file is only present if the acpi-cpufreq driver is in use. + + What: /sys/devices/system/cpu/cpu*/cache/index3/cache_disable_{0,1} Date: August 2008 KernelVersion: 2.6.27 diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index ce9bee98b43b..b4436cca97a8 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -44,6 +44,16 @@ Description: or 0 (unset). Attempts to write any other values to it will cause -EINVAL to be returned. +What: /sys/firmware/acpi/hotplug/force_remove +Date: May 2013 +Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com> +Description: + The number in this file (0 or 1) determines whether (1) or not + (0) the ACPI subsystem will allow devices to be hot-removed even + if they cannot be put offline gracefully (from the kernel's + viewpoint). That number can be changed by writing a boolean + value to this file. + What: /sys/firmware/acpi/interrupts/ Date: February 2008 Contact: Len Brown <lenb@kernel.org> diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index c36892c072da..fca34192cf80 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -297,10 +297,10 @@ KAO --> </sect1> <sect1><title>Frame Buffer Fonts</title> <para> - Refer to the file drivers/video/console/fonts.c for more information. + Refer to the file lib/fonts/fonts.c for more information. </para> <!-- FIXME: Removed for now since no structured comments in source -X!Idrivers/video/console/fonts.c +X!Ilib/fonts/fonts.c --> </sect1> </chapter> diff --git a/Documentation/acpi/namespace.txt b/Documentation/acpi/namespace.txt new file mode 100644 index 000000000000..260f6a3661fa --- /dev/null +++ b/Documentation/acpi/namespace.txt @@ -0,0 +1,395 @@ +ACPI Device Tree - Representation of ACPI Namespace + +Copyright (C) 2013, Intel Corporation +Author: Lv Zheng <lv.zheng@intel.com> + + +Abstract: + +The Linux ACPI subsystem converts ACPI namespace objects into a Linux +device tree under the /sys/devices/LNXSYSTEM:00 and updates it upon +receiving ACPI hotplug notification events. For each device object in this +hierarchy there is a corresponding symbolic link in the +/sys/bus/acpi/devices. +This document illustrates the structure of the ACPI device tree. + + +Credit: + +Thanks for the help from Zhang Rui <rui.zhang@intel.com> and Rafael J. +Wysocki <rafael.j.wysocki@intel.com>. + + +1. ACPI Definition Blocks + + The ACPI firmware sets up RSDP (Root System Description Pointer) in the + system memory address space pointing to the XSDT (Extended System + Description Table). The XSDT always points to the FADT (Fixed ACPI + Description Table) using its first entry, the data within the FADT + includes various fixed-length entries that describe fixed ACPI features + of the hardware. The FADT contains a pointer to the DSDT + (Differentiated System Descripition Table). The XSDT also contains + entries pointing to possibly multiple SSDTs (Secondary System + Description Table). + + The DSDT and SSDT data is organized in data structures called definition + blocks that contain definitions of various objects, including ACPI + control methods, encoded in AML (ACPI Machine Language). The data block + of the DSDT along with the contents of SSDTs represents a hierarchical + data structure called the ACPI namespace whose topology reflects the + structure of the underlying hardware platform. + + The relationships between ACPI System Definition Tables described above + are illustrated in the following diagram. + + +---------+ +-------+ +--------+ +------------------------+ + | RSDP | +->| XSDT | +->| FADT | | +-------------------+ | + +---------+ | +-------+ | +--------+ +-|->| DSDT | | + | Pointer | | | Entry |-+ | ...... | | | +-------------------+ | + +---------+ | +-------+ | X_DSDT |--+ | | Definition Blocks | | + | Pointer |-+ | ..... | | ...... | | +-------------------+ | + +---------+ +-------+ +--------+ | +-------------------+ | + | Entry |------------------|->| SSDT | | + +- - - -+ | +-------------------| | + | Entry | - - - - - - - -+ | | Definition Blocks | | + +- - - -+ | | +-------------------+ | + | | +- - - - - - - - - -+ | + +-|->| SSDT | | + | +-------------------+ | + | | Definition Blocks | | + | +- - - - - - - - - -+ | + +------------------------+ + | + OSPM Loading | + \|/ + +----------------+ + | ACPI Namespace | + +----------------+ + + Figure 1. ACPI Definition Blocks + + NOTE: RSDP can also contain a pointer to the RSDT (Root System + Description Table). Platforms provide RSDT to enable + compatibility with ACPI 1.0 operating systems. The OS is expected + to use XSDT, if present. + + +2. Example ACPI Namespace + + All definition blocks are loaded into a single namespace. The namespace + is a hierarchy of objects identified by names and paths. + The following naming conventions apply to object names in the ACPI + namespace: + 1. All names are 32 bits long. + 2. The first byte of a name must be one of 'A' - 'Z', '_'. + 3. Each of the remaining bytes of a name must be one of 'A' - 'Z', '0' + - '9', '_'. + 4. Names starting with '_' are reserved by the ACPI specification. + 5. The '\' symbol represents the root of the namespace (i.e. names + prepended with '\' are relative to the namespace root). + 6. The '^' symbol represents the parent of the current namespace node + (i.e. names prepended with '^' are relative to the parent of the + current namespace node). + + The figure below shows an example ACPI namespace. + + +------+ + | \ | Root + +------+ + | + | +------+ + +-| _PR | Scope(_PR): the processor namespace + | +------+ + | | + | | +------+ + | +-| CPU0 | Processor(CPU0): the first processor + | +------+ + | + | +------+ + +-| _SB | Scope(_SB): the system bus namespace + | +------+ + | | + | | +------+ + | +-| LID0 | Device(LID0); the lid device + | | +------+ + | | | + | | | +------+ + | | +-| _HID | Name(_HID, "PNP0C0D"): the hardware ID + | | | +------+ + | | | + | | | +------+ + | | +-| _STA | Method(_STA): the status control method + | | +------+ + | | + | | +------+ + | +-| PCI0 | Device(PCI0); the PCI root bridge + | +------+ + | | + | | +------+ + | +-| _HID | Name(_HID, "PNP0A08"): the hardware ID + | | +------+ + | | + | | +------+ + | +-| _CID | Name(_CID, "PNP0A03"): the compatible ID + | | +------+ + | | + | | +------+ + | +-| RP03 | Scope(RP03): the PCI0 power scope + | | +------+ + | | | + | | | +------+ + | | +-| PXP3 | PowerResource(PXP3): the PCI0 power resource + | | +------+ + | | + | | +------+ + | +-| GFX0 | Device(GFX0): the graphics adapter + | +------+ + | | + | | +------+ + | +-| _ADR | Name(_ADR, 0x00020000): the PCI bus address + | | +------+ + | | + | | +------+ + | +-| DD01 | Device(DD01): the LCD output device + | +------+ + | | + | | +------+ + | +-| _BCL | Method(_BCL): the backlight control method + | +------+ + | + | +------+ + +-| _TZ | Scope(_TZ): the thermal zone namespace + | +------+ + | | + | | +------+ + | +-| FN00 | PowerResource(FN00): the FAN0 power resource + | | +------+ + | | + | | +------+ + | +-| FAN0 | Device(FAN0): the FAN0 cooling device + | | +------+ + | | | + | | | +------+ + | | +-| _HID | Name(_HID, "PNP0A0B"): the hardware ID + | | +------+ + | | + | | +------+ + | +-| TZ00 | ThermalZone(TZ00); the FAN thermal zone + | +------+ + | + | +------+ + +-| _GPE | Scope(_GPE): the GPE namespace + +------+ + + Figure 2. Example ACPI Namespace + + +3. Linux ACPI Device Objects + + The Linux kernel's core ACPI subsystem creates struct acpi_device + objects for ACPI namespace objects representing devices, power resources + processors, thermal zones. Those objects are exported to user space via + sysfs as directories in the subtree under /sys/devices/LNXSYSTM:00. The + format of their names is <bus_id:instance>, where 'bus_id' refers to the + ACPI namespace representation of the given object and 'instance' is used + for distinguishing different object of the same 'bus_id' (it is + two-digit decimal representation of an unsigned integer). + + The value of 'bus_id' depends on the type of the object whose name it is + part of as listed in the table below. + + +---+-----------------+-------+----------+ + | | Object/Feature | Table | bus_id | + +---+-----------------+-------+----------+ + | N | Root | xSDT | LNXSYSTM | + +---+-----------------+-------+----------+ + | N | Device | xSDT | _HID | + +---+-----------------+-------+----------+ + | N | Processor | xSDT | LNXCPU | + +---+-----------------+-------+----------+ + | N | ThermalZone | xSDT | LNXTHERM | + +---+-----------------+-------+----------+ + | N | PowerResource | xSDT | LNXPOWER | + +---+-----------------+-------+----------+ + | N | Other Devices | xSDT | device | + +---+-----------------+-------+----------+ + | F | PWR_BUTTON | FADT | LNXPWRBN | + +---+-----------------+-------+----------+ + | F | SLP_BUTTON | FADT | LNXSLPBN | + +---+-----------------+-------+----------+ + | M | Video Extension | xSDT | LNXVIDEO | + +---+-----------------+-------+----------+ + | M | ATA Controller | xSDT | LNXIOBAY | + +---+-----------------+-------+----------+ + | M | Docking Station | xSDT | LNXDOCK | + +---+-----------------+-------+----------+ + + Table 1. ACPI Namespace Objects Mapping + + The following rules apply when creating struct acpi_device objects on + the basis of the contents of ACPI System Description Tables (as + indicated by the letter in the first column and the notation in the + second column of the table above): + N: + The object's source is an ACPI namespace node (as indicated by the + named object's type in the second column). In that case the object's + directory in sysfs will contain the 'path' attribute whose value is + the full path to the node from the namespace root. + struct acpi_device objects are created for the ACPI namespace nodes + whose _STA control methods return PRESENT or FUNCTIONING. The power + resource nodes or nodes without _STA are assumed to be both PRESENT + and FUNCTIONING. + F: + The struct acpi_device object is created for a fixed hardware + feature (as indicated by the fixed feature flag's name in the second + column), so its sysfs directory will not contain the 'path' + attribute. + M: + The struct acpi_device object is created for an ACPI namespace node + with specific control methods (as indicated by the ACPI defined + device's type in the second column). The 'path' attribute containing + its namespace path will be present in its sysfs directory. For + example, if the _BCL method is present for an ACPI namespace node, a + struct acpi_device object with LNXVIDEO 'bus_id' will be created for + it. + + The third column of the above table indicates which ACPI System + Description Tables contain information used for the creation of the + struct acpi_device objects represented by the given row (xSDT means DSDT + or SSDT). + + The forth column of the above table indicates the 'bus_id' generation + rule of the struct acpi_device object: + _HID: + _HID in the last column of the table means that the object's bus_id + is derived from the _HID/_CID identification objects present under + the corresponding ACPI namespace node. The object's sysfs directory + will then contain the 'hid' and 'modalias' attributes that can be + used to retrieve the _HID and _CIDs of that object. + LNXxxxxx: + The 'modalias' attribute is also present for struct acpi_device + objects having bus_id of the "LNXxxxxx" form (pseudo devices), in + which cases it contains the bus_id string itself. + device: + 'device' in the last column of the table indicates that the object's + bus_id cannot be determined from _HID/_CID of the corresponding + ACPI namespace node, although that object represents a device (for + example, it may be a PCI device with _ADR defined and without _HID + or _CID). In that case the string 'device' will be used as the + object's bus_id. + + +4. Linux ACPI Physical Device Glue + + ACPI device (i.e. struct acpi_device) objects may be linked to other + objects in the Linux' device hierarchy that represent "physical" devices + (for example, devices on the PCI bus). If that happens, it means that + the ACPI device object is a "companion" of a device otherwise + represented in a different way and is used (1) to provide configuration + information on that device which cannot be obtained by other means and + (2) to do specific things to the device with the help of its ACPI + control methods. One ACPI device object may be linked this way to + multiple "physical" devices. + + If an ACPI device object is linked to a "physical" device, its sysfs + directory contains the "physical_node" symbolic link to the sysfs + directory of the target device object. In turn, the target device's + sysfs directory will then contain the "firmware_node" symbolic link to + the sysfs directory of the companion ACPI device object. + The linking mechanism relies on device identification provided by the + ACPI namespace. For example, if there's an ACPI namespace object + representing a PCI device (i.e. a device object under an ACPI namespace + object representing a PCI bridge) whose _ADR returns 0x00020000 and the + bus number of the parent PCI bridge is 0, the sysfs directory + representing the struct acpi_device object created for that ACPI + namespace object will contain the 'physical_node' symbolic link to the + /sys/devices/pci0000:00/0000:00:02:0/ sysfs directory of the + corresponding PCI device. + + The linking mechanism is generally bus-specific. The core of its + implementation is located in the drivers/acpi/glue.c file, but there are + complementary parts depending on the bus types in question located + elsewhere. For example, the PCI-specific part of it is located in + drivers/pci/pci-acpi.c. + + +5. Example Linux ACPI Device Tree + + The sysfs hierarchy of struct acpi_device objects corresponding to the + example ACPI namespace illustrated in Figure 2 with the addition of + fixed PWR_BUTTON/SLP_BUTTON devices is shown below. + + +--------------+---+-----------------+ + | LNXSYSTEM:00 | \ | acpi:LNXSYSTEM: | + +--------------+---+-----------------+ + | + | +-------------+-----+----------------+ + +-| LNXPWRBN:00 | N/A | acpi:LNXPWRBN: | + | +-------------+-----+----------------+ + | + | +-------------+-----+----------------+ + +-| LNXSLPBN:00 | N/A | acpi:LNXSLPBN: | + | +-------------+-----+----------------+ + | + | +-----------+------------+--------------+ + +-| LNXCPU:00 | \_PR_.CPU0 | acpi:LNXCPU: | + | +-----------+------------+--------------+ + | + | +-------------+-------+----------------+ + +-| LNXSYBUS:00 | \_SB_ | acpi:LNXSYBUS: | + | +-------------+-------+----------------+ + | | + | | +- - - - - - - +- - - - - - +- - - - - - - -+ + | +-| * PNP0C0D:00 | \_SB_.LID0 | acpi:PNP0C0D: | + | | +- - - - - - - +- - - - - - +- - - - - - - -+ + | | + | | +------------+------------+-----------------------+ + | +-| PNP0A08:00 | \_SB_.PCI0 | acpi:PNP0A08:PNP0A03: | + | +------------+------------+-----------------------+ + | | + | | +-----------+-----------------+-----+ + | +-| device:00 | \_SB_.PCI0.RP03 | N/A | + | | +-----------+-----------------+-----+ + | | | + | | | +-------------+----------------------+----------------+ + | | +-| LNXPOWER:00 | \_SB_.PCI0.RP03.PXP3 | acpi:LNXPOWER: | + | | +-------------+----------------------+----------------+ + | | + | | +-------------+-----------------+----------------+ + | +-| LNXVIDEO:00 | \_SB_.PCI0.GFX0 | acpi:LNXVIDEO: | + | +-------------+-----------------+----------------+ + | | + | | +-----------+-----------------+-----+ + | +-| device:01 | \_SB_.PCI0.DD01 | N/A | + | +-----------+-----------------+-----+ + | + | +-------------+-------+----------------+ + +-| LNXSYBUS:01 | \_TZ_ | acpi:LNXSYBUS: | + +-------------+-------+----------------+ + | + | +-------------+------------+----------------+ + +-| LNXPOWER:0a | \_TZ_.FN00 | acpi:LNXPOWER: | + | +-------------+------------+----------------+ + | + | +------------+------------+---------------+ + +-| PNP0C0B:00 | \_TZ_.FAN0 | acpi:PNP0C0B: | + | +------------+------------+---------------+ + | + | +-------------+------------+----------------+ + +-| LNXTHERM:00 | \_TZ_.TZ00 | acpi:LNXTHERM: | + +-------------+------------+----------------+ + + Figure 3. Example Linux ACPI Device Tree + + NOTE: Each node is represented as "object/path/modalias", where: + 1. 'object' is the name of the object's directory in sysfs. + 2. 'path' is the ACPI namespace path of the corresponding + ACPI namespace object, as returned by the object's 'path' + sysfs attribute. + 3. 'modalias' is the value of the object's 'modalias' sysfs + attribute (as described earlier in this document). + NOTE: N/A indicates the device object does not have the 'path' or the + 'modalias' attribute. + NOTE: The PNP0C0D device listed above is highlighted (marked by "*") + to indicate it will be created only when its _STA methods return + PRESENT or FUNCTIONING. diff --git a/Documentation/acpi/video_extension.txt b/Documentation/acpi/video_extension.txt new file mode 100644 index 000000000000..78b32ac02466 --- /dev/null +++ b/Documentation/acpi/video_extension.txt @@ -0,0 +1,106 @@ +ACPI video extensions +~~~~~~~~~~~~~~~~~~~~~ + +This driver implement the ACPI Extensions For Display Adapters for +integrated graphics devices on motherboard, as specified in ACPI 2.0 +Specification, Appendix B, allowing to perform some basic control like +defining the video POST device, retrieving EDID information or to +setup a video output, etc. Note that this is an ref. implementation +only. It may or may not work for your integrated video device. + +The ACPI video driver does 3 things regarding backlight control: + +1 Export a sysfs interface for user space to control backlight level + +If the ACPI table has a video device, and acpi_backlight=vendor kernel +command line is not present, the driver will register a backlight device +and set the required backlight operation structure for it for the sysfs +interface control. For every registered class device, there will be a +directory named acpi_videoX under /sys/class/backlight. + +The backlight sysfs interface has a standard definition here: +Documentation/ABI/stable/sysfs-class-backlight. + +And what ACPI video driver does is: +actual_brightness: on read, control method _BQC will be evaluated to +get the brightness level the firmware thinks it is at; +bl_power: not implemented, will set the current brightness instead; +brightness: on write, control method _BCM will run to set the requested +brightness level; +max_brightness: Derived from the _BCL package(see below); +type: firmware + +Note that ACPI video backlight driver will always use index for +brightness, actual_brightness and max_brightness. So if we have +the following _BCL package: + +Method (_BCL, 0, NotSerialized) +{ + Return (Package (0x0C) + { + 0x64, + 0x32, + 0x0A, + 0x14, + 0x1E, + 0x28, + 0x32, + 0x3C, + 0x46, + 0x50, + 0x5A, + 0x64 + }) +} + +The first two levels are for when laptop are on AC or on battery and are +not used by Linux currently. The remaining 10 levels are supported levels +that we can choose from. The applicable index values are from 0 (that +corresponds to the 0x0A brightness value) to 9 (that corresponds to the +0x64 brightness value) inclusive. Each of those index values is regarded +as a "brightness level" indicator. Thus from the user space perspective +the range of available brightness levels is from 0 to 9 (max_brightness) +inclusive. + +2 Notify user space about hotkey event + +There are generally two cases for hotkey event reporting: +i) For some laptops, when user presses the hotkey, a scancode will be + generated and sent to user space through the input device created by + the keyboard driver as a key type input event, with proper remap, the + following key code will appear to user space: + + EV_KEY, KEY_BRIGHTNESSUP + EV_KEY, KEY_BRIGHTNESSDOWN + etc. + +For this case, ACPI video driver does not need to do anything(actually, +it doesn't even know this happened). + +ii) For some laptops, the press of the hotkey will not generate the + scancode, instead, firmware will notify the video device ACPI node + about the event. The event value is defined in the ACPI spec. ACPI + video driver will generate an key type input event according to the + notify value it received and send the event to user space through the + input device it created: + + event keycode + 0x86 KEY_BRIGHTNESSUP + 0x87 KEY_BRIGHTNESSDOWN + etc. + +so this would lead to the same effect as case i) now. + +Once user space tool receives this event, it can modify the backlight +level through the sysfs interface. + +3 Change backlight level in the kernel + +This works for machines covered by case ii) in Section 2. Once the driver +received a notification, it will set the backlight level accordingly. This does +not affect the sending of event to user space, they are always sent to user +space regardless of whether or not the video module controls the backlight level +directly. This behaviour can be controlled through the brightness_switch_enabled +module parameter as documented in kernel-parameters.txt. It is recommended to +disable this behaviour once a GUI environment starts up and wants to have full +control of the backlight level. diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt index a3585eac83b6..19fa98e07bf7 100644 --- a/Documentation/cpu-freq/cpu-drivers.txt +++ b/Documentation/cpu-freq/cpu-drivers.txt @@ -186,7 +186,7 @@ As most cpufreq processors only allow for being set to a few specific frequencies, a "frequency table" with some functions might assist in some work of the processor driver. Such a "frequency table" consists of an array of struct cpufreq_frequency_table entries, with any value in -"index" you want to use, and the corresponding frequency in +"driver_data" you want to use, and the corresponding frequency in "frequency". At the end of the table, you need to add a cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END. And if you want to skip one entry in the table, set the frequency to @@ -214,10 +214,4 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, is the corresponding frequency table helper for the ->target stage. Just pass the values to this function, and the unsigned int index returns the number of the frequency table entry which contains -the frequency the CPU shall be set to. PLEASE NOTE: This is not the -"index" which is in this cpufreq_table_entry.index, but instead -cpufreq_table[index]. So, the new frequency is -cpufreq_table[index].frequency, and the value you stored into the -frequency table "index" field is -cpufreq_table[index].index. - +the frequency the CPU shall be set to. diff --git a/Documentation/cpu-hotplug.txt b/Documentation/cpu-hotplug.txt index 0efd1b905b9d..edd4b4df3932 100644 --- a/Documentation/cpu-hotplug.txt +++ b/Documentation/cpu-hotplug.txt @@ -370,8 +370,10 @@ A: There is no clear spec defined way from ACPI that can give us that CPUs in MADT as hotpluggable CPUS. In the case there are no disabled CPUS we assume 1/2 the number of CPUs currently present can be hotplugged. - Caveat: Today's ACPI MADT can only provide 256 entries since the apicid field - in MADT is only 8 bits. + Caveat: ACPI MADT can only provide 256 entries in systems with only ACPI 2.0c + or earlier ACPI version supported, because the apicid field in MADT is only + 8 bits. From ACPI 3.0, this limitation was removed since the apicid field + was extended to 32 bits with x2APIC introduced. User Space Notification diff --git a/Documentation/devicetree/bindings/clock/nspire-clock.txt b/Documentation/devicetree/bindings/clock/nspire-clock.txt new file mode 100644 index 000000000000..7c3bc8bb5b9f --- /dev/null +++ b/Documentation/devicetree/bindings/clock/nspire-clock.txt @@ -0,0 +1,24 @@ +TI-NSPIRE Clocks + +Required properties: +- compatible: Valid compatible properties include: + "lsi,nspire-cx-ahb-divider" for the AHB divider in the CX model + "lsi,nspire-classic-ahb-divider" for the AHB divider in the older model + "lsi,nspire-cx-clock" for the base clock in the CX model + "lsi,nspire-classic-clock" for the base clock in the older model + +- reg: Physical base address of the controller and length of memory mapped + region. + +Optional: +- clocks: For the "nspire-*-ahb-divider" compatible clocks, this is the parent + clock where it divides the rate from. + +Example: + +ahb_clk { + #clock-cells = <0>; + compatible = "lsi,nspire-cx-clock"; + reg = <0x900B0000 0x4>; + clocks = <&base_clk>; +}; diff --git a/Documentation/devicetree/bindings/clock/rockchip.txt b/Documentation/devicetree/bindings/clock/rockchip.txt new file mode 100644 index 000000000000..a891c823ed44 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/rockchip.txt @@ -0,0 +1,74 @@ +Device Tree Clock bindings for arch-rockchip + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +== Gate clocks == + +The gate registers form a continuos block which makes the dt node +structure a matter of taste, as either all gates can be put into +one gate clock spanning all registers or they can be divided into +the 10 individual gates containing 16 clocks each. +The code supports both approaches. + +Required properties: +- compatible : "rockchip,rk2928-gate-clk" +- reg : shall be the control register address(es) for the clock. +- #clock-cells : from common clock binding; shall be set to 1 +- clock-output-names : the corresponding gate names that the clock controls +- clocks : should contain the parent clock for each individual gate, + therefore the number of clocks elements should match the number of + clock-output-names + +Example using multiple gate clocks: + + clk_gates0: gate-clk@200000d0 { + compatible = "rockchip,rk2928-gate-clk"; + reg = <0x200000d0 0x4>; + clocks = <&dummy>, <&dummy>, + <&dummy>, <&dummy>, + <&dummy>, <&dummy>, + <&dummy>, <&dummy>, + <&dummy>, <&dummy>, + <&dummy>, <&dummy>, + <&dummy>, <&dummy>, + <&dummy>, <&dummy>; + + clock-output-names = + "gate_core_periph", "gate_cpu_gpll", + "gate_ddrphy", "gate_aclk_cpu", + "gate_hclk_cpu", "gate_pclk_cpu", + "gate_atclk_cpu", "gate_i2s0", + "gate_i2s0_frac", "gate_i2s1", + "gate_i2s1_frac", "gate_i2s2", + "gate_i2s2_frac", "gate_spdif", + "gate_spdif_frac", "gate_testclk"; + + #clock-cells = <1>; + }; + + clk_gates1: gate-clk@200000d4 { + compatible = "rockchip,rk2928-gate-clk"; + reg = <0x200000d4 0x4>; + clocks = <&xin24m>, <&xin24m>, + <&xin24m>, <&dummy>, + <&dummy>, <&xin24m>, + <&xin24m>, <&dummy>, + <&xin24m>, <&dummy>, + <&xin24m>, <&dummy>, + <&xin24m>, <&dummy>, + <&xin24m>, <&dummy>; + + clock-output-names = + "gate_timer0", "gate_timer1", + "gate_timer2", "gate_jtag", + "gate_aclk_lcdc1_src", "gate_otgphy0", + "gate_otgphy1", "gate_ddr_gpll", + "gate_uart0", "gate_frac_uart0", + "gate_uart1", "gate_frac_uart1", + "gate_uart2", "gate_frac_uart2", + "gate_uart3", "gate_frac_uart3"; + + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt index cc374651662c..66c75b2d6158 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si5351.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt @@ -44,6 +44,11 @@ Optional child node properties: - silabs,multisynth-source: source pll A(0) or B(1) of corresponding multisynth divider. - silabs,pll-master: boolean, multisynth can change pll frequency. +- silabs,disable-state : clock output disable state, shall be + 0 = clock output is driven LOW when disabled + 1 = clock output is driven HIGH when disabled + 2 = clock output is FLOATING (HIGH-Z) when disabled + 3 = clock output is NEVER disabled ==Example== diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 729f52426fe1..d495521a79d2 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -12,22 +12,30 @@ Required properties: "allwinner,sun4i-axi-clk" - for the AXI clock "allwinner,sun4i-axi-gates-clk" - for the AXI gates "allwinner,sun4i-ahb-clk" - for the AHB clock - "allwinner,sun4i-ahb-gates-clk" - for the AHB gates + "allwinner,sun4i-ahb-gates-clk" - for the AHB gates on A10 + "allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13 "allwinner,sun4i-apb0-clk" - for the APB0 clock - "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates + "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates on A10 + "allwinner,sun5i-a13-apb0-gates-clk" - for the APB0 gates on A13 "allwinner,sun4i-apb1-clk" - for the APB1 clock "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing - "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates + "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates on A10 + "allwinner,sun5i-a13-apb1-gates-clk" - for the APB1 gates on A13 Required properties for all clocks: - reg : shall be the control register address for the clock. - clocks : shall be the input parent clock(s) phandle for the clock - #clock-cells : from common clock binding; shall be set to 0 except for - "allwinner,sun4i-*-gates-clk" where it shall be set to 1 + "allwinner,*-gates-clk" where it shall be set to 1 -Additionally, "allwinner,sun4i-*-gates-clk" clocks require: +Additionally, "allwinner,*-gates-clk" clocks require: - clock-output-names : the corresponding gate names that the clock controls +Clock consumers should specify the desired clocks they use with a +"clocks" phandle cell. Consumers that are using a gated clock should +provide an additional ID in their clock property. The values of this +ID are documented in sunxi/<soc>-gates.txt. + For example: osc24M: osc24M@01c20050 { @@ -50,102 +58,3 @@ cpu: cpu@01c20054 { reg = <0x01c20054 0x4>; clocks = <&osc32k>, <&osc24M>, <&pll1>; }; - - - -Gate clock outputs - -The "allwinner,sun4i-*-gates-clk" clocks provide several gatable outputs; -their corresponding offsets as present on sun4i are listed below. Note that -some of these gates are not present on sun5i. - - * AXI gates ("allwinner,sun4i-axi-gates-clk") - - DRAM 0 - - * AHB gates ("allwinner,sun4i-ahb-gates-clk") - - USB0 0 - EHCI0 1 - OHCI0 2* - EHCI1 3 - OHCI1 4* - SS 5 - DMA 6 - BIST 7 - MMC0 8 - MMC1 9 - MMC2 10 - MMC3 11 - MS 12** - NAND 13 - SDRAM 14 - - ACE 16 - EMAC 17 - TS 18 - - SPI0 20 - SPI1 21 - SPI2 22 - SPI3 23 - PATA 24 - SATA 25** - GPS 26* - - VE 32 - TVD 33 - TVE0 34 - TVE1 35 - LCD0 36 - LCD1 37 - - CSI0 40 - CSI1 41 - - HDMI 43 - DE_BE0 44 - DE_BE1 45 - DE_FE0 46 - DE_FE1 47 - - MP 50 - - MALI400 52 - - * APB0 gates ("allwinner,sun4i-apb0-gates-clk") - - CODEC 0 - SPDIF 1* - AC97 2 - IIS 3 - - PIO 5 - IR0 6 - IR1 7 - - KEYPAD 10 - - * APB1 gates ("allwinner,sun4i-apb1-gates-clk") - - I2C0 0 - I2C1 1 - I2C2 2 - - CAN 4 - SCR 5 - PS20 6 - PS21 7 - - UART0 16 - UART1 17 - UART2 18 - UART3 19 - UART4 20 - UART5 21 - UART6 22 - UART7 23 - -Notation: - [*]: The datasheet didn't mention these, but they are present on AW code - [**]: The datasheet had this marked as "NC" but they are used on AW code diff --git a/Documentation/devicetree/bindings/clock/sunxi/sun4i-a10-gates.txt b/Documentation/devicetree/bindings/clock/sunxi/sun4i-a10-gates.txt new file mode 100644 index 000000000000..6a03475bbfe2 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi/sun4i-a10-gates.txt @@ -0,0 +1,93 @@ +Gate clock outputs +------------------ + + * AXI gates ("allwinner,sun4i-axi-gates-clk") + + DRAM 0 + + * AHB gates ("allwinner,sun4i-ahb-gates-clk") + + USB0 0 + EHCI0 1 + OHCI0 2* + EHCI1 3 + OHCI1 4* + SS 5 + DMA 6 + BIST 7 + MMC0 8 + MMC1 9 + MMC2 10 + MMC3 11 + MS 12** + NAND 13 + SDRAM 14 + + ACE 16 + EMAC 17 + TS 18 + + SPI0 20 + SPI1 21 + SPI2 22 + SPI3 23 + PATA 24 + SATA 25** + GPS 26* + + VE 32 + TVD 33 + TVE0 34 + TVE1 35 + LCD0 36 + LCD1 37 + + CSI0 40 + CSI1 41 + + HDMI 43 + DE_BE0 44 + DE_BE1 45 + DE_FE1 46 + DE_FE1 47 + + MP 50 + + MALI400 52 + + * APB0 gates ("allwinner,sun4i-apb0-gates-clk") + + CODEC 0 + SPDIF 1* + AC97 2 + IIS 3 + + PIO 5 + IR0 6 + IR1 7 + + KEYPAD 10 + + * APB1 gates ("allwinner,sun4i-apb1-gates-clk") + + I2C0 0 + I2C1 1 + I2C2 2 + + CAN 4 + SCR 5 + PS20 6 + PS21 7 + + UART0 16 + UART1 17 + UART2 18 + UART3 19 + UART4 20 + UART5 21 + UART6 22 + UART7 23 + +Notation: + [*]: The datasheet didn't mention these, but they are present on AW code + [**]: The datasheet had this marked as "NC" but they are used on AW code diff --git a/Documentation/devicetree/bindings/clock/sunxi/sun5i-a13-gates.txt b/Documentation/devicetree/bindings/clock/sunxi/sun5i-a13-gates.txt new file mode 100644 index 000000000000..006b6dfc4703 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi/sun5i-a13-gates.txt @@ -0,0 +1,58 @@ +Gate clock outputs +------------------ + + * AXI gates ("allwinner,sun4i-axi-gates-clk") + + DRAM 0 + + * AHB gates ("allwinner,sun5i-a13-ahb-gates-clk") + + USBOTG 0 + EHCI 1 + OHCI 2 + + SS 5 + DMA 6 + BIST 7 + MMC0 8 + MMC1 9 + MMC2 10 + + NAND 13 + SDRAM 14 + + SPI0 20 + SPI1 21 + SPI2 22 + + STIMER 28 + + VE 32 + + LCD 36 + + CSI 40 + + DE_BE 44 + + DE_FE 46 + + IEP 51 + MALI400 52 + + * APB0 gates ("allwinner,sun5i-a13-apb0-gates-clk") + + CODEC 0 + + PIO 5 + IR 6 + + * APB1 gates ("allwinner,sun5i-a13-apb1-gates-clk") + + I2C0 0 + I2C1 1 + I2C2 2 + + UART1 17 + + UART3 19 diff --git a/Documentation/devicetree/bindings/clock/vt8500.txt b/Documentation/devicetree/bindings/clock/vt8500.txt index a880c70d0047..91d71cc0314a 100644 --- a/Documentation/devicetree/bindings/clock/vt8500.txt +++ b/Documentation/devicetree/bindings/clock/vt8500.txt @@ -8,6 +8,8 @@ Required properties: - compatible : shall be one of the following: "via,vt8500-pll-clock" - for a VT8500/WM8505 PLL clock "wm,wm8650-pll-clock" - for a WM8650 PLL clock + "wm,wm8750-pll-clock" - for a WM8750 PLL clock + "wm,wm8850-pll-clock" - for a WM8850 PLL clock "via,vt8500-device-clock" - for a VT/WM device clock Required properties for PLL clocks: diff --git a/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt b/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt new file mode 100644 index 000000000000..63bf4becd5f0 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt @@ -0,0 +1,48 @@ +Xilinx plb/axi GPIO controller + +Dual channel GPIO controller with configurable number of pins +(from 1 to 32 per channel). Every pin can be configured as +input/output/tristate. Both channels share the same global IRQ but +local interrupts can be enabled on channel basis. + +Required properties: +- compatible : Should be "xlnx,xps-gpio-1.00.a" +- reg : Address and length of the register set for the device +- #gpio-cells : Should be two. The first cell is the pin number and the + second cell is used to specify optional parameters (currently unused). +- gpio-controller : Marks the device node as a GPIO controller. + +Optional properties: +- interrupts : Interrupt mapping for GPIO IRQ. +- interrupt-parent : Phandle for the interrupt controller that + services interrupts for this device. +- xlnx,all-inputs : if n-th bit is setup, GPIO-n is input +- xlnx,dout-default : if n-th bit is 1, GPIO-n default value is 1 +- xlnx,gpio-width : gpio width +- xlnx,tri-default : if n-th bit is 1, GPIO-n is in tristate mode +- xlnx,is-dual : if 1, controller also uses the second channel +- xlnx,all-inputs-2 : as above but for the second channel +- xlnx,dout-default-2 : as above but the second channel +- xlnx,gpio2-width : as above but for the second channel +- xlnx,tri-default-2 : as above but for the second channel + + +Example: +gpio: gpio@40000000 { + #gpio-cells = <2>; + compatible = "xlnx,xps-gpio-1.00.a"; + gpio-controller ; + interrupt-parent = <µblaze_0_intc>; + interrupts = < 6 2 >; + reg = < 0x40000000 0x10000 >; + xlnx,all-inputs = <0x0>; + xlnx,all-inputs-2 = <0x0>; + xlnx,dout-default = <0x0>; + xlnx,dout-default-2 = <0x0>; + xlnx,gpio-width = <0x2>; + xlnx,gpio2-width = <0x2>; + xlnx,interrupt-present = <0x1>; + xlnx,is-dual = <0x1>; + xlnx,tri-default = <0xffffffff>; + xlnx,tri-default-2 = <0xffffffff>; +} ; diff --git a/Documentation/devicetree/bindings/mfd/max77693.txt b/Documentation/devicetree/bindings/mfd/max77693.txt new file mode 100644 index 000000000000..11921cc417bf --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/max77693.txt @@ -0,0 +1,55 @@ +Maxim MAX77693 multi-function device + +MAX77693 is a Multifunction device with the following submodules: +- PMIC, +- CHARGER, +- LED, +- MUIC, +- HAPTIC + +It is interfaced to host controller using i2c. +This document describes the bindings for the mfd device. + +Required properties: +- compatible : Must be "maxim,max77693". +- reg : Specifies the i2c slave address of PMIC block. +- interrupts : This i2c device has an IRQ line connected to the main SoC. +- interrupt-parent : The parent interrupt controller. + +Optional properties: +- regulators : The regulators of max77693 have to be instantiated under subnod + named "regulators" using the following format. + + regulators { + regualtor-compatible = ESAFEOUT1/ESAFEOUT2/CHARGER + standard regulator constratints[*]. + }; + + [*] refer Documentation/devicetree/bindings/regulator/regulator.txt + +Example: + max77693@66 { + compatible = "maxim,max77693"; + reg = <0x66>; + interrupt-parent = <&gpx1>; + interrupts = <5 2>; + + regulators { + esafeout@1 { + regulator-compatible = "ESAFEOUT1"; + regulator-name = "ESAFEOUT1"; + regulator-boot-on; + }; + esafeout@2 { + regulator-compatible = "ESAFEOUT2"; + regulator-name = "ESAFEOUT2"; + }; + charger@0 { + regulator-compatible = "CHARGER"; + regulator-name = "CHARGER"; + regulator-min-microamp = <60000>; + regulator-max-microamp = <2580000>; + regulator-boot-on; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,vf610-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fsl,vf610-pinctrl.txt new file mode 100644 index 000000000000..ddcdeb697c29 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/fsl,vf610-pinctrl.txt @@ -0,0 +1,41 @@ +Freescale Vybrid VF610 IOMUX Controller + +Please refer to fsl,imx-pinctrl.txt in this directory for common binding part +and usage. + +Required properties: +- compatible: "fsl,vf610-iomuxc" +- fsl,pins: two integers array, represents a group of pins mux and config + setting. The format is fsl,pins = <PIN_FUNC_ID CONFIG>, PIN_FUNC_ID is + a pin working on a specific function, CONFIG is the pad setting value + such as pull-up, speed, ode for this pin. Please refer to Vybrid VF610 + datasheet for the valid pad config settings. + +CONFIG bits definition: +PAD_CTL_SPEED_LOW (1 << 12) +PAD_CTL_SPEED_MED (2 << 12) +PAD_CTL_SPEED_HIGH (3 << 12) +PAD_CTL_SRE_FAST (1 << 11) +PAD_CTL_SRE_SLOW (0 << 11) +PAD_CTL_ODE (1 << 10) +PAD_CTL_HYS (1 << 9) +PAD_CTL_DSE_DISABLE (0 << 6) +PAD_CTL_DSE_150ohm (1 << 6) +PAD_CTL_DSE_75ohm (2 << 6) +PAD_CTL_DSE_50ohm (3 << 6) +PAD_CTL_DSE_37ohm (4 << 6) +PAD_CTL_DSE_30ohm (5 << 6) +PAD_CTL_DSE_25ohm (6 << 6) +PAD_CTL_DSE_20ohm (7 << 6) +PAD_CTL_PUS_100K_DOWN (0 << 4) +PAD_CTL_PUS_47K_UP (1 << 4) +PAD_CTL_PUS_100K_UP (2 << 4) +PAD_CTL_PUS_22K_UP (3 << 4) +PAD_CTL_PKE (1 << 3) +PAD_CTL_PUE (1 << 2) +PAD_CTL_OBE_ENABLE (1 << 1) +PAD_CTL_IBE_ENABLE (1 << 0) +PAD_CTL_OBE_IBE_ENABLE (3 << 0) + +Please refer to vf610-pinfunc.h in device tree source folder +for all available PIN_FUNC_ID for Vybrid VF610. diff --git a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt new file mode 100644 index 000000000000..a186181c402b --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt @@ -0,0 +1,127 @@ +ImgTec TZ1090 PDC pin controller + +Required properties: +- compatible: "img,tz1090-pdc-pinctrl" +- reg: Should contain the register physical address and length of the + SOC_GPIO_CONTROL registers in the PDC register region. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +TZ1090-PDC's pin configuration nodes act as a container for an abitrary number +of subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as pull-up, drive strength, etc. + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Each subnode only affects those parameters that are explicitly listed. In +other words, a subnode that lists a mux function but no pin configuration +parameters implies no information about any pin configuration parameters. +Similarly, a pin subnode that describes a pullup parameter implies no +information about e.g. the mux function. For this reason, even seemingly boolean +values are actually tristates in this binding: unspecified, off, or on. +Unspecified is represented as an absent property, and off/on are represented as +integer values 0 and 1. + +Required subnode-properties: +- tz1090,pins : An array of strings. Each string contains the name of a pin or + group. Valid values for these names are listed below. + +Optional subnode-properties: +- tz1090,function: A string containing the name of the function to mux to the + pin or group. Valid values for function names are listed below, including + which pingroups can be muxed to them. +- supported generic pinconfig properties (for further details see + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt): + - bias-disable + - bias-high-impedance + - bias-bus-hold + - bias-pull-up + - bias-pull-down + - input-schmitt-enable + - input-schmitt-disable + - drive-strength: Integer, control drive strength of pins in mA. + 2: 2mA + 4: 4mA + 8: 8mA + 12: 12mA + - low-power-enable: Flag, power-on-start weak pull-down for invalid power. + - low-power-disable: Flag, power-on-start weak pull-down disabled. + +Note that many of these properties are only valid for certain specific pins +or groups. See the TZ1090 TRM for complete details regarding which groups +support which functionality. The Linux pinctrl driver may also be a useful +reference. + +Valid values for pin and group names are: + + pins: + + These all support bias-high-impediance, bias-pull-up, bias-pull-down, and + bias-bus-hold (which can also be provided to any of the groups below to set + it for all gpio pins in that group). + + gpio0, gpio1, sys_wake0, sys_wake1, sys_wake2, ir_data, ext_power. + + mux groups: + + These all support function. + + gpio0 + pins: gpio0. + function: ir_mod_stable_out. + gpio1 + pins: gpio1. + function: ir_mod_power_out. + + drive groups: + + These support input-schmitt-enable, input-schmitt-disable, + drive-strength, low-power-enable, and low-power-disable. + + pdc + pins: gpio0, gpio1, sys_wake0, sys_wake1, sys_wake2, ir_data, + ext_power. + +Example: + + pinctrl_pdc: pinctrl@02006500 { + #gpio-range-cells = <3>; + compatible = "img,tz1090-pdc-pinctrl"; + reg = <0x02006500 0x100>; + }; + +Example board file extracts: + + &pinctrl_pdc { + pinctrl-names = "default"; + pinctrl-0 = <&syswake_default>; + + syswake_default: syswakes { + syswake_cfg { + tz1090,pins = "sys_wake0", + "sys_wake1", + "sys_wake2"; + pull-up; + }; + }; + irmod_default: irmod { + gpio0_cfg { + tz1090,pins = "gpio0"; + tz1090,function = "ir_mod_stable_out"; + }; + gpio1_cfg { + tz1090,pins = "gpio1"; + tz1090,function = "ir_mod_power_out"; + }; + }; + }; + + ir: ir@02006200 { + pinctrl-names = "default"; + pinctrl-0 = <&irmod_default>; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt new file mode 100644 index 000000000000..4b27c99f7f9d --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt @@ -0,0 +1,227 @@ +ImgTec TZ1090 pin controller + +Required properties: +- compatible: "img,tz1090-pinctrl" +- reg: Should contain the register physical address and length of the pad + configuration registers (CR_PADS_* and CR_IF_CTL0). + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +TZ1090's pin configuration nodes act as a container for an abitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as pull-up, drive strength, etc. + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Each subnode only affects those parameters that are explicitly listed. In +other words, a subnode that lists a mux function but no pin configuration +parameters implies no information about any pin configuration parameters. +Similarly, a pin subnode that describes a pullup parameter implies no +information about e.g. the mux function. For this reason, even seemingly boolean +values are actually tristates in this binding: unspecified, off, or on. +Unspecified is represented as an absent property, and off/on are represented as +integer values 0 and 1. + +Required subnode-properties: +- tz1090,pins : An array of strings. Each string contains the name of a pin or + group. Valid values for these names are listed below. + +Optional subnode-properties: +- tz1090,function: A string containing the name of the function to mux to the + pin or group. Valid values for function names are listed below, including + which pingroups can be muxed to them. +- supported generic pinconfig properties (for further details see + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt): + - bias-disable + - bias-high-impedance + - bias-bus-hold + - bias-pull-up + - bias-pull-down + - input-schmitt-enable + - input-schmitt-disable + - drive-strength: Integer, control drive strength of pins in mA. + 2: 2mA + 4: 4mA + 8: 8mA + 12: 12mA + + +Note that many of these properties are only valid for certain specific pins +or groups. See the TZ1090 TRM for complete details regarding which groups +support which functionality. The Linux pinctrl driver may also be a useful +reference. + +Valid values for pin and group names are: + + gpio pins: + + These all support bias-high-impediance, bias-pull-up, bias-pull-down, and + bias-bus-hold (which can also be provided to any of the groups below to set + it for all pins in that group). + + They also all support the some form of muxing. Any pins which are contained + in one of the mux groups (see below) can be muxed only to the functions + supported by the mux group. All other pins can be muxed to the "perip" + function which which enables them with their intended peripheral. + + Different pins in the same mux group cannot be muxed to different functions, + however it is possible to mux only a subset of the pins in a mux group to a + particular function and leave the remaining pins unmuxed. This is useful if + the board connects certain pins in a group to other devices to be controlled + by GPIO, and you don't want the usual peripheral to have any control of the + pin. + + ant_sel0, ant_sel1, gain0, gain1, gain2, gain3, gain4, gain5, gain6, gain7, + i2s_bclk_out, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2, i2s_lrclk_out, + i2s_mclk, pa_on, pdm_a, pdm_b, pdm_c, pdm_d, pll_on, rx_hp, rx_on, + scb0_sclk, scb0_sdat, scb1_sclk, scb1_sdat, scb2_sclk, scb2_sdat, sdh_cd, + sdh_clk_in, sdh_wp, sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, sdio_d3, + spi0_cs0, spi0_cs1, spi0_cs2, spi0_din, spi0_dout, spi0_mclk, spi1_cs0, + spi1_cs1, spi1_cs2, spi1_din, spi1_dout, spi1_mclk, tft_blank_ls, tft_blue0, + tft_blue1, tft_blue2, tft_blue3, tft_blue4, tft_blue5, tft_blue6, tft_blue7, + tft_green0, tft_green1, tft_green2, tft_green3, tft_green4, tft_green5, + tft_green6, tft_green7, tft_hsync_nr, tft_panelclk, tft_pwrsave, tft_red0, + tft_red1, tft_red2, tft_red3, tft_red4, tft_red5, tft_red6, tft_red7, + tft_vd12acb, tft_vdden_gd, tft_vsync_ns, tx_on, uart0_cts, uart0_rts, + uart0_rxd, uart0_txd, uart1_rxd, uart1_txd. + + bias-high-impediance: supported. + bias-pull-up: supported. + bias-pull-down: supported. + bias-bus-hold: supported. + function: perip or those supported by pin's mux group. + + other pins: + + These other pins are part of various pin groups below, but can't be + controlled as GPIOs. They do however support bias-high-impediance, + bias-pull-up, bias-pull-down, and bias-bus-hold (which can also be provided + to any of the groups below to set it for all pins in that group). + + clk_out0, clk_out1, tck, tdi, tdo, tms, trst. + + bias-high-impediance: supported. + bias-pull-up: supported. + bias-pull-down: supported. + bias-bus-hold: supported. + + mux groups: + + These all support function, and some support drive configs. + + afe + pins: tx_on, rx_on, pll_on, pa_on, rx_hp, ant_sel0, + ant_sel1, gain0, gain1, gain2, gain3, gain4, + gain5, gain6, gain7. + function: afe, ts_out_0. + input-schmitt-enable: supported. + input-schmitt-disable: supported. + drive-strength: supported. + pdm_d + pins: pdm_d. + function: pdm_dac, usb_vbus. + sdh + pins: sdh_cd, sdh_wp, sdh_clk_in. + function: sdh, sdio. + sdio + pins: sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, + sdio_d3. + function: sdio, sdh. + spi1_cs2 + pins: spi1_cs2. + function: spi1_cs2, usb_vbus. + tft + pins: tft_red0, tft_red1, tft_red2, tft_red3, + tft_red4, tft_red5, tft_red6, tft_red7, + tft_green0, tft_green1, tft_green2, tft_green3, + tft_green4, tft_green5, tft_green6, tft_green7, + tft_blue0, tft_blue1, tft_blue2, tft_blue3, + tft_blue4, tft_blue5, tft_blue6, tft_blue7, + tft_vdden_gd, tft_panelclk, tft_blank_ls, + tft_vsync_ns, tft_hsync_nr, tft_vd12acb, + tft_pwrsave. + function: tft, ext_dac, not_iqadc_stb, iqdac_stb, ts_out_1, + lcd_trace, phy_ringosc. + input-schmitt-enable: supported. + input-schmitt-disable: supported. + drive-strength: supported. + + drive groups: + + These all support input-schmitt-enable, input-schmitt-disable, + and drive-strength. + + jtag + pins: tck, trst, tdi, tdo, tms. + scb1 + pins: scb1_sdat, scb1_sclk. + scb2 + pins: scb2_sdat, scb2_sclk. + spi0 + pins: spi0_mclk, spi0_cs0, spi0_cs1, spi0_cs2, spi0_dout, spi0_din. + spi1 + pins: spi1_mclk, spi1_cs0, spi1_cs1, spi1_cs2, spi1_dout, spi1_din. + uart + pins: uart0_txd, uart0_rxd, uart0_rts, uart0_cts, + uart1_txd, uart1_rxd. + drive_i2s + pins: clk_out1, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2, + i2s_lrclk_out, i2s_bclk_out, i2s_mclk. + drive_pdm + pins: clk_out0, pdm_b, pdm_a. + drive_scb0 + pins: scb0_sclk, scb0_sdat, pdm_d, pdm_c. + drive_sdio + pins: sdio_clk, sdio_cmd, sdio_d0, sdio_d1, sdio_d2, sdio_d3, + sdh_wp, sdh_cd, sdh_clk_in. + + convenience groups: + + These are just convenient groupings of pins and don't support any drive + configs. + + uart0 + pins: uart0_cts, uart0_rts, uart0_rxd, uart0_txd. + uart1 + pins: uart1_rxd, uart1_txd. + scb0 + pins: scb0_sclk, scb0_sdat. + i2s + pins: i2s_bclk_out, i2s_din, i2s_dout0, i2s_dout1, i2s_dout2, + i2s_lrclk_out, i2s_mclk. + +Example: + + pinctrl: pinctrl@02005800 { + #gpio-range-cells = <3>; + compatible = "img,tz1090-pinctrl"; + reg = <0x02005800 0xe4>; + }; + +Example board file extract: + + &pinctrl { + uart0_default: uart0 { + uart0_cfg { + tz1090,pins = "uart0_rxd", + "uart0_txd"; + tz1090,function = "perip"; + }; + }; + tft_default: tft { + tft_cfg { + tz1090,pins = "tft"; + tz1090,function = "tft"; + }; + }; + }; + + uart@02004b00 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_default>; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,dove-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,dove-pinctrl.txt index a648aaad6110..50ec3512a292 100644 --- a/Documentation/devicetree/bindings/pinctrl/marvell,dove-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/marvell,dove-pinctrl.txt @@ -10,29 +10,31 @@ Required properties: Available mpp pins/groups and functions: Note: brackets (x) are not part of the mpp name for marvell,function and given only for more detailed description in this document. +Note: pmu* also allows for Power Management functions listed below name pins functions ================================================================================ -mpp0 0 gpio, pmu, uart2(rts), sdio0(cd), lcd0(pwm) -mpp1 1 gpio, pmu, uart2(cts), sdio0(wp), lcd1(pwm) +mpp0 0 gpio, pmu, uart2(rts), sdio0(cd), lcd0(pwm), pmu* +mpp1 1 gpio, pmu, uart2(cts), sdio0(wp), lcd1(pwm), pmu* mpp2 2 gpio, pmu, uart2(txd), sdio0(buspwr), sata(prsnt), - uart1(rts) + uart1(rts), pmu* mpp3 3 gpio, pmu, uart2(rxd), sdio0(ledctrl), sata(act), - uart1(cts), lcd-spi(cs1) -mpp4 4 gpio, pmu, uart3(rts), sdio1(cd), spi1(miso) -mpp5 5 gpio, pmu, uart3(cts), sdio1(wp), spi1(cs) -mpp6 6 gpio, pmu, uart3(txd), sdio1(buspwr), spi1(mosi) -mpp7 7 gpio, pmu, uart3(rxd), sdio1(ledctrl), spi1(sck) -mpp8 8 gpio, pmu, watchdog(rstout) -mpp9 9 gpio, pmu, pex1(clkreq) -mpp10 10 gpio, pmu, ssp(sclk) + uart1(cts), lcd-spi(cs1), pmu* +mpp4 4 gpio, pmu, uart3(rts), sdio1(cd), spi1(miso), pmu* +mpp5 5 gpio, pmu, uart3(cts), sdio1(wp), spi1(cs), pmu* +mpp6 6 gpio, pmu, uart3(txd), sdio1(buspwr), spi1(mosi), pmu* +mpp7 7 gpio, pmu, uart3(rxd), sdio1(ledctrl), spi1(sck), pmu* +mpp8 8 gpio, pmu, watchdog(rstout), pmu* +mpp9 9 gpio, pmu, pex1(clkreq), pmu* +mpp10 10 gpio, pmu, ssp(sclk), pmu* mpp11 11 gpio, pmu, sata(prsnt), sata-1(act), sdio0(ledctrl), - sdio1(ledctrl), pex0(clkreq) -mpp12 12 gpio, pmu, uart2(rts), audio0(extclk), sdio1(cd), sata(act) + sdio1(ledctrl), pex0(clkreq), pmu* +mpp12 12 gpio, pmu, uart2(rts), audio0(extclk), sdio1(cd), + sata(act), pmu* mpp13 13 gpio, pmu, uart2(cts), audio1(extclk), sdio1(wp), - ssp(extclk) -mpp14 14 gpio, pmu, uart2(txd), sdio1(buspwr), ssp(rxd) -mpp15 15 gpio, pmu, uart2(rxd), sdio1(ledctrl), ssp(sfrm) + ssp(extclk), pmu* +mpp14 14 gpio, pmu, uart2(txd), sdio1(buspwr), ssp(rxd), pmu* +mpp15 15 gpio, pmu, uart2(rxd), sdio1(ledctrl), ssp(sfrm), pmu* mpp16 16 gpio, uart3(rts), sdio0(cd), ac97(sdi1), lcd-spi(cs1) mpp17 17 gpio, uart3(cts), sdio0(wp), ac97(sdi2), twsi(sda), ac97-1(sysclko) @@ -57,6 +59,21 @@ mpp_nand 64-71 gpo, nand audio0 - i2s, ac97 twsi - none, opt1, opt2, opt3 +Power Management functions (pmu*): +pmu-nc Pin not driven by any PM function +pmu-low Pin driven low (0) +pmu-high Pin driven high (1) +pmic(sdi) Pin is used for PMIC SDI +cpu-pwr-down Pin is used for CPU_PWRDWN +standby-pwr-down Pin is used for STBY_PWRDWN +core-pwr-good Pin is used for CORE_PWR_GOOD (Pins 0-7 only) +cpu-pwr-good Pin is used for CPU_PWR_GOOD (Pins 8-15 only) +bat-fault Pin is used for BATTERY_FAULT +ext0-wakeup Pin is used for EXT0_WU +ext1-wakeup Pin is used for EXT0_WU +ext2-wakeup Pin is used for EXT0_WU +pmu-blink Pin is used for blink function + Notes: * group "mpp_audio1" allows the following functions and gpio pins: - gpio : gpio on pins 52-57 diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt index c95ea8278f87..aeb3c995cc04 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt @@ -126,3 +126,51 @@ device; they may be grandchildren, for example. Whether this is legal, and whether there is any interaction between the child and intermediate parent nodes, is again defined entirely by the binding for the individual pin controller device. + +== Using generic pinconfig options == + +Generic pinconfig parameters can be used by defining a separate node containing +the applicable parameters (and optional values), like: + +pcfg_pull_up: pcfg_pull_up { + bias-pull-up; + drive-strength = <20>; +}; + +This node should then be referenced in the appropriate pinctrl node as a phandle +and parsed in the driver using the pinconf_generic_parse_dt_config function. + +Supported configuration parameters are: + +bias-disable - disable any pin bias +bias-high-impedance - high impedance mode ("third-state", "floating") +bias-bus-hold - latch weakly +bias-pull-up - pull up the pin +bias-pull-down - pull down the pin +bias-pull-pin-default - use pin-default pull state +drive-push-pull - drive actively high and low +drive-open-drain - drive with open drain +drive-open-source - drive with open source +drive-strength - sink or source at most X mA +input-schmitt-enable - enable schmitt-trigger mode +input-schmitt-disable - disable schmitt-trigger mode +input-debounce - debounce mode with debound time X +low-power-enable - enable low power mode +low-power-disable - disable low power mode +output-low - set the pin to output mode with low level +output-high - set the pin to output mode with high level + +Arguments for parameters: + +- bias-pull-up, -down and -pin-default take as optional argument on hardware + supporting it the pull strength in Ohm. bias-disable will disable the pull. + +- drive-strength takes as argument the target strength in mA. + +- input-debounce takes the debounce time in usec as argument + or 0 to disable debouncing + +All parameters not listed here, do not take an argument. + +More in-depth documentation on these parameters can be found in +<include/linux/pinctrl/pinconfig-generic.h> diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt index 08f0c3d01575..5a02e30dd262 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt @@ -18,7 +18,8 @@ Optional properties: pin functions is ignored - pinctrl-single,bit-per-mux : boolean to indicate that one register controls - more than one pin + more than one pin, for which "pinctrl-single,function-mask" property specifies + position mask of pin. - pinctrl-single,drive-strength : array of value that are used to configure drive strength in the pinmux register. They're value of drive strength diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt new file mode 100644 index 000000000000..05bf82a07dfd --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-st.txt @@ -0,0 +1,110 @@ +*ST pin controller. + +Each multi-function pin is controlled, driven and routed through the +PIO multiplexing block. Each pin supports GPIO functionality (ALT0) +and multiple alternate functions(ALT1 - ALTx) that directly connect +the pin to different hardware blocks. + +When a pin is in GPIO mode, Output Enable (OE), Open Drain(OD), and +Pull Up (PU) are driven by the related PIO block. + +ST pinctrl driver controls PIO multiplexing block and also interacts with +gpio driver to configure a pin. + +Required properties: (PIO multiplexing block) +- compatible : should be "st,<SOC>-<pio-block>-pinctrl" + like st,stih415-sbc-pinctrl, st,stih415-front-pinctrl and so on. +- gpio-controller : Indicates this device is a GPIO controller +- #gpio-cells : Should be one. The first cell is the pin number. +- st,retime-pin-mask : Should be mask to specify which pins can be retimed. + If the property is not present, it is assumed that all the pins in the + bank are capable of retiming. Retiming is mainly used to improve the + IO timing margins of external synchronous interfaces. +- st,bank-name : Should be a name string for this bank as + specified in datasheet. +- st,syscfg : Should be a phandle of the syscfg node. + +Example: + pin-controller-sbc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,stih415-sbc-pinctrl"; + st,syscfg = <&syscfg_sbc>; + ranges = <0 0xfe610000 0x5000>; + PIO0: gpio@fe610000 { + gpio-controller; + #gpio-cells = <1>; + reg = <0 0x100>; + st,bank-name = "PIO0"; + }; + ... + pin-functions nodes follow... + }; + + +Contents of function subnode node: +---------------------- +Required properties for pin configuration node: +- st,pins : Child node with list of pins with configuration. + +Below is the format of how each pin conf should look like. + +<bank offset mux mode rt_type rt_delay rt_clk> + +Every PIO is represented with 4-7 parameters depending on retime configuration. +Each parameter is explained as below. + +-bank : Should be bank phandle to which this PIO belongs. +-offset : Offset in the PIO bank. +-mux : Should be alternate function number associated this pin. + Use same numbers from datasheet. +-mode :pin configuration is selected from one of the below values. + IN + IN_PU + OUT + BIDIR + BIDIR_PU + +-rt_type Retiming Configuration for the pin. + Possible retime configuration are: + + ------- ------------- + value args + ------- ------------- + NICLK <delay> <clk> + ICLK_IO <delay> <clk> + BYPASS <delay> + DE_IO <delay> <clk> + SE_ICLK_IO <delay> <clk> + SE_NICLK_IO <delay> <clk> + +- delay is retime delay in pico seconds as mentioned in data sheet. + +- rt_clk :clk to be use for retime. + Possible values are: + CLK_A + CLK_B + CLK_C + CLK_D + +Example of mmcclk pin which is a bi-direction pull pu with retime config +as non inverted clock retimed with CLK_B and delay of 0 pico seconds: + +pin-controller { + ... + mmc0 { + pinctrl_mmc: mmc { + st,pins { + mmcclk = <&PIO13 4 ALT4 BIDIR_PU NICLK 0 CLK_B>; + ... + }; + }; + ... + }; +}; + +sdhci0:sdhci@fe810000{ + ... + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mmc>; +}; diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt new file mode 100644 index 000000000000..d5dac7b843a9 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/renesas,pfc-pinctrl.txt @@ -0,0 +1,153 @@ +* Renesas Pin Function Controller (GPIO and Pin Mux/Config) + +The Pin Function Controller (PFC) is a Pin Mux/Config controller. On SH7372, +SH73A0, R8A73A4 and R8A7740 it also acts as a GPIO controller. + + +Pin Control +----------- + +Required Properties: + + - compatible: should be one of the following. + - "renesas,pfc-r8a73a4": for R8A73A4 (R-Mobile APE6) compatible pin-controller. + - "renesas,pfc-r8a7740": for R8A7740 (R-Mobile A1) compatible pin-controller. + - "renesas,pfc-r8a7778": for R8A7778 (R-Mobile M1) compatible pin-controller. + - "renesas,pfc-r8a7779": for R8A7779 (R-Car H1) compatible pin-controller. + - "renesas,pfc-r8a7790": for R8A7790 (R-Car H2) compatible pin-controller. + - "renesas,pfc-sh7372": for SH7372 (SH-Mobile AP4) compatible pin-controller. + - "renesas,pfc-sh73a0": for SH73A0 (SH-Mobile AG5) compatible pin-controller. + + - reg: Base address and length of each memory resource used by the pin + controller hardware module. + +Optional properties: + + - #gpio-range-cells: Mandatory when the PFC doesn't handle GPIO, forbidden + otherwise. Should be 3. + +The PFC node also acts as a container for pin configuration nodes. Please refer +to pinctrl-bindings.txt in this directory for the definition of the term "pin +configuration node" and for the common pinctrl bindings used by client devices. + +Each pin configuration node represents a desired configuration for a pin, a +pin group, or a list of pins or pin groups. The configuration can include the +function to select on those pin(s) and pin configuration parameters (such as +pull-up and pull-down). + +Pin configuration nodes contain pin configuration properties, either directly +or grouped in child subnodes. Both pin muxing and configuration parameters can +be grouped in that way and referenced as a single pin configuration node by +client devices. + +A configuration node or subnode must reference at least one pin (through the +pins or pin groups properties) and contain at least a function or one +configuration parameter. When the function is present only pin groups can be +used to reference pins. + +All pin configuration nodes and subnodes names are ignored. All of those nodes +are parsed through phandles and processed purely based on their content. + +Pin Configuration Node Properties: + +- renesas,pins : An array of strings, each string containing the name of a pin. +- renesas,groups : An array of strings, each string containing the name of a pin + group. + +- renesas,function: A string containing the name of the function to mux to the + pin group(s) specified by the renesas,groups property + + Valid values for pin, group and function names can be found in the group and + function arrays of the PFC data file corresponding to the SoC + (drivers/pinctrl/sh-pfc/pfc-*.c) + +The pin configuration parameters use the generic pinconf bindings defined in +pinctrl-bindings.txt in this directory. The supported parameters are +bias-disable, bias-pull-up and bias-pull-down. + + +GPIO +---- + +On SH7372, SH73A0, R8A73A4 and R8A7740 the PFC node is also a GPIO controller +node. + +Required Properties: + + - gpio-controller: Marks the device node as a gpio controller. + + - #gpio-cells: Should be 2. The first cell is the GPIO number and the second + cell specifies GPIO flags, as defined in <dt-bindings/gpio/gpio.h>. Only the + GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported. + +The syntax of the gpio specifier used by client nodes should be the following +with values derived from the SoC user manual. + + <[phandle of the gpio controller node] + [pin number within the gpio controller] + [flags]> + +On other mach-shmobile platforms GPIO is handled by the gpio-rcar driver. +Please refer to Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt +for documentation of the GPIO device tree bindings on those platforms. + + +Examples +-------- + +Example 1: SH73A0 (SH-Mobile AG5) pin controller node + + pfc: pfc@e6050000 { + compatible = "renesas,pfc-sh73a0"; + reg = <0xe6050000 0x8000>, + <0xe605801c 0x1c>; + gpio-controller; + #gpio-cells = <2>; + }; + +Example 2: A GPIO LED node that references a GPIO + + #include <dt-bindings/gpio/gpio.h> + + leds { + compatible = "gpio-leds"; + led1 { + gpios = <&pfc 20 GPIO_ACTIVE_LOW>; + }; + }; + +Example 3: KZM-A9-GT (SH-Mobile AG5) default pin state hog and pin control maps + for the MMCIF and SCIFA4 devices + + &pfc { + pinctrl-0 = <&scifa4_pins>; + pinctrl-names = "default"; + + mmcif_pins: mmcif { + mux { + renesas,groups = "mmc0_data8_0", "mmc0_ctrl_0"; + renesas,function = "mmc0"; + }; + cfg { + renesas,groups = "mmc0_data8_0"; + renesas,pins = "PORT279"; + bias-pull-up; + }; + }; + + scifa4_pins: scifa4 { + renesas,groups = "scifa4_data", "scifa4_ctrl"; + renesas,function = "scifa4"; + }; + }; + +Example 4: KZM-A9-GT (SH-Mobile AG5) default pin state for the MMCIF device + + &mmcif { + pinctrl-0 = <&mmcif_pins>; + pinctrl-names = "default"; + + bus-width = <8>; + vmmc-supply = <®_1p8v>; + status = "okay"; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt new file mode 100644 index 000000000000..b0fb1018d7ad --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt @@ -0,0 +1,97 @@ +* Rockchip Pinmux Controller + +The Rockchip Pinmux Controller, enables the IC +to share one PAD to several functional blocks. The sharing is done by +multiplexing the PAD input/output signals. For each PAD there are up to +4 muxing options with option 0 being the use as a GPIO. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +The Rockchip pin configuration node is a node of a group of pins which can be +used for a specific device or function. This node represents both mux and +config of the pins in that group. The 'pins' selects the function mode(also +named pin mode) this pin can work on and the 'config' configures various pad +settings such as pull-up, etc. + +The pins are grouped into up to 5 individual pin banks which need to be +defined as gpio sub-nodes of the pinmux controller. + +Required properties for iomux controller: + - compatible: one of "rockchip,rk2928-pinctrl", "rockchip,rk3066a-pinctrl" + "rockchip,rk3066b-pinctrl", "rockchip,rk3188-pinctrl" + +Required properties for gpio sub nodes: + - compatible: "rockchip,gpio-bank" + - reg: register of the gpio bank (different than the iomux registerset) + - interrupts: base interrupt of the gpio bank in the interrupt controller + - clocks: clock that drives this bank + - gpio-controller: identifies the node as a gpio controller and pin bank. + - #gpio-cells: number of cells in GPIO specifier. Since the generic GPIO + binding is used, the amount of cells must be specified as 2. See generic + GPIO binding documentation for description of particular cells. + - interrupt-controller: identifies the controller node as interrupt-parent. + - #interrupt-cells: the value of this property should be 2 and the interrupt + cells should use the standard two-cell scheme described in + bindings/interrupt-controller/interrupts.txt + +Required properties for pin configuration node: + - rockchip,pins: 3 integers array, represents a group of pins mux and config + setting. The format is rockchip,pins = <PIN_BANK PIN_BANK_IDX MUX &phandle>. + The MUX 0 means gpio and MUX 1 to 3 mean the specific device function. + The phandle of a node containing the generic pinconfig options + to use, as described in pinctrl-bindings.txt in this directory. + +Examples: + +#include <dt-bindings/pinctrl/rockchip.h> + +... + +pinctrl@20008000 { + compatible = "rockchip,rk3066a-pinctrl"; + reg = <0x20008000 0x150>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + gpio0: gpio0@20034000 { + compatible = "rockchip,gpio-bank"; + reg = <0x20034000 0x100>; + interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_gates8 9>; + + gpio-controller; + #gpio-cells = <2>; + + interrupt-controller; + #interrupt-cells = <2>; + }; + + ... + + pcfg_pull_default: pcfg_pull_default { + bias-pull-pin-default + }; + + uart2 { + uart2_xfer: uart2-xfer { + rockchip,pins = <RK_GPIO1 8 1 &pcfg_pull_default>, + <RK_GPIO1 9 1 &pcfg_pull_default>; + }; + }; +}; + +uart2: serial@20064000 { + compatible = "snps,dw-apb-uart"; + reg = <0x20064000 0x400>; + interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; + reg-shift = <2>; + reg-io-width = <1>; + clocks = <&mux_uart2>; + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&uart2_xfer>; +}; diff --git a/Documentation/devicetree/bindings/pinctrl/ste,abx500.txt b/Documentation/devicetree/bindings/pinctrl/ste,abx500.txt new file mode 100644 index 000000000000..e3865e136067 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ste,abx500.txt @@ -0,0 +1,352 @@ +ST Ericsson abx500 pinmux controller + +Required properties: +- compatible: "stericsson,ab8500-gpio", "stericsson,ab8540-gpio", + "stericsson,ab8505-gpio", "stericsson,ab9540-gpio", + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +ST Ericsson's pin configuration nodes act as a container for an arbitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as input, output, pull up, pull down... + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Required subnode-properties: +- ste,pins : An array of strings. Each string contains the name of a pin or + group. + +Optional subnode-properties: +- ste,function: A string containing the name of the function to mux to the + pin or group. + +- generic pin configuration option to use. Example : + + default_cfg { + ste,pins = "GPIO1"; + bias-disable; + }; + +- ste,config: Handle of pin configuration node containing the generic + pinconfig options to use, as described in pinctrl-bindings.txt in + this directory. Example : + + pcfg_bias_disable: pcfg_bias_disable { + bias-disable; + }; + + default_cfg { + ste,pins = "GPIO1"; + ste.config = <&pcfg_bias_disable>; + }; + +Example board file extract: + +&pinctrl_abx500 { + pinctrl-names = "default"; + pinctrl-0 = <&sysclkreq2_default_mode>, <&sysclkreq3_default_mode>, <&gpio3_default_mode>, <&sysclkreq6_default_mode>, <&pwmout1_default_mode>, <&pwmout2_default_mode>, <&pwmout3_default_mode>, <&adi1_default_mode>, <&dmic12_default_mode>, <&dmic34_default_mode>, <&dmic56_default_mode>, <&sysclkreq5_default_mode>, <&batremn_default_mode>, <&service_default_mode>, <&pwrctrl0_default_mode>, <&pwrctrl1_default_mode>, <&pwmextvibra1_default_mode>, <&pwmextvibra2_default_mode>, <&gpio51_default_mode>, <&gpio52_default_mode>, <&gpio53_default_mode>, <&gpio54_default_mode>, <&pdmclkdat_default_mode>; + + sysclkreq2 { + sysclkreq2_default_mode: sysclkreq2_default { + default_mux { + ste,function = "sysclkreq"; + ste,pins = "sysclkreq2_d_1"; + }; + default_cfg { + ste,pins = "GPIO1"; + bias-disable; + }; + }; + }; + sysclkreq3 { + sysclkreq3_default_mode: sysclkreq3_default { + default_mux { + ste,function = "sysclkreq"; + ste,pins = "sysclkreq3_d_1"; + }; + default_cfg { + ste,pins = "GPIO2"; + output-low; + }; + }; + }; + gpio3 { + gpio3_default_mode: gpio3_default { + default_mux { + ste,function = "gpio"; + ste,pins = "gpio3_a_1"; + }; + default_cfg { + ste,pins = "GPIO3"; + output-low; + }; + }; + }; + sysclkreq6 { + sysclkreq6_default_mode: sysclkreq6_default { + default_mux { + ste,function = "sysclkreq"; + ste,pins = "sysclkreq6_d_1"; + }; + default_cfg { + ste,pins = "GPIO4"; + bias-disable; + }; + }; + }; + pwmout1 { + pwmout1_default_mode: pwmout1_default { + default_mux { + ste,function = "pwmout"; + ste,pins = "pwmout1_d_1"; + }; + default_cfg { + ste,pins = "GPIO14"; + output-low; + }; + }; + }; + pwmout2 { + pwmout2_default_mode: pwmout2_default { + pwmout2_default_mux { + ste,function = "pwmout"; + ste,pins = "pwmout2_d_1"; + }; + pwmout2_default_cfg { + ste,pins = "GPIO15"; + output-low; + }; + }; + }; + pwmout3 { + pwmout3_default_mode: pwmout3_default { + pwmout3_default_mux { + ste,function = "pwmout"; + ste,pins = "pwmout3_d_1"; + }; + pwmout3_default_cfg { + ste,pins = "GPIO16"; + output-low; + }; + }; + }; + adi1 { + + adi1_default_mode: adi1_default { + adi1_default_mux { + ste,function = "adi1"; + ste,pins = "adi1_d_1"; + }; + adi1_default_cfg1 { + ste,pins = "GPIO17","GPIO19","GPIO20"; + bias-disable; + }; + adi1_default_cfg2 { + ste,pins = "GPIO18"; + output-low; + }; + }; + }; + dmic12 { + dmic12_default_mode: dmic12_default { + dmic12_default_mux { + ste,function = "dmic"; + ste,pins = "dmic12_d_1"; + }; + dmic12_default_cfg1 { + ste,pins = "GPIO27"; + output-low; + }; + dmic12_default_cfg2 { + ste,pins = "GPIO28"; + bias-disable; + }; + }; + }; + dmic34 { + dmic34_default_mode: dmic34_default { + dmic34_default_mux { + ste,function = "dmic"; + ste,pins = "dmic34_d_1"; + }; + dmic34_default_cfg1 { + ste,pins = "GPIO29"; + output-low; + }; + dmic34_default_cfg2 { + ste,pins = "GPIO30"; + bias-disable;{ + + }; + }; + }; + dmic56 { + dmic56_default_mode: dmic56_default { + dmic56_default_mux { + ste,function = "dmic"; + ste,pins = "dmic56_d_1"; + }; + dmic56_default_cfg1 { + ste,pins = "GPIO31"; + output-low; + }; + dmic56_default_cfg2 { + ste,pins = "GPIO32"; + bias-disable; + }; + }; + }; + sysclkreq5 { + sysclkreq5_default_mode: sysclkreq5_default { + sysclkreq5_default_mux { + ste,function = "sysclkreq"; + ste,pins = "sysclkreq5_d_1"; + }; + sysclkreq5_default_cfg { + ste,pins = "GPIO42"; + output-low; + }; + }; + }; + batremn { + batremn_default_mode: batremn_default { + batremn_default_mux { + ste,function = "batremn"; + ste,pins = "batremn_d_1"; + }; + batremn_default_cfg { + ste,pins = "GPIO43"; + bias-disable; + }; + }; + }; + service { + service_default_mode: service_default { + service_default_mux { + ste,function = "service"; + ste,pins = "service_d_1"; + }; + service_default_cfg { + ste,pins = "GPIO44"; + bias-disable; + }; + }; + }; + pwrctrl0 { + pwrctrl0_default_mux: pwrctrl0_mux { + pwrctrl0_default_mux { + ste,function = "pwrctrl"; + ste,pins = "pwrctrl0_d_1"; + }; + }; + pwrctrl0_default_mode: pwrctrl0_default { + pwrctrl0_default_cfg { + ste,pins = "GPIO45"; + bias-disable; + }; + }; + }; + pwrctrl1 { + pwrctrl1_default_mux: pwrctrl1_mux { + pwrctrl1_default_mux { + ste,function = "pwrctrl"; + ste,pins = "pwrctrl1_d_1"; + }; + }; + pwrctrl1_default_mode: pwrctrl1_default { + pwrctrl1_default_cfg { + ste,pins = "GPIO46"; + bias-disable; + }; + }; + }; + pwmextvibra1 { + pwmextvibra1_default_mode: pwmextvibra1_default { + pwmextvibra1_default_mux { + ste,function = "pwmextvibra"; + ste,pins = "pwmextvibra1_d_1"; + }; + pwmextvibra1_default_cfg { + ste,pins = "GPIO47"; + bias-disable; + }; + }; + }; + pwmextvibra2 { + pwmextvibra2_default_mode: pwmextvibra2_default { + pwmextvibra2_default_mux { + ste,function = "pwmextvibra"; + ste,pins = "pwmextvibra2_d_1"; + }; + pwmextvibra1_default_cfg { + ste,pins = "GPIO48"; + bias-disable; + }; + }; + }; + gpio51 { + gpio51_default_mode: gpio51_default { + gpio51_default_mux { + ste,function = "gpio"; + ste,pins = "gpio51_a_1"; + }; + gpio51_default_cfg { + ste,pins = "GPIO51"; + output-low; + }; + }; + }; + gpio52 { + gpio52_default_mode: gpio52_default { + gpio52_default_mux { + ste,function = "gpio"; + ste,pins = "gpio52_a_1"; + }; + gpio52_default_cfg { + ste,pins = "GPIO52"; + bias-pull-down; + }; + }; + }; + gpio53 { + gpio53_default_mode: gpio53_default { + gpio53_default_mux { + ste,function = "gpio"; + ste,pins = "gpio53_a_1"; + }; + gpio53_default_cfg { + ste,pins = "GPIO53"; + bias-pull-down; + }; + }; + }; + gpio54 { + gpio54_default_mode: gpio54_default { + gpio54_default_mux { + ste,function = "gpio"; + ste,pins = "gpio54_a_1"; + }; + gpio54_default_cfg { + ste,pins = "GPIO54"; + output-low; + }; + }; + }; + pdmclkdat { + pdmclkdat_default_mode: pdmclkdat_default { + pdmclkdat_default_mux { + ste,function = "pdm"; + ste,pins = "pdmclkdat_d_1"; + }; + pdmclkdat_default_cfg { + ste,pins = "GPIO55", "GPIO56"; + bias-disable; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/regulator/lp872x.txt b/Documentation/devicetree/bindings/regulator/lp872x.txt new file mode 100644 index 000000000000..78183182dad9 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/lp872x.txt @@ -0,0 +1,160 @@ +Binding for TI/National Semiconductor LP872x Driver + +Required properties: + - compatible: "ti,lp8720" or "ti,lp8725" + - reg: I2C slave address. 0x7d = LP8720, 0x7a = LP8725 + +Optional properties: + - ti,general-config: the value of LP872X_GENERAL_CFG register (u8) + (LP8720) + bit[2]: BUCK output voltage control by external DVS pin or register + 1 = external pin, 0 = bit7 of register 08h + bit[1]: sleep control by external DVS pin or register + 1 = external pin, 0 = bit6 of register 08h + bit[0]: time step unit(usec). 1 = 25, 0 = 50 + + (LP8725) + bit[7:6]: time step unit(usec). 00 = 32, 01 = 64, 10 = 128, 11 = 256 + bit[4]: BUCK2 enable control. 1 = enable, 0 = disable + bit[3]: BUCK2 output voltage register address. 1 = 0Ah, 0 = 0Bh + bit[2]: BUCK1 output voltage control by external DVS pin or register + 1 = register 08h, 0 = DVS + bit[1]: LDO sleep control. 1 = sleep mode, 0 = normal + bit[0]: BUCK1 enable control, 1 = enable, 0 = disable + + For more details, please see the datasheet. + + - ti,update-config: define it when LP872X_GENERAL_CFG register should be set + - ti,dvs-gpio: GPIO specifier for external DVS pin control of LP872x devices. + - ti,dvs-vsel: DVS selector. 0 = SEL_V1, 1 = SEL_V2. + - ti,dvs-state: initial DVS pin state. 0 = DVS_LOW, 1 = DVS_HIGH. + + Sub nodes for regulator_init_data + LP8720 has maximum 6 nodes. (child name: ldo1 ~ 5 and buck) + LP8725 has maximum 9 nodes. (child name: ldo1 ~ 5, lilo1,2 and buck1,2) + For more details, please see the following binding document. + (Documentation/devicetree/bindings/regulator/regulator.txt) + +Datasheet + - LP8720: http://www.ti.com/lit/ds/symlink/lp8720.pdf + - LP8725: http://www.ti.com/lit/ds/symlink/lp8725.pdf + +Example 1) LP8720 + +lp8720@7d { + compatible = "ti,lp8720"; + reg = <0x7d>; + + /* external DVS pin used, timestep is 25usec */ + ti,general-config = /bits/ 8 <0x03>; + ti,update-config; + + /* + * The dvs-gpio depends on the processor environment. + * For example, following GPIO specifier means GPIO134 in OMAP4. + */ + ti,dvs-gpio = <&gpio5 6 0>; + ti,dvs-vsel = /bits/ 8 <1>; /* SEL_V2 */ + ti,dvs-state = /bits/ 8 <1>; /* DVS_HIGH */ + + vaf: ldo1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vmmc: ldo2 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcam_io: ldo3 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + + vcam_core: ldo4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2850000>; + regulator-boot-on; + }; + + vcam: ldo5 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcc: buck { + regulator-name = "VBUCK"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2300000>; + }; +}; + +Example 2) LP8725 + +lp8725@7a { + compatible = "ti,lp8725"; + reg = <0x7a>; + + /* Enable BUCK1,2, no DVS, normal LDO mode, timestep is 256usec */ + ti,general-config = /bits/ 8 <0xdd>; + ti,update-config; + + vcam_io: ldo1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcam_core: ldo2 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcam: ldo3 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcmmb_io: ldo4 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + + vcmmb_core: ldo5 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + + vaux1: lilo1 { + regulator-name = "VAUX1"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + vaux2: lilo2 { + regulator-name = "VAUX2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + vcc1: buck1 { + regulator-name = "VBUCK1"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3000000>; + regulator-min-microamp = <460000>; + regulator-max-microamp = <1370000>; + regulator-boot-on; + }; + + vcc2: buck2 { + regulator-name = "VBUCK2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3000000>; + regulator-min-microamp = <460000>; + regulator-max-microamp = <1370000>; + regulator-boot-on; + }; +}; diff --git a/Documentation/devicetree/bindings/regulator/max8973-regulator.txt b/Documentation/devicetree/bindings/regulator/max8973-regulator.txt new file mode 100644 index 000000000000..4f15d8a1bfd0 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8973-regulator.txt @@ -0,0 +1,21 @@ +* Maxim MAX8973 Voltage Regulator + +Required properties: + +- compatible: must be "maxim,max8973" +- reg: the i2c slave address of the regulator. It should be 0x1b. + +Any standard regulator properties can be used to configure the single max8973 +DCDC. + +Example: + + max8973@1b { + compatible = "maxim,max8973"; + reg = <0x1b>; + + regulator-min-microvolt = <935000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + regulator-always-on; + }; diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index ecfc6ccd67ef..48a3b8e5d6bd 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -9,6 +9,7 @@ Optional properties: - regulator-max-microamp: largest current consumers may set - regulator-always-on: boolean, regulator should never be disabled - regulator-boot-on: bootloader/firmware enabled regulator +- regulator-allow-bypass: allow the regulator to go into bypass mode - <name>-supply: phandle to the parent supply/regulator node - regulator-ramp-delay: ramp delay for regulator(in uV/uS) diff --git a/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt b/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt new file mode 100644 index 000000000000..2e57a33e9029 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt @@ -0,0 +1,128 @@ +Adaptive Body Bias(ABB) SoC internal LDO regulator for Texas Instruments SoCs + +Required Properties: +- compatible: Should be one of: + - "ti,abb-v1" for older SoCs like OMAP3 + - "ti,abb-v2" for newer SoCs like OMAP4, OMAP5 +- reg: Address and length of the register set for the device. It contains + the information of registers in the same order as described by reg-names +- reg-names: Should contain the reg names + - "base-address" - contains base address of ABB module + - "int-address" - contains address of interrupt register for ABB module + (also see Optional properties) +- #address-cell: should be 0 +- #size-cell: should be 0 +- clocks: should point to the clock node used by ABB module +- ti,settling-time: Settling time in uSecs from SoC documentation for ABB module + to settle down(target time for SR2_WTCNT_VALUE). +- ti,clock-cycles: SoC specific data about count of system ti,clock-cycles used for + computing settling time from SoC Documentation for ABB module(clock + cycles for SR2_WTCNT_VALUE). +- ti,tranxdone-status-mask: Mask to the int-register to write-to-clear mask + indicating LDO tranxdone (operation complete). +- ti,abb_info: An array of 6-tuples u32 items providing information about ABB + configuration needed per operational voltage of the device. + Each item consists of the following in the same order: + volt: voltage in uV - Only used to index ABB information. + ABB mode: one of the following: + 0-bypass + 1-Forward Body Bias(FBB) + 3-Reverse Body Bias(RBB) + efuse: (see Optional properties) + RBB enable efuse Mask: (See Optional properties) + FBB enable efuse Mask: (See Optional properties) + Vset value efuse Mask: (See Optional properties) + + NOTE: If more than 1 entry is present, then regulator is setup to change + voltage, allowing for various modes to be selected indexed off + the regulator. Further, ABB LDOs are considered always-on by + default. + +Optional Properties: +- reg-names: In addition to the required properties, the following are optional + - "efuse-address" - Contains efuse base address used to pick up ABB info. + - "ldo-address" - Contains address of ABB LDO overide register address. + "efuse-address" is required for this. +- ti,ldovbb-vset-mask - Required if ldo-address is set, mask for LDO override + register to provide override vset value. +- ti,ldovbb-override-mask - Required if ldo-address is set, mask for LDO + override register to enable override vset value. +- ti,abb_opp_sel: Addendum to the description in required properties + efuse: Mandatory if 'efuse-address' register is defined. Provides offset + from efuse-address to pick up ABB characteristics. Set to 0 if + 'efuse-address' is not defined. + RBB enable efuse Mask: Optional if 'efuse-address' register is defined. + 'ABB mode' is force set to RBB mode if value at "efuse-address" + + efuse maps to RBB mask. Set to 0 to ignore this. + FBB enable efuse Mask: Optional if 'efuse-address' register is defined. + 'ABB mode' is force set to FBB mode if value at "efuse-address" + + efuse maps to FBB mask (valid only if RBB mask does not match) + Set to 0 to ignore this. + Vset value efuse Mask: Mandatory if ldo-address is set. Picks up from + efuse the value to set in 'ti,ldovbb-vset-mask' at ldo-address. + +Example #1: Simplest configuration (no efuse data, hard coded ABB table): +abb_x: regulator-abb-x { + compatible = "ti,abb-v1"; + regulator-name = "abb_x"; + #address-cell = <0>; + #size-cells = <0>; + reg = <0x483072f0 0x8>, <0x48306818 0x4>; + reg-names = "base-address", "int-address"; + ti,tranxdone-status-mask = <0x4000000>; + clocks = <&sysclk>; + ti,settling-time = <30>; + ti,clock-cycles = <8>; + ti,abb_info = < + /* uV ABB efuse rbb_m fbb_m vset_m */ + 1012500 0 0 0 0 0 /* Bypass */ + 1200000 3 0 0 0 0 /* RBB mandatory */ + 1320000 1 0 0 0 0 /* FBB mandatory */ + >; +}; + +Example #2: Efuse bits contain ABB mode setting (no LDO override capability) +abb_y: regulator-abb-y { + compatible = "ti,abb-v2"; + regulator-name = "abb_y"; + #address-cell = <0>; + #size-cells = <0>; + reg = <0x4a307bd0 0x8>, <0x4a306014 0x4>, <0x4A002268 0x8>; + reg-names = "base-address", "int-address", "efuse-address"; + ti,tranxdone-status-mask = <0x4000000>; + clocks = <&sysclk>; + ti,settling-time = <50>; + ti,clock-cycles = <16>; + ti,abb_info = < + /* uV ABB efuse rbb_m fbb_m vset_m */ + 975000 0 0 0 0 0 /* Bypass */ + 1012500 0 0 0x40000 0 0 /* RBB optional */ + 1200000 0 0x4 0 0x40000 0 /* FBB optional */ + 1320000 1 0 0 0 0 /* FBB mandatory */ + >; +}; + +Example #3: Efuse bits contain ABB mode setting and LDO override capability +abb_z: regulator-abb-z { + compatible = "ti,abb-v2"; + regulator-name = "abb_z"; + #address-cell = <0>; + #size-cells = <0>; + reg = <0x4ae07ce4 0x8>, <0x4ae06010 0x4>, + <0x4a002194 0x8>, <0x4ae0C314 0x4>; + reg-names = "base-address", "int-address", + "efuse-address", "ldo-address"; + ti,tranxdone-status-mask = <0x8000000>; + /* LDOVBBMM_MUX_CTRL */ + ti,ldovbb-override-mask = <0x400>; + /* LDOVBBMM_VSET_OUT */ + ti,ldovbb-vset-mask = <0x1F>; + clocks = <&sysclk>; + ti,settling-time = <50>; + ti,clock-cycles = <16>; + ti,abb_info = < + /* uV ABB efuse rbb_m fbb_m vset_m */ + 975000 0 0 0 0 0 /* Bypass */ + 1200000 0 0x4 0 0x40000 0x1f00 /* FBB optional, vset */ + >; +}; diff --git a/Documentation/devicetree/bindings/spi/omap-spi.txt b/Documentation/devicetree/bindings/spi/omap-spi.txt index 938809c6829b..4c85c4c69584 100644 --- a/Documentation/devicetree/bindings/spi/omap-spi.txt +++ b/Documentation/devicetree/bindings/spi/omap-spi.txt @@ -10,7 +10,18 @@ Required properties: input. The default is D0 as input and D1 as output. -Example: +Optional properties: +- dmas: List of DMA specifiers with the controller specific format + as described in the generic DMA client binding. A tx and rx + specifier is required for each chip select. +- dma-names: List of DMA request names. These strings correspond + 1:1 with the DMA specifiers listed in dmas. The string naming + is to be "rxN" and "txN" for RX and TX requests, + respectively, where N equals the chip select number. + +Examples: + +[hwmod populated DMA resources] mcspi1: mcspi@1 { #address-cells = <1>; @@ -20,3 +31,17 @@ mcspi1: mcspi@1 { ti,spi-num-cs = <4>; }; +[generic DMA request binding] + +mcspi1: mcspi@1 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,omap4-mcspi"; + ti,hwmods = "mcspi1"; + ti,spi-num-cs = <2>; + dmas = <&edma 42 + &edma 43 + &edma 44 + &edma 45>; + dma-names = "tx0", "rx0", "tx1", "rx1"; +}; diff --git a/Documentation/i2c/busses/i2c-piix4 b/Documentation/i2c/busses/i2c-piix4 index 1e6634f54c50..a370b2047cf3 100644 --- a/Documentation/i2c/busses/i2c-piix4 +++ b/Documentation/i2c/busses/i2c-piix4 @@ -13,7 +13,7 @@ Supported adapters: * AMD SP5100 (SB700 derivative found on some server mainboards) Datasheet: Publicly available at the AMD website http://support.amd.com/us/Embedded_TechDocs/44413.pdf - * AMD Hudson-2 + * AMD Hudson-2, CZ Datasheet: Not publicly available * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge Datasheet: Publicly available at the SMSC website http://www.smsc.com diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 237acab169dd..2a5f0e14efa3 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -72,6 +72,7 @@ Code Seq#(hex) Include File Comments 0x06 all linux/lp.h 0x09 all linux/raid/md_u.h 0x10 00-0F drivers/char/s390/vmcp.h +0x10 10-1F arch/s390/include/uapi/sclp_ctl.h 0x12 all linux/fs.h linux/blkpg.h 0x1b all InfiniBand Subsystem <http://infiniband.sourceforge.net/> diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 81732b8abd3a..ef8bd35814cf 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1129,11 +1129,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted. The builtin appraise policy appraises all files owned by uid=0. - ima_audit= [IMA] - Format: { "0" | "1" } - 0 -- integrity auditing messages. (Default) - 1 -- enable informational integrity auditing messages. - ima_hash= [IMA] Format: { "sha1" | "md5" } default: "sha1" @@ -1158,6 +1153,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. inport.irq= [HW] Inport (ATI XL and Microsoft) busmouse driver Format: <irq> + int_pln_enable [x86] Enable power limit notification interrupt + + integrity_audit=[IMA] + Format: { "0" | "1" } + 0 -- basic integrity auditing messages. (Default) + 1 -- additional integrity auditing messages. + intel_iommu= [DMAR] Intel IOMMU driver (DMAR) option on Enable intel iommu driver. @@ -3229,6 +3231,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted. video= [FB] Frame buffer configuration See Documentation/fb/modedb.txt. + video.brightness_switch_enabled= [0,1] + If set to 1, on receiving an ACPI notify event + generated by hotkey, video driver will adjust brightness + level and then send out the event to user space through + the allocated input device; If set to 0, video driver + will only send out the event without touching backlight + brightness level. + default: 1 + virtio_mmio.device= [VMMIO] Memory mapped virtio (platform) device. diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 447fd4cd54ec..c5948c7d662a 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -203,15 +203,8 @@ using a certain resistor value - pull up and pull down - so that the pin has a stable value when nothing is driving the rail it is connected to, or when it's unconnected. -Pin configuration can be programmed either using the explicit APIs described -immediately below, or by adding configuration entries into the mapping table; -see section "Board/machine configuration" below. - -For example, a platform may do the following to pull up a pin to VDD: - -#include <linux/pinctrl/consumer.h> - -ret = pin_config_set("foo-dev", "FOO_GPIO_PIN", PLATFORM_X_PULL_UP); +Pin configuration can be programmed by adding configuration entries into the +mapping table; see section "Board/machine configuration" below. The format and meaning of the configuration parameter, PLATFORM_X_PULL_UP above, is entirely defined by the pin controller driver. @@ -350,6 +343,23 @@ chip b: - GPIO range : [48 .. 55] - pin range : [64 .. 71] +The above examples assume the mapping between the GPIOs and pins is +linear. If the mapping is sparse or haphazard, an array of arbitrary pin +numbers can be encoded in the range like this: + +static const unsigned range_pins[] = { 14, 1, 22, 17, 10, 8, 6, 2 }; + +static struct pinctrl_gpio_range gpio_range = { + .name = "chip", + .id = 0, + .base = 32, + .pins = &range_pins, + .npins = ARRAY_SIZE(range_pins), + .gc = &chip; +}; + +In this case the pin_base property will be ignored. + When GPIO-specific functions in the pin control subsystem are called, these ranges will be used to look up the appropriate pin controller by inspecting and matching the pin to the pin ranges across all controllers. When a @@ -357,9 +367,9 @@ pin controller handling the matching range is found, GPIO-specific functions will be called on that specific pin controller. For all functionalities dealing with pin biasing, pin muxing etc, the pin -controller subsystem will subtract the range's .base offset from the passed -in gpio number, and add the ranges's .pin_base offset to retrive a pin number. -After that, the subsystem passes it on to the pin control driver, so the driver +controller subsystem will look up the corresponding pin number from the passed +in gpio number, and use the range's internals to retrive a pin number. After +that, the subsystem passes it on to the pin control driver, so the driver will get an pin number into its handled number range. Further it is also passed the range ID value, so that the pin controller knows which range it should deal with. @@ -368,6 +378,7 @@ Calling pinctrl_add_gpio_range from pinctrl driver is DEPRECATED. Please see section 2.1 of Documentation/devicetree/bindings/gpio/gpio.txt on how to bind pinctrl and gpio drivers. + PINMUX interfaces ================= @@ -1226,8 +1237,8 @@ setting up the config and muxing for the pins right before the device is probing, nevertheless orthogonal to the GPIO subsystem. But there are also situations where it makes sense for the GPIO subsystem -to communicate directly with with the pinctrl subsystem, using the latter -as a back-end. This is when the GPIO driver may call out to the functions +to communicate directly with the pinctrl subsystem, using the latter as a +back-end. This is when the GPIO driver may call out to the functions described in the section "Pin control interaction with the GPIO subsystem" above. This only involves per-pin multiplexing, and will be completely hidden behind the gpio_*() function namespace. In this case, the driver diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt index 79a2a58425ee..483632087788 100644 --- a/Documentation/power/pm_qos_interface.txt +++ b/Documentation/power/pm_qos_interface.txt @@ -7,7 +7,7 @@ one of the parameters. Two different PM QoS frameworks are available: 1. PM QoS classes for cpu_dma_latency, network_latency, network_throughput. 2. the per-device PM QoS framework provides the API to manage the per-device latency -constraints. +constraints and PM QoS flags. Each parameters have defined units: * latency: usec @@ -86,13 +86,17 @@ To remove the user mode request for a target value simply close the device node. -2. PM QoS per-device latency framework +2. PM QoS per-device latency and flags framework + +For each device, there are two lists of PM QoS requests. One is maintained +along with the aggregated target of latency value and the other is for PM QoS +flags. Values are updated in response to changes of the request list. + +Target latency value is simply the minimum of the request values held in the +parameter list elements. The PM QoS flags aggregate value is a gather (bitwise +OR) of all list elements' values. Two device PM QoS flags are defined currently: +PM_QOS_FLAG_NO_POWER_OFF and PM_QOS_FLAG_REMOTE_WAKEUP. -For each device a list of performance requests is maintained along with -an aggregated target value. The aggregated target value is updated with -changes to the request list or elements of the list. Typically the -aggregated target value is simply the max or min of the request values held -in the parameter list elements. Note: the aggregated target value is implemented as an atomic variable so that reading the aggregated value does not require any locking mechanism. @@ -119,6 +123,38 @@ the request. s32 dev_pm_qos_read_value(device): Returns the aggregated value for a given device's constraints list. +enum pm_qos_flags_status dev_pm_qos_flags(device, mask) +Check PM QoS flags of the given device against the given mask of flags. +The meaning of the return values is as follows: + PM_QOS_FLAGS_ALL: All flags from the mask are set + PM_QOS_FLAGS_SOME: Some flags from the mask are set + PM_QOS_FLAGS_NONE: No flags from the mask are set + PM_QOS_FLAGS_UNDEFINED: The device's PM QoS structure has not been + initialized or the list of requests is empty. + +int dev_pm_qos_add_ancestor_request(dev, handle, value) +Add a PM QoS request for the first direct ancestor of the given device whose +power.ignore_children flag is unset. + +int dev_pm_qos_expose_latency_limit(device, value) +Add a request to the device's PM QoS list of latency constraints and create +a sysfs attribute pm_qos_resume_latency_us under the device's power directory +allowing user space to manipulate that request. + +void dev_pm_qos_hide_latency_limit(device) +Drop the request added by dev_pm_qos_expose_latency_limit() from the device's +PM QoS list of latency constraints and remove sysfs attribute pm_qos_resume_latency_us +from the device's power directory. + +int dev_pm_qos_expose_flags(device, value) +Add a request to the device's PM QoS list of flags and create sysfs attributes +pm_qos_no_power_off and pm_qos_remote_wakeup under the device's power directory +allowing user space to change these flags' value. + +void dev_pm_qos_hide_flags(device) +Drop the request added by dev_pm_qos_expose_flags() from the device's PM QoS list +of flags and remove sysfs attributes pm_qos_no_power_off and pm_qos_remote_wakeup +under the device's power directory. Notification mechanisms: The per-device PM QoS framework has 2 different and distinct notification trees: diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 6c9f5d9aa115..71d8fe4e75d3 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -144,8 +144,12 @@ The action performed by the idle callback is totally dependent on the subsystem (or driver) in question, but the expected and recommended action is to check if the device can be suspended (i.e. if all of the conditions necessary for suspending the device are satisfied) and to queue up a suspend request for the -device in that case. The value returned by this callback is ignored by the PM -core. +device in that case. If there is no idle callback, or if the callback returns +0, then the PM core will attempt to carry out a runtime suspend of the device; +in essence, it will call pm_runtime_suspend() directly. To prevent this (for +example, if the callback routine has started a delayed suspend), the routine +should return a non-zero value. Negative error return codes are ignored by the +PM core. The helper functions provided by the PM core, described in Section 4, guarantee that the following constraints are met with respect to runtime PM callbacks for @@ -301,9 +305,10 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: removing the device from device hierarchy int pm_runtime_idle(struct device *dev); - - execute the subsystem-level idle callback for the device; returns 0 on - success or error code on failure, where -EINPROGRESS means that - ->runtime_idle() is already being executed + - execute the subsystem-level idle callback for the device; returns an + error code on failure, where -EINPROGRESS means that ->runtime_idle() is + already being executed; if there is no callback or the callback returns 0 + then run pm_runtime_suspend(dev) and return its result int pm_runtime_suspend(struct device *dev); - execute the subsystem-level suspend callback for the device; returns 0 on @@ -660,11 +665,6 @@ Subsystems may wish to conserve code space by using the set of generic power management callbacks provided by the PM core, defined in driver/base/power/generic_ops.c: - int pm_generic_runtime_idle(struct device *dev); - - invoke the ->runtime_idle() callback provided by the driver of this - device, if defined, and call pm_runtime_suspend() for this device if the - return value is 0 or the callback is not defined - int pm_generic_runtime_suspend(struct device *dev); - invoke the ->runtime_suspend() callback provided by the driver of this device and return its result, or return -EINVAL if not defined diff --git a/Documentation/power/video_extension.txt b/Documentation/power/video_extension.txt deleted file mode 100644 index b2f9b1598ac2..000000000000 --- a/Documentation/power/video_extension.txt +++ /dev/null @@ -1,37 +0,0 @@ -ACPI video extensions -~~~~~~~~~~~~~~~~~~~~~ - -This driver implement the ACPI Extensions For Display Adapters for -integrated graphics devices on motherboard, as specified in ACPI 2.0 -Specification, Appendix B, allowing to perform some basic control like -defining the video POST device, retrieving EDID information or to -setup a video output, etc. Note that this is an ref. implementation -only. It may or may not work for your integrated video device. - -Interfaces exposed to userland through /proc/acpi/video: - -VGA/info : display the supported video bus device capability like Video ROM, CRT/LCD/TV. -VGA/ROM : Used to get a copy of the display devices' ROM data (up to 4k). -VGA/POST_info : Used to determine what options are implemented. -VGA/POST : Used to get/set POST device. -VGA/DOS : Used to get/set ownership of output switching: - Please refer ACPI spec B.4.1 _DOS -VGA/CRT : CRT output -VGA/LCD : LCD output -VGA/TVO : TV output -VGA/*/brightness : Used to get/set brightness of output device - -Notify event through /proc/acpi/event: - -#define ACPI_VIDEO_NOTIFY_SWITCH 0x80 -#define ACPI_VIDEO_NOTIFY_PROBE 0x81 -#define ACPI_VIDEO_NOTIFY_CYCLE 0x82 -#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83 -#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84 - -#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x82 -#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x83 -#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x84 -#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x85 -#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x86 - diff --git a/Documentation/trace/events-power.txt b/Documentation/trace/events-power.txt index e1498ff8cf94..3bd33b8dc7c4 100644 --- a/Documentation/trace/events-power.txt +++ b/Documentation/trace/events-power.txt @@ -63,3 +63,34 @@ power_domain_target "%s state=%lu cpu_id=%lu" The first parameter gives the power domain name (e.g. "mpu_pwrdm"). The second parameter is the power domain target state. +4. PM QoS events +================ +The PM QoS events are used for QoS add/update/remove request and for +target/flags update. + +pm_qos_add_request "pm_qos_class=%s value=%d" +pm_qos_update_request "pm_qos_class=%s value=%d" +pm_qos_remove_request "pm_qos_class=%s value=%d" +pm_qos_update_request_timeout "pm_qos_class=%s value=%d, timeout_us=%ld" + +The first parameter gives the QoS class name (e.g. "CPU_DMA_LATENCY"). +The second parameter is value to be added/updated/removed. +The third parameter is timeout value in usec. + +pm_qos_update_target "action=%s prev_value=%d curr_value=%d" +pm_qos_update_flags "action=%s prev_value=0x%x curr_value=0x%x" + +The first parameter gives the QoS action name (e.g. "ADD_REQ"). +The second parameter is the previous QoS value. +The third parameter is the current QoS value to update. + +And, there are also events used for device PM QoS add/update/remove request. + +dev_pm_qos_add_request "device=%s type=%s new_value=%d" +dev_pm_qos_update_request "device=%s type=%s new_value=%d" +dev_pm_qos_remove_request "device=%s type=%s new_value=%d" + +The first parameter gives the device name which tries to add/update/remove +QoS requests. +The second parameter gives the request type (e.g. "DEV_PM_QOS_LATENCY"). +The third parameter is value to be added/updated/removed. diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 9bfadeb8be31..66dd2aa53ba4 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2278,7 +2278,7 @@ return indicates the attribute is implemented. It does not necessarily indicate that the attribute can be read or written in the device's current state. "addr" is ignored. -4.77 KVM_ARM_VCPU_INIT +4.82 KVM_ARM_VCPU_INIT Capability: basic Architectures: arm, arm64 @@ -2304,7 +2304,7 @@ Possible features: Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only). -4.78 KVM_GET_REG_LIST +4.83 KVM_GET_REG_LIST Capability: basic Architectures: arm, arm64 @@ -2324,7 +2324,7 @@ This ioctl returns the guest registers that are supported for the KVM_GET_ONE_REG/KVM_SET_ONE_REG calls. -4.80 KVM_ARM_SET_DEVICE_ADDR +4.84 KVM_ARM_SET_DEVICE_ADDR Capability: KVM_CAP_ARM_SET_DEVICE_ADDR Architectures: arm, arm64 @@ -2362,7 +2362,7 @@ must be called after calling KVM_CREATE_IRQCHIP, but before calling KVM_RUN on any of the VCPUs. Calling this ioctl twice for any of the base addresses will return -EEXIST. -4.82 KVM_PPC_RTAS_DEFINE_TOKEN +4.85 KVM_PPC_RTAS_DEFINE_TOKEN Capability: KVM_CAP_PPC_RTAS Architectures: ppc diff --git a/Documentation/virtual/kvm/mmu.txt b/Documentation/virtual/kvm/mmu.txt index 43fcb761ed16..290894176142 100644 --- a/Documentation/virtual/kvm/mmu.txt +++ b/Documentation/virtual/kvm/mmu.txt @@ -191,12 +191,12 @@ Shadow pages contain the following information: A counter keeping track of how many hardware registers (guest cr3 or pdptrs) are now pointing at the page. While this counter is nonzero, the page cannot be destroyed. See role.invalid. - multimapped: - Whether there exist multiple sptes pointing at this page. - parent_pte/parent_ptes: - If multimapped is zero, parent_pte points at the single spte that points at - this page's spt. Otherwise, parent_ptes points at a data structure - with a list of parent_ptes. + parent_ptes: + The reverse mapping for the pte/ptes pointing at this page's spt. If + parent_ptes bit 0 is zero, only one spte points at this pages and + parent_ptes points at this single spte, otherwise, there exists multiple + sptes pointing at this page and (parent_ptes & ~0x1) points at a data + structure with a list of parent_ptes. unsync: If true, then the translations in this page may not match the guest's translation. This is equivalent to the state of the tlb when a pte is @@ -210,6 +210,24 @@ Shadow pages contain the following information: A bitmap indicating which sptes in spt point (directly or indirectly) at pages that may be unsynchronized. Used to quickly locate all unsychronized pages reachable from a given page. + mmu_valid_gen: + Generation number of the page. It is compared with kvm->arch.mmu_valid_gen + during hash table lookup, and used to skip invalidated shadow pages (see + "Zapping all pages" below.) + clear_spte_count: + Only present on 32-bit hosts, where a 64-bit spte cannot be written + atomically. The reader uses this while running out of the MMU lock + to detect in-progress updates and retry them until the writer has + finished the write. + write_flooding_count: + A guest may write to a page table many times, causing a lot of + emulations if the page needs to be write-protected (see "Synchronized + and unsynchronized pages" below). Leaf pages can be unsynchronized + so that they do not trigger frequent emulation, but this is not + possible for non-leafs. This field counts the number of emulations + since the last time the page table was actually used; if emulation + is triggered too frequently on this page, KVM will unmap the page + to avoid emulation in the future. Reverse map =========== @@ -258,14 +276,26 @@ This is the most complicated event. The cause of a page fault can be: Handling a page fault is performed as follows: + - if the RSV bit of the error code is set, the page fault is caused by guest + accessing MMIO and cached MMIO information is available. + - walk shadow page table + - check for valid generation number in the spte (see "Fast invalidation of + MMIO sptes" below) + - cache the information to vcpu->arch.mmio_gva, vcpu->arch.access and + vcpu->arch.mmio_gfn, and call the emulator + - If both P bit and R/W bit of error code are set, this could possibly + be handled as a "fast page fault" (fixed without taking the MMU lock). See + the description in Documentation/virtual/kvm/locking.txt. - if needed, walk the guest page tables to determine the guest translation (gva->gpa or ngpa->gpa) - if permissions are insufficient, reflect the fault back to the guest - determine the host page - - if this is an mmio request, there is no host page; call the emulator - to emulate the instruction instead + - if this is an mmio request, there is no host page; cache the info to + vcpu->arch.mmio_gva, vcpu->arch.access and vcpu->arch.mmio_gfn - walk the shadow page table to find the spte for the translation, instantiating missing intermediate page tables as necessary + - If this is an mmio request, cache the mmio info to the spte and set some + reserved bit on the spte (see callers of kvm_mmu_set_mmio_spte_mask) - try to unsynchronize the page - if successful, we can let the guest continue and modify the gpte - emulate the instruction @@ -351,6 +381,51 @@ causes its write_count to be incremented, thus preventing instantiation of a large spte. The frames at the end of an unaligned memory slot have artificially inflated ->write_counts so they can never be instantiated. +Zapping all pages (page generation count) +========================================= + +For the large memory guests, walking and zapping all pages is really slow +(because there are a lot of pages), and also blocks memory accesses of +all VCPUs because it needs to hold the MMU lock. + +To make it be more scalable, kvm maintains a global generation number +which is stored in kvm->arch.mmu_valid_gen. Every shadow page stores +the current global generation-number into sp->mmu_valid_gen when it +is created. Pages with a mismatching generation number are "obsolete". + +When KVM need zap all shadow pages sptes, it just simply increases the global +generation-number then reload root shadow pages on all vcpus. As the VCPUs +create new shadow page tables, the old pages are not used because of the +mismatching generation number. + +KVM then walks through all pages and zaps obsolete pages. While the zap +operation needs to take the MMU lock, the lock can be released periodically +so that the VCPUs can make progress. + +Fast invalidation of MMIO sptes +=============================== + +As mentioned in "Reaction to events" above, kvm will cache MMIO +information in leaf sptes. When a new memslot is added or an existing +memslot is changed, this information may become stale and needs to be +invalidated. This also needs to hold the MMU lock while walking all +shadow pages, and is made more scalable with a similar technique. + +MMIO sptes have a few spare bits, which are used to store a +generation number. The global generation number is stored in +kvm_memslots(kvm)->generation, and increased whenever guest memory info +changes. This generation number is distinct from the one described in +the previous section. + +When KVM finds an MMIO spte, it checks the generation number of the spte. +If the generation number of the spte does not equal the global generation +number, it will ignore the cached MMIO information and handle the page +fault through the slow path. + +Since only 19 bits are used to store generation-number on mmio spte, all +pages are zapped when there is an overflow. + + Further reading =============== diff --git a/MAINTAINERS b/MAINTAINERS index 8e12f6908247..3f7710151a75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -242,6 +242,11 @@ F: drivers/acpi/ F: drivers/pnp/pnpacpi/ F: include/linux/acpi.h F: include/acpi/ +F: Documentation/acpi +F: Documentation/ABI/testing/sysfs-bus-acpi +F: drivers/pci/*acpi* +F: drivers/pci/*/*acpi* +F: drivers/pci/*/*/*acpi* ACPI FAN DRIVER M: Zhang Rui <rui.zhang@intel.com> @@ -1320,6 +1325,7 @@ W: http://wiki.xilinx.com T: git git://git.xilinx.com/linux-xlnx.git S: Supported F: arch/arm/mach-zynq/ +F: drivers/cpuidle/cpuidle-zynq.c ARM64 PORT (AARCH64 ARCHITECTURE) M: Catalin Marinas <catalin.marinas@arm.com> @@ -2132,9 +2138,10 @@ M: Mike Turquette <mturquette@linaro.org> L: linux-arm-kernel@lists.infradead.org (same as CLK API & CLKDEV) T: git git://git.linaro.org/people/mturquette/linux.git S: Maintained -F: drivers/clk/clk.c -F: drivers/clk/clk-* +F: drivers/clk/ +X: drivers/clk/clkdev.c F: include/linux/clk-pr* +F: include/linux/clk/ COMMON INTERNET FILE SYSTEM (CIFS) M: Steve French <sfrench@samba.org> @@ -2227,7 +2234,8 @@ M: Viresh Kumar <viresh.kumar@linaro.org> L: cpufreq@vger.kernel.org L: linux-pm@vger.kernel.org S: Maintained -T: git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git +T: git git://git.linaro.org/people/vireshk/linux.git (For ARM Updates) F: drivers/cpufreq/ F: include/linux/cpufreq.h @@ -2528,7 +2536,7 @@ F: drivers/usb/dwc3/ DEVICE FREQUENCY (DEVFREQ) M: MyungJoo Ham <myungjoo.ham@samsung.com> M: Kyungmin Park <kyungmin.park@samsung.com> -L: linux-kernel@vger.kernel.org +L: linux-pm@vger.kernel.org S: Maintained F: drivers/devfreq/ @@ -3588,6 +3596,7 @@ GPIO SUBSYSTEM M: Grant Likely <grant.likely@linaro.org> M: Linus Walleij <linus.walleij@linaro.org> S: Maintained +L: linux-gpio@vger.kernel.org T: git git://git.secretlab.ca/git/linux-2.6.git F: Documentation/gpio.txt F: drivers/gpio/ @@ -4000,7 +4009,8 @@ S: Maintained F: arch/ia64/ IBM Power in-Nest Crypto Acceleration -M: Kent Yoder <key@linux.vnet.ibm.com> +M: Marcelo Henrique Cerri <mhcerri@linux.vnet.ibm.com> +M: Fionnuala Gunter <fin@linux.vnet.ibm.com> L: linux-crypto@vger.kernel.org S: Supported F: drivers/crypto/nx/ @@ -4129,6 +4139,7 @@ F: drivers/ipack/ INTEGRITY MEASUREMENT ARCHITECTURE (IMA) M: Mimi Zohar <zohar@us.ibm.com> +M: Dmitry Kasatkin <d.kasatkin@samsung.com> S: Supported F: security/integrity/ima/ @@ -4732,10 +4743,10 @@ F: arch/s390/kvm/ F: drivers/s390/kvm/ KERNEL VIRTUAL MACHINE (KVM) FOR ARM -M: Christoffer Dall <cdall@cs.columbia.edu> +M: Christoffer Dall <christoffer.dall@linaro.org> L: kvmarm@lists.cs.columbia.edu W: http://systems.cs.columbia.edu/projects/kvm-arm -S: Maintained +S: Supported F: arch/arm/include/uapi/asm/kvm* F: arch/arm/include/asm/kvm* F: arch/arm/kvm/ @@ -6297,6 +6308,16 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/pinctrl/pinctrl-at91.c +PIN CONTROLLER - SAMSUNG +M: Tomasz Figa <t.figa@samsung.com> +M: Thomas Abraham <thomas.abraham@linaro.org> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers) +S: Maintained +F: drivers/pinctrl/pinctrl-exynos.* +F: drivers/pinctrl/pinctrl-s3c* +F: drivers/pinctrl/pinctrl-samsung.* + PIN CONTROLLER - ST SPEAR M: Viresh Kumar <viresh.linux@gmail.com> L: spear-devel@list.st.com @@ -8274,7 +8295,8 @@ S: Odd fixes F: drivers/media/usb/tm6000/ TPM DEVICE DRIVER -M: Kent Yoder <key@linux.vnet.ibm.com> +M: Leonidas Da Silva Barbosa <leosilva@linux.vnet.ibm.com> +M: Ashley Lai <ashley@ashleylai.com> M: Rajiv Andrade <mail@srajiv.net> W: http://tpmdd.sourceforge.net M: Marcel Selhorst <tpmdd@selhorst.net> diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 5917099470ea..4a0e54fc01b2 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -184,6 +184,7 @@ config ARC_CACHE_PAGES config ARC_CACHE_VIPT_ALIASING bool "Support VIPT Aliasing D$" + depends on ARC_HAS_DCACHE default n endif #ARC_CACHE @@ -361,13 +362,6 @@ config ARC_MISALIGN_ACCESS Use ONLY-IF-ABS-NECESSARY as it will be very slow and also can hide potential bugs in code -config ARC_STACK_NONEXEC - bool "Make stack non-executable" - default n - help - To disable the execute permissions of stack/heap of processes - which are enabled by default. - config HZ int "Timer Frequency" default 100 diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 183397fd289e..8c0b1aa56f7e 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -9,25 +9,27 @@ UTS_MACHINE := arc ifeq ($(CROSS_COMPILE),) -CROSS_COMPILE := arc-elf32- +CROSS_COMPILE := arc-linux-uclibc- endif KBUILD_DEFCONFIG := fpga_defconfig cflags-y += -mA7 -fno-common -pipe -fno-builtin -D__linux__ -LINUXINCLUDE += -include ${src}/arch/arc/include/asm/defines.h - ifdef CONFIG_ARC_CURR_IN_REG # For a global register defintion, make sure it gets passed to every file # We had a customer reported bug where some code built in kernel was NOT using # any kernel headers, and missing the r25 global register -# Can't do unconditionally (like above) because of recursive include issues +# Can't do unconditionally because of recursive include issues # due to <linux/thread_info.h> LINUXINCLUDE += -include ${src}/arch/arc/include/asm/current.h endif -atleast_gcc44 := $(call cc-ifversion, -gt, 0402, y) +upto_gcc42 := $(call cc-ifversion, -le, 0402, y) +upto_gcc44 := $(call cc-ifversion, -le, 0404, y) +atleast_gcc44 := $(call cc-ifversion, -ge, 0404, y) +atleast_gcc48 := $(call cc-ifversion, -ge, 0408, y) + cflags-$(atleast_gcc44) += -fsection-anchors cflags-$(CONFIG_ARC_HAS_LLSC) += -mlock @@ -35,6 +37,11 @@ cflags-$(CONFIG_ARC_HAS_SWAPE) += -mswape cflags-$(CONFIG_ARC_HAS_RTSC) += -mrtsc cflags-$(CONFIG_ARC_DW2_UNWIND) += -fasynchronous-unwind-tables +# By default gcc 4.8 generates dwarf4 which kernel unwinder can't grok +ifeq ($(atleast_gcc48),y) +cflags-$(CONFIG_ARC_DW2_UNWIND) += -gdwarf-2 +endif + ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE # Generic build system uses -O2, we want -O3 cflags-y += -O3 @@ -48,11 +55,10 @@ cflags-$(disable_small_data) += -mno-sdata -fcall-used-gp cflags-$(CONFIG_CPU_BIG_ENDIAN) += -mbig-endian ldflags-$(CONFIG_CPU_BIG_ENDIAN) += -EB -# STAR 9000518362: +# STAR 9000518362: (fixed with binutils shipping with gcc 4.8) # arc-linux-uclibc-ld (buildroot) or arceb-elf32-ld (EZChip) don't accept -# --build-id w/o "-marclinux". -# Default arc-elf32-ld is OK -ldflags-y += -marclinux +# --build-id w/o "-marclinux". Default arc-elf32-ld is OK +ldflags-$(upto_gcc44) += -marclinux ARC_LIBGCC := -mA7 cflags-$(CONFIG_ARC_HAS_HW_MPY) += -multcost=16 @@ -66,8 +72,8 @@ ifndef CONFIG_ARC_HAS_HW_MPY # With gcc 4.4.7, -mno-mpy is enough to make any other related adjustments, # e.g. increased cost of MPY. With gcc 4.2.1 this had to be explicitly hinted - ARC_LIBGCC := -marc600 - ifneq ($(atleast_gcc44),y) + ifeq ($(upto_gcc42),y) + ARC_LIBGCC := -marc600 cflags-y += -multcost=30 endif endif diff --git a/arch/arc/configs/fpga_defconfig b/arch/arc/configs/fpga_defconfig index 95350be6ef6f..c109af320274 100644 --- a/arch/arc/configs/fpga_defconfig +++ b/arch/arc/configs/fpga_defconfig @@ -1,4 +1,4 @@ -CONFIG_CROSS_COMPILE="arc-elf32-" +CONFIG_CROSS_COMPILE="arc-linux-uclibc-" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_DEFAULT_HOSTNAME="ARCLinux" # CONFIG_SWAP is not set diff --git a/arch/arc/configs/nsimosci_defconfig b/arch/arc/configs/nsimosci_defconfig index 446c96c24eff..451af30914f6 100644 --- a/arch/arc/configs/nsimosci_defconfig +++ b/arch/arc/configs/nsimosci_defconfig @@ -1,4 +1,4 @@ -CONFIG_CROSS_COMPILE="arc-elf32-" +CONFIG_CROSS_COMPILE="arc-linux-uclibc-" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_DEFAULT_HOSTNAME="ARCLinux" # CONFIG_SWAP is not set diff --git a/arch/arc/configs/tb10x_defconfig b/arch/arc/configs/tb10x_defconfig index 4fa5cd9f2202..6be6492442d6 100644 --- a/arch/arc/configs/tb10x_defconfig +++ b/arch/arc/configs/tb10x_defconfig @@ -1,4 +1,4 @@ -CONFIG_CROSS_COMPILE="arc-elf32-" +CONFIG_CROSS_COMPILE="arc-linux-uclibc-" # CONFIG_LOCALVERSION_AUTO is not set CONFIG_DEFAULT_HOSTNAME="tb10x" CONFIG_SYSVIPC=y diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 1b907c465666..355cb470c2a4 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -20,7 +20,6 @@ #define ARC_REG_PERIBASE_BCR 0x69 #define ARC_REG_FP_BCR 0x6B /* Single-Precision FPU */ #define ARC_REG_DPFP_BCR 0x6C /* Dbl Precision FPU */ -#define ARC_REG_MMU_BCR 0x6f #define ARC_REG_DCCM_BCR 0x74 /* DCCM Present + SZ */ #define ARC_REG_TIMERS_BCR 0x75 #define ARC_REG_ICCM_BCR 0x78 @@ -34,22 +33,12 @@ #define ARC_REG_D_UNCACH_BCR 0x6A /* status32 Bits Positions */ -#define STATUS_H_BIT 0 /* CPU Halted */ -#define STATUS_E1_BIT 1 /* Int 1 enable */ -#define STATUS_E2_BIT 2 /* Int 2 enable */ -#define STATUS_A1_BIT 3 /* Int 1 active */ -#define STATUS_A2_BIT 4 /* Int 2 active */ #define STATUS_AE_BIT 5 /* Exception active */ #define STATUS_DE_BIT 6 /* PC is in delay slot */ #define STATUS_U_BIT 7 /* User/Kernel mode */ #define STATUS_L_BIT 12 /* Loop inhibit */ /* These masks correspond to the status word(STATUS_32) bits */ -#define STATUS_H_MASK (1<<STATUS_H_BIT) -#define STATUS_E1_MASK (1<<STATUS_E1_BIT) -#define STATUS_E2_MASK (1<<STATUS_E2_BIT) -#define STATUS_A1_MASK (1<<STATUS_A1_BIT) -#define STATUS_A2_MASK (1<<STATUS_A2_BIT) #define STATUS_AE_MASK (1<<STATUS_AE_BIT) #define STATUS_DE_MASK (1<<STATUS_DE_BIT) #define STATUS_U_MASK (1<<STATUS_U_BIT) @@ -71,6 +60,7 @@ #define ECR_V_ITLB_MISS 0x21 #define ECR_V_DTLB_MISS 0x22 #define ECR_V_PROTV 0x23 +#define ECR_V_TRAP 0x25 /* Protection Violation Exception Cause Code Values */ #define ECR_C_PROTV_INST_FETCH 0x00 @@ -79,94 +69,23 @@ #define ECR_C_PROTV_XCHG 0x03 #define ECR_C_PROTV_MISALIG_DATA 0x04 +#define ECR_C_BIT_PROTV_MISALIG_DATA 10 + +/* Machine Check Cause Code Values */ +#define ECR_C_MCHK_DUP_TLB 0x01 + /* DTLB Miss Exception Cause Code Values */ #define ECR_C_BIT_DTLB_LD_MISS 8 #define ECR_C_BIT_DTLB_ST_MISS 9 +/* Dummy ECR values for Interrupts */ +#define event_IRQ1 0x0031abcd +#define event_IRQ2 0x0032abcd /* Auxiliary registers */ #define AUX_IDENTITY 4 #define AUX_INTR_VEC_BASE 0x25 -#define AUX_IRQ_LEV 0x200 /* IRQ Priority: L1 or L2 */ -#define AUX_IRQ_HINT 0x201 /* For generating Soft Interrupts */ -#define AUX_IRQ_LV12 0x43 /* interrupt level register */ - -#define AUX_IENABLE 0x40c -#define AUX_ITRIGGER 0x40d -#define AUX_IPULSE 0x415 - -/* Timer related Aux registers */ -#define ARC_REG_TIMER0_LIMIT 0x23 /* timer 0 limit */ -#define ARC_REG_TIMER0_CTRL 0x22 /* timer 0 control */ -#define ARC_REG_TIMER0_CNT 0x21 /* timer 0 count */ -#define ARC_REG_TIMER1_LIMIT 0x102 /* timer 1 limit */ -#define ARC_REG_TIMER1_CTRL 0x101 /* timer 1 control */ -#define ARC_REG_TIMER1_CNT 0x100 /* timer 1 count */ - -#define TIMER_CTRL_IE (1 << 0) /* Interupt when Count reachs limit */ -#define TIMER_CTRL_NH (1 << 1) /* Count only when CPU NOT halted */ - -/* MMU Management regs */ -#define ARC_REG_TLBPD0 0x405 -#define ARC_REG_TLBPD1 0x406 -#define ARC_REG_TLBINDEX 0x407 -#define ARC_REG_TLBCOMMAND 0x408 -#define ARC_REG_PID 0x409 -#define ARC_REG_SCRATCH_DATA0 0x418 - -/* Bits in MMU PID register */ -#define MMU_ENABLE (1 << 31) /* Enable MMU for process */ - -/* Error code if probe fails */ -#define TLB_LKUP_ERR 0x80000000 - -/* TLB Commands */ -#define TLBWrite 0x1 -#define TLBRead 0x2 -#define TLBGetIndex 0x3 -#define TLBProbe 0x4 - -#if (CONFIG_ARC_MMU_VER >= 2) -#define TLBWriteNI 0x5 /* write JTLB without inv uTLBs */ -#define TLBIVUTLB 0x6 /* explicitly inv uTLBs */ -#else -#undef TLBWriteNI /* These cmds don't exist on older MMU */ -#undef TLBIVUTLB -#endif -/* Instruction cache related Auxiliary registers */ -#define ARC_REG_IC_BCR 0x77 /* Build Config reg */ -#define ARC_REG_IC_IVIC 0x10 -#define ARC_REG_IC_CTRL 0x11 -#define ARC_REG_IC_IVIL 0x19 -#if (CONFIG_ARC_MMU_VER > 2) -#define ARC_REG_IC_PTAG 0x1E -#endif - -/* Bit val in IC_CTRL */ -#define IC_CTRL_CACHE_DISABLE 0x1 - -/* Data cache related Auxiliary registers */ -#define ARC_REG_DC_BCR 0x72 -#define ARC_REG_DC_IVDC 0x47 -#define ARC_REG_DC_CTRL 0x48 -#define ARC_REG_DC_IVDL 0x4A -#define ARC_REG_DC_FLSH 0x4B -#define ARC_REG_DC_FLDL 0x4C -#if (CONFIG_ARC_MMU_VER > 2) -#define ARC_REG_DC_PTAG 0x5C -#endif - -/* Bit val in DC_CTRL */ -#define DC_CTRL_INV_MODE_FLUSH 0x40 -#define DC_CTRL_FLUSH_STATUS 0x100 - -/* MMU Management regs */ -#define ARC_REG_PID 0x409 -#define ARC_REG_SCRATCH_DATA0 0x418 - -/* Bits in MMU PID register */ -#define MMU_ENABLE (1 << 31) /* Enable MMU for process */ /* * Floating Pt Registers @@ -293,24 +212,6 @@ struct bcr_identity { #endif }; -struct bcr_mmu_1_2 { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int ver:8, ways:4, sets:4, u_itlb:8, u_dtlb:8; -#else - unsigned int u_dtlb:8, u_itlb:8, sets:4, ways:4, ver:8; -#endif -}; - -struct bcr_mmu_3 { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int ver:8, ways:4, sets:4, osm:1, reserv:3, pg_sz:4, - u_itlb:4, u_dtlb:4; -#else - unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, reserv:3, osm:1, sets:4, - ways:4, ver:8; -#endif -}; - #define EXTN_SWAP_VALID 0x1 #define EXTN_NORM_VALID 0x2 #define EXTN_MINMAX_VALID 0x2 @@ -343,14 +244,6 @@ struct bcr_extn_xymem { #endif }; -struct bcr_cache { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int pad:12, line_len:4, sz:4, config:4, ver:8; -#else - unsigned int ver:8, config:4, sz:4, line_len:4, pad:12; -#endif -}; - struct bcr_perip { #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int start:8, pad2:8, sz:8, pad:8; @@ -403,7 +296,7 @@ struct cpuinfo_arc_mmu { }; struct cpuinfo_arc_cache { - unsigned int has_aliasing, sz, line_len, assoc, ver; + unsigned int sz, line_len, assoc, ver; }; struct cpuinfo_arc_ccm { diff --git a/arch/arc/include/asm/bug.h b/arch/arc/include/asm/bug.h index 2ad8f9b1c54b..5b18e94c6678 100644 --- a/arch/arc/include/asm/bug.h +++ b/arch/arc/include/asm/bug.h @@ -18,9 +18,8 @@ struct task_struct; void show_regs(struct pt_regs *regs); void show_stacktrace(struct task_struct *tsk, struct pt_regs *regs); void show_kernel_fault_diag(const char *str, struct pt_regs *regs, - unsigned long address, unsigned long cause_reg); -void die(const char *str, struct pt_regs *regs, unsigned long address, - unsigned long cause_reg); + unsigned long address); +void die(const char *str, struct pt_regs *regs, unsigned long address); #define BUG() do { \ dump_stack(); \ diff --git a/arch/arc/include/asm/cache.h b/arch/arc/include/asm/cache.h index d5555fe4742a..5802849a6cae 100644 --- a/arch/arc/include/asm/cache.h +++ b/arch/arc/include/asm/cache.h @@ -18,21 +18,19 @@ #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) -#define ARC_ICACHE_WAYS 2 -#define ARC_DCACHE_WAYS 4 - -/* Helpers */ +/* For a rare case where customers have differently config I/D */ #define ARC_ICACHE_LINE_LEN L1_CACHE_BYTES #define ARC_DCACHE_LINE_LEN L1_CACHE_BYTES #define ICACHE_LINE_MASK (~(ARC_ICACHE_LINE_LEN - 1)) #define DCACHE_LINE_MASK (~(ARC_DCACHE_LINE_LEN - 1)) -#if ARC_ICACHE_LINE_LEN != ARC_DCACHE_LINE_LEN -#error "Need to fix some code as I/D cache lines not same" -#else -#define is_not_cache_aligned(p) ((unsigned long)p & (~DCACHE_LINE_MASK)) -#endif +/* + * ARC700 doesn't cache any access in top 256M. + * Ideal for wiring memory mapped peripherals as we don't need to do + * explicit uncached accesses (LD.di/ST.di) hence more portable drivers + */ +#define ARC_UNCACHED_ADDR_SPACE 0xc0000000 #ifndef __ASSEMBLY__ @@ -57,16 +55,10 @@ #define ARCH_DMA_MINALIGN L1_CACHE_BYTES -/* - * ARC700 doesn't cache any access in top 256M. - * Ideal for wiring memory mapped peripherals as we don't need to do - * explicit uncached accesses (LD.di/ST.di) hence more portable drivers - */ -#define ARC_UNCACHED_ADDR_SPACE 0xc0000000 - extern void arc_cache_init(void); extern char *arc_cache_mumbojumbo(int cpu_id, char *buf, int len); extern void __init read_decode_cache_bcr(void); -#endif + +#endif /* !__ASSEMBLY__ */ #endif /* _ASM_CACHE_H */ diff --git a/arch/arc/include/asm/cacheflush.h b/arch/arc/include/asm/cacheflush.h index ef62682e8d95..6abc4972bc93 100644 --- a/arch/arc/include/asm/cacheflush.h +++ b/arch/arc/include/asm/cacheflush.h @@ -81,16 +81,19 @@ void flush_anon_page(struct vm_area_struct *vma, #endif /* CONFIG_ARC_CACHE_VIPT_ALIASING */ /* + * A new pagecache page has PG_arch_1 clear - thus dcache dirty by default + * This works around some PIO based drivers which don't call flush_dcache_page + * to record that they dirtied the dcache + */ +#define PG_dc_clean PG_arch_1 + +/* * Simple wrapper over config option * Bootup code ensures that hardware matches kernel configuration */ static inline int cache_is_vipt_aliasing(void) { -#ifdef CONFIG_ARC_CACHE_VIPT_ALIASING - return 1; -#else - return 0; -#endif + return IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING); } #define CACHE_COLOR(addr) (((unsigned long)(addr) >> (PAGE_SHIFT)) & 1) diff --git a/arch/arc/include/asm/defines.h b/arch/arc/include/asm/defines.h deleted file mode 100644 index 6097bb439cc5..000000000000 --- a/arch/arc/include/asm/defines.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.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 __ARC_ASM_DEFINES_H__ -#define __ARC_ASM_DEFINES_H__ - -#if defined(CONFIG_ARC_MMU_V1) -#define CONFIG_ARC_MMU_VER 1 -#elif defined(CONFIG_ARC_MMU_V2) -#define CONFIG_ARC_MMU_VER 2 -#elif defined(CONFIG_ARC_MMU_V3) -#define CONFIG_ARC_MMU_VER 3 -#endif - -#ifdef CONFIG_ARC_HAS_LLSC -#define __CONFIG_ARC_HAS_LLSC_VAL 1 -#else -#define __CONFIG_ARC_HAS_LLSC_VAL 0 -#endif - -#ifdef CONFIG_ARC_HAS_SWAPE -#define __CONFIG_ARC_HAS_SWAPE_VAL 1 -#else -#define __CONFIG_ARC_HAS_SWAPE_VAL 0 -#endif - -#ifdef CONFIG_ARC_HAS_RTSC -#define __CONFIG_ARC_HAS_RTSC_VAL 1 -#else -#define __CONFIG_ARC_HAS_RTSC_VAL 0 -#endif - -#ifdef CONFIG_ARC_MMU_SASID -#define __CONFIG_ARC_MMU_SASID_VAL 1 -#else -#define __CONFIG_ARC_MMU_SASID_VAL 0 -#endif - -#ifdef CONFIG_ARC_HAS_ICACHE -#define __CONFIG_ARC_HAS_ICACHE 1 -#else -#define __CONFIG_ARC_HAS_ICACHE 0 -#endif - -#ifdef CONFIG_ARC_HAS_DCACHE -#define __CONFIG_ARC_HAS_DCACHE 1 -#else -#define __CONFIG_ARC_HAS_DCACHE 0 -#endif - -#endif /* __ARC_ASM_DEFINES_H__ */ diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h index eb2ae53187d9..8943c028d4bb 100644 --- a/arch/arc/include/asm/entry.h +++ b/arch/arc/include/asm/entry.h @@ -50,194 +50,177 @@ * Eff Addr for load = [reg2] */ +.macro PUSH reg + st.a \reg, [sp, -4] +.endm + +.macro PUSHAX aux + lr r9, [\aux] + PUSH r9 +.endm + +.macro POP reg + ld.ab \reg, [sp, 4] +.endm + +.macro POPAX aux + POP r9 + sr r9, [\aux] +.endm + /*-------------------------------------------------------------- - * Save caller saved registers (scratch registers) ( r0 - r12 ) - * Registers are pushed / popped in the order defined in struct ptregs - * in asm/ptrace.h + * Helpers to save/restore Scratch Regs: + * used by Interrupt/Exception Prologue/Epilogue *-------------------------------------------------------------*/ -.macro SAVE_CALLER_SAVED - st.a r0, [sp, -4] - st.a r1, [sp, -4] - st.a r2, [sp, -4] - st.a r3, [sp, -4] - st.a r4, [sp, -4] - st.a r5, [sp, -4] - st.a r6, [sp, -4] - st.a r7, [sp, -4] - st.a r8, [sp, -4] - st.a r9, [sp, -4] - st.a r10, [sp, -4] - st.a r11, [sp, -4] - st.a r12, [sp, -4] +.macro SAVE_R0_TO_R12 + PUSH r0 + PUSH r1 + PUSH r2 + PUSH r3 + PUSH r4 + PUSH r5 + PUSH r6 + PUSH r7 + PUSH r8 + PUSH r9 + PUSH r10 + PUSH r11 + PUSH r12 +.endm + +.macro RESTORE_R12_TO_R0 + POP r12 + POP r11 + POP r10 + POP r9 + POP r8 + POP r7 + POP r6 + POP r5 + POP r4 + POP r3 + POP r2 + POP r1 + POP r0 + +#ifdef CONFIG_ARC_CURR_IN_REG + ld r25, [sp, 12] +#endif .endm /*-------------------------------------------------------------- - * Restore caller saved registers (scratch registers) + * Helpers to save/restore callee-saved regs: + * used by several macros below *-------------------------------------------------------------*/ -.macro RESTORE_CALLER_SAVED - ld.ab r12, [sp, 4] - ld.ab r11, [sp, 4] - ld.ab r10, [sp, 4] - ld.ab r9, [sp, 4] - ld.ab r8, [sp, 4] - ld.ab r7, [sp, 4] - ld.ab r6, [sp, 4] - ld.ab r5, [sp, 4] - ld.ab r4, [sp, 4] - ld.ab r3, [sp, 4] - ld.ab r2, [sp, 4] - ld.ab r1, [sp, 4] - ld.ab r0, [sp, 4] +.macro SAVE_R13_TO_R24 + PUSH r13 + PUSH r14 + PUSH r15 + PUSH r16 + PUSH r17 + PUSH r18 + PUSH r19 + PUSH r20 + PUSH r21 + PUSH r22 + PUSH r23 + PUSH r24 +.endm + +.macro RESTORE_R24_TO_R13 + POP r24 + POP r23 + POP r22 + POP r21 + POP r20 + POP r19 + POP r18 + POP r17 + POP r16 + POP r15 + POP r14 + POP r13 .endm +#define OFF_USER_R25_FROM_R24 (SZ_CALLEE_REGS + SZ_PT_REGS - 8)/4 /*-------------------------------------------------------------- - * Save callee saved registers (non scratch registers) ( r13 - r25 ) - * on kernel stack. - * User mode callee regs need to be saved in case of - * -fork and friends for replicating from parent to child - * -before going into do_signal( ) for ptrace/core-dump - * Special case handling is required for r25 in case it is used by kernel - * for caching task ptr. Low level exception/ISR save user mode r25 - * into task->thread.user_r25. So it needs to be retrieved from there and - * saved into kernel stack with rest of callee reg-file + * Collect User Mode callee regs as struct callee_regs - needed by + * fork/do_signal/unaligned-access-emulation. + * (By default only scratch regs are saved on entry to kernel) + * + * Special handling for r25 if used for caching Task Pointer. + * It would have been saved in task->thread.user_r25 already, but to keep + * the interface same it is copied into regular r25 placeholder in + * struct callee_regs. *-------------------------------------------------------------*/ .macro SAVE_CALLEE_SAVED_USER - st.a r13, [sp, -4] - st.a r14, [sp, -4] - st.a r15, [sp, -4] - st.a r16, [sp, -4] - st.a r17, [sp, -4] - st.a r18, [sp, -4] - st.a r19, [sp, -4] - st.a r20, [sp, -4] - st.a r21, [sp, -4] - st.a r22, [sp, -4] - st.a r23, [sp, -4] - st.a r24, [sp, -4] + + SAVE_R13_TO_R24 #ifdef CONFIG_ARC_CURR_IN_REG ; Retrieve orig r25 and save it on stack - ld r12, [r25, TASK_THREAD + THREAD_USER_R25] + ld.as r12, [sp, OFF_USER_R25_FROM_R24] st.a r12, [sp, -4] #else - st.a r25, [sp, -4] + PUSH r25 #endif - /* move up by 1 word to "create" callee_regs->"stack_place_holder" */ - sub sp, sp, 4 .endm /*-------------------------------------------------------------- - * Save callee saved registers (non scratch registers) ( r13 - r25 ) - * kernel mode callee regs needed to be saved in case of context switch - * If r25 is used for caching task pointer then that need not be saved - * as it can be re-created from current task global + * Save kernel Mode callee regs at the time of Contect Switch. + * + * Special handling for r25 if used for caching Task Pointer. + * Kernel simply skips saving it since it will be loaded with + * incoming task pointer anyways *-------------------------------------------------------------*/ .macro SAVE_CALLEE_SAVED_KERNEL - st.a r13, [sp, -4] - st.a r14, [sp, -4] - st.a r15, [sp, -4] - st.a r16, [sp, -4] - st.a r17, [sp, -4] - st.a r18, [sp, -4] - st.a r19, [sp, -4] - st.a r20, [sp, -4] - st.a r21, [sp, -4] - st.a r22, [sp, -4] - st.a r23, [sp, -4] - st.a r24, [sp, -4] + + SAVE_R13_TO_R24 + #ifdef CONFIG_ARC_CURR_IN_REG - sub sp, sp, 8 -#else - st.a r25, [sp, -4] sub sp, sp, 4 +#else + PUSH r25 #endif .endm /*-------------------------------------------------------------- - * RESTORE_CALLEE_SAVED_KERNEL: - * Loads callee (non scratch) Reg File by popping from Kernel mode stack. - * This is reverse of SAVE_CALLEE_SAVED, - * - * NOTE: - * Ideally this shd only be called in switch_to for loading - * switched-IN task's CALLEE Reg File. - * For all other cases RESTORE_CALLEE_SAVED_FAST must be used - * which simply pops the stack w/o touching regs. + * Opposite of SAVE_CALLEE_SAVED_KERNEL *-------------------------------------------------------------*/ .macro RESTORE_CALLEE_SAVED_KERNEL - #ifdef CONFIG_ARC_CURR_IN_REG - add sp, sp, 8 /* skip callee_reg gutter and user r25 placeholder */ + add sp, sp, 4 /* skip usual r25 placeholder */ #else - add sp, sp, 4 /* skip "callee_regs->stack_place_holder" */ - ld.ab r25, [sp, 4] + POP r25 #endif - - ld.ab r24, [sp, 4] - ld.ab r23, [sp, 4] - ld.ab r22, [sp, 4] - ld.ab r21, [sp, 4] - ld.ab r20, [sp, 4] - ld.ab r19, [sp, 4] - ld.ab r18, [sp, 4] - ld.ab r17, [sp, 4] - ld.ab r16, [sp, 4] - ld.ab r15, [sp, 4] - ld.ab r14, [sp, 4] - ld.ab r13, [sp, 4] - + RESTORE_R24_TO_R13 .endm /*-------------------------------------------------------------- - * RESTORE_CALLEE_SAVED_USER: - * This is called after do_signal where tracer might have changed callee regs - * thus we need to restore the reg file. - * Special case handling is required for r25 in case it is used by kernel - * for caching task ptr. Ptrace would have modified on-kernel-stack value of - * r25, which needs to be shoved back into task->thread.user_r25 where from - * Low level exception/ISR return code will retrieve to populate with rest of - * callee reg-file. + * Opposite of SAVE_CALLEE_SAVED_USER + * + * ptrace tracer or unaligned-access fixup might have changed a user mode + * callee reg which is saved back to usual r25 storage location *-------------------------------------------------------------*/ .macro RESTORE_CALLEE_SAVED_USER - add sp, sp, 4 /* skip "callee_regs->stack_place_holder" */ - #ifdef CONFIG_ARC_CURR_IN_REG ld.ab r12, [sp, 4] - st r12, [r25, TASK_THREAD + THREAD_USER_R25] + st.as r12, [sp, OFF_USER_R25_FROM_R24] #else - ld.ab r25, [sp, 4] + POP r25 #endif - - ld.ab r24, [sp, 4] - ld.ab r23, [sp, 4] - ld.ab r22, [sp, 4] - ld.ab r21, [sp, 4] - ld.ab r20, [sp, 4] - ld.ab r19, [sp, 4] - ld.ab r18, [sp, 4] - ld.ab r17, [sp, 4] - ld.ab r16, [sp, 4] - ld.ab r15, [sp, 4] - ld.ab r14, [sp, 4] - ld.ab r13, [sp, 4] + RESTORE_R24_TO_R13 .endm /*-------------------------------------------------------------- * Super FAST Restore callee saved regs by simply re-adjusting SP *-------------------------------------------------------------*/ .macro DISCARD_CALLEE_SAVED_USER - add sp, sp, 14 * 4 -.endm - -/*-------------------------------------------------------------- - * Restore User mode r25 saved in task_struct->thread.user_r25 - *-------------------------------------------------------------*/ -.macro RESTORE_USER_R25 - ld r25, [r25, TASK_THREAD + THREAD_USER_R25] + add sp, sp, SZ_CALLEE_REGS .endm /*------------------------------------------------------------- @@ -252,7 +235,7 @@ ld \out, [\tsk, TASK_THREAD_INFO] /* Go to end of page where stack begins (grows upwards) */ - add2 \out, \out, (THREAD_SIZE - 4)/4 /* one word GUTTER */ + add2 \out, \out, (THREAD_SIZE)/4 .endm @@ -305,33 +288,28 @@ * safe-keeping not really needed, but it keeps the epilogue code * (SP restore) simpler/uniform. */ - b.d 77f - - st.a sp, [sp, -12] ; Make room for orig_r0 and orig_r8 + b.d 66f + mov r9, sp 88: /*------Intr/Ecxp happened in user mode, "switch" stack ------ */ GET_CURR_TASK_ON_CPU r9 -#ifdef CONFIG_ARC_CURR_IN_REG - - /* If current task pointer cached in r25, time to - * -safekeep USER r25 in task->thread_struct->user_r25 - * -load r25 with current task ptr - */ - st.as r25, [r9, (TASK_THREAD + THREAD_USER_R25)/4] - mov r25, r9 -#endif - /* With current tsk in r9, get it's kernel mode stack base */ GET_TSK_STACK_BASE r9, r9 -#ifdef PT_REGS_CANARY - st 0xabcdabcd, [r9, 0] +66: +#ifdef CONFIG_ARC_CURR_IN_REG + /* + * Treat r25 as scratch reg, save it on stack first + * Load it with current task pointer + */ + st r25, [r9, -4] + GET_CURR_TASK_ON_CPU r25 #endif /* Save Pre Intr/Exception User SP on kernel stack */ - st.a sp, [r9, -12] ; Make room for orig_r0 and orig_r8 + st.a sp, [r9, -16] ; Make room for orig_r0, ECR, user_r25 /* CAUTION: * SP should be set at the very end when we are done with everything @@ -342,7 +320,7 @@ /* set SP to point to kernel mode stack */ mov sp, r9 -77: /* ----- Stack Switched to kernel Mode, Now save REG FILE ----- */ + /* ----- Stack Switched to kernel Mode, Now save REG FILE ----- */ .endm @@ -369,7 +347,7 @@ * @reg [OUT] &thread_info of "current" */ .macro GET_CURR_THR_INFO_FROM_SP reg - and \reg, sp, ~(THREAD_SIZE - 1) + bic \reg, sp, (THREAD_SIZE - 1) .endm /* @@ -413,62 +391,25 @@ * Note that syscalls are implemented via TRAP which is also a exception * from CPU's point of view *-------------------------------------------------------------*/ -.macro SAVE_ALL_EXCEPTION marker +.macro SAVE_ALL_SYS - st \marker, [sp, 8] /* orig_r8 */ + lr r9, [ecr] + st r9, [sp, 8] /* ECR */ st r0, [sp, 4] /* orig_r0, needed only for sys calls */ /* Restore r9 used to code the early prologue */ EXCPN_PROLOG_RESTORE_REG r9 - SAVE_CALLER_SAVED - st.a r26, [sp, -4] /* gp */ - st.a fp, [sp, -4] - st.a blink, [sp, -4] - lr r9, [eret] - st.a r9, [sp, -4] - lr r9, [erstatus] - st.a r9, [sp, -4] - st.a lp_count, [sp, -4] - lr r9, [lp_end] - st.a r9, [sp, -4] - lr r9, [lp_start] - st.a r9, [sp, -4] - lr r9, [erbta] - st.a r9, [sp, -4] - -#ifdef PT_REGS_CANARY - mov r9, 0xdeadbeef - st r9, [sp, -4] -#endif - - /* move up by 1 word to "create" pt_regs->"stack_place_holder" */ - sub sp, sp, 4 -.endm - -/*-------------------------------------------------------------- - * Save scratch regs for exceptions - *-------------------------------------------------------------*/ -.macro SAVE_ALL_SYS - SAVE_ALL_EXCEPTION orig_r8_IS_EXCPN -.endm - -/*-------------------------------------------------------------- - * Save scratch regs for sys calls - *-------------------------------------------------------------*/ -.macro SAVE_ALL_TRAP - /* - * Setup pt_regs->orig_r8. - * Encode syscall number (r8) in upper short word of event type (r9) - * N.B. #1: This is already endian safe (see ptrace.h) - * #2: Only r9 can be used as scratch as it is already clobbered - * and it's contents are no longer needed by the latter part - * of exception prologue - */ - lsl r9, r8, 16 - or r9, r9, orig_r8_IS_SCALL - - SAVE_ALL_EXCEPTION r9 + SAVE_R0_TO_R12 + PUSH gp + PUSH fp + PUSH blink + PUSHAX eret + PUSHAX erstatus + PUSH lp_count + PUSHAX lp_end + PUSHAX lp_start + PUSHAX erbta .endm /*-------------------------------------------------------------- @@ -483,28 +424,22 @@ * by hardware and that is not good. *-------------------------------------------------------------*/ .macro RESTORE_ALL_SYS + POPAX erbta + POPAX lp_start + POPAX lp_end + + POP r9 + mov lp_count, r9 ;LD to lp_count is not allowed - add sp, sp, 4 /* hop over unused "pt_regs->stack_place_holder" */ - - ld.ab r9, [sp, 4] - sr r9, [erbta] - ld.ab r9, [sp, 4] - sr r9, [lp_start] - ld.ab r9, [sp, 4] - sr r9, [lp_end] - ld.ab r9, [sp, 4] - mov lp_count, r9 - ld.ab r9, [sp, 4] - sr r9, [erstatus] - ld.ab r9, [sp, 4] - sr r9, [eret] - ld.ab blink, [sp, 4] - ld.ab fp, [sp, 4] - ld.ab r26, [sp, 4] /* gp */ - RESTORE_CALLER_SAVED + POPAX erstatus + POPAX eret + POP blink + POP fp + POP gp + RESTORE_R12_TO_R0 ld sp, [sp] /* restore original sp */ - /* orig_r0 and orig_r8 skipped automatically */ + /* orig_r0, ECR, user_r25 skipped automatically */ .endm @@ -513,9 +448,7 @@ *-------------------------------------------------------------*/ .macro SAVE_ALL_INT1 - /* restore original r9 , saved in int1_saved_reg - * It will be saved on stack in macro: SAVE_CALLER_SAVED - */ + /* restore original r9 to be saved as part of reg-file */ #ifdef CONFIG_SMP lr r9, [ARC_REG_SCRATCH_DATA0] #else @@ -523,29 +456,19 @@ #endif /* now we are ready to save the remaining context :) */ - st orig_r8_IS_IRQ1, [sp, 8] /* Event Type */ + st event_IRQ1, [sp, 8] /* Dummy ECR */ st 0, [sp, 4] /* orig_r0 , N/A for IRQ */ - SAVE_CALLER_SAVED - st.a r26, [sp, -4] /* gp */ - st.a fp, [sp, -4] - st.a blink, [sp, -4] - st.a ilink1, [sp, -4] - lr r9, [status32_l1] - st.a r9, [sp, -4] - st.a lp_count, [sp, -4] - lr r9, [lp_end] - st.a r9, [sp, -4] - lr r9, [lp_start] - st.a r9, [sp, -4] - lr r9, [bta_l1] - st.a r9, [sp, -4] - -#ifdef PT_REGS_CANARY - mov r9, 0xdeadbee1 - st r9, [sp, -4] -#endif - /* move up by 1 word to "create" pt_regs->"stack_place_holder" */ - sub sp, sp, 4 + + SAVE_R0_TO_R12 + PUSH gp + PUSH fp + PUSH blink + PUSH ilink1 + PUSHAX status32_l1 + PUSH lp_count + PUSHAX lp_end + PUSHAX lp_start + PUSHAX bta_l1 .endm .macro SAVE_ALL_INT2 @@ -558,30 +481,19 @@ ld r9, [@int2_saved_reg] /* now we are ready to save the remaining context :) */ - st orig_r8_IS_IRQ2, [sp, 8] /* Event Type */ + st event_IRQ2, [sp, 8] /* Dummy ECR */ st 0, [sp, 4] /* orig_r0 , N/A for IRQ */ - SAVE_CALLER_SAVED - st.a r26, [sp, -4] /* gp */ - st.a fp, [sp, -4] - st.a blink, [sp, -4] - st.a ilink2, [sp, -4] - lr r9, [status32_l2] - st.a r9, [sp, -4] - st.a lp_count, [sp, -4] - lr r9, [lp_end] - st.a r9, [sp, -4] - lr r9, [lp_start] - st.a r9, [sp, -4] - lr r9, [bta_l2] - st.a r9, [sp, -4] - -#ifdef PT_REGS_CANARY - mov r9, 0xdeadbee2 - st r9, [sp, -4] -#endif - /* move up by 1 word to "create" pt_regs->"stack_place_holder" */ - sub sp, sp, 4 + SAVE_R0_TO_R12 + PUSH gp + PUSH fp + PUSH blink + PUSH ilink2 + PUSHAX status32_l2 + PUSH lp_count + PUSHAX lp_end + PUSHAX lp_start + PUSHAX bta_l2 .endm /*-------------------------------------------------------------- @@ -595,52 +507,41 @@ *-------------------------------------------------------------*/ .macro RESTORE_ALL_INT1 - add sp, sp, 4 /* hop over unused "pt_regs->stack_place_holder" */ - - ld.ab r9, [sp, 4] /* Actual reg file */ - sr r9, [bta_l1] - ld.ab r9, [sp, 4] - sr r9, [lp_start] - ld.ab r9, [sp, 4] - sr r9, [lp_end] - ld.ab r9, [sp, 4] - mov lp_count, r9 - ld.ab r9, [sp, 4] - sr r9, [status32_l1] - ld.ab r9, [sp, 4] - mov ilink1, r9 - ld.ab blink, [sp, 4] - ld.ab fp, [sp, 4] - ld.ab r26, [sp, 4] /* gp */ - RESTORE_CALLER_SAVED + POPAX bta_l1 + POPAX lp_start + POPAX lp_end + + POP r9 + mov lp_count, r9 ;LD to lp_count is not allowed + + POPAX status32_l1 + POP ilink1 + POP blink + POP fp + POP gp + RESTORE_R12_TO_R0 ld sp, [sp] /* restore original sp */ - /* orig_r0 and orig_r8 skipped automatically */ + /* orig_r0, ECR, user_r25 skipped automatically */ .endm .macro RESTORE_ALL_INT2 - add sp, sp, 4 /* hop over unused "pt_regs->stack_place_holder" */ - - ld.ab r9, [sp, 4] - sr r9, [bta_l2] - ld.ab r9, [sp, 4] - sr r9, [lp_start] - ld.ab r9, [sp, 4] - sr r9, [lp_end] - ld.ab r9, [sp, 4] - mov lp_count, r9 - ld.ab r9, [sp, 4] - sr r9, [status32_l2] - ld.ab r9, [sp, 4] - mov ilink2, r9 - ld.ab blink, [sp, 4] - ld.ab fp, [sp, 4] - ld.ab r26, [sp, 4] /* gp */ - RESTORE_CALLER_SAVED + POPAX bta_l2 + POPAX lp_start + POPAX lp_end - ld sp, [sp] /* restore original sp */ - /* orig_r0 and orig_r8 skipped automatically */ + POP r9 + mov lp_count, r9 ;LD to lp_count is not allowed + POPAX status32_l2 + POP ilink2 + POP blink + POP fp + POP gp + RESTORE_R12_TO_R0 + + ld sp, [sp] /* restore original sp */ + /* orig_r0, ECR, user_r25 skipped automatically */ .endm diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h index 57898a17eb82..c0a72105ee0b 100644 --- a/arch/arc/include/asm/irq.h +++ b/arch/arc/include/asm/irq.h @@ -21,6 +21,6 @@ extern void __init arc_init_IRQ(void); extern int __init get_hw_config_num_irq(void); -void __cpuinit arc_local_timer_setup(unsigned int cpu); +void arc_local_timer_setup(unsigned int cpu); #endif diff --git a/arch/arc/include/asm/irqflags.h b/arch/arc/include/asm/irqflags.h index eac071668201..d99f79bcf865 100644 --- a/arch/arc/include/asm/irqflags.h +++ b/arch/arc/include/asm/irqflags.h @@ -19,6 +19,26 @@ #include <asm/arcregs.h> +/* status32 Reg bits related to Interrupt Handling */ +#define STATUS_E1_BIT 1 /* Int 1 enable */ +#define STATUS_E2_BIT 2 /* Int 2 enable */ +#define STATUS_A1_BIT 3 /* Int 1 active */ +#define STATUS_A2_BIT 4 /* Int 2 active */ + +#define STATUS_E1_MASK (1<<STATUS_E1_BIT) +#define STATUS_E2_MASK (1<<STATUS_E2_BIT) +#define STATUS_A1_MASK (1<<STATUS_A1_BIT) +#define STATUS_A2_MASK (1<<STATUS_A2_BIT) + +/* Other Interrupt Handling related Aux regs */ +#define AUX_IRQ_LEV 0x200 /* IRQ Priority: L1 or L2 */ +#define AUX_IRQ_HINT 0x201 /* For generating Soft Interrupts */ +#define AUX_IRQ_LV12 0x43 /* interrupt level register */ + +#define AUX_IENABLE 0x40c +#define AUX_ITRIGGER 0x40d +#define AUX_IPULSE 0x415 + #ifndef __ASSEMBLY__ /****************************************************************** diff --git a/arch/arc/include/asm/kgdb.h b/arch/arc/include/asm/kgdb.h index 4930957ca3d3..b65fca7ffeb5 100644 --- a/arch/arc/include/asm/kgdb.h +++ b/arch/arc/include/asm/kgdb.h @@ -31,7 +31,7 @@ static inline void arch_kgdb_breakpoint(void) __asm__ __volatile__ ("trap_s 0x4\n"); } -extern void kgdb_trap(struct pt_regs *regs, int param); +extern void kgdb_trap(struct pt_regs *regs); enum arc700_linux_regnums { _R0 = 0, @@ -53,7 +53,7 @@ enum arc700_linux_regnums { }; #else -#define kgdb_trap(regs, param) +#define kgdb_trap(regs) #endif #endif /* __ARC_KGDB_H__ */ diff --git a/arch/arc/include/asm/kprobes.h b/arch/arc/include/asm/kprobes.h index 4d9c211fce70..944dbedb38b5 100644 --- a/arch/arc/include/asm/kprobes.h +++ b/arch/arc/include/asm/kprobes.h @@ -50,11 +50,9 @@ struct kprobe_ctlblk { int kprobe_fault_handler(struct pt_regs *regs, unsigned long cause); void kretprobe_trampoline(void); -void trap_is_kprobe(unsigned long cause, unsigned long address, - struct pt_regs *regs); +void trap_is_kprobe(unsigned long address, struct pt_regs *regs); #else -static void trap_is_kprobe(unsigned long cause, unsigned long address, - struct pt_regs *regs) +static void trap_is_kprobe(unsigned long address, struct pt_regs *regs) { } #endif diff --git a/arch/arc/include/asm/mmu.h b/arch/arc/include/asm/mmu.h index 56b02320f1a9..7c03fe61759c 100644 --- a/arch/arc/include/asm/mmu.h +++ b/arch/arc/include/asm/mmu.h @@ -9,6 +9,40 @@ #ifndef _ASM_ARC_MMU_H #define _ASM_ARC_MMU_H +#if defined(CONFIG_ARC_MMU_V1) +#define CONFIG_ARC_MMU_VER 1 +#elif defined(CONFIG_ARC_MMU_V2) +#define CONFIG_ARC_MMU_VER 2 +#elif defined(CONFIG_ARC_MMU_V3) +#define CONFIG_ARC_MMU_VER 3 +#endif + +/* MMU Management regs */ +#define ARC_REG_MMU_BCR 0x06f +#define ARC_REG_TLBPD0 0x405 +#define ARC_REG_TLBPD1 0x406 +#define ARC_REG_TLBINDEX 0x407 +#define ARC_REG_TLBCOMMAND 0x408 +#define ARC_REG_PID 0x409 +#define ARC_REG_SCRATCH_DATA0 0x418 + +/* Bits in MMU PID register */ +#define MMU_ENABLE (1 << 31) /* Enable MMU for process */ + +/* Error code if probe fails */ +#define TLB_LKUP_ERR 0x80000000 + +/* TLB Commands */ +#define TLBWrite 0x1 +#define TLBRead 0x2 +#define TLBGetIndex 0x3 +#define TLBProbe 0x4 + +#if (CONFIG_ARC_MMU_VER >= 2) +#define TLBWriteNI 0x5 /* write JTLB without inv uTLBs */ +#define TLBIVUTLB 0x6 /* explicitly inv uTLBs */ +#endif + #ifndef __ASSEMBLY__ typedef struct { @@ -18,6 +52,16 @@ typedef struct { #endif } mm_context_t; +#ifdef CONFIG_ARC_DBG_TLB_PARANOIA +void tlb_paranoid_check(unsigned int pid_sw, unsigned long address); +#else +#define tlb_paranoid_check(a, b) #endif +void arc_mmu_init(void); +extern char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len); +void __init read_decode_mmu_bcr(void); + +#endif /* !__ASSEMBLY__ */ + #endif diff --git a/arch/arc/include/asm/page.h b/arch/arc/include/asm/page.h index ab84bf131fe1..9c8aa41e45c2 100644 --- a/arch/arc/include/asm/page.h +++ b/arch/arc/include/asm/page.h @@ -96,13 +96,8 @@ typedef unsigned long pgtable_t; #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) -/* Default Permissions for page, used in mmap.c */ -#ifdef CONFIG_ARC_STACK_NONEXEC +/* Default Permissions for stack/heaps pages (Non Executable) */ #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE) -#else -#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ - VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) -#endif #define WANT_PAGE_VIRTUAL 1 diff --git a/arch/arc/include/asm/pgtable.h b/arch/arc/include/asm/pgtable.h index c110ac87d22b..4749a0eee1cf 100644 --- a/arch/arc/include/asm/pgtable.h +++ b/arch/arc/include/asm/pgtable.h @@ -135,6 +135,12 @@ /* ioremap */ #define PAGE_KERNEL_NO_CACHE __pgprot(_K_PAGE_PERMS) +/* Masks for actual TLB "PD"s */ +#define PTE_BITS_IN_PD0 (_PAGE_GLOBAL | _PAGE_PRESENT) +#define PTE_BITS_IN_PD1 (PAGE_MASK | _PAGE_CACHEABLE | \ + _PAGE_U_EXECUTE | _PAGE_U_WRITE | _PAGE_U_READ | \ + _PAGE_K_EXECUTE | _PAGE_K_WRITE | _PAGE_K_READ) + /************************************************************************** * Mapping of vm_flags (Generic VM) to PTE flags (arch specific) * diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h index 5f26b2c1cba0..15334ab66b56 100644 --- a/arch/arc/include/asm/processor.h +++ b/arch/arc/include/asm/processor.h @@ -19,6 +19,7 @@ #ifndef __ASSEMBLY__ #include <asm/arcregs.h> /* for STATUS_E1_MASK et all */ +#include <asm/ptrace.h> /* Arch specific stuff which needs to be saved per task. * However these items are not so important so as to earn a place in @@ -28,10 +29,6 @@ struct thread_struct { unsigned long ksp; /* kernel mode stack pointer */ unsigned long callee_reg; /* pointer to callee regs */ unsigned long fault_address; /* dbls as brkpt holder as well */ - unsigned long cause_code; /* Exception Cause Code (ECR) */ -#ifdef CONFIG_ARC_CURR_IN_REG - unsigned long user_r25; -#endif #ifdef CONFIG_ARC_FPU_SAVE_RESTORE struct arc_fpu fpu; #endif @@ -50,7 +47,7 @@ struct task_struct; unsigned long thread_saved_pc(struct task_struct *t); #define task_pt_regs(p) \ - ((struct pt_regs *)(THREAD_SIZE - 4 + (void *)task_stack_page(p)) - 1) + ((struct pt_regs *)(THREAD_SIZE + (void *)task_stack_page(p)) - 1) /* Free all resources held by a thread. */ #define release_thread(thread) do { } while (0) @@ -75,11 +72,15 @@ unsigned long thread_saved_pc(struct task_struct *t); /* * Where abouts of Task's sp, fp, blink when it was last seen in kernel mode. - * These can't be derived from pt_regs as that would give correp user-mode val + * Look in process.c for details of kernel stack layout */ #define KSTK_ESP(tsk) (tsk->thread.ksp) -#define KSTK_BLINK(tsk) (*((unsigned int *)((KSTK_ESP(tsk)) + (13+1+1)*4))) -#define KSTK_FP(tsk) (*((unsigned int *)((KSTK_ESP(tsk)) + (13+1)*4))) + +#define KSTK_REG(tsk, off) (*((unsigned int *)(KSTK_ESP(tsk) + \ + sizeof(struct callee_regs) + off))) + +#define KSTK_BLINK(tsk) KSTK_REG(tsk, 4) +#define KSTK_FP(tsk) KSTK_REG(tsk, 0) /* * Do necessary setup to start up a newly executed thread. diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 6179de7e07c2..c9938e7a7dbd 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -17,12 +17,6 @@ /* THE pt_regs: Defines how regs are saved during entry into kernel */ struct pt_regs { - /* - * 1 word gutter after reg-file has been saved - * Technically not needed, Since SP always points to a "full" location - * (vs. "empty"). But pt_regs is shared with tools.... - */ - long res; /* Real registers */ long bta; /* bta_l1, bta_l2, erbta */ @@ -50,22 +44,32 @@ struct pt_regs { long sp; /* user/kernel sp depending on where we came from */ long orig_r0; - /*to distinguish bet excp, syscall, irq */ + /* + * To distinguish bet excp, syscall, irq + * For traps and exceptions, Exception Cause Register. + * ECR: <00> <VV> <CC> <PP> + * Last word used by Linux for extra state mgmt (syscall-restart) + * For interrupts, use artificial ECR values to note current prio-level + */ union { + struct { #ifdef CONFIG_CPU_BIG_ENDIAN - /* so that assembly code is same for LE/BE */ - unsigned long orig_r8:16, event:16; + unsigned long state:8, ecr_vec:8, + ecr_cause:8, ecr_param:8; #else - unsigned long event:16, orig_r8:16; + unsigned long ecr_param:8, ecr_cause:8, + ecr_vec:8, state:8; #endif - long orig_r8_word; + }; + unsigned long event; }; + + long user_r25; }; /* Callee saved registers - need to be saved only when you are scheduled out */ struct callee_regs { - long res; /* Again this is not needed */ long r25; long r24; long r23; @@ -99,18 +103,20 @@ struct callee_regs { /* return 1 if PC in delay slot */ #define delay_mode(regs) ((regs->status32 & STATUS_DE_MASK) == STATUS_DE_MASK) -#define in_syscall(regs) (regs->event & orig_r8_IS_SCALL) -#define in_brkpt_trap(regs) (regs->event & orig_r8_IS_BRKPT) +#define in_syscall(regs) ((regs->ecr_vec == ECR_V_TRAP) && !regs->ecr_param) +#define in_brkpt_trap(regs) ((regs->ecr_vec == ECR_V_TRAP) && regs->ecr_param) + +#define STATE_SCALL_RESTARTED 0x01 -#define syscall_wont_restart(regs) (regs->event |= orig_r8_IS_SCALL_RESTARTED) -#define syscall_restartable(regs) !(regs->event & orig_r8_IS_SCALL_RESTARTED) +#define syscall_wont_restart(reg) (reg->state |= STATE_SCALL_RESTARTED) +#define syscall_restartable(reg) !(reg->state & STATE_SCALL_RESTARTED) #define current_pt_regs() \ ({ \ /* open-coded current_thread_info() */ \ register unsigned long sp asm ("sp"); \ unsigned long pg_start = (sp & ~(THREAD_SIZE - 1)); \ - (struct pt_regs *)(pg_start + THREAD_SIZE - 4) - 1; \ + (struct pt_regs *)(pg_start + THREAD_SIZE) - 1; \ }) static inline long regs_return_value(struct pt_regs *regs) @@ -120,11 +126,4 @@ static inline long regs_return_value(struct pt_regs *regs) #endif /* !__ASSEMBLY__ */ -#define orig_r8_IS_SCALL 0x0001 -#define orig_r8_IS_SCALL_RESTARTED 0x0002 -#define orig_r8_IS_BRKPT 0x0004 -#define orig_r8_IS_EXCPN 0x0008 -#define orig_r8_IS_IRQ1 0x0010 -#define orig_r8_IS_IRQ2 0x0020 - #endif /* __ASM_PTRACE_H */ diff --git a/arch/arc/include/asm/syscall.h b/arch/arc/include/asm/syscall.h index 33ab3048e9b2..29de09804306 100644 --- a/arch/arc/include/asm/syscall.h +++ b/arch/arc/include/asm/syscall.h @@ -18,7 +18,7 @@ static inline long syscall_get_nr(struct task_struct *task, struct pt_regs *regs) { if (user_mode(regs) && in_syscall(regs)) - return regs->orig_r8; + return regs->r8; else return -1; } @@ -26,8 +26,7 @@ syscall_get_nr(struct task_struct *task, struct pt_regs *regs) static inline void syscall_rollback(struct task_struct *task, struct pt_regs *regs) { - /* XXX: I can't fathom how pt_regs->r8 will be clobbered ? */ - regs->r8 = regs->orig_r8; + regs->r0 = regs->orig_r0; } static inline long diff --git a/arch/arc/include/asm/tlb-mmu1.h b/arch/arc/include/asm/tlb-mmu1.h index a5ff961b1efc..8a1ec96012ae 100644 --- a/arch/arc/include/asm/tlb-mmu1.h +++ b/arch/arc/include/asm/tlb-mmu1.h @@ -9,9 +9,9 @@ #ifndef __ASM_TLB_MMU_V1_H__ #define __ASM_TLB_MMU_V1_H__ -#if defined(__ASSEMBLY__) && defined(CONFIG_ARC_MMU_VER == 1) +#include <asm/mmu.h> -#include <asm/tlb.h> +#if defined(__ASSEMBLY__) && (CONFIG_ARC_MMU_VER == 1) .macro TLB_WRITE_HEURISTICS diff --git a/arch/arc/include/asm/tlb.h b/arch/arc/include/asm/tlb.h index cb0c708ca665..a9db5f62aaf3 100644 --- a/arch/arc/include/asm/tlb.h +++ b/arch/arc/include/asm/tlb.h @@ -9,18 +9,6 @@ #ifndef _ASM_ARC_TLB_H #define _ASM_ARC_TLB_H -#ifdef __KERNEL__ - -#include <asm/pgtable.h> - -/* Masks for actual TLB "PD"s */ -#define PTE_BITS_IN_PD0 (_PAGE_GLOBAL | _PAGE_PRESENT) -#define PTE_BITS_IN_PD1 (PAGE_MASK | _PAGE_CACHEABLE | \ - _PAGE_U_EXECUTE | _PAGE_U_WRITE | _PAGE_U_READ | \ - _PAGE_K_EXECUTE | _PAGE_K_WRITE | _PAGE_K_READ) - -#ifndef __ASSEMBLY__ - #define tlb_flush(tlb) \ do { \ if (tlb->fullmm) \ @@ -56,18 +44,4 @@ do { \ #include <linux/pagemap.h> #include <asm-generic/tlb.h> -#ifdef CONFIG_ARC_DBG_TLB_PARANOIA -void tlb_paranoid_check(unsigned int pid_sw, unsigned long address); -#else -#define tlb_paranoid_check(a, b) -#endif - -void arc_mmu_init(void); -extern char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len); -void __init read_decode_mmu_bcr(void); - -#endif /* __ASSEMBLY__ */ - -#endif /* __KERNEL__ */ - #endif /* _ASM_ARC_TLB_H */ diff --git a/arch/arc/include/asm/unaligned.h b/arch/arc/include/asm/unaligned.h index 5dbe63f17b66..60702f3751d2 100644 --- a/arch/arc/include/asm/unaligned.h +++ b/arch/arc/include/asm/unaligned.h @@ -16,11 +16,11 @@ #ifdef CONFIG_ARC_MISALIGN_ACCESS int misaligned_fixup(unsigned long address, struct pt_regs *regs, - unsigned long cause, struct callee_regs *cregs); + struct callee_regs *cregs); #else static inline int misaligned_fixup(unsigned long address, struct pt_regs *regs, - unsigned long cause, struct callee_regs *cregs) + struct callee_regs *cregs) { return 0; } diff --git a/arch/arc/include/uapi/asm/ptrace.h b/arch/arc/include/uapi/asm/ptrace.h index 30333cec0fef..2618cc13ba75 100644 --- a/arch/arc/include/uapi/asm/ptrace.h +++ b/arch/arc/include/uapi/asm/ptrace.h @@ -20,28 +20,31 @@ * * This is to decouple pt_regs from user-space ABI, to be able to change it * w/o affecting the ABI. - * Although the layout (initial padding) is similar to pt_regs to have some - * optimizations when copying pt_regs to/from user_regs_struct. + * + * The intermediate pad,pad2 are relics of initial layout based on pt_regs + * for optimizations when copying pt_regs to/from user_regs_struct. + * We no longer need them, but can't be changed as they are part of ABI now. * * Also, sigcontext only care about the scratch regs as that is what we really - * save/restore for signal handling. + * save/restore for signal handling. However gdb also uses the same struct + * hence callee regs need to be in there too. */ struct user_regs_struct { + long pad; struct { - long pad; long bta, lp_start, lp_end, lp_count; long status32, ret, blink, fp, gp; long r12, r11, r10, r9, r8, r7, r6, r5, r4, r3, r2, r1, r0; long sp; } scratch; + long pad2; struct { - long pad; long r25, r24, r23, r22, r21, r20; long r19, r18, r17, r16, r15, r14, r13; } callee; long efa; /* break pt addr, for break points in delay slots */ - long stop_pc; /* give dbg stop_pc directly after checking orig_r8 */ + long stop_pc; /* give dbg stop_pc after ensuring brkpt trap */ }; #endif /* !__ASSEMBLY__ */ diff --git a/arch/arc/kernel/asm-offsets.c b/arch/arc/kernel/asm-offsets.c index 7dcda7025241..6c3aa0edb9b5 100644 --- a/arch/arc/kernel/asm-offsets.c +++ b/arch/arc/kernel/asm-offsets.c @@ -24,9 +24,6 @@ int main(void) DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); DEFINE(THREAD_CALLEE_REG, offsetof(struct thread_struct, callee_reg)); -#ifdef CONFIG_ARC_CURR_IN_REG - DEFINE(THREAD_USER_R25, offsetof(struct thread_struct, user_r25)); -#endif DEFINE(THREAD_FAULT_ADDR, offsetof(struct thread_struct, fault_address)); @@ -49,7 +46,7 @@ int main(void) BLANK(); DEFINE(PT_status32, offsetof(struct pt_regs, status32)); - DEFINE(PT_orig_r8, offsetof(struct pt_regs, orig_r8_word)); + DEFINE(PT_event, offsetof(struct pt_regs, event)); DEFINE(PT_sp, offsetof(struct pt_regs, sp)); DEFINE(PT_r0, offsetof(struct pt_regs, r0)); DEFINE(PT_r1, offsetof(struct pt_regs, r1)); @@ -60,5 +57,7 @@ int main(void) DEFINE(PT_r6, offsetof(struct pt_regs, r6)); DEFINE(PT_r7, offsetof(struct pt_regs, r7)); + DEFINE(SZ_CALLEE_REGS, sizeof(struct callee_regs)); + DEFINE(SZ_PT_REGS, sizeof(struct pt_regs)); return 0; } diff --git a/arch/arc/kernel/ctx_sw.c b/arch/arc/kernel/ctx_sw.c index 60844dac6132..34410eb1a308 100644 --- a/arch/arc/kernel/ctx_sw.c +++ b/arch/arc/kernel/ctx_sw.c @@ -23,10 +23,6 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task) unsigned int tmp; unsigned int prev = (unsigned int)prev_task; unsigned int next = (unsigned int)next_task; - int num_words_to_skip = 1; -#ifdef CONFIG_ARC_CURR_IN_REG - num_words_to_skip++; -#endif __asm__ __volatile__( /* FP/BLINK save generated by gcc (standard function prologue */ @@ -44,8 +40,9 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task) "st.a r24, [sp, -4] \n\t" #ifndef CONFIG_ARC_CURR_IN_REG "st.a r25, [sp, -4] \n\t" +#else + "sub sp, sp, 4 \n\t" /* usual r25 placeholder */ #endif - "sub sp, sp, %4 \n\t" /* create gutter at top */ /* set ksp of outgoing task in tsk->thread.ksp */ "st.as sp, [%3, %1] \n\t" @@ -76,10 +73,10 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task) /* start loading it's CALLEE reg file */ - "add sp, sp, %4 \n\t" /* skip gutter at top */ - #ifndef CONFIG_ARC_CURR_IN_REG "ld.ab r25, [sp, 4] \n\t" +#else + "add sp, sp, 4 \n\t" #endif "ld.ab r24, [sp, 4] \n\t" "ld.ab r23, [sp, 4] \n\t" @@ -100,8 +97,7 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task) /* FP/BLINK restore generated by gcc (standard func epilogue */ : "=r"(tmp) - : "n"((TASK_THREAD + THREAD_KSP) / 4), "r"(next), "r"(prev), - "n"(num_words_to_skip * 4) + : "n"((TASK_THREAD + THREAD_KSP) / 4), "r"(next), "r"(prev) : "blink" ); diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 0c6d664d4a83..1d7165156e17 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -142,7 +142,7 @@ VECTOR reserved ; Reserved Exceptions .endr #include <linux/linkage.h> /* ARC_{EXTRY,EXIT} */ -#include <asm/entry.h> /* SAVE_ALL_{INT1,INT2,TRAP...} */ +#include <asm/entry.h> /* SAVE_ALL_{INT1,INT2,SYS...} */ #include <asm/errno.h> #include <asm/arcregs.h> #include <asm/irqflags.h> @@ -274,10 +274,8 @@ ARC_ENTRY instr_service SWITCH_TO_KERNEL_STK SAVE_ALL_SYS - lr r0, [ecr] - lr r1, [efa] - - mov r2, sp + lr r0, [efa] + mov r1, sp FAKE_RET_FROM_EXCPN r9 @@ -298,9 +296,8 @@ ARC_ENTRY mem_service SWITCH_TO_KERNEL_STK SAVE_ALL_SYS - lr r0, [ecr] - lr r1, [efa] - mov r2, sp + lr r0, [efa] + mov r1, sp bl do_memory_error b ret_from_exception ARC_EXIT mem_service @@ -317,11 +314,14 @@ ARC_ENTRY EV_MachineCheck SWITCH_TO_KERNEL_STK SAVE_ALL_SYS - lr r0, [ecr] - lr r1, [efa] - mov r2, sp + lr r2, [ecr] + lr r0, [efa] + mov r1, sp + + lsr r3, r2, 8 + bmsk r3, r3, 7 + brne r3, ECR_C_MCHK_DUP_TLB, 1f - brne r0, 0x200100, 1f bl do_tlb_overlap_fault b ret_from_exception @@ -355,8 +355,8 @@ ARC_ENTRY EV_TLBProtV ; ecr and efa were not saved in case an Intr sneaks in ; after fake rtie ; - lr r3, [ecr] - lr r4, [efa] + lr r2, [ecr] + lr r1, [efa] ; Faulting Data address ; --------(4) Return from CPU Exception Mode --------- ; Fake a rtie, but rtie to next label @@ -368,31 +368,25 @@ ARC_ENTRY EV_TLBProtV ;------ (5) Type of Protection Violation? ---------- ; ; ProtV Hardware Exception is triggered for Access Faults of 2 types - ; -Access Violaton (WRITE to READ ONLY Page) - for linux COW - ; -Unaligned Access (READ/WRITE on odd boundary) + ; -Access Violaton : 00_23_(00|01|02|03)_00 + ; x r w r+w + ; -Unaligned Access : 00_23_04_00 ; - cmp r3, 0x230400 ; Misaligned data access ? - beq 4f + bbit1 r2, ECR_C_BIT_PROTV_MISALIG_DATA, 4f ;========= (6a) Access Violation Processing ======== - cmp r3, 0x230100 - mov r1, 0x0 ; if LD exception ? write = 0 - mov.ne r1, 0x1 ; else write = 1 - - mov r2, r4 ; faulting address mov r0, sp ; pt_regs bl do_page_fault b ret_from_exception ;========== (6b) Non aligned access ============ 4: - mov r0, r3 ; cause code - mov r1, r4 ; faulting address - mov r2, sp ; pt_regs + mov r0, r1 + mov r1, sp ; pt_regs #ifdef CONFIG_ARC_MISALIGN_ACCESS SAVE_CALLEE_SAVED_USER - mov r3, sp ; callee_regs + mov r2, sp ; callee_regs bl do_misaligned_access @@ -419,9 +413,8 @@ ARC_ENTRY EV_PrivilegeV SWITCH_TO_KERNEL_STK SAVE_ALL_SYS - lr r0, [ecr] - lr r1, [efa] - mov r2, sp + lr r0, [efa] + mov r1, sp FAKE_RET_FROM_EXCPN r9 @@ -440,9 +433,8 @@ ARC_ENTRY EV_Extension SWITCH_TO_KERNEL_STK SAVE_ALL_SYS - lr r0, [ecr] - lr r1, [efa] - mov r2, sp + lr r0, [efa] + mov r1, sp bl do_extension_fault b ret_from_exception ARC_EXIT EV_Extension @@ -498,11 +490,8 @@ tracesys_exit: trap_with_param: ; stop_pc info by gdb needs this info - stw orig_r8_IS_BRKPT, [sp, PT_orig_r8] - - mov r0, r12 - lr r1, [efa] - mov r2, sp + lr r0, [efa] + mov r1, sp ; Now that we have read EFA, its safe to do "fake" rtie ; and get out of CPU exception mode @@ -544,11 +533,11 @@ ARC_ENTRY EV_Trap lr r9, [erstatus] SWITCH_TO_KERNEL_STK - SAVE_ALL_TRAP + SAVE_ALL_SYS ;------- (4) What caused the Trap -------------- lr r12, [ecr] - and.f 0, r12, ECR_PARAM_MASK + bmsk.f 0, r12, 7 bnz trap_with_param ; ======= (5a) Trap is due to System Call ======== @@ -589,11 +578,7 @@ ARC_ENTRY ret_from_exception ; Pre-{IRQ,Trap,Exception} K/U mode from pt_regs->status32 ld r8, [sp, PT_status32] ; returning to User/Kernel Mode -#ifdef CONFIG_PREEMPT bbit0 r8, STATUS_U_BIT, resume_kernel_mode -#else - bbit0 r8, STATUS_U_BIT, restore_regs -#endif ; Before returning to User mode check-for-and-complete any pending work ; such as rescheduling/signal-delivery etc. @@ -653,10 +638,10 @@ resume_user_mode_begin: b resume_user_mode_begin ; unconditionally back to U mode ret chks ; for single exit point from this block -#ifdef CONFIG_PREEMPT - resume_kernel_mode: +#ifdef CONFIG_PREEMPT + ; Can't preempt if preemption disabled GET_CURR_THR_INFO_FROM_SP r10 ld r8, [r10, THREAD_INFO_PREEMPT_COUNT] @@ -687,17 +672,6 @@ restore_regs : ; XXX can this be optimised out IRQ_DISABLE_SAVE r9, r10 ;@r10 has prisitine (pre-disable) copy -#ifdef CONFIG_ARC_CURR_IN_REG - ; Restore User R25 - ; Earlier this used to be only for returning to user mode - ; However with 2 levels of IRQ this can also happen even if - ; in kernel mode - ld r9, [sp, PT_sp] - brhs r9, VMALLOC_START, 8f - RESTORE_USER_R25 -8: -#endif - ; Restore REG File. In case multiple Events outstanding, ; use the same priorty as rtie: EXCPN, L2 IRQ, L1 IRQ, None ; Note that we use realtime STATUS32 (not pt_regs->status32) to @@ -714,28 +688,33 @@ not_exception: #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS + ; Level 2 interrupt return Path - from hardware standpoint bbit0 r10, STATUS_A2_BIT, not_level2_interrupt ;------------------------------------------------------------------ + ; However the context returning might not have taken L2 intr itself + ; e.g. Task'A' user-code -> L2 intr -> schedule -> 'B' user-code ret + ; Special considerations needed for the context which took L2 intr + + ld r9, [sp, PT_event] ; Ensure this is L2 intr context + brne r9, event_IRQ2, 149f + + ;------------------------------------------------------------------ ; if L2 IRQ interrupted a L1 ISR, we'd disbaled preemption earlier ; so that sched doesnt move to new task, causing L1 to be delayed ; undeterministically. Now that we've achieved that, lets reset ; things to what they were, before returning from L2 context ;---------------------------------------------------------------- - ldw r9, [sp, PT_orig_r8] ; get orig_r8 to make sure it is - brne r9, orig_r8_IS_IRQ2, 149f ; infact a L2 ISR ret path - ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs) bbit0 r9, STATUS_A1_BIT, 149f ; L1 not active when L2 IRQ, so normal - ; A1 is set in status32_l2 ; decrement thread_info->preempt_count (re-enable preemption) GET_CURR_THR_INFO_FROM_SP r10 ld r9, [r10, THREAD_INFO_PREEMPT_COUNT] ; paranoid check, given A1 was active when A2 happened, preempt count - ; must not be 0 beccause we would have incremented it. + ; must not be 0 because we would have incremented it. ; If this does happen we simply HALT as it means a BUG !!! cmp r9, 0 bnz 2f diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S index 006dec3fc353..2a913f85a747 100644 --- a/arch/arc/kernel/head.S +++ b/arch/arc/kernel/head.S @@ -27,6 +27,8 @@ stext: ; Don't clobber r0-r4 yet. It might have bootloader provided info ;------------------------------------------------------------------- + sr @_int_vec_base_lds, [AUX_INTR_VEC_BASE] + #ifdef CONFIG_SMP ; Only Boot (Master) proceeds. Others wait in platform dependent way ; IDENTITY Reg [ 3 2 1 0 ] diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index 8115fa531575..305b3f866aa7 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -28,25 +28,17 @@ * -Disable all IRQs (on CPU side) * -Optionally, setup the High priority Interrupts as Level 2 IRQs */ -void __cpuinit arc_init_IRQ(void) +void arc_init_IRQ(void) { int level_mask = 0; - write_aux_reg(AUX_INTR_VEC_BASE, _int_vec_base_lds); - /* Disable all IRQs: enable them as devices request */ write_aux_reg(AUX_IENABLE, 0); /* setup any high priority Interrupts (Level2 in ARCompact jargon) */ -#ifdef CONFIG_ARC_IRQ3_LV2 - level_mask |= (1 << 3); -#endif -#ifdef CONFIG_ARC_IRQ5_LV2 - level_mask |= (1 << 5); -#endif -#ifdef CONFIG_ARC_IRQ6_LV2 - level_mask |= (1 << 6); -#endif + level_mask |= IS_ENABLED(CONFIG_ARC_IRQ3_LV2) << 3; + level_mask |= IS_ENABLED(CONFIG_ARC_IRQ5_LV2) << 5; + level_mask |= IS_ENABLED(CONFIG_ARC_IRQ6_LV2) << 6; if (level_mask) { pr_info("Level-2 interrupts bitset %x\n", level_mask); diff --git a/arch/arc/kernel/kgdb.c b/arch/arc/kernel/kgdb.c index 52bdc83c1495..a7698fb14818 100644 --- a/arch/arc/kernel/kgdb.c +++ b/arch/arc/kernel/kgdb.c @@ -169,7 +169,7 @@ int kgdb_arch_init(void) return 0; } -void kgdb_trap(struct pt_regs *regs, int param) +void kgdb_trap(struct pt_regs *regs) { /* trap_s 3 is used for breakpoints that overwrite existing * instructions, while trap_s 4 is used for compiled breakpoints. @@ -181,7 +181,7 @@ void kgdb_trap(struct pt_regs *regs, int param) * with trap_s 4 (compiled) breakpoints, continuation needs to * start after the breakpoint. */ - if (param == 3) + if (regs->ecr_param == 3) instruction_pointer(regs) -= BREAK_INSTR_SIZE; kgdb_handle_exception(1, SIGTRAP, 0, regs); diff --git a/arch/arc/kernel/kprobes.c b/arch/arc/kernel/kprobes.c index 5a7b80e2d883..72f97822784a 100644 --- a/arch/arc/kernel/kprobes.c +++ b/arch/arc/kernel/kprobes.c @@ -517,8 +517,7 @@ int __kprobes arch_trampoline_kprobe(struct kprobe *p) return 0; } -void trap_is_kprobe(unsigned long cause, unsigned long address, - struct pt_regs *regs) +void trap_is_kprobe(unsigned long address, struct pt_regs *regs) { - notify_die(DIE_TRAP, "kprobe_trap", regs, address, cause, SIGTRAP); + notify_die(DIE_TRAP, "kprobe_trap", regs, address, 0, SIGTRAP); } diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c index cad66851e0c4..07a3a968fe49 100644 --- a/arch/arc/kernel/process.c +++ b/arch/arc/kernel/process.c @@ -55,10 +55,8 @@ asmlinkage void ret_from_fork(void); * | ... | * | unused | * | | - * ------------------ <==== top of Stack (thread.ksp) - * | UNUSED 1 word| * ------------------ - * | r25 | + * | r25 | <==== top of Stack (thread.ksp) * ~ ~ * | --to-- | (CALLEE Regs of user mode) * | r13 | @@ -76,7 +74,10 @@ asmlinkage void ret_from_fork(void); * | --to-- | (scratch Regs of user mode) * | r0 | * ------------------ - * | UNUSED 1 word| + * | SP | + * | orig_r0 | + * | event/ECR | + * | user_r25 | * ------------------ <===== END of PAGE */ int copy_thread(unsigned long clone_flags, diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index c6a81c58d0f3..333238564b67 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c @@ -40,7 +40,15 @@ static int genregs_get(struct task_struct *target, offsetof(struct user_regs_struct, LOC), \ offsetof(struct user_regs_struct, LOC) + 4); +#define REG_O_ZERO(LOC) \ + if (!ret) \ + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, \ + offsetof(struct user_regs_struct, LOC), \ + offsetof(struct user_regs_struct, LOC) + 4); + + REG_O_ZERO(pad); REG_O_CHUNK(scratch, callee, ptregs); + REG_O_ZERO(pad2); REG_O_CHUNK(callee, efa, cregs); REG_O_CHUNK(efa, stop_pc, &target->thread.fault_address); @@ -88,8 +96,10 @@ static int genregs_set(struct task_struct *target, offsetof(struct user_regs_struct, LOC), \ offsetof(struct user_regs_struct, LOC) + 4); - /* TBD: disallow updates to STATUS32, orig_r8 etc*/ - REG_IN_CHUNK(scratch, callee, ptregs); /* pt_regs[bta..orig_r8] */ + REG_IGNORE_ONE(pad); + /* TBD: disallow updates to STATUS32 etc*/ + REG_IN_CHUNK(scratch, pad2, ptregs); /* pt_regs[bta..sp] */ + REG_IGNORE_ONE(pad2); REG_IN_CHUNK(callee, efa, cregs); /* callee_regs[r25..r13] */ REG_IGNORE_ONE(efa); /* efa update invalid */ REG_IN_ONE(stop_pc, &ptregs->ret); /* stop_pc: PC update */ diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index b2b3731dd1e9..6b083454d039 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -31,14 +31,14 @@ int running_on_hw = 1; /* vs. on ISS */ char __initdata command_line[COMMAND_LINE_SIZE]; -struct machine_desc *machine_desc __cpuinitdata; +struct machine_desc *machine_desc; struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ struct cpuinfo_arc cpuinfo_arc700[NR_CPUS]; -void __cpuinit read_arc_build_cfg_regs(void) +void read_arc_build_cfg_regs(void) { struct bcr_perip uncached_space; struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; @@ -182,7 +182,7 @@ char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len) FIX_PTR(cpu); #define IS_AVAIL1(var, str) ((var) ? str : "") #define IS_AVAIL2(var, str) ((var == 0x2) ? str : "") -#define IS_USED(var) ((var) ? "(in-use)" : "(not used)") +#define IS_USED(cfg) (IS_ENABLED(cfg) ? "(in-use)" : "(not used)") n += scnprintf(buf + n, len - n, "Extn [700-Base]\t: %s %s %s %s %s %s\n", @@ -202,9 +202,9 @@ char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len) if (cpu->core.family == 0x34) { n += scnprintf(buf + n, len - n, "Extn [700-4.10]\t: LLOCK/SCOND %s, SWAPE %s, RTSC %s\n", - IS_USED(__CONFIG_ARC_HAS_LLSC_VAL), - IS_USED(__CONFIG_ARC_HAS_SWAPE_VAL), - IS_USED(__CONFIG_ARC_HAS_RTSC_VAL)); + IS_USED(CONFIG_ARC_HAS_LLSC), + IS_USED(CONFIG_ARC_HAS_SWAPE), + IS_USED(CONFIG_ARC_HAS_RTSC)); } n += scnprintf(buf + n, len - n, "Extn [CCM]\t: %s", @@ -237,7 +237,7 @@ char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len) return buf; } -void __cpuinit arc_chk_ccms(void) +void arc_chk_ccms(void) { #if defined(CONFIG_ARC_HAS_DCCM) || defined(CONFIG_ARC_HAS_ICCM) struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; @@ -272,7 +272,7 @@ void __cpuinit arc_chk_ccms(void) * hardware has dedicated regs which need to be saved/restored on ctx-sw * (Single Precision uses core regs), thus kernel is kind of oblivious to it */ -void __cpuinit arc_chk_fpu(void) +void arc_chk_fpu(void) { struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; @@ -293,7 +293,7 @@ void __cpuinit arc_chk_fpu(void) * such as only for boot CPU etc */ -void __cpuinit setup_processor(void) +void setup_processor(void) { char str[512]; int cpu_id = smp_processor_id(); diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index 5c7fd603d216..bca3052c956d 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -117,7 +117,7 @@ const char *arc_platform_smp_cpuinfo(void) * Called from asm stub in head.S * "current"/R25 already setup by low level boot code */ -void __cpuinit start_kernel_secondary(void) +void start_kernel_secondary(void) { struct mm_struct *mm = &init_mm; unsigned int cpu = smp_processor_id(); @@ -154,7 +154,7 @@ void __cpuinit start_kernel_secondary(void) * * Essential requirements being where to run from (PC) and stack (SP) */ -int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) +int __cpu_up(unsigned int cpu, struct task_struct *idle) { unsigned long wait_till; diff --git a/arch/arc/kernel/stacktrace.c b/arch/arc/kernel/stacktrace.c index ca0207b9d5b6..f8b7d880304d 100644 --- a/arch/arc/kernel/stacktrace.c +++ b/arch/arc/kernel/stacktrace.c @@ -79,7 +79,7 @@ static void seed_unwind_frame_info(struct task_struct *tsk, * assembly code */ frame_info->regs.r27 = 0; - frame_info->regs.r28 += 64; + frame_info->regs.r28 += 60; frame_info->call_frame = 0; } else { diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c index 09f4309aa2c0..0e51e69cf30d 100644 --- a/arch/arc/kernel/time.c +++ b/arch/arc/kernel/time.c @@ -44,13 +44,24 @@ #include <asm/clk.h> #include <asm/mach_desc.h> +/* Timer related Aux registers */ +#define ARC_REG_TIMER0_LIMIT 0x23 /* timer 0 limit */ +#define ARC_REG_TIMER0_CTRL 0x22 /* timer 0 control */ +#define ARC_REG_TIMER0_CNT 0x21 /* timer 0 count */ +#define ARC_REG_TIMER1_LIMIT 0x102 /* timer 1 limit */ +#define ARC_REG_TIMER1_CTRL 0x101 /* timer 1 control */ +#define ARC_REG_TIMER1_CNT 0x100 /* timer 1 count */ + +#define TIMER_CTRL_IE (1 << 0) /* Interupt when Count reachs limit */ +#define TIMER_CTRL_NH (1 << 1) /* Count only when CPU NOT halted */ + #define ARC_TIMER_MAX 0xFFFFFFFF /********** Clock Source Device *********/ #ifdef CONFIG_ARC_HAS_RTSC -int __cpuinit arc_counter_setup(void) +int arc_counter_setup(void) { /* RTSC insn taps into cpu clk, needs no setup */ @@ -105,7 +116,7 @@ static bool is_usable_as_clocksource(void) /* * set 32bit TIMER1 to keep counting monotonically and wraparound */ -int __cpuinit arc_counter_setup(void) +int arc_counter_setup(void) { write_aux_reg(ARC_REG_TIMER1_LIMIT, ARC_TIMER_MAX); write_aux_reg(ARC_REG_TIMER1_CNT, 0); @@ -212,7 +223,7 @@ static struct irqaction arc_timer_irq = { * Setup the local event timer for @cpu * N.B. weak so that some exotic ARC SoCs can completely override it */ -void __attribute__((weak)) __cpuinit arc_local_timer_setup(unsigned int cpu) +void __attribute__((weak)) arc_local_timer_setup(unsigned int cpu) { struct clock_event_device *clk = &per_cpu(arc_clockevent_device, cpu); diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c index 0471d9c9dd54..e21692d2fdab 100644 --- a/arch/arc/kernel/traps.c +++ b/arch/arc/kernel/traps.c @@ -28,10 +28,9 @@ void __init trap_init(void) return; } -void die(const char *str, struct pt_regs *regs, unsigned long address, - unsigned long cause_reg) +void die(const char *str, struct pt_regs *regs, unsigned long address) { - show_kernel_fault_diag(str, regs, address, cause_reg); + show_kernel_fault_diag(str, regs, address); /* DEAD END */ __asm__("flag 1"); @@ -42,14 +41,13 @@ void die(const char *str, struct pt_regs *regs, unsigned long address, * -for user faults enqueues requested signal * -for kernel, chk if due to copy_(to|from)_user, otherwise die() */ -static noinline int handle_exception(unsigned long cause, char *str, - struct pt_regs *regs, siginfo_t *info) +static noinline int +handle_exception(const char *str, struct pt_regs *regs, siginfo_t *info) { if (user_mode(regs)) { struct task_struct *tsk = current; tsk->thread.fault_address = (__force unsigned int)info->si_addr; - tsk->thread.cause_code = cause; force_sig_info(info->si_signo, info, tsk); @@ -58,14 +56,14 @@ static noinline int handle_exception(unsigned long cause, char *str, if (fixup_exception(regs)) return 0; - die(str, regs, (unsigned long)info->si_addr, cause); + die(str, regs, (unsigned long)info->si_addr); } return 1; } #define DO_ERROR_INFO(signr, str, name, sicode) \ -int name(unsigned long cause, unsigned long address, struct pt_regs *regs) \ +int name(unsigned long address, struct pt_regs *regs) \ { \ siginfo_t info = { \ .si_signo = signr, \ @@ -73,7 +71,7 @@ int name(unsigned long cause, unsigned long address, struct pt_regs *regs) \ .si_code = sicode, \ .si_addr = (void __user *)address, \ }; \ - return handle_exception(cause, str, regs, &info);\ + return handle_exception(str, regs, &info);\ } /* @@ -90,11 +88,11 @@ DO_ERROR_INFO(SIGBUS, "Misaligned Access", do_misaligned_error, BUS_ADRALN) /* * Entry Point for Misaligned Data access Exception, for emulating in software */ -int do_misaligned_access(unsigned long cause, unsigned long address, - struct pt_regs *regs, struct callee_regs *cregs) +int do_misaligned_access(unsigned long address, struct pt_regs *regs, + struct callee_regs *cregs) { - if (misaligned_fixup(address, regs, cause, cregs) != 0) - return do_misaligned_error(cause, address, regs); + if (misaligned_fixup(address, regs, cregs) != 0) + return do_misaligned_error(address, regs); return 0; } @@ -104,10 +102,9 @@ int do_misaligned_access(unsigned long cause, unsigned long address, * Entry point for miscll errors such as Nested Exceptions * -Duplicate TLB entry is handled seperately though */ -void do_machine_check_fault(unsigned long cause, unsigned long address, - struct pt_regs *regs) +void do_machine_check_fault(unsigned long address, struct pt_regs *regs) { - die("Machine Check Exception", regs, address, cause); + die("Machine Check Exception", regs, address); } @@ -120,23 +117,22 @@ void do_machine_check_fault(unsigned long cause, unsigned long address, * -1 used for software breakpointing (gdb) * -2 used by kprobes */ -void do_non_swi_trap(unsigned long cause, unsigned long address, - struct pt_regs *regs) +void do_non_swi_trap(unsigned long address, struct pt_regs *regs) { - unsigned int param = cause & 0xff; + unsigned int param = regs->ecr_param; switch (param) { case 1: - trap_is_brkpt(cause, address, regs); + trap_is_brkpt(address, regs); break; case 2: - trap_is_kprobe(param, address, regs); + trap_is_kprobe(address, regs); break; case 3: case 4: - kgdb_trap(regs, param); + kgdb_trap(regs); break; default: @@ -149,14 +145,14 @@ void do_non_swi_trap(unsigned long cause, unsigned long address, * -For a corner case, ARC kprobes implementation resorts to using * this exception, hence the check */ -void do_insterror_or_kprobe(unsigned long cause, - unsigned long address, - struct pt_regs *regs) +void do_insterror_or_kprobe(unsigned long address, struct pt_regs *regs) { + int rc; + /* Check if this exception is caused by kprobes */ - if (notify_die(DIE_IERR, "kprobe_ierr", regs, address, - cause, SIGILL) == NOTIFY_STOP) + rc = notify_die(DIE_IERR, "kprobe_ierr", regs, address, 0, SIGILL); + if (rc == NOTIFY_STOP) return; - insterror_is_error(cause, address, regs); + insterror_is_error(address, regs); } diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c index a03528ecd276..73a7450ee622 100644 --- a/arch/arc/kernel/troubleshoot.c +++ b/arch/arc/kernel/troubleshoot.c @@ -117,23 +117,22 @@ static void show_faulting_vma(unsigned long address, char *buf) static void show_ecr_verbose(struct pt_regs *regs) { - unsigned int vec, cause_code, cause_reg; + unsigned int vec, cause_code; unsigned long address; - cause_reg = current->thread.cause_code; - pr_info("\n[ECR ]: 0x%08x => ", cause_reg); + pr_info("\n[ECR ]: 0x%08lx => ", regs->event); /* For Data fault, this is data address not instruction addr */ address = current->thread.fault_address; - vec = cause_reg >> 16; - cause_code = (cause_reg >> 8) & 0xFF; + vec = regs->ecr_vec; + cause_code = regs->ecr_cause; /* For DTLB Miss or ProtV, display the memory involved too */ if (vec == ECR_V_DTLB_MISS) { - pr_cont("Invalid %s 0x%08lx by insn @ 0x%08lx\n", - (cause_code == 0x01) ? "Read From" : - ((cause_code == 0x02) ? "Write to" : "EX"), + pr_cont("Invalid %s @ 0x%08lx by insn @ 0x%08lx\n", + (cause_code == 0x01) ? "Read" : + ((cause_code == 0x02) ? "Write" : "EX"), address, regs->ret); } else if (vec == ECR_V_ITLB_MISS) { pr_cont("Insn could not be fetched\n"); @@ -144,14 +143,12 @@ static void show_ecr_verbose(struct pt_regs *regs) } else if (vec == ECR_V_PROTV) { if (cause_code == ECR_C_PROTV_INST_FETCH) pr_cont("Execute from Non-exec Page\n"); - else if (cause_code == ECR_C_PROTV_LOAD) - pr_cont("Read from Non-readable Page\n"); - else if (cause_code == ECR_C_PROTV_STORE) - pr_cont("Write to Non-writable Page\n"); - else if (cause_code == ECR_C_PROTV_XCHG) - pr_cont("Data exchange protection violation\n"); else if (cause_code == ECR_C_PROTV_MISALIG_DATA) pr_cont("Misaligned r/w from 0x%08lx\n", address); + else + pr_cont("%s access not allowed on page\n", + (cause_code == 0x01) ? "Read" : + ((cause_code == 0x02) ? "Write" : "EX")); } else if (vec == ECR_V_INSN_ERR) { pr_cont("Illegal Insn\n"); } else { @@ -176,8 +173,7 @@ void show_regs(struct pt_regs *regs) print_task_path_n_nm(tsk, buf); show_regs_print_info(KERN_INFO); - if (current->thread.cause_code) - show_ecr_verbose(regs); + show_ecr_verbose(regs); pr_info("[EFA ]: 0x%08lx\n[BLINK ]: %pS\n[ERET ]: %pS\n", current->thread.fault_address, @@ -213,10 +209,9 @@ void show_regs(struct pt_regs *regs) } void show_kernel_fault_diag(const char *str, struct pt_regs *regs, - unsigned long address, unsigned long cause_reg) + unsigned long address) { current->thread.fault_address = address; - current->thread.cause_code = cause_reg; /* Caller and Callee regs */ show_regs(regs); diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c index 4cd81633febd..c0f832f595d3 100644 --- a/arch/arc/kernel/unaligned.c +++ b/arch/arc/kernel/unaligned.c @@ -187,7 +187,7 @@ fault: state->fault = 1; * Returns 0 if successfully handled, 1 if some error happened */ int misaligned_fixup(unsigned long address, struct pt_regs *regs, - unsigned long cause, struct callee_regs *cregs) + struct callee_regs *cregs) { struct disasm_state state; char buf[TASK_COMM_LEN]; diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c index a8d02223da44..e550b117ec4f 100644 --- a/arch/arc/kernel/unwind.c +++ b/arch/arc/kernel/unwind.c @@ -289,6 +289,8 @@ static void __init setup_unwind_table(struct unwind_table *table, * instead of the initial loc addr * return; */ + WARN(1, "unwinder: FDE->initial_location NULL %p\n", + (const u8 *)(fde + 1) + *fde); } ++n; } diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S index d3c92f52d444..2555f5886af6 100644 --- a/arch/arc/kernel/vmlinux.lds.S +++ b/arch/arc/kernel/vmlinux.lds.S @@ -125,6 +125,11 @@ SECTIONS *(.debug_frame) __end_unwind = .; } + /* + * gcc 4.8 generates this for -fasynchonous-unwind-tables, + * while we still use the .debug_frame based unwinder + */ + /DISCARD/ : { *(.eh_frame) } #else /DISCARD/ : { *(.debug_frame) } #endif @@ -142,15 +147,18 @@ SECTIONS *(.arcextmap.*) } +#ifndef CONFIG_DEBUG_INFO /* open-coded because we need .debug_frame seperately for unwinding */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - .debug_info 0 : { *(.debug_info) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } + /DISCARD/ : { *(.debug_aranges) } + /DISCARD/ : { *(.debug_pubnames) } + /DISCARD/ : { *(.debug_info) } + /DISCARD/ : { *(.debug_abbrev) } + /DISCARD/ : { *(.debug_line) } + /DISCARD/ : { *(.debug_str) } + /DISCARD/ : { *(.debug_loc) } + /DISCARD/ : { *(.debug_macinfo) } + /DISCARD/ : { *(.debug_ranges) } +#endif #ifdef CONFIG_ARC_HAS_DCCM . = CONFIG_ARC_DCCM_BASE; diff --git a/arch/arc/mm/cache_arc700.c b/arch/arc/mm/cache_arc700.c index aedce1905441..f415d851b765 100644 --- a/arch/arc/mm/cache_arc700.c +++ b/arch/arc/mm/cache_arc700.c @@ -73,6 +73,33 @@ #include <asm/cachectl.h> #include <asm/setup.h> +/* Instruction cache related Auxiliary registers */ +#define ARC_REG_IC_BCR 0x77 /* Build Config reg */ +#define ARC_REG_IC_IVIC 0x10 +#define ARC_REG_IC_CTRL 0x11 +#define ARC_REG_IC_IVIL 0x19 +#if (CONFIG_ARC_MMU_VER > 2) +#define ARC_REG_IC_PTAG 0x1E +#endif + +/* Bit val in IC_CTRL */ +#define IC_CTRL_CACHE_DISABLE 0x1 + +/* Data cache related Auxiliary registers */ +#define ARC_REG_DC_BCR 0x72 /* Build Config reg */ +#define ARC_REG_DC_IVDC 0x47 +#define ARC_REG_DC_CTRL 0x48 +#define ARC_REG_DC_IVDL 0x4A +#define ARC_REG_DC_FLSH 0x4B +#define ARC_REG_DC_FLDL 0x4C +#if (CONFIG_ARC_MMU_VER > 2) +#define ARC_REG_DC_PTAG 0x5C +#endif + +/* Bit val in DC_CTRL */ +#define DC_CTRL_INV_MODE_FLUSH 0x40 +#define DC_CTRL_FLUSH_STATUS 0x100 + char *arc_cache_mumbojumbo(int cpu_id, char *buf, int len) { int n = 0; @@ -89,8 +116,10 @@ char *arc_cache_mumbojumbo(int cpu_id, char *buf, int len) enb ? "" : "DISABLED (kernel-build)"); \ } - PR_CACHE(&cpuinfo_arc700[c].icache, __CONFIG_ARC_HAS_ICACHE, "I-Cache"); - PR_CACHE(&cpuinfo_arc700[c].dcache, __CONFIG_ARC_HAS_DCACHE, "D-Cache"); + PR_CACHE(&cpuinfo_arc700[c].icache, IS_ENABLED(CONFIG_ARC_HAS_ICACHE), + "I-Cache"); + PR_CACHE(&cpuinfo_arc700[c].dcache, IS_ENABLED(CONFIG_ARC_HAS_DCACHE), + "D-Cache"); return buf; } @@ -100,17 +129,23 @@ char *arc_cache_mumbojumbo(int cpu_id, char *buf, int len) * the cpuinfo structure for later use. * No Validation done here, simply read/convert the BCRs */ -void __cpuinit read_decode_cache_bcr(void) +void read_decode_cache_bcr(void) { - struct bcr_cache ibcr, dbcr; struct cpuinfo_arc_cache *p_ic, *p_dc; unsigned int cpu = smp_processor_id(); + struct bcr_cache { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int pad:12, line_len:4, sz:4, config:4, ver:8; +#else + unsigned int ver:8, config:4, sz:4, line_len:4, pad:12; +#endif + } ibcr, dbcr; p_ic = &cpuinfo_arc700[cpu].icache; READ_BCR(ARC_REG_IC_BCR, ibcr); - if (ibcr.config == 0x3) - p_ic->assoc = 2; + BUG_ON(ibcr.config != 3); + p_ic->assoc = 2; /* Fixed to 2w set assoc */ p_ic->line_len = 8 << ibcr.line_len; p_ic->sz = 0x200 << ibcr.sz; p_ic->ver = ibcr.ver; @@ -118,8 +153,8 @@ void __cpuinit read_decode_cache_bcr(void) p_dc = &cpuinfo_arc700[cpu].dcache; READ_BCR(ARC_REG_DC_BCR, dbcr); - if (dbcr.config == 0x2) - p_dc->assoc = 4; + BUG_ON(dbcr.config != 2); + p_dc->assoc = 4; /* Fixed to 4w set assoc */ p_dc->line_len = 16 << dbcr.line_len; p_dc->sz = 0x200 << dbcr.sz; p_dc->ver = dbcr.ver; @@ -132,14 +167,12 @@ void __cpuinit read_decode_cache_bcr(void) * 3. Enable the Caches, setup default flush mode for D-Cache * 3. Calculate the SHMLBA used by user space */ -void __cpuinit arc_cache_init(void) +void arc_cache_init(void) { - unsigned int temp; unsigned int cpu = smp_processor_id(); struct cpuinfo_arc_cache *ic = &cpuinfo_arc700[cpu].icache; struct cpuinfo_arc_cache *dc = &cpuinfo_arc700[cpu].dcache; - int way_pg_ratio = way_pg_ratio; - int dcache_does_alias; + unsigned int dcache_does_alias, temp; char str[256]; printk(arc_cache_mumbojumbo(0, str, sizeof(str))); @@ -149,20 +182,11 @@ void __cpuinit arc_cache_init(void) #ifdef CONFIG_ARC_HAS_ICACHE /* 1. Confirm some of I-cache params which Linux assumes */ - if ((ic->assoc != ARC_ICACHE_WAYS) || - (ic->line_len != ARC_ICACHE_LINE_LEN)) { + if (ic->line_len != ARC_ICACHE_LINE_LEN) panic("Cache H/W doesn't match kernel Config"); - } -#if (CONFIG_ARC_MMU_VER > 2) - if (ic->ver != 3) { - if (running_on_hw) - panic("Cache ver doesn't match MMU ver\n"); - - /* For ISS - suggest the toggles to use */ - pr_err("Use -prop=icache_version=3,-prop=dcache_version=3\n"); - } -#endif + if (ic->ver != CONFIG_ARC_MMU_VER) + panic("Cache ver doesn't match MMU ver\n"); #endif /* Enable/disable I-Cache */ @@ -181,14 +205,12 @@ chk_dc: return; #ifdef CONFIG_ARC_HAS_DCACHE - if ((dc->assoc != ARC_DCACHE_WAYS) || - (dc->line_len != ARC_DCACHE_LINE_LEN)) { + if (dc->line_len != ARC_DCACHE_LINE_LEN) panic("Cache H/W doesn't match kernel Config"); - } - - dcache_does_alias = (dc->sz / ARC_DCACHE_WAYS) > PAGE_SIZE; /* check for D-Cache aliasing */ + dcache_does_alias = (dc->sz / dc->assoc) > PAGE_SIZE; + if (dcache_does_alias && !cache_is_vipt_aliasing()) panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); else if (!dcache_does_alias && cache_is_vipt_aliasing()) @@ -239,11 +261,9 @@ static inline void wait_for_flush(void) */ static inline void __dc_entire_op(const int cacheop) { - unsigned long flags, tmp = tmp; + unsigned int tmp = tmp; int aux; - local_irq_save(flags); - if (cacheop == OP_FLUSH_N_INV) { /* Dcache provides 2 cmd: FLUSH or INV * INV inturn has sub-modes: DISCARD or FLUSH-BEFORE @@ -267,8 +287,6 @@ static inline void __dc_entire_op(const int cacheop) /* Switch back the DISCARD ONLY Invalidate mode */ if (cacheop == OP_FLUSH_N_INV) write_aux_reg(ARC_REG_DC_CTRL, tmp & ~DC_CTRL_INV_MODE_FLUSH); - - local_irq_restore(flags); } /* @@ -459,8 +477,15 @@ static void __ic_line_inv_vaddr(unsigned long paddr, unsigned long vaddr, local_irq_restore(flags); } +static inline void __ic_entire_inv(void) +{ + write_aux_reg(ARC_REG_IC_IVIC, 1); + read_aux_reg(ARC_REG_IC_CTRL); /* blocks */ +} + #else +#define __ic_entire_inv() #define __ic_line_inv_vaddr(pstart, vstart, sz) #endif /* CONFIG_ARC_HAS_ICACHE */ @@ -487,7 +512,7 @@ void flush_dcache_page(struct page *page) struct address_space *mapping; if (!cache_is_vipt_aliasing()) { - set_bit(PG_arch_1, &page->flags); + clear_bit(PG_dc_clean, &page->flags); return; } @@ -501,7 +526,7 @@ void flush_dcache_page(struct page *page) * Make a note that K-mapping is dirty */ if (!mapping_mapped(mapping)) { - set_bit(PG_arch_1, &page->flags); + clear_bit(PG_dc_clean, &page->flags); } else if (page_mapped(page)) { /* kernel reading from page with U-mapping */ @@ -629,26 +654,13 @@ void ___flush_dcache_page(unsigned long paddr, unsigned long vaddr) __dc_line_op(paddr, vaddr & PAGE_MASK, PAGE_SIZE, OP_FLUSH_N_INV); } -void flush_icache_all(void) -{ - unsigned long flags; - - local_irq_save(flags); - - write_aux_reg(ARC_REG_IC_IVIC, 1); - - /* lr will not complete till the icache inv operation is not over */ - read_aux_reg(ARC_REG_IC_CTRL); - local_irq_restore(flags); -} - noinline void flush_cache_all(void) { unsigned long flags; local_irq_save(flags); - flush_icache_all(); + __ic_entire_inv(); __dc_entire_op(OP_FLUSH_N_INV); local_irq_restore(flags); @@ -667,7 +679,12 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long u_vaddr, { unsigned int paddr = pfn << PAGE_SHIFT; - __sync_icache_dcache(paddr, u_vaddr, PAGE_SIZE); + u_vaddr &= PAGE_MASK; + + ___flush_dcache_page(paddr, u_vaddr); + + if (vma->vm_flags & VM_EXEC) + __inv_icache_page(paddr, u_vaddr); } void flush_cache_range(struct vm_area_struct *vma, unsigned long start, @@ -717,7 +734,7 @@ void copy_user_highpage(struct page *to, struct page *from, * non copied user pages (e.g. read faults which wire in pagecache page * directly). */ - set_bit(PG_arch_1, &to->flags); + clear_bit(PG_dc_clean, &to->flags); /* * if SRC was already usermapped and non-congruent to kernel mapping @@ -725,15 +742,16 @@ void copy_user_highpage(struct page *to, struct page *from, */ if (clean_src_k_mappings) { __flush_dcache_page(kfrom, kfrom); + set_bit(PG_dc_clean, &from->flags); } else { - set_bit(PG_arch_1, &from->flags); + clear_bit(PG_dc_clean, &from->flags); } } void clear_user_page(void *to, unsigned long u_vaddr, struct page *page) { clear_page(to); - set_bit(PG_arch_1, &page->flags); + clear_bit(PG_dc_clean, &page->flags); } diff --git a/arch/arc/mm/fault.c b/arch/arc/mm/fault.c index 689ffd86d5e9..318164cabdfc 100644 --- a/arch/arc/mm/fault.c +++ b/arch/arc/mm/fault.c @@ -15,6 +15,7 @@ #include <linux/uaccess.h> #include <linux/kdebug.h> #include <asm/pgalloc.h> +#include <asm/mmu.h> static int handle_vmalloc_fault(struct mm_struct *mm, unsigned long address) { @@ -51,14 +52,14 @@ bad_area: return 1; } -void do_page_fault(struct pt_regs *regs, int write, unsigned long address, - unsigned long cause_code) +void do_page_fault(struct pt_regs *regs, unsigned long address) { struct vm_area_struct *vma = NULL; struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; siginfo_t info; int fault, ret; + int write = regs->ecr_cause & ECR_C_PROTV_STORE; /* ST/EX */ unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | (write ? FAULT_FLAG_WRITE : 0); @@ -109,7 +110,8 @@ good_area: /* Handle protection violation, execute on heap or stack */ - if (cause_code == ((ECR_V_PROTV << 16) | ECR_C_PROTV_INST_FETCH)) + if ((regs->ecr_vec == ECR_V_PROTV) && + (regs->ecr_cause == ECR_C_PROTV_INST_FETCH)) goto bad_area; if (write) { @@ -176,7 +178,6 @@ bad_area_nosemaphore: /* User mode accesses just cause a SIGSEGV */ if (user_mode(regs)) { tsk->thread.fault_address = address; - tsk->thread.cause_code = cause_code; info.si_signo = SIGSEGV; info.si_errno = 0; /* info.si_code has been set above */ @@ -197,7 +198,7 @@ no_context: if (fixup_exception(regs)) return; - die("Oops", regs, address, cause_code); + die("Oops", regs, address); out_of_memory: if (is_global_init(tsk)) { @@ -218,7 +219,6 @@ do_sigbus: goto no_context; tsk->thread.fault_address = address; - tsk->thread.cause_code = cause_code; info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRERR; diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index fe1c5a073afe..7957dc4e4d4a 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c @@ -55,7 +55,7 @@ #include <asm/arcregs.h> #include <asm/setup.h> #include <asm/mmu_context.h> -#include <asm/tlb.h> +#include <asm/mmu.h> /* Need for ARC MMU v2 * @@ -97,6 +97,7 @@ * J-TLB entry got evicted/replaced. */ + /* A copy of the ASID from the PID reg is kept in asid_cache */ int asid_cache = FIRST_ASID; @@ -432,9 +433,14 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr_unaligned, { unsigned long vaddr = vaddr_unaligned & PAGE_MASK; unsigned long paddr = pte_val(*ptep) & PAGE_MASK; + struct page *page = pfn_to_page(pte_pfn(*ptep)); create_tlb(vma, vaddr, ptep); + if (page == ZERO_PAGE(0)) { + return; + } + /* * Exec page : Independent of aliasing/page-color considerations, * since icache doesn't snoop dcache on ARC, any dirty @@ -446,9 +452,8 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr_unaligned, */ if ((vma->vm_flags & VM_EXEC) || addr_not_cache_congruent(paddr, vaddr)) { - struct page *page = pfn_to_page(pte_pfn(*ptep)); - int dirty = test_and_clear_bit(PG_arch_1, &page->flags); + int dirty = !test_and_set_bit(PG_dc_clean, &page->flags); if (dirty) { /* wback + inv dcache lines */ __flush_dcache_page(paddr, paddr); @@ -464,12 +469,27 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr_unaligned, * the cpuinfo structure for later use. * No Validation is done here, simply read/convert the BCRs */ -void __cpuinit read_decode_mmu_bcr(void) +void read_decode_mmu_bcr(void) { - unsigned int tmp; - struct bcr_mmu_1_2 *mmu2; /* encoded MMU2 attr */ - struct bcr_mmu_3 *mmu3; /* encoded MMU3 attr */ struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; + unsigned int tmp; + struct bcr_mmu_1_2 { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int ver:8, ways:4, sets:4, u_itlb:8, u_dtlb:8; +#else + unsigned int u_dtlb:8, u_itlb:8, sets:4, ways:4, ver:8; +#endif + } *mmu2; + + struct bcr_mmu_3 { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int ver:8, ways:4, sets:4, osm:1, reserv:3, pg_sz:4, + u_itlb:4, u_dtlb:4; +#else + unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, reserv:3, osm:1, sets:4, + ways:4, ver:8; +#endif + } *mmu3; tmp = read_aux_reg(ARC_REG_MMU_BCR); mmu->ver = (tmp >> 24); @@ -505,12 +525,12 @@ char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len) "J-TLB %d (%dx%d), uDTLB %d, uITLB %d, %s\n", p_mmu->num_tlb, p_mmu->sets, p_mmu->ways, p_mmu->u_dtlb, p_mmu->u_itlb, - __CONFIG_ARC_MMU_SASID_VAL ? "SASID" : ""); + IS_ENABLED(CONFIG_ARC_MMU_SASID) ? "SASID" : ""); return buf; } -void __cpuinit arc_mmu_init(void) +void arc_mmu_init(void) { char str[256]; struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S index 3357d26ffe54..5c5bb23001b0 100644 --- a/arch/arc/mm/tlbex.S +++ b/arch/arc/mm/tlbex.S @@ -39,7 +39,7 @@ #include <linux/linkage.h> #include <asm/entry.h> -#include <asm/tlb.h> +#include <asm/mmu.h> #include <asm/pgtable.h> #include <asm/arcregs.h> #include <asm/cache.h> @@ -147,9 +147,9 @@ ex_saved_reg1: #ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT and.f 0, r0, _PAGE_PRESENT bz 1f - ld r2, [num_pte_not_present] - add r2, r2, 1 - st r2, [num_pte_not_present] + ld r3, [num_pte_not_present] + add r3, r3, 1 + st r3, [num_pte_not_present] 1: #endif @@ -271,22 +271,22 @@ ARC_ENTRY EV_TLBMissI #endif ;---------------------------------------------------------------- - ; Get the PTE corresponding to V-addr accessed + ; Get the PTE corresponding to V-addr accessed, r2 is setup with EFA LOAD_FAULT_PTE ;---------------------------------------------------------------- ; VERIFY_PTE: Check if PTE permissions approp for executing code cmp_s r2, VMALLOC_START - mov.lo r2, (_PAGE_PRESENT | _PAGE_U_READ | _PAGE_U_EXECUTE) - mov.hs r2, (_PAGE_PRESENT | _PAGE_K_READ | _PAGE_K_EXECUTE) + mov.lo r2, (_PAGE_PRESENT | _PAGE_U_EXECUTE) + mov.hs r2, (_PAGE_PRESENT | _PAGE_K_EXECUTE) and r3, r0, r2 ; Mask out NON Flag bits from PTE xor.f r3, r3, r2 ; check ( ( pte & flags_test ) == flags_test ) bnz do_slow_path_pf ; Let Linux VM know that the page was accessed - or r0, r0, (_PAGE_PRESENT | _PAGE_ACCESSED) ; set Accessed Bit - st_s r0, [r1] ; Write back PTE + or r0, r0, _PAGE_ACCESSED ; set Accessed Bit + st_s r0, [r1] ; Write back PTE CONV_PTE_TO_TLB COMMIT_ENTRY_TO_MMU @@ -311,7 +311,7 @@ ARC_ENTRY EV_TLBMissD ;---------------------------------------------------------------- ; Get the PTE corresponding to V-addr accessed - ; If PTE exists, it will setup, r0 = PTE, r1 = Ptr to PTE + ; If PTE exists, it will setup, r0 = PTE, r1 = Ptr to PTE, r2 = EFA LOAD_FAULT_PTE ;---------------------------------------------------------------- @@ -345,7 +345,7 @@ ARC_ENTRY EV_TLBMissD ;---------------------------------------------------------------- ; UPDATE_PTE: Let Linux VM know that page was accessed/dirty lr r3, [ecr] - or r0, r0, (_PAGE_PRESENT | _PAGE_ACCESSED) ; Accessed bit always + or r0, r0, _PAGE_ACCESSED ; Accessed bit always btst_s r3, ECR_C_BIT_DTLB_ST_MISS ; See if it was a Write Access ? or.nz r0, r0, _PAGE_MODIFIED ; if Write, set Dirty bit as well st_s r0, [r1] ; Write back PTE @@ -381,18 +381,7 @@ do_slow_path_pf: ; ------- setup args for Linux Page fault Hanlder --------- mov_s r0, sp - lr r2, [efa] - lr r3, [ecr] - - ; Both st and ex imply WRITE access of some sort, hence do_page_fault( ) - ; invoked with write=1 for DTLB-st/ex Miss and write=0 for ITLB miss or - ; DTLB-ld Miss - ; DTLB Miss Cause code is ld = 0x01 , st = 0x02, ex = 0x03 - ; Following code uses that fact that st/ex have one bit in common - - btst_s r3, ECR_C_BIT_DTLB_ST_MISS - mov.z r1, 0 - mov.nz r1, 1 + lr r1, [efa] ; We don't want exceptions to be disabled while the fault is handled. ; Now that we have saved the context we return from exception hence diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index b3700c064c06..d71f3c3bcf24 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -77,6 +77,7 @@ static void __init setup_bvci_lat_unit(void) /*----------------------- Platform Devices -----------------------------*/ +#if IS_ENABLED(CONFIG_SERIAL_ARC) static unsigned long arc_uart_info[] = { 0, /* uart->is_emulated (runtime @running_on_hw) */ 0, /* uart->port.uartclk */ @@ -115,7 +116,7 @@ static struct platform_device arc_uart0_dev = { static struct platform_device *fpga_early_devs[] __initdata = { &arc_uart0_dev, }; -#endif +#endif /* CONFIG_SERIAL_ARC_CONSOLE */ static void arc_fpga_serial_init(void) { @@ -152,8 +153,13 @@ static void arc_fpga_serial_init(void) * otherwise the early console never gets a chance to run. */ add_preferred_console("ttyARC", 0, "115200"); -#endif +#endif /* CONFIG_SERIAL_ARC_CONSOLE */ +} +#else /* !IS_ENABLED(CONFIG_SERIAL_ARC) */ +static void arc_fpga_serial_init(void) +{ } +#endif static void __init plat_fpga_early_init(void) { @@ -169,7 +175,7 @@ static void __init plat_fpga_early_init(void) } static struct of_dev_auxdata plat_auxdata_lookup[] __initdata = { -#if defined(CONFIG_SERIAL_ARC) || defined(CONFIG_SERIAL_ARC_MODULE) +#if IS_ENABLED(CONFIG_SERIAL_ARC) OF_DEV_AUXDATA("snps,arc-uart", UART0_BASE, "arc-uart", arc_uart_info), #endif {} diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 120b83bfde20..48d0a44270bd 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -27,7 +27,7 @@ OBJS += misc.o decompress.o ifeq ($(CONFIG_DEBUG_UNCOMPRESS),y) OBJS += debug.o endif -FONTC = $(srctree)/drivers/video/console/font_acorn_8x8.c +FONTC = $(srctree)/lib/fonts/font_acorn_8x8.c # string library code (-Os is enforced to keep it much smaller) OBJS += string.o diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index 124623e5ef14..64e96960de29 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h @@ -135,7 +135,6 @@ #define KVM_PHYS_MASK (KVM_PHYS_SIZE - 1ULL) #define PTRS_PER_S2_PGD (1ULL << (KVM_PHYS_SHIFT - 30)) #define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t)) -#define S2_PGD_SIZE (1 << S2_PGD_ORDER) /* Virtualization Translation Control Register (VTCR) bits */ #define VTCR_SH0 (3 << 12) diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h index 18d50322a9e2..a2f43ddcc300 100644 --- a/arch/arm/include/asm/kvm_asm.h +++ b/arch/arm/include/asm/kvm_asm.h @@ -37,16 +37,18 @@ #define c5_AIFSR 15 /* Auxilary Instrunction Fault Status R */ #define c6_DFAR 16 /* Data Fault Address Register */ #define c6_IFAR 17 /* Instruction Fault Address Register */ -#define c9_L2CTLR 18 /* Cortex A15 L2 Control Register */ -#define c10_PRRR 19 /* Primary Region Remap Register */ -#define c10_NMRR 20 /* Normal Memory Remap Register */ -#define c12_VBAR 21 /* Vector Base Address Register */ -#define c13_CID 22 /* Context ID Register */ -#define c13_TID_URW 23 /* Thread ID, User R/W */ -#define c13_TID_URO 24 /* Thread ID, User R/O */ -#define c13_TID_PRIV 25 /* Thread ID, Privileged */ -#define c14_CNTKCTL 26 /* Timer Control Register (PL1) */ -#define NR_CP15_REGS 27 /* Number of regs (incl. invalid) */ +#define c7_PAR 18 /* Physical Address Register */ +#define c7_PAR_high 19 /* PAR top 32 bits */ +#define c9_L2CTLR 20 /* Cortex A15 L2 Control Register */ +#define c10_PRRR 21 /* Primary Region Remap Register */ +#define c10_NMRR 22 /* Normal Memory Remap Register */ +#define c12_VBAR 23 /* Vector Base Address Register */ +#define c13_CID 24 /* Context ID Register */ +#define c13_TID_URW 25 /* Thread ID, User R/W */ +#define c13_TID_URO 26 /* Thread ID, User R/O */ +#define c13_TID_PRIV 27 /* Thread ID, Privileged */ +#define c14_CNTKCTL 28 /* Timer Control Register (PL1) */ +#define NR_CP15_REGS 29 /* Number of regs (incl. invalid) */ #define ARM_EXCEPTION_RESET 0 #define ARM_EXCEPTION_UNDEFINED 1 @@ -72,8 +74,6 @@ extern char __kvm_hyp_vector[]; extern char __kvm_hyp_code_start[]; extern char __kvm_hyp_code_end[]; -extern void __kvm_tlb_flush_vmid(struct kvm *kvm); - extern void __kvm_flush_vm_context(void); extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa); diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h index 82b4babead2c..a464e8d7b6c5 100644 --- a/arch/arm/include/asm/kvm_emulate.h +++ b/arch/arm/include/asm/kvm_emulate.h @@ -65,11 +65,6 @@ static inline bool vcpu_mode_priv(struct kvm_vcpu *vcpu) return cpsr_mode > USR_MODE;; } -static inline bool kvm_vcpu_reg_is_pc(struct kvm_vcpu *vcpu, int reg) -{ - return reg == 15; -} - static inline u32 kvm_vcpu_get_hsr(struct kvm_vcpu *vcpu) { return vcpu->arch.fault.hsr; diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 57cb786a6203..7d22517d8071 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -23,9 +23,14 @@ #include <asm/kvm_asm.h> #include <asm/kvm_mmio.h> #include <asm/fpstate.h> -#include <asm/kvm_arch_timer.h> +#include <kvm/arm_arch_timer.h> +#if defined(CONFIG_KVM_ARM_MAX_VCPUS) #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS +#else +#define KVM_MAX_VCPUS 0 +#endif + #define KVM_USER_MEM_SLOTS 32 #define KVM_PRIVATE_MEM_SLOTS 4 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -38,7 +43,7 @@ #define KVM_NR_PAGE_SIZES 1 #define KVM_PAGES_PER_HPAGE(x) (1UL<<31) -#include <asm/kvm_vgic.h> +#include <kvm/arm_vgic.h> struct kvm_vcpu; u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode); @@ -190,8 +195,8 @@ int kvm_arm_coproc_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, int exception_index); -static inline void __cpu_init_hyp_mode(unsigned long long boot_pgd_ptr, - unsigned long long pgd_ptr, +static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr, + phys_addr_t pgd_ptr, unsigned long hyp_stack_ptr, unsigned long vector_ptr) { diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig index 370e1a8af6ac..ebf5015508b5 100644 --- a/arch/arm/kvm/Kconfig +++ b/arch/arm/kvm/Kconfig @@ -41,9 +41,9 @@ config KVM_ARM_HOST Provides host support for ARM processors. config KVM_ARM_MAX_VCPUS - int "Number maximum supported virtual CPUs per VM" if KVM_ARM_HOST - default 4 if KVM_ARM_HOST - default 0 + int "Number maximum supported virtual CPUs per VM" + depends on KVM_ARM_HOST + default 4 help Static number of max supported virtual CPUs per VM. @@ -67,6 +67,4 @@ config KVM_ARM_TIMER ---help--- Adds support for the Architected Timers in virtual machines -source drivers/virtio/Kconfig - endif # VIRTUALIZATION diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index 53c5ed83d16f..d99bee4950e5 100644 --- a/arch/arm/kvm/Makefile +++ b/arch/arm/kvm/Makefile @@ -14,10 +14,11 @@ CFLAGS_mmu.o := -I. AFLAGS_init.o := -Wa,-march=armv7-a$(plus_virt) AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt) -kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o) +KVM := ../../../virt/kvm +kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o obj-y += kvm-arm.o init.o interrupts.o obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o obj-y += coproc.o coproc_a15.o mmio.o psci.o perf.o -obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o -obj-$(CONFIG_KVM_ARM_TIMER) += arch_timer.o +obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o +obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index ef1703b9587b..741f66a2edbd 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -800,8 +800,8 @@ long kvm_arch_vm_ioctl(struct file *filp, static void cpu_init_hyp_mode(void *dummy) { - unsigned long long boot_pgd_ptr; - unsigned long long pgd_ptr; + phys_addr_t boot_pgd_ptr; + phys_addr_t pgd_ptr; unsigned long hyp_stack_ptr; unsigned long stack_page; unsigned long vector_ptr; @@ -809,8 +809,8 @@ static void cpu_init_hyp_mode(void *dummy) /* Switch from the HYP stub to our own HYP init vector */ __hyp_set_vectors(kvm_get_idmap_vector()); - boot_pgd_ptr = (unsigned long long)kvm_mmu_get_boot_httbr(); - pgd_ptr = (unsigned long long)kvm_mmu_get_httbr(); + boot_pgd_ptr = kvm_mmu_get_boot_httbr(); + pgd_ptr = kvm_mmu_get_httbr(); stack_page = __get_cpu_var(kvm_arm_hyp_stack_page); hyp_stack_ptr = stack_page + PAGE_SIZE; vector_ptr = (unsigned long)__kvm_hyp_vector; diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index 8eea97be1ed5..4a5199070430 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c @@ -180,6 +180,10 @@ static const struct coproc_reg cp15_regs[] = { NULL, reset_unknown, c6_DFAR }, { CRn( 6), CRm( 0), Op1( 0), Op2( 2), is32, NULL, reset_unknown, c6_IFAR }, + + /* PAR swapped by interrupt.S */ + { CRn( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR }, + /* * DC{C,I,CI}SW operations: */ diff --git a/arch/arm/kvm/handle_exit.c b/arch/arm/kvm/handle_exit.c index 3d74a0be47db..df4c82d47ad7 100644 --- a/arch/arm/kvm/handle_exit.c +++ b/arch/arm/kvm/handle_exit.c @@ -52,9 +52,6 @@ static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) { - if (kvm_psci_call(vcpu)) - return 1; - kvm_inject_undefined(vcpu); return 1; } diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S index f7793df62f58..16cd4ba5d7fd 100644 --- a/arch/arm/kvm/interrupts.S +++ b/arch/arm/kvm/interrupts.S @@ -49,6 +49,7 @@ __kvm_hyp_code_start: ENTRY(__kvm_tlb_flush_vmid_ipa) push {r2, r3} + dsb ishst add r0, r0, #KVM_VTTBR ldrd r2, r3, [r0] mcrr p15, 6, r2, r3, c2 @ Write VTTBR @@ -291,6 +292,7 @@ THUMB( orr r2, r2, #PSR_T_BIT ) ldr r2, =BSYM(panic) msr ELR_hyp, r2 ldr r0, =\panic_str + clrex @ Clear exclusive monitor eret .endm @@ -414,6 +416,10 @@ guest_trap: mrcne p15, 4, r2, c6, c0, 4 @ HPFAR bne 3f + /* Preserve PAR */ + mrrc p15, 0, r0, r1, c7 @ PAR + push {r0, r1} + /* Resolve IPA using the xFAR */ mcr p15, 0, r2, c7, c8, 0 @ ATS1CPR isb @@ -424,13 +430,20 @@ guest_trap: lsl r2, r2, #4 orr r2, r2, r1, lsl #24 + /* Restore PAR */ + pop {r0, r1} + mcrr p15, 0, r0, r1, c7 @ PAR + 3: load_vcpu @ Load VCPU pointer to r0 str r2, [r0, #VCPU_HPFAR] 1: mov r1, #ARM_EXCEPTION_HVC b __kvm_vcpu_return -4: pop {r0, r1, r2} @ Failed translation, return to guest +4: pop {r0, r1} @ Failed translation, return to guest + mcrr p15, 0, r0, r1, c7 @ PAR + clrex + pop {r0, r1, r2} eret /* @@ -456,6 +469,7 @@ switch_to_guest_vfp: pop {r3-r7} pop {r0-r2} + clrex eret #endif diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S index d43cfb5b37c4..6f18695a09cb 100644 --- a/arch/arm/kvm/interrupts_head.S +++ b/arch/arm/kvm/interrupts_head.S @@ -302,11 +302,14 @@ vcpu .req r0 @ vcpu pointer always in r0 .endif mrc p15, 0, r2, c14, c1, 0 @ CNTKCTL + mrrc p15, 0, r4, r5, c7 @ PAR .if \store_to_vcpu == 0 - push {r2} + push {r2,r4-r5} .else str r2, [vcpu, #CP15_OFFSET(c14_CNTKCTL)] + add r12, vcpu, #CP15_OFFSET(c7_PAR) + strd r4, r5, [r12] .endif .endm @@ -319,12 +322,15 @@ vcpu .req r0 @ vcpu pointer always in r0 */ .macro write_cp15_state read_from_vcpu .if \read_from_vcpu == 0 - pop {r2} + pop {r2,r4-r5} .else ldr r2, [vcpu, #CP15_OFFSET(c14_CNTKCTL)] + add r12, vcpu, #CP15_OFFSET(c7_PAR) + ldrd r4, r5, [r12] .endif mcr p15, 0, r2, c14, c1, 0 @ CNTKCTL + mcrr p15, 0, r4, r5, c7 @ PAR .if \read_from_vcpu == 0 pop {r2-r12} diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c index 72a12f2171b2..b8e06b7a2833 100644 --- a/arch/arm/kvm/mmio.c +++ b/arch/arm/kvm/mmio.c @@ -86,12 +86,6 @@ static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, sign_extend = kvm_vcpu_dabt_issext(vcpu); rt = kvm_vcpu_dabt_get_rd(vcpu); - if (kvm_vcpu_reg_is_pc(vcpu, rt)) { - /* IO memory trying to read/write pc */ - kvm_inject_pabt(vcpu, kvm_vcpu_get_hfar(vcpu)); - return 1; - } - mmio->is_write = is_write; mmio->phys_addr = fault_ipa; mmio->len = len; diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 84ba67b982c0..ca6bea4859b4 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -382,9 +382,6 @@ int kvm_alloc_stage2_pgd(struct kvm *kvm) if (!pgd) return -ENOMEM; - /* stage-2 pgd must be aligned to its size */ - VM_BUG_ON((unsigned long)pgd & (S2_PGD_SIZE - 1)); - memset(pgd, 0, PTRS_PER_S2_PGD * sizeof(pgd_t)); kvm_clean_pgd(pgd); kvm->arch.pgd = pgd; diff --git a/arch/arm/kvm/psci.c b/arch/arm/kvm/psci.c index 7ee5bb7a3667..86a693a02ba3 100644 --- a/arch/arm/kvm/psci.c +++ b/arch/arm/kvm/psci.c @@ -75,7 +75,7 @@ static unsigned long kvm_psci_vcpu_on(struct kvm_vcpu *source_vcpu) * kvm_psci_call - handle PSCI call if r0 value is in range * @vcpu: Pointer to the VCPU struct * - * Handle PSCI calls from guests through traps from HVC or SMC instructions. + * Handle PSCI calls from guests through traps from HVC instructions. * The calling convention is similar to SMC calls to the secure world where * the function number is placed in r0 and this function returns true if the * function number specified in r0 is withing the PSCI range, and false diff --git a/arch/arm/kvm/reset.c b/arch/arm/kvm/reset.c index b80256b554cd..b7840e7aa452 100644 --- a/arch/arm/kvm/reset.c +++ b/arch/arm/kvm/reset.c @@ -27,6 +27,8 @@ #include <asm/kvm_arm.h> #include <asm/kvm_coproc.h> +#include <kvm/arm_arch_timer.h> + /****************************************************************************** * Cortex-A15 Reset Values */ @@ -37,6 +39,11 @@ static struct kvm_regs a15_regs_reset = { .usr_regs.ARM_cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT, }; +static const struct kvm_irq_level a15_vtimer_irq = { + .irq = 27, + .level = 1, +}; + /******************************************************************************* * Exported reset function @@ -52,6 +59,7 @@ static struct kvm_regs a15_regs_reset = { int kvm_reset_vcpu(struct kvm_vcpu *vcpu) { struct kvm_regs *cpu_reset; + const struct kvm_irq_level *cpu_vtimer_irq; switch (vcpu->arch.target) { case KVM_ARM_TARGET_CORTEX_A15: @@ -59,6 +67,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu) return -EINVAL; cpu_reset = &a15_regs_reset; vcpu->arch.midr = read_cpuid_id(); + cpu_vtimer_irq = &a15_vtimer_irq; break; default: return -ENODEV; @@ -70,5 +79,8 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu) /* Reset CP15 registers */ kvm_reset_coprocs(vcpu); + /* Reset arch_timer context */ + kvm_timer_vcpu_reset(vcpu, cpu_vtimer_irq); + return 0; } diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index a075b3e0c5c7..e026b19b23ea 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -40,6 +40,7 @@ config ARCH_DAVINCI_DA850 bool "DA850/OMAP-L138/AM18x based system" select ARCH_DAVINCI_DA8XX select ARCH_HAS_CPUFREQ + select CPU_FREQ_TABLE select CP_INTC config ARCH_DAVINCI_DA8XX diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 4d6933848abf..a0d4f6038b60 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1004,7 +1004,7 @@ static const struct da850_opp da850_opp_96 = { #define OPP(freq) \ { \ - .index = (unsigned int) &da850_opp_##freq, \ + .driver_data = (unsigned int) &da850_opp_##freq, \ .frequency = freq * 1000, \ } @@ -1016,7 +1016,7 @@ static struct cpufreq_frequency_table da850_freq_table[] = { OPP(200), OPP(96), { - .index = 0, + .driver_data = 0, .frequency = CPUFREQ_TABLE_END, }, }; @@ -1044,7 +1044,7 @@ static int da850_set_voltage(unsigned int index) if (!cvdd) return -ENODEV; - opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].driver_data; return regulator_set_voltage(cvdd, opp->cvdd_min, opp->cvdd_max); } @@ -1125,7 +1125,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) struct pll_data *pll = clk->pll_data; int ret; - opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].driver_data; prediv = opp->prediv; mult = opp->mult; postdiv = opp->postdiv; diff --git a/arch/arm/mach-omap2/omap_device.c b/arch/arm/mach-omap2/omap_device.c index 68be532f8688..5cc92874be7e 100644 --- a/arch/arm/mach-omap2/omap_device.c +++ b/arch/arm/mach-omap2/omap_device.c @@ -588,11 +588,6 @@ static int _od_runtime_suspend(struct device *dev) return ret; } -static int _od_runtime_idle(struct device *dev) -{ - return pm_generic_runtime_idle(dev); -} - static int _od_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -648,7 +643,7 @@ static int _od_resume_noirq(struct device *dev) struct dev_pm_domain omap_device_pm_domain = { .ops = { SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume, - _od_runtime_idle) + NULL) USE_PLATFORM_PM_SLEEP_OPS .suspend_noirq = _od_suspend_noirq, .resume_noirq = _od_resume_noirq, diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c index aee3c8940a30..7a42e1960c3b 100644 --- a/arch/arm/mach-omap2/smartreflex-class3.c +++ b/arch/arm/mach-omap2/smartreflex-class3.c @@ -26,14 +26,14 @@ static int sr_class3_enable(struct omap_sr *sr) } omap_vp_enable(sr->voltdm); - return sr_enable(sr->voltdm, volt); + return sr_enable(sr, volt); } static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset) { - sr_disable_errgen(sr->voltdm); + sr_disable_errgen(sr); omap_vp_disable(sr->voltdm); - sr_disable(sr->voltdm); + sr_disable(sr); if (is_volt_reset) voltdm_reset(sr->voltdm); @@ -42,7 +42,7 @@ static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset) static int sr_class3_configure(struct omap_sr *sr) { - return sr_configure_errgen(sr->voltdm); + return sr_configure_errgen(sr); } /* SR class3 structure */ diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig index 96100dbf5a2e..a8427115ee07 100644 --- a/arch/arm/mach-pxa/Kconfig +++ b/arch/arm/mach-pxa/Kconfig @@ -615,12 +615,14 @@ endmenu config PXA25x bool select CPU_XSCALE + select CPU_FREQ_TABLE if CPU_FREQ help Select code specific to PXA21x/25x/26x variants config PXA27x bool select CPU_XSCALE + select CPU_FREQ_TABLE if CPU_FREQ help Select code specific to PXA27x variants @@ -633,6 +635,7 @@ config CPU_PXA26x config PXA3xx bool select CPU_XSC3 + select CPU_FREQ_TABLE if CPU_FREQ help Select code specific to PXA3xx variants diff --git a/arch/arm/mach-s3c24xx/cpufreq-utils.c b/arch/arm/mach-s3c24xx/cpufreq-utils.c index ddd8280e3875..2a0aa5684e72 100644 --- a/arch/arm/mach-s3c24xx/cpufreq-utils.c +++ b/arch/arm/mach-s3c24xx/cpufreq-utils.c @@ -60,5 +60,5 @@ void s3c2410_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg) */ void s3c2410_set_fvco(struct s3c_cpufreq_config *cfg) { - __raw_writel(cfg->pll.index, S3C2410_MPLLCON); + __raw_writel(cfg->pll.driver_data, S3C2410_MPLLCON); } diff --git a/arch/arm/mach-s3c24xx/pll-s3c2410.c b/arch/arm/mach-s3c24xx/pll-s3c2410.c index dcf3420a3271..5e37d368594b 100644 --- a/arch/arm/mach-s3c24xx/pll-s3c2410.c +++ b/arch/arm/mach-s3c24xx/pll-s3c2410.c @@ -33,36 +33,36 @@ #include <plat/cpu-freq-core.h> static struct cpufreq_frequency_table pll_vals_12MHz[] = { - { .frequency = 34000000, .index = PLLVAL(82, 2, 3), }, - { .frequency = 45000000, .index = PLLVAL(82, 1, 3), }, - { .frequency = 51000000, .index = PLLVAL(161, 3, 3), }, - { .frequency = 48000000, .index = PLLVAL(120, 2, 3), }, - { .frequency = 56000000, .index = PLLVAL(142, 2, 3), }, - { .frequency = 68000000, .index = PLLVAL(82, 2, 2), }, - { .frequency = 79000000, .index = PLLVAL(71, 1, 2), }, - { .frequency = 85000000, .index = PLLVAL(105, 2, 2), }, - { .frequency = 90000000, .index = PLLVAL(112, 2, 2), }, - { .frequency = 101000000, .index = PLLVAL(127, 2, 2), }, - { .frequency = 113000000, .index = PLLVAL(105, 1, 2), }, - { .frequency = 118000000, .index = PLLVAL(150, 2, 2), }, - { .frequency = 124000000, .index = PLLVAL(116, 1, 2), }, - { .frequency = 135000000, .index = PLLVAL(82, 2, 1), }, - { .frequency = 147000000, .index = PLLVAL(90, 2, 1), }, - { .frequency = 152000000, .index = PLLVAL(68, 1, 1), }, - { .frequency = 158000000, .index = PLLVAL(71, 1, 1), }, - { .frequency = 170000000, .index = PLLVAL(77, 1, 1), }, - { .frequency = 180000000, .index = PLLVAL(82, 1, 1), }, - { .frequency = 186000000, .index = PLLVAL(85, 1, 1), }, - { .frequency = 192000000, .index = PLLVAL(88, 1, 1), }, - { .frequency = 203000000, .index = PLLVAL(161, 3, 1), }, + { .frequency = 34000000, .driver_data = PLLVAL(82, 2, 3), }, + { .frequency = 45000000, .driver_data = PLLVAL(82, 1, 3), }, + { .frequency = 51000000, .driver_data = PLLVAL(161, 3, 3), }, + { .frequency = 48000000, .driver_data = PLLVAL(120, 2, 3), }, + { .frequency = 56000000, .driver_data = PLLVAL(142, 2, 3), }, + { .frequency = 68000000, .driver_data = PLLVAL(82, 2, 2), }, + { .frequency = 79000000, .driver_data = PLLVAL(71, 1, 2), }, + { .frequency = 85000000, .driver_data = PLLVAL(105, 2, 2), }, + { .frequency = 90000000, .driver_data = PLLVAL(112, 2, 2), }, + { .frequency = 101000000, .driver_data = PLLVAL(127, 2, 2), }, + { .frequency = 113000000, .driver_data = PLLVAL(105, 1, 2), }, + { .frequency = 118000000, .driver_data = PLLVAL(150, 2, 2), }, + { .frequency = 124000000, .driver_data = PLLVAL(116, 1, 2), }, + { .frequency = 135000000, .driver_data = PLLVAL(82, 2, 1), }, + { .frequency = 147000000, .driver_data = PLLVAL(90, 2, 1), }, + { .frequency = 152000000, .driver_data = PLLVAL(68, 1, 1), }, + { .frequency = 158000000, .driver_data = PLLVAL(71, 1, 1), }, + { .frequency = 170000000, .driver_data = PLLVAL(77, 1, 1), }, + { .frequency = 180000000, .driver_data = PLLVAL(82, 1, 1), }, + { .frequency = 186000000, .driver_data = PLLVAL(85, 1, 1), }, + { .frequency = 192000000, .driver_data = PLLVAL(88, 1, 1), }, + { .frequency = 203000000, .driver_data = PLLVAL(161, 3, 1), }, /* 2410A extras */ - { .frequency = 210000000, .index = PLLVAL(132, 2, 1), }, - { .frequency = 226000000, .index = PLLVAL(105, 1, 1), }, - { .frequency = 266000000, .index = PLLVAL(125, 1, 1), }, - { .frequency = 268000000, .index = PLLVAL(126, 1, 1), }, - { .frequency = 270000000, .index = PLLVAL(127, 1, 1), }, + { .frequency = 210000000, .driver_data = PLLVAL(132, 2, 1), }, + { .frequency = 226000000, .driver_data = PLLVAL(105, 1, 1), }, + { .frequency = 266000000, .driver_data = PLLVAL(125, 1, 1), }, + { .frequency = 268000000, .driver_data = PLLVAL(126, 1, 1), }, + { .frequency = 270000000, .driver_data = PLLVAL(127, 1, 1), }, }; static int s3c2410_plls_add(struct device *dev, struct subsys_interface *sif) diff --git a/arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c b/arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c index 673781758319..a19460e6e7b0 100644 --- a/arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c +++ b/arch/arm/mach-s3c24xx/pll-s3c2440-12000000.c @@ -21,33 +21,33 @@ #include <plat/cpu-freq-core.h> static struct cpufreq_frequency_table s3c2440_plls_12[] __initdata = { - { .frequency = 75000000, .index = PLLVAL(0x75, 3, 3), }, /* FVco 600.000000 */ - { .frequency = 80000000, .index = PLLVAL(0x98, 4, 3), }, /* FVco 640.000000 */ - { .frequency = 90000000, .index = PLLVAL(0x70, 2, 3), }, /* FVco 720.000000 */ - { .frequency = 100000000, .index = PLLVAL(0x5c, 1, 3), }, /* FVco 800.000000 */ - { .frequency = 110000000, .index = PLLVAL(0x66, 1, 3), }, /* FVco 880.000000 */ - { .frequency = 120000000, .index = PLLVAL(0x70, 1, 3), }, /* FVco 960.000000 */ - { .frequency = 150000000, .index = PLLVAL(0x75, 3, 2), }, /* FVco 600.000000 */ - { .frequency = 160000000, .index = PLLVAL(0x98, 4, 2), }, /* FVco 640.000000 */ - { .frequency = 170000000, .index = PLLVAL(0x4d, 1, 2), }, /* FVco 680.000000 */ - { .frequency = 180000000, .index = PLLVAL(0x70, 2, 2), }, /* FVco 720.000000 */ - { .frequency = 190000000, .index = PLLVAL(0x57, 1, 2), }, /* FVco 760.000000 */ - { .frequency = 200000000, .index = PLLVAL(0x5c, 1, 2), }, /* FVco 800.000000 */ - { .frequency = 210000000, .index = PLLVAL(0x84, 2, 2), }, /* FVco 840.000000 */ - { .frequency = 220000000, .index = PLLVAL(0x66, 1, 2), }, /* FVco 880.000000 */ - { .frequency = 230000000, .index = PLLVAL(0x6b, 1, 2), }, /* FVco 920.000000 */ - { .frequency = 240000000, .index = PLLVAL(0x70, 1, 2), }, /* FVco 960.000000 */ - { .frequency = 300000000, .index = PLLVAL(0x75, 3, 1), }, /* FVco 600.000000 */ - { .frequency = 310000000, .index = PLLVAL(0x93, 4, 1), }, /* FVco 620.000000 */ - { .frequency = 320000000, .index = PLLVAL(0x98, 4, 1), }, /* FVco 640.000000 */ - { .frequency = 330000000, .index = PLLVAL(0x66, 2, 1), }, /* FVco 660.000000 */ - { .frequency = 340000000, .index = PLLVAL(0x4d, 1, 1), }, /* FVco 680.000000 */ - { .frequency = 350000000, .index = PLLVAL(0xa7, 4, 1), }, /* FVco 700.000000 */ - { .frequency = 360000000, .index = PLLVAL(0x70, 2, 1), }, /* FVco 720.000000 */ - { .frequency = 370000000, .index = PLLVAL(0xb1, 4, 1), }, /* FVco 740.000000 */ - { .frequency = 380000000, .index = PLLVAL(0x57, 1, 1), }, /* FVco 760.000000 */ - { .frequency = 390000000, .index = PLLVAL(0x7a, 2, 1), }, /* FVco 780.000000 */ - { .frequency = 400000000, .index = PLLVAL(0x5c, 1, 1), }, /* FVco 800.000000 */ + { .frequency = 75000000, .driver_data = PLLVAL(0x75, 3, 3), }, /* FVco 600.000000 */ + { .frequency = 80000000, .driver_data = PLLVAL(0x98, 4, 3), }, /* FVco 640.000000 */ + { .frequency = 90000000, .driver_data = PLLVAL(0x70, 2, 3), }, /* FVco 720.000000 */ + { .frequency = 100000000, .driver_data = PLLVAL(0x5c, 1, 3), }, /* FVco 800.000000 */ + { .frequency = 110000000, .driver_data = PLLVAL(0x66, 1, 3), }, /* FVco 880.000000 */ + { .frequency = 120000000, .driver_data = PLLVAL(0x70, 1, 3), }, /* FVco 960.000000 */ + { .frequency = 150000000, .driver_data = PLLVAL(0x75, 3, 2), }, /* FVco 600.000000 */ + { .frequency = 160000000, .driver_data = PLLVAL(0x98, 4, 2), }, /* FVco 640.000000 */ + { .frequency = 170000000, .driver_data = PLLVAL(0x4d, 1, 2), }, /* FVco 680.000000 */ + { .frequency = 180000000, .driver_data = PLLVAL(0x70, 2, 2), }, /* FVco 720.000000 */ + { .frequency = 190000000, .driver_data = PLLVAL(0x57, 1, 2), }, /* FVco 760.000000 */ + { .frequency = 200000000, .driver_data = PLLVAL(0x5c, 1, 2), }, /* FVco 800.000000 */ + { .frequency = 210000000, .driver_data = PLLVAL(0x84, 2, 2), }, /* FVco 840.000000 */ + { .frequency = 220000000, .driver_data = PLLVAL(0x66, 1, 2), }, /* FVco 880.000000 */ + { .frequency = 230000000, .driver_data = PLLVAL(0x6b, 1, 2), }, /* FVco 920.000000 */ + { .frequency = 240000000, .driver_data = PLLVAL(0x70, 1, 2), }, /* FVco 960.000000 */ + { .frequency = 300000000, .driver_data = PLLVAL(0x75, 3, 1), }, /* FVco 600.000000 */ + { .frequency = 310000000, .driver_data = PLLVAL(0x93, 4, 1), }, /* FVco 620.000000 */ + { .frequency = 320000000, .driver_data = PLLVAL(0x98, 4, 1), }, /* FVco 640.000000 */ + { .frequency = 330000000, .driver_data = PLLVAL(0x66, 2, 1), }, /* FVco 660.000000 */ + { .frequency = 340000000, .driver_data = PLLVAL(0x4d, 1, 1), }, /* FVco 680.000000 */ + { .frequency = 350000000, .driver_data = PLLVAL(0xa7, 4, 1), }, /* FVco 700.000000 */ + { .frequency = 360000000, .driver_data = PLLVAL(0x70, 2, 1), }, /* FVco 720.000000 */ + { .frequency = 370000000, .driver_data = PLLVAL(0xb1, 4, 1), }, /* FVco 740.000000 */ + { .frequency = 380000000, .driver_data = PLLVAL(0x57, 1, 1), }, /* FVco 760.000000 */ + { .frequency = 390000000, .driver_data = PLLVAL(0x7a, 2, 1), }, /* FVco 780.000000 */ + { .frequency = 400000000, .driver_data = PLLVAL(0x5c, 1, 1), }, /* FVco 800.000000 */ }; static int s3c2440_plls12_add(struct device *dev, struct subsys_interface *sif) diff --git a/arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c b/arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c index debfa106289b..1191b2905625 100644 --- a/arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c +++ b/arch/arm/mach-s3c24xx/pll-s3c2440-16934400.c @@ -21,61 +21,61 @@ #include <plat/cpu-freq-core.h> static struct cpufreq_frequency_table s3c2440_plls_169344[] __initdata = { - { .frequency = 78019200, .index = PLLVAL(121, 5, 3), }, /* FVco 624.153600 */ - { .frequency = 84067200, .index = PLLVAL(131, 5, 3), }, /* FVco 672.537600 */ - { .frequency = 90115200, .index = PLLVAL(141, 5, 3), }, /* FVco 720.921600 */ - { .frequency = 96163200, .index = PLLVAL(151, 5, 3), }, /* FVco 769.305600 */ - { .frequency = 102135600, .index = PLLVAL(185, 6, 3), }, /* FVco 817.084800 */ - { .frequency = 108259200, .index = PLLVAL(171, 5, 3), }, /* FVco 866.073600 */ - { .frequency = 114307200, .index = PLLVAL(127, 3, 3), }, /* FVco 914.457600 */ - { .frequency = 120234240, .index = PLLVAL(134, 3, 3), }, /* FVco 961.873920 */ - { .frequency = 126161280, .index = PLLVAL(141, 3, 3), }, /* FVco 1009.290240 */ - { .frequency = 132088320, .index = PLLVAL(148, 3, 3), }, /* FVco 1056.706560 */ - { .frequency = 138015360, .index = PLLVAL(155, 3, 3), }, /* FVco 1104.122880 */ - { .frequency = 144789120, .index = PLLVAL(163, 3, 3), }, /* FVco 1158.312960 */ - { .frequency = 150100363, .index = PLLVAL(187, 9, 2), }, /* FVco 600.401454 */ - { .frequency = 156038400, .index = PLLVAL(121, 5, 2), }, /* FVco 624.153600 */ - { .frequency = 162086400, .index = PLLVAL(126, 5, 2), }, /* FVco 648.345600 */ - { .frequency = 168134400, .index = PLLVAL(131, 5, 2), }, /* FVco 672.537600 */ - { .frequency = 174048000, .index = PLLVAL(177, 7, 2), }, /* FVco 696.192000 */ - { .frequency = 180230400, .index = PLLVAL(141, 5, 2), }, /* FVco 720.921600 */ - { .frequency = 186278400, .index = PLLVAL(124, 4, 2), }, /* FVco 745.113600 */ - { .frequency = 192326400, .index = PLLVAL(151, 5, 2), }, /* FVco 769.305600 */ - { .frequency = 198132480, .index = PLLVAL(109, 3, 2), }, /* FVco 792.529920 */ - { .frequency = 204271200, .index = PLLVAL(185, 6, 2), }, /* FVco 817.084800 */ - { .frequency = 210268800, .index = PLLVAL(141, 4, 2), }, /* FVco 841.075200 */ - { .frequency = 216518400, .index = PLLVAL(171, 5, 2), }, /* FVco 866.073600 */ - { .frequency = 222264000, .index = PLLVAL(97, 2, 2), }, /* FVco 889.056000 */ - { .frequency = 228614400, .index = PLLVAL(127, 3, 2), }, /* FVco 914.457600 */ - { .frequency = 234259200, .index = PLLVAL(158, 4, 2), }, /* FVco 937.036800 */ - { .frequency = 240468480, .index = PLLVAL(134, 3, 2), }, /* FVco 961.873920 */ - { .frequency = 246960000, .index = PLLVAL(167, 4, 2), }, /* FVco 987.840000 */ - { .frequency = 252322560, .index = PLLVAL(141, 3, 2), }, /* FVco 1009.290240 */ - { .frequency = 258249600, .index = PLLVAL(114, 2, 2), }, /* FVco 1032.998400 */ - { .frequency = 264176640, .index = PLLVAL(148, 3, 2), }, /* FVco 1056.706560 */ - { .frequency = 270950400, .index = PLLVAL(120, 2, 2), }, /* FVco 1083.801600 */ - { .frequency = 276030720, .index = PLLVAL(155, 3, 2), }, /* FVco 1104.122880 */ - { .frequency = 282240000, .index = PLLVAL(92, 1, 2), }, /* FVco 1128.960000 */ - { .frequency = 289578240, .index = PLLVAL(163, 3, 2), }, /* FVco 1158.312960 */ - { .frequency = 294235200, .index = PLLVAL(131, 2, 2), }, /* FVco 1176.940800 */ - { .frequency = 300200727, .index = PLLVAL(187, 9, 1), }, /* FVco 600.401454 */ - { .frequency = 306358690, .index = PLLVAL(191, 9, 1), }, /* FVco 612.717380 */ - { .frequency = 312076800, .index = PLLVAL(121, 5, 1), }, /* FVco 624.153600 */ - { .frequency = 318366720, .index = PLLVAL(86, 3, 1), }, /* FVco 636.733440 */ - { .frequency = 324172800, .index = PLLVAL(126, 5, 1), }, /* FVco 648.345600 */ - { .frequency = 330220800, .index = PLLVAL(109, 4, 1), }, /* FVco 660.441600 */ - { .frequency = 336268800, .index = PLLVAL(131, 5, 1), }, /* FVco 672.537600 */ - { .frequency = 342074880, .index = PLLVAL(93, 3, 1), }, /* FVco 684.149760 */ - { .frequency = 348096000, .index = PLLVAL(177, 7, 1), }, /* FVco 696.192000 */ - { .frequency = 355622400, .index = PLLVAL(118, 4, 1), }, /* FVco 711.244800 */ - { .frequency = 360460800, .index = PLLVAL(141, 5, 1), }, /* FVco 720.921600 */ - { .frequency = 366206400, .index = PLLVAL(165, 6, 1), }, /* FVco 732.412800 */ - { .frequency = 372556800, .index = PLLVAL(124, 4, 1), }, /* FVco 745.113600 */ - { .frequency = 378201600, .index = PLLVAL(126, 4, 1), }, /* FVco 756.403200 */ - { .frequency = 384652800, .index = PLLVAL(151, 5, 1), }, /* FVco 769.305600 */ - { .frequency = 391608000, .index = PLLVAL(177, 6, 1), }, /* FVco 783.216000 */ - { .frequency = 396264960, .index = PLLVAL(109, 3, 1), }, /* FVco 792.529920 */ - { .frequency = 402192000, .index = PLLVAL(87, 2, 1), }, /* FVco 804.384000 */ + { .frequency = 78019200, .driver_data = PLLVAL(121, 5, 3), }, /* FVco 624.153600 */ + { .frequency = 84067200, .driver_data = PLLVAL(131, 5, 3), }, /* FVco 672.537600 */ + { .frequency = 90115200, .driver_data = PLLVAL(141, 5, 3), }, /* FVco 720.921600 */ + { .frequency = 96163200, .driver_data = PLLVAL(151, 5, 3), }, /* FVco 769.305600 */ + { .frequency = 102135600, .driver_data = PLLVAL(185, 6, 3), }, /* FVco 817.084800 */ + { .frequency = 108259200, .driver_data = PLLVAL(171, 5, 3), }, /* FVco 866.073600 */ + { .frequency = 114307200, .driver_data = PLLVAL(127, 3, 3), }, /* FVco 914.457600 */ + { .frequency = 120234240, .driver_data = PLLVAL(134, 3, 3), }, /* FVco 961.873920 */ + { .frequency = 126161280, .driver_data = PLLVAL(141, 3, 3), }, /* FVco 1009.290240 */ + { .frequency = 132088320, .driver_data = PLLVAL(148, 3, 3), }, /* FVco 1056.706560 */ + { .frequency = 138015360, .driver_data = PLLVAL(155, 3, 3), }, /* FVco 1104.122880 */ + { .frequency = 144789120, .driver_data = PLLVAL(163, 3, 3), }, /* FVco 1158.312960 */ + { .frequency = 150100363, .driver_data = PLLVAL(187, 9, 2), }, /* FVco 600.401454 */ + { .frequency = 156038400, .driver_data = PLLVAL(121, 5, 2), }, /* FVco 624.153600 */ + { .frequency = 162086400, .driver_data = PLLVAL(126, 5, 2), }, /* FVco 648.345600 */ + { .frequency = 168134400, .driver_data = PLLVAL(131, 5, 2), }, /* FVco 672.537600 */ + { .frequency = 174048000, .driver_data = PLLVAL(177, 7, 2), }, /* FVco 696.192000 */ + { .frequency = 180230400, .driver_data = PLLVAL(141, 5, 2), }, /* FVco 720.921600 */ + { .frequency = 186278400, .driver_data = PLLVAL(124, 4, 2), }, /* FVco 745.113600 */ + { .frequency = 192326400, .driver_data = PLLVAL(151, 5, 2), }, /* FVco 769.305600 */ + { .frequency = 198132480, .driver_data = PLLVAL(109, 3, 2), }, /* FVco 792.529920 */ + { .frequency = 204271200, .driver_data = PLLVAL(185, 6, 2), }, /* FVco 817.084800 */ + { .frequency = 210268800, .driver_data = PLLVAL(141, 4, 2), }, /* FVco 841.075200 */ + { .frequency = 216518400, .driver_data = PLLVAL(171, 5, 2), }, /* FVco 866.073600 */ + { .frequency = 222264000, .driver_data = PLLVAL(97, 2, 2), }, /* FVco 889.056000 */ + { .frequency = 228614400, .driver_data = PLLVAL(127, 3, 2), }, /* FVco 914.457600 */ + { .frequency = 234259200, .driver_data = PLLVAL(158, 4, 2), }, /* FVco 937.036800 */ + { .frequency = 240468480, .driver_data = PLLVAL(134, 3, 2), }, /* FVco 961.873920 */ + { .frequency = 246960000, .driver_data = PLLVAL(167, 4, 2), }, /* FVco 987.840000 */ + { .frequency = 252322560, .driver_data = PLLVAL(141, 3, 2), }, /* FVco 1009.290240 */ + { .frequency = 258249600, .driver_data = PLLVAL(114, 2, 2), }, /* FVco 1032.998400 */ + { .frequency = 264176640, .driver_data = PLLVAL(148, 3, 2), }, /* FVco 1056.706560 */ + { .frequency = 270950400, .driver_data = PLLVAL(120, 2, 2), }, /* FVco 1083.801600 */ + { .frequency = 276030720, .driver_data = PLLVAL(155, 3, 2), }, /* FVco 1104.122880 */ + { .frequency = 282240000, .driver_data = PLLVAL(92, 1, 2), }, /* FVco 1128.960000 */ + { .frequency = 289578240, .driver_data = PLLVAL(163, 3, 2), }, /* FVco 1158.312960 */ + { .frequency = 294235200, .driver_data = PLLVAL(131, 2, 2), }, /* FVco 1176.940800 */ + { .frequency = 300200727, .driver_data = PLLVAL(187, 9, 1), }, /* FVco 600.401454 */ + { .frequency = 306358690, .driver_data = PLLVAL(191, 9, 1), }, /* FVco 612.717380 */ + { .frequency = 312076800, .driver_data = PLLVAL(121, 5, 1), }, /* FVco 624.153600 */ + { .frequency = 318366720, .driver_data = PLLVAL(86, 3, 1), }, /* FVco 636.733440 */ + { .frequency = 324172800, .driver_data = PLLVAL(126, 5, 1), }, /* FVco 648.345600 */ + { .frequency = 330220800, .driver_data = PLLVAL(109, 4, 1), }, /* FVco 660.441600 */ + { .frequency = 336268800, .driver_data = PLLVAL(131, 5, 1), }, /* FVco 672.537600 */ + { .frequency = 342074880, .driver_data = PLLVAL(93, 3, 1), }, /* FVco 684.149760 */ + { .frequency = 348096000, .driver_data = PLLVAL(177, 7, 1), }, /* FVco 696.192000 */ + { .frequency = 355622400, .driver_data = PLLVAL(118, 4, 1), }, /* FVco 711.244800 */ + { .frequency = 360460800, .driver_data = PLLVAL(141, 5, 1), }, /* FVco 720.921600 */ + { .frequency = 366206400, .driver_data = PLLVAL(165, 6, 1), }, /* FVco 732.412800 */ + { .frequency = 372556800, .driver_data = PLLVAL(124, 4, 1), }, /* FVco 745.113600 */ + { .frequency = 378201600, .driver_data = PLLVAL(126, 4, 1), }, /* FVco 756.403200 */ + { .frequency = 384652800, .driver_data = PLLVAL(151, 5, 1), }, /* FVco 769.305600 */ + { .frequency = 391608000, .driver_data = PLLVAL(177, 6, 1), }, /* FVco 783.216000 */ + { .frequency = 396264960, .driver_data = PLLVAL(109, 3, 1), }, /* FVco 792.529920 */ + { .frequency = 402192000, .driver_data = PLLVAL(87, 2, 1), }, /* FVco 804.384000 */ }; static int s3c2440_plls169344_add(struct device *dev, diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c index 7e105932c09d..5390c6bbbc02 100644 --- a/arch/arm/mach-shmobile/clock-sh7372.c +++ b/arch/arm/mach-shmobile/clock-sh7372.c @@ -142,15 +142,15 @@ static void pllc2_table_rebuild(struct clk *clk) /* Initialise PLLC2 frequency table */ for (i = 0; i < ARRAY_SIZE(pllc2_freq_table) - 2; i++) { pllc2_freq_table[i].frequency = clk->parent->rate * (i + 20) * 2; - pllc2_freq_table[i].index = i; + pllc2_freq_table[i].driver_data = i; } /* This is a special entry - switching PLL off makes it a repeater */ pllc2_freq_table[i].frequency = clk->parent->rate; - pllc2_freq_table[i].index = i; + pllc2_freq_table[i].driver_data = i; pllc2_freq_table[++i].frequency = CPUFREQ_TABLE_END; - pllc2_freq_table[i].index = i; + pllc2_freq_table[i].driver_data = i; } static unsigned long pllc2_recalc(struct clk *clk) diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 65c5ae6fa386..ef3a8da49b2d 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -28,7 +28,6 @@ config ARCH_TEGRA_2x_SOC select ARM_ERRATA_754327 if SMP select ARM_ERRATA_764369 if SMP select ARM_GIC - select CPU_FREQ_TABLE if CPU_FREQ select CPU_V7 select PINCTRL select PINCTRL_TEGRA20 @@ -46,7 +45,6 @@ config ARCH_TEGRA_3x_SOC select ARM_ERRATA_754322 select ARM_ERRATA_764369 if SMP select ARM_GIC - select CPU_FREQ_TABLE if CPU_FREQ select CPU_V7 select PINCTRL select PINCTRL_TEGRA30 @@ -63,7 +61,6 @@ config ARCH_TEGRA_114_SOC select HAVE_ARM_ARCH_TIMER select ARM_GIC select ARM_L1_CACHE_SHIFT_6 - select CPU_FREQ_TABLE if CPU_FREQ select CPU_V7 select PINCTRL select PINCTRL_TEGRA114 diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index ec5836b1e713..b25153e2ebaa 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -23,7 +23,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/irqchip.h> -#include <linux/clk/tegra.h> +#include <linux/clk-provider.h> #include <asm/hardware/cache-l2x0.h> @@ -60,7 +60,7 @@ u32 tegra_uart_config[4] = { #ifdef CONFIG_OF void __init tegra_dt_init_irq(void) { - tegra_clocks_init(); + of_clk_init(NULL); tegra_pmc_init(); tegra_init_irq(); irqchip_init(); diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index b6145ea51641..e6fb0239151b 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -76,13 +76,15 @@ void __init ux500_init_irq(void) } else if (cpu_is_u9540()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K - 1); ux500_pm_init(U8500_PRCMU_BASE, SZ_8K - 1); - u8500_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, + u9540_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, U8500_CLKRST3_BASE, U8500_CLKRST5_BASE, U8500_CLKRST6_BASE); } else if (cpu_is_u8540()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K + SZ_4K - 1); ux500_pm_init(U8500_PRCMU_BASE, SZ_8K + SZ_4K - 1); - u8540_clk_init(); + u8540_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, + U8500_CLKRST3_BASE, U8500_CLKRST5_BASE, + U8500_CLKRST6_BASE); } } diff --git a/arch/arm/plat-samsung/include/plat/cpu-freq-core.h b/arch/arm/plat-samsung/include/plat/cpu-freq-core.h index d7e17150028a..7231c8e4975e 100644 --- a/arch/arm/plat-samsung/include/plat/cpu-freq-core.h +++ b/arch/arm/plat-samsung/include/plat/cpu-freq-core.h @@ -285,7 +285,7 @@ static inline int s3c_cpufreq_addfreq(struct cpufreq_frequency_table *table, s3c_freq_dbg("%s: { %d = %u kHz }\n", __func__, index, freq); - table[index].index = index; + table[index].driver_data = index; table[index].frequency = freq; } diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 7c2f6685bf43..7f8759a8a92a 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1060,7 +1060,9 @@ struct platform_device *__init at32_add_device_usart(unsigned int id) void __init at32_setup_serial_console(unsigned int usart_id) { +#ifdef CONFIG_SERIAL_ATMEL atmel_default_console_device = at32_usarts[usart_id]; +#endif } /* -------------------------------------------------------------------- diff --git a/arch/c6x/include/asm/Kbuild b/arch/c6x/include/asm/Kbuild index 4258b088aa93..e49f918531ad 100644 --- a/arch/c6x/include/asm/Kbuild +++ b/arch/c6x/include/asm/Kbuild @@ -55,3 +55,4 @@ generic-y += types.h generic-y += ucontext.h generic-y += user.h generic-y += vga.h +generic-y += xor.h diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index 8769a9045a54..3201ddb8da6a 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -134,11 +134,13 @@ config SVINTO_SIM config ETRAXFS bool "ETRAX-FS-V32" + select CPU_FREQ_TABLE if CPU_FREQ help Support CRIS V32. config CRIS_MACH_ARTPEC3 bool "ARTPEC-3" + select CPU_FREQ_TABLE if CPU_FREQ help Support Axis ARTPEC-3. @@ -637,40 +639,10 @@ endchoice endmenu -source "drivers/base/Kconfig" - -# standard linux drivers -source "drivers/mtd/Kconfig" - -source "drivers/parport/Kconfig" - -source "drivers/pnp/Kconfig" - -source "drivers/block/Kconfig" - -source "drivers/ide/Kconfig" - -source "drivers/net/Kconfig" - -source "drivers/i2c/Kconfig" - -source "drivers/rtc/Kconfig" - -# -# input before char - char/joystick depends on it. As does USB. -# -source "drivers/input/Kconfig" - -source "drivers/char/Kconfig" +source "drivers/Kconfig" source "fs/Kconfig" -source "drivers/usb/Kconfig" - -source "drivers/uwb/Kconfig" - -source "drivers/staging/Kconfig" - source "arch/cris/Kconfig.debug" source "security/Kconfig" diff --git a/arch/cris/arch-v10/kernel/kgdb.c b/arch/cris/arch-v10/kernel/kgdb.c index 37e6d2c50b76..22d846bfc570 100644 --- a/arch/cris/arch-v10/kernel/kgdb.c +++ b/arch/cris/arch-v10/kernel/kgdb.c @@ -230,46 +230,6 @@ struct register_image unsigned int usp; /* 0x66 User mode stack pointer */ } registers; -/************** Prototypes for local library functions ***********************/ - -/* Copy of strcpy from libc. */ -static char *gdb_cris_strcpy (char *s1, const char *s2); - -/* Copy of strlen from libc. */ -static int gdb_cris_strlen (const char *s); - -/* Copy of memchr from libc. */ -static void *gdb_cris_memchr (const void *s, int c, int n); - -/* Copy of strtol from libc. Does only support base 16. */ -static int gdb_cris_strtol (const char *s, char **endptr, int base); - -/********************** Prototypes for local functions. **********************/ -/* Copy the content of a register image into another. The size n is - the size of the register image. Due to struct assignment generation of - memcpy in libc. */ -static void copy_registers (registers *dptr, registers *sptr, int n); - -/* Copy the stored registers from the stack. Put the register contents - of thread thread_id in the struct reg. */ -static void copy_registers_from_stack (int thread_id, registers *reg); - -/* Copy the registers to the stack. Put the register contents of thread - thread_id from struct reg to the stack. */ -static void copy_registers_to_stack (int thread_id, registers *reg); - -/* Write a value to a specified register regno in the register image - of the current thread. */ -static int write_register (int regno, char *val); - -/* Write a value to a specified register in the stack of a thread other - than the current thread. */ -static int write_stack_register(int thread_id, int regno, char *valptr); - -/* Read a value from a specified register in the register image. Returns the - status of the read operation. The register value is returned in valptr. */ -static int read_register (char regno, unsigned int *valptr); - /* Serial port, reads one character. ETRAX 100 specific. from debugport.c */ int getDebugChar (void); @@ -278,42 +238,6 @@ void putDebugChar (int val); void enableDebugIRQ (void); -/* Returns the integer equivalent of a hexadecimal character. */ -static int hex (char ch); - -/* Convert the memory, pointed to by mem into hexadecimal representation. - Put the result in buf, and return a pointer to the last character - in buf (null). */ -static char *mem2hex (char *buf, unsigned char *mem, int count); - -/* Convert the array, in hexadecimal representation, pointed to by buf into - binary representation. Put the result in mem, and return a pointer to - the character after the last byte written. */ -static unsigned char *hex2mem (unsigned char *mem, char *buf, int count); - -/* Put the content of the array, in binary representation, pointed to by buf - into memory pointed to by mem, and return a pointer to - the character after the last byte written. */ -static unsigned char *bin2mem (unsigned char *mem, unsigned char *buf, int count); - -/* Await the sequence $<data>#<checksum> and store <data> in the array buffer - returned. */ -static void getpacket (char *buffer); - -/* Send $<data>#<checksum> from the <data> in the array buffer. */ -static void putpacket (char *buffer); - -/* Build and send a response packet in order to inform the host the - stub is stopped. */ -static void stub_is_stopped (int sigval); - -/* All expected commands are sent from remote.c. Send a response according - to the description in remote.c. */ -static void handle_exception (int sigval); - -/* Performs a complete re-start from scratch. ETRAX specific. */ -static void kill_restart (void); - /******************** Prototypes for global functions. ***********************/ /* The string str is prepended with the GDB printout token and sent. */ @@ -336,10 +260,6 @@ extern unsigned char executing_task; /* The number of characters used for a 64 bit thread identifier. */ #define HEXCHARS_IN_THREAD_ID 16 -/* Avoid warning as the internal_stack is not used in the C-code. */ -#define USEDVAR(name) { if (name) { ; } } -#define USEDFUN(name) { void (*pf)(void) = (void *)name; USEDVAR(pf) } - /********************************** Packet I/O ******************************/ /* BUFMAX defines the maximum number of characters in inbound/outbound buffers */ @@ -405,7 +325,7 @@ static int register_size[] = /* Contains the register image of the executing thread in the assembler part of the code in order to avoid horrible addressing modes. */ -static registers reg; +registers cris_reg; /* FIXME: Should this be used? Delete otherwise. */ /* Contains the assumed consistency state of the register image. Uses the @@ -413,7 +333,7 @@ static registers reg; static int consistency_status = SUCCESS; /********************************** Handle exceptions ************************/ -/* The variable reg contains the register image associated with the +/* The variable cris_reg contains the register image associated with the current_thread_c variable. It is a complete register image created at entry. The reg_g contains a register image of a task where the general registers are taken from the stack and all special registers are taken @@ -421,18 +341,10 @@ static int consistency_status = SUCCESS; in order to provide access mainly for 'g', 'G' and 'P'. */ -/* Need two task id pointers in order to handle Hct and Hgt commands. */ -static int current_thread_c = 0; -static int current_thread_g = 0; - -/* Need two register images in order to handle Hct and Hgt commands. The - variable reg_g is in addition to reg above. */ -static registers reg_g; - /********************************** Breakpoint *******************************/ /* Use an internal stack in the breakpoint and interrupt response routines */ #define INTERNAL_STACK_SIZE 1024 -static char internal_stack[INTERNAL_STACK_SIZE]; +char internal_stack[INTERNAL_STACK_SIZE]; /* Due to the breakpoint return pointer, a state variable is needed to keep track of whether it is a static (compiled) or dynamic (gdb-invoked) @@ -500,164 +412,6 @@ gdb_cris_strtol (const char *s, char **endptr, int base) return x; } -/********************************* Register image ****************************/ -/* Copy the content of a register image into another. The size n is - the size of the register image. Due to struct assignment generation of - memcpy in libc. */ -static void -copy_registers (registers *dptr, registers *sptr, int n) -{ - unsigned char *dreg; - unsigned char *sreg; - - for (dreg = (unsigned char*)dptr, sreg = (unsigned char*)sptr; n > 0; n--) - *dreg++ = *sreg++; -} - -#ifdef PROCESS_SUPPORT -/* Copy the stored registers from the stack. Put the register contents - of thread thread_id in the struct reg. */ -static void -copy_registers_from_stack (int thread_id, registers *regptr) -{ - int j; - stack_registers *s = (stack_registers *)stack_list[thread_id]; - unsigned int *d = (unsigned int *)regptr; - - for (j = 13; j >= 0; j--) - *d++ = s->r[j]; - regptr->sp = (unsigned int)stack_list[thread_id]; - regptr->pc = s->pc; - regptr->dccr = s->dccr; - regptr->srp = s->srp; -} - -/* Copy the registers to the stack. Put the register contents of thread - thread_id from struct reg to the stack. */ -static void -copy_registers_to_stack (int thread_id, registers *regptr) -{ - int i; - stack_registers *d = (stack_registers *)stack_list[thread_id]; - unsigned int *s = (unsigned int *)regptr; - - for (i = 0; i < 14; i++) { - d->r[i] = *s++; - } - d->pc = regptr->pc; - d->dccr = regptr->dccr; - d->srp = regptr->srp; -} -#endif - -/* Write a value to a specified register in the register image of the current - thread. Returns status code SUCCESS, E02 or E05. */ -static int -write_register (int regno, char *val) -{ - int status = SUCCESS; - registers *current_reg = ® - - if (regno >= R0 && regno <= PC) { - /* 32-bit register with simple offset. */ - hex2mem ((unsigned char *)current_reg + regno * sizeof(unsigned int), - val, sizeof(unsigned int)); - } - else if (regno == P0 || regno == VR || regno == P4 || regno == P8) { - /* Do not support read-only registers. */ - status = E02; - } - else if (regno == CCR) { - /* 16 bit register with complex offset. (P4 is read-only, P6 is not implemented, - and P7 (MOF) is 32 bits in ETRAX 100LX. */ - hex2mem ((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short), - val, sizeof(unsigned short)); - } - else if (regno >= MOF && regno <= USP) { - /* 32 bit register with complex offset. (P8 has been taken care of.) */ - hex2mem ((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int), - val, sizeof(unsigned int)); - } - else { - /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ - status = E05; - } - return status; -} - -#ifdef PROCESS_SUPPORT -/* Write a value to a specified register in the stack of a thread other - than the current thread. Returns status code SUCCESS or E07. */ -static int -write_stack_register (int thread_id, int regno, char *valptr) -{ - int status = SUCCESS; - stack_registers *d = (stack_registers *)stack_list[thread_id]; - unsigned int val; - - hex2mem ((unsigned char *)&val, valptr, sizeof(unsigned int)); - if (regno >= R0 && regno < SP) { - d->r[regno] = val; - } - else if (regno == SP) { - stack_list[thread_id] = val; - } - else if (regno == PC) { - d->pc = val; - } - else if (regno == SRP) { - d->srp = val; - } - else if (regno == DCCR) { - d->dccr = val; - } - else { - /* Do not support registers in the current thread. */ - status = E07; - } - return status; -} -#endif - -/* Read a value from a specified register in the register image. Returns the - value in the register or -1 for non-implemented registers. - Should check consistency_status after a call which may be E05 after changes - in the implementation. */ -static int -read_register (char regno, unsigned int *valptr) -{ - registers *current_reg = ® - - if (regno >= R0 && regno <= PC) { - /* 32-bit register with simple offset. */ - *valptr = *(unsigned int *)((char *)current_reg + regno * sizeof(unsigned int)); - return SUCCESS; - } - else if (regno == P0 || regno == VR) { - /* 8 bit register with complex offset. */ - *valptr = (unsigned int)(*(unsigned char *) - ((char *)&(current_reg->p0) + (regno-P0) * sizeof(char))); - return SUCCESS; - } - else if (regno == P4 || regno == CCR) { - /* 16 bit register with complex offset. */ - *valptr = (unsigned int)(*(unsigned short *) - ((char *)&(current_reg->p4) + (regno-P4) * sizeof(unsigned short))); - return SUCCESS; - } - else if (regno >= MOF && regno <= USP) { - /* 32 bit register with complex offset. */ - *valptr = *(unsigned int *)((char *)&(current_reg->p8) - + (regno-P8) * sizeof(unsigned int)); - return SUCCESS; - } - else { - /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ - consistency_status = E05; - return E05; - } -} - /********************************** Packet I/O ******************************/ /* Returns the integer equivalent of a hexadecimal character. */ static int @@ -676,8 +430,6 @@ hex (char ch) Put the result in buf, and return a pointer to the last character in buf (null). */ -static int do_printk = 0; - static char * mem2hex(char *buf, unsigned char *mem, int count) { @@ -761,7 +513,7 @@ getpacket (char *buffer) xmitcsum = -1; count = 0; /* Read until a # or the end of the buffer is reached */ - while (count < BUFMAX) { + while (count < BUFMAX - 1) { ch = getDebugChar (); if (ch == '#') break; @@ -845,6 +597,81 @@ putDebugString (const unsigned char *str, int length) putpacket(remcomOutBuffer); } +/********************************* Register image ****************************/ +/* Write a value to a specified register in the register image of the current + thread. Returns status code SUCCESS, E02 or E05. */ +static int +write_register (int regno, char *val) +{ + int status = SUCCESS; + registers *current_reg = &cris_reg; + + if (regno >= R0 && regno <= PC) { + /* 32-bit register with simple offset. */ + hex2mem ((unsigned char *)current_reg + regno * sizeof(unsigned int), + val, sizeof(unsigned int)); + } + else if (regno == P0 || regno == VR || regno == P4 || regno == P8) { + /* Do not support read-only registers. */ + status = E02; + } + else if (regno == CCR) { + /* 16 bit register with complex offset. (P4 is read-only, P6 is not implemented, + and P7 (MOF) is 32 bits in ETRAX 100LX. */ + hex2mem ((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short), + val, sizeof(unsigned short)); + } + else if (regno >= MOF && regno <= USP) { + /* 32 bit register with complex offset. (P8 has been taken care of.) */ + hex2mem ((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int), + val, sizeof(unsigned int)); + } + else { + /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ + status = E05; + } + return status; +} + +/* Read a value from a specified register in the register image. Returns the + value in the register or -1 for non-implemented registers. + Should check consistency_status after a call which may be E05 after changes + in the implementation. */ +static int +read_register (char regno, unsigned int *valptr) +{ + registers *current_reg = &cris_reg; + + if (regno >= R0 && regno <= PC) { + /* 32-bit register with simple offset. */ + *valptr = *(unsigned int *)((char *)current_reg + regno * sizeof(unsigned int)); + return SUCCESS; + } + else if (regno == P0 || regno == VR) { + /* 8 bit register with complex offset. */ + *valptr = (unsigned int)(*(unsigned char *) + ((char *)&(current_reg->p0) + (regno-P0) * sizeof(char))); + return SUCCESS; + } + else if (regno == P4 || regno == CCR) { + /* 16 bit register with complex offset. */ + *valptr = (unsigned int)(*(unsigned short *) + ((char *)&(current_reg->p4) + (regno-P4) * sizeof(unsigned short))); + return SUCCESS; + } + else if (regno >= MOF && regno <= USP) { + /* 32 bit register with complex offset. */ + *valptr = *(unsigned int *)((char *)&(current_reg->p8) + + (regno-P8) * sizeof(unsigned int)); + return SUCCESS; + } + else { + /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ + consistency_status = E05; + return E05; + } +} + /********************************** Handle exceptions ************************/ /* Build and send a response packet in order to inform the host the stub is stopped. TAAn...:r...;n...:r...;n...:r...; @@ -891,26 +718,6 @@ stub_is_stopped(int sigval) } -#ifdef PROCESS_SUPPORT - /* Store the registers of the executing thread. Assume that both step, - continue, and register content requests are with respect to this - thread. The executing task is from the operating system scheduler. */ - - current_thread_c = executing_task; - current_thread_g = executing_task; - - /* A struct assignment translates into a libc memcpy call. Avoid - all libc functions in order to prevent recursive break points. */ - copy_registers (®_g, ®, sizeof(registers)); - - /* Store thread:r...; with the executing task TID. */ - gdb_cris_strcpy (&remcomOutBuffer[pos], "thread:"); - pos += gdb_cris_strlen ("thread:"); - remcomOutBuffer[pos++] = hex_asc_hi(executing_task); - remcomOutBuffer[pos++] = hex_asc_lo(executing_task); - gdb_cris_strcpy (&remcomOutBuffer[pos], ";"); -#endif - /* null-terminate and send it off */ *ptr = 0; @@ -918,16 +725,18 @@ stub_is_stopped(int sigval) putpacket (remcomOutBuffer); } +/* Performs a complete re-start from scratch. */ +static void +kill_restart (void) +{ + machine_restart(""); +} + /* All expected commands are sent from remote.c. Send a response according to the description in remote.c. */ -static void +void handle_exception (int sigval) { - /* Avoid warning of not used. */ - - USEDFUN(handle_exception); - USEDVAR(internal_stack[0]); - /* Send response. */ stub_is_stopped (sigval); @@ -943,19 +752,7 @@ handle_exception (int sigval) in a register are in the same order the machine uses. Failure: void. */ - { -#ifdef PROCESS_SUPPORT - /* Use the special register content in the executing thread. */ - copy_registers (®_g, ®, sizeof(registers)); - /* Replace the content available on the stack. */ - if (current_thread_g != executing_task) { - copy_registers_from_stack (current_thread_g, ®_g); - } - mem2hex ((unsigned char *)remcomOutBuffer, (unsigned char *)®_g, sizeof(registers)); -#else - mem2hex(remcomOutBuffer, (char *)®, sizeof(registers)); -#endif - } + mem2hex(remcomOutBuffer, (char *)&cris_reg, sizeof(registers)); break; case 'G': @@ -963,17 +760,7 @@ handle_exception (int sigval) Each byte of register data is described by two hex digits. Success: OK Failure: void. */ -#ifdef PROCESS_SUPPORT - hex2mem ((unsigned char *)®_g, &remcomInBuffer[1], sizeof(registers)); - if (current_thread_g == executing_task) { - copy_registers (®, ®_g, sizeof(registers)); - } - else { - copy_registers_to_stack(current_thread_g, ®_g); - } -#else - hex2mem((char *)®, &remcomInBuffer[1], sizeof(registers)); -#endif + hex2mem((char *)&cris_reg, &remcomInBuffer[1], sizeof(registers)); gdb_cris_strcpy (remcomOutBuffer, "OK"); break; @@ -989,12 +776,7 @@ handle_exception (int sigval) char *suffix; int regno = gdb_cris_strtol (&remcomInBuffer[1], &suffix, 16); int status; -#ifdef PROCESS_SUPPORT - if (current_thread_g != executing_task) - status = write_stack_register (current_thread_g, regno, suffix+1); - else -#endif - status = write_register (regno, suffix+1); + status = write_register (regno, suffix+1); switch (status) { case E02: @@ -1073,7 +855,7 @@ handle_exception (int sigval) Success: return to the executing thread. Failure: will never know. */ if (remcomInBuffer[1] != '\0') { - reg.pc = gdb_cris_strtol (&remcomInBuffer[1], 0, 16); + cris_reg.pc = gdb_cris_strtol (&remcomInBuffer[1], 0, 16); } enableDebugIRQ(); return; @@ -1129,119 +911,6 @@ handle_exception (int sigval) Not supported: E04 */ gdb_cris_strcpy (remcomOutBuffer, error_message[E04]); break; -#ifdef PROCESS_SUPPORT - - case 'T': - /* Thread alive. TXX - Is thread XX alive? - Success: OK, thread XX is alive. - Failure: E03, thread XX is dead. */ - { - int thread_id = (int)gdb_cris_strtol (&remcomInBuffer[1], 0, 16); - /* Cannot tell whether it is alive or not. */ - if (thread_id >= 0 && thread_id < number_of_tasks) - gdb_cris_strcpy (remcomOutBuffer, "OK"); - } - break; - - case 'H': - /* Set thread for subsequent operations: Hct - c = 'c' for thread used in step and continue; - t can be -1 for all threads. - c = 'g' for thread used in other operations. - t = 0 means pick any thread. - Success: OK - Failure: E01 */ - { - int thread_id = gdb_cris_strtol (&remcomInBuffer[2], 0, 16); - if (remcomInBuffer[1] == 'c') { - /* c = 'c' for thread used in step and continue */ - /* Do not change current_thread_c here. It would create a mess in - the scheduler. */ - gdb_cris_strcpy (remcomOutBuffer, "OK"); - } - else if (remcomInBuffer[1] == 'g') { - /* c = 'g' for thread used in other operations. - t = 0 means pick any thread. Impossible since the scheduler does - not allow that. */ - if (thread_id >= 0 && thread_id < number_of_tasks) { - current_thread_g = thread_id; - gdb_cris_strcpy (remcomOutBuffer, "OK"); - } - else { - /* Not expected - send an error message. */ - gdb_cris_strcpy (remcomOutBuffer, error_message[E01]); - } - } - else { - /* Not expected - send an error message. */ - gdb_cris_strcpy (remcomOutBuffer, error_message[E01]); - } - } - break; - - case 'q': - case 'Q': - /* Query of general interest. qXXXX - Set general value XXXX. QXXXX=yyyy */ - { - int pos; - int nextpos; - int thread_id; - - switch (remcomInBuffer[1]) { - case 'C': - /* Identify the remote current thread. */ - gdb_cris_strcpy (&remcomOutBuffer[0], "QC"); - remcomOutBuffer[2] = hex_asc_hi(current_thread_c); - remcomOutBuffer[3] = hex_asc_lo(current_thread_c); - remcomOutBuffer[4] = '\0'; - break; - case 'L': - gdb_cris_strcpy (&remcomOutBuffer[0], "QM"); - /* Reply with number of threads. */ - if (os_is_started()) { - remcomOutBuffer[2] = hex_asc_hi(number_of_tasks); - remcomOutBuffer[3] = hex_asc_lo(number_of_tasks); - } - else { - remcomOutBuffer[2] = hex_asc_hi(0); - remcomOutBuffer[3] = hex_asc_lo(1); - } - /* Done with the reply. */ - remcomOutBuffer[4] = hex_asc_lo(1); - pos = 5; - /* Expects the argument thread id. */ - for (; pos < (5 + HEXCHARS_IN_THREAD_ID); pos++) - remcomOutBuffer[pos] = remcomInBuffer[pos]; - /* Reply with the thread identifiers. */ - if (os_is_started()) { - /* Store the thread identifiers of all tasks. */ - for (thread_id = 0; thread_id < number_of_tasks; thread_id++) { - nextpos = pos + HEXCHARS_IN_THREAD_ID - 1; - for (; pos < nextpos; pos ++) - remcomOutBuffer[pos] = hex_asc_lo(0); - remcomOutBuffer[pos++] = hex_asc_lo(thread_id); - } - } - else { - /* Store the thread identifier of the boot task. */ - nextpos = pos + HEXCHARS_IN_THREAD_ID - 1; - for (; pos < nextpos; pos ++) - remcomOutBuffer[pos] = hex_asc_lo(0); - remcomOutBuffer[pos++] = hex_asc_lo(current_thread_c); - } - remcomOutBuffer[pos] = '\0'; - break; - default: - /* Not supported: "" */ - /* Request information about section offsets: qOffsets. */ - remcomOutBuffer[0] = 0; - break; - } - } - break; -#endif /* PROCESS_SUPPORT */ default: /* The stub should ignore other request and send an empty @@ -1254,13 +923,6 @@ handle_exception (int sigval) } } -/* Performs a complete re-start from scratch. */ -static void -kill_restart () -{ - machine_restart(""); -} - /********************************** Breakpoint *******************************/ /* The hook for both a static (compiled) and a dynamic breakpoint set by GDB. An internal stack is used by the stub. The register image of the caller is @@ -1270,93 +932,93 @@ kill_restart () void kgdb_handle_breakpoint(void); -asm (" - .global kgdb_handle_breakpoint -kgdb_handle_breakpoint: -;; -;; Response to the break-instruction -;; -;; Create a register image of the caller -;; - move $dccr,[reg+0x5E] ; Save the flags in DCCR before disable interrupts - di ; Disable interrupts - move.d $r0,[reg] ; Save R0 - move.d $r1,[reg+0x04] ; Save R1 - move.d $r2,[reg+0x08] ; Save R2 - move.d $r3,[reg+0x0C] ; Save R3 - move.d $r4,[reg+0x10] ; Save R4 - move.d $r5,[reg+0x14] ; Save R5 - move.d $r6,[reg+0x18] ; Save R6 - move.d $r7,[reg+0x1C] ; Save R7 - move.d $r8,[reg+0x20] ; Save R8 - move.d $r9,[reg+0x24] ; Save R9 - move.d $r10,[reg+0x28] ; Save R10 - move.d $r11,[reg+0x2C] ; Save R11 - move.d $r12,[reg+0x30] ; Save R12 - move.d $r13,[reg+0x34] ; Save R13 - move.d $sp,[reg+0x38] ; Save SP (R14) -;; Due to the old assembler-versions BRP might not be recognized - .word 0xE670 ; move brp,$r0 - subq 2,$r0 ; Set to address of previous instruction. - move.d $r0,[reg+0x3c] ; Save the address in PC (R15) - clear.b [reg+0x40] ; Clear P0 - move $vr,[reg+0x41] ; Save special register P1 - clear.w [reg+0x42] ; Clear P4 - move $ccr,[reg+0x44] ; Save special register CCR - move $mof,[reg+0x46] ; P7 - clear.d [reg+0x4A] ; Clear P8 - move $ibr,[reg+0x4E] ; P9, - move $irp,[reg+0x52] ; P10, - move $srp,[reg+0x56] ; P11, - move $dtp0,[reg+0x5A] ; P12, register BAR, assembler might not know BAR - ; P13, register DCCR already saved -;; Due to the old assembler-versions BRP might not be recognized - .word 0xE670 ; move brp,r0 -;; Static (compiled) breakpoints must return to the next instruction in order -;; to avoid infinite loops. Dynamic (gdb-invoked) must restore the instruction -;; in order to execute it when execution is continued. - test.b [is_dyn_brkp] ; Is this a dynamic breakpoint? - beq is_static ; No, a static breakpoint - nop - subq 2,$r0 ; rerun the instruction the break replaced -is_static: - moveq 1,$r1 - move.b $r1,[is_dyn_brkp] ; Set the state variable to dynamic breakpoint - move.d $r0,[reg+0x62] ; Save the return address in BRP - move $usp,[reg+0x66] ; USP -;; -;; Handle the communication -;; - move.d internal_stack+1020,$sp ; Use the internal stack which grows upward - moveq 5,$r10 ; SIGTRAP - jsr handle_exception ; Interactive routine -;; -;; Return to the caller -;; - move.d [reg],$r0 ; Restore R0 - move.d [reg+0x04],$r1 ; Restore R1 - move.d [reg+0x08],$r2 ; Restore R2 - move.d [reg+0x0C],$r3 ; Restore R3 - move.d [reg+0x10],$r4 ; Restore R4 - move.d [reg+0x14],$r5 ; Restore R5 - move.d [reg+0x18],$r6 ; Restore R6 - move.d [reg+0x1C],$r7 ; Restore R7 - move.d [reg+0x20],$r8 ; Restore R8 - move.d [reg+0x24],$r9 ; Restore R9 - move.d [reg+0x28],$r10 ; Restore R10 - move.d [reg+0x2C],$r11 ; Restore R11 - move.d [reg+0x30],$r12 ; Restore R12 - move.d [reg+0x34],$r13 ; Restore R13 -;; -;; FIXME: Which registers should be restored? -;; - move.d [reg+0x38],$sp ; Restore SP (R14) - move [reg+0x56],$srp ; Restore the subroutine return pointer. - move [reg+0x5E],$dccr ; Restore DCCR - move [reg+0x66],$usp ; Restore USP - jump [reg+0x62] ; A jump to the content in register BRP works. - nop ; -"); +asm ("\n" +" .global kgdb_handle_breakpoint\n" +"kgdb_handle_breakpoint:\n" +";;\n" +";; Response to the break-instruction\n" +";;\n" +";; Create a register image of the caller\n" +";;\n" +" move $dccr,[cris_reg+0x5E] ; Save the flags in DCCR before disable interrupts\n" +" di ; Disable interrupts\n" +" move.d $r0,[cris_reg] ; Save R0\n" +" move.d $r1,[cris_reg+0x04] ; Save R1\n" +" move.d $r2,[cris_reg+0x08] ; Save R2\n" +" move.d $r3,[cris_reg+0x0C] ; Save R3\n" +" move.d $r4,[cris_reg+0x10] ; Save R4\n" +" move.d $r5,[cris_reg+0x14] ; Save R5\n" +" move.d $r6,[cris_reg+0x18] ; Save R6\n" +" move.d $r7,[cris_reg+0x1C] ; Save R7\n" +" move.d $r8,[cris_reg+0x20] ; Save R8\n" +" move.d $r9,[cris_reg+0x24] ; Save R9\n" +" move.d $r10,[cris_reg+0x28] ; Save R10\n" +" move.d $r11,[cris_reg+0x2C] ; Save R11\n" +" move.d $r12,[cris_reg+0x30] ; Save R12\n" +" move.d $r13,[cris_reg+0x34] ; Save R13\n" +" move.d $sp,[cris_reg+0x38] ; Save SP (R14)\n" +";; Due to the old assembler-versions BRP might not be recognized\n" +" .word 0xE670 ; move brp,$r0\n" +" subq 2,$r0 ; Set to address of previous instruction.\n" +" move.d $r0,[cris_reg+0x3c] ; Save the address in PC (R15)\n" +" clear.b [cris_reg+0x40] ; Clear P0\n" +" move $vr,[cris_reg+0x41] ; Save special register P1\n" +" clear.w [cris_reg+0x42] ; Clear P4\n" +" move $ccr,[cris_reg+0x44] ; Save special register CCR\n" +" move $mof,[cris_reg+0x46] ; P7\n" +" clear.d [cris_reg+0x4A] ; Clear P8\n" +" move $ibr,[cris_reg+0x4E] ; P9,\n" +" move $irp,[cris_reg+0x52] ; P10,\n" +" move $srp,[cris_reg+0x56] ; P11,\n" +" move $dtp0,[cris_reg+0x5A] ; P12, register BAR, assembler might not know BAR\n" +" ; P13, register DCCR already saved\n" +";; Due to the old assembler-versions BRP might not be recognized\n" +" .word 0xE670 ; move brp,r0\n" +";; Static (compiled) breakpoints must return to the next instruction in order\n" +";; to avoid infinite loops. Dynamic (gdb-invoked) must restore the instruction\n" +";; in order to execute it when execution is continued.\n" +" test.b [is_dyn_brkp] ; Is this a dynamic breakpoint?\n" +" beq is_static ; No, a static breakpoint\n" +" nop\n" +" subq 2,$r0 ; rerun the instruction the break replaced\n" +"is_static:\n" +" moveq 1,$r1\n" +" move.b $r1,[is_dyn_brkp] ; Set the state variable to dynamic breakpoint\n" +" move.d $r0,[cris_reg+0x62] ; Save the return address in BRP\n" +" move $usp,[cris_reg+0x66] ; USP\n" +";;\n" +";; Handle the communication\n" +";;\n" +" move.d internal_stack+1020,$sp ; Use the internal stack which grows upward\n" +" moveq 5,$r10 ; SIGTRAP\n" +" jsr handle_exception ; Interactive routine\n" +";;\n" +";; Return to the caller\n" +";;\n" +" move.d [cris_reg],$r0 ; Restore R0\n" +" move.d [cris_reg+0x04],$r1 ; Restore R1\n" +" move.d [cris_reg+0x08],$r2 ; Restore R2\n" +" move.d [cris_reg+0x0C],$r3 ; Restore R3\n" +" move.d [cris_reg+0x10],$r4 ; Restore R4\n" +" move.d [cris_reg+0x14],$r5 ; Restore R5\n" +" move.d [cris_reg+0x18],$r6 ; Restore R6\n" +" move.d [cris_reg+0x1C],$r7 ; Restore R7\n" +" move.d [cris_reg+0x20],$r8 ; Restore R8\n" +" move.d [cris_reg+0x24],$r9 ; Restore R9\n" +" move.d [cris_reg+0x28],$r10 ; Restore R10\n" +" move.d [cris_reg+0x2C],$r11 ; Restore R11\n" +" move.d [cris_reg+0x30],$r12 ; Restore R12\n" +" move.d [cris_reg+0x34],$r13 ; Restore R13\n" +";;\n" +";; FIXME: Which registers should be restored?\n" +";;\n" +" move.d [cris_reg+0x38],$sp ; Restore SP (R14)\n" +" move [cris_reg+0x56],$srp ; Restore the subroutine return pointer.\n" +" move [cris_reg+0x5E],$dccr ; Restore DCCR\n" +" move [cris_reg+0x66],$usp ; Restore USP\n" +" jump [cris_reg+0x62] ; A jump to the content in register BRP works.\n" +" nop ;\n" +"\n"); /* The hook for an interrupt generated by GDB. An internal stack is used by the stub. The register image of the caller is stored in the structure @@ -1367,94 +1029,94 @@ is_static: void kgdb_handle_serial(void); -asm (" - .global kgdb_handle_serial -kgdb_handle_serial: -;; -;; Response to a serial interrupt -;; - - move $dccr,[reg+0x5E] ; Save the flags in DCCR - di ; Disable interrupts - move.d $r0,[reg] ; Save R0 - move.d $r1,[reg+0x04] ; Save R1 - move.d $r2,[reg+0x08] ; Save R2 - move.d $r3,[reg+0x0C] ; Save R3 - move.d $r4,[reg+0x10] ; Save R4 - move.d $r5,[reg+0x14] ; Save R5 - move.d $r6,[reg+0x18] ; Save R6 - move.d $r7,[reg+0x1C] ; Save R7 - move.d $r8,[reg+0x20] ; Save R8 - move.d $r9,[reg+0x24] ; Save R9 - move.d $r10,[reg+0x28] ; Save R10 - move.d $r11,[reg+0x2C] ; Save R11 - move.d $r12,[reg+0x30] ; Save R12 - move.d $r13,[reg+0x34] ; Save R13 - move.d $sp,[reg+0x38] ; Save SP (R14) - move $irp,[reg+0x3c] ; Save the address in PC (R15) - clear.b [reg+0x40] ; Clear P0 - move $vr,[reg+0x41] ; Save special register P1, - clear.w [reg+0x42] ; Clear P4 - move $ccr,[reg+0x44] ; Save special register CCR - move $mof,[reg+0x46] ; P7 - clear.d [reg+0x4A] ; Clear P8 - move $ibr,[reg+0x4E] ; P9, - move $irp,[reg+0x52] ; P10, - move $srp,[reg+0x56] ; P11, - move $dtp0,[reg+0x5A] ; P12, register BAR, assembler might not know BAR - ; P13, register DCCR already saved -;; Due to the old assembler-versions BRP might not be recognized - .word 0xE670 ; move brp,r0 - move.d $r0,[reg+0x62] ; Save the return address in BRP - move $usp,[reg+0x66] ; USP - -;; get the serial character (from debugport.c) and check if it is a ctrl-c - - jsr getDebugChar - cmp.b 3, $r10 - bne goback - nop - - move.d [reg+0x5E], $r10 ; Get DCCR - btstq 8, $r10 ; Test the U-flag. - bmi goback - nop - -;; -;; Handle the communication -;; - move.d internal_stack+1020,$sp ; Use the internal stack - moveq 2,$r10 ; SIGINT - jsr handle_exception ; Interactive routine - -goback: -;; -;; Return to the caller -;; - move.d [reg],$r0 ; Restore R0 - move.d [reg+0x04],$r1 ; Restore R1 - move.d [reg+0x08],$r2 ; Restore R2 - move.d [reg+0x0C],$r3 ; Restore R3 - move.d [reg+0x10],$r4 ; Restore R4 - move.d [reg+0x14],$r5 ; Restore R5 - move.d [reg+0x18],$r6 ; Restore R6 - move.d [reg+0x1C],$r7 ; Restore R7 - move.d [reg+0x20],$r8 ; Restore R8 - move.d [reg+0x24],$r9 ; Restore R9 - move.d [reg+0x28],$r10 ; Restore R10 - move.d [reg+0x2C],$r11 ; Restore R11 - move.d [reg+0x30],$r12 ; Restore R12 - move.d [reg+0x34],$r13 ; Restore R13 -;; -;; FIXME: Which registers should be restored? -;; - move.d [reg+0x38],$sp ; Restore SP (R14) - move [reg+0x56],$srp ; Restore the subroutine return pointer. - move [reg+0x5E],$dccr ; Restore DCCR - move [reg+0x66],$usp ; Restore USP - reti ; Return from the interrupt routine - nop -"); +asm ("\n" +" .global kgdb_handle_serial\n" +"kgdb_handle_serial:\n" +";;\n" +";; Response to a serial interrupt\n" +";;\n" +"\n" +" move $dccr,[cris_reg+0x5E] ; Save the flags in DCCR\n" +" di ; Disable interrupts\n" +" move.d $r0,[cris_reg] ; Save R0\n" +" move.d $r1,[cris_reg+0x04] ; Save R1\n" +" move.d $r2,[cris_reg+0x08] ; Save R2\n" +" move.d $r3,[cris_reg+0x0C] ; Save R3\n" +" move.d $r4,[cris_reg+0x10] ; Save R4\n" +" move.d $r5,[cris_reg+0x14] ; Save R5\n" +" move.d $r6,[cris_reg+0x18] ; Save R6\n" +" move.d $r7,[cris_reg+0x1C] ; Save R7\n" +" move.d $r8,[cris_reg+0x20] ; Save R8\n" +" move.d $r9,[cris_reg+0x24] ; Save R9\n" +" move.d $r10,[cris_reg+0x28] ; Save R10\n" +" move.d $r11,[cris_reg+0x2C] ; Save R11\n" +" move.d $r12,[cris_reg+0x30] ; Save R12\n" +" move.d $r13,[cris_reg+0x34] ; Save R13\n" +" move.d $sp,[cris_reg+0x38] ; Save SP (R14)\n" +" move $irp,[cris_reg+0x3c] ; Save the address in PC (R15)\n" +" clear.b [cris_reg+0x40] ; Clear P0\n" +" move $vr,[cris_reg+0x41] ; Save special register P1,\n" +" clear.w [cris_reg+0x42] ; Clear P4\n" +" move $ccr,[cris_reg+0x44] ; Save special register CCR\n" +" move $mof,[cris_reg+0x46] ; P7\n" +" clear.d [cris_reg+0x4A] ; Clear P8\n" +" move $ibr,[cris_reg+0x4E] ; P9,\n" +" move $irp,[cris_reg+0x52] ; P10,\n" +" move $srp,[cris_reg+0x56] ; P11,\n" +" move $dtp0,[cris_reg+0x5A] ; P12, register BAR, assembler might not know BAR\n" +" ; P13, register DCCR already saved\n" +";; Due to the old assembler-versions BRP might not be recognized\n" +" .word 0xE670 ; move brp,r0\n" +" move.d $r0,[cris_reg+0x62] ; Save the return address in BRP\n" +" move $usp,[cris_reg+0x66] ; USP\n" +"\n" +";; get the serial character (from debugport.c) and check if it is a ctrl-c\n" +"\n" +" jsr getDebugChar\n" +" cmp.b 3, $r10\n" +" bne goback\n" +" nop\n" +"\n" +" move.d [cris_reg+0x5E], $r10 ; Get DCCR\n" +" btstq 8, $r10 ; Test the U-flag.\n" +" bmi goback\n" +" nop\n" +"\n" +";;\n" +";; Handle the communication\n" +";;\n" +" move.d internal_stack+1020,$sp ; Use the internal stack\n" +" moveq 2,$r10 ; SIGINT\n" +" jsr handle_exception ; Interactive routine\n" +"\n" +"goback:\n" +";;\n" +";; Return to the caller\n" +";;\n" +" move.d [cris_reg],$r0 ; Restore R0\n" +" move.d [cris_reg+0x04],$r1 ; Restore R1\n" +" move.d [cris_reg+0x08],$r2 ; Restore R2\n" +" move.d [cris_reg+0x0C],$r3 ; Restore R3\n" +" move.d [cris_reg+0x10],$r4 ; Restore R4\n" +" move.d [cris_reg+0x14],$r5 ; Restore R5\n" +" move.d [cris_reg+0x18],$r6 ; Restore R6\n" +" move.d [cris_reg+0x1C],$r7 ; Restore R7\n" +" move.d [cris_reg+0x20],$r8 ; Restore R8\n" +" move.d [cris_reg+0x24],$r9 ; Restore R9\n" +" move.d [cris_reg+0x28],$r10 ; Restore R10\n" +" move.d [cris_reg+0x2C],$r11 ; Restore R11\n" +" move.d [cris_reg+0x30],$r12 ; Restore R12\n" +" move.d [cris_reg+0x34],$r13 ; Restore R13\n" +";;\n" +";; FIXME: Which registers should be restored?\n" +";;\n" +" move.d [cris_reg+0x38],$sp ; Restore SP (R14)\n" +" move [cris_reg+0x56],$srp ; Restore the subroutine return pointer.\n" +" move [cris_reg+0x5E],$dccr ; Restore DCCR\n" +" move [cris_reg+0x66],$usp ; Restore USP\n" +" reti ; Return from the interrupt routine\n" +" nop\n" +"\n"); /* Use this static breakpoint in the start-up only. */ diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig index ab725edbc680..acff3df8c43f 100644 --- a/arch/cris/arch-v32/drivers/Kconfig +++ b/arch/cris/arch-v32/drivers/Kconfig @@ -640,8 +640,6 @@ config ETRAX_STREAMCOPROC This option enables a driver for the stream co-processor for cryptographic operations. -source drivers/mmc/Kconfig - config ETRAX_MMC_IOP tristate "MMC/SD host driver using IO-processor" depends on ETRAX_ARCH_V32 && MMC @@ -833,9 +831,4 @@ config ETRAX_SPI_MMC_WP_GPIO_PIN The pin to use for the SD/MMC write-protect signal for a memory card. If defined as " " (space), the card is considered writable. -# Avoid choices causing non-working configs by conditionalizing the inclusion. -if ETRAX_SPI_MMC -source drivers/spi/Kconfig -endif - endif diff --git a/arch/cris/include/asm/Kbuild b/arch/cris/include/asm/Kbuild index f1e79edc9dd2..c8325455520e 100644 --- a/arch/cris/include/asm/Kbuild +++ b/arch/cris/include/asm/Kbuild @@ -5,5 +5,9 @@ header-y += arch-v32/ generic-y += clkdev.h generic-y += exec.h +generic-y += kvm_para.h +generic-y += linkage.h generic-y += module.h generic-y += trace_clock.h +generic-y += vga.h +generic-y += xor.h diff --git a/arch/cris/include/asm/io.h b/arch/cris/include/asm/io.h index ac12ae2b9286..5d3047e5563b 100644 --- a/arch/cris/include/asm/io.h +++ b/arch/cris/include/asm/io.h @@ -167,6 +167,9 @@ static inline void outsl(unsigned int port, const void *addr, cris_iops->write_io(port, (void *)addr, 4, count); } +#define inb_p(port) inb(port) +#define outb_p(val, port) outb((val), (port)) + /* * Convert a physical pointer to a virtual kernel pointer for /dev/mem * access diff --git a/arch/cris/include/asm/linkage.h b/arch/cris/include/asm/linkage.h deleted file mode 100644 index 291c2d01c44f..000000000000 --- a/arch/cris/include/asm/linkage.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __ASM_LINKAGE_H -#define __ASM_LINKAGE_H - -/* Nothing to see here... */ - -#endif diff --git a/arch/frv/kernel/head.S b/arch/frv/kernel/head.S index e9a8cc63ac94..a7d0bea9c036 100644 --- a/arch/frv/kernel/head.S +++ b/arch/frv/kernel/head.S @@ -479,11 +479,6 @@ __head_mmu_enabled: LEDS 0x000c - # initialise the processor and the peripherals - #call SYMBOL_NAME(processor_init) - #call SYMBOL_NAME(unit_init) - #LEDS 0x0aff - sethi.p #0xe5e5,gr3 setlo #0xe5e5,gr3 or.p gr3,gr0,gr4 diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 303e4f9a79d1..3d6759ee382f 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -94,126 +94,10 @@ endmenu source "net/Kconfig" -source "drivers/base/Kconfig" - -source "drivers/mtd/Kconfig" - -source "drivers/block/Kconfig" - -source "drivers/ide/Kconfig" +source "drivers/Kconfig" source "arch/h8300/Kconfig.ide" -source "drivers/net/Kconfig" - -# -# input - input/joystick depends on it. As does USB. -# -source "drivers/input/Kconfig" - -menu "Character devices" - -config VT - bool "Virtual terminal" - ---help--- - If you say Y here, you will get support for terminal devices with - display and keyboard devices. These are called "virtual" because you - can run several virtual terminals (also called virtual consoles) on - one physical terminal. This is rather useful, for example one - virtual terminal can collect system messages and warnings, another - one can be used for a text-mode user session, and a third could run - an X session, all in parallel. Switching between virtual terminals - is done with certain key combinations, usually Alt-<function key>. - - The setterm command ("man setterm") can be used to change the - properties (such as colors or beeping) of a virtual terminal. The - man page console_codes(4) ("man console_codes") contains the special - character sequences that can be used to change those properties - directly. The fonts used on virtual terminals can be changed with - the setfont ("man setfont") command and the key bindings are defined - with the loadkeys ("man loadkeys") command. - - You need at least one virtual terminal device in order to make use - of your keyboard and monitor. Therefore, only people configuring an - embedded system would want to say N here in order to save some - memory; the only way to log into such a system is then via a serial - or network connection. - - If unsure, say Y, or else you won't be able to do much with your new - shiny Linux system :-) - -config VT_CONSOLE - bool "Support for console on virtual terminal" - depends on VT - ---help--- - The system console is the device which receives all kernel messages - and warnings and which allows logins in single user mode. If you - answer Y here, a virtual terminal (the device used to interact with - a physical terminal) can be used as system console. This is the most - common mode of operations, so you should say Y here unless you want - the kernel messages be output only to a serial port (in which case - you should say Y to "Console on serial port", below). - - If you do say Y here, by default the currently visible virtual - terminal (/dev/tty0) will be used as system console. You can change - that with a kernel command line option such as "console=tty3" which - would use the third virtual terminal as system console. (Try "man - bootparam" or see the documentation of your boot loader (lilo or - loadlin) about how to pass options to the kernel at boot time.) - - If unsure, say Y. - -config HW_CONSOLE - bool - depends on VT - default y - -comment "Unix98 PTY support" - -config UNIX98_PTYS - bool "Unix98 PTY support" - ---help--- - A pseudo terminal (PTY) is a software device consisting of two - halves: a master and a slave. The slave device behaves identical to - a physical terminal; the master device is used by a process to - read data from and write data to the slave, thereby emulating a - terminal. Typical programs for the master side are telnet servers - and xterms. - - Linux has traditionally used the BSD-like names /dev/ptyxx for - masters and /dev/ttyxx for slaves of pseudo terminals. This scheme - has a number of problems. The GNU C library glibc 2.1 and later, - however, supports the Unix98 naming standard: in order to acquire a - pseudo terminal, a process opens /dev/ptmx; the number of the pseudo - terminal is then made available to the process and the pseudo - terminal slave can be accessed as /dev/pts/<number>. What was - traditionally /dev/ttyp2 will then be /dev/pts/2, for example. - - The entries in /dev/pts/ are created on the fly by a virtual - file system; therefore, if you say Y here you should say Y to - "/dev/pts file system for Unix98 PTYs" as well. - - If you want to say Y here, you need to have the C library glibc 2.1 - or later (equal to libc-6.1, check with "ls -l /lib/libc.so.*"). - Read the instructions in <file:Documentation/Changes> pertaining to - pseudo terminals. It's safe to say N. - -source "drivers/char/pcmcia/Kconfig" - -source "drivers/tty/serial/Kconfig" - -source "drivers/i2c/Kconfig" - -source "drivers/hwmon/Kconfig" - -source "drivers/usb/Kconfig" - -source "drivers/uwb/Kconfig" - -endmenu - -source "drivers/staging/Kconfig" - source "fs/Kconfig" source "arch/h8300/Kconfig.debug" diff --git a/arch/h8300/Kconfig.cpu b/arch/h8300/Kconfig.cpu index 321f3922728b..cdee771460ed 100644 --- a/arch/h8300/Kconfig.cpu +++ b/arch/h8300/Kconfig.cpu @@ -64,6 +64,7 @@ choice config H83002 bool "H8/3001,3002,3003" + depends on BROKEN select CPU_H8300H config H83007 @@ -72,6 +73,7 @@ config H83007 config H83048 bool "H8/3044,3045,3046,3047,3048,3052" + depends on BROKEN select CPU_H8300H config H83068 @@ -155,10 +157,12 @@ config H8300_TIMER16_CH config H8300_ITU_CH int "ITU channel" depends on H8300_ITU + range 0 4 config H8300_TPU_CH int "TPU channel" depends on H8300_TPU + range 0 4 source "kernel/Kconfig.preempt" diff --git a/arch/h8300/boot/compressed/Makefile b/arch/h8300/boot/compressed/Makefile index 6745cb1ffb4f..a6c98fe3bbc3 100644 --- a/arch/h8300/boot/compressed/Makefile +++ b/arch/h8300/boot/compressed/Makefile @@ -16,7 +16,7 @@ OBJECTS = $(obj)/head.o $(obj)/misc.o # CONFIG_MEMORY_START ?= 0x00400000 CONFIG_BOOT_LINK_OFFSET ?= 0x00140000 -IMAGE_OFFSET := $(shell printf "0x%08x" $$[$(CONFIG_MEMORY_START)+$(CONFIG_BOOT_LINK_OFFSET)]) +IMAGE_OFFSET := $(shell printf "0x%08x" $$(($(CONFIG_MEMORY_START)+$(CONFIG_BOOT_LINK_OFFSET)))) LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -estartup $(obj)/vmlinux.lds diff --git a/arch/h8300/include/asm/Kbuild b/arch/h8300/include/asm/Kbuild index 995eb47e01bb..8ada3cf0c98d 100644 --- a/arch/h8300/include/asm/Kbuild +++ b/arch/h8300/include/asm/Kbuild @@ -1,6 +1,8 @@ generic-y += clkdev.h generic-y += exec.h +generic-y += linkage.h generic-y += mmu.h generic-y += module.h generic-y += trace_clock.h +generic-y += xor.h diff --git a/arch/h8300/include/asm/barrier.h b/arch/h8300/include/asm/barrier.h index c7283c343c55..9e0aa9fc195d 100644 --- a/arch/h8300/include/asm/barrier.h +++ b/arch/h8300/include/asm/barrier.h @@ -12,6 +12,8 @@ #define wmb() asm volatile ("" : : :"memory") #define set_mb(var, value) do { xchg(&var, value); } while (0) +#define read_barrier_depends() do { } while (0) + #ifdef CONFIG_SMP #define smp_mb() mb() #define smp_rmb() rmb() diff --git a/arch/h8300/include/asm/linkage.h b/arch/h8300/include/asm/linkage.h deleted file mode 100644 index 1d81604fb0ad..000000000000 --- a/arch/h8300/include/asm/linkage.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _H8300_LINKAGE_H -#define _H8300_LINKAGE_H - -#undef SYMBOL_NAME_LABEL -#define SYMBOL_NAME_LABEL(_name_) _##_name_##: -#endif diff --git a/arch/h8300/include/asm/tlb.h b/arch/h8300/include/asm/tlb.h index 3dea80ad9e6f..7f0743051ad5 100644 --- a/arch/h8300/include/asm/tlb.h +++ b/arch/h8300/include/asm/tlb.h @@ -1,16 +1,3 @@ -/* - include/asm-h8300/tlb.h -*/ - -#ifndef __H8300_TLB_H__ -#define __H8300_TLB_H__ - -#define tlb_flush(tlb) do { } while(0) - -/* - include/asm-h8300/tlb.h -*/ - #ifndef __H8300_TLB_H__ #define __H8300_TLB_H__ @@ -19,5 +6,3 @@ #include <asm-generic/tlb.h> #endif - -#endif diff --git a/arch/h8300/kernel/entry.S b/arch/h8300/kernel/entry.S index 617a6878787f..94bd30f11df6 100644 --- a/arch/h8300/kernel/entry.S +++ b/arch/h8300/kernel/entry.S @@ -87,13 +87,13 @@ INTERRUPTS = 128 bne 5f /* user mode */ - mov.l sp,@SYMBOL_NAME(sw_usp) + mov.l sp,@_sw_usp mov.l @sp,er0 /* restore saved er0 */ orc #0x10,ccr /* switch kernel stack */ - mov.l @SYMBOL_NAME(sw_ksp),sp + mov.l @_sw_ksp,sp sub.l #(LRET-LORIG),sp /* allocate LORIG - LRET */ SAVEREGS - mov.l @SYMBOL_NAME(sw_usp),er0 + mov.l @_sw_usp,er0 mov.l @(USERRET:16,er0),er1 /* copy the RET addr */ mov.l er1,@(LRET-LER3:16,sp) SAVEEXR @@ -128,7 +128,7 @@ INTERRUPTS = 128 bne 7f orc #0x80,ccr - mov.l @SYMBOL_NAME(sw_usp),er0 + mov.l @_sw_usp,er0 mov.l @(LER0-LER1:16,sp),er1 /* restore ER0 */ mov.l er1,@er0 RESTOREEXR @@ -141,7 +141,7 @@ INTERRUPTS = 128 mov.l @sp+,er1 add.l #(LRET-LER1),sp /* remove LORIG - LRET */ - mov.l sp,@SYMBOL_NAME(sw_ksp) + mov.l sp,@_sw_ksp andc #0xef,ccr /* switch to user mode */ mov.l er0,sp bra 8f @@ -155,20 +155,20 @@ INTERRUPTS = 128 rte .endm -.globl SYMBOL_NAME(system_call) -.globl SYMBOL_NAME(ret_from_exception) -.globl SYMBOL_NAME(ret_from_fork) -.globl SYMBOL_NAME(ret_from_kernel_thread) -.globl SYMBOL_NAME(ret_from_interrupt) -.globl SYMBOL_NAME(interrupt_redirect_table) -.globl SYMBOL_NAME(sw_ksp),SYMBOL_NAME(sw_usp) -.globl SYMBOL_NAME(resume) -.globl SYMBOL_NAME(interrupt_entry) -.globl SYMBOL_NAME(trace_break) +.globl _system_call +.globl _ret_from_exception +.globl _ret_from_fork +.globl _ret_from_kernel_thread +.globl _ret_from_interrupt +.globl _interrupt_redirect_table +.globl _sw_ksp,_sw_usp +.globl _resume +.globl _interrupt_entry +.globl _trace_break #if defined(CONFIG_ROMKERNEL) .section .int_redirect,"ax" -SYMBOL_NAME_LABEL(interrupt_redirect_table) +_interrupt_redirect_table: #if defined(CONFIG_CPU_H8300H) .rept 7 .long 0 @@ -178,54 +178,54 @@ SYMBOL_NAME_LABEL(interrupt_redirect_table) .rept 5 .long 0 .endr - jmp @SYMBOL_NAME(trace_break) + jmp @_trace_break .long 0 #endif - jsr @SYMBOL_NAME(interrupt_entry) /* NMI */ - jmp @SYMBOL_NAME(system_call) /* TRAPA #0 (System call) */ + jsr @_interrupt_entry /* NMI */ + jmp @_system_call /* TRAPA #0 (System call) */ .long 0 .long 0 - jmp @SYMBOL_NAME(trace_break) /* TRAPA #3 (breakpoint) */ + jmp @_trace_break /* TRAPA #3 (breakpoint) */ .rept INTERRUPTS-12 - jsr @SYMBOL_NAME(interrupt_entry) + jsr @_interrupt_entry .endr #endif #if defined(CONFIG_RAMKERNEL) -.globl SYMBOL_NAME(interrupt_redirect_table) +.globl _interrupt_redirect_table .section .bss -SYMBOL_NAME_LABEL(interrupt_redirect_table) +_interrupt_redirect_table: .space 4 #endif .section .text .align 2 -SYMBOL_NAME_LABEL(interrupt_entry) +_interrupt_entry: SAVE_ALL mov.l sp,er0 add.l #LVEC,er0 btst #4,r1l bne 1f /* user LVEC */ - mov.l @SYMBOL_NAME(sw_usp),er0 + mov.l @_sw_usp,er0 adds #4,er0 1: mov.l @er0,er0 /* LVEC address */ #if defined(CONFIG_ROMKERNEL) - sub.l #SYMBOL_NAME(interrupt_redirect_table),er0 + sub.l #_interrupt_redirect_table,er0 #endif #if defined(CONFIG_RAMKERNEL) - mov.l @SYMBOL_NAME(interrupt_redirect_table),er1 + mov.l @_interrupt_redirect_table,er1 sub.l er1,er0 #endif SHLR2 er0 dec.l #1,er0 mov.l sp,er1 subs #4,er1 /* adjust ret_pc */ - jsr @SYMBOL_NAME(do_IRQ) - jmp @SYMBOL_NAME(ret_from_interrupt) + jsr @_do_IRQ + jmp @_ret_from_interrupt -SYMBOL_NAME_LABEL(system_call) +_system_call: subs #4,sp /* dummy LVEC */ SAVE_ALL andc #0x7f,ccr @@ -233,21 +233,21 @@ SYMBOL_NAME_LABEL(system_call) /* save top of frame */ mov.l sp,er0 - jsr @SYMBOL_NAME(set_esp0) + jsr @_set_esp0 mov.l sp,er2 and.w #0xe000,r2 mov.b @((TI_FLAGS+3-(TIF_SYSCALL_TRACE >> 3)):16,er2),r2l btst #(TIF_SYSCALL_TRACE & 7),r2l beq 1f - jsr @SYMBOL_NAME(do_syscall_trace) + jsr @_do_syscall_trace 1: cmp.l #NR_syscalls,er4 bcc badsys SHLL2 er4 - mov.l #SYMBOL_NAME(sys_call_table),er0 + mov.l #_sys_call_table,er0 add.l er4,er0 mov.l @er0,er4 - beq SYMBOL_NAME(ret_from_exception):16 + beq _ret_from_exception:16 mov.l @(LER1:16,sp),er0 mov.l @(LER2:16,sp),er1 mov.l @(LER3:16,sp),er2 @@ -258,10 +258,10 @@ SYMBOL_NAME_LABEL(system_call) mov.b @((TI_FLAGS+3-(TIF_SYSCALL_TRACE >> 3)):16,er2),r2l btst #(TIF_SYSCALL_TRACE & 7),r2l beq 2f - jsr @SYMBOL_NAME(do_syscall_trace) + jsr @_do_syscall_trace 2: #if defined(CONFIG_SYSCALL_PRINT) - jsr @SYMBOL_NAME(syscall_print) + jsr @_syscall_print #endif orc #0x80,ccr bra resume_userspace @@ -275,11 +275,11 @@ badsys: #define resume_kernel restore_all #endif -SYMBOL_NAME_LABEL(ret_from_exception) +_ret_from_exception: #if defined(CONFIG_PREEMPT) orc #0x80,ccr #endif -SYMBOL_NAME_LABEL(ret_from_interrupt) +_ret_from_interrupt: mov.b @(LCCR+1:16,sp),r0l btst #4,r0l bne resume_kernel:8 /* return from kernel */ @@ -296,12 +296,12 @@ work_pending: /* work notifysig */ mov.l sp,er0 subs #4,er0 /* er0: pt_regs */ - jsr @SYMBOL_NAME(do_notify_resume) + jsr @_do_notify_resume bra restore_all:8 work_resched: mov.l sp,er0 - jsr @SYMBOL_NAME(set_esp0) - jsr @SYMBOL_NAME(schedule) + jsr @_set_esp0 + jsr @_schedule bra resume_userspace:8 restore_all: RESTORE_ALL /* Does RTE */ @@ -320,26 +320,26 @@ need_resched: mov.l er0,@(TI_PRE_COUNT:16,er4) andc #0x7f,ccr mov.l sp,er0 - jsr @SYMBOL_NAME(set_esp0) - jsr @SYMBOL_NAME(schedule) + jsr @_set_esp0 + jsr @_schedule orc #0x80,ccr bra need_resched:8 #endif -SYMBOL_NAME_LABEL(ret_from_fork) +_ret_from_fork: mov.l er2,er0 - jsr @SYMBOL_NAME(schedule_tail) - jmp @SYMBOL_NAME(ret_from_exception) + jsr @_schedule_tail + jmp @_ret_from_exception -SYMBOL_NAME_LABEL(ret_from_kernel_thread) +_ret_from_kernel_thread: mov.l er2,er0 - jsr @SYMBOL_NAME(schedule_tail) + jsr @_schedule_tail mov.l @(LER4:16,sp),er0 mov.l @(LER5:16,sp),er1 jsr @er1 - jmp @SYMBOL_NAME(ret_from_exception) + jmp @_ret_from_exception -SYMBOL_NAME_LABEL(resume) +_resume: /* * Beware - when entering resume, offset of tss is in d1, * prev (the current task) is in a0, next (the new task) @@ -355,7 +355,7 @@ SYMBOL_NAME_LABEL(resume) /* disable interrupts */ orc #0x80,ccr - mov.l @SYMBOL_NAME(sw_usp),er3 + mov.l @_sw_usp,er3 mov.l er3,@(THREAD_USP:16,er0) mov.l sp,@(THREAD_KSP:16,er0) @@ -363,7 +363,7 @@ SYMBOL_NAME_LABEL(resume) /* FIXME: what did we hack out of here, this does nothing! */ mov.l @(THREAD_USP:16,er1),er0 - mov.l er0,@SYMBOL_NAME(sw_usp) + mov.l er0,@_sw_usp mov.l @(THREAD_KSP:16,er1),sp /* restore status register */ @@ -372,15 +372,15 @@ SYMBOL_NAME_LABEL(resume) ldc r3l,ccr rts -SYMBOL_NAME_LABEL(trace_break) +_trace_break: subs #4,sp SAVE_ALL sub.l er1,er1 dec.l #1,er1 mov.l er1,@(LORIG,sp) mov.l sp,er0 - jsr @SYMBOL_NAME(set_esp0) - mov.l @SYMBOL_NAME(sw_usp),er0 + jsr @_set_esp0 + mov.l @_sw_usp,er0 mov.l @er0,er1 mov.w @(-2:16,er1),r2 cmp.w #0x5730,r2 @@ -390,13 +390,13 @@ SYMBOL_NAME_LABEL(trace_break) 1: and.w #0xff,e1 mov.l er1,er0 - jsr @SYMBOL_NAME(trace_trap) - jmp @SYMBOL_NAME(ret_from_exception) + jsr @_trace_trap + jmp @_ret_from_exception .section .bss -SYMBOL_NAME_LABEL(sw_ksp) +_sw_ksp: .space 4 -SYMBOL_NAME_LABEL(sw_usp) +_sw_usp: .space 4 .end diff --git a/arch/h8300/kernel/syscalls.S b/arch/h8300/kernel/syscalls.S index 5c2168fb9b9e..c55e0ed270d5 100644 --- a/arch/h8300/kernel/syscalls.S +++ b/arch/h8300/kernel/syscalls.S @@ -2,8 +2,10 @@ #include <linux/sys.h> #include <asm/linkage.h> #include <asm/unistd.h> - -.globl SYMBOL_NAME(sys_call_table) + +#define CALL(x) .long _ ## x + +.globl _sys_call_table #if defined(CONFIG_CPU_H8300H) .h8300h @@ -13,324 +15,324 @@ #endif .section .text .align 2 -SYMBOL_NAME_LABEL(sys_call_table) - .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ - .long SYMBOL_NAME(sys_exit) - .long SYMBOL_NAME(sys_fork) - .long SYMBOL_NAME(sys_read) - .long SYMBOL_NAME(sys_write) - .long SYMBOL_NAME(sys_open) /* 5 */ - .long SYMBOL_NAME(sys_close) - .long SYMBOL_NAME(sys_waitpid) - .long SYMBOL_NAME(sys_creat) - .long SYMBOL_NAME(sys_link) - .long SYMBOL_NAME(sys_unlink) /* 10 */ - .long SYMBOL_NAME(sys_execve) - .long SYMBOL_NAME(sys_chdir) - .long SYMBOL_NAME(sys_time) - .long SYMBOL_NAME(sys_mknod) - .long SYMBOL_NAME(sys_chmod) /* 15 */ - .long SYMBOL_NAME(sys_chown16) - .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ - .long SYMBOL_NAME(sys_stat) - .long SYMBOL_NAME(sys_lseek) - .long SYMBOL_NAME(sys_getpid) /* 20 */ - .long SYMBOL_NAME(sys_mount) - .long SYMBOL_NAME(sys_oldumount) - .long SYMBOL_NAME(sys_setuid16) - .long SYMBOL_NAME(sys_getuid16) - .long SYMBOL_NAME(sys_stime) /* 25 */ - .long SYMBOL_NAME(sys_ptrace) - .long SYMBOL_NAME(sys_alarm) - .long SYMBOL_NAME(sys_fstat) - .long SYMBOL_NAME(sys_pause) - .long SYMBOL_NAME(sys_utime) /* 30 */ - .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */ - .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */ - .long SYMBOL_NAME(sys_access) - .long SYMBOL_NAME(sys_nice) - .long SYMBOL_NAME(sys_ni_syscall) /* 35 old ftime syscall holder */ - .long SYMBOL_NAME(sys_sync) - .long SYMBOL_NAME(sys_kill) - .long SYMBOL_NAME(sys_rename) - .long SYMBOL_NAME(sys_mkdir) - .long SYMBOL_NAME(sys_rmdir) /* 40 */ - .long SYMBOL_NAME(sys_dup) - .long SYMBOL_NAME(sys_pipe) - .long SYMBOL_NAME(sys_times) - .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ - .long SYMBOL_NAME(sys_brk) /* 45 */ - .long SYMBOL_NAME(sys_setgid16) - .long SYMBOL_NAME(sys_getgid16) - .long SYMBOL_NAME(sys_signal) - .long SYMBOL_NAME(sys_geteuid16) - .long SYMBOL_NAME(sys_getegid16) /* 50 */ - .long SYMBOL_NAME(sys_acct) - .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ - .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ - .long SYMBOL_NAME(sys_ioctl) - .long SYMBOL_NAME(sys_fcntl) /* 55 */ - .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */ - .long SYMBOL_NAME(sys_setpgid) - .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */ - .long SYMBOL_NAME(sys_ni_syscall) - .long SYMBOL_NAME(sys_umask) /* 60 */ - .long SYMBOL_NAME(sys_chroot) - .long SYMBOL_NAME(sys_ustat) - .long SYMBOL_NAME(sys_dup2) - .long SYMBOL_NAME(sys_getppid) - .long SYMBOL_NAME(sys_getpgrp) /* 65 */ - .long SYMBOL_NAME(sys_setsid) - .long SYMBOL_NAME(sys_sigaction) - .long SYMBOL_NAME(sys_sgetmask) - .long SYMBOL_NAME(sys_ssetmask) - .long SYMBOL_NAME(sys_setreuid16) /* 70 */ - .long SYMBOL_NAME(sys_setregid16) - .long SYMBOL_NAME(sys_sigsuspend) - .long SYMBOL_NAME(sys_sigpending) - .long SYMBOL_NAME(sys_sethostname) - .long SYMBOL_NAME(sys_setrlimit) /* 75 */ - .long SYMBOL_NAME(sys_old_getrlimit) - .long SYMBOL_NAME(sys_getrusage) - .long SYMBOL_NAME(sys_gettimeofday) - .long SYMBOL_NAME(sys_settimeofday) - .long SYMBOL_NAME(sys_getgroups16) /* 80 */ - .long SYMBOL_NAME(sys_setgroups16) - .long SYMBOL_NAME(sys_old_select) - .long SYMBOL_NAME(sys_symlink) - .long SYMBOL_NAME(sys_lstat) - .long SYMBOL_NAME(sys_readlink) /* 85 */ - .long SYMBOL_NAME(sys_uselib) - .long SYMBOL_NAME(sys_swapon) - .long SYMBOL_NAME(sys_reboot) - .long SYMBOL_NAME(sys_old_readdir) - .long SYMBOL_NAME(sys_old_mmap) /* 90 */ - .long SYMBOL_NAME(sys_munmap) - .long SYMBOL_NAME(sys_truncate) - .long SYMBOL_NAME(sys_ftruncate) - .long SYMBOL_NAME(sys_fchmod) - .long SYMBOL_NAME(sys_fchown16) /* 95 */ - .long SYMBOL_NAME(sys_getpriority) - .long SYMBOL_NAME(sys_setpriority) - .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ - .long SYMBOL_NAME(sys_statfs) - .long SYMBOL_NAME(sys_fstatfs) /* 100 */ - .long SYMBOL_NAME(sys_ni_syscall) /* ioperm for i386 */ - .long SYMBOL_NAME(sys_socketcall) - .long SYMBOL_NAME(sys_syslog) - .long SYMBOL_NAME(sys_setitimer) - .long SYMBOL_NAME(sys_getitimer) /* 105 */ - .long SYMBOL_NAME(sys_newstat) - .long SYMBOL_NAME(sys_newlstat) - .long SYMBOL_NAME(sys_newfstat) - .long SYMBOL_NAME(sys_ni_syscall) - .long SYMBOL_NAME(sys_ni_syscall) /* iopl for i386 */ /* 110 */ - .long SYMBOL_NAME(sys_vhangup) - .long SYMBOL_NAME(sys_ni_syscall) /* obsolete idle() syscall */ - .long SYMBOL_NAME(sys_ni_syscall) /* vm86old for i386 */ - .long SYMBOL_NAME(sys_wait4) - .long SYMBOL_NAME(sys_swapoff) /* 115 */ - .long SYMBOL_NAME(sys_sysinfo) - .long SYMBOL_NAME(sys_ipc) - .long SYMBOL_NAME(sys_fsync) - .long SYMBOL_NAME(sys_sigreturn) - .long SYMBOL_NAME(sys_clone) /* 120 */ - .long SYMBOL_NAME(sys_setdomainname) - .long SYMBOL_NAME(sys_newuname) - .long SYMBOL_NAME(sys_cacheflush) /* modify_ldt for i386 */ - .long SYMBOL_NAME(sys_adjtimex) - .long SYMBOL_NAME(sys_ni_syscall) /* 125 sys_mprotect */ - .long SYMBOL_NAME(sys_sigprocmask) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_create_module */ - .long SYMBOL_NAME(sys_init_module) - .long SYMBOL_NAME(sys_delete_module) - .long SYMBOL_NAME(sys_ni_syscall) /* 130 sys_get_kernel_syms */ - .long SYMBOL_NAME(sys_quotactl) - .long SYMBOL_NAME(sys_getpgid) - .long SYMBOL_NAME(sys_fchdir) - .long SYMBOL_NAME(sys_bdflush) - .long SYMBOL_NAME(sys_sysfs) /* 135 */ - .long SYMBOL_NAME(sys_personality) - .long SYMBOL_NAME(sys_ni_syscall) /* for afs_syscall */ - .long SYMBOL_NAME(sys_setfsuid16) - .long SYMBOL_NAME(sys_setfsgid16) - .long SYMBOL_NAME(sys_llseek) /* 140 */ - .long SYMBOL_NAME(sys_getdents) - .long SYMBOL_NAME(sys_select) - .long SYMBOL_NAME(sys_flock) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_msync */ - .long SYMBOL_NAME(sys_readv) /* 145 */ - .long SYMBOL_NAME(sys_writev) - .long SYMBOL_NAME(sys_getsid) - .long SYMBOL_NAME(sys_fdatasync) - .long SYMBOL_NAME(sys_sysctl) - .long SYMBOL_NAME(sys_ni_syscall) /* 150 sys_mlock */ - .long SYMBOL_NAME(sys_ni_syscall) /* sys_munlock */ - .long SYMBOL_NAME(sys_ni_syscall) /* sys_mlockall */ - .long SYMBOL_NAME(sys_ni_syscall) /* sys_munlockall */ - .long SYMBOL_NAME(sys_sched_setparam) - .long SYMBOL_NAME(sys_sched_getparam) /* 155 */ - .long SYMBOL_NAME(sys_sched_setscheduler) - .long SYMBOL_NAME(sys_sched_getscheduler) - .long SYMBOL_NAME(sys_sched_yield) - .long SYMBOL_NAME(sys_sched_get_priority_max) - .long SYMBOL_NAME(sys_sched_get_priority_min) /* 160 */ - .long SYMBOL_NAME(sys_sched_rr_get_interval) - .long SYMBOL_NAME(sys_nanosleep) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_mremap */ - .long SYMBOL_NAME(sys_setresuid16) - .long SYMBOL_NAME(sys_getresuid16) /* 165 */ - .long SYMBOL_NAME(sys_ni_syscall) /* for vm86 */ - .long SYMBOL_NAME(sys_ni_syscall) /* sys_query_module */ - .long SYMBOL_NAME(sys_poll) - .long SYMBOL_NAME(sys_ni_syscall) /* old nfsservctl */ - .long SYMBOL_NAME(sys_setresgid16) /* 170 */ - .long SYMBOL_NAME(sys_getresgid16) - .long SYMBOL_NAME(sys_prctl) - .long SYMBOL_NAME(sys_rt_sigreturn) - .long SYMBOL_NAME(sys_rt_sigaction) - .long SYMBOL_NAME(sys_rt_sigprocmask) /* 175 */ - .long SYMBOL_NAME(sys_rt_sigpending) - .long SYMBOL_NAME(sys_rt_sigtimedwait) - .long SYMBOL_NAME(sys_rt_sigqueueinfo) - .long SYMBOL_NAME(sys_rt_sigsuspend) - .long SYMBOL_NAME(sys_pread64) /* 180 */ - .long SYMBOL_NAME(sys_pwrite64) - .long SYMBOL_NAME(sys_lchown16); - .long SYMBOL_NAME(sys_getcwd) - .long SYMBOL_NAME(sys_capget) - .long SYMBOL_NAME(sys_capset) /* 185 */ - .long SYMBOL_NAME(sys_sigaltstack) - .long SYMBOL_NAME(sys_sendfile) - .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ - .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ - .long SYMBOL_NAME(sys_vfork) /* 190 */ - .long SYMBOL_NAME(sys_getrlimit) - .long SYMBOL_NAME(sys_mmap_pgoff) - .long SYMBOL_NAME(sys_truncate64) - .long SYMBOL_NAME(sys_ftruncate64) - .long SYMBOL_NAME(sys_stat64) /* 195 */ - .long SYMBOL_NAME(sys_lstat64) - .long SYMBOL_NAME(sys_fstat64) - .long SYMBOL_NAME(sys_chown) - .long SYMBOL_NAME(sys_getuid) - .long SYMBOL_NAME(sys_getgid) /* 200 */ - .long SYMBOL_NAME(sys_geteuid) - .long SYMBOL_NAME(sys_getegid) - .long SYMBOL_NAME(sys_setreuid) - .long SYMBOL_NAME(sys_setregid) - .long SYMBOL_NAME(sys_getgroups) /* 205 */ - .long SYMBOL_NAME(sys_setgroups) - .long SYMBOL_NAME(sys_fchown) - .long SYMBOL_NAME(sys_setresuid) - .long SYMBOL_NAME(sys_getresuid) - .long SYMBOL_NAME(sys_setresgid) /* 210 */ - .long SYMBOL_NAME(sys_getresgid) - .long SYMBOL_NAME(sys_lchown) - .long SYMBOL_NAME(sys_setuid) - .long SYMBOL_NAME(sys_setgid) - .long SYMBOL_NAME(sys_setfsuid) /* 215 */ - .long SYMBOL_NAME(sys_setfsgid) - .long SYMBOL_NAME(sys_pivot_root) - .long SYMBOL_NAME(sys_ni_syscall) - .long SYMBOL_NAME(sys_ni_syscall) - .long SYMBOL_NAME(sys_getdents64) /* 220 */ - .long SYMBOL_NAME(sys_fcntl64) - .long SYMBOL_NAME(sys_ni_syscall) /* reserved TUX */ - .long SYMBOL_NAME(sys_ni_syscall) /* reserved Security */ - .long SYMBOL_NAME(sys_gettid) - .long SYMBOL_NAME(sys_readahead) /* 225 */ - .long SYMBOL_NAME(sys_setxattr) - .long SYMBOL_NAME(sys_lsetxattr) - .long SYMBOL_NAME(sys_fsetxattr) - .long SYMBOL_NAME(sys_getxattr) - .long SYMBOL_NAME(sys_lgetxattr) /* 230 */ - .long SYMBOL_NAME(sys_fgetxattr) - .long SYMBOL_NAME(sys_listxattr) - .long SYMBOL_NAME(sys_llistxattr) - .long SYMBOL_NAME(sys_flistxattr) - .long SYMBOL_NAME(sys_removexattr) /* 235 */ - .long SYMBOL_NAME(sys_lremovexattr) - .long SYMBOL_NAME(sys_fremovexattr) - .long SYMBOL_NAME(sys_tkill) - .long SYMBOL_NAME(sys_sendfile64) - .long SYMBOL_NAME(sys_futex) /* 240 */ - .long SYMBOL_NAME(sys_sched_setaffinity) - .long SYMBOL_NAME(sys_sched_getaffinity) - .long SYMBOL_NAME(sys_ni_syscall) - .long SYMBOL_NAME(sys_ni_syscall) - .long SYMBOL_NAME(sys_io_setup) /* 245 */ - .long SYMBOL_NAME(sys_io_destroy) - .long SYMBOL_NAME(sys_io_getevents) - .long SYMBOL_NAME(sys_io_submit) - .long SYMBOL_NAME(sys_io_cancel) - .long SYMBOL_NAME(sys_fadvise64) /* 250 */ - .long SYMBOL_NAME(sys_ni_syscall) - .long SYMBOL_NAME(sys_exit_group) - .long SYMBOL_NAME(sys_lookup_dcookie) - .long SYMBOL_NAME(sys_epoll_create) - .long SYMBOL_NAME(sys_epoll_ctl) /* 255 */ - .long SYMBOL_NAME(sys_epoll_wait) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_remap_file_pages */ - .long SYMBOL_NAME(sys_set_tid_address) - .long SYMBOL_NAME(sys_timer_create) - .long SYMBOL_NAME(sys_timer_settime) /* 260 */ - .long SYMBOL_NAME(sys_timer_gettime) - .long SYMBOL_NAME(sys_timer_getoverrun) - .long SYMBOL_NAME(sys_timer_delete) - .long SYMBOL_NAME(sys_clock_settime) - .long SYMBOL_NAME(sys_clock_gettime) /* 265 */ - .long SYMBOL_NAME(sys_clock_getres) - .long SYMBOL_NAME(sys_clock_nanosleep) - .long SYMBOL_NAME(sys_statfs64) - .long SYMBOL_NAME(sys_fstatfs64) - .long SYMBOL_NAME(sys_tgkill) /* 270 */ - .long SYMBOL_NAME(sys_utimes) - .long SYMBOL_NAME(sys_fadvise64_64) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_vserver */ - .long SYMBOL_NAME(sys_ni_syscall) - .long SYMBOL_NAME(sys_get_mempolicy) /* 275 */ - .long SYMBOL_NAME(sys_set_mempolicy) - .long SYMBOL_NAME(sys_mq_open) - .long SYMBOL_NAME(sys_mq_unlink) - .long SYMBOL_NAME(sys_mq_timedsend) - .long SYMBOL_NAME(sys_mq_timedreceive) /* 280 */ - .long SYMBOL_NAME(sys_mq_notify) - .long SYMBOL_NAME(sys_mq_getsetattr) - .long SYMBOL_NAME(sys_waitid) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_kexec_load */ - .long SYMBOL_NAME(sys_add_key) /* 285 */ - .long SYMBOL_NAME(sys_request_key) - .long SYMBOL_NAME(sys_keyctl) - .long SYMBOL_NAME(sys_ioprio_set) - .long SYMBOL_NAME(sys_ioprio_get) /* 290 */ - .long SYMBOL_NAME(sys_inotify_init) - .long SYMBOL_NAME(sys_inotify_add_watch) - .long SYMBOL_NAME(sys_inotify_rm_watch) - .long SYMBOL_NAME(sys_migrate_pages) - .long SYMBOL_NAME(sys_openat) /* 295 */ - .long SYMBOL_NAME(sys_mkdirat) - .long SYMBOL_NAME(sys_mknodat) - .long SYMBOL_NAME(sys_fchownat) - .long SYMBOL_NAME(sys_futimesat) - .long SYMBOL_NAME(sys_fstatat64) /* 300 */ - .long SYMBOL_NAME(sys_unlinkat) - .long SYMBOL_NAME(sys_renameat) - .long SYMBOL_NAME(sys_linkat) - .long SYMBOL_NAME(sys_symlinkat) - .long SYMBOL_NAME(sys_readlinkat) /* 305 */ - .long SYMBOL_NAME(sys_fchmodat) - .long SYMBOL_NAME(sys_faccessat) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_pselect6 */ - .long SYMBOL_NAME(sys_ni_syscall) /* sys_ppoll */ - .long SYMBOL_NAME(sys_unshare) /* 310 */ - .long SYMBOL_NAME(sys_set_robust_list) - .long SYMBOL_NAME(sys_get_robust_list) - .long SYMBOL_NAME(sys_splice) - .long SYMBOL_NAME(sys_sync_file_range) - .long SYMBOL_NAME(sys_tee) /* 315 */ - .long SYMBOL_NAME(sys_vmsplice) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_move_pages */ - .long SYMBOL_NAME(sys_getcpu) - .long SYMBOL_NAME(sys_ni_syscall) /* sys_epoll_pwait */ - .long SYMBOL_NAME(sys_setns) /* 320 */ +_sys_call_table: + CALL(sys_ni_syscall) /* 0 - old "setup()" system call*/ + CALL(sys_exit) + CALL(sys_fork) + CALL(sys_read) + CALL(sys_write) + CALL(sys_open) /* 5 */ + CALL(sys_close) + CALL(sys_waitpid) + CALL(sys_creat) + CALL(sys_link) + CALL(sys_unlink) /* 10 */ + CALL(sys_execve) + CALL(sys_chdir) + CALL(sys_time) + CALL(sys_mknod) + CALL(sys_chmod) /* 15 */ + CALL(sys_chown16) + CALL(sys_ni_syscall) /* old break syscall holder */ + CALL(sys_stat) + CALL(sys_lseek) + CALL(sys_getpid) /* 20 */ + CALL(sys_mount) + CALL(sys_oldumount) + CALL(sys_setuid16) + CALL(sys_getuid16) + CALL(sys_stime) /* 25 */ + CALL(sys_ptrace) + CALL(sys_alarm) + CALL(sys_fstat) + CALL(sys_pause) + CALL(sys_utime) /* 30 */ + CALL(sys_ni_syscall) /* old stty syscall holder */ + CALL(sys_ni_syscall) /* old gtty syscall holder */ + CALL(sys_access) + CALL(sys_nice) + CALL(sys_ni_syscall) /* 35 old ftime syscall holder */ + CALL(sys_sync) + CALL(sys_kill) + CALL(sys_rename) + CALL(sys_mkdir) + CALL(sys_rmdir) /* 40 */ + CALL(sys_dup) + CALL(sys_pipe) + CALL(sys_times) + CALL(sys_ni_syscall) /* old prof syscall holder */ + CALL(sys_brk) /* 45 */ + CALL(sys_setgid16) + CALL(sys_getgid16) + CALL(sys_signal) + CALL(sys_geteuid16) + CALL(sys_getegid16) /* 50 */ + CALL(sys_acct) + CALL(sys_umount) /* recycled never used phys() */ + CALL(sys_ni_syscall) /* old lock syscall holder */ + CALL(sys_ioctl) + CALL(sys_fcntl) /* 55 */ + CALL(sys_ni_syscall) /* old mpx syscall holder */ + CALL(sys_setpgid) + CALL(sys_ni_syscall) /* old ulimit syscall holder */ + CALL(sys_ni_syscall) + CALL(sys_umask) /* 60 */ + CALL(sys_chroot) + CALL(sys_ustat) + CALL(sys_dup2) + CALL(sys_getppid) + CALL(sys_getpgrp) /* 65 */ + CALL(sys_setsid) + CALL(sys_sigaction) + CALL(sys_sgetmask) + CALL(sys_ssetmask) + CALL(sys_setreuid16) /* 70 */ + CALL(sys_setregid16) + CALL(sys_sigsuspend) + CALL(sys_sigpending) + CALL(sys_sethostname) + CALL(sys_setrlimit) /* 75 */ + CALL(sys_old_getrlimit) + CALL(sys_getrusage) + CALL(sys_gettimeofday) + CALL(sys_settimeofday) + CALL(sys_getgroups16) /* 80 */ + CALL(sys_setgroups16) + CALL(sys_old_select) + CALL(sys_symlink) + CALL(sys_lstat) + CALL(sys_readlink) /* 85 */ + CALL(sys_uselib) + CALL(sys_swapon) + CALL(sys_reboot) + CALL(sys_old_readdir) + CALL(sys_old_mmap) /* 90 */ + CALL(sys_munmap) + CALL(sys_truncate) + CALL(sys_ftruncate) + CALL(sys_fchmod) + CALL(sys_fchown16) /* 95 */ + CALL(sys_getpriority) + CALL(sys_setpriority) + CALL(sys_ni_syscall) /* old profil syscall holder */ + CALL(sys_statfs) + CALL(sys_fstatfs) /* 100 */ + CALL(sys_ni_syscall) /* ioperm for i386 */ + CALL(sys_socketcall) + CALL(sys_syslog) + CALL(sys_setitimer) + CALL(sys_getitimer) /* 105 */ + CALL(sys_newstat) + CALL(sys_newlstat) + CALL(sys_newfstat) + CALL(sys_ni_syscall) + CALL(sys_ni_syscall) /* iopl for i386 */ /* 110 */ + CALL(sys_vhangup) + CALL(sys_ni_syscall) /* obsolete idle() syscall */ + CALL(sys_ni_syscall) /* vm86old for i386 */ + CALL(sys_wait4) + CALL(sys_swapoff) /* 115 */ + CALL(sys_sysinfo) + CALL(sys_ipc) + CALL(sys_fsync) + CALL(sys_sigreturn) + CALL(sys_clone) /* 120 */ + CALL(sys_setdomainname) + CALL(sys_newuname) + CALL(sys_cacheflush) /* modify_ldt for i386 */ + CALL(sys_adjtimex) + CALL(sys_ni_syscall) /* 125 sys_mprotect */ + CALL(sys_sigprocmask) + CALL(sys_ni_syscall) /* sys_create_module */ + CALL(sys_init_module) + CALL(sys_delete_module) + CALL(sys_ni_syscall) /* 130 sys_get_kernel_syms */ + CALL(sys_quotactl) + CALL(sys_getpgid) + CALL(sys_fchdir) + CALL(sys_bdflush) + CALL(sys_sysfs) /* 135 */ + CALL(sys_personality) + CALL(sys_ni_syscall) /* for afs_syscall */ + CALL(sys_setfsuid16) + CALL(sys_setfsgid16) + CALL(sys_llseek) /* 140 */ + CALL(sys_getdents) + CALL(sys_select) + CALL(sys_flock) + CALL(sys_ni_syscall) /* sys_msync */ + CALL(sys_readv) /* 145 */ + CALL(sys_writev) + CALL(sys_getsid) + CALL(sys_fdatasync) + CALL(sys_sysctl) + CALL(sys_ni_syscall) /* 150 sys_mlock */ + CALL(sys_ni_syscall) /* sys_munlock */ + CALL(sys_ni_syscall) /* sys_mlockall */ + CALL(sys_ni_syscall) /* sys_munlockall */ + CALL(sys_sched_setparam) + CALL(sys_sched_getparam) /* 155 */ + CALL(sys_sched_setscheduler) + CALL(sys_sched_getscheduler) + CALL(sys_sched_yield) + CALL(sys_sched_get_priority_max) + CALL(sys_sched_get_priority_min) /* 160 */ + CALL(sys_sched_rr_get_interval) + CALL(sys_nanosleep) + CALL(sys_ni_syscall) /* sys_mremap */ + CALL(sys_setresuid16) + CALL(sys_getresuid16) /* 165 */ + CALL(sys_ni_syscall) /* for vm86 */ + CALL(sys_ni_syscall) /* sys_query_module */ + CALL(sys_poll) + CALL(sys_ni_syscall) /* old nfsservctl */ + CALL(sys_setresgid16) /* 170 */ + CALL(sys_getresgid16) + CALL(sys_prctl) + CALL(sys_rt_sigreturn) + CALL(sys_rt_sigaction) + CALL(sys_rt_sigprocmask) /* 175 */ + CALL(sys_rt_sigpending) + CALL(sys_rt_sigtimedwait) + CALL(sys_rt_sigqueueinfo) + CALL(sys_rt_sigsuspend) + CALL(sys_pread64) /* 180 */ + CALL(sys_pwrite64) + CALL(sys_lchown16); + CALL(sys_getcwd) + CALL(sys_capget) + CALL(sys_capset) /* 185 */ + CALL(sys_sigaltstack) + CALL(sys_sendfile) + CALL(sys_ni_syscall) /* streams1 */ + CALL(sys_ni_syscall) /* streams2 */ + CALL(sys_vfork) /* 190 */ + CALL(sys_getrlimit) + CALL(sys_mmap_pgoff) + CALL(sys_truncate64) + CALL(sys_ftruncate64) + CALL(sys_stat64) /* 195 */ + CALL(sys_lstat64) + CALL(sys_fstat64) + CALL(sys_chown) + CALL(sys_getuid) + CALL(sys_getgid) /* 200 */ + CALL(sys_geteuid) + CALL(sys_getegid) + CALL(sys_setreuid) + CALL(sys_setregid) + CALL(sys_getgroups) /* 205 */ + CALL(sys_setgroups) + CALL(sys_fchown) + CALL(sys_setresuid) + CALL(sys_getresuid) + CALL(sys_setresgid) /* 210 */ + CALL(sys_getresgid) + CALL(sys_lchown) + CALL(sys_setuid) + CALL(sys_setgid) + CALL(sys_setfsuid) /* 215 */ + CALL(sys_setfsgid) + CALL(sys_pivot_root) + CALL(sys_ni_syscall) + CALL(sys_ni_syscall) + CALL(sys_getdents64) /* 220 */ + CALL(sys_fcntl64) + CALL(sys_ni_syscall) /* reserved TUX */ + CALL(sys_ni_syscall) /* reserved Security */ + CALL(sys_gettid) + CALL(sys_readahead) /* 225 */ + CALL(sys_setxattr) + CALL(sys_lsetxattr) + CALL(sys_fsetxattr) + CALL(sys_getxattr) + CALL(sys_lgetxattr) /* 230 */ + CALL(sys_fgetxattr) + CALL(sys_listxattr) + CALL(sys_llistxattr) + CALL(sys_flistxattr) + CALL(sys_removexattr) /* 235 */ + CALL(sys_lremovexattr) + CALL(sys_fremovexattr) + CALL(sys_tkill) + CALL(sys_sendfile64) + CALL(sys_futex) /* 240 */ + CALL(sys_sched_setaffinity) + CALL(sys_sched_getaffinity) + CALL(sys_ni_syscall) + CALL(sys_ni_syscall) + CALL(sys_io_setup) /* 245 */ + CALL(sys_io_destroy) + CALL(sys_io_getevents) + CALL(sys_io_submit) + CALL(sys_io_cancel) + CALL(sys_fadvise64) /* 250 */ + CALL(sys_ni_syscall) + CALL(sys_exit_group) + CALL(sys_lookup_dcookie) + CALL(sys_epoll_create) + CALL(sys_epoll_ctl) /* 255 */ + CALL(sys_epoll_wait) + CALL(sys_ni_syscall) /* sys_remap_file_pages */ + CALL(sys_set_tid_address) + CALL(sys_timer_create) + CALL(sys_timer_settime) /* 260 */ + CALL(sys_timer_gettime) + CALL(sys_timer_getoverrun) + CALL(sys_timer_delete) + CALL(sys_clock_settime) + CALL(sys_clock_gettime) /* 265 */ + CALL(sys_clock_getres) + CALL(sys_clock_nanosleep) + CALL(sys_statfs64) + CALL(sys_fstatfs64) + CALL(sys_tgkill) /* 270 */ + CALL(sys_utimes) + CALL(sys_fadvise64_64) + CALL(sys_ni_syscall) /* sys_vserver */ + CALL(sys_ni_syscall) + CALL(sys_get_mempolicy) /* 275 */ + CALL(sys_set_mempolicy) + CALL(sys_mq_open) + CALL(sys_mq_unlink) + CALL(sys_mq_timedsend) + CALL(sys_mq_timedreceive) /* 280 */ + CALL(sys_mq_notify) + CALL(sys_mq_getsetattr) + CALL(sys_waitid) + CALL(sys_ni_syscall) /* sys_kexec_load */ + CALL(sys_add_key) /* 285 */ + CALL(sys_request_key) + CALL(sys_keyctl) + CALL(sys_ioprio_set) + CALL(sys_ioprio_get) /* 290 */ + CALL(sys_inotify_init) + CALL(sys_inotify_add_watch) + CALL(sys_inotify_rm_watch) + CALL(sys_migrate_pages) + CALL(sys_openat) /* 295 */ + CALL(sys_mkdirat) + CALL(sys_mknodat) + CALL(sys_fchownat) + CALL(sys_futimesat) + CALL(sys_fstatat64) /* 300 */ + CALL(sys_unlinkat) + CALL(sys_renameat) + CALL(sys_linkat) + CALL(sys_symlinkat) + CALL(sys_readlinkat) /* 305 */ + CALL(sys_fchmodat) + CALL(sys_faccessat) + CALL(sys_ni_syscall) /* sys_pselect6 */ + CALL(sys_ni_syscall) /* sys_ppoll */ + CALL(sys_unshare) /* 310 */ + CALL(sys_set_robust_list) + CALL(sys_get_robust_list) + CALL(sys_splice) + CALL(sys_sync_file_range) + CALL(sys_tee) /* 315 */ + CALL(sys_vmsplice) + CALL(sys_ni_syscall) /* sys_move_pages */ + CALL(sys_getcpu) + CALL(sys_ni_syscall) /* sys_epoll_pwait */ + CALL(sys_setns) /* 320 */ diff --git a/arch/h8300/lib/abs.S b/arch/h8300/lib/abs.S index cabdd46b41db..ddd1fb3d01ad 100644 --- a/arch/h8300/lib/abs.S +++ b/arch/h8300/lib/abs.S @@ -9,10 +9,10 @@ .h8300s #endif .text -.global SYMBOL_NAME(abs) +.global _abs ;;; int abs(int n) -SYMBOL_NAME_LABEL(abs) +_abs: mov.l er0,er0 bpl 1f neg.l er0 diff --git a/arch/h8300/lib/memcpy.S b/arch/h8300/lib/memcpy.S index fdcbc1ee673c..cad325e2c0e8 100644 --- a/arch/h8300/lib/memcpy.S +++ b/arch/h8300/lib/memcpy.S @@ -10,10 +10,10 @@ #endif .text -.global SYMBOL_NAME(memcpy) +.global _memcpy ;;; void *memcpy(void *to, void *from, size_t n) -SYMBOL_NAME_LABEL(memcpy) +_memcpy: mov.l er2,er2 bne 1f rts diff --git a/arch/h8300/lib/memset.S b/arch/h8300/lib/memset.S index 59abdf9485a5..4549a64c5b79 100644 --- a/arch/h8300/lib/memset.S +++ b/arch/h8300/lib/memset.S @@ -10,13 +10,13 @@ #endif .text -.global SYMBOL_NAME(memset) +.global _memset ;;void *memset(*ptr, int c, size_t count) ;; ptr = er0 ;; c = er1(r1l) ;; count = er2 -SYMBOL_NAME_LABEL(memset) +_memset: btst #0,r0l beq 2f diff --git a/arch/h8300/platform/h8300h/aki3068net/crt0_ram.S b/arch/h8300/platform/h8300h/aki3068net/crt0_ram.S index ecaeb31ae9a4..b2ad0f2d0417 100644 --- a/arch/h8300/platform/h8300h/aki3068net/crt0_ram.S +++ b/arch/h8300/platform/h8300h/aki3068net/crt0_ram.S @@ -22,10 +22,10 @@ #define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS #endif - .global SYMBOL_NAME(_start) - .global SYMBOL_NAME(command_line) - .global SYMBOL_NAME(_platform_gpio_table) - .global SYMBOL_NAME(_target_name) + .global __start + .global _command_line + .global __platform_gpio_table + .global __target_name .h8300h @@ -33,7 +33,7 @@ .file "crt0_ram.S" /* CPU Reset entry */ -SYMBOL_NAME_LABEL(_start) +__start: mov.l #RAMEND,sp ldc #0x80,ccr @@ -59,13 +59,13 @@ SYMBOL_NAME_LABEL(_start) /* copy kernel commandline */ mov.l #COMMAND_START,er5 - mov.l #SYMBOL_NAME(command_line),er6 + mov.l #_command_line,er6 mov.w #512,r4 eepmov.w /* uClinux kernel start */ ldc #0x90,ccr /* running kernel */ - mov.l #SYMBOL_NAME(init_thread_union),sp + mov.l #_init_thread_union,sp add.l #0x2000,sp jsr @_start_kernel _exit: @@ -107,4 +107,4 @@ __target_name: .asciz "AE-3068" .section .bootvec,"ax" - jmp @SYMBOL_NAME(_start) + jmp @__start diff --git a/arch/h8300/platform/h8300h/generic/crt0_ram.S b/arch/h8300/platform/h8300h/generic/crt0_ram.S index 80d0e16a4499..5ab7d9c12910 100644 --- a/arch/h8300/platform/h8300h/generic/crt0_ram.S +++ b/arch/h8300/platform/h8300h/generic/crt0_ram.S @@ -22,10 +22,10 @@ #define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS #endif - .global SYMBOL_NAME(_start) - .global SYMBOL_NAME(command_line) - .global SYMBOL_NAME(_platform_gpio_table) - .global SYMBOL_NAME(_target_name) + .global __start + .global _command_line + .global __platform_gpio_table + .global __target_name .h8300h @@ -33,7 +33,7 @@ .file "crt0_ram.S" /* CPU Reset entry */ -SYMBOL_NAME_LABEL(_start) +__start: mov.l #RAMEND,sp ldc #0x80,ccr @@ -59,13 +59,13 @@ SYMBOL_NAME_LABEL(_start) /* copy kernel commandline */ mov.l #COMMAND_START,er5 - mov.l #SYMBOL_NAME(command_line),er6 + mov.l #_command_line,er6 mov.w #512,r4 eepmov.w /* uClinux kernel start */ ldc #0x90,ccr /* running kernel */ - mov.l #SYMBOL_NAME(init_thread_union),sp + mov.l #_init_thread_union,sp add.l #0x2000,sp jsr @_start_kernel _exit: diff --git a/arch/h8300/platform/h8300h/generic/crt0_rom.S b/arch/h8300/platform/h8300h/generic/crt0_rom.S index 120add7ca832..dda1dfa15a5e 100644 --- a/arch/h8300/platform/h8300h/generic/crt0_rom.S +++ b/arch/h8300/platform/h8300h/generic/crt0_rom.S @@ -12,17 +12,17 @@ #include <asm/linkage.h> - .global SYMBOL_NAME(_start) - .global SYMBOL_NAME(_command_line) - .global SYMBOL_NAME(_platform_gpio_table) - .global SYMBOL_NAME(_target_name) + .global __start + .global __command_line + .global __platform_gpio_table + .global __target_name .h8300h .section .text .file "crt0_rom.S" /* CPU Reset entry */ -SYMBOL_NAME_LABEL(_start) +__start: mov.l #__ramend,sp ldc #0x80,ccr @@ -60,13 +60,13 @@ SYMBOL_NAME_LABEL(_start) /* copy kernel commandline */ mov.l #COMMAND_START,er5 - mov.l #SYMBOL_NAME(_command_line),er6 + mov.l #__command_line,er6 mov.w #512,r4 eepmov.w /* linux kernel start */ ldc #0x90,ccr /* running kernel */ - mov.l #SYMBOL_NAME(init_thread_union),sp + mov.l #_init_thread_union,sp add.l #0x2000,sp jsr @_start_kernel _exit: diff --git a/arch/h8300/platform/h8300h/h8max/crt0_ram.S b/arch/h8300/platform/h8300h/h8max/crt0_ram.S index efcbefb91b67..6a0d4e2d9ec6 100644 --- a/arch/h8300/platform/h8300h/h8max/crt0_ram.S +++ b/arch/h8300/platform/h8300h/h8max/crt0_ram.S @@ -22,10 +22,10 @@ #define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS #endif - .global SYMBOL_NAME(_start) - .global SYMBOL_NAME(command_line) - .global SYMBOL_NAME(_platform_gpio_table) - .global SYMBOL_NAME(_target_name) + .global __start + .global _command_line + .global __platform_gpio_table + .global __target_name .h8300h @@ -33,7 +33,7 @@ .file "crt0_ram.S" /* CPU Reset entry */ -SYMBOL_NAME_LABEL(_start) +__start: mov.l #RAMEND,sp ldc #0x80,ccr @@ -59,13 +59,13 @@ SYMBOL_NAME_LABEL(_start) /* copy kernel commandline */ mov.l #COMMAND_START,er5 - mov.l #SYMBOL_NAME(command_line),er6 + mov.l #_command_line,er6 mov.w #512,r4 eepmov.w /* uClinux kernel start */ ldc #0x90,ccr /* running kernel */ - mov.l #SYMBOL_NAME(init_thread_union),sp + mov.l #_init_thread_union,sp add.l #0x2000,sp jsr @_start_kernel _exit: @@ -107,4 +107,4 @@ __target_name: .asciz "H8MAX" .section .bootvec,"ax" - jmp @SYMBOL_NAME(_start) + jmp @__start diff --git a/arch/h8300/platform/h8s/edosk2674/crt0_ram.S b/arch/h8300/platform/h8s/edosk2674/crt0_ram.S index d12b0debe478..5ed191b37cde 100644 --- a/arch/h8300/platform/h8s/edosk2674/crt0_ram.S +++ b/arch/h8300/platform/h8s/edosk2674/crt0_ram.S @@ -23,10 +23,10 @@ #define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS #endif - .global SYMBOL_NAME(_start) - .global SYMBOL_NAME(_command_line) - .global SYMBOL_NAME(_platform_gpio_table) - .global SYMBOL_NAME(_target_name) + .global __start + .global __command_line + .global __platform_gpio_table + .global __target_name .h8300s @@ -34,7 +34,7 @@ .file "crt0_ram.S" /* CPU Reset entry */ -SYMBOL_NAME_LABEL(_start) +__start: mov.l #RAMEND,sp ldc #0x80,ccr ldc #0x00,exr @@ -66,13 +66,13 @@ SYMBOL_NAME_LABEL(_start) /* copy kernel commandline */ mov.l #COMMAND_START,er5 - mov.l #SYMBOL_NAME(command_line),er6 + mov.l #_command_line,er6 mov.w #512,r4 eepmov.w /* uClinux kernel start */ ldc #0x90,ccr /* running kernel */ - mov.l #SYMBOL_NAME(init_thread_union),sp + mov.l #_init_thread_union,sp add.l #0x2000,sp jsr @_start_kernel _exit: @@ -127,4 +127,4 @@ __target_name: .asciz "EDOSK-2674" .section .bootvec,"ax" - jmp @SYMBOL_NAME(_start) + jmp @__start diff --git a/arch/h8300/platform/h8s/edosk2674/crt0_rom.S b/arch/h8300/platform/h8s/edosk2674/crt0_rom.S index c03d23c6fe12..06d1d7f324ca 100644 --- a/arch/h8300/platform/h8s/edosk2674/crt0_rom.S +++ b/arch/h8300/platform/h8s/edosk2674/crt0_rom.S @@ -13,17 +13,17 @@ #include <asm/linkage.h> #include <asm/regs267x.h> - .global SYMBOL_NAME(_start) - .global SYMBOL_NAME(_command_line) - .global SYMBOL_NAME(_platform_gpio_table) - .global SYMBOL_NAME(_target_name) + .global __start + .global __command_line + .global __platform_gpio_table + .global __target_name .h8300s .section .text .file "crt0_rom.S" /* CPU Reset entry */ -SYMBOL_NAME_LABEL(_start) +__start: mov.l #__ramend,sp ldc #0x80,ccr ldc #0,exr @@ -82,13 +82,13 @@ SYMBOL_NAME_LABEL(_start) /* copy kernel commandline */ mov.l #COMMAND_START,er5 - mov.l #SYMBOL_NAME(_command_line),er6 + mov.l #__command_line,er6 mov.w #512,r4 eepmov.w /* linux kernel start */ ldc #0x90,ccr /* running kernel */ - mov.l #SYMBOL_NAME(init_thread_union),sp + mov.l #_init_thread_union,sp add.l #0x2000,sp jsr @_start_kernel _exit: diff --git a/arch/h8300/platform/h8s/generic/crt0_ram.S b/arch/h8300/platform/h8s/generic/crt0_ram.S index b04541069976..7018915de74f 100644 --- a/arch/h8300/platform/h8s/generic/crt0_ram.S +++ b/arch/h8300/platform/h8s/generic/crt0_ram.S @@ -23,10 +23,10 @@ #define RAMEND CONFIG_BLKDEV_RESERVE_ADDRESS #endif - .global SYMBOL_NAME(_start) - .global SYMBOL_NAME(_command_line) - .global SYMBOL_NAME(_platform_gpio_table) - .global SYMBOL_NAME(_target_name) + .global __start + .global __command_line + .global __platform_gpio_table + .global __target_name .h8300s @@ -34,7 +34,7 @@ .file "crt0_ram.S" /* CPU Reset entry */ -SYMBOL_NAME_LABEL(_start) +__start: mov.l #RAMEND,sp ldc #0x80,ccr ldc #0x00,exr @@ -63,13 +63,13 @@ SYMBOL_NAME_LABEL(_start) /* copy kernel commandline */ mov.l #COMMAND_START,er5 - mov.l #SYMBOL_NAME(command_line),er6 + mov.l #_command_line,er6 mov.w #512,r4 eepmov.w /* uClinux kernel start */ ldc #0x90,ccr /* running kernel */ - mov.l #SYMBOL_NAME(init_thread_union),sp + mov.l #_init_thread_union,sp add.l #0x2000,sp jsr @_start_kernel _exit: @@ -124,4 +124,4 @@ __target_name: .asciz "generic" .section .bootvec,"ax" - jmp @SYMBOL_NAME(_start) + jmp @__start diff --git a/arch/h8300/platform/h8s/generic/crt0_rom.S b/arch/h8300/platform/h8s/generic/crt0_rom.S index 95b6f2898f52..623ba7828193 100644 --- a/arch/h8300/platform/h8s/generic/crt0_rom.S +++ b/arch/h8300/platform/h8s/generic/crt0_rom.S @@ -13,17 +13,17 @@ #include <asm/linkage.h> #include <asm/regs267x.h> - .global SYMBOL_NAME(_start) - .global SYMBOL_NAME(_command_line) - .global SYMBOL_NAME(_platform_gpio_table) - .global SYMBOL_NAME(_target_name) + .global __start + .global __command_line + .global __platform_gpio_table + .global __target_name .h8300s .section .text .file "crt0_rom.S" /* CPU Reset entry */ -SYMBOL_NAME_LABEL(_start) +__start: mov.l #__ramend,sp ldc #0x80,ccr ldc #0,exr @@ -61,7 +61,7 @@ SYMBOL_NAME_LABEL(_start) /* linux kernel start */ ldc #0x90,ccr /* running kernel */ - mov.l #SYMBOL_NAME(init_thread_union),sp + mov.l #_init_thread_union,sp add.l #0x2000,sp jsr @_start_kernel _exit: diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index bcda5b2d121a..d43daf192b21 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -2042,7 +2042,8 @@ sba_map_ioc_to_node(struct ioc *ioc, acpi_handle handle) #endif static int __init -acpi_sba_ioc_add(struct acpi_device *device) +acpi_sba_ioc_add(struct acpi_device *device, + const struct acpi_device_id *not_used) { struct ioc *ioc; acpi_status status; @@ -2090,14 +2091,18 @@ static const struct acpi_device_id hp_ioc_iommu_device_ids[] = { {"HWP0004", 0}, {"", 0}, }; -static struct acpi_driver acpi_sba_ioc_driver = { - .name = "IOC IOMMU Driver", - .ids = hp_ioc_iommu_device_ids, - .ops = { - .add = acpi_sba_ioc_add, - }, +static struct acpi_scan_handler acpi_sba_ioc_handler = { + .ids = hp_ioc_iommu_device_ids, + .attach = acpi_sba_ioc_add, }; +static int __init acpi_sba_ioc_init_acpi(void) +{ + return acpi_scan_add_handler(&acpi_sba_ioc_handler); +} +/* This has to run before acpi_scan_init(). */ +arch_initcall(acpi_sba_ioc_init_acpi); + extern struct dma_map_ops swiotlb_dma_ops; static int __init @@ -2122,7 +2127,10 @@ sba_init(void) } #endif - acpi_bus_register_driver(&acpi_sba_ioc_driver); + /* + * ioc_list should be populated by the acpi_sba_ioc_handler's .attach() + * routine, but that only happens if acpi_scan_init() has already run. + */ if (!ioc_list) { #ifdef CONFIG_IA64_GENERIC /* diff --git a/arch/ia64/hp/sim/simscsi.c b/arch/ia64/hp/sim/simscsi.c index 331de723c676..3a428f19a001 100644 --- a/arch/ia64/hp/sim/simscsi.c +++ b/arch/ia64/hp/sim/simscsi.c @@ -88,8 +88,8 @@ simscsi_setup (char *s) if (strlen(s) > MAX_ROOT_LEN) { printk(KERN_ERR "simscsi_setup: prefix too long---using default %s\n", simscsi_root); - } - simscsi_root = s; + } else + simscsi_root = s; return 1; } diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 5e04b591e423..80775f55f03f 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -89,9 +89,9 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, #define pci_legacy_read platform_pci_legacy_read #define pci_legacy_write platform_pci_legacy_write -struct pci_window { - struct resource resource; - u64 offset; +struct iospace_resource { + struct list_head list; + struct resource res; }; struct pci_controller { @@ -100,12 +100,10 @@ struct pci_controller { int segment; int node; /* nearest node with memory or -1 for global allocation */ - unsigned int windows; - struct pci_window *window; - void *platform_data; }; + #define PCI_CONTROLLER(busdev) ((struct pci_controller *) busdev->sysdata) #define pci_domain_nr(busdev) (PCI_CONTROLLER(busdev)->segment) diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 335eb07480fe..5eb71d22c3d5 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -807,7 +807,7 @@ int acpi_isa_irq_to_gsi(unsigned isa_irq, u32 *gsi) * ACPI based hotplug CPU support */ #ifdef CONFIG_ACPI_HOTPLUG_CPU -static __cpuinit +static int acpi_map_cpu2node(acpi_handle handle, int cpu, int physid) { #ifdef CONFIG_ACPI_NUMA @@ -882,7 +882,7 @@ __init void prefill_possible_map(void) set_cpu_possible(i, true); } -static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu) +static int _acpi_map_lsapic(acpi_handle handle, int *pcpu) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; diff --git a/arch/ia64/kernel/err_inject.c b/arch/ia64/kernel/err_inject.c index 2d67317a1ec2..f59c0b844e88 100644 --- a/arch/ia64/kernel/err_inject.c +++ b/arch/ia64/kernel/err_inject.c @@ -225,17 +225,17 @@ static struct attribute_group err_inject_attr_group = { .name = "err_inject" }; /* Add/Remove err_inject interface for CPU device */ -static int __cpuinit err_inject_add_dev(struct device * sys_dev) +static int err_inject_add_dev(struct device *sys_dev) { return sysfs_create_group(&sys_dev->kobj, &err_inject_attr_group); } -static int __cpuinit err_inject_remove_dev(struct device * sys_dev) +static int err_inject_remove_dev(struct device *sys_dev) { sysfs_remove_group(&sys_dev->kobj, &err_inject_attr_group); return 0; } -static int __cpuinit err_inject_cpu_callback(struct notifier_block *nfb, +static int err_inject_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; @@ -256,7 +256,7 @@ static int __cpuinit err_inject_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } -static struct notifier_block __cpuinitdata err_inject_cpu_notifier = +static struct notifier_block err_inject_cpu_notifier = { .notifier_call = err_inject_cpu_callback, }; diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index d7396dbb07bb..b8edfa75a83f 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -631,7 +631,7 @@ ia64_mca_register_cpev (int cpev) * Outputs * None */ -void __cpuinit +void ia64_mca_cmc_vector_setup (void) { cmcv_reg_t cmcv; @@ -1814,7 +1814,7 @@ static struct irqaction mca_cpep_irqaction = { * format most of the fields. */ -static void __cpuinit +static void format_mca_init_stack(void *mca_data, unsigned long offset, const char *type, int cpu) { @@ -1844,7 +1844,7 @@ static void * __init_refok mca_bootmem(void) } /* Do per-CPU MCA-related initialization. */ -void __cpuinit +void ia64_mca_cpu_init(void *cpu_data) { void *pal_vaddr; @@ -1896,7 +1896,7 @@ ia64_mca_cpu_init(void *cpu_data) PAGE_KERNEL)); } -static void __cpuinit ia64_mca_cmc_vector_adjust(void *dummy) +static void ia64_mca_cmc_vector_adjust(void *dummy) { unsigned long flags; @@ -1906,7 +1906,7 @@ static void __cpuinit ia64_mca_cmc_vector_adjust(void *dummy) local_irq_restore(flags); } -static int __cpuinit mca_cpu_callback(struct notifier_block *nfb, +static int mca_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -1922,7 +1922,7 @@ static int __cpuinit mca_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } -static struct notifier_block mca_cpu_notifier __cpuinitdata = { +static struct notifier_block mca_cpu_notifier = { .notifier_call = mca_cpu_callback }; diff --git a/arch/ia64/kernel/numa.c b/arch/ia64/kernel/numa.c index c93420c97409..d288cde93606 100644 --- a/arch/ia64/kernel/numa.c +++ b/arch/ia64/kernel/numa.c @@ -30,7 +30,7 @@ EXPORT_SYMBOL(cpu_to_node_map); cpumask_t node_to_cpu_mask[MAX_NUMNODES] __cacheline_aligned; EXPORT_SYMBOL(node_to_cpu_mask); -void __cpuinit map_cpu_to_node(int cpu, int nid) +void map_cpu_to_node(int cpu, int nid) { int oldnid; if (nid < 0) { /* just initialize by zero */ @@ -51,7 +51,7 @@ void __cpuinit map_cpu_to_node(int cpu, int nid) return; } -void __cpuinit unmap_cpu_from_node(int cpu, int nid) +void unmap_cpu_from_node(int cpu, int nid) { WARN_ON(!cpu_isset(cpu, node_to_cpu_mask[nid])); WARN_ON(cpu_to_node_map[cpu] != nid); diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c index 2b3c2d79256f..ab333284f4b2 100644 --- a/arch/ia64/kernel/palinfo.c +++ b/arch/ia64/kernel/palinfo.c @@ -932,7 +932,7 @@ static const struct file_operations proc_palinfo_fops = { .release = single_release, }; -static void __cpuinit +static void create_palinfo_proc_entries(unsigned int cpu) { pal_func_cpu_u_t f; @@ -962,7 +962,7 @@ remove_palinfo_proc_entries(unsigned int hcpu) remove_proc_subtree(cpustr, palinfo_dir); } -static int __cpuinit palinfo_cpu_callback(struct notifier_block *nfb, +static int palinfo_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int hotcpu = (unsigned long)hcpu; diff --git a/arch/ia64/kernel/pci-dma.c b/arch/ia64/kernel/pci-dma.c index 1ddcfe5ef353..992c1098c522 100644 --- a/arch/ia64/kernel/pci-dma.c +++ b/arch/ia64/kernel/pci-dma.c @@ -33,15 +33,6 @@ int force_iommu __read_mostly; int iommu_pass_through; -/* Dummy device used for NULL arguments (normally ISA). Better would - be probably a smaller DMA mask, but this is bug-to-bug compatible - to i386. */ -struct device fallback_dev = { - .init_name = "fallback device", - .coherent_dma_mask = DMA_BIT_MASK(32), - .dma_mask = &fallback_dev.coherent_dma_mask, -}; - extern struct dma_map_ops intel_dma_ops; static int __init pci_iommu_init(void) diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 9ea25fce06d5..5a9ff1c3c3e9 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -5647,24 +5647,8 @@ pfm_proc_show_header(struct seq_file *m) list_for_each(pos, &pfm_buffer_fmt_list) { entry = list_entry(pos, pfm_buffer_fmt_t, fmt_list); - seq_printf(m, "format : %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x %s\n", - entry->fmt_uuid[0], - entry->fmt_uuid[1], - entry->fmt_uuid[2], - entry->fmt_uuid[3], - entry->fmt_uuid[4], - entry->fmt_uuid[5], - entry->fmt_uuid[6], - entry->fmt_uuid[7], - entry->fmt_uuid[8], - entry->fmt_uuid[9], - entry->fmt_uuid[10], - entry->fmt_uuid[11], - entry->fmt_uuid[12], - entry->fmt_uuid[13], - entry->fmt_uuid[14], - entry->fmt_uuid[15], - entry->fmt_name); + seq_printf(m, "format : %16phD %s\n", + entry->fmt_uuid, entry->fmt_name); } spin_unlock(&pfm_buffer_fmt_lock); diff --git a/arch/ia64/kernel/salinfo.c b/arch/ia64/kernel/salinfo.c index 4bc580af67b3..960a396f5929 100644 --- a/arch/ia64/kernel/salinfo.c +++ b/arch/ia64/kernel/salinfo.c @@ -568,7 +568,7 @@ static const struct file_operations salinfo_data_fops = { .llseek = default_llseek, }; -static int __cpuinit +static int salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) { unsigned int i, cpu = (unsigned long)hcpu; @@ -609,7 +609,7 @@ salinfo_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu return NOTIFY_OK; } -static struct notifier_block salinfo_cpu_notifier __cpuinitdata = +static struct notifier_block salinfo_cpu_notifier = { .notifier_call = salinfo_cpu_callback, .priority = 0, diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index 13bfdd22afc8..4fc2e9569bb2 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c @@ -748,7 +748,7 @@ const struct seq_operations cpuinfo_op = { #define MAX_BRANDS 8 static char brandname[MAX_BRANDS][128]; -static char * __cpuinit +static char * get_model_name(__u8 family, __u8 model) { static int overflow; @@ -778,7 +778,7 @@ get_model_name(__u8 family, __u8 model) return "Unknown"; } -static void __cpuinit +static void identify_cpu (struct cpuinfo_ia64 *c) { union { @@ -850,7 +850,7 @@ identify_cpu (struct cpuinfo_ia64 *c) * 2. the minimum of the i-cache stride sizes for "flush_icache_range()". * 3. the minimum of the cache stride sizes for "clflush_cache_range()". */ -static void __cpuinit +static void get_cache_info(void) { unsigned long line_size, max = 1; @@ -915,10 +915,10 @@ get_cache_info(void) * cpu_init() initializes state that is per-CPU. This function acts * as a 'CPU state barrier', nothing should get across. */ -void __cpuinit +void cpu_init (void) { - extern void __cpuinit ia64_mmu_init (void *); + extern void ia64_mmu_init(void *); static unsigned long max_num_phys_stacked = IA64_NUM_PHYS_STACK_REG; unsigned long num_phys_stacked; pal_vm_info_2_u_t vmi; diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index 8d87168d218d..547a48d78bd7 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -351,7 +351,7 @@ static inline void smp_setup_percpu_timer(void) { } -static void __cpuinit +static void smp_callin (void) { int cpuid, phys_id, itc_master; @@ -442,7 +442,7 @@ smp_callin (void) /* * Activate a secondary processor. head.S calls this. */ -int __cpuinit +int start_secondary (void *unused) { /* Early console may use I/O ports */ @@ -459,7 +459,7 @@ start_secondary (void *unused) return 0; } -static int __cpuinit +static int do_boot_cpu (int sapicid, int cpu, struct task_struct *idle) { int timeout; @@ -728,7 +728,7 @@ static inline void set_cpu_sibling_map(int cpu) } } -int __cpuinit +int __cpu_up(unsigned int cpu, struct task_struct *tidle) { int ret; diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c index dc00b2c1b42a..ca69a5a96dcc 100644 --- a/arch/ia64/kernel/topology.c +++ b/arch/ia64/kernel/topology.c @@ -135,11 +135,11 @@ struct cpu_cache_info { struct kobject kobj; }; -static struct cpu_cache_info all_cpu_cache_info[NR_CPUS] __cpuinitdata; +static struct cpu_cache_info all_cpu_cache_info[NR_CPUS]; #define LEAF_KOBJECT_PTR(x,y) (&all_cpu_cache_info[x].cache_leaves[y]) #ifdef CONFIG_SMP -static void __cpuinit cache_shared_cpu_map_setup( unsigned int cpu, +static void cache_shared_cpu_map_setup(unsigned int cpu, struct cache_info * this_leaf) { pal_cache_shared_info_t csi; @@ -174,7 +174,7 @@ static void __cpuinit cache_shared_cpu_map_setup( unsigned int cpu, &csi) == PAL_STATUS_SUCCESS); } #else -static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, +static void cache_shared_cpu_map_setup(unsigned int cpu, struct cache_info * this_leaf) { cpu_set(cpu, this_leaf->shared_cpu_map); @@ -298,7 +298,7 @@ static struct kobj_type cache_ktype_percpu_entry = { .sysfs_ops = &cache_sysfs_ops, }; -static void __cpuinit cpu_cache_sysfs_exit(unsigned int cpu) +static void cpu_cache_sysfs_exit(unsigned int cpu) { kfree(all_cpu_cache_info[cpu].cache_leaves); all_cpu_cache_info[cpu].cache_leaves = NULL; @@ -307,7 +307,7 @@ static void __cpuinit cpu_cache_sysfs_exit(unsigned int cpu) return; } -static int __cpuinit cpu_cache_sysfs_init(unsigned int cpu) +static int cpu_cache_sysfs_init(unsigned int cpu) { unsigned long i, levels, unique_caches; pal_cache_config_info_t cci; @@ -351,7 +351,7 @@ static int __cpuinit cpu_cache_sysfs_init(unsigned int cpu) } /* Add cache interface for CPU device */ -static int __cpuinit cache_add_dev(struct device * sys_dev) +static int cache_add_dev(struct device *sys_dev) { unsigned int cpu = sys_dev->id; unsigned long i, j; @@ -401,7 +401,7 @@ static int __cpuinit cache_add_dev(struct device * sys_dev) } /* Remove cache interface for CPU device */ -static int __cpuinit cache_remove_dev(struct device * sys_dev) +static int cache_remove_dev(struct device *sys_dev) { unsigned int cpu = sys_dev->id; unsigned long i; @@ -425,7 +425,7 @@ static int __cpuinit cache_remove_dev(struct device * sys_dev) * When a cpu is hot-plugged, do a check and initiate * cache kobject if necessary */ -static int __cpuinit cache_cpu_callback(struct notifier_block *nfb, +static int cache_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; @@ -445,7 +445,7 @@ static int __cpuinit cache_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } -static struct notifier_block __cpuinitdata cache_cpu_notifier = +static struct notifier_block cache_cpu_notifier = { .notifier_call = cache_cpu_callback }; diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index f7f9f9c6caf0..d3636e67a98e 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -630,7 +630,7 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa, printk(KERN_ERR " iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx\n", iip, ifa, isr); force_sig(SIGSEGV, current); - break; + return; case 46: printk(KERN_ERR "Unexpected IA-32 intercept trap (Trap 46)\n"); diff --git a/arch/ia64/kvm/Makefile b/arch/ia64/kvm/Makefile index 1a4053789d01..18e45ec49bbf 100644 --- a/arch/ia64/kvm/Makefile +++ b/arch/ia64/kvm/Makefile @@ -47,12 +47,13 @@ FORCE : $(obj)/$(offsets-file) ccflags-y := -Ivirt/kvm -Iarch/ia64/kvm/ asflags-y := -Ivirt/kvm -Iarch/ia64/kvm/ +KVM := ../../../virt/kvm -common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \ - coalesced_mmio.o irq_comm.o) +common-objs = $(KVM)/kvm_main.o $(KVM)/ioapic.o \ + $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o ifeq ($(CONFIG_KVM_DEVICE_ASSIGNMENT),y) -common-objs += $(addprefix ../../../virt/kvm/, assigned-dev.o iommu.o) +common-objs += $(KVM)/assigned-dev.o $(KVM)/iommu.o endif kvm-objs := $(common-objs) kvm-ia64.o kvm_fw.o diff --git a/arch/ia64/mm/contig.c b/arch/ia64/mm/contig.c index e4a6a5366dea..da5237d636d6 100644 --- a/arch/ia64/mm/contig.c +++ b/arch/ia64/mm/contig.c @@ -156,8 +156,7 @@ static void *cpu_data; * * Allocate and setup per-cpu data areas. */ -void * __cpuinit -per_cpu_init (void) +void *per_cpu_init(void) { static bool first_time = true; void *cpu0_data = __cpu0_per_cpu; diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 58550b8f7d40..2de08f4d9930 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -592,7 +592,7 @@ void __init find_memory(void) * find_pernode_space() does most of this already, we just need to set * local_per_cpu_offset */ -void __cpuinit *per_cpu_init(void) +void *per_cpu_init(void) { int cpu; static int first_time = 1; diff --git a/arch/ia64/mm/numa.c b/arch/ia64/mm/numa.c index 4248492b9321..ea21d4cad540 100644 --- a/arch/ia64/mm/numa.c +++ b/arch/ia64/mm/numa.c @@ -86,7 +86,7 @@ int __meminit __early_pfn_to_nid(unsigned long pfn) return -1; } -void __cpuinit numa_clear_node(int cpu) +void numa_clear_node(int cpu) { unmap_cpu_from_node(cpu, NUMA_NO_NODE); } diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index de1474ff0bc5..2326790b7d8b 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -134,6 +134,10 @@ struct pci_root_info { struct acpi_device *bridge; struct pci_controller *controller; struct list_head resources; + struct resource *res; + resource_size_t *res_offset; + unsigned int res_num; + struct list_head io_resources; char *name; }; @@ -153,7 +157,7 @@ new_space (u64 phys_base, int sparse) return i; if (num_io_spaces == MAX_IO_SPACES) { - printk(KERN_ERR "PCI: Too many IO port spaces " + pr_err("PCI: Too many IO port spaces " "(MAX_IO_SPACES=%lu)\n", MAX_IO_SPACES); return ~0; } @@ -168,25 +172,22 @@ new_space (u64 phys_base, int sparse) static u64 add_io_space(struct pci_root_info *info, struct acpi_resource_address64 *addr) { + struct iospace_resource *iospace; struct resource *resource; char *name; unsigned long base, min, max, base_port; unsigned int sparse = 0, space_nr, len; - resource = kzalloc(sizeof(*resource), GFP_KERNEL); - if (!resource) { - printk(KERN_ERR "PCI: No memory for %s I/O port space\n", - info->name); + len = strlen(info->name) + 32; + iospace = kzalloc(sizeof(*iospace) + len, GFP_KERNEL); + if (!iospace) { + dev_err(&info->bridge->dev, + "PCI: No memory for %s I/O port space\n", + info->name); goto out; } - len = strlen(info->name) + 32; - name = kzalloc(len, GFP_KERNEL); - if (!name) { - printk(KERN_ERR "PCI: No memory for %s I/O port space name\n", - info->name); - goto free_resource; - } + name = (char *)(iospace + 1); min = addr->minimum; max = min + addr->address_length - 1; @@ -195,7 +196,7 @@ static u64 add_io_space(struct pci_root_info *info, space_nr = new_space(addr->translation_offset, sparse); if (space_nr == ~0) - goto free_name; + goto free_resource; base = __pa(io_space[space_nr].mmio_base); base_port = IO_SPACE_BASE(space_nr); @@ -210,18 +211,23 @@ static u64 add_io_space(struct pci_root_info *info, if (space_nr == 0) sparse = 1; + resource = &iospace->res; resource->name = name; resource->flags = IORESOURCE_MEM; resource->start = base + (sparse ? IO_SPACE_SPARSE_ENCODING(min) : min); resource->end = base + (sparse ? IO_SPACE_SPARSE_ENCODING(max) : max); - insert_resource(&iomem_resource, resource); + if (insert_resource(&iomem_resource, resource)) { + dev_err(&info->bridge->dev, + "can't allocate host bridge io space resource %pR\n", + resource); + goto free_resource; + } + list_add_tail(&iospace->list, &info->io_resources); return base_port; -free_name: - kfree(name); free_resource: - kfree(resource); + kfree(iospace); out: return ~0; } @@ -265,7 +271,7 @@ static acpi_status count_window(struct acpi_resource *resource, void *data) static acpi_status add_window(struct acpi_resource *res, void *data) { struct pci_root_info *info = data; - struct pci_window *window; + struct resource *resource; struct acpi_resource_address64 addr; acpi_status status; unsigned long flags, offset = 0; @@ -289,55 +295,146 @@ static acpi_status add_window(struct acpi_resource *res, void *data) } else return AE_OK; - window = &info->controller->window[info->controller->windows++]; - window->resource.name = info->name; - window->resource.flags = flags; - window->resource.start = addr.minimum + offset; - window->resource.end = window->resource.start + addr.address_length - 1; - window->offset = offset; + resource = &info->res[info->res_num]; + resource->name = info->name; + resource->flags = flags; + resource->start = addr.minimum + offset; + resource->end = resource->start + addr.address_length - 1; + info->res_offset[info->res_num] = offset; - if (insert_resource(root, &window->resource)) { + if (insert_resource(root, resource)) { dev_err(&info->bridge->dev, "can't allocate host bridge window %pR\n", - &window->resource); + resource); } else { if (offset) dev_info(&info->bridge->dev, "host bridge window %pR " "(PCI address [%#llx-%#llx])\n", - &window->resource, - window->resource.start - offset, - window->resource.end - offset); + resource, + resource->start - offset, + resource->end - offset); else dev_info(&info->bridge->dev, - "host bridge window %pR\n", - &window->resource); + "host bridge window %pR\n", resource); } - /* HP's firmware has a hack to work around a Windows bug. * Ignore these tiny memory ranges */ - if (!((window->resource.flags & IORESOURCE_MEM) && - (window->resource.end - window->resource.start < 16))) - pci_add_resource_offset(&info->resources, &window->resource, - window->offset); + if (!((resource->flags & IORESOURCE_MEM) && + (resource->end - resource->start < 16))) + pci_add_resource_offset(&info->resources, resource, + info->res_offset[info->res_num]); + info->res_num++; return AE_OK; } +static void free_pci_root_info_res(struct pci_root_info *info) +{ + struct iospace_resource *iospace, *tmp; + + list_for_each_entry_safe(iospace, tmp, &info->io_resources, list) + kfree(iospace); + + kfree(info->name); + kfree(info->res); + info->res = NULL; + kfree(info->res_offset); + info->res_offset = NULL; + info->res_num = 0; + kfree(info->controller); + info->controller = NULL; +} + +static void __release_pci_root_info(struct pci_root_info *info) +{ + int i; + struct resource *res; + struct iospace_resource *iospace; + + list_for_each_entry(iospace, &info->io_resources, list) + release_resource(&iospace->res); + + for (i = 0; i < info->res_num; i++) { + res = &info->res[i]; + + if (!res->parent) + continue; + + if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) + continue; + + release_resource(res); + } + + free_pci_root_info_res(info); + kfree(info); +} + +static void release_pci_root_info(struct pci_host_bridge *bridge) +{ + struct pci_root_info *info = bridge->release_data; + + __release_pci_root_info(info); +} + +static int +probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, + int busnum, int domain) +{ + char *name; + + name = kmalloc(16, GFP_KERNEL); + if (!name) + return -ENOMEM; + + sprintf(name, "PCI Bus %04x:%02x", domain, busnum); + info->bridge = device; + info->name = name; + + acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_window, + &info->res_num); + if (info->res_num) { + info->res = + kzalloc_node(sizeof(*info->res) * info->res_num, + GFP_KERNEL, info->controller->node); + if (!info->res) { + kfree(name); + return -ENOMEM; + } + + info->res_offset = + kzalloc_node(sizeof(*info->res_offset) * info->res_num, + GFP_KERNEL, info->controller->node); + if (!info->res_offset) { + kfree(name); + kfree(info->res); + info->res = NULL; + return -ENOMEM; + } + + info->res_num = 0; + acpi_walk_resources(device->handle, METHOD_NAME__CRS, + add_window, info); + } else + kfree(name); + + return 0; +} + struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) { struct acpi_device *device = root->device; int domain = root->segment; int bus = root->secondary.start; struct pci_controller *controller; - unsigned int windows = 0; - struct pci_root_info info; + struct pci_root_info *info = NULL; + int busnum = root->secondary.start; struct pci_bus *pbus; - char *name; - int pxm; + int pxm, ret; controller = alloc_pci_controller(domain); if (!controller) - goto out1; + return NULL; controller->acpi_handle = device->handle; @@ -347,29 +444,27 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) controller->node = pxm_to_node(pxm); #endif - INIT_LIST_HEAD(&info.resources); - /* insert busn resource at first */ - pci_add_resource(&info.resources, &root->secondary); - acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_window, - &windows); - if (windows) { - controller->window = - kzalloc_node(sizeof(*controller->window) * windows, - GFP_KERNEL, controller->node); - if (!controller->window) - goto out2; - - name = kmalloc(16, GFP_KERNEL); - if (!name) - goto out3; - - sprintf(name, "PCI Bus %04x:%02x", domain, bus); - info.bridge = device; - info.controller = controller; - info.name = name; - acpi_walk_resources(device->handle, METHOD_NAME__CRS, - add_window, &info); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&device->dev, + "pci_bus %04x:%02x: ignored (out of memory)\n", + domain, busnum); + kfree(controller); + return NULL; } + + info->controller = controller; + INIT_LIST_HEAD(&info->io_resources); + INIT_LIST_HEAD(&info->resources); + + ret = probe_pci_root_info(info, device, busnum, domain); + if (ret) { + kfree(info->controller); + kfree(info); + return NULL; + } + /* insert busn resource at first */ + pci_add_resource(&info->resources, &root->secondary); /* * See arch/x86/pci/acpi.c. * The desired pci bus might already be scanned in a quirk. We @@ -377,21 +472,17 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) * such quirk. So we just ignore the case now. */ pbus = pci_create_root_bus(NULL, bus, &pci_root_ops, controller, - &info.resources); + &info->resources); if (!pbus) { - pci_free_resource_list(&info.resources); + pci_free_resource_list(&info->resources); + __release_pci_root_info(info); return NULL; } + pci_set_host_bridge_release(to_pci_host_bridge(pbus->bridge), + release_pci_root_info, info); pci_scan_child_bus(pbus); return pbus; - -out3: - kfree(controller->window); -out2: - kfree(controller); -out1: - return NULL; } int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) @@ -691,7 +782,7 @@ static void __init set_pci_dfl_cacheline_size(void) status = ia64_pal_cache_summary(&levels, &unique_caches); if (status != 0) { - printk(KERN_ERR "%s: ia64_pal_cache_summary() failed " + pr_err("%s: ia64_pal_cache_summary() failed " "(status=%ld)\n", __func__, status); return; } @@ -699,7 +790,7 @@ static void __init set_pci_dfl_cacheline_size(void) status = ia64_pal_cache_config_info(levels - 1, /* cache_type (data_or_unified)= */ 2, &cci); if (status != 0) { - printk(KERN_ERR "%s: ia64_pal_cache_config_info() failed " + pr_err("%s: ia64_pal_cache_config_info() failed " "(status=%ld)\n", __func__, status); return; } diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c index 238e2c511d94..0b5ce82d203d 100644 --- a/arch/ia64/sn/kernel/io_init.c +++ b/arch/ia64/sn/kernel/io_init.c @@ -118,76 +118,26 @@ static void __init sn_fixup_ionodes(void) } /* - * sn_pci_legacy_window_fixup - Create PCI controller windows for + * sn_pci_legacy_window_fixup - Setup PCI resources for * legacy IO and MEM space. This needs to * be done here, as the PROM does not have * ACPI support defining the root buses * and their resources (_CRS), */ static void -sn_legacy_pci_window_fixup(struct pci_controller *controller, - u64 legacy_io, u64 legacy_mem) +sn_legacy_pci_window_fixup(struct resource *res, + u64 legacy_io, u64 legacy_mem) { - controller->window = kcalloc(2, sizeof(struct pci_window), - GFP_KERNEL); - BUG_ON(controller->window == NULL); - controller->window[0].offset = legacy_io; - controller->window[0].resource.name = "legacy_io"; - controller->window[0].resource.flags = IORESOURCE_IO; - controller->window[0].resource.start = legacy_io; - controller->window[0].resource.end = - controller->window[0].resource.start + 0xffff; - controller->window[0].resource.parent = &ioport_resource; - controller->window[1].offset = legacy_mem; - controller->window[1].resource.name = "legacy_mem"; - controller->window[1].resource.flags = IORESOURCE_MEM; - controller->window[1].resource.start = legacy_mem; - controller->window[1].resource.end = - controller->window[1].resource.start + (1024 * 1024) - 1; - controller->window[1].resource.parent = &iomem_resource; - controller->windows = 2; -} - -/* - * sn_pci_window_fixup() - Create a pci_window for each device resource. - * It will setup pci_windows for use by - * pcibios_bus_to_resource(), pcibios_resource_to_bus(), - * etc. - */ -static void -sn_pci_window_fixup(struct pci_dev *dev, unsigned int count, - s64 * pci_addrs) -{ - struct pci_controller *controller = PCI_CONTROLLER(dev->bus); - unsigned int i; - unsigned int idx; - unsigned int new_count; - struct pci_window *new_window; - - if (count == 0) - return; - idx = controller->windows; - new_count = controller->windows + count; - new_window = kcalloc(new_count, sizeof(struct pci_window), GFP_KERNEL); - BUG_ON(new_window == NULL); - if (controller->window) { - memcpy(new_window, controller->window, - sizeof(struct pci_window) * controller->windows); - kfree(controller->window); - } - - /* Setup a pci_window for each device resource. */ - for (i = 0; i <= PCI_ROM_RESOURCE; i++) { - if (pci_addrs[i] == -1) - continue; - - new_window[idx].offset = dev->resource[i].start - pci_addrs[i]; - new_window[idx].resource = dev->resource[i]; - idx++; - } - - controller->windows = new_count; - controller->window = new_window; + res[0].name = "legacy_io"; + res[0].flags = IORESOURCE_IO; + res[0].start = legacy_io; + res[0].end = res[0].start + 0xffff; + res[0].parent = &ioport_resource; + res[1].name = "legacy_mem"; + res[1].flags = IORESOURCE_MEM; + res[1].start = legacy_mem; + res[1].end = res[1].start + (1024 * 1024) - 1; + res[1].parent = &iomem_resource; } /* @@ -199,9 +149,7 @@ sn_pci_window_fixup(struct pci_dev *dev, unsigned int count, void sn_io_slot_fixup(struct pci_dev *dev) { - unsigned int count = 0; int idx; - s64 pci_addrs[PCI_ROM_RESOURCE + 1]; unsigned long addr, end, size, start; struct pcidev_info *pcidev_info; struct sn_irq_info *sn_irq_info; @@ -229,7 +177,6 @@ sn_io_slot_fixup(struct pci_dev *dev) for (idx = 0; idx <= PCI_ROM_RESOURCE; idx++) { if (!pcidev_info->pdi_pio_mapped_addr[idx]) { - pci_addrs[idx] = -1; continue; } @@ -237,11 +184,8 @@ sn_io_slot_fixup(struct pci_dev *dev) end = dev->resource[idx].end; size = end - start; if (size == 0) { - pci_addrs[idx] = -1; continue; } - pci_addrs[idx] = start; - count++; addr = pcidev_info->pdi_pio_mapped_addr[idx]; addr = ((addr << 4) >> 4) | __IA64_UNCACHED_OFFSET; dev->resource[idx].start = addr; @@ -276,11 +220,6 @@ sn_io_slot_fixup(struct pci_dev *dev) IORESOURCE_ROM_BIOS_COPY; } } - /* Create a pci_window in the pci_controller struct for - * each device resource. - */ - if (count > 0) - sn_pci_window_fixup(dev, count, pci_addrs); sn_pci_fixup_slot(dev, pcidev_info, sn_irq_info); } @@ -297,8 +236,8 @@ sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus) s64 status = 0; struct pci_controller *controller; struct pcibus_bussoft *prom_bussoft_ptr; + struct resource *res; LIST_HEAD(resources); - int i; status = sal_get_pcibus_info((u64) segment, (u64) busnum, (u64) ia64_tpa(&prom_bussoft_ptr)); @@ -310,32 +249,29 @@ sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus) BUG_ON(!controller); controller->segment = segment; + res = kcalloc(2, sizeof(struct resource), GFP_KERNEL); + BUG_ON(!res); + /* * Temporarily save the prom_bussoft_ptr for use by sn_bus_fixup(). * (platform_data will be overwritten later in sn_common_bus_fixup()) */ controller->platform_data = prom_bussoft_ptr; - sn_legacy_pci_window_fixup(controller, - prom_bussoft_ptr->bs_legacy_io, - prom_bussoft_ptr->bs_legacy_mem); - for (i = 0; i < controller->windows; i++) - pci_add_resource_offset(&resources, - &controller->window[i].resource, - controller->window[i].offset); + sn_legacy_pci_window_fixup(res, + prom_bussoft_ptr->bs_legacy_io, + prom_bussoft_ptr->bs_legacy_mem); + pci_add_resource_offset(&resources, &res[0], + prom_bussoft_ptr->bs_legacy_io); + pci_add_resource_offset(&resources, &res[1], + prom_bussoft_ptr->bs_legacy_mem); + bus = pci_scan_root_bus(NULL, busnum, &pci_root_ops, controller, &resources); - if (bus == NULL) - goto error_return; /* error, or bus already scanned */ - - bus->sysdata = controller; - - return; - -error_return: - - kfree(controller); - return; + if (bus == NULL) { + kfree(res); + kfree(controller); + } } /* diff --git a/arch/ia64/sn/kernel/setup.c b/arch/ia64/sn/kernel/setup.c index f82e7b462b7b..53b01b8e2f19 100644 --- a/arch/ia64/sn/kernel/setup.c +++ b/arch/ia64/sn/kernel/setup.c @@ -192,7 +192,7 @@ void __init early_sn_setup(void) } extern int platform_intr_list[]; -static int __cpuinitdata shub_1_1_found; +static int shub_1_1_found; /* * sn_check_for_wars @@ -200,7 +200,7 @@ static int __cpuinitdata shub_1_1_found; * Set flag for enabling shub specific wars */ -static inline int __cpuinit is_shub_1_1(int nasid) +static inline int is_shub_1_1(int nasid) { unsigned long id; int rev; @@ -212,7 +212,7 @@ static inline int __cpuinit is_shub_1_1(int nasid) return rev <= 2; } -static void __cpuinit sn_check_for_wars(void) +static void sn_check_for_wars(void) { int cnode; @@ -558,7 +558,7 @@ static void __init sn_init_pdas(char **cmdline_p) * Also sets up a few fields in the nodepda. Also known as * platform_cpu_init() by the ia64 machvec code. */ -void __cpuinit sn_cpu_init(void) +void sn_cpu_init(void) { int cpuid; int cpuphyid; diff --git a/arch/ia64/xen/hypervisor.c b/arch/ia64/xen/hypervisor.c index 52172eee8591..fab62528a80b 100644 --- a/arch/ia64/xen/hypervisor.c +++ b/arch/ia64/xen/hypervisor.c @@ -74,7 +74,7 @@ void __init xen_setup_vcpu_info_placement(void) xen_vcpu_setup(cpu); } -void __cpuinit +void xen_cpu_init(void) { xen_smp_intr_init(); diff --git a/arch/m68k/Kconfig.debug b/arch/m68k/Kconfig.debug index fa12283d58fc..229682721240 100644 --- a/arch/m68k/Kconfig.debug +++ b/arch/m68k/Kconfig.debug @@ -11,9 +11,8 @@ config BOOTPARAM_STRING depends on BOOTPARAM config EARLY_PRINTK - bool "Early printk" if EMBEDDED + bool "Early printk" depends on MVME16x || MAC - default y help Write kernel log output directly to a serial port. diff --git a/arch/m68k/configs/multi_defconfig b/arch/m68k/configs/multi_defconfig index 0f795d8e65fa..b17a8837f0e1 100644 --- a/arch/m68k/configs/multi_defconfig +++ b/arch/m68k/configs/multi_defconfig @@ -214,6 +214,7 @@ CONFIG_DEVTMPFS=y # CONFIG_FW_LOADER_USER_HELPER is not set CONFIG_CONNECTOR=m CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m CONFIG_PARPORT_AMIGA=m CONFIG_PARPORT_MFC3=m CONFIG_PARPORT_ATARI=m @@ -325,6 +326,7 @@ CONFIG_ZORRO8390=y # CONFIG_NET_VENDOR_SEEQ is not set # CONFIG_NET_VENDOR_STMICRO is not set # CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PLIP=m CONFIG_PPP=m CONFIG_PPP_BSDCOMP=m CONFIG_PPP_DEFLATE=m diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig index 8982370e8b42..be1496ed9b66 100644 --- a/arch/m68k/configs/q40_defconfig +++ b/arch/m68k/configs/q40_defconfig @@ -199,6 +199,9 @@ CONFIG_DEVTMPFS=y # CONFIG_FIRMWARE_IN_KERNEL is not set # CONFIG_FW_LOADER_USER_HELPER is not set CONFIG_CONNECTOR=m +CONFIG_PARPORT=m +CONFIG_PARPORT_PC=m +CONFIG_PARPORT_1284=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_DRBD=m @@ -267,6 +270,7 @@ CONFIG_NE2000=m # CONFIG_NET_VENDOR_SMSC is not set # CONFIG_NET_VENDOR_STMICRO is not set # CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PLIP=m CONFIG_PPP=m CONFIG_PPP_BSDCOMP=m CONFIG_PPP_DEFLATE=m @@ -292,9 +296,11 @@ CONFIG_SERIO_Q40KBD=y CONFIG_VT_HW_CONSOLE_BINDING=y # CONFIG_LEGACY_PTYS is not set # CONFIG_DEVKMEM is not set +CONFIG_PRINTER=m # CONFIG_HW_RANDOM is not set CONFIG_NTP_PPS=y CONFIG_PPS_CLIENT_LDISC=m +CONFIG_PPS_CLIENT_PARPORT=m CONFIG_PTP_1588_CLOCK=m # CONFIG_HWMON is not set CONFIG_FB=y diff --git a/arch/m68k/include/asm/parport.h b/arch/m68k/include/asm/parport.h index 5ea75e6a7399..c85cece778e8 100644 --- a/arch/m68k/include/asm/parport.h +++ b/arch/m68k/include/asm/parport.h @@ -11,6 +11,8 @@ #ifndef _ASM_M68K_PARPORT_H #define _ASM_M68K_PARPORT_H 1 +#undef insl +#undef outsl #define insl(port,buf,len) isa_insb(port,buf,(len)<<2) #define outsl(port,buf,len) isa_outsb(port,buf,(len)<<2) diff --git a/arch/m68k/include/asm/string.h b/arch/m68k/include/asm/string.h index 9aea9f11fa25..c30c03d98581 100644 --- a/arch/m68k/include/asm/string.h +++ b/arch/m68k/include/asm/string.h @@ -4,20 +4,6 @@ #include <linux/types.h> #include <linux/compiler.h> -static inline char *__kernel_strcpy(char *dest, const char *src) -{ - char *xdest = dest; - - asm volatile ("\n" - "1: move.b (%1)+,(%0)+\n" - " jne 1b" - : "+a" (dest), "+a" (src) - : : "memory"); - return xdest; -} - -#ifndef __IN_STRING_C - #define __HAVE_ARCH_STRNLEN static inline size_t strnlen(const char *s, size_t count) { @@ -34,16 +20,6 @@ static inline size_t strnlen(const char *s, size_t count) return sc - s; } -#define __HAVE_ARCH_STRCPY -#if __GNUC__ >= 4 -#define strcpy(d, s) (__builtin_constant_p(s) && \ - __builtin_strlen(s) <= 32 ? \ - __builtin_strcpy(d, s) : \ - __kernel_strcpy(d, s)) -#else -#define strcpy(d, s) __kernel_strcpy(d, s) -#endif - #define __HAVE_ARCH_STRNCPY static inline char *strncpy(char *dest, const char *src, size_t n) { @@ -61,12 +37,6 @@ static inline char *strncpy(char *dest, const char *src, size_t n) return xdest; } -#define __HAVE_ARCH_STRCAT -#define strcat(d, s) ({ \ - char *__d = (d); \ - strcpy(__d + strlen(__d), (s)); \ -}) - #ifndef CONFIG_COLDFIRE #define __HAVE_ARCH_STRCMP static inline int strcmp(const char *cs, const char *ct) @@ -100,6 +70,4 @@ extern void *memset(void *, int, __kernel_size_t); extern void *memcpy(void *, const void *, __kernel_size_t); #define memcpy(d, s, n) __builtin_memcpy(d, s, n) -#endif - #endif /* _M68K_STRING_H_ */ diff --git a/arch/m68k/include/asm/uaccess_mm.h b/arch/m68k/include/asm/uaccess_mm.h index 472c891a4aee..15901db435b9 100644 --- a/arch/m68k/include/asm/uaccess_mm.h +++ b/arch/m68k/include/asm/uaccess_mm.h @@ -90,7 +90,7 @@ asm volatile ("\n" \ __put_user_asm(__pu_err, __pu_val, ptr, b, d, -EFAULT); \ break; \ case 2: \ - __put_user_asm(__pu_err, __pu_val, ptr, w, d, -EFAULT); \ + __put_user_asm(__pu_err, __pu_val, ptr, w, r, -EFAULT); \ break; \ case 4: \ __put_user_asm(__pu_err, __pu_val, ptr, l, r, -EFAULT); \ @@ -158,7 +158,7 @@ asm volatile ("\n" \ __get_user_asm(__gu_err, x, ptr, u8, b, d, -EFAULT); \ break; \ case 2: \ - __get_user_asm(__gu_err, x, ptr, u16, w, d, -EFAULT); \ + __get_user_asm(__gu_err, x, ptr, u16, w, r, -EFAULT); \ break; \ case 4: \ __get_user_asm(__gu_err, x, ptr, u32, l, r, -EFAULT); \ @@ -245,7 +245,7 @@ __constant_copy_from_user(void *to, const void __user *from, unsigned long n) __get_user_asm(res, *(u8 *)to, (u8 __user *)from, u8, b, d, 1); break; case 2: - __get_user_asm(res, *(u16 *)to, (u16 __user *)from, u16, w, d, 2); + __get_user_asm(res, *(u16 *)to, (u16 __user *)from, u16, w, r, 2); break; case 3: __constant_copy_from_user_asm(res, to, from, tmp, 3, w, b,); @@ -326,7 +326,7 @@ __constant_copy_to_user(void __user *to, const void *from, unsigned long n) __put_user_asm(res, *(u8 *)from, (u8 __user *)to, b, d, 1); break; case 2: - __put_user_asm(res, *(u16 *)from, (u16 __user *)to, w, d, 2); + __put_user_asm(res, *(u16 *)from, (u16 __user *)to, w, r, 2); break; case 3: __constant_copy_to_user_asm(res, to, from, tmp, 3, w, b,); diff --git a/arch/m68k/kernel/asm-offsets.c b/arch/m68k/kernel/asm-offsets.c index a972b00cd77d..8b7b22846366 100644 --- a/arch/m68k/kernel/asm-offsets.c +++ b/arch/m68k/kernel/asm-offsets.c @@ -77,7 +77,7 @@ int main(void) DEFINE(BIR_SIZE, offsetof(struct bi_record, size)); DEFINE(BIR_DATA, offsetof(struct bi_record, data)); - /* offsets into font_desc (drivers/video/console/font.h) */ + /* offsets into the font_desc struct */ DEFINE(FONT_DESC_IDX, offsetof(struct font_desc, idx)); DEFINE(FONT_DESC_NAME, offsetof(struct font_desc, name)); DEFINE(FONT_DESC_WIDTH, offsetof(struct font_desc, width)); diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c index 6b32b64bac35..4d7da384eea0 100644 --- a/arch/m68k/kernel/ints.c +++ b/arch/m68k/kernel/ints.c @@ -101,7 +101,7 @@ void __init m68k_setup_user_interrupt(unsigned int vec, unsigned int cnt) BUG_ON(IRQ_USER + cnt > NR_IRQS); m68k_first_user_vec = vec; for (i = 0; i < cnt; i++) - irq_set_chip(IRQ_USER + i, &user_irq_chip); + irq_set_chip_and_handler(i, &user_irq_chip, handle_simple_irq); *user_irqvec_fixup = vec - IRQ_USER; flush_icache(); } diff --git a/arch/m68k/lib/Makefile b/arch/m68k/lib/Makefile index a9d782d34276..fcd8eb1d7c7d 100644 --- a/arch/m68k/lib/Makefile +++ b/arch/m68k/lib/Makefile @@ -6,7 +6,7 @@ lib-y := ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ memcpy.o memset.o memmove.o -lib-$(CONFIG_MMU) += string.o uaccess.o +lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_CPU_HAS_NO_MULDIV64) += mulsi3.o divsi3.o udivsi3.o lib-$(CONFIG_CPU_HAS_NO_MULDIV64) += modsi3.o umodsi3.o diff --git a/arch/m68k/lib/string.c b/arch/m68k/lib/string.c deleted file mode 100644 index 4d61fa8a112c..000000000000 --- a/arch/m68k/lib/string.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - */ - -#define __IN_STRING_C - -#include <linux/module.h> -#include <linux/string.h> - -char *strcpy(char *dest, const char *src) -{ - return __kernel_strcpy(dest, src); -} -EXPORT_SYMBOL(strcpy); - -char *strcat(char *dest, const char *src) -{ - return __kernel_strcpy(dest + strlen(dest), src); -} -EXPORT_SYMBOL(strcat); diff --git a/arch/m68k/lib/uaccess.c b/arch/m68k/lib/uaccess.c index 5e97f2ee7c11..35d1442dee89 100644 --- a/arch/m68k/lib/uaccess.c +++ b/arch/m68k/lib/uaccess.c @@ -52,7 +52,7 @@ unsigned long __generic_copy_from_user(void *to, const void __user *from, " .long 3b,30b\n" " .long 5b,50b\n" " .previous" - : "=d" (res), "+a" (from), "+a" (to), "=&r" (tmp) + : "=d" (res), "+a" (from), "+a" (to), "=&d" (tmp) : "0" (n / 4), "d" (n & 3)); return res; @@ -96,7 +96,7 @@ unsigned long __generic_copy_to_user(void __user *to, const void *from, " .long 7b,50b\n" " .long 8b,50b\n" " .previous" - : "=d" (res), "+a" (from), "+a" (to), "=&r" (tmp) + : "=d" (res), "+a" (from), "+a" (to), "=&d" (tmp) : "0" (n / 4), "d" (n & 3)); return res; @@ -141,7 +141,7 @@ unsigned long __clear_user(void __user *to, unsigned long n) " .long 7b,40b\n" " .previous" : "=d" (res), "+a" (to) - : "r" (0), "0" (n / 4), "d" (n & 3)); + : "d" (0), "0" (n / 4), "d" (n & 3)); return res; } diff --git a/arch/m68k/math-emu/fp_arith.c b/arch/m68k/math-emu/fp_arith.c index 08f286db3c5a..239eb1990184 100644 --- a/arch/m68k/math-emu/fp_arith.c +++ b/arch/m68k/math-emu/fp_arith.c @@ -519,7 +519,7 @@ static void fp_roundint(struct fp_ext *dest, int mode) return; break; case 0x401e: - if (!(oldmant.m32[1] >= 0)) + if (oldmant.m32[1] & 0x80000000) return; if (oldmant.m32[0] & 1) break; diff --git a/arch/m68k/platform/coldfire/pci.c b/arch/m68k/platform/coldfire/pci.c index 8572246db84d..b33f97a13e6d 100644 --- a/arch/m68k/platform/coldfire/pci.c +++ b/arch/m68k/platform/coldfire/pci.c @@ -320,7 +320,6 @@ static int __init mcf_pci_init(void) pci_bus_size_bridges(rootbus); pci_bus_assign_resources(rootbus); pci_enable_bridges(rootbus); - pci_bus_add_devices(rootbus); return 0; } diff --git a/arch/m68k/sun3/sun3dvma.c b/arch/m68k/sun3/sun3dvma.c index ca0966cac72a..cab54482ca34 100644 --- a/arch/m68k/sun3/sun3dvma.c +++ b/arch/m68k/sun3/sun3dvma.c @@ -275,7 +275,7 @@ void dvma_init(void) } -inline unsigned long dvma_map_align(unsigned long kaddr, int len, int align) +unsigned long dvma_map_align(unsigned long kaddr, int len, int align) { unsigned long baddr; diff --git a/arch/mips/loongson/lemote-2f/clock.c b/arch/mips/loongson/lemote-2f/clock.c index bc739d4bab2e..4dc2f5fa3f67 100644 --- a/arch/mips/loongson/lemote-2f/clock.c +++ b/arch/mips/loongson/lemote-2f/clock.c @@ -121,7 +121,8 @@ int clk_set_rate(struct clk *clk, unsigned long rate) clk->rate = rate; regval = LOONGSON_CHIPCFG0; - regval = (regval & ~0x7) | (loongson2_clockmod_table[i].index - 1); + regval = (regval & ~0x7) | + (loongson2_clockmod_table[i].driver_data - 1); LOONGSON_CHIPCFG0 = regval; return ret; diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild index f20d01d9aaf9..195653e851da 100644 --- a/arch/openrisc/include/asm/Kbuild +++ b/arch/openrisc/include/asm/Kbuild @@ -66,3 +66,4 @@ generic-y += types.h generic-y += ucontext.h generic-y += user.h generic-y += word-at-a-time.h +generic-y += xor.h diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h index 349ed85c7d61..08891d07aeb6 100644 --- a/arch/powerpc/include/asm/kvm_book3s.h +++ b/arch/powerpc/include/asm/kvm_book3s.h @@ -107,8 +107,9 @@ struct kvmppc_vcpu_book3s { #define CONTEXT_GUEST 1 #define CONTEXT_GUEST_END 2 -#define VSID_REAL 0x1fffffffffc00000ULL -#define VSID_BAT 0x1fffffffffb00000ULL +#define VSID_REAL 0x0fffffffffc00000ULL +#define VSID_BAT 0x0fffffffffb00000ULL +#define VSID_1T 0x1000000000000000ULL #define VSID_REAL_DR 0x2000000000000000ULL #define VSID_REAL_IR 0x4000000000000000ULL #define VSID_PR 0x8000000000000000ULL @@ -123,6 +124,7 @@ extern void kvmppc_mmu_book3s_32_init(struct kvm_vcpu *vcpu); extern void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu); extern int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte); extern int kvmppc_mmu_map_segment(struct kvm_vcpu *vcpu, ulong eaddr); +extern void kvmppc_mmu_flush_segment(struct kvm_vcpu *vcpu, ulong eaddr, ulong seg_size); extern void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu); extern int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, unsigned long addr, diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 2a67e9baa59f..6b0ba5854d99 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -128,7 +128,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, const char *type; struct pci_slot *slot; - dev = alloc_pci_dev(); + dev = pci_alloc_dev(bus); if (!dev) return NULL; type = of_get_property(node, "device_type", NULL); @@ -137,7 +137,6 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, pr_debug(" create device, devfn: %x, type: %s\n", devfn, type); - dev->bus = bus; dev->dev.of_node = of_node_get(node); dev->dev.parent = bus->bridge; dev->dev.bus = &pci_bus_type; @@ -165,7 +164,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, pr_debug(" class: 0x%x\n", dev->class); pr_debug(" revision: 0x%x\n", dev->revision); - dev->current_state = 4; /* unknown power state */ + dev->current_state = PCI_UNKNOWN; /* unknown power state */ dev->error_state = pci_channel_io_normal; dev->dma_mask = 0xffffffff; diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile index 422de3f4d46c..008cd856c5b5 100644 --- a/arch/powerpc/kvm/Makefile +++ b/arch/powerpc/kvm/Makefile @@ -5,9 +5,10 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror ccflags-y := -Ivirt/kvm -Iarch/powerpc/kvm +KVM := ../../../virt/kvm -common-objs-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o \ - eventfd.o) +common-objs-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ + $(KVM)/eventfd.o CFLAGS_44x_tlb.o := -I. CFLAGS_e500_mmu.o := -I. @@ -53,7 +54,7 @@ kvm-e500mc-objs := \ kvm-objs-$(CONFIG_KVM_E500MC) := $(kvm-e500mc-objs) kvm-book3s_64-objs-$(CONFIG_KVM_BOOK3S_64_PR) := \ - ../../../virt/kvm/coalesced_mmio.o \ + $(KVM)/coalesced_mmio.o \ fpu.o \ book3s_paired_singles.o \ book3s_pr.o \ @@ -86,8 +87,8 @@ kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \ book3s_xics.o kvm-book3s_64-module-objs := \ - ../../../virt/kvm/kvm_main.o \ - ../../../virt/kvm/eventfd.o \ + $(KVM)/kvm_main.o \ + $(KVM)/eventfd.o \ powerpc.o \ emulate.o \ book3s.o \ @@ -111,7 +112,7 @@ kvm-book3s_32-objs := \ kvm-objs-$(CONFIG_KVM_BOOK3S_32) := $(kvm-book3s_32-objs) kvm-objs-$(CONFIG_KVM_MPIC) += mpic.o -kvm-objs-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(addprefix ../../../virt/kvm/, irqchip.o) +kvm-objs-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(KVM)/irqchip.o kvm-objs := $(kvm-objs-m) $(kvm-objs-y) diff --git a/arch/powerpc/kvm/book3s_64_mmu.c b/arch/powerpc/kvm/book3s_64_mmu.c index b871721c0050..739bfbadb85e 100644 --- a/arch/powerpc/kvm/book3s_64_mmu.c +++ b/arch/powerpc/kvm/book3s_64_mmu.c @@ -26,6 +26,7 @@ #include <asm/tlbflush.h> #include <asm/kvm_ppc.h> #include <asm/kvm_book3s.h> +#include <asm/mmu-hash64.h> /* #define DEBUG_MMU */ @@ -76,6 +77,24 @@ static struct kvmppc_slb *kvmppc_mmu_book3s_64_find_slbe( return NULL; } +static int kvmppc_slb_sid_shift(struct kvmppc_slb *slbe) +{ + return slbe->tb ? SID_SHIFT_1T : SID_SHIFT; +} + +static u64 kvmppc_slb_offset_mask(struct kvmppc_slb *slbe) +{ + return (1ul << kvmppc_slb_sid_shift(slbe)) - 1; +} + +static u64 kvmppc_slb_calc_vpn(struct kvmppc_slb *slb, gva_t eaddr) +{ + eaddr &= kvmppc_slb_offset_mask(slb); + + return (eaddr >> VPN_SHIFT) | + ((slb->vsid) << (kvmppc_slb_sid_shift(slb) - VPN_SHIFT)); +} + static u64 kvmppc_mmu_book3s_64_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr, bool data) { @@ -85,11 +104,7 @@ static u64 kvmppc_mmu_book3s_64_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr, if (!slb) return 0; - if (slb->tb) - return (((u64)eaddr >> 12) & 0xfffffff) | - (((u64)slb->vsid) << 28); - - return (((u64)eaddr >> 12) & 0xffff) | (((u64)slb->vsid) << 16); + return kvmppc_slb_calc_vpn(slb, eaddr); } static int kvmppc_mmu_book3s_64_get_pagesize(struct kvmppc_slb *slbe) @@ -100,7 +115,8 @@ static int kvmppc_mmu_book3s_64_get_pagesize(struct kvmppc_slb *slbe) static u32 kvmppc_mmu_book3s_64_get_page(struct kvmppc_slb *slbe, gva_t eaddr) { int p = kvmppc_mmu_book3s_64_get_pagesize(slbe); - return ((eaddr & 0xfffffff) >> p); + + return ((eaddr & kvmppc_slb_offset_mask(slbe)) >> p); } static hva_t kvmppc_mmu_book3s_64_get_pteg( @@ -109,13 +125,15 @@ static hva_t kvmppc_mmu_book3s_64_get_pteg( bool second) { u64 hash, pteg, htabsize; - u32 page; + u32 ssize; hva_t r; + u64 vpn; - page = kvmppc_mmu_book3s_64_get_page(slbe, eaddr); htabsize = ((1 << ((vcpu_book3s->sdr1 & 0x1f) + 11)) - 1); - hash = slbe->vsid ^ page; + vpn = kvmppc_slb_calc_vpn(slbe, eaddr); + ssize = slbe->tb ? MMU_SEGSIZE_1T : MMU_SEGSIZE_256M; + hash = hpt_hash(vpn, kvmppc_mmu_book3s_64_get_pagesize(slbe), ssize); if (second) hash = ~hash; hash &= ((1ULL << 39ULL) - 1ULL); @@ -146,7 +164,7 @@ static u64 kvmppc_mmu_book3s_64_get_avpn(struct kvmppc_slb *slbe, gva_t eaddr) u64 avpn; avpn = kvmppc_mmu_book3s_64_get_page(slbe, eaddr); - avpn |= slbe->vsid << (28 - p); + avpn |= slbe->vsid << (kvmppc_slb_sid_shift(slbe) - p); if (p < 24) avpn >>= ((80 - p) - 56) - 8; @@ -167,7 +185,6 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr, int i; u8 key = 0; bool found = false; - bool perm_err = false; int second = 0; ulong mp_ea = vcpu->arch.magic_page_ea; @@ -190,13 +207,15 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr, if (!slbe) goto no_seg_found; + avpn = kvmppc_mmu_book3s_64_get_avpn(slbe, eaddr); + if (slbe->tb) + avpn |= SLB_VSID_B_1T; + do_second: ptegp = kvmppc_mmu_book3s_64_get_pteg(vcpu_book3s, slbe, eaddr, second); if (kvm_is_error_hva(ptegp)) goto no_page_found; - avpn = kvmppc_mmu_book3s_64_get_avpn(slbe, eaddr); - if(copy_from_user(pteg, (void __user *)ptegp, sizeof(pteg))) { printk(KERN_ERR "KVM can't copy data from 0x%lx!\n", ptegp); goto no_page_found; @@ -219,7 +238,7 @@ do_second: continue; /* AVPN compare */ - if (HPTE_V_AVPN_VAL(avpn) == HPTE_V_AVPN_VAL(v)) { + if (HPTE_V_COMPARE(avpn, v)) { u8 pp = (r & HPTE_R_PP) | key; int eaddr_mask = 0xFFF; @@ -248,11 +267,6 @@ do_second: break; } - if (!gpte->may_read) { - perm_err = true; - continue; - } - dprintk("KVM MMU: Translated 0x%lx [0x%llx] -> 0x%llx " "-> 0x%lx\n", eaddr, avpn, gpte->vpage, gpte->raddr); @@ -281,6 +295,8 @@ do_second: if (pteg[i+1] != oldr) copy_to_user((void __user *)ptegp, pteg, sizeof(pteg)); + if (!gpte->may_read) + return -EPERM; return 0; } else { dprintk("KVM MMU: No PTE found (ea=0x%lx sdr1=0x%llx " @@ -296,13 +312,7 @@ do_second: } } - no_page_found: - - - if (perm_err) - return -EPERM; - return -ENOENT; no_seg_found: @@ -334,7 +344,7 @@ static void kvmppc_mmu_book3s_64_slbmte(struct kvm_vcpu *vcpu, u64 rs, u64 rb) slbe->large = (rs & SLB_VSID_L) ? 1 : 0; slbe->tb = (rs & SLB_VSID_B_1T) ? 1 : 0; slbe->esid = slbe->tb ? esid_1t : esid; - slbe->vsid = rs >> 12; + slbe->vsid = (rs & ~SLB_VSID_B) >> (kvmppc_slb_sid_shift(slbe) - 16); slbe->valid = (rb & SLB_ESID_V) ? 1 : 0; slbe->Ks = (rs & SLB_VSID_KS) ? 1 : 0; slbe->Kp = (rs & SLB_VSID_KP) ? 1 : 0; @@ -375,6 +385,7 @@ static u64 kvmppc_mmu_book3s_64_slbmfev(struct kvm_vcpu *vcpu, u64 slb_nr) static void kvmppc_mmu_book3s_64_slbie(struct kvm_vcpu *vcpu, u64 ea) { struct kvmppc_slb *slbe; + u64 seg_size; dprintk("KVM MMU: slbie(0x%llx)\n", ea); @@ -386,8 +397,11 @@ static void kvmppc_mmu_book3s_64_slbie(struct kvm_vcpu *vcpu, u64 ea) dprintk("KVM MMU: slbie(0x%llx, 0x%llx)\n", ea, slbe->esid); slbe->valid = false; + slbe->orige = 0; + slbe->origv = 0; - kvmppc_mmu_map_segment(vcpu, ea); + seg_size = 1ull << kvmppc_slb_sid_shift(slbe); + kvmppc_mmu_flush_segment(vcpu, ea & ~(seg_size - 1), seg_size); } static void kvmppc_mmu_book3s_64_slbia(struct kvm_vcpu *vcpu) @@ -396,8 +410,11 @@ static void kvmppc_mmu_book3s_64_slbia(struct kvm_vcpu *vcpu) dprintk("KVM MMU: slbia()\n"); - for (i = 1; i < vcpu->arch.slb_nr; i++) + for (i = 1; i < vcpu->arch.slb_nr; i++) { vcpu->arch.slb[i].valid = false; + vcpu->arch.slb[i].orige = 0; + vcpu->arch.slb[i].origv = 0; + } if (vcpu->arch.shared->msr & MSR_IR) { kvmppc_mmu_flush_segments(vcpu); @@ -467,8 +484,14 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid, if (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) { slb = kvmppc_mmu_book3s_64_find_slbe(vcpu, ea); - if (slb) + if (slb) { gvsid = slb->vsid; + if (slb->tb) { + gvsid <<= SID_SHIFT_1T - SID_SHIFT; + gvsid |= esid & ((1ul << (SID_SHIFT_1T - SID_SHIFT)) - 1); + gvsid |= VSID_1T; + } + } } switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) { diff --git a/arch/powerpc/kvm/book3s_64_mmu_host.c b/arch/powerpc/kvm/book3s_64_mmu_host.c index 3a9a1aceb14f..b350d9494b26 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_host.c +++ b/arch/powerpc/kvm/book3s_64_mmu_host.c @@ -301,6 +301,23 @@ out: return r; } +void kvmppc_mmu_flush_segment(struct kvm_vcpu *vcpu, ulong ea, ulong seg_size) +{ + struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu); + ulong seg_mask = -seg_size; + int i; + + for (i = 1; i < svcpu->slb_max; i++) { + if ((svcpu->slb[i].esid & SLB_ESID_V) && + (svcpu->slb[i].esid & seg_mask) == ea) { + /* Invalidate this entry */ + svcpu->slb[i].esid = 0; + } + } + + svcpu_put(svcpu); +} + void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu) { struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu); @@ -325,9 +342,9 @@ int kvmppc_mmu_init(struct kvm_vcpu *vcpu) return -1; vcpu3s->context_id[0] = err; - vcpu3s->proto_vsid_max = ((vcpu3s->context_id[0] + 1) + vcpu3s->proto_vsid_max = ((u64)(vcpu3s->context_id[0] + 1) << ESID_BITS) - 1; - vcpu3s->proto_vsid_first = vcpu3s->context_id[0] << ESID_BITS; + vcpu3s->proto_vsid_first = (u64)vcpu3s->context_id[0] << ESID_BITS; vcpu3s->proto_vsid_next = vcpu3s->proto_vsid_first; kvmppc_mmu_hpte_init(vcpu); diff --git a/arch/powerpc/kvm/book3s_64_slb.S b/arch/powerpc/kvm/book3s_64_slb.S index 56b983e7b738..4f0caecc0f9d 100644 --- a/arch/powerpc/kvm/book3s_64_slb.S +++ b/arch/powerpc/kvm/book3s_64_slb.S @@ -66,10 +66,6 @@ slb_exit_skip_ ## num: ld r12, PACA_SLBSHADOWPTR(r13) - /* Save off the first entry so we can slbie it later */ - ld r10, SHADOW_SLB_ESID(0)(r12) - ld r11, SHADOW_SLB_VSID(0)(r12) - /* Remove bolted entries */ UNBOLT_SLB_ENTRY(0) UNBOLT_SLB_ENTRY(1) @@ -81,15 +77,10 @@ slb_exit_skip_ ## num: /* Flush SLB */ + li r10, 0 + slbmte r10, r10 slbia - /* r0 = esid & ESID_MASK */ - rldicr r10, r10, 0, 35 - /* r0 |= CLASS_BIT(VSID) */ - rldic r12, r11, 56 - 36, 36 - or r10, r10, r12 - slbie r10 - /* Fill SLB with our shadow */ lbz r12, SVCPU_SLB_MAX(r3) diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index bdc40b8e77d9..19498a567a81 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -1239,8 +1239,7 @@ out: #ifdef CONFIG_PPC64 int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm, struct kvm_ppc_smmu_info *info) { - /* No flags */ - info->flags = 0; + info->flags = KVM_PPC_1T_SEGMENTS; /* SLB is always 64 entries */ info->slb_size = 64; diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 1a1b51189773..dcc94f016007 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -796,7 +796,7 @@ static void kvmppc_restart_interrupt(struct kvm_vcpu *vcpu, kvmppc_fill_pt_regs(®s); timer_interrupt(®s); break; -#if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3E_64) +#if defined(CONFIG_PPC_DOORBELL) case BOOKE_INTERRUPT_DOORBELL: kvmppc_fill_pt_regs(®s); doorbell_exception(®s); diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index 631a2650e4e4..2c52ada30775 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -169,6 +169,9 @@ static int kvmppc_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) vcpu->arch.shared->sprg3 = spr_val; break; + /* PIR can legally be written, but we ignore it */ + case SPRN_PIR: break; + default: emulated = kvmppc_core_emulate_mtspr(vcpu, sprn, spr_val); diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index b62aab3e22ec..e17cdfc5ba40 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -193,37 +193,6 @@ config PPC_IO_WORKAROUNDS source "drivers/cpufreq/Kconfig" -menu "CPU Frequency drivers" - depends on CPU_FREQ - -config CPU_FREQ_PMAC - bool "Support for Apple PowerBooks" - depends on ADB_PMU && PPC32 - select CPU_FREQ_TABLE - help - This adds support for frequency switching on Apple PowerBooks, - this currently includes some models of iBook & Titanium - PowerBook. - -config CPU_FREQ_PMAC64 - bool "Support for some Apple G5s" - depends on PPC_PMAC && PPC64 - select CPU_FREQ_TABLE - help - This adds support for frequency switching on Apple iMac G5, - and some of the more recent desktop G5 machines as well. - -config PPC_PASEMI_CPUFREQ - bool "Support for PA Semi PWRficient" - depends on PPC_PASEMI - default y - select CPU_FREQ_TABLE - help - This adds the support for frequency switching on PA Semi - PWRficient processors. - -endmenu - menu "CPUIdle driver" source "drivers/cpuidle/Kconfig" diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 54f3936001aa..7819c40a6bc3 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -158,6 +158,7 @@ config E500 config PPC_E500MC bool "e500mc Support" select PPC_FPU + select COMMON_CLK depends on E500 help This must be enabled for running on e500mc (and derivatives diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile index ce6d789e0741..8e8d4cae5ebe 100644 --- a/arch/powerpc/platforms/pasemi/Makefile +++ b/arch/powerpc/platforms/pasemi/Makefile @@ -1,3 +1,2 @@ obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o dma_lib.o misc.o obj-$(CONFIG_PPC_PASEMI_MDIO) += gpio_mdio.o -obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o diff --git a/arch/powerpc/platforms/powermac/Makefile b/arch/powerpc/platforms/powermac/Makefile index ea47df66fee5..52c6ce1cc985 100644 --- a/arch/powerpc/platforms/powermac/Makefile +++ b/arch/powerpc/platforms/powermac/Makefile @@ -9,8 +9,6 @@ obj-y += pic.o setup.o time.o feature.o pci.o \ sleep.o low_i2c.o cache.o pfunc_core.o \ pfunc_base.o udbg_scc.o udbg_adb.o obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o -obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o -obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o # CONFIG_NVRAM is an arch. independent tristate symbol, for pmac32 we really # need this to be a bool. Cheat here and pretend CONFIG_NVRAM=m is really # CONFIG_NVRAM=y diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c index 7ef60b52d6e0..42be53743133 100644 --- a/arch/s390/appldata/appldata_mem.c +++ b/arch/s390/appldata/appldata_mem.c @@ -32,7 +32,7 @@ * book: * http://oss.software.ibm.com/developerworks/opensource/linux390/index.shtml */ -static struct appldata_mem_data { +struct appldata_mem_data { u64 timestamp; u32 sync_count_1; /* after VM collected the record data, */ u32 sync_count_2; /* sync_count_1 and sync_count_2 should be the @@ -63,7 +63,7 @@ static struct appldata_mem_data { u64 pgmajfault; /* page faults (major only) */ // <-- New in 2.6 -} __attribute__((packed)) appldata_mem_data; +} __packed; /* @@ -118,7 +118,6 @@ static struct appldata_ops ops = { .record_nr = APPLDATA_RECORD_MEM_ID, .size = sizeof(struct appldata_mem_data), .callback = &appldata_get_mem_data, - .data = &appldata_mem_data, .owner = THIS_MODULE, .mod_lvl = {0xF0, 0xF0}, /* EBCDIC "00" */ }; @@ -131,7 +130,17 @@ static struct appldata_ops ops = { */ static int __init appldata_mem_init(void) { - return appldata_register_ops(&ops); + int ret; + + ops.data = kzalloc(sizeof(struct appldata_mem_data), GFP_KERNEL); + if (!ops.data) + return -ENOMEM; + + ret = appldata_register_ops(&ops); + if (ret) + kfree(ops.data); + + return ret; } /* @@ -142,6 +151,7 @@ static int __init appldata_mem_init(void) static void __exit appldata_mem_exit(void) { appldata_unregister_ops(&ops); + kfree(ops.data); } diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c index 2d224b945355..66037d2622b4 100644 --- a/arch/s390/appldata/appldata_net_sum.c +++ b/arch/s390/appldata/appldata_net_sum.c @@ -29,7 +29,7 @@ * book: * http://oss.software.ibm.com/developerworks/opensource/linux390/index.shtml */ -static struct appldata_net_sum_data { +struct appldata_net_sum_data { u64 timestamp; u32 sync_count_1; /* after VM collected the record data, */ u32 sync_count_2; /* sync_count_1 and sync_count_2 should be the @@ -51,7 +51,7 @@ static struct appldata_net_sum_data { u64 rx_dropped; /* no space in linux buffers */ u64 tx_dropped; /* no space available in linux */ u64 collisions; /* collisions while transmitting */ -} __attribute__((packed)) appldata_net_sum_data; +} __packed; /* @@ -121,7 +121,6 @@ static struct appldata_ops ops = { .record_nr = APPLDATA_RECORD_NET_SUM_ID, .size = sizeof(struct appldata_net_sum_data), .callback = &appldata_get_net_sum_data, - .data = &appldata_net_sum_data, .owner = THIS_MODULE, .mod_lvl = {0xF0, 0xF0}, /* EBCDIC "00" */ }; @@ -134,7 +133,17 @@ static struct appldata_ops ops = { */ static int __init appldata_net_init(void) { - return appldata_register_ops(&ops); + int ret; + + ops.data = kzalloc(sizeof(struct appldata_net_sum_data), GFP_KERNEL); + if (!ops.data) + return -ENOMEM; + + ret = appldata_register_ops(&ops); + if (ret) + kfree(ops.data); + + return ret; } /* @@ -145,6 +154,7 @@ static int __init appldata_net_init(void) static void __exit appldata_net_exit(void) { appldata_unregister_ops(&ops); + kfree(ops.data); } diff --git a/arch/s390/hypfs/hypfs_diag.c b/arch/s390/hypfs/hypfs_diag.c index 7fd3690b6760..138893e5f736 100644 --- a/arch/s390/hypfs/hypfs_diag.c +++ b/arch/s390/hypfs/hypfs_diag.c @@ -651,9 +651,7 @@ static int hypfs_create_cpu_files(struct super_block *sb, } diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer); rc = hypfs_create_str(sb, cpu_dir, "type", buffer); - if (IS_ERR(rc)) - return PTR_ERR(rc); - return 0; + return PTR_RET(rc); } static void *hypfs_create_lpar_files(struct super_block *sb, @@ -702,9 +700,7 @@ static int hypfs_create_phys_cpu_files(struct super_block *sb, return PTR_ERR(rc); diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer); rc = hypfs_create_str(sb, cpu_dir, "type", buffer); - if (IS_ERR(rc)) - return PTR_ERR(rc); - return 0; + return PTR_RET(rc); } static void *hypfs_create_phys_files(struct super_block *sb, diff --git a/arch/s390/include/asm/airq.h b/arch/s390/include/asm/airq.h index 9819891ed7a2..4066cee0c2d2 100644 --- a/arch/s390/include/asm/airq.h +++ b/arch/s390/include/asm/airq.h @@ -9,9 +9,18 @@ #ifndef _ASM_S390_AIRQ_H #define _ASM_S390_AIRQ_H -typedef void (*adapter_int_handler_t)(void *, void *); +struct airq_struct { + struct hlist_node list; /* Handler queueing. */ + void (*handler)(struct airq_struct *); /* Thin-interrupt handler */ + u8 *lsi_ptr; /* Local-Summary-Indicator pointer */ + u8 lsi_mask; /* Local-Summary-Indicator mask */ + u8 isc; /* Interrupt-subclass */ + u8 flags; +}; -void *s390_register_adapter_interrupt(adapter_int_handler_t, void *, u8); -void s390_unregister_adapter_interrupt(void *, u8); +#define AIRQ_PTR_ALLOCATED 0x01 + +int register_adapter_interrupt(struct airq_struct *airq); +void unregister_adapter_interrupt(struct airq_struct *airq); #endif /* _ASM_S390_AIRQ_H */ diff --git a/arch/s390/include/asm/dma-mapping.h b/arch/s390/include/asm/dma-mapping.h index 2f8c1abeb086..3fbc67d9e197 100644 --- a/arch/s390/include/asm/dma-mapping.h +++ b/arch/s390/include/asm/dma-mapping.h @@ -53,7 +53,7 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) debug_dma_mapping_error(dev, dma_addr); if (dma_ops->mapping_error) return dma_ops->mapping_error(dev, dma_addr); - return (dma_addr == DMA_ERROR_CODE); + return dma_addr == DMA_ERROR_CODE; } static inline void *dma_alloc_coherent(struct device *dev, size_t size, diff --git a/arch/s390/include/asm/facility.h b/arch/s390/include/asm/facility.h index 2ee66a65f2d4..0aa6a7ed95a3 100644 --- a/arch/s390/include/asm/facility.h +++ b/arch/s390/include/asm/facility.h @@ -13,6 +13,16 @@ #define MAX_FACILITY_BIT (256*8) /* stfle_fac_list has 256 bytes */ +static inline int __test_facility(unsigned long nr, void *facilities) +{ + unsigned char *ptr; + + if (nr >= MAX_FACILITY_BIT) + return 0; + ptr = (unsigned char *) facilities + (nr >> 3); + return (*ptr & (0x80 >> (nr & 7))) != 0; +} + /* * The test_facility function uses the bit odering where the MSB is bit 0. * That makes it easier to query facility bits with the bit number as @@ -20,12 +30,7 @@ */ static inline int test_facility(unsigned long nr) { - unsigned char *ptr; - - if (nr >= MAX_FACILITY_BIT) - return 0; - ptr = (unsigned char *) &S390_lowcore.stfle_fac_list + (nr >> 3); - return (*ptr & (0x80 >> (nr & 7))) != 0; + return __test_facility(nr, &S390_lowcore.stfle_fac_list); } /** diff --git a/arch/s390/include/asm/io.h b/arch/s390/include/asm/io.h index fd9be010f9b2..cd6b9ee7b69c 100644 --- a/arch/s390/include/asm/io.h +++ b/arch/s390/include/asm/io.h @@ -13,28 +13,6 @@ #include <asm/page.h> #include <asm/pci_io.h> -/* - * Change virtual addresses to physical addresses and vv. - * These are pretty trivial - */ -static inline unsigned long virt_to_phys(volatile void * address) -{ - unsigned long real_address; - asm volatile( - " lra %0,0(%1)\n" - " jz 0f\n" - " la %0,0\n" - "0:" - : "=a" (real_address) : "a" (address) : "cc"); - return real_address; -} -#define virt_to_phys virt_to_phys - -static inline void * phys_to_virt(unsigned long address) -{ - return (void *) address; -} - void *xlate_dev_mem_ptr(unsigned long phys); #define xlate_dev_mem_ptr xlate_dev_mem_ptr void unxlate_dev_mem_ptr(unsigned long phys, void *addr); diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 16bd5d169cdb..3238d4004e84 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -62,13 +62,20 @@ struct sca_block { #define CPUSTAT_MCDS 0x00000100 #define CPUSTAT_SM 0x00000080 #define CPUSTAT_G 0x00000008 +#define CPUSTAT_GED 0x00000004 #define CPUSTAT_J 0x00000002 #define CPUSTAT_P 0x00000001 struct kvm_s390_sie_block { atomic_t cpuflags; /* 0x0000 */ __u32 prefix; /* 0x0004 */ - __u8 reserved8[32]; /* 0x0008 */ + __u8 reserved08[4]; /* 0x0008 */ +#define PROG_IN_SIE (1<<0) + __u32 prog0c; /* 0x000c */ + __u8 reserved10[16]; /* 0x0010 */ +#define PROG_BLOCK_SIE 0x00000001 + atomic_t prog20; /* 0x0020 */ + __u8 reserved24[4]; /* 0x0024 */ __u64 cputm; /* 0x0028 */ __u64 ckc; /* 0x0030 */ __u64 epoch; /* 0x0038 */ @@ -90,7 +97,8 @@ struct kvm_s390_sie_block { __u32 scaoh; /* 0x005c */ __u8 reserved60; /* 0x0060 */ __u8 ecb; /* 0x0061 */ - __u8 reserved62[2]; /* 0x0062 */ + __u8 ecb2; /* 0x0062 */ + __u8 reserved63[1]; /* 0x0063 */ __u32 scaol; /* 0x0064 */ __u8 reserved68[4]; /* 0x0068 */ __u32 todpr; /* 0x006c */ @@ -130,6 +138,7 @@ struct kvm_vcpu_stat { u32 deliver_program_int; u32 deliver_io_int; u32 exit_wait_state; + u32 instruction_pfmf; u32 instruction_stidp; u32 instruction_spx; u32 instruction_stpx; @@ -166,7 +175,7 @@ struct kvm_s390_ext_info { }; #define PGM_OPERATION 0x01 -#define PGM_PRIVILEGED_OPERATION 0x02 +#define PGM_PRIVILEGED_OP 0x02 #define PGM_EXECUTE 0x03 #define PGM_PROTECTION 0x04 #define PGM_ADDRESSING 0x05 @@ -219,7 +228,7 @@ struct kvm_s390_local_interrupt { atomic_t active; struct kvm_s390_float_interrupt *float_int; int timer_due; /* event indicator for waitqueue below */ - wait_queue_head_t wq; + wait_queue_head_t *wq; atomic_t *cpuflags; unsigned int action_bits; }; @@ -266,4 +275,5 @@ struct kvm_arch{ }; extern int sie64a(struct kvm_s390_sie_block *, u64 *); +extern char sie_exit; #endif diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 6c1801235db9..6e577ba0e5da 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -120,7 +120,6 @@ struct zpci_dev { struct dentry *debugfs_dev; struct dentry *debugfs_perf; - struct dentry *debugfs_debug; }; struct pci_hp_callback_ops { @@ -143,7 +142,6 @@ int zpci_enable_device(struct zpci_dev *); int zpci_disable_device(struct zpci_dev *); void zpci_stop_device(struct zpci_dev *); void zpci_free_device(struct zpci_dev *); -int zpci_scan_device(struct zpci_dev *); int zpci_register_ioat(struct zpci_dev *, u8, u64, u64, u64); int zpci_unregister_ioat(struct zpci_dev *, u8); diff --git a/arch/s390/include/asm/perf_event.h b/arch/s390/include/asm/perf_event.h index 5f0173a31693..1141fb3e7b21 100644 --- a/arch/s390/include/asm/perf_event.h +++ b/arch/s390/include/asm/perf_event.h @@ -14,3 +14,13 @@ /* Per-CPU flags for PMU states */ #define PMU_F_RESERVED 0x1000 #define PMU_F_ENABLED 0x2000 + +#ifdef CONFIG_64BIT + +/* Perf callbacks */ +struct pt_regs; +extern unsigned long perf_instruction_pointer(struct pt_regs *regs); +extern unsigned long perf_misc_flags(struct pt_regs *regs); +#define perf_misc_flags(regs) perf_misc_flags(regs) + +#endif /* CONFIG_64BIT */ diff --git a/arch/s390/include/asm/pgalloc.h b/arch/s390/include/asm/pgalloc.h index 590c3219c634..e1408ddb94f8 100644 --- a/arch/s390/include/asm/pgalloc.h +++ b/arch/s390/include/asm/pgalloc.h @@ -22,6 +22,9 @@ unsigned long *page_table_alloc(struct mm_struct *, unsigned long); void page_table_free(struct mm_struct *, unsigned long *); void page_table_free_rcu(struct mmu_gather *, unsigned long *); +int set_guest_storage_key(struct mm_struct *mm, unsigned long addr, + unsigned long key, bool nq); + static inline void clear_table(unsigned long *s, unsigned long val, size_t n) { typedef struct { char _[n]; } addrtype; diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 9aefa3c64eb2..0ea4e591fa78 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -296,18 +296,16 @@ extern unsigned long MODULES_END; #define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INV) /* Page status table bits for virtualization */ -#define RCP_ACC_BITS 0xf0000000UL -#define RCP_FP_BIT 0x08000000UL -#define RCP_PCL_BIT 0x00800000UL -#define RCP_HR_BIT 0x00400000UL -#define RCP_HC_BIT 0x00200000UL -#define RCP_GR_BIT 0x00040000UL -#define RCP_GC_BIT 0x00020000UL -#define RCP_IN_BIT 0x00002000UL /* IPTE notify bit */ - -/* User dirty / referenced bit for KVM's migration feature */ -#define KVM_UR_BIT 0x00008000UL -#define KVM_UC_BIT 0x00004000UL +#define PGSTE_ACC_BITS 0xf0000000UL +#define PGSTE_FP_BIT 0x08000000UL +#define PGSTE_PCL_BIT 0x00800000UL +#define PGSTE_HR_BIT 0x00400000UL +#define PGSTE_HC_BIT 0x00200000UL +#define PGSTE_GR_BIT 0x00040000UL +#define PGSTE_GC_BIT 0x00020000UL +#define PGSTE_UR_BIT 0x00008000UL +#define PGSTE_UC_BIT 0x00004000UL /* user dirty (migration) */ +#define PGSTE_IN_BIT 0x00002000UL /* IPTE notify bit */ #else /* CONFIG_64BIT */ @@ -364,18 +362,16 @@ extern unsigned long MODULES_END; | _SEGMENT_ENTRY_SPLIT | _SEGMENT_ENTRY_CO) /* Page status table bits for virtualization */ -#define RCP_ACC_BITS 0xf000000000000000UL -#define RCP_FP_BIT 0x0800000000000000UL -#define RCP_PCL_BIT 0x0080000000000000UL -#define RCP_HR_BIT 0x0040000000000000UL -#define RCP_HC_BIT 0x0020000000000000UL -#define RCP_GR_BIT 0x0004000000000000UL -#define RCP_GC_BIT 0x0002000000000000UL -#define RCP_IN_BIT 0x0000200000000000UL /* IPTE notify bit */ - -/* User dirty / referenced bit for KVM's migration feature */ -#define KVM_UR_BIT 0x0000800000000000UL -#define KVM_UC_BIT 0x0000400000000000UL +#define PGSTE_ACC_BITS 0xf000000000000000UL +#define PGSTE_FP_BIT 0x0800000000000000UL +#define PGSTE_PCL_BIT 0x0080000000000000UL +#define PGSTE_HR_BIT 0x0040000000000000UL +#define PGSTE_HC_BIT 0x0020000000000000UL +#define PGSTE_GR_BIT 0x0004000000000000UL +#define PGSTE_GC_BIT 0x0002000000000000UL +#define PGSTE_UR_BIT 0x0000800000000000UL +#define PGSTE_UC_BIT 0x0000400000000000UL /* user dirty (migration) */ +#define PGSTE_IN_BIT 0x0000200000000000UL /* IPTE notify bit */ #endif /* CONFIG_64BIT */ @@ -615,8 +611,8 @@ static inline pgste_t pgste_get_lock(pte_t *ptep) asm( " lg %0,%2\n" "0: lgr %1,%0\n" - " nihh %0,0xff7f\n" /* clear RCP_PCL_BIT in old */ - " oihh %1,0x0080\n" /* set RCP_PCL_BIT in new */ + " nihh %0,0xff7f\n" /* clear PCL bit in old */ + " oihh %1,0x0080\n" /* set PCL bit in new */ " csg %0,%1,%2\n" " jl 0b\n" : "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE]) @@ -629,7 +625,7 @@ static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste) { #ifdef CONFIG_PGSTE asm( - " nihh %1,0xff7f\n" /* clear RCP_PCL_BIT */ + " nihh %1,0xff7f\n" /* clear PCL bit */ " stg %1,%0\n" : "=Q" (ptep[PTRS_PER_PTE]) : "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE]) @@ -662,14 +658,14 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) else if (bits) page_reset_referenced(address); /* Transfer page changed & referenced bit to guest bits in pgste */ - pgste_val(pgste) |= bits << 48; /* RCP_GR_BIT & RCP_GC_BIT */ + pgste_val(pgste) |= bits << 48; /* GR bit & GC bit */ /* Get host changed & referenced bits from pgste */ - bits |= (pgste_val(pgste) & (RCP_HR_BIT | RCP_HC_BIT)) >> 52; + bits |= (pgste_val(pgste) & (PGSTE_HR_BIT | PGSTE_HC_BIT)) >> 52; /* Transfer page changed & referenced bit to kvm user bits */ - pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */ + pgste_val(pgste) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */ /* Clear relevant host bits in pgste. */ - pgste_val(pgste) &= ~(RCP_HR_BIT | RCP_HC_BIT); - pgste_val(pgste) &= ~(RCP_ACC_BITS | RCP_FP_BIT); + pgste_val(pgste) &= ~(PGSTE_HR_BIT | PGSTE_HC_BIT); + pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT); /* Copy page access key and fetch protection bit to pgste */ pgste_val(pgste) |= (unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; @@ -690,15 +686,15 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste) /* Get referenced bit from storage key */ young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK); if (young) - pgste_val(pgste) |= RCP_GR_BIT; + pgste_val(pgste) |= PGSTE_GR_BIT; /* Get host referenced bit from pgste */ - if (pgste_val(pgste) & RCP_HR_BIT) { - pgste_val(pgste) &= ~RCP_HR_BIT; + if (pgste_val(pgste) & PGSTE_HR_BIT) { + pgste_val(pgste) &= ~PGSTE_HR_BIT; young = 1; } /* Transfer referenced bit to kvm user bits and pte */ if (young) { - pgste_val(pgste) |= KVM_UR_BIT; + pgste_val(pgste) |= PGSTE_UR_BIT; pte_val(*ptep) |= _PAGE_SWR; } #endif @@ -720,7 +716,7 @@ static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry) * The guest C/R information is still in the PGSTE, set real * key C/R to 0. */ - nkey = (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56; + nkey = (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56; page_set_storage_key(address, nkey, 0); #endif } @@ -750,6 +746,7 @@ struct gmap { struct mm_struct *mm; unsigned long *table; unsigned long asce; + void *private; struct list_head crst_list; }; @@ -808,8 +805,8 @@ static inline pgste_t pgste_ipte_notify(struct mm_struct *mm, pte_t *ptep, pgste_t pgste) { #ifdef CONFIG_PGSTE - if (pgste_val(pgste) & RCP_IN_BIT) { - pgste_val(pgste) &= ~RCP_IN_BIT; + if (pgste_val(pgste) & PGSTE_IN_BIT) { + pgste_val(pgste) &= ~PGSTE_IN_BIT; gmap_do_ipte_notify(mm, addr, ptep); } #endif @@ -977,8 +974,8 @@ static inline int ptep_test_and_clear_user_dirty(struct mm_struct *mm, if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); pgste = pgste_update_all(ptep, pgste); - dirty = !!(pgste_val(pgste) & KVM_UC_BIT); - pgste_val(pgste) &= ~KVM_UC_BIT; + dirty = !!(pgste_val(pgste) & PGSTE_UC_BIT); + pgste_val(pgste) &= ~PGSTE_UC_BIT; pgste_set_unlock(ptep, pgste); return dirty; } @@ -997,8 +994,8 @@ static inline int ptep_test_and_clear_user_young(struct mm_struct *mm, if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); pgste = pgste_update_young(ptep, pgste); - young = !!(pgste_val(pgste) & KVM_UR_BIT); - pgste_val(pgste) &= ~KVM_UR_BIT; + young = !!(pgste_val(pgste) & PGSTE_UR_BIT); + pgste_val(pgste) &= ~PGSTE_UR_BIT; pgste_set_unlock(ptep, pgste); } return young; diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 559512a455da..52b56533c57c 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -24,6 +24,7 @@ struct pt_regs unsigned long gprs[NUM_GPRS]; unsigned long orig_gpr2; unsigned int int_code; + unsigned int int_parm; unsigned long int_parm_long; }; diff --git a/arch/s390/include/uapi/asm/Kbuild b/arch/s390/include/uapi/asm/Kbuild index 9ccd1905bdad..6a9a9eb645f5 100644 --- a/arch/s390/include/uapi/asm/Kbuild +++ b/arch/s390/include/uapi/asm/Kbuild @@ -35,6 +35,7 @@ header-y += siginfo.h header-y += signal.h header-y += socket.h header-y += sockios.h +header-y += sclp_ctl.h header-y += stat.h header-y += statfs.h header-y += swab.h diff --git a/arch/s390/include/uapi/asm/chsc.h b/arch/s390/include/uapi/asm/chsc.h index 1c6a7f85a581..65dc694725a8 100644 --- a/arch/s390/include/uapi/asm/chsc.h +++ b/arch/s390/include/uapi/asm/chsc.h @@ -29,6 +29,16 @@ struct chsc_async_area { __u8 data[CHSC_SIZE - sizeof(struct chsc_async_header)]; } __attribute__ ((packed)); +struct chsc_header { + __u16 length; + __u16 code; +} __attribute__ ((packed)); + +struct chsc_sync_area { + struct chsc_header header; + __u8 data[CHSC_SIZE - sizeof(struct chsc_header)]; +} __attribute__ ((packed)); + struct chsc_response_struct { __u16 length; __u16 code; @@ -126,5 +136,8 @@ struct chsc_cpd_info { #define CHSC_INFO_CCL _IOWR(CHSC_IOCTL_MAGIC, 0x86, struct chsc_comp_list) #define CHSC_INFO_CPD _IOWR(CHSC_IOCTL_MAGIC, 0x87, struct chsc_cpd_info) #define CHSC_INFO_DCAL _IOWR(CHSC_IOCTL_MAGIC, 0x88, struct chsc_dcal) +#define CHSC_START_SYNC _IOWR(CHSC_IOCTL_MAGIC, 0x89, struct chsc_sync_area) +#define CHSC_ON_CLOSE_SET _IOWR(CHSC_IOCTL_MAGIC, 0x8a, struct chsc_async_area) +#define CHSC_ON_CLOSE_REMOVE _IO(CHSC_IOCTL_MAGIC, 0x8b) #endif diff --git a/arch/s390/include/uapi/asm/dasd.h b/arch/s390/include/uapi/asm/dasd.h index 38eca3ba40e2..5812a3b2df9e 100644 --- a/arch/s390/include/uapi/asm/dasd.h +++ b/arch/s390/include/uapi/asm/dasd.h @@ -261,6 +261,10 @@ struct dasd_snid_ioctl_data { #define BIODASDQUIESCE _IO(DASD_IOCTL_LETTER,6) /* Resume IO on device */ #define BIODASDRESUME _IO(DASD_IOCTL_LETTER,7) +/* Abort all I/O on a device */ +#define BIODASDABORTIO _IO(DASD_IOCTL_LETTER, 240) +/* Allow I/O on a device */ +#define BIODASDALLOWIO _IO(DASD_IOCTL_LETTER, 241) /* retrieve API version number */ diff --git a/arch/s390/include/uapi/asm/sclp_ctl.h b/arch/s390/include/uapi/asm/sclp_ctl.h new file mode 100644 index 000000000000..f2818613ee41 --- /dev/null +++ b/arch/s390/include/uapi/asm/sclp_ctl.h @@ -0,0 +1,24 @@ +/* + * IOCTL interface for SCLP + * + * Copyright IBM Corp. 2012 + * + * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + +#ifndef _ASM_SCLP_CTL_H +#define _ASM_SCLP_CTL_H + +#include <linux/types.h> + +struct sclp_ctl_sccb { + __u32 cmdw; + __u64 sccb; +} __attribute__((packed)); + +#define SCLP_CTL_IOCTL_MAGIC 0x10 + +#define SCLP_CTL_SCCB \ + _IOWR(SCLP_CTL_IOCTL_MAGIC, 0x10, struct sclp_ctl_sccb) + +#endif diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 7a82f9f70100..2416138ebd3e 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -7,6 +7,7 @@ #define ASM_OFFSETS_C #include <linux/kbuild.h> +#include <linux/kvm_host.h> #include <linux/sched.h> #include <asm/cputime.h> #include <asm/vdso.h> @@ -47,6 +48,7 @@ int main(void) DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs)); DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2)); DEFINE(__PT_INT_CODE, offsetof(struct pt_regs, int_code)); + DEFINE(__PT_INT_PARM, offsetof(struct pt_regs, int_parm)); DEFINE(__PT_INT_PARM_LONG, offsetof(struct pt_regs, int_parm_long)); DEFINE(__PT_SIZE, sizeof(struct pt_regs)); BLANK(); @@ -161,6 +163,8 @@ int main(void) DEFINE(__LC_PGM_TDB, offsetof(struct _lowcore, pgm_tdb)); DEFINE(__THREAD_trap_tdb, offsetof(struct task_struct, thread.trap_tdb)); DEFINE(__GMAP_ASCE, offsetof(struct gmap, asce)); + DEFINE(__SIE_PROG0C, offsetof(struct kvm_s390_sie_block, prog0c)); + DEFINE(__SIE_PROG20, offsetof(struct kvm_s390_sie_block, prog20)); #endif /* CONFIG_32BIT */ return 0; } diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 4d5e6f8a7978..be7a408be7a1 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -429,11 +429,19 @@ io_skip: stm %r0,%r7,__PT_R0(%r11) mvc __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC stm %r8,%r9,__PT_PSW(%r11) + mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID TRACE_IRQS_OFF xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) +io_loop: l %r1,BASED(.Ldo_IRQ) lr %r2,%r11 # pass pointer to pt_regs basr %r14,%r1 # call do_IRQ + tm __LC_MACHINE_FLAGS+2,0x10 # MACHINE_FLAG_LPAR + jz io_return + tpi 0 + jz io_return + mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID + j io_loop io_return: LOCKDEP_SYS_EXIT TRACE_IRQS_ON @@ -573,10 +581,10 @@ ext_skip: stm %r0,%r7,__PT_R0(%r11) mvc __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC stm %r8,%r9,__PT_PSW(%r11) + mvc __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR + mvc __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS TRACE_IRQS_OFF lr %r2,%r11 # pass pointer to pt_regs - l %r3,__LC_EXT_CPU_ADDR # get cpu address + interruption code - l %r4,__LC_EXT_PARAMS # get external parameters l %r1,BASED(.Ldo_extint) basr %r14,%r1 # call do_extint j io_return diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index aa0ab02e9595..3ddbc26d246e 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -54,7 +54,7 @@ void handle_signal32(unsigned long sig, struct k_sigaction *ka, void do_notify_resume(struct pt_regs *regs); struct ext_code; -void do_extint(struct pt_regs *regs, struct ext_code, unsigned int, unsigned long); +void do_extint(struct pt_regs *regs); void do_restart(void); void __init startup_init(void); void die(struct pt_regs *regs, const char *str); diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 4c17eece707e..1c039d0c24c7 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -47,7 +47,6 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \ _TIF_MCCK_PENDING) _TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ _TIF_SYSCALL_TRACEPOINT) -_TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING) #define BASED(name) name-system_call(%r13) @@ -81,23 +80,27 @@ _TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING) #endif .endm - .macro HANDLE_SIE_INTERCEPT scratch,pgmcheck + .macro HANDLE_SIE_INTERCEPT scratch,reason #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) tmhh %r8,0x0001 # interrupting from user ? - jnz .+42 + jnz .+62 lgr \scratch,%r9 - slg \scratch,BASED(.Lsie_loop) - clg \scratch,BASED(.Lsie_length) - .if \pgmcheck + slg \scratch,BASED(.Lsie_critical) + clg \scratch,BASED(.Lsie_critical_length) + .if \reason==1 # Some program interrupts are suppressing (e.g. protection). # We must also check the instruction after SIE in that case. # do_protection_exception will rewind to rewind_pad - jh .+22 + jh .+42 .else - jhe .+22 + jhe .+42 .endif - lg %r9,BASED(.Lsie_loop) - LPP BASED(.Lhost_id) # set host id + lg %r14,__SF_EMPTY(%r15) # get control block pointer + LPP __SF_EMPTY+16(%r15) # set host id + ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE + lctlg %c1,%c1,__LC_USER_ASCE # load primary asce + larl %r9,sie_exit # skip forward to sie_exit + mvi __SF_EMPTY+31(%r15),\reason # set exit reason #endif .endm @@ -450,7 +453,7 @@ ENTRY(io_int_handler) lg %r12,__LC_THREAD_INFO larl %r13,system_call lmg %r8,%r9,__LC_IO_OLD_PSW - HANDLE_SIE_INTERCEPT %r14,0 + HANDLE_SIE_INTERCEPT %r14,2 SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT tmhh %r8,0x0001 # interrupting from user? jz io_skip @@ -460,10 +463,18 @@ io_skip: stmg %r0,%r7,__PT_R0(%r11) mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC stmg %r8,%r9,__PT_PSW(%r11) + mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID TRACE_IRQS_OFF xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) +io_loop: lgr %r2,%r11 # pass pointer to pt_regs brasl %r14,do_IRQ + tm __LC_MACHINE_FLAGS+6,0x10 # MACHINE_FLAG_LPAR + jz io_return + tpi 0 + jz io_return + mvc __PT_INT_CODE(12,%r11),__LC_SUBCHANNEL_ID + j io_loop io_return: LOCKDEP_SYS_EXIT TRACE_IRQS_ON @@ -595,7 +606,7 @@ ENTRY(ext_int_handler) lg %r12,__LC_THREAD_INFO larl %r13,system_call lmg %r8,%r9,__LC_EXT_OLD_PSW - HANDLE_SIE_INTERCEPT %r14,0 + HANDLE_SIE_INTERCEPT %r14,3 SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT tmhh %r8,0x0001 # interrupting from user ? jz ext_skip @@ -605,13 +616,13 @@ ext_skip: stmg %r0,%r7,__PT_R0(%r11) mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC stmg %r8,%r9,__PT_PSW(%r11) + lghi %r1,__LC_EXT_PARAMS2 + mvc __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR + mvc __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS + mvc __PT_INT_PARM_LONG(8,%r11),0(%r1) TRACE_IRQS_OFF xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) - lghi %r1,4096 lgr %r2,%r11 # pass pointer to pt_regs - llgf %r3,__LC_EXT_CPU_ADDR # get cpu address + interruption code - llgf %r4,__LC_EXT_PARAMS # get external parameter - lg %r5,__LC_EXT_PARAMS2-4096(%r1) # get 64 bit external parameter brasl %r14,do_extint j io_return @@ -643,7 +654,7 @@ ENTRY(mcck_int_handler) lg %r12,__LC_THREAD_INFO larl %r13,system_call lmg %r8,%r9,__LC_MCK_OLD_PSW - HANDLE_SIE_INTERCEPT %r14,0 + HANDLE_SIE_INTERCEPT %r14,4 tm __LC_MCCK_CODE,0x80 # system damage? jo mcck_panic # yes -> rest of mcck code invalid lghi %r14,__LC_CPU_TIMER_SAVE_AREA @@ -937,56 +948,50 @@ ENTRY(sie64a) stmg %r6,%r14,__SF_GPRS(%r15) # save kernel registers stg %r2,__SF_EMPTY(%r15) # save control block pointer stg %r3,__SF_EMPTY+8(%r15) # save guest register save area - xc __SF_EMPTY+16(8,%r15),__SF_EMPTY+16(%r15) # host id == 0 + xc __SF_EMPTY+16(16,%r15),__SF_EMPTY+16(%r15) # host id & reason lmg %r0,%r13,0(%r3) # load guest gprs 0-13 -# some program checks are suppressing. C code (e.g. do_protection_exception) -# will rewind the PSW by the ILC, which is 4 bytes in case of SIE. Other -# instructions in the sie_loop should not cause program interrupts. So -# lets use a nop (47 00 00 00) as a landing pad. -# See also HANDLE_SIE_INTERCEPT -rewind_pad: - nop 0 -sie_loop: - lg %r14,__LC_THREAD_INFO # pointer thread_info struct - tm __TI_flags+7(%r14),_TIF_EXIT_SIE - jnz sie_exit lg %r14,__LC_GMAP # get gmap pointer ltgr %r14,%r14 jz sie_gmap lctlg %c1,%c1,__GMAP_ASCE(%r14) # load primary asce sie_gmap: lg %r14,__SF_EMPTY(%r15) # get control block pointer + oi __SIE_PROG0C+3(%r14),1 # we are going into SIE now + tm __SIE_PROG20+3(%r14),1 # last exit... + jnz sie_done LPP __SF_EMPTY(%r15) # set guest id sie 0(%r14) sie_done: LPP __SF_EMPTY+16(%r15) # set host id - lg %r14,__LC_THREAD_INFO # pointer thread_info struct -sie_exit: + ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE lctlg %c1,%c1,__LC_USER_ASCE # load primary asce +# some program checks are suppressing. C code (e.g. do_protection_exception) +# will rewind the PSW by the ILC, which is 4 bytes in case of SIE. Other +# instructions beween sie64a and sie_done should not cause program +# interrupts. So lets use a nop (47 00 00 00) as a landing pad. +# See also HANDLE_SIE_INTERCEPT +rewind_pad: + nop 0 + .globl sie_exit +sie_exit: lg %r14,__SF_EMPTY+8(%r15) # load guest register save area stmg %r0,%r13,0(%r14) # save guest gprs 0-13 lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers - lghi %r2,0 + lg %r2,__SF_EMPTY+24(%r15) # return exit reason code br %r14 sie_fault: - lctlg %c1,%c1,__LC_USER_ASCE # load primary asce - lg %r14,__LC_THREAD_INFO # pointer thread_info struct - lg %r14,__SF_EMPTY+8(%r15) # load guest register save area - stmg %r0,%r13,0(%r14) # save guest gprs 0-13 - lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers - lghi %r2,-EFAULT - br %r14 + lghi %r14,-EFAULT + stg %r14,__SF_EMPTY+24(%r15) # set exit reason code + j sie_exit .align 8 -.Lsie_loop: - .quad sie_loop -.Lsie_length: - .quad sie_done - sie_loop -.Lhost_id: - .quad 0 +.Lsie_critical: + .quad sie_gmap +.Lsie_critical_length: + .quad sie_done - sie_gmap EX_TABLE(rewind_pad,sie_fault) - EX_TABLE(sie_loop,sie_fault) + EX_TABLE(sie_exit,sie_fault) #endif .section .rodata, "a" diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index dd3c1994b8bd..54b0995514e8 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -234,9 +234,9 @@ int unregister_external_interrupt(u16 code, ext_int_handler_t handler) } EXPORT_SYMBOL(unregister_external_interrupt); -void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, - unsigned int param32, unsigned long param64) +void __irq_entry do_extint(struct pt_regs *regs) { + struct ext_code ext_code; struct pt_regs *old_regs; struct ext_int_info *p; int index; @@ -248,6 +248,7 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, clock_comparator_work(); } kstat_incr_irqs_this_cpu(EXTERNAL_INTERRUPT, NULL); + ext_code = *(struct ext_code *) ®s->int_code; if (ext_code.code != 0x1004) __get_cpu_var(s390_idle).nohz_delay = 1; @@ -255,7 +256,8 @@ void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code, rcu_read_lock(); list_for_each_entry_rcu(p, &ext_int_hash[index], entry) if (likely(p->code == ext_code.code)) - p->handler(ext_code, param32, param64); + p->handler(ext_code, regs->int_parm, + regs->int_parm_long); rcu_read_unlock(); irq_exit(); set_irq_regs(old_regs); diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c index f58f37f66824..a6fc037671b1 100644 --- a/arch/s390/kernel/perf_event.c +++ b/arch/s390/kernel/perf_event.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/perf_event.h> +#include <linux/kvm_host.h> #include <linux/percpu.h> #include <linux/export.h> #include <asm/irq.h> @@ -39,6 +40,57 @@ int perf_num_counters(void) } EXPORT_SYMBOL(perf_num_counters); +static struct kvm_s390_sie_block *sie_block(struct pt_regs *regs) +{ + struct stack_frame *stack = (struct stack_frame *) regs->gprs[15]; + + if (!stack) + return NULL; + + return (struct kvm_s390_sie_block *) stack->empty1[0]; +} + +static bool is_in_guest(struct pt_regs *regs) +{ + unsigned long ip = instruction_pointer(regs); + + if (user_mode(regs)) + return false; + + return ip == (unsigned long) &sie_exit; +} + +static unsigned long guest_is_user_mode(struct pt_regs *regs) +{ + return sie_block(regs)->gpsw.mask & PSW_MASK_PSTATE; +} + +static unsigned long instruction_pointer_guest(struct pt_regs *regs) +{ + return sie_block(regs)->gpsw.addr & PSW_ADDR_INSN; +} + +unsigned long perf_instruction_pointer(struct pt_regs *regs) +{ + return is_in_guest(regs) ? instruction_pointer_guest(regs) + : instruction_pointer(regs); +} + +static unsigned long perf_misc_guest_flags(struct pt_regs *regs) +{ + return guest_is_user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER + : PERF_RECORD_MISC_GUEST_KERNEL; +} + +unsigned long perf_misc_flags(struct pt_regs *regs) +{ + if (is_in_guest(regs)) + return perf_misc_guest_flags(regs); + + return user_mode(regs) ? PERF_RECORD_MISC_USER + : PERF_RECORD_MISC_KERNEL; +} + void perf_event_print_debug(void) { struct cpumf_ctr_info cf_info; diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c index 9bdbcef1da9e..3bac589844a7 100644 --- a/arch/s390/kernel/s390_ksyms.c +++ b/arch/s390/kernel/s390_ksyms.c @@ -7,6 +7,7 @@ EXPORT_SYMBOL(_mcount); #endif #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) EXPORT_SYMBOL(sie64a); +EXPORT_SYMBOL(sie_exit); #endif EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memset); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 4f977d0d25c2..15a016c10563 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -49,7 +49,6 @@ enum { ec_schedule = 0, - ec_call_function, ec_call_function_single, ec_stop_cpu, }; @@ -438,8 +437,6 @@ static void smp_handle_ext_call(void) smp_stop_cpu(); if (test_bit(ec_schedule, &bits)) scheduler_ipi(); - if (test_bit(ec_call_function, &bits)) - generic_smp_call_function_interrupt(); if (test_bit(ec_call_function_single, &bits)) generic_smp_call_function_single_interrupt(); } @@ -456,7 +453,7 @@ void arch_send_call_function_ipi_mask(const struct cpumask *mask) int cpu; for_each_cpu(cpu, mask) - pcpu_ec_call(pcpu_devices + cpu, ec_call_function); + pcpu_ec_call(pcpu_devices + cpu, ec_call_function_single); } void arch_send_call_function_single_ipi(int cpu) diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile index 8fe9d65a4585..40b4c6470f88 100644 --- a/arch/s390/kvm/Makefile +++ b/arch/s390/kvm/Makefile @@ -6,7 +6,8 @@ # it under the terms of the GNU General Public License (version 2 only) # as published by the Free Software Foundation. -common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o eventfd.o) +KVM := ../../../virt/kvm +common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o ccflags-y := -Ivirt/kvm -Iarch/s390/kvm diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c index 1c01a9912989..3074475c8ae0 100644 --- a/arch/s390/kvm/diag.c +++ b/arch/s390/kvm/diag.c @@ -132,6 +132,9 @@ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu) { int code = (vcpu->arch.sie_block->ipb & 0xfff0000) >> 16; + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + trace_kvm_s390_handle_diag(vcpu, code); switch (code) { case 0x10: diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index b7d1b2edeeb3..5ee56e5acc23 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -22,87 +22,6 @@ #include "trace.h" #include "trace-s390.h" -static int handle_lctlg(struct kvm_vcpu *vcpu) -{ - int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; - int reg3 = vcpu->arch.sie_block->ipa & 0x000f; - u64 useraddr; - int reg, rc; - - vcpu->stat.instruction_lctlg++; - - useraddr = kvm_s390_get_base_disp_rsy(vcpu); - - if (useraddr & 7) - return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); - - reg = reg1; - - VCPU_EVENT(vcpu, 5, "lctlg r1:%x, r3:%x, addr:%llx", reg1, reg3, - useraddr); - trace_kvm_s390_handle_lctl(vcpu, 1, reg1, reg3, useraddr); - - do { - rc = get_guest(vcpu, vcpu->arch.sie_block->gcr[reg], - (u64 __user *) useraddr); - if (rc) - return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); - useraddr += 8; - if (reg == reg3) - break; - reg = (reg + 1) % 16; - } while (1); - return 0; -} - -static int handle_lctl(struct kvm_vcpu *vcpu) -{ - int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; - int reg3 = vcpu->arch.sie_block->ipa & 0x000f; - u64 useraddr; - u32 val = 0; - int reg, rc; - - vcpu->stat.instruction_lctl++; - - useraddr = kvm_s390_get_base_disp_rs(vcpu); - - if (useraddr & 3) - return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); - - VCPU_EVENT(vcpu, 5, "lctl r1:%x, r3:%x, addr:%llx", reg1, reg3, - useraddr); - trace_kvm_s390_handle_lctl(vcpu, 0, reg1, reg3, useraddr); - - reg = reg1; - do { - rc = get_guest(vcpu, val, (u32 __user *) useraddr); - if (rc) - return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); - vcpu->arch.sie_block->gcr[reg] &= 0xffffffff00000000ul; - vcpu->arch.sie_block->gcr[reg] |= val; - useraddr += 4; - if (reg == reg3) - break; - reg = (reg + 1) % 16; - } while (1); - return 0; -} - -static const intercept_handler_t eb_handlers[256] = { - [0x2f] = handle_lctlg, - [0x8a] = kvm_s390_handle_priv_eb, -}; - -static int handle_eb(struct kvm_vcpu *vcpu) -{ - intercept_handler_t handler; - - handler = eb_handlers[vcpu->arch.sie_block->ipb & 0xff]; - if (handler) - return handler(vcpu); - return -EOPNOTSUPP; -} static const intercept_handler_t instruction_handlers[256] = { [0x01] = kvm_s390_handle_01, @@ -110,10 +29,10 @@ static const intercept_handler_t instruction_handlers[256] = { [0x83] = kvm_s390_handle_diag, [0xae] = kvm_s390_handle_sigp, [0xb2] = kvm_s390_handle_b2, - [0xb7] = handle_lctl, + [0xb7] = kvm_s390_handle_lctl, [0xb9] = kvm_s390_handle_b9, [0xe5] = kvm_s390_handle_e5, - [0xeb] = handle_eb, + [0xeb] = kvm_s390_handle_eb, }; static int handle_noop(struct kvm_vcpu *vcpu) @@ -174,47 +93,12 @@ static int handle_stop(struct kvm_vcpu *vcpu) static int handle_validity(struct kvm_vcpu *vcpu) { - unsigned long vmaddr; int viwhy = vcpu->arch.sie_block->ipb >> 16; - int rc; vcpu->stat.exit_validity++; trace_kvm_s390_intercept_validity(vcpu, viwhy); - if (viwhy == 0x37) { - vmaddr = gmap_fault(vcpu->arch.sie_block->prefix, - vcpu->arch.gmap); - if (IS_ERR_VALUE(vmaddr)) { - rc = -EOPNOTSUPP; - goto out; - } - rc = fault_in_pages_writeable((char __user *) vmaddr, - PAGE_SIZE); - if (rc) { - /* user will receive sigsegv, exit to user */ - rc = -EOPNOTSUPP; - goto out; - } - vmaddr = gmap_fault(vcpu->arch.sie_block->prefix + PAGE_SIZE, - vcpu->arch.gmap); - if (IS_ERR_VALUE(vmaddr)) { - rc = -EOPNOTSUPP; - goto out; - } - rc = fault_in_pages_writeable((char __user *) vmaddr, - PAGE_SIZE); - if (rc) { - /* user will receive sigsegv, exit to user */ - rc = -EOPNOTSUPP; - goto out; - } - } else - rc = -EOPNOTSUPP; - -out: - if (rc) - VCPU_EVENT(vcpu, 2, "unhandled validity intercept code %d", - viwhy); - return rc; + WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy); + return -EOPNOTSUPP; } static int handle_instruction(struct kvm_vcpu *vcpu) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 5c948177529e..7f35cb33e510 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -438,7 +438,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) no_timer: spin_lock(&vcpu->arch.local_int.float_int->lock); spin_lock_bh(&vcpu->arch.local_int.lock); - add_wait_queue(&vcpu->arch.local_int.wq, &wait); + add_wait_queue(&vcpu->wq, &wait); while (list_empty(&vcpu->arch.local_int.list) && list_empty(&vcpu->arch.local_int.float_int->list) && (!vcpu->arch.local_int.timer_due) && @@ -452,7 +452,7 @@ no_timer: } __unset_cpu_idle(vcpu); __set_current_state(TASK_RUNNING); - remove_wait_queue(&vcpu->arch.local_int.wq, &wait); + remove_wait_queue(&vcpu->wq, &wait); spin_unlock_bh(&vcpu->arch.local_int.lock); spin_unlock(&vcpu->arch.local_int.float_int->lock); hrtimer_try_to_cancel(&vcpu->arch.ckc_timer); @@ -465,8 +465,8 @@ void kvm_s390_tasklet(unsigned long parm) spin_lock(&vcpu->arch.local_int.lock); vcpu->arch.local_int.timer_due = 1; - if (waitqueue_active(&vcpu->arch.local_int.wq)) - wake_up_interruptible(&vcpu->arch.local_int.wq); + if (waitqueue_active(&vcpu->wq)) + wake_up_interruptible(&vcpu->wq); spin_unlock(&vcpu->arch.local_int.lock); } @@ -613,7 +613,7 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) spin_lock_bh(&li->lock); list_add(&inti->list, &li->list); atomic_set(&li->active, 1); - BUG_ON(waitqueue_active(&li->wq)); + BUG_ON(waitqueue_active(li->wq)); spin_unlock_bh(&li->lock); return 0; } @@ -746,8 +746,8 @@ int kvm_s390_inject_vm(struct kvm *kvm, li = fi->local_int[sigcpu]; spin_lock_bh(&li->lock); atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); - if (waitqueue_active(&li->wq)) - wake_up_interruptible(&li->wq); + if (waitqueue_active(li->wq)) + wake_up_interruptible(li->wq); spin_unlock_bh(&li->lock); spin_unlock(&fi->lock); mutex_unlock(&kvm->lock); @@ -832,8 +832,8 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, if (inti->type == KVM_S390_SIGP_STOP) li->action_bits |= ACTION_STOP_ON_STOP; atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); - if (waitqueue_active(&li->wq)) - wake_up_interruptible(&vcpu->arch.local_int.wq); + if (waitqueue_active(&vcpu->wq)) + wake_up_interruptible(&vcpu->wq); spin_unlock_bh(&li->lock); mutex_unlock(&vcpu->kvm->lock); return 0; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index c1c7c683fa26..ba694d2ba51e 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -59,6 +59,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "deliver_restart_signal", VCPU_STAT(deliver_restart_signal) }, { "deliver_program_interruption", VCPU_STAT(deliver_program_int) }, { "exit_wait_state", VCPU_STAT(exit_wait_state) }, + { "instruction_pfmf", VCPU_STAT(instruction_pfmf) }, { "instruction_stidp", VCPU_STAT(instruction_stidp) }, { "instruction_spx", VCPU_STAT(instruction_spx) }, { "instruction_stpx", VCPU_STAT(instruction_stpx) }, @@ -84,6 +85,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { }; static unsigned long long *facilities; +static struct gmap_notifier gmap_notifier; /* Section: not file related */ int kvm_arch_hardware_enable(void *garbage) @@ -96,13 +98,18 @@ void kvm_arch_hardware_disable(void *garbage) { } +static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address); + int kvm_arch_hardware_setup(void) { + gmap_notifier.notifier_call = kvm_gmap_notifier; + gmap_register_ipte_notifier(&gmap_notifier); return 0; } void kvm_arch_hardware_unsetup(void) { + gmap_unregister_ipte_notifier(&gmap_notifier); } void kvm_arch_check_processor_compat(void *rtn) @@ -239,6 +246,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.gmap = gmap_alloc(current->mm); if (!kvm->arch.gmap) goto out_nogmap; + kvm->arch.gmap->private = kvm; } kvm->arch.css_support = 0; @@ -270,7 +278,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) free_page((unsigned long)(vcpu->arch.sie_block)); kvm_vcpu_uninit(vcpu); - kfree(vcpu); + kmem_cache_free(kvm_vcpu_cache, vcpu); } static void kvm_free_vcpus(struct kvm *kvm) @@ -309,6 +317,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) vcpu->arch.gmap = gmap_alloc(current->mm); if (!vcpu->arch.gmap) return -ENOMEM; + vcpu->arch.gmap->private = vcpu->kvm; return 0; } @@ -373,8 +382,10 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) { atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH | CPUSTAT_SM | - CPUSTAT_STOPPED); + CPUSTAT_STOPPED | + CPUSTAT_GED); vcpu->arch.sie_block->ecb = 6; + vcpu->arch.sie_block->ecb2 = 8; vcpu->arch.sie_block->eca = 0xC1002001U; vcpu->arch.sie_block->fac = (int) (long) facilities; hrtimer_init(&vcpu->arch.ckc_timer, CLOCK_REALTIME, HRTIMER_MODE_ABS); @@ -397,7 +408,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, rc = -ENOMEM; - vcpu = kzalloc(sizeof(struct kvm_vcpu), GFP_KERNEL); + vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); if (!vcpu) goto out; @@ -427,7 +438,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, vcpu->arch.local_int.float_int = &kvm->arch.float_int; spin_lock(&kvm->arch.float_int.lock); kvm->arch.float_int.local_int[id] = &vcpu->arch.local_int; - init_waitqueue_head(&vcpu->arch.local_int.wq); + vcpu->arch.local_int.wq = &vcpu->wq; vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags; spin_unlock(&kvm->arch.float_int.lock); @@ -442,7 +453,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, out_free_sie_block: free_page((unsigned long)(vcpu->arch.sie_block)); out_free_cpu: - kfree(vcpu); + kmem_cache_free(kvm_vcpu_cache, vcpu); out: return ERR_PTR(rc); } @@ -454,6 +465,50 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) return 0; } +void s390_vcpu_block(struct kvm_vcpu *vcpu) +{ + atomic_set_mask(PROG_BLOCK_SIE, &vcpu->arch.sie_block->prog20); +} + +void s390_vcpu_unblock(struct kvm_vcpu *vcpu) +{ + atomic_clear_mask(PROG_BLOCK_SIE, &vcpu->arch.sie_block->prog20); +} + +/* + * Kick a guest cpu out of SIE and wait until SIE is not running. + * If the CPU is not running (e.g. waiting as idle) the function will + * return immediately. */ +void exit_sie(struct kvm_vcpu *vcpu) +{ + atomic_set_mask(CPUSTAT_STOP_INT, &vcpu->arch.sie_block->cpuflags); + while (vcpu->arch.sie_block->prog0c & PROG_IN_SIE) + cpu_relax(); +} + +/* Kick a guest cpu out of SIE and prevent SIE-reentry */ +void exit_sie_sync(struct kvm_vcpu *vcpu) +{ + s390_vcpu_block(vcpu); + exit_sie(vcpu); +} + +static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address) +{ + int i; + struct kvm *kvm = gmap->private; + struct kvm_vcpu *vcpu; + + kvm_for_each_vcpu(i, vcpu, kvm) { + /* match against both prefix pages */ + if (vcpu->arch.sie_block->prefix == (address & ~0x1000UL)) { + VCPU_EVENT(vcpu, 2, "gmap notifier for %lx", address); + kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu); + exit_sie_sync(vcpu); + } + } +} + int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) { /* kvm common code refers to this, but never calls it */ @@ -606,6 +661,27 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, return -EINVAL; /* not implemented yet */ } +static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu) +{ + /* + * We use MMU_RELOAD just to re-arm the ipte notifier for the + * guest prefix page. gmap_ipte_notify will wait on the ptl lock. + * This ensures that the ipte instruction for this request has + * already finished. We might race against a second unmapper that + * wants to set the blocking bit. Lets just retry the request loop. + */ + while (kvm_check_request(KVM_REQ_MMU_RELOAD, vcpu)) { + int rc; + rc = gmap_ipte_notify(vcpu->arch.gmap, + vcpu->arch.sie_block->prefix, + PAGE_SIZE * 2); + if (rc) + return rc; + s390_vcpu_unblock(vcpu); + } + return 0; +} + static int __vcpu_run(struct kvm_vcpu *vcpu) { int rc; @@ -621,6 +697,10 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) if (!kvm_is_ucontrol(vcpu->kvm)) kvm_s390_deliver_pending_interrupts(vcpu); + rc = kvm_s390_handle_requests(vcpu); + if (rc) + return rc; + vcpu->arch.sie_block->icptcode = 0; preempt_disable(); kvm_guest_enter(); @@ -630,7 +710,9 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) trace_kvm_s390_sie_enter(vcpu, atomic_read(&vcpu->arch.sie_block->cpuflags)); rc = sie64a(vcpu->arch.sie_block, vcpu->run->s.regs.gprs); - if (rc) { + if (rc > 0) + rc = 0; + if (rc < 0) { if (kvm_is_ucontrol(vcpu->kvm)) { rc = SIE_INTERCEPT_UCONTROL; } else { @@ -1046,7 +1128,7 @@ static int __init kvm_s390_init(void) return -ENOMEM; } memcpy(facilities, S390_lowcore.stfle_fac_list, 16); - facilities[0] &= 0xff00fff3f47c0000ULL; + facilities[0] &= 0xff82fff3f47c0000ULL; facilities[1] &= 0x001c000000000000ULL; return 0; } @@ -1059,3 +1141,12 @@ static void __exit kvm_s390_exit(void) module_init(kvm_s390_init); module_exit(kvm_s390_exit); + +/* + * Enable autoloading of the kvm module. + * Note that we add the module alias here instead of virt/kvm/kvm_main.c + * since x86 takes a different approach. + */ +#include <linux/miscdevice.h> +MODULE_ALIAS_MISCDEV(KVM_MINOR); +MODULE_ALIAS("devname:kvm"); diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index efc14f687265..028ca9fd2158 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -63,6 +63,7 @@ static inline void kvm_s390_set_prefix(struct kvm_vcpu *vcpu, u32 prefix) { vcpu->arch.sie_block->prefix = prefix & 0x7fffe000u; vcpu->arch.sie_block->ihcpu = 0xffff; + kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu); } static inline u64 kvm_s390_get_base_disp_s(struct kvm_vcpu *vcpu) @@ -85,6 +86,12 @@ static inline void kvm_s390_get_base_disp_sse(struct kvm_vcpu *vcpu, *address2 = (base2 ? vcpu->run->s.regs.gprs[base2] : 0) + disp2; } +static inline void kvm_s390_get_regs_rre(struct kvm_vcpu *vcpu, int *r1, int *r2) +{ + *r1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 20; + *r2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16; +} + static inline u64 kvm_s390_get_base_disp_rsy(struct kvm_vcpu *vcpu) { u32 base2 = vcpu->arch.sie_block->ipb >> 28; @@ -125,7 +132,8 @@ int kvm_s390_handle_e5(struct kvm_vcpu *vcpu); int kvm_s390_handle_01(struct kvm_vcpu *vcpu); int kvm_s390_handle_b9(struct kvm_vcpu *vcpu); int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu); -int kvm_s390_handle_priv_eb(struct kvm_vcpu *vcpu); +int kvm_s390_handle_lctl(struct kvm_vcpu *vcpu); +int kvm_s390_handle_eb(struct kvm_vcpu *vcpu); /* implemented in sigp.c */ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); @@ -133,6 +141,10 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); /* implemented in kvm-s390.c */ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr); +void s390_vcpu_block(struct kvm_vcpu *vcpu); +void s390_vcpu_unblock(struct kvm_vcpu *vcpu); +void exit_sie(struct kvm_vcpu *vcpu); +void exit_sie_sync(struct kvm_vcpu *vcpu); /* implemented in diag.c */ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu); diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 6bbd7b5a0bbe..0da3e6eb6be6 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -1,7 +1,7 @@ /* * handling privileged instructions * - * Copyright IBM Corp. 2008 + * Copyright IBM Corp. 2008, 2013 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License (version 2 only) @@ -20,6 +20,9 @@ #include <asm/debug.h> #include <asm/ebcdic.h> #include <asm/sysinfo.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/io.h> #include <asm/ptrace.h> #include <asm/compat.h> #include "gaccess.h" @@ -34,6 +37,9 @@ static int handle_set_prefix(struct kvm_vcpu *vcpu) vcpu->stat.instruction_spx++; + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + operand2 = kvm_s390_get_base_disp_s(vcpu); /* must be word boundary */ @@ -65,6 +71,9 @@ static int handle_store_prefix(struct kvm_vcpu *vcpu) vcpu->stat.instruction_stpx++; + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + operand2 = kvm_s390_get_base_disp_s(vcpu); /* must be word boundary */ @@ -89,6 +98,9 @@ static int handle_store_cpu_address(struct kvm_vcpu *vcpu) vcpu->stat.instruction_stap++; + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + useraddr = kvm_s390_get_base_disp_s(vcpu); if (useraddr & 1) @@ -105,7 +117,12 @@ static int handle_store_cpu_address(struct kvm_vcpu *vcpu) static int handle_skey(struct kvm_vcpu *vcpu) { vcpu->stat.instruction_storage_key++; - vcpu->arch.sie_block->gpsw.addr -= 4; + + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + + vcpu->arch.sie_block->gpsw.addr = + __rewind_psw(vcpu->arch.sie_block->gpsw, 4); VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation"); return 0; } @@ -129,9 +146,10 @@ static int handle_tpi(struct kvm_vcpu *vcpu) * Store the two-word I/O interruption code into the * provided area. */ - put_guest(vcpu, inti->io.subchannel_id, (u16 __user *) addr); - put_guest(vcpu, inti->io.subchannel_nr, (u16 __user *) (addr + 2)); - put_guest(vcpu, inti->io.io_int_parm, (u32 __user *) (addr + 4)); + if (put_guest(vcpu, inti->io.subchannel_id, (u16 __user *)addr) + || put_guest(vcpu, inti->io.subchannel_nr, (u16 __user *)(addr + 2)) + || put_guest(vcpu, inti->io.io_int_parm, (u32 __user *)(addr + 4))) + return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); } else { /* * Store the three-word I/O interruption code into @@ -182,6 +200,9 @@ static int handle_io_inst(struct kvm_vcpu *vcpu) { VCPU_EVENT(vcpu, 4, "%s", "I/O instruction"); + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + if (vcpu->kvm->arch.css_support) { /* * Most I/O instructions will be handled by userspace. @@ -210,8 +231,12 @@ static int handle_stfl(struct kvm_vcpu *vcpu) int rc; vcpu->stat.instruction_stfl++; + + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + /* only pass the facility bits, which we can handle */ - facility_list = S390_lowcore.stfl_fac_list & 0xff00fff3; + facility_list = S390_lowcore.stfl_fac_list & 0xff82fff3; rc = copy_to_guest(vcpu, offsetof(struct _lowcore, stfl_fac_list), &facility_list, sizeof(facility_list)); @@ -255,8 +280,8 @@ int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu) u64 addr; if (gpsw->mask & PSW_MASK_PSTATE) - return kvm_s390_inject_program_int(vcpu, - PGM_PRIVILEGED_OPERATION); + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + addr = kvm_s390_get_base_disp_s(vcpu); if (addr & 7) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); @@ -278,6 +303,9 @@ static int handle_lpswe(struct kvm_vcpu *vcpu) psw_t new_psw; u64 addr; + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + addr = kvm_s390_get_base_disp_s(vcpu); if (addr & 7) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); @@ -296,6 +324,9 @@ static int handle_stidp(struct kvm_vcpu *vcpu) vcpu->stat.instruction_stidp++; + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + operand2 = kvm_s390_get_base_disp_s(vcpu); if (operand2 & 7) @@ -351,16 +382,30 @@ static int handle_stsi(struct kvm_vcpu *vcpu) vcpu->stat.instruction_stsi++; VCPU_EVENT(vcpu, 4, "stsi: fc: %x sel1: %x sel2: %x", fc, sel1, sel2); - operand2 = kvm_s390_get_base_disp_s(vcpu); + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + + if (fc > 3) { + vcpu->arch.sie_block->gpsw.mask |= 3ul << 44; /* cc 3 */ + return 0; + } - if (operand2 & 0xfff && fc > 0) + if (vcpu->run->s.regs.gprs[0] & 0x0fffff00 + || vcpu->run->s.regs.gprs[1] & 0xffff0000) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); - switch (fc) { - case 0: + if (fc == 0) { vcpu->run->s.regs.gprs[0] = 3 << 28; - vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); + vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); /* cc 0 */ return 0; + } + + operand2 = kvm_s390_get_base_disp_s(vcpu); + + if (operand2 & 0xfff) + return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + + switch (fc) { case 1: /* same handling for 1 and 2 */ case 2: mem = get_zeroed_page(GFP_KERNEL); @@ -377,8 +422,6 @@ static int handle_stsi(struct kvm_vcpu *vcpu) goto out_no_data; handle_stsi_3_2_2(vcpu, (void *) mem); break; - default: - goto out_no_data; } if (copy_to_guest_absolute(vcpu, operand2, (void *) mem, PAGE_SIZE)) { @@ -432,20 +475,14 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu) intercept_handler_t handler; /* - * a lot of B2 instructions are priviledged. We first check for - * the privileged ones, that we can handle in the kernel. If the - * kernel can handle this instruction, we check for the problem - * state bit and (a) handle the instruction or (b) send a code 2 - * program check. - * Anything else goes to userspace.*/ + * A lot of B2 instructions are priviledged. Here we check for + * the privileged ones, that we can handle in the kernel. + * Anything else goes to userspace. + */ handler = b2_handlers[vcpu->arch.sie_block->ipa & 0x00ff]; - if (handler) { - if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) - return kvm_s390_inject_program_int(vcpu, - PGM_PRIVILEGED_OPERATION); - else - return handler(vcpu); - } + if (handler) + return handler(vcpu); + return -EOPNOTSUPP; } @@ -453,8 +490,7 @@ static int handle_epsw(struct kvm_vcpu *vcpu) { int reg1, reg2; - reg1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 24; - reg2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16; + kvm_s390_get_regs_rre(vcpu, ®1, ®2); /* This basically extracts the mask half of the psw. */ vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000; @@ -467,9 +503,88 @@ static int handle_epsw(struct kvm_vcpu *vcpu) return 0; } +#define PFMF_RESERVED 0xfffc0101UL +#define PFMF_SK 0x00020000UL +#define PFMF_CF 0x00010000UL +#define PFMF_UI 0x00008000UL +#define PFMF_FSC 0x00007000UL +#define PFMF_NQ 0x00000800UL +#define PFMF_MR 0x00000400UL +#define PFMF_MC 0x00000200UL +#define PFMF_KEY 0x000000feUL + +static int handle_pfmf(struct kvm_vcpu *vcpu) +{ + int reg1, reg2; + unsigned long start, end; + + vcpu->stat.instruction_pfmf++; + + kvm_s390_get_regs_rre(vcpu, ®1, ®2); + + if (!MACHINE_HAS_PFMF) + return kvm_s390_inject_program_int(vcpu, PGM_OPERATION); + + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + + if (vcpu->run->s.regs.gprs[reg1] & PFMF_RESERVED) + return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + + /* Only provide non-quiescing support if the host supports it */ + if (vcpu->run->s.regs.gprs[reg1] & PFMF_NQ && + S390_lowcore.stfl_fac_list & 0x00020000) + return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + + /* No support for conditional-SSKE */ + if (vcpu->run->s.regs.gprs[reg1] & (PFMF_MR | PFMF_MC)) + return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + + start = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK; + switch (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) { + case 0x00000000: + end = (start + (1UL << 12)) & ~((1UL << 12) - 1); + break; + case 0x00001000: + end = (start + (1UL << 20)) & ~((1UL << 20) - 1); + break; + /* We dont support EDAT2 + case 0x00002000: + end = (start + (1UL << 31)) & ~((1UL << 31) - 1); + break;*/ + default: + return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + } + while (start < end) { + unsigned long useraddr; + + useraddr = gmap_translate(start, vcpu->arch.gmap); + if (IS_ERR((void *)useraddr)) + return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); + + if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) { + if (clear_user((void __user *)useraddr, PAGE_SIZE)) + return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); + } + + if (vcpu->run->s.regs.gprs[reg1] & PFMF_SK) { + if (set_guest_storage_key(current->mm, useraddr, + vcpu->run->s.regs.gprs[reg1] & PFMF_KEY, + vcpu->run->s.regs.gprs[reg1] & PFMF_NQ)) + return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); + } + + start += PAGE_SIZE; + } + if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) + vcpu->run->s.regs.gprs[reg2] = end; + return 0; +} + static const intercept_handler_t b9_handlers[256] = { [0x8d] = handle_epsw, [0x9c] = handle_io_inst, + [0xaf] = handle_pfmf, }; int kvm_s390_handle_b9(struct kvm_vcpu *vcpu) @@ -478,29 +593,96 @@ int kvm_s390_handle_b9(struct kvm_vcpu *vcpu) /* This is handled just as for the B2 instructions. */ handler = b9_handlers[vcpu->arch.sie_block->ipa & 0x00ff]; - if (handler) { - if ((handler != handle_epsw) && - (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)) - return kvm_s390_inject_program_int(vcpu, - PGM_PRIVILEGED_OPERATION); - else - return handler(vcpu); - } + if (handler) + return handler(vcpu); + return -EOPNOTSUPP; } +int kvm_s390_handle_lctl(struct kvm_vcpu *vcpu) +{ + int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; + int reg3 = vcpu->arch.sie_block->ipa & 0x000f; + u64 useraddr; + u32 val = 0; + int reg, rc; + + vcpu->stat.instruction_lctl++; + + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + + useraddr = kvm_s390_get_base_disp_rs(vcpu); + + if (useraddr & 3) + return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + + VCPU_EVENT(vcpu, 5, "lctl r1:%x, r3:%x, addr:%llx", reg1, reg3, + useraddr); + trace_kvm_s390_handle_lctl(vcpu, 0, reg1, reg3, useraddr); + + reg = reg1; + do { + rc = get_guest(vcpu, val, (u32 __user *) useraddr); + if (rc) + return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); + vcpu->arch.sie_block->gcr[reg] &= 0xffffffff00000000ul; + vcpu->arch.sie_block->gcr[reg] |= val; + useraddr += 4; + if (reg == reg3) + break; + reg = (reg + 1) % 16; + } while (1); + + return 0; +} + +static int handle_lctlg(struct kvm_vcpu *vcpu) +{ + int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; + int reg3 = vcpu->arch.sie_block->ipa & 0x000f; + u64 useraddr; + int reg, rc; + + vcpu->stat.instruction_lctlg++; + + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + + useraddr = kvm_s390_get_base_disp_rsy(vcpu); + + if (useraddr & 7) + return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + + reg = reg1; + + VCPU_EVENT(vcpu, 5, "lctlg r1:%x, r3:%x, addr:%llx", reg1, reg3, + useraddr); + trace_kvm_s390_handle_lctl(vcpu, 1, reg1, reg3, useraddr); + + do { + rc = get_guest(vcpu, vcpu->arch.sie_block->gcr[reg], + (u64 __user *) useraddr); + if (rc) + return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); + useraddr += 8; + if (reg == reg3) + break; + reg = (reg + 1) % 16; + } while (1); + + return 0; +} + static const intercept_handler_t eb_handlers[256] = { + [0x2f] = handle_lctlg, [0x8a] = handle_io_inst, }; -int kvm_s390_handle_priv_eb(struct kvm_vcpu *vcpu) +int kvm_s390_handle_eb(struct kvm_vcpu *vcpu) { intercept_handler_t handler; - /* All eb instructions that end up here are privileged. */ - if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) - return kvm_s390_inject_program_int(vcpu, - PGM_PRIVILEGED_OPERATION); handler = eb_handlers[vcpu->arch.sie_block->ipb & 0xff]; if (handler) return handler(vcpu); @@ -515,6 +697,9 @@ static int handle_tprot(struct kvm_vcpu *vcpu) vcpu->stat.instruction_tprot++; + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); + kvm_s390_get_base_disp_sse(vcpu, &address1, &address2); /* we only handle the Linux memory detection case: @@ -560,8 +745,7 @@ static int handle_sckpf(struct kvm_vcpu *vcpu) u32 value; if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) - return kvm_s390_inject_program_int(vcpu, - PGM_PRIVILEGED_OPERATION); + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); if (vcpu->run->s.regs.gprs[0] & 0x00000000ffff0000) return kvm_s390_inject_program_int(vcpu, diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c index 1c48ab2845e0..bec398c57acf 100644 --- a/arch/s390/kvm/sigp.c +++ b/arch/s390/kvm/sigp.c @@ -79,8 +79,8 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr) list_add_tail(&inti->list, &li->list); atomic_set(&li->active, 1); atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); - if (waitqueue_active(&li->wq)) - wake_up_interruptible(&li->wq); + if (waitqueue_active(li->wq)) + wake_up_interruptible(li->wq); spin_unlock_bh(&li->lock); rc = SIGP_CC_ORDER_CODE_ACCEPTED; VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr); @@ -117,8 +117,8 @@ static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) list_add_tail(&inti->list, &li->list); atomic_set(&li->active, 1); atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); - if (waitqueue_active(&li->wq)) - wake_up_interruptible(&li->wq); + if (waitqueue_active(li->wq)) + wake_up_interruptible(li->wq); spin_unlock_bh(&li->lock); rc = SIGP_CC_ORDER_CODE_ACCEPTED; VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr); @@ -145,8 +145,8 @@ static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) atomic_set(&li->active, 1); atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags); li->action_bits |= action; - if (waitqueue_active(&li->wq)) - wake_up_interruptible(&li->wq); + if (waitqueue_active(li->wq)) + wake_up_interruptible(li->wq); out: spin_unlock_bh(&li->lock); @@ -250,8 +250,8 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address, list_add_tail(&inti->list, &li->list); atomic_set(&li->active, 1); - if (waitqueue_active(&li->wq)) - wake_up_interruptible(&li->wq); + if (waitqueue_active(li->wq)) + wake_up_interruptible(li->wq); rc = SIGP_CC_ORDER_CODE_ACCEPTED; VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", cpu_addr, address); @@ -333,8 +333,7 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) /* sigp in userspace can exit */ if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) - return kvm_s390_inject_program_int(vcpu, - PGM_PRIVILEGED_OPERATION); + return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); order_code = kvm_s390_get_base_disp_rs(vcpu); diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index a938b548f07e..17bf4d3d303a 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -689,7 +689,7 @@ int gmap_ipte_notify(struct gmap *gmap, unsigned long start, unsigned long len) entry = *ptep; if ((pte_val(entry) & (_PAGE_INVALID | _PAGE_RO)) == 0) { pgste = pgste_get_lock(ptep); - pgste_val(pgste) |= RCP_IN_BIT; + pgste_val(pgste) |= PGSTE_IN_BIT; pgste_set_unlock(ptep, pgste); start += PAGE_SIZE; len -= PAGE_SIZE; @@ -771,6 +771,54 @@ static inline void page_table_free_pgste(unsigned long *table) __free_page(page); } +int set_guest_storage_key(struct mm_struct *mm, unsigned long addr, + unsigned long key, bool nq) +{ + spinlock_t *ptl; + pgste_t old, new; + pte_t *ptep; + + down_read(&mm->mmap_sem); + ptep = get_locked_pte(current->mm, addr, &ptl); + if (unlikely(!ptep)) { + up_read(&mm->mmap_sem); + return -EFAULT; + } + + new = old = pgste_get_lock(ptep); + pgste_val(new) &= ~(PGSTE_GR_BIT | PGSTE_GC_BIT | + PGSTE_ACC_BITS | PGSTE_FP_BIT); + pgste_val(new) |= (key & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48; + pgste_val(new) |= (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; + if (!(pte_val(*ptep) & _PAGE_INVALID)) { + unsigned long address, bits; + unsigned char skey; + + address = pte_val(*ptep) & PAGE_MASK; + skey = page_get_storage_key(address); + bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED); + /* Set storage key ACC and FP */ + page_set_storage_key(address, + (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)), + !nq); + + /* Merge host changed & referenced into pgste */ + pgste_val(new) |= bits << 52; + /* Transfer skey changed & referenced bit to kvm user bits */ + pgste_val(new) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */ + } + /* changing the guest storage key is considered a change of the page */ + if ((pgste_val(new) ^ pgste_val(old)) & + (PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT)) + pgste_val(new) |= PGSTE_UC_BIT; + + pgste_set_unlock(ptep, new); + pte_unmap_unlock(*ptep, ptl); + up_read(&mm->mmap_sem); + return 0; +} +EXPORT_SYMBOL(set_guest_storage_key); + #else /* CONFIG_PGSTE */ static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, diff --git a/arch/s390/oprofile/hwsampler.h b/arch/s390/oprofile/hwsampler.h index 1912f3bb190c..0022e1ebfbde 100644 --- a/arch/s390/oprofile/hwsampler.h +++ b/arch/s390/oprofile/hwsampler.h @@ -81,8 +81,8 @@ struct hws_data_entry { unsigned int:16; unsigned int prim_asn:16; /* primary ASN */ unsigned long long ia; /* Instruction Address */ - unsigned long long lpp; /* Logical-Partition Program Param. */ - unsigned long long vpp; /* Virtual-Machine Program Param. */ + unsigned long long gpp; /* Guest Program Parameter */ + unsigned long long hpp; /* Host Program Parameter */ }; struct hws_trailer_entry { diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index f1e5be85d592..e2956ad39a4f 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -82,10 +82,13 @@ struct intr_bucket { static struct intr_bucket *bucket; -/* Adapter local summary indicator */ -static u8 *zpci_irq_si; +/* Adapter interrupt definitions */ +static void zpci_irq_handler(struct airq_struct *airq); -static atomic_t irq_retries = ATOMIC_INIT(0); +static struct airq_struct zpci_airq = { + .handler = zpci_irq_handler, + .isc = PCI_ISC, +}; /* I/O Map */ static DEFINE_SPINLOCK(zpci_iomap_lock); @@ -404,7 +407,7 @@ static struct pci_ops pci_root_ops = { /* store the last handled bit to implement fair scheduling of devices */ static DEFINE_PER_CPU(unsigned long, next_sbit); -static void zpci_irq_handler(void *dont, void *need) +static void zpci_irq_handler(struct airq_struct *airq) { unsigned long sbit, mbit, last = 0, start = __get_cpu_var(next_sbit); int rescan = 0, max = aisb_max; @@ -452,7 +455,6 @@ scan: max = aisb_max; sbit = find_first_bit_left(bucket->aisb, max); if (sbit != max) { - atomic_inc(&irq_retries); rescan++; goto scan; } @@ -565,7 +567,21 @@ static void zpci_map_resources(struct zpci_dev *zdev) pr_debug("BAR%i: -> start: %Lx end: %Lx\n", i, pdev->resource[i].start, pdev->resource[i].end); } -}; +} + +static void zpci_unmap_resources(struct zpci_dev *zdev) +{ + struct pci_dev *pdev = zdev->pdev; + resource_size_t len; + int i; + + for (i = 0; i < PCI_BAR_COUNT; i++) { + len = pci_resource_len(pdev, i); + if (!len) + continue; + pci_iounmap(pdev, (void *) pdev->resource[i].start); + } +} struct zpci_dev *zpci_alloc_device(void) { @@ -701,25 +717,20 @@ static int __init zpci_irq_init(void) goto out_alloc; } - isc_register(PCI_ISC); - zpci_irq_si = s390_register_adapter_interrupt(&zpci_irq_handler, NULL, PCI_ISC); - if (IS_ERR(zpci_irq_si)) { - rc = PTR_ERR(zpci_irq_si); - zpci_irq_si = NULL; + rc = register_adapter_interrupt(&zpci_airq); + if (rc) goto out_ai; - } + /* Set summary to 1 to be called every time for the ISC. */ + *zpci_airq.lsi_ptr = 1; for_each_online_cpu(cpu) per_cpu(next_sbit, cpu) = 0; spin_lock_init(&bucket->lock); - /* set summary to 1 to be called every time for the ISC */ - *zpci_irq_si = 1; set_irq_ctrl(SIC_IRQ_MODE_SINGLE, NULL, PCI_ISC); return 0; out_ai: - isc_unregister(PCI_ISC); free_page((unsigned long) bucket->alloc); out_alloc: free_page((unsigned long) bucket->aisb); @@ -732,21 +743,10 @@ static void zpci_irq_exit(void) { free_page((unsigned long) bucket->alloc); free_page((unsigned long) bucket->aisb); - s390_unregister_adapter_interrupt(zpci_irq_si, PCI_ISC); - isc_unregister(PCI_ISC); + unregister_adapter_interrupt(&zpci_airq); kfree(bucket); } -void zpci_debug_info(struct zpci_dev *zdev, struct seq_file *m) -{ - if (!zdev) - return; - - seq_printf(m, "global irq retries: %u\n", atomic_read(&irq_retries)); - seq_printf(m, "aibv[0]:%016lx aibv[1]:%016lx aisb:%016lx\n", - get_imap(0)->aibv, get_imap(1)->aibv, *bucket->aisb); -} - static struct resource *zpci_alloc_bus_resource(unsigned long start, unsigned long size, unsigned long flags, int domain) { @@ -810,6 +810,16 @@ int pcibios_add_device(struct pci_dev *pdev) return 0; } +void pcibios_release_device(struct pci_dev *pdev) +{ + struct zpci_dev *zdev = get_zdev(pdev); + + zpci_unmap_resources(zdev); + zpci_fmb_disable_device(zdev); + zpci_debug_exit_device(zdev); + zdev->pdev = NULL; +} + static int zpci_scan_bus(struct zpci_dev *zdev) { struct resource *res; @@ -950,25 +960,6 @@ void zpci_stop_device(struct zpci_dev *zdev) } EXPORT_SYMBOL_GPL(zpci_stop_device); -int zpci_scan_device(struct zpci_dev *zdev) -{ - zdev->pdev = pci_scan_single_device(zdev->bus, ZPCI_DEVFN); - if (!zdev->pdev) { - pr_err("pci_scan_single_device failed for fid: 0x%x\n", - zdev->fid); - goto out; - } - - pci_bus_add_devices(zdev->bus); - - return 0; -out: - zpci_dma_exit_device(zdev); - clp_disable_fh(zdev); - return -EIO; -} -EXPORT_SYMBOL_GPL(zpci_scan_device); - static inline int barsize(u8 size) { return (size) ? (1 << size) >> 10 : 0; diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c index bd34359d1546..2e9539625d93 100644 --- a/arch/s390/pci/pci_clp.c +++ b/arch/s390/pci/pci_clp.c @@ -236,7 +236,6 @@ int clp_disable_fh(struct zpci_dev *zdev) if (!zdev_enabled(zdev)) return 0; - dev_info(&zdev->pdev->dev, "disabling fn handle: 0x%x\n", fh); rc = clp_set_pci_fn(&fh, 0, CLP_SET_DISABLE_PCI_FN); if (!rc) /* Success -> store disabled handle in zdev */ diff --git a/arch/s390/pci/pci_debug.c b/arch/s390/pci/pci_debug.c index 771b82359af4..75c69b402e05 100644 --- a/arch/s390/pci/pci_debug.c +++ b/arch/s390/pci/pci_debug.c @@ -115,27 +115,6 @@ static const struct file_operations debugfs_pci_perf_fops = { .release = single_release, }; -static int pci_debug_show(struct seq_file *m, void *v) -{ - struct zpci_dev *zdev = m->private; - - zpci_debug_info(zdev, m); - return 0; -} - -static int pci_debug_seq_open(struct inode *inode, struct file *filp) -{ - return single_open(filp, pci_debug_show, - file_inode(filp)->i_private); -} - -static const struct file_operations debugfs_pci_debug_fops = { - .open = pci_debug_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - void zpci_debug_init_device(struct zpci_dev *zdev) { zdev->debugfs_dev = debugfs_create_dir(dev_name(&zdev->pdev->dev), @@ -149,19 +128,11 @@ void zpci_debug_init_device(struct zpci_dev *zdev) &debugfs_pci_perf_fops); if (IS_ERR(zdev->debugfs_perf)) zdev->debugfs_perf = NULL; - - zdev->debugfs_debug = debugfs_create_file("debug", - S_IFREG | S_IRUGO | S_IWUSR, - zdev->debugfs_dev, zdev, - &debugfs_pci_debug_fops); - if (IS_ERR(zdev->debugfs_debug)) - zdev->debugfs_debug = NULL; } void zpci_debug_exit_device(struct zpci_dev *zdev) { debugfs_remove(zdev->debugfs_perf); - debugfs_remove(zdev->debugfs_debug); debugfs_remove(zdev->debugfs_dev); } diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c index f8e69d5bc0a9..a2343c1f6e04 100644 --- a/arch/s390/pci/pci_dma.c +++ b/arch/s390/pci/pci_dma.c @@ -263,7 +263,7 @@ static dma_addr_t s390_dma_map_pages(struct device *dev, struct page *page, enum dma_data_direction direction, struct dma_attrs *attrs) { - struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev)); + struct zpci_dev *zdev = get_zdev(to_pci_dev(dev)); unsigned long nr_pages, iommu_page_index; unsigned long pa = page_to_phys(page) + offset; int flags = ZPCI_PTE_VALID; @@ -304,7 +304,7 @@ static void s390_dma_unmap_pages(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction, struct dma_attrs *attrs) { - struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev)); + struct zpci_dev *zdev = get_zdev(to_pci_dev(dev)); unsigned long iommu_page_index; int npages; @@ -323,7 +323,7 @@ static void *s390_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag, struct dma_attrs *attrs) { - struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev)); + struct zpci_dev *zdev = get_zdev(to_pci_dev(dev)); struct page *page; unsigned long pa; dma_addr_t map; diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index a42cce69d0a0..e99a2557f186 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -15,40 +15,36 @@ static ssize_t show_fid(struct device *dev, struct device_attribute *attr, char *buf) { - struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev)); + struct zpci_dev *zdev = get_zdev(to_pci_dev(dev)); - sprintf(buf, "0x%08x\n", zdev->fid); - return strlen(buf); + return sprintf(buf, "0x%08x\n", zdev->fid); } static DEVICE_ATTR(function_id, S_IRUGO, show_fid, NULL); static ssize_t show_fh(struct device *dev, struct device_attribute *attr, char *buf) { - struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev)); + struct zpci_dev *zdev = get_zdev(to_pci_dev(dev)); - sprintf(buf, "0x%08x\n", zdev->fh); - return strlen(buf); + return sprintf(buf, "0x%08x\n", zdev->fh); } static DEVICE_ATTR(function_handle, S_IRUGO, show_fh, NULL); static ssize_t show_pchid(struct device *dev, struct device_attribute *attr, char *buf) { - struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev)); + struct zpci_dev *zdev = get_zdev(to_pci_dev(dev)); - sprintf(buf, "0x%04x\n", zdev->pchid); - return strlen(buf); + return sprintf(buf, "0x%04x\n", zdev->pchid); } static DEVICE_ATTR(pchid, S_IRUGO, show_pchid, NULL); static ssize_t show_pfgid(struct device *dev, struct device_attribute *attr, char *buf) { - struct zpci_dev *zdev = get_zdev(container_of(dev, struct pci_dev, dev)); + struct zpci_dev *zdev = get_zdev(to_pci_dev(dev)); - sprintf(buf, "0x%02x\n", zdev->pfgid); - return strlen(buf); + return sprintf(buf, "0x%02x\n", zdev->pfgid); } static DEVICE_ATTR(pfgid, S_IRUGO, show_pfgid, NULL); diff --git a/arch/score/include/asm/Kbuild b/arch/score/include/asm/Kbuild index cebaff8069a1..e1c7bb999b06 100644 --- a/arch/score/include/asm/Kbuild +++ b/arch/score/include/asm/Kbuild @@ -3,3 +3,4 @@ header-y += generic-y += clkdev.h generic-y += trace_clock.h +generic-y += xor.h diff --git a/arch/score/include/asm/dma-mapping.h b/arch/score/include/asm/dma-mapping.h deleted file mode 100644 index f9c0193c7a53..000000000000 --- a/arch/score/include/asm/dma-mapping.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_SCORE_DMA_MAPPING_H -#define _ASM_SCORE_DMA_MAPPING_H - -#include <asm-generic/dma-mapping-broken.h> - -#endif /* _ASM_SCORE_DMA_MAPPING_H */ diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 2031c65fd4ea..bc4d3f5d2e5d 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -254,7 +254,7 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, const char *type; u32 class; - dev = alloc_pci_dev(); + dev = pci_alloc_dev(bus); if (!dev) return NULL; @@ -281,7 +281,6 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, printk(" create device, devfn: %x, type: %s\n", devfn, type); - dev->bus = bus; dev->sysdata = node; dev->dev.parent = bus->bridge; dev->dev.bus = &pci_bus_type; @@ -327,7 +326,7 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) pci_set_master(dev); - dev->current_state = 4; /* unknown power state */ + dev->current_state = PCI_UNKNOWN; /* unknown power state */ dev->error_state = pci_channel_io_normal; dev->dma_mask = 0xffffffff; diff --git a/arch/unicore32/boot/compressed/Makefile b/arch/unicore32/boot/compressed/Makefile index 950a9afa38f8..96494fb646f7 100644 --- a/arch/unicore32/boot/compressed/Makefile +++ b/arch/unicore32/boot/compressed/Makefile @@ -17,7 +17,7 @@ OBJS := misc.o # font.c and font.o CFLAGS_font.o := -Dstatic= -$(obj)/font.c: $(srctree)/drivers/video/console/font_8x8.c +$(obj)/font.c: $(srctree)/lib/fonts/font_8x8.c $(call cmd,shipped) # piggy.S and piggy.o diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c index ef69c0c82825..374a055a8e6b 100644 --- a/arch/unicore32/kernel/pci.c +++ b/arch/unicore32/kernel/pci.c @@ -277,11 +277,6 @@ static int __init pci_common_init(void) pci_bus_assign_resources(puv3_bus); } - /* - * Tell drivers about devices found. - */ - pci_bus_add_devices(puv3_bus); - return 0; } subsys_initcall(pci_common_init); diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index b31bf97775fc..2dfac58f3b11 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -111,7 +111,7 @@ static inline void acpi_disable_pci(void) } /* Low-level suspend routine. */ -extern int acpi_suspend_lowlevel(void); +extern int (*acpi_suspend_lowlevel)(void); /* Physical address to resume after wakeup */ #define acpi_wakeup_address ((unsigned long)(real_mode_header->wakeup_start)) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index af9c5525434d..f87f7fcefa0a 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -222,14 +222,22 @@ struct kvm_mmu_page { int root_count; /* Currently serving as active root */ unsigned int unsync_children; unsigned long parent_ptes; /* Reverse mapping for parent_pte */ + + /* The page is obsolete if mmu_valid_gen != kvm->arch.mmu_valid_gen. */ + unsigned long mmu_valid_gen; + DECLARE_BITMAP(unsync_child_bitmap, 512); #ifdef CONFIG_X86_32 + /* + * Used out of the mmu-lock to avoid reading spte values while an + * update is in progress; see the comments in __get_spte_lockless(). + */ int clear_spte_count; #endif + /* Number of writes since the last time traversal visited this page. */ int write_flooding_count; - bool mmio_cached; }; struct kvm_pio_request { @@ -529,11 +537,14 @@ struct kvm_arch { unsigned int n_requested_mmu_pages; unsigned int n_max_mmu_pages; unsigned int indirect_shadow_pages; + unsigned long mmu_valid_gen; struct hlist_head mmu_page_hash[KVM_NUM_MMU_PAGES]; /* * Hash table of struct kvm_mmu_page. */ struct list_head active_mmu_pages; + struct list_head zapped_obsolete_pages; + struct list_head assigned_dev_head; struct iommu_domain *iommu_domain; int iommu_flags; @@ -769,7 +780,7 @@ void kvm_mmu_write_protect_pt_masked(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn_offset, unsigned long mask); void kvm_mmu_zap_all(struct kvm *kvm); -void kvm_mmu_zap_mmio_sptes(struct kvm *kvm); +void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm); unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm); void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned int kvm_nr_mmu_pages); diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 230c8ea878e5..d81a972dd506 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -44,6 +44,7 @@ #include <asm/mpspec.h> #include <asm/smp.h> +#include "sleep.h" /* To include x86_acpi_suspend_lowlevel */ static int __initdata acpi_force = 0; u32 acpi_rsdt_forced; int acpi_disabled; @@ -559,6 +560,12 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi, int (*__acpi_register_gsi)(struct device *dev, u32 gsi, int trigger, int polarity) = acpi_register_gsi_pic; +#ifdef CONFIG_ACPI_SLEEP +int (*acpi_suspend_lowlevel)(void) = x86_acpi_suspend_lowlevel; +#else +int (*acpi_suspend_lowlevel)(void); +#endif + /* * success: return IRQ number (>=0) * failure: return < 0 diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index b44577bc9744..2a34aaf3c8f1 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -26,12 +26,12 @@ static char temp_stack[4096]; #endif /** - * acpi_suspend_lowlevel - save kernel state + * x86_acpi_suspend_lowlevel - save kernel state * * Create an identity mapped page table and copy the wakeup routine to * low memory. */ -int acpi_suspend_lowlevel(void) +int x86_acpi_suspend_lowlevel(void) { struct wakeup_header *header = (struct wakeup_header *) __va(real_mode_header->wakeup_header); diff --git a/arch/x86/kernel/acpi/sleep.h b/arch/x86/kernel/acpi/sleep.h index 67f59f8c6956..c9c2c982d5e4 100644 --- a/arch/x86/kernel/acpi/sleep.h +++ b/arch/x86/kernel/acpi/sleep.h @@ -15,3 +15,5 @@ extern unsigned long acpi_copy_wakeup_routine(unsigned long); extern void wakeup_long64(void); extern void do_suspend_lowlevel(void); + +extern int x86_acpi_suspend_lowlevel(void); diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c index 2f3a7995e56a..98f2083832eb 100644 --- a/arch/x86/kernel/cpu/mcheck/therm_throt.c +++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c @@ -182,11 +182,6 @@ static int therm_throt_process(bool new_event, int event, int level) this_cpu, level == CORE_LEVEL ? "Core" : "Package", state->count); - else - printk(KERN_CRIT "CPU%d: %s power limit notification (total events = %lu)\n", - this_cpu, - level == CORE_LEVEL ? "Core" : "Package", - state->count); return 1; } if (old_event) { @@ -194,10 +189,6 @@ static int therm_throt_process(bool new_event, int event, int level) printk(KERN_INFO "CPU%d: %s temperature/speed normal\n", this_cpu, level == CORE_LEVEL ? "Core" : "Package"); - else - printk(KERN_INFO "CPU%d: %s power limit normal\n", - this_cpu, - level == CORE_LEVEL ? "Core" : "Package"); return 1; } @@ -220,6 +211,15 @@ static int thresh_event_valid(int event) return 1; } +static bool int_pln_enable; +static int __init int_pln_enable_setup(char *s) +{ + int_pln_enable = true; + + return 1; +} +__setup("int_pln_enable", int_pln_enable_setup); + #ifdef CONFIG_SYSFS /* Add/Remove thermal_throttle interface for CPU device: */ static __cpuinit int thermal_throttle_add_dev(struct device *dev, @@ -232,7 +232,7 @@ static __cpuinit int thermal_throttle_add_dev(struct device *dev, if (err) return err; - if (cpu_has(c, X86_FEATURE_PLN)) + if (cpu_has(c, X86_FEATURE_PLN) && int_pln_enable) err = sysfs_add_file_to_group(&dev->kobj, &dev_attr_core_power_limit_count.attr, thermal_attr_group.name); @@ -240,7 +240,7 @@ static __cpuinit int thermal_throttle_add_dev(struct device *dev, err = sysfs_add_file_to_group(&dev->kobj, &dev_attr_package_throttle_count.attr, thermal_attr_group.name); - if (cpu_has(c, X86_FEATURE_PLN)) + if (cpu_has(c, X86_FEATURE_PLN) && int_pln_enable) err = sysfs_add_file_to_group(&dev->kobj, &dev_attr_package_power_limit_count.attr, thermal_attr_group.name); @@ -353,7 +353,7 @@ static void intel_thermal_interrupt(void) CORE_LEVEL) != 0) mce_log_therm_throt_event(msr_val); - if (this_cpu_has(X86_FEATURE_PLN)) + if (this_cpu_has(X86_FEATURE_PLN) && int_pln_enable) therm_throt_process(msr_val & THERM_STATUS_POWER_LIMIT, POWER_LIMIT_EVENT, CORE_LEVEL); @@ -363,7 +363,7 @@ static void intel_thermal_interrupt(void) therm_throt_process(msr_val & PACKAGE_THERM_STATUS_PROCHOT, THERMAL_THROTTLING_EVENT, PACKAGE_LEVEL); - if (this_cpu_has(X86_FEATURE_PLN)) + if (this_cpu_has(X86_FEATURE_PLN) && int_pln_enable) therm_throt_process(msr_val & PACKAGE_THERM_STATUS_POWER_LIMIT, POWER_LIMIT_EVENT, @@ -482,9 +482,13 @@ void intel_init_thermal(struct cpuinfo_x86 *c) apic_write(APIC_LVTTHMR, h); rdmsr(MSR_IA32_THERM_INTERRUPT, l, h); - if (cpu_has(c, X86_FEATURE_PLN)) + if (cpu_has(c, X86_FEATURE_PLN) && !int_pln_enable) wrmsr(MSR_IA32_THERM_INTERRUPT, - l | (THERM_INT_LOW_ENABLE + (l | (THERM_INT_LOW_ENABLE + | THERM_INT_HIGH_ENABLE)) & ~THERM_INT_PLN_ENABLE, h); + else if (cpu_has(c, X86_FEATURE_PLN) && int_pln_enable) + wrmsr(MSR_IA32_THERM_INTERRUPT, + l | (THERM_INT_LOW_ENABLE | THERM_INT_HIGH_ENABLE | THERM_INT_PLN_ENABLE), h); else wrmsr(MSR_IA32_THERM_INTERRUPT, @@ -492,9 +496,14 @@ void intel_init_thermal(struct cpuinfo_x86 *c) if (cpu_has(c, X86_FEATURE_PTS)) { rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); - if (cpu_has(c, X86_FEATURE_PLN)) + if (cpu_has(c, X86_FEATURE_PLN) && !int_pln_enable) wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, - l | (PACKAGE_THERM_INT_LOW_ENABLE + (l | (PACKAGE_THERM_INT_LOW_ENABLE + | PACKAGE_THERM_INT_HIGH_ENABLE)) + & ~PACKAGE_THERM_INT_PLN_ENABLE, h); + else if (cpu_has(c, X86_FEATURE_PLN) && int_pln_enable) + wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, + l | (PACKAGE_THERM_INT_LOW_ENABLE | PACKAGE_THERM_INT_HIGH_ENABLE | PACKAGE_THERM_INT_PLN_ENABLE), h); else diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index d609e1d84048..bf4fb04d0112 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -5,12 +5,13 @@ CFLAGS_x86.o := -I. CFLAGS_svm.o := -I. CFLAGS_vmx.o := -I. -kvm-y += $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \ - coalesced_mmio.o irq_comm.o eventfd.o \ - irqchip.o) -kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += $(addprefix ../../../virt/kvm/, \ - assigned-dev.o iommu.o) -kvm-$(CONFIG_KVM_ASYNC_PF) += $(addprefix ../../../virt/kvm/, async_pf.o) +KVM := ../../../virt/kvm + +kvm-y += $(KVM)/kvm_main.o $(KVM)/ioapic.o \ + $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o \ + $(KVM)/eventfd.o $(KVM)/irqchip.o +kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += $(KVM)/assigned-dev.o $(KVM)/iommu.o +kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \ i8254.o cpuid.o pmu.o diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 5953dcea752d..2bc1e81045b0 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -61,6 +61,8 @@ #define OpMem8 26ull /* 8-bit zero extended memory operand */ #define OpImm64 27ull /* Sign extended 16/32/64-bit immediate */ #define OpXLat 28ull /* memory at BX/EBX/RBX + zero-extended AL */ +#define OpAccLo 29ull /* Low part of extended acc (AX/AX/EAX/RAX) */ +#define OpAccHi 30ull /* High part of extended acc (-/DX/EDX/RDX) */ #define OpBits 5 /* Width of operand field */ #define OpMask ((1ull << OpBits) - 1) @@ -86,6 +88,7 @@ #define DstMem64 (OpMem64 << DstShift) #define DstImmUByte (OpImmUByte << DstShift) #define DstDX (OpDX << DstShift) +#define DstAccLo (OpAccLo << DstShift) #define DstMask (OpMask << DstShift) /* Source operand type. */ #define SrcShift 6 @@ -108,6 +111,7 @@ #define SrcImm64 (OpImm64 << SrcShift) #define SrcDX (OpDX << SrcShift) #define SrcMem8 (OpMem8 << SrcShift) +#define SrcAccHi (OpAccHi << SrcShift) #define SrcMask (OpMask << SrcShift) #define BitOp (1<<11) #define MemAbs (1<<12) /* Memory operand is absolute displacement */ @@ -138,6 +142,7 @@ /* Source 2 operand type */ #define Src2Shift (31) #define Src2None (OpNone << Src2Shift) +#define Src2Mem (OpMem << Src2Shift) #define Src2CL (OpCL << Src2Shift) #define Src2ImmByte (OpImmByte << Src2Shift) #define Src2One (OpOne << Src2Shift) @@ -155,6 +160,9 @@ #define Avx ((u64)1 << 43) /* Advanced Vector Extensions */ #define Fastop ((u64)1 << 44) /* Use opcode::u.fastop */ #define NoWrite ((u64)1 << 45) /* No writeback */ +#define SrcWrite ((u64)1 << 46) /* Write back src operand */ + +#define DstXacc (DstAccLo | SrcAccHi | SrcWrite) #define X2(x...) x, x #define X3(x...) X2(x), x @@ -171,10 +179,11 @@ /* * fastop functions have a special calling convention: * - * dst: [rdx]:rax (in/out) - * src: rbx (in/out) + * dst: rax (in/out) + * src: rdx (in/out) * src2: rcx (in) * flags: rflags (in/out) + * ex: rsi (in:fastop pointer, out:zero if exception) * * Moreover, they are all exactly FASTOP_SIZE bytes long, so functions for * different operand sizes can be reached by calculation, rather than a jump @@ -276,174 +285,17 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) } /* - * Instruction emulation: - * Most instructions are emulated directly via a fragment of inline assembly - * code. This allows us to save/restore EFLAGS and thus very easily pick up - * any modified flags. - */ - -#if defined(CONFIG_X86_64) -#define _LO32 "k" /* force 32-bit operand */ -#define _STK "%%rsp" /* stack pointer */ -#elif defined(__i386__) -#define _LO32 "" /* force 32-bit operand */ -#define _STK "%%esp" /* stack pointer */ -#endif - -/* * These EFLAGS bits are restored from saved value during emulation, and * any changes are written back to the saved value after emulation. */ #define EFLAGS_MASK (EFLG_OF|EFLG_SF|EFLG_ZF|EFLG_AF|EFLG_PF|EFLG_CF) -/* Before executing instruction: restore necessary bits in EFLAGS. */ -#define _PRE_EFLAGS(_sav, _msk, _tmp) \ - /* EFLAGS = (_sav & _msk) | (EFLAGS & ~_msk); _sav &= ~_msk; */ \ - "movl %"_sav",%"_LO32 _tmp"; " \ - "push %"_tmp"; " \ - "push %"_tmp"; " \ - "movl %"_msk",%"_LO32 _tmp"; " \ - "andl %"_LO32 _tmp",("_STK"); " \ - "pushf; " \ - "notl %"_LO32 _tmp"; " \ - "andl %"_LO32 _tmp",("_STK"); " \ - "andl %"_LO32 _tmp","__stringify(BITS_PER_LONG/4)"("_STK"); " \ - "pop %"_tmp"; " \ - "orl %"_LO32 _tmp",("_STK"); " \ - "popf; " \ - "pop %"_sav"; " - -/* After executing instruction: write-back necessary bits in EFLAGS. */ -#define _POST_EFLAGS(_sav, _msk, _tmp) \ - /* _sav |= EFLAGS & _msk; */ \ - "pushf; " \ - "pop %"_tmp"; " \ - "andl %"_msk",%"_LO32 _tmp"; " \ - "orl %"_LO32 _tmp",%"_sav"; " - #ifdef CONFIG_X86_64 #define ON64(x) x #else #define ON64(x) #endif -#define ____emulate_2op(ctxt, _op, _x, _y, _suffix, _dsttype) \ - do { \ - __asm__ __volatile__ ( \ - _PRE_EFLAGS("0", "4", "2") \ - _op _suffix " %"_x"3,%1; " \ - _POST_EFLAGS("0", "4", "2") \ - : "=m" ((ctxt)->eflags), \ - "+q" (*(_dsttype*)&(ctxt)->dst.val), \ - "=&r" (_tmp) \ - : _y ((ctxt)->src.val), "i" (EFLAGS_MASK)); \ - } while (0) - - -/* Raw emulation: instruction has two explicit operands. */ -#define __emulate_2op_nobyte(ctxt,_op,_wx,_wy,_lx,_ly,_qx,_qy) \ - do { \ - unsigned long _tmp; \ - \ - switch ((ctxt)->dst.bytes) { \ - case 2: \ - ____emulate_2op(ctxt,_op,_wx,_wy,"w",u16); \ - break; \ - case 4: \ - ____emulate_2op(ctxt,_op,_lx,_ly,"l",u32); \ - break; \ - case 8: \ - ON64(____emulate_2op(ctxt,_op,_qx,_qy,"q",u64)); \ - break; \ - } \ - } while (0) - -#define __emulate_2op(ctxt,_op,_bx,_by,_wx,_wy,_lx,_ly,_qx,_qy) \ - do { \ - unsigned long _tmp; \ - switch ((ctxt)->dst.bytes) { \ - case 1: \ - ____emulate_2op(ctxt,_op,_bx,_by,"b",u8); \ - break; \ - default: \ - __emulate_2op_nobyte(ctxt, _op, \ - _wx, _wy, _lx, _ly, _qx, _qy); \ - break; \ - } \ - } while (0) - -/* Source operand is byte-sized and may be restricted to just %cl. */ -#define emulate_2op_SrcB(ctxt, _op) \ - __emulate_2op(ctxt, _op, "b", "c", "b", "c", "b", "c", "b", "c") - -/* Source operand is byte, word, long or quad sized. */ -#define emulate_2op_SrcV(ctxt, _op) \ - __emulate_2op(ctxt, _op, "b", "q", "w", "r", _LO32, "r", "", "r") - -/* Source operand is word, long or quad sized. */ -#define emulate_2op_SrcV_nobyte(ctxt, _op) \ - __emulate_2op_nobyte(ctxt, _op, "w", "r", _LO32, "r", "", "r") - -/* Instruction has three operands and one operand is stored in ECX register */ -#define __emulate_2op_cl(ctxt, _op, _suffix, _type) \ - do { \ - unsigned long _tmp; \ - _type _clv = (ctxt)->src2.val; \ - _type _srcv = (ctxt)->src.val; \ - _type _dstv = (ctxt)->dst.val; \ - \ - __asm__ __volatile__ ( \ - _PRE_EFLAGS("0", "5", "2") \ - _op _suffix " %4,%1 \n" \ - _POST_EFLAGS("0", "5", "2") \ - : "=m" ((ctxt)->eflags), "+r" (_dstv), "=&r" (_tmp) \ - : "c" (_clv) , "r" (_srcv), "i" (EFLAGS_MASK) \ - ); \ - \ - (ctxt)->src2.val = (unsigned long) _clv; \ - (ctxt)->src2.val = (unsigned long) _srcv; \ - (ctxt)->dst.val = (unsigned long) _dstv; \ - } while (0) - -#define emulate_2op_cl(ctxt, _op) \ - do { \ - switch ((ctxt)->dst.bytes) { \ - case 2: \ - __emulate_2op_cl(ctxt, _op, "w", u16); \ - break; \ - case 4: \ - __emulate_2op_cl(ctxt, _op, "l", u32); \ - break; \ - case 8: \ - ON64(__emulate_2op_cl(ctxt, _op, "q", ulong)); \ - break; \ - } \ - } while (0) - -#define __emulate_1op(ctxt, _op, _suffix) \ - do { \ - unsigned long _tmp; \ - \ - __asm__ __volatile__ ( \ - _PRE_EFLAGS("0", "3", "2") \ - _op _suffix " %1; " \ - _POST_EFLAGS("0", "3", "2") \ - : "=m" ((ctxt)->eflags), "+m" ((ctxt)->dst.val), \ - "=&r" (_tmp) \ - : "i" (EFLAGS_MASK)); \ - } while (0) - -/* Instruction has only one explicit operand (no source operand). */ -#define emulate_1op(ctxt, _op) \ - do { \ - switch ((ctxt)->dst.bytes) { \ - case 1: __emulate_1op(ctxt, _op, "b"); break; \ - case 2: __emulate_1op(ctxt, _op, "w"); break; \ - case 4: __emulate_1op(ctxt, _op, "l"); break; \ - case 8: ON64(__emulate_1op(ctxt, _op, "q")); break; \ - } \ - } while (0) - static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); #define FOP_ALIGN ".align " __stringify(FASTOP_SIZE) " \n\t" @@ -462,7 +314,10 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); #define FOPNOP() FOP_ALIGN FOP_RET #define FOP1E(op, dst) \ - FOP_ALIGN #op " %" #dst " \n\t" FOP_RET + FOP_ALIGN "10: " #op " %" #dst " \n\t" FOP_RET + +#define FOP1EEX(op, dst) \ + FOP1E(op, dst) _ASM_EXTABLE(10b, kvm_fastop_exception) #define FASTOP1(op) \ FOP_START(op) \ @@ -472,24 +327,42 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); ON64(FOP1E(op##q, rax)) \ FOP_END +/* 1-operand, using src2 (for MUL/DIV r/m) */ +#define FASTOP1SRC2(op, name) \ + FOP_START(name) \ + FOP1E(op, cl) \ + FOP1E(op, cx) \ + FOP1E(op, ecx) \ + ON64(FOP1E(op, rcx)) \ + FOP_END + +/* 1-operand, using src2 (for MUL/DIV r/m), with exceptions */ +#define FASTOP1SRC2EX(op, name) \ + FOP_START(name) \ + FOP1EEX(op, cl) \ + FOP1EEX(op, cx) \ + FOP1EEX(op, ecx) \ + ON64(FOP1EEX(op, rcx)) \ + FOP_END + #define FOP2E(op, dst, src) \ FOP_ALIGN #op " %" #src ", %" #dst " \n\t" FOP_RET #define FASTOP2(op) \ FOP_START(op) \ - FOP2E(op##b, al, bl) \ - FOP2E(op##w, ax, bx) \ - FOP2E(op##l, eax, ebx) \ - ON64(FOP2E(op##q, rax, rbx)) \ + FOP2E(op##b, al, dl) \ + FOP2E(op##w, ax, dx) \ + FOP2E(op##l, eax, edx) \ + ON64(FOP2E(op##q, rax, rdx)) \ FOP_END /* 2 operand, word only */ #define FASTOP2W(op) \ FOP_START(op) \ FOPNOP() \ - FOP2E(op##w, ax, bx) \ - FOP2E(op##l, eax, ebx) \ - ON64(FOP2E(op##q, rax, rbx)) \ + FOP2E(op##w, ax, dx) \ + FOP2E(op##l, eax, edx) \ + ON64(FOP2E(op##q, rax, rdx)) \ FOP_END /* 2 operand, src is CL */ @@ -508,14 +381,17 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); #define FASTOP3WCL(op) \ FOP_START(op) \ FOPNOP() \ - FOP3E(op##w, ax, bx, cl) \ - FOP3E(op##l, eax, ebx, cl) \ - ON64(FOP3E(op##q, rax, rbx, cl)) \ + FOP3E(op##w, ax, dx, cl) \ + FOP3E(op##l, eax, edx, cl) \ + ON64(FOP3E(op##q, rax, rdx, cl)) \ FOP_END /* Special case for SETcc - 1 instruction per cc */ #define FOP_SETCC(op) ".align 4; " #op " %al; ret \n\t" +asm(".global kvm_fastop_exception \n" + "kvm_fastop_exception: xor %esi, %esi; ret"); + FOP_START(setcc) FOP_SETCC(seto) FOP_SETCC(setno) @@ -538,47 +414,6 @@ FOP_END; FOP_START(salc) "pushf; sbb %al, %al; popf \n\t" FOP_RET FOP_END; -#define __emulate_1op_rax_rdx(ctxt, _op, _suffix, _ex) \ - do { \ - unsigned long _tmp; \ - ulong *rax = reg_rmw((ctxt), VCPU_REGS_RAX); \ - ulong *rdx = reg_rmw((ctxt), VCPU_REGS_RDX); \ - \ - __asm__ __volatile__ ( \ - _PRE_EFLAGS("0", "5", "1") \ - "1: \n\t" \ - _op _suffix " %6; " \ - "2: \n\t" \ - _POST_EFLAGS("0", "5", "1") \ - ".pushsection .fixup,\"ax\" \n\t" \ - "3: movb $1, %4 \n\t" \ - "jmp 2b \n\t" \ - ".popsection \n\t" \ - _ASM_EXTABLE(1b, 3b) \ - : "=m" ((ctxt)->eflags), "=&r" (_tmp), \ - "+a" (*rax), "+d" (*rdx), "+qm"(_ex) \ - : "i" (EFLAGS_MASK), "m" ((ctxt)->src.val)); \ - } while (0) - -/* instruction has only one source operand, destination is implicit (e.g. mul, div, imul, idiv) */ -#define emulate_1op_rax_rdx(ctxt, _op, _ex) \ - do { \ - switch((ctxt)->src.bytes) { \ - case 1: \ - __emulate_1op_rax_rdx(ctxt, _op, "b", _ex); \ - break; \ - case 2: \ - __emulate_1op_rax_rdx(ctxt, _op, "w", _ex); \ - break; \ - case 4: \ - __emulate_1op_rax_rdx(ctxt, _op, "l", _ex); \ - break; \ - case 8: ON64( \ - __emulate_1op_rax_rdx(ctxt, _op, "q", _ex)); \ - break; \ - } \ - } while (0) - static int emulator_check_intercept(struct x86_emulate_ctxt *ctxt, enum x86_intercept intercept, enum x86_intercept_stage stage) @@ -988,6 +823,11 @@ FASTOP2(xor); FASTOP2(cmp); FASTOP2(test); +FASTOP1SRC2(mul, mul_ex); +FASTOP1SRC2(imul, imul_ex); +FASTOP1SRC2EX(div, div_ex); +FASTOP1SRC2EX(idiv, idiv_ex); + FASTOP3WCL(shld); FASTOP3WCL(shrd); @@ -1013,6 +853,8 @@ FASTOP2W(bts); FASTOP2W(btr); FASTOP2W(btc); +FASTOP2(xadd); + static u8 test_cc(unsigned int condition, unsigned long flags) { u8 rc; @@ -1726,45 +1568,42 @@ static void write_register_operand(struct operand *op) } } -static int writeback(struct x86_emulate_ctxt *ctxt) +static int writeback(struct x86_emulate_ctxt *ctxt, struct operand *op) { int rc; - if (ctxt->d & NoWrite) - return X86EMUL_CONTINUE; - - switch (ctxt->dst.type) { + switch (op->type) { case OP_REG: - write_register_operand(&ctxt->dst); + write_register_operand(op); break; case OP_MEM: if (ctxt->lock_prefix) rc = segmented_cmpxchg(ctxt, - ctxt->dst.addr.mem, - &ctxt->dst.orig_val, - &ctxt->dst.val, - ctxt->dst.bytes); + op->addr.mem, + &op->orig_val, + &op->val, + op->bytes); else rc = segmented_write(ctxt, - ctxt->dst.addr.mem, - &ctxt->dst.val, - ctxt->dst.bytes); + op->addr.mem, + &op->val, + op->bytes); if (rc != X86EMUL_CONTINUE) return rc; break; case OP_MEM_STR: rc = segmented_write(ctxt, - ctxt->dst.addr.mem, - ctxt->dst.data, - ctxt->dst.bytes * ctxt->dst.count); + op->addr.mem, + op->data, + op->bytes * op->count); if (rc != X86EMUL_CONTINUE) return rc; break; case OP_XMM: - write_sse_reg(ctxt, &ctxt->dst.vec_val, ctxt->dst.addr.xmm); + write_sse_reg(ctxt, &op->vec_val, op->addr.xmm); break; case OP_MM: - write_mmx_reg(ctxt, &ctxt->dst.mm_val, ctxt->dst.addr.mm); + write_mmx_reg(ctxt, &op->mm_val, op->addr.mm); break; case OP_NONE: /* no writeback */ @@ -2117,42 +1956,6 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -static int em_mul_ex(struct x86_emulate_ctxt *ctxt) -{ - u8 ex = 0; - - emulate_1op_rax_rdx(ctxt, "mul", ex); - return X86EMUL_CONTINUE; -} - -static int em_imul_ex(struct x86_emulate_ctxt *ctxt) -{ - u8 ex = 0; - - emulate_1op_rax_rdx(ctxt, "imul", ex); - return X86EMUL_CONTINUE; -} - -static int em_div_ex(struct x86_emulate_ctxt *ctxt) -{ - u8 de = 0; - - emulate_1op_rax_rdx(ctxt, "div", de); - if (de) - return emulate_de(ctxt); - return X86EMUL_CONTINUE; -} - -static int em_idiv_ex(struct x86_emulate_ctxt *ctxt) -{ - u8 de = 0; - - emulate_1op_rax_rdx(ctxt, "idiv", de); - if (de) - return emulate_de(ctxt); - return X86EMUL_CONTINUE; -} - static int em_grp45(struct x86_emulate_ctxt *ctxt) { int rc = X86EMUL_CONTINUE; @@ -3734,10 +3537,10 @@ static const struct opcode group3[] = { F(DstMem | SrcImm | NoWrite, em_test), F(DstMem | SrcNone | Lock, em_not), F(DstMem | SrcNone | Lock, em_neg), - I(SrcMem, em_mul_ex), - I(SrcMem, em_imul_ex), - I(SrcMem, em_div_ex), - I(SrcMem, em_idiv_ex), + F(DstXacc | Src2Mem, em_mul_ex), + F(DstXacc | Src2Mem, em_imul_ex), + F(DstXacc | Src2Mem, em_div_ex), + F(DstXacc | Src2Mem, em_idiv_ex), }; static const struct opcode group4[] = { @@ -4064,7 +3867,7 @@ static const struct opcode twobyte_table[256] = { F(DstReg | SrcMem | ModRM, em_bsf), F(DstReg | SrcMem | ModRM, em_bsr), D(DstReg | SrcMem8 | ModRM | Mov), D(DstReg | SrcMem16 | ModRM | Mov), /* 0xC0 - 0xC7 */ - D2bv(DstMem | SrcReg | ModRM | Lock), + F2bv(DstMem | SrcReg | ModRM | SrcWrite | Lock, em_xadd), N, D(DstMem | SrcReg | ModRM | Mov), N, N, N, GD(0, &group9), /* 0xC8 - 0xCF */ @@ -4172,6 +3975,24 @@ static int decode_operand(struct x86_emulate_ctxt *ctxt, struct operand *op, fetch_register_operand(op); op->orig_val = op->val; break; + case OpAccLo: + op->type = OP_REG; + op->bytes = (ctxt->d & ByteOp) ? 2 : ctxt->op_bytes; + op->addr.reg = reg_rmw(ctxt, VCPU_REGS_RAX); + fetch_register_operand(op); + op->orig_val = op->val; + break; + case OpAccHi: + if (ctxt->d & ByteOp) { + op->type = OP_NONE; + break; + } + op->type = OP_REG; + op->bytes = ctxt->op_bytes; + op->addr.reg = reg_rmw(ctxt, VCPU_REGS_RDX); + fetch_register_operand(op); + op->orig_val = op->val; + break; case OpDI: op->type = OP_MEM; op->bytes = (ctxt->d & ByteOp) ? 1 : ctxt->op_bytes; @@ -4553,11 +4374,15 @@ static void fetch_possible_mmx_operand(struct x86_emulate_ctxt *ctxt, static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)) { ulong flags = (ctxt->eflags & EFLAGS_MASK) | X86_EFLAGS_IF; - fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE; + if (!(ctxt->d & ByteOp)) + fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE; asm("push %[flags]; popf; call *%[fastop]; pushf; pop %[flags]\n" - : "+a"(ctxt->dst.val), "+b"(ctxt->src.val), [flags]"+D"(flags) - : "c"(ctxt->src2.val), [fastop]"S"(fop)); + : "+a"(ctxt->dst.val), "+d"(ctxt->src.val), [flags]"+D"(flags), + [fastop]"+S"(fop) + : "c"(ctxt->src2.val)); ctxt->eflags = (ctxt->eflags & ~EFLAGS_MASK) | (flags & EFLAGS_MASK); + if (!fop) /* exception is returned in fop variable */ + return emulate_de(ctxt); return X86EMUL_CONTINUE; } @@ -4773,9 +4598,17 @@ special_insn: goto done; writeback: - rc = writeback(ctxt); - if (rc != X86EMUL_CONTINUE) - goto done; + if (!(ctxt->d & NoWrite)) { + rc = writeback(ctxt, &ctxt->dst); + if (rc != X86EMUL_CONTINUE) + goto done; + } + if (ctxt->d & SrcWrite) { + BUG_ON(ctxt->src.type == OP_MEM || ctxt->src.type == OP_MEM_STR); + rc = writeback(ctxt, &ctxt->src); + if (rc != X86EMUL_CONTINUE) + goto done; + } /* * restore dst type in case the decoding will be reused @@ -4872,12 +4705,6 @@ twobyte_insn: ctxt->dst.val = (ctxt->src.bytes == 1) ? (s8) ctxt->src.val : (s16) ctxt->src.val; break; - case 0xc0 ... 0xc1: /* xadd */ - fastop(ctxt, em_add); - /* Write back the register source. */ - ctxt->src.val = ctxt->dst.orig_val; - write_register_operand(&ctxt->src); - break; case 0xc3: /* movnti */ ctxt->dst.bytes = ctxt->op_bytes; ctxt->dst.val = (ctxt->op_bytes == 4) ? (u32) ctxt->src.val : diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 0eee2c8b64d1..afc11245827c 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1608,8 +1608,8 @@ void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu) return; if (atomic_read(&apic->lapic_timer.pending) > 0) { - if (kvm_apic_local_deliver(apic, APIC_LVTT)) - atomic_dec(&apic->lapic_timer.pending); + kvm_apic_local_deliver(apic, APIC_LVTT); + atomic_set(&apic->lapic_timer.pending, 0); } } diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 004cc87b781c..0d094da49541 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -197,15 +197,63 @@ void kvm_mmu_set_mmio_spte_mask(u64 mmio_mask) } EXPORT_SYMBOL_GPL(kvm_mmu_set_mmio_spte_mask); -static void mark_mmio_spte(u64 *sptep, u64 gfn, unsigned access) +/* + * spte bits of bit 3 ~ bit 11 are used as low 9 bits of generation number, + * the bits of bits 52 ~ bit 61 are used as high 10 bits of generation + * number. + */ +#define MMIO_SPTE_GEN_LOW_SHIFT 3 +#define MMIO_SPTE_GEN_HIGH_SHIFT 52 + +#define MMIO_GEN_SHIFT 19 +#define MMIO_GEN_LOW_SHIFT 9 +#define MMIO_GEN_LOW_MASK ((1 << MMIO_GEN_LOW_SHIFT) - 1) +#define MMIO_GEN_MASK ((1 << MMIO_GEN_SHIFT) - 1) +#define MMIO_MAX_GEN ((1 << MMIO_GEN_SHIFT) - 1) + +static u64 generation_mmio_spte_mask(unsigned int gen) { - struct kvm_mmu_page *sp = page_header(__pa(sptep)); + u64 mask; + + WARN_ON(gen > MMIO_MAX_GEN); + + mask = (gen & MMIO_GEN_LOW_MASK) << MMIO_SPTE_GEN_LOW_SHIFT; + mask |= ((u64)gen >> MMIO_GEN_LOW_SHIFT) << MMIO_SPTE_GEN_HIGH_SHIFT; + return mask; +} + +static unsigned int get_mmio_spte_generation(u64 spte) +{ + unsigned int gen; + + spte &= ~shadow_mmio_mask; + + gen = (spte >> MMIO_SPTE_GEN_LOW_SHIFT) & MMIO_GEN_LOW_MASK; + gen |= (spte >> MMIO_SPTE_GEN_HIGH_SHIFT) << MMIO_GEN_LOW_SHIFT; + return gen; +} + +static unsigned int kvm_current_mmio_generation(struct kvm *kvm) +{ + /* + * Init kvm generation close to MMIO_MAX_GEN to easily test the + * code of handling generation number wrap-around. + */ + return (kvm_memslots(kvm)->generation + + MMIO_MAX_GEN - 150) & MMIO_GEN_MASK; +} + +static void mark_mmio_spte(struct kvm *kvm, u64 *sptep, u64 gfn, + unsigned access) +{ + unsigned int gen = kvm_current_mmio_generation(kvm); + u64 mask = generation_mmio_spte_mask(gen); access &= ACC_WRITE_MASK | ACC_USER_MASK; + mask |= shadow_mmio_mask | access | gfn << PAGE_SHIFT; - sp->mmio_cached = true; - trace_mark_mmio_spte(sptep, gfn, access); - mmu_spte_set(sptep, shadow_mmio_mask | access | gfn << PAGE_SHIFT); + trace_mark_mmio_spte(sptep, gfn, access, gen); + mmu_spte_set(sptep, mask); } static bool is_mmio_spte(u64 spte) @@ -215,24 +263,38 @@ static bool is_mmio_spte(u64 spte) static gfn_t get_mmio_spte_gfn(u64 spte) { - return (spte & ~shadow_mmio_mask) >> PAGE_SHIFT; + u64 mask = generation_mmio_spte_mask(MMIO_MAX_GEN) | shadow_mmio_mask; + return (spte & ~mask) >> PAGE_SHIFT; } static unsigned get_mmio_spte_access(u64 spte) { - return (spte & ~shadow_mmio_mask) & ~PAGE_MASK; + u64 mask = generation_mmio_spte_mask(MMIO_MAX_GEN) | shadow_mmio_mask; + return (spte & ~mask) & ~PAGE_MASK; } -static bool set_mmio_spte(u64 *sptep, gfn_t gfn, pfn_t pfn, unsigned access) +static bool set_mmio_spte(struct kvm *kvm, u64 *sptep, gfn_t gfn, + pfn_t pfn, unsigned access) { if (unlikely(is_noslot_pfn(pfn))) { - mark_mmio_spte(sptep, gfn, access); + mark_mmio_spte(kvm, sptep, gfn, access); return true; } return false; } +static bool check_mmio_spte(struct kvm *kvm, u64 spte) +{ + unsigned int kvm_gen, spte_gen; + + kvm_gen = kvm_current_mmio_generation(kvm); + spte_gen = get_mmio_spte_generation(spte); + + trace_check_mmio_spte(spte, kvm_gen, spte_gen); + return likely(kvm_gen == spte_gen); +} + static inline u64 rsvd_bits(int s, int e) { return ((1ULL << (e - s + 1)) - 1) << s; @@ -404,9 +466,20 @@ static u64 __update_clear_spte_slow(u64 *sptep, u64 spte) /* * The idea using the light way get the spte on x86_32 guest is from * gup_get_pte(arch/x86/mm/gup.c). - * The difference is we can not catch the spte tlb flush if we leave - * guest mode, so we emulate it by increase clear_spte_count when spte - * is cleared. + * + * An spte tlb flush may be pending, because kvm_set_pte_rmapp + * coalesces them and we are running out of the MMU lock. Therefore + * we need to protect against in-progress updates of the spte. + * + * Reading the spte while an update is in progress may get the old value + * for the high part of the spte. The race is fine for a present->non-present + * change (because the high part of the spte is ignored for non-present spte), + * but for a present->present change we must reread the spte. + * + * All such changes are done in two steps (present->non-present and + * non-present->present), hence it is enough to count the number of + * present->non-present updates: if it changed while reading the spte, + * we might have hit the race. This is done using clear_spte_count. */ static u64 __get_spte_lockless(u64 *sptep) { @@ -1511,6 +1584,12 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, if (!direct) sp->gfns = mmu_memory_cache_alloc(&vcpu->arch.mmu_page_cache); set_page_private(virt_to_page(sp->spt), (unsigned long)sp); + + /* + * The active_mmu_pages list is the FIFO list, do not move the + * page until it is zapped. kvm_zap_obsolete_pages depends on + * this feature. See the comments in kvm_zap_obsolete_pages(). + */ list_add(&sp->link, &vcpu->kvm->arch.active_mmu_pages); sp->parent_ptes = 0; mmu_page_add_parent_pte(vcpu, sp, parent_pte); @@ -1648,6 +1727,16 @@ static int kvm_mmu_prepare_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp, static void kvm_mmu_commit_zap_page(struct kvm *kvm, struct list_head *invalid_list); +/* + * NOTE: we should pay more attention on the zapped-obsolete page + * (is_obsolete_sp(sp) && sp->role.invalid) when you do hash list walk + * since it has been deleted from active_mmu_pages but still can be found + * at hast list. + * + * for_each_gfn_indirect_valid_sp has skipped that kind of page and + * kvm_mmu_get_page(), the only user of for_each_gfn_sp(), has skipped + * all the obsolete pages. + */ #define for_each_gfn_sp(_kvm, _sp, _gfn) \ hlist_for_each_entry(_sp, \ &(_kvm)->arch.mmu_page_hash[kvm_page_table_hashfn(_gfn)], hash_link) \ @@ -1838,6 +1927,11 @@ static void clear_sp_write_flooding_count(u64 *spte) __clear_sp_write_flooding_count(sp); } +static bool is_obsolete_sp(struct kvm *kvm, struct kvm_mmu_page *sp) +{ + return unlikely(sp->mmu_valid_gen != kvm->arch.mmu_valid_gen); +} + static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, gfn_t gfn, gva_t gaddr, @@ -1864,6 +1958,9 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, role.quadrant = quadrant; } for_each_gfn_sp(vcpu->kvm, sp, gfn) { + if (is_obsolete_sp(vcpu->kvm, sp)) + continue; + if (!need_sync && sp->unsync) need_sync = true; @@ -1900,6 +1997,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, account_shadowed(vcpu->kvm, gfn); } + sp->mmu_valid_gen = vcpu->kvm->arch.mmu_valid_gen; init_shadow_page_table(sp); trace_kvm_mmu_get_page(sp, true); return sp; @@ -2070,8 +2168,10 @@ static int kvm_mmu_prepare_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp, ret = mmu_zap_unsync_children(kvm, sp, invalid_list); kvm_mmu_page_unlink_children(kvm, sp); kvm_mmu_unlink_parents(kvm, sp); + if (!sp->role.invalid && !sp->role.direct) unaccount_shadowed(kvm, sp->gfn); + if (sp->unsync) kvm_unlink_unsync_page(kvm, sp); if (!sp->root_count) { @@ -2081,7 +2181,13 @@ static int kvm_mmu_prepare_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp, kvm_mod_used_mmu_pages(kvm, -1); } else { list_move(&sp->link, &kvm->arch.active_mmu_pages); - kvm_reload_remote_mmus(kvm); + + /* + * The obsolete pages can not be used on any vcpus. + * See the comments in kvm_mmu_invalidate_zap_all_pages(). + */ + if (!sp->role.invalid && !is_obsolete_sp(kvm, sp)) + kvm_reload_remote_mmus(kvm); } sp->role.invalid = 1; @@ -2331,7 +2437,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, u64 spte; int ret = 0; - if (set_mmio_spte(sptep, gfn, pfn, pte_access)) + if (set_mmio_spte(vcpu->kvm, sptep, gfn, pfn, pte_access)) return 0; spte = PT_PRESENT_MASK; @@ -2869,22 +2975,25 @@ static void mmu_free_roots(struct kvm_vcpu *vcpu) if (!VALID_PAGE(vcpu->arch.mmu.root_hpa)) return; - spin_lock(&vcpu->kvm->mmu_lock); + if (vcpu->arch.mmu.shadow_root_level == PT64_ROOT_LEVEL && (vcpu->arch.mmu.root_level == PT64_ROOT_LEVEL || vcpu->arch.mmu.direct_map)) { hpa_t root = vcpu->arch.mmu.root_hpa; + spin_lock(&vcpu->kvm->mmu_lock); sp = page_header(root); --sp->root_count; if (!sp->root_count && sp->role.invalid) { kvm_mmu_prepare_zap_page(vcpu->kvm, sp, &invalid_list); kvm_mmu_commit_zap_page(vcpu->kvm, &invalid_list); } - vcpu->arch.mmu.root_hpa = INVALID_PAGE; spin_unlock(&vcpu->kvm->mmu_lock); + vcpu->arch.mmu.root_hpa = INVALID_PAGE; return; } + + spin_lock(&vcpu->kvm->mmu_lock); for (i = 0; i < 4; ++i) { hpa_t root = vcpu->arch.mmu.pae_root[i]; @@ -3148,17 +3257,12 @@ static u64 walk_shadow_page_get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr) return spte; } -/* - * If it is a real mmio page fault, return 1 and emulat the instruction - * directly, return 0 to let CPU fault again on the address, -1 is - * returned if bug is detected. - */ int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct) { u64 spte; if (quickly_check_mmio_pf(vcpu, addr, direct)) - return 1; + return RET_MMIO_PF_EMULATE; spte = walk_shadow_page_get_mmio_spte(vcpu, addr); @@ -3166,12 +3270,15 @@ int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct) gfn_t gfn = get_mmio_spte_gfn(spte); unsigned access = get_mmio_spte_access(spte); + if (!check_mmio_spte(vcpu->kvm, spte)) + return RET_MMIO_PF_INVALID; + if (direct) addr = 0; trace_handle_mmio_page_fault(addr, gfn, access); vcpu_cache_mmio_info(vcpu, addr, gfn, access); - return 1; + return RET_MMIO_PF_EMULATE; } /* @@ -3179,13 +3286,13 @@ int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct) * it's a BUG if the gfn is not a mmio page. */ if (direct && !check_direct_spte_mmio_pf(spte)) - return -1; + return RET_MMIO_PF_BUG; /* * If the page table is zapped by other cpus, let CPU fault again on * the address. */ - return 0; + return RET_MMIO_PF_RETRY; } EXPORT_SYMBOL_GPL(handle_mmio_page_fault_common); @@ -3195,7 +3302,7 @@ static int handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, int ret; ret = handle_mmio_page_fault_common(vcpu, addr, direct); - WARN_ON(ret < 0); + WARN_ON(ret == RET_MMIO_PF_BUG); return ret; } @@ -3207,8 +3314,12 @@ static int nonpaging_page_fault(struct kvm_vcpu *vcpu, gva_t gva, pgprintk("%s: gva %lx error %x\n", __func__, gva, error_code); - if (unlikely(error_code & PFERR_RSVD_MASK)) - return handle_mmio_page_fault(vcpu, gva, error_code, true); + if (unlikely(error_code & PFERR_RSVD_MASK)) { + r = handle_mmio_page_fault(vcpu, gva, error_code, true); + + if (likely(r != RET_MMIO_PF_INVALID)) + return r; + } r = mmu_topup_memory_caches(vcpu); if (r) @@ -3284,8 +3395,12 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code, ASSERT(vcpu); ASSERT(VALID_PAGE(vcpu->arch.mmu.root_hpa)); - if (unlikely(error_code & PFERR_RSVD_MASK)) - return handle_mmio_page_fault(vcpu, gpa, error_code, true); + if (unlikely(error_code & PFERR_RSVD_MASK)) { + r = handle_mmio_page_fault(vcpu, gpa, error_code, true); + + if (likely(r != RET_MMIO_PF_INVALID)) + return r; + } r = mmu_topup_memory_caches(vcpu); if (r) @@ -3391,8 +3506,8 @@ static inline void protect_clean_gpte(unsigned *access, unsigned gpte) *access &= mask; } -static bool sync_mmio_spte(u64 *sptep, gfn_t gfn, unsigned access, - int *nr_present) +static bool sync_mmio_spte(struct kvm *kvm, u64 *sptep, gfn_t gfn, + unsigned access, int *nr_present) { if (unlikely(is_mmio_spte(*sptep))) { if (gfn != get_mmio_spte_gfn(*sptep)) { @@ -3401,7 +3516,7 @@ static bool sync_mmio_spte(u64 *sptep, gfn_t gfn, unsigned access, } (*nr_present)++; - mark_mmio_spte(sptep, gfn, access); + mark_mmio_spte(kvm, sptep, gfn, access); return true; } @@ -3764,9 +3879,7 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu) if (r) goto out; r = mmu_alloc_roots(vcpu); - spin_lock(&vcpu->kvm->mmu_lock); - mmu_sync_roots(vcpu); - spin_unlock(&vcpu->kvm->mmu_lock); + kvm_mmu_sync_roots(vcpu); if (r) goto out; /* set_cr3() should ensure TLB has been flushed */ @@ -4179,39 +4292,107 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot) spin_unlock(&kvm->mmu_lock); } -void kvm_mmu_zap_all(struct kvm *kvm) +#define BATCH_ZAP_PAGES 10 +static void kvm_zap_obsolete_pages(struct kvm *kvm) { struct kvm_mmu_page *sp, *node; - LIST_HEAD(invalid_list); + int batch = 0; - spin_lock(&kvm->mmu_lock); restart: - list_for_each_entry_safe(sp, node, &kvm->arch.active_mmu_pages, link) - if (kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list)) + list_for_each_entry_safe_reverse(sp, node, + &kvm->arch.active_mmu_pages, link) { + int ret; + + /* + * No obsolete page exists before new created page since + * active_mmu_pages is the FIFO list. + */ + if (!is_obsolete_sp(kvm, sp)) + break; + + /* + * Since we are reversely walking the list and the invalid + * list will be moved to the head, skip the invalid page + * can help us to avoid the infinity list walking. + */ + if (sp->role.invalid) + continue; + + /* + * Need not flush tlb since we only zap the sp with invalid + * generation number. + */ + if (batch >= BATCH_ZAP_PAGES && + cond_resched_lock(&kvm->mmu_lock)) { + batch = 0; + goto restart; + } + + ret = kvm_mmu_prepare_zap_page(kvm, sp, + &kvm->arch.zapped_obsolete_pages); + batch += ret; + + if (ret) goto restart; + } - kvm_mmu_commit_zap_page(kvm, &invalid_list); - spin_unlock(&kvm->mmu_lock); + /* + * Should flush tlb before free page tables since lockless-walking + * may use the pages. + */ + kvm_mmu_commit_zap_page(kvm, &kvm->arch.zapped_obsolete_pages); } -void kvm_mmu_zap_mmio_sptes(struct kvm *kvm) +/* + * Fast invalidate all shadow pages and use lock-break technique + * to zap obsolete pages. + * + * It's required when memslot is being deleted or VM is being + * destroyed, in these cases, we should ensure that KVM MMU does + * not use any resource of the being-deleted slot or all slots + * after calling the function. + */ +void kvm_mmu_invalidate_zap_all_pages(struct kvm *kvm) { - struct kvm_mmu_page *sp, *node; - LIST_HEAD(invalid_list); - spin_lock(&kvm->mmu_lock); -restart: - list_for_each_entry_safe(sp, node, &kvm->arch.active_mmu_pages, link) { - if (!sp->mmio_cached) - continue; - if (kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list)) - goto restart; - } + trace_kvm_mmu_invalidate_zap_all_pages(kvm); + kvm->arch.mmu_valid_gen++; - kvm_mmu_commit_zap_page(kvm, &invalid_list); + /* + * Notify all vcpus to reload its shadow page table + * and flush TLB. Then all vcpus will switch to new + * shadow page table with the new mmu_valid_gen. + * + * Note: we should do this under the protection of + * mmu-lock, otherwise, vcpu would purge shadow page + * but miss tlb flush. + */ + kvm_reload_remote_mmus(kvm); + + kvm_zap_obsolete_pages(kvm); spin_unlock(&kvm->mmu_lock); } +static bool kvm_has_zapped_obsolete_pages(struct kvm *kvm) +{ + return unlikely(!list_empty_careful(&kvm->arch.zapped_obsolete_pages)); +} + +void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm) +{ + /* + * The very rare case: if the generation-number is round, + * zap all shadow pages. + * + * The max value is MMIO_MAX_GEN - 1 since it is not called + * when mark memslot invalid. + */ + if (unlikely(kvm_current_mmio_generation(kvm) >= (MMIO_MAX_GEN - 1))) { + printk_ratelimited(KERN_INFO "kvm: zapping shadow pages for mmio generation wraparound\n"); + kvm_mmu_invalidate_zap_all_pages(kvm); + } +} + static int mmu_shrink(struct shrinker *shrink, struct shrink_control *sc) { struct kvm *kvm; @@ -4240,15 +4421,23 @@ static int mmu_shrink(struct shrinker *shrink, struct shrink_control *sc) * want to shrink a VM that only started to populate its MMU * anyway. */ - if (!kvm->arch.n_used_mmu_pages) + if (!kvm->arch.n_used_mmu_pages && + !kvm_has_zapped_obsolete_pages(kvm)) continue; idx = srcu_read_lock(&kvm->srcu); spin_lock(&kvm->mmu_lock); + if (kvm_has_zapped_obsolete_pages(kvm)) { + kvm_mmu_commit_zap_page(kvm, + &kvm->arch.zapped_obsolete_pages); + goto unlock; + } + prepare_zap_oldest_mmu_page(kvm, &invalid_list); kvm_mmu_commit_zap_page(kvm, &invalid_list); +unlock: spin_unlock(&kvm->mmu_lock); srcu_read_unlock(&kvm->srcu, idx); diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 2adcbc2cac6d..5b59c573aba7 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -52,6 +52,23 @@ int kvm_mmu_get_spte_hierarchy(struct kvm_vcpu *vcpu, u64 addr, u64 sptes[4]); void kvm_mmu_set_mmio_spte_mask(u64 mmio_mask); + +/* + * Return values of handle_mmio_page_fault_common: + * RET_MMIO_PF_EMULATE: it is a real mmio page fault, emulate the instruction + * directly. + * RET_MMIO_PF_INVALID: invalid spte is detected then let the real page + * fault path update the mmio spte. + * RET_MMIO_PF_RETRY: let CPU fault again on the address. + * RET_MMIO_PF_BUG: bug is detected. + */ +enum { + RET_MMIO_PF_EMULATE = 1, + RET_MMIO_PF_INVALID = 2, + RET_MMIO_PF_RETRY = 0, + RET_MMIO_PF_BUG = -1 +}; + int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct); int kvm_init_shadow_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context); @@ -97,4 +114,5 @@ static inline bool permission_fault(struct kvm_mmu *mmu, unsigned pte_access, return (mmu->permissions[pfec >> 1] >> pte_access) & 1; } +void kvm_mmu_invalidate_zap_all_pages(struct kvm *kvm); #endif diff --git a/arch/x86/kvm/mmutrace.h b/arch/x86/kvm/mmutrace.h index b8f6172f4174..9d2e0ffcb190 100644 --- a/arch/x86/kvm/mmutrace.h +++ b/arch/x86/kvm/mmutrace.h @@ -7,16 +7,18 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM kvmmmu -#define KVM_MMU_PAGE_FIELDS \ - __field(__u64, gfn) \ - __field(__u32, role) \ - __field(__u32, root_count) \ +#define KVM_MMU_PAGE_FIELDS \ + __field(unsigned long, mmu_valid_gen) \ + __field(__u64, gfn) \ + __field(__u32, role) \ + __field(__u32, root_count) \ __field(bool, unsync) -#define KVM_MMU_PAGE_ASSIGN(sp) \ - __entry->gfn = sp->gfn; \ - __entry->role = sp->role.word; \ - __entry->root_count = sp->root_count; \ +#define KVM_MMU_PAGE_ASSIGN(sp) \ + __entry->mmu_valid_gen = sp->mmu_valid_gen; \ + __entry->gfn = sp->gfn; \ + __entry->role = sp->role.word; \ + __entry->root_count = sp->root_count; \ __entry->unsync = sp->unsync; #define KVM_MMU_PAGE_PRINTK() ({ \ @@ -28,8 +30,8 @@ \ role.word = __entry->role; \ \ - trace_seq_printf(p, "sp gfn %llx %u%s q%u%s %s%s" \ - " %snxe root %u %s%c", \ + trace_seq_printf(p, "sp gen %lx gfn %llx %u%s q%u%s %s%s" \ + " %snxe root %u %s%c", __entry->mmu_valid_gen, \ __entry->gfn, role.level, \ role.cr4_pae ? " pae" : "", \ role.quadrant, \ @@ -197,23 +199,25 @@ DEFINE_EVENT(kvm_mmu_page_class, kvm_mmu_prepare_zap_page, TRACE_EVENT( mark_mmio_spte, - TP_PROTO(u64 *sptep, gfn_t gfn, unsigned access), - TP_ARGS(sptep, gfn, access), + TP_PROTO(u64 *sptep, gfn_t gfn, unsigned access, unsigned int gen), + TP_ARGS(sptep, gfn, access, gen), TP_STRUCT__entry( __field(void *, sptep) __field(gfn_t, gfn) __field(unsigned, access) + __field(unsigned int, gen) ), TP_fast_assign( __entry->sptep = sptep; __entry->gfn = gfn; __entry->access = access; + __entry->gen = gen; ), - TP_printk("sptep:%p gfn %llx access %x", __entry->sptep, __entry->gfn, - __entry->access) + TP_printk("sptep:%p gfn %llx access %x gen %x", __entry->sptep, + __entry->gfn, __entry->access, __entry->gen) ); TRACE_EVENT( @@ -274,6 +278,50 @@ TRACE_EVENT( __spte_satisfied(old_spte), __spte_satisfied(new_spte) ) ); + +TRACE_EVENT( + kvm_mmu_invalidate_zap_all_pages, + TP_PROTO(struct kvm *kvm), + TP_ARGS(kvm), + + TP_STRUCT__entry( + __field(unsigned long, mmu_valid_gen) + __field(unsigned int, mmu_used_pages) + ), + + TP_fast_assign( + __entry->mmu_valid_gen = kvm->arch.mmu_valid_gen; + __entry->mmu_used_pages = kvm->arch.n_used_mmu_pages; + ), + + TP_printk("kvm-mmu-valid-gen %lx used_pages %x", + __entry->mmu_valid_gen, __entry->mmu_used_pages + ) +); + + +TRACE_EVENT( + check_mmio_spte, + TP_PROTO(u64 spte, unsigned int kvm_gen, unsigned int spte_gen), + TP_ARGS(spte, kvm_gen, spte_gen), + + TP_STRUCT__entry( + __field(unsigned int, kvm_gen) + __field(unsigned int, spte_gen) + __field(u64, spte) + ), + + TP_fast_assign( + __entry->kvm_gen = kvm_gen; + __entry->spte_gen = spte_gen; + __entry->spte = spte; + ), + + TP_printk("spte %llx kvm_gen %x spte-gen %x valid %d", __entry->spte, + __entry->kvm_gen, __entry->spte_gen, + __entry->kvm_gen == __entry->spte_gen + ) +); #endif /* _TRACE_KVMMMU_H */ #undef TRACE_INCLUDE_PATH diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index da20860b457a..7769699d48a8 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -552,9 +552,12 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, pgprintk("%s: addr %lx err %x\n", __func__, addr, error_code); - if (unlikely(error_code & PFERR_RSVD_MASK)) - return handle_mmio_page_fault(vcpu, addr, error_code, + if (unlikely(error_code & PFERR_RSVD_MASK)) { + r = handle_mmio_page_fault(vcpu, addr, error_code, mmu_is_nested(vcpu)); + if (likely(r != RET_MMIO_PF_INVALID)) + return r; + }; r = mmu_topup_memory_caches(vcpu); if (r) @@ -792,7 +795,8 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) pte_access &= gpte_access(vcpu, gpte); protect_clean_gpte(&pte_access, gpte); - if (sync_mmio_spte(&sp->spt[i], gfn, pte_access, &nr_present)) + if (sync_mmio_spte(vcpu->kvm, &sp->spt[i], gfn, pte_access, + &nr_present)) continue; if (gfn != sp->gfns[i]) { diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index a14a6eaf871d..c0bc80391e40 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -1026,7 +1026,10 @@ static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) g_tsc_offset = svm->vmcb->control.tsc_offset - svm->nested.hsave->control.tsc_offset; svm->nested.hsave->control.tsc_offset = offset; - } + } else + trace_kvm_write_tsc_offset(vcpu->vcpu_id, + svm->vmcb->control.tsc_offset, + offset); svm->vmcb->control.tsc_offset = offset + g_tsc_offset; @@ -1044,6 +1047,11 @@ static void svm_adjust_tsc_offset(struct kvm_vcpu *vcpu, s64 adjustment, bool ho svm->vmcb->control.tsc_offset += adjustment; if (is_guest_mode(vcpu)) svm->nested.hsave->control.tsc_offset += adjustment; + else + trace_kvm_write_tsc_offset(vcpu->vcpu_id, + svm->vmcb->control.tsc_offset - adjustment, + svm->vmcb->control.tsc_offset); + mark_dirty(svm->vmcb, VMCB_INTERCEPTS); } diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index fe5e00ed7036..545245d7cc63 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -756,6 +756,27 @@ TRACE_EVENT( __entry->gpa_match ? "GPA" : "GVA") ); +TRACE_EVENT(kvm_write_tsc_offset, + TP_PROTO(unsigned int vcpu_id, __u64 previous_tsc_offset, + __u64 next_tsc_offset), + TP_ARGS(vcpu_id, previous_tsc_offset, next_tsc_offset), + + TP_STRUCT__entry( + __field( unsigned int, vcpu_id ) + __field( __u64, previous_tsc_offset ) + __field( __u64, next_tsc_offset ) + ), + + TP_fast_assign( + __entry->vcpu_id = vcpu_id; + __entry->previous_tsc_offset = previous_tsc_offset; + __entry->next_tsc_offset = next_tsc_offset; + ), + + TP_printk("vcpu=%u prev=%llu next=%llu", __entry->vcpu_id, + __entry->previous_tsc_offset, __entry->next_tsc_offset) +); + #ifdef CONFIG_X86_64 #define host_clocks \ diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index b30f5a54a2ab..a7e18551c968 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2096,6 +2096,8 @@ static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) (nested_cpu_has(vmcs12, CPU_BASED_USE_TSC_OFFSETING) ? vmcs12->tsc_offset : 0)); } else { + trace_kvm_write_tsc_offset(vcpu->vcpu_id, + vmcs_read64(TSC_OFFSET), offset); vmcs_write64(TSC_OFFSET, offset); } } @@ -2103,11 +2105,14 @@ static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) static void vmx_adjust_tsc_offset(struct kvm_vcpu *vcpu, s64 adjustment, bool host) { u64 offset = vmcs_read64(TSC_OFFSET); + vmcs_write64(TSC_OFFSET, offset + adjustment); if (is_guest_mode(vcpu)) { /* Even when running L2, the adjustment needs to apply to L1 */ to_vmx(vcpu)->nested.vmcs01_tsc_offset += adjustment; - } + } else + trace_kvm_write_tsc_offset(vcpu->vcpu_id, offset, + offset + adjustment); } static u64 vmx_compute_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc) @@ -4176,10 +4181,10 @@ static void ept_set_mmio_spte_mask(void) /* * EPT Misconfigurations can be generated if the value of bits 2:0 * of an EPT paging-structure entry is 110b (write/execute). - * Also, magic bits (0xffull << 49) is set to quickly identify mmio + * Also, magic bits (0x3ull << 62) is set to quickly identify mmio * spte. */ - kvm_mmu_set_mmio_spte_mask(0xffull << 49 | 0x6ull); + kvm_mmu_set_mmio_spte_mask((0x3ull << 62) | 0x6ull); } /* @@ -5366,10 +5371,14 @@ static int handle_ept_misconfig(struct kvm_vcpu *vcpu) gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS); ret = handle_mmio_page_fault_common(vcpu, gpa, true); - if (likely(ret == 1)) + if (likely(ret == RET_MMIO_PF_EMULATE)) return x86_emulate_instruction(vcpu, gpa, 0, NULL, 0) == EMULATE_DONE; - if (unlikely(!ret)) + + if (unlikely(ret == RET_MMIO_PF_INVALID)) + return kvm_mmu_page_fault(vcpu, gpa, 0, NULL, 0); + + if (unlikely(ret == RET_MMIO_PF_RETRY)) return 1; /* It is the real ept misconfig */ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 292e6ca89f42..d21bce505315 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1193,20 +1193,37 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu, struct msr_data *msr) elapsed = ns - kvm->arch.last_tsc_nsec; if (vcpu->arch.virtual_tsc_khz) { + int faulted = 0; + /* n.b - signed multiplication and division required */ usdiff = data - kvm->arch.last_tsc_write; #ifdef CONFIG_X86_64 usdiff = (usdiff * 1000) / vcpu->arch.virtual_tsc_khz; #else /* do_div() only does unsigned */ - asm("idivl %2; xor %%edx, %%edx" - : "=A"(usdiff) - : "A"(usdiff * 1000), "rm"(vcpu->arch.virtual_tsc_khz)); + asm("1: idivl %[divisor]\n" + "2: xor %%edx, %%edx\n" + " movl $0, %[faulted]\n" + "3:\n" + ".section .fixup,\"ax\"\n" + "4: movl $1, %[faulted]\n" + " jmp 3b\n" + ".previous\n" + + _ASM_EXTABLE(1b, 4b) + + : "=A"(usdiff), [faulted] "=r" (faulted) + : "A"(usdiff * 1000), [divisor] "rm"(vcpu->arch.virtual_tsc_khz)); + #endif do_div(elapsed, 1000); usdiff -= elapsed; if (usdiff < 0) usdiff = -usdiff; + + /* idivl overflow => difference is larger than USEC_PER_SEC */ + if (faulted) + usdiff = USEC_PER_SEC; } else usdiff = USEC_PER_SEC; /* disable TSC match window below */ @@ -1587,6 +1604,30 @@ static int kvm_guest_time_update(struct kvm_vcpu *v) return 0; } +/* + * kvmclock updates which are isolated to a given vcpu, such as + * vcpu->cpu migration, should not allow system_timestamp from + * the rest of the vcpus to remain static. Otherwise ntp frequency + * correction applies to one vcpu's system_timestamp but not + * the others. + * + * So in those cases, request a kvmclock update for all vcpus. + * The worst case for a remote vcpu to update its kvmclock + * is then bounded by maximum nohz sleep latency. + */ + +static void kvm_gen_kvmclock_update(struct kvm_vcpu *v) +{ + int i; + struct kvm *kvm = v->kvm; + struct kvm_vcpu *vcpu; + + kvm_for_each_vcpu(i, vcpu, kvm) { + set_bit(KVM_REQ_CLOCK_UPDATE, &vcpu->requests); + kvm_vcpu_kick(vcpu); + } +} + static bool msr_mtrr_valid(unsigned msr) { switch (msr) { @@ -1984,7 +2025,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) kvmclock_reset(vcpu); vcpu->arch.time = data; - kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); + kvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu); /* we verify if the enable bit is set... */ if (!(data & 1)) @@ -2701,7 +2742,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) * kvmclock on vcpu->cpu migration */ if (!vcpu->kvm->arch.use_master_clock || vcpu->cpu == -1) - kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); + kvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu); if (vcpu->cpu != cpu) kvm_migrate_timers(vcpu); vcpu->cpu = cpu; @@ -5238,7 +5279,13 @@ static void kvm_set_mmio_spte_mask(void) * Set the reserved bits and the present bit of an paging-structure * entry to generate page fault with PFER.RSV = 1. */ - mask = ((1ull << (62 - maxphyaddr + 1)) - 1) << maxphyaddr; + /* Mask the reserved physical address bits. */ + mask = ((1ull << (51 - maxphyaddr + 1)) - 1) << maxphyaddr; + + /* Bit 62 is always reserved for 32bit host. */ + mask |= 0x3ull << 62; + + /* Set the present bit. */ mask |= 1ull; #ifdef CONFIG_X86_64 @@ -5498,13 +5545,6 @@ static int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt) char instruction[3]; unsigned long rip = kvm_rip_read(vcpu); - /* - * Blow out the MMU to ensure that no other VCPU has an active mapping - * to ensure that the updated hypercall appears atomically across all - * VCPUs. - */ - kvm_mmu_zap_all(vcpu->kvm); - kvm_x86_ops->patch_hypercall(vcpu, instruction); return emulator_write_emulated(ctxt, rip, instruction, 3, NULL); @@ -5702,6 +5742,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) __kvm_migrate_timers(vcpu); if (kvm_check_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu)) kvm_gen_update_masterclock(vcpu->kvm); + if (kvm_check_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu)) + kvm_gen_kvmclock_update(vcpu); if (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu)) { r = kvm_guest_time_update(vcpu); if (unlikely(r)) @@ -6812,6 +6854,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) return -EINVAL; INIT_LIST_HEAD(&kvm->arch.active_mmu_pages); + INIT_LIST_HEAD(&kvm->arch.zapped_obsolete_pages); INIT_LIST_HEAD(&kvm->arch.assigned_dev_head); /* Reserve bit 0 of irq_sources_bitmap for userspace irq source */ @@ -7040,22 +7083,18 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, * If memory slot is created, or moved, we need to clear all * mmio sptes. */ - if ((change == KVM_MR_CREATE) || (change == KVM_MR_MOVE)) { - kvm_mmu_zap_mmio_sptes(kvm); - kvm_reload_remote_mmus(kvm); - } + kvm_mmu_invalidate_mmio_sptes(kvm); } void kvm_arch_flush_shadow_all(struct kvm *kvm) { - kvm_mmu_zap_all(kvm); - kvm_reload_remote_mmus(kvm); + kvm_mmu_invalidate_zap_all_pages(kvm); } void kvm_arch_flush_shadow_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) { - kvm_arch_flush_shadow_all(kvm); + kvm_mmu_invalidate_zap_all_pages(kvm); } int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) @@ -7263,3 +7302,4 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_nested_intr_vmexit); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_invlpga); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_skinit); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_nested_intercepts); +EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_write_tsc_offset); diff --git a/arch/x86/lguest/Makefile b/arch/x86/lguest/Makefile index 94e0e54056a9..8f38d577a2fa 100644 --- a/arch/x86/lguest/Makefile +++ b/arch/x86/lguest/Makefile @@ -1,2 +1,2 @@ -obj-y := i386_head.o boot.o +obj-y := head_32.o boot.o CFLAGS_boot.o := $(call cc-option, -fno-stack-protector) diff --git a/arch/x86/lguest/i386_head.S b/arch/x86/lguest/head_32.S index 6ddfe4fc23c3..6ddfe4fc23c3 100644 --- a/arch/x86/lguest/i386_head.S +++ b/arch/x86/lguest/head_32.S diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 3e724256dbee..d641897a1f4e 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -324,14 +324,11 @@ setup_resource(struct acpi_resource *acpi_res, void *data) res->start = start; res->end = end; info->res_offset[info->res_num] = addr.translation_offset; + info->res_num++; - if (!pci_use_crs) { + if (!pci_use_crs) dev_printk(KERN_DEBUG, &info->bridge->dev, "host bridge window %pR (ignored)\n", res); - return AE_OK; - } - - info->res_num++; return AE_OK; } diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index d99cae8147d1..c1367b29c3b1 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -40,11 +40,15 @@ cpumask_var_t xen_cpu_initialized_map; -static DEFINE_PER_CPU(int, xen_resched_irq); -static DEFINE_PER_CPU(int, xen_callfunc_irq); -static DEFINE_PER_CPU(int, xen_callfuncsingle_irq); -static DEFINE_PER_CPU(int, xen_irq_work); -static DEFINE_PER_CPU(int, xen_debug_irq) = -1; +struct xen_common_irq { + int irq; + char *name; +}; +static DEFINE_PER_CPU(struct xen_common_irq, xen_resched_irq) = { .irq = -1 }; +static DEFINE_PER_CPU(struct xen_common_irq, xen_callfunc_irq) = { .irq = -1 }; +static DEFINE_PER_CPU(struct xen_common_irq, xen_callfuncsingle_irq) = { .irq = -1 }; +static DEFINE_PER_CPU(struct xen_common_irq, xen_irq_work) = { .irq = -1 }; +static DEFINE_PER_CPU(struct xen_common_irq, xen_debug_irq) = { .irq = -1 }; static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id); static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id); @@ -99,10 +103,47 @@ static void __cpuinit cpu_bringup_and_idle(void) cpu_startup_entry(CPUHP_ONLINE); } +static void xen_smp_intr_free(unsigned int cpu) +{ + if (per_cpu(xen_resched_irq, cpu).irq >= 0) { + unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu).irq, NULL); + per_cpu(xen_resched_irq, cpu).irq = -1; + kfree(per_cpu(xen_resched_irq, cpu).name); + per_cpu(xen_resched_irq, cpu).name = NULL; + } + if (per_cpu(xen_callfunc_irq, cpu).irq >= 0) { + unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu).irq, NULL); + per_cpu(xen_callfunc_irq, cpu).irq = -1; + kfree(per_cpu(xen_callfunc_irq, cpu).name); + per_cpu(xen_callfunc_irq, cpu).name = NULL; + } + if (per_cpu(xen_debug_irq, cpu).irq >= 0) { + unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu).irq, NULL); + per_cpu(xen_debug_irq, cpu).irq = -1; + kfree(per_cpu(xen_debug_irq, cpu).name); + per_cpu(xen_debug_irq, cpu).name = NULL; + } + if (per_cpu(xen_callfuncsingle_irq, cpu).irq >= 0) { + unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu).irq, + NULL); + per_cpu(xen_callfuncsingle_irq, cpu).irq = -1; + kfree(per_cpu(xen_callfuncsingle_irq, cpu).name); + per_cpu(xen_callfuncsingle_irq, cpu).name = NULL; + } + if (xen_hvm_domain()) + return; + + if (per_cpu(xen_irq_work, cpu).irq >= 0) { + unbind_from_irqhandler(per_cpu(xen_irq_work, cpu).irq, NULL); + per_cpu(xen_irq_work, cpu).irq = -1; + kfree(per_cpu(xen_irq_work, cpu).name); + per_cpu(xen_irq_work, cpu).name = NULL; + } +}; static int xen_smp_intr_init(unsigned int cpu) { int rc; - const char *resched_name, *callfunc_name, *debug_name; + char *resched_name, *callfunc_name, *debug_name; resched_name = kasprintf(GFP_KERNEL, "resched%d", cpu); rc = bind_ipi_to_irqhandler(XEN_RESCHEDULE_VECTOR, @@ -113,7 +154,8 @@ static int xen_smp_intr_init(unsigned int cpu) NULL); if (rc < 0) goto fail; - per_cpu(xen_resched_irq, cpu) = rc; + per_cpu(xen_resched_irq, cpu).irq = rc; + per_cpu(xen_resched_irq, cpu).name = resched_name; callfunc_name = kasprintf(GFP_KERNEL, "callfunc%d", cpu); rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_VECTOR, @@ -124,7 +166,8 @@ static int xen_smp_intr_init(unsigned int cpu) NULL); if (rc < 0) goto fail; - per_cpu(xen_callfunc_irq, cpu) = rc; + per_cpu(xen_callfunc_irq, cpu).irq = rc; + per_cpu(xen_callfunc_irq, cpu).name = callfunc_name; debug_name = kasprintf(GFP_KERNEL, "debug%d", cpu); rc = bind_virq_to_irqhandler(VIRQ_DEBUG, cpu, xen_debug_interrupt, @@ -132,7 +175,8 @@ static int xen_smp_intr_init(unsigned int cpu) debug_name, NULL); if (rc < 0) goto fail; - per_cpu(xen_debug_irq, cpu) = rc; + per_cpu(xen_debug_irq, cpu).irq = rc; + per_cpu(xen_debug_irq, cpu).name = debug_name; callfunc_name = kasprintf(GFP_KERNEL, "callfuncsingle%d", cpu); rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_SINGLE_VECTOR, @@ -143,7 +187,8 @@ static int xen_smp_intr_init(unsigned int cpu) NULL); if (rc < 0) goto fail; - per_cpu(xen_callfuncsingle_irq, cpu) = rc; + per_cpu(xen_callfuncsingle_irq, cpu).irq = rc; + per_cpu(xen_callfuncsingle_irq, cpu).name = callfunc_name; /* * The IRQ worker on PVHVM goes through the native path and uses the @@ -161,26 +206,13 @@ static int xen_smp_intr_init(unsigned int cpu) NULL); if (rc < 0) goto fail; - per_cpu(xen_irq_work, cpu) = rc; + per_cpu(xen_irq_work, cpu).irq = rc; + per_cpu(xen_irq_work, cpu).name = callfunc_name; return 0; fail: - if (per_cpu(xen_resched_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu), NULL); - if (per_cpu(xen_callfunc_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu), NULL); - if (per_cpu(xen_debug_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu), NULL); - if (per_cpu(xen_callfuncsingle_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu), - NULL); - if (xen_hvm_domain()) - return rc; - - if (per_cpu(xen_irq_work, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_irq_work, cpu), NULL); - + xen_smp_intr_free(cpu); return rc; } @@ -433,12 +465,7 @@ static void xen_cpu_die(unsigned int cpu) current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/10); } - unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu), NULL); - unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu), NULL); - unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu), NULL); - unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu), NULL); - if (!xen_hvm_domain()) - unbind_from_irqhandler(per_cpu(xen_irq_work, cpu), NULL); + xen_smp_intr_free(cpu); xen_uninit_lock_cpu(cpu); xen_teardown_timer(cpu); } diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index 3002ec1bb71a..a40f8508e760 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c @@ -7,6 +7,7 @@ #include <linux/debugfs.h> #include <linux/log2.h> #include <linux/gfp.h> +#include <linux/slab.h> #include <asm/paravirt.h> @@ -165,6 +166,7 @@ static int xen_spin_trylock(struct arch_spinlock *lock) return old == 0; } +static DEFINE_PER_CPU(char *, irq_name); static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); @@ -362,7 +364,7 @@ static irqreturn_t dummy_handler(int irq, void *dev_id) void __cpuinit xen_init_lock_cpu(int cpu) { int irq; - const char *name; + char *name; WARN(per_cpu(lock_kicker_irq, cpu) >= 0, "spinlock on CPU%d exists on IRQ%d!\n", cpu, per_cpu(lock_kicker_irq, cpu)); @@ -385,6 +387,7 @@ void __cpuinit xen_init_lock_cpu(int cpu) if (irq >= 0) { disable_irq(irq); /* make sure it's never delivered */ per_cpu(lock_kicker_irq, cpu) = irq; + per_cpu(irq_name, cpu) = name; } printk("cpu %d spinlock event irq %d\n", cpu, irq); @@ -401,6 +404,8 @@ void xen_uninit_lock_cpu(int cpu) unbind_from_irqhandler(per_cpu(lock_kicker_irq, cpu), NULL); per_cpu(lock_kicker_irq, cpu) = -1; + kfree(per_cpu(irq_name, cpu)); + per_cpu(irq_name, cpu) = NULL; } void __init xen_init_spinlocks(void) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 3d88bfdf9e1c..a690868be837 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -14,6 +14,7 @@ #include <linux/kernel_stat.h> #include <linux/math64.h> #include <linux/gfp.h> +#include <linux/slab.h> #include <asm/pvclock.h> #include <asm/xen/hypervisor.h> @@ -36,9 +37,8 @@ static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate); /* snapshots of runstate info */ static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate_snapshot); -/* unused ns of stolen and blocked time */ +/* unused ns of stolen time */ static DEFINE_PER_CPU(u64, xen_residual_stolen); -static DEFINE_PER_CPU(u64, xen_residual_blocked); /* return an consistent snapshot of 64-bit time/counter value */ static u64 get64(const u64 *p) @@ -115,7 +115,7 @@ static void do_stolen_accounting(void) { struct vcpu_runstate_info state; struct vcpu_runstate_info *snap; - s64 blocked, runnable, offline, stolen; + s64 runnable, offline, stolen; cputime_t ticks; get_runstate_snapshot(&state); @@ -125,7 +125,6 @@ static void do_stolen_accounting(void) snap = &__get_cpu_var(xen_runstate_snapshot); /* work out how much time the VCPU has not been runn*ing* */ - blocked = state.time[RUNSTATE_blocked] - snap->time[RUNSTATE_blocked]; runnable = state.time[RUNSTATE_runnable] - snap->time[RUNSTATE_runnable]; offline = state.time[RUNSTATE_offline] - snap->time[RUNSTATE_offline]; @@ -141,17 +140,6 @@ static void do_stolen_accounting(void) ticks = iter_div_u64_rem(stolen, NS_PER_TICK, &stolen); __this_cpu_write(xen_residual_stolen, stolen); account_steal_ticks(ticks); - - /* Add the appropriate number of ticks of blocked time, - including any left-overs from last time. */ - blocked += __this_cpu_read(xen_residual_blocked); - - if (blocked < 0) - blocked = 0; - - ticks = iter_div_u64_rem(blocked, NS_PER_TICK, &blocked); - __this_cpu_write(xen_residual_blocked, blocked); - account_idle_ticks(ticks); } /* Get the TSC speed from Xen */ @@ -377,11 +365,16 @@ static const struct clock_event_device xen_vcpuop_clockevent = { static const struct clock_event_device *xen_clockevent = &xen_timerop_clockevent; -static DEFINE_PER_CPU(struct clock_event_device, xen_clock_events) = { .irq = -1 }; + +struct xen_clock_event_device { + struct clock_event_device evt; + char *name; +}; +static DEFINE_PER_CPU(struct xen_clock_event_device, xen_clock_events) = { .evt.irq = -1 }; static irqreturn_t xen_timer_interrupt(int irq, void *dev_id) { - struct clock_event_device *evt = &__get_cpu_var(xen_clock_events); + struct clock_event_device *evt = &__get_cpu_var(xen_clock_events).evt; irqreturn_t ret; ret = IRQ_NONE; @@ -395,14 +388,30 @@ static irqreturn_t xen_timer_interrupt(int irq, void *dev_id) return ret; } +void xen_teardown_timer(int cpu) +{ + struct clock_event_device *evt; + BUG_ON(cpu == 0); + evt = &per_cpu(xen_clock_events, cpu).evt; + + if (evt->irq >= 0) { + unbind_from_irqhandler(evt->irq, NULL); + evt->irq = -1; + kfree(per_cpu(xen_clock_events, cpu).name); + per_cpu(xen_clock_events, cpu).name = NULL; + } +} + void xen_setup_timer(int cpu) { - const char *name; + char *name; struct clock_event_device *evt; int irq; - evt = &per_cpu(xen_clock_events, cpu); + evt = &per_cpu(xen_clock_events, cpu).evt; WARN(evt->irq >= 0, "IRQ%d for CPU%d is already allocated\n", evt->irq, cpu); + if (evt->irq >= 0) + xen_teardown_timer(cpu); printk(KERN_INFO "installing Xen timer for CPU %d\n", cpu); @@ -420,22 +429,15 @@ void xen_setup_timer(int cpu) evt->cpumask = cpumask_of(cpu); evt->irq = irq; + per_cpu(xen_clock_events, cpu).name = name; } -void xen_teardown_timer(int cpu) -{ - struct clock_event_device *evt; - BUG_ON(cpu == 0); - evt = &per_cpu(xen_clock_events, cpu); - unbind_from_irqhandler(evt->irq, NULL); - evt->irq = -1; -} void xen_setup_cpu_clockevents(void) { BUG_ON(preemptible()); - clockevents_register_device(&__get_cpu_var(xen_clock_events)); + clockevents_register_device(&__get_cpu_var(xen_clock_events).evt); } void xen_timer_resume(void) diff --git a/block/blk-core.c b/block/blk-core.c index 0852e5d43436..93a18d1d3da8 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2315,6 +2315,9 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) case -EBADE: error_type = "critical nexus"; break; + case -ETIMEDOUT: + error_type = "timeout"; + break; case -EIO: default: error_type = "I/O"; diff --git a/block/blk-timeout.c b/block/blk-timeout.c index 6e4744cbfb56..65f103563969 100644 --- a/block/blk-timeout.c +++ b/block/blk-timeout.c @@ -82,9 +82,10 @@ void blk_delete_timer(struct request *req) static void blk_rq_timed_out(struct request *req) { struct request_queue *q = req->q; - enum blk_eh_timer_return ret; + enum blk_eh_timer_return ret = BLK_EH_RESET_TIMER; - ret = q->rq_timed_out_fn(req); + if (q->rq_timed_out_fn) + ret = q->rq_timed_out_fn(req); switch (ret) { case BLK_EH_HANDLED: __blk_complete_request(req); diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 536562c626a2..81dbeb83bb45 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -34,6 +34,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o acpi-y += bus.o glue.o acpi-y += scan.o acpi-y += resource.o +acpi-y += acpi_processor.o acpi-y += processor_core.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o @@ -43,6 +44,7 @@ acpi-y += acpi_platform.o acpi-y += power.o acpi-y += event.o acpi-y += sysfs.o +acpi-$(CONFIG_X86) += acpi_cmos_rtc.o acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-$(CONFIG_ACPI_NUMA) += numa.o acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o diff --git a/drivers/acpi/acpi_cmos_rtc.c b/drivers/acpi/acpi_cmos_rtc.c new file mode 100644 index 000000000000..84190ed89c04 --- /dev/null +++ b/drivers/acpi/acpi_cmos_rtc.c @@ -0,0 +1,92 @@ +/* + * ACPI support for CMOS RTC Address Space access + * + * Copyright (C) 2013, Intel Corporation + * Authors: Lan Tianyu <tianyu.lan@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/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm-generic/rtc.h> + +#include "internal.h" + +#define PREFIX "ACPI: " + +ACPI_MODULE_NAME("cmos rtc"); + +static const struct acpi_device_id acpi_cmos_rtc_ids[] = { + { "PNP0B00" }, + { "PNP0B01" }, + { "PNP0B02" }, + {} +}; + +static acpi_status +acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + int i; + u8 *value = (u8 *)&value64; + + if (address > 0xff || !value64) + return AE_BAD_PARAMETER; + + if (function != ACPI_WRITE && function != ACPI_READ) + return AE_BAD_PARAMETER; + + spin_lock_irq(&rtc_lock); + + for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value) + if (function == ACPI_READ) + *value = CMOS_READ(address); + else + CMOS_WRITE(*value, address); + + spin_unlock_irq(&rtc_lock); + + return AE_OK; +} + +static int acpi_install_cmos_rtc_space_handler(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + acpi_status status; + + status = acpi_install_address_space_handler(adev->handle, + ACPI_ADR_SPACE_CMOS, + &acpi_cmos_rtc_space_handler, + NULL, NULL); + if (ACPI_FAILURE(status)) { + pr_err(PREFIX "Error installing CMOS-RTC region handler\n"); + return -ENODEV; + } + + return 0; +} + +static void acpi_remove_cmos_rtc_space_handler(struct acpi_device *adev) +{ + if (ACPI_FAILURE(acpi_remove_address_space_handler(adev->handle, + ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler))) + pr_err(PREFIX "Error removing CMOS-RTC region handler\n"); +} + +static struct acpi_scan_handler cmos_rtc_handler = { + .ids = acpi_cmos_rtc_ids, + .attach = acpi_install_cmos_rtc_space_handler, + .detach = acpi_remove_cmos_rtc_space_handler, +}; + +void __init acpi_cmos_rtc_init(void) +{ + acpi_scan_add_handler(&cmos_rtc_handler); +} diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index cab13f2fc28e..6a382188fa20 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -32,12 +32,26 @@ ACPI_MODULE_NAME("acpi_lpss"); #define LPSS_GENERAL_LTR_MODE_SW BIT(2) #define LPSS_SW_LTR 0x10 #define LPSS_AUTO_LTR 0x14 +#define LPSS_TX_INT 0x20 +#define LPSS_TX_INT_MASK BIT(1) + +struct lpss_shared_clock { + const char *name; + unsigned long rate; + struct clk *clk; +}; + +struct lpss_private_data; struct lpss_device_desc { bool clk_required; const char *clkdev_name; bool ltr_required; unsigned int prv_offset; + size_t prv_size_override; + bool clk_gate; + struct lpss_shared_clock *shared_clock; + void (*setup)(struct lpss_private_data *pdata); }; static struct lpss_device_desc lpss_dma_desc = { @@ -52,17 +66,76 @@ struct lpss_private_data { const struct lpss_device_desc *dev_desc; }; +static void lpss_uart_setup(struct lpss_private_data *pdata) +{ + unsigned int tx_int_offset = pdata->dev_desc->prv_offset + LPSS_TX_INT; + u32 reg; + + reg = readl(pdata->mmio_base + tx_int_offset); + writel(reg | LPSS_TX_INT_MASK, pdata->mmio_base + tx_int_offset); +} + static struct lpss_device_desc lpt_dev_desc = { .clk_required = true, .prv_offset = 0x800, .ltr_required = true, + .clk_gate = true, +}; + +static struct lpss_device_desc lpt_uart_dev_desc = { + .clk_required = true, + .prv_offset = 0x800, + .ltr_required = true, + .clk_gate = true, + .setup = lpss_uart_setup, }; static struct lpss_device_desc lpt_sdio_dev_desc = { .prv_offset = 0x1000, + .prv_size_override = 0x1018, .ltr_required = true, }; +static struct lpss_shared_clock uart_clock = { + .name = "uart_clk", + .rate = 44236800, +}; + +static struct lpss_device_desc byt_uart_dev_desc = { + .clk_required = true, + .prv_offset = 0x800, + .clk_gate = true, + .shared_clock = &uart_clock, + .setup = lpss_uart_setup, +}; + +static struct lpss_shared_clock spi_clock = { + .name = "spi_clk", + .rate = 50000000, +}; + +static struct lpss_device_desc byt_spi_dev_desc = { + .clk_required = true, + .prv_offset = 0x400, + .clk_gate = true, + .shared_clock = &spi_clock, +}; + +static struct lpss_device_desc byt_sdio_dev_desc = { + .clk_required = true, +}; + +static struct lpss_shared_clock i2c_clock = { + .name = "i2c_clk", + .rate = 100000000, +}; + +static struct lpss_device_desc byt_i2c_dev_desc = { + .clk_required = true, + .prv_offset = 0x800, + .shared_clock = &i2c_clock, +}; + static const struct acpi_device_id acpi_lpss_device_ids[] = { /* Generic LPSS devices */ { "INTL9C60", (unsigned long)&lpss_dma_desc }, @@ -72,11 +145,18 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { { "INT33C1", (unsigned long)&lpt_dev_desc }, { "INT33C2", (unsigned long)&lpt_dev_desc }, { "INT33C3", (unsigned long)&lpt_dev_desc }, - { "INT33C4", (unsigned long)&lpt_dev_desc }, - { "INT33C5", (unsigned long)&lpt_dev_desc }, + { "INT33C4", (unsigned long)&lpt_uart_dev_desc }, + { "INT33C5", (unsigned long)&lpt_uart_dev_desc }, { "INT33C6", (unsigned long)&lpt_sdio_dev_desc }, { "INT33C7", }, + /* BayTrail LPSS devices */ + { "80860F0A", (unsigned long)&byt_uart_dev_desc }, + { "80860F0E", (unsigned long)&byt_spi_dev_desc }, + { "80860F14", (unsigned long)&byt_sdio_dev_desc }, + { "80860F41", (unsigned long)&byt_i2c_dev_desc }, + { "INT33B2", }, + { } }; @@ -98,7 +178,10 @@ static int register_device_clock(struct acpi_device *adev, struct lpss_private_data *pdata) { const struct lpss_device_desc *dev_desc = pdata->dev_desc; + struct lpss_shared_clock *shared_clock = dev_desc->shared_clock; + struct clk *clk = ERR_PTR(-ENODEV); struct lpss_clk_data *clk_data; + const char *parent; if (!lpss_clk_dev) lpt_register_clock_device(); @@ -117,14 +200,30 @@ static int register_device_clock(struct acpi_device *adev, || pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE) return -ENODATA; - pdata->clk = clk_register_gate(NULL, dev_name(&adev->dev), - clk_data->name, 0, - pdata->mmio_base + dev_desc->prv_offset, - 0, 0, NULL); - if (IS_ERR(pdata->clk)) - return PTR_ERR(pdata->clk); + parent = clk_data->name; + + if (shared_clock) { + clk = shared_clock->clk; + if (!clk) { + clk = clk_register_fixed_rate(NULL, shared_clock->name, + "lpss_clk", 0, + shared_clock->rate); + shared_clock->clk = clk; + } + parent = shared_clock->name; + } + + if (dev_desc->clk_gate) { + clk = clk_register_gate(NULL, dev_name(&adev->dev), parent, 0, + pdata->mmio_base + dev_desc->prv_offset, + 0, 0, NULL); + pdata->clk = clk; + } - clk_register_clkdev(pdata->clk, NULL, dev_name(&adev->dev)); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + clk_register_clkdev(clk, NULL, dev_name(&adev->dev)); return 0; } @@ -152,7 +251,10 @@ static int acpi_lpss_create_device(struct acpi_device *adev, list_for_each_entry(rentry, &resource_list, node) if (resource_type(&rentry->res) == IORESOURCE_MEM) { - pdata->mmio_size = resource_size(&rentry->res); + if (dev_desc->prv_size_override) + pdata->mmio_size = dev_desc->prv_size_override; + else + pdata->mmio_size = resource_size(&rentry->res); pdata->mmio_base = ioremap(rentry->res.start, pdata->mmio_size); pdata->dev_desc = dev_desc; @@ -182,6 +284,9 @@ static int acpi_lpss_create_device(struct acpi_device *adev, goto err_out; } + if (dev_desc->setup) + dev_desc->setup(pdata); + adev->driver_data = pdata; ret = acpi_create_platform_device(adev, id); if (ret > 0) diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 5e6301e94920..c711d1144044 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -28,6 +28,7 @@ */ #include <linux/acpi.h> +#include <linux/memory.h> #include <linux/memory_hotplug.h> #include "internal.h" @@ -166,13 +167,50 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device) return 0; } +static unsigned long acpi_meminfo_start_pfn(struct acpi_memory_info *info) +{ + return PFN_DOWN(info->start_addr); +} + +static unsigned long acpi_meminfo_end_pfn(struct acpi_memory_info *info) +{ + return PFN_UP(info->start_addr + info->length-1); +} + +static int acpi_bind_memblk(struct memory_block *mem, void *arg) +{ + return acpi_bind_one(&mem->dev, (acpi_handle)arg); +} + +static int acpi_bind_memory_blocks(struct acpi_memory_info *info, + acpi_handle handle) +{ + return walk_memory_range(acpi_meminfo_start_pfn(info), + acpi_meminfo_end_pfn(info), (void *)handle, + acpi_bind_memblk); +} + +static int acpi_unbind_memblk(struct memory_block *mem, void *arg) +{ + acpi_unbind_one(&mem->dev); + return 0; +} + +static void acpi_unbind_memory_blocks(struct acpi_memory_info *info, + acpi_handle handle) +{ + walk_memory_range(acpi_meminfo_start_pfn(info), + acpi_meminfo_end_pfn(info), NULL, acpi_unbind_memblk); +} + static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) { + acpi_handle handle = mem_device->device->handle; int result, num_enabled = 0; struct acpi_memory_info *info; int node; - node = acpi_get_node(mem_device->device->handle); + node = acpi_get_node(handle); /* * Tell the VM there is more memory here... * Note: Assume that this function returns zero on success @@ -203,6 +241,12 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) if (result && result != -EEXIST) continue; + result = acpi_bind_memory_blocks(info, handle); + if (result) { + acpi_unbind_memory_blocks(info, handle); + return -ENODEV; + } + info->enabled = 1; /* @@ -227,12 +271,11 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) return 0; } -static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) +static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device) { - int result = 0, nid; + acpi_handle handle = mem_device->device->handle; struct acpi_memory_info *info, *n; - - nid = acpi_get_node(mem_device->device->handle); + int nid = acpi_get_node(handle); list_for_each_entry_safe(info, n, &mem_device->res_list, list) { if (!info->enabled) @@ -240,15 +283,12 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) if (nid < 0) nid = memory_add_physaddr_to_nid(info->start_addr); - result = remove_memory(nid, info->start_addr, info->length); - if (result) - return result; + acpi_unbind_memory_blocks(info, handle); + remove_memory(nid, info->start_addr, info->length); list_del(&info->list); kfree(info); } - - return result; } static void acpi_memory_device_free(struct acpi_memory_device *mem_device) @@ -300,7 +340,7 @@ static int acpi_memory_device_add(struct acpi_device *device, if (result) { dev_err(&device->dev, "acpi_memory_enable_device() error\n"); acpi_memory_device_free(mem_device); - return -ENODEV; + return result; } dev_dbg(&device->dev, "Memory device configured by ACPI\n"); diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c new file mode 100644 index 000000000000..e9b01e35ac37 --- /dev/null +++ b/drivers/acpi/acpi_processor.c @@ -0,0 +1,494 @@ +/* + * acpi_processor.c - ACPI processor enumeration support + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> + * Copyright (C) 2013, Intel Corporation + * 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/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> + +#include <acpi/processor.h> + +#include <asm/cpu.h> + +#include "internal.h" + +#define _COMPONENT ACPI_PROCESSOR_COMPONENT + +ACPI_MODULE_NAME("processor"); + +DEFINE_PER_CPU(struct acpi_processor *, processors); +EXPORT_PER_CPU_SYMBOL(processors); + +/* -------------------------------------------------------------------------- + Errata Handling + -------------------------------------------------------------------------- */ + +struct acpi_processor_errata errata __read_mostly; +EXPORT_SYMBOL_GPL(errata); + +static int acpi_processor_errata_piix4(struct pci_dev *dev) +{ + u8 value1 = 0; + u8 value2 = 0; + + + if (!dev) + return -EINVAL; + + /* + * Note that 'dev' references the PIIX4 ACPI Controller. + */ + + switch (dev->revision) { + case 0: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n")); + break; + case 1: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n")); + break; + case 2: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n")); + break; + case 3: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n")); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n")); + break; + } + + switch (dev->revision) { + + case 0: /* PIIX4 A-step */ + case 1: /* PIIX4 B-step */ + /* + * See specification changes #13 ("Manual Throttle Duty Cycle") + * and #14 ("Enabling and Disabling Manual Throttle"), plus + * erratum #5 ("STPCLK# Deassertion Time") from the January + * 2002 PIIX4 specification update. Applies to only older + * PIIX4 models. + */ + errata.piix4.throttle = 1; + + case 2: /* PIIX4E */ + case 3: /* PIIX4M */ + /* + * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA + * Livelock") from the January 2002 PIIX4 specification update. + * Applies to all PIIX4 models. + */ + + /* + * BM-IDE + * ------ + * Find the PIIX4 IDE Controller and get the Bus Master IDE + * Status register address. We'll use this later to read + * each IDE controller's DMA status to make sure we catch all + * DMA activity. + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB, + PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + errata.piix4.bmisx = pci_resource_start(dev, 4); + pci_dev_put(dev); + } + + /* + * Type-F DMA + * ---------- + * Find the PIIX4 ISA Controller and read the Motherboard + * DMA controller's status to see if Type-F (Fast) DMA mode + * is enabled (bit 7) on either channel. Note that we'll + * disable C3 support if this is enabled, as some legacy + * devices won't operate well if fast DMA is disabled. + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, + PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + pci_read_config_byte(dev, 0x76, &value1); + pci_read_config_byte(dev, 0x77, &value2); + if ((value1 & 0x80) || (value2 & 0x80)) + errata.piix4.fdma = 1; + pci_dev_put(dev); + } + + break; + } + + if (errata.piix4.bmisx) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Bus master activity detection (BM-IDE) erratum enabled\n")); + if (errata.piix4.fdma) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Type-F DMA livelock erratum (C3 disabled)\n")); + + return 0; +} + +static int acpi_processor_errata(struct acpi_processor *pr) +{ + int result = 0; + struct pci_dev *dev = NULL; + + + if (!pr) + return -EINVAL; + + /* + * PIIX4 + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, + PCI_ANY_ID, NULL); + if (dev) { + result = acpi_processor_errata_piix4(dev); + pci_dev_put(dev); + } + + return result; +} + +/* -------------------------------------------------------------------------- + Initialization + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_HOTPLUG_CPU +static int acpi_processor_hotadd_init(struct acpi_processor *pr) +{ + unsigned long long sta; + acpi_status status; + int ret; + + status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) + return -ENODEV; + + ret = acpi_map_lsapic(pr->handle, &pr->id); + if (ret) + return ret; + + ret = arch_register_cpu(pr->id); + if (ret) { + acpi_unmap_lsapic(pr->id); + return ret; + } + + /* + * CPU got hot-added, but cpu_data is not initialized yet. Set a flag + * to delay cpu_idle/throttling initialization and do it when the CPU + * gets online for the first time. + */ + pr_info("CPU%d has been hot-added\n", pr->id); + pr->flags.need_hotplug_init = 1; + return 0; +} +#else +static inline int acpi_processor_hotadd_init(struct acpi_processor *pr) +{ + return -ENODEV; +} +#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +static int acpi_processor_get_info(struct acpi_device *device) +{ + union acpi_object object = { 0 }; + struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; + struct acpi_processor *pr = acpi_driver_data(device); + int cpu_index, device_declaration = 0; + acpi_status status = AE_OK; + static int cpu0_initialized; + + if (num_online_cpus() > 1) + errata.smp = TRUE; + + acpi_processor_errata(pr); + + /* + * Check to see if we have bus mastering arbitration control. This + * is required for proper C3 usage (to maintain cache coherency). + */ + if (acpi_gbl_FADT.pm2_control_block && acpi_gbl_FADT.pm2_control_length) { + pr->flags.bm_control = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Bus mastering arbitration control present\n")); + } else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No bus mastering arbitration control\n")); + + if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) { + /* Declared with "Processor" statement; match ProcessorID */ + status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, + "Failed to evaluate processor object (0x%x)\n", + status); + return -ENODEV; + } + + /* + * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. + * >>> 'acpi_get_processor_id(acpi_id, &id)' in + * arch/xxx/acpi.c + */ + pr->acpi_id = object.processor.proc_id; + } else { + /* + * Declared with "Device" statement; match _UID. + * Note that we don't handle string _UIDs yet. + */ + unsigned long long value; + status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID, + NULL, &value); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, + "Failed to evaluate processor _UID (0x%x)\n", + status); + return -ENODEV; + } + device_declaration = 1; + pr->acpi_id = value; + } + cpu_index = acpi_get_cpuid(pr->handle, device_declaration, pr->acpi_id); + + /* Handle UP system running SMP kernel, with no LAPIC in MADT */ + if (!cpu0_initialized && (cpu_index == -1) && + (num_online_cpus() == 1)) { + cpu_index = 0; + } + + cpu0_initialized = 1; + + pr->id = cpu_index; + + /* + * Extra Processor objects may be enumerated on MP systems with + * less than the max # of CPUs. They should be ignored _iff + * they are physically not present. + */ + if (pr->id == -1) { + int ret = acpi_processor_hotadd_init(pr); + if (ret) + return ret; + } + /* + * On some boxes several processors use the same processor bus id. + * But they are located in different scope. For example: + * \_SB.SCK0.CPU0 + * \_SB.SCK1.CPU0 + * Rename the processor device bus id. And the new bus id will be + * generated as the following format: + * CPU+CPU ID. + */ + sprintf(acpi_device_bid(device), "CPU%X", pr->id); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id, + pr->acpi_id)); + + if (!object.processor.pblk_address) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n")); + else if (object.processor.pblk_length != 6) + dev_err(&device->dev, "Invalid PBLK length [%d]\n", + object.processor.pblk_length); + else { + pr->throttling.address = object.processor.pblk_address; + pr->throttling.duty_offset = acpi_gbl_FADT.duty_offset; + pr->throttling.duty_width = acpi_gbl_FADT.duty_width; + + pr->pblk = object.processor.pblk_address; + + /* + * We don't care about error returns - we just try to mark + * these reserved so that nobody else is confused into thinking + * that this region might be unused.. + * + * (In particular, allocating the IO range for Cardbus) + */ + request_region(pr->throttling.address, 6, "ACPI CPU throttle"); + } + + /* + * If ACPI describes a slot number for this CPU, we can use it to + * ensure we get the right value in the "physical id" field + * of /proc/cpuinfo + */ + status = acpi_evaluate_object(pr->handle, "_SUN", NULL, &buffer); + if (ACPI_SUCCESS(status)) + arch_fix_phys_package_id(pr->id, object.integer.value); + + return 0; +} + +/* + * Do not put anything in here which needs the core to be online. + * For example MSR access or setting up things which check for cpuinfo_x86 + * (cpu_data(cpu)) values, like CPU feature flags, family, model, etc. + * Such things have to be put in and set up by the processor driver's .probe(). + */ +static DEFINE_PER_CPU(void *, processor_device_array); + +static int __cpuinit acpi_processor_add(struct acpi_device *device, + const struct acpi_device_id *id) +{ + struct acpi_processor *pr; + struct device *dev; + int result = 0; + + pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); + if (!pr) + return -ENOMEM; + + if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) { + result = -ENOMEM; + goto err_free_pr; + } + + pr->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); + device->driver_data = pr; + + result = acpi_processor_get_info(device); + if (result) /* Processor is not physically present or unavailable */ + return 0; + +#ifdef CONFIG_SMP + if (pr->id >= setup_max_cpus && pr->id != 0) + return 0; +#endif + + BUG_ON(pr->id >= nr_cpu_ids); + + /* + * Buggy BIOS check. + * ACPI id of processors can be reported wrongly by the BIOS. + * Don't trust it blindly + */ + if (per_cpu(processor_device_array, pr->id) != NULL && + per_cpu(processor_device_array, pr->id) != device) { + dev_warn(&device->dev, + "BIOS reported wrong ACPI id %d for the processor\n", + pr->id); + /* Give up, but do not abort the namespace scan. */ + goto err; + } + /* + * processor_device_array is not cleared on errors to allow buggy BIOS + * checks. + */ + per_cpu(processor_device_array, pr->id) = device; + per_cpu(processors, pr->id) = pr; + + dev = get_cpu_device(pr->id); + if (!dev) { + result = -ENODEV; + goto err; + } + + result = acpi_bind_one(dev, pr->handle); + if (result) + goto err; + + pr->dev = dev; + dev->offline = pr->flags.need_hotplug_init; + + /* Trigger the processor driver's .probe() if present. */ + if (device_attach(dev) >= 0) + return 1; + + dev_err(dev, "Processor driver could not be attached\n"); + acpi_unbind_one(dev); + + err: + free_cpumask_var(pr->throttling.shared_cpu_map); + device->driver_data = NULL; + per_cpu(processors, pr->id) = NULL; + err_free_pr: + kfree(pr); + return result; +} + +#ifdef CONFIG_ACPI_HOTPLUG_CPU +/* -------------------------------------------------------------------------- + Removal + -------------------------------------------------------------------------- */ + +static void acpi_processor_remove(struct acpi_device *device) +{ + struct acpi_processor *pr; + + if (!device || !acpi_driver_data(device)) + return; + + pr = acpi_driver_data(device); + if (pr->id >= nr_cpu_ids) + goto out; + + /* + * The only reason why we ever get here is CPU hot-removal. The CPU is + * already offline and the ACPI device removal locking prevents it from + * being put back online at this point. + * + * Unbind the driver from the processor device and detach it from the + * ACPI companion object. + */ + device_release_driver(pr->dev); + acpi_unbind_one(pr->dev); + + /* Clean up. */ + per_cpu(processor_device_array, pr->id) = NULL; + per_cpu(processors, pr->id) = NULL; + try_offline_node(cpu_to_node(pr->id)); + + /* Remove the CPU. */ + get_online_cpus(); + arch_unregister_cpu(pr->id); + acpi_unmap_lsapic(pr->id); + put_online_cpus(); + + out: + free_cpumask_var(pr->throttling.shared_cpu_map); + kfree(pr); +} +#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +/* + * The following ACPI IDs are known to be suitable for representing as + * processor devices. + */ +static const struct acpi_device_id processor_device_ids[] = { + + { ACPI_PROCESSOR_OBJECT_HID, }, + { ACPI_PROCESSOR_DEVICE_HID, }, + + { } +}; + +static struct acpi_scan_handler __refdata processor_handler = { + .ids = processor_device_ids, + .attach = acpi_processor_add, +#ifdef CONFIG_ACPI_HOTPLUG_CPU + .detach = acpi_processor_remove, +#endif + .hotplug = { + .enabled = true, + }, +}; + +void __init acpi_processor_init(void) +{ + acpi_scan_add_handler_with_hotplug(&processor_handler, "processor"); +} diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 7ddf29eca9f5..438304086ff1 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -83,6 +83,7 @@ acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o acpi-y += \ nsaccess.o \ nsalloc.o \ + nsarguments.o \ nsconvert.o \ nsdump.o \ nseval.o \ @@ -137,6 +138,7 @@ acpi-y += \ tbfadt.o \ tbfind.o \ tbinstal.o \ + tbprint.o \ tbutils.o \ tbxface.o \ tbxfload.o \ @@ -145,11 +147,13 @@ acpi-y += \ acpi-y += \ utaddress.o \ utalloc.o \ + utbuffer.o \ utcopy.o \ utexcep.o \ utdebug.o \ utdecode.o \ utdelete.o \ + uterror.o \ uteval.o \ utglobal.o \ utids.o \ diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 07160928ca25..b8d38117a20c 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -132,6 +132,12 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_truncate_io_addresses, FALSE); */ u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_auto_repair, FALSE); +/* + * Optionally do not load any SSDTs from the RSDT/XSDT during initialization. + * This can be useful for debugging ACPI problems on some machines. + */ +u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_ssdt_table_load, FALSE); + /* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */ struct acpi_table_fadt acpi_gbl_FADT; diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index d5bfbd331bfd..dfed26545ba2 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -362,23 +362,6 @@ union acpi_predefined_info { #pragma pack() -/* Data block used during object validation */ - -struct acpi_predefined_data { - char *pathname; - const union acpi_predefined_info *predefined; - union acpi_operand_object *parent_package; - struct acpi_namespace_node *node; - u32 flags; - u32 return_btype; - u8 node_flags; -}; - -/* Defines for Flags field above */ - -#define ACPI_OBJECT_REPAIRED 1 -#define ACPI_OBJECT_WRAPPED 2 - /* Return object auto-repair info */ typedef acpi_status(*acpi_object_converter) (union acpi_operand_object diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 53666bd9193d..530a2f8c1252 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -374,10 +374,11 @@ * the plist contains a set of parens to allow variable-length lists. * These macros are used for both the debug and non-debug versions of the code. */ -#define ACPI_ERROR_NAMESPACE(s, e) acpi_ut_namespace_error (AE_INFO, s, e); -#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ut_method_error (AE_INFO, s, n, p, e); -#define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist -#define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist +#define ACPI_ERROR_NAMESPACE(s, e) acpi_ut_namespace_error (AE_INFO, s, e); +#define ACPI_ERROR_METHOD(s, n, p, e) acpi_ut_method_error (AE_INFO, s, n, p, e); +#define ACPI_WARN_PREDEFINED(plist) acpi_ut_predefined_warning plist +#define ACPI_INFO_PREDEFINED(plist) acpi_ut_predefined_info plist +#define ACPI_BIOS_ERROR_PREDEFINED(plist) acpi_ut_predefined_bios_error plist #else @@ -387,6 +388,7 @@ #define ACPI_ERROR_METHOD(s, n, p, e) #define ACPI_WARN_PREDEFINED(plist) #define ACPI_INFO_PREDEFINED(plist) +#define ACPI_BIOS_ERROR_PREDEFINED(plist) #endif /* ACPI_NO_ERROR_MESSAGES */ diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index d2e491876bc0..b83dc32a5ae0 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -223,22 +223,33 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info); void acpi_ns_exec_module_code_list(void); /* - * nspredef - Support for predefined/reserved names + * nsarguments - Argument count/type checking for predefined/reserved names */ -acpi_status -acpi_ns_check_predefined_names(struct acpi_namespace_node *node, - u32 user_param_count, - acpi_status return_status, - union acpi_operand_object **return_object); +void +acpi_ns_check_argument_count(char *pathname, + struct acpi_namespace_node *node, + u32 user_param_count, + const union acpi_predefined_info *info); void -acpi_ns_check_parameter_count(char *pathname, +acpi_ns_check_acpi_compliance(char *pathname, struct acpi_namespace_node *node, - u32 user_param_count, - const union acpi_predefined_info *info); + const union acpi_predefined_info *predefined); + +void acpi_ns_check_argument_types(struct acpi_evaluate_info *info); + +/* + * nspredef - Return value checking for predefined/reserved names + */ +acpi_status +acpi_ns_check_return_value(struct acpi_namespace_node *node, + struct acpi_evaluate_info *info, + u32 user_param_count, + acpi_status return_status, + union acpi_operand_object **return_object); acpi_status -acpi_ns_check_object_type(struct acpi_predefined_data *data, +acpi_ns_check_object_type(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr, u32 expected_btypes, u32 package_index); @@ -246,7 +257,7 @@ acpi_ns_check_object_type(struct acpi_predefined_data *data, * nsprepkg - Validation of predefined name packages */ acpi_status -acpi_ns_check_package(struct acpi_predefined_data *data, +acpi_ns_check_package(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr); /* @@ -308,24 +319,24 @@ acpi_ns_get_attached_data(struct acpi_namespace_node *node, * predefined methods/objects */ acpi_status -acpi_ns_simple_repair(struct acpi_predefined_data *data, +acpi_ns_simple_repair(struct acpi_evaluate_info *info, u32 expected_btypes, u32 package_index, union acpi_operand_object **return_object_ptr); acpi_status -acpi_ns_wrap_with_package(struct acpi_predefined_data *data, +acpi_ns_wrap_with_package(struct acpi_evaluate_info *info, union acpi_operand_object *original_object, union acpi_operand_object **obj_desc_ptr); acpi_status -acpi_ns_repair_null_element(struct acpi_predefined_data *data, +acpi_ns_repair_null_element(struct acpi_evaluate_info *info, u32 expected_btypes, u32 package_index, union acpi_operand_object **return_object_ptr); void -acpi_ns_remove_null_elements(struct acpi_predefined_data *data, +acpi_ns_remove_null_elements(struct acpi_evaluate_info *info, u8 package_type, union acpi_operand_object *obj_desc); @@ -334,7 +345,7 @@ acpi_ns_remove_null_elements(struct acpi_predefined_data *data, * predefined methods/objects */ acpi_status -acpi_ns_complex_repairs(struct acpi_predefined_data *data, +acpi_ns_complex_repairs(struct acpi_evaluate_info *info, struct acpi_namespace_node *node, acpi_status validate_status, union acpi_operand_object **return_object_ptr); diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index b22b70944fd6..f600aded7261 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -128,8 +128,8 @@ enum acpi_return_package_types { #define ARG_COUNT_IS_MINIMUM 0x8000 #define METHOD_MAX_ARG_TYPE ACPI_TYPE_PACKAGE -#define METHOD_GET_COUNT(arg_list) (arg_list & METHOD_ARG_MASK) -#define METHOD_GET_NEXT_ARG(arg_list) (arg_list >> METHOD_ARG_BIT_WIDTH) +#define METHOD_GET_ARG_COUNT(arg_list) ((arg_list) & METHOD_ARG_MASK) +#define METHOD_GET_NEXT_TYPE(arg_list) (((arg_list) >>= METHOD_ARG_BIT_WIDTH) & METHOD_ARG_MASK) /* Macros used to build the predefined info table */ diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h index 7896d85876ca..fc83c0a5ca70 100644 --- a/drivers/acpi/acpica/acstruct.h +++ b/drivers/acpi/acpica/acstruct.h @@ -178,25 +178,41 @@ union acpi_aml_operands { }; /* - * Structure used to pass object evaluation parameters. + * Structure used to pass object evaluation information and parameters. * Purpose is to reduce CPU stack use. */ struct acpi_evaluate_info { - struct acpi_namespace_node *prefix_node; - char *pathname; - union acpi_operand_object *obj_desc; - union acpi_operand_object **parameters; - struct acpi_namespace_node *resolved_node; - union acpi_operand_object *return_object; - u8 param_count; - u8 pass_number; - u8 return_object_type; - u8 flags; + /* The first 3 elements are passed by the caller to acpi_ns_evaluate */ + + struct acpi_namespace_node *prefix_node; /* Input: starting node */ + char *relative_pathname; /* Input: path relative to prefix_node */ + union acpi_operand_object **parameters; /* Input: argument list */ + + struct acpi_namespace_node *node; /* Resolved node (prefix_node:relative_pathname) */ + union acpi_operand_object *obj_desc; /* Object attached to the resolved node */ + char *full_pathname; /* Full pathname of the resolved node */ + + const union acpi_predefined_info *predefined; /* Used if Node is a predefined name */ + union acpi_operand_object *return_object; /* Object returned from the evaluation */ + union acpi_operand_object *parent_package; /* Used if return object is a Package */ + + u32 return_flags; /* Used for return value analysis */ + u32 return_btype; /* Bitmapped type of the returned object */ + u16 param_count; /* Count of the input argument list */ + u8 pass_number; /* Parser pass number */ + u8 return_object_type; /* Object type of the returned object */ + u8 node_flags; /* Same as Node->Flags */ + u8 flags; /* General flags */ }; /* Values for Flags above */ -#define ACPI_IGNORE_RETURN_VALUE 1 +#define ACPI_IGNORE_RETURN_VALUE 1 + +/* Defines for return_flags field above */ + +#define ACPI_OBJECT_REPAIRED 1 +#define ACPI_OBJECT_WRAPPED 2 /* Info used by acpi_ns_initialize_devices */ diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 202f4f12d3e2..3c76edea6803 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -87,6 +87,48 @@ extern const char *acpi_gbl_fc_decode[]; extern const char *acpi_gbl_pt_decode[]; #endif +/* + * For the iASL compiler case, the output is redirected to stderr so that + * any of the various ACPI errors and warnings do not appear in the output + * files, for either the compiler or disassembler portions of the tool. + */ +#ifdef ACPI_ASL_COMPILER + +#include <stdio.h> +extern FILE *acpi_gbl_output_file; + +#define ACPI_MSG_REDIRECT_BEGIN \ + FILE *output_file = acpi_gbl_output_file; \ + acpi_os_redirect_output (stderr); + +#define ACPI_MSG_REDIRECT_END \ + acpi_os_redirect_output (output_file); + +#else +/* + * non-iASL case - no redirection, nothing to do + */ +#define ACPI_MSG_REDIRECT_BEGIN +#define ACPI_MSG_REDIRECT_END +#endif + +/* + * Common error message prefixes + */ +#define ACPI_MSG_ERROR "ACPI Error: " +#define ACPI_MSG_EXCEPTION "ACPI Exception: " +#define ACPI_MSG_WARNING "ACPI Warning: " +#define ACPI_MSG_INFO "ACPI: " + +#define ACPI_MSG_BIOS_ERROR "ACPI BIOS Error (bug): " +#define ACPI_MSG_BIOS_WARNING "ACPI BIOS Warning (bug): " + +/* + * Common message suffix + */ +#define ACPI_MSG_SUFFIX \ + acpi_os_printf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number) + /* Types for Resource descriptor entries */ #define ACPI_INVALID_RESOURCE 0 @@ -578,7 +620,7 @@ void acpi_ut_print_string(char *string, u8 max_length); void ut_convert_backslashes(char *pathname); -u8 acpi_ut_valid_acpi_name(u32 name); +u8 acpi_ut_valid_acpi_name(char *name); u8 acpi_ut_valid_acpi_char(char character, u32 position); @@ -670,6 +712,12 @@ acpi_ut_predefined_info(const char *module_name, u32 line_number, char *pathname, u8 node_flags, const char *format, ...); +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_bios_error(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...); + void acpi_ut_namespace_error(const char *module_name, u32 line_number, diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c index 7ea0f162f11c..eb56b66444b5 100644 --- a/drivers/acpi/acpica/dscontrol.c +++ b/drivers/acpi/acpica/dscontrol.c @@ -78,7 +78,6 @@ acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state, switch (op->common.aml_opcode) { case AML_WHILE_OP: - /* * If this is an additional iteration of a while loop, continue. * There is no need to allocate a new control state. @@ -99,7 +98,6 @@ acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state, /*lint -fallthrough */ case AML_IF_OP: - /* * IF/WHILE: Create a new control state to manage these * constructs. We need to manage these as a stack, in order @@ -142,6 +140,7 @@ acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state, break; default: + break; } @@ -344,6 +343,7 @@ acpi_ds_exec_end_control_op(struct acpi_walk_state * walk_state, case AML_NOOP_OP: /* Just do nothing! */ + break; case AML_BREAK_POINT_OP: diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index feadeed1012d..d4bfe7b7f90a 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -563,21 +563,25 @@ acpi_ds_init_field_objects(union acpi_parse_object *op, */ switch (walk_state->opcode) { case AML_FIELD_OP: + arg = acpi_ps_get_arg(op, 2); type = ACPI_TYPE_LOCAL_REGION_FIELD; break; case AML_BANK_FIELD_OP: + arg = acpi_ps_get_arg(op, 4); type = ACPI_TYPE_LOCAL_BANK_FIELD; break; case AML_INDEX_FIELD_OP: + arg = acpi_ps_get_arg(op, 3); type = ACPI_TYPE_LOCAL_INDEX_FIELD; break; default: + return_ACPI_STATUS(AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c index bc8e63f7784b..14424200d246 100644 --- a/drivers/acpi/acpica/dsinit.c +++ b/drivers/acpi/acpica/dsinit.c @@ -127,6 +127,7 @@ acpi_ds_init_one_object(acpi_handle obj_handle, break; default: + break; } diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c index 3da80460ce38..c4b0b3657237 100644 --- a/drivers/acpi/acpica/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -285,6 +285,7 @@ acpi_ds_method_data_get_node(u8 type, break; default: + ACPI_ERROR((AE_INFO, "Type %u is invalid", type)); return_ACPI_STATUS(AE_TYPE); } @@ -428,7 +429,6 @@ acpi_ds_method_data_get_value(u8 type, return_ACPI_STATUS(AE_AML_UNINITIALIZED_ARG); case ACPI_REFCLASS_LOCAL: - /* * No error message for this case, will be trapped again later to * detect and ignore cases of Store(local_x,local_x) diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index e20e9f84eee8..63f0d220ca3d 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -648,7 +648,6 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, switch (obj_desc->common.type) { case ACPI_TYPE_BUFFER: - /* * Defer evaluation of Buffer term_arg operand */ @@ -660,7 +659,6 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, break; case ACPI_TYPE_PACKAGE: - /* * Defer evaluation of Package term_arg operand */ @@ -741,6 +739,7 @@ acpi_ds_init_object_from_op(struct acpi_walk_state *walk_state, break; default: + ACPI_ERROR((AE_INFO, "Unknown Integer type 0x%X", op_info->type)); status = AE_AML_OPERAND_TYPE; diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index ee6367b8eaf7..1fc1ff114f26 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -636,6 +636,7 @@ acpi_ds_eval_data_object_operands(struct acpi_walk_state *walk_state, break; default: + return_ACPI_STATUS(AE_AML_BAD_OPCODE); } diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c index 99778997c35a..c666fc014987 100644 --- a/drivers/acpi/acpica/dsutils.c +++ b/drivers/acpi/acpica/dsutils.c @@ -240,7 +240,6 @@ acpi_ds_is_result_used(union acpi_parse_object * op, case AML_IF_OP: case AML_WHILE_OP: - /* * If we are executing the predicate AND this is the predicate op, * we will use the return value @@ -254,7 +253,9 @@ acpi_ds_is_result_used(union acpi_parse_object * op, break; default: + /* Ignore other control opcodes */ + break; } @@ -263,7 +264,6 @@ acpi_ds_is_result_used(union acpi_parse_object * op, goto result_not_used; case AML_CLASS_CREATE: - /* * These opcodes allow term_arg(s) as operands and therefore * the operands can be method calls. The result is used. @@ -292,7 +292,6 @@ acpi_ds_is_result_used(union acpi_parse_object * op, goto result_not_used; default: - /* * In all other cases. the parent will actually use the return * object, so keep it. diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index e2199a947470..151d924817e1 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -327,6 +327,7 @@ acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state, break; default: + break; } @@ -488,7 +489,6 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state) break; case AML_TYPE_METHOD_CALL: - /* * If the method is referenced from within a package * declaration, it is not a invocation of the method, just @@ -582,7 +582,6 @@ acpi_status acpi_ds_exec_end_op(struct acpi_walk_state *walk_state) switch (op->common.parent->common.aml_opcode) { case AML_NAME_OP: - /* * Put the Node on the object stack (Contains the ACPI Name * of this object) diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 6e17c0e24e63..95e681a36f9c 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -74,6 +74,7 @@ acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number) switch (pass_number) { case 1: + walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 | ACPI_PARSE_DELETE_TREE; walk_state->descending_callback = acpi_ds_load1_begin_op; @@ -81,6 +82,7 @@ acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number) break; case 2: + walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 | ACPI_PARSE_DELETE_TREE; walk_state->descending_callback = acpi_ds_load2_begin_op; @@ -88,6 +90,7 @@ acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number) break; case 3: + #ifndef ACPI_NO_METHOD_EXECUTION walk_state->parse_flags |= ACPI_PARSE_EXECUTE | ACPI_PARSE_DELETE_TREE; @@ -97,6 +100,7 @@ acpi_ds_init_callbacks(struct acpi_walk_state *walk_state, u32 pass_number) break; default: + return (AE_BAD_PARAMETER); } @@ -161,7 +165,6 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state, switch (walk_state->opcode) { case AML_SCOPE_OP: - /* * The target name of the Scope() operator must exist at this point so * that we can actually open the scope to enter new names underneath it. @@ -210,7 +213,6 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state, case ACPI_TYPE_INTEGER: case ACPI_TYPE_STRING: case ACPI_TYPE_BUFFER: - /* * These types we will allow, but we will change the type. * This enables some existing code of the form: @@ -232,7 +234,6 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state, break; case ACPI_TYPE_METHOD: - /* * Allow scope change to root during execution of module-level * code. Root is typed METHOD during this time. diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c index 4407ff2377d5..b1f8f4725c23 100644 --- a/drivers/acpi/acpica/dswload2.c +++ b/drivers/acpi/acpica/dswload2.c @@ -509,6 +509,7 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) break; default: + /* All NAMED_FIELD opcodes must be handled above */ break; } @@ -548,6 +549,7 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) break; default: + /* Unknown opcode */ status = AE_OK; @@ -674,6 +676,7 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) #endif /* ACPI_NO_METHOD_EXECUTION */ default: + /* All NAMED_COMPLEX opcodes must be handled above */ break; } @@ -721,6 +724,7 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) break; default: + break; } diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c index a621481c6cf2..fdb0a76e40a3 100644 --- a/drivers/acpi/acpica/evglock.c +++ b/drivers/acpi/acpica/evglock.c @@ -128,6 +128,7 @@ acpi_status acpi_ev_remove_global_lock_handler(void) status = acpi_remove_fixed_event_handler(ACPI_EVENT_GLOBAL, acpi_ev_global_lock_handler); + acpi_os_delete_lock(acpi_gbl_global_lock_pending_lock); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index a493b528f8f9..c8a1f7d5931f 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -529,7 +529,6 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) switch (local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { case ACPI_GPE_DISPATCH_NOTIFY: - /* * Implicit notify. * Dispatch a DEVICE_WAKE notify to the appropriate handler. @@ -579,11 +578,11 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) (local_gpe_event_info->dispatch. method_node))); } - break; default: - return_VOID; /* Should never happen */ + + return_VOID; /* Should never happen */ } /* Defer enabling of GPE until all notify handlers are done */ @@ -755,7 +754,6 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, case ACPI_GPE_DISPATCH_METHOD: case ACPI_GPE_DISPATCH_NOTIFY: - /* * Execute the method associated with the GPE * NOTE: Level-triggered GPEs are cleared after the method completes. @@ -771,7 +769,6 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, break; default: - /* * No handler or method to run! * 03/2010: This case should no longer be possible. We will not allow diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index a2d688bbac02..c1aa1eda26c3 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -382,6 +382,8 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device, status = acpi_ev_install_gpe_block(gpe_block, interrupt_number); if (ACPI_FAILURE(status)) { + ACPI_FREE(gpe_block->register_info); + ACPI_FREE(gpe_block->event_info); ACPI_FREE(gpe_block); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 72b8f6b3f4ca..9037f17c9608 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -363,14 +363,17 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, */ switch (name[1]) { case 'L': + type = ACPI_GPE_LEVEL_TRIGGERED; break; case 'E': + type = ACPI_GPE_EDGE_TRIGGERED; break; default: + /* Unknown method type, just ignore it */ ACPI_DEBUG_PRINT((ACPI_DB_LOAD, diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c index d4f83112c2e2..068af96134b8 100644 --- a/drivers/acpi/acpica/evhandler.c +++ b/drivers/acpi/acpica/evhandler.c @@ -354,36 +354,43 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node, switch (space_id) { case ACPI_ADR_SPACE_SYSTEM_MEMORY: + handler = acpi_ex_system_memory_space_handler; setup = acpi_ev_system_memory_region_setup; break; case ACPI_ADR_SPACE_SYSTEM_IO: + handler = acpi_ex_system_io_space_handler; setup = acpi_ev_io_space_region_setup; break; case ACPI_ADR_SPACE_PCI_CONFIG: + handler = acpi_ex_pci_config_space_handler; setup = acpi_ev_pci_config_region_setup; break; case ACPI_ADR_SPACE_CMOS: + handler = acpi_ex_cmos_space_handler; setup = acpi_ev_cmos_region_setup; break; case ACPI_ADR_SPACE_PCI_BAR_TARGET: + handler = acpi_ex_pci_bar_space_handler; setup = acpi_ev_pci_bar_region_setup; break; case ACPI_ADR_SPACE_DATA_TABLE: + handler = acpi_ex_data_table_space_handler; setup = NULL; break; default: + status = AE_BAD_PARAMETER; goto unlock_and_exit; } diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index c986b2336b81..1b111ef74903 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -78,6 +78,7 @@ u8 acpi_ev_is_notify_object(struct acpi_namespace_node *node) return (TRUE); default: + return (FALSE); } } @@ -275,6 +276,8 @@ void acpi_ev_terminate(void) ACPI_ERROR((AE_INFO, "Could not remove Global Lock handler")); } + + acpi_gbl_events_initialized = FALSE; } /* Deallocate all handler objects installed within GPE info structs */ diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index 6555e350fc1f..cea14d6fc76c 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -54,7 +54,8 @@ extern u8 acpi_gbl_default_address_spaces[]; /* Local prototypes */ -static void acpi_ev_orphan_ec_reg_method(void); +static void +acpi_ev_orphan_ec_reg_method(struct acpi_namespace_node *ec_device_node); static acpi_status acpi_ev_reg_run(acpi_handle obj_handle, @@ -532,7 +533,7 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function) } info->prefix_node = region_obj2->extra.method_REG; - info->pathname = NULL; + info->relative_pathname = NULL; info->parameters = args; info->flags = ACPI_IGNORE_RETURN_VALUE; @@ -612,7 +613,7 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node, /* Special case for EC: handle "orphan" _REG methods with no region */ if (space_id == ACPI_ADR_SPACE_EC) { - acpi_ev_orphan_ec_reg_method(); + acpi_ev_orphan_ec_reg_method(node); } return_ACPI_STATUS(status); @@ -681,7 +682,7 @@ acpi_ev_reg_run(acpi_handle obj_handle, * * FUNCTION: acpi_ev_orphan_ec_reg_method * - * PARAMETERS: None + * PARAMETERS: ec_device_node - Namespace node for an EC device * * RETURN: None * @@ -693,37 +694,27 @@ acpi_ev_reg_run(acpi_handle obj_handle, * detected by providing a _REG method object underneath the * Embedded Controller device." * - * To quickly access the EC device, we use the EC_ID that appears - * within the ECDT. Otherwise, we would need to perform a time- - * consuming namespace walk, executing _HID methods to find the - * EC device. + * To quickly access the EC device, we use the ec_device_node used + * during EC handler installation. Otherwise, we would need to + * perform a time consuming namespace walk, executing _HID + * methods to find the EC device. + * + * MUTEX: Assumes the namespace is locked * ******************************************************************************/ -static void acpi_ev_orphan_ec_reg_method(void) +static void +acpi_ev_orphan_ec_reg_method(struct acpi_namespace_node *ec_device_node) { - struct acpi_table_ecdt *table; + acpi_handle reg_method; + struct acpi_namespace_node *next_node; acpi_status status; struct acpi_object_list args; union acpi_object objects[2]; - struct acpi_namespace_node *ec_device_node; - struct acpi_namespace_node *reg_method; - struct acpi_namespace_node *next_node; ACPI_FUNCTION_TRACE(ev_orphan_ec_reg_method); - /* Get the ECDT (if present in system) */ - - status = acpi_get_table(ACPI_SIG_ECDT, 0, - ACPI_CAST_INDIRECT_PTR(struct acpi_table_header, - &table)); - if (ACPI_FAILURE(status)) { - return_VOID; - } - - /* We need a valid EC_ID string */ - - if (!(*table->id)) { + if (!ec_device_node) { return_VOID; } @@ -731,22 +722,11 @@ static void acpi_ev_orphan_ec_reg_method(void) (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - /* Get a handle to the EC device referenced in the ECDT */ - - status = acpi_get_handle(NULL, - ACPI_CAST_PTR(char, table->id), - ACPI_CAST_PTR(acpi_handle, &ec_device_node)); - if (ACPI_FAILURE(status)) { - goto exit; - } - /* Get a handle to a _REG method immediately under the EC device */ - status = acpi_get_handle(ec_device_node, - METHOD_NAME__REG, ACPI_CAST_PTR(acpi_handle, - ®_method)); + status = acpi_get_handle(ec_device_node, METHOD_NAME__REG, ®_method); if (ACPI_FAILURE(status)) { - goto exit; + goto exit; /* There is no _REG method present */ } /* @@ -754,19 +734,20 @@ static void acpi_ev_orphan_ec_reg_method(void) * this scope with the Embedded Controller space ID. Otherwise, it * will already have been executed. Note, this allows for Regions * with other space IDs to be present; but the code below will then - * execute the _REG method with the EC space ID argument. + * execute the _REG method with the embedded_control space_ID argument. */ next_node = acpi_ns_get_next_node(ec_device_node, NULL); while (next_node) { if ((next_node->type == ACPI_TYPE_REGION) && (next_node->object) && (next_node->object->region.space_id == ACPI_ADR_SPACE_EC)) { - goto exit; /* Do not execute _REG */ + goto exit; /* Do not execute the _REG */ } + next_node = acpi_ns_get_next_node(ec_device_node, next_node); } - /* Evaluate the _REG(EC,Connect) method */ + /* Evaluate the _REG(embedded_control,Connect) method */ args.count = 2; args.pointer = objects; diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 3bb616794b3b..8354c4f7f10c 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -596,7 +596,9 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj, break; default: + /* Ignore other objects */ + break; } diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index aff4cc261211..7662f1a42ff6 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -366,16 +366,19 @@ acpi_set_gpe_wake_mask(acpi_handle gpe_device, u32 gpe_number, u8 action) switch (action) { case ACPI_GPE_ENABLE: + ACPI_SET_BIT(gpe_register_info->enable_for_wake, (u8)register_bit); break; case ACPI_GPE_DISABLE: + ACPI_CLEAR_BIT(gpe_register_info->enable_for_wake, (u8)register_bit); break; default: + ACPI_ERROR((AE_INFO, "%u, Invalid action", action)); status = AE_BAD_PARAMETER; break; diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index 96c9e5f355ae..80cecf838591 100644 --- a/drivers/acpi/acpica/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -139,6 +139,7 @@ acpi_install_address_space_handler(acpi_handle device, break; default: + break; } diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index d93b70be60ad..06d216c8d43a 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -480,6 +480,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, break; default: + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } @@ -588,7 +589,7 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) (ACPI_GET_DESCRIPTOR_TYPE(ddb_handle) != ACPI_DESC_TYPE_OPERAND) || (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE) || (!(ddb_handle->common.flags & AOPOBJ_DATA_VALID))) { - return_ACPI_STATUS(AE_BAD_PARAMETER); + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } /* Get the table index from the ddb_handle */ diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index d2b9613bbf01..69e4a8cc9b71 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -99,6 +99,7 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, break; default: + return_ACPI_STATUS(AE_TYPE); } @@ -117,7 +118,6 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, switch (obj_desc->common.type) { case ACPI_TYPE_STRING: - /* * Convert string to an integer - for most cases, the string must be * hexadecimal as per the ACPI specification. The only exception (as @@ -161,6 +161,7 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, default: /* No other types can get here */ + break; } @@ -213,7 +214,6 @@ acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_OK); case ACPI_TYPE_INTEGER: - /* * Create a new Buffer object. * Need enough space for one integer @@ -233,7 +233,6 @@ acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc, break; case ACPI_TYPE_STRING: - /* * Create a new Buffer object * Size will be the string length @@ -258,6 +257,7 @@ acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc, break; default: + return_ACPI_STATUS(AE_TYPE); } @@ -304,15 +304,18 @@ acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 data_width) switch (data_width) { case 1: + decimal_length = ACPI_MAX8_DECIMAL_DIGITS; break; case 4: + decimal_length = ACPI_MAX32_DECIMAL_DIGITS; break; case 8: default: + decimal_length = ACPI_MAX64_DECIMAL_DIGITS; break; } @@ -546,6 +549,7 @@ acpi_ex_convert_to_string(union acpi_operand_object * obj_desc, break; default: + return_ACPI_STATUS(AE_TYPE); } @@ -599,6 +603,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, break; default: + /* No conversion allowed for these types */ if (destination_type != source_desc->common.type) { @@ -649,6 +654,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, break; default: + ACPI_ERROR((AE_INFO, "Bad destination type during conversion: 0x%X", destination_type)); @@ -664,6 +670,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, break; default: + ACPI_ERROR((AE_INFO, "Unknown Target type ID 0x%X AmlOpcode 0x%X DestType %s", GET_CURRENT_ARG_TYPE(walk_state->op_info-> diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index 26a13f67977e..269e81d86ef4 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -103,7 +103,6 @@ acpi_status acpi_ex_create_alias(struct acpi_walk_state *walk_state) case ACPI_TYPE_BUFFER: case ACPI_TYPE_PACKAGE: case ACPI_TYPE_BUFFER_FIELD: - /* * These types open a new scope, so we need the NS node in order to access * any children. @@ -113,7 +112,6 @@ acpi_status acpi_ex_create_alias(struct acpi_walk_state *walk_state) case ACPI_TYPE_PROCESSOR: case ACPI_TYPE_THERMAL: case ACPI_TYPE_LOCAL_SCOPE: - /* * The new alias has the type ALIAS and points to the original * NS node, not the object itself. @@ -124,7 +122,6 @@ acpi_status acpi_ex_create_alias(struct acpi_walk_state *walk_state) break; case ACPI_TYPE_METHOD: - /* * Control method aliases need to be differentiated */ diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c index 7eb853cd279f..81c72a4ecd82 100644 --- a/drivers/acpi/acpica/exdebug.c +++ b/drivers/acpi/acpica/exdebug.c @@ -193,6 +193,7 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc, return_VOID; default: + break; } @@ -226,6 +227,7 @@ acpi_ex_do_debug_object(union acpi_operand_object *source_desc, break; default: + acpi_ex_do_debug_object((source_desc-> reference. node)->object, diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index e5a3c249f7fa..c740f24e3101 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -357,6 +357,7 @@ acpi_ex_dump_object(union acpi_operand_object *obj_desc, switch (info->opcode) { case ACPI_EXD_INIT: + break; case ACPI_EXD_TYPE: @@ -718,6 +719,7 @@ void acpi_ex_dump_operand(union acpi_operand_object *obj_desc, u32 depth) break; default: + /* Unknown Type */ acpi_os_printf("Unknown Type %X\n", obj_desc->common.type); diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index 7d4bae71e8c6..c2a65aaf29af 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -331,21 +331,25 @@ acpi_ex_write_data_to_field(union acpi_operand_object *source_desc, switch (source_desc->common.type) { case ACPI_TYPE_INTEGER: + buffer = &source_desc->integer.value; length = sizeof(source_desc->integer.value); break; case ACPI_TYPE_BUFFER: + buffer = source_desc->buffer.pointer; length = source_desc->buffer.length; break; case ACPI_TYPE_STRING: + buffer = source_desc->string.pointer; length = source_desc->string.length; break; default: + return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index c84ee956fa4c..7e0afe72487e 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -446,7 +446,6 @@ acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, break; case ACPI_TYPE_LOCAL_BANK_FIELD: - /* * Ensure that the bank_value is not beyond the capacity of * the register @@ -488,7 +487,6 @@ acpi_ex_field_datum_io(union acpi_operand_object *obj_desc, break; case ACPI_TYPE_LOCAL_INDEX_FIELD: - /* * Ensure that the index_value is not beyond the capacity of * the register diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c index 72a2a13b6d36..00bf29877574 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -105,7 +105,6 @@ acpi_ex_get_object_reference(union acpi_operand_object *obj_desc, break; case ACPI_DESC_TYPE_NAMED: - /* * A named reference that has already been resolved to a Node */ @@ -261,20 +260,24 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0, */ switch (operand0->common.type) { case ACPI_TYPE_INTEGER: + status = acpi_ex_convert_to_integer(operand1, &local_operand1, 16); break; case ACPI_TYPE_STRING: + status = acpi_ex_convert_to_string(operand1, &local_operand1, ACPI_IMPLICIT_CONVERT_HEX); break; case ACPI_TYPE_BUFFER: + status = acpi_ex_convert_to_buffer(operand1, &local_operand1); break; default: + ACPI_ERROR((AE_INFO, "Invalid object type: 0x%X", operand0->common.type)); status = AE_AML_INTERNAL; @@ -519,6 +522,7 @@ acpi_ex_do_logical_numeric_op(u16 opcode, break; default: + status = AE_AML_INTERNAL; break; } @@ -580,20 +584,24 @@ acpi_ex_do_logical_op(u16 opcode, */ switch (operand0->common.type) { case ACPI_TYPE_INTEGER: + status = acpi_ex_convert_to_integer(operand1, &local_operand1, 16); break; case ACPI_TYPE_STRING: + status = acpi_ex_convert_to_string(operand1, &local_operand1, ACPI_IMPLICIT_CONVERT_HEX); break; case ACPI_TYPE_BUFFER: + status = acpi_ex_convert_to_buffer(operand1, &local_operand1); break; default: + status = AE_AML_INTERNAL; break; } @@ -636,6 +644,7 @@ acpi_ex_do_logical_op(u16 opcode, break; default: + status = AE_AML_INTERNAL; break; } @@ -703,6 +712,7 @@ acpi_ex_do_logical_op(u16 opcode, break; default: + status = AE_AML_INTERNAL; break; } diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index b60c877f5906..814b4a3d656a 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -327,7 +327,6 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state) break; case AML_FROM_BCD_OP: /* from_bcd (BCDValue, Result) */ - /* * The 64-bit ACPI integer can hold 16 4-bit BCD characters * (if table is 32-bit, integer can hold 8 BCD characters) @@ -407,7 +406,6 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state) break; case AML_COND_REF_OF_OP: /* cond_ref_of (source_object, Result) */ - /* * This op is a little strange because the internal return value is * different than the return value stored in the result descriptor @@ -442,13 +440,14 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state) goto cleanup; default: + /* No other opcodes get here */ + break; } break; case AML_STORE_OP: /* Store (Source, Target) */ - /* * A store operand is typically a number, string, buffer or lvalue * Be careful about deleting the source object, @@ -615,7 +614,6 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) case AML_DECREMENT_OP: /* Decrement (Operand) */ case AML_INCREMENT_OP: /* Increment (Operand) */ - /* * Create a new integer. Can't just get the base integer and * increment it because it may be an Arg or Field. @@ -682,7 +680,6 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) break; case AML_TYPE_OP: /* object_type (source_object) */ - /* * Note: The operand is not resolved at this point because we want to * get the associated object, not its value. For example, we don't @@ -709,7 +706,6 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) break; case AML_SIZE_OF_OP: /* size_of (source_object) */ - /* * Note: The operand is not resolved at this point because we want to * get the associated object, not its value. @@ -735,10 +731,12 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) */ switch (type) { case ACPI_TYPE_INTEGER: + value = acpi_gbl_integer_byte_width; break; case ACPI_TYPE_STRING: + value = temp_desc->string.length; break; @@ -759,6 +757,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) break; default: + ACPI_ERROR((AE_INFO, "Operand must be Buffer/Integer/String/Package - found type %s", acpi_ut_get_type_name(type))); @@ -860,9 +859,11 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) break; case ACPI_TYPE_STRING: + break; default: + status = AE_AML_OPERAND_TYPE; goto cleanup; } @@ -923,7 +924,6 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) */ switch (operand[0]->reference.class) { case ACPI_REFCLASS_INDEX: - /* * The target type for the Index operator must be * either a Buffer or a Package @@ -956,7 +956,6 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) break; case ACPI_TYPE_PACKAGE: - /* * Return the referenced element of the package. We must * add another reference to the referenced object, however. @@ -999,6 +998,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) break; default: + ACPI_ERROR((AE_INFO, "Unknown class in reference(%p) - 0x%2.2X", operand[0], diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c index b0838a4ea53e..d5088f7030c7 100644 --- a/drivers/acpi/acpica/exoparg2.c +++ b/drivers/acpi/acpica/exoparg2.c @@ -304,7 +304,6 @@ acpi_status acpi_ex_opcode_2A_1T_1R(struct acpi_walk_state *walk_state) break; case AML_TO_STRING_OP: /* to_string (Buffer, Length, Result) (ACPI 2.0) */ - /* * Input object is guaranteed to be a buffer at this point (it may have * been converted.) Copy the raw buffer data to a new object of diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c index 2d7491f3126e..37656f12f204 100644 --- a/drivers/acpi/acpica/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -155,7 +155,6 @@ acpi_status acpi_ex_opcode_3A_1T_1R(struct acpi_walk_state *walk_state) switch (walk_state->opcode) { case AML_MID_OP: /* Mid (Source[0], Index[1], Length[2], Result[3]) */ - /* * Create the return object. The Source operand is guaranteed to be * either a String or a Buffer, so just use its type. diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c index b76b97002dff..879b6cd8319c 100644 --- a/drivers/acpi/acpica/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -119,7 +119,6 @@ acpi_ex_do_match(u32 match_op, break; case MATCH_MEQ: - /* * True if equal: (P[i] == M) * Change to: (M == P[i]) @@ -133,7 +132,6 @@ acpi_ex_do_match(u32 match_op, break; case MATCH_MLE: - /* * True if less than or equal: (P[i] <= M) (P[i] not_greater than M) * Change to: (M >= P[i]) (M not_less than P[i]) @@ -148,7 +146,6 @@ acpi_ex_do_match(u32 match_op, break; case MATCH_MLT: - /* * True if less than: (P[i] < M) * Change to: (M > P[i]) @@ -162,7 +159,6 @@ acpi_ex_do_match(u32 match_op, break; case MATCH_MGE: - /* * True if greater than or equal: (P[i] >= M) (P[i] not_less than M) * Change to: (M <= P[i]) (M not_greater than P[i]) @@ -177,7 +173,6 @@ acpi_ex_do_match(u32 match_op, break; case MATCH_MGT: - /* * True if greater than: (P[i] > M) * Change to: (M < P[i]) diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index 6b728aef2dca..5a588611ab48 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -253,26 +253,31 @@ acpi_ex_decode_field_access(union acpi_operand_object *obj_desc, case AML_FIELD_ACCESS_BYTE: case AML_FIELD_ACCESS_BUFFER: /* ACPI 2.0 (SMBus Buffer) */ + byte_alignment = 1; bit_length = 8; break; case AML_FIELD_ACCESS_WORD: + byte_alignment = 2; bit_length = 16; break; case AML_FIELD_ACCESS_DWORD: + byte_alignment = 4; bit_length = 32; break; case AML_FIELD_ACCESS_QWORD: /* ACPI 2.0 */ + byte_alignment = 8; bit_length = 64; break; default: + /* Invalid field access type */ ACPI_ERROR((AE_INFO, "Unknown field access type 0x%X", access)); @@ -598,7 +603,9 @@ acpi_status acpi_ex_prep_field_value(struct acpi_create_field_info *info) break; default: + /* No other types should get here */ + break; } diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index 182abaf045e1..303429bb4d5d 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -88,22 +88,27 @@ acpi_ex_system_memory_space_handler(u32 function, switch (bit_width) { case 8: + length = 1; break; case 16: + length = 2; break; case 32: + length = 4; break; case 64: + length = 8; break; default: + ACPI_ERROR((AE_INFO, "Invalid SystemMemory width %u", bit_width)); return_ACPI_STATUS(AE_AML_OPERAND_VALUE); @@ -214,23 +219,29 @@ acpi_ex_system_memory_space_handler(u32 function, *value = 0; switch (bit_width) { case 8: - *value = (u64) ACPI_GET8(logical_addr_ptr); + + *value = (u64)ACPI_GET8(logical_addr_ptr); break; case 16: - *value = (u64) ACPI_GET16(logical_addr_ptr); + + *value = (u64)ACPI_GET16(logical_addr_ptr); break; case 32: - *value = (u64) ACPI_GET32(logical_addr_ptr); + + *value = (u64)ACPI_GET32(logical_addr_ptr); break; case 64: - *value = (u64) ACPI_GET64(logical_addr_ptr); + + *value = (u64)ACPI_GET64(logical_addr_ptr); break; default: + /* bit_width was already validated */ + break; } break; @@ -239,28 +250,35 @@ acpi_ex_system_memory_space_handler(u32 function, switch (bit_width) { case 8: + ACPI_SET8(logical_addr_ptr, *value); break; case 16: + ACPI_SET16(logical_addr_ptr, *value); break; case 32: + ACPI_SET32(logical_addr_ptr, *value); break; case 64: + ACPI_SET64(logical_addr_ptr, *value); break; default: + /* bit_width was already validated */ + break; } break; default: + status = AE_BAD_PARAMETER; break; } @@ -320,6 +338,7 @@ acpi_ex_system_io_space_handler(u32 function, break; default: + status = AE_BAD_PARAMETER; break; } diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c index 8565b6bd12bb..acd34f599313 100644 --- a/drivers/acpi/acpica/exresnte.c +++ b/drivers/acpi/acpica/exresnte.c @@ -248,6 +248,7 @@ acpi_ex_resolve_node_to_value(struct acpi_namespace_node **object_ptr, break; default: + /* No named references are allowed here */ ACPI_ERROR((AE_INFO, diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c index e4f9dfbb2a13..ac04278ad28f 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -156,7 +156,6 @@ acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr, switch (ref_type) { case ACPI_REFCLASS_LOCAL: case ACPI_REFCLASS_ARG: - /* * Get the local from the method's state info * Note: this increments the local's object reference count @@ -309,6 +308,7 @@ acpi_ex_resolve_object_to_value(union acpi_operand_object **stack_ptr, break; default: + break; } @@ -348,10 +348,12 @@ acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state, switch (ACPI_GET_DESCRIPTOR_TYPE(obj_desc)) { case ACPI_DESC_TYPE_OPERAND: + type = obj_desc->common.type; break; case ACPI_DESC_TYPE_NAMED: + type = ((struct acpi_namespace_node *)obj_desc)->type; obj_desc = acpi_ns_get_attached_object((struct acpi_namespace_node *) @@ -538,7 +540,9 @@ acpi_ex_resolve_multiple(struct acpi_walk_state *walk_state, break; default: + /* No change to Type required */ + break; } diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index 9fb9f5e9a4da..00e5af7129c1 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -307,7 +307,6 @@ acpi_ex_resolve_operands(u16 opcode, case ARGI_TARGETREF: /* Allows implicit conversion rules before store */ case ARGI_FIXED_TARGET: /* No implicit conversion before store to target */ case ARGI_SIMPLE_TARGET: /* Name, Local, or arg - no implicit conversion */ - /* * Need an operand of type ACPI_TYPE_LOCAL_REFERENCE * A Namespace Node is OK as-is @@ -326,7 +325,6 @@ acpi_ex_resolve_operands(u16 opcode, goto next_operand; case ARGI_DATAREFOBJ: /* Store operator only */ - /* * We don't want to resolve index_op reference objects during * a store because this would be an implicit de_ref_of operation. @@ -343,7 +341,9 @@ acpi_ex_resolve_operands(u16 opcode, break; default: + /* All cases covered above */ + break; } @@ -433,7 +433,6 @@ acpi_ex_resolve_operands(u16 opcode, goto next_operand; case ARGI_BUFFER: - /* * Need an operand of type ACPI_TYPE_BUFFER, * But we can implicitly convert from a STRING or INTEGER @@ -459,7 +458,6 @@ acpi_ex_resolve_operands(u16 opcode, goto next_operand; case ARGI_STRING: - /* * Need an operand of type ACPI_TYPE_STRING, * But we can implicitly convert from a BUFFER or INTEGER @@ -562,6 +560,7 @@ acpi_ex_resolve_operands(u16 opcode, break; default: + ACPI_ERROR((AE_INFO, "Needed [Buffer/String/Package/Reference], found [%s] %p", acpi_ut_get_object_type_name @@ -584,6 +583,7 @@ acpi_ex_resolve_operands(u16 opcode, break; default: + ACPI_ERROR((AE_INFO, "Needed [Buffer/String/Package], found [%s] %p", acpi_ut_get_object_type_name @@ -605,6 +605,7 @@ acpi_ex_resolve_operands(u16 opcode, break; default: + ACPI_ERROR((AE_INFO, "Needed [Region/Buffer], found [%s] %p", acpi_ut_get_object_type_name diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index 93c6049c2d75..2bdba6f7d762 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -114,6 +114,7 @@ acpi_ex_store(union acpi_operand_object *source_desc, switch (dest_desc->common.type) { case ACPI_TYPE_LOCAL_REFERENCE: + break; case ACPI_TYPE_INTEGER: @@ -178,7 +179,6 @@ acpi_ex_store(union acpi_operand_object *source_desc, break; case ACPI_REFCLASS_DEBUG: - /* * Storing to the Debug object causes the value stored to be * displayed and otherwise has no effect -- see ACPI Specification @@ -291,7 +291,6 @@ acpi_ex_store_object_to_index(union acpi_operand_object *source_desc, break; case ACPI_TYPE_BUFFER_FIELD: - /* * Store into a Buffer or String (not actually a real buffer_field) * at a location defined by an Index. @@ -447,7 +446,6 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, case ACPI_TYPE_INTEGER: case ACPI_TYPE_STRING: case ACPI_TYPE_BUFFER: - /* * These target types are all of type Integer/String/Buffer, and * therefore support implicit conversion before the store. diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c index 1cefe777068e..20d809d90c5b 100644 --- a/drivers/acpi/acpica/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -85,11 +85,9 @@ acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr, * These cases all require only Integers or values that * can be converted to Integers (Strings or Buffers) */ - case ACPI_TYPE_INTEGER: case ACPI_TYPE_STRING: case ACPI_TYPE_BUFFER: - /* * Stores into a Field/Region or into a Integer/Buffer/String * are all essentially the same. This case handles the @@ -133,7 +131,6 @@ acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr, case ACPI_TYPE_LOCAL_ALIAS: case ACPI_TYPE_LOCAL_METHOD_ALIAS: - /* * All aliases should have been resolved earlier, during the * operand resolution phase. @@ -144,7 +141,6 @@ acpi_ex_resolve_object(union acpi_operand_object **source_desc_ptr, case ACPI_TYPE_PACKAGE: default: - /* * All other types than Alias and the various Fields come here, * including the untyped case - ACPI_TYPE_ANY. diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c index 579c3a53ac87..3d36df828f52 100644 --- a/drivers/acpi/acpica/hwacpi.c +++ b/drivers/acpi/acpica/hwacpi.c @@ -108,7 +108,6 @@ acpi_status acpi_hw_set_mode(u32 mode) break; case ACPI_SYS_MODE_LEGACY: - /* * BIOS should clear all fixed status bits and restore fixed event * enable bits to default @@ -120,6 +119,7 @@ acpi_status acpi_hw_set_mode(u32 mode) break; default: + return_ACPI_STATUS(AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 20d02e93c990..96540506058f 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -127,14 +127,17 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) /*lint -fallthrough */ case ACPI_GPE_ENABLE: + ACPI_SET_BIT(enable_mask, register_bit); break; case ACPI_GPE_DISABLE: + ACPI_CLEAR_BIT(enable_mask, register_bit); break; default: + ACPI_ERROR((AE_INFO, "Invalid GPE Action, %u", action)); return (AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index 083d6551f0e2..8d2e866be15f 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -419,6 +419,7 @@ acpi_status acpi_hw_register_read(u32 register_id, u32 *return_value) break; default: + ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id)); status = AE_BAD_PARAMETER; break; @@ -491,7 +492,6 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) break; case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */ - /* * Perform a read first to preserve certain bits (per ACPI spec) * Note: This includes SCI_EN, we never want to change this bit @@ -520,7 +520,6 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) break; case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ - /* * For control registers, all reserved bits must be preserved, * as per the ACPI spec. @@ -555,6 +554,7 @@ acpi_status acpi_hw_register_write(u32 register_id, u32 value) break; default: + ACPI_ERROR((AE_INFO, "Unknown Register ID: 0x%X", register_id)); status = AE_BAD_PARAMETER; break; diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index 04c2e16f2c0a..5ee7a814cd92 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -495,7 +495,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) * Evaluate the \_Sx namespace object containing the register values * for this state */ - info->pathname = + info->relative_pathname = ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]); status = acpi_ns_evaluate(info); if (ACPI_FAILURE(status)) { @@ -506,7 +506,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) if (!info->return_object) { ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]", - info->pathname)); + info->relative_pathname)); status = AE_AML_NO_RETURN_VALUE; goto cleanup; } @@ -528,10 +528,12 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) elements = info->return_object->package.elements; switch (info->return_object->package.count) { case 0: + status = AE_AML_PACKAGE_LIMIT; break; case 1: + if (elements[0]->common.type != ACPI_TYPE_INTEGER) { status = AE_AML_OPERAND_TYPE; break; @@ -545,6 +547,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) case 2: default: + if ((elements[0]->common.type != ACPI_TYPE_INTEGER) || (elements[1]->common.type != ACPI_TYPE_INTEGER)) { status = AE_AML_OPERAND_TYPE; @@ -565,7 +568,7 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 *sleep_type_a, u8 *sleep_type_b) if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "While evaluating Sleep State [%s]", - info->pathname)); + info->relative_pathname)); } ACPI_FREE(info); diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c index 35eebdac0f9d..f2e669db8b65 100644 --- a/drivers/acpi/acpica/hwxfsleep.c +++ b/drivers/acpi/acpica/hwxfsleep.c @@ -240,12 +240,14 @@ static acpi_status acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id) &acpi_sleep_dispatch[function_id]; #if (!ACPI_REDUCED_HARDWARE) - /* * If the Hardware Reduced flag is set (from the FADT), we must - * use the extended sleep registers + * use the extended sleep registers (FADT). Note: As per the ACPI + * specification, these extended registers are to be used for HW-reduced + * platforms only. They are not general-purpose replacements for the + * legacy PM register sleep support. */ - if (acpi_gbl_reduced_hardware || acpi_gbl_FADT.sleep_control.address) { + if (acpi_gbl_reduced_hardware) { status = sleep_functions->extended_function(sleep_state); } else { /* Legacy sleep */ @@ -314,20 +316,24 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state) switch (sleep_state) { case ACPI_STATE_S0: + sst_value = ACPI_SST_WORKING; break; case ACPI_STATE_S1: case ACPI_STATE_S2: case ACPI_STATE_S3: + sst_value = ACPI_SST_SLEEPING; break; case ACPI_STATE_S4: + sst_value = ACPI_SST_SLEEP_CONTEXT; break; default: + sst_value = ACPI_SST_INDICATOR_OFF; /* Default is off */ break; } diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index 8769cf83b044..c5316e5bd4ab 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -151,6 +151,7 @@ acpi_status acpi_ns_root_initialize(void) */ switch (init_val->type) { case ACPI_TYPE_METHOD: + obj_desc->method.param_count = (u8) ACPI_TO_INTEGER(val); obj_desc->common.flags |= AOPOBJ_DATA_VALID; diff --git a/drivers/acpi/acpica/nsarguments.c b/drivers/acpi/acpica/nsarguments.c new file mode 100644 index 000000000000..74b24c82707e --- /dev/null +++ b/drivers/acpi/acpica/nsarguments.c @@ -0,0 +1,294 @@ +/****************************************************************************** + * + * Module Name: nsarguments - Validation of args for ACPI predefined methods + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2013, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acnamesp.h" +#include "acpredef.h" + +#define _COMPONENT ACPI_NAMESPACE +ACPI_MODULE_NAME("nsarguments") + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_argument_types + * + * PARAMETERS: info - Method execution information block + * + * RETURN: None + * + * DESCRIPTION: Check the incoming argument count and all argument types + * against the argument type list for a predefined name. + * + ******************************************************************************/ +void acpi_ns_check_argument_types(struct acpi_evaluate_info *info) +{ + u16 arg_type_list; + u8 arg_count; + u8 arg_type; + u8 user_arg_type; + u32 i; + + /* If not a predefined name, cannot typecheck args */ + + if (!info->predefined) { + return; + } + + arg_type_list = info->predefined->info.argument_list; + arg_count = METHOD_GET_ARG_COUNT(arg_type_list); + + /* Typecheck all arguments */ + + for (i = 0; ((i < arg_count) && (i < info->param_count)); i++) { + arg_type = METHOD_GET_NEXT_TYPE(arg_type_list); + user_arg_type = info->parameters[i]->common.type; + + if (user_arg_type != arg_type) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + ACPI_WARN_ALWAYS, + "Argument #%u type mismatch - " + "Found [%s], ACPI requires [%s]", + (i + 1), + acpi_ut_get_type_name + (user_arg_type), + acpi_ut_get_type_name(arg_type))); + } + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_acpi_compliance + * + * PARAMETERS: pathname - Full pathname to the node (for error msgs) + * node - Namespace node for the method/object + * predefined - Pointer to entry in predefined name table + * + * RETURN: None + * + * DESCRIPTION: Check that the declared parameter count (in ASL/AML) for a + * predefined name is what is expected (matches what is defined in + * the ACPI specification for this predefined name.) + * + ******************************************************************************/ + +void +acpi_ns_check_acpi_compliance(char *pathname, + struct acpi_namespace_node *node, + const union acpi_predefined_info *predefined) +{ + u32 aml_param_count; + u32 required_param_count; + + if (!predefined) { + return; + } + + /* Get the ACPI-required arg count from the predefined info table */ + + required_param_count = + METHOD_GET_ARG_COUNT(predefined->info.argument_list); + + /* + * If this object is not a control method, we can check if the ACPI + * spec requires that it be a method. + */ + if (node->type != ACPI_TYPE_METHOD) { + if (required_param_count > 0) { + + /* Object requires args, must be implemented as a method */ + + ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Object (%s) must be a control method with %u arguments", + acpi_ut_get_type_name(node-> + type), + required_param_count)); + } else if (!required_param_count + && !predefined->info.expected_btypes) { + + /* Object requires no args and no return value, must be a method */ + + ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Object (%s) must be a control method " + "with no arguments and no return value", + acpi_ut_get_type_name(node-> + type))); + } + + return; + } + + /* + * This is a control method. + * Check that the ASL/AML-defined parameter count for this method + * matches the ACPI-required parameter count + * + * Some methods are allowed to have a "minimum" number of args (_SCP) + * because their definition in ACPI has changed over time. + * + * Note: These are BIOS errors in the declaration of the object + */ + aml_param_count = node->object->method.param_count; + + if (aml_param_count < required_param_count) { + ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS, + "Insufficient arguments - " + "ASL declared %u, ACPI requires %u", + aml_param_count, + required_param_count)); + } else if ((aml_param_count > required_param_count) + && !(predefined->info. + argument_list & ARG_COUNT_IS_MINIMUM)) { + ACPI_BIOS_ERROR_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS, + "Excess arguments - " + "ASL declared %u, ACPI requires %u", + aml_param_count, + required_param_count)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_check_argument_count + * + * PARAMETERS: pathname - Full pathname to the node (for error msgs) + * node - Namespace node for the method/object + * user_param_count - Number of args passed in by the caller + * predefined - Pointer to entry in predefined name table + * + * RETURN: None + * + * DESCRIPTION: Check that incoming argument count matches the declared + * parameter count (in the ASL/AML) for an object. + * + ******************************************************************************/ + +void +acpi_ns_check_argument_count(char *pathname, + struct acpi_namespace_node *node, + u32 user_param_count, + const union acpi_predefined_info *predefined) +{ + u32 aml_param_count; + u32 required_param_count; + + if (!predefined) { + /* + * Not a predefined name. Check the incoming user argument count + * against the count that is specified in the method/object. + */ + if (node->type != ACPI_TYPE_METHOD) { + if (user_param_count) { + ACPI_INFO_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "%u arguments were passed to a non-method ACPI object (%s)", + user_param_count, + acpi_ut_get_type_name + (node->type))); + } + + return; + } + + /* + * This is a control method. Check the parameter count. + * We can only check the incoming argument count against the + * argument count declared for the method in the ASL/AML. + * + * Emit a message if too few or too many arguments have been passed + * by the caller. + * + * Note: Too many arguments will not cause the method to + * fail. However, the method will fail if there are too few + * arguments and the method attempts to use one of the missing ones. + */ + aml_param_count = node->object->method.param_count; + + if (user_param_count < aml_param_count) { + ACPI_WARN_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Insufficient arguments - " + "Caller passed %u, method requires %u", + user_param_count, + aml_param_count)); + } else if (user_param_count > aml_param_count) { + ACPI_INFO_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Excess arguments - " + "Caller passed %u, method requires %u", + user_param_count, + aml_param_count)); + } + + return; + } + + /* + * This is a predefined name. Validate the user-supplied parameter + * count against the ACPI specification. We don't validate against + * the method itself because what is important here is that the + * caller is in conformance with the spec. (The arg count for the + * method was checked against the ACPI spec earlier.) + * + * Some methods are allowed to have a "minimum" number of args (_SCP) + * because their definition in ACPI has changed over time. + */ + required_param_count = + METHOD_GET_ARG_COUNT(predefined->info.argument_list); + + if (user_param_count < required_param_count) { + ACPI_WARN_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS, + "Insufficient arguments - " + "Caller passed %u, ACPI requires %u", + user_param_count, required_param_count)); + } else if ((user_param_count > required_param_count) && + !(predefined->info.argument_list & ARG_COUNT_IS_MINIMUM)) { + ACPI_INFO_PREDEFINED((AE_INFO, pathname, ACPI_WARN_ALWAYS, + "Excess arguments - " + "Caller passed %u, ACPI requires %u", + user_param_count, required_param_count)); + } +} diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c index 8f79a9d2d50e..acd2964c2690 100644 --- a/drivers/acpi/acpica/nsconvert.c +++ b/drivers/acpi/acpica/nsconvert.c @@ -103,6 +103,7 @@ acpi_ns_convert_to_integer(union acpi_operand_object *original_object, break; default: + return (AE_AML_OPERAND_TYPE); } @@ -191,6 +192,7 @@ acpi_ns_convert_to_string(union acpi_operand_object *original_object, break; default: + return (AE_AML_OPERAND_TYPE); } @@ -294,6 +296,7 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object, break; default: + return (AE_AML_OPERAND_TYPE); } diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index ce6e97326205..7418c77fde8c 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -244,10 +244,12 @@ acpi_ns_dump_one_object(acpi_handle obj_handle, case ACPI_TYPE_BUFFER: case ACPI_TYPE_STRING: case ACPI_TYPE_METHOD: + acpi_os_printf("<No attached object>"); break; default: + break; } @@ -433,6 +435,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle, break; default: + break; } break; @@ -567,32 +570,39 @@ acpi_ns_dump_one_object(acpi_handle obj_handle, goto cleanup; case ACPI_TYPE_BUFFER_FIELD: + obj_desc = (union acpi_operand_object *)obj_desc->buffer_field. buffer_obj; break; case ACPI_TYPE_PACKAGE: + obj_desc = (void *)obj_desc->package.elements; break; case ACPI_TYPE_METHOD: + obj_desc = (void *)obj_desc->method.aml_start; break; case ACPI_TYPE_LOCAL_REGION_FIELD: + obj_desc = (void *)obj_desc->field.region_obj; break; case ACPI_TYPE_LOCAL_BANK_FIELD: + obj_desc = (void *)obj_desc->bank_field.region_obj; break; case ACPI_TYPE_LOCAL_INDEX_FIELD: + obj_desc = (void *)obj_desc->index_field.index_obj; break; default: + goto cleanup; } diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index b61db69d5675..18108bc2e51c 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -61,7 +61,7 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj, * * PARAMETERS: info - Evaluation info block, contains: * prefix_node - Prefix or Method/Object Node to execute - * pathname - Name of method to execute, If NULL, the + * relative_path - Name of method to execute, If NULL, the * Node is the object to execute * parameters - List of parameters to pass to the method, * terminated by NULL. Params itself may be @@ -82,10 +82,9 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj, * ******************************************************************************/ -acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) +acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info) { acpi_status status; - struct acpi_namespace_node *node; ACPI_FUNCTION_TRACE(ns_evaluate); @@ -93,83 +92,138 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) return_ACPI_STATUS(AE_BAD_PARAMETER); } - /* Initialize the return value to an invalid object */ - - info->return_object = NULL; - info->param_count = 0; - - if (!info->resolved_node) { + if (!info->node) { /* - * Get the actual namespace node for the target object if we need to. - * Handles these cases: + * Get the actual namespace node for the target object if we + * need to. Handles these cases: * - * 1) Null node, Pathname (absolute path) - * 2) Node, Pathname (path relative to Node) - * 3) Node, Null Pathname + * 1) Null node, valid pathname from root (absolute path) + * 2) Node and valid pathname (path relative to Node) + * 3) Node, Null pathname */ - status = acpi_ns_get_node(info->prefix_node, info->pathname, - ACPI_NS_NO_UPSEARCH, - &info->resolved_node); + status = + acpi_ns_get_node(info->prefix_node, info->relative_pathname, + ACPI_NS_NO_UPSEARCH, &info->node); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } } /* - * For a method alias, we must grab the actual method node so that proper - * scoping context will be established before execution. + * For a method alias, we must grab the actual method node so that + * proper scoping context will be established before execution. */ - if (acpi_ns_get_type(info->resolved_node) == - ACPI_TYPE_LOCAL_METHOD_ALIAS) { - info->resolved_node = + if (acpi_ns_get_type(info->node) == ACPI_TYPE_LOCAL_METHOD_ALIAS) { + info->node = ACPI_CAST_PTR(struct acpi_namespace_node, - info->resolved_node->object); + info->node->object); + } + + /* Complete the info block initialization */ + + info->return_object = NULL; + info->node_flags = info->node->flags; + info->obj_desc = acpi_ns_get_attached_object(info->node); + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "%s [%p] Value %p\n", + info->relative_pathname, info->node, + acpi_ns_get_attached_object(info->node))); + + /* Get info if we have a predefined name (_HID, etc.) */ + + info->predefined = + acpi_ut_match_predefined_method(info->node->name.ascii); + + /* Get the full pathname to the object, for use in warning messages */ + + info->full_pathname = acpi_ns_get_external_pathname(info->node); + if (!info->full_pathname) { + return_ACPI_STATUS(AE_NO_MEMORY); } - ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "%s [%p] Value %p\n", info->pathname, - info->resolved_node, - acpi_ns_get_attached_object(info->resolved_node))); + /* Count the number of arguments being passed in */ + + info->param_count = 0; + if (info->parameters) { + while (info->parameters[info->param_count]) { + info->param_count++; + } + + /* Warn on impossible argument count */ + + if (info->param_count > ACPI_METHOD_NUM_ARGS) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + ACPI_WARN_ALWAYS, + "Excess arguments (%u) - using only %u", + info->param_count, + ACPI_METHOD_NUM_ARGS)); + + info->param_count = ACPI_METHOD_NUM_ARGS; + } + } + + /* + * For predefined names: Check that the declared argument count + * matches the ACPI spec -- otherwise this is a BIOS error. + */ + acpi_ns_check_acpi_compliance(info->full_pathname, info->node, + info->predefined); + + /* + * For all names: Check that the incoming argument count for + * this method/object matches the actual ASL/AML definition. + */ + acpi_ns_check_argument_count(info->full_pathname, info->node, + info->param_count, info->predefined); - node = info->resolved_node; + /* For predefined names: Typecheck all incoming arguments */ + + acpi_ns_check_argument_types(info); /* - * Two major cases here: + * Three major evaluation cases: * - * 1) The object is a control method -- execute it - * 2) The object is not a method -- just return it's current value + * 1) Object types that cannot be evaluated by definition + * 2) The object is a control method -- execute it + * 3) The object is not a method -- just return it's current value */ - if (acpi_ns_get_type(info->resolved_node) == ACPI_TYPE_METHOD) { + switch (acpi_ns_get_type(info->node)) { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_EVENT: + case ACPI_TYPE_MUTEX: + case ACPI_TYPE_REGION: + case ACPI_TYPE_THERMAL: + case ACPI_TYPE_LOCAL_SCOPE: + /* + * 1) Disallow evaluation of certain object types. For these, + * object evaluation is undefined and not supported. + */ + ACPI_ERROR((AE_INFO, + "%s: Evaluation of object type [%s] is not supported", + info->full_pathname, + acpi_ut_get_type_name(info->node->type))); + + status = AE_TYPE; + goto cleanup; + + case ACPI_TYPE_METHOD: /* - * 1) Object is a control method - execute it + * 2) Object is a control method - execute it */ /* Verify that there is a method object associated with this node */ - info->obj_desc = - acpi_ns_get_attached_object(info->resolved_node); if (!info->obj_desc) { ACPI_ERROR((AE_INFO, - "Control method has no attached sub-object")); - return_ACPI_STATUS(AE_NULL_OBJECT); + "%s: Method has no attached sub-object", + info->full_pathname)); + status = AE_NULL_OBJECT; + goto cleanup; } - /* Count the number of arguments being passed to the method */ - - if (info->parameters) { - while (info->parameters[info->param_count]) { - if (info->param_count > ACPI_METHOD_MAX_ARG) { - return_ACPI_STATUS(AE_LIMIT); - } - info->param_count++; - } - } - - - ACPI_DUMP_PATHNAME(info->resolved_node, "ACPI: Execute Method", - ACPI_LV_INFO, _COMPONENT); - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "Method at AML address %p Length %X\n", + "**** Execute method [%s] at AML address %p length %X\n", + info->full_pathname, info->obj_desc->method.aml_start + 1, info->obj_desc->method.aml_length - 1)); @@ -184,81 +238,61 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) acpi_ex_enter_interpreter(); status = acpi_ps_execute_method(info); acpi_ex_exit_interpreter(); - } else { + break; + + default: /* - * 2) Object is not a method, return its current value - * - * Disallow certain object types. For these, "evaluation" is undefined. + * 3) All other non-method objects -- get the current object value */ - switch (info->resolved_node->type) { - case ACPI_TYPE_DEVICE: - case ACPI_TYPE_EVENT: - case ACPI_TYPE_MUTEX: - case ACPI_TYPE_REGION: - case ACPI_TYPE_THERMAL: - case ACPI_TYPE_LOCAL_SCOPE: - - ACPI_ERROR((AE_INFO, - "[%4.4s] Evaluation of object type [%s] is not supported", - info->resolved_node->name.ascii, - acpi_ut_get_type_name(info->resolved_node-> - type))); - - return_ACPI_STATUS(AE_TYPE); - - default: - break; - } /* - * Objects require additional resolution steps (e.g., the Node may be - * a field that must be read, etc.) -- we can't just grab the object - * out of the node. + * Some objects require additional resolution steps (e.g., the Node + * may be a field that must be read, etc.) -- we can't just grab + * the object out of the node. * * Use resolve_node_to_value() to get the associated value. * * NOTE: we can get away with passing in NULL for a walk state because - * resolved_node is guaranteed to not be a reference to either a method + * the Node is guaranteed to not be a reference to either a method * local or a method argument (because this interface is never called * from a running method.) * * Even though we do not directly invoke the interpreter for object - * resolution, we must lock it because we could access an opregion. - * The opregion access code assumes that the interpreter is locked. + * resolution, we must lock it because we could access an op_region. + * The op_region access code assumes that the interpreter is locked. */ acpi_ex_enter_interpreter(); - /* Function has a strange interface */ + /* TBD: resolve_node_to_value has a strange interface, fix */ + + info->return_object = + ACPI_CAST_PTR(union acpi_operand_object, info->node); status = - acpi_ex_resolve_node_to_value(&info->resolved_node, NULL); + acpi_ex_resolve_node_to_value(ACPI_CAST_INDIRECT_PTR + (struct acpi_namespace_node, + &info->return_object), NULL); acpi_ex_exit_interpreter(); - /* - * If acpi_ex_resolve_node_to_value() succeeded, the return value was placed - * in resolved_node. - */ - if (ACPI_SUCCESS(status)) { - status = AE_CTRL_RETURN_VALUE; - info->return_object = - ACPI_CAST_PTR(union acpi_operand_object, - info->resolved_node); - - ACPI_DEBUG_PRINT((ACPI_DB_NAMES, - "Returning object %p [%s]\n", - info->return_object, - acpi_ut_get_object_type_name(info-> - return_object))); + if (ACPI_FAILURE(status)) { + goto cleanup; } + + ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "Returned object %p [%s]\n", + info->return_object, + acpi_ut_get_object_type_name(info-> + return_object))); + + status = AE_CTRL_RETURN_VALUE; /* Always has a "return value" */ + break; } /* - * Check input argument count against the ASL-defined count for a method. - * Also check predefined names: argument count and return value against - * the ACPI specification. Some incorrect return value types are repaired. + * For predefined names, check the return value against the ACPI + * specification. Some incorrect return value types are repaired. */ - (void)acpi_ns_check_predefined_names(node, info->param_count, - status, &info->return_object); + (void)acpi_ns_check_return_value(info->node, info, info->param_count, + status, &info->return_object); /* Check if there is a return value that must be dealt with */ @@ -278,12 +312,15 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info) ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "*** Completed evaluation of object %s ***\n", - info->pathname)); + info->relative_pathname)); + cleanup: /* * Namespace was unlocked by the handling acpi_ns* function, so we - * just return + * just free the pathname and return */ + ACPI_FREE(info->full_pathname); + info->full_pathname = NULL; return_ACPI_STATUS(status); } diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c index 46f0f83417a1..dd2ceae3f717 100644 --- a/drivers/acpi/acpica/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -176,7 +176,7 @@ acpi_status acpi_ns_initialize_devices(void) * part of the ACPI specification. */ info.evaluate_info->prefix_node = acpi_gbl_root_node; - info.evaluate_info->pathname = METHOD_NAME__INI; + info.evaluate_info->relative_pathname = METHOD_NAME__INI; info.evaluate_info->parameters = NULL; info.evaluate_info->flags = ACPI_IGNORE_RETURN_VALUE; @@ -266,28 +266,34 @@ acpi_ns_init_one_object(acpi_handle obj_handle, switch (type) { case ACPI_TYPE_REGION: + info->op_region_count++; break; case ACPI_TYPE_BUFFER_FIELD: + info->field_count++; break; case ACPI_TYPE_LOCAL_BANK_FIELD: + info->field_count++; break; case ACPI_TYPE_BUFFER: + info->buffer_count++; break; case ACPI_TYPE_PACKAGE: + info->package_count++; break; default: /* No init required, just exit now */ + return (AE_OK); } @@ -337,7 +343,9 @@ acpi_ns_init_one_object(acpi_handle obj_handle, break; default: + /* No other types can get here */ + break; } @@ -416,6 +424,7 @@ acpi_ns_find_ini_methods(acpi_handle obj_handle, break; default: + break; } @@ -560,7 +569,7 @@ acpi_ns_init_one_device(acpi_handle obj_handle, ACPI_MEMSET(info, 0, sizeof(struct acpi_evaluate_info)); info->prefix_node = device_node; - info->pathname = METHOD_NAME__INI; + info->relative_pathname = METHOD_NAME__INI; info->parameters = NULL; info->flags = ACPI_IGNORE_RETURN_VALUE; @@ -574,8 +583,7 @@ acpi_ns_init_one_device(acpi_handle obj_handle, /* Ignore error and move on to next device */ - char *scope_name = - acpi_ns_get_external_pathname(info->resolved_node); + char *scope_name = acpi_ns_get_external_pathname(info->node); ACPI_EXCEPTION((AE_INFO, status, "during %s._INI execution", scope_name)); diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 8a52916148cb..24b71a01bf93 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -61,28 +61,29 @@ ACPI_MODULE_NAME("nspredef") * There are several areas that are validated: * * 1) The number of input arguments as defined by the method/object in the - * ASL is validated against the ACPI specification. + * ASL is validated against the ACPI specification. * 2) The type of the return object (if any) is validated against the ACPI - * specification. + * specification. * 3) For returned package objects, the count of package elements is - * validated, as well as the type of each package element. Nested - * packages are supported. + * validated, as well as the type of each package element. Nested + * packages are supported. * * For any problems found, a warning message is issued. * ******************************************************************************/ /* Local prototypes */ static acpi_status -acpi_ns_check_reference(struct acpi_predefined_data *data, +acpi_ns_check_reference(struct acpi_evaluate_info *info, union acpi_operand_object *return_object); static u32 acpi_ns_get_bitmapped_type(union acpi_operand_object *return_object); /******************************************************************************* * - * FUNCTION: acpi_ns_check_predefined_names + * FUNCTION: acpi_ns_check_return_value * * PARAMETERS: node - Namespace node for the method/object + * info - Method execution information block * user_param_count - Number of parameters actually passed * return_status - Status from the object evaluation * return_object_ptr - Pointer to the object returned from the @@ -90,44 +91,25 @@ static u32 acpi_ns_get_bitmapped_type(union acpi_operand_object *return_object); * * RETURN: Status * - * DESCRIPTION: Check an ACPI name for a match in the predefined name list. + * DESCRIPTION: Check the value returned from a predefined name. * ******************************************************************************/ acpi_status -acpi_ns_check_predefined_names(struct acpi_namespace_node *node, - u32 user_param_count, - acpi_status return_status, - union acpi_operand_object **return_object_ptr) +acpi_ns_check_return_value(struct acpi_namespace_node *node, + struct acpi_evaluate_info *info, + u32 user_param_count, + acpi_status return_status, + union acpi_operand_object **return_object_ptr) { - acpi_status status = AE_OK; + acpi_status status; const union acpi_predefined_info *predefined; - char *pathname; - struct acpi_predefined_data *data; - - /* Match the name for this method/object against the predefined list */ - - predefined = acpi_ut_match_predefined_method(node->name.ascii); - - /* Get the full pathname to the object, for use in warning messages */ - - pathname = acpi_ns_get_external_pathname(node); - if (!pathname) { - return (AE_OK); /* Could not get pathname, ignore */ - } - - /* - * Check that the parameter count for this method matches the ASL - * definition. For predefined names, ensure that both the caller and - * the method itself are in accordance with the ACPI specification. - */ - acpi_ns_check_parameter_count(pathname, node, user_param_count, - predefined); /* If not a predefined name, we cannot validate the return object */ + predefined = info->predefined; if (!predefined) { - goto cleanup; + return (AE_OK); } /* @@ -135,7 +117,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, * validate the return object */ if ((return_status != AE_OK) && (return_status != AE_CTRL_RETURN_VALUE)) { - goto cleanup; + return (AE_OK); } /* @@ -154,25 +136,14 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, if (acpi_gbl_disable_auto_repair || (!predefined->info.expected_btypes) || (predefined->info.expected_btypes == ACPI_RTYPE_ALL)) { - goto cleanup; - } - - /* Create the parameter data block for object validation */ - - data = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_predefined_data)); - if (!data) { - goto cleanup; + return (AE_OK); } - data->predefined = predefined; - data->node = node; - data->node_flags = node->flags; - data->pathname = pathname; /* * Check that the type of the main return object is what is expected * for this predefined name */ - status = acpi_ns_check_object_type(data, return_object_ptr, + status = acpi_ns_check_object_type(info, return_object_ptr, predefined->info.expected_btypes, ACPI_NOT_PACKAGE_ELEMENT); if (ACPI_FAILURE(status)) { @@ -184,10 +155,16 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, * Note: Package may have been newly created by call above. */ if ((*return_object_ptr)->common.type == ACPI_TYPE_PACKAGE) { - data->parent_package = *return_object_ptr; - status = acpi_ns_check_package(data, return_object_ptr); + info->parent_package = *return_object_ptr; + status = acpi_ns_check_package(info, return_object_ptr); if (ACPI_FAILURE(status)) { - goto exit; + + /* We might be able to fix some errors */ + + if ((status != AE_AML_OPERAND_TYPE) && + (status != AE_AML_OPERAND_VALUE)) { + goto exit; + } } } @@ -199,7 +176,7 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, * performed on a per-name basis, i.e., the code is specific to * particular predefined names. */ - status = acpi_ns_complex_repairs(data, node, status, return_object_ptr); + status = acpi_ns_complex_repairs(info, node, status, return_object_ptr); exit: /* @@ -207,112 +184,18 @@ exit: * or more objects, mark the parent node to suppress further warning * messages during the next evaluation of the same method/object. */ - if (ACPI_FAILURE(status) || (data->flags & ACPI_OBJECT_REPAIRED)) { + if (ACPI_FAILURE(status) || (info->return_flags & ACPI_OBJECT_REPAIRED)) { node->flags |= ANOBJ_EVALUATED; } - ACPI_FREE(data); -cleanup: - ACPI_FREE(pathname); return (status); } /******************************************************************************* * - * FUNCTION: acpi_ns_check_parameter_count - * - * PARAMETERS: pathname - Full pathname to the node (for error msgs) - * node - Namespace node for the method/object - * user_param_count - Number of args passed in by the caller - * predefined - Pointer to entry in predefined name table - * - * RETURN: None - * - * DESCRIPTION: Check that the declared (in ASL/AML) parameter count for a - * predefined name is what is expected (i.e., what is defined in - * the ACPI specification for this predefined name.) - * - ******************************************************************************/ - -void -acpi_ns_check_parameter_count(char *pathname, - struct acpi_namespace_node *node, - u32 user_param_count, - const union acpi_predefined_info *predefined) -{ - u32 param_count; - u32 required_params_current; - u32 required_params_old; - - /* Methods have 0-7 parameters. All other types have zero. */ - - param_count = 0; - if (node->type == ACPI_TYPE_METHOD) { - param_count = node->object->method.param_count; - } - - if (!predefined) { - /* - * Check the parameter count for non-predefined methods/objects. - * - * Warning if too few or too many arguments have been passed by the - * caller. An incorrect number of arguments may not cause the method - * to fail. However, the method will fail if there are too few - * arguments and the method attempts to use one of the missing ones. - */ - if (user_param_count < param_count) { - ACPI_WARN_PREDEFINED((AE_INFO, pathname, - ACPI_WARN_ALWAYS, - "Insufficient arguments - needs %u, found %u", - param_count, user_param_count)); - } else if (user_param_count > param_count) { - ACPI_WARN_PREDEFINED((AE_INFO, pathname, - ACPI_WARN_ALWAYS, - "Excess arguments - needs %u, found %u", - param_count, user_param_count)); - } - return; - } - - /* - * Validate the user-supplied parameter count. - * Allow two different legal argument counts (_SCP, etc.) - */ - required_params_current = - predefined->info.argument_list & METHOD_ARG_MASK; - required_params_old = - predefined->info.argument_list >> METHOD_ARG_BIT_WIDTH; - - if (user_param_count != ACPI_UINT32_MAX) { - if ((user_param_count != required_params_current) && - (user_param_count != required_params_old)) { - ACPI_WARN_PREDEFINED((AE_INFO, pathname, - ACPI_WARN_ALWAYS, - "Parameter count mismatch - " - "caller passed %u, ACPI requires %u", - user_param_count, - required_params_current)); - } - } - - /* - * Check that the ASL-defined parameter count is what is expected for - * this predefined name (parameter count as defined by the ACPI - * specification) - */ - if ((param_count != required_params_current) && - (param_count != required_params_old)) { - ACPI_WARN_PREDEFINED((AE_INFO, pathname, node->flags, - "Parameter count mismatch - ASL declared %u, ACPI requires %u", - param_count, required_params_current)); - } -} - -/******************************************************************************* - * * FUNCTION: acpi_ns_check_object_type * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * expected_btypes - Bitmap of expected return type(s) @@ -328,7 +211,7 @@ acpi_ns_check_parameter_count(char *pathname, ******************************************************************************/ acpi_status -acpi_ns_check_object_type(struct acpi_predefined_data *data, +acpi_ns_check_object_type(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr, u32 expected_btypes, u32 package_index) { @@ -340,7 +223,8 @@ acpi_ns_check_object_type(struct acpi_predefined_data *data, if (return_object && ACPI_GET_DESCRIPTOR_TYPE(return_object) == ACPI_DESC_TYPE_NAMED) { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, "Invalid return type - Found a Namespace node [%4.4s] type %s", return_object->node.name.ascii, acpi_ut_get_type_name(return_object->node. @@ -356,8 +240,8 @@ acpi_ns_check_object_type(struct acpi_predefined_data *data, * from all of the predefined names (including elements of returned * packages) */ - data->return_btype = acpi_ns_get_bitmapped_type(return_object); - if (data->return_btype == ACPI_RTYPE_ANY) { + info->return_btype = acpi_ns_get_bitmapped_type(return_object); + if (info->return_btype == ACPI_RTYPE_ANY) { /* Not one of the supported objects, must be incorrect */ goto type_error_exit; @@ -365,16 +249,18 @@ acpi_ns_check_object_type(struct acpi_predefined_data *data, /* For reference objects, check that the reference type is correct */ - if ((data->return_btype & expected_btypes) == ACPI_RTYPE_REFERENCE) { - status = acpi_ns_check_reference(data, return_object); + if ((info->return_btype & expected_btypes) == ACPI_RTYPE_REFERENCE) { + status = acpi_ns_check_reference(info, return_object); return (status); } /* Attempt simple repair of the returned object if necessary */ - status = acpi_ns_simple_repair(data, expected_btypes, + status = acpi_ns_simple_repair(info, expected_btypes, package_index, return_object_ptr); - return (status); + if (ACPI_SUCCESS(status)) { + return (AE_OK); /* Successful repair */ + } type_error_exit: @@ -383,12 +269,14 @@ acpi_ns_check_object_type(struct acpi_predefined_data *data, acpi_ut_get_expected_return_types(type_buffer, expected_btypes); if (package_index == ACPI_NOT_PACKAGE_ELEMENT) { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, "Return type mismatch - found %s, expected %s", acpi_ut_get_object_type_name (return_object), type_buffer)); } else { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, "Return Package type mismatch at index %u - " "found %s, expected %s", package_index, acpi_ut_get_object_type_name @@ -402,7 +290,7 @@ acpi_ns_check_object_type(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_check_reference * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object - Object returned from the evaluation of a * method or object * @@ -415,7 +303,7 @@ acpi_ns_check_object_type(struct acpi_predefined_data *data, ******************************************************************************/ static acpi_status -acpi_ns_check_reference(struct acpi_predefined_data *data, +acpi_ns_check_reference(struct acpi_evaluate_info *info, union acpi_operand_object *return_object) { @@ -428,7 +316,7 @@ acpi_ns_check_reference(struct acpi_predefined_data *data, return (AE_OK); } - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, "Return type mismatch - unexpected reference object type [%s] %2.2X", acpi_ut_get_reference_name(return_object), return_object->reference.class)); @@ -462,26 +350,32 @@ static u32 acpi_ns_get_bitmapped_type(union acpi_operand_object *return_object) switch (return_object->common.type) { case ACPI_TYPE_INTEGER: + return_btype = ACPI_RTYPE_INTEGER; break; case ACPI_TYPE_BUFFER: + return_btype = ACPI_RTYPE_BUFFER; break; case ACPI_TYPE_STRING: + return_btype = ACPI_RTYPE_STRING; break; case ACPI_TYPE_PACKAGE: + return_btype = ACPI_RTYPE_PACKAGE; break; case ACPI_TYPE_LOCAL_REFERENCE: + return_btype = ACPI_RTYPE_REFERENCE; break; default: + /* Not one of the supported objects, must be incorrect */ return_btype = ACPI_RTYPE_ANY; diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c index 77cdd539de16..6d55cef7916c 100644 --- a/drivers/acpi/acpica/nsprepkg.c +++ b/drivers/acpi/acpica/nsprepkg.c @@ -51,12 +51,12 @@ ACPI_MODULE_NAME("nsprepkg") /* Local prototypes */ static acpi_status -acpi_ns_check_package_list(struct acpi_predefined_data *data, +acpi_ns_check_package_list(struct acpi_evaluate_info *info, const union acpi_predefined_info *package, union acpi_operand_object **elements, u32 count); static acpi_status -acpi_ns_check_package_elements(struct acpi_predefined_data *data, +acpi_ns_check_package_elements(struct acpi_evaluate_info *info, union acpi_operand_object **elements, u8 type1, u32 count1, @@ -66,7 +66,7 @@ acpi_ns_check_package_elements(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_check_package * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * @@ -78,7 +78,7 @@ acpi_ns_check_package_elements(struct acpi_predefined_data *data, ******************************************************************************/ acpi_status -acpi_ns_check_package(struct acpi_predefined_data *data, +acpi_ns_check_package(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr) { union acpi_operand_object *return_object = *return_object_ptr; @@ -93,18 +93,18 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* The package info for this name is in the next table entry */ - package = data->predefined + 1; + package = info->predefined + 1; ACPI_DEBUG_PRINT((ACPI_DB_NAMES, "%s Validating return Package of Type %X, Count %X\n", - data->pathname, package->ret_info.type, + info->full_pathname, package->ret_info.type, return_object->package.count)); /* * For variable-length Packages, we can safely remove all embedded * and trailing NULL package elements */ - acpi_ns_remove_null_elements(data, package->ret_info.type, + acpi_ns_remove_null_elements(info, package->ret_info.type, return_object); /* Extract package count and elements array */ @@ -121,7 +121,8 @@ acpi_ns_check_package(struct acpi_predefined_data *data, return (AE_OK); } - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, "Return Package has no elements (empty)")); return (AE_AML_OPERAND_VALUE); @@ -135,7 +136,6 @@ acpi_ns_check_package(struct acpi_predefined_data *data, */ switch (package->ret_info.type) { case ACPI_PTYPE1_FIXED: - /* * The package count is fixed and there are no sub-packages * @@ -150,13 +150,13 @@ acpi_ns_check_package(struct acpi_predefined_data *data, ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s: Return Package is larger than needed - " "found %u, expected %u\n", - data->pathname, count, + info->full_pathname, count, expected_count)); } /* Validate all elements of the returned package */ - status = acpi_ns_check_package_elements(data, elements, + status = acpi_ns_check_package_elements(info, elements, package->ret_info. object_type1, package->ret_info. @@ -168,13 +168,12 @@ acpi_ns_check_package(struct acpi_predefined_data *data, break; case ACPI_PTYPE1_VAR: - /* * The package count is variable, there are no sub-packages, and all * elements must be of the same type */ for (i = 0; i < count; i++) { - status = acpi_ns_check_object_type(data, elements, + status = acpi_ns_check_object_type(info, elements, package->ret_info. object_type1, i); if (ACPI_FAILURE(status)) { @@ -185,7 +184,6 @@ acpi_ns_check_package(struct acpi_predefined_data *data, break; case ACPI_PTYPE1_OPTION: - /* * The package count is variable, there are no sub-packages. There are * a fixed number of required elements, and a variable number of @@ -206,7 +204,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* These are the required package elements (0, 1, or 2) */ status = - acpi_ns_check_object_type(data, elements, + acpi_ns_check_object_type(info, elements, package-> ret_info3. object_type[i], @@ -218,7 +216,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* These are the optional package elements */ status = - acpi_ns_check_object_type(data, elements, + acpi_ns_check_object_type(info, elements, package-> ret_info3. tail_object_type, @@ -235,7 +233,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* First element is the (Integer) revision */ - status = acpi_ns_check_object_type(data, elements, + status = acpi_ns_check_object_type(info, elements, ACPI_RTYPE_INTEGER, 0); if (ACPI_FAILURE(status)) { return (status); @@ -247,14 +245,14 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* Examine the sub-packages */ status = - acpi_ns_check_package_list(data, package, elements, count); + acpi_ns_check_package_list(info, package, elements, count); break; case ACPI_PTYPE2_PKG_COUNT: /* First element is the (Integer) count of sub-packages to follow */ - status = acpi_ns_check_object_type(data, elements, + status = acpi_ns_check_object_type(info, elements, ACPI_RTYPE_INTEGER, 0); if (ACPI_FAILURE(status)) { return (status); @@ -275,7 +273,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* Examine the sub-packages */ status = - acpi_ns_check_package_list(data, package, elements, count); + acpi_ns_check_package_list(info, package, elements, count); break; case ACPI_PTYPE2: @@ -283,7 +281,6 @@ acpi_ns_check_package(struct acpi_predefined_data *data, case ACPI_PTYPE2_MIN: case ACPI_PTYPE2_COUNT: case ACPI_PTYPE2_FIX_VAR: - /* * These types all return a single Package that consists of a * variable number of sub-Packages. @@ -300,7 +297,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* Create the new outer package and populate it */ status = - acpi_ns_wrap_with_package(data, return_object, + acpi_ns_wrap_with_package(info, return_object, return_object_ptr); if (ACPI_FAILURE(status)) { return (status); @@ -316,14 +313,15 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* Examine the sub-packages */ status = - acpi_ns_check_package_list(data, package, elements, count); + acpi_ns_check_package_list(info, package, elements, count); break; default: /* Should not get here if predefined info table is correct */ - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, "Invalid internal return type in table entry: %X", package->ret_info.type)); @@ -336,7 +334,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, /* Error exit for the case with an incorrect package count */ - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, "Return Package is too small - found %u elements, expected %u", count, expected_count)); @@ -347,7 +345,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_check_package_list * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * package - Pointer to package-specific info for method * elements - Element list of parent package. All elements * of this list should be of type Package. @@ -360,7 +358,7 @@ acpi_ns_check_package(struct acpi_predefined_data *data, ******************************************************************************/ static acpi_status -acpi_ns_check_package_list(struct acpi_predefined_data *data, +acpi_ns_check_package_list(struct acpi_evaluate_info *info, const union acpi_predefined_info *package, union acpi_operand_object **elements, u32 count) { @@ -381,11 +379,11 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, for (i = 0; i < count; i++) { sub_package = *elements; sub_elements = sub_package->package.elements; - data->parent_package = sub_package; + info->parent_package = sub_package; /* Each sub-object must be of type Package */ - status = acpi_ns_check_object_type(data, &sub_package, + status = acpi_ns_check_object_type(info, &sub_package, ACPI_RTYPE_PACKAGE, i); if (ACPI_FAILURE(status)) { return (status); @@ -393,7 +391,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, /* Examine the different types of expected sub-packages */ - data->parent_package = sub_package; + info->parent_package = sub_package; switch (package->ret_info.type) { case ACPI_PTYPE2: case ACPI_PTYPE2_PKG_COUNT: @@ -408,7 +406,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, } status = - acpi_ns_check_package_elements(data, sub_elements, + acpi_ns_check_package_elements(info, sub_elements, package->ret_info. object_type1, package->ret_info. @@ -434,7 +432,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, } status = - acpi_ns_check_package_elements(data, sub_elements, + acpi_ns_check_package_elements(info, sub_elements, package->ret_info. object_type1, package->ret_info. @@ -463,7 +461,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, for (j = 0; j < expected_count; j++) { status = - acpi_ns_check_object_type(data, + acpi_ns_check_object_type(info, &sub_elements[j], package-> ret_info2. @@ -487,7 +485,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, /* Check the type of each sub-package element */ status = - acpi_ns_check_package_elements(data, sub_elements, + acpi_ns_check_package_elements(info, sub_elements, package->ret_info. object_type1, sub_package->package. @@ -498,12 +496,11 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, break; case ACPI_PTYPE2_COUNT: - /* * First element is the (Integer) count of elements, including * the count field (the ACPI name is num_elements) */ - status = acpi_ns_check_object_type(data, sub_elements, + status = acpi_ns_check_object_type(info, sub_elements, ACPI_RTYPE_INTEGER, 0); if (ACPI_FAILURE(status)) { @@ -537,7 +534,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, /* Check the type of each sub-package element */ status = - acpi_ns_check_package_elements(data, + acpi_ns_check_package_elements(info, (sub_elements + 1), package->ret_info. object_type1, @@ -562,7 +559,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, /* The sub-package count was smaller than required */ - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, info->node_flags, "Return Sub-Package[%u] is too small - found %u elements, expected %u", i, sub_package->package.count, expected_count)); @@ -573,7 +570,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_check_package_elements * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * elements - Pointer to the package elements array * type1 - Object type for first group * count1 - Count for first group @@ -589,7 +586,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, ******************************************************************************/ static acpi_status -acpi_ns_check_package_elements(struct acpi_predefined_data *data, +acpi_ns_check_package_elements(struct acpi_evaluate_info *info, union acpi_operand_object **elements, u8 type1, u32 count1, @@ -605,7 +602,7 @@ acpi_ns_check_package_elements(struct acpi_predefined_data *data, * The second group can have a count of zero. */ for (i = 0; i < count1; i++) { - status = acpi_ns_check_object_type(data, this_element, + status = acpi_ns_check_object_type(info, this_element, type1, i + start_index); if (ACPI_FAILURE(status)) { return (status); @@ -614,7 +611,7 @@ acpi_ns_check_package_elements(struct acpi_predefined_data *data, } for (i = 0; i < count2; i++) { - status = acpi_ns_check_object_type(data, this_element, + status = acpi_ns_check_object_type(info, this_element, type2, (i + count1 + start_index)); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index 18f02e4ece01..f8e71ea60319 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -130,7 +130,7 @@ static const struct acpi_simple_repair_info acpi_object_repair_info[] = { * * FUNCTION: acpi_ns_simple_repair * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * expected_btypes - Object types expected * package_index - Index of object within parent package (if * applicable - ACPI_NOT_PACKAGE_ELEMENT @@ -146,7 +146,7 @@ static const struct acpi_simple_repair_info acpi_object_repair_info[] = { ******************************************************************************/ acpi_status -acpi_ns_simple_repair(struct acpi_predefined_data *data, +acpi_ns_simple_repair(struct acpi_evaluate_info *info, u32 expected_btypes, u32 package_index, union acpi_operand_object **return_object_ptr) @@ -162,12 +162,12 @@ acpi_ns_simple_repair(struct acpi_predefined_data *data, * Special repairs for certain names that are in the repair table. * Check if this name is in the list of repairable names. */ - predefined = acpi_ns_match_simple_repair(data->node, - data->return_btype, + predefined = acpi_ns_match_simple_repair(info->node, + info->return_btype, package_index); if (predefined) { if (!return_object) { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, ACPI_WARN_ALWAYS, "Missing expected return value")); } @@ -191,7 +191,7 @@ acpi_ns_simple_repair(struct acpi_predefined_data *data, * Do not perform simple object repair unless the return type is not * expected. */ - if (data->return_btype & expected_btypes) { + if (info->return_btype & expected_btypes) { return (AE_OK); } @@ -211,7 +211,7 @@ acpi_ns_simple_repair(struct acpi_predefined_data *data, */ if (!return_object) { if (expected_btypes && (!(expected_btypes & ACPI_RTYPE_NONE))) { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, ACPI_WARN_ALWAYS, "Missing expected return value")); @@ -247,14 +247,14 @@ acpi_ns_simple_repair(struct acpi_predefined_data *data, * for correct contents (expected object type or types). */ status = - acpi_ns_wrap_with_package(data, return_object, &new_object); + acpi_ns_wrap_with_package(info, return_object, &new_object); if (ACPI_SUCCESS(status)) { /* * The original object just had its reference count * incremented for being inserted into the new package. */ *return_object_ptr = new_object; /* New Package object */ - data->flags |= ACPI_OBJECT_REPAIRED; + info->return_flags |= ACPI_OBJECT_REPAIRED; return (AE_OK); } } @@ -277,7 +277,7 @@ acpi_ns_simple_repair(struct acpi_predefined_data *data, * package object as part of the repair, we don't need to * change the reference count. */ - if (!(data->flags & ACPI_OBJECT_WRAPPED)) { + if (!(info->return_flags & ACPI_OBJECT_WRAPPED)) { new_object->common.reference_count = return_object->common.reference_count; @@ -288,14 +288,14 @@ acpi_ns_simple_repair(struct acpi_predefined_data *data, ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s: Converted %s to expected %s at Package index %u\n", - data->pathname, + info->full_pathname, acpi_ut_get_object_type_name(return_object), acpi_ut_get_object_type_name(new_object), package_index)); } else { ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s: Converted %s to expected %s\n", - data->pathname, + info->full_pathname, acpi_ut_get_object_type_name(return_object), acpi_ut_get_object_type_name(new_object))); } @@ -304,7 +304,7 @@ acpi_ns_simple_repair(struct acpi_predefined_data *data, acpi_ut_remove_reference(return_object); *return_object_ptr = new_object; - data->flags |= ACPI_OBJECT_REPAIRED; + info->return_flags |= ACPI_OBJECT_REPAIRED; return (AE_OK); } @@ -359,7 +359,7 @@ static const struct acpi_simple_repair_info *acpi_ns_match_simple_repair(struct * * FUNCTION: acpi_ns_repair_null_element * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * expected_btypes - Object types expected * package_index - Index of object within parent package (if * applicable - ACPI_NOT_PACKAGE_ELEMENT @@ -374,7 +374,7 @@ static const struct acpi_simple_repair_info *acpi_ns_match_simple_repair(struct ******************************************************************************/ acpi_status -acpi_ns_repair_null_element(struct acpi_predefined_data *data, +acpi_ns_repair_null_element(struct acpi_evaluate_info * info, u32 expected_btypes, u32 package_index, union acpi_operand_object **return_object_ptr) @@ -424,16 +424,16 @@ acpi_ns_repair_null_element(struct acpi_predefined_data *data, /* Set the reference count according to the parent Package object */ new_object->common.reference_count = - data->parent_package->common.reference_count; + info->parent_package->common.reference_count; ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s: Converted NULL package element to expected %s at index %u\n", - data->pathname, + info->full_pathname, acpi_ut_get_object_type_name(new_object), package_index)); *return_object_ptr = new_object; - data->flags |= ACPI_OBJECT_REPAIRED; + info->return_flags |= ACPI_OBJECT_REPAIRED; return (AE_OK); } @@ -441,7 +441,7 @@ acpi_ns_repair_null_element(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_remove_null_elements * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * package_type - An acpi_return_package_types value * obj_desc - A Package object * @@ -454,7 +454,7 @@ acpi_ns_repair_null_element(struct acpi_predefined_data *data, *****************************************************************************/ void -acpi_ns_remove_null_elements(struct acpi_predefined_data *data, +acpi_ns_remove_null_elements(struct acpi_evaluate_info *info, u8 package_type, union acpi_operand_object *obj_desc) { @@ -480,6 +480,7 @@ acpi_ns_remove_null_elements(struct acpi_predefined_data *data, case ACPI_PTYPE2_MIN: case ACPI_PTYPE2_REV_FIXED: case ACPI_PTYPE2_FIX_VAR: + break; default: @@ -511,7 +512,7 @@ acpi_ns_remove_null_elements(struct acpi_predefined_data *data, if (new_count < count) { ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s: Found and removed %u NULL elements\n", - data->pathname, (count - new_count))); + info->full_pathname, (count - new_count))); /* NULL terminate list and update the package count */ @@ -524,7 +525,7 @@ acpi_ns_remove_null_elements(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_wrap_with_package * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * original_object - Pointer to the object to repair. * obj_desc_ptr - The new package object is returned here * @@ -545,7 +546,7 @@ acpi_ns_remove_null_elements(struct acpi_predefined_data *data, ******************************************************************************/ acpi_status -acpi_ns_wrap_with_package(struct acpi_predefined_data *data, +acpi_ns_wrap_with_package(struct acpi_evaluate_info *info, union acpi_operand_object *original_object, union acpi_operand_object **obj_desc_ptr) { @@ -566,12 +567,12 @@ acpi_ns_wrap_with_package(struct acpi_predefined_data *data, ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s: Wrapped %s with expected Package object\n", - data->pathname, + info->full_pathname, acpi_ut_get_object_type_name(original_object))); /* Return the new object in the object pointer */ *obj_desc_ptr = pkg_obj_desc; - data->flags |= ACPI_OBJECT_REPAIRED | ACPI_OBJECT_WRAPPED; + info->return_flags |= ACPI_OBJECT_REPAIRED | ACPI_OBJECT_WRAPPED; return (AE_OK); } diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 149e9b9c2c1b..c84603ee83ae 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -54,7 +54,7 @@ ACPI_MODULE_NAME("nsrepair2") * be repaired on a per-name basis. */ typedef -acpi_status(*acpi_repair_function) (struct acpi_predefined_data *data, +acpi_status(*acpi_repair_function) (struct acpi_evaluate_info * info, union acpi_operand_object **return_object_ptr); @@ -71,45 +71,57 @@ static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct *node); static acpi_status -acpi_ns_repair_ALR(struct acpi_predefined_data *data, +acpi_ns_repair_ALR(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr); static acpi_status -acpi_ns_repair_CID(struct acpi_predefined_data *data, +acpi_ns_repair_CID(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr); static acpi_status -acpi_ns_repair_FDE(struct acpi_predefined_data *data, +acpi_ns_repair_CST(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr); static acpi_status -acpi_ns_repair_HID(struct acpi_predefined_data *data, +acpi_ns_repair_FDE(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr); static acpi_status -acpi_ns_repair_PSS(struct acpi_predefined_data *data, +acpi_ns_repair_HID(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr); static acpi_status -acpi_ns_repair_TSS(struct acpi_predefined_data *data, +acpi_ns_repair_PRT(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr); static acpi_status -acpi_ns_check_sorted_list(struct acpi_predefined_data *data, +acpi_ns_repair_PSS(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_repair_TSS(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr); + +static acpi_status +acpi_ns_check_sorted_list(struct acpi_evaluate_info *info, union acpi_operand_object *return_object, + u32 start_index, u32 expected_count, u32 sort_index, u8 sort_direction, char *sort_key_name); -static void -acpi_ns_sort_list(union acpi_operand_object **elements, - u32 count, u32 index, u8 sort_direction); - /* Values for sort_direction above */ #define ACPI_SORT_ASCENDING 0 #define ACPI_SORT_DESCENDING 1 +static void +acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index); + +static void +acpi_ns_sort_list(union acpi_operand_object **elements, + u32 count, u32 index, u8 sort_direction); + /* * This table contains the names of the predefined methods for which we can * perform more complex repairs. @@ -118,9 +130,11 @@ acpi_ns_sort_list(union acpi_operand_object **elements, * * _ALR: Sort the list ascending by ambient_illuminance * _CID: Strings: uppercase all, remove any leading asterisk + * _CST: Sort the list ascending by C state type * _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs * _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs * _HID: Strings: uppercase all, remove any leading asterisk + * _PRT: Fix reversed source_name and source_index * _PSS: Sort the list descending by Power * _TSS: Sort the list descending by Power * @@ -134,9 +148,11 @@ acpi_ns_sort_list(union acpi_operand_object **elements, static const struct acpi_repair_info acpi_ns_repairable_names[] = { {"_ALR", acpi_ns_repair_ALR}, {"_CID", acpi_ns_repair_CID}, + {"_CST", acpi_ns_repair_CST}, {"_FDE", acpi_ns_repair_FDE}, {"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */ {"_HID", acpi_ns_repair_HID}, + {"_PRT", acpi_ns_repair_PRT}, {"_PSS", acpi_ns_repair_PSS}, {"_TSS", acpi_ns_repair_TSS}, {{0, 0, 0, 0}, NULL} /* Table terminator */ @@ -150,7 +166,7 @@ static const struct acpi_repair_info acpi_ns_repairable_names[] = { * * FUNCTION: acpi_ns_complex_repairs * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * node - Namespace node for the method/object * validate_status - Original status of earlier validation * return_object_ptr - Pointer to the object returned from the @@ -165,7 +181,7 @@ static const struct acpi_repair_info acpi_ns_repairable_names[] = { *****************************************************************************/ acpi_status -acpi_ns_complex_repairs(struct acpi_predefined_data *data, +acpi_ns_complex_repairs(struct acpi_evaluate_info *info, struct acpi_namespace_node *node, acpi_status validate_status, union acpi_operand_object **return_object_ptr) @@ -180,7 +196,7 @@ acpi_ns_complex_repairs(struct acpi_predefined_data *data, return (validate_status); } - status = predefined->repair_function(data, return_object_ptr); + status = predefined->repair_function(info, return_object_ptr); return (status); } @@ -219,7 +235,7 @@ static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct * * FUNCTION: acpi_ns_repair_ALR * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * @@ -231,13 +247,13 @@ static const struct acpi_repair_info *acpi_ns_match_complex_repair(struct *****************************************************************************/ static acpi_status -acpi_ns_repair_ALR(struct acpi_predefined_data *data, +acpi_ns_repair_ALR(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr) { union acpi_operand_object *return_object = *return_object_ptr; acpi_status status; - status = acpi_ns_check_sorted_list(data, return_object, 2, 1, + status = acpi_ns_check_sorted_list(info, return_object, 0, 2, 1, ACPI_SORT_ASCENDING, "AmbientIlluminance"); @@ -248,7 +264,7 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_repair_FDE * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * @@ -262,7 +278,7 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data, *****************************************************************************/ static acpi_status -acpi_ns_repair_FDE(struct acpi_predefined_data *data, +acpi_ns_repair_FDE(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr) { union acpi_operand_object *return_object = *return_object_ptr; @@ -285,8 +301,8 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, /* We can only repair if we have exactly 5 BYTEs */ if (return_object->buffer.length != ACPI_FDE_BYTE_BUFFER_SIZE) { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, - data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, "Incorrect return buffer length %u, expected %u", return_object->buffer.length, ACPI_FDE_DWORD_BUFFER_SIZE)); @@ -316,10 +332,11 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s Expanded Byte Buffer to expected DWord Buffer\n", - data->pathname)); + info->full_pathname)); break; default: + return (AE_AML_OPERAND_TYPE); } @@ -328,7 +345,7 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, acpi_ut_remove_reference(return_object); *return_object_ptr = buffer_object; - data->flags |= ACPI_OBJECT_REPAIRED; + info->return_flags |= ACPI_OBJECT_REPAIRED; return (AE_OK); } @@ -336,7 +353,7 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_repair_CID * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * @@ -349,7 +366,7 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, *****************************************************************************/ static acpi_status -acpi_ns_repair_CID(struct acpi_predefined_data *data, +acpi_ns_repair_CID(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr) { acpi_status status; @@ -362,7 +379,7 @@ acpi_ns_repair_CID(struct acpi_predefined_data *data, /* Check for _CID as a simple string */ if (return_object->common.type == ACPI_TYPE_STRING) { - status = acpi_ns_repair_HID(data, return_object_ptr); + status = acpi_ns_repair_HID(info, return_object_ptr); return (status); } @@ -379,7 +396,7 @@ acpi_ns_repair_CID(struct acpi_predefined_data *data, original_element = *element_ptr; original_ref_count = original_element->common.reference_count; - status = acpi_ns_repair_HID(data, element_ptr); + status = acpi_ns_repair_HID(info, element_ptr); if (ACPI_FAILURE(status)) { return (status); } @@ -404,9 +421,95 @@ acpi_ns_repair_CID(struct acpi_predefined_data *data, /****************************************************************************** * + * FUNCTION: acpi_ns_repair_CST + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _CST object: + * 1. Sort the list ascending by C state type + * 2. Ensure type cannot be zero + * 3. A sub-package count of zero means _CST is meaningless + * 4. Count must match the number of C state sub-packages + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_CST(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object **outer_elements; + u32 outer_element_count; + union acpi_operand_object *obj_desc; + acpi_status status; + u8 removing; + u32 i; + + ACPI_FUNCTION_NAME(ns_repair_CST); + + /* + * Check if the C-state type values are proportional. + */ + outer_element_count = return_object->package.count - 1; + i = 0; + while (i < outer_element_count) { + outer_elements = &return_object->package.elements[i + 1]; + removing = FALSE; + + if ((*outer_elements)->package.count == 0) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "SubPackage[%u] - removing entry due to zero count", + i)); + removing = TRUE; + goto remove_element; + } + + obj_desc = (*outer_elements)->package.elements[1]; /* Index1 = Type */ + if ((u32)obj_desc->integer.value == 0) { + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "SubPackage[%u] - removing entry due to invalid Type(0)", + i)); + removing = TRUE; + } + + remove_element: + if (removing) { + acpi_ns_remove_element(return_object, i + 1); + outer_element_count--; + } else { + i++; + } + } + + /* Update top-level package count, Type "Integer" checked elsewhere */ + + obj_desc = return_object->package.elements[0]; + obj_desc->integer.value = outer_element_count; + + /* + * Entries (subpackages) in the _CST Package must be sorted by the + * C-state type, in ascending order. + */ + status = acpi_ns_check_sorted_list(info, return_object, 1, 4, 1, + ACPI_SORT_ASCENDING, "C-State Type"); + if (ACPI_FAILURE(status)) { + return (status); + } + + return (AE_OK); +} + +/****************************************************************************** + * * FUNCTION: acpi_ns_repair_HID * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * @@ -418,7 +521,7 @@ acpi_ns_repair_CID(struct acpi_predefined_data *data, *****************************************************************************/ static acpi_status -acpi_ns_repair_HID(struct acpi_predefined_data *data, +acpi_ns_repair_HID(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr) { union acpi_operand_object *return_object = *return_object_ptr; @@ -435,12 +538,13 @@ acpi_ns_repair_HID(struct acpi_predefined_data *data, } if (return_object->string.length == 0) { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, "Invalid zero-length _HID or _CID string")); /* Return AE_OK anyway, let driver handle it */ - data->flags |= ACPI_OBJECT_REPAIRED; + info->return_flags |= ACPI_OBJECT_REPAIRED; return (AE_OK); } @@ -464,7 +568,7 @@ acpi_ns_repair_HID(struct acpi_predefined_data *data, ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s: Removed invalid leading asterisk\n", - data->pathname)); + info->full_pathname)); } /* @@ -486,53 +590,69 @@ acpi_ns_repair_HID(struct acpi_predefined_data *data, /****************************************************************************** * - * FUNCTION: acpi_ns_repair_TSS + * FUNCTION: acpi_ns_repair_PRT * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * * RETURN: Status. AE_OK if object is OK or was repaired successfully * - * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list - * descending by the power dissipation values. + * DESCRIPTION: Repair for the _PRT object. If necessary, fix reversed + * source_name and source_index field, a common BIOS bug. * *****************************************************************************/ static acpi_status -acpi_ns_repair_TSS(struct acpi_predefined_data *data, +acpi_ns_repair_PRT(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr) { - union acpi_operand_object *return_object = *return_object_ptr; - acpi_status status; - struct acpi_namespace_node *node; + union acpi_operand_object *package_object = *return_object_ptr; + union acpi_operand_object **top_object_list; + union acpi_operand_object **sub_object_list; + union acpi_operand_object *obj_desc; + u32 element_count; + u32 index; - /* - * We can only sort the _TSS return package if there is no _PSS in the - * same scope. This is because if _PSS is present, the ACPI specification - * dictates that the _TSS Power Dissipation field is to be ignored, and - * therefore some BIOSs leave garbage values in the _TSS Power field(s). - * In this case, it is best to just return the _TSS package as-is. - * (May, 2011) - */ - status = - acpi_ns_get_node(data->node, "^_PSS", ACPI_NS_NO_UPSEARCH, &node); - if (ACPI_SUCCESS(status)) { - return (AE_OK); - } + /* Each element in the _PRT package is a subpackage */ - status = acpi_ns_check_sorted_list(data, return_object, 5, 1, - ACPI_SORT_DESCENDING, - "PowerDissipation"); + top_object_list = package_object->package.elements; + element_count = package_object->package.count; - return (status); + for (index = 0; index < element_count; index++) { + sub_object_list = (*top_object_list)->package.elements; + + /* + * If the BIOS has erroneously reversed the _PRT source_name (index 2) + * and the source_index (index 3), fix it. _PRT is important enough to + * workaround this BIOS error. This also provides compatibility with + * other ACPI implementations. + */ + obj_desc = sub_object_list[3]; + if (!obj_desc || (obj_desc->common.type != ACPI_TYPE_INTEGER)) { + sub_object_list[3] = sub_object_list[2]; + sub_object_list[2] = obj_desc; + info->return_flags |= ACPI_OBJECT_REPAIRED; + + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, + "PRT[%X]: Fixed reversed SourceName and SourceIndex", + index)); + } + + /* Point to the next union acpi_operand_object in the top level package */ + + top_object_list++; + } + + return (AE_OK); } /****************************************************************************** * * FUNCTION: acpi_ns_repair_PSS * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object_ptr - Pointer to the object returned from the * evaluation of a method or object * @@ -546,7 +666,7 @@ acpi_ns_repair_TSS(struct acpi_predefined_data *data, *****************************************************************************/ static acpi_status -acpi_ns_repair_PSS(struct acpi_predefined_data *data, +acpi_ns_repair_PSS(struct acpi_evaluate_info *info, union acpi_operand_object **return_object_ptr) { union acpi_operand_object *return_object = *return_object_ptr; @@ -564,7 +684,7 @@ acpi_ns_repair_PSS(struct acpi_predefined_data *data, * incorrectly sorted, sort it. We sort by cpu_frequency, since this * should be proportional to the power. */ - status = acpi_ns_check_sorted_list(data, return_object, 6, 0, + status = acpi_ns_check_sorted_list(info, return_object, 0, 6, 0, ACPI_SORT_DESCENDING, "CpuFrequency"); if (ACPI_FAILURE(status)) { @@ -584,8 +704,8 @@ acpi_ns_repair_PSS(struct acpi_predefined_data *data, obj_desc = elements[1]; /* Index1 = power_dissipation */ if ((u32) obj_desc->integer.value > previous_value) { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, - data->node_flags, + ACPI_WARN_PREDEFINED((AE_INFO, info->full_pathname, + info->node_flags, "SubPackage[%u,%u] - suspicious power dissipation values", i - 1, i)); } @@ -599,10 +719,55 @@ acpi_ns_repair_PSS(struct acpi_predefined_data *data, /****************************************************************************** * + * FUNCTION: acpi_ns_repair_TSS + * + * PARAMETERS: info - Method execution information block + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list + * descending by the power dissipation values. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_TSS(struct acpi_evaluate_info *info, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + acpi_status status; + struct acpi_namespace_node *node; + + /* + * We can only sort the _TSS return package if there is no _PSS in the + * same scope. This is because if _PSS is present, the ACPI specification + * dictates that the _TSS Power Dissipation field is to be ignored, and + * therefore some BIOSs leave garbage values in the _TSS Power field(s). + * In this case, it is best to just return the _TSS package as-is. + * (May, 2011) + */ + status = acpi_ns_get_node(info->node, "^_PSS", + ACPI_NS_NO_UPSEARCH, &node); + if (ACPI_SUCCESS(status)) { + return (AE_OK); + } + + status = acpi_ns_check_sorted_list(info, return_object, 0, 5, 1, + ACPI_SORT_DESCENDING, + "PowerDissipation"); + + return (status); +} + +/****************************************************************************** + * * FUNCTION: acpi_ns_check_sorted_list * - * PARAMETERS: data - Pointer to validation data structure + * PARAMETERS: info - Method execution information block * return_object - Pointer to the top-level returned object + * start_index - Index of the first sub-package * expected_count - Minimum length of each sub-package * sort_index - Sub-package entry to sort on * sort_direction - Ascending or descending @@ -617,8 +782,9 @@ acpi_ns_repair_PSS(struct acpi_predefined_data *data, *****************************************************************************/ static acpi_status -acpi_ns_check_sorted_list(struct acpi_predefined_data *data, +acpi_ns_check_sorted_list(struct acpi_evaluate_info *info, union acpi_operand_object *return_object, + u32 start_index, u32 expected_count, u32 sort_index, u8 sort_direction, char *sort_key_name) @@ -643,12 +809,14 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, * Any NULL elements should have been removed by earlier call * to acpi_ns_remove_null_elements. */ - outer_elements = return_object->package.elements; outer_element_count = return_object->package.count; - if (!outer_element_count) { + if (!outer_element_count || start_index >= outer_element_count) { return (AE_AML_PACKAGE_LIMIT); } + outer_elements = &return_object->package.elements[start_index]; + outer_element_count -= start_index; + previous_value = 0; if (sort_direction == ACPI_SORT_DESCENDING) { previous_value = ACPI_UINT32_MAX; @@ -685,15 +853,16 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, (obj_desc->integer.value < previous_value)) || ((sort_direction == ACPI_SORT_DESCENDING) && (obj_desc->integer.value > previous_value))) { - acpi_ns_sort_list(return_object->package.elements, + acpi_ns_sort_list(&return_object->package. + elements[start_index], outer_element_count, sort_index, sort_direction); - data->flags |= ACPI_OBJECT_REPAIRED; + info->return_flags |= ACPI_OBJECT_REPAIRED; ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, "%s: Repaired unsorted list - now sorted by %s\n", - data->pathname, sort_key_name)); + info->full_pathname, sort_key_name)); return (AE_OK); } @@ -752,3 +921,52 @@ acpi_ns_sort_list(union acpi_operand_object **elements, } } } + +/****************************************************************************** + * + * FUNCTION: acpi_ns_remove_element + * + * PARAMETERS: obj_desc - Package object element list + * index - Index of element to remove + * + * RETURN: None + * + * DESCRIPTION: Remove the requested element of a package and delete it. + * + *****************************************************************************/ + +static void +acpi_ns_remove_element(union acpi_operand_object *obj_desc, u32 index) +{ + union acpi_operand_object **source; + union acpi_operand_object **dest; + u32 count; + u32 new_count; + u32 i; + + ACPI_FUNCTION_NAME(ns_remove_element); + + count = obj_desc->package.count; + new_count = count - 1; + + source = obj_desc->package.elements; + dest = source; + + /* Examine all elements of the package object, remove matched index */ + + for (i = 0; i < count; i++) { + if (i == index) { + acpi_ut_remove_reference(*source); /* Remove one ref for being in pkg */ + acpi_ut_remove_reference(*source); + } else { + *dest = *source; + dest++; + } + source++; + } + + /* NULL terminate list and update the package count */ + + *dest = NULL; + obj_desc->package.count = new_count; +} diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index 2808586fad30..08c0b5beec88 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -419,10 +419,12 @@ acpi_ns_externalize_name(u32 internal_name_length, switch (internal_name[0]) { case AML_ROOT_PREFIX: + prefix_length = 1; break; case AML_PARENT_PREFIX: + for (i = 0; i < internal_name_length; i++) { if (ACPI_IS_PARENT_PREFIX(internal_name[i])) { prefix_length = i + 1; @@ -438,6 +440,7 @@ acpi_ns_externalize_name(u32 internal_name_length, break; default: + break; } diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index fc69949151bb..f553cfdb71dd 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -187,8 +187,6 @@ acpi_evaluate_object(acpi_handle handle, return_ACPI_STATUS(AE_NO_MEMORY); } - info->pathname = pathname; - /* Convert and validate the device handle */ info->prefix_node = acpi_ns_validate_handle(handle); @@ -198,17 +196,64 @@ acpi_evaluate_object(acpi_handle handle, } /* - * If there are parameters to be passed to a control method, the external - * objects must all be converted to internal objects + * Get the actual namespace node for the target object. + * Handles these cases: + * + * 1) Null node, valid pathname from root (absolute path) + * 2) Node and valid pathname (path relative to Node) + * 3) Node, Null pathname + */ + if ((pathname) && (ACPI_IS_ROOT_PREFIX(pathname[0]))) { + + /* The path is fully qualified, just evaluate by name */ + + info->prefix_node = NULL; + } else if (!handle) { + /* + * A handle is optional iff a fully qualified pathname is specified. + * Since we've already handled fully qualified names above, this is + * an error. + */ + if (!pathname) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Both Handle and Pathname are NULL")); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Null Handle with relative pathname [%s]", + pathname)); + } + + status = AE_BAD_PARAMETER; + goto cleanup; + } + + info->relative_pathname = pathname; + + /* + * Convert all external objects passed as arguments to the + * internal version(s). */ if (external_params && external_params->count) { + info->param_count = (u16)external_params->count; + + /* Warn on impossible argument count */ + + if (info->param_count > ACPI_METHOD_NUM_ARGS) { + ACPI_WARN_PREDEFINED((AE_INFO, pathname, + ACPI_WARN_ALWAYS, + "Excess arguments (%u) - using only %u", + info->param_count, + ACPI_METHOD_NUM_ARGS)); + + info->param_count = ACPI_METHOD_NUM_ARGS; + } + /* * Allocate a new parameter block for the internal objects * Add 1 to count to allow for null terminated internal list */ - info->parameters = ACPI_ALLOCATE_ZEROED(((acpi_size) - external_params-> - count + + info->parameters = ACPI_ALLOCATE_ZEROED(((acpi_size) info-> + param_count + 1) * sizeof(void *)); if (!info->parameters) { status = AE_NO_MEMORY; @@ -217,7 +262,7 @@ acpi_evaluate_object(acpi_handle handle, /* Convert each external object in the list to an internal object */ - for (i = 0; i < external_params->count; i++) { + for (i = 0; i < info->param_count; i++) { status = acpi_ut_copy_eobject_to_iobject(&external_params-> pointer[i], @@ -227,43 +272,96 @@ acpi_evaluate_object(acpi_handle handle, goto cleanup; } } - info->parameters[external_params->count] = NULL; + + info->parameters[info->param_count] = NULL; } +#if 0 + /* - * Three major cases: - * 1) Fully qualified pathname - * 2) No handle, not fully qualified pathname (error) - * 3) Valid handle + * Begin incoming argument count analysis. Check for too few args + * and too many args. */ - if ((pathname) && (ACPI_IS_ROOT_PREFIX(pathname[0]))) { - /* The path is fully qualified, just evaluate by name */ + switch (acpi_ns_get_type(info->node)) { + case ACPI_TYPE_METHOD: + + /* Check incoming argument count against the method definition */ + + if (info->obj_desc->method.param_count > info->param_count) { + ACPI_ERROR((AE_INFO, + "Insufficient arguments (%u) - %u are required", + info->param_count, + info->obj_desc->method.param_count)); + + status = AE_MISSING_ARGUMENTS; + goto cleanup; + } + + else if (info->obj_desc->method.param_count < info->param_count) { + ACPI_WARNING((AE_INFO, + "Excess arguments (%u) - only %u are required", + info->param_count, + info->obj_desc->method.param_count)); + + /* Just pass the required number of arguments */ + + info->param_count = info->obj_desc->method.param_count; + } - info->prefix_node = NULL; - status = acpi_ns_evaluate(info); - } else if (!handle) { /* - * A handle is optional iff a fully qualified pathname is specified. - * Since we've already handled fully qualified names above, this is - * an error + * Any incoming external objects to be passed as arguments to the + * method must be converted to internal objects */ - if (!pathname) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Both Handle and Pathname are NULL")); - } else { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Null Handle with relative pathname [%s]", - pathname)); + if (info->param_count) { + /* + * Allocate a new parameter block for the internal objects + * Add 1 to count to allow for null terminated internal list + */ + info->parameters = ACPI_ALLOCATE_ZEROED(((acpi_size) + info-> + param_count + + 1) * + sizeof(void *)); + if (!info->parameters) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Convert each external object in the list to an internal object */ + + for (i = 0; i < info->param_count; i++) { + status = + acpi_ut_copy_eobject_to_iobject + (&external_params->pointer[i], + &info->parameters[i]); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + } + + info->parameters[info->param_count] = NULL; } + break; - status = AE_BAD_PARAMETER; - } else { - /* We have a namespace a node and a possible relative path */ + default: + + /* Warn if arguments passed to an object that is not a method */ - status = acpi_ns_evaluate(info); + if (info->param_count) { + ACPI_WARNING((AE_INFO, + "%u arguments were passed to a non-method ACPI object", + info->param_count)); + } + break; } +#endif + + /* Now we can evaluate the object */ + + status = acpi_ns_evaluate(info); + /* * If we are expecting a return value, and all went well above, * copy the return value to an external object. @@ -413,6 +511,7 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info) break; default: + return; } diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index 9f25a3d4e992..91a5a69db80c 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -629,24 +629,28 @@ static union acpi_parse_object *acpi_ps_get_next_field(struct acpi_parse_state switch (opcode) { case AML_BYTE_OP: /* AML_BYTEDATA_ARG */ + buffer_length = ACPI_GET8(parser_state->aml); parser_state->aml += 1; break; case AML_WORD_OP: /* AML_WORDDATA_ARG */ + buffer_length = ACPI_GET16(parser_state->aml); parser_state->aml += 2; break; case AML_DWORD_OP: /* AML_DWORDATA_ARG */ + buffer_length = ACPI_GET32(parser_state->aml); parser_state->aml += 4; break; default: + buffer_length = 0; break; } diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index 63c455447481..065b44ae538f 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -164,7 +164,6 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, case AML_IF_OP: case AML_ELSE_OP: case AML_WHILE_OP: - /* * Currently supported module-level opcodes are: * IF/ELSE/WHILE. These appear to be the most common, @@ -289,6 +288,7 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, default: /* No action for all other opcodes */ + break; } diff --git a/drivers/acpi/acpica/psobject.c b/drivers/acpi/acpica/psobject.c index 12c4028002b1..95dc608a66a8 100644 --- a/drivers/acpi/acpica/psobject.c +++ b/drivers/acpi/acpica/psobject.c @@ -402,6 +402,7 @@ acpi_ps_complete_op(struct acpi_walk_state *walk_state, switch (status) { case AE_OK: + break; case AE_CTRL_TRANSFER: diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index abc4c48b2edd..86198a9139b5 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -176,10 +176,10 @@ acpi_ps_complete_this_op(struct acpi_walk_state * walk_state, switch (parent_info->class) { case AML_CLASS_CONTROL: + break; case AML_CLASS_CREATE: - /* * These opcodes contain term_arg operands. The current * op must be replaced by a placeholder return op @@ -192,7 +192,6 @@ acpi_ps_complete_this_op(struct acpi_walk_state * walk_state, break; case AML_CLASS_NAMED_OBJECT: - /* * These opcodes contain term_arg operands. The current * op must be replaced by a placeholder return op diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c index c1934bf04f0a..877dc0de8df3 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -308,7 +308,9 @@ union acpi_parse_object *acpi_ps_get_child(union acpi_parse_object *op) break; default: + /* All others have no children */ + break; } diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index f68254268965..11b99ab20bb3 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -125,7 +125,7 @@ static void acpi_ps_start_trace(struct acpi_evaluate_info *info) } if ((!acpi_gbl_trace_method_name) || - (acpi_gbl_trace_method_name != info->resolved_node->name.integer)) { + (acpi_gbl_trace_method_name != info->node->name.integer)) { goto exit; } @@ -170,7 +170,7 @@ static void acpi_ps_stop_trace(struct acpi_evaluate_info *info) } if ((!acpi_gbl_trace_method_name) || - (acpi_gbl_trace_method_name != info->resolved_node->name.integer)) { + (acpi_gbl_trace_method_name != info->node->name.integer)) { goto exit; } @@ -226,15 +226,14 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) /* Validate the Info and method Node */ - if (!info || !info->resolved_node) { + if (!info || !info->node) { return_ACPI_STATUS(AE_NULL_ENTRY); } /* Init for new method, wait on concurrency semaphore */ status = - acpi_ds_begin_method_execution(info->resolved_node, info->obj_desc, - NULL); + acpi_ds_begin_method_execution(info->node, info->obj_desc, NULL); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } @@ -253,8 +252,7 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) */ ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Begin Method Parse/Execute [%4.4s] **** Node=%p Obj=%p\n", - info->resolved_node->name.ascii, info->resolved_node, - info->obj_desc)); + info->node->name.ascii, info->node, info->obj_desc)); /* Create and init a Root Node */ @@ -275,7 +273,7 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) goto cleanup; } - status = acpi_ds_init_aml_walk(walk_state, op, info->resolved_node, + status = acpi_ds_init_aml_walk(walk_state, op, info->node, info->obj_desc->method.aml_start, info->obj_desc->method.aml_length, info, info->pass_number); diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index 72077fa1eea5..b62a0f4f4f9b 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -352,6 +352,7 @@ acpi_rs_get_aml_length(struct acpi_resource * resource, acpi_size * size_needed) break; default: + break; } @@ -539,6 +540,7 @@ acpi_rs_get_list_length(u8 * aml_buffer, break; default: + break; } @@ -650,8 +652,9 @@ acpi_rs_get_pci_routing_table_length(union acpi_operand_object *package_object, name_found = FALSE; - for (table_index = 0; table_index < 4 && !name_found; - table_index++) { + for (table_index = 0; + table_index < package_element->package.count + && !name_found; table_index++) { if (*sub_object_list && /* Null object allowed */ ((ACPI_TYPE_STRING == (*sub_object_list)->common.type) || diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c index f8b55b426c9d..65f3e1c5b598 100644 --- a/drivers/acpi/acpica/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -273,17 +273,6 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, */ user_prt->length = (sizeof(struct acpi_pci_routing_table) - 4); - /* Each element of the top-level package must also be a package */ - - if ((*top_object_list)->common.type != ACPI_TYPE_PACKAGE) { - ACPI_ERROR((AE_INFO, - "(PRT[%u]) Need sub-package, found %s", - index, - acpi_ut_get_object_type_name - (*top_object_list))); - return_ACPI_STATUS(AE_AML_OPERAND_TYPE); - } - /* Each sub-package must be of length 4 */ if ((*top_object_list)->package.count != 4) { @@ -327,22 +316,6 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, user_prt->pin = (u32) obj_desc->integer.value; /* - * If the BIOS has erroneously reversed the _PRT source_name (index 2) - * and the source_index (index 3), fix it. _PRT is important enough to - * workaround this BIOS error. This also provides compatibility with - * other ACPI implementations. - */ - obj_desc = sub_object_list[3]; - if (!obj_desc || (obj_desc->common.type != ACPI_TYPE_INTEGER)) { - sub_object_list[3] = sub_object_list[2]; - sub_object_list[2] = obj_desc; - - ACPI_WARNING((AE_INFO, - "(PRT[%X].Source) SourceName and SourceIndex are reversed, fixed", - index)); - } - - /* * 3) Third subobject: Dereference the PRT.source_name * The name may be unresolved (slack mode), so allow a null object */ diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index b5fc0db2e87b..8a2d4986b0aa 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -120,17 +120,20 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) /* Strings */ case ACPI_RSD_LITERAL: + acpi_rs_out_string(name, ACPI_CAST_PTR(char, table->pointer)); break; case ACPI_RSD_STRING: + acpi_rs_out_string(name, ACPI_CAST_PTR(char, target)); break; /* Data items, 8/16/32/64 bit */ case ACPI_RSD_UINT8: + if (table->pointer) { acpi_rs_out_string(name, ACPI_CAST_PTR(char, table-> @@ -142,20 +145,24 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) break; case ACPI_RSD_UINT16: + acpi_rs_out_integer16(name, ACPI_GET16(target)); break; case ACPI_RSD_UINT32: + acpi_rs_out_integer32(name, ACPI_GET32(target)); break; case ACPI_RSD_UINT64: + acpi_rs_out_integer64(name, ACPI_GET64(target)); break; /* Flags: 1-bit and 2-bit flags supported */ case ACPI_RSD_1BITFLAG: + acpi_rs_out_string(name, ACPI_CAST_PTR(char, table-> pointer[*target & @@ -163,6 +170,7 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) break; case ACPI_RSD_2BITFLAG: + acpi_rs_out_string(name, ACPI_CAST_PTR(char, table-> pointer[*target & @@ -170,6 +178,7 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) break; case ACPI_RSD_3BITFLAG: + acpi_rs_out_string(name, ACPI_CAST_PTR(char, table-> pointer[*target & @@ -258,6 +267,7 @@ acpi_rs_dump_descriptor(void *resource, struct acpi_rsdump_info *table) break; default: + acpi_os_printf("**** Invalid table opcode [%X] ****\n", table->opcode); return; diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c index d5bf05a96096..80d12994e0d0 100644 --- a/drivers/acpi/acpica/rsmisc.c +++ b/drivers/acpi/acpica/rsmisc.c @@ -194,7 +194,6 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, break; case ACPI_RSC_COUNT_GPIO_RES: - /* * Vendor data is optional (length/offset may both be zero) * Examine vendor data length field first @@ -410,12 +409,14 @@ acpi_rs_convert_aml_to_resource(struct acpi_resource *resource, */ switch (info->resource_offset) { case ACPI_RSC_COMPARE_AML_LENGTH: + if (aml_resource_length != info->value) { goto exit; } break; case ACPI_RSC_COMPARE_VALUE: + if (ACPI_GET8(source) != info->value) { goto exit; } diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c index a44953c6f75d..480b6b40c5ea 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -147,6 +147,7 @@ acpi_rs_move_data(void *destination, void *source, u16 item_count, u8 move_type) case ACPI_RSC_MOVE_GPIO_RES: case ACPI_RSC_MOVE_SERIAL_VEN: case ACPI_RSC_MOVE_SERIAL_RES: + ACPI_MEMCPY(destination, source, item_count); return; @@ -157,21 +158,25 @@ acpi_rs_move_data(void *destination, void *source, u16 item_count, u8 move_type) */ case ACPI_RSC_MOVE16: case ACPI_RSC_MOVE_GPIO_PIN: + ACPI_MOVE_16_TO_16(&ACPI_CAST_PTR(u16, destination)[i], &ACPI_CAST_PTR(u16, source)[i]); break; case ACPI_RSC_MOVE32: + ACPI_MOVE_32_TO_32(&ACPI_CAST_PTR(u32, destination)[i], &ACPI_CAST_PTR(u32, source)[i]); break; case ACPI_RSC_MOVE64: + ACPI_MOVE_64_TO_64(&ACPI_CAST_PTR(u64, destination)[i], &ACPI_CAST_PTR(u64, source)[i]); break; default: + return; } } @@ -736,7 +741,7 @@ acpi_rs_set_srs_method_data(struct acpi_namespace_node *node, } info->prefix_node = node; - info->pathname = METHOD_NAME__SRS; + info->relative_pathname = METHOD_NAME__SRS; info->parameters = args; info->flags = ACPI_IGNORE_RETURN_VALUE; diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index c0e5d2d3ce67..94e3517554f9 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -402,6 +402,7 @@ acpi_resource_to_address64(struct acpi_resource *resource, break; default: + return (AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index e57cd38004e3..42a13c0d7015 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -141,8 +141,7 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc, u32 *table_index) ACPI_BIOS_ERROR((AE_INFO, "Table has invalid signature [%4.4s] (0x%8.8X), " "must be SSDT or OEMx", - acpi_ut_valid_acpi_name(*(u32 *)table_desc-> - pointer-> + acpi_ut_valid_acpi_name(table_desc->pointer-> signature) ? table_desc->pointer->signature : "????", *(u32 *)table_desc->pointer->signature)); @@ -471,15 +470,19 @@ void acpi_tb_delete_table(struct acpi_table_desc *table_desc) } switch (table_desc->flags & ACPI_TABLE_ORIGIN_MASK) { case ACPI_TABLE_ORIGIN_MAPPED: + acpi_os_unmap_memory(table_desc->pointer, table_desc->length); break; + case ACPI_TABLE_ORIGIN_ALLOCATED: + ACPI_FREE(table_desc->pointer); break; /* Not mapped or allocated, there is nothing we can do */ default: + return; } diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c new file mode 100644 index 000000000000..dc963f823d2c --- /dev/null +++ b/drivers/acpi/acpica/tbprint.c @@ -0,0 +1,237 @@ +/****************************************************************************** + * + * Module Name: tbprint - Table output utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2013, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "actables.h" + +#define _COMPONENT ACPI_TABLES +ACPI_MODULE_NAME("tbprint") + +/* Local prototypes */ +static void acpi_tb_fix_string(char *string, acpi_size length); + +static void +acpi_tb_cleanup_table_header(struct acpi_table_header *out_header, + struct acpi_table_header *header); + +/******************************************************************************* + * + * FUNCTION: acpi_tb_fix_string + * + * PARAMETERS: string - String to be repaired + * length - Maximum length + * + * RETURN: None + * + * DESCRIPTION: Replace every non-printable or non-ascii byte in the string + * with a question mark '?'. + * + ******************************************************************************/ + +static void acpi_tb_fix_string(char *string, acpi_size length) +{ + + while (length && *string) { + if (!ACPI_IS_PRINT(*string)) { + *string = '?'; + } + string++; + length--; + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_cleanup_table_header + * + * PARAMETERS: out_header - Where the cleaned header is returned + * header - Input ACPI table header + * + * RETURN: Returns the cleaned header in out_header + * + * DESCRIPTION: Copy the table header and ensure that all "string" fields in + * the header consist of printable characters. + * + ******************************************************************************/ + +static void +acpi_tb_cleanup_table_header(struct acpi_table_header *out_header, + struct acpi_table_header *header) +{ + + ACPI_MEMCPY(out_header, header, sizeof(struct acpi_table_header)); + + acpi_tb_fix_string(out_header->signature, ACPI_NAME_SIZE); + acpi_tb_fix_string(out_header->oem_id, ACPI_OEM_ID_SIZE); + acpi_tb_fix_string(out_header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE); + acpi_tb_fix_string(out_header->asl_compiler_id, ACPI_NAME_SIZE); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_print_table_header + * + * PARAMETERS: address - Table physical address + * header - Table header + * + * RETURN: None + * + * DESCRIPTION: Print an ACPI table header. Special cases for FACS and RSDP. + * + ******************************************************************************/ + +void +acpi_tb_print_table_header(acpi_physical_address address, + struct acpi_table_header *header) +{ + struct acpi_table_header local_header; + + /* + * The reason that the Address is cast to a void pointer is so that we + * can use %p which will work properly on both 32-bit and 64-bit hosts. + */ + if (ACPI_COMPARE_NAME(header->signature, ACPI_SIG_FACS)) { + + /* FACS only has signature and length fields */ + + ACPI_INFO((AE_INFO, "%4.4s %p %05X", + header->signature, ACPI_CAST_PTR(void, address), + header->length)); + } else if (ACPI_COMPARE_NAME(header->signature, ACPI_SIG_RSDP)) { + + /* RSDP has no common fields */ + + ACPI_MEMCPY(local_header.oem_id, + ACPI_CAST_PTR(struct acpi_table_rsdp, + header)->oem_id, ACPI_OEM_ID_SIZE); + acpi_tb_fix_string(local_header.oem_id, ACPI_OEM_ID_SIZE); + + ACPI_INFO((AE_INFO, "RSDP %p %05X (v%.2d %6.6s)", + ACPI_CAST_PTR(void, address), + (ACPI_CAST_PTR(struct acpi_table_rsdp, header)-> + revision > + 0) ? ACPI_CAST_PTR(struct acpi_table_rsdp, + header)->length : 20, + ACPI_CAST_PTR(struct acpi_table_rsdp, + header)->revision, + local_header.oem_id)); + } else { + /* Standard ACPI table with full common header */ + + acpi_tb_cleanup_table_header(&local_header, header); + + ACPI_INFO((AE_INFO, + "%4.4s %p %05X (v%.2d %6.6s %8.8s %08X %4.4s %08X)", + local_header.signature, ACPI_CAST_PTR(void, address), + local_header.length, local_header.revision, + local_header.oem_id, local_header.oem_table_id, + local_header.oem_revision, + local_header.asl_compiler_id, + local_header.asl_compiler_revision)); + } +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_validate_checksum + * + * PARAMETERS: table - ACPI table to verify + * length - Length of entire table + * + * RETURN: Status + * + * DESCRIPTION: Verifies that the table checksums to zero. Optionally returns + * exception on bad checksum. + * + ******************************************************************************/ + +acpi_status acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length) +{ + u8 checksum; + + /* Compute the checksum on the table */ + + checksum = acpi_tb_checksum(ACPI_CAST_PTR(u8, table), length); + + /* Checksum ok? (should be zero) */ + + if (checksum) { + ACPI_BIOS_WARNING((AE_INFO, + "Incorrect checksum in table [%4.4s] - 0x%2.2X, " + "should be 0x%2.2X", + table->signature, table->checksum, + (u8)(table->checksum - checksum))); + +#if (ACPI_CHECKSUM_ABORT) + return (AE_BAD_CHECKSUM); +#endif + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_tb_checksum + * + * PARAMETERS: buffer - Pointer to memory region to be checked + * length - Length of this memory region + * + * RETURN: Checksum (u8) + * + * DESCRIPTION: Calculates circular checksum of memory region. + * + ******************************************************************************/ + +u8 acpi_tb_checksum(u8 *buffer, u32 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) { + sum = (u8)(sum + *(buffer++)); + } + + return (sum); +} diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index ce3d5db39a9c..bffdfc7b8322 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Module Name: tbutils - table utilities + * Module Name: tbutils - ACPI Table utilities * *****************************************************************************/ @@ -49,12 +49,6 @@ ACPI_MODULE_NAME("tbutils") /* Local prototypes */ -static void acpi_tb_fix_string(char *string, acpi_size length); - -static void -acpi_tb_cleanup_table_header(struct acpi_table_header *out_header, - struct acpi_table_header *header); - static acpi_physical_address acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size); @@ -176,189 +170,6 @@ u8 acpi_tb_tables_loaded(void) /******************************************************************************* * - * FUNCTION: acpi_tb_fix_string - * - * PARAMETERS: string - String to be repaired - * length - Maximum length - * - * RETURN: None - * - * DESCRIPTION: Replace every non-printable or non-ascii byte in the string - * with a question mark '?'. - * - ******************************************************************************/ - -static void acpi_tb_fix_string(char *string, acpi_size length) -{ - - while (length && *string) { - if (!ACPI_IS_PRINT(*string)) { - *string = '?'; - } - string++; - length--; - } -} - -/******************************************************************************* - * - * FUNCTION: acpi_tb_cleanup_table_header - * - * PARAMETERS: out_header - Where the cleaned header is returned - * header - Input ACPI table header - * - * RETURN: Returns the cleaned header in out_header - * - * DESCRIPTION: Copy the table header and ensure that all "string" fields in - * the header consist of printable characters. - * - ******************************************************************************/ - -static void -acpi_tb_cleanup_table_header(struct acpi_table_header *out_header, - struct acpi_table_header *header) -{ - - ACPI_MEMCPY(out_header, header, sizeof(struct acpi_table_header)); - - acpi_tb_fix_string(out_header->signature, ACPI_NAME_SIZE); - acpi_tb_fix_string(out_header->oem_id, ACPI_OEM_ID_SIZE); - acpi_tb_fix_string(out_header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE); - acpi_tb_fix_string(out_header->asl_compiler_id, ACPI_NAME_SIZE); -} - -/******************************************************************************* - * - * FUNCTION: acpi_tb_print_table_header - * - * PARAMETERS: address - Table physical address - * header - Table header - * - * RETURN: None - * - * DESCRIPTION: Print an ACPI table header. Special cases for FACS and RSDP. - * - ******************************************************************************/ - -void -acpi_tb_print_table_header(acpi_physical_address address, - struct acpi_table_header *header) -{ - struct acpi_table_header local_header; - - /* - * The reason that the Address is cast to a void pointer is so that we - * can use %p which will work properly on both 32-bit and 64-bit hosts. - */ - if (ACPI_COMPARE_NAME(header->signature, ACPI_SIG_FACS)) { - - /* FACS only has signature and length fields */ - - ACPI_INFO((AE_INFO, "%4.4s %p %05X", - header->signature, ACPI_CAST_PTR(void, address), - header->length)); - } else if (ACPI_COMPARE_NAME(header->signature, ACPI_SIG_RSDP)) { - - /* RSDP has no common fields */ - - ACPI_MEMCPY(local_header.oem_id, - ACPI_CAST_PTR(struct acpi_table_rsdp, - header)->oem_id, ACPI_OEM_ID_SIZE); - acpi_tb_fix_string(local_header.oem_id, ACPI_OEM_ID_SIZE); - - ACPI_INFO((AE_INFO, "RSDP %p %05X (v%.2d %6.6s)", - ACPI_CAST_PTR (void, address), - (ACPI_CAST_PTR(struct acpi_table_rsdp, header)-> - revision > - 0) ? ACPI_CAST_PTR(struct acpi_table_rsdp, - header)->length : 20, - ACPI_CAST_PTR(struct acpi_table_rsdp, - header)->revision, - local_header.oem_id)); - } else { - /* Standard ACPI table with full common header */ - - acpi_tb_cleanup_table_header(&local_header, header); - - ACPI_INFO((AE_INFO, - "%4.4s %p %05X (v%.2d %6.6s %8.8s %08X %4.4s %08X)", - local_header.signature, ACPI_CAST_PTR(void, address), - local_header.length, local_header.revision, - local_header.oem_id, local_header.oem_table_id, - local_header.oem_revision, - local_header.asl_compiler_id, - local_header.asl_compiler_revision)); - - } -} - -/******************************************************************************* - * - * FUNCTION: acpi_tb_validate_checksum - * - * PARAMETERS: table - ACPI table to verify - * length - Length of entire table - * - * RETURN: Status - * - * DESCRIPTION: Verifies that the table checksums to zero. Optionally returns - * exception on bad checksum. - * - ******************************************************************************/ - -acpi_status acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length) -{ - u8 checksum; - - /* Compute the checksum on the table */ - - checksum = acpi_tb_checksum(ACPI_CAST_PTR(u8, table), length); - - /* Checksum ok? (should be zero) */ - - if (checksum) { - ACPI_BIOS_WARNING((AE_INFO, - "Incorrect checksum in table [%4.4s] - 0x%2.2X, " - "should be 0x%2.2X", - table->signature, table->checksum, - (u8)(table->checksum - checksum))); - -#if (ACPI_CHECKSUM_ABORT) - - return (AE_BAD_CHECKSUM); -#endif - } - - return (AE_OK); -} - -/******************************************************************************* - * - * FUNCTION: acpi_tb_checksum - * - * PARAMETERS: buffer - Pointer to memory region to be checked - * length - Length of this memory region - * - * RETURN: Checksum (u8) - * - * DESCRIPTION: Calculates circular checksum of memory region. - * - ******************************************************************************/ - -u8 acpi_tb_checksum(u8 *buffer, u32 length) -{ - u8 sum = 0; - u8 *end = buffer + length; - - while (buffer < end) { - sum = (u8) (sum + *(buffer++)); - } - - return (sum); -} - -/******************************************************************************* - * * FUNCTION: acpi_tb_check_dsdt_header * * PARAMETERS: None diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index 67e046ec8f0a..0ba9e328d5d7 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -53,8 +53,6 @@ ACPI_MODULE_NAME("tbxfload") /* Local prototypes */ static acpi_status acpi_tb_load_namespace(void); -static int no_auto_ssdt; - /******************************************************************************* * * FUNCTION: acpi_load_tables @@ -180,8 +178,16 @@ static acpi_status acpi_tb_load_namespace(void) continue; } - if (no_auto_ssdt) { - printk(KERN_WARNING "ACPI: SSDT ignored due to \"acpi_no_auto_ssdt\"\n"); + /* + * Optionally do not load any SSDTs from the RSDT/XSDT. This can + * be useful for debugging ACPI problems on some machines. + */ + if (acpi_gbl_disable_ssdt_table_load) { + ACPI_INFO((AE_INFO, "Ignoring %4.4s at %p", + acpi_gbl_root_table_list.tables[i].signature. + ascii, ACPI_CAST_PTR(void, + acpi_gbl_root_table_list. + tables[i].address))); continue; } @@ -376,14 +382,3 @@ acpi_status acpi_unload_parent_table(acpi_handle object) } ACPI_EXPORT_SYMBOL(acpi_unload_parent_table) - -static int __init acpi_no_auto_ssdt_setup(char *s) { - - printk(KERN_NOTICE "ACPI: SSDT auto-load disabled\n"); - - no_auto_ssdt = 1; - - return 1; -} - -__setup("acpi_no_auto_ssdt", acpi_no_auto_ssdt_setup); diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c new file mode 100644 index 000000000000..11fde93be120 --- /dev/null +++ b/drivers/acpi/acpica/utbuffer.c @@ -0,0 +1,201 @@ +/****************************************************************************** + * + * Module Name: utbuffer - Buffer dump routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2013, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utbuffer") + +/******************************************************************************* + * + * FUNCTION: acpi_ut_dump_buffer + * + * PARAMETERS: buffer - Buffer to dump + * count - Amount to dump, in bytes + * display - BYTE, WORD, DWORD, or QWORD display: + * DB_BYTE_DISPLAY + * DB_WORD_DISPLAY + * DB_DWORD_DISPLAY + * DB_QWORD_DISPLAY + * base_offset - Beginning buffer offset (display only) + * + * RETURN: None + * + * DESCRIPTION: Generic dump buffer in both hex and ascii. + * + ******************************************************************************/ +void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 base_offset) +{ + u32 i = 0; + u32 j; + u32 temp32; + u8 buf_char; + + if (!buffer) { + acpi_os_printf("Null Buffer Pointer in DumpBuffer!\n"); + return; + } + + if ((count < 4) || (count & 0x01)) { + display = DB_BYTE_DISPLAY; + } + + /* Nasty little dump buffer routine! */ + + while (i < count) { + + /* Print current offset */ + + acpi_os_printf("%6.4X: ", (base_offset + i)); + + /* Print 16 hex chars */ + + for (j = 0; j < 16;) { + if (i + j >= count) { + + /* Dump fill spaces */ + + acpi_os_printf("%*s", ((display * 2) + 1), " "); + j += display; + continue; + } + + switch (display) { + case DB_BYTE_DISPLAY: + default: /* Default is BYTE display */ + + acpi_os_printf("%02X ", + buffer[(acpi_size) i + j]); + break; + + case DB_WORD_DISPLAY: + + ACPI_MOVE_16_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_os_printf("%04X ", temp32); + break; + + case DB_DWORD_DISPLAY: + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_os_printf("%08X ", temp32); + break; + + case DB_QWORD_DISPLAY: + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j]); + acpi_os_printf("%08X", temp32); + + ACPI_MOVE_32_TO_32(&temp32, + &buffer[(acpi_size) i + j + + 4]); + acpi_os_printf("%08X ", temp32); + break; + } + + j += display; + } + + /* + * Print the ASCII equivalent characters but watch out for the bad + * unprintable ones (printable chars are 0x20 through 0x7E) + */ + acpi_os_printf(" "); + for (j = 0; j < 16; j++) { + if (i + j >= count) { + acpi_os_printf("\n"); + return; + } + + buf_char = buffer[(acpi_size) i + j]; + if (ACPI_IS_PRINT(buf_char)) { + acpi_os_printf("%c", buf_char); + } else { + acpi_os_printf("."); + } + } + + /* Done with that line. */ + + acpi_os_printf("\n"); + i += 16; + } + + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_debug_dump_buffer + * + * PARAMETERS: buffer - Buffer to dump + * count - Amount to dump, in bytes + * display - BYTE, WORD, DWORD, or QWORD display: + * DB_BYTE_DISPLAY + * DB_WORD_DISPLAY + * DB_DWORD_DISPLAY + * DB_QWORD_DISPLAY + * component_ID - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Generic dump buffer in both hex and ascii. + * + ******************************************************************************/ + +void +acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id) +{ + + /* Only dump the buffer if tracing is enabled */ + + if (!((ACPI_LV_TABLES & acpi_dbg_level) && + (component_id & acpi_dbg_layer))) { + return; + } + + acpi_ut_dump_buffer(buffer, count, display, 0); +} diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index e4c9291fc0a3..1731c27c36a6 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -178,7 +178,6 @@ acpi_ut_copy_isimple_to_esimple(union acpi_operand_object *internal_object, switch (internal_object->reference.class) { case ACPI_REFCLASS_NAME: - /* * For namepath, return the object handle ("reference") * We are referring to the namespace node @@ -264,7 +263,6 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type, switch (object_type) { case ACPI_COPY_TYPE_SIMPLE: - /* * This is a simple or null object */ @@ -278,7 +276,6 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type, break; case ACPI_COPY_TYPE_PACKAGE: - /* * Build the package object */ @@ -304,6 +301,7 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type, break; default: + return (AE_BAD_PARAMETER); } @@ -481,6 +479,7 @@ acpi_ut_copy_esimple_to_isimple(union acpi_object *external_object, return_ACPI_STATUS(AE_OK); default: + /* All other types are not supported */ ACPI_ERROR((AE_INFO, @@ -544,7 +543,9 @@ acpi_ut_copy_esimple_to_isimple(union acpi_object *external_object, break; default: + /* Other types can't get here */ + break; } @@ -800,7 +801,9 @@ acpi_ut_copy_simple_object(union acpi_operand_object *source_desc, break; default: + /* Nothing to do for other simple objects */ + break; } @@ -868,7 +871,6 @@ acpi_ut_copy_ielement_to_ielement(u8 object_type, break; case ACPI_COPY_TYPE_PACKAGE: - /* * This object is a package - go down another nesting level * Create and build the package object @@ -891,6 +893,7 @@ acpi_ut_copy_ielement_to_ielement(u8 object_type, break; default: + return (AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index c57d9cc07ba9..5796e11a0671 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Module Name: utdebug - Debug print routines + * Module Name: utdebug - Debug print/trace routines * *****************************************************************************/ @@ -543,149 +543,3 @@ acpi_ut_ptr_exit(u32 line_number, } #endif - -/******************************************************************************* - * - * FUNCTION: acpi_ut_dump_buffer - * - * PARAMETERS: buffer - Buffer to dump - * count - Amount to dump, in bytes - * display - BYTE, WORD, DWORD, or QWORD display - * offset - Beginning buffer offset (display only) - * - * RETURN: None - * - * DESCRIPTION: Generic dump buffer in both hex and ascii. - * - ******************************************************************************/ - -void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 base_offset) -{ - u32 i = 0; - u32 j; - u32 temp32; - u8 buf_char; - - if (!buffer) { - acpi_os_printf("Null Buffer Pointer in DumpBuffer!\n"); - return; - } - - if ((count < 4) || (count & 0x01)) { - display = DB_BYTE_DISPLAY; - } - - /* Nasty little dump buffer routine! */ - - while (i < count) { - - /* Print current offset */ - - acpi_os_printf("%6.4X: ", (base_offset + i)); - - /* Print 16 hex chars */ - - for (j = 0; j < 16;) { - if (i + j >= count) { - - /* Dump fill spaces */ - - acpi_os_printf("%*s", ((display * 2) + 1), " "); - j += display; - continue; - } - - switch (display) { - case DB_BYTE_DISPLAY: - default: /* Default is BYTE display */ - - acpi_os_printf("%02X ", - buffer[(acpi_size) i + j]); - break; - - case DB_WORD_DISPLAY: - - ACPI_MOVE_16_TO_32(&temp32, - &buffer[(acpi_size) i + j]); - acpi_os_printf("%04X ", temp32); - break; - - case DB_DWORD_DISPLAY: - - ACPI_MOVE_32_TO_32(&temp32, - &buffer[(acpi_size) i + j]); - acpi_os_printf("%08X ", temp32); - break; - - case DB_QWORD_DISPLAY: - - ACPI_MOVE_32_TO_32(&temp32, - &buffer[(acpi_size) i + j]); - acpi_os_printf("%08X", temp32); - - ACPI_MOVE_32_TO_32(&temp32, - &buffer[(acpi_size) i + j + - 4]); - acpi_os_printf("%08X ", temp32); - break; - } - - j += display; - } - - /* - * Print the ASCII equivalent characters but watch out for the bad - * unprintable ones (printable chars are 0x20 through 0x7E) - */ - acpi_os_printf(" "); - for (j = 0; j < 16; j++) { - if (i + j >= count) { - acpi_os_printf("\n"); - return; - } - - buf_char = buffer[(acpi_size) i + j]; - if (ACPI_IS_PRINT(buf_char)) { - acpi_os_printf("%c", buf_char); - } else { - acpi_os_printf("."); - } - } - - /* Done with that line. */ - - acpi_os_printf("\n"); - i += 16; - } - - return; -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_debug_dump_buffer - * - * PARAMETERS: buffer - Buffer to dump - * count - Amount to dump, in bytes - * display - BYTE, WORD, DWORD, or QWORD display - * component_ID - Caller's component ID - * - * RETURN: None - * - * DESCRIPTION: Generic dump buffer in both hex and ascii. - * - ******************************************************************************/ - -void -acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id) -{ - - /* Only dump the buffer if tracing is enabled */ - - if (!((ACPI_LV_TABLES & acpi_dbg_level) && - (component_id & acpi_dbg_layer))) { - return; - } - - acpi_ut_dump_buffer(buffer, count, display, 0); -} diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index 29b930250b6f..d6b33f29d327 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -303,6 +303,7 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) break; default: + break; } @@ -508,7 +509,6 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) case ACPI_TYPE_PROCESSOR: case ACPI_TYPE_POWER: case ACPI_TYPE_THERMAL: - /* * Update the notify objects for these types (if present) * Two lists, system and device notify handlers. @@ -623,6 +623,7 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) case ACPI_TYPE_REGION: default: + break; /* No subobjects for all other types */ } diff --git a/drivers/acpi/acpica/uterror.c b/drivers/acpi/acpica/uterror.c new file mode 100644 index 000000000000..154fdcaa5830 --- /dev/null +++ b/drivers/acpi/acpica/uterror.c @@ -0,0 +1,289 @@ +/******************************************************************************* + * + * Module Name: uterror - Various internal error/warning output functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2013, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("uterror") + +/* + * This module contains internal error functions that may + * be configured out. + */ +#if !defined (ACPI_NO_ERROR_MESSAGES) +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_warning + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Warnings for the predefined validation module. Messages are + * only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of error + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_warning(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...) +{ + va_list arg_list; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf(ACPI_MSG_WARNING "%s: ", pathname); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_info + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: Info messages for the predefined validation module. Messages + * are only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_info(const char *module_name, + u32 line_number, + char *pathname, u8 node_flags, const char *format, ...) +{ + va_list arg_list; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf(ACPI_MSG_INFO "%s: ", pathname); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_predefined_bios_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * pathname - Full pathname to the node + * node_flags - From Namespace node for the method/object + * format - Printf format string + additional args + * + * RETURN: None + * + * DESCRIPTION: BIOS error message for predefined names. Messages + * are only emitted the first time a problem with a particular + * method/object is detected. This prevents a flood of + * messages for methods that are repeatedly evaluated. + * + ******************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_predefined_bios_error(const char *module_name, + u32 line_number, + char *pathname, + u8 node_flags, const char *format, ...) +{ + va_list arg_list; + + /* + * Warning messages for this method/object will be disabled after the + * first time a validation fails or an object is successfully repaired. + */ + if (node_flags & ANOBJ_EVALUATED) { + return; + } + + acpi_os_printf(ACPI_MSG_BIOS_ERROR "%s: ", pathname); + + va_start(arg_list, format); + acpi_os_vprintf(format, arg_list); + ACPI_MSG_SUFFIX; + va_end(arg_list); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_namespace_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * internal_name - Name or path of the namespace node + * lookup_status - Exception code from NS lookup + * + * RETURN: None + * + * DESCRIPTION: Print error message with the full pathname for the NS node. + * + ******************************************************************************/ + +void +acpi_ut_namespace_error(const char *module_name, + u32 line_number, + const char *internal_name, acpi_status lookup_status) +{ + acpi_status status; + u32 bad_name; + char *name = NULL; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_ERROR); + + if (lookup_status == AE_BAD_CHARACTER) { + + /* There is a non-ascii character in the name */ + + ACPI_MOVE_32_TO_32(&bad_name, + ACPI_CAST_PTR(u32, internal_name)); + acpi_os_printf("[0x%.8X] (NON-ASCII)", bad_name); + } else { + /* Convert path to external format */ + + status = acpi_ns_externalize_name(ACPI_UINT32_MAX, + internal_name, NULL, &name); + + /* Print target name */ + + if (ACPI_SUCCESS(status)) { + acpi_os_printf("[%s]", name); + } else { + acpi_os_printf("[COULD NOT EXTERNALIZE NAME]"); + } + + if (name) { + ACPI_FREE(name); + } + } + + acpi_os_printf(" Namespace lookup failure, %s", + acpi_format_exception(lookup_status)); + + ACPI_MSG_SUFFIX; + ACPI_MSG_REDIRECT_END; +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_method_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * message - Error message to use on failure + * prefix_node - Prefix relative to the path + * path - Path to the node (optional) + * method_status - Execution status + * + * RETURN: None + * + * DESCRIPTION: Print error message with the full pathname for the method. + * + ******************************************************************************/ + +void +acpi_ut_method_error(const char *module_name, + u32 line_number, + const char *message, + struct acpi_namespace_node *prefix_node, + const char *path, acpi_status method_status) +{ + acpi_status status; + struct acpi_namespace_node *node = prefix_node; + + ACPI_MSG_REDIRECT_BEGIN; + acpi_os_printf(ACPI_MSG_ERROR); + + if (path) { + status = + acpi_ns_get_node(prefix_node, path, ACPI_NS_NO_UPSEARCH, + &node); + if (ACPI_FAILURE(status)) { + acpi_os_printf("[Could not get node by pathname]"); + } + } + + acpi_ns_print_node_pathname(node, message); + acpi_os_printf(", %s", acpi_format_exception(method_status)); + + ACPI_MSG_SUFFIX; + ACPI_MSG_REDIRECT_END; +} + +#endif /* ACPI_NO_ERROR_MESSAGES */ diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index c3f3a7e7bdc7..ee83adb97b1e 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -68,7 +68,7 @@ ACPI_MODULE_NAME("uteval") ******************************************************************************/ acpi_status -acpi_ut_evaluate_object(struct acpi_namespace_node * prefix_node, +acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, char *path, u32 expected_return_btypes, union acpi_operand_object **return_desc) @@ -87,7 +87,7 @@ acpi_ut_evaluate_object(struct acpi_namespace_node * prefix_node, } info->prefix_node = prefix_node; - info->pathname = path; + info->relative_pathname = path; /* Evaluate the object/method */ @@ -123,22 +123,27 @@ acpi_ut_evaluate_object(struct acpi_namespace_node * prefix_node, switch ((info->return_object)->common.type) { case ACPI_TYPE_INTEGER: + return_btype = ACPI_BTYPE_INTEGER; break; case ACPI_TYPE_BUFFER: + return_btype = ACPI_BTYPE_BUFFER; break; case ACPI_TYPE_STRING: + return_btype = ACPI_BTYPE_STRING; break; case ACPI_TYPE_PACKAGE: + return_btype = ACPI_BTYPE_PACKAGE; break; default: + return_btype = 0; break; } diff --git a/drivers/acpi/acpica/utexcep.c b/drivers/acpi/acpica/utexcep.c index b543a144941a..ff6d9e8aa842 100644 --- a/drivers/acpi/acpica/utexcep.c +++ b/drivers/acpi/acpica/utexcep.c @@ -146,6 +146,7 @@ const struct acpi_exception_info *acpi_ut_validate_exception(acpi_status status) break; default: + break; } diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c index 43a170a74a61..fa69071db418 100644 --- a/drivers/acpi/acpica/utids.c +++ b/drivers/acpi/acpica/utids.c @@ -341,14 +341,17 @@ acpi_ut_execute_CID(struct acpi_namespace_node *device_node, switch (cid_objects[i]->common.type) { case ACPI_TYPE_INTEGER: + string_area_size += ACPI_EISAID_STRING_SIZE; break; case ACPI_TYPE_STRING: + string_area_size += cid_objects[i]->string.length + 1; break; default: + status = AE_TYPE; goto cleanup; } diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index 785fdd07ae56..02f9101b65e4 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -382,10 +382,12 @@ acpi_ut_display_init_pathname(u8 type, switch (type) { case ACPI_TYPE_METHOD: + acpi_os_printf("Executing "); break; default: + acpi_os_printf("Initializing "); break; } diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index 1099f5c069f8..aa61f66ee861 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -129,6 +129,7 @@ union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char break; default: + /* All others have no secondary object */ break; } @@ -353,6 +354,7 @@ u8 acpi_ut_valid_internal_object(void *object) return (TRUE); default: + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "%p is not not an ACPI operand obj [%s]\n", object, acpi_ut_get_descriptor_name(object))); @@ -509,7 +511,6 @@ acpi_ut_get_simple_object_size(union acpi_operand_object *internal_object, switch (internal_object->reference.class) { case ACPI_REFCLASS_NAME: - /* * Get the actual length of the full pathname to this object. * The reference will be converted to the pathname to the object @@ -525,7 +526,6 @@ acpi_ut_get_simple_object_size(union acpi_operand_object *internal_object, break; default: - /* * No other reference opcodes are supported. * Notably, Locals and Args are not supported, but this may be @@ -585,7 +585,6 @@ acpi_ut_get_element_length(u8 object_type, switch (object_type) { case ACPI_COPY_TYPE_SIMPLE: - /* * Simple object - just get the size (Null object/entry is handled * here also) and sum it into the running package length diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c index 29459479148f..2b1ce4cd3207 100644 --- a/drivers/acpi/acpica/utpredef.c +++ b/drivers/acpi/acpica/utpredef.c @@ -147,6 +147,11 @@ void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes) u32 i; u32 j; + if (!expected_btypes) { + ACPI_STRCPY(buffer, "NONE"); + return; + } + j = 1; buffer[0] = 0; this_rtype = ACPI_RTYPE_INTEGER; @@ -328,9 +333,7 @@ static u32 acpi_ut_get_argument_types(char *buffer, u16 argument_types) /* First field in the types list is the count of args to follow */ - arg_count = (argument_types & METHOD_ARG_MASK); - argument_types >>= METHOD_ARG_BIT_WIDTH; - + arg_count = METHOD_GET_ARG_COUNT(argument_types); if (arg_count > METHOD_PREDEF_ARGS_MAX) { printf("**** Invalid argument count (%u) " "in predefined info structure\n", arg_count); @@ -340,7 +343,8 @@ static u32 acpi_ut_get_argument_types(char *buffer, u16 argument_types) /* Get each argument from the list, convert to ascii, store to buffer */ for (i = 0; i < arg_count; i++) { - this_argument_type = (argument_types & METHOD_ARG_MASK); + this_argument_type = METHOD_GET_NEXT_TYPE(argument_types); + if (!this_argument_type || (this_argument_type > METHOD_MAX_ARG_TYPE)) { printf("**** Invalid argument type (%u) " @@ -351,10 +355,6 @@ static u32 acpi_ut_get_argument_types(char *buffer, u16 argument_types) strcat(buffer, ut_external_type_names[this_argument_type] + sub_index); - - /* Shift to next argument type field */ - - argument_types >>= METHOD_ARG_BIT_WIDTH; sub_index = 0; } diff --git a/drivers/acpi/acpica/utstring.c b/drivers/acpi/acpica/utstring.c index b3e36a81aa4d..c53759b76a3f 100644 --- a/drivers/acpi/acpica/utstring.c +++ b/drivers/acpi/acpica/utstring.c @@ -186,10 +186,13 @@ acpi_status acpi_ut_strtoul64(char *string, u32 base, u64 *ret_integer) switch (base) { case ACPI_ANY_BASE: case 16: + break; default: + /* Invalid Base */ + return_ACPI_STATUS(AE_BAD_PARAMETER); } @@ -355,36 +358,44 @@ void acpi_ut_print_string(char *string, u8 max_length) switch (string[i]) { case 0x07: + acpi_os_printf("\\a"); /* BELL */ break; case 0x08: + acpi_os_printf("\\b"); /* BACKSPACE */ break; case 0x0C: + acpi_os_printf("\\f"); /* FORMFEED */ break; case 0x0A: + acpi_os_printf("\\n"); /* LINEFEED */ break; case 0x0D: + acpi_os_printf("\\r"); /* CARRIAGE RETURN */ break; case 0x09: + acpi_os_printf("\\t"); /* HORIZONTAL TAB */ break; case 0x0B: + acpi_os_printf("\\v"); /* VERTICAL TAB */ break; case '\'': /* Single Quote */ case '\"': /* Double Quote */ case '\\': /* Backslash */ + acpi_os_printf("\\%c", (int)string[i]); break; @@ -451,7 +462,8 @@ u8 acpi_ut_valid_acpi_char(char character, u32 position) * * FUNCTION: acpi_ut_valid_acpi_name * - * PARAMETERS: name - The name to be examined + * PARAMETERS: name - The name to be examined. Does not have to + * be NULL terminated string. * * RETURN: TRUE if the name is valid, FALSE otherwise * @@ -462,15 +474,14 @@ u8 acpi_ut_valid_acpi_char(char character, u32 position) * ******************************************************************************/ -u8 acpi_ut_valid_acpi_name(u32 name) +u8 acpi_ut_valid_acpi_name(char *name) { u32 i; ACPI_FUNCTION_ENTRY(); for (i = 0; i < ACPI_NAME_SIZE; i++) { - if (!acpi_ut_valid_acpi_char - ((ACPI_CAST_PTR(char, &name))[i], i)) { + if (!acpi_ut_valid_acpi_char(name[i], i)) { return (FALSE); } } diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c index 62774c7b76a8..160f13f4aab5 100644 --- a/drivers/acpi/acpica/uttrack.c +++ b/drivers/acpi/acpica/uttrack.c @@ -603,6 +603,7 @@ void acpi_ut_dump_allocations(u32 component, const char *module) switch (ACPI_GET_DESCRIPTOR_TYPE (descriptor)) { case ACPI_DESC_TYPE_OPERAND: + if (element->size == sizeof(union acpi_operand_object)) @@ -613,6 +614,7 @@ void acpi_ut_dump_allocations(u32 component, const char *module) break; case ACPI_DESC_TYPE_PARSER: + if (element->size == sizeof(union acpi_parse_object)) { @@ -622,6 +624,7 @@ void acpi_ut_dump_allocations(u32 component, const char *module) break; case ACPI_DESC_TYPE_NAMED: + if (element->size == sizeof(struct acpi_namespace_node)) @@ -632,6 +635,7 @@ void acpi_ut_dump_allocations(u32 component, const char *module) break; default: + break; } @@ -639,6 +643,7 @@ void acpi_ut_dump_allocations(u32 component, const char *module) switch (descriptor_type) { case ACPI_DESC_TYPE_OPERAND: + acpi_os_printf ("%12.12s RefCount 0x%04X\n", acpi_ut_get_type_name @@ -649,6 +654,7 @@ void acpi_ut_dump_allocations(u32 component, const char *module) break; case ACPI_DESC_TYPE_PARSER: + acpi_os_printf ("AmlOpcode 0x%04hX\n", descriptor->op.asl. @@ -656,6 +662,7 @@ void acpi_ut_dump_allocations(u32 component, const char *module) break; case ACPI_DESC_TYPE_NAMED: + acpi_os_printf("%4.4s\n", acpi_ut_get_node_name (&descriptor-> @@ -663,6 +670,7 @@ void acpi_ut_dump_allocations(u32 component, const char *module) break; default: + acpi_os_printf("\n"); break; } diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c index 976b6c734fce..e966a2e47b76 100644 --- a/drivers/acpi/acpica/utxferror.c +++ b/drivers/acpi/acpica/utxferror.c @@ -44,7 +44,6 @@ #include <linux/export.h> #include <acpi/acpi.h> #include "accommon.h" -#include "acnamesp.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utxferror") @@ -52,43 +51,7 @@ ACPI_MODULE_NAME("utxferror") /* * This module is used for the in-kernel ACPICA as well as the ACPICA * tools/applications. - * - * For the iASL compiler case, the output is redirected to stderr so that - * any of the various ACPI errors and warnings do not appear in the output - * files, for either the compiler or disassembler portions of the tool. */ -#ifdef ACPI_ASL_COMPILER -#include <stdio.h> -extern FILE *acpi_gbl_output_file; - -#define ACPI_MSG_REDIRECT_BEGIN \ - FILE *output_file = acpi_gbl_output_file; \ - acpi_os_redirect_output (stderr); - -#define ACPI_MSG_REDIRECT_END \ - acpi_os_redirect_output (output_file); - -#else -/* - * non-iASL case - no redirection, nothing to do - */ -#define ACPI_MSG_REDIRECT_BEGIN -#define ACPI_MSG_REDIRECT_END -#endif -/* - * Common message prefixes - */ -#define ACPI_MSG_ERROR "ACPI Error: " -#define ACPI_MSG_EXCEPTION "ACPI Exception: " -#define ACPI_MSG_WARNING "ACPI Warning: " -#define ACPI_MSG_INFO "ACPI: " -#define ACPI_MSG_BIOS_ERROR "ACPI BIOS Bug: Error: " -#define ACPI_MSG_BIOS_WARNING "ACPI BIOS Bug: Warning: " -/* - * Common message suffix - */ -#define ACPI_MSG_SUFFIX \ - acpi_os_printf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number) /******************************************************************************* * * FUNCTION: acpi_error @@ -285,200 +248,3 @@ acpi_bios_warning(const char *module_name, } ACPI_EXPORT_SYMBOL(acpi_bios_warning) - -/* - * The remainder of this module contains internal error functions that may - * be configured out. - */ -#if !defined (ACPI_NO_ERROR_MESSAGES) && !defined (ACPI_BIN_APP) -/******************************************************************************* - * - * FUNCTION: acpi_ut_predefined_warning - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * pathname - Full pathname to the node - * node_flags - From Namespace node for the method/object - * format - Printf format string + additional args - * - * RETURN: None - * - * DESCRIPTION: Warnings for the predefined validation module. Messages are - * only emitted the first time a problem with a particular - * method/object is detected. This prevents a flood of error - * messages for methods that are repeatedly evaluated. - * - ******************************************************************************/ -void ACPI_INTERNAL_VAR_XFACE -acpi_ut_predefined_warning(const char *module_name, - u32 line_number, - char *pathname, - u8 node_flags, const char *format, ...) -{ - va_list arg_list; - - /* - * Warning messages for this method/object will be disabled after the - * first time a validation fails or an object is successfully repaired. - */ - if (node_flags & ANOBJ_EVALUATED) { - return; - } - - acpi_os_printf(ACPI_MSG_WARNING "For %s: ", pathname); - - va_start(arg_list, format); - acpi_os_vprintf(format, arg_list); - ACPI_MSG_SUFFIX; - va_end(arg_list); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_predefined_info - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * pathname - Full pathname to the node - * node_flags - From Namespace node for the method/object - * format - Printf format string + additional args - * - * RETURN: None - * - * DESCRIPTION: Info messages for the predefined validation module. Messages - * are only emitted the first time a problem with a particular - * method/object is detected. This prevents a flood of - * messages for methods that are repeatedly evaluated. - * - ******************************************************************************/ - -void ACPI_INTERNAL_VAR_XFACE -acpi_ut_predefined_info(const char *module_name, - u32 line_number, - char *pathname, u8 node_flags, const char *format, ...) -{ - va_list arg_list; - - /* - * Warning messages for this method/object will be disabled after the - * first time a validation fails or an object is successfully repaired. - */ - if (node_flags & ANOBJ_EVALUATED) { - return; - } - - acpi_os_printf(ACPI_MSG_INFO "For %s: ", pathname); - - va_start(arg_list, format); - acpi_os_vprintf(format, arg_list); - ACPI_MSG_SUFFIX; - va_end(arg_list); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_namespace_error - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * internal_name - Name or path of the namespace node - * lookup_status - Exception code from NS lookup - * - * RETURN: None - * - * DESCRIPTION: Print error message with the full pathname for the NS node. - * - ******************************************************************************/ - -void -acpi_ut_namespace_error(const char *module_name, - u32 line_number, - const char *internal_name, acpi_status lookup_status) -{ - acpi_status status; - u32 bad_name; - char *name = NULL; - - ACPI_MSG_REDIRECT_BEGIN; - acpi_os_printf(ACPI_MSG_ERROR); - - if (lookup_status == AE_BAD_CHARACTER) { - - /* There is a non-ascii character in the name */ - - ACPI_MOVE_32_TO_32(&bad_name, - ACPI_CAST_PTR(u32, internal_name)); - acpi_os_printf("[0x%.8X] (NON-ASCII)", bad_name); - } else { - /* Convert path to external format */ - - status = acpi_ns_externalize_name(ACPI_UINT32_MAX, - internal_name, NULL, &name); - - /* Print target name */ - - if (ACPI_SUCCESS(status)) { - acpi_os_printf("[%s]", name); - } else { - acpi_os_printf("[COULD NOT EXTERNALIZE NAME]"); - } - - if (name) { - ACPI_FREE(name); - } - } - - acpi_os_printf(" Namespace lookup failure, %s", - acpi_format_exception(lookup_status)); - - ACPI_MSG_SUFFIX; - ACPI_MSG_REDIRECT_END; -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_method_error - * - * PARAMETERS: module_name - Caller's module name (for error output) - * line_number - Caller's line number (for error output) - * message - Error message to use on failure - * prefix_node - Prefix relative to the path - * path - Path to the node (optional) - * method_status - Execution status - * - * RETURN: None - * - * DESCRIPTION: Print error message with the full pathname for the method. - * - ******************************************************************************/ - -void -acpi_ut_method_error(const char *module_name, - u32 line_number, - const char *message, - struct acpi_namespace_node *prefix_node, - const char *path, acpi_status method_status) -{ - acpi_status status; - struct acpi_namespace_node *node = prefix_node; - - ACPI_MSG_REDIRECT_BEGIN; - acpi_os_printf(ACPI_MSG_ERROR); - - if (path) { - status = - acpi_ns_get_node(prefix_node, path, ACPI_NS_NO_UPSEARCH, - &node); - if (ACPI_FAILURE(status)) { - acpi_os_printf("[Could not get node by pathname]"); - } - } - - acpi_ns_print_node_pathname(node, message); - acpi_os_printf(", %s", acpi_format_exception(method_status)); - - ACPI_MSG_SUFFIX; - ACPI_MSG_REDIRECT_END; -} - -#endif /* ACPI_NO_ERROR_MESSAGES */ diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 6d894bfd8b8f..f7b3b39e94fc 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -1180,20 +1180,28 @@ static int __init erst_init(void) if (!erst_erange.vaddr) goto err_release_erange; + pr_info(ERST_PFX + "Error Record Serialization Table (ERST) support is initialized.\n"); + buf = kmalloc(erst_erange.size, GFP_KERNEL); spin_lock_init(&erst_info.buf_lock); if (buf) { erst_info.buf = buf + sizeof(struct cper_pstore_record); erst_info.bufsize = erst_erange.size - sizeof(struct cper_pstore_record); - if (pstore_register(&erst_info)) { - pr_info(ERST_PFX "Could not register with persistent store\n"); + rc = pstore_register(&erst_info); + if (rc) { + if (rc != -EPERM) + pr_info(ERST_PFX + "Could not register with persistent store\n"); + erst_info.buf = NULL; + erst_info.bufsize = 0; kfree(buf); } - } - - pr_info(ERST_PFX - "Error Record Serialization Table (ERST) support is initialized.\n"); + } else + pr_err(ERST_PFX + "Failed to allocate %lld bytes for persistent store error log\n", + erst_erange.size); return 0; diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index fcd7d91cec34..ec9b57d428a1 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -449,9 +449,19 @@ static void ghes_do_proc(struct ghes *ghes, pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { unsigned int devfn; int aer_severity; + devfn = PCI_DEVFN(pcie_err->device_id.device, pcie_err->device_id.function); aer_severity = cper_severity_to_aer(sev); + + /* + * If firmware reset the component to contain + * the error, we must reinitialize it before + * use, so treat it as a fatal AER error. + */ + if (gdata->flags & CPER_SEC_RESET) + aer_severity = AER_FATAL; + aer_recover_queue(pcie_err->device_id.segment, pcie_err->device_id.bus, devfn, aer_severity, diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index e7100459ac4a..082b4dd252a8 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -425,7 +425,7 @@ static int acpi_battery_get_info(struct acpi_battery *battery) { int result = -EFAULT; acpi_status status = 0; - char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags)? + char *name = test_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags) ? "_BIX" : "_BIF"; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -661,11 +661,11 @@ static void find_battery(const struct dmi_header *dm, void *private) static void acpi_battery_quirks(struct acpi_battery *battery) { if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)) - return ; + return; - if (battery->full_charge_capacity == 100 && - battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN && - battery->capacity_now >=0 && battery->capacity_now <= 100) { + if (battery->full_charge_capacity == 100 && + battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN && + battery->capacity_now >= 0 && battery->capacity_now <= 100) { set_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags); battery->full_charge_capacity = battery->design_capacity; battery->capacity_now = (battery->capacity_now * @@ -673,7 +673,7 @@ static void acpi_battery_quirks(struct acpi_battery *battery) } if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH, &battery->flags)) - return ; + return; if (battery->power_unit && dmi_name_in_vendors("LENOVO")) { const char *s; @@ -761,7 +761,7 @@ static int acpi_battery_print_info(struct seq_file *seq, int result) goto end; seq_printf(seq, "present: %s\n", - acpi_battery_present(battery)?"yes":"no"); + acpi_battery_present(battery) ? "yes" : "no"); if (!acpi_battery_present(battery)) goto end; if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) @@ -817,12 +817,12 @@ static int acpi_battery_print_state(struct seq_file *seq, int result) goto end; seq_printf(seq, "present: %s\n", - acpi_battery_present(battery)?"yes":"no"); + acpi_battery_present(battery) ? "yes" : "no"); if (!acpi_battery_present(battery)) goto end; seq_printf(seq, "capacity state: %s\n", - (battery->state & 0x04)?"critical":"ok"); + (battery->state & 0x04) ? "critical" : "ok"); if ((battery->state & 0x01) && (battery->state & 0x02)) seq_printf(seq, "charging state: charging/discharging\n"); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 292de3cab9cc..a5bb33bab448 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -91,8 +91,7 @@ static struct dmi_system_id dsdt_dmi_table[] __initdata = { int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) { - acpi_status status = AE_OK; - + acpi_status status; if (!device) return -EINVAL; @@ -162,7 +161,7 @@ EXPORT_SYMBOL(acpi_bus_private_data_handler); int acpi_bus_get_private_data(acpi_handle handle, void **data) { - acpi_status status = AE_OK; + acpi_status status; if (!*data) return -EINVAL; @@ -361,7 +360,7 @@ extern int event_is_open; int acpi_bus_generate_proc_event4(const char *device_class, const char *bus_id, u8 type, int data) { struct acpi_bus_event *event; - unsigned long flags = 0; + unsigned long flags; /* drop event on the floor if no one's listening */ if (!event_is_open) @@ -400,7 +399,7 @@ EXPORT_SYMBOL(acpi_bus_generate_proc_event); int acpi_bus_receive_event(struct acpi_bus_event *event) { - unsigned long flags = 0; + unsigned long flags; struct acpi_bus_event *entry = NULL; DECLARE_WAITQUEUE(wait, current); @@ -593,7 +592,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) static int __init acpi_bus_init_irq(void) { - acpi_status status = AE_OK; + acpi_status status; union acpi_object arg = { ACPI_TYPE_INTEGER }; struct acpi_object_list arg_list = { 1, &arg }; char *message = NULL; @@ -640,7 +639,7 @@ u8 acpi_gbl_permanent_mmap; void __init acpi_early_init(void) { - acpi_status status = AE_OK; + acpi_status status; if (acpi_disabled) return; @@ -714,8 +713,8 @@ void __init acpi_early_init(void) static int __init acpi_bus_init(void) { - int result = 0; - acpi_status status = AE_OK; + int result; + acpi_status status; extern acpi_status acpi_os_initialize1(void); acpi_os_initialize1(); diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 31c217a42839..e9e8bb24785b 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -419,62 +419,73 @@ bool acpi_bus_can_wakeup(acpi_handle handle) EXPORT_SYMBOL(acpi_bus_can_wakeup); /** - * acpi_device_power_state - Get preferred power state of ACPI device. + * acpi_dev_pm_get_state - Get preferred power state of ACPI device. * @dev: Device whose preferred target power state to return. * @adev: ACPI device node corresponding to @dev. * @target_state: System state to match the resultant device state. - * @d_max_in: Deepest low-power state to take into consideration. - * @d_min_p: Location to store the upper limit of the allowed states range. - * Return value: Preferred power state of the device on success, -ENODEV - * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure + * @d_min_p: Location to store the highest power state available to the device. + * @d_max_p: Location to store the lowest power state available to the device. * - * Find the lowest power (highest number) ACPI device power state that the - * device can be in while the system is in the state represented by - * @target_state. If @d_min_p is set, the highest power (lowest number) device - * power state that @dev can be in for the given system sleep state is stored - * at the location pointed to by it. + * Find the lowest power (highest number) and highest power (lowest number) ACPI + * device power states that the device can be in while the system is in the + * state represented by @target_state. Store the integer numbers representing + * those stats in the memory locations pointed to by @d_max_p and @d_min_p, + * respectively. * * Callers must ensure that @dev and @adev are valid pointers and that @adev * actually corresponds to @dev before using this function. + * + * Returns 0 on success or -ENODATA when one of the ACPI methods fails or + * returns a value that doesn't make sense. The memory locations pointed to by + * @d_max_p and @d_min_p are only modified on success. */ -int acpi_device_power_state(struct device *dev, struct acpi_device *adev, - u32 target_state, int d_max_in, int *d_min_p) +static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, + u32 target_state, int *d_min_p, int *d_max_p) { - char acpi_method[] = "_SxD"; - unsigned long long d_min, d_max; + char method[] = { '_', 'S', '0' + target_state, 'D', '\0' }; + acpi_handle handle = adev->handle; + unsigned long long ret; + int d_min, d_max; bool wakeup = false; + acpi_status status; - if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3) - return -EINVAL; - - if (d_max_in > ACPI_STATE_D3_HOT) { - enum pm_qos_flags_status stat; - - stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); - if (stat == PM_QOS_FLAGS_ALL) - d_max_in = ACPI_STATE_D3_HOT; - } - - acpi_method[2] = '0' + target_state; /* - * If the sleep state is S0, the lowest limit from ACPI is D3, - * but if the device has _S0W, we will use the value from _S0W - * as the lowest limit from ACPI. Finally, we will constrain - * the lowest limit with the specified one. + * If the system state is S0, the lowest power state the device can be + * in is D3cold, unless the device has _S0W and is supposed to signal + * wakeup, in which case the return value of _S0W has to be used as the + * lowest power state available to the device. */ d_min = ACPI_STATE_D0; - d_max = ACPI_STATE_D3; + d_max = ACPI_STATE_D3_COLD; /* * If present, _SxD methods return the minimum D-state (highest power * state) we can use for the corresponding S-states. Otherwise, the * minimum D-state is D0 (ACPI 3.x). - * - * NOTE: We rely on acpi_evaluate_integer() not clobbering the integer - * provided -- that's our fault recovery, we ignore retval. */ if (target_state > ACPI_STATE_S0) { - acpi_evaluate_integer(adev->handle, acpi_method, NULL, &d_min); + /* + * We rely on acpi_evaluate_integer() not clobbering the integer + * provided if AE_NOT_FOUND is returned. + */ + ret = d_min; + status = acpi_evaluate_integer(handle, method, NULL, &ret); + if ((ACPI_FAILURE(status) && status != AE_NOT_FOUND) + || ret > ACPI_STATE_D3_COLD) + return -ENODATA; + + /* + * We need to handle legacy systems where D3hot and D3cold are + * the same and 3 is returned in both cases, so fall back to + * D3cold if D3hot is not a valid state. + */ + if (!adev->power.states[ret].flags.valid) { + if (ret == ACPI_STATE_D3_HOT) + ret = ACPI_STATE_D3_COLD; + else + return -ENODATA; + } + d_min = ret; wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid && adev->wakeup.sleep_state >= target_state; } else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != @@ -490,38 +501,30 @@ int acpi_device_power_state(struct device *dev, struct acpi_device *adev, * can wake the system. _S0W may be valid, too. */ if (wakeup) { - acpi_status status; - - acpi_method[3] = 'W'; - status = acpi_evaluate_integer(adev->handle, acpi_method, NULL, - &d_max); - if (ACPI_FAILURE(status)) { - if (target_state != ACPI_STATE_S0 || - status != AE_NOT_FOUND) + method[3] = 'W'; + status = acpi_evaluate_integer(handle, method, NULL, &ret); + if (status == AE_NOT_FOUND) { + if (target_state > ACPI_STATE_S0) d_max = d_min; - } else if (d_max < d_min) { - /* Warn the user of the broken DSDT */ - printk(KERN_WARNING "ACPI: Wrong value from %s\n", - acpi_method); - /* Sanitize it */ - d_min = d_max; + } else if (ACPI_SUCCESS(status) && ret <= ACPI_STATE_D3_COLD) { + /* Fall back to D3cold if ret is not a valid state. */ + if (!adev->power.states[ret].flags.valid) + ret = ACPI_STATE_D3_COLD; + + d_max = ret > d_min ? ret : d_min; + } else { + return -ENODATA; } } - if (d_max_in < d_min) - return -EINVAL; if (d_min_p) *d_min_p = d_min; - /* constrain d_max with specified lowest limit (max number) */ - if (d_max > d_max_in) { - for (d_max = d_max_in; d_max > d_min; d_max--) { - if (adev->power.states[d_max].flags.valid) - break; - } - } - return d_max; + + if (d_max_p) + *d_max_p = d_max; + + return 0; } -EXPORT_SYMBOL_GPL(acpi_device_power_state); /** * acpi_pm_device_sleep_state - Get preferred power state of ACPI device. @@ -529,7 +532,8 @@ EXPORT_SYMBOL_GPL(acpi_device_power_state); * @d_min_p: Location to store the upper limit of the allowed states range. * @d_max_in: Deepest low-power state to take into consideration. * Return value: Preferred power state of the device on success, -ENODEV - * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure + * if there's no 'struct acpi_device' for @dev, -EINVAL if @d_max_in is + * incorrect, or -ENODATA on ACPI method failure. * * The caller must ensure that @dev is valid before using this function. */ @@ -537,14 +541,43 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) { acpi_handle handle = DEVICE_ACPI_HANDLE(dev); struct acpi_device *adev; + int ret, d_min, d_max; + + if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) + return -EINVAL; + + if (d_max_in > ACPI_STATE_D3_HOT) { + enum pm_qos_flags_status stat; + + stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); + if (stat == PM_QOS_FLAGS_ALL) + d_max_in = ACPI_STATE_D3_HOT; + } if (!handle || acpi_bus_get_device(handle, &adev)) { dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); return -ENODEV; } - return acpi_device_power_state(dev, adev, acpi_target_system_state(), - d_max_in, d_min_p); + ret = acpi_dev_pm_get_state(dev, adev, acpi_target_system_state(), + &d_min, &d_max); + if (ret) + return ret; + + if (d_max_in < d_min) + return -EINVAL; + + if (d_max > d_max_in) { + for (d_max = d_max_in; d_max > d_min; d_max--) { + if (adev->power.states[d_max].flags.valid) + break; + } + } + + if (d_min_p) + *d_min_p = d_min; + + return d_max; } EXPORT_SYMBOL(acpi_pm_device_sleep_state); @@ -695,17 +728,13 @@ struct acpi_device *acpi_dev_pm_get_node(struct device *dev) static int acpi_dev_pm_low_power(struct device *dev, struct acpi_device *adev, u32 system_state) { - int power_state; + int ret, state; if (!acpi_device_power_manageable(adev)) return 0; - power_state = acpi_device_power_state(dev, adev, system_state, - ACPI_STATE_D3, NULL); - if (power_state < ACPI_STATE_D0 || power_state > ACPI_STATE_D3) - return -EIO; - - return acpi_device_set_power(adev, power_state); + ret = acpi_dev_pm_get_state(dev, adev, system_state, NULL, &state); + return ret ? ret : acpi_device_set_power(adev, state); } /** @@ -908,7 +937,6 @@ static struct dev_pm_domain acpi_general_pm_domain = { #ifdef CONFIG_PM_RUNTIME .runtime_suspend = acpi_subsys_runtime_suspend, .runtime_resume = acpi_subsys_runtime_resume, - .runtime_idle = pm_generic_runtime_idle, #endif #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index edc00818c803..80403c1a89f8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -983,6 +983,10 @@ static struct dmi_system_id __initdata ec_dmi_table[] = { ec_enlarge_storm_threshold, "CLEVO hardware", { DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."), DMI_MATCH(DMI_PRODUCT_NAME, "M720T/M730T"),}, NULL}, + { + ec_skip_dsdt_scan, "HP Folio 13", { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13"),}, NULL}, {}, }; diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c index 7586544fddb4..4e7b798900f2 100644 --- a/drivers/acpi/ec_sys.c +++ b/drivers/acpi/ec_sys.c @@ -12,6 +12,7 @@ #include <linux/acpi.h> #include <linux/debugfs.h> #include <linux/module.h> +#include <linux/uaccess.h> #include "internal.h" MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); @@ -34,7 +35,6 @@ static ssize_t acpi_ec_read_io(struct file *f, char __user *buf, * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; */ unsigned int size = EC_SPACE_SIZE; - u8 *data = (u8 *) buf; loff_t init_off = *off; int err = 0; @@ -47,9 +47,15 @@ static ssize_t acpi_ec_read_io(struct file *f, char __user *buf, size = count; while (size) { - err = ec_read(*off, &data[*off - init_off]); + u8 byte_read; + err = ec_read(*off, &byte_read); if (err) return err; + if (put_user(byte_read, buf + *off - init_off)) { + if (*off - init_off) + return *off - init_off; /* partial read */ + return -EFAULT; + } *off += 1; size--; } @@ -65,7 +71,6 @@ static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, unsigned int size = count; loff_t init_off = *off; - u8 *data = (u8 *) buf; int err = 0; if (*off >= EC_SPACE_SIZE) @@ -76,7 +81,12 @@ static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, } while (size) { - u8 byte_write = data[*off - init_off]; + u8 byte_write; + if (get_user(byte_write, buf + *off - init_off)) { + if (*off - init_off) + return *off - init_off; /* partial write */ + return -EFAULT; + } err = ec_write(*off, byte_write); if (err) return err; diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 40a84cc6740c..f68095756fb7 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -81,13 +81,15 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) static acpi_status do_acpi_find_child(acpi_handle handle, u32 lvl_not_used, void *addr_p, void **ret_p) { - unsigned long long addr; + unsigned long long addr, sta; acpi_status status; status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); if (ACPI_SUCCESS(status) && addr == *((u64 *)addr_p)) { *ret_p = handle; - return AE_CTRL_TERMINATE; + status = acpi_bus_get_status_handle(handle, &sta); + if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_ENABLED)) + return AE_CTRL_TERMINATE; } return AE_OK; } @@ -105,7 +107,7 @@ acpi_handle acpi_get_child(acpi_handle parent, u64 address) } EXPORT_SYMBOL(acpi_get_child); -static int acpi_bind_one(struct device *dev, acpi_handle handle) +int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; acpi_status status; @@ -188,8 +190,9 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) kfree(physical_node); goto err; } +EXPORT_SYMBOL_GPL(acpi_bind_one); -static int acpi_unbind_one(struct device *dev) +int acpi_unbind_one(struct device *dev) { struct acpi_device_physical_node *entry; struct acpi_device *acpi_dev; @@ -238,6 +241,7 @@ err: dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); return -EINVAL; } +EXPORT_SYMBOL_GPL(acpi_unbind_one); static int acpi_platform_notify(struct device *dev) { diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index c610a76d92c4..3a50a34fe176 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -33,6 +33,7 @@ static inline void acpi_pci_slot_init(void) { } void acpi_pci_root_init(void); void acpi_pci_link_init(void); void acpi_pci_root_hp_init(void); +void acpi_processor_init(void); void acpi_platform_init(void); int acpi_sysfs_init(void); #ifdef CONFIG_ACPI_CONTAINER @@ -50,6 +51,13 @@ void acpi_memory_hotplug_init(void); #else static inline void acpi_memory_hotplug_init(void) {} #endif +#ifdef CONFIG_X86 +void acpi_cmos_rtc_init(void); +#else +static inline void acpi_cmos_rtc_init(void) {} +#endif + +extern bool acpi_force_hot_remove; void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, const char *name); @@ -81,6 +89,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, unsigned long long sta); void acpi_device_add_finalize(struct acpi_device *device); void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); +int acpi_bind_one(struct device *dev, acpi_handle handle); +int acpi_unbind_one(struct device *dev); /* -------------------------------------------------------------------------- Power Resource diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index e72186340fec..6ab2c3505520 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -835,19 +835,9 @@ void acpi_os_stall(u32 us) */ u64 acpi_os_get_timer(void) { - static u64 t; - -#ifdef CONFIG_HPET - /* TBD: use HPET if available */ -#endif - -#ifdef CONFIG_X86_PM_TIMER - /* TBD: default to PM timer if HPET was not available */ -#endif - if (!t) - printk(KERN_ERR PREFIX "acpi_os_get_timer() TBD\n"); - - return ++t; + u64 time_ns = ktime_to_ns(ktime_get()); + do_div(time_ns, 100); + return time_ns; } acpi_status acpi_os_read_port(acpi_io_address port, u32 * value, u32 width) @@ -1715,6 +1705,17 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) } #endif +static int __init acpi_no_auto_ssdt_setup(char *s) +{ + printk(KERN_NOTICE PREFIX "SSDT auto-load disabled\n"); + + acpi_gbl_disable_ssdt_table_load = TRUE; + + return 1; +} + +__setup("acpi_no_auto_ssdt", acpi_no_auto_ssdt_setup); + acpi_status __init acpi_os_initialize(void) { acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1a_event_block); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index e427dc516c76..5917839321b8 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -65,10 +65,6 @@ static struct acpi_scan_handler pci_root_handler = { .detach = acpi_pci_root_remove, }; -/* Lock to protect both acpi_pci_roots lists */ -static DEFINE_MUTEX(acpi_pci_root_lock); -static LIST_HEAD(acpi_pci_roots); - static DEFINE_MUTEX(osc_lock); /** @@ -100,13 +96,12 @@ get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) { struct resource *res = data; struct acpi_resource_address64 address; + acpi_status status; - if (resource->type != ACPI_RESOURCE_TYPE_ADDRESS16 && - resource->type != ACPI_RESOURCE_TYPE_ADDRESS32 && - resource->type != ACPI_RESOURCE_TYPE_ADDRESS64) + status = acpi_resource_to_address64(resource, &address); + if (ACPI_FAILURE(status)) return AE_OK; - acpi_resource_to_address64(resource, &address); if ((address.address_length > 0) && (address.resource_type == ACPI_BUS_NUMBER_RANGE)) { res->start = address.minimum; @@ -382,23 +377,24 @@ static int acpi_pci_root_add(struct acpi_device *device, int result; struct acpi_pci_root *root; u32 flags, base_flags; + acpi_handle handle = device->handle; root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); if (!root) return -ENOMEM; segment = 0; - status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL, + status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segment); if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { - printk(KERN_ERR PREFIX "can't evaluate _SEG\n"); + dev_err(&device->dev, "can't evaluate _SEG\n"); result = -ENODEV; goto end; } /* Check _CRS first, then _BBN. If no _BBN, default to zero. */ root->secondary.flags = IORESOURCE_BUS; - status = try_get_root_bridge_busnr(device->handle, &root->secondary); + status = try_get_root_bridge_busnr(handle, &root->secondary); if (ACPI_FAILURE(status)) { /* * We need both the start and end of the downstream bus range @@ -407,33 +403,32 @@ static int acpi_pci_root_add(struct acpi_device *device, * can do is assume [_BBN-0xFF] or [0-0xFF]. */ root->secondary.end = 0xFF; - printk(KERN_WARNING FW_BUG PREFIX - "no secondary bus range in _CRS\n"); - status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, + dev_warn(&device->dev, + FW_BUG "no secondary bus range in _CRS\n"); + status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &bus); if (ACPI_SUCCESS(status)) root->secondary.start = bus; else if (status == AE_NOT_FOUND) root->secondary.start = 0; else { - printk(KERN_ERR PREFIX "can't evaluate _BBN\n"); + dev_err(&device->dev, "can't evaluate _BBN\n"); result = -ENODEV; goto end; } } - INIT_LIST_HEAD(&root->node); root->device = device; root->segment = segment & 0xFFFF; strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); device->driver_data = root; - printk(KERN_INFO PREFIX "%s [%s] (domain %04x %pR)\n", + pr_info(PREFIX "%s [%s] (domain %04x %pR)\n", acpi_device_name(device), acpi_device_bid(device), root->segment, &root->secondary); - root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); + root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle); /* * All supported architectures that use ACPI have support for @@ -446,10 +441,6 @@ static int acpi_pci_root_add(struct acpi_device *device, * TBD: Need PCI interface for enumeration/configuration of roots. */ - mutex_lock(&acpi_pci_root_lock); - list_add_tail(&root->node, &acpi_pci_roots); - mutex_unlock(&acpi_pci_root_lock); - /* * Scan the Root Bridge * -------------------- @@ -459,11 +450,11 @@ static int acpi_pci_root_add(struct acpi_device *device, */ root->bus = pci_acpi_scan_root(root); if (!root->bus) { - printk(KERN_ERR PREFIX - "Bus %04x:%02x not present in PCI namespace\n", - root->segment, (unsigned int)root->secondary.start); + dev_err(&device->dev, + "Bus %04x:%02x not present in PCI namespace\n", + root->segment, (unsigned int)root->secondary.start); result = -ENODEV; - goto out_del_root; + goto end; } /* Indicate support for various _OSC capabilities. */ @@ -502,7 +493,7 @@ static int acpi_pci_root_add(struct acpi_device *device, dev_info(&device->dev, "Requesting ACPI _OSC control (0x%02x)\n", flags); - status = acpi_pci_osc_control_set(device->handle, &flags, + status = acpi_pci_osc_control_set(handle, &flags, OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); if (ACPI_SUCCESS(status)) { dev_info(&device->dev, @@ -519,8 +510,8 @@ static int acpi_pci_root_add(struct acpi_device *device, "ACPI _OSC request failed (%s), " "returned control mask: 0x%02x\n", acpi_format_exception(status), flags); - pr_info("ACPI _OSC control for PCIe not granted, " - "disabling ASPM\n"); + dev_info(&device->dev, + "ACPI _OSC control for PCIe not granted, disabling ASPM\n"); pcie_no_aspm(); } } else { @@ -536,20 +527,14 @@ static int acpi_pci_root_add(struct acpi_device *device, if (system_state != SYSTEM_BOOTING) { pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_bus_resources(root->bus); - } - /* need to after hot-added ioapic is registered */ - if (system_state != SYSTEM_BOOTING) + /* need to after hot-added ioapic is registered */ pci_enable_bridges(root->bus); + } pci_bus_add_devices(root->bus); return 1; -out_del_root: - mutex_lock(&acpi_pci_root_lock); - list_del(&root->node); - mutex_unlock(&acpi_pci_root_lock); - end: kfree(root); return result; @@ -566,9 +551,6 @@ static void acpi_pci_root_remove(struct acpi_device *device) pci_remove_root_bus(root->bus); - mutex_lock(&acpi_pci_root_lock); - list_del(&root->node); - mutex_unlock(&acpi_pci_root_lock); kfree(root); } @@ -588,12 +570,13 @@ static void handle_root_bridge_insertion(acpi_handle handle) struct acpi_device *device; if (!acpi_bus_get_device(handle, &device)) { - printk(KERN_DEBUG "acpi device exists...\n"); + dev_printk(KERN_DEBUG, &device->dev, + "acpi device already exists; ignoring notify\n"); return; } if (acpi_bus_scan(handle)) - printk(KERN_ERR "cannot add bridge to acpi list\n"); + acpi_handle_err(handle, "cannot add bridge to acpi list\n"); } static void handle_root_bridge_removal(struct acpi_device *device) @@ -622,7 +605,6 @@ static void handle_root_bridge_removal(struct acpi_device *device) static void _handle_hotplug_event_root(struct work_struct *work) { struct acpi_pci_root *root; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER }; struct acpi_hp_work *hp_work; acpi_handle handle; u32 type; @@ -634,13 +616,12 @@ static void _handle_hotplug_event_root(struct work_struct *work) acpi_scan_lock_acquire(); root = acpi_pci_find_root(handle); - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); switch (type) { case ACPI_NOTIFY_BUS_CHECK: /* bus enumerate */ - printk(KERN_DEBUG "%s: Bus check notify on %s\n", __func__, - (char *)buffer.pointer); + acpi_handle_printk(KERN_DEBUG, handle, + "Bus check notify on %s\n", __func__); if (root) acpiphp_check_host_bridge(handle); else @@ -650,28 +631,28 @@ static void _handle_hotplug_event_root(struct work_struct *work) case ACPI_NOTIFY_DEVICE_CHECK: /* device check */ - printk(KERN_DEBUG "%s: Device check notify on %s\n", __func__, - (char *)buffer.pointer); + acpi_handle_printk(KERN_DEBUG, handle, + "Device check notify on %s\n", __func__); if (!root) handle_root_bridge_insertion(handle); break; case ACPI_NOTIFY_EJECT_REQUEST: /* request device eject */ - printk(KERN_DEBUG "%s: Device eject notify on %s\n", __func__, - (char *)buffer.pointer); + acpi_handle_printk(KERN_DEBUG, handle, + "Device eject notify on %s\n", __func__); if (root) handle_root_bridge_removal(root->device); break; default: - printk(KERN_WARNING "notify_handler: unknown event type 0x%x for %s\n", - type, (char *)buffer.pointer); + acpi_handle_warn(handle, + "notify_handler: unknown event type 0x%x\n", + type); break; } acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ - kfree(buffer.pointer); } static void handle_hotplug_event_root(acpi_handle handle, u32 type, @@ -685,9 +666,6 @@ static acpi_status __init find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status; - char objname[64]; - struct acpi_buffer buffer = { .length = sizeof(objname), - .pointer = objname }; int *count = (int *)context; if (!acpi_is_root_bridge(handle)) @@ -695,16 +673,15 @@ find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) (*count)++; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, handle_hotplug_event_root, NULL); if (ACPI_FAILURE(status)) - printk(KERN_DEBUG "acpi root: %s notify handler is not installed, exit status: %u\n", - objname, (unsigned int)status); + acpi_handle_printk(KERN_DEBUG, handle, + "notify handler is not installed, exit status: %u\n", + (unsigned int)status); else - printk(KERN_DEBUG "acpi root: %s notify handler is installed\n", - objname); + acpi_handle_printk(KERN_DEBUG, handle, + "notify handler is installed\n"); return AE_OK; } diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index c266cdc11784..823be116619e 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -1,11 +1,13 @@ /* - * acpi_processor.c - ACPI Processor Driver ($Revision: 71 $) + * processor_driver.c - ACPI Processor Driver * * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> * - Added processor hotplug support + * Copyright (C) 2013, Intel Corporation + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -24,55 +26,26 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * TBD: - * 1. Make # power states dynamic. - * 2. Support duty_cycle values that span bit 4. - * 3. Optimize by having scheduler determine business instead of - * having us try to calculate it here. - * 4. Need C1 timing -- must modify kernel (IRQ handler) to get this. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/types.h> -#include <linux/pci.h> -#include <linux/pm.h> #include <linux/cpufreq.h> #include <linux/cpu.h> -#include <linux/dmi.h> -#include <linux/moduleparam.h> #include <linux/cpuidle.h> #include <linux/slab.h> #include <linux/acpi.h> -#include <linux/memory_hotplug.h> - -#include <asm/io.h> -#include <asm/cpu.h> -#include <asm/delay.h> -#include <asm/uaccess.h> -#include <asm/processor.h> -#include <asm/smp.h> -#include <asm/acpi.h> - -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> + #include <acpi/processor.h> +#include "internal.h" + #define PREFIX "ACPI: " -#define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_DEVICE_NAME "Processor" -#define ACPI_PROCESSOR_FILE_INFO "info" -#define ACPI_PROCESSOR_FILE_THROTTLING "throttling" -#define ACPI_PROCESSOR_FILE_LIMIT "limit" #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 #define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 -#define ACPI_PROCESSOR_DEVICE_HID "ACPI0007" - -#define ACPI_PROCESSOR_LIMIT_USER 0 -#define ACPI_PROCESSOR_LIMIT_THERMAL 1 #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_driver"); @@ -81,12 +54,8 @@ MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Processor Driver"); MODULE_LICENSE("GPL"); -static int acpi_processor_add(struct acpi_device *device); -static int acpi_processor_remove(struct acpi_device *device); -static void acpi_processor_notify(struct acpi_device *device, u32 event); -static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr); -static int acpi_processor_handle_eject(struct acpi_processor *pr); -static int acpi_processor_start(struct acpi_processor *pr); +static int acpi_processor_start(struct device *dev); +static int acpi_processor_stop(struct device *dev); static const struct acpi_device_id processor_device_ids[] = { {ACPI_PROCESSOR_OBJECT_HID, 0}, @@ -95,295 +64,24 @@ static const struct acpi_device_id processor_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); -static struct acpi_driver acpi_processor_driver = { +static struct device_driver acpi_processor_driver = { .name = "processor", - .class = ACPI_PROCESSOR_CLASS, - .ids = processor_device_ids, - .ops = { - .add = acpi_processor_add, - .remove = acpi_processor_remove, - .notify = acpi_processor_notify, - }, + .bus = &cpu_subsys, + .acpi_match_table = processor_device_ids, + .probe = acpi_processor_start, + .remove = acpi_processor_stop, }; -#define INSTALL_NOTIFY_HANDLER 1 -#define UNINSTALL_NOTIFY_HANDLER 2 - -DEFINE_PER_CPU(struct acpi_processor *, processors); -EXPORT_PER_CPU_SYMBOL(processors); - -struct acpi_processor_errata errata __read_mostly; - -/* -------------------------------------------------------------------------- - Errata Handling - -------------------------------------------------------------------------- */ - -static int acpi_processor_errata_piix4(struct pci_dev *dev) -{ - u8 value1 = 0; - u8 value2 = 0; - - - if (!dev) - return -EINVAL; - - /* - * Note that 'dev' references the PIIX4 ACPI Controller. - */ - - switch (dev->revision) { - case 0: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n")); - break; - case 1: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n")); - break; - case 2: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n")); - break; - case 3: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n")); - break; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n")); - break; - } - - switch (dev->revision) { - - case 0: /* PIIX4 A-step */ - case 1: /* PIIX4 B-step */ - /* - * See specification changes #13 ("Manual Throttle Duty Cycle") - * and #14 ("Enabling and Disabling Manual Throttle"), plus - * erratum #5 ("STPCLK# Deassertion Time") from the January - * 2002 PIIX4 specification update. Applies to only older - * PIIX4 models. - */ - errata.piix4.throttle = 1; - - case 2: /* PIIX4E */ - case 3: /* PIIX4M */ - /* - * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA - * Livelock") from the January 2002 PIIX4 specification update. - * Applies to all PIIX4 models. - */ - - /* - * BM-IDE - * ------ - * Find the PIIX4 IDE Controller and get the Bus Master IDE - * Status register address. We'll use this later to read - * each IDE controller's DMA status to make sure we catch all - * DMA activity. - */ - dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB, - PCI_ANY_ID, PCI_ANY_ID, NULL); - if (dev) { - errata.piix4.bmisx = pci_resource_start(dev, 4); - pci_dev_put(dev); - } - - /* - * Type-F DMA - * ---------- - * Find the PIIX4 ISA Controller and read the Motherboard - * DMA controller's status to see if Type-F (Fast) DMA mode - * is enabled (bit 7) on either channel. Note that we'll - * disable C3 support if this is enabled, as some legacy - * devices won't operate well if fast DMA is disabled. - */ - dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB_0, - PCI_ANY_ID, PCI_ANY_ID, NULL); - if (dev) { - pci_read_config_byte(dev, 0x76, &value1); - pci_read_config_byte(dev, 0x77, &value2); - if ((value1 & 0x80) || (value2 & 0x80)) - errata.piix4.fdma = 1; - pci_dev_put(dev); - } - - break; - } - - if (errata.piix4.bmisx) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Bus master activity detection (BM-IDE) erratum enabled\n")); - if (errata.piix4.fdma) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Type-F DMA livelock erratum (C3 disabled)\n")); - - return 0; -} - -static int acpi_processor_errata(struct acpi_processor *pr) +static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) { - int result = 0; - struct pci_dev *dev = NULL; - - - if (!pr) - return -EINVAL; - - /* - * PIIX4 - */ - dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, - PCI_ANY_ID, NULL); - if (dev) { - result = acpi_processor_errata_piix4(dev); - pci_dev_put(dev); - } - - return result; -} - -/* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ - -static int acpi_processor_get_info(struct acpi_device *device) -{ - acpi_status status = 0; - union acpi_object object = { 0 }; - struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; + struct acpi_device *device = data; struct acpi_processor *pr; - int cpu_index, device_declaration = 0; - static int cpu0_initialized; - - pr = acpi_driver_data(device); - if (!pr) - return -EINVAL; - - if (num_online_cpus() > 1) - errata.smp = TRUE; - - acpi_processor_errata(pr); - - /* - * Check to see if we have bus mastering arbitration control. This - * is required for proper C3 usage (to maintain cache coherency). - */ - if (acpi_gbl_FADT.pm2_control_block && acpi_gbl_FADT.pm2_control_length) { - pr->flags.bm_control = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Bus mastering arbitration control present\n")); - } else - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "No bus mastering arbitration control\n")); - - if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) { - /* Declared with "Processor" statement; match ProcessorID */ - status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); - if (ACPI_FAILURE(status)) { - dev_err(&device->dev, - "Failed to evaluate processor object (0x%x)\n", - status); - return -ENODEV; - } - - /* - * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. - * >>> 'acpi_get_processor_id(acpi_id, &id)' in - * arch/xxx/acpi.c - */ - pr->acpi_id = object.processor.proc_id; - } else { - /* - * Declared with "Device" statement; match _UID. - * Note that we don't handle string _UIDs yet. - */ - unsigned long long value; - status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID, - NULL, &value); - if (ACPI_FAILURE(status)) { - dev_err(&device->dev, - "Failed to evaluate processor _UID (0x%x)\n", - status); - return -ENODEV; - } - device_declaration = 1; - pr->acpi_id = value; - } - cpu_index = acpi_get_cpuid(pr->handle, device_declaration, pr->acpi_id); - - /* Handle UP system running SMP kernel, with no LAPIC in MADT */ - if (!cpu0_initialized && (cpu_index == -1) && - (num_online_cpus() == 1)) { - cpu_index = 0; - } - - cpu0_initialized = 1; - - pr->id = cpu_index; - - /* - * Extra Processor objects may be enumerated on MP systems with - * less than the max # of CPUs. They should be ignored _iff - * they are physically not present. - */ - if (pr->id == -1) { - if (ACPI_FAILURE(acpi_processor_hotadd_init(pr))) - return -ENODEV; - } - /* - * On some boxes several processors use the same processor bus id. - * But they are located in different scope. For example: - * \_SB.SCK0.CPU0 - * \_SB.SCK1.CPU0 - * Rename the processor device bus id. And the new bus id will be - * generated as the following format: - * CPU+CPU ID. - */ - sprintf(acpi_device_bid(device), "CPU%X", pr->id); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id, - pr->acpi_id)); - - if (!object.processor.pblk_address) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n")); - else if (object.processor.pblk_length != 6) - dev_err(&device->dev, "Invalid PBLK length [%d]\n", - object.processor.pblk_length); - else { - pr->throttling.address = object.processor.pblk_address; - pr->throttling.duty_offset = acpi_gbl_FADT.duty_offset; - pr->throttling.duty_width = acpi_gbl_FADT.duty_width; - - pr->pblk = object.processor.pblk_address; - - /* - * We don't care about error returns - we just try to mark - * these reserved so that nobody else is confused into thinking - * that this region might be unused.. - * - * (In particular, allocating the IO range for Cardbus) - */ - request_region(pr->throttling.address, 6, "ACPI CPU throttle"); - } - - /* - * If ACPI describes a slot number for this CPU, we can use it - * ensure we get the right value in the "physical id" field - * of /proc/cpuinfo - */ - status = acpi_evaluate_object(pr->handle, "_SUN", NULL, &buffer); - if (ACPI_SUCCESS(status)) - arch_fix_phys_package_id(pr->id, object.integer.value); - - return 0; -} - -static DEFINE_PER_CPU(void *, processor_device_array); - -static void acpi_processor_notify(struct acpi_device *device, u32 event) -{ - struct acpi_processor *pr = acpi_driver_data(device); int saved; + if (device->handle != handle) + return; + + pr = acpi_driver_data(device); if (!pr) return; @@ -420,55 +118,62 @@ static void acpi_processor_notify(struct acpi_device *device, u32 event) return; } -static int acpi_cpu_soft_notify(struct notifier_block *nfb, - unsigned long action, void *hcpu) +static __cpuinit int __acpi_processor_start(struct acpi_device *device); + +static int __cpuinit acpi_cpu_soft_notify(struct notifier_block *nfb, + unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; struct acpi_processor *pr = per_cpu(processors, cpu); + struct acpi_device *device; + + if (!pr || acpi_bus_get_device(pr->handle, &device)) + return NOTIFY_DONE; - if (action == CPU_ONLINE && pr) { - /* CPU got physically hotplugged and onlined the first time: - * Initialize missing things + if (action == CPU_ONLINE) { + /* + * CPU got physically hotplugged and onlined for the first time: + * Initialize missing things. */ if (pr->flags.need_hotplug_init) { + int ret; + pr_info("Will online and init hotplugged CPU: %d\n", pr->id); - WARN(acpi_processor_start(pr), "Failed to start CPU:" - " %d\n", pr->id); pr->flags.need_hotplug_init = 0; - /* Normal CPU soft online event */ + ret = __acpi_processor_start(device); + WARN(ret, "Failed to start CPU: %d\n", pr->id); } else { + /* Normal CPU soft online event. */ acpi_processor_ppc_has_changed(pr, 0); acpi_processor_hotplug(pr); acpi_processor_reevaluate_tstate(pr, action); acpi_processor_tstate_has_changed(pr); } - } - if (action == CPU_DEAD && pr) { - /* invalidate the flag.throttling after one CPU is offline */ + } else if (action == CPU_DEAD) { + /* Invalidate flag.throttling after the CPU is offline. */ acpi_processor_reevaluate_tstate(pr, action); } return NOTIFY_OK; } -static struct notifier_block acpi_cpu_notifier = +static struct notifier_block __refdata acpi_cpu_notifier = { .notifier_call = acpi_cpu_soft_notify, }; -/* - * acpi_processor_start() is called by the cpu_hotplug_notifier func: - * acpi_cpu_soft_notify(). Getting it __cpuinit{data} is difficult, the - * root cause seem to be that acpi_processor_uninstall_hotplug_notify() - * is in the module_exit (__exit) func. Allowing acpi_processor_start() - * to not be in __cpuinit section, but being called from __cpuinit funcs - * via __ref looks like the right thing to do here. - */ -static __ref int acpi_processor_start(struct acpi_processor *pr) +static __cpuinit int __acpi_processor_start(struct acpi_device *device) { - struct acpi_device *device = per_cpu(processor_device_array, pr->id); + struct acpi_processor *pr = acpi_driver_data(device); + acpi_status status; int result = 0; + if (!pr) + return -ENODEV; + + if (pr->flags.need_hotplug_init) + return 0; + #ifdef CONFIG_CPU_FREQ acpi_processor_ppc_has_changed(pr, 0); acpi_processor_load_module(pr); @@ -506,462 +211,95 @@ static __ref int acpi_processor_start(struct acpi_processor *pr) goto err_remove_sysfs_thermal; } - return 0; + status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_processor_notify, device); + if (ACPI_SUCCESS(status)) + return 0; -err_remove_sysfs_thermal: + sysfs_remove_link(&pr->cdev->device.kobj, "device"); + err_remove_sysfs_thermal: sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); -err_thermal_unregister: + err_thermal_unregister: thermal_cooling_device_unregister(pr->cdev); -err_power_exit: + err_power_exit: acpi_processor_power_exit(pr); - return result; } -/* - * Do not put anything in here which needs the core to be online. - * For example MSR access or setting up things which check for cpuinfo_x86 - * (cpu_data(cpu)) values, like CPU feature flags, family, model, etc. - * Such things have to be put in and set up above in acpi_processor_start() - */ -static int __cpuinit acpi_processor_add(struct acpi_device *device) +static int __cpuinit acpi_processor_start(struct device *dev) { - struct acpi_processor *pr = NULL; - int result = 0; - struct device *dev; - - pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); - if (!pr) - return -ENOMEM; - - if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) { - result = -ENOMEM; - goto err_free_pr; - } - - pr->handle = device->handle; - strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); - device->driver_data = pr; - - result = acpi_processor_get_info(device); - if (result) { - /* Processor is physically not present */ - return 0; - } + struct acpi_device *device; -#ifdef CONFIG_SMP - if (pr->id >= setup_max_cpus && pr->id != 0) - return 0; -#endif - - BUG_ON(pr->id >= nr_cpu_ids); - - /* - * Buggy BIOS check - * ACPI id of processors can be reported wrongly by the BIOS. - * Don't trust it blindly - */ - if (per_cpu(processor_device_array, pr->id) != NULL && - per_cpu(processor_device_array, pr->id) != device) { - dev_warn(&device->dev, - "BIOS reported wrong ACPI id %d for the processor\n", - pr->id); - result = -ENODEV; - goto err_free_cpumask; - } - per_cpu(processor_device_array, pr->id) = device; + if (acpi_bus_get_device(ACPI_HANDLE(dev), &device)) + return -ENODEV; - per_cpu(processors, pr->id) = pr; - - dev = get_cpu_device(pr->id); - if (sysfs_create_link(&device->dev.kobj, &dev->kobj, "sysdev")) { - result = -EFAULT; - goto err_clear_processor; - } - - /* - * Do not start hotplugged CPUs now, but when they - * are onlined the first time - */ - if (pr->flags.need_hotplug_init) - return 0; - - result = acpi_processor_start(pr); - if (result) - goto err_remove_sysfs; - - return 0; - -err_remove_sysfs: - sysfs_remove_link(&device->dev.kobj, "sysdev"); -err_clear_processor: - /* - * processor_device_array is not cleared to allow checks for buggy BIOS - */ - per_cpu(processors, pr->id) = NULL; -err_free_cpumask: - free_cpumask_var(pr->throttling.shared_cpu_map); -err_free_pr: - kfree(pr); - return result; + return __acpi_processor_start(device); } -static int acpi_processor_remove(struct acpi_device *device) +static int acpi_processor_stop(struct device *dev) { - struct acpi_processor *pr = NULL; + struct acpi_device *device; + struct acpi_processor *pr; + if (acpi_bus_get_device(ACPI_HANDLE(dev), &device)) + return 0; - if (!device || !acpi_driver_data(device)) - return -EINVAL; + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_processor_notify); pr = acpi_driver_data(device); - - if (pr->id >= nr_cpu_ids) - goto free; - - if (device->removal_type == ACPI_BUS_REMOVAL_EJECT) { - if (acpi_processor_handle_eject(pr)) - return -EINVAL; - } + if (!pr) + return 0; acpi_processor_power_exit(pr); - sysfs_remove_link(&device->dev.kobj, "sysdev"); - if (pr->cdev) { sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&pr->cdev->device.kobj, "device"); thermal_cooling_device_unregister(pr->cdev); pr->cdev = NULL; } - - per_cpu(processors, pr->id) = NULL; - per_cpu(processor_device_array, pr->id) = NULL; - try_offline_node(cpu_to_node(pr->id)); - -free: - free_cpumask_var(pr->throttling.shared_cpu_map); - kfree(pr); - return 0; } -#ifdef CONFIG_ACPI_HOTPLUG_CPU -/**************************************************************************** - * Acpi processor hotplug support * - ****************************************************************************/ - -static int is_processor_present(acpi_handle handle) -{ - acpi_status status; - unsigned long long sta = 0; - - - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - - if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_PRESENT)) - return 1; - - /* - * _STA is mandatory for a processor that supports hot plug - */ - if (status == AE_NOT_FOUND) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Processor does not support hot plug\n")); - else - ACPI_EXCEPTION((AE_INFO, status, - "Processor Device is not present")); - return 0; -} - -static void acpi_processor_hotplug_notify(acpi_handle handle, - u32 event, void *data) -{ - struct acpi_device *device = NULL; - struct acpi_eject_event *ej_event = NULL; - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ - acpi_status status; - int result; - - acpi_scan_lock_acquire(); - - switch (event) { - case ACPI_NOTIFY_BUS_CHECK: - case ACPI_NOTIFY_DEVICE_CHECK: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Processor driver received %s event\n", - (event == ACPI_NOTIFY_BUS_CHECK) ? - "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK")); - - if (!is_processor_present(handle)) - break; - - if (!acpi_bus_get_device(handle, &device)) - break; - - result = acpi_bus_scan(handle); - if (result) { - acpi_handle_err(handle, "Unable to add the device\n"); - break; - } - result = acpi_bus_get_device(handle, &device); - if (result) { - acpi_handle_err(handle, "Missing device object\n"); - break; - } - ost_code = ACPI_OST_SC_SUCCESS; - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "received ACPI_NOTIFY_EJECT_REQUEST\n")); - - if (acpi_bus_get_device(handle, &device)) { - acpi_handle_err(handle, - "Device don't exist, dropping EJECT\n"); - break; - } - if (!acpi_driver_data(device)) { - acpi_handle_err(handle, - "Driver data is NULL, dropping EJECT\n"); - break; - } - - ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); - if (!ej_event) { - acpi_handle_err(handle, "No memory, dropping EJECT\n"); - break; - } - - get_device(&device->dev); - ej_event->device = device; - ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; - /* The eject is carried out asynchronously. */ - status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, - ej_event); - if (ACPI_FAILURE(status)) { - put_device(&device->dev); - kfree(ej_event); - break; - } - goto out; - - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - - /* non-hotplug event; possibly handled by other handler */ - goto out; - } - - /* Inform firmware that the hotplug operation has completed */ - (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); - - out: - acpi_scan_lock_release(); -} - -static acpi_status is_processor_device(acpi_handle handle) -{ - struct acpi_device_info *info; - char *hid; - acpi_status status; - - status = acpi_get_object_info(handle, &info); - if (ACPI_FAILURE(status)) - return status; - - if (info->type == ACPI_TYPE_PROCESSOR) { - kfree(info); - return AE_OK; /* found a processor object */ - } - - if (!(info->valid & ACPI_VALID_HID)) { - kfree(info); - return AE_ERROR; - } - - hid = info->hardware_id.string; - if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) { - kfree(info); - return AE_ERROR; - } - - kfree(info); - return AE_OK; /* found a processor device object */ -} - -static acpi_status -processor_walk_namespace_cb(acpi_handle handle, - u32 lvl, void *context, void **rv) -{ - acpi_status status; - int *action = context; - - status = is_processor_device(handle); - if (ACPI_FAILURE(status)) - return AE_OK; /* not a processor; continue to walk */ - - switch (*action) { - case INSTALL_NOTIFY_HANDLER: - acpi_install_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - acpi_processor_hotplug_notify, - NULL); - break; - case UNINSTALL_NOTIFY_HANDLER: - acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - acpi_processor_hotplug_notify); - break; - default: - break; - } - - /* found a processor; skip walking underneath */ - return AE_CTRL_DEPTH; -} - -static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) -{ - acpi_handle handle = pr->handle; - - if (!is_processor_present(handle)) { - return AE_ERROR; - } - - if (acpi_map_lsapic(handle, &pr->id)) - return AE_ERROR; - - if (arch_register_cpu(pr->id)) { - acpi_unmap_lsapic(pr->id); - return AE_ERROR; - } - - /* CPU got hot-plugged, but cpu_data is not initialized yet - * Set flag to delay cpu_idle/throttling initialization - * in: - * acpi_processor_add() - * acpi_processor_get_info() - * and do it when the CPU gets online the first time - * TBD: Cleanup above functions and try to do this more elegant. - */ - pr_info("CPU %d got hotplugged\n", pr->id); - pr->flags.need_hotplug_init = 1; - - return AE_OK; -} - -static int acpi_processor_handle_eject(struct acpi_processor *pr) -{ - if (cpu_online(pr->id)) - cpu_down(pr->id); - - get_online_cpus(); - /* - * The cpu might become online again at this point. So we check whether - * the cpu has been onlined or not. If the cpu became online, it means - * that someone wants to use the cpu. So acpi_processor_handle_eject() - * returns -EAGAIN. - */ - if (unlikely(cpu_online(pr->id))) { - put_online_cpus(); - pr_warn("Failed to remove CPU %d, because other task " - "brought the CPU back online\n", pr->id); - return -EAGAIN; - } - arch_unregister_cpu(pr->id); - acpi_unmap_lsapic(pr->id); - put_online_cpus(); - return (0); -} -#else -static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) -{ - return AE_ERROR; -} -static int acpi_processor_handle_eject(struct acpi_processor *pr) -{ - return (-EINVAL); -} -#endif - -static -void acpi_processor_install_hotplug_notify(void) -{ -#ifdef CONFIG_ACPI_HOTPLUG_CPU - int action = INSTALL_NOTIFY_HANDLER; - acpi_walk_namespace(ACPI_TYPE_ANY, - ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, - processor_walk_namespace_cb, NULL, &action, NULL); -#endif - register_hotcpu_notifier(&acpi_cpu_notifier); -} - -static -void acpi_processor_uninstall_hotplug_notify(void) -{ -#ifdef CONFIG_ACPI_HOTPLUG_CPU - int action = UNINSTALL_NOTIFY_HANDLER; - acpi_walk_namespace(ACPI_TYPE_ANY, - ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, - processor_walk_namespace_cb, NULL, &action, NULL); -#endif - unregister_hotcpu_notifier(&acpi_cpu_notifier); -} - /* * We keep the driver loaded even when ACPI is not running. * This is needed for the powernow-k8 driver, that works even without * ACPI, but needs symbols from this driver */ -static int __init acpi_processor_init(void) +static int __init acpi_processor_driver_init(void) { int result = 0; if (acpi_disabled) return 0; - result = acpi_bus_register_driver(&acpi_processor_driver); + result = driver_register(&acpi_processor_driver); if (result < 0) return result; acpi_processor_syscore_init(); - - acpi_processor_install_hotplug_notify(); - + register_hotcpu_notifier(&acpi_cpu_notifier); acpi_thermal_cpufreq_init(); - acpi_processor_ppc_init(); - acpi_processor_throttling_init(); - return 0; } -static void __exit acpi_processor_exit(void) +static void __exit acpi_processor_driver_exit(void) { if (acpi_disabled) return; acpi_processor_ppc_exit(); - acpi_thermal_cpufreq_exit(); - - acpi_processor_uninstall_hotplug_notify(); - + unregister_hotcpu_notifier(&acpi_cpu_notifier); acpi_processor_syscore_exit(); - - acpi_bus_unregister_driver(&acpi_processor_driver); - - return; + driver_unregister(&acpi_processor_driver); } -module_init(acpi_processor_init); -module_exit(acpi_processor_exit); +module_init(acpi_processor_driver_init); +module_exit(acpi_processor_driver_exit); MODULE_ALIAS("processor"); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index eb133c77aadb..0461ccc92c54 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -214,13 +214,13 @@ static void lapic_timer_state_broadcast(struct acpi_processor *pr, #ifdef CONFIG_PM_SLEEP static u32 saved_bm_rld; -int acpi_processor_suspend(void) +static int acpi_processor_suspend(void) { acpi_read_bit_register(ACPI_BITREG_BUS_MASTER_RLD, &saved_bm_rld); return 0; } -void acpi_processor_resume(void) +static void acpi_processor_resume(void) { u32 resumed_bm_rld; diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index e854582f29a6..1e9732d809bf 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -639,7 +639,7 @@ end: int acpi_processor_preregister_performance( struct acpi_processor_performance __percpu *performance) { - int count, count_target; + int count_target; int retval = 0; unsigned int i, j; cpumask_var_t covered_cpus; @@ -711,7 +711,6 @@ int acpi_processor_preregister_performance( /* Validate the Domain info */ count_target = pdomain->num_processors; - count = 1; if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL; else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) @@ -745,7 +744,6 @@ int acpi_processor_preregister_performance( cpumask_set_cpu(j, covered_cpus); cpumask_set_cpu(j, pr->performance->shared_cpu_map); - count++; } for_each_possible_cpu(j) { diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 27da63061e11..dfe76f17cfc4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -27,6 +27,12 @@ extern struct acpi_device *acpi_root; #define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) +/* + * If set, devices will be hot-removed even if they cannot be put offline + * gracefully (from the kernel's standpoint). + */ +bool acpi_force_hot_remove; + static const char *dummy_hid = "device"; static LIST_HEAD(acpi_device_list); @@ -120,12 +126,78 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); +static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl, + void *data, void **ret_p) +{ + struct acpi_device *device = NULL; + struct acpi_device_physical_node *pn; + bool second_pass = (bool)data; + acpi_status status = AE_OK; + + if (acpi_bus_get_device(handle, &device)) + return AE_OK; + + mutex_lock(&device->physical_node_lock); + + list_for_each_entry(pn, &device->physical_node_list, node) { + int ret; + + if (second_pass) { + /* Skip devices offlined by the first pass. */ + if (pn->put_online) + continue; + } else { + pn->put_online = false; + } + ret = device_offline(pn->dev); + if (acpi_force_hot_remove) + continue; + + if (ret >= 0) { + pn->put_online = !ret; + } else { + *ret_p = pn->dev; + if (second_pass) { + status = AE_ERROR; + break; + } + } + } + + mutex_unlock(&device->physical_node_lock); + + return status; +} + +static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl, + void *data, void **ret_p) +{ + struct acpi_device *device = NULL; + struct acpi_device_physical_node *pn; + + if (acpi_bus_get_device(handle, &device)) + return AE_OK; + + mutex_lock(&device->physical_node_lock); + + list_for_each_entry(pn, &device->physical_node_list, node) + if (pn->put_online) { + device_online(pn->dev); + pn->put_online = false; + } + + mutex_unlock(&device->physical_node_lock); + + return AE_OK; +} + static int acpi_scan_hot_remove(struct acpi_device *device) { acpi_handle handle = device->handle; acpi_handle not_used; struct acpi_object_list arg_list; union acpi_object arg; + struct device *errdev; acpi_status status; unsigned long long sta; @@ -136,10 +208,53 @@ static int acpi_scan_hot_remove(struct acpi_device *device) return -EINVAL; } + lock_device_hotplug(); + + /* + * Carry out two passes here and ignore errors in the first pass, + * because if the devices in question are memory blocks and + * CONFIG_MEMCG is set, one of the blocks may hold data structures + * that the other blocks depend on, but it is not known in advance which + * block holds them. + * + * If the first pass is successful, the second one isn't needed, though. + */ + errdev = NULL; + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + NULL, acpi_bus_offline_companions, + (void *)false, (void **)&errdev); + acpi_bus_offline_companions(handle, 0, (void *)false, (void **)&errdev); + if (errdev) { + errdev = NULL; + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + NULL, acpi_bus_offline_companions, + (void *)true , (void **)&errdev); + if (!errdev || acpi_force_hot_remove) + acpi_bus_offline_companions(handle, 0, (void *)true, + (void **)&errdev); + + if (errdev && !acpi_force_hot_remove) { + dev_warn(errdev, "Offline failed.\n"); + acpi_bus_online_companions(handle, 0, NULL, NULL); + acpi_walk_namespace(ACPI_TYPE_ANY, handle, + ACPI_UINT32_MAX, + acpi_bus_online_companions, NULL, + NULL, NULL); + + unlock_device_hotplug(); + + put_device(&device->dev); + return -EBUSY; + } + } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); acpi_bus_trim(device); + + unlock_device_hotplug(); + /* Device node has been unregistered. */ put_device(&device->dev); device = NULL; @@ -236,6 +351,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) int error; mutex_lock(&acpi_scan_lock); + lock_device_hotplug(); acpi_bus_get_device(handle, &device); if (device) { @@ -259,6 +375,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); out: + unlock_device_hotplug(); acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); mutex_unlock(&acpi_scan_lock); } @@ -816,32 +933,43 @@ static void acpi_device_remove_notify_handler(struct acpi_device *device) acpi_device_notify); } -static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *); -static int acpi_device_probe(struct device * dev) +static int acpi_device_probe(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); int ret; - ret = acpi_bus_driver_init(acpi_dev, acpi_drv); - if (!ret) { - if (acpi_drv->ops.notify) { - ret = acpi_device_install_notify_handler(acpi_dev); - if (ret) { - if (acpi_drv->ops.remove) - acpi_drv->ops.remove(acpi_dev); - acpi_dev->driver = NULL; - acpi_dev->driver_data = NULL; - return ret; - } - } + if (acpi_dev->handler) + return -EINVAL; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found driver [%s] for device [%s]\n", - acpi_drv->name, acpi_dev->pnp.bus_id)); - get_device(dev); + if (!acpi_drv->ops.add) + return -ENOSYS; + + ret = acpi_drv->ops.add(acpi_dev); + if (ret) + return ret; + + acpi_dev->driver = acpi_drv; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Driver [%s] successfully bound to device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + + if (acpi_drv->ops.notify) { + ret = acpi_device_install_notify_handler(acpi_dev); + if (ret) { + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev); + + acpi_dev->driver = NULL; + acpi_dev->driver_data = NULL; + return ret; + } } - return ret; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found driver [%s] for device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + get_device(dev); + return 0; } static int acpi_device_remove(struct device * dev) @@ -952,7 +1080,6 @@ int acpi_device_add(struct acpi_device *device, printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", dev_name(&device->dev)); - device->removal_type = ACPI_BUS_REMOVAL_NORMAL; return 0; err: @@ -998,41 +1125,6 @@ static void acpi_device_unregister(struct acpi_device *device) Driver Management -------------------------------------------------------------------------- */ /** - * acpi_bus_driver_init - add a device to a driver - * @device: the device to add and initialize - * @driver: driver for the device - * - * Used to initialize a device via its device driver. Called whenever a - * driver is bound to a device. Invokes the driver's add() ops. - */ -static int -acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver) -{ - int result = 0; - - if (!device || !driver) - return -EINVAL; - - if (!driver->ops.add) - return -ENOSYS; - - result = driver->ops.add(device); - if (result) - return result; - - device->driver = driver; - - /* - * TBD - Configuration Management: Assign resources to device based - * upon possible configuration and currently allocated resources. - */ - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Driver successfully bound to device\n")); - return 0; -} - -/** * acpi_bus_register_driver - register a driver with the ACPI bus * @driver: driver being registered * @@ -1939,7 +2031,6 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, if (!acpi_bus_get_device(handle, &device)) { struct acpi_scan_handler *dev_handler = device->handler; - device->removal_type = ACPI_BUS_REMOVAL_EJECT; if (dev_handler) { if (dev_handler->detach) dev_handler->detach(device); @@ -2038,8 +2129,10 @@ int __init acpi_scan_init(void) acpi_pci_root_init(); acpi_pci_link_init(); + acpi_processor_init(); acpi_platform_init(); acpi_lpss_init(); + acpi_cmos_rtc_init(); acpi_container_init(); acpi_memory_hotplug_init(); acpi_dock_init(); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 9c1a435d10e6..187ab61889e6 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -494,6 +494,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state) break; case ACPI_STATE_S3: + if (!acpi_suspend_lowlevel) + return -ENOSYS; error = acpi_suspend_lowlevel(); if (error) return error; diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 193745d96fcc..05306a59aedc 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -779,6 +779,33 @@ void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name); } +static ssize_t force_remove_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", !!acpi_force_hot_remove); +} + +static ssize_t force_remove_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + lock_device_hotplug(); + acpi_force_hot_remove = val; + unlock_device_hotplug(); + return size; +} + +static const struct kobj_attribute force_remove_attr = + __ATTR(force_remove, S_IRUGO | S_IWUSR, force_remove_show, + force_remove_store); + int __init acpi_sysfs_init(void) { int result; @@ -788,6 +815,10 @@ int __init acpi_sysfs_init(void) return result; hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); + result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr); + if (result) + return result; + result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); return result; } diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 440eadf2d32c..5d7075d25700 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1722,9 +1722,6 @@ static int acpi_video_bus_add(struct acpi_device *device) int error; acpi_status status; - if (device->handler) - return -EINVAL; - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, device->parent->handle, 1, acpi_video_bus_match, NULL, diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index cdbad3a454a0..c6707278a6bb 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -284,7 +284,7 @@ static const struct dev_pm_ops amba_pm = { SET_RUNTIME_PM_OPS( amba_pm_runtime_suspend, amba_pm_runtime_resume, - pm_generic_runtime_idle + NULL ) }; diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 2b50dfdf1cfc..fe1a889c6910 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -310,6 +310,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* AMD */ { PCI_VDEVICE(AMD, 0x7800), board_ahci }, /* AMD Hudson-2 */ + { PCI_VDEVICE(AMD, 0x7900), board_ahci }, /* AMD CZ */ /* AMD is using RAID class only for ahci controllers */ { PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci }, diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index adf002a3c584..6e458a40a93b 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5436,7 +5436,7 @@ static int ata_port_runtime_idle(struct device *dev) return -EBUSY; } - return pm_runtime_suspend(dev); + return 0; } static int ata_port_runtime_suspend(struct device *dev) diff --git a/drivers/base/core.c b/drivers/base/core.c index 6fdc53d46fa0..dc3ea237f086 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -403,6 +403,36 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, static struct device_attribute uevent_attr = __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent); +static ssize_t show_online(struct device *dev, struct device_attribute *attr, + char *buf) +{ + bool val; + + lock_device_hotplug(); + val = !dev->offline; + unlock_device_hotplug(); + return sprintf(buf, "%u\n", val); +} + +static ssize_t store_online(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + lock_device_hotplug(); + ret = val ? device_online(dev) : device_offline(dev); + unlock_device_hotplug(); + return ret < 0 ? ret : count; +} + +static struct device_attribute online_attr = + __ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online); + static int device_add_attributes(struct device *dev, struct device_attribute *attrs) { @@ -516,6 +546,12 @@ static int device_add_attrs(struct device *dev) if (error) goto err_remove_type_groups; + if (device_supports_offline(dev) && !dev->offline_disabled) { + error = device_create_file(dev, &online_attr); + if (error) + goto err_remove_type_groups; + } + return 0; err_remove_type_groups: @@ -536,6 +572,7 @@ static void device_remove_attrs(struct device *dev) struct class *class = dev->class; const struct device_type *type = dev->type; + device_remove_file(dev, &online_attr); device_remove_groups(dev, dev->groups); if (type) @@ -1435,6 +1472,99 @@ EXPORT_SYMBOL_GPL(put_device); EXPORT_SYMBOL_GPL(device_create_file); EXPORT_SYMBOL_GPL(device_remove_file); +static DEFINE_MUTEX(device_hotplug_lock); + +void lock_device_hotplug(void) +{ + mutex_lock(&device_hotplug_lock); +} + +void unlock_device_hotplug(void) +{ + mutex_unlock(&device_hotplug_lock); +} + +static int device_check_offline(struct device *dev, void *not_used) +{ + int ret; + + ret = device_for_each_child(dev, NULL, device_check_offline); + if (ret) + return ret; + + return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0; +} + +/** + * device_offline - Prepare the device for hot-removal. + * @dev: Device to be put offline. + * + * Execute the device bus type's .offline() callback, if present, to prepare + * the device for a subsequent hot-removal. If that succeeds, the device must + * not be used until either it is removed or its bus type's .online() callback + * is executed. + * + * Call under device_hotplug_lock. + */ +int device_offline(struct device *dev) +{ + int ret; + + if (dev->offline_disabled) + return -EPERM; + + ret = device_for_each_child(dev, NULL, device_check_offline); + if (ret) + return ret; + + device_lock(dev); + if (device_supports_offline(dev)) { + if (dev->offline) { + ret = 1; + } else { + ret = dev->bus->offline(dev); + if (!ret) { + kobject_uevent(&dev->kobj, KOBJ_OFFLINE); + dev->offline = true; + } + } + } + device_unlock(dev); + + return ret; +} + +/** + * device_online - Put the device back online after successful device_offline(). + * @dev: Device to be put back online. + * + * If device_offline() has been successfully executed for @dev, but the device + * has not been removed subsequently, execute its bus type's .online() callback + * to indicate that the device can be used again. + * + * Call under device_hotplug_lock. + */ +int device_online(struct device *dev) +{ + int ret = 0; + + device_lock(dev); + if (device_supports_offline(dev)) { + if (dev->offline) { + ret = dev->bus->online(dev); + if (!ret) { + kobject_uevent(&dev->kobj, KOBJ_ONLINE); + dev->offline = false; + } + } else { + ret = 1; + } + } + device_unlock(dev); + + return ret; +} + struct root_device { struct device dev; struct module *owner; diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index c377673320ed..a16d20e389f0 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -13,17 +13,21 @@ #include <linux/gfp.h> #include <linux/slab.h> #include <linux/percpu.h> +#include <linux/acpi.h> #include "base.h" -struct bus_type cpu_subsys = { - .name = "cpu", - .dev_name = "cpu", -}; -EXPORT_SYMBOL_GPL(cpu_subsys); - static DEFINE_PER_CPU(struct device *, cpu_sys_devices); +static int cpu_subsys_match(struct device *dev, struct device_driver *drv) +{ + /* ACPI style match is the only one that may succeed. */ + if (acpi_driver_match_device(dev, drv)) + return 1; + + return 0; +} + #ifdef CONFIG_HOTPLUG_CPU static void change_cpu_under_node(struct cpu *cpu, unsigned int from_nid, unsigned int to_nid) @@ -34,65 +38,38 @@ static void change_cpu_under_node(struct cpu *cpu, cpu->node_id = to_nid; } -static ssize_t show_online(struct device *dev, - struct device_attribute *attr, - char *buf) +static int __ref cpu_subsys_online(struct device *dev) { struct cpu *cpu = container_of(dev, struct cpu, dev); + int cpuid = dev->id; + int from_nid, to_nid; + int ret; + + cpu_hotplug_driver_lock(); - return sprintf(buf, "%u\n", !!cpu_online(cpu->dev.id)); + from_nid = cpu_to_node(cpuid); + ret = cpu_up(cpuid); + /* + * When hot adding memory to memoryless node and enabling a cpu + * on the node, node number of the cpu may internally change. + */ + to_nid = cpu_to_node(cpuid); + if (from_nid != to_nid) + change_cpu_under_node(cpu, from_nid, to_nid); + + cpu_hotplug_driver_unlock(); + return ret; } -static ssize_t __ref store_online(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int cpu_subsys_offline(struct device *dev) { - struct cpu *cpu = container_of(dev, struct cpu, dev); - int cpuid = cpu->dev.id; - int from_nid, to_nid; - ssize_t ret; + int ret; cpu_hotplug_driver_lock(); - switch (buf[0]) { - case '0': - ret = cpu_down(cpuid); - if (!ret) - kobject_uevent(&dev->kobj, KOBJ_OFFLINE); - break; - case '1': - from_nid = cpu_to_node(cpuid); - ret = cpu_up(cpuid); - - /* - * When hot adding memory to memoryless node and enabling a cpu - * on the node, node number of the cpu may internally change. - */ - to_nid = cpu_to_node(cpuid); - if (from_nid != to_nid) - change_cpu_under_node(cpu, from_nid, to_nid); - - if (!ret) - kobject_uevent(&dev->kobj, KOBJ_ONLINE); - break; - default: - ret = -EINVAL; - } + ret = cpu_down(dev->id); cpu_hotplug_driver_unlock(); - - if (ret >= 0) - ret = count; return ret; } -static DEVICE_ATTR(online, 0644, show_online, store_online); - -static struct attribute *hotplug_cpu_attrs[] = { - &dev_attr_online.attr, - NULL -}; - -static struct attribute_group hotplug_cpu_attr_group = { - .attrs = hotplug_cpu_attrs, -}; void unregister_cpu(struct cpu *cpu) { @@ -127,6 +104,17 @@ static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ #endif /* CONFIG_HOTPLUG_CPU */ +struct bus_type cpu_subsys = { + .name = "cpu", + .dev_name = "cpu", + .match = cpu_subsys_match, +#ifdef CONFIG_HOTPLUG_CPU + .online = cpu_subsys_online, + .offline = cpu_subsys_offline, +#endif +}; +EXPORT_SYMBOL_GPL(cpu_subsys); + #ifdef CONFIG_KEXEC #include <linux/kexec.h> @@ -185,9 +173,6 @@ static const struct attribute_group *hotplugable_cpu_attr_groups[] = { #ifdef CONFIG_KEXEC &crash_note_cpu_attr_group, #endif -#ifdef CONFIG_HOTPLUG_CPU - &hotplug_cpu_attr_group, -#endif NULL }; @@ -302,6 +287,8 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; cpu->dev.release = cpu_device_release; + cpu->dev.offline_disabled = !cpu->hotpluggable; + cpu->dev.offline = !cpu_online(num); #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE cpu->dev.bus->uevent = arch_cpu_uevent; #endif diff --git a/drivers/base/memory.c b/drivers/base/memory.c index e315051cfeeb..2b7813ec6d02 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -37,9 +37,14 @@ static inline int base_memory_block_id(int section_nr) return section_nr / sections_per_block; } +static int memory_subsys_online(struct device *dev); +static int memory_subsys_offline(struct device *dev); + static struct bus_type memory_subsys = { .name = MEMORY_CLASS_NAME, .dev_name = MEMORY_CLASS_NAME, + .online = memory_subsys_online, + .offline = memory_subsys_offline, }; static BLOCKING_NOTIFIER_HEAD(memory_chain); @@ -262,33 +267,64 @@ static int __memory_block_change_state(struct memory_block *mem, { int ret = 0; - if (mem->state != from_state_req) { - ret = -EINVAL; - goto out; - } + if (mem->state != from_state_req) + return -EINVAL; if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; ret = memory_block_action(mem->start_section_nr, to_state, online_type); + mem->state = ret ? from_state_req : to_state; + return ret; +} - if (ret) { - mem->state = from_state_req; - goto out; - } +static int memory_subsys_online(struct device *dev) +{ + struct memory_block *mem = container_of(dev, struct memory_block, dev); + int ret; - mem->state = to_state; - switch (mem->state) { - case MEM_OFFLINE: - kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); - break; - case MEM_ONLINE: - kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); - break; - default: - break; + mutex_lock(&mem->state_mutex); + + ret = mem->state == MEM_ONLINE ? 0 : + __memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, + ONLINE_KEEP); + + mutex_unlock(&mem->state_mutex); + return ret; +} + +static int memory_subsys_offline(struct device *dev) +{ + struct memory_block *mem = container_of(dev, struct memory_block, dev); + int ret; + + mutex_lock(&mem->state_mutex); + + ret = mem->state == MEM_OFFLINE ? 0 : + __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); + + mutex_unlock(&mem->state_mutex); + return ret; +} + +static int __memory_block_change_state_uevent(struct memory_block *mem, + unsigned long to_state, unsigned long from_state_req, + int online_type) +{ + int ret = __memory_block_change_state(mem, to_state, from_state_req, + online_type); + if (!ret) { + switch (mem->state) { + case MEM_OFFLINE: + kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); + break; + case MEM_ONLINE: + kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); + break; + default: + break; + } } -out: return ret; } @@ -299,8 +335,8 @@ static int memory_block_change_state(struct memory_block *mem, int ret; mutex_lock(&mem->state_mutex); - ret = __memory_block_change_state(mem, to_state, from_state_req, - online_type); + ret = __memory_block_change_state_uevent(mem, to_state, from_state_req, + online_type); mutex_unlock(&mem->state_mutex); return ret; @@ -310,22 +346,34 @@ store_mem_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct memory_block *mem; + bool offline; int ret = -EINVAL; mem = container_of(dev, struct memory_block, dev); - if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) + lock_device_hotplug(); + + if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) { + offline = false; ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, ONLINE_KERNEL); - else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) + } else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) { + offline = false; ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, ONLINE_MOVABLE); - else if (!strncmp(buf, "online", min_t(int, count, 6))) + } else if (!strncmp(buf, "online", min_t(int, count, 6))) { + offline = false; ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, ONLINE_KEEP); - else if(!strncmp(buf, "offline", min_t(int, count, 7))) + } else if(!strncmp(buf, "offline", min_t(int, count, 7))) { + offline = true; ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); + } + if (!ret) + dev->offline = offline; + + unlock_device_hotplug(); if (ret) return ret; @@ -523,6 +571,7 @@ int register_memory(struct memory_block *memory) memory->dev.id = memory->start_section_nr / sections_per_block; memory->dev.release = memory_block_release; memory->dev.groups = memory_memblk_attr_groups; + memory->dev.offline = memory->state == MEM_OFFLINE; error = device_register(&memory->dev); return error; @@ -646,21 +695,6 @@ int unregister_memory_section(struct mem_section *section) } #endif /* CONFIG_MEMORY_HOTREMOVE */ -/* - * offline one memory block. If the memory block has been offlined, do nothing. - */ -int offline_memory_block(struct memory_block *mem) -{ - int ret = 0; - - mutex_lock(&mem->state_mutex); - if (mem->state != MEM_OFFLINE) - ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); - mutex_unlock(&mem->state_mutex); - - return ret; -} - /* return true if the memory block is offlined, otherwise, return false */ bool is_memblock_offlined(struct memory_block *mem) { diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c index 67a274e86727..5fb74b43848e 100644 --- a/drivers/base/pinctrl.c +++ b/drivers/base/pinctrl.c @@ -48,6 +48,25 @@ int pinctrl_bind_pins(struct device *dev) goto cleanup_get; } +#ifdef CONFIG_PM + /* + * If power management is enabled, we also look for the optional + * sleep and idle pin states, with semantics as defined in + * <linux/pinctrl/pinctrl-state.h> + */ + dev->pins->sleep_state = pinctrl_lookup_state(dev->pins->p, + PINCTRL_STATE_SLEEP); + if (IS_ERR(dev->pins->sleep_state)) + /* Not supplying this state is perfectly legal */ + dev_dbg(dev, "no sleep pinctrl state\n"); + + dev->pins->idle_state = pinctrl_lookup_state(dev->pins->p, + PINCTRL_STATE_IDLE); + if (IS_ERR(dev->pins->idle_state)) + /* Not supplying this state is perfectly legal */ + dev_dbg(dev, "no idle pinctrl state\n"); +#endif + return 0; /* diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ed75cf6ef9c9..6eaa7ab9e4bc 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -890,7 +890,6 @@ int platform_pm_restore(struct device *dev) static const struct dev_pm_ops platform_dev_pm_ops = { .runtime_suspend = pm_generic_runtime_suspend, .runtime_resume = pm_generic_runtime_resume, - .runtime_idle = pm_generic_runtime_idle, USE_PLATFORM_PM_SLEEP_OPS }; diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 7072404c8b6d..bfb8955c406c 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2143,7 +2143,6 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->max_off_time_changed = true; genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend; genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume; - genpd->domain.ops.runtime_idle = pm_generic_runtime_idle; genpd->domain.ops.prepare = pm_genpd_prepare; genpd->domain.ops.suspend = pm_genpd_suspend; genpd->domain.ops.suspend_late = pm_genpd_suspend_late; diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index bfd898b8988e..5ee030a864f9 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -12,29 +12,6 @@ #ifdef CONFIG_PM_RUNTIME /** - * pm_generic_runtime_idle - Generic runtime idle callback for subsystems. - * @dev: Device to handle. - * - * If PM operations are defined for the @dev's driver and they include - * ->runtime_idle(), execute it and return its error code, if nonzero. - * Otherwise, execute pm_runtime_suspend() for the device and return 0. - */ -int pm_generic_runtime_idle(struct device *dev) -{ - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - if (pm && pm->runtime_idle) { - int ret = pm->runtime_idle(dev); - if (ret) - return ret; - } - - pm_runtime_suspend(dev); - return 0; -} -EXPORT_SYMBOL_GPL(pm_generic_runtime_idle); - -/** * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems. * @dev: Device to suspend. * diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index f0077cb8e249..c8ec186303db 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -648,14 +648,14 @@ int opp_init_cpufreq_table(struct device *dev, list_for_each_entry(opp, &dev_opp->opp_list, node) { if (opp->available) { - freq_table[i].index = i; + freq_table[i].driver_data = i; freq_table[i].frequency = opp->rate / 1000; i++; } } mutex_unlock(&dev_opp_list_lock); - freq_table[i].index = i; + freq_table[i].driver_data = i; freq_table[i].frequency = CPUFREQ_TABLE_END; *table = &freq_table[0]; diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 71671c42ef45..5c1361a9e5dd 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -42,6 +42,7 @@ #include <linux/export.h> #include <linux/pm_runtime.h> #include <linux/err.h> +#include <trace/events/power.h> #include "power.h" @@ -305,6 +306,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, else if (!dev->power.qos) ret = dev_pm_qos_constraints_allocate(dev); + trace_dev_pm_qos_add_request(dev_name(dev), type, value); if (!ret) { req->dev = dev; req->type = type; @@ -349,6 +351,8 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, return -EINVAL; } + trace_dev_pm_qos_update_request(dev_name(req->dev), req->type, + new_value); if (curr_value != new_value) ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); @@ -398,6 +402,8 @@ static int __dev_pm_qos_remove_request(struct dev_pm_qos_request *req) if (IS_ERR_OR_NULL(req->dev->power.qos)) return -ENODEV; + trace_dev_pm_qos_remove_request(dev_name(req->dev), req->type, + PM_QOS_DEFAULT_VALUE); ret = apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); return ret; diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index ef13ad08afb2..268a35097578 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -293,11 +293,8 @@ static int rpm_idle(struct device *dev, int rpmflags) /* Pending requests need to be canceled. */ dev->power.request = RPM_REQ_NONE; - if (dev->power.no_callbacks) { - /* Assume ->runtime_idle() callback would have suspended. */ - retval = rpm_suspend(dev, rpmflags); + if (dev->power.no_callbacks) goto out; - } /* Carry out an asynchronous or a synchronous idle notification. */ if (rpmflags & RPM_ASYNC) { @@ -306,7 +303,8 @@ static int rpm_idle(struct device *dev, int rpmflags) dev->power.request_pending = true; queue_work(pm_wq, &dev->power.work); } - goto out; + trace_rpm_return_int(dev, _THIS_IP_, 0); + return 0; } dev->power.idle_notification = true; @@ -326,14 +324,14 @@ static int rpm_idle(struct device *dev, int rpmflags) callback = dev->driver->pm->runtime_idle; if (callback) - __rpm_callback(callback, dev); + retval = __rpm_callback(callback, dev); dev->power.idle_notification = false; wake_up_all(&dev->power.wait_queue); out: trace_rpm_return_int(dev, _THIS_IP_, retval); - return retval; + return retval ? retval : rpm_suspend(dev, rpmflags); } /** diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 79715e7fa43e..2d56f4113ae7 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -659,7 +659,7 @@ void pm_wakeup_event(struct device *dev, unsigned int msec) } EXPORT_SYMBOL_GPL(pm_wakeup_event); -static void print_active_wakeup_sources(void) +void pm_print_active_wakeup_sources(void) { struct wakeup_source *ws; int active = 0; @@ -683,6 +683,7 @@ static void print_active_wakeup_sources(void) last_activity_ws->name); rcu_read_unlock(); } +EXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources); /** * pm_wakeup_pending - Check if power transition in progress should be aborted. @@ -707,8 +708,10 @@ bool pm_wakeup_pending(void) } spin_unlock_irqrestore(&events_lock, flags); - if (ret) - print_active_wakeup_sources(); + if (ret) { + pr_info("PM: Wakeup pending, aborting suspend\n"); + pm_print_active_wakeup_sources(); + } return ret; } diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index c130536e0ab0..29c83160ca29 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -52,6 +52,7 @@ struct regmap_async { struct regmap { struct mutex mutex; spinlock_t spinlock; + unsigned long spinlock_flags; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ @@ -148,6 +149,7 @@ struct regcache_ops { int (*read)(struct regmap *map, unsigned int reg, unsigned int *value); int (*write)(struct regmap *map, unsigned int reg, unsigned int value); int (*sync)(struct regmap *map, unsigned int min, unsigned int max); + int (*drop)(struct regmap *map, unsigned int min, unsigned int max); }; bool regmap_writeable(struct regmap *map, unsigned int reg); @@ -174,6 +176,14 @@ struct regmap_range_node { unsigned int window_len; }; +struct regmap_field { + struct regmap *regmap; + unsigned int mask; + /* lsb */ + unsigned int shift; + unsigned int reg; +}; + #ifdef CONFIG_DEBUG_FS extern void regmap_debugfs_initcall(void); extern void regmap_debugfs_init(struct regmap *map, const char *name); diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 02f490bad30f..5c1435c4e210 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -304,6 +304,48 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, return 0; } +static struct regcache_rbtree_node * +regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) +{ + struct regcache_rbtree_node *rbnode; + const struct regmap_range *range; + int i; + + rbnode = kzalloc(sizeof(*rbnode), GFP_KERNEL); + if (!rbnode) + return NULL; + + /* If there is a read table then use it to guess at an allocation */ + if (map->rd_table) { + for (i = 0; i < map->rd_table->n_yes_ranges; i++) { + if (regmap_reg_in_range(reg, + &map->rd_table->yes_ranges[i])) + break; + } + + if (i != map->rd_table->n_yes_ranges) { + range = &map->rd_table->yes_ranges[i]; + rbnode->blklen = range->range_max - range->range_min + + 1; + rbnode->base_reg = range->range_min; + } + } + + if (!rbnode->blklen) { + rbnode->blklen = sizeof(*rbnode); + rbnode->base_reg = reg; + } + + rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, + GFP_KERNEL); + if (!rbnode->block) { + kfree(rbnode); + return NULL; + } + + return rbnode; +} + static int regcache_rbtree_write(struct regmap *map, unsigned int reg, unsigned int value) { @@ -354,23 +396,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return 0; } } - /* we did not manage to find a place to insert it in an existing - * block so create a new rbnode with a single register in its block. - * This block will get populated further if any other adjacent - * registers get modified in the future. + + /* We did not manage to find a place to insert it in + * an existing block so create a new rbnode. */ - rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); + rbnode = regcache_rbtree_node_alloc(map, reg); if (!rbnode) return -ENOMEM; - rbnode->blklen = sizeof(*rbnode); - rbnode->base_reg = reg; - rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, - GFP_KERNEL); - if (!rbnode->block) { - kfree(rbnode); - return -ENOMEM; - } - regcache_rbtree_set_register(map, rbnode, 0, value); + regcache_rbtree_set_register(map, rbnode, + reg - rbnode->base_reg, value); regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 507ee2da0f6e..e69102696533 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -250,6 +250,38 @@ int regcache_write(struct regmap *map, return 0; } +static int regcache_default_sync(struct regmap *map, unsigned int min, + unsigned int max) +{ + unsigned int reg; + + for (reg = min; reg <= max; reg++) { + unsigned int val; + int ret; + + if (regmap_volatile(map, reg)) + continue; + + ret = regcache_read(map, reg, &val); + if (ret) + return ret; + + /* Is this the hardware default? If so skip. */ + ret = regcache_lookup_reg(map, reg); + if (ret >= 0 && val == map->reg_defaults[ret].def) + continue; + + map->cache_bypass = 1; + ret = _regmap_write(map, reg, val); + map->cache_bypass = 0; + if (ret) + return ret; + dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val); + } + + return 0; +} + /** * regcache_sync: Sync the register cache with the hardware. * @@ -268,7 +300,7 @@ int regcache_sync(struct regmap *map) const char *name; unsigned int bypass; - BUG_ON(!map->cache_ops || !map->cache_ops->sync); + BUG_ON(!map->cache_ops); map->lock(map->lock_arg); /* Remember the initial bypass state */ @@ -297,7 +329,10 @@ int regcache_sync(struct regmap *map) } map->cache_bypass = 0; - ret = map->cache_ops->sync(map, 0, map->max_register); + if (map->cache_ops->sync) + ret = map->cache_ops->sync(map, 0, map->max_register); + else + ret = regcache_default_sync(map, 0, map->max_register); if (ret == 0) map->cache_dirty = false; @@ -331,7 +366,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, const char *name; unsigned int bypass; - BUG_ON(!map->cache_ops || !map->cache_ops->sync); + BUG_ON(!map->cache_ops); map->lock(map->lock_arg); @@ -346,7 +381,10 @@ int regcache_sync_region(struct regmap *map, unsigned int min, if (!map->cache_dirty) goto out; - ret = map->cache_ops->sync(map, min, max); + if (map->cache_ops->sync) + ret = map->cache_ops->sync(map, min, max); + else + ret = regcache_default_sync(map, min, max); out: trace_regcache_sync(map->dev, name, "stop region"); @@ -359,6 +397,43 @@ out: EXPORT_SYMBOL_GPL(regcache_sync_region); /** + * regcache_drop_region: Discard part of the register cache + * + * @map: map to operate on + * @min: first register to discard + * @max: last register to discard + * + * Discard part of the register cache. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_drop_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + unsigned int reg; + int ret = 0; + + if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) + return -EINVAL; + + map->lock(map->lock_arg); + + trace_regcache_drop_region(map->dev, min, max); + + if (map->cache_present) + for (reg = min; reg < max + 1; reg++) + clear_bit(reg, map->cache_present); + + if (map->cache_ops && map->cache_ops->drop) + ret = map->cache_ops->drop(map, min, max); + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regcache_drop_region); + +/** * regcache_cache_only: Put a register map into cache only mode * * @map: map to configure diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 975719bc3450..53495753fbdb 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -84,6 +84,10 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, unsigned int fpos_offset; unsigned int reg_offset; + /* Suppress the cache if we're using a subrange */ + if (from) + return from; + /* * If we don't have a cache build one so we don't have to do a * linear scan each time. @@ -145,7 +149,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, reg_offset = fpos_offset / map->debugfs_tot_len; *pos = c->min + (reg_offset * map->debugfs_tot_len); mutex_unlock(&map->cache_lock); - return c->base_reg + reg_offset; + return c->base_reg + (reg_offset * map->reg_stride); } *pos = c->max; @@ -281,7 +285,7 @@ static ssize_t regmap_map_write_file(struct file *file, return -EINVAL; /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); ret = regmap_write(map, reg, value); if (ret < 0) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index a941dcfe7590..95920583e31e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -65,9 +65,8 @@ bool regmap_reg_in_ranges(unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_reg_in_ranges); -static bool _regmap_check_range_table(struct regmap *map, - unsigned int reg, - const struct regmap_access_table *table) +bool regmap_check_range_table(struct regmap *map, unsigned int reg, + const struct regmap_access_table *table) { /* Check "no ranges" first */ if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges)) @@ -80,6 +79,7 @@ static bool _regmap_check_range_table(struct regmap *map, return regmap_reg_in_ranges(reg, table->yes_ranges, table->n_yes_ranges); } +EXPORT_SYMBOL_GPL(regmap_check_range_table); bool regmap_writeable(struct regmap *map, unsigned int reg) { @@ -90,7 +90,7 @@ bool regmap_writeable(struct regmap *map, unsigned int reg) return map->writeable_reg(map->dev, reg); if (map->wr_table) - return _regmap_check_range_table(map, reg, map->wr_table); + return regmap_check_range_table(map, reg, map->wr_table); return true; } @@ -107,7 +107,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg) return map->readable_reg(map->dev, reg); if (map->rd_table) - return _regmap_check_range_table(map, reg, map->rd_table); + return regmap_check_range_table(map, reg, map->rd_table); return true; } @@ -121,9 +121,12 @@ bool regmap_volatile(struct regmap *map, unsigned int reg) return map->volatile_reg(map->dev, reg); if (map->volatile_table) - return _regmap_check_range_table(map, reg, map->volatile_table); + return regmap_check_range_table(map, reg, map->volatile_table); - return true; + if (map->cache_ops) + return false; + else + return true; } bool regmap_precious(struct regmap *map, unsigned int reg) @@ -135,7 +138,7 @@ bool regmap_precious(struct regmap *map, unsigned int reg) return map->precious_reg(map->dev, reg); if (map->precious_table) - return _regmap_check_range_table(map, reg, map->precious_table); + return regmap_check_range_table(map, reg, map->precious_table); return false; } @@ -302,13 +305,16 @@ static void regmap_unlock_mutex(void *__map) static void regmap_lock_spinlock(void *__map) { struct regmap *map = __map; - spin_lock(&map->spinlock); + unsigned long flags; + + spin_lock_irqsave(&map->spinlock, flags); + map->spinlock_flags = flags; } static void regmap_unlock_spinlock(void *__map) { struct regmap *map = __map; - spin_unlock(&map->spinlock); + spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); } static void dev_get_regmap_release(struct device *dev, void *res) @@ -801,6 +807,95 @@ struct regmap *devm_regmap_init(struct device *dev, } EXPORT_SYMBOL_GPL(devm_regmap_init); +static void regmap_field_init(struct regmap_field *rm_field, + struct regmap *regmap, struct reg_field reg_field) +{ + int field_bits = reg_field.msb - reg_field.lsb + 1; + rm_field->regmap = regmap; + rm_field->reg = reg_field.reg; + rm_field->shift = reg_field.lsb; + rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb); +} + +/** + * devm_regmap_field_alloc(): Allocate and initialise a register field + * in a register map. + * + * @dev: Device that will be interacted with + * @regmap: regmap bank in which this register field is located. + * @reg_field: Register field with in the bank. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap_field. The regmap_field will be automatically freed + * by the device management code. + */ +struct regmap_field *devm_regmap_field_alloc(struct device *dev, + struct regmap *regmap, struct reg_field reg_field) +{ + struct regmap_field *rm_field = devm_kzalloc(dev, + sizeof(*rm_field), GFP_KERNEL); + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; + +} +EXPORT_SYMBOL_GPL(devm_regmap_field_alloc); + +/** + * devm_regmap_field_free(): Free register field allocated using + * devm_regmap_field_alloc. Usally drivers need not call this function, + * as the memory allocated via devm will be freed as per device-driver + * life-cyle. + * + * @dev: Device that will be interacted with + * @field: regmap field which should be freed. + */ +void devm_regmap_field_free(struct device *dev, + struct regmap_field *field) +{ + devm_kfree(dev, field); +} +EXPORT_SYMBOL_GPL(devm_regmap_field_free); + +/** + * regmap_field_alloc(): Allocate and initialise a register field + * in a register map. + * + * @regmap: regmap bank in which this register field is located. + * @reg_field: Register field with in the bank. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap_field. The regmap_field should be freed by the + * user once its finished working with it using regmap_field_free(). + */ +struct regmap_field *regmap_field_alloc(struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); + + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} +EXPORT_SYMBOL_GPL(regmap_field_alloc); + +/** + * regmap_field_free(): Free register field allocated using regmap_field_alloc + * + * @field: regmap field which should be freed. + */ +void regmap_field_free(struct regmap_field *field) +{ + kfree(field); +} +EXPORT_SYMBOL_GPL(regmap_field_free); + /** * regmap_reinit_cache(): Reinitialise the current register cache * @@ -1249,6 +1344,22 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_raw_write); +/** + * regmap_field_write(): Write a value to a single register field + * + * @field: Register field to write to + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_write(struct regmap_field *field, unsigned int val) +{ + return regmap_update_bits(field->regmap, field->reg, + field->mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_field_write); + /* * regmap_bulk_write(): Write multiple registers to the device * @@ -1532,6 +1643,31 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, EXPORT_SYMBOL_GPL(regmap_raw_read); /** + * regmap_field_read(): Read a value to a single register field + * + * @field: Register field to read from + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_read(struct regmap_field *field, unsigned int *val) +{ + int ret; + unsigned int reg_val; + ret = regmap_read(field->regmap, field->reg, ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_field_read); + +/** * regmap_bulk_read(): Read multiple registers from the device * * @map: Register map to write to diff --git a/drivers/block/swim.c b/drivers/block/swim.c index 2f445b7a174e..8ed6ccb748cf 100644 --- a/drivers/block/swim.c +++ b/drivers/block/swim.c @@ -893,7 +893,7 @@ static int swim_probe(struct platform_device *dev) swim_base = ioremap(res->start, resource_size(res)); if (!swim_base) { - return -ENOMEM; + ret = -ENOMEM; goto out_release_io; } diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 64723953e1c9..5cdf88b7ad9e 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -20,7 +20,7 @@ module_param(use_bio, bool, S_IRUGO); static int major; static DEFINE_IDA(vd_index_ida); -struct workqueue_struct *virtblk_wq; +static struct workqueue_struct *virtblk_wq; struct virtio_blk { diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index dd84af4d4f7e..199b8e99f7d7 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -174,7 +174,7 @@ alpha_core_agp_setup(void) /* * Build a fake pci_dev struct */ - pdev = alloc_pci_dev(); + pdev = pci_alloc_dev(NULL); if (!pdev) return -ENOMEM; pdev->vendor = 0xffff; diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 94821ab01c6d..bf5d2477cb77 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -333,7 +333,7 @@ parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa) struct agp_bridge_data *bridge; int error = 0; - fake_bridge_dev = alloc_pci_dev(); + fake_bridge_dev = pci_alloc_dev(NULL); if (!fake_bridge_dev) { error = -ENOMEM; goto fail; diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 7c3b3dcbfbc8..e3c974a6c522 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1472,7 +1472,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); * Once all references to platform device are down to 0, * release all allocated structures. */ -static void tpm_dev_release(struct device *dev) +void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 0770d1d79366..a7bfc176ed43 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -272,7 +272,6 @@ typedef union { struct tpm_output_header out; } tpm_cmd_header; -#define TPM_DIGEST_SIZE 20 struct tpm_pcrread_out { u8 pcr_result[TPM_DIGEST_SIZE]; } __packed; @@ -333,6 +332,7 @@ extern struct tpm_chip* tpm_register_hardware(struct device *, const struct tpm_vendor_specific *); extern int tpm_open(struct inode *, struct file *); extern int tpm_release(struct inode *, struct file *); +extern void tpm_dev_release(struct device *dev); extern void tpm_dev_vendor_release(struct tpm_chip *); extern ssize_t tpm_write(struct file *, const char __user *, size_t, loff_t *); diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 37d5dcc10ea7..b8735de8ce95 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -24,7 +24,6 @@ #include <linux/init.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/wait.h> #include "tpm.h" @@ -74,7 +73,6 @@ struct tpm_inf_dev { }; static struct tpm_inf_dev tpm_dev; -static struct i2c_driver tpm_tis_i2c_driver; /* * iic_tpm_read() - read from TPM register @@ -744,11 +742,9 @@ static int tpm_tis_i2c_probe(struct i2c_client *client, return -ENODEV; } - client->driver = &tpm_tis_i2c_driver; tpm_dev.client = client; rc = tpm_tis_i2c_init(&client->dev); if (rc != 0) { - client->driver = NULL; tpm_dev.client = NULL; rc = -ENODEV; } diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 8a41b6be23a0..4519cb332987 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -884,12 +884,19 @@ static int __init init_tis(void) rc = platform_driver_register(&tis_drv); if (rc < 0) return rc; - if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) - return PTR_ERR(pdev); - if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) { - platform_device_unregister(pdev); - platform_driver_unregister(&tis_drv); + pdev = platform_device_register_simple("tpm_tis", -1, NULL, 0); + if (IS_ERR(pdev)) { + rc = PTR_ERR(pdev); + goto err_dev; } + rc = tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0); + if (rc) + goto err_init; + return 0; +err_init: + platform_device_unregister(pdev); +err_dev: + platform_driver_unregister(&tis_drv); return rc; } diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 0357ac44638b..51380d655d1a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -42,7 +42,7 @@ config COMMON_CLK_WM831X config COMMON_CLK_VERSATILE bool "Clock driver for ARM Reference designs" - depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS + depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64 ---help--- Supports clocking on ARM Reference designs: - Integrator/AP and Integrator/CP @@ -58,7 +58,6 @@ config COMMON_CLK_MAX77686 config COMMON_CLK_SI5351 tristate "Clock driver for SiLabs 5351A/B/C" depends on I2C - depends on OF select REGMAP_I2C select RATIONAL ---help--- @@ -81,6 +80,13 @@ config COMMON_CLK_AXI_CLKGEN Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx FPGAs. It is commonly used in Analog Devices' reference designs. +config CLK_PPC_CORENET + bool "Clock driver for PowerPC corenet platforms" + depends on PPC_E500MC && OF + ---help--- + This adds the clock driver support for Freescale PowerPC corenet + platforms using common clock framework. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index f9cf7085ef70..4038c2bdf334 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-composite.o obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o +obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ @@ -24,6 +25,7 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o @@ -39,3 +41,4 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o +obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 6d9674160430..6d55eb2cb959 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -150,6 +150,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, struct clk_divider *divider = to_clk_divider(hw); int i, bestdiv = 0; unsigned long parent_rate, best = 0, now, maxdiv; + unsigned long parent_rate_saved = *best_parent_rate; if (!rate) rate = 1; @@ -173,6 +174,15 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, for (i = 1; i <= maxdiv; i++) { if (!_is_valid_div(divider, i)) continue; + if (rate * i == parent_rate_saved) { + /* + * It's the most ideal case if the requested rate can be + * divided from parent clock without needing to change + * parent rate, so return the divider immediately. + */ + *best_parent_rate = parent_rate_saved; + return i; + } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); now = parent_rate / i; @@ -217,8 +227,12 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, if (divider->lock) spin_lock_irqsave(divider->lock, flags); - val = readl(divider->reg); - val &= ~(div_mask(divider) << divider->shift); + if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { + val = div_mask(divider) << (divider->shift + 16); + } else { + val = readl(divider->reg); + val &= ~(div_mask(divider) << divider->shift); + } val |= value << divider->shift; writel(val, divider->reg); @@ -245,6 +259,13 @@ static struct clk *_register_divider(struct device *dev, const char *name, struct clk *clk; struct clk_init_data init; + if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { + if (width + shift > 16) { + pr_warn("divider value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + /* allocate the divider */ div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); if (!div) { diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 15114febfd92..790306e921c8 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -53,12 +53,18 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable) if (gate->lock) spin_lock_irqsave(gate->lock, flags); - reg = readl(gate->reg); - - if (set) - reg |= BIT(gate->bit_idx); - else - reg &= ~BIT(gate->bit_idx); + if (gate->flags & CLK_GATE_HIWORD_MASK) { + reg = BIT(gate->bit_idx + 16); + if (set) + reg |= BIT(gate->bit_idx); + } else { + reg = readl(gate->reg); + + if (set) + reg |= BIT(gate->bit_idx); + else + reg &= ~BIT(gate->bit_idx); + } writel(reg, gate->reg); @@ -121,6 +127,13 @@ struct clk *clk_register_gate(struct device *dev, const char *name, struct clk *clk; struct clk_init_data init; + if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { + if (bit_idx > 16) { + pr_err("gate bit exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } + /* allocate the gate */ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); if (!gate) { diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 25b1734560d0..614444ca40cd 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -86,8 +86,12 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) if (mux->lock) spin_lock_irqsave(mux->lock, flags); - val = readl(mux->reg); - val &= ~(mux->mask << mux->shift); + if (mux->flags & CLK_MUX_HIWORD_MASK) { + val = mux->mask << (mux->shift + 16); + } else { + val = readl(mux->reg); + val &= ~(mux->mask << mux->shift); + } val |= index << mux->shift; writel(val, mux->reg); @@ -111,6 +115,15 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, struct clk_mux *mux; struct clk *clk; struct clk_init_data init; + u8 width = 0; + + if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { + width = fls(mask) - ffs(mask) + 1; + if (width + shift > 16) { + pr_err("mux value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + } /* allocate the mux */ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); diff --git a/drivers/clk/clk-nspire.c b/drivers/clk/clk-nspire.c new file mode 100644 index 000000000000..a378db7b2382 --- /dev/null +++ b/drivers/clk/clk-nspire.c @@ -0,0 +1,153 @@ +/* + * + * Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au> + * + * 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/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#define MHZ (1000 * 1000) + +#define BASE_CPU_SHIFT 1 +#define BASE_CPU_MASK 0x7F + +#define CPU_AHB_SHIFT 12 +#define CPU_AHB_MASK 0x07 + +#define FIXED_BASE_SHIFT 8 +#define FIXED_BASE_MASK 0x01 + +#define CLASSIC_BASE_SHIFT 16 +#define CLASSIC_BASE_MASK 0x1F + +#define CX_BASE_SHIFT 15 +#define CX_BASE_MASK 0x3F + +#define CX_UNKNOWN_SHIFT 21 +#define CX_UNKNOWN_MASK 0x03 + +struct nspire_clk_info { + u32 base_clock; + u16 base_cpu_ratio; + u16 base_ahb_ratio; +}; + + +#define EXTRACT(var, prop) (((var)>>prop##_SHIFT) & prop##_MASK) +static void nspire_clkinfo_cx(u32 val, struct nspire_clk_info *clk) +{ + if (EXTRACT(val, FIXED_BASE)) + clk->base_clock = 48 * MHZ; + else + clk->base_clock = 6 * EXTRACT(val, CX_BASE) * MHZ; + + clk->base_cpu_ratio = EXTRACT(val, BASE_CPU) * EXTRACT(val, CX_UNKNOWN); + clk->base_ahb_ratio = clk->base_cpu_ratio * (EXTRACT(val, CPU_AHB) + 1); +} + +static void nspire_clkinfo_classic(u32 val, struct nspire_clk_info *clk) +{ + if (EXTRACT(val, FIXED_BASE)) + clk->base_clock = 27 * MHZ; + else + clk->base_clock = (300 - 6 * EXTRACT(val, CLASSIC_BASE)) * MHZ; + + clk->base_cpu_ratio = EXTRACT(val, BASE_CPU) * 2; + clk->base_ahb_ratio = clk->base_cpu_ratio * (EXTRACT(val, CPU_AHB) + 1); +} + +static void __init nspire_ahbdiv_setup(struct device_node *node, + void (*get_clkinfo)(u32, struct nspire_clk_info *)) +{ + u32 val; + void __iomem *io; + struct clk *clk; + const char *clk_name = node->name; + const char *parent_name; + struct nspire_clk_info info; + + io = of_iomap(node, 0); + if (!io) + return; + val = readl(io); + iounmap(io); + + get_clkinfo(val, &info); + + of_property_read_string(node, "clock-output-names", &clk_name); + parent_name = of_clk_get_parent_name(node, 0); + + clk = clk_register_fixed_factor(NULL, clk_name, parent_name, 0, + 1, info.base_ahb_ratio); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); +} + +static void __init nspire_ahbdiv_setup_cx(struct device_node *node) +{ + nspire_ahbdiv_setup(node, nspire_clkinfo_cx); +} + +static void __init nspire_ahbdiv_setup_classic(struct device_node *node) +{ + nspire_ahbdiv_setup(node, nspire_clkinfo_classic); +} + +CLK_OF_DECLARE(nspire_ahbdiv_cx, "lsi,nspire-cx-ahb-divider", + nspire_ahbdiv_setup_cx); +CLK_OF_DECLARE(nspire_ahbdiv_classic, "lsi,nspire-classic-ahb-divider", + nspire_ahbdiv_setup_classic); + +static void __init nspire_clk_setup(struct device_node *node, + void (*get_clkinfo)(u32, struct nspire_clk_info *)) +{ + u32 val; + void __iomem *io; + struct clk *clk; + const char *clk_name = node->name; + struct nspire_clk_info info; + + io = of_iomap(node, 0); + if (!io) + return; + val = readl(io); + iounmap(io); + + get_clkinfo(val, &info); + + of_property_read_string(node, "clock-output-names", &clk_name); + + clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, + info.base_clock); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + else + return; + + pr_info("TI-NSPIRE Base: %uMHz CPU: %uMHz AHB: %uMHz\n", + info.base_clock / MHZ, + info.base_clock / info.base_cpu_ratio / MHZ, + info.base_clock / info.base_ahb_ratio / MHZ); +} + +static void __init nspire_clk_setup_cx(struct device_node *node) +{ + nspire_clk_setup(node, nspire_clkinfo_cx); +} + +static void __init nspire_clk_setup_classic(struct device_node *node) +{ + nspire_clk_setup(node, nspire_clkinfo_classic); +} + +CLK_OF_DECLARE(nspire_clk_cx, "lsi,nspire-cx-clock", nspire_clk_setup_cx); +CLK_OF_DECLARE(nspire_clk_classic, "lsi,nspire-classic-clock", + nspire_clk_setup_classic); diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c new file mode 100644 index 000000000000..e9587073bd32 --- /dev/null +++ b/drivers/clk/clk-ppc-corenet.c @@ -0,0 +1,280 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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. + * + * clock driver for Freescale PowerPC corenet SoCs. + */ +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/of.h> +#include <linux/slab.h> + +struct cmux_clk { + struct clk_hw hw; + void __iomem *reg; + u32 flags; +}; + +#define PLL_KILL BIT(31) +#define CLKSEL_SHIFT 27 +#define CLKSEL_ADJUST BIT(0) +#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw) + +static void __iomem *base; +static unsigned int clocks_per_pll; + +static int cmux_set_parent(struct clk_hw *hw, u8 idx) +{ + struct cmux_clk *clk = to_cmux_clk(hw); + u32 clksel; + + clksel = ((idx / clocks_per_pll) << 2) + idx % clocks_per_pll; + if (clk->flags & CLKSEL_ADJUST) + clksel += 8; + clksel = (clksel & 0xf) << CLKSEL_SHIFT; + iowrite32be(clksel, clk->reg); + + return 0; +} + +static u8 cmux_get_parent(struct clk_hw *hw) +{ + struct cmux_clk *clk = to_cmux_clk(hw); + u32 clksel; + + clksel = ioread32be(clk->reg); + clksel = (clksel >> CLKSEL_SHIFT) & 0xf; + if (clk->flags & CLKSEL_ADJUST) + clksel -= 8; + clksel = (clksel >> 2) * clocks_per_pll + clksel % 4; + + return clksel; +} + +const struct clk_ops cmux_ops = { + .get_parent = cmux_get_parent, + .set_parent = cmux_set_parent, +}; + +static void __init core_mux_init(struct device_node *np) +{ + struct clk *clk; + struct clk_init_data init; + struct cmux_clk *cmux_clk; + struct device_node *node; + int rc, count, i; + u32 offset; + const char *clk_name; + const char **parent_names; + + rc = of_property_read_u32(np, "reg", &offset); + if (rc) { + pr_err("%s: could not get reg property\n", np->name); + return; + } + + /* get the input clock source count */ + count = of_property_count_strings(np, "clock-names"); + if (count < 0) { + pr_err("%s: get clock count error\n", np->name); + return; + } + parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL); + if (!parent_names) { + pr_err("%s: could not allocate parent_names\n", __func__); + return; + } + + for (i = 0; i < count; i++) + parent_names[i] = of_clk_get_parent_name(np, i); + + cmux_clk = kzalloc(sizeof(struct cmux_clk), GFP_KERNEL); + if (!cmux_clk) { + pr_err("%s: could not allocate cmux_clk\n", __func__); + goto err_name; + } + cmux_clk->reg = base + offset; + + node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen"); + if (node && (offset >= 0x80)) + cmux_clk->flags = CLKSEL_ADJUST; + + rc = of_property_read_string_index(np, "clock-output-names", + 0, &clk_name); + if (rc) { + pr_err("%s: read clock names error\n", np->name); + goto err_clk; + } + + init.name = clk_name; + init.ops = &cmux_ops; + init.parent_names = parent_names; + init.num_parents = count; + init.flags = 0; + cmux_clk->hw.init = &init; + + clk = clk_register(NULL, &cmux_clk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clock\n", clk_name); + goto err_clk; + } + + rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (rc) { + pr_err("Could not register clock provider for node:%s\n", + np->name); + goto err_clk; + } + goto err_name; + +err_clk: + kfree(cmux_clk); +err_name: + /* free *_names because they are reallocated when registered */ + kfree(parent_names); +} + +static void __init core_pll_init(struct device_node *np) +{ + u32 offset, mult; + int i, rc, count; + const char *clk_name, *parent_name; + struct clk_onecell_data *onecell_data; + struct clk **subclks; + + rc = of_property_read_u32(np, "reg", &offset); + if (rc) { + pr_err("%s: could not get reg property\n", np->name); + return; + } + + /* get the multiple of PLL */ + mult = ioread32be(base + offset); + + /* check if this PLL is disabled */ + if (mult & PLL_KILL) { + pr_debug("PLL:%s is disabled\n", np->name); + return; + } + mult = (mult >> 1) & 0x3f; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) { + pr_err("PLL: %s must have a parent\n", np->name); + return; + } + + count = of_property_count_strings(np, "clock-output-names"); + if (count < 0 || count > 4) { + pr_err("%s: clock is not supported\n", np->name); + return; + } + + /* output clock number per PLL */ + clocks_per_pll = count; + + subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL); + if (!subclks) { + pr_err("%s: could not allocate subclks\n", __func__); + return; + } + + onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!onecell_data) { + pr_err("%s: could not allocate onecell_data\n", __func__); + goto err_clks; + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(np, "clock-output-names", + i, &clk_name); + if (rc) { + pr_err("%s: could not get clock names\n", np->name); + goto err_cell; + } + + /* + * when count == 4, there are 4 output clocks: + * /1, /2, /3, /4 respectively + * when count < 4, there are at least 2 output clocks: + * /1, /2, (/4, if count == 3) respectively. + */ + if (count == 4) + subclks[i] = clk_register_fixed_factor(NULL, clk_name, + parent_name, 0, mult, 1 + i); + else + + subclks[i] = clk_register_fixed_factor(NULL, clk_name, + parent_name, 0, mult, 1 << i); + + if (IS_ERR(subclks[i])) { + pr_err("%s: could not register clock\n", clk_name); + goto err_cell; + } + } + + onecell_data->clks = subclks; + onecell_data->clk_num = count; + + rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data); + if (rc) { + pr_err("Could not register clk provider for node:%s\n", + np->name); + goto err_cell; + } + + return; +err_cell: + kfree(onecell_data); +err_clks: + kfree(subclks); +} + +static const struct of_device_id clk_match[] __initconst = { + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { .compatible = "fsl,core-pll-clock", .data = core_pll_init, }, + { .compatible = "fsl,core-mux-clock", .data = core_mux_init, }, + {} +}; + +static int __init ppc_corenet_clk_probe(struct platform_device *pdev) +{ + struct device_node *np; + + np = pdev->dev.of_node; + base = of_iomap(np, 0); + if (!base) { + dev_err(&pdev->dev, "iomap error\n"); + return -ENOMEM; + } + of_clk_init(clk_match); + + return 0; +} + +static const struct of_device_id ppc_clk_ids[] __initconst = { + { .compatible = "fsl,qoriq-clockgen-1.0", }, + { .compatible = "fsl,qoriq-clockgen-2.0", }, + {} +}; + +static struct platform_driver ppc_corenet_clk_driver = { + .driver = { + .name = "ppc_corenet_clock", + .owner = THIS_MODULE, + .of_match_table = ppc_clk_ids, + }, + .probe = ppc_corenet_clk_probe, +}; + +static int __init ppc_corenet_clk_init(void) +{ + return platform_driver_register(&ppc_corenet_clk_driver); +} +subsys_initcall(ppc_corenet_clk_init); diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 24f553673b72..c50e83744b0a 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -851,6 +851,41 @@ static int _si5351_clkout_set_drive_strength( return 0; } +static int _si5351_clkout_set_disable_state( + struct si5351_driver_data *drvdata, int num, + enum si5351_disable_state state) +{ + u8 reg = (num < 4) ? SI5351_CLK3_0_DISABLE_STATE : + SI5351_CLK7_4_DISABLE_STATE; + u8 shift = (num < 4) ? (2 * num) : (2 * (num-4)); + u8 mask = SI5351_CLK_DISABLE_STATE_MASK << shift; + u8 val; + + if (num > 8) + return -EINVAL; + + switch (state) { + case SI5351_DISABLE_LOW: + val = SI5351_CLK_DISABLE_STATE_LOW; + break; + case SI5351_DISABLE_HIGH: + val = SI5351_CLK_DISABLE_STATE_HIGH; + break; + case SI5351_DISABLE_FLOATING: + val = SI5351_CLK_DISABLE_STATE_FLOAT; + break; + case SI5351_DISABLE_NEVER: + val = SI5351_CLK_DISABLE_STATE_NEVER; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, reg, mask, val << shift); + + return 0; +} + static int si5351_clkout_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = @@ -1225,6 +1260,33 @@ static int si5351_dt_parse(struct i2c_client *client) } } + if (!of_property_read_u32(child, "silabs,disable-state", + &val)) { + switch (val) { + case 0: + pdata->clkout[num].disable_state = + SI5351_DISABLE_LOW; + break; + case 1: + pdata->clkout[num].disable_state = + SI5351_DISABLE_HIGH; + break; + case 2: + pdata->clkout[num].disable_state = + SI5351_DISABLE_FLOATING; + break; + case 3: + pdata->clkout[num].disable_state = + SI5351_DISABLE_NEVER; + break; + default: + dev_err(&client->dev, + "invalid disable state %d for clkout %d\n", + val, num); + return -EINVAL; + } + } + if (!of_property_read_u32(child, "clock-frequency", &val)) pdata->clkout[num].rate = val; @@ -1281,9 +1343,6 @@ static int si5351_i2c_probe(struct i2c_client *client, /* Disable interrupts */ si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0); - /* Set disabled output drivers to drive low */ - si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00); - si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00); /* Ensure pll select is on XTAL for Si5351A/B */ if (drvdata->variant != SI5351_VARIANT_C) si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, @@ -1327,6 +1386,15 @@ static int si5351_i2c_probe(struct i2c_client *client, n, pdata->clkout[n].drive); return ret; } + + ret = _si5351_clkout_set_disable_state(drvdata, n, + pdata->clkout[n].disable_state); + if (ret) { + dev_err(&client->dev, + "failed set disable state of clkout%d to %d\n", + n, pdata->clkout[n].disable_state); + return ret; + } } /* register xtal input clock gate */ @@ -1500,7 +1568,10 @@ static int si5351_i2c_probe(struct i2c_client *client, } static const struct i2c_device_id si5351_i2c_ids[] = { - { "silabs,si5351", 0 }, + { "si5351a", 0 }, + { "si5351a-msop", 0 }, + { "si5351b", 0 }, + { "si5351c", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids); diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h index af41b5080f43..c0dbf2676872 100644 --- a/drivers/clk/clk-si5351.h +++ b/drivers/clk/clk-si5351.h @@ -81,6 +81,7 @@ #define SI5351_CLK3_0_DISABLE_STATE 24 #define SI5351_CLK7_4_DISABLE_STATE 25 +#define SI5351_CLK_DISABLE_STATE_MASK 3 #define SI5351_CLK_DISABLE_STATE_LOW 0 #define SI5351_CLK_DISABLE_STATE_HIGH 1 #define SI5351_CLK_DISABLE_STATE_FLOAT 2 diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index 3af729b1b89d..1ada79a28052 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -95,14 +95,14 @@ static int twl6040_clk_probe(struct platform_device *pdev) if (IS_ERR(clkdata->clk)) return PTR_ERR(clkdata->clk); - dev_set_drvdata(&pdev->dev, clkdata); + platform_set_drvdata(pdev, clkdata); return 0; } static int twl6040_clk_remove(struct platform_device *pdev) { - struct twl6040_clk *clkdata = dev_get_drvdata(&pdev->dev); + struct twl6040_clk *clkdata = platform_get_drvdata(pdev); clk_unregister(clkdata->clk); diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index 553ac35bcc91..82306f5fb9c2 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -42,6 +42,7 @@ struct clk_device { #define PLL_TYPE_VT8500 0 #define PLL_TYPE_WM8650 1 #define PLL_TYPE_WM8750 2 +#define PLL_TYPE_WM8850 3 struct clk_pll { struct clk_hw hw; @@ -156,10 +157,6 @@ static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate, divisor = parent_rate / rate; - /* If prate / rate would be decimal, incr the divisor */ - if (rate * divisor < parent_rate) - divisor++; - if (divisor == cdev->div_mask + 1) divisor = 0; @@ -327,6 +324,15 @@ CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init); #define WM8750_BITS_TO_VAL(f, m, d1, d2) \ ((f << 24) | ((m - 1) << 16) | ((d1 - 1) << 8) | d2) +/* Helper macros for PLL_WM8850 */ +#define WM8850_PLL_MUL(x) ((((x >> 16) & 0x7F) + 1) * 2) +#define WM8850_PLL_DIV(x) ((((x >> 8) & 1) + 1) * (1 << (x & 3))) + +#define WM8850_BITS_TO_FREQ(r, m, d1, d2) \ + (r * ((m + 1) * 2) / ((d1+1) * (1 << d2))) + +#define WM8850_BITS_TO_VAL(m, d1, d2) \ + ((((m / 2) - 1) << 16) | ((d1 - 1) << 8) | d2) static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate, u32 *multiplier, u32 *prediv) @@ -466,6 +472,49 @@ static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate, *divisor2 = best_div2; } +static void wm8850_find_pll_bits(unsigned long rate, unsigned long parent_rate, + u32 *multiplier, u32 *divisor1, u32 *divisor2) +{ + u32 mul, div1, div2; + u32 best_mul, best_div1, best_div2; + unsigned long tclk, rate_err, best_err; + + best_err = (unsigned long)-1; + + /* Find the closest match (lower or equal to requested) */ + for (div1 = 1; div1 >= 0; div1--) + for (div2 = 3; div2 >= 0; div2--) + for (mul = 0; mul <= 127; mul++) { + tclk = parent_rate * ((mul + 1) * 2) / + ((div1 + 1) * (1 << div2)); + if (tclk > rate) + continue; + /* error will always be +ve */ + rate_err = rate - tclk; + if (rate_err == 0) { + *multiplier = mul; + *divisor1 = div1; + *divisor2 = div2; + return; + } + + if (rate_err < best_err) { + best_err = rate_err; + best_mul = mul; + best_div1 = div1; + best_div2 = div2; + } + } + + /* if we got here, it wasn't an exact match */ + pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, + rate - best_err); + + *multiplier = best_mul; + *divisor1 = best_div1; + *divisor2 = best_div2; +} + static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -489,6 +538,10 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2); pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2); break; + case PLL_TYPE_WM8850: + wm8850_find_pll_bits(rate, parent_rate, &mul, &div1, &div2); + pll_val = WM8850_BITS_TO_VAL(mul, div1, div2); + break; default: pr_err("%s: invalid pll type\n", __func__); return 0; @@ -525,6 +578,10 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate, wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2); round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2); break; + case PLL_TYPE_WM8850: + wm8850_find_pll_bits(rate, *prate, &mul, &div1, &div2); + round_rate = WM8850_BITS_TO_FREQ(*prate, mul, div1, div2); + break; default: round_rate = 0; } @@ -552,6 +609,10 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw, pll_freq = parent_rate * WM8750_PLL_MUL(pll_val); pll_freq /= WM8750_PLL_DIV(pll_val); break; + case PLL_TYPE_WM8850: + pll_freq = parent_rate * WM8850_PLL_MUL(pll_val); + pll_freq /= WM8850_PLL_DIV(pll_val); + break; default: pll_freq = 0; } @@ -628,6 +689,12 @@ static void __init wm8750_pll_init(struct device_node *node) } CLK_OF_DECLARE(wm8750_pll, "wm,wm8750-pll-clock", wm8750_pll_init); +static void __init wm8850_pll_init(struct device_node *node) +{ + vtwm_pll_clk_init(node, PLL_TYPE_WM8850); +} +CLK_OF_DECLARE(wm8850_pll, "wm,wm8850-pll-clock", wm8850_pll_init); + void __init vtwm_clk_init(void __iomem *base) { if (!base) diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 16ed06808554..1b3f8c9b98cc 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -97,7 +97,7 @@ static int wm831x_fll_prepare(struct clk_hw *hw) struct wm831x *wm831x = clkdata->wm831x; int ret; - ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, + ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, WM831X_FLL_ENA); if (ret != 0) dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret); @@ -114,9 +114,9 @@ static void wm831x_fll_unprepare(struct clk_hw *hw) struct wm831x *wm831x = clkdata->wm831x; int ret; - ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, WM831X_FLL_ENA, 0); + ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, 0); if (ret != 0) - dev_crit(wm831x->dev, "Failed to disaable FLL: %d\n", ret); + dev_crit(wm831x->dev, "Failed to disable FLL: %d\n", ret); } static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw, @@ -299,8 +299,8 @@ static void wm831x_clkout_unprepare(struct clk_hw *hw) } static const char *wm831x_clkout_parents[] = { - "xtal", "fll", + "xtal", }; static u8 wm831x_clkout_get_parent(struct clk_hw *hw) @@ -318,9 +318,9 @@ static u8 wm831x_clkout_get_parent(struct clk_hw *hw) } if (ret & WM831X_CLKOUT_SRC) - return 0; - else return 1; + else + return 0; } static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent) @@ -384,7 +384,7 @@ static int wm831x_clk_probe(struct platform_device *pdev) if (IS_ERR(clkdata->clkout)) return PTR_ERR(clkdata->clkout); - dev_set_drvdata(&pdev->dev, clkdata); + platform_set_drvdata(pdev, clkdata); return 0; } diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1144e8c7579d..54a191c5bbf0 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -107,7 +107,7 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "%*s%-*s %-11d %-12d %-10lu", level * 3 + 1, "", 30 - level * 3, c->name, - c->enable_count, c->prepare_count, c->rate); + c->enable_count, c->prepare_count, clk_get_rate(c)); seq_printf(s, "\n"); } @@ -166,7 +166,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "\"%s\": { ", c->name); seq_printf(s, "\"enable_count\": %d,", c->enable_count); seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); - seq_printf(s, "\"rate\": %lu", c->rate); + seq_printf(s, "\"rate\": %lu", clk_get_rate(c)); } static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) @@ -534,7 +534,7 @@ static int clk_disable_unused(void) return 0; } -late_initcall(clk_disable_unused); +late_initcall_sync(clk_disable_unused); /*** helper functions ***/ @@ -1216,7 +1216,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) clk_prepare_lock(); /* bail early if nothing to do */ - if (rate == clk->rate) + if (rate == clk_get_rate(clk)) goto out; if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) { @@ -1377,23 +1377,33 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) unsigned long flags; int ret = 0; struct clk *old_parent = clk->parent; - bool migrated_enable = false; - /* migrate prepare */ - if (clk->prepare_count) + /* + * Migrate prepare state between parents and prevent race with + * clk_enable(). + * + * If the clock is not prepared, then a race with + * clk_enable/disable() is impossible since we already have the + * prepare lock (future calls to clk_enable() need to be preceded by + * a clk_prepare()). + * + * If the clock is prepared, migrate the prepared state to the new + * parent and also protect against a race with clk_enable() by + * forcing the clock and the new parent on. This ensures that all + * future calls to clk_enable() are practically NOPs with respect to + * hardware and software states. + * + * See also: Comment for clk_set_parent() below. + */ + if (clk->prepare_count) { __clk_prepare(parent); - - flags = clk_enable_lock(); - - /* migrate enable */ - if (clk->enable_count) { - __clk_enable(parent); - migrated_enable = true; + clk_enable(parent); + clk_enable(clk); } /* update the clk tree topology */ + flags = clk_enable_lock(); clk_reparent(clk, parent); - clk_enable_unlock(flags); /* change clock input source */ @@ -1401,43 +1411,27 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) ret = clk->ops->set_parent(clk->hw, p_index); if (ret) { - /* - * The error handling is tricky due to that we need to release - * the spinlock while issuing the .set_parent callback. This - * means the new parent might have been enabled/disabled in - * between, which must be considered when doing rollback. - */ flags = clk_enable_lock(); - clk_reparent(clk, old_parent); - - if (migrated_enable && clk->enable_count) { - __clk_disable(parent); - } else if (migrated_enable && (clk->enable_count == 0)) { - __clk_disable(old_parent); - } else if (!migrated_enable && clk->enable_count) { - __clk_disable(parent); - __clk_enable(old_parent); - } - clk_enable_unlock(flags); - if (clk->prepare_count) + if (clk->prepare_count) { + clk_disable(clk); + clk_disable(parent); __clk_unprepare(parent); - + } return ret; } - /* clean up enable for old parent if migration was done */ - if (migrated_enable) { - flags = clk_enable_lock(); - __clk_disable(old_parent); - clk_enable_unlock(flags); - } - - /* clean up prepare for old parent if migration was done */ - if (clk->prepare_count) + /* + * Finish the migration of prepare state and undo the changes done + * for preventing a race with clk_enable(). + */ + if (clk->prepare_count) { + clk_disable(clk); + clk_disable(old_parent); __clk_unprepare(old_parent); + } /* update debugfs with new clk tree topology */ clk_debug_reparent(clk, parent); @@ -1449,12 +1443,17 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) * @clk: the mux clk whose input we are switching * @parent: the new input to clk * - * Re-parent clk to use parent as it's new input source. If clk has the - * CLK_SET_PARENT_GATE flag set then clk must be gated for this - * operation to succeed. After successfully changing clk's parent - * clk_set_parent will update the clk topology, sysfs topology and - * propagate rate recalculation via __clk_recalc_rates. Returns 0 on - * success, -EERROR otherwise. + * Re-parent clk to use parent as its new input source. If clk is in + * prepared state, the clk will get enabled for the duration of this call. If + * that's not acceptable for a specific clk (Eg: the consumer can't handle + * that, the reparenting is glitchy in hardware, etc), use the + * CLK_SET_PARENT_GATE flag to allow reparenting only when clk is unprepared. + * + * After successfully changing clk's parent clk_set_parent will update the + * clk topology, sysfs topology and propagate rate recalculation via + * __clk_recalc_rates. + * + * Returns 0 on success, -EERROR otherwise. */ int clk_set_parent(struct clk *clk, struct clk *parent) { @@ -1494,8 +1493,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) } /* propagate PRE_RATE_CHANGE notifications */ - if (clk->notifier_count) - ret = __clk_speculate_rates(clk, p_rate); + ret = __clk_speculate_rates(clk, p_rate); /* abort if a driver objects */ if (ret & NOTIFY_STOP_MASK) diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile new file mode 100644 index 000000000000..8d3aefad2e73 --- /dev/null +++ b/drivers/clk/rockchip/Makefile @@ -0,0 +1,5 @@ +# +# Rockchip Clock specific Makefile +# + +obj-y += clk-rockchip.o diff --git a/drivers/clk/rockchip/clk-rockchip.c b/drivers/clk/rockchip/clk-rockchip.c new file mode 100644 index 000000000000..967c141b1a20 --- /dev/null +++ b/drivers/clk/rockchip/clk-rockchip.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner <heiko@sntech.de> + * + * 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-provider.h> +#include <linux/clkdev.h> +#include <linux/of.h> +#include <linux/of_address.h> + +static DEFINE_SPINLOCK(clk_lock); + +/* + * Gate clocks + */ + +static void __init rk2928_gate_clk_init(struct device_node *node, + void *data) +{ + struct clk_onecell_data *clk_data; + const char *clk_parent; + const char *clk_name; + void __iomem *reg; + void __iomem *reg_idx; + int flags; + int qty; + int reg_bit; + int clkflags = CLK_SET_RATE_PARENT; + int i; + + qty = of_property_count_strings(node, "clock-output-names"); + if (qty < 0) { + pr_err("%s: error in clock-output-names %d\n", __func__, qty); + return; + } + + if (qty == 0) { + pr_info("%s: nothing to do\n", __func__); + return; + } + + reg = of_iomap(node, 0); + + clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->clks = kzalloc(qty * sizeof(struct clk *), GFP_KERNEL); + if (!clk_data->clks) { + kfree(clk_data); + return; + } + + flags = CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE; + + for (i = 0; i < qty; i++) { + of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + + /* ignore empty slots */ + if (!strcmp("reserved", clk_name)) + continue; + + clk_parent = of_clk_get_parent_name(node, i); + + /* keep all gates untouched for now */ + clkflags |= CLK_IGNORE_UNUSED; + + reg_idx = reg + (4 * (i / 16)); + reg_bit = (i % 16); + + clk_data->clks[i] = clk_register_gate(NULL, clk_name, + clk_parent, clkflags, + reg_idx, reg_bit, + flags, + &clk_lock); + WARN_ON(IS_ERR(clk_data->clks[i])); + } + + clk_data->clk_num = qty; + + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} +CLK_OF_DECLARE(rk2928_gate, "rockchip,rk2928-gate-clk", rk2928_gate_clk_init); diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index addc738a06fb..1bdb882c845b 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -356,8 +356,8 @@ struct samsung_fixed_rate_clock exynos4210_fixed_rate_clks[] __initdata = { /* list of mux clocks supported in all exynos4 soc's */ struct samsung_mux_clock exynos4_mux_clks[] __initdata = { - MUX_F(mout_apll, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, - CLK_SET_RATE_PARENT, 0), + MUX_FA(mout_apll, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, + CLK_SET_RATE_PARENT, 0, "mout_apll"), MUX(none, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), MUX(none, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(none, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), @@ -385,9 +385,9 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { MUX(none, "mout_g2d", mout_g2d_p, E4210_SRC_IMAGE, 8, 1), MUX(none, "mout_fimd1", group1_p4210, E4210_SRC_LCD1, 0, 4), MUX(none, "mout_mipi1", group1_p4210, E4210_SRC_LCD1, 12, 4), - MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1, "sclk_mpll"), + MUX_A(sclk_mpll, "sclk_mpll", mout_mpll_p, SRC_CPU, 8, 1, "mout_mpll"), MUX_A(mout_core, "mout_core", mout_core_p4210, - SRC_CPU, 16, 1, "mout_core"), + SRC_CPU, 16, 1, "moutcore"), MUX_A(sclk_vpll, "sclk_vpll", sclk_vpll_p4210, SRC_TOP0, 8, 1, "sclk_vpll"), MUX(mout_fimc0, "mout_fimc0", group1_p4210, SRC_CAM, 0, 4), @@ -424,8 +424,8 @@ struct samsung_mux_clock exynos4210_mux_clks[] __initdata = { /* list of mux clocks supported in exynos4x12 soc */ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { - MUX(mout_mpll_user_c, "mout_mpll_user_c", mout_mpll_user_p4x12, - SRC_CPU, 24, 1), + MUX_A(mout_mpll_user_c, "mout_mpll_user_c", mout_mpll_user_p4x12, + SRC_CPU, 24, 1, "mout_mpll"), MUX(none, "mout_aclk266_gps", aclk_p4412, SRC_TOP1, 4, 1), MUX(none, "mout_aclk400_mcuisp", aclk_p4412, SRC_TOP1, 8, 1), MUX(mout_mpll_user_t, "mout_mpll_user_t", mout_mpll_user_p4x12, @@ -449,7 +449,8 @@ struct samsung_mux_clock exynos4x12_mux_clks[] __initdata = { SRC_DMC, 12, 1, "sclk_mpll"), MUX_A(sclk_vpll, "sclk_vpll", mout_vpll_p, SRC_TOP0, 8, 1, "sclk_vpll"), - MUX(mout_core, "mout_core", mout_core_p4x12, SRC_CPU, 16, 1), + MUX_A(mout_core, "mout_core", mout_core_p4x12, + SRC_CPU, 16, 1, "moutcore"), MUX(mout_fimc0, "mout_fimc0", group1_p4x12, SRC_CAM, 0, 4), MUX(mout_fimc1, "mout_fimc1", group1_p4x12, SRC_CAM, 4, 4), MUX(mout_fimc2, "mout_fimc2", group1_p4x12, SRC_CAM, 8, 4), @@ -537,7 +538,7 @@ struct samsung_div_clock exynos4_div_clks[] __initdata = { DIV(none, "div_spi_pre2", "div_spi2", DIV_PERIL2, 8, 8), DIV(none, "div_audio1", "mout_audio1", DIV_PERIL4, 0, 4), DIV(none, "div_audio2", "mout_audio2", DIV_PERIL4, 16, 4), - DIV_A(arm_clk, "arm_clk", "div_core2", DIV_CPU0, 28, 3, "arm_clk"), + DIV_A(arm_clk, "arm_clk", "div_core2", DIV_CPU0, 28, 3, "armclk"), DIV_A(sclk_apll, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3, "sclk_apll"), DIV_F(none, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4, @@ -1070,9 +1071,9 @@ void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc exynos4_so pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n" "\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk = %ld\n", exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12", - _get_rate("sclk_apll"), _get_rate("sclk_mpll"), + _get_rate("sclk_apll"), _get_rate("mout_mpll"), _get_rate("sclk_epll"), _get_rate("sclk_vpll"), - _get_rate("arm_clk")); + _get_rate("armclk")); } diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index e4ad6ea9aa76..2f7dba20ced8 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -144,6 +144,9 @@ struct samsung_mux_clock { #define MUX_F(_id, cname, pnames, o, s, w, f, mf) \ __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, NULL) +#define MUX_FA(_id, cname, pnames, o, s, w, f, mf, a) \ + __MUX(_id, NULL, cname, pnames, o, s, w, f, mf, a) + /** * @id: platform specific id of the clock. * struct samsung_div_clock: information about div clock diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 8492ad1d5360..412912bbba53 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -239,7 +239,7 @@ struct mux_data { u8 shift; }; -static const __initconst struct mux_data cpu_data = { +static const __initconst struct mux_data cpu_mux_data = { .shift = 16, }; @@ -333,22 +333,34 @@ struct gates_data { DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); }; -static const __initconst struct gates_data axi_gates_data = { +static const __initconst struct gates_data sun4i_axi_gates_data = { .mask = {1}, }; -static const __initconst struct gates_data ahb_gates_data = { +static const __initconst struct gates_data sun4i_ahb_gates_data = { .mask = {0x7F77FFF, 0x14FB3F}, }; -static const __initconst struct gates_data apb0_gates_data = { +static const __initconst struct gates_data sun5i_a13_ahb_gates_data = { + .mask = {0x107067e7, 0x185111}, +}; + +static const __initconst struct gates_data sun4i_apb0_gates_data = { .mask = {0x4EF}, }; -static const __initconst struct gates_data apb1_gates_data = { +static const __initconst struct gates_data sun5i_a13_apb0_gates_data = { + .mask = {0x61}, +}; + +static const __initconst struct gates_data sun4i_apb1_gates_data = { .mask = {0xFF00F7}, }; +static const __initconst struct gates_data sun5i_a13_apb1_gates_data = { + .mask = {0xa0007}, +}; + static void __init sunxi_gates_clk_setup(struct device_node *node, struct gates_data *data) { @@ -421,17 +433,20 @@ static const __initconst struct of_device_id clk_div_match[] = { /* Matches for mux clocks */ static const __initconst struct of_device_id clk_mux_match[] = { - {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_mux_data,}, {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,}, {} }; /* Matches for gate clocks */ static const __initconst struct of_device_id clk_gates_match[] = { - {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,}, - {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,}, - {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,}, - {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,}, + {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,}, + {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, + {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,}, + {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, + {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,}, + {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, + {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,}, {} }; diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 17c2cc086eb4..197074a57754 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -117,10 +117,6 @@ #define PLLCX_MISC2_DEFAULT 0x30211200 #define PLLCX_MISC3_DEFAULT 0x200 -#define PMC_PLLM_WB0_OVERRIDE 0x1dc -#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 -#define PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK BIT(27) - #define PMC_SATA_PWRGT 0x1ac #define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5) #define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4) @@ -128,38 +124,31 @@ #define pll_readl(offset, p) readl_relaxed(p->clk_base + offset) #define pll_readl_base(p) pll_readl(p->params->base_reg, p) #define pll_readl_misc(p) pll_readl(p->params->misc_reg, p) +#define pll_override_readl(offset, p) readl_relaxed(p->pmc + offset) #define pll_writel(val, offset, p) writel_relaxed(val, p->clk_base + offset) #define pll_writel_base(val, p) pll_writel(val, p->params->base_reg, p) #define pll_writel_misc(val, p) pll_writel(val, p->params->misc_reg, p) +#define pll_override_writel(val, offset, p) writel(val, p->pmc + offset) #define mask(w) ((1 << (w)) - 1) -#define divm_mask(p) mask(p->divm_width) -#define divn_mask(p) mask(p->divn_width) +#define divm_mask(p) mask(p->params->div_nmp->divm_width) +#define divn_mask(p) mask(p->params->div_nmp->divn_width) #define divp_mask(p) (p->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK : \ - mask(p->divp_width)) + mask(p->params->div_nmp->divp_width)) #define divm_max(p) (divm_mask(p)) #define divn_max(p) (divn_mask(p)) #define divp_max(p) (1 << (divp_mask(p))) - -#ifdef CONFIG_ARCH_TEGRA_114_SOC -/* PLLXC has 4-bit PDIV, but entry 15 is not allowed in h/w */ -#define PLLXC_PDIV_MAX 14 - -/* non-monotonic mapping below is not a typo */ -static u8 pllxc_p[PLLXC_PDIV_MAX + 1] = { - /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ - /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32 -}; - -#define PLLCX_PDIV_MAX 7 -static u8 pllcx_p[PLLCX_PDIV_MAX + 1] = { - /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7 */ - /* p: */ 1, 2, 3, 4, 6, 8, 12, 16 +static struct div_nmp default_nmp = { + .divn_shift = PLL_BASE_DIVN_SHIFT, + .divn_width = PLL_BASE_DIVN_WIDTH, + .divm_shift = PLL_BASE_DIVM_SHIFT, + .divm_width = PLL_BASE_DIVM_WIDTH, + .divp_shift = PLL_BASE_DIVP_SHIFT, + .divp_width = PLL_BASE_DIVP_WIDTH, }; -#endif static void clk_pll_enable_lock(struct tegra_clk_pll *pll) { @@ -297,6 +286,39 @@ static void clk_pll_disable(struct clk_hw *hw) spin_unlock_irqrestore(pll->lock, flags); } +static int _p_div_to_hw(struct clk_hw *hw, u8 p_div) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + + if (p_tohw) { + while (p_tohw->pdiv) { + if (p_div <= p_tohw->pdiv) + return p_tohw->hw_val; + p_tohw++; + } + return -EINVAL; + } + return -EINVAL; +} + +static int _hw_to_p_div(struct clk_hw *hw, u8 p_div_hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + + if (p_tohw) { + while (p_tohw->pdiv) { + if (p_div_hw == p_tohw->hw_val) + return p_tohw->pdiv; + p_tohw++; + } + return -EINVAL; + } + + return 1 << p_div_hw; +} + static int _get_table_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) @@ -326,9 +348,9 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - struct pdiv_map *p_tohw = pll->params->pdiv_tohw; unsigned long cfreq; u32 p_div = 0; + int ret; switch (parent_rate) { case 12000000: @@ -369,20 +391,16 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, || cfg->output_rate > pll->params->vco_max) { pr_err("%s: Failed to set %s rate %lu\n", __func__, __clk_get_name(hw->clk), rate); + WARN_ON(1); return -EINVAL; } - if (p_tohw) { - p_div = 1 << p_div; - while (p_tohw->pdiv) { - if (p_div <= p_tohw->pdiv) { - cfg->p = p_tohw->hw_val; - break; - } - p_tohw++; - } - if (!p_tohw->pdiv) - return -EINVAL; + if (pll->params->pdiv_tohw) { + ret = _p_div_to_hw(hw, 1 << p_div); + if (ret < 0) + return ret; + else + cfg->p = ret; } else cfg->p = p_div; @@ -393,29 +411,61 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_freq_table *cfg) { u32 val; + struct tegra_clk_pll_params *params = pll->params; + struct div_nmp *div_nmp = params->div_nmp; + + if ((pll->flags & TEGRA_PLLM) && + (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & + PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { + val = pll_override_readl(params->pmc_divp_reg, pll); + val &= ~(divp_mask(pll) << div_nmp->override_divp_shift); + val |= cfg->p << div_nmp->override_divp_shift; + pll_override_writel(val, params->pmc_divp_reg, pll); + + val = pll_override_readl(params->pmc_divnm_reg, pll); + val &= ~(divm_mask(pll) << div_nmp->override_divm_shift) | + ~(divn_mask(pll) << div_nmp->override_divn_shift); + val |= (cfg->m << div_nmp->override_divm_shift) | + (cfg->n << div_nmp->override_divn_shift); + pll_override_writel(val, params->pmc_divnm_reg, pll); + } else { + val = pll_readl_base(pll); - val = pll_readl_base(pll); + val &= ~((divm_mask(pll) << div_nmp->divm_shift) | + (divn_mask(pll) << div_nmp->divn_shift) | + (divp_mask(pll) << div_nmp->divp_shift)); - val &= ~((divm_mask(pll) << pll->divm_shift) | - (divn_mask(pll) << pll->divn_shift) | - (divp_mask(pll) << pll->divp_shift)); - val |= ((cfg->m << pll->divm_shift) | - (cfg->n << pll->divn_shift) | - (cfg->p << pll->divp_shift)); + val |= ((cfg->m << div_nmp->divm_shift) | + (cfg->n << div_nmp->divn_shift) | + (cfg->p << div_nmp->divp_shift)); - pll_writel_base(val, pll); + pll_writel_base(val, pll); + } } static void _get_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_freq_table *cfg) { u32 val; + struct tegra_clk_pll_params *params = pll->params; + struct div_nmp *div_nmp = params->div_nmp; + + if ((pll->flags & TEGRA_PLLM) && + (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & + PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { + val = pll_override_readl(params->pmc_divp_reg, pll); + cfg->p = (val >> div_nmp->override_divp_shift) & divp_mask(pll); + + val = pll_override_readl(params->pmc_divnm_reg, pll); + cfg->m = (val >> div_nmp->override_divm_shift) & divm_mask(pll); + cfg->n = (val >> div_nmp->override_divn_shift) & divn_mask(pll); + } else { + val = pll_readl_base(pll); - val = pll_readl_base(pll); - - cfg->m = (val >> pll->divm_shift) & (divm_mask(pll)); - cfg->n = (val >> pll->divn_shift) & (divn_mask(pll)); - cfg->p = (val >> pll->divp_shift) & (divp_mask(pll)); + cfg->m = (val >> div_nmp->divm_shift) & divm_mask(pll); + cfg->n = (val >> div_nmp->divn_shift) & divn_mask(pll); + cfg->p = (val >> div_nmp->divp_shift) & divp_mask(pll); + } } static void _update_pll_cpcon(struct tegra_clk_pll *pll, @@ -485,9 +535,10 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, } if (_get_table_rate(hw, &cfg, rate, parent_rate) && - _calc_rate(hw, &cfg, rate, parent_rate)) + _calc_rate(hw, &cfg, rate, parent_rate)) { + WARN_ON(1); return -EINVAL; - + } if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -507,7 +558,6 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; - u64 output_rate = *prate; if (pll->flags & TEGRA_PLL_FIXED) return pll->fixed_rate; @@ -517,13 +567,12 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, return __clk_get_rate(hw->clk); if (_get_table_rate(hw, &cfg, rate, *prate) && - _calc_rate(hw, &cfg, rate, *prate)) + _calc_rate(hw, &cfg, rate, *prate)) { + WARN_ON(1); return -EINVAL; + } - output_rate *= cfg.n; - do_div(output_rate, cfg.m * (1 << cfg.p)); - - return output_rate; + return cfg.output_rate; } static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, @@ -531,7 +580,6 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; - struct pdiv_map *p_tohw = pll->params->pdiv_tohw; u32 val; u64 rate = parent_rate; int pdiv; @@ -553,21 +601,11 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, _get_pll_mnp(pll, &cfg); - if (p_tohw) { - while (p_tohw->pdiv) { - if (cfg.p == p_tohw->hw_val) { - pdiv = p_tohw->pdiv; - break; - } - p_tohw++; - } - - if (!p_tohw->pdiv) { - WARN_ON(1); - pdiv = 1; - } - } else - pdiv = 1 << cfg.p; + pdiv = _hw_to_p_div(hw, cfg.p); + if (pdiv < 0) { + WARN_ON(1); + pdiv = 1; + } cfg.m *= pdiv; @@ -647,9 +685,9 @@ static int clk_plle_enable(struct clk_hw *hw) val = pll_readl_base(pll); val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll)); val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT); - val |= sel.m << pll->divm_shift; - val |= sel.n << pll->divn_shift; - val |= sel.p << pll->divp_shift; + val |= sel.m << pll->params->div_nmp->divm_shift; + val |= sel.n << pll->params->div_nmp->divn_shift; + val |= sel.p << pll->params->div_nmp->divp_shift; val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; pll_writel_base(val, pll); } @@ -680,9 +718,9 @@ static unsigned long clk_plle_recalc_rate(struct clk_hw *hw, u32 divn = 0, divm = 0, divp = 0; u64 rate = parent_rate; - divp = (val >> pll->divp_shift) & (divp_mask(pll)); - divn = (val >> pll->divn_shift) & (divn_mask(pll)); - divm = (val >> pll->divm_shift) & (divm_mask(pll)); + divp = (val >> pll->params->div_nmp->divp_shift) & (divp_mask(pll)); + divn = (val >> pll->params->div_nmp->divn_shift) & (divn_mask(pll)); + divm = (val >> pll->params->div_nmp->divm_shift) & (divm_mask(pll)); divm *= divp; rate *= divn; @@ -769,16 +807,22 @@ static int _calc_dynamic_ramp_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); unsigned int p; + int p_div; if (!rate) return -EINVAL; p = DIV_ROUND_UP(pll->params->vco_min, rate); cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); - cfg->p = p; - cfg->output_rate = rate * cfg->p; + cfg->output_rate = rate * p; cfg->n = cfg->output_rate * cfg->m / parent_rate; + p_div = _p_div_to_hw(hw, p); + if (p_div < 0) + return p_div; + else + cfg->p = p_div; + if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) return -EINVAL; @@ -790,18 +834,25 @@ static int _pll_ramp_calc_pll(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - int err = 0; + int err = 0, p_div; err = _get_table_rate(hw, cfg, rate, parent_rate); if (err < 0) err = _calc_dynamic_ramp_rate(hw, cfg, rate, parent_rate); - else if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { + else { + if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { WARN_ON(1); err = -EINVAL; goto out; + } + p_div = _p_div_to_hw(hw, cfg->p); + if (p_div < 0) + return p_div; + else + cfg->p = p_div; } - if (!cfg->p || (cfg->p > pll->params->max_p)) + if (cfg->p > pll->params->max_p) err = -EINVAL; out: @@ -815,7 +866,6 @@ static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, struct tegra_clk_pll_freq_table cfg, old_cfg; unsigned long flags = 0; int ret = 0; - u8 old_p; ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); if (ret < 0) @@ -826,11 +876,8 @@ static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, _get_pll_mnp(pll, &old_cfg); - old_p = pllxc_p[old_cfg.p]; - if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_p != cfg.p) { - cfg.p -= 1; + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p) ret = _program_pll(hw, &cfg, rate); - } if (pll->lock) spin_unlock_irqrestore(pll->lock, flags); @@ -842,15 +889,19 @@ static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct tegra_clk_pll_freq_table cfg; - int ret = 0; + int ret = 0, p_div; u64 output_rate = *prate; ret = _pll_ramp_calc_pll(hw, &cfg, rate, *prate); if (ret < 0) return ret; + p_div = _hw_to_p_div(hw, cfg.p); + if (p_div < 0) + return p_div; + output_rate *= cfg.n; - do_div(output_rate, cfg.m * cfg.p); + do_div(output_rate, cfg.m * p_div); return output_rate; } @@ -862,7 +913,6 @@ static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, struct tegra_clk_pll *pll = to_clk_pll(hw); unsigned long flags = 0; int state, ret = 0; - u32 val; if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -881,22 +931,7 @@ static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, if (ret < 0) goto out; - cfg.p -= 1; - - val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); - if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) { - val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); - val = cfg.p ? (val | PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK) : - (val & ~PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK); - writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); - - val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); - val &= ~(divn_mask(pll) | divm_mask(pll)); - val |= (cfg.m << pll->divm_shift) | (cfg.n << pll->divn_shift); - writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE); - } else - _update_pll_mnp(pll, &cfg); - + _update_pll_mnp(pll, &cfg); out: if (pll->lock) @@ -1010,13 +1045,10 @@ static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll, static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll_freq_table cfg, old_cfg; struct tegra_clk_pll *pll = to_clk_pll(hw); unsigned long flags = 0; int state, ret = 0; - u32 val; - u16 old_m, old_n; - u8 old_p; if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -1025,21 +1057,16 @@ static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate, if (ret < 0) goto out; - val = pll_readl_base(pll); - old_m = (val >> pll->divm_shift) & (divm_mask(pll)); - old_n = (val >> pll->divn_shift) & (divn_mask(pll)); - old_p = pllcx_p[(val >> pll->divp_shift) & (divp_mask(pll))]; + _get_pll_mnp(pll, &old_cfg); - if (cfg.m != old_m) { + if (cfg.m != old_cfg.m) { WARN_ON(1); goto out; } - if (old_n == cfg.n && old_p == cfg.p) + if (old_cfg.n == cfg.n && old_cfg.p == cfg.p) goto out; - cfg.p -= 1; - state = clk_pll_is_enabled(hw); if (state) _clk_pllc_disable(hw); @@ -1178,8 +1205,8 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw) val = pll_readl_base(pll); val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll)); val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT); - val |= sel.m << pll->divm_shift; - val |= sel.n << pll->divn_shift; + val |= sel.m << pll->params->div_nmp->divm_shift; + val |= sel.n << pll->params->div_nmp->divn_shift; val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; pll_writel_base(val, pll); udelay(1); @@ -1240,12 +1267,8 @@ static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, pll->flags = pll_flags; pll->lock = lock; - pll->divp_shift = PLL_BASE_DIVP_SHIFT; - pll->divp_width = PLL_BASE_DIVP_WIDTH; - pll->divn_shift = PLL_BASE_DIVN_SHIFT; - pll->divn_width = PLL_BASE_DIVN_WIDTH; - pll->divm_shift = PLL_BASE_DIVM_SHIFT; - pll->divm_width = PLL_BASE_DIVM_WIDTH; + if (!pll_params->div_nmp) + pll_params->div_nmp = &default_nmp; return pll; } @@ -1401,7 +1424,7 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, struct tegra_clk_pll *pll; struct clk *clk; - pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_LOCK_MISC; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) @@ -1428,7 +1451,6 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, val &= ~BIT(29); pll_writel_misc(val, pll); - pll_flags |= TEGRA_PLL_LOCK_MISC; clk = _tegra_clk_register_pll(pll, name, parent_name, flags, &tegra_clk_pllre_ops); if (IS_ERR(clk)) @@ -1453,6 +1475,7 @@ struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, pll_flags |= TEGRA_PLL_BYPASS; pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll_flags |= TEGRA_PLLM; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 40d939d091bf..b6015cb4fc01 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -21,6 +21,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/delay.h> +#include <linux/export.h> #include <linux/clk/tegra.h> #include "clk.h" @@ -28,6 +29,7 @@ #define RST_DEVICES_L 0x004 #define RST_DEVICES_H 0x008 #define RST_DEVICES_U 0x00C +#define RST_DFLL_DVCO 0x2F4 #define RST_DEVICES_V 0x358 #define RST_DEVICES_W 0x35C #define RST_DEVICES_X 0x28C @@ -41,8 +43,36 @@ #define RST_DEVICES_CLR_V 0x434 #define RST_DEVICES_SET_W 0x438 #define RST_DEVICES_CLR_W 0x43c +#define CPU_FINETRIM_SELECT 0x4d4 /* override default prop dlys */ +#define CPU_FINETRIM_DR 0x4d8 /* rise->rise prop dly A */ +#define CPU_FINETRIM_R 0x4e4 /* rise->rise prop dly inc A */ #define RST_DEVICES_NUM 5 +/* RST_DFLL_DVCO bitfields */ +#define DVFS_DFLL_RESET_SHIFT 0 + +/* CPU_FINETRIM_SELECT and CPU_FINETRIM_DR bitfields */ +#define CPU_FINETRIM_1_FCPU_1 BIT(0) /* fcpu0 */ +#define CPU_FINETRIM_1_FCPU_2 BIT(1) /* fcpu1 */ +#define CPU_FINETRIM_1_FCPU_3 BIT(2) /* fcpu2 */ +#define CPU_FINETRIM_1_FCPU_4 BIT(3) /* fcpu3 */ +#define CPU_FINETRIM_1_FCPU_5 BIT(4) /* fl2 */ +#define CPU_FINETRIM_1_FCPU_6 BIT(5) /* ftop */ + +/* CPU_FINETRIM_R bitfields */ +#define CPU_FINETRIM_R_FCPU_1_SHIFT 0 /* fcpu0 */ +#define CPU_FINETRIM_R_FCPU_1_MASK (0x3 << CPU_FINETRIM_R_FCPU_1_SHIFT) +#define CPU_FINETRIM_R_FCPU_2_SHIFT 2 /* fcpu1 */ +#define CPU_FINETRIM_R_FCPU_2_MASK (0x3 << CPU_FINETRIM_R_FCPU_2_SHIFT) +#define CPU_FINETRIM_R_FCPU_3_SHIFT 4 /* fcpu2 */ +#define CPU_FINETRIM_R_FCPU_3_MASK (0x3 << CPU_FINETRIM_R_FCPU_3_SHIFT) +#define CPU_FINETRIM_R_FCPU_4_SHIFT 6 /* fcpu3 */ +#define CPU_FINETRIM_R_FCPU_4_MASK (0x3 << CPU_FINETRIM_R_FCPU_4_SHIFT) +#define CPU_FINETRIM_R_FCPU_5_SHIFT 8 /* fl2 */ +#define CPU_FINETRIM_R_FCPU_5_MASK (0x3 << CPU_FINETRIM_R_FCPU_5_SHIFT) +#define CPU_FINETRIM_R_FCPU_6_SHIFT 10 /* ftop */ +#define CPU_FINETRIM_R_FCPU_6_MASK (0x3 << CPU_FINETRIM_R_FCPU_6_SHIFT) + #define CLK_OUT_ENB_L 0x010 #define CLK_OUT_ENB_H 0x014 #define CLK_OUT_ENB_U 0x018 @@ -127,6 +157,7 @@ #define PMC_DPD_PADS_ORIDE_BLINK_ENB 20 #define PMC_CTRL 0 #define PMC_CTRL_BLINK_ENB 7 +#define PMC_BLINK_TIMER 0x40 #define OSC_CTRL 0x50 #define OSC_CTRL_OSC_FREQ_SHIFT 28 @@ -242,6 +273,8 @@ #define CLK_SOURCE_I2CSLOW 0x3fc #define CLK_SOURCE_SE 0x42c #define CLK_SOURCE_MSELECT 0x3b4 +#define CLK_SOURCE_DFLL_REF 0x62c +#define CLK_SOURCE_DFLL_SOC 0x630 #define CLK_SOURCE_SOC_THERM 0x644 #define CLK_SOURCE_XUSB_HOST_SRC 0x600 #define CLK_SOURCE_XUSB_FALCON_SRC 0x604 @@ -250,6 +283,10 @@ #define CLK_SOURCE_XUSB_DEV_SRC 0x60c #define CLK_SOURCE_EMC 0x19c +/* PLLM override registers */ +#define PMC_PLLM_WB0_OVERRIDE 0x1dc +#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 + /* Tegra CPU clock and reset control regs */ #define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470 @@ -267,6 +304,15 @@ static DEFINE_SPINLOCK(clk_doubler_lock); static DEFINE_SPINLOCK(clk_out_lock); static DEFINE_SPINLOCK(sysrate_lock); +static struct div_nmp pllxc_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 20, + .divp_width = 4, +}; + static struct pdiv_map pllxc_p[] = { { .pdiv = 1, .hw_val = 0 }, { .pdiv = 2, .hw_val = 1 }, @@ -315,6 +361,16 @@ static struct tegra_clk_pll_params pll_c_params = { .stepa_shift = 17, .stepb_shift = 9, .pdiv_tohw = pllxc_p, + .div_nmp = &pllxc_nmp, +}; + +static struct div_nmp pllcx_nmp = { + .divm_shift = 0, + .divm_width = 2, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 20, + .divp_width = 3, }; static struct pdiv_map pllc_p[] = { @@ -348,6 +404,8 @@ static struct tegra_clk_pll_params pll_c2_params = { .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, .pdiv_tohw = pllc_p, + .div_nmp = &pllcx_nmp, + .max_p = 7, .ext_misc_reg[0] = 0x4f0, .ext_misc_reg[1] = 0x4f4, .ext_misc_reg[2] = 0x4f8, @@ -366,11 +424,25 @@ static struct tegra_clk_pll_params pll_c3_params = { .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, .pdiv_tohw = pllc_p, + .div_nmp = &pllcx_nmp, + .max_p = 7, .ext_misc_reg[0] = 0x504, .ext_misc_reg[1] = 0x508, .ext_misc_reg[2] = 0x50c, }; +static struct div_nmp pllm_nmp = { + .divm_shift = 0, + .divm_width = 8, + .override_divm_shift = 0, + .divn_shift = 8, + .divn_width = 8, + .override_divn_shift = 8, + .divp_shift = 20, + .divp_width = 1, + .override_divp_shift = 27, +}; + static struct pdiv_map pllm_p[] = { { .pdiv = 1, .hw_val = 0 }, { .pdiv = 2, .hw_val = 1 }, @@ -400,6 +472,18 @@ static struct tegra_clk_pll_params pll_m_params = { .lock_delay = 300, .max_p = 2, .pdiv_tohw = pllm_p, + .div_nmp = &pllm_nmp, + .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, + .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2, +}; + +static struct div_nmp pllp_nmp = { + .divm_shift = 0, + .divm_width = 5, + .divn_shift = 8, + .divn_width = 10, + .divp_shift = 20, + .divp_width = 3, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { @@ -423,6 +507,7 @@ static struct tegra_clk_pll_params pll_p_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { @@ -449,6 +534,7 @@ static struct tegra_clk_pll_params pll_a_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { @@ -484,6 +570,7 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_params pll_d2_params = { @@ -498,6 +585,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .div_nmp = &pllp_nmp, }; static struct pdiv_map pllu_p[] = { @@ -506,6 +594,15 @@ static struct pdiv_map pllu_p[] = { { .pdiv = 0, .hw_val = 0 }, }; +static struct div_nmp pllu_nmp = { + .divm_shift = 0, + .divm_width = 5, + .divn_shift = 8, + .divn_width = 10, + .divp_shift = 20, + .divp_width = 1, +}; + static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { {12000000, 480000000, 960, 12, 0, 12}, {13000000, 480000000, 960, 13, 0, 12}, @@ -528,6 +625,7 @@ static struct tegra_clk_pll_params pll_u_params = { .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, .pdiv_tohw = pllu_p, + .div_nmp = &pllu_nmp, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { @@ -560,6 +658,7 @@ static struct tegra_clk_pll_params pll_x_params = { .stepa_shift = 16, .stepb_shift = 24, .pdiv_tohw = pllxc_p, + .div_nmp = &pllxc_nmp, }; static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { @@ -569,6 +668,15 @@ static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { {0, 0, 0, 0, 0, 0}, }; +static struct div_nmp plle_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 24, + .divp_width = 4, +}; + static struct tegra_clk_pll_params pll_e_params = { .input_min = 12000000, .input_max = 1000000000, @@ -582,6 +690,16 @@ static struct tegra_clk_pll_params pll_e_params = { .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &plle_nmp, +}; + +static struct div_nmp pllre_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 16, + .divp_width = 4, }; static struct tegra_clk_pll_params pll_re_vco_params = { @@ -598,6 +716,7 @@ static struct tegra_clk_pll_params pll_re_vco_params = { .lock_delay = 300, .iddq_reg = PLLRE_MISC, .iddq_bit_idx = PLLRE_IDDQ_BIT, + .div_nmp = &pllre_nmp, }; /* Peripheral clock registers */ @@ -765,6 +884,7 @@ enum tegra114_clk { audio1, audio2, audio3, audio4, spdif, clk_out_1, clk_out_2, clk_out_3, blink, xusb_host_src = 252, xusb_falcon_src, xusb_fs_src, xusb_ss_src, xusb_dev_src, xusb_dev, xusb_hs_src, sclk, hclk, pclk, cclk_g, cclk_lp, + dfll_ref = 264, dfll_soc, /* Mux clocks */ @@ -1202,8 +1322,8 @@ static void __init tegra114_pll_init(void __iomem *clk_base, /* PLLP_OUT2 */ clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p", clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | - TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, - &pll_div_lock); + TEGRA_DIVIDER_ROUND_UP | TEGRA_DIVIDER_INT, 24, + 8, 1, &pll_div_lock); clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div", clk_base + PLLP_OUTA, 17, 16, CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, @@ -1605,7 +1725,7 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) /* clk_out_2 */ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out2_parents), 0, pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, &clk_out_lock); clks[clk_out_2_mux] = clk; @@ -1617,7 +1737,7 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) /* clk_out_3 */ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out3_parents), 0, pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, &clk_out_lock); clks[clk_out_3_mux] = clk; @@ -1628,6 +1748,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) clks[clk_out_3] = clk; /* blink */ + /* clear the blink timer register to directly output clk_32k */ + writel_relaxed(0, pmc_base + PMC_BLINK_TIMER); clk = clk_register_gate(NULL, "blink_override", "clk_32k", 0, pmc_base + PMC_DPD_PADS_ORIDE, PMC_DPD_PADS_ORIDE_BLINK_ENB, 0, NULL); @@ -1640,7 +1762,7 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) } static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", - "pll_p_out3", "pll_p_out2", "unused", + "pll_p", "pll_p_out2", "unused", "clk_32k", "pll_m_out1" }; static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", @@ -1750,7 +1872,7 @@ static struct tegra_periph_init_data tegra_periph_clk_list[] = { TEGRA_INIT_DATA_MUX("vi_sensor", "vi_sensor", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI_SENSOR, 20, &periph_l_regs, TEGRA_PERIPH_NO_RESET, vi_sensor), TEGRA_INIT_DATA_INT8("vi", "vi", "tegra_camera", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_VI, 20, &periph_l_regs, 0, vi), TEGRA_INIT_DATA_INT8("epp", NULL, "epp", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_EPP, 19, &periph_l_regs, 0, epp), - TEGRA_INIT_DATA_INT8("msenc", NULL, "msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, &periph_h_regs, TEGRA_PERIPH_WAR_1005168, msenc), + TEGRA_INIT_DATA_INT8("msenc", NULL, "msenc", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_MSENC, 91, &periph_u_regs, TEGRA_PERIPH_WAR_1005168, msenc), TEGRA_INIT_DATA_INT8("tsec", NULL, "tsec", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_TSEC, 83, &periph_u_regs, 0, tsec), TEGRA_INIT_DATA_INT8("host1x", NULL, "host1x", mux_pllm_pllc2_c_c3_pllp_plla, CLK_SOURCE_HOST1X, 28, &periph_l_regs, 0, host1x), TEGRA_INIT_DATA_MUX8("hdmi", NULL, "hdmi", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_HDMI, 51, &periph_h_regs, 0, hdmi), @@ -1767,6 +1889,8 @@ static struct tegra_periph_init_data tegra_periph_clk_list[] = { TEGRA_INIT_DATA_MUX("i2cslow", NULL, "i2cslow", mux_pllp_pllc_clk32_clkm, CLK_SOURCE_I2CSLOW, 81, &periph_u_regs, TEGRA_PERIPH_ON_APB, i2cslow), TEGRA_INIT_DATA_INT8("se", NULL, "se", mux_pllp_pllc2_c_c3_pllm_clkm, CLK_SOURCE_SE, 127, &periph_v_regs, TEGRA_PERIPH_ON_APB, se), TEGRA_INIT_DATA_INT_FLAGS("mselect", NULL, "mselect", mux_pllp_clkm, CLK_SOURCE_MSELECT, 99, &periph_v_regs, 0, mselect, CLK_IGNORE_UNUSED), + TEGRA_INIT_DATA_MUX("dfll_ref", "ref", "t114_dfll", mux_pllp_clkm, CLK_SOURCE_DFLL_REF, 155, &periph_w_regs, TEGRA_PERIPH_ON_APB, dfll_ref), + TEGRA_INIT_DATA_MUX("dfll_soc", "soc", "t114_dfll", mux_pllp_clkm, CLK_SOURCE_DFLL_SOC, 155, &periph_w_regs, TEGRA_PERIPH_ON_APB, dfll_soc), TEGRA_INIT_DATA_MUX8("soc_therm", NULL, "soc_therm", mux_pllm_pllc_pllp_plla, CLK_SOURCE_SOC_THERM, 78, &periph_u_regs, TEGRA_PERIPH_ON_APB, soc_therm), TEGRA_INIT_DATA_XUSB("xusb_host_src", "host_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_HOST_SRC, 143, &periph_w_regs, TEGRA_PERIPH_ON_APB | TEGRA_PERIPH_NO_RESET, xusb_host_src), TEGRA_INIT_DATA_XUSB("xusb_falcon_src", "falcon_src", "tegra_xhci", mux_clkm_pllp_pllc_pllre, CLK_SOURCE_XUSB_FALCON_SRC, 143, &periph_w_regs, TEGRA_PERIPH_NO_RESET, xusb_falcon_src), @@ -2028,6 +2152,10 @@ static const struct of_device_id pmc_match[] __initconst = { {}, }; +/* + * dfll_soc/dfll_ref apparently must be kept enabled, otherwise I2C5 + * breaks + */ static __initdata struct tegra_clk_init_table init_table[] = { {uarta, pll_p, 408000000, 0}, {uartb, pll_p, 408000000, 0}, @@ -2043,6 +2171,8 @@ static __initdata struct tegra_clk_init_table init_table[] = { {i2s2, pll_a_out0, 11289600, 0}, {i2s3, pll_a_out0, 11289600, 0}, {i2s4, pll_a_out0, 11289600, 0}, + {dfll_soc, pll_p, 51000000, 1}, + {dfll_ref, pll_p, 51000000, 1}, {clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */ }; @@ -2051,7 +2181,132 @@ static void __init tegra114_clock_apply_init_table(void) tegra_init_from_table(init_table, clks, clk_max); } -void __init tegra114_clock_init(struct device_node *np) + +/** + * tegra114_car_barrier - wait for pending writes to the CAR to complete + * + * Wait for any outstanding writes to the CAR MMIO space from this CPU + * to complete before continuing execution. No return value. + */ +static void tegra114_car_barrier(void) +{ + wmb(); /* probably unnecessary */ + readl_relaxed(clk_base + CPU_FINETRIM_SELECT); +} + +/** + * tegra114_clock_tune_cpu_trimmers_high - use high-voltage propagation delays + * + * When the CPU rail voltage is in the high-voltage range, use the + * built-in hardwired clock propagation delays in the CPU clock + * shaper. No return value. + */ +void tegra114_clock_tune_cpu_trimmers_high(void) +{ + u32 select = 0; + + /* Use hardwired rise->rise & fall->fall clock propagation delays */ + select |= ~(CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 | + CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 | + CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6); + writel_relaxed(select, clk_base + CPU_FINETRIM_SELECT); + + tegra114_car_barrier(); +} +EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_high); + +/** + * tegra114_clock_tune_cpu_trimmers_low - use low-voltage propagation delays + * + * When the CPU rail voltage is in the low-voltage range, use the + * extended clock propagation delays set by + * tegra114_clock_tune_cpu_trimmers_init(). The intention is to + * maintain the input clock duty cycle that the FCPU subsystem + * expects. No return value. + */ +void tegra114_clock_tune_cpu_trimmers_low(void) +{ + u32 select = 0; + + /* + * Use software-specified rise->rise & fall->fall clock + * propagation delays (from + * tegra114_clock_tune_cpu_trimmers_init() + */ + select |= (CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 | + CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 | + CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6); + writel_relaxed(select, clk_base + CPU_FINETRIM_SELECT); + + tegra114_car_barrier(); +} +EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_low); + +/** + * tegra114_clock_tune_cpu_trimmers_init - set up and enable clk prop delays + * + * Program extended clock propagation delays into the FCPU clock + * shaper and enable them. XXX Define the purpose - peak current + * reduction? No return value. + */ +/* XXX Initial voltage rail state assumption issues? */ +void tegra114_clock_tune_cpu_trimmers_init(void) +{ + u32 dr = 0, r = 0; + + /* Increment the rise->rise clock delay by four steps */ + r |= (CPU_FINETRIM_R_FCPU_1_MASK | CPU_FINETRIM_R_FCPU_2_MASK | + CPU_FINETRIM_R_FCPU_3_MASK | CPU_FINETRIM_R_FCPU_4_MASK | + CPU_FINETRIM_R_FCPU_5_MASK | CPU_FINETRIM_R_FCPU_6_MASK); + writel_relaxed(r, clk_base + CPU_FINETRIM_R); + + /* + * Use the rise->rise clock propagation delay specified in the + * r field + */ + dr |= (CPU_FINETRIM_1_FCPU_1 | CPU_FINETRIM_1_FCPU_2 | + CPU_FINETRIM_1_FCPU_3 | CPU_FINETRIM_1_FCPU_4 | + CPU_FINETRIM_1_FCPU_5 | CPU_FINETRIM_1_FCPU_6); + writel_relaxed(dr, clk_base + CPU_FINETRIM_DR); + + tegra114_clock_tune_cpu_trimmers_low(); +} +EXPORT_SYMBOL(tegra114_clock_tune_cpu_trimmers_init); + +/** + * tegra114_clock_assert_dfll_dvco_reset - assert the DFLL's DVCO reset + * + * Assert the reset line of the DFLL's DVCO. No return value. + */ +void tegra114_clock_assert_dfll_dvco_reset(void) +{ + u32 v; + + v = readl_relaxed(clk_base + RST_DFLL_DVCO); + v |= (1 << DVFS_DFLL_RESET_SHIFT); + writel_relaxed(v, clk_base + RST_DFLL_DVCO); + tegra114_car_barrier(); +} +EXPORT_SYMBOL(tegra114_clock_assert_dfll_dvco_reset); + +/** + * tegra114_clock_deassert_dfll_dvco_reset - deassert the DFLL's DVCO reset + * + * Deassert the reset line of the DFLL's DVCO, allowing the DVCO to + * operate. No return value. + */ +void tegra114_clock_deassert_dfll_dvco_reset(void) +{ + u32 v; + + v = readl_relaxed(clk_base + RST_DFLL_DVCO); + v &= ~(1 << DVFS_DFLL_RESET_SHIFT); + writel_relaxed(v, clk_base + RST_DFLL_DVCO); + tegra114_car_barrier(); +} +EXPORT_SYMBOL(tegra114_clock_deassert_dfll_dvco_reset); + +static void __init tegra114_clock_init(struct device_node *np) { struct device_node *node; int i; @@ -2104,3 +2359,4 @@ void __init tegra114_clock_init(struct device_node *np) tegra_cpu_car_ops = &tegra114_cpu_car_ops; } +CLK_OF_DECLARE(tegra114, "nvidia,tegra114-car", tegra114_clock_init); diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index 075db0c99edb..759ca47be753 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -1287,7 +1287,7 @@ static const struct of_device_id pmc_match[] __initconst = { {}, }; -void __init tegra20_clock_init(struct device_node *np) +static void __init tegra20_clock_init(struct device_node *np) { int i; struct device_node *node; @@ -1339,3 +1339,4 @@ void __init tegra20_clock_init(struct device_node *np) tegra_cpu_car_ops = &tegra20_cpu_car_ops; } +CLK_OF_DECLARE(tegra20, "nvidia,tegra20-car", tegra20_clock_init); diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index ba99e3844106..e2c6ca0431d6 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -252,6 +252,9 @@ #define CLK_RESET_CCLK_RUN_POLICY 2 #define CLK_RESET_CCLK_BURST_POLICY_PLLX 8 +/* PLLM override registers */ +#define PMC_PLLM_WB0_OVERRIDE 0x1dc + #ifdef CONFIG_PM_SLEEP static struct cpu_clk_suspend_context { u32 pllx_misc; @@ -563,6 +566,18 @@ static struct tegra_clk_pll_params pll_c_params = { .lock_delay = 300, }; +static struct div_nmp pllm_nmp = { + .divn_shift = 8, + .divn_width = 10, + .override_divn_shift = 5, + .divm_shift = 0, + .divm_width = 5, + .override_divm_shift = 0, + .divp_shift = 20, + .divp_width = 3, + .override_divp_shift = 15, +}; + static struct tegra_clk_pll_params pll_m_params = { .input_min = 2000000, .input_max = 31000000, @@ -575,6 +590,9 @@ static struct tegra_clk_pll_params pll_m_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllm_nmp, + .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, + .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE, }; static struct tegra_clk_pll_params pll_p_params = { @@ -1223,7 +1241,7 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_2 */ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out2_parents), 0, pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, &clk_out_lock); clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, @@ -1234,7 +1252,7 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_3 */ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out3_parents), 0, pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, &clk_out_lock); clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, @@ -1954,7 +1972,7 @@ static const struct of_device_id pmc_match[] __initconst = { {}, }; -void __init tegra30_clock_init(struct device_node *np) +static void __init tegra30_clock_init(struct device_node *np) { struct device_node *node; int i; @@ -2005,3 +2023,4 @@ void __init tegra30_clock_init(struct device_node *np) tegra_cpu_car_ops = &tegra30_cpu_car_ops; } +CLK_OF_DECLARE(tegra30, "nvidia,tegra30-car", tegra30_clock_init); diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 923ca7ee4694..86581ac1fd69 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -74,18 +74,6 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, } } -static const struct of_device_id tegra_dt_clk_match[] = { - { .compatible = "nvidia,tegra20-car", .data = tegra20_clock_init }, - { .compatible = "nvidia,tegra30-car", .data = tegra30_clock_init }, - { .compatible = "nvidia,tegra114-car", .data = tegra114_clock_init }, - { } -}; - -void __init tegra_clocks_init(void) -{ - of_clk_init(tegra_dt_clk_match); -} - tegra_clk_apply_init_table_func tegra_clk_apply_init_table; void __init tegra_clocks_apply_init_table(void) diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index e0565620d68e..07cfacd91686 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -128,6 +128,31 @@ struct pdiv_map { }; /** + * struct div_nmp - offset and width of m,n and p fields + * + * @divn_shift: shift to the feedback divider bit field + * @divn_width: width of the feedback divider bit field + * @divm_shift: shift to the input divider bit field + * @divm_width: width of the input divider bit field + * @divp_shift: shift to the post divider bit field + * @divp_width: width of the post divider bit field + * @override_divn_shift: shift to the feedback divider bitfield in override reg + * @override_divm_shift: shift to the input divider bitfield in override reg + * @override_divp_shift: shift to the post divider bitfield in override reg + */ +struct div_nmp { + u8 divn_shift; + u8 divn_width; + u8 divm_shift; + u8 divm_width; + u8 divp_shift; + u8 divp_width; + u8 override_divn_shift; + u8 override_divm_shift; + u8 override_divp_shift; +}; + +/** * struct clk_pll_params - PLL parameters * * @input_min: Minimum input frequency @@ -161,11 +186,14 @@ struct tegra_clk_pll_params { u32 aux_reg; u32 dyn_ramp_reg; u32 ext_misc_reg[3]; + u32 pmc_divnm_reg; + u32 pmc_divp_reg; int stepa_shift; int stepb_shift; int lock_delay; int max_p; struct pdiv_map *pdiv_tohw; + struct div_nmp *div_nmp; }; /** @@ -179,12 +207,6 @@ struct tegra_clk_pll_params { * @flags: PLL flags * @fixed_rate: PLL rate if it is fixed * @lock: register lock - * @divn_shift: shift to the feedback divider bit field - * @divn_width: width of the feedback divider bit field - * @divm_shift: shift to the input divider bit field - * @divm_width: width of the input divider bit field - * @divp_shift: shift to the post divider bit field - * @divp_width: width of the post divider bit field * * Flags: * TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for @@ -214,12 +236,6 @@ struct tegra_clk_pll { u32 flags; unsigned long fixed_rate; spinlock_t *lock; - u8 divn_shift; - u8 divn_width; - u8 divm_shift; - u8 divm_width; - u8 divp_shift; - u8 divp_width; struct tegra_clk_pll_freq_table *freq_table; struct tegra_clk_pll_params *params; }; @@ -571,23 +587,11 @@ void tegra_init_from_table(struct tegra_clk_init_table *tbl, void tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, struct clk *clks[], int clk_max); -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -void tegra20_clock_init(struct device_node *np); -#else -static inline void tegra20_clock_init(struct device_node *np) {} -#endif /* CONFIG_ARCH_TEGRA_2x_SOC */ - -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -void tegra30_clock_init(struct device_node *np); -#else -static inline void tegra30_clock_init(struct device_node *np) {} -#endif /* CONFIG_ARCH_TEGRA_3x_SOC */ - -#ifdef CONFIG_ARCH_TEGRA_114_SOC -void tegra114_clock_init(struct device_node *np); -#else -static inline void tegra114_clock_init(struct device_node *np) {} -#endif /* CONFIG_ARCH_TEGRA114_SOC */ +void tegra114_clock_tune_cpu_trimmers_high(void); +void tegra114_clock_tune_cpu_trimmers_low(void); +void tegra114_clock_tune_cpu_trimmers_init(void); +void tegra114_clock_assert_dfll_dvco_reset(void); +void tegra114_clock_deassert_dfll_dvco_reset(void); typedef void (*tegra_clk_apply_init_table_func)(void); extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c index a0fca004abc1..e7bd62cf60b3 100644 --- a/drivers/clk/ux500/abx500-clk.c +++ b/drivers/clk/ux500/abx500-clk.c @@ -45,7 +45,7 @@ static int ab8500_reg_clks(struct device *dev) CLK_IS_ROOT); clk_register_clkdev(clk, "sysclk", "ab8500-usb.0"); clk_register_clkdev(clk, "sysclk", "ab-iddet.0"); - clk_register_clkdev(clk, "sysclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "sysclk", "snd-soc-mop500.0"); clk_register_clkdev(clk, "sysclk", "shrm_bus"); /* ab8500_sysclk2 */ @@ -70,19 +70,19 @@ static int ab8500_reg_clks(struct device *dev) AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, 38400000, 9000, CLK_IS_ROOT); - clk_register_clkdev(clk, "ulpclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "ulpclk", "snd-soc-mop500.0"); /* ab8500_intclk */ clk = clk_reg_sysctrl_set_parent(dev , "intclk", intclk_parents, 2, intclk_reg_sel, intclk_reg_mask, intclk_reg_bits, 0); - clk_register_clkdev(clk, "intclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "intclk", "snd-soc-mop500.0"); clk_register_clkdev(clk, NULL, "ab8500-pwm.1"); /* ab8500_audioclk */ clk = clk_reg_sysctrl_gate(dev , "audioclk", "intclk", AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, 0, 0); - clk_register_clkdev(clk, "audioclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "audioclk", "ab8500-codec.0"); return 0; } diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index 10adfd2ead21..f26258869deb 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c @@ -12,10 +12,568 @@ #include <linux/clk-provider.h> #include <linux/mfd/dbx500-prcmu.h> #include <linux/platform_data/clk-ux500.h> - #include "clk.h" -void u8540_clk_init(void) +void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { - /* register clocks here */ + struct clk *clk; + + /* Clock sources. */ + /* Fixed ClockGen */ + clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "soc0_pll", NULL); + + clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "soc1_pll", NULL); + + clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "ddr_pll", NULL); + + clk = clk_register_fixed_rate(NULL, "rtc32k", NULL, + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 32768); + clk_register_clkdev(clk, "clk32k", NULL); + clk_register_clkdev(clk, "apb_pclk", "rtc-pl031"); + + clk = clk_register_fixed_rate(NULL, "ulp38m4", NULL, + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 38400000); + + clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "UART"); + + /* msp02clk needs a abx500 clk as parent. Handle by abx500 clk driver */ + clk = clk_reg_prcmu_gate("msp02clk", "ab9540_sysclk12_b1", + PRCMU_MSP02CLK, 0); + clk_register_clkdev(clk, NULL, "MSP02"); + + clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "MSP1"); + + clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "I2C"); + + clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "slim"); + + clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH1"); + + clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH2"); + + clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH3"); + + clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH5"); + + clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH6"); + + clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH7"); + + clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "lcd"); + clk_register_clkdev(clk, "lcd", "mcde"); + + clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BML8580CLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "bml"); + + clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + + clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + + clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "hdmi"); + clk_register_clkdev(clk, "hdmi", "mcde"); + + clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "apeat"); + + clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "apetrace"); + + clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mcde"); + clk_register_clkdev(clk, "mcde", "mcde"); + clk_register_clkdev(clk, NULL, "dsilink.0"); + clk_register_clkdev(clk, NULL, "dsilink.1"); + clk_register_clkdev(clk, NULL, "dsilink.2"); + + clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "ipi2"); + + clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "dsialt"); + + clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "dma40.0"); + + clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "b2r2"); + clk_register_clkdev(clk, NULL, "b2r2_core"); + clk_register_clkdev(clk, NULL, "U8500-B2R2.0"); + clk_register_clkdev(clk, NULL, "b2r2_1_core"); + + clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "tv"); + clk_register_clkdev(clk, "tv", "mcde"); + + clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "SSP"); + + clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "rngclk"); + + clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "uicc"); + + clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mtu0"); + clk_register_clkdev(clk, NULL, "mtu1"); + + clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, + PRCMU_SDMMCCLK, 100000000, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdmmc"); + + clk = clk_reg_prcmu_opp_volt_scalable("sdmmchclk", NULL, + PRCMU_SDMMCHCLK, 400000000, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdmmchclk"); + + clk = clk_reg_prcmu_gate("hvaclk", NULL, PRCMU_HVACLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "hva"); + + clk = clk_reg_prcmu_gate("g1clk", NULL, PRCMU_G1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "g1"); + + clk = clk_reg_prcmu_scalable("spare1clk", NULL, PRCMU_SPARE1CLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilcd", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk", + PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs2", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.2"); + + clk = clk_reg_prcmu_scalable("dsilcd_pll", "spare1clk", + PRCMU_PLLDSI_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilcd_pll", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll", + PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs0", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi0lcdclk", "dsilcd_pll", + PRCMU_DSI0CLK_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs0", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.0"); + + clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll", + PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs1", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi1lcdclk", "dsilcd_pll", + PRCMU_DSI1CLK_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs1", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.1"); + + clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk", + PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.0"); + clk_register_clkdev(clk, "dsilp0", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk", + PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.1"); + clk_register_clkdev(clk, "dsilp1", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk", + PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.2"); + clk_register_clkdev(clk, "dsilp2", "mcde"); + + clk = clk_reg_prcmu_scalable_rate("armss", NULL, + PRCMU_ARMSS, 0, CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "armss", NULL); + + clk = clk_register_fixed_factor(NULL, "smp_twd", "armss", + CLK_IGNORE_UNUSED, 1, 2); + clk_register_clkdev(clk, NULL, "smp_twd"); + + /* PRCC P-clocks */ + /* Peripheral 1 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", clkrst1_base, + BIT(0), 0); + clk_register_clkdev(clk, "apb_pclk", "uart0"); + + clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", clkrst1_base, + BIT(1), 0); + clk_register_clkdev(clk, "apb_pclk", "uart1"); + + clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", clkrst1_base, + BIT(2), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.1"); + + clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", clkrst1_base, + BIT(3), 0); + clk_register_clkdev(clk, "apb_pclk", "msp0"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.0"); + + clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", clkrst1_base, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "msp1"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.1"); + + clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", clkrst1_base, + BIT(5), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi0"); + + clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", clkrst1_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.2"); + + clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", clkrst1_base, + BIT(7), 0); + clk_register_clkdev(clk, NULL, "spi3"); + + clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", clkrst1_base, + BIT(8), 0); + clk_register_clkdev(clk, "apb_pclk", "slimbus0"); + + clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", clkrst1_base, + BIT(9), 0); + clk_register_clkdev(clk, NULL, "gpio.0"); + clk_register_clkdev(clk, NULL, "gpio.1"); + clk_register_clkdev(clk, NULL, "gpioblock0"); + clk_register_clkdev(clk, "apb_pclk", "ab85xx-codec.0"); + + clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", clkrst1_base, + BIT(10), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.4"); + + clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", clkrst1_base, + BIT(11), 0); + clk_register_clkdev(clk, "apb_pclk", "msp3"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.3"); + + /* Peripheral 2 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", clkrst2_base, + BIT(0), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.3"); + + clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", clkrst2_base, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "spi2"); + + clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", clkrst2_base, + BIT(2), 0); + clk_register_clkdev(clk, NULL, "spi1"); + + clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", clkrst2_base, + BIT(3), 0); + clk_register_clkdev(clk, NULL, "pwl"); + + clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", clkrst2_base, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi4"); + + clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", clkrst2_base, + BIT(5), 0); + clk_register_clkdev(clk, "apb_pclk", "msp2"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.2"); + + clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", clkrst2_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi1"); + + clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", clkrst2_base, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi3"); + + clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", clkrst2_base, + BIT(8), 0); + clk_register_clkdev(clk, NULL, "spi0"); + + clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", clkrst2_base, + BIT(9), 0); + clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0"); + + clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", clkrst2_base, + BIT(10), 0); + clk_register_clkdev(clk, "hsit_hclk", "ste_hsi.0"); + + clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", clkrst2_base, + BIT(11), 0); + clk_register_clkdev(clk, NULL, "gpio.6"); + clk_register_clkdev(clk, NULL, "gpio.7"); + clk_register_clkdev(clk, NULL, "gpioblock1"); + + clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", clkrst2_base, + BIT(12), 0); + clk_register_clkdev(clk, "msp4-pclk", "ab85xx-codec.0"); + + /* Peripheral 3 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", clkrst3_base, + BIT(0), 0); + clk_register_clkdev(clk, NULL, "fsmc"); + + clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", clkrst3_base, + BIT(1), 0); + clk_register_clkdev(clk, "apb_pclk", "ssp0"); + + clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", clkrst3_base, + BIT(2), 0); + clk_register_clkdev(clk, "apb_pclk", "ssp1"); + + clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", clkrst3_base, + BIT(3), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.0"); + + clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", clkrst3_base, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi2"); + + clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", clkrst3_base, + BIT(5), 0); + clk_register_clkdev(clk, "apb_pclk", "ske"); + clk_register_clkdev(clk, "apb_pclk", "nmk-ske-keypad"); + + clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", clkrst3_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "uart2"); + + clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", clkrst3_base, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi5"); + + clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", clkrst3_base, + BIT(8), 0); + clk_register_clkdev(clk, NULL, "gpio.2"); + clk_register_clkdev(clk, NULL, "gpio.3"); + clk_register_clkdev(clk, NULL, "gpio.4"); + clk_register_clkdev(clk, NULL, "gpio.5"); + clk_register_clkdev(clk, NULL, "gpioblock2"); + + clk = clk_reg_prcc_pclk("p3_pclk9", "per3clk", clkrst3_base, + BIT(9), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.5"); + + clk = clk_reg_prcc_pclk("p3_pclk10", "per3clk", clkrst3_base, + BIT(10), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.6"); + + clk = clk_reg_prcc_pclk("p3_pclk11", "per3clk", clkrst3_base, + BIT(11), 0); + clk_register_clkdev(clk, "apb_pclk", "uart3"); + + clk = clk_reg_prcc_pclk("p3_pclk12", "per3clk", clkrst3_base, + BIT(12), 0); + clk_register_clkdev(clk, "apb_pclk", "uart4"); + + /* Peripheral 5 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", clkrst5_base, + BIT(0), 0); + clk_register_clkdev(clk, "usb", "musb-ux500.0"); + clk_register_clkdev(clk, "usbclk", "ab-iddet.0"); + + clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", clkrst5_base, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "gpio.8"); + clk_register_clkdev(clk, NULL, "gpioblock3"); + + /* Peripheral 6 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", clkrst6_base, + BIT(0), 0); + clk_register_clkdev(clk, "apb_pclk", "rng"); + + clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", clkrst6_base, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "cryp0"); + clk_register_clkdev(clk, NULL, "cryp1"); + + clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", clkrst6_base, + BIT(2), 0); + clk_register_clkdev(clk, NULL, "hash0"); + + clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", clkrst6_base, + BIT(3), 0); + clk_register_clkdev(clk, NULL, "pka"); + + clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", clkrst6_base, + BIT(4), 0); + clk_register_clkdev(clk, NULL, "db8540-hash1"); + + clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", clkrst6_base, + BIT(5), 0); + clk_register_clkdev(clk, NULL, "cfgreg"); + + clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", clkrst6_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "mtu0"); + + clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", clkrst6_base, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "mtu1"); + + /* + * PRCC K-clocks ==> see table PRCC_PCKEN/PRCC_KCKEN + * This differs from the internal implementation: + * We don't use the PERPIH[n| clock as parent, since those _should_ + * only be used as parents for the P-clocks. + * TODO: "parentjoin" with corresponding P-clocks for all K-clocks. + */ + + /* Peripheral 1 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk", + clkrst1_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart0"); + + clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk", + clkrst1_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart1"); + + clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk", + clkrst1_base, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.1"); + + clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk", + clkrst1_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp0"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.0"); + + clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk", + clkrst1_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp1"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.1"); + + clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmchclk", + clkrst1_base, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi0"); + + clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk", + clkrst1_base, BIT(6), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.2"); + + clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk", + clkrst1_base, BIT(8), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "slimbus0"); + + clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk", + clkrst1_base, BIT(9), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.4"); + + clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk", + clkrst1_base, BIT(10), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp3"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.3"); + + /* Peripheral 2 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk", + clkrst2_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.3"); + + clk = clk_reg_prcc_kclk("p2_pwl_kclk", "rtc32k", + clkrst2_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "pwl"); + + clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmchclk", + clkrst2_base, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi4"); + + clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk", + clkrst2_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp2"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.2"); + + clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmchclk", + clkrst2_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi1"); + + clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk", + clkrst2_base, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi3"); + + clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk", + clkrst2_base, BIT(6), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + clk_register_clkdev(clk, "hsir_hsirxclk", "ste_hsi.0"); + + clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk", + clkrst2_base, BIT(7), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + clk_register_clkdev(clk, "hsit_hsitxclk", "ste_hsi.0"); + + /* Should only be 9540, but might be added for 85xx as well */ + clk = clk_reg_prcc_kclk("p2_msp4_kclk", "msp02clk", + clkrst2_base, BIT(9), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp4"); + clk_register_clkdev(clk, "msp4", "ab85xx-codec.0"); + + /* Peripheral 3 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk", + clkrst3_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "ssp0"); + + clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk", + clkrst3_base, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "ssp1"); + + clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk", + clkrst3_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.0"); + + clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmchclk", + clkrst3_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi2"); + + clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k", + clkrst3_base, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "ske"); + clk_register_clkdev(clk, NULL, "nmk-ske-keypad"); + + clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk", + clkrst3_base, BIT(6), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart2"); + + clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk", + clkrst3_base, BIT(7), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi5"); + + clk = clk_reg_prcc_kclk("p3_i2c5_kclk", "i2cclk", + clkrst3_base, BIT(8), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.5"); + + clk = clk_reg_prcc_kclk("p3_i2c6_kclk", "i2cclk", + clkrst3_base, BIT(9), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.6"); + + clk = clk_reg_prcc_kclk("p3_uart3_kclk", "uartclk", + clkrst3_base, BIT(10), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart3"); + + clk = clk_reg_prcc_kclk("p3_uart4_kclk", "uartclk", + clkrst3_base, BIT(11), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart4"); + + /* Peripheral 6 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p6_rng_kclk", "rngclk", + clkrst6_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "rng"); } diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c index dbc0191e16c8..44794782e7e0 100644 --- a/drivers/clk/ux500/u9540_clk.c +++ b/drivers/clk/ux500/u9540_clk.c @@ -12,10 +12,10 @@ #include <linux/clk-provider.h> #include <linux/mfd/dbx500-prcmu.h> #include <linux/platform_data/clk-ux500.h> - #include "clk.h" -void u9540_clk_init(void) +void u9540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { /* register clocks here */ } diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c index 256c8be74df8..2dc8b41a339d 100644 --- a/drivers/clk/versatile/clk-vexpress-osc.c +++ b/drivers/clk/versatile/clk-vexpress-osc.c @@ -107,7 +107,7 @@ void __init vexpress_osc_of_setup(struct device_node *node) osc->func = vexpress_config_func_get_by_node(node); if (!osc->func) { pr_err("Failed to obtain config func for node '%s'!\n", - node->name); + node->full_name); goto error; } @@ -119,7 +119,7 @@ void __init vexpress_osc_of_setup(struct device_node *node) of_property_read_string(node, "clock-output-names", &init.name); if (!init.name) - init.name = node->name; + init.name = node->full_name; init.ops = &vexpress_osc_ops; init.flags = CLK_IS_ROOT; diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c index 4f45eee9e33b..812f83f8b0c6 100644 --- a/drivers/clk/x86/clk-lpt.c +++ b/drivers/clk/x86/clk-lpt.c @@ -1,5 +1,5 @@ /* - * Intel Lynxpoint LPSS clocks. + * Intel Low Power Subsystem clocks. * * Copyright (C) 2013, Intel Corporation * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> @@ -18,8 +18,6 @@ #include <linux/platform_data/clk-lpss.h> #include <linux/platform_device.h> -#define PRV_CLOCK_PARAMS 0x800 - static int lpt_clk_probe(struct platform_device *pdev) { struct lpss_clk_data *drvdata; diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index a92440896868..de4d5d93c3fd 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -5,6 +5,7 @@ config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK + select CPU_FREQ_TABLE help This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. @@ -18,6 +19,7 @@ config ARM_DT_BL_CPUFREQ config ARM_EXYNOS_CPUFREQ bool "SAMSUNG EXYNOS SoCs" depends on ARCH_EXYNOS + select CPU_FREQ_TABLE default y help This adds the CPUFreq driver common part for Samsung @@ -46,6 +48,7 @@ config ARM_EXYNOS5250_CPUFREQ config ARM_EXYNOS5440_CPUFREQ def_bool SOC_EXYNOS5440 depends on HAVE_CLK && PM_OPP && OF + select CPU_FREQ_TABLE help This adds the CPUFreq driver for Samsung EXYNOS5440 SoC. The nature of exynos5440 clock controller is @@ -55,7 +58,6 @@ config ARM_EXYNOS5440_CPUFREQ config ARM_HIGHBANK_CPUFREQ tristate "Calxeda Highbank-based" depends on ARCH_HIGHBANK - select CPU_FREQ_TABLE select GENERIC_CPUFREQ_CPU0 select PM_OPP select REGULATOR @@ -71,6 +73,7 @@ config ARM_IMX6Q_CPUFREQ tristate "Freescale i.MX6Q cpufreq support" depends on SOC_IMX6Q depends on REGULATOR_ANATOP + select CPU_FREQ_TABLE help This adds cpufreq driver support for Freescale i.MX6Q SOC. @@ -86,6 +89,7 @@ config ARM_INTEGRATOR config ARM_KIRKWOOD_CPUFREQ def_bool ARCH_KIRKWOOD && OF + select CPU_FREQ_TABLE help This adds the CPUFreq driver for Marvell Kirkwood SoCs. @@ -149,6 +153,7 @@ config ARM_S3C2412_CPUFREQ config ARM_S3C2416_CPUFREQ bool "S3C2416 CPU Frequency scaling support" depends on CPU_S3C2416 + select CPU_FREQ_TABLE help This adds the CPUFreq driver for the Samsung S3C2416 and S3C2450 SoC. The S3C2416 supports changing the rate of the @@ -179,6 +184,7 @@ config ARM_S3C2440_CPUFREQ config ARM_S3C64XX_CPUFREQ bool "Samsung S3C64XX" depends on CPU_S3C6410 + select CPU_FREQ_TABLE default y help This adds the CPUFreq driver for Samsung S3C6410 SoC. @@ -205,6 +211,15 @@ config ARM_SA1110_CPUFREQ config ARM_SPEAR_CPUFREQ bool "SPEAr CPUFreq support" depends on PLAT_SPEAR + select CPU_FREQ_TABLE default y help This adds the CPUFreq driver support for SPEAr SOCs. + +config ARM_TEGRA_CPUFREQ + bool "TEGRA CPUFreq support" + depends on ARCH_TEGRA + select CPU_FREQ_TABLE + default y + help + This adds the CPUFreq driver support for TEGRA SOCs. diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc index 9c926ca0d718..25ca9db62e09 100644 --- a/drivers/cpufreq/Kconfig.powerpc +++ b/drivers/cpufreq/Kconfig.powerpc @@ -1,6 +1,7 @@ config CPU_FREQ_CBE tristate "CBE frequency scaling" depends on CBE_RAS && PPC_CELL + select CPU_FREQ_TABLE default m help This adds the cpufreq driver for Cell BE processors. @@ -23,3 +24,39 @@ config CPU_FREQ_MAPLE help This adds support for frequency switching on Maple 970FX Evaluation Board and compatible boards (IBM JS2x blades). + +config PPC_CORENET_CPUFREQ + tristate "CPU frequency scaling driver for Freescale E500MC SoCs" + depends on PPC_E500MC && OF && COMMON_CLK + select CPU_FREQ_TABLE + select CLK_PPC_CORENET + help + This adds the CPUFreq driver support for Freescale e500mc, + e5500 and e6500 series SoCs which are capable of changing + the CPU's frequency dynamically. + +config CPU_FREQ_PMAC + bool "Support for Apple PowerBooks" + depends on ADB_PMU && PPC32 + select CPU_FREQ_TABLE + help + This adds support for frequency switching on Apple PowerBooks, + this currently includes some models of iBook & Titanium + PowerBook. + +config CPU_FREQ_PMAC64 + bool "Support for some Apple G5s" + depends on PPC_PMAC && PPC64 + select CPU_FREQ_TABLE + help + This adds support for frequency switching on Apple iMac G5, + and some of the more recent desktop G5 machines as well. + +config PPC_PASEMI_CPUFREQ + bool "Support for PA Semi PWRficient" + depends on PPC_PASEMI + select CPU_FREQ_TABLE + default y + help + This adds the support for frequency switching on PA Semi + PWRficient processors. diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index 6bd63d63d356..e2b6eabef221 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -132,6 +132,7 @@ config X86_POWERNOW_K8 config X86_AMD_FREQ_SENSITIVITY tristate "AMD frequency sensitivity feedback powersave bias" depends on CPU_FREQ_GOV_ONDEMAND && X86_ACPI_CPUFREQ && CPU_SUP_AMD + select CPU_FREQ_TABLE help This adds AMD-specific powersave bias function to the ondemand governor, which allows it to make more power-conscious frequency diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 6ad0b913ca17..d345b5a7aa71 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -76,7 +76,7 @@ obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o -obj-$(CONFIG_ARCH_TEGRA) += tegra-cpufreq.o +obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o ################################################################################## # PowerPC platform drivers @@ -84,11 +84,15 @@ obj-$(CONFIG_CPU_FREQ_CBE) += ppc-cbe-cpufreq.o ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o +obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o +obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o +obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o +obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o ################################################################################## # Other platform drivers obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o -obj-$(CONFIG_BLACKFIN) += blackfin-cpufreq.o +obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index edc089e9d0c4..39264020b88a 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -70,6 +70,7 @@ struct acpi_cpufreq_data { struct cpufreq_frequency_table *freq_table; unsigned int resume; unsigned int cpu_feature; + cpumask_var_t freqdomain_cpus; }; static DEFINE_PER_CPU(struct acpi_cpufreq_data *, acfreq_data); @@ -176,6 +177,15 @@ static struct global_attr global_boost = __ATTR(boost, 0644, show_global_boost, store_global_boost); +static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) +{ + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + + return cpufreq_show_cpus(data->freqdomain_cpus, buf); +} + +cpufreq_freq_attr_ro(freqdomain_cpus); + #ifdef CONFIG_X86_ACPI_CPUFREQ_CPB static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf, size_t count) @@ -232,7 +242,7 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data) perf = data->acpi_data; for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - if (msr == perf->states[data->freq_table[i].index].status) + if (msr == perf->states[data->freq_table[i].driver_data].status) return data->freq_table[i].frequency; } return data->freq_table[0].frequency; @@ -442,7 +452,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, goto out; } - next_perf_state = data->freq_table[next_state].index; + next_perf_state = data->freq_table[next_state].driver_data; if (perf->state == next_perf_state) { if (unlikely(data->resume)) { pr_debug("Called after resume, resetting to P%d\n", @@ -494,12 +504,14 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, pr_debug("acpi_cpufreq_target failed (%d)\n", policy->cpu); result = -EAGAIN; - goto out; + freqs.new = freqs.old; } } cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - perf->state = next_perf_state; + + if (!result) + perf->state = next_perf_state; out: return result; @@ -702,6 +714,11 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (!data) return -ENOMEM; + if (!zalloc_cpumask_var(&data->freqdomain_cpus, GFP_KERNEL)) { + result = -ENOMEM; + goto err_free; + } + data->acpi_data = per_cpu_ptr(acpi_perf_data, cpu); per_cpu(acfreq_data, cpu) = data; @@ -710,7 +727,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) result = acpi_processor_register_performance(data->acpi_data, cpu); if (result) - goto err_free; + goto err_free_mask; perf = data->acpi_data; policy->shared_type = perf->shared_type; @@ -723,6 +740,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) { cpumask_copy(policy->cpus, perf->shared_cpu_map); } + cpumask_copy(data->freqdomain_cpus, perf->shared_cpu_map); #ifdef CONFIG_SMP dmi_check_system(sw_any_bug_dmi_table); @@ -734,6 +752,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (check_amd_hwpstate_cpu(cpu) && !acpi_pstate_strict) { cpumask_clear(policy->cpus); cpumask_set_cpu(cpu, policy->cpus); + cpumask_copy(data->freqdomain_cpus, cpu_sibling_mask(cpu)); policy->shared_type = CPUFREQ_SHARED_TYPE_HW; pr_info_once(PFX "overriding BIOS provided _PSD data\n"); } @@ -811,7 +830,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) data->freq_table[valid_states-1].frequency / 1000) continue; - data->freq_table[valid_states].index = i; + data->freq_table[valid_states].driver_data = i; data->freq_table[valid_states].frequency = perf->states[i].core_frequency * 1000; valid_states++; @@ -868,6 +887,8 @@ err_freqfree: kfree(data->freq_table); err_unreg: acpi_processor_unregister_performance(perf, cpu); +err_free_mask: + free_cpumask_var(data->freqdomain_cpus); err_free: kfree(data); per_cpu(acfreq_data, cpu) = NULL; @@ -886,6 +907,7 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) per_cpu(acfreq_data, policy->cpu) = NULL; acpi_processor_unregister_performance(data->acpi_data, policy->cpu); + free_cpumask_var(data->freqdomain_cpus); kfree(data->freq_table); kfree(data); } @@ -906,6 +928,7 @@ static int acpi_cpufreq_resume(struct cpufreq_policy *policy) static struct freq_attr *acpi_cpufreq_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, + &freqdomain_cpus, NULL, /* this is a placeholder for cpb, do not remove */ NULL, }; @@ -947,7 +970,7 @@ static void __init acpi_cpufreq_boost_init(void) /* We create the boost file in any case, though for systems without * hardware support it will be read-only and hardwired to return 0. */ - if (sysfs_create_file(cpufreq_global_kobject, &(global_boost.attr))) + if (cpufreq_sysfs_create_file(&(global_boost.attr))) pr_warn(PFX "could not register global boost sysfs file\n"); else pr_debug("registered global boost sysfs file\n"); @@ -955,7 +978,7 @@ static void __init acpi_cpufreq_boost_init(void) static void __exit acpi_cpufreq_boost_exit(void) { - sysfs_remove_file(cpufreq_global_kobject, &(global_boost.attr)); + cpufreq_sysfs_remove_file(&(global_boost.attr)); if (msrs) { unregister_cpu_notifier(&boost_nb); @@ -1034,4 +1057,11 @@ static const struct x86_cpu_id acpi_cpufreq_ids[] = { }; MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids); +static const struct acpi_device_id processor_device_ids[] = { + {ACPI_PROCESSOR_OBJECT_HID, }, + {ACPI_PROCESSOR_DEVICE_HID, }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, processor_device_ids); + MODULE_ALIAS("acpi"); diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 5d7f53fcd6f5..3549f0784af1 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -84,11 +84,9 @@ static int bL_cpufreq_set_target(struct cpufreq_policy *policy, ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000); if (ret) { pr_err("clk_set_rate failed: %d\n", ret); - return ret; + freqs.new = freqs.old; } - policy->cur = freqs.new; - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); return ret; diff --git a/drivers/cpufreq/blackfin-cpufreq.c b/drivers/cpufreq/blackfin-cpufreq.c index 995511e80bef..9cdbbd278a80 100644 --- a/drivers/cpufreq/blackfin-cpufreq.c +++ b/drivers/cpufreq/blackfin-cpufreq.c @@ -20,23 +20,23 @@ /* this is the table of CCLK frequencies, in Hz */ -/* .index is the entry in the auxiliary dpm_state_table[] */ +/* .driver_data is the entry in the auxiliary dpm_state_table[] */ static struct cpufreq_frequency_table bfin_freq_table[] = { { .frequency = CPUFREQ_TABLE_END, - .index = 0, + .driver_data = 0, }, { .frequency = CPUFREQ_TABLE_END, - .index = 1, + .driver_data = 1, }, { .frequency = CPUFREQ_TABLE_END, - .index = 2, + .driver_data = 2, }, { .frequency = CPUFREQ_TABLE_END, - .index = 0, + .driver_data = 0, }, }; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 2d53f47d1747..6a015ada5285 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -3,6 +3,7 @@ * * Copyright (C) 2001 Russell King * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> + * (C) 2013 Viresh Kumar <viresh.kumar@linaro.org> * * Oct 2005 - Ashok Raj <ashok.raj@intel.com> * Added handling for CPU hotplug @@ -12,12 +13,13 @@ * 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) KBUILD_MODNAME ": " fmt +#include <asm/cputime.h> #include <linux/kernel.h> +#include <linux/kernel_stat.h> #include <linux/module.h> #include <linux/init.h> #include <linux/notifier.h> @@ -25,6 +27,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <linux/tick.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/cpu.h> @@ -41,11 +44,13 @@ */ static struct cpufreq_driver *cpufreq_driver; static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); +static DEFINE_RWLOCK(cpufreq_driver_lock); +static DEFINE_MUTEX(cpufreq_governor_lock); + #ifdef CONFIG_HOTPLUG_CPU /* This one keeps track of the previously set governor of a removed CPU */ static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); #endif -static DEFINE_RWLOCK(cpufreq_driver_lock); /* * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure @@ -132,6 +137,51 @@ bool have_governor_per_policy(void) { return cpufreq_driver->have_governor_per_policy; } +EXPORT_SYMBOL_GPL(have_governor_per_policy); + +struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy) +{ + if (have_governor_per_policy()) + return &policy->kobj; + else + return cpufreq_global_kobject; +} +EXPORT_SYMBOL_GPL(get_governor_parent_kobj); + +static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) +{ + u64 idle_time; + u64 cur_wall_time; + u64 busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = cputime_to_usecs(cur_wall_time); + + return cputime_to_usecs(idle_time); +} + +u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL); + + if (idle_time == -1ULL) + return get_cpu_idle_time_jiffy(cpu, wall); + else if (!io_busy) + idle_time += get_cpu_iowait_time_us(cpu, wall); + + return idle_time; +} +EXPORT_SYMBOL_GPL(get_cpu_idle_time); static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) { @@ -150,7 +200,6 @@ static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) if (!try_module_get(cpufreq_driver->owner)) goto err_out_unlock; - /* get the CPU */ data = per_cpu(cpufreq_cpu_data, cpu); @@ -220,7 +269,7 @@ static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data) */ #ifndef CONFIG_SMP static unsigned long l_p_j_ref; -static unsigned int l_p_j_ref_freq; +static unsigned int l_p_j_ref_freq; static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { @@ -233,7 +282,7 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) pr_debug("saving %lu as reference value for loops_per_jiffy; " "freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq); } - if ((val == CPUFREQ_POSTCHANGE && ci->old != ci->new) || + if ((val == CPUFREQ_POSTCHANGE && ci->old != ci->new) || (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); @@ -248,8 +297,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) } #endif - -void __cpufreq_notify_transition(struct cpufreq_policy *policy, +static void __cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) { BUG_ON(irqs_disabled()); @@ -264,6 +312,12 @@ void __cpufreq_notify_transition(struct cpufreq_policy *policy, switch (state) { case CPUFREQ_PRECHANGE: + if (WARN(policy->transition_ongoing, + "In middle of another frequency transition\n")) + return; + + policy->transition_ongoing = true; + /* detect if the driver reported a value as "old frequency" * which is not equal to what the cpufreq core thinks is * "old frequency". @@ -283,6 +337,12 @@ void __cpufreq_notify_transition(struct cpufreq_policy *policy, break; case CPUFREQ_POSTCHANGE: + if (WARN(!policy->transition_ongoing, + "No frequency transition in progress\n")) + return; + + policy->transition_ongoing = false; + adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, (unsigned long)freqs->cpu); @@ -294,6 +354,7 @@ void __cpufreq_notify_transition(struct cpufreq_policy *policy, break; } } + /** * cpufreq_notify_transition - call notifier chain and adjust_jiffies * on frequency transition. @@ -311,7 +372,6 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy, EXPORT_SYMBOL_GPL(cpufreq_notify_transition); - /********************************************************************* * SYSFS INTERFACE * *********************************************************************/ @@ -376,7 +436,6 @@ out: return err; } - /** * cpufreq_per_cpu_attr_read() / show_##file_name() - * print out cpufreq information @@ -441,7 +500,6 @@ static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy *policy, return sprintf(buf, "%u\n", cur_freq); } - /** * show_scaling_governor - show the current policy for the specified CPU */ @@ -457,7 +515,6 @@ static ssize_t show_scaling_governor(struct cpufreq_policy *policy, char *buf) return -EINVAL; } - /** * store_scaling_governor - store policy for the specified CPU */ @@ -480,8 +537,10 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy, &new_policy.governor)) return -EINVAL; - /* Do not use cpufreq_set_policy here or the user_policy.max - will be wrongly overridden */ + /* + * Do not use cpufreq_set_policy here or the user_policy.max + * will be wrongly overridden + */ ret = __cpufreq_set_policy(policy, &new_policy); policy->user_policy.policy = policy->policy; @@ -526,7 +585,7 @@ out: return i; } -static ssize_t show_cpus(const struct cpumask *mask, char *buf) +ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf) { ssize_t i = 0; unsigned int cpu; @@ -541,6 +600,7 @@ static ssize_t show_cpus(const struct cpumask *mask, char *buf) i += sprintf(&buf[i], "\n"); return i; } +EXPORT_SYMBOL_GPL(cpufreq_show_cpus); /** * show_related_cpus - show the CPUs affected by each transition even if @@ -548,7 +608,7 @@ static ssize_t show_cpus(const struct cpumask *mask, char *buf) */ static ssize_t show_related_cpus(struct cpufreq_policy *policy, char *buf) { - return show_cpus(policy->related_cpus, buf); + return cpufreq_show_cpus(policy->related_cpus, buf); } /** @@ -556,7 +616,7 @@ static ssize_t show_related_cpus(struct cpufreq_policy *policy, char *buf) */ static ssize_t show_affected_cpus(struct cpufreq_policy *policy, char *buf) { - return show_cpus(policy->cpus, buf); + return cpufreq_show_cpus(policy->cpus, buf); } static ssize_t store_scaling_setspeed(struct cpufreq_policy *policy, @@ -630,9 +690,6 @@ static struct attribute *default_attrs[] = { NULL }; -struct kobject *cpufreq_global_kobject; -EXPORT_SYMBOL(cpufreq_global_kobject); - #define to_policy(k) container_of(k, struct cpufreq_policy, kobj) #define to_attr(a) container_of(a, struct freq_attr, attr) @@ -703,6 +760,49 @@ static struct kobj_type ktype_cpufreq = { .release = cpufreq_sysfs_release, }; +struct kobject *cpufreq_global_kobject; +EXPORT_SYMBOL(cpufreq_global_kobject); + +static int cpufreq_global_kobject_usage; + +int cpufreq_get_global_kobject(void) +{ + if (!cpufreq_global_kobject_usage++) + return kobject_add(cpufreq_global_kobject, + &cpu_subsys.dev_root->kobj, "%s", "cpufreq"); + + return 0; +} +EXPORT_SYMBOL(cpufreq_get_global_kobject); + +void cpufreq_put_global_kobject(void) +{ + if (!--cpufreq_global_kobject_usage) + kobject_del(cpufreq_global_kobject); +} +EXPORT_SYMBOL(cpufreq_put_global_kobject); + +int cpufreq_sysfs_create_file(const struct attribute *attr) +{ + int ret = cpufreq_get_global_kobject(); + + if (!ret) { + ret = sysfs_create_file(cpufreq_global_kobject, attr); + if (ret) + cpufreq_put_global_kobject(); + } + + return ret; +} +EXPORT_SYMBOL(cpufreq_sysfs_create_file); + +void cpufreq_sysfs_remove_file(const struct attribute *attr) +{ + sysfs_remove_file(cpufreq_global_kobject, attr); + cpufreq_put_global_kobject(); +} +EXPORT_SYMBOL(cpufreq_sysfs_remove_file); + /* symlink affected CPUs */ static int cpufreq_add_dev_symlink(unsigned int cpu, struct cpufreq_policy *policy) @@ -1005,7 +1105,8 @@ static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu) * Caller should already have policy_rwsem in write mode for this CPU. * This routine frees the rwsem before returning. */ -static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) +static int __cpufreq_remove_dev(struct device *dev, + struct subsys_interface *sif) { unsigned int cpu = dev->id, ret, cpus; unsigned long flags; @@ -1112,7 +1213,6 @@ static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif return 0; } - static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) { unsigned int cpu = dev->id; @@ -1125,7 +1225,6 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) return retval; } - static void handle_update(struct work_struct *work) { struct cpufreq_policy *policy = @@ -1136,7 +1235,8 @@ static void handle_update(struct work_struct *work) } /** - * cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're in deep trouble. + * cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're + * in deep trouble. * @cpu: cpu number * @old_freq: CPU frequency the kernel thinks the CPU runs at * @new_freq: CPU frequency the CPU actually runs at @@ -1151,7 +1251,6 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, struct cpufreq_freqs freqs; unsigned long flags; - pr_debug("Warning: CPU frequency out of sync: cpufreq and timing " "core thinks of %u, is %u kHz.\n", old_freq, new_freq); @@ -1166,7 +1265,6 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); } - /** * cpufreq_quick_get - get the CPU frequency (in kHz) from policy->cur * @cpu: CPU number @@ -1212,7 +1310,6 @@ unsigned int cpufreq_quick_get_max(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_quick_get_max); - static unsigned int __cpufreq_get(unsigned int cpu) { struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); @@ -1271,7 +1368,6 @@ static struct subsys_interface cpufreq_interface = { .remove_dev = cpufreq_remove_dev, }; - /** * cpufreq_bp_suspend - Prepare the boot CPU for system suspend. * @@ -1408,11 +1504,10 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) } EXPORT_SYMBOL(cpufreq_register_notifier); - /** * cpufreq_unregister_notifier - unregister a driver with cpufreq * @nb: notifier block to be unregistered - * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER + * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER * * Remove a driver from the CPU frequency notifier list. * @@ -1448,7 +1543,6 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); * GOVERNORS * *********************************************************************/ - int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) @@ -1458,6 +1552,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, if (cpufreq_disabled()) return -ENODEV; + if (policy->transition_ongoing) + return -EBUSY; /* Make sure that target_freq is within supported range */ if (target_freq > policy->max) @@ -1484,10 +1580,6 @@ int cpufreq_driver_target(struct cpufreq_policy *policy, { int ret = -EINVAL; - policy = cpufreq_cpu_get(policy->cpu); - if (!policy) - goto no_policy; - if (unlikely(lock_policy_rwsem_write(policy->cpu))) goto fail; @@ -1496,30 +1588,19 @@ int cpufreq_driver_target(struct cpufreq_policy *policy, unlock_policy_rwsem_write(policy->cpu); fail: - cpufreq_cpu_put(policy); -no_policy: return ret; } EXPORT_SYMBOL_GPL(cpufreq_driver_target); int __cpufreq_driver_getavg(struct cpufreq_policy *policy, unsigned int cpu) { - int ret = 0; - if (cpufreq_disabled()) - return ret; + return 0; if (!cpufreq_driver->getavg) return 0; - policy = cpufreq_cpu_get(policy->cpu); - if (!policy) - return -EINVAL; - - ret = cpufreq_driver->getavg(policy, cpu); - - cpufreq_cpu_put(policy); - return ret; + return cpufreq_driver->getavg(policy, cpu); } EXPORT_SYMBOL_GPL(__cpufreq_driver_getavg); @@ -1562,6 +1643,21 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, pr_debug("__cpufreq_governor for CPU %u, event %u\n", policy->cpu, event); + + mutex_lock(&cpufreq_governor_lock); + if ((!policy->governor_enabled && (event == CPUFREQ_GOV_STOP)) || + (policy->governor_enabled && (event == CPUFREQ_GOV_START))) { + mutex_unlock(&cpufreq_governor_lock); + return -EBUSY; + } + + if (event == CPUFREQ_GOV_STOP) + policy->governor_enabled = false; + else if (event == CPUFREQ_GOV_START) + policy->governor_enabled = true; + + mutex_unlock(&cpufreq_governor_lock); + ret = policy->governor->governor(policy, event); if (!ret) { @@ -1569,6 +1665,14 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, policy->governor->initialized++; else if (event == CPUFREQ_GOV_POLICY_EXIT) policy->governor->initialized--; + } else { + /* Restore original values */ + mutex_lock(&cpufreq_governor_lock); + if (event == CPUFREQ_GOV_STOP) + policy->governor_enabled = true; + else if (event == CPUFREQ_GOV_START) + policy->governor_enabled = false; + mutex_unlock(&cpufreq_governor_lock); } /* we keep one module reference alive for @@ -1581,7 +1685,6 @@ static int __cpufreq_governor(struct cpufreq_policy *policy, return ret; } - int cpufreq_register_governor(struct cpufreq_governor *governor) { int err; @@ -1606,7 +1709,6 @@ int cpufreq_register_governor(struct cpufreq_governor *governor) } EXPORT_SYMBOL_GPL(cpufreq_register_governor); - void cpufreq_unregister_governor(struct cpufreq_governor *governor) { #ifdef CONFIG_HOTPLUG_CPU @@ -1636,7 +1738,6 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); - /********************************************************************* * POLICY INTERFACE * *********************************************************************/ @@ -1665,7 +1766,6 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) } EXPORT_SYMBOL(cpufreq_get_policy); - /* * data : current policy. * policy : policy to be set. @@ -1699,8 +1799,10 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_INCOMPATIBLE, policy); - /* verify the cpu speed can be set within this limit, - which might be different to the first one */ + /* + * verify the cpu speed can be set within this limit, which might be + * different to the first one + */ ret = cpufreq_driver->verify(policy); if (ret) goto error_out; @@ -1802,8 +1904,10 @@ int cpufreq_update_policy(unsigned int cpu) policy.policy = data->user_policy.policy; policy.governor = data->user_policy.governor; - /* BIOS might change freq behind our back - -> ask driver for current freq and notify governors about a change */ + /* + * BIOS might change freq behind our back + * -> ask driver for current freq and notify governors about a change + */ if (cpufreq_driver->get) { policy.cur = cpufreq_driver->get(cpu); if (!data->cur) { @@ -1852,7 +1956,7 @@ static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb, } static struct notifier_block __refdata cpufreq_cpu_notifier = { - .notifier_call = cpufreq_cpu_callback, + .notifier_call = cpufreq_cpu_callback, }; /********************************************************************* @@ -1864,7 +1968,7 @@ static struct notifier_block __refdata cpufreq_cpu_notifier = { * @driver_data: A struct cpufreq_driver containing the values# * submitted by the CPU Frequency driver. * - * Registers a CPU Frequency driver to this core code. This code + * Registers a CPU Frequency driver to this core code. This code * returns zero on success, -EBUSY when another driver got here first * (and isn't unregistered in the meantime). * @@ -1931,11 +2035,10 @@ err_null_driver: } EXPORT_SYMBOL_GPL(cpufreq_register_driver); - /** * cpufreq_unregister_driver - unregister the current CPUFreq driver * - * Unregister the current CPUFreq driver. Only call this if you have + * Unregister the current CPUFreq driver. Only call this if you have * the right to do so, i.e. if you have succeeded in initialising before! * Returns zero if successful, and -EINVAL if the cpufreq_driver is * currently not initialised. @@ -1972,7 +2075,7 @@ static int __init cpufreq_core_init(void) init_rwsem(&per_cpu(cpu_policy_rwsem, cpu)); } - cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj); + cpufreq_global_kobject = kobject_create(); BUG_ON(!cpufreq_global_kobject); register_syscore_ops(&cpufreq_syscore_ops); diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index dc9b72e25c1a..464587697561 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -23,21 +23,12 @@ #include <linux/kernel_stat.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/tick.h> #include <linux/types.h> #include <linux/workqueue.h> #include <linux/cpu.h> #include "cpufreq_governor.h" -static struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy) -{ - if (have_governor_per_policy()) - return &policy->kobj; - else - return cpufreq_global_kobject; -} - static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data) { if (have_governor_per_policy()) @@ -46,41 +37,6 @@ static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data) return dbs_data->cdata->attr_group_gov_sys; } -static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) -{ - u64 idle_time; - u64 cur_wall_time; - u64 busy_time; - - cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); - - busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; - - idle_time = cur_wall_time - busy_time; - if (wall) - *wall = cputime_to_usecs(cur_wall_time); - - return cputime_to_usecs(idle_time); -} - -u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy) -{ - u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL); - - if (idle_time == -1ULL) - return get_cpu_idle_time_jiffy(cpu, wall); - else if (!io_busy) - idle_time += get_cpu_iowait_time_us(cpu, wall); - - return idle_time; -} -EXPORT_SYMBOL_GPL(get_cpu_idle_time); - void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) { struct cpu_dbs_common_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu); @@ -278,6 +234,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, return rc; } + if (!have_governor_per_policy()) + WARN_ON(cpufreq_get_global_kobject()); + rc = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); if (rc) { @@ -316,6 +275,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, sysfs_remove_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); + if (!have_governor_per_policy()) + cpufreq_put_global_kobject(); + if ((dbs_data->cdata->governor == GOV_CONSERVATIVE) && (policy->governor->initialized == 1)) { struct cs_ops *cs_ops = dbs_data->cdata->gov_ops; @@ -404,6 +366,7 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, mutex_lock(&dbs_data->mutex); mutex_destroy(&cpu_cdbs->timer_mutex); + cpu_cdbs->cur_policy = NULL; mutex_unlock(&dbs_data->mutex); diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index e16a96130cb3..6663ec3b3056 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -81,7 +81,7 @@ static ssize_t show_##file_name##_gov_sys \ return sprintf(buf, "%u\n", tuners->file_name); \ } \ \ -static ssize_t show_##file_name##_gov_pol \ +static ssize_t show_##file_name##_gov_pol \ (struct cpufreq_policy *policy, char *buf) \ { \ struct dbs_data *dbs_data = policy->governor_data; \ @@ -91,7 +91,7 @@ static ssize_t show_##file_name##_gov_pol \ #define store_one(_gov, file_name) \ static ssize_t store_##file_name##_gov_sys \ -(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) \ +(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) \ { \ struct dbs_data *dbs_data = _gov##_dbs_cdata.gdbs_data; \ return store_##file_name(dbs_data, buf, count); \ @@ -256,7 +256,6 @@ static ssize_t show_sampling_rate_min_gov_pol \ return sprintf(buf, "%u\n", dbs_data->min_sampling_rate); \ } -u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); void dbs_check_cpu(struct dbs_data *dbs_data, int cpu); bool need_load_eval(struct cpu_dbs_common_info *cdbs, unsigned int sampling_rate); diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c index ceee06849b91..9fef7d6e4e6a 100644 --- a/drivers/cpufreq/cpufreq_performance.c +++ b/drivers/cpufreq/cpufreq_performance.c @@ -17,7 +17,6 @@ #include <linux/cpufreq.h> #include <linux/init.h> - static int cpufreq_governor_performance(struct cpufreq_policy *policy, unsigned int event) { @@ -44,19 +43,16 @@ struct cpufreq_governor cpufreq_gov_performance = { .owner = THIS_MODULE, }; - static int __init cpufreq_gov_performance_init(void) { return cpufreq_register_governor(&cpufreq_gov_performance); } - static void __exit cpufreq_gov_performance_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_performance); } - MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>"); MODULE_DESCRIPTION("CPUfreq policy governor 'performance'"); MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c index 2d948a171155..32109a14f5dc 100644 --- a/drivers/cpufreq/cpufreq_powersave.c +++ b/drivers/cpufreq/cpufreq_powersave.c @@ -1,7 +1,7 @@ /* - * linux/drivers/cpufreq/cpufreq_powersave.c + * linux/drivers/cpufreq/cpufreq_powersave.c * - * Copyright (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * * * This program is free software; you can redistribute it and/or modify @@ -48,13 +48,11 @@ static int __init cpufreq_gov_powersave_init(void) return cpufreq_register_governor(&cpufreq_gov_powersave); } - static void __exit cpufreq_gov_powersave_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_powersave); } - MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>"); MODULE_DESCRIPTION("CPUfreq policy governor 'powersave'"); MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index fb65decffa28..cd9e81713a71 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -27,7 +27,7 @@ static spinlock_t cpufreq_stats_lock; struct cpufreq_stats { unsigned int cpu; unsigned int total_trans; - unsigned long long last_time; + unsigned long long last_time; unsigned int max_state; unsigned int state_num; unsigned int last_index; @@ -116,7 +116,7 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ", stat->freq_table[i]); - for (j = 0; j < stat->state_num; j++) { + for (j = 0; j < stat->state_num; j++) { if (len >= PAGE_SIZE) break; len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", @@ -349,6 +349,7 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: cpufreq_update_policy(cpu); break; case CPU_DOWN_PREPARE: diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index bbeb9c0720a6..03078090b5f7 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -13,55 +13,13 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/smp.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> #include <linux/cpufreq.h> -#include <linux/cpu.h> -#include <linux/types.h> -#include <linux/fs.h> -#include <linux/sysfs.h> +#include <linux/init.h> +#include <linux/module.h> #include <linux/mutex.h> -/** - * A few values needed by the userspace governor - */ -static DEFINE_PER_CPU(unsigned int, cpu_max_freq); -static DEFINE_PER_CPU(unsigned int, cpu_min_freq); -static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */ -static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by - userspace */ static DEFINE_PER_CPU(unsigned int, cpu_is_managed); - static DEFINE_MUTEX(userspace_mutex); -static int cpus_using_userspace_governor; - -/* keep track of frequency transitions */ -static int -userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, - void *data) -{ - struct cpufreq_freqs *freq = data; - - if (!per_cpu(cpu_is_managed, freq->cpu)) - return 0; - - if (val == CPUFREQ_POSTCHANGE) { - pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n", - freq->cpu, freq->new); - per_cpu(cpu_cur_freq, freq->cpu) = freq->new; - } - - return 0; -} - -static struct notifier_block userspace_cpufreq_notifier_block = { - .notifier_call = userspace_cpufreq_notifier -}; - /** * cpufreq_set - set the CPU frequency @@ -80,13 +38,6 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) if (!per_cpu(cpu_is_managed, policy->cpu)) goto err; - per_cpu(cpu_set_freq, policy->cpu) = freq; - - if (freq < per_cpu(cpu_min_freq, policy->cpu)) - freq = per_cpu(cpu_min_freq, policy->cpu); - if (freq > per_cpu(cpu_max_freq, policy->cpu)) - freq = per_cpu(cpu_max_freq, policy->cpu); - /* * We're safe from concurrent calls to ->target() here * as we hold the userspace_mutex lock. If we were calling @@ -104,10 +55,9 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) return ret; } - static ssize_t show_speed(struct cpufreq_policy *policy, char *buf) { - return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu)); + return sprintf(buf, "%u\n", policy->cur); } static int cpufreq_governor_userspace(struct cpufreq_policy *policy, @@ -119,73 +69,37 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, switch (event) { case CPUFREQ_GOV_START: BUG_ON(!policy->cur); - mutex_lock(&userspace_mutex); - - if (cpus_using_userspace_governor == 0) { - cpufreq_register_notifier( - &userspace_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - } - cpus_using_userspace_governor++; + pr_debug("started managing cpu %u\n", cpu); + mutex_lock(&userspace_mutex); per_cpu(cpu_is_managed, cpu) = 1; - per_cpu(cpu_min_freq, cpu) = policy->min; - per_cpu(cpu_max_freq, cpu) = policy->max; - per_cpu(cpu_cur_freq, cpu) = policy->cur; - per_cpu(cpu_set_freq, cpu) = policy->cur; - pr_debug("managing cpu %u started " - "(%u - %u kHz, currently %u kHz)\n", - cpu, - per_cpu(cpu_min_freq, cpu), - per_cpu(cpu_max_freq, cpu), - per_cpu(cpu_cur_freq, cpu)); - mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_STOP: - mutex_lock(&userspace_mutex); - cpus_using_userspace_governor--; - if (cpus_using_userspace_governor == 0) { - cpufreq_unregister_notifier( - &userspace_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - } + pr_debug("managing cpu %u stopped\n", cpu); + mutex_lock(&userspace_mutex); per_cpu(cpu_is_managed, cpu) = 0; - per_cpu(cpu_min_freq, cpu) = 0; - per_cpu(cpu_max_freq, cpu) = 0; - per_cpu(cpu_set_freq, cpu) = 0; - pr_debug("managing cpu %u stopped\n", cpu); mutex_unlock(&userspace_mutex); break; case CPUFREQ_GOV_LIMITS: mutex_lock(&userspace_mutex); - pr_debug("limit event for cpu %u: %u - %u kHz, " - "currently %u kHz, last set to %u kHz\n", + pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz\n", cpu, policy->min, policy->max, - per_cpu(cpu_cur_freq, cpu), - per_cpu(cpu_set_freq, cpu)); - if (policy->max < per_cpu(cpu_set_freq, cpu)) { + policy->cur); + + if (policy->max < policy->cur) __cpufreq_driver_target(policy, policy->max, CPUFREQ_RELATION_H); - } else if (policy->min > per_cpu(cpu_set_freq, cpu)) { + else if (policy->min > policy->cur) __cpufreq_driver_target(policy, policy->min, CPUFREQ_RELATION_L); - } else { - __cpufreq_driver_target(policy, - per_cpu(cpu_set_freq, cpu), - CPUFREQ_RELATION_L); - } - per_cpu(cpu_min_freq, cpu) = policy->min; - per_cpu(cpu_max_freq, cpu) = policy->max; - per_cpu(cpu_cur_freq, cpu) = policy->cur; mutex_unlock(&userspace_mutex); break; } return rc; } - #ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE static #endif @@ -202,13 +116,11 @@ static int __init cpufreq_gov_userspace_init(void) return cpufreq_register_governor(&cpufreq_gov_userspace); } - static void __exit cpufreq_gov_userspace_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_userspace); } - MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, " "Russell King <rmk@arm.linux.org.uk>"); MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'"); diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c index c33c76c360fa..551dd655c6f2 100644 --- a/drivers/cpufreq/davinci-cpufreq.c +++ b/drivers/cpufreq/davinci-cpufreq.c @@ -114,6 +114,9 @@ static int davinci_target(struct cpufreq_policy *policy, pdata->set_voltage(idx); out: + if (ret) + freqs.new = freqs.old; + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); return ret; diff --git a/drivers/cpufreq/dbx500-cpufreq.c b/drivers/cpufreq/dbx500-cpufreq.c index 6ec6539ae041..1fdb02b9f1ec 100644 --- a/drivers/cpufreq/dbx500-cpufreq.c +++ b/drivers/cpufreq/dbx500-cpufreq.c @@ -57,13 +57,13 @@ static int dbx500_cpufreq_target(struct cpufreq_policy *policy, if (ret) { pr_err("dbx500-cpufreq: Failed to set armss_clk to %d Hz: error %d\n", freqs.new * 1000, ret); - return ret; + freqs.new = freqs.old; } /* post change notification */ cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - return 0; + return ret; } static unsigned int dbx500_cpufreq_getspeed(unsigned int cpu) diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index 37380fb92621..a60efaeb4cf8 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -161,6 +161,9 @@ postchange: current_multiplier); } #endif + if (err) + freqs.new = freqs.old; + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); return err; } @@ -188,7 +191,7 @@ static int eps_target(struct cpufreq_policy *policy, } /* Make frequency transition */ - dest_state = centaur->freq_table[newstate].index & 0xffff; + dest_state = centaur->freq_table[newstate].driver_data & 0xffff; ret = eps_set_state(centaur, policy, dest_state); if (ret) printk(KERN_ERR "eps: Timeout!\n"); @@ -380,9 +383,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy) f_table = ¢aur->freq_table[0]; if (brand != EPS_BRAND_C7M) { f_table[0].frequency = fsb * min_multiplier; - f_table[0].index = (min_multiplier << 8) | min_voltage; + f_table[0].driver_data = (min_multiplier << 8) | min_voltage; f_table[1].frequency = fsb * max_multiplier; - f_table[1].index = (max_multiplier << 8) | max_voltage; + f_table[1].driver_data = (max_multiplier << 8) | max_voltage; f_table[2].frequency = CPUFREQ_TABLE_END; } else { k = 0; @@ -391,7 +394,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) for (i = min_multiplier; i <= max_multiplier; i++) { voltage = (k * step) / 256 + min_voltage; f_table[k].frequency = fsb * i; - f_table[k].index = (i << 8) | voltage; + f_table[k].driver_data = (i << 8) | voltage; k++; } f_table[k].frequency = CPUFREQ_TABLE_END; diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 475b4f607f0d..0d32f02ef4d6 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -113,7 +113,8 @@ static int exynos_cpufreq_scale(unsigned int target_freq) if (ret) { pr_err("%s: failed to set cpu voltage to %d\n", __func__, arm_volt); - goto out; + freqs.new = freqs.old; + goto post_notify; } } @@ -123,14 +124,19 @@ static int exynos_cpufreq_scale(unsigned int target_freq) if (ret) { pr_err("%s: failed to set cpu voltage to %d\n", __func__, safe_arm_volt); - goto out; + freqs.new = freqs.old; + goto post_notify; } } exynos_info->set_freq(old_index, index); +post_notify: cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + if (ret) + goto out; + /* When the new frequency is lower than current frequency */ if ((freqs.new < freqs.old) || ((freqs.new > freqs.old) && safe_arm_volt)) { diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index d7a79662e24c..f0d87412cc91 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -34,8 +34,8 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, continue; } - pr_debug("table entry %u: %u kHz, %u index\n", - i, freq, table[i].index); + pr_debug("table entry %u: %u kHz, %u driver_data\n", + i, freq, table[i].driver_data); if (freq < min_freq) min_freq = freq; if (freq > max_freq) @@ -97,11 +97,11 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, unsigned int *index) { struct cpufreq_frequency_table optimal = { - .index = ~0, + .driver_data = ~0, .frequency = 0, }; struct cpufreq_frequency_table suboptimal = { - .index = ~0, + .driver_data = ~0, .frequency = 0, }; unsigned int i; @@ -129,12 +129,12 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, if (freq <= target_freq) { if (freq >= optimal.frequency) { optimal.frequency = freq; - optimal.index = i; + optimal.driver_data = i; } } else { if (freq <= suboptimal.frequency) { suboptimal.frequency = freq; - suboptimal.index = i; + suboptimal.driver_data = i; } } break; @@ -142,26 +142,26 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, if (freq >= target_freq) { if (freq <= optimal.frequency) { optimal.frequency = freq; - optimal.index = i; + optimal.driver_data = i; } } else { if (freq >= suboptimal.frequency) { suboptimal.frequency = freq; - suboptimal.index = i; + suboptimal.driver_data = i; } } break; } } - if (optimal.index > i) { - if (suboptimal.index > i) + if (optimal.driver_data > i) { + if (suboptimal.driver_data > i) return -EINVAL; - *index = suboptimal.index; + *index = suboptimal.driver_data; } else - *index = optimal.index; + *index = optimal.driver_data; pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency, - table[*index].index); + table[*index].driver_data); return 0; } diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index c0075dbaa633..573c14ea802d 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -326,7 +326,7 @@ acpi_cpufreq_cpu_init ( /* table init */ for (i = 0; i <= data->acpi_data.state_count; i++) { - data->freq_table[i].index = i; + data->freq_table[i].driver_data = i; if (i < data->acpi_data.state_count) { data->freq_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000; diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index b78bc35973ba..e37cdaedbb5b 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -68,8 +68,6 @@ static int imx6q_set_target(struct cpufreq_policy *policy, if (freqs.old == freqs.new) return 0; - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - rcu_read_lock(); opp = opp_find_freq_ceil(cpu_dev, &freq_hz); if (IS_ERR(opp)) { @@ -86,13 +84,16 @@ static int imx6q_set_target(struct cpufreq_policy *policy, freqs.old / 1000, volt_old / 1000, freqs.new / 1000, volt / 1000); + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + /* scaling up? scale voltage before frequency */ if (freqs.new > freqs.old) { ret = regulator_set_voltage_tol(arm_reg, volt, 0); if (ret) { dev_err(cpu_dev, "failed to scale vddarm up: %d\n", ret); - return ret; + freqs.new = freqs.old; + goto post_notify; } /* @@ -145,15 +146,18 @@ static int imx6q_set_target(struct cpufreq_policy *policy, if (ret) { dev_err(cpu_dev, "failed to set clock rate: %d\n", ret); regulator_set_voltage_tol(arm_reg, volt_old, 0); - return ret; + freqs.new = freqs.old; + goto post_notify; } /* scaling down? scale voltage after frequency */ if (freqs.new < freqs.old) { ret = regulator_set_voltage_tol(arm_reg, volt, 0); - if (ret) + if (ret) { dev_warn(cpu_dev, "failed to scale vddarm down: %d\n", ret); + ret = 0; + } if (freqs.old == FREQ_1P2_GHZ / 1000) { regulator_set_voltage_tol(pu_reg, @@ -163,9 +167,10 @@ static int imx6q_set_target(struct cpufreq_policy *policy, } } +post_notify: cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - return 0; + return ret; } static int imx6q_cpufreq_init(struct cpufreq_policy *policy) diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index b2644af985ec..c233ea617366 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -59,7 +59,7 @@ static void kirkwood_cpufreq_set_cpu_state(struct cpufreq_policy *policy, unsigned int index) { struct cpufreq_freqs freqs; - unsigned int state = kirkwood_freq_table[index].index; + unsigned int state = kirkwood_freq_table[index].driver_data; unsigned long reg; freqs.old = kirkwood_cpufreq_get_cpu_frequency(0); diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index b448638e34de..b6a0a7a406b0 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -254,7 +254,7 @@ static void longhaul_setstate(struct cpufreq_policy *policy, u32 bm_timeout = 1000; unsigned int dir = 0; - mults_index = longhaul_table[table_index].index; + mults_index = longhaul_table[table_index].driver_data; /* Safety precautions */ mult = mults[mults_index & 0x1f]; if (mult == -1) @@ -487,7 +487,7 @@ static int __cpuinit longhaul_get_ranges(void) if (ratio > maxmult || ratio < minmult) continue; longhaul_table[k].frequency = calc_speed(ratio); - longhaul_table[k].index = j; + longhaul_table[k].driver_data = j; k++; } if (k <= 1) { @@ -508,8 +508,8 @@ static int __cpuinit longhaul_get_ranges(void) if (min_i != j) { swap(longhaul_table[j].frequency, longhaul_table[min_i].frequency); - swap(longhaul_table[j].index, - longhaul_table[min_i].index); + swap(longhaul_table[j].driver_data, + longhaul_table[min_i].driver_data); } } @@ -517,7 +517,7 @@ static int __cpuinit longhaul_get_ranges(void) /* Find index we are running on */ for (j = 0; j < k; j++) { - if (mults[longhaul_table[j].index & 0x1f] == mult) { + if (mults[longhaul_table[j].driver_data & 0x1f] == mult) { longhaul_index = j; break; } @@ -613,7 +613,7 @@ static void __cpuinit longhaul_setup_voltagescaling(void) pos = (speed - min_vid_speed) / kHz_step + minvid.pos; else pos = minvid.pos; - longhaul_table[j].index |= mV_vrm_table[pos] << 8; + longhaul_table[j].driver_data |= mV_vrm_table[pos] << 8; vid = vrm_mV_table[mV_vrm_table[pos]]; printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n", speed, j, vid.mV); @@ -656,12 +656,12 @@ static int longhaul_target(struct cpufreq_policy *policy, * this in hardware, C3 is old and we need to do this * in software. */ i = longhaul_index; - current_vid = (longhaul_table[longhaul_index].index >> 8); + current_vid = (longhaul_table[longhaul_index].driver_data >> 8); current_vid &= 0x1f; if (table_index > longhaul_index) dir = 1; while (i != table_index) { - vid = (longhaul_table[i].index >> 8) & 0x1f; + vid = (longhaul_table[i].driver_data >> 8) & 0x1f; if (vid != current_vid) { longhaul_setstate(policy, i); current_vid = vid; diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index d53912768946..bb838b985077 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -72,7 +72,7 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy, freq = ((cpu_clock_freq / 1000) * - loongson2_clockmod_table[newstate].index) / 8; + loongson2_clockmod_table[newstate].driver_data) / 8; if (freq < policy->min || freq > policy->max) return -EINVAL; diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 0279d18a57f9..29468a522ee9 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -93,9 +93,6 @@ static int omap_target(struct cpufreq_policy *policy, if (freqs.old == freqs.new && policy->cur == freqs.new) return ret; - /* notifiers */ - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - freq = freqs.new * 1000; ret = clk_round_rate(mpu_clk, freq); if (IS_ERR_VALUE(ret)) { @@ -125,6 +122,9 @@ static int omap_target(struct cpufreq_policy *policy, freqs.old / 1000, volt_old ? volt_old / 1000 : -1, freqs.new / 1000, volt ? volt / 1000 : -1); + /* notifiers */ + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + /* scaling up? scale voltage before frequency */ if (mpu_reg && (freqs.new > freqs.old)) { r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol); diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index 421ef37d0bb3..9ee78170ff86 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -118,7 +118,7 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy, return -EINVAL; freqs.old = cpufreq_p4_get(policy->cpu); - freqs.new = stock_freq * p4clockmod_table[newstate].index / 8; + freqs.new = stock_freq * p4clockmod_table[newstate].driver_data / 8; if (freqs.new == freqs.old) return 0; @@ -131,7 +131,7 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy, * Developer's Manual, Volume 3 */ for_each_cpu(i, policy->cpus) - cpufreq_p4_setdc(i, p4clockmod_table[newstate].index); + cpufreq_p4_setdc(i, p4clockmod_table[newstate].driver_data); /* notifiers */ cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); diff --git a/arch/powerpc/platforms/pasemi/cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c index be1e7958909e..b704da404067 100644 --- a/arch/powerpc/platforms/pasemi/cpufreq.c +++ b/drivers/cpufreq/pasemi-cpufreq.c @@ -204,7 +204,8 @@ static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) /* initialize frequency table */ for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { - pas_freqs[i].frequency = get_astate_freq(pas_freqs[i].index) * 100000; + pas_freqs[i].frequency = + get_astate_freq(pas_freqs[i].driver_data) * 100000; pr_debug("%d: %d\n", i, pas_freqs[i].frequency); } @@ -280,7 +281,7 @@ static int pas_cpufreq_target(struct cpufreq_policy *policy, pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", policy->cpu, pas_freqs[pas_astate_new].frequency, - pas_freqs[pas_astate_new].index); + pas_freqs[pas_astate_new].driver_data); current_astate = pas_astate_new; diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index 0de00081a81e..1581fcc4cf4a 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -243,6 +243,8 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, return 0; cmd_incomplete: + freqs.new = freqs.old; + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); iowrite16(0, &pcch_hdr->status); spin_unlock(&pcc_lock); return -EINVAL; diff --git a/arch/powerpc/platforms/powermac/cpufreq_32.c b/drivers/cpufreq/pmac32-cpufreq.c index 3104fad82480..3104fad82480 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_32.c +++ b/drivers/cpufreq/pmac32-cpufreq.c diff --git a/arch/powerpc/platforms/powermac/cpufreq_64.c b/drivers/cpufreq/pmac64-cpufreq.c index 7ba423431cfe..7ba423431cfe 100644 --- a/arch/powerpc/platforms/powermac/cpufreq_64.c +++ b/drivers/cpufreq/pmac64-cpufreq.c diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index ea0222a45b7b..ea8e10382ec5 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -58,7 +58,7 @@ static int powernow_k6_get_cpu_multiplier(void) msrval = POWERNOW_IOPORT + 0x0; wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */ - return clock_ratio[(invalue >> 5)&7].index; + return clock_ratio[(invalue >> 5)&7].driver_data; } @@ -75,13 +75,13 @@ static void powernow_k6_set_state(struct cpufreq_policy *policy, unsigned long msrval; struct cpufreq_freqs freqs; - if (clock_ratio[best_i].index > max_multiplier) { + if (clock_ratio[best_i].driver_data > max_multiplier) { printk(KERN_ERR PFX "invalid target frequency\n"); return; } freqs.old = busfreq * powernow_k6_get_cpu_multiplier(); - freqs.new = busfreq * clock_ratio[best_i].index; + freqs.new = busfreq * clock_ratio[best_i].driver_data; cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); @@ -156,7 +156,7 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy) /* table init */ for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) { - f = clock_ratio[i].index; + f = clock_ratio[i].driver_data; if (f > max_multiplier) clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID; else diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index 53888dacbe58..b9f80b713fda 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -186,7 +186,7 @@ static int get_ranges(unsigned char *pst) fid = *pst++; powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10; - powernow_table[j].index = fid; /* lower 8 bits */ + powernow_table[j].driver_data = fid; /* lower 8 bits */ speed = powernow_table[j].frequency; @@ -203,7 +203,7 @@ static int get_ranges(unsigned char *pst) maximum_speed = speed; vid = *pst++; - powernow_table[j].index |= (vid << 8); /* upper 8 bits */ + powernow_table[j].driver_data |= (vid << 8); /* upper 8 bits */ pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) " "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10, @@ -212,7 +212,7 @@ static int get_ranges(unsigned char *pst) mobile_vid_table[vid]%1000); } powernow_table[number_scales].frequency = CPUFREQ_TABLE_END; - powernow_table[number_scales].index = 0; + powernow_table[number_scales].driver_data = 0; return 0; } @@ -260,8 +260,8 @@ static void change_speed(struct cpufreq_policy *policy, unsigned int index) * vid are the upper 8 bits. */ - fid = powernow_table[index].index & 0xFF; - vid = (powernow_table[index].index & 0xFF00) >> 8; + fid = powernow_table[index].driver_data & 0xFF; + vid = (powernow_table[index].driver_data & 0xFF00) >> 8; rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val); cfid = fidvidstatus.bits.CFID; @@ -373,8 +373,8 @@ static int powernow_acpi_init(void) fid = pc.bits.fid; powernow_table[i].frequency = fsb * fid_codes[fid] / 10; - powernow_table[i].index = fid; /* lower 8 bits */ - powernow_table[i].index |= (vid << 8); /* upper 8 bits */ + powernow_table[i].driver_data = fid; /* lower 8 bits */ + powernow_table[i].driver_data |= (vid << 8); /* upper 8 bits */ speed = powernow_table[i].frequency; speed_mhz = speed / 1000; @@ -417,7 +417,7 @@ static int powernow_acpi_init(void) } powernow_table[i].frequency = CPUFREQ_TABLE_END; - powernow_table[i].index = 0; + powernow_table[i].driver_data = 0; /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index b828efe4b2f8..78f018f2a5de 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -584,9 +584,9 @@ static void print_basics(struct powernow_k8_data *data) CPUFREQ_ENTRY_INVALID) { printk(KERN_INFO PFX "fid 0x%x (%d MHz), vid 0x%x\n", - data->powernow_table[j].index & 0xff, + data->powernow_table[j].driver_data & 0xff, data->powernow_table[j].frequency/1000, - data->powernow_table[j].index >> 8); + data->powernow_table[j].driver_data >> 8); } } if (data->batps) @@ -632,13 +632,13 @@ static int fill_powernow_table(struct powernow_k8_data *data, for (j = 0; j < data->numps; j++) { int freq; - powernow_table[j].index = pst[j].fid; /* lower 8 bits */ - powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */ + powernow_table[j].driver_data = pst[j].fid; /* lower 8 bits */ + powernow_table[j].driver_data |= (pst[j].vid << 8); /* upper 8 bits */ freq = find_khz_freq_from_fid(pst[j].fid); powernow_table[j].frequency = freq; } powernow_table[data->numps].frequency = CPUFREQ_TABLE_END; - powernow_table[data->numps].index = 0; + powernow_table[data->numps].driver_data = 0; if (query_current_values_with_pending_wait(data)) { kfree(powernow_table); @@ -810,7 +810,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END; - powernow_table[data->acpi_data.state_count].index = 0; + powernow_table[data->acpi_data.state_count].driver_data = 0; data->powernow_table = powernow_table; if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu) @@ -865,7 +865,7 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data, pr_debug(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid); index = fid | (vid<<8); - powernow_table[i].index = index; + powernow_table[i].driver_data = index; freq = find_khz_freq_from_fid(fid); powernow_table[i].frequency = freq; @@ -941,8 +941,8 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, * the cpufreq frequency table in find_psb_table, vid * are the upper 8 bits. */ - fid = data->powernow_table[index].index & 0xFF; - vid = (data->powernow_table[index].index & 0xFF00) >> 8; + fid = data->powernow_table[index].driver_data & 0xFF; + vid = (data->powernow_table[index].driver_data & 0xFF00) >> 8; pr_debug("table matched fid 0x%x, giving vid 0x%x\n", fid, vid); @@ -967,9 +967,9 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, res = transition_fid_vid(data, fid, vid); if (res) - return res; - - freqs.new = find_khz_freq_from_fid(data->currfid); + freqs.new = freqs.old; + else + freqs.new = find_khz_freq_from_fid(data->currfid); cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); return res; diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c new file mode 100644 index 000000000000..3cae4529f959 --- /dev/null +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c @@ -0,0 +1,380 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs. + * + * 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) KBUILD_MODNAME ": " fmt + +#include <linux/clk.h> +#include <linux/cpufreq.h> +#include <linux/errno.h> +#include <sysdev/fsl_soc.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/smp.h> + +/** + * struct cpu_data - per CPU data struct + * @clk: the clk of CPU + * @parent: the parent node of cpu clock + * @table: frequency table + */ +struct cpu_data { + struct clk *clk; + struct device_node *parent; + struct cpufreq_frequency_table *table; +}; + +/** + * struct soc_data - SoC specific data + * @freq_mask: mask the disallowed frequencies + * @flag: unique flags + */ +struct soc_data { + u32 freq_mask[4]; + u32 flag; +}; + +#define FREQ_MASK 1 +/* see hardware specification for the allowed frqeuencies */ +static const struct soc_data sdata[] = { + { /* used by p2041 and p3041 */ + .freq_mask = {0x8, 0x8, 0x2, 0x2}, + .flag = FREQ_MASK, + }, + { /* used by p5020 */ + .freq_mask = {0x8, 0x2}, + .flag = FREQ_MASK, + }, + { /* used by p4080, p5040 */ + .freq_mask = {0}, + .flag = 0, + }, +}; + +/* + * the minimum allowed core frequency, in Hz + * for chassis v1.0, >= platform frequency + * for chassis v2.0, >= platform frequency / 2 + */ +static u32 min_cpufreq; +static const u32 *fmask; + +/* serialize frequency changes */ +static DEFINE_MUTEX(cpufreq_lock); +static DEFINE_PER_CPU(struct cpu_data *, cpu_data); + +/* cpumask in a cluster */ +static DEFINE_PER_CPU(cpumask_var_t, cpu_mask); + +#ifndef CONFIG_SMP +static inline const struct cpumask *cpu_core_mask(int cpu) +{ + return cpumask_of(0); +} +#endif + +static unsigned int corenet_cpufreq_get_speed(unsigned int cpu) +{ + struct cpu_data *data = per_cpu(cpu_data, cpu); + + return clk_get_rate(data->clk) / 1000; +} + +/* reduce the duplicated frequencies in frequency table */ +static void freq_table_redup(struct cpufreq_frequency_table *freq_table, + int count) +{ + int i, j; + + for (i = 1; i < count; i++) { + for (j = 0; j < i; j++) { + if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID || + freq_table[j].frequency != + freq_table[i].frequency) + continue; + + freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; + break; + } + } +} + +/* sort the frequencies in frequency table in descenting order */ +static void freq_table_sort(struct cpufreq_frequency_table *freq_table, + int count) +{ + int i, j, ind; + unsigned int freq, max_freq; + struct cpufreq_frequency_table table; + for (i = 0; i < count - 1; i++) { + max_freq = freq_table[i].frequency; + ind = i; + for (j = i + 1; j < count; j++) { + freq = freq_table[j].frequency; + if (freq == CPUFREQ_ENTRY_INVALID || + freq <= max_freq) + continue; + ind = j; + max_freq = freq; + } + + if (ind != i) { + /* exchange the frequencies */ + table.driver_data = freq_table[i].driver_data; + table.frequency = freq_table[i].frequency; + freq_table[i].driver_data = freq_table[ind].driver_data; + freq_table[i].frequency = freq_table[ind].frequency; + freq_table[ind].driver_data = table.driver_data; + freq_table[ind].frequency = table.frequency; + } + } +} + +static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + struct device_node *np; + int i, count, ret; + u32 freq, mask; + struct clk *clk; + struct cpufreq_frequency_table *table; + struct cpu_data *data; + unsigned int cpu = policy->cpu; + + np = of_get_cpu_node(cpu, NULL); + if (!np) + return -ENODEV; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + pr_err("%s: no memory\n", __func__); + goto err_np; + } + + data->clk = of_clk_get(np, 0); + if (IS_ERR(data->clk)) { + pr_err("%s: no clock information\n", __func__); + goto err_nomem2; + } + + data->parent = of_parse_phandle(np, "clocks", 0); + if (!data->parent) { + pr_err("%s: could not get clock information\n", __func__); + goto err_nomem2; + } + + count = of_property_count_strings(data->parent, "clock-names"); + table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL); + if (!table) { + pr_err("%s: no memory\n", __func__); + goto err_node; + } + + if (fmask) + mask = fmask[get_hard_smp_processor_id(cpu)]; + else + mask = 0x0; + + for (i = 0; i < count; i++) { + clk = of_clk_get(data->parent, i); + freq = clk_get_rate(clk); + /* + * the clock is valid if its frequency is not masked + * and large than minimum allowed frequency. + */ + if (freq < min_cpufreq || (mask & (1 << i))) + table[i].frequency = CPUFREQ_ENTRY_INVALID; + else + table[i].frequency = freq / 1000; + table[i].driver_data = i; + } + freq_table_redup(table, count); + freq_table_sort(table, count); + table[i].frequency = CPUFREQ_TABLE_END; + + /* set the min and max frequency properly */ + ret = cpufreq_frequency_table_cpuinfo(policy, table); + if (ret) { + pr_err("invalid frequency table: %d\n", ret); + goto err_nomem1; + } + + data->table = table; + per_cpu(cpu_data, cpu) = data; + + /* update ->cpus if we have cluster, no harm if not */ + cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu)); + for_each_cpu(i, per_cpu(cpu_mask, cpu)) + per_cpu(cpu_data, i) = data; + + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + policy->cur = corenet_cpufreq_get_speed(policy->cpu); + + cpufreq_frequency_table_get_attr(table, cpu); + of_node_put(np); + + return 0; + +err_nomem1: + kfree(table); +err_node: + of_node_put(data->parent); +err_nomem2: + per_cpu(cpu_data, cpu) = NULL; + kfree(data); +err_np: + of_node_put(np); + + return -ENODEV; +} + +static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + struct cpu_data *data = per_cpu(cpu_data, policy->cpu); + unsigned int cpu; + + cpufreq_frequency_table_put_attr(policy->cpu); + of_node_put(data->parent); + kfree(data->table); + kfree(data); + + for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu)) + per_cpu(cpu_data, cpu) = NULL; + + return 0; +} + +static int corenet_cpufreq_verify(struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *table = + per_cpu(cpu_data, policy->cpu)->table; + + return cpufreq_frequency_table_verify(policy, table); +} + +static int corenet_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + struct cpufreq_freqs freqs; + unsigned int new; + struct clk *parent; + int ret; + struct cpu_data *data = per_cpu(cpu_data, policy->cpu); + + cpufreq_frequency_table_target(policy, data->table, + target_freq, relation, &new); + + if (policy->cur == data->table[new].frequency) + return 0; + + freqs.old = policy->cur; + freqs.new = data->table[new].frequency; + + mutex_lock(&cpufreq_lock); + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + + parent = of_clk_get(data->parent, data->table[new].driver_data); + ret = clk_set_parent(data->clk, parent); + if (ret) + freqs.new = freqs.old; + + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + mutex_unlock(&cpufreq_lock); + + return ret; +} + +static struct freq_attr *corenet_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver ppc_corenet_cpufreq_driver = { + .name = "ppc_cpufreq", + .owner = THIS_MODULE, + .flags = CPUFREQ_CONST_LOOPS, + .init = corenet_cpufreq_cpu_init, + .exit = __exit_p(corenet_cpufreq_cpu_exit), + .verify = corenet_cpufreq_verify, + .target = corenet_cpufreq_target, + .get = corenet_cpufreq_get_speed, + .attr = corenet_cpufreq_attr, +}; + +static const struct of_device_id node_matches[] __initdata = { + { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], }, + { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], }, + { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], }, + { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], }, + { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], }, + { .compatible = "fsl,qoriq-clockgen-2.0", }, + {} +}; + +static int __init ppc_corenet_cpufreq_init(void) +{ + int ret; + struct device_node *np; + const struct of_device_id *match; + const struct soc_data *data; + unsigned int cpu; + + np = of_find_matching_node(NULL, node_matches); + if (!np) + return -ENODEV; + + for_each_possible_cpu(cpu) { + if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL)) + goto err_mask; + cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu)); + } + + match = of_match_node(node_matches, np); + data = match->data; + if (data) { + if (data->flag) + fmask = data->freq_mask; + min_cpufreq = fsl_get_sys_freq(); + } else { + min_cpufreq = fsl_get_sys_freq() / 2; + } + + of_node_put(np); + + ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver); + if (!ret) + pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n"); + + return ret; + +err_mask: + for_each_possible_cpu(cpu) + free_cpumask_var(per_cpu(cpu_mask, cpu)); + + return -ENOMEM; +} +module_init(ppc_corenet_cpufreq_init); + +static void __exit ppc_corenet_cpufreq_exit(void) +{ + unsigned int cpu; + + for_each_possible_cpu(cpu) + free_cpumask_var(per_cpu(cpu_mask, cpu)); + + cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver); +} +module_exit(ppc_corenet_cpufreq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>"); +MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs"); diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c index e577a1dbbfcd..5936f8d6f2cc 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq.c @@ -106,7 +106,7 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy) /* initialize frequency table */ for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { - cbe_freqs[i].frequency = max_freq / cbe_freqs[i].index; + cbe_freqs[i].frequency = max_freq / cbe_freqs[i].driver_data; pr_debug("%d: %d\n", i, cbe_freqs[i].frequency); } @@ -165,7 +165,7 @@ static int cbe_cpufreq_target(struct cpufreq_policy *policy, "1/%d of max frequency\n", policy->cpu, cbe_freqs[cbe_pmode_new].frequency, - cbe_freqs[cbe_pmode_new].index); + cbe_freqs[cbe_pmode_new].driver_data); rc = set_pmode(policy->cpu, cbe_pmode_new); diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index 9e5bc8e388a0..fb3981ac829f 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -420,7 +420,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) /* Generate pxa25x the run cpufreq_frequency_table struct */ for (i = 0; i < NUM_PXA25x_RUN_FREQS; i++) { pxa255_run_freq_table[i].frequency = pxa255_run_freqs[i].khz; - pxa255_run_freq_table[i].index = i; + pxa255_run_freq_table[i].driver_data = i; } pxa255_run_freq_table[i].frequency = CPUFREQ_TABLE_END; @@ -428,7 +428,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) for (i = 0; i < NUM_PXA25x_TURBO_FREQS; i++) { pxa255_turbo_freq_table[i].frequency = pxa255_turbo_freqs[i].khz; - pxa255_turbo_freq_table[i].index = i; + pxa255_turbo_freq_table[i].driver_data = i; } pxa255_turbo_freq_table[i].frequency = CPUFREQ_TABLE_END; @@ -440,9 +440,9 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) if (freq > pxa27x_maxfreq) break; pxa27x_freq_table[i].frequency = freq; - pxa27x_freq_table[i].index = i; + pxa27x_freq_table[i].driver_data = i; } - pxa27x_freq_table[i].index = i; + pxa27x_freq_table[i].driver_data = i; pxa27x_freq_table[i].frequency = CPUFREQ_TABLE_END; /* diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index 15d60f857ad5..9c92ef032a9e 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -98,10 +98,10 @@ static int setup_freqs_table(struct cpufreq_policy *policy, return -ENOMEM; for (i = 0; i < num; i++) { - table[i].index = i; + table[i].driver_data = i; table[i].frequency = freqs[i].cpufreq_mhz * 1000; } - table[num].index = i; + table[num].driver_data = i; table[num].frequency = CPUFREQ_TABLE_END; pxa3xx_freqs = freqs; diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c index 4f1881eee3f1..e35865af96e2 100644 --- a/drivers/cpufreq/s3c2416-cpufreq.c +++ b/drivers/cpufreq/s3c2416-cpufreq.c @@ -244,7 +244,7 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy, if (ret != 0) goto out; - idx = s3c_freq->freq_table[i].index; + idx = s3c_freq->freq_table[i].driver_data; if (idx == SOURCE_HCLK) to_dvs = 1; @@ -312,7 +312,7 @@ static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq) if (freq->frequency == CPUFREQ_ENTRY_INVALID) continue; - dvfs = &s3c2416_dvfs_table[freq->index]; + dvfs = &s3c2416_dvfs_table[freq->driver_data]; found = 0; /* Check only the min-voltage, more is always ok on S3C2416 */ @@ -462,7 +462,7 @@ static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) freq = s3c_freq->freq_table; while (freq->frequency != CPUFREQ_TABLE_END) { /* special handling for dvs mode */ - if (freq->index == 0) { + if (freq->driver_data == 0) { if (!s3c_freq->hclk) { pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n", freq->frequency); diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 3c0e78ede0da..3513e7477160 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -70,7 +70,7 @@ static void s3c_cpufreq_getcur(struct s3c_cpufreq_config *cfg) cfg->freq.pclk = pclk = clk_get_rate(clk_pclk); cfg->freq.armclk = armclk = clk_get_rate(clk_arm); - cfg->pll.index = __raw_readl(S3C2410_MPLLCON); + cfg->pll.driver_data = __raw_readl(S3C2410_MPLLCON); cfg->pll.frequency = fclk; cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10); @@ -431,7 +431,7 @@ static unsigned int suspend_freq; static int s3c_cpufreq_suspend(struct cpufreq_policy *policy) { suspend_pll.frequency = clk_get_rate(_clk_mpll); - suspend_pll.index = __raw_readl(S3C2410_MPLLCON); + suspend_pll.driver_data = __raw_readl(S3C2410_MPLLCON); suspend_freq = s3c_cpufreq_get(0) * 1000; return 0; diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index 27cacb524796..13bb4bae64ee 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -87,7 +87,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, freqs.old = clk_get_rate(armclk) / 1000; freqs.new = s3c64xx_freq_table[i].frequency; freqs.flags = 0; - dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].index]; + dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].driver_data]; if (freqs.old == freqs.new) return 0; @@ -104,7 +104,8 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, if (ret != 0) { pr_err("Failed to set VDDARM for %dkHz: %d\n", freqs.new, ret); - goto err; + freqs.new = freqs.old; + goto post_notify; } } #endif @@ -113,10 +114,13 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, if (ret < 0) { pr_err("Failed to set rate %dkHz: %d\n", freqs.new, ret); - goto err; + freqs.new = freqs.old; } +post_notify: cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + if (ret) + goto err; #ifdef CONFIG_REGULATOR if (vddarm && freqs.new < freqs.old) { diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c index f740b134d27b..77a210975fc4 100644 --- a/drivers/cpufreq/sc520_freq.c +++ b/drivers/cpufreq/sc520_freq.c @@ -71,7 +71,7 @@ static void sc520_freq_set_cpu_state(struct cpufreq_policy *policy, local_irq_disable(); clockspeed_reg = *cpuctl & ~0x03; - *cpuctl = clockspeed_reg | sc520_freq_table[state].index; + *cpuctl = clockspeed_reg | sc520_freq_table[state].driver_data; local_irq_enable(); diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c index 306ae462bba6..93061a408773 100644 --- a/drivers/cpufreq/sparc-us2e-cpufreq.c +++ b/drivers/cpufreq/sparc-us2e-cpufreq.c @@ -308,17 +308,17 @@ static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy) struct cpufreq_frequency_table *table = &us2e_freq_table[cpu].table[0]; - table[0].index = 0; + table[0].driver_data = 0; table[0].frequency = clock_tick / 1; - table[1].index = 1; + table[1].driver_data = 1; table[1].frequency = clock_tick / 2; - table[2].index = 2; + table[2].driver_data = 2; table[2].frequency = clock_tick / 4; - table[2].index = 3; + table[2].driver_data = 3; table[2].frequency = clock_tick / 6; - table[2].index = 4; + table[2].driver_data = 4; table[2].frequency = clock_tick / 8; - table[2].index = 5; + table[2].driver_data = 5; table[3].frequency = CPUFREQ_TABLE_END; policy->cpuinfo.transition_latency = 0; diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c index c71ee142347a..880ee293d61e 100644 --- a/drivers/cpufreq/sparc-us3-cpufreq.c +++ b/drivers/cpufreq/sparc-us3-cpufreq.c @@ -169,13 +169,13 @@ static int __init us3_freq_cpu_init(struct cpufreq_policy *policy) struct cpufreq_frequency_table *table = &us3_freq_table[cpu].table[0]; - table[0].index = 0; + table[0].driver_data = 0; table[0].frequency = clock_tick / 1; - table[1].index = 1; + table[1].driver_data = 1; table[1].frequency = clock_tick / 2; - table[2].index = 2; + table[2].driver_data = 2; table[2].frequency = clock_tick / 32; - table[3].index = 0; + table[3].driver_data = 0; table[3].frequency = CPUFREQ_TABLE_END; policy->cpuinfo.transition_latency = 0; diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 156829f4576d..c3efa7f2a908 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -250,11 +250,11 @@ static int spear_cpufreq_driver_init(void) } for (i = 0; i < cnt; i++) { - freq_tbl[i].index = i; + freq_tbl[i].driver_data = i; freq_tbl[i].frequency = be32_to_cpup(val++); } - freq_tbl[i].index = i; + freq_tbl[i].driver_data = i; freq_tbl[i].frequency = CPUFREQ_TABLE_END; spear_cpufreq.freq_tbl = freq_tbl; diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c index 618e6f417b1c..0915e712fbdc 100644 --- a/drivers/cpufreq/speedstep-centrino.c +++ b/drivers/cpufreq/speedstep-centrino.c @@ -79,11 +79,11 @@ static struct cpufreq_driver centrino_driver; /* Computes the correct form for IA32_PERF_CTL MSR for a particular frequency/voltage operating point; frequency in MHz, volts in mV. - This is stored as "index" in the structure. */ + This is stored as "driver_data" in the structure. */ #define OP(mhz, mv) \ { \ .frequency = (mhz) * 1000, \ - .index = (((mhz)/100) << 8) | ((mv - 700) / 16) \ + .driver_data = (((mhz)/100) << 8) | ((mv - 700) / 16) \ } /* @@ -307,7 +307,7 @@ static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe) per_cpu(centrino_model, cpu)->op_points[i].frequency != CPUFREQ_TABLE_END; i++) { - if (msr == per_cpu(centrino_model, cpu)->op_points[i].index) + if (msr == per_cpu(centrino_model, cpu)->op_points[i].driver_data) return per_cpu(centrino_model, cpu)-> op_points[i].frequency; } @@ -501,7 +501,7 @@ static int centrino_target (struct cpufreq_policy *policy, break; } - msr = per_cpu(centrino_model, cpu)->op_points[newstate].index; + msr = per_cpu(centrino_model, cpu)->op_points[newstate].driver_data; if (first_cpu) { rdmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr, &h); diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c index c74c0e130ef4..cd66b85d927c 100644 --- a/drivers/cpufreq/tegra-cpufreq.c +++ b/drivers/cpufreq/tegra-cpufreq.c @@ -28,17 +28,16 @@ #include <linux/io.h> #include <linux/suspend.h> -/* Frequency table index must be sequential starting at 0 */ static struct cpufreq_frequency_table freq_table[] = { - { 0, 216000 }, - { 1, 312000 }, - { 2, 456000 }, - { 3, 608000 }, - { 4, 760000 }, - { 5, 816000 }, - { 6, 912000 }, - { 7, 1000000 }, - { 8, CPUFREQ_TABLE_END }, + { .frequency = 216000 }, + { .frequency = 312000 }, + { .frequency = 456000 }, + { .frequency = 608000 }, + { .frequency = 760000 }, + { .frequency = 816000 }, + { .frequency = 912000 }, + { .frequency = 1000000 }, + { .frequency = CPUFREQ_TABLE_END }, }; #define NUM_CPUS 2 @@ -138,12 +137,12 @@ static int tegra_update_cpu_speed(struct cpufreq_policy *policy, if (ret) { pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n", freqs.new); - return ret; + freqs.new = freqs.old; } cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - return 0; + return ret; } static unsigned long tegra_cpu_highest_speed(void) diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index e21cdfa4002a..0e2cd5cab4d0 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -1,7 +1,9 @@ -config CPU_IDLE +menuconfig CPU_IDLE bool "CPU idle PM support" default y if ACPI || PPC_PSERIES + select CPU_IDLE_GOV_LADDER if (!NO_HZ && !NO_HZ_IDLE) + select CPU_IDLE_GOV_MENU if (NO_HZ || NO_HZ_IDLE) help CPU idle is a generic framework for supporting software-controlled idle processor power management. It includes modular cross-platform @@ -9,9 +11,10 @@ config CPU_IDLE If you're using an ACPI-enabled platform, you should say Y here. +if CPU_IDLE + config CPU_IDLE_MULTIPLE_DRIVERS bool "Support multiple cpuidle drivers" - depends on CPU_IDLE default n help Allows the cpuidle framework to use different drivers for each CPU. @@ -19,20 +22,13 @@ config CPU_IDLE_MULTIPLE_DRIVERS states. If unsure say N. config CPU_IDLE_GOV_LADDER - bool - depends on CPU_IDLE + bool "Ladder governor (for periodic timer tick)" default y config CPU_IDLE_GOV_MENU - bool - depends on CPU_IDLE && NO_HZ + bool "Menu governor (for tickless system)" default y -config ARCH_NEEDS_CPU_IDLE_COUPLED - def_bool n - -if CPU_IDLE - config CPU_IDLE_CALXEDA bool "CPU Idle Driver for Calxeda processors" depends on ARCH_HIGHBANK @@ -40,4 +36,13 @@ config CPU_IDLE_CALXEDA help Select this to enable cpuidle on Calxeda processors. +config CPU_IDLE_ZYNQ + bool "CPU Idle Driver for Xilinx Zynq processors" + depends on ARCH_ZYNQ + help + Select this to enable cpuidle on Xilinx Zynq processors. + endif + +config ARCH_NEEDS_CPU_IDLE_COUPLED + def_bool n diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 0d8bd55e776f..8767a7b3eb91 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o +obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o diff --git a/drivers/cpuidle/cpuidle-zynq.c b/drivers/cpuidle/cpuidle-zynq.c new file mode 100644 index 000000000000..38e03a183591 --- /dev/null +++ b/drivers/cpuidle/cpuidle-zynq.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012-2013 Xilinx + * + * CPU idle support for Xilinx Zynq + * + * based on arch/arm/mach-at91/cpuidle.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/>. + * + * The cpu idle uses wait-for-interrupt and RAM self refresh in order + * to implement two idle states - + * #1 wait-for-interrupt + * #2 wait-for-interrupt and RAM self refresh + * + * Maintainer: Michal Simek <michal.simek@xilinx.com> + */ + +#include <linux/init.h> +#include <linux/cpu_pm.h> +#include <linux/cpuidle.h> +#include <linux/of.h> +#include <asm/proc-fns.h> +#include <asm/cpuidle.h> + +#define ZYNQ_MAX_STATES 2 + +/* Actual code that puts the SoC in different idle states */ +static int zynq_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + /* Devices must be stopped here */ + cpu_pm_enter(); + + /* Add code for DDR self refresh start */ + cpu_do_idle(); + + /* Add code for DDR self refresh stop */ + cpu_pm_exit(); + + return index; +} + +static struct cpuidle_driver zynq_idle_driver = { + .name = "zynq_idle", + .owner = THIS_MODULE, + .states = { + ARM_CPUIDLE_WFI_STATE, + { + .enter = zynq_enter_idle, + .exit_latency = 10, + .target_residency = 10000, + .flags = CPUIDLE_FLAG_TIME_VALID | + CPUIDLE_FLAG_TIMER_STOP, + .name = "RAM_SR", + .desc = "WFI and RAM Self Refresh", + }, + }, + .safe_state_index = 0, + .state_count = ZYNQ_MAX_STATES, +}; + +/* Initialize CPU idle by registering the idle states */ +static int __init zynq_cpuidle_init(void) +{ + if (!of_machine_is_compatible("xlnx,zynq-7000")) + return -ENODEV; + + pr_info("Xilinx Zynq CpuIdle Driver started\n"); + + return cpuidle_register(&zynq_idle_driver, NULL); +} + +device_initcall(zynq_cpuidle_init); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index c3a93fece819..fdc432f18022 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -466,7 +466,7 @@ void cpuidle_unregister(struct cpuidle_driver *drv) int cpu; struct cpuidle_device *device; - for_each_possible_cpu(cpu) { + for_each_cpu(cpu, drv->cpumask) { device = &per_cpu(cpuidle_dev, cpu); cpuidle_unregister_device(device); } @@ -498,7 +498,7 @@ int cpuidle_register(struct cpuidle_driver *drv, return ret; } - for_each_possible_cpu(cpu) { + for_each_cpu(cpu, drv->cpumask) { device = &per_cpu(cpuidle_dev, cpu); device->cpu = cpu; diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 8dfaaae94444..3ac499d5a207 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -18,182 +18,249 @@ DEFINE_SPINLOCK(cpuidle_driver_lock); -static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); -static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); +#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS -static void cpuidle_setup_broadcast_timer(void *arg) +static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); + +/** + * __cpuidle_get_cpu_driver - return the cpuidle driver tied to a CPU. + * @cpu: the CPU handled by the driver + * + * Returns a pointer to struct cpuidle_driver or NULL if no driver has been + * registered for @cpu. + */ +static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) { - int cpu = smp_processor_id(); - clockevents_notify((long)(arg), &cpu); + return per_cpu(cpuidle_drivers, cpu); } -static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu) +/** + * __cpuidle_unset_driver - unset per CPU driver variables. + * @drv: a valid pointer to a struct cpuidle_driver + * + * For each CPU in the driver's CPU mask, unset the registered driver per CPU + * variable. If @drv is different from the registered driver, the corresponding + * variable is not cleared. + */ +static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) { - int i; - - drv->refcnt = 0; + int cpu; - for (i = drv->state_count - 1; i >= 0 ; i--) { + for_each_cpu(cpu, drv->cpumask) { - if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) + if (drv != __cpuidle_get_cpu_driver(cpu)) continue; - drv->bctimer = 1; - on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); - break; + per_cpu(cpuidle_drivers, cpu) = NULL; } } -static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) +/** + * __cpuidle_set_driver - set per CPU driver variables the the given driver. + * @drv: a valid pointer to a struct cpuidle_driver + * + * For each CPU in the driver's cpumask, unset the registered driver per CPU + * to @drv. + * + * Returns 0 on success, -EBUSY if the CPUs have driver(s) already. + */ +static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) { - if (!drv || !drv->state_count) - return -EINVAL; - - if (cpuidle_disabled()) - return -ENODEV; + int cpu; - if (__cpuidle_get_cpu_driver(cpu)) - return -EBUSY; + for_each_cpu(cpu, drv->cpumask) { - __cpuidle_driver_init(drv, cpu); + if (__cpuidle_get_cpu_driver(cpu)) { + __cpuidle_unset_driver(drv); + return -EBUSY; + } - __cpuidle_set_cpu_driver(drv, cpu); + per_cpu(cpuidle_drivers, cpu) = drv; + } return 0; } -static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) -{ - if (drv != __cpuidle_get_cpu_driver(cpu)) - return; +#else - if (!WARN_ON(drv->refcnt > 0)) - __cpuidle_set_cpu_driver(NULL, cpu); +static struct cpuidle_driver *cpuidle_curr_driver; - if (drv->bctimer) { - drv->bctimer = 0; - on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); - } +/** + * __cpuidle_get_cpu_driver - return the global cpuidle driver pointer. + * @cpu: ignored without the multiple driver support + * + * Return a pointer to a struct cpuidle_driver object or NULL if no driver was + * previously registered. + */ +static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +{ + return cpuidle_curr_driver; } -#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS +/** + * __cpuidle_set_driver - assign the global cpuidle driver variable. + * @drv: pointer to a struct cpuidle_driver object + * + * Returns 0 on success, -EBUSY if the driver is already registered. + */ +static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) +{ + if (cpuidle_curr_driver) + return -EBUSY; -static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); + cpuidle_curr_driver = drv; -static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - per_cpu(cpuidle_drivers, cpu) = drv; + return 0; } -static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +/** + * __cpuidle_unset_driver - unset the global cpuidle driver variable. + * @drv: a pointer to a struct cpuidle_driver + * + * Reset the global cpuidle variable to NULL. If @drv does not match the + * registered driver, do nothing. + */ +static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) { - return per_cpu(cpuidle_drivers, cpu); + if (drv == cpuidle_curr_driver) + cpuidle_curr_driver = NULL; } -static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) +#endif + +/** + * cpuidle_setup_broadcast_timer - enable/disable the broadcast timer + * @arg: a void pointer used to match the SMP cross call API + * + * @arg is used as a value of type 'long' with on of the two values: + * - CLOCK_EVT_NOTIFY_BROADCAST_ON + * - CLOCK_EVT_NOTIFY_BROADCAST_OFF + * + * Set the broadcast timer notification for the current CPU. This function + * is executed per CPU by an SMP cross call. It not supposed to be called + * directly. + */ +static void cpuidle_setup_broadcast_timer(void *arg) { - int cpu; - for_each_present_cpu(cpu) - __cpuidle_unregister_driver(drv, cpu); + int cpu = smp_processor_id(); + clockevents_notify((long)(arg), &cpu); } -static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) +/** + * __cpuidle_driver_init - initialize the driver's internal data + * @drv: a valid pointer to a struct cpuidle_driver + * + * Returns 0 on success, a negative error code otherwise. + */ +static int __cpuidle_driver_init(struct cpuidle_driver *drv) { - int ret = 0; - int i, cpu; + int i; - for_each_present_cpu(cpu) { - ret = __cpuidle_register_driver(drv, cpu); - if (ret) - break; - } + drv->refcnt = 0; - if (ret) - for_each_present_cpu(i) { - if (i == cpu) - break; - __cpuidle_unregister_driver(drv, i); - } + /* + * Use all possible CPUs as the default, because if the kernel boots + * with some CPUs offline and then we online one of them, the CPU + * notifier has to know which driver to assign. + */ + if (!drv->cpumask) + drv->cpumask = (struct cpumask *)cpu_possible_mask; + + /* + * Look for the timer stop flag in the different states, so that we know + * if the broadcast timer has to be set up. The loop is in the reverse + * order, because usually on of the the deeper states has this flag set. + */ + for (i = drv->state_count - 1; i >= 0 ; i--) { + if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) + continue; - return ret; + drv->bctimer = 1; + break; + } + + return 0; } -int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) +/** + * __cpuidle_register_driver: register the driver + * @drv: a valid pointer to a struct cpuidle_driver + * + * Do some sanity checks, initialize the driver, assign the driver to the + * global cpuidle driver variable(s) and set up the broadcast timer if the + * cpuidle driver has some states that shut down the local timer. + * + * Returns 0 on success, a negative error code otherwise: + * * -EINVAL if the driver pointer is NULL or no idle states are available + * * -ENODEV if the cpuidle framework is disabled + * * -EBUSY if the driver is already assigned to the global variable(s) + */ +static int __cpuidle_register_driver(struct cpuidle_driver *drv) { int ret; - spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); + if (!drv || !drv->state_count) + return -EINVAL; - return ret; -} + if (cpuidle_disabled()) + return -ENODEV; -void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); -} + ret = __cpuidle_driver_init(drv); + if (ret) + return ret; -/** - * cpuidle_register_driver - registers a driver - * @drv: the driver - */ -int cpuidle_register_driver(struct cpuidle_driver *drv) -{ - int ret; + ret = __cpuidle_set_driver(drv); + if (ret) + return ret; - spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_all_cpu_driver(drv); - spin_unlock(&cpuidle_driver_lock); + if (drv->bctimer) + on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); - return ret; + return 0; } -EXPORT_SYMBOL_GPL(cpuidle_register_driver); /** - * cpuidle_unregister_driver - unregisters a driver - * @drv: the driver + * __cpuidle_unregister_driver - unregister the driver + * @drv: a valid pointer to a struct cpuidle_driver + * + * Check if the driver is no longer in use, reset the global cpuidle driver + * variable(s) and disable the timer broadcast notification mechanism if it was + * in use. + * */ -void cpuidle_unregister_driver(struct cpuidle_driver *drv) +static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) { - spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_all_cpu_driver(drv); - spin_unlock(&cpuidle_driver_lock); -} -EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); - -#else - -static struct cpuidle_driver *cpuidle_curr_driver; + if (WARN_ON(drv->refcnt > 0)) + return; -static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - cpuidle_curr_driver = drv; -} + if (drv->bctimer) { + drv->bctimer = 0; + on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); + } -static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) -{ - return cpuidle_curr_driver; + __cpuidle_unset_driver(drv); } /** * cpuidle_register_driver - registers a driver - * @drv: the driver + * @drv: a pointer to a valid struct cpuidle_driver + * + * Register the driver under a lock to prevent concurrent attempts to + * [un]register the driver from occuring at the same time. + * + * Returns 0 on success, a negative error code (returned by + * __cpuidle_register_driver()) otherwise. */ int cpuidle_register_driver(struct cpuidle_driver *drv) { - int ret, cpu; + int ret; - cpu = get_cpu(); spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv, cpu); + ret = __cpuidle_register_driver(drv); spin_unlock(&cpuidle_driver_lock); - put_cpu(); return ret; } @@ -201,23 +268,24 @@ EXPORT_SYMBOL_GPL(cpuidle_register_driver); /** * cpuidle_unregister_driver - unregisters a driver - * @drv: the driver + * @drv: a pointer to a valid struct cpuidle_driver + * + * Unregisters the cpuidle driver under a lock to prevent concurrent attempts + * to [un]register the driver from occuring at the same time. @drv has to + * match the currently registered driver. */ void cpuidle_unregister_driver(struct cpuidle_driver *drv) { - int cpu; - - cpu = get_cpu(); spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv, cpu); + __cpuidle_unregister_driver(drv); spin_unlock(&cpuidle_driver_lock); - put_cpu(); } EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); -#endif /** - * cpuidle_get_driver - return the current driver + * cpuidle_get_driver - return the driver tied to the current CPU. + * + * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered. */ struct cpuidle_driver *cpuidle_get_driver(void) { @@ -233,7 +301,11 @@ struct cpuidle_driver *cpuidle_get_driver(void) EXPORT_SYMBOL_GPL(cpuidle_get_driver); /** - * cpuidle_get_cpu_driver - return the driver tied with a cpu + * cpuidle_get_cpu_driver - return the driver registered for a CPU. + * @dev: a valid pointer to a struct cpuidle_device + * + * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered + * for the CPU associated with @dev. */ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) { @@ -244,6 +316,14 @@ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) } EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); +/** + * cpuidle_driver_ref - get a reference to the driver. + * + * Increment the reference counter of the cpuidle driver associated with + * the current CPU. + * + * Returns a pointer to the driver, or NULL if the current CPU has no driver. + */ struct cpuidle_driver *cpuidle_driver_ref(void) { struct cpuidle_driver *drv; @@ -257,6 +337,12 @@ struct cpuidle_driver *cpuidle_driver_ref(void) return drv; } +/** + * cpuidle_driver_unref - puts down the refcount for the driver + * + * Decrement the reference counter of the cpuidle driver associated with + * the current CPU. + */ void cpuidle_driver_unref(void) { struct cpuidle_driver *drv = cpuidle_get_driver(); diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 0f079be13305..31f3adba4cf3 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -67,7 +67,7 @@ comment "DEVFREQ Drivers" config ARM_EXYNOS4_BUS_DEVFREQ bool "ARM Exynos4210/4212/4412 Memory Bus DEVFREQ Driver" - depends on CPU_EXYNOS4210 || CPU_EXYNOS4212 || CPU_EXYNOS4412 + depends on CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412 select ARCH_HAS_OPP select DEVFREQ_GOV_SIMPLE_ONDEMAND help @@ -78,4 +78,14 @@ config ARM_EXYNOS4_BUS_DEVFREQ To operate with optimal voltages, ASV support is required (CONFIG_EXYNOS_ASV). +config ARM_EXYNOS5_BUS_DEVFREQ + bool "ARM Exynos5250 Bus DEVFREQ Driver" + depends on SOC_EXYNOS5250 + select ARCH_HAS_OPP + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Exynos5250 bus interface (vdd_int). + It reads PPMU counters of memory controllers and adjusts the + operating frequencies and voltages with OPP support. + endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 8c464234f7e7..16138c9e0d58 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o # DEVFREQ Drivers -obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos4_bus.o +obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ +obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index af8372feb587..e94e619fe050 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -271,6 +271,7 @@ void devfreq_monitor_suspend(struct devfreq *devfreq) return; } + devfreq_update_status(devfreq, devfreq->previous_freq); devfreq->stop_polling = true; mutex_unlock(&devfreq->lock); cancel_delayed_work_sync(&devfreq->work); @@ -287,6 +288,8 @@ EXPORT_SYMBOL(devfreq_monitor_suspend); */ void devfreq_monitor_resume(struct devfreq *devfreq) { + unsigned long freq; + mutex_lock(&devfreq->lock); if (!devfreq->stop_polling) goto out; @@ -295,8 +298,14 @@ void devfreq_monitor_resume(struct devfreq *devfreq) devfreq->profile->polling_ms) queue_delayed_work(devfreq_wq, &devfreq->work, msecs_to_jiffies(devfreq->profile->polling_ms)); + + devfreq->last_stat_updated = jiffies; devfreq->stop_polling = false; + if (devfreq->profile->get_cur_freq && + !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq)) + devfreq->previous_freq = freq; + out: mutex_unlock(&devfreq->lock); } @@ -518,6 +527,8 @@ EXPORT_SYMBOL(devfreq_add_device); /** * devfreq_remove_device() - Remove devfreq feature from a device. * @devfreq: the devfreq instance to be removed + * + * The opposite of devfreq_add_device(). */ int devfreq_remove_device(struct devfreq *devfreq) { @@ -533,6 +544,10 @@ EXPORT_SYMBOL(devfreq_remove_device); /** * devfreq_suspend_device() - Suspend devfreq of a device. * @devfreq: the devfreq instance to be suspended + * + * This function is intended to be called by the pm callbacks + * (e.g., runtime_suspend, suspend) of the device driver that + * holds the devfreq. */ int devfreq_suspend_device(struct devfreq *devfreq) { @@ -550,6 +565,10 @@ EXPORT_SYMBOL(devfreq_suspend_device); /** * devfreq_resume_device() - Resume devfreq of a device. * @devfreq: the devfreq instance to be resumed + * + * This function is intended to be called by the pm callbacks + * (e.g., runtime_resume, resume) of the device driver that + * holds the devfreq. */ int devfreq_resume_device(struct devfreq *devfreq) { @@ -905,11 +924,11 @@ static ssize_t show_trans_table(struct device *dev, struct device_attribute *att { struct devfreq *devfreq = to_devfreq(dev); ssize_t len; - int i, j, err; + int i, j; unsigned int max_state = devfreq->profile->max_state; - err = devfreq_update_status(devfreq, devfreq->previous_freq); - if (err) + if (!devfreq->stop_polling && + devfreq_update_status(devfreq, devfreq->previous_freq)) return 0; len = sprintf(buf, " From : To\n"); diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile new file mode 100644 index 000000000000..bfaaf5b0d61d --- /dev/null +++ b/drivers/devfreq/exynos/Makefile @@ -0,0 +1,3 @@ +# Exynos DEVFREQ Drivers +obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos4_bus.o +obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos_ppmu.o exynos5_bus.o diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos/exynos4_bus.c index 3f37f3b3f268..c5f86d8caca3 100644 --- a/drivers/devfreq/exynos4_bus.c +++ b/drivers/devfreq/exynos/exynos4_bus.c @@ -974,6 +974,7 @@ static int exynos4_busfreq_pm_notifier_event(struct notifier_block *this, rcu_read_unlock(); dev_err(data->dev, "%s: unable to find a min freq\n", __func__); + mutex_unlock(&data->lock); return PTR_ERR(opp); } new_oppinfo.rate = opp_get_freq(opp); diff --git a/drivers/devfreq/exynos/exynos5_bus.c b/drivers/devfreq/exynos/exynos5_bus.c new file mode 100644 index 000000000000..574b16b59be5 --- /dev/null +++ b/drivers/devfreq/exynos/exynos5_bus.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS5 INT clock frequency scaling support using DEVFREQ framework + * Based on work done by Jonghwan Choi <jhbird.choi@samsung.com> + * Support for only EXYNOS5250 is present. + * + * 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/module.h> +#include <linux/devfreq.h> +#include <linux/io.h> +#include <linux/opp.h> +#include <linux/slab.h> +#include <linux/suspend.h> +#include <linux/opp.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/pm_qos.h> +#include <linux/regulator/consumer.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#include "exynos_ppmu.h" + +#define MAX_SAFEVOLT 1100000 /* 1.10V */ +/* Assume that the bus is saturated if the utilization is 25% */ +#define INT_BUS_SATURATION_RATIO 25 + +enum int_level_idx { + LV_0, + LV_1, + LV_2, + LV_3, + LV_4, + _LV_END +}; + +enum exynos_ppmu_list { + PPMU_RIGHT, + PPMU_END, +}; + +struct busfreq_data_int { + struct device *dev; + struct devfreq *devfreq; + struct regulator *vdd_int; + struct exynos_ppmu ppmu[PPMU_END]; + unsigned long curr_freq; + bool disabled; + + struct notifier_block pm_notifier; + struct mutex lock; + struct pm_qos_request int_req; + struct clk *int_clk; +}; + +struct int_bus_opp_table { + unsigned int idx; + unsigned long clk; + unsigned long volt; +}; + +static struct int_bus_opp_table exynos5_int_opp_table[] = { + {LV_0, 266000, 1025000}, + {LV_1, 200000, 1025000}, + {LV_2, 160000, 1025000}, + {LV_3, 133000, 1025000}, + {LV_4, 100000, 1025000}, + {0, 0, 0}, +}; + +static void busfreq_mon_reset(struct busfreq_data_int *data) +{ + unsigned int i; + + for (i = PPMU_RIGHT; i < PPMU_END; i++) { + void __iomem *ppmu_base = data->ppmu[i].hw_base; + + /* Reset the performance and cycle counters */ + exynos_ppmu_reset(ppmu_base); + + /* Setup count registers to monitor read/write transactions */ + data->ppmu[i].event[PPMU_PMNCNT3] = RDWR_DATA_COUNT; + exynos_ppmu_setevent(ppmu_base, PPMU_PMNCNT3, + data->ppmu[i].event[PPMU_PMNCNT3]); + + exynos_ppmu_start(ppmu_base); + } +} + +static void exynos5_read_ppmu(struct busfreq_data_int *data) +{ + int i, j; + + for (i = PPMU_RIGHT; i < PPMU_END; i++) { + void __iomem *ppmu_base = data->ppmu[i].hw_base; + + exynos_ppmu_stop(ppmu_base); + + /* Update local data from PPMU */ + data->ppmu[i].ccnt = __raw_readl(ppmu_base + PPMU_CCNT); + + for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { + if (data->ppmu[i].event[j] == 0) + data->ppmu[i].count[j] = 0; + else + data->ppmu[i].count[j] = + exynos_ppmu_read(ppmu_base, j); + } + } + + busfreq_mon_reset(data); +} + +static int exynos5_int_setvolt(struct busfreq_data_int *data, + unsigned long volt) +{ + return regulator_set_voltage(data->vdd_int, volt, MAX_SAFEVOLT); +} + +static int exynos5_busfreq_int_target(struct device *dev, unsigned long *_freq, + u32 flags) +{ + int err = 0; + struct platform_device *pdev = container_of(dev, struct platform_device, + dev); + struct busfreq_data_int *data = platform_get_drvdata(pdev); + struct opp *opp; + unsigned long old_freq, freq; + unsigned long volt; + + rcu_read_lock(); + opp = devfreq_recommended_opp(dev, _freq, flags); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(dev, "%s: Invalid OPP.\n", __func__); + return PTR_ERR(opp); + } + + freq = opp_get_freq(opp); + volt = opp_get_voltage(opp); + rcu_read_unlock(); + + old_freq = data->curr_freq; + + if (old_freq == freq) + return 0; + + dev_dbg(dev, "targetting %lukHz %luuV\n", freq, volt); + + mutex_lock(&data->lock); + + if (data->disabled) + goto out; + + if (freq > exynos5_int_opp_table[0].clk) + pm_qos_update_request(&data->int_req, freq * 16 / 1000); + else + pm_qos_update_request(&data->int_req, -1); + + if (old_freq < freq) + err = exynos5_int_setvolt(data, volt); + if (err) + goto out; + + err = clk_set_rate(data->int_clk, freq * 1000); + + if (err) + goto out; + + if (old_freq > freq) + err = exynos5_int_setvolt(data, volt); + if (err) + goto out; + + data->curr_freq = freq; +out: + mutex_unlock(&data->lock); + return err; +} + +static int exynos5_get_busier_dmc(struct busfreq_data_int *data) +{ + int i, j; + int busy = 0; + unsigned int temp = 0; + + for (i = PPMU_RIGHT; i < PPMU_END; i++) { + for (j = PPMU_PMNCNT0; j < PPMU_PMNCNT_MAX; j++) { + if (data->ppmu[i].count[j] > temp) { + temp = data->ppmu[i].count[j]; + busy = i; + } + } + } + + return busy; +} + +static int exynos5_int_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, + dev); + struct busfreq_data_int *data = platform_get_drvdata(pdev); + int busier_dmc; + + exynos5_read_ppmu(data); + busier_dmc = exynos5_get_busier_dmc(data); + + stat->current_frequency = data->curr_freq; + + /* Number of cycles spent on memory access */ + stat->busy_time = data->ppmu[busier_dmc].count[PPMU_PMNCNT3]; + stat->busy_time *= 100 / INT_BUS_SATURATION_RATIO; + stat->total_time = data->ppmu[busier_dmc].ccnt; + + return 0; +} +static void exynos5_int_exit(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, + dev); + struct busfreq_data_int *data = platform_get_drvdata(pdev); + + devfreq_unregister_opp_notifier(dev, data->devfreq); +} + +static struct devfreq_dev_profile exynos5_devfreq_int_profile = { + .initial_freq = 160000, + .polling_ms = 100, + .target = exynos5_busfreq_int_target, + .get_dev_status = exynos5_int_get_dev_status, + .exit = exynos5_int_exit, +}; + +static int exynos5250_init_int_tables(struct busfreq_data_int *data) +{ + int i, err = 0; + + for (i = LV_0; i < _LV_END; i++) { + err = opp_add(data->dev, exynos5_int_opp_table[i].clk, + exynos5_int_opp_table[i].volt); + if (err) { + dev_err(data->dev, "Cannot add opp entries.\n"); + return err; + } + } + + return 0; +} + +static int exynos5_busfreq_int_pm_notifier_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct busfreq_data_int *data = container_of(this, + struct busfreq_data_int, pm_notifier); + struct opp *opp; + unsigned long maxfreq = ULONG_MAX; + unsigned long freq; + unsigned long volt; + int err = 0; + + switch (event) { + case PM_SUSPEND_PREPARE: + /* Set Fastest and Deactivate DVFS */ + mutex_lock(&data->lock); + + data->disabled = true; + + rcu_read_lock(); + opp = opp_find_freq_floor(data->dev, &maxfreq); + if (IS_ERR(opp)) { + rcu_read_unlock(); + err = PTR_ERR(opp); + goto unlock; + } + freq = opp_get_freq(opp); + volt = opp_get_voltage(opp); + rcu_read_unlock(); + + err = exynos5_int_setvolt(data, volt); + if (err) + goto unlock; + + err = clk_set_rate(data->int_clk, freq * 1000); + + if (err) + goto unlock; + + data->curr_freq = freq; +unlock: + mutex_unlock(&data->lock); + if (err) + return NOTIFY_BAD; + return NOTIFY_OK; + case PM_POST_RESTORE: + case PM_POST_SUSPEND: + /* Reactivate */ + mutex_lock(&data->lock); + data->disabled = false; + mutex_unlock(&data->lock); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int exynos5_busfreq_int_probe(struct platform_device *pdev) +{ + struct busfreq_data_int *data; + struct opp *opp; + struct device *dev = &pdev->dev; + struct device_node *np; + unsigned long initial_freq; + unsigned long initial_volt; + int err = 0; + int i; + + data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data_int), + GFP_KERNEL); + if (data == NULL) { + dev_err(dev, "Cannot allocate memory.\n"); + return -ENOMEM; + } + + np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-ppmu"); + if (np == NULL) { + pr_err("Unable to find PPMU node\n"); + return -ENOENT; + } + + for (i = PPMU_RIGHT; i < PPMU_END; i++) { + /* map PPMU memory region */ + data->ppmu[i].hw_base = of_iomap(np, i); + if (data->ppmu[i].hw_base == NULL) { + dev_err(&pdev->dev, "failed to map memory region\n"); + return -ENOMEM; + } + } + data->pm_notifier.notifier_call = exynos5_busfreq_int_pm_notifier_event; + data->dev = dev; + mutex_init(&data->lock); + + err = exynos5250_init_int_tables(data); + if (err) + goto err_regulator; + + data->vdd_int = regulator_get(dev, "vdd_int"); + if (IS_ERR(data->vdd_int)) { + dev_err(dev, "Cannot get the regulator \"vdd_int\"\n"); + err = PTR_ERR(data->vdd_int); + goto err_regulator; + } + + data->int_clk = clk_get(dev, "int_clk"); + if (IS_ERR(data->int_clk)) { + dev_err(dev, "Cannot get clock \"int_clk\"\n"); + err = PTR_ERR(data->int_clk); + goto err_clock; + } + + rcu_read_lock(); + opp = opp_find_freq_floor(dev, + &exynos5_devfreq_int_profile.initial_freq); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(dev, "Invalid initial frequency %lu kHz.\n", + exynos5_devfreq_int_profile.initial_freq); + err = PTR_ERR(opp); + goto err_opp_add; + } + initial_freq = opp_get_freq(opp); + initial_volt = opp_get_voltage(opp); + rcu_read_unlock(); + data->curr_freq = initial_freq; + + err = clk_set_rate(data->int_clk, initial_freq * 1000); + if (err) { + dev_err(dev, "Failed to set initial frequency\n"); + goto err_opp_add; + } + + err = exynos5_int_setvolt(data, initial_volt); + if (err) + goto err_opp_add; + + platform_set_drvdata(pdev, data); + + busfreq_mon_reset(data); + + data->devfreq = devfreq_add_device(dev, &exynos5_devfreq_int_profile, + "simple_ondemand", NULL); + + if (IS_ERR(data->devfreq)) { + err = PTR_ERR(data->devfreq); + goto err_devfreq_add; + } + + devfreq_register_opp_notifier(dev, data->devfreq); + + err = register_pm_notifier(&data->pm_notifier); + if (err) { + dev_err(dev, "Failed to setup pm notifier\n"); + goto err_devfreq_add; + } + + /* TODO: Add a new QOS class for int/mif bus */ + pm_qos_add_request(&data->int_req, PM_QOS_NETWORK_THROUGHPUT, -1); + + return 0; + +err_devfreq_add: + devfreq_remove_device(data->devfreq); + platform_set_drvdata(pdev, NULL); +err_opp_add: + clk_put(data->int_clk); +err_clock: + regulator_put(data->vdd_int); +err_regulator: + return err; +} + +static int exynos5_busfreq_int_remove(struct platform_device *pdev) +{ + struct busfreq_data_int *data = platform_get_drvdata(pdev); + + pm_qos_remove_request(&data->int_req); + unregister_pm_notifier(&data->pm_notifier); + devfreq_remove_device(data->devfreq); + regulator_put(data->vdd_int); + clk_put(data->int_clk); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static int exynos5_busfreq_int_resume(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, + dev); + struct busfreq_data_int *data = platform_get_drvdata(pdev); + + busfreq_mon_reset(data); + return 0; +} + +static const struct dev_pm_ops exynos5_busfreq_int_pm = { + .resume = exynos5_busfreq_int_resume, +}; + +/* platform device pointer for exynos5 devfreq device. */ +static struct platform_device *exynos5_devfreq_pdev; + +static struct platform_driver exynos5_busfreq_int_driver = { + .probe = exynos5_busfreq_int_probe, + .remove = exynos5_busfreq_int_remove, + .driver = { + .name = "exynos5-bus-int", + .owner = THIS_MODULE, + .pm = &exynos5_busfreq_int_pm, + }, +}; + +static int __init exynos5_busfreq_int_init(void) +{ + int ret; + + ret = platform_driver_register(&exynos5_busfreq_int_driver); + if (ret < 0) + goto out; + + exynos5_devfreq_pdev = + platform_device_register_simple("exynos5-bus-int", -1, NULL, 0); + if (IS_ERR_OR_NULL(exynos5_devfreq_pdev)) { + ret = PTR_ERR(exynos5_devfreq_pdev); + goto out1; + } + + return 0; +out1: + platform_driver_unregister(&exynos5_busfreq_int_driver); +out: + return ret; +} +late_initcall(exynos5_busfreq_int_init); + +static void __exit exynos5_busfreq_int_exit(void) +{ + platform_device_unregister(exynos5_devfreq_pdev); + platform_driver_unregister(&exynos5_busfreq_int_driver); +} +module_exit(exynos5_busfreq_int_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EXYNOS5 busfreq driver with devfreq framework"); diff --git a/drivers/devfreq/exynos/exynos_ppmu.c b/drivers/devfreq/exynos/exynos_ppmu.c new file mode 100644 index 000000000000..85fc5ac1036a --- /dev/null +++ b/drivers/devfreq/exynos/exynos_ppmu.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS - PPMU support + * + * 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/kernel.h> +#include <linux/types.h> +#include <linux/io.h> + +#include "exynos_ppmu.h" + +void exynos_ppmu_reset(void __iomem *ppmu_base) +{ + __raw_writel(PPMU_CYCLE_RESET | PPMU_COUNTER_RESET, ppmu_base); + __raw_writel(PPMU_ENABLE_CYCLE | + PPMU_ENABLE_COUNT0 | + PPMU_ENABLE_COUNT1 | + PPMU_ENABLE_COUNT2 | + PPMU_ENABLE_COUNT3, + ppmu_base + PPMU_CNTENS); +} + +void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch, + unsigned int evt) +{ + __raw_writel(evt, ppmu_base + PPMU_BEVTSEL(ch)); +} + +void exynos_ppmu_start(void __iomem *ppmu_base) +{ + __raw_writel(PPMU_ENABLE, ppmu_base); +} + +void exynos_ppmu_stop(void __iomem *ppmu_base) +{ + __raw_writel(PPMU_DISABLE, ppmu_base); +} + +unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch) +{ + unsigned int total; + + if (ch == PPMU_PMNCNT3) + total = ((__raw_readl(ppmu_base + PMCNT_OFFSET(ch)) << 8) | + __raw_readl(ppmu_base + PMCNT_OFFSET(ch + 1))); + else + total = __raw_readl(ppmu_base + PMCNT_OFFSET(ch)); + + return total; +} diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h new file mode 100644 index 000000000000..7dfb221eaccd --- /dev/null +++ b/drivers/devfreq/exynos/exynos_ppmu.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS PPMU header + * + * 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 __DEVFREQ_EXYNOS_PPMU_H +#define __DEVFREQ_EXYNOS_PPMU_H __FILE__ + +#include <linux/ktime.h> + +/* For PPMU Control */ +#define PPMU_ENABLE BIT(0) +#define PPMU_DISABLE 0x0 +#define PPMU_CYCLE_RESET BIT(1) +#define PPMU_COUNTER_RESET BIT(2) + +#define PPMU_ENABLE_COUNT0 BIT(0) +#define PPMU_ENABLE_COUNT1 BIT(1) +#define PPMU_ENABLE_COUNT2 BIT(2) +#define PPMU_ENABLE_COUNT3 BIT(3) +#define PPMU_ENABLE_CYCLE BIT(31) + +#define PPMU_CNTENS 0x10 +#define PPMU_FLAG 0x50 +#define PPMU_CCNT_OVERFLOW BIT(31) +#define PPMU_CCNT 0x100 + +#define PPMU_PMCNT0 0x110 +#define PPMU_PMCNT_OFFSET 0x10 +#define PMCNT_OFFSET(x) (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * x)) + +#define PPMU_BEVT0SEL 0x1000 +#define PPMU_BEVTSEL_OFFSET 0x100 +#define PPMU_BEVTSEL(x) (PPMU_BEVT0SEL + (ch * PPMU_BEVTSEL_OFFSET)) + +/* For Event Selection */ +#define RD_DATA_COUNT 0x5 +#define WR_DATA_COUNT 0x6 +#define RDWR_DATA_COUNT 0x7 + +enum ppmu_counter { + PPMU_PMNCNT0, + PPMU_PMCCNT1, + PPMU_PMNCNT2, + PPMU_PMNCNT3, + PPMU_PMNCNT_MAX, +}; + +struct bus_opp_table { + unsigned int idx; + unsigned long clk; + unsigned long volt; +}; + +struct exynos_ppmu { + void __iomem *hw_base; + unsigned int ccnt; + unsigned int event[PPMU_PMNCNT_MAX]; + unsigned int count[PPMU_PMNCNT_MAX]; + unsigned long long ns; + ktime_t reset_time; + bool ccnt_overflow; + bool count_overflow[PPMU_PMNCNT_MAX]; +}; + +void exynos_ppmu_reset(void __iomem *ppmu_base); +void exynos_ppmu_setevent(void __iomem *ppmu_base, unsigned int ch, + unsigned int evt); +void exynos_ppmu_start(void __iomem *ppmu_base); +void exynos_ppmu_stop(void __iomem *ppmu_base); +unsigned int exynos_ppmu_read(void __iomem *ppmu_base, unsigned int ch); +#endif /* __DEVFREQ_EXYNOS_PPMU_H */ diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index a0de82e21a7c..a975ebebea8a 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -1405,7 +1405,7 @@ static int dma_runtime_idle(struct device *dev) return -EAGAIN; } - return pm_schedule_suspend(dev, 0); + return 0; } /****************************************************************************** diff --git a/drivers/edac/amd64_edac_inj.c b/drivers/edac/amd64_edac_inj.c index 845f04786c2d..0d66ae68d468 100644 --- a/drivers/edac/amd64_edac_inj.c +++ b/drivers/edac/amd64_edac_inj.c @@ -24,7 +24,7 @@ static ssize_t amd64_inject_section_store(struct device *dev, unsigned long value; int ret; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) return ret; @@ -61,7 +61,7 @@ static ssize_t amd64_inject_word_store(struct device *dev, unsigned long value; int ret; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) return ret; @@ -97,7 +97,7 @@ static ssize_t amd64_inject_ecc_vector_store(struct device *dev, unsigned long value; int ret; - ret = strict_strtoul(data, 16, &value); + ret = kstrtoul(data, 16, &value); if (ret < 0) return ret; @@ -124,7 +124,7 @@ static ssize_t amd64_inject_read_store(struct device *dev, u32 section, word_bits; int ret; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) return ret; @@ -157,7 +157,7 @@ static ssize_t amd64_inject_write_store(struct device *dev, unsigned long value; int ret; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) return ret; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 67610a6ebf87..ef15a7e613bc 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -678,7 +678,7 @@ static ssize_t mci_sdram_scrub_rate_store(struct device *dev, unsigned long bandwidth = 0; int new_bw = 0; - if (strict_strtoul(data, 10, &bandwidth) < 0) + if (kstrtoul(data, 10, &bandwidth) < 0) return -EINVAL; new_bw = mci->set_sdram_scrub_rate(mci, bandwidth); diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 0ec3e95a12cd..80a963d64e58 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -704,7 +704,7 @@ static ssize_t i7core_inject_section_store(struct device *dev, if (pvt->inject.enable) disable_inject(mci); - rc = strict_strtoul(data, 10, &value); + rc = kstrtoul(data, 10, &value); if ((rc < 0) || (value > 3)) return -EIO; @@ -741,7 +741,7 @@ struct i7core_pvt *pvt = mci->pvt_info; if (pvt->inject.enable) disable_inject(mci); - rc = strict_strtoul(data, 10, &value); + rc = kstrtoul(data, 10, &value); if ((rc < 0) || (value > 7)) return -EIO; @@ -781,7 +781,7 @@ static ssize_t i7core_inject_eccmask_store(struct device *dev, if (pvt->inject.enable) disable_inject(mci); - rc = strict_strtoul(data, 10, &value); + rc = kstrtoul(data, 10, &value); if (rc < 0) return -EIO; @@ -830,7 +830,7 @@ static ssize_t i7core_inject_store_##param( \ if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\ value = -1; \ else { \ - rc = strict_strtoul(data, 10, &value); \ + rc = kstrtoul(data, 10, &value); \ if ((rc < 0) || (value >= limit)) \ return -EIO; \ } \ @@ -934,7 +934,7 @@ static ssize_t i7core_inject_enable_store(struct device *dev, if (!pvt->pci_ch[pvt->inject.channel][0]) return 0; - rc = strict_strtoul(data, 10, &enable); + rc = kstrtoul(data, 10, &enable); if ((rc < 0)) return 0; diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index f3f0c930d550..30f7309446a6 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -134,7 +134,8 @@ static const char * const mc5_mce_desc[] = { "Physical register file AG0 port", "Physical register file AG1 port", "Flag register file", - "DE error occurred" + "DE error occurred", + "Retire status queue" }; static bool f12h_mc0_mce(u16 ec, u8 xec) @@ -624,7 +625,7 @@ static void decode_mc5_mce(struct mce *m) if (xec == 0x0 || xec == 0xc) pr_cont("%s.\n", mc5_mce_desc[xec]); - else if (xec < 0xd) + else if (xec <= 0xd) pr_cont("%s parity error.\n", mc5_mce_desc[xec]); else goto wrong_mc5_mce; diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c index 2ae78f20cc28..5e46a9fea31b 100644 --- a/drivers/edac/mce_amd_inj.c +++ b/drivers/edac/mce_amd_inj.c @@ -43,7 +43,7 @@ static ssize_t edac_inject_##reg##_store(struct kobject *kobj, \ int ret = 0; \ unsigned long value; \ \ - ret = strict_strtoul(data, 16, &value); \ + ret = kstrtoul(data, 16, &value); \ if (ret < 0) \ printk(KERN_ERR "Error writing MCE " #reg " field.\n"); \ \ @@ -83,7 +83,7 @@ static ssize_t edac_inject_bank_store(struct kobject *kobj, int ret = 0; unsigned long value; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) { printk(KERN_ERR "Invalid bank value!\n"); return -EINVAL; diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 202d2c85ba2e..91864ad200ff 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -79,10 +79,9 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) &entry->var.DataSize, entry->var.Data); size = entry->var.DataSize; - *cb_data->buf = kmalloc(size, GFP_KERNEL); + *cb_data->buf = kmemdup(entry->var.Data, size, GFP_KERNEL); if (*cb_data->buf == NULL) return -ENOMEM; - memcpy(*cb_data->buf, entry->var.Data, size); return size; } @@ -236,7 +235,11 @@ static __init int efivars_pstore_init(void) efi_pstore_info.bufsize = 1024; spin_lock_init(&efi_pstore_info.buf_lock); - pstore_register(&efi_pstore_info); + if (pstore_register(&efi_pstore_info)) { + kfree(efi_pstore_info.buf); + efi_pstore_info.buf = NULL; + efi_pstore_info.bufsize = 0; + } return 0; } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 94624370caad..b2450ba14138 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -244,7 +244,7 @@ config GPIO_TS5500 config GPIO_XILINX bool "Xilinx GPIO support" - depends on PPC_OF || MICROBLAZE + depends on PPC_OF || MICROBLAZE || ARCH_ZYNQ help Say yes here to support the Xilinx FPGA GPIO device @@ -340,7 +340,7 @@ config GPIO_MAX7300 depends on I2C select GPIO_MAX730X help - GPIO driver for Maxim MAX7301 I2C-based GPIO expander. + GPIO driver for Maxim MAX7300 I2C-based GPIO expander. config GPIO_MAX732X tristate "MAX7319, MAX7320-7327 I2C Port Expanders" diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 1077754f8289..3e7812f0405e 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -34,10 +34,10 @@ static int devm_gpio_match(struct device *dev, void *res, void *data) } /** - * devm_gpio_request - request a gpio for a managed device - * @dev: device to request the gpio for - * @gpio: gpio to allocate - * @label: the name of the requested gpio + * devm_gpio_request - request a GPIO for a managed device + * @dev: device to request the GPIO for + * @gpio: GPIO to allocate + * @label: the name of the requested GPIO * * Except for the extra @dev argument, this function takes the * same arguments and performs the same function as @@ -101,9 +101,9 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio, EXPORT_SYMBOL(devm_gpio_request_one); /** - * devm_gpio_free - free an interrupt - * @dev: device to free gpio for - * @gpio: gpio to free + * devm_gpio_free - free a GPIO + * @dev: device to free GPIO for + * @gpio: GPIO to free * * Except for the extra @dev argument, this function takes the * same arguments and performs the same function as gpio_free(). diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 7d9d7cb35f28..8369e71ebe4f 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -286,7 +286,7 @@ static int bt8xxgpio_resume(struct pci_dev *pdev) unsigned long flags; int err; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); err = pci_enable_device(pdev); if (err) return err; diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 8e08b8647655..84d2478ec294 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -235,8 +235,8 @@ static irqreturn_t grgpio_irq_handler(int irq, void *dev) * This function will be called as a consequence of the call to * irq_create_mapping in grgpio_to_irq */ -int grgpio_irq_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) +static int grgpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct grgpio_priv *priv = d->host_data; struct grgpio_lirq *lirq; @@ -291,7 +291,7 @@ int grgpio_irq_map(struct irq_domain *d, unsigned int irq, return ret; } -void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) +static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) { struct grgpio_priv *priv = d->host_data; int index; diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index e16d932fd444..2729e3d2d5bb 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -41,12 +41,14 @@ enum GPIO_REG { GPIO_USE_SEL = 0, GPIO_IO_SEL, GPIO_LVL, + GPO_BLINK }; -static const u8 ichx_regs[3][3] = { +static const u8 ichx_regs[4][3] = { {0x00, 0x30, 0x40}, /* USE_SEL[1-3] offsets */ {0x04, 0x34, 0x44}, /* IO_SEL[1-3] offsets */ {0x0c, 0x38, 0x48}, /* LVL[1-3] offsets */ + {0x18, 0x18, 0x18}, /* BLINK offset */ }; static const u8 ichx_reglen[3] = { @@ -148,6 +150,10 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, int val) { + /* Disable blink hardware which is available for GPIOs from 0 to 31. */ + if (nr < 32) + ichx_write_bit(GPO_BLINK, nr, 0, 0); + /* Set GPIO output value. */ ichx_write_bit(GPIO_LVL, nr, val, 0); diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index 62ef10a641c4..bfa1af1b519f 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -1,7 +1,7 @@ /* * Moorestown platform Langwell chip GPIO driver * - * Copyright (c) 2008 - 2009, Intel Corporation. + * Copyright (c) 2008, 2009, 2013, Intel Corporation. * * 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 @@ -20,7 +20,6 @@ /* Supports: * Moorestown platform Langwell chip. * Medfield platform Penwell chip. - * Whitney point. */ #include <linux/module.h> @@ -65,7 +64,7 @@ enum GPIO_REG { struct lnw_gpio { struct gpio_chip chip; - void *reg_base; + void __iomem *reg_base; spinlock_t lock; struct pci_dev *pdev; struct irq_domain *domain; @@ -74,15 +73,13 @@ struct lnw_gpio { #define to_lnw_priv(chip) container_of(chip, struct lnw_gpio, chip) static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, - enum GPIO_REG reg_type) + enum GPIO_REG reg_type) { struct lnw_gpio *lnw = to_lnw_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 32; - void __iomem *ptr; - ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); - return ptr; + return lnw->reg_base + reg_type * nreg * 4 + reg * 4; } static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, @@ -91,10 +88,8 @@ static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, struct lnw_gpio *lnw = to_lnw_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 16; - void __iomem *ptr; - ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); - return ptr; + return lnw->reg_base + reg_type * nreg * 4 + reg * 4; } static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) @@ -305,11 +300,7 @@ static const struct irq_domain_ops lnw_gpio_irq_ops = { static int lnw_gpio_runtime_idle(struct device *dev) { - int err = pm_schedule_suspend(dev, 500); - - if (!err) - return 0; - + pm_schedule_suspend(dev, 500); return -EBUSY; } @@ -318,56 +309,40 @@ static const struct dev_pm_ops lnw_gpio_pm_ops = { }; static int lnw_gpio_probe(struct pci_dev *pdev, - const struct pci_device_id *id) + const struct pci_device_id *id) { - void *base; - resource_size_t start, len; + void __iomem *base; struct lnw_gpio *lnw; u32 gpio_base; u32 irq_base; int retval; int ngpio = id->driver_data; - retval = pci_enable_device(pdev); + retval = pcim_enable_device(pdev); if (retval) return retval; - retval = pci_request_regions(pdev, "langwell_gpio"); + retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev)); if (retval) { - dev_err(&pdev->dev, "error requesting resources\n"); - goto err_pci_req_region; - } - /* get the gpio_base from bar1 */ - start = pci_resource_start(pdev, 1); - len = pci_resource_len(pdev, 1); - base = ioremap_nocache(start, len); - if (!base) { - dev_err(&pdev->dev, "error mapping bar1\n"); - retval = -EFAULT; - goto err_ioremap; + dev_err(&pdev->dev, "I/O memory mapping error\n"); + return retval; } - irq_base = *(u32 *)base; - gpio_base = *((u32 *)base + 1); + + base = pcim_iomap_table(pdev)[1]; + + irq_base = readl(base); + gpio_base = readl(sizeof(u32) + base); + /* release the IO mapping, since we already get the info from bar1 */ - iounmap(base); - /* get the register base from bar0 */ - start = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - base = devm_ioremap_nocache(&pdev->dev, start, len); - if (!base) { - dev_err(&pdev->dev, "error mapping bar0\n"); - retval = -EFAULT; - goto err_ioremap; - } + pcim_iounmap_regions(pdev, 1 << 1); lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL); if (!lnw) { - dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); - retval = -ENOMEM; - goto err_ioremap; + dev_err(&pdev->dev, "can't allocate chip data\n"); + return -ENOMEM; } - lnw->reg_base = base; + lnw->reg_base = pcim_iomap_table(pdev)[0]; lnw->chip.label = dev_name(&pdev->dev); lnw->chip.request = lnw_gpio_request; lnw->chip.direction_input = lnw_gpio_direction_input; @@ -380,18 +355,18 @@ static int lnw_gpio_probe(struct pci_dev *pdev, lnw->chip.can_sleep = 0; lnw->pdev = pdev; + spin_lock_init(&lnw->lock); + lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ngpio, irq_base, &lnw_gpio_irq_ops, lnw); - if (!lnw->domain) { - retval = -ENOMEM; - goto err_ioremap; - } + if (!lnw->domain) + return -ENOMEM; pci_set_drvdata(pdev, lnw); retval = gpiochip_add(&lnw->chip); if (retval) { - dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); - goto err_ioremap; + dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); + return retval; } lnw_irq_init_hw(lnw); @@ -399,18 +374,10 @@ static int lnw_gpio_probe(struct pci_dev *pdev, irq_set_handler_data(pdev->irq, lnw); irq_set_chained_handler(pdev->irq, lnw_irq_handler); - spin_lock_init(&lnw->lock); - pm_runtime_put_noidle(&pdev->dev); pm_runtime_allow(&pdev->dev); return 0; - -err_ioremap: - pci_release_regions(pdev); -err_pci_req_region: - pci_disable_device(pdev); - return retval; } static struct pci_driver lnw_gpio_driver = { @@ -422,88 +389,9 @@ static struct pci_driver lnw_gpio_driver = { }, }; - -static int wp_gpio_probe(struct platform_device *pdev) -{ - struct lnw_gpio *lnw; - struct gpio_chip *gc; - struct resource *rc; - int retval = 0; - - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!rc) - return -EINVAL; - - lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); - if (!lnw) { - dev_err(&pdev->dev, - "can't allocate whitneypoint_gpio chip data\n"); - return -ENOMEM; - } - lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc)); - if (lnw->reg_base == NULL) { - retval = -EINVAL; - goto err_kmalloc; - } - spin_lock_init(&lnw->lock); - gc = &lnw->chip; - gc->label = dev_name(&pdev->dev); - gc->owner = THIS_MODULE; - gc->direction_input = lnw_gpio_direction_input; - gc->direction_output = lnw_gpio_direction_output; - gc->get = lnw_gpio_get; - gc->set = lnw_gpio_set; - gc->to_irq = NULL; - gc->base = 0; - gc->ngpio = 64; - gc->can_sleep = 0; - retval = gpiochip_add(gc); - if (retval) { - dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n", - retval); - goto err_ioremap; - } - platform_set_drvdata(pdev, lnw); - return 0; -err_ioremap: - iounmap(lnw->reg_base); -err_kmalloc: - kfree(lnw); - return retval; -} - -static int wp_gpio_remove(struct platform_device *pdev) -{ - struct lnw_gpio *lnw = platform_get_drvdata(pdev); - int err; - err = gpiochip_remove(&lnw->chip); - if (err) - dev_err(&pdev->dev, "failed to remove gpio_chip.\n"); - iounmap(lnw->reg_base); - kfree(lnw); - platform_set_drvdata(pdev, NULL); - return 0; -} - -static struct platform_driver wp_gpio_driver = { - .probe = wp_gpio_probe, - .remove = wp_gpio_remove, - .driver = { - .name = "wp_gpio", - .owner = THIS_MODULE, - }, -}; - static int __init lnw_gpio_init(void) { - int ret; - ret = pci_register_driver(&lnw_gpio_driver); - if (ret < 0) - return ret; - ret = platform_driver_register(&wp_gpio_driver); - if (ret < 0) - pci_unregister_driver(&lnw_gpio_driver); - return ret; + return pci_register_driver(&lnw_gpio_driver); } device_initcall(lnw_gpio_init); diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 86c17de87692..761c4705dfbb 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -447,7 +447,6 @@ static int lp_gpio_remove(struct platform_device *pdev) err = gpiochip_remove(&lg->chip); if (err) dev_warn(&pdev->dev, "failed to remove gpio_chip.\n"); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 0966f2637ad2..6da6d7667c6d 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -465,6 +465,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, dev_warn(&pdev->dev, "ml_ioh_gpio: Failed to get IRQ base num\n"); chip->irq_base = -1; + ret = irq_base; goto err_irq_alloc_descs; } chip->irq_base = irq_base; diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c index fb2cc90d0134..e3ceaacde45c 100644 --- a/drivers/gpio/gpio-msm-v1.c +++ b/drivers/gpio/gpio-msm-v1.c @@ -652,14 +652,14 @@ static int gpio_msm_v1_probe(struct platform_device *pdev) return irq2; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base1 = devm_request_and_ioremap(&pdev->dev, res); - if (!base1) - return -EADDRNOTAVAIL; + base1 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base1)) + return PTR_ERR(base1); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - base2 = devm_request_and_ioremap(&pdev->dev, res); - if (!base2) - return -EADDRNOTAVAIL; + base2 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base2)) + return PTR_ERR(base2); for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) { if (i - FIRST_GPIO_IRQ >= diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 4a430360af5a..dfeb3a3a8f20 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1482,7 +1482,7 @@ static void omap_gpio_restore_context(struct gpio_bank *bank) #else #define omap_gpio_runtime_suspend NULL #define omap_gpio_runtime_resume NULL -static void omap_gpio_init_context(struct gpio_bank *p) {} +static inline void omap_gpio_init_context(struct gpio_bank *p) {} #endif static const struct dev_pm_ops gpio_pm_ops = { diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 6ec82f76f019..e8198dd68615 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -232,7 +232,14 @@ static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) { - return (int)(gpio_rcar_read(gpio_to_priv(chip), INDT) & BIT(offset)); + u32 bit = BIT(offset); + + /* testing on r8a7790 shows that INDT does not show correct pin state + * when configured as output, so use OUTDT in case of output pins */ + if (gpio_rcar_read(gpio_to_priv(chip), INOUTSEL) & bit) + return (int)(gpio_rcar_read(gpio_to_priv(chip), OUTDT) & bit); + else + return (int)(gpio_rcar_read(gpio_to_priv(chip), INDT) & bit); } static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c index 1bf55f67f7a5..368c3c00fca5 100644 --- a/drivers/gpio/gpio-rdc321x.c +++ b/drivers/gpio/gpio-rdc321x.c @@ -187,20 +187,18 @@ static int rdc321x_gpio_probe(struct platform_device *pdev) rdc321x_gpio_dev->reg1_data_base, &rdc321x_gpio_dev->data_reg[0]); if (err) - goto out_drvdata; + goto out_free; err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, rdc321x_gpio_dev->reg2_data_base, &rdc321x_gpio_dev->data_reg[1]); if (err) - goto out_drvdata; + goto out_free; dev_info(&pdev->dev, "registering %d GPIOs\n", rdc321x_gpio_dev->chip.ngpio); return gpiochip_add(&rdc321x_gpio_dev->chip); -out_drvdata: - platform_set_drvdata(pdev, NULL); out_free: kfree(rdc321x_gpio_dev); return err; @@ -216,7 +214,6 @@ static int rdc321x_gpio_remove(struct platform_device *pdev) dev_err(&pdev->dev, "failed to unregister chip\n"); kfree(rdc321x_gpio_dev); - platform_set_drvdata(pdev, NULL); return ret; } diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c index 558542552aae..f43ab6aea281 100644 --- a/drivers/gpio/gpio-sta2x11.c +++ b/drivers/gpio/gpio-sta2x11.c @@ -371,8 +371,12 @@ static int gsta_probe(struct platform_device *dev) res = platform_get_resource(dev, IORESOURCE_MEM, 0); chip = devm_kzalloc(&dev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; chip->dev = &dev->dev; - chip->reg_base = devm_request_and_ioremap(&dev->dev, res); + chip->reg_base = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(chip->reg_base)) + return PTR_ERR(chip->reg_base); for (i = 0; i < GSTA_NR_BLOCKS; i++) { chip->regs[i] = chip->reg_base + i * 4096; diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 3ce5bc38ac31..b33bad1bb4df 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -271,8 +271,8 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) return IRQ_HANDLED; } -int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hwirq) +static int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) { struct stmpe_gpio *stmpe_gpio = d->host_data; @@ -292,7 +292,7 @@ int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, return 0; } -void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) +static void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) { #ifdef CONFIG_ARM set_irq_flags(virq, 0); @@ -431,7 +431,6 @@ static int stmpe_gpio_remove(struct platform_device *pdev) if (irq >= 0) free_irq(irq, stmpe_gpio); - platform_set_drvdata(pdev, NULL); kfree(stmpe_gpio); return 0; diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index 796b6c42fa70..f371732591d2 100644 --- a/drivers/gpio/gpio-sx150x.c +++ b/drivers/gpio/gpio-sx150x.c @@ -548,7 +548,8 @@ static int sx150x_install_irq_chip(struct sx150x_chip *chip, #endif } - err = request_threaded_irq(irq_summary, + err = devm_request_threaded_irq(&chip->client->dev, + irq_summary, NULL, sx150x_irq_thread_fn, IRQF_SHARED | IRQF_TRIGGER_FALLING, @@ -567,8 +568,6 @@ static void sx150x_remove_irq_chip(struct sx150x_chip *chip) unsigned n; unsigned irq; - free_irq(chip->irq_summary, chip); - for (n = 0; n < chip->dev_cfg->ngpios; ++n) { irq = chip->irq_base + n; irq_set_chip_and_handler(irq, NULL, NULL); @@ -591,18 +590,19 @@ static int sx150x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, i2c_funcs)) return -ENOSYS; - chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL); + chip = devm_kzalloc(&client->dev, + sizeof(struct sx150x_chip), GFP_KERNEL); if (!chip) return -ENOMEM; sx150x_init_chip(chip, client, id->driver_data, pdata); rc = sx150x_init_hw(chip, pdata); if (rc < 0) - goto probe_fail_pre_gpiochip_add; + return rc; rc = gpiochip_add(&chip->gpio_chip); - if (rc < 0) - goto probe_fail_pre_gpiochip_add; + if (rc) + return rc; if (pdata->irq_summary >= 0) { rc = sx150x_install_irq_chip(chip, @@ -617,8 +617,6 @@ static int sx150x_probe(struct i2c_client *client, return 0; probe_fail_post_gpiochip_add: WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0); -probe_fail_pre_gpiochip_add: - kfree(chip); return rc; } @@ -635,8 +633,6 @@ static int sx150x_remove(struct i2c_client *client) if (chip->irq_summary >= 0) sx150x_remove_irq_chip(chip); - kfree(chip); - return 0; } diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index d34d80dfb083..4a5de273c230 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -407,7 +407,6 @@ static int tc3589x_gpio_remove(struct platform_device *pdev) free_irq(irq, tc3589x_gpio); - platform_set_drvdata(pdev, NULL); kfree(tc3589x_gpio); return 0; diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index 43774058b693..4c65f8883204 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -342,8 +342,6 @@ static int timbgpio_remove(struct platform_device *pdev) release_mem_region(iomem->start, resource_size(iomem)); kfree(tgpio); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 2b7252cb2427..cddfa22edb41 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -279,7 +279,6 @@ out_release: release_region(res_gpi->start, resource_size(res_gpi)); if (vg->gpo_reserved) release_region(res_gpi->start, resource_size(res_gpo)); - platform_set_drvdata(pdev, NULL); kfree(vg); return ret; } @@ -301,7 +300,6 @@ static int vx855gpio_remove(struct platform_device *pdev) release_region(res->start, resource_size(res)); } - platform_set_drvdata(pdev, NULL); kfree(vg); return 0; } diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 9ae7aa8ca48a..792a05ad4649 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -1,7 +1,7 @@ /* - * Xilinx gpio driver + * Xilinx gpio driver for xps/axi_gpio IP. * - * Copyright 2008 Xilinx, Inc. + * Copyright 2008 - 2013 Xilinx, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -12,6 +12,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/bitops.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/module.h> @@ -26,11 +27,31 @@ #define XGPIO_DATA_OFFSET (0x0) /* Data register */ #define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */ +#define XGPIO_CHANNEL_OFFSET 0x8 + +/* Read/Write access to the GPIO registers */ +#ifdef CONFIG_ARCH_ZYNQ +# define xgpio_readreg(offset) readl(offset) +# define xgpio_writereg(offset, val) writel(val, offset) +#else +# define xgpio_readreg(offset) __raw_readl(offset) +# define xgpio_writereg(offset, val) __raw_writel(val, offset) +#endif + +/** + * struct xgpio_instance - Stores information about GPIO device + * struct of_mm_gpio_chip mmchip: OF GPIO chip for memory mapped banks + * gpio_state: GPIO state shadow register + * gpio_dir: GPIO direction shadow register + * offset: GPIO channel offset + * gpio_lock: Lock used for synchronization + */ struct xgpio_instance { struct of_mm_gpio_chip mmchip; - u32 gpio_state; /* GPIO state shadow register */ - u32 gpio_dir; /* GPIO direction shadow register */ - spinlock_t gpio_lock; /* Lock used for synchronization */ + u32 gpio_state; + u32 gpio_dir; + u32 offset; + spinlock_t gpio_lock; }; /** @@ -44,8 +65,12 @@ struct xgpio_instance { static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) { struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); - return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1; + void __iomem *regs = mm_gc->regs + chip->offset; + + return !!(xgpio_readreg(regs + XGPIO_DATA_OFFSET) & BIT(gpio)); } /** @@ -63,15 +88,18 @@ static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct xgpio_instance *chip = container_of(mm_gc, struct xgpio_instance, mmchip); + void __iomem *regs = mm_gc->regs; spin_lock_irqsave(&chip->gpio_lock, flags); /* Write to GPIO signal and set its direction to output */ if (val) - chip->gpio_state |= 1 << gpio; + chip->gpio_state |= BIT(gpio); else - chip->gpio_state &= ~(1 << gpio); - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + chip->gpio_state &= ~BIT(gpio); + + xgpio_writereg(regs + chip->offset + XGPIO_DATA_OFFSET, + chip->gpio_state); spin_unlock_irqrestore(&chip->gpio_lock, flags); } @@ -91,12 +119,13 @@ static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct xgpio_instance *chip = container_of(mm_gc, struct xgpio_instance, mmchip); + void __iomem *regs = mm_gc->regs; spin_lock_irqsave(&chip->gpio_lock, flags); /* Set the GPIO bit in shadow register and set direction as input */ - chip->gpio_dir |= (1 << gpio); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + chip->gpio_dir |= BIT(gpio); + xgpio_writereg(regs + chip->offset + XGPIO_TRI_OFFSET, chip->gpio_dir); spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -119,19 +148,21 @@ static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); struct xgpio_instance *chip = container_of(mm_gc, struct xgpio_instance, mmchip); + void __iomem *regs = mm_gc->regs; spin_lock_irqsave(&chip->gpio_lock, flags); /* Write state of GPIO signal */ if (val) - chip->gpio_state |= 1 << gpio; + chip->gpio_state |= BIT(gpio); else - chip->gpio_state &= ~(1 << gpio); - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + chip->gpio_state &= ~BIT(gpio); + xgpio_writereg(regs + chip->offset + XGPIO_DATA_OFFSET, + chip->gpio_state); /* Clear the GPIO bit in shadow register and set direction as output */ - chip->gpio_dir &= (~(1 << gpio)); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + chip->gpio_dir &= ~BIT(gpio); + xgpio_writereg(regs + chip->offset + XGPIO_TRI_OFFSET, chip->gpio_dir); spin_unlock_irqrestore(&chip->gpio_lock, flags); @@ -147,8 +178,10 @@ static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) struct xgpio_instance *chip = container_of(mm_gc, struct xgpio_instance, mmchip); - out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); - out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + xgpio_writereg(mm_gc->regs + chip->offset + XGPIO_DATA_OFFSET, + chip->gpio_state); + xgpio_writereg(mm_gc->regs + chip->offset + XGPIO_TRI_OFFSET, + chip->gpio_dir); } /** @@ -170,24 +203,20 @@ static int xgpio_of_probe(struct device_node *np) return -ENOMEM; /* Update GPIO state shadow register with default value */ - tree_info = of_get_property(np, "xlnx,dout-default", NULL); - if (tree_info) - chip->gpio_state = be32_to_cpup(tree_info); + of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state); + + /* By default, all pins are inputs */ + chip->gpio_dir = 0xFFFFFFFF; /* Update GPIO direction shadow register with default value */ - chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ - tree_info = of_get_property(np, "xlnx,tri-default", NULL); - if (tree_info) - chip->gpio_dir = be32_to_cpup(tree_info); + of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir); + + /* By default assume full GPIO controller */ + chip->mmchip.gc.ngpio = 32; /* Check device node and parent device node for device width */ - chip->mmchip.gc.ngpio = 32; /* By default assume full GPIO controller */ - tree_info = of_get_property(np, "xlnx,gpio-width", NULL); - if (!tree_info) - tree_info = of_get_property(np->parent, - "xlnx,gpio-width", NULL); - if (tree_info) - chip->mmchip.gc.ngpio = be32_to_cpup(tree_info); + of_property_read_u32(np, "xlnx,gpio-width", + (u32 *)&chip->mmchip.gc.ngpio); spin_lock_init(&chip->gpio_lock); @@ -206,6 +235,57 @@ static int xgpio_of_probe(struct device_node *np) np->full_name, status); return status; } + + pr_info("XGpio: %s: registered, base is %d\n", np->full_name, + chip->mmchip.gc.base); + + tree_info = of_get_property(np, "xlnx,is-dual", NULL); + if (tree_info && be32_to_cpup(tree_info)) { + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + /* Add dual channel offset */ + chip->offset = XGPIO_CHANNEL_OFFSET; + + /* Update GPIO state shadow register with default value */ + of_property_read_u32(np, "xlnx,dout-default-2", + &chip->gpio_state); + + /* By default, all pins are inputs */ + chip->gpio_dir = 0xFFFFFFFF; + + /* Update GPIO direction shadow register with default value */ + of_property_read_u32(np, "xlnx,tri-default-2", &chip->gpio_dir); + + /* By default assume full GPIO controller */ + chip->mmchip.gc.ngpio = 32; + + /* Check device node and parent device node for device width */ + of_property_read_u32(np, "xlnx,gpio2-width", + (u32 *)&chip->mmchip.gc.ngpio); + + spin_lock_init(&chip->gpio_lock); + + chip->mmchip.gc.direction_input = xgpio_dir_in; + chip->mmchip.gc.direction_output = xgpio_dir_out; + chip->mmchip.gc.get = xgpio_get; + chip->mmchip.gc.set = xgpio_set; + + chip->mmchip.save_regs = xgpio_save_regs; + + /* Call the OF gpio helper to setup and register the GPIO dev */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { + kfree(chip); + pr_err("%s: error in probe function with status %d\n", + np->full_name, status); + return status; + } + pr_info("XGpio: %s: dual channel registered, base is %d\n", + np->full_name, chip->mmchip.gc.base); + } + return 0; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c2534d62911c..ff0fd655729f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1214,15 +1214,14 @@ int gpiochip_add(struct gpio_chip *chip) } } + spin_unlock_irqrestore(&gpio_lock, flags); + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&chip->pin_ranges); #endif of_gpiochip_add(chip); -unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - if (status) goto fail; @@ -1235,6 +1234,9 @@ unlock: chip->label ? : "generic"); return 0; + +unlock: + spin_unlock_irqrestore(&gpio_lock, flags); fail: /* failures here can mean systems won't boot... */ pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n", diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 73e2e7db2b64..527503617ee2 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -150,6 +150,7 @@ config I2C_PIIX4 ATI SB700/SP5100 ATI SB800 AMD Hudson-2 + AMD CZ Serverworks OSB4 Serverworks CSB5 Serverworks CSB6 diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 650293ff4d62..c7e3b0c1a1ca 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -148,10 +148,6 @@ struct i2c_nmk_client { * @stop: stop condition. * @xfer_complete: acknowledge completion for a I2C message. * @result: controller propogated result. - * @pinctrl: pinctrl handle. - * @pins_default: default state for the pins. - * @pins_idle: idle state for the pins. - * @pins_sleep: sleep state for the pins. * @busy: Busy doing transfer. */ struct nmk_i2c_dev { @@ -165,11 +161,6 @@ struct nmk_i2c_dev { int stop; struct completion xfer_complete; int result; - /* Three pin states - default, idle & sleep */ - struct pinctrl *pinctrl; - struct pinctrl_state *pins_default; - struct pinctrl_state *pins_idle; - struct pinctrl_state *pins_sleep; bool busy; }; @@ -645,13 +636,7 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap, } /* Optionaly enable pins to be muxed in and configured */ - if (!IS_ERR(dev->pins_default)) { - status = pinctrl_select_state(dev->pinctrl, - dev->pins_default); - if (status) - dev_err(&dev->adev->dev, - "could not set default pins\n"); - } + pinctrl_pm_select_default_state(&dev->adev->dev); status = init_hw(dev); if (status) @@ -681,13 +666,7 @@ out: clk_disable_unprepare(dev->clk); out_clk: /* Optionally let pins go into idle state */ - if (!IS_ERR(dev->pins_idle)) { - status = pinctrl_select_state(dev->pinctrl, - dev->pins_idle); - if (status) - dev_err(&dev->adev->dev, - "could not set pins to idle state\n"); - } + pinctrl_pm_select_idle_state(&dev->adev->dev); pm_runtime_put_sync(&dev->adev->dev); @@ -882,41 +861,22 @@ static int nmk_i2c_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev); - int ret; if (nmk_i2c->busy) return -EBUSY; - if (!IS_ERR(nmk_i2c->pins_sleep)) { - ret = pinctrl_select_state(nmk_i2c->pinctrl, - nmk_i2c->pins_sleep); - if (ret) - dev_err(dev, "could not set pins to sleep state\n"); - } + pinctrl_pm_select_sleep_state(dev); return 0; } static int nmk_i2c_resume(struct device *dev) { - struct amba_device *adev = to_amba_device(dev); - struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev); - int ret; - /* First go to the default state */ - if (!IS_ERR(nmk_i2c->pins_default)) { - ret = pinctrl_select_state(nmk_i2c->pinctrl, - nmk_i2c->pins_default); - if (ret) - dev_err(dev, "could not set pins to default state\n"); - } + pinctrl_pm_select_default_state(dev); /* Then let's idle the pins until the next transfer happens */ - if (!IS_ERR(nmk_i2c->pins_idle)) { - ret = pinctrl_select_state(nmk_i2c->pinctrl, - nmk_i2c->pins_idle); - if (ret) - dev_err(dev, "could not set pins to idle state\n"); - } + pinctrl_pm_select_idle_state(dev); + return 0; } #else @@ -1004,39 +964,10 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) dev->adev = adev; amba_set_drvdata(adev, dev); - dev->pinctrl = devm_pinctrl_get(&adev->dev); - if (IS_ERR(dev->pinctrl)) { - ret = PTR_ERR(dev->pinctrl); - goto err_pinctrl; - } - - dev->pins_default = pinctrl_lookup_state(dev->pinctrl, - PINCTRL_STATE_DEFAULT); - if (IS_ERR(dev->pins_default)) { - dev_err(&adev->dev, "could not get default pinstate\n"); - } else { - ret = pinctrl_select_state(dev->pinctrl, - dev->pins_default); - if (ret) - dev_dbg(&adev->dev, "could not set default pinstate\n"); - } - - dev->pins_idle = pinctrl_lookup_state(dev->pinctrl, - PINCTRL_STATE_IDLE); - if (IS_ERR(dev->pins_idle)) { - dev_dbg(&adev->dev, "could not get idle pinstate\n"); - } else { - /* If possible, let's go to idle until the first transfer */ - ret = pinctrl_select_state(dev->pinctrl, - dev->pins_idle); - if (ret) - dev_dbg(&adev->dev, "could not set idle pinstate\n"); - } - - dev->pins_sleep = pinctrl_lookup_state(dev->pinctrl, - PINCTRL_STATE_SLEEP); - if (IS_ERR(dev->pins_sleep)) - dev_dbg(&adev->dev, "could not get sleep pinstate\n"); + /* Select default pin state */ + pinctrl_pm_select_default_state(&adev->dev); + /* If possible, let's go to idle until the first transfer */ + pinctrl_pm_select_idle_state(&adev->dev); dev->virtbase = ioremap(adev->res.start, resource_size(&adev->res)); if (!dev->virtbase) { @@ -1106,7 +1037,6 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) iounmap(dev->virtbase); err_no_ioremap: kfree(dev); - err_pinctrl: err_no_mem: return ret; diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 39ab78c1a02c..d05ad590af29 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -22,7 +22,7 @@ Intel PIIX4, 440MX Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800 - AMD Hudson-2 + AMD Hudson-2, CZ SMSC Victory66 Note: we assume there can only be one device, with one or more @@ -522,6 +522,7 @@ static DEFINE_PCI_DEVICE_TABLE(piix4_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x790b) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 48e31ed69dbf..f32ca293ae0e 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -435,7 +435,7 @@ static const struct dev_pm_ops i2c_device_pm_ops = { SET_RUNTIME_PM_OPS( pm_generic_runtime_suspend, pm_generic_runtime_resume, - pm_generic_runtime_idle + NULL ) }; diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index 49557f27bfa6..7e8b0a52af25 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -206,33 +206,6 @@ static int cros_ec_keyb_work(struct notifier_block *nb, return NOTIFY_DONE; } -/* Clear any keys in the buffer */ -static void cros_ec_keyb_clear_keyboard(struct cros_ec_keyb *ckdev) -{ - uint8_t old_state[ckdev->cols]; - uint8_t new_state[ckdev->cols]; - unsigned long duration; - int i, ret; - - /* - * Keep reading until we see that the scan state does not change. - * That indicates that we are done. - * - * Assume that the EC keyscan buffer is at most 32 deep. - */ - duration = jiffies; - ret = cros_ec_keyb_get_state(ckdev, new_state); - for (i = 1; !ret && i < 32; i++) { - memcpy(old_state, new_state, sizeof(old_state)); - ret = cros_ec_keyb_get_state(ckdev, new_state); - if (0 == memcmp(old_state, new_state, sizeof(old_state))) - break; - } - duration = jiffies - duration; - dev_info(ckdev->dev, "Discarded %d keyscan(s) in %dus\n", i, - jiffies_to_usecs(duration)); -} - static int cros_ec_keyb_probe(struct platform_device *pdev) { struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); @@ -299,6 +272,33 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP +/* Clear any keys in the buffer */ +static void cros_ec_keyb_clear_keyboard(struct cros_ec_keyb *ckdev) +{ + uint8_t old_state[ckdev->cols]; + uint8_t new_state[ckdev->cols]; + unsigned long duration; + int i, ret; + + /* + * Keep reading until we see that the scan state does not change. + * That indicates that we are done. + * + * Assume that the EC keyscan buffer is at most 32 deep. + */ + duration = jiffies; + ret = cros_ec_keyb_get_state(ckdev, new_state); + for (i = 1; !ret && i < 32; i++) { + memcpy(old_state, new_state, sizeof(old_state)); + ret = cros_ec_keyb_get_state(ckdev, new_state); + if (0 == memcmp(old_state, new_state, sizeof(old_state))) + break; + } + duration = jiffies - duration; + dev_info(ckdev->dev, "Discarded %d keyscan(s) in %dus\n", i, + jiffies_to_usecs(duration)); +} + static int cros_ec_keyb_resume(struct device *dev) { struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index dcfea4e39be7..39f81aeefcd6 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -51,26 +51,27 @@ static void irq_remapping_disable_io_apic(void) static int do_setup_msi_irqs(struct pci_dev *dev, int nvec) { - int node, ret, sub_handle, index = 0; + int node, ret, sub_handle, nvec_pow2, index = 0; unsigned int irq; struct msi_desc *msidesc; - nvec = __roundup_pow_of_two(nvec); - WARN_ON(!list_is_singular(&dev->msi_list)); msidesc = list_entry(dev->msi_list.next, struct msi_desc, list); WARN_ON(msidesc->irq); WARN_ON(msidesc->msi_attrib.multiple); + WARN_ON(msidesc->nvec_used); node = dev_to_node(&dev->dev); irq = __create_irqs(get_nr_irqs_gsi(), nvec, node); if (irq == 0) return -ENOSPC; - msidesc->msi_attrib.multiple = ilog2(nvec); + nvec_pow2 = __roundup_pow_of_two(nvec); + msidesc->nvec_used = nvec; + msidesc->msi_attrib.multiple = ilog2(nvec_pow2); for (sub_handle = 0; sub_handle < nvec; sub_handle++) { if (!sub_handle) { - index = msi_alloc_remapped_irq(dev, irq, nvec); + index = msi_alloc_remapped_irq(dev, irq, nvec_pow2); if (index < 0) { ret = index; goto error; @@ -95,6 +96,7 @@ error: * IRQs from tearing down again in default_teardown_msi_irqs() */ msidesc->irq = 0; + msidesc->nvec_used = 0; msidesc->msi_attrib.multiple = 0; return ret; diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index bce695d42f10..25eaf61b98b4 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -220,7 +220,7 @@ if V4L_TEST_DRIVERS config VIDEO_VIVI tristate "Virtual Video Driver" depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 - depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE + select FONT_SUPPORT select FONT_8x16 select VIDEOBUF2_VMALLOC default n diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 13f7866de46e..3598b0ecf8c7 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -886,12 +886,6 @@ static int ab8500_gpadc_runtime_resume(struct device *dev) return ret; } -static int ab8500_gpadc_runtime_idle(struct device *dev) -{ - pm_runtime_suspend(dev); - return 0; -} - static int ab8500_gpadc_suspend(struct device *dev) { struct ab8500_gpadc *gpadc = dev_get_drvdata(dev); @@ -1039,7 +1033,7 @@ static int ab8500_gpadc_remove(struct platform_device *pdev) static const struct dev_pm_ops ab8500_gpadc_pm_ops = { SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend, ab8500_gpadc_runtime_resume, - ab8500_gpadc_runtime_idle) + NULL) SET_SYSTEM_SLEEP_PM_OPS(ab8500_gpadc_suspend, ab8500_gpadc_resume) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 66f80973596b..3c157faee645 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -480,6 +480,7 @@ struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true), CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true), CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true), + CLK_MGT_ENTRY(BML8580CLK, PLL_DIV, true), CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true), CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true), CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true), @@ -1724,9 +1725,9 @@ static long round_clock_rate(u8 clock, unsigned long rate) /* CPU FREQ table, may be changed due to if MAX_OPP is supported. */ static struct cpufreq_frequency_table db8500_cpufreq_table[] = { - { .frequency = 200000, .index = ARM_EXTCLK,}, - { .frequency = 400000, .index = ARM_50_OPP,}, - { .frequency = 800000, .index = ARM_100_OPP,}, + { .frequency = 200000, .driver_data = ARM_EXTCLK,}, + { .frequency = 400000, .driver_data = ARM_50_OPP,}, + { .frequency = 800000, .driver_data = ARM_100_OPP,}, { .frequency = CPUFREQ_TABLE_END,}, /* To be used for MAX_OPP. */ { .frequency = CPUFREQ_TABLE_END,}, }; @@ -1901,7 +1902,7 @@ static int set_armss_rate(unsigned long rate) return -EINVAL; /* Set the new arm opp. */ - return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].index); + return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].driver_data); } static int set_plldsi_rate(unsigned long rate) @@ -3105,7 +3106,7 @@ static void db8500_prcmu_update_cpufreq(void) { if (prcmu_has_arm_maxopp()) { db8500_cpufreq_table[3].frequency = 1000000; - db8500_cpufreq_table[3].index = ARM_MAX_OPP; + db8500_cpufreq_table[3].driver_data = ARM_MAX_OPP; } } diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index d14836ed2114..ca355dd423a6 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -32,6 +32,7 @@ #define PRCM_PER7CLK_MGT (0x040) #define PRCM_LCDCLK_MGT (0x044) #define PRCM_BMLCLK_MGT (0x04C) +#define PRCM_BML8580CLK_MGT (0x108) #define PRCM_HSITXCLK_MGT (0x050) #define PRCM_HSIRXCLK_MGT (0x054) #define PRCM_HDMICLK_MGT (0x058) diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 59e0ee247e86..0c1fcbc23d04 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -145,7 +145,6 @@ static struct spi_board_info timberdale_spi_8bit_board_info[] = { static struct xspi_platform_data timberdale_xspi_platform_data = { .num_chipselect = 3, - .little_endian = true, /* bits per word and devices will be filled in runtime depending * on the HW config */ diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index e219c97a02a4..9d5c71125576 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -164,7 +164,7 @@ static int mmc_runtime_resume(struct device *dev) static int mmc_runtime_idle(struct device *dev) { - return pm_runtime_suspend(dev); + return 0; } #endif /* !CONFIG_PM_RUNTIME */ diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 546c67c2bbbf..6d67492a9247 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -211,7 +211,7 @@ static const struct dev_pm_ops sdio_bus_pm_ops = { SET_RUNTIME_PM_OPS( pm_generic_runtime_suspend, pm_generic_runtime_resume, - pm_generic_runtime_idle + NULL ) }; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index d1a769f35f9d..da4415d9dee6 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -35,6 +35,7 @@ #include <linux/if_vlan.h> #include <linux/platform_data/cpsw.h> +#include <linux/pinctrl/consumer.h> #include "cpsw_ale.h" #include "cpts.h" @@ -1689,6 +1690,9 @@ static int cpsw_probe(struct platform_device *pdev) */ pm_runtime_enable(&pdev->dev); + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + if (cpsw_probe_dt(&priv->data, pdev)) { pr_err("cpsw: platform data missing\n"); ret = -ENODEV; @@ -1981,6 +1985,9 @@ static int cpsw_suspend(struct device *dev) soft_reset("sliver 1", &priv->slaves[1].sliver->soft_reset); pm_runtime_put_sync(&pdev->dev); + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(&pdev->dev); + return 0; } @@ -1990,6 +1997,10 @@ static int cpsw_resume(struct device *dev) struct net_device *ndev = platform_get_drvdata(pdev); pm_runtime_get_sync(&pdev->dev); + + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + if (netif_running(ndev)) cpsw_ndo_open(ndev); return 0; diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index c47f0dbcebb5..ce7c4991e41c 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -38,6 +38,7 @@ #include <linux/davinci_emac.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> /* * This timeout definition is a worst-case ultra defensive measure against @@ -347,6 +348,9 @@ static int davinci_mdio_probe(struct platform_device *pdev) data->bus->parent = dev; data->bus->priv = data; + /* Select default pin state */ + pinctrl_pm_select_default_state(&pdev->dev); + pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); data->clk = clk_get(&pdev->dev, "fck"); @@ -453,6 +457,9 @@ static int davinci_mdio_suspend(struct device *dev) spin_unlock(&data->lock); pm_runtime_put_sync(data->dev); + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -460,6 +467,9 @@ static int davinci_mdio_resume(struct device *dev) { struct davinci_mdio_data *data = dev_get_drvdata(dev); + /* Select default pin state */ + pinctrl_pm_select_default_state(dev); + pm_runtime_get_sync(data->dev); spin_lock(&data->lock); diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig index a50576081b34..dc82ef096f3b 100644 --- a/drivers/parport/Kconfig +++ b/drivers/parport/Kconfig @@ -36,7 +36,9 @@ if PARPORT config PARPORT_PC tristate "PC-style hardware" depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && !S390 && \ - (!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN && !XTENSA + (!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN && \ + !XTENSA && !CRIS + ---help--- You should say Y here if you have a PC-style parallel port. All IBM PC compatible computers and some Alphas have PC-style diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 32e66a6f12d9..b1ff02ab4f13 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -283,6 +283,21 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), } EXPORT_SYMBOL_GPL(pci_walk_bus); +struct pci_bus *pci_bus_get(struct pci_bus *bus) +{ + if (bus) + get_device(&bus->dev); + return bus; +} +EXPORT_SYMBOL(pci_bus_get); + +void pci_bus_put(struct pci_bus *bus) +{ + if (bus) + put_device(&bus->dev); +} +EXPORT_SYMBOL(pci_bus_put); + EXPORT_SYMBOL(pci_bus_alloc_resource); EXPORT_SYMBOL_GPL(pci_bus_add_device); EXPORT_SYMBOL(pci_bus_add_devices); diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 46a7b738f61f..ea3fa90d020a 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -41,6 +41,28 @@ struct slot { struct zpci_dev *zdev; }; +static inline int slot_configure(struct slot *slot) +{ + int ret = sclp_pci_configure(slot->zdev->fid); + + zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, ret); + if (!ret) + slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; + + return ret; +} + +static inline int slot_deconfigure(struct slot *slot) +{ + int ret = sclp_pci_deconfigure(slot->zdev->fid); + + zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, ret); + if (!ret) + slot->zdev->state = ZPCI_FN_STATE_STANDBY; + + return ret; +} + static int enable_slot(struct hotplug_slot *hotplug_slot) { struct slot *slot = hotplug_slot->private; @@ -49,14 +71,23 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) return -EIO; - rc = sclp_pci_configure(slot->zdev->fid); - zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, rc); - if (!rc) { - slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; - /* automatically scan the device after is was configured */ - zpci_enable_device(slot->zdev); - zpci_scan_device(slot->zdev); - } + rc = slot_configure(slot); + if (rc) + return rc; + + rc = zpci_enable_device(slot->zdev); + if (rc) + goto out_deconfigure; + + slot->zdev->state = ZPCI_FN_STATE_ONLINE; + + pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN); + pci_bus_add_devices(slot->zdev->bus); + + return rc; + +out_deconfigure: + slot_deconfigure(slot); return rc; } @@ -68,17 +99,14 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) if (!zpci_fn_configured(slot->zdev->state)) return -EIO; + if (slot->zdev->pdev) + pci_stop_and_remove_bus_device(slot->zdev->pdev); + rc = zpci_disable_device(slot->zdev); if (rc) return rc; - /* TODO: we rely on the user to unbind/remove the device, is that plausible - * or do we need to trigger that here? - */ - rc = sclp_pci_deconfigure(slot->zdev->fid); - zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, rc); - if (!rc) - slot->zdev->state = ZPCI_FN_STATE_STANDBY; - return rc; + + return slot_deconfigure(slot); } static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) diff --git a/drivers/pci/ioapic.c b/drivers/pci/ioapic.c index 3c6bbdd059a4..1b90579b233a 100644 --- a/drivers/pci/ioapic.c +++ b/drivers/pci/ioapic.c @@ -113,17 +113,6 @@ static struct pci_driver ioapic_driver = { .remove = ioapic_remove, }; -static int __init ioapic_init(void) -{ - return pci_register_driver(&ioapic_driver); -} - -static void __exit ioapic_exit(void) -{ - pci_unregister_driver(&ioapic_driver); -} - -module_init(ioapic_init); -module_exit(ioapic_exit); +module_pci_driver(ioapic_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index c93071d428f5..de8ffacf9c9b 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -47,51 +47,43 @@ static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) return NULL; pci_bus_insert_busn_res(child, busnr, busnr); - bus->is_added = 1; return child; } -static void virtfn_remove_bus(struct pci_bus *bus, int busnr) +static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) { - struct pci_bus *child; - - if (bus->number == busnr) - return; - - child = pci_find_bus(pci_domain_nr(bus), busnr); - BUG_ON(!child); - - if (list_empty(&child->devices)) - pci_remove_bus(child); + if (physbus != virtbus && list_empty(&virtbus->devices)) + pci_remove_bus(virtbus); } static int virtfn_add(struct pci_dev *dev, int id, int reset) { int i; - int rc; + int rc = -ENOMEM; u64 size; char buf[VIRTFN_ID_LEN]; struct pci_dev *virtfn; struct resource *res; struct pci_sriov *iov = dev->sriov; + struct pci_bus *bus; - virtfn = alloc_pci_dev(); + mutex_lock(&iov->dev->sriov->lock); + bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); + if (!bus) + goto failed; + + virtfn = pci_alloc_dev(bus); if (!virtfn) - return -ENOMEM; + goto failed0; - mutex_lock(&iov->dev->sriov->lock); - virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); - if (!virtfn->bus) { - kfree(virtfn); - mutex_unlock(&iov->dev->sriov->lock); - return -ENOMEM; - } virtfn->devfn = virtfn_devfn(dev, id); virtfn->vendor = dev->vendor; pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); pci_setup_device(virtfn); virtfn->dev.parent = dev->dev.parent; + virtfn->physfn = pci_dev_get(dev); + virtfn->is_virtfn = 1; for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { res = dev->resource + PCI_IOV_RESOURCES + i; @@ -113,9 +105,6 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) pci_device_add(virtfn, virtfn->bus); mutex_unlock(&iov->dev->sriov->lock); - virtfn->physfn = pci_dev_get(dev); - virtfn->is_virtfn = 1; - rc = pci_bus_add_device(virtfn); sprintf(buf, "virtfn%u", id); rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf); @@ -135,7 +124,9 @@ failed1: pci_dev_put(dev); mutex_lock(&iov->dev->sriov->lock); pci_stop_and_remove_bus_device(virtfn); - virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); +failed0: + virtfn_remove_bus(dev->bus, bus); +failed: mutex_unlock(&iov->dev->sriov->lock); return rc; @@ -144,20 +135,15 @@ failed1: static void virtfn_remove(struct pci_dev *dev, int id, int reset) { char buf[VIRTFN_ID_LEN]; - struct pci_bus *bus; struct pci_dev *virtfn; struct pci_sriov *iov = dev->sriov; - bus = pci_find_bus(pci_domain_nr(dev->bus), virtfn_bus(dev, id)); - if (!bus) - return; - - virtfn = pci_get_slot(bus, virtfn_devfn(dev, id)); + virtfn = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), + virtfn_bus(dev, id), + virtfn_devfn(dev, id)); if (!virtfn) return; - pci_dev_put(virtfn); - if (reset) { device_release_driver(&virtfn->dev); __pci_reset_function(virtfn); @@ -175,9 +161,11 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) mutex_lock(&iov->dev->sriov->lock); pci_stop_and_remove_bus_device(virtfn); - virtfn_remove_bus(dev->bus, virtfn_bus(dev, id)); + virtfn_remove_bus(dev->bus, virtfn->bus); mutex_unlock(&iov->dev->sriov->lock); + /* balance pci_get_domain_bus_and_slot() */ + pci_dev_put(virtfn); pci_dev_put(dev); } @@ -334,13 +322,14 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) if (!pdev) return -ENODEV; - pci_dev_put(pdev); - - if (!pdev->is_physfn) + if (!pdev->is_physfn) { + pci_dev_put(pdev); return -ENODEV; + } rc = sysfs_create_link(&dev->dev.kobj, &pdev->dev.kobj, "dep_link"); + pci_dev_put(pdev); if (rc) return rc; } diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 2c1075213bec..aca7578b05e5 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -81,7 +81,10 @@ void default_teardown_msi_irqs(struct pci_dev *dev) int i, nvec; if (entry->irq == 0) continue; - nvec = 1 << entry->msi_attrib.multiple; + if (entry->nvec_used) + nvec = entry->nvec_used; + else + nvec = 1 << entry->msi_attrib.multiple; for (i = 0; i < nvec; i++) arch_teardown_msi_irq(entry->irq + i); } @@ -336,7 +339,10 @@ static void free_msi_irqs(struct pci_dev *dev) int i, nvec; if (!entry->irq) continue; - nvec = 1 << entry->msi_attrib.multiple; + if (entry->nvec_used) + nvec = entry->nvec_used; + else + nvec = 1 << entry->msi_attrib.multiple; #ifdef CONFIG_GENERIC_HARDIRQS for (i = 0; i < nvec; i++) BUG_ON(irq_has_action(entry->irq + i)); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index e4b1fb2c0f5d..dbdc5f7e2b29 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -186,8 +186,8 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) [PCI_D0] = ACPI_STATE_D0, [PCI_D1] = ACPI_STATE_D1, [PCI_D2] = ACPI_STATE_D2, - [PCI_D3hot] = ACPI_STATE_D3, - [PCI_D3cold] = ACPI_STATE_D3 + [PCI_D3hot] = ACPI_STATE_D3_COLD, + [PCI_D3cold] = ACPI_STATE_D3_COLD, }; int error = -EINVAL; @@ -211,7 +211,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (!error) dev_info(&dev->dev, "power state changed by ACPI to %s\n", - pci_power_name(state)); + acpi_power_state_string(state_conv[state])); return error; } @@ -376,12 +376,12 @@ static int __init acpi_pci_init(void) int ret; if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) { - printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n"); + pr_info("ACPI FADT declares the system doesn't support MSI, so disable it\n"); pci_no_msi(); } if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { - printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); + pr_info("ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); pcie_no_aspm(); } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 79277fb36c6b..e6515e21afa3 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1050,26 +1050,22 @@ static int pci_pm_runtime_idle(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int ret = 0; /* * If pci_dev->driver is not set (unbound), the device should * always remain in D0 regardless of the runtime PM status */ if (!pci_dev->driver) - goto out; + return 0; if (!pm) return -ENOSYS; - if (pm->runtime_idle) { - int ret = pm->runtime_idle(dev); - if (ret) - return ret; - } + if (pm->runtime_idle) + ret = pm->runtime_idle(dev); -out: - pm_runtime_suspend(dev); - return 0; + return ret; } #else /* !CONFIG_PM_RUNTIME */ diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 5b4a9d9cd200..c0dbe1f61362 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -66,7 +66,7 @@ static ssize_t broken_parity_status_store(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); unsigned long val; - if (strict_strtoul(buf, 0, &val) < 0) + if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; pdev->broken_parity_status = !!val; @@ -188,7 +188,7 @@ static ssize_t is_enabled_store(struct device *dev, { struct pci_dev *pdev = to_pci_dev(dev); unsigned long val; - ssize_t result = strict_strtoul(buf, 0, &val); + ssize_t result = kstrtoul(buf, 0, &val); if (result < 0) return result; @@ -259,7 +259,7 @@ msi_bus_store(struct device *dev, struct device_attribute *attr, struct pci_dev *pdev = to_pci_dev(dev); unsigned long val; - if (strict_strtoul(buf, 0, &val) < 0) + if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; /* bad things may happen if the no_msi flag is changed @@ -291,7 +291,7 @@ static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf, unsigned long val; struct pci_bus *b = NULL; - if (strict_strtoul(buf, 0, &val) < 0) + if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; if (val) { @@ -315,7 +315,7 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr, unsigned long val; struct pci_dev *pdev = to_pci_dev(dev); - if (strict_strtoul(buf, 0, &val) < 0) + if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; if (val) { @@ -325,6 +325,8 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr, } return count; } +struct device_attribute dev_rescan_attr = __ATTR(rescan, (S_IWUSR|S_IWGRP), + NULL, dev_rescan_store); static void remove_callback(struct device *dev) { @@ -342,7 +344,7 @@ remove_store(struct device *dev, struct device_attribute *dummy, int ret = 0; unsigned long val; - if (strict_strtoul(buf, 0, &val) < 0) + if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; /* An attribute cannot be unregistered by one of its own methods, @@ -354,6 +356,8 @@ remove_store(struct device *dev, struct device_attribute *dummy, count = ret; return count; } +struct device_attribute dev_remove_attr = __ATTR(remove, (S_IWUSR|S_IWGRP), + NULL, remove_store); static ssize_t dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, @@ -362,7 +366,7 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, unsigned long val; struct pci_bus *bus = to_pci_bus(dev); - if (strict_strtoul(buf, 0, &val) < 0) + if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; if (val) { @@ -384,7 +388,7 @@ static ssize_t d3cold_allowed_store(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); unsigned long val; - if (strict_strtoul(buf, 0, &val) < 0) + if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; pdev->d3cold_allowed = !!val; @@ -504,8 +508,6 @@ struct device_attribute pci_dev_attrs[] = { __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), broken_parity_status_show,broken_parity_status_store), __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), - __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), - __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) __ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store), #endif @@ -1236,7 +1238,7 @@ static ssize_t reset_store(struct device *dev, { struct pci_dev *pdev = to_pci_dev(dev); unsigned long val; - ssize_t result = strict_strtoul(buf, 0, &val); + ssize_t result = kstrtoul(buf, 0, &val); if (result < 0) return result; @@ -1463,6 +1465,29 @@ static umode_t pci_dev_attrs_are_visible(struct kobject *kobj, return a->mode; } +static struct attribute *pci_dev_hp_attrs[] = { + &dev_remove_attr.attr, + &dev_rescan_attr.attr, + NULL, +}; + +static umode_t pci_dev_hp_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + if (pdev->is_virtfn) + return 0; + + return a->mode; +} + +static struct attribute_group pci_dev_hp_attr_group = { + .attrs = pci_dev_hp_attrs, + .is_visible = pci_dev_hp_attrs_are_visible, +}; + #ifdef CONFIG_PCI_IOV static struct attribute *sriov_dev_attrs[] = { &sriov_totalvfs_attr.attr, @@ -1494,6 +1519,7 @@ static struct attribute_group pci_dev_attr_group = { static const struct attribute_group *pci_dev_attr_groups[] = { &pci_dev_attr_group, + &pci_dev_hp_attr_group, #ifdef CONFIG_PCI_IOV &sriov_dev_attr_group, #endif diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a899d8bb190d..e37fea6e178d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -805,7 +805,7 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) { pci_power_t ret; - if (!pci_find_capability(dev, PCI_CAP_ID_PM)) + if (!dev->pm_cap) return PCI_D0; ret = platform_pci_choose_state(dev); @@ -1335,6 +1335,16 @@ int __weak pcibios_add_device (struct pci_dev *dev) } /** + * pcibios_release_device - provide arch specific hooks when releasing device dev + * @dev: the PCI device being released + * + * Permits the platform to provide architecture specific functionality when + * devices are released. This is the default implementation. Architecture + * implementations can override this. + */ +void __weak pcibios_release_device(struct pci_dev *dev) {} + +/** * pcibios_disable_device - disable arch specific PCI resources for device dev * @dev: the PCI device to disable * @@ -2421,7 +2431,7 @@ bool pci_acs_path_enabled(struct pci_dev *start, /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device - * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) + * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) * * Perform INTx swizzling for a device behind one level of bridge. This is * required by section 9.1 of the PCI-to-PCI bridge specification for devices diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index d12c77cd6991..90ea3e88041f 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -13,10 +13,6 @@ #include <linux/aer.h> #include <linux/interrupt.h> -#define AER_NONFATAL 0 -#define AER_FATAL 1 -#define AER_CORRECTABLE 2 - #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ PCI_EXP_RTCTL_SEFEE) diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 5194a7d41730..cf611ab2193a 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -29,6 +29,22 @@ static inline int hest_match_pci(struct acpi_hest_aer_common *p, p->function == PCI_FUNC(pci->devfn)); } +static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, + struct pci_dev *dev) +{ + u16 hest_type = hest_hdr->type; + u8 pcie_type = pci_pcie_type(dev); + + if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT && + pcie_type == PCI_EXP_TYPE_ROOT_PORT) || + (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT && + pcie_type == PCI_EXP_TYPE_ENDPOINT) || + (hest_type == ACPI_HEST_TYPE_AER_BRIDGE && + (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)) + return true; + return false; +} + struct aer_hest_parse_info { struct pci_dev *pci_dev; int firmware_first; @@ -38,34 +54,16 @@ static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) { struct aer_hest_parse_info *info = data; struct acpi_hest_aer_common *p; - u8 pcie_type = 0; - u8 bridge = 0; - int ff = 0; - - switch (hest_hdr->type) { - case ACPI_HEST_TYPE_AER_ROOT_PORT: - pcie_type = PCI_EXP_TYPE_ROOT_PORT; - break; - case ACPI_HEST_TYPE_AER_ENDPOINT: - pcie_type = PCI_EXP_TYPE_ENDPOINT; - break; - case ACPI_HEST_TYPE_AER_BRIDGE: - if ((info->pci_dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) - bridge = 1; - break; - default: - return 0; - } + int ff; p = (struct acpi_hest_aer_common *)(hest_hdr + 1); + ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); if (p->flags & ACPI_HEST_GLOBAL) { - if ((pci_is_pcie(info->pci_dev) && - pci_pcie_type(info->pci_dev) == pcie_type) || bridge) - ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); + if (hest_match_type(hest_hdr, info->pci_dev)) + info->firmware_first = ff; } else if (hest_match_pci(p, info->pci_dev)) - ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); - info->firmware_first = ff; + info->firmware_first = ff; return 0; } @@ -89,6 +87,9 @@ static void aer_set_firmware_first(struct pci_dev *pci_dev) int pcie_aer_get_firmware_first(struct pci_dev *dev) { + if (!pci_is_pcie(dev)) + return 0; + if (!dev->__aer_firmware_first_valid) aer_set_firmware_first(dev); return dev->__aer_firmware_first; diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 0f4554e48cc5..8b68ae59b7b6 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -400,16 +400,16 @@ void aer_do_secondary_bus_reset(struct pci_dev *dev) } /** - * default_downstream_reset_link - default reset function for Downstream Port - * @dev: pointer to downstream port's pci_dev data structure + * default_reset_link - default reset function + * @dev: pointer to pci_dev data structure * - * Invoked when performing link reset at Downstream Port w/ no aer driver. + * Invoked when performing link reset on a Downstream Port or a + * Root Port with no aer driver. */ -static pci_ers_result_t default_downstream_reset_link(struct pci_dev *dev) +static pci_ers_result_t default_reset_link(struct pci_dev *dev) { aer_do_secondary_bus_reset(dev); - dev_printk(KERN_DEBUG, &dev->dev, - "Downstream Port link has been reset\n"); + dev_printk(KERN_DEBUG, &dev->dev, "downstream link has been reset\n"); return PCI_ERS_RESULT_RECOVERED; } @@ -458,8 +458,9 @@ static pci_ers_result_t reset_link(struct pci_dev *dev) if (driver && driver->reset_link) { status = driver->reset_link(udev); - } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM) { - status = default_downstream_reset_link(udev); + } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM || + pci_pcie_type(udev) == PCI_EXP_TYPE_ROOT_PORT) { + status = default_reset_link(udev); } else { dev_printk(KERN_DEBUG, &dev->dev, "no link-reset support at upstream device %s\n", diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index d320df6375a2..403a44374ed5 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -714,19 +714,12 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev) up_read(&pci_bus_sem); } -/* - * pci_disable_link_state - disable pci device's link state, so the link will - * never enter specific states - */ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem, bool force) { struct pci_dev *parent = pdev->bus->self; struct pcie_link_state *link; - if (aspm_disabled && !force) - return; - if (!pci_is_pcie(pdev)) return; @@ -736,6 +729,19 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem, if (!parent || !parent->link_state) return; + /* + * A driver requested that ASPM be disabled on this device, but + * if we don't have permission to manage ASPM (e.g., on ACPI + * systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and + * the _OSC method), we can't honor that request. Windows has + * a similar mechanism using "PciASPMOptOut", which is also + * ignored in this situation. + */ + if (aspm_disabled && !force) { + dev_warn(&pdev->dev, "can't disable ASPM; OS doesn't have ASPM control\n"); + return; + } + if (sem) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); @@ -761,6 +767,15 @@ void pci_disable_link_state_locked(struct pci_dev *pdev, int state) } EXPORT_SYMBOL(pci_disable_link_state_locked); +/** + * pci_disable_link_state - Disable device's link state, so the link will + * never enter specific states. Note that if the BIOS didn't grant ASPM + * control to the OS, this does nothing because we can't touch the LNKCTL + * register. + * + * @pdev: PCI device + * @state: ASPM link state to disable + */ void pci_disable_link_state(struct pci_dev *pdev, int state) { __pci_disable_link_state(pdev, state, true, false); diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 795db1f9d50c..e56e594ce112 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -408,7 +408,7 @@ static int pcie_pme_resume(struct pcie_device *srv) /** * pcie_pme_remove - Prepare PCIe PME service device for removal. - * @srv - PCIe service device to resume. + * @srv - PCIe service device to remove. */ static void pcie_pme_remove(struct pcie_device *srv) { diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 70f10fa3c1b2..46ada5c098eb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -170,7 +170,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, { u32 l, sz, mask; u16 orig_cmd; - struct pci_bus_region region; + struct pci_bus_region region, inverted_region; bool bar_too_big = false, bar_disabled = false; mask = type ? PCI_ROM_ADDRESS_MASK : ~0; @@ -250,12 +250,10 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, pci_write_config_dword(dev, pos + 4, 0); region.start = 0; region.end = sz64; - pcibios_bus_to_resource(dev, res, ®ion); bar_disabled = true; } else { region.start = l64; region.end = l64 + sz64; - pcibios_bus_to_resource(dev, res, ®ion); } } else { sz = pci_size(l, sz, mask); @@ -265,7 +263,28 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, region.start = l; region.end = l + sz; - pcibios_bus_to_resource(dev, res, ®ion); + } + + pcibios_bus_to_resource(dev, res, ®ion); + pcibios_resource_to_bus(dev, &inverted_region, res); + + /* + * If "A" is a BAR value (a bus address), "bus_to_resource(A)" is + * the corresponding resource address (the physical address used by + * the CPU. Converting that resource address back to a bus address + * should yield the original BAR value: + * + * resource_to_bus(bus_to_resource(A)) == A + * + * If it doesn't, CPU accesses to "bus_to_resource(A)" will not + * be claimed by the device. + */ + if (inverted_region.start != region.start) { + dev_info(&dev->dev, "reg 0x%x: initial BAR value %pa invalid; forcing reassignment\n", + pos, ®ion.start); + res->flags |= IORESOURCE_UNSET; + res->end -= res->start; + res->start = 0; } goto out; @@ -278,9 +297,9 @@ out: pci_write_config_word(dev, PCI_COMMAND, orig_cmd); if (bar_too_big) - dev_err(&dev->dev, "reg %x: can't handle 64-bit BAR\n", pos); + dev_err(&dev->dev, "reg 0x%x: can't handle 64-bit BAR\n", pos); if (res->flags && !bar_disabled) - dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); + dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res); return (res->flags & IORESOURCE_MEM_64) ? 1 : 0; } @@ -451,33 +470,46 @@ void pci_read_bridge_bases(struct pci_bus *child) } } -static struct pci_bus * pci_alloc_bus(void) +static struct pci_bus *pci_alloc_bus(void) { struct pci_bus *b; b = kzalloc(sizeof(*b), GFP_KERNEL); - if (b) { - INIT_LIST_HEAD(&b->node); - INIT_LIST_HEAD(&b->children); - INIT_LIST_HEAD(&b->devices); - INIT_LIST_HEAD(&b->slots); - INIT_LIST_HEAD(&b->resources); - b->max_bus_speed = PCI_SPEED_UNKNOWN; - b->cur_bus_speed = PCI_SPEED_UNKNOWN; - } + if (!b) + return NULL; + + INIT_LIST_HEAD(&b->node); + INIT_LIST_HEAD(&b->children); + INIT_LIST_HEAD(&b->devices); + INIT_LIST_HEAD(&b->slots); + INIT_LIST_HEAD(&b->resources); + b->max_bus_speed = PCI_SPEED_UNKNOWN; + b->cur_bus_speed = PCI_SPEED_UNKNOWN; return b; } +static void pci_release_host_bridge_dev(struct device *dev) +{ + struct pci_host_bridge *bridge = to_pci_host_bridge(dev); + + if (bridge->release_fn) + bridge->release_fn(bridge); + + pci_free_resource_list(&bridge->windows); + + kfree(bridge); +} + static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b) { struct pci_host_bridge *bridge; bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); - if (bridge) { - INIT_LIST_HEAD(&bridge->windows); - bridge->bus = b; - } + if (!bridge) + return NULL; + INIT_LIST_HEAD(&bridge->windows); + bridge->bus = b; return bridge; } @@ -1132,6 +1164,8 @@ static void pci_release_dev(struct device *dev) pci_dev = to_pci_dev(dev); pci_release_capabilities(pci_dev); pci_release_of_node(pci_dev); + pcibios_release_device(pci_dev); + pci_bus_put(pci_dev->bus); kfree(pci_dev); } @@ -1188,19 +1222,7 @@ int pci_cfg_space_size(struct pci_dev *dev) return PCI_CFG_SPACE_SIZE; } -static void pci_release_bus_bridge_dev(struct device *dev) -{ - struct pci_host_bridge *bridge = to_pci_host_bridge(dev); - - if (bridge->release_fn) - bridge->release_fn(bridge); - - pci_free_resource_list(&bridge->windows); - - kfree(bridge); -} - -struct pci_dev *alloc_pci_dev(void) +struct pci_dev *pci_alloc_dev(struct pci_bus *bus) { struct pci_dev *dev; @@ -1210,9 +1232,16 @@ struct pci_dev *alloc_pci_dev(void) INIT_LIST_HEAD(&dev->bus_list); dev->dev.type = &pci_dev_type; + dev->bus = pci_bus_get(bus); return dev; } +EXPORT_SYMBOL(pci_alloc_dev); + +struct pci_dev *alloc_pci_dev(void) +{ + return pci_alloc_dev(NULL); +} EXPORT_SYMBOL(alloc_pci_dev); bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, @@ -1263,11 +1292,10 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) return NULL; - dev = alloc_pci_dev(); + dev = pci_alloc_dev(bus); if (!dev) return NULL; - dev->bus = bus; dev->devfn = devfn; dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; @@ -1275,6 +1303,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) pci_set_of_node(dev); if (pci_setup_device(dev)) { + pci_bus_put(dev->bus); kfree(dev); return NULL; } @@ -1700,15 +1729,19 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, goto err_out; bridge->dev.parent = parent; - bridge->dev.release = pci_release_bus_bridge_dev; + bridge->dev.release = pci_release_host_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); error = pcibios_root_bridge_prepare(bridge); - if (error) - goto bridge_dev_reg_err; + if (error) { + kfree(bridge); + goto err_out; + } error = device_register(&bridge->dev); - if (error) - goto bridge_dev_reg_err; + if (error) { + put_device(&bridge->dev); + goto err_out; + } b->bridge = get_device(&bridge->dev); device_enable_async_suspend(b->bridge); pci_set_bus_of_node(b); @@ -1764,8 +1797,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, class_dev_reg_err: put_device(&bridge->dev); device_unregister(&bridge->dev); -bridge_dev_reg_err: - kfree(bridge); err_out: kfree(b); return NULL; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 7d68aeebf56b..e85d23044ae0 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1022,6 +1022,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x7900, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, 0x7900, quirk_amd_ide_mode); /* * Serverworks CSB5 IDE does not fully support native mode @@ -1832,7 +1834,6 @@ static void quirk_e100_interrupt(struct pci_dev *dev) u16 command, pmcsr; u8 __iomem *csr; u8 cmd_hi; - int pm; switch (dev->device) { /* PCI IDs taken from drivers/net/e100.c */ @@ -1870,9 +1871,8 @@ static void quirk_e100_interrupt(struct pci_dev *dev) * Check that the device is in the D0 power state. If it's not, * there is no point to look any further. */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - if (pm) { - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + if (dev->pm_cap) { + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) return; } @@ -2865,6 +2865,31 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata); +/* + * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. To + * work around this, query the size it should be configured to by the device and + * modify the resource end to correspond to this new size. + */ +static void quirk_intel_ntb(struct pci_dev *dev) +{ + int rc; + u8 val; + + rc = pci_read_config_byte(dev, 0x00D0, &val); + if (rc) + return; + + dev->resource[2].end = dev->resource[2].start + ((u64) 1 << val) - 1; + + rc = pci_read_config_byte(dev, 0x00D1, &val); + if (rc) + return; + + dev->resource[4].end = dev->resource[4].start + ((u64) 1 << val) - 1; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); + static ktime_t fixup_debug_start(struct pci_dev *dev, void (*fn)(struct pci_dev *dev)) { diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 966abc6054d7..f7197a790341 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -678,10 +678,9 @@ static int pcifront_connect_and_init_dma(struct pcifront_device *pdev) if (!pcifront_dev) { dev_info(&pdev->xdev->dev, "Installing PCI frontend\n"); pcifront_dev = pdev; - } else { - dev_err(&pdev->xdev->dev, "PCI frontend already installed!\n"); + } else err = -EEXIST; - } + spin_unlock(&pcifront_dev_lock); if (!err && !swiotlb_nr_tbl()) { @@ -848,7 +847,7 @@ static int pcifront_try_connect(struct pcifront_device *pdev) goto out; err = pcifront_connect_and_init_dma(pdev); - if (err) { + if (err && err != -EEXIST) { xenbus_dev_fatal(pdev->xdev, err, "Error setting up PCI Frontend"); goto out; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 901a388dbea7..5a8ad5139312 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -58,6 +58,18 @@ config PINCTRL_AT91 help Say Y here to enable the at91 pinctrl driver +config PINCTRL_BAYTRAIL + bool "Intel Baytrail GPIO pin control" + depends on GPIOLIB && ACPI && X86 + select IRQ_DOMAIN + help + driver for memory mapped GPIO functionality on Intel Baytrail + platforms. Supports 3 banks with 102, 28 and 44 gpios. + Most pins are usually muxed to some other functionality by firmware, + so only a small amount is available for gpio use. + + Requires ACPI device enumeration code to set up a platform device. + config PINCTRL_BCM2835 bool select PINMUX @@ -108,6 +120,14 @@ config PINCTRL_IMX6SL help Say Y here to enable the imx6sl pinctrl driver +config PINCTRL_VF610 + bool "Freescale Vybrid VF610 pinctrl driver" + depends on OF + depends on SOC_VF610 + select PINCTRL_IMX + help + Say Y here to enable the Freescale Vybrid VF610 pinctrl driver + config PINCTRL_LANTIQ bool depends on LANTIQ @@ -150,6 +170,12 @@ config PINCTRL_DB8540 bool "DB8540 pin controller driver" depends on PINCTRL_NOMADIK && ARCH_U8500 +config PINCTRL_ROCKCHIP + bool + select PINMUX + select GENERIC_PINCONF + select GENERIC_IRQ_CHIP + config PINCTRL_SINGLE tristate "One-register-per-pin type device tree based pinctrl driver" depends on OF @@ -169,6 +195,12 @@ config PINCTRL_SUNXI select PINMUX select GENERIC_PINCONF +config PINCTRL_ST + bool + depends on OF + select PINMUX + select PINCONF + config PINCTRL_TEGRA bool select PINMUX @@ -186,6 +218,18 @@ config PINCTRL_TEGRA114 bool select PINCTRL_TEGRA +config PINCTRL_TZ1090 + bool "Toumaz Xenif TZ1090 pin control driver" + depends on SOC_TZ1090 + select PINMUX + select GENERIC_PINCONF + +config PINCTRL_TZ1090_PDC + bool "Toumaz Xenif TZ1090 PDC pin control driver" + depends on SOC_TZ1090 + select PINMUX + select PINCONF + config PINCTRL_U300 bool "U300 pin controller driver" depends on ARCH_U300 diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index f90b645fb601..d64563bf6fb4 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -16,12 +16,14 @@ obj-$(CONFIG_PINCTRL_AB9540) += pinctrl-ab9540.o obj-$(CONFIG_PINCTRL_AB8505) += pinctrl-ab8505.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o +obj-$(CONFIG_PINCTRL_BAYTRAIL) += pinctrl-baytrail.o obj-$(CONFIG_PINCTRL_IMX) += pinctrl-imx.o obj-$(CONFIG_PINCTRL_IMX35) += pinctrl-imx35.o obj-$(CONFIG_PINCTRL_IMX51) += pinctrl-imx51.o obj-$(CONFIG_PINCTRL_IMX53) += pinctrl-imx53.o obj-$(CONFIG_PINCTRL_IMX6Q) += pinctrl-imx6q.o obj-$(CONFIG_PINCTRL_IMX6Q) += pinctrl-imx6dl.o +obj-$(CONFIG_PINCTRL_IMX6SL) += pinctrl-imx6sl.o obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o obj-$(CONFIG_PINCTRL_IMX23) += pinctrl-imx23.o @@ -30,13 +32,16 @@ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o +obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o -obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o +obj-$(CONFIG_PINCTRL_SIRF) += sirf/ obj-$(CONFIG_PINCTRL_SUNXI) += pinctrl-sunxi.o obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o +obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o +obj-$(CONFIG_PINCTRL_TZ1090_PDC) += pinctrl-tz1090-pdc.o obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o @@ -46,6 +51,8 @@ obj-$(CONFIG_PINCTRL_S3C24XX) += pinctrl-s3c24xx.o obj-$(CONFIG_PINCTRL_S3C64XX) += pinctrl-s3c64xx.o obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o +obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o +obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o obj-$(CONFIG_PLAT_ORION) += mvebu/ obj-$(CONFIG_ARCH_SHMOBILE) += sh-pfc/ diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 5327f35d9b5c..5b272bfd261d 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -41,13 +41,13 @@ static bool pinctrl_dummy_state; /* Mutex taken to protect pinctrl_list */ -DEFINE_MUTEX(pinctrl_list_mutex); +static DEFINE_MUTEX(pinctrl_list_mutex); /* Mutex taken to protect pinctrl_maps */ DEFINE_MUTEX(pinctrl_maps_mutex); /* Mutex taken to protect pinctrldev_list */ -DEFINE_MUTEX(pinctrldev_list_mutex); +static DEFINE_MUTEX(pinctrldev_list_mutex); /* Global list of pin control devices (struct pinctrl_dev) */ static LIST_HEAD(pinctrldev_list); @@ -101,20 +101,23 @@ EXPORT_SYMBOL_GPL(pinctrl_dev_get_drvdata); struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname) { struct pinctrl_dev *pctldev = NULL; - bool found = false; if (!devname) return NULL; + mutex_lock(&pinctrldev_list_mutex); + list_for_each_entry(pctldev, &pinctrldev_list, node) { if (!strcmp(dev_name(pctldev->dev), devname)) { /* Matched on device name */ - found = true; - break; + mutex_unlock(&pinctrldev_list_mutex); + return pctldev; } } - return found ? pctldev : NULL; + mutex_unlock(&pinctrldev_list_mutex); + + return NULL; } struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np) @@ -280,6 +283,29 @@ static int pinctrl_register_pins(struct pinctrl_dev *pctldev, } /** + * gpio_to_pin() - GPIO range GPIO number to pin number translation + * @range: GPIO range used for the translation + * @gpio: gpio pin to translate to a pin number + * + * Finds the pin number for a given GPIO using the specified GPIO range + * as a base for translation. The distinction between linear GPIO ranges + * and pin list based GPIO ranges is managed correctly by this function. + * + * This function assumes the gpio is part of the specified GPIO range, use + * only after making sure this is the case (e.g. by calling it on the + * result of successful pinctrl_get_device_gpio_range calls)! + */ +static inline int gpio_to_pin(struct pinctrl_gpio_range *range, + unsigned int gpio) +{ + unsigned int offset = gpio - range->base; + if (range->pins) + return range->pins[offset]; + else + return range->pin_base + offset; +} + +/** * pinctrl_match_gpio_range() - check if a certain GPIO pin is in range * @pctldev: pin controller device to check * @gpio: gpio pin to check taken from the global GPIO pin space @@ -326,6 +352,8 @@ static bool pinctrl_ready_for_gpio_range(unsigned gpio) struct pinctrl_gpio_range *range = NULL; struct gpio_chip *chip = gpio_to_chip(gpio); + mutex_lock(&pinctrldev_list_mutex); + /* Loop over the pin controllers */ list_for_each_entry(pctldev, &pinctrldev_list, node) { /* Loop over the ranges */ @@ -334,9 +362,13 @@ static bool pinctrl_ready_for_gpio_range(unsigned gpio) if (range->base + range->npins - 1 < chip->base || range->base > chip->base + chip->ngpio - 1) continue; + mutex_unlock(&pinctrldev_list_mutex); return true; } } + + mutex_unlock(&pinctrldev_list_mutex); + return false; } #else @@ -408,8 +440,6 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, { struct pinctrl_dev *pctldev; - mutex_lock(&pinctrldev_list_mutex); - pctldev = get_pinctrl_dev_from_devname(devname); /* @@ -418,13 +448,10 @@ struct pinctrl_dev *pinctrl_find_and_add_gpio_range(const char *devname, * range need to defer probing. */ if (!pctldev) { - mutex_unlock(&pinctrldev_list_mutex); return ERR_PTR(-EPROBE_DEFER); } pinctrl_add_gpio_range(pctldev, range); - mutex_unlock(&pinctrldev_list_mutex); - return pctldev; } EXPORT_SYMBOL_GPL(pinctrl_find_and_add_gpio_range); @@ -438,21 +465,26 @@ struct pinctrl_gpio_range * pinctrl_find_gpio_range_from_pin(struct pinctrl_dev *pctldev, unsigned int pin) { - struct pinctrl_gpio_range *range = NULL; + struct pinctrl_gpio_range *range; mutex_lock(&pctldev->mutex); /* Loop over the ranges */ list_for_each_entry(range, &pctldev->gpio_ranges, node) { /* Check if we're in the valid range */ - if (pin >= range->pin_base && - pin < range->pin_base + range->npins) { - mutex_unlock(&pctldev->mutex); - return range; - } + if (range->pins) { + int a; + for (a = 0; a < range->npins; a++) { + if (range->pins[a] == pin) + goto out; + } + } else if (pin >= range->pin_base && + pin < range->pin_base + range->npins) + goto out; } + range = NULL; +out: mutex_unlock(&pctldev->mutex); - - return NULL; + return range; } EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin); @@ -517,22 +549,18 @@ int pinctrl_request_gpio(unsigned gpio) int ret; int pin; - mutex_lock(&pinctrldev_list_mutex); - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) { if (pinctrl_ready_for_gpio_range(gpio)) ret = 0; - mutex_unlock(&pinctrldev_list_mutex); return ret; } /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; + pin = gpio_to_pin(range, gpio); ret = pinmux_request_gpio(pctldev, range, pin, gpio); - mutex_unlock(&pinctrldev_list_mutex); return ret; } EXPORT_SYMBOL_GPL(pinctrl_request_gpio); @@ -552,22 +580,18 @@ void pinctrl_free_gpio(unsigned gpio) int ret; int pin; - mutex_lock(&pinctrldev_list_mutex); - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) { - mutex_unlock(&pinctrldev_list_mutex); return; } mutex_lock(&pctldev->mutex); /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; + pin = gpio_to_pin(range, gpio); pinmux_free_gpio(pctldev, pin, range); mutex_unlock(&pctldev->mutex); - mutex_unlock(&pinctrldev_list_mutex); } EXPORT_SYMBOL_GPL(pinctrl_free_gpio); @@ -578,22 +602,18 @@ static int pinctrl_gpio_direction(unsigned gpio, bool input) int ret; int pin; - mutex_lock(&pinctrldev_list_mutex); - ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); if (ret) { - mutex_unlock(&pinctrldev_list_mutex); return ret; } mutex_lock(&pctldev->mutex); /* Convert to the pin controllers number space */ - pin = gpio - range->base + range->pin_base; + pin = gpio_to_pin(range, gpio); ret = pinmux_gpio_direction(pctldev, range, pin, input); mutex_unlock(&pctldev->mutex); - mutex_unlock(&pinctrldev_list_mutex); return ret; } @@ -1204,6 +1224,69 @@ int pinctrl_force_default(struct pinctrl_dev *pctldev) } EXPORT_SYMBOL_GPL(pinctrl_force_default); +#ifdef CONFIG_PM + +/** + * pinctrl_pm_select_default_state() - select default pinctrl state for PM + * @dev: device to select default state for + */ +int pinctrl_pm_select_default_state(struct device *dev) +{ + struct dev_pin_info *pins = dev->pins; + int ret; + + if (!pins) + return 0; + if (IS_ERR(pins->default_state)) + return 0; /* No default state */ + ret = pinctrl_select_state(pins->p, pins->default_state); + if (ret) + dev_err(dev, "failed to activate default pinctrl state\n"); + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_pm_select_default_state); + +/** + * pinctrl_pm_select_sleep_state() - select sleep pinctrl state for PM + * @dev: device to select sleep state for + */ +int pinctrl_pm_select_sleep_state(struct device *dev) +{ + struct dev_pin_info *pins = dev->pins; + int ret; + + if (!pins) + return 0; + if (IS_ERR(pins->sleep_state)) + return 0; /* No sleep state */ + ret = pinctrl_select_state(pins->p, pins->sleep_state); + if (ret) + dev_err(dev, "failed to activate pinctrl sleep state\n"); + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_pm_select_sleep_state); + +/** + * pinctrl_pm_select_idle_state() - select idle pinctrl state for PM + * @dev: device to select idle state for + */ +int pinctrl_pm_select_idle_state(struct device *dev) +{ + struct dev_pin_info *pins = dev->pins; + int ret; + + if (!pins) + return 0; + if (IS_ERR(pins->idle_state)) + return 0; /* No idle state */ + ret = pinctrl_select_state(pins->p, pins->idle_state); + if (ret) + dev_err(dev, "failed to activate pinctrl idle state\n"); + return ret; +} +EXPORT_SYMBOL_GPL(pinctrl_pm_select_idle_state); +#endif + #ifdef CONFIG_DEBUG_FS static int pinctrl_pins_show(struct seq_file *s, void *what) @@ -1296,11 +1379,21 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what) /* Loop over the ranges */ list_for_each_entry(range, &pctldev->gpio_ranges, node) { - seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n", - range->id, range->name, - range->base, (range->base + range->npins - 1), - range->pin_base, - (range->pin_base + range->npins - 1)); + if (range->pins) { + int a; + seq_printf(s, "%u: %s GPIOS [%u - %u] PINS {", + range->id, range->name, + range->base, (range->base + range->npins - 1)); + for (a = 0; a < range->npins - 1; a++) + seq_printf(s, "%u, ", range->pins[a]); + seq_printf(s, "%u}\n", range->pins[a]); + } + else + seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n", + range->id, range->name, + range->base, (range->base + range->npins - 1), + range->pin_base, + (range->pin_base + range->npins - 1)); } mutex_unlock(&pctldev->mutex); diff --git a/drivers/pinctrl/mvebu/pinctrl-dove.c b/drivers/pinctrl/mvebu/pinctrl-dove.c index 428ea96a94d3..048ae80adabd 100644 --- a/drivers/pinctrl/mvebu/pinctrl-dove.c +++ b/drivers/pinctrl/mvebu/pinctrl-dove.c @@ -26,6 +26,9 @@ #define DOVE_MPP_VIRT_BASE (DOVE_SB_REGS_VIRT_BASE + 0xd0200) #define DOVE_PMU_MPP_GENERAL_CTRL (DOVE_MPP_VIRT_BASE + 0x10) #define DOVE_AU0_AC97_SEL BIT(16) +#define DOVE_PMU_SIGNAL_SELECT_0 (DOVE_SB_REGS_VIRT_BASE + 0xd802C) +#define DOVE_PMU_SIGNAL_SELECT_1 (DOVE_SB_REGS_VIRT_BASE + 0xd8030) +#define DOVE_GLOBAL_CONFIG_1 (DOVE_SB_REGS_VIRT_BASE + 0xe802C) #define DOVE_GLOBAL_CONFIG_1 (DOVE_SB_REGS_VIRT_BASE + 0xe802C) #define DOVE_TWSI_ENABLE_OPTION1 BIT(7) #define DOVE_GLOBAL_CONFIG_2 (DOVE_SB_REGS_VIRT_BASE + 0xe8030) @@ -58,12 +61,16 @@ static int dove_pmu_mpp_ctrl_get(struct mvebu_mpp_ctrl *ctrl, unsigned off = (ctrl->pid / MPPS_PER_REG) * MPP_BITS; unsigned shift = (ctrl->pid % MPPS_PER_REG) * MPP_BITS; unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL); - unsigned long mpp = readl(DOVE_MPP_VIRT_BASE + off); - - if (pmu & (1 << ctrl->pid)) - *config = CONFIG_PMU; - else - *config = (mpp >> shift) & MPP_MASK; + unsigned long func; + + if (pmu & (1 << ctrl->pid)) { + func = readl(DOVE_PMU_SIGNAL_SELECT_0 + off); + *config = (func >> shift) & MPP_MASK; + *config |= CONFIG_PMU; + } else { + func = readl(DOVE_MPP_VIRT_BASE + off); + *config = (func >> shift) & MPP_MASK; + } return 0; } @@ -73,15 +80,20 @@ static int dove_pmu_mpp_ctrl_set(struct mvebu_mpp_ctrl *ctrl, unsigned off = (ctrl->pid / MPPS_PER_REG) * MPP_BITS; unsigned shift = (ctrl->pid % MPPS_PER_REG) * MPP_BITS; unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL); - unsigned long mpp = readl(DOVE_MPP_VIRT_BASE + off); + unsigned long func; - if (config == CONFIG_PMU) + if (config & CONFIG_PMU) { writel(pmu | (1 << ctrl->pid), DOVE_PMU_MPP_GENERAL_CTRL); - else { + func = readl(DOVE_PMU_SIGNAL_SELECT_0 + off); + func &= ~(MPP_MASK << shift); + func |= (config & MPP_MASK) << shift; + writel(func, DOVE_PMU_SIGNAL_SELECT_0 + off); + } else { writel(pmu & ~(1 << ctrl->pid), DOVE_PMU_MPP_GENERAL_CTRL); - mpp &= ~(MPP_MASK << shift); - mpp |= config << shift; - writel(mpp, DOVE_MPP_VIRT_BASE + off); + func = readl(DOVE_MPP_VIRT_BASE + off); + func &= ~(MPP_MASK << shift); + func |= (config & MPP_MASK) << shift; + writel(func, DOVE_MPP_VIRT_BASE + off); } return 0; } @@ -378,20 +390,53 @@ static struct mvebu_mpp_mode dove_mpp_modes[] = { MPP_FUNCTION(0x02, "uart2", "rts"), MPP_FUNCTION(0x03, "sdio0", "cd"), MPP_FUNCTION(0x0f, "lcd0", "pwm"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "core-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(1, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart2", "cts"), MPP_FUNCTION(0x03, "sdio0", "wp"), MPP_FUNCTION(0x0f, "lcd1", "pwm"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "core-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(2, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x01, "sata", "prsnt"), MPP_FUNCTION(0x02, "uart2", "txd"), MPP_FUNCTION(0x03, "sdio0", "buspwr"), MPP_FUNCTION(0x04, "uart1", "rts"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "core-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(3, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x01, "sata", "act"), @@ -399,43 +444,131 @@ static struct mvebu_mpp_mode dove_mpp_modes[] = { MPP_FUNCTION(0x03, "sdio0", "ledctrl"), MPP_FUNCTION(0x04, "uart1", "cts"), MPP_FUNCTION(0x0f, "lcd-spi", "cs1"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "core-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(4, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart3", "rts"), MPP_FUNCTION(0x03, "sdio1", "cd"), MPP_FUNCTION(0x04, "spi1", "miso"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "core-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(5, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart3", "cts"), MPP_FUNCTION(0x03, "sdio1", "wp"), MPP_FUNCTION(0x04, "spi1", "cs"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "core-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(6, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart3", "txd"), MPP_FUNCTION(0x03, "sdio1", "buspwr"), MPP_FUNCTION(0x04, "spi1", "mosi"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "core-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(7, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart3", "rxd"), MPP_FUNCTION(0x03, "sdio1", "ledctrl"), MPP_FUNCTION(0x04, "spi1", "sck"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "core-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(8, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x01, "watchdog", "rstout"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "cpu-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(9, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x05, "pex1", "clkreq"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "cpu-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(10, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x05, "ssp", "sclk"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "cpu-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(11, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x01, "sata", "prsnt"), @@ -443,33 +576,88 @@ static struct mvebu_mpp_mode dove_mpp_modes[] = { MPP_FUNCTION(0x03, "sdio0", "ledctrl"), MPP_FUNCTION(0x04, "sdio1", "ledctrl"), MPP_FUNCTION(0x05, "pex0", "clkreq"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "cpu-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(12, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x01, "sata", "act"), MPP_FUNCTION(0x02, "uart2", "rts"), MPP_FUNCTION(0x03, "audio0", "extclk"), MPP_FUNCTION(0x04, "sdio1", "cd"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "cpu-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(13, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart2", "cts"), MPP_FUNCTION(0x03, "audio1", "extclk"), MPP_FUNCTION(0x04, "sdio1", "wp"), MPP_FUNCTION(0x05, "ssp", "extclk"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "cpu-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(14, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart2", "txd"), MPP_FUNCTION(0x04, "sdio1", "buspwr"), MPP_FUNCTION(0x05, "ssp", "rxd"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "cpu-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(15, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart2", "rxd"), MPP_FUNCTION(0x04, "sdio1", "ledctrl"), MPP_FUNCTION(0x05, "ssp", "sfrm"), - MPP_FUNCTION(0x10, "pmu", NULL)), + MPP_FUNCTION(CONFIG_PMU | 0x0, "pmu-nc", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x1, "pmu-low", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x2, "pmu-high", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x3, "pmic", "sdi"), + MPP_FUNCTION(CONFIG_PMU | 0x4, "cpu-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x5, "standby-pwr-down", NULL), + MPP_FUNCTION(CONFIG_PMU | 0x8, "cpu-pwr-good", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xa, "bat-fault", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xb, "ext0-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xc, "ext1-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xd, "ext2-wakeup", NULL), + MPP_FUNCTION(CONFIG_PMU | 0xe, "pmu-blink", NULL)), MPP_MODE(16, MPP_FUNCTION(0x00, "gpio", NULL), MPP_FUNCTION(0x02, "uart3", "rts"), diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 2ad5a8d337b5..8594f033ac21 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -21,6 +21,7 @@ #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> +#include <linux/of.h> #include "core.h" #include "pinconf.h" @@ -37,14 +38,18 @@ struct pin_config_item { static struct pin_config_item conf_items[] = { PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL), PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL), + PCONFDUMP(PIN_CONFIG_BIAS_BUS_HOLD, "input bias bus hold", NULL), PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL), PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL), + PCONFDUMP(PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, + "input bias pull to pin specific state", NULL), PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL), PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL), PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL), + PCONFDUMP(PIN_CONFIG_DRIVE_STRENGTH, "output drive strength", "mA"), PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT_ENABLE, "input schmitt enabled", NULL), PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL), - PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "time units"), + PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "usec"), PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector"), PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL), PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode"), @@ -135,3 +140,100 @@ void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinconf_generic_dump_config); #endif + +#ifdef CONFIG_OF +struct pinconf_generic_dt_params { + const char * const property; + enum pin_config_param param; + u32 default_value; +}; + +static struct pinconf_generic_dt_params dt_params[] = { + { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, + { "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 }, + { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 }, + { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, + { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, + { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, + { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 }, + { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 }, + { "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 }, + { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, + { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, + { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, + { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 }, + { "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 }, + { "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 }, + { "output-low", PIN_CONFIG_OUTPUT, 0, }, + { "output-high", PIN_CONFIG_OUTPUT, 1, }, +}; + +/** + * pinconf_generic_parse_dt_config() + * parse the config properties into generic pinconfig values. + * @np: node containing the pinconfig properties + * @configs: array with nconfigs entries containing the generic pinconf values + * @nconfigs: umber of configurations + */ +int pinconf_generic_parse_dt_config(struct device_node *np, + unsigned long **configs, + unsigned int *nconfigs) +{ + unsigned long *cfg; + unsigned int ncfg = 0; + int ret; + int i; + u32 val; + + if (!np) + return -EINVAL; + + /* allocate a temporary array big enough to hold one of each option */ + cfg = kzalloc(sizeof(*cfg) * ARRAY_SIZE(dt_params), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(dt_params); i++) { + struct pinconf_generic_dt_params *par = &dt_params[i]; + ret = of_property_read_u32(np, par->property, &val); + + /* property not found */ + if (ret == -EINVAL) + continue; + + /* use default value, when no value is specified */ + if (ret) + val = par->default_value; + + pr_debug("found %s with value %u\n", par->property, val); + cfg[ncfg] = pinconf_to_config_packed(par->param, val); + ncfg++; + } + + ret = 0; + + /* no configs found at all */ + if (ncfg == 0) { + *configs = NULL; + *nconfigs = 0; + goto out; + } + + /* + * Now limit the number of configs to the real number of + * found properties. + */ + *configs = kzalloc(ncfg * sizeof(unsigned long), GFP_KERNEL); + if (!*configs) { + ret = -ENOMEM; + goto out; + } + + memcpy(*configs, cfg, ncfg * sizeof(unsigned long)); + *nconfigs = ncfg; + +out: + kfree(cfg); + return ret; +} +#endif diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 694c3ace4520..e875f21a5908 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -75,98 +75,6 @@ int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin, return ops->pin_config_get(pctldev, pin, config); } -/** - * pin_config_get() - get the configuration of a single pin parameter - * @dev_name: name of the pin controller device for this pin - * @name: name of the pin to get the config for - * @config: the config pointed to by this argument will be filled in with the - * current pin state, it can be used directly by drivers as a numeral, or - * it can be dereferenced to any struct. - */ -int pin_config_get(const char *dev_name, const char *name, - unsigned long *config) -{ - struct pinctrl_dev *pctldev; - int pin; - - pctldev = get_pinctrl_dev_from_devname(dev_name); - if (!pctldev) { - pin = -EINVAL; - return pin; - } - - mutex_lock(&pctldev->mutex); - - pin = pin_get_from_name(pctldev, name); - if (pin < 0) - goto unlock; - - pin = pin_config_get_for_pin(pctldev, pin, config); - -unlock: - mutex_unlock(&pctldev->mutex); - return pin; -} -EXPORT_SYMBOL(pin_config_get); - -static int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config) -{ - const struct pinconf_ops *ops = pctldev->desc->confops; - int ret; - - if (!ops || !ops->pin_config_set) { - dev_err(pctldev->dev, "cannot configure pin, missing " - "config function in driver\n"); - return -EINVAL; - } - - ret = ops->pin_config_set(pctldev, pin, config); - if (ret) { - dev_err(pctldev->dev, - "unable to set pin configuration on pin %d\n", pin); - return ret; - } - - return 0; -} - -/** - * pin_config_set() - set the configuration of a single pin parameter - * @dev_name: name of pin controller device for this pin - * @name: name of the pin to set the config for - * @config: the config in this argument will contain the desired pin state, it - * can be used directly by drivers as a numeral, or it can be dereferenced - * to any struct. - */ -int pin_config_set(const char *dev_name, const char *name, - unsigned long config) -{ - struct pinctrl_dev *pctldev; - int pin, ret; - - pctldev = get_pinctrl_dev_from_devname(dev_name); - if (!pctldev) { - ret = -EINVAL; - return ret; - } - - mutex_lock(&pctldev->mutex); - - pin = pin_get_from_name(pctldev, name); - if (pin < 0) { - ret = pin; - goto unlock; - } - - ret = pin_config_set_for_pin(pctldev, pin, config); - -unlock: - mutex_unlock(&pctldev->mutex); - return ret; -} -EXPORT_SYMBOL(pin_config_set); - int pin_config_group_get(const char *dev_name, const char *pin_group, unsigned long *config) { @@ -204,88 +112,6 @@ unlock: mutex_unlock(&pctldev->mutex); return ret; } -EXPORT_SYMBOL(pin_config_group_get); - -int pin_config_group_set(const char *dev_name, const char *pin_group, - unsigned long config) -{ - struct pinctrl_dev *pctldev; - const struct pinconf_ops *ops; - const struct pinctrl_ops *pctlops; - int selector; - const unsigned *pins; - unsigned num_pins; - int ret; - int i; - - pctldev = get_pinctrl_dev_from_devname(dev_name); - if (!pctldev) { - ret = -EINVAL; - return ret; - } - - mutex_lock(&pctldev->mutex); - - ops = pctldev->desc->confops; - pctlops = pctldev->desc->pctlops; - - if (!ops || (!ops->pin_config_group_set && !ops->pin_config_set)) { - dev_err(pctldev->dev, "cannot configure pin group, missing " - "config function in driver\n"); - ret = -EINVAL; - goto unlock; - } - - selector = pinctrl_get_group_selector(pctldev, pin_group); - if (selector < 0) { - ret = selector; - goto unlock; - } - - ret = pctlops->get_group_pins(pctldev, selector, &pins, &num_pins); - if (ret) { - dev_err(pctldev->dev, "cannot configure pin group, error " - "getting pins\n"); - goto unlock; - } - - /* - * If the pin controller supports handling entire groups we use that - * capability. - */ - if (ops->pin_config_group_set) { - ret = ops->pin_config_group_set(pctldev, selector, config); - /* - * If the pin controller prefer that a certain group be handled - * pin-by-pin as well, it returns -EAGAIN. - */ - if (ret != -EAGAIN) - goto unlock; - } - - /* - * If the controller cannot handle entire groups, we configure each pin - * individually. - */ - if (!ops->pin_config_set) { - ret = 0; - goto unlock; - } - - for (i = 0; i < num_pins; i++) { - ret = ops->pin_config_set(pctldev, pins[i], config); - if (ret < 0) - goto unlock; - } - - ret = 0; - -unlock: - mutex_unlock(&pctldev->mutex); - - return ret; -} -EXPORT_SYMBOL(pin_config_group_set); int pinconf_map_to_setting(struct pinctrl_map const *map, struct pinctrl_setting *setting) diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 92c7267244d2..a4a5417e1413 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -123,3 +123,9 @@ static inline void pinconf_generic_dump_config(struct pinctrl_dev *pctldev, return; } #endif + +#if defined(CONFIG_GENERIC_PINCONF) && defined(CONFIG_OF) +int pinconf_generic_parse_dt_config(struct device_node *np, + unsigned long **configs, + unsigned int *nconfigs); +#endif diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c index 6d4532702f80..1d3f988c2c8b 100644 --- a/drivers/pinctrl/pinctrl-abx500.c +++ b/drivers/pinctrl/pinctrl-abx500.c @@ -30,8 +30,11 @@ #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/machine.h> #include "pinctrl-abx500.h" +#include "core.h" +#include "pinconf.h" /* * The AB9540 and AB8540 GPIO support are extended versions @@ -93,13 +96,15 @@ #define AB8540_GPIOX_VBAT_START 51 #define AB8540_GPIOX_VBAT_END 54 +#define ABX500_GPIO_INPUT 0 +#define ABX500_GPIO_OUTPUT 1 + struct abx500_pinctrl { struct device *dev; struct pinctrl_dev *pctldev; struct abx500_pinctrl_soc_data *soc; struct gpio_chip chip; struct ab8500 *parent; - struct mutex lock; struct abx500_gpio_irq_cluster *irq_cluster; int irq_cluster_size; }; @@ -129,8 +134,8 @@ static int abx500_gpio_get_bit(struct gpio_chip *chip, u8 reg, if (ret < 0) dev_err(pct->dev, - "%s read reg =%x, offset=%x failed\n", - __func__, reg, offset); + "%s read reg =%x, offset=%x failed (%d)\n", + __func__, reg, offset, ret); return ret; } @@ -146,7 +151,8 @@ static int abx500_gpio_set_bits(struct gpio_chip *chip, u8 reg, ret = abx500_mask_and_set_register_interruptible(pct->dev, AB8500_MISC, reg, BIT(pos), val << pos); if (ret < 0) - dev_err(pct->dev, "%s write failed\n", __func__); + dev_err(pct->dev, "%s write reg, %x offset %x failed (%d)\n", + __func__, reg, offset, ret); return ret; } @@ -160,12 +166,24 @@ static int abx500_gpio_get(struct gpio_chip *chip, unsigned offset) { struct abx500_pinctrl *pct = to_abx500_pinctrl(chip); bool bit; + bool is_out; + u8 gpio_offset = offset - 1; int ret; - ret = abx500_gpio_get_bit(chip, AB8500_GPIO_IN1_REG, - offset, &bit); + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, + gpio_offset, &is_out); + if (ret < 0) + goto out; + + if (is_out) + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_OUT1_REG, + gpio_offset, &bit); + else + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_IN1_REG, + gpio_offset, &bit); +out: if (ret < 0) { - dev_err(pct->dev, "%s failed\n", __func__); + dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; } @@ -179,13 +197,14 @@ static void abx500_gpio_set(struct gpio_chip *chip, unsigned offset, int val) ret = abx500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val); if (ret < 0) - dev_err(pct->dev, "%s write failed\n", __func__); + dev_err(pct->dev, "%s write failed (%d)\n", __func__, ret); } -static int abx500_config_pull_updown(struct abx500_pinctrl *pct, - int offset, enum abx500_gpio_pull_updown val) +static int abx500_get_pull_updown(struct abx500_pinctrl *pct, int offset, + enum abx500_gpio_pull_updown *pull_updown) { u8 pos; + u8 val; int ret; struct pullud *pullud; @@ -204,7 +223,41 @@ static int abx500_config_pull_updown(struct abx500_pinctrl *pct, goto out; } - pos = offset << 1; + ret = abx500_get_register_interruptible(pct->dev, + AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG, &val); + + pos = (offset - pullud->first_pin) << 1; + *pull_updown = (val >> pos) & AB8540_GPIO_PULL_UPDOWN_MASK; + +out: + if (ret < 0) + dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); + + return ret; +} + +static int abx500_set_pull_updown(struct abx500_pinctrl *pct, + int offset, enum abx500_gpio_pull_updown val) +{ + u8 pos; + int ret; + struct pullud *pullud; + + if (!pct->soc->pullud) { + dev_err(pct->dev, "%s AB chip doesn't support pull up/down feature", + __func__); + ret = -EPERM; + goto out; + } + + pullud = pct->soc->pullud; + + if ((offset < pullud->first_pin) + || (offset > pullud->last_pin)) { + ret = -EINVAL; + goto out; + } + pos = (offset - pullud->first_pin) << 1; ret = abx500_mask_and_set_register_interruptible(pct->dev, AB8500_MISC, AB8540_GPIO_PULL_UPDOWN_REG, @@ -217,33 +270,51 @@ out: return ret; } +static bool abx500_pullud_supported(struct gpio_chip *chip, unsigned gpio) +{ + struct abx500_pinctrl *pct = to_abx500_pinctrl(chip); + struct pullud *pullud = pct->soc->pullud; + + return (pullud && + gpio >= pullud->first_pin && + gpio <= pullud->last_pin); +} + static int abx500_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int val) { struct abx500_pinctrl *pct = to_abx500_pinctrl(chip); - struct pullud *pullud = pct->soc->pullud; unsigned gpio; int ret; /* set direction as output */ - ret = abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 1); + ret = abx500_gpio_set_bits(chip, + AB8500_GPIO_DIR1_REG, + offset, + ABX500_GPIO_OUTPUT); if (ret < 0) - return ret; + goto out; /* disable pull down */ - ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, offset, 1); + ret = abx500_gpio_set_bits(chip, + AB8500_GPIO_PUD1_REG, + offset, + ABX500_GPIO_PULL_NONE); if (ret < 0) - return ret; + goto out; /* if supported, disable both pull down and pull up */ gpio = offset + 1; - if (pullud && gpio >= pullud->first_pin && gpio <= pullud->last_pin) { - ret = abx500_config_pull_updown(pct, + if (abx500_pullud_supported(chip, gpio)) { + ret = abx500_set_pull_updown(pct, gpio, ABX500_GPIO_PULL_NONE); - if (ret < 0) - return ret; + } +out: + if (ret < 0) { + dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); + return ret; } /* set the output as 1 or 0 */ @@ -253,7 +324,10 @@ static int abx500_gpio_direction_output(struct gpio_chip *chip, static int abx500_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { /* set the register as input */ - return abx500_gpio_set_bits(chip, AB8500_GPIO_DIR1_REG, offset, 0); + return abx500_gpio_set_bits(chip, + AB8500_GPIO_DIR1_REG, + offset, + ABX500_GPIO_INPUT); } static int abx500_gpio_to_irq(struct gpio_chip *chip, unsigned offset) @@ -338,10 +412,16 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, if (af.alt_bit1 != UNUSED) { ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG, offset, 0); + if (ret < 0) + goto out; + ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit1, !!(af.alta_val && BIT(0))); + if (ret < 0) + goto out; + if (af.alt_bit2 != UNUSED) ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, @@ -355,8 +435,14 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, case ABX500_ALT_B: ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG, offset, 0); + if (ret < 0) + goto out; + ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit1, !!(af.altb_val && BIT(0))); + if (ret < 0) + goto out; + if (af.alt_bit2 != UNUSED) ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, @@ -367,8 +453,14 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, case ABX500_ALT_C: ret = abx500_gpio_set_bits(chip, AB8500_GPIO_SEL1_REG, offset, 0); + if (ret < 0) + goto out; + ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2, !!(af.altc_val && BIT(0))); + if (ret < 0) + goto out; + ret = abx500_gpio_set_bits(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2, !!(af.altc_val && BIT(1))); break; @@ -378,11 +470,14 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, return -EINVAL; } +out: + if (ret < 0) + dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; } -static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, +static int abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, unsigned gpio) { u8 mode; @@ -393,6 +488,7 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, struct alternate_functions af = pct->soc->alternate_functions[gpio]; /* on ABx5xx, there is no GPIO0, so adjust the offset */ unsigned offset = gpio - 1; + int ret; /* * if gpiosel_bit is set to unused, @@ -402,8 +498,11 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, return ABX500_DEFAULT; /* read GpioSelx register */ - abx500_gpio_get_bit(chip, AB8500_GPIO_SEL1_REG + (offset / 8), + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_SEL1_REG + (offset / 8), af.gpiosel_bit, &bit_mode); + if (ret < 0) + goto out; + mode = bit_mode; /* sanity check */ @@ -435,14 +534,19 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, * pin use the AlternatFunction register * read alt_bit1 value */ - abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit1, &alt_bit1); + if (ret < 0) + goto out; - if (af.alt_bit2 != UNUSED) + if (af.alt_bit2 != UNUSED) { /* read alt_bit2 value */ - abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, af.alt_bit2, + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_ALTFUN_REG, + af.alt_bit2, &alt_bit2); - else + if (ret < 0) + goto out; + } else alt_bit2 = 0; mode = (alt_bit2 << 1) + alt_bit1; @@ -452,6 +556,10 @@ static u8 abx500_get_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, return ABX500_ALT_B; else return ABX500_ALT_C; + +out: + dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); + return ret; } #ifdef CONFIG_DEBUG_FS @@ -463,11 +571,14 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s, struct gpio_chip *chip, unsigned offset, unsigned gpio) { + struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); const char *label = gpiochip_is_requested(chip, offset - 1); u8 gpio_offset = offset - 1; int mode = -1; bool is_out; - bool pull; + bool pd; + enum abx500_gpio_pull_updown pud = 0; + int ret; const char *modes[] = { [ABX500_DEFAULT] = "default", @@ -476,21 +587,48 @@ static void abx500_gpio_dbg_show_one(struct seq_file *s, [ABX500_ALT_C] = "altC", }; - abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, gpio_offset, &is_out); - abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG, gpio_offset, &pull); + const char *pull_up_down[] = { + [ABX500_GPIO_PULL_DOWN] = "pull down", + [ABX500_GPIO_PULL_NONE] = "pull none", + [ABX500_GPIO_PULL_NONE + 1] = "pull none", + [ABX500_GPIO_PULL_UP] = "pull up", + }; + + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_DIR1_REG, + gpio_offset, &is_out); + if (ret < 0) + goto out; + + seq_printf(s, " gpio-%-3d (%-20.20s) %-3s", + gpio, label ?: "(none)", + is_out ? "out" : "in "); + + if (!is_out) { + if (abx500_pullud_supported(chip, offset)) { + ret = abx500_get_pull_updown(pct, offset, &pud); + if (ret < 0) + goto out; + + seq_printf(s, " %-9s", pull_up_down[pud]); + } else { + ret = abx500_gpio_get_bit(chip, AB8500_GPIO_PUD1_REG, + gpio_offset, &pd); + if (ret < 0) + goto out; + + seq_printf(s, " %-9s", pull_up_down[pd]); + } + } else + seq_printf(s, " %-9s", chip->get(chip, offset) ? "hi" : "lo"); if (pctldev) mode = abx500_get_mode(pctldev, chip, offset); - seq_printf(s, " gpio-%-3d (%-20.20s) %-3s %-9s %s", - gpio, label ?: "(none)", - is_out ? "out" : "in ", - is_out ? - (chip->get - ? (chip->get(chip, offset) ? "hi" : "lo") - : "? ") - : (pull ? "pull up" : "pull down"), - (mode < 0) ? "unknown" : modes[mode]); + seq_printf(s, " %s", (mode < 0) ? "unknown" : modes[mode]); + +out: + if (ret < 0) + dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); } static void abx500_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -594,6 +732,9 @@ static int abx500_pmx_enable(struct pinctrl_dev *pctldev, unsigned function, ret = abx500_set_mode(pctldev, chip, g->pins[i], g->altsetting); } + if (ret < 0) + dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); + return ret; } @@ -642,10 +783,8 @@ static int abx500_gpio_request_enable(struct pinctrl_dev *pctldev, ret = abx500_set_mode(pct->pctldev, &pct->chip, offset, p->altfunc); - if (ret < 0) { + if (ret < 0) dev_err(pct->dev, "%s setting altfunc failed\n", __func__); - return ret; - } return ret; } @@ -704,11 +843,193 @@ static void abx500_pin_dbg_show(struct pinctrl_dev *pctldev, chip->base + offset - 1); } +static void abx500_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(map[i].data.configs.configs); + kfree(map); +} + +static int abx500_dt_reserve_map(struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps, + unsigned reserve) +{ + unsigned old_num = *reserved_maps; + unsigned new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + + return 0; +} + +static int abx500_dt_add_map_mux(struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps, const char *group, + const char *function) +{ + if (*num_maps == *reserved_maps) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} + +static int abx500_dt_add_map_configs(struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps, const char *group, + unsigned long *configs, unsigned num_configs) +{ + unsigned long *dup_configs; + + if (*num_maps == *reserved_maps) + return -ENOSPC; + + dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), + GFP_KERNEL); + if (!dup_configs) + return -ENOMEM; + + (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_PIN; + + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} + +static const char *abx500_find_pin_name(struct pinctrl_dev *pctldev, + const char *pin_name) +{ + int i, pin_number; + struct abx500_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); + + if (sscanf((char *)pin_name, "GPIO%d", &pin_number) == 1) + for (i = 0; i < npct->soc->npins; i++) + if (npct->soc->pins[i].number == pin_number) + return npct->soc->pins[i].name; + return NULL; +} + +static int abx500_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps) +{ + int ret; + const char *function = NULL; + unsigned long *configs; + unsigned int nconfigs = 0; + bool has_config = 0; + unsigned reserve = 0; + struct property *prop; + const char *group, *gpio_name; + struct device_node *np_config; + + ret = of_property_read_string(np, "ste,function", &function); + if (ret >= 0) + reserve = 1; + + ret = pinconf_generic_parse_dt_config(np, &configs, &nconfigs); + if (nconfigs) + has_config = 1; + + np_config = of_parse_phandle(np, "ste,config", 0); + if (np_config) { + ret = pinconf_generic_parse_dt_config(np_config, &configs, + &nconfigs); + if (ret) + goto exit; + has_config |= nconfigs; + } + + ret = of_property_count_strings(np, "ste,pins"); + if (ret < 0) + goto exit; + + if (has_config) + reserve++; + + reserve *= ret; + + ret = abx500_dt_reserve_map(map, reserved_maps, num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "ste,pins", prop, group) { + if (function) { + ret = abx500_dt_add_map_mux(map, reserved_maps, + num_maps, group, function); + if (ret < 0) + goto exit; + } + if (has_config) { + gpio_name = abx500_find_pin_name(pctldev, group); + + ret = abx500_dt_add_map_configs(map, reserved_maps, + num_maps, gpio_name, configs, 1); + if (ret < 0) + goto exit; + } + + } +exit: + return ret; +} + +static int abx500_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + unsigned reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = abx500_dt_subnode_to_map(pctldev, np, map, + &reserved_maps, num_maps); + if (ret < 0) { + abx500_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + + return 0; +} + static const struct pinctrl_ops abx500_pinctrl_ops = { .get_groups_count = abx500_get_groups_cnt, .get_group_name = abx500_get_group_name, .get_group_pins = abx500_get_group_pins, .pin_dbg_show = abx500_pin_dbg_show, + .dt_node_to_map = abx500_dt_node_to_map, + .dt_free_map = abx500_dt_free_map, }; static int abx500_pin_config_get(struct pinctrl_dev *pctldev, @@ -723,10 +1044,9 @@ static int abx500_pin_config_set(struct pinctrl_dev *pctldev, unsigned long config) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); - struct pullud *pullud = pct->soc->pullud; struct gpio_chip *chip = &pct->chip; unsigned offset; - int ret; + int ret = -EINVAL; enum pin_config_param param = pinconf_to_config_param(config); enum pin_config_param argument = pinconf_to_config_argument(config); @@ -739,41 +1059,83 @@ static int abx500_pin_config_set(struct pinctrl_dev *pctldev, offset = pin - 1; switch (param) { - case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_DISABLE: + ret = abx500_gpio_direction_input(chip, offset); + if (ret < 0) + goto out; /* - * if argument = 1 set the pull down - * else clear the pull down + * Some chips only support pull down, while some actually + * support both pull up and pull down. Such chips have + * a "pullud" range specified for the pins that support + * both features. If the pin is not within that range, we + * fall back to the old bit set that only support pull down. */ + if (abx500_pullud_supported(chip, pin)) + ret = abx500_set_pull_updown(pct, + pin, + ABX500_GPIO_PULL_NONE); + else + /* Chip only supports pull down */ + ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, + offset, ABX500_GPIO_PULL_NONE); + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: ret = abx500_gpio_direction_input(chip, offset); + if (ret < 0) + goto out; /* + * if argument = 1 set the pull down + * else clear the pull down * Some chips only support pull down, while some actually * support both pull up and pull down. Such chips have * a "pullud" range specified for the pins that support * both features. If the pin is not within that range, we * fall back to the old bit set that only support pull down. */ - if (pullud && - pin >= pullud->first_pin && - pin <= pullud->last_pin) - ret = abx500_config_pull_updown(pct, + if (abx500_pullud_supported(chip, pin)) + ret = abx500_set_pull_updown(pct, pin, argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE); else /* Chip only supports pull down */ ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, - offset, argument ? 0 : 1); + offset, + argument ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + ret = abx500_gpio_direction_input(chip, offset); + if (ret < 0) + goto out; + /* + * if argument = 1 set the pull up + * else clear the pull up + */ + ret = abx500_gpio_direction_input(chip, offset); + /* + * Some chips only support pull down, while some actually + * support both pull up and pull down. Such chips have + * a "pullud" range specified for the pins that support + * both features. If the pin is not within that range, do + * nothing + */ + if (abx500_pullud_supported(chip, pin)) + ret = abx500_set_pull_updown(pct, + pin, + argument ? ABX500_GPIO_PULL_UP : ABX500_GPIO_PULL_NONE); break; case PIN_CONFIG_OUTPUT: ret = abx500_gpio_direction_output(chip, offset, argument); - break; default: dev_err(chip->dev, "illegal configuration requested\n"); - - return -EINVAL; } +out: + if (ret < 0) + dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); return ret; } @@ -881,9 +1243,6 @@ static int abx500_gpio_probe(struct platform_device *pdev) id = (unsigned long)match->data; } - /* initialize the lock */ - mutex_init(&pct->lock); - /* Poke in other ASIC variants here */ switch (id) { case PINCTRL_AB8500: @@ -900,13 +1259,11 @@ static int abx500_gpio_probe(struct platform_device *pdev) break; default: dev_err(&pdev->dev, "Unsupported pinctrl sub driver (%d)\n", id); - mutex_destroy(&pct->lock); return -EINVAL; } if (!pct->soc) { dev_err(&pdev->dev, "Invalid SOC data\n"); - mutex_destroy(&pct->lock); return -EINVAL; } @@ -917,7 +1274,6 @@ static int abx500_gpio_probe(struct platform_device *pdev) ret = gpiochip_add(&pct->chip); if (ret) { dev_err(&pdev->dev, "unable to add gpiochip: %d\n", ret); - mutex_destroy(&pct->lock); return ret; } dev_info(&pdev->dev, "added gpiochip\n"); @@ -954,7 +1310,6 @@ out_rem_chip: if (err) dev_info(&pdev->dev, "failed to remove gpiochip\n"); - mutex_destroy(&pct->lock); return ret; } @@ -974,8 +1329,6 @@ static int abx500_gpio_remove(struct platform_device *pdev) return ret; } - mutex_destroy(&pct->lock); - return 0; } diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 5d7529ed5392..b90a3a0ac534 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1543,12 +1543,6 @@ static int at91_gpio_probe(struct platform_device *pdev) goto err; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENOENT; - goto err; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = irq; @@ -1561,6 +1555,7 @@ static int at91_gpio_probe(struct platform_device *pdev) goto err; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); at91_chip->regbase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(at91_chip->regbase)) { ret = PTR_ERR(at91_chip->regbase); diff --git a/drivers/pinctrl/pinctrl-baytrail.c b/drivers/pinctrl/pinctrl-baytrail.c new file mode 100644 index 000000000000..e9d735dcebfb --- /dev/null +++ b/drivers/pinctrl/pinctrl-baytrail.c @@ -0,0 +1,543 @@ +/* + * Pinctrl GPIO driver for Intel Baytrail + * Copyright (c) 2012-2013, Intel Corporation. + * + * Author: Mathias Nyman <mathias.nyman@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/irqdomain.h> +#include <linux/acpi.h> +#include <linux/acpi_gpio.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/io.h> +#include <linux/pm_runtime.h> +#include <linux/pinctrl/pinctrl.h> + +/* memory mapped register offsets */ +#define BYT_CONF0_REG 0x000 +#define BYT_CONF1_REG 0x004 +#define BYT_VAL_REG 0x008 +#define BYT_DFT_REG 0x00c +#define BYT_INT_STAT_REG 0x800 + +/* BYT_CONF0_REG register bits */ +#define BYT_TRIG_NEG BIT(26) +#define BYT_TRIG_POS BIT(25) +#define BYT_TRIG_LVL BIT(24) +#define BYT_PIN_MUX 0x07 + +/* BYT_VAL_REG register bits */ +#define BYT_INPUT_EN BIT(2) /* 0: input enabled (active low)*/ +#define BYT_OUTPUT_EN BIT(1) /* 0: output enabled (active low)*/ +#define BYT_LEVEL BIT(0) + +#define BYT_DIR_MASK (BIT(1) | BIT(2)) +#define BYT_TRIG_MASK (BIT(26) | BIT(25) | BIT(24)) + +#define BYT_NGPIO_SCORE 102 +#define BYT_NGPIO_NCORE 28 +#define BYT_NGPIO_SUS 44 + +/* + * Baytrail gpio controller consist of three separate sub-controllers called + * SCORE, NCORE and SUS. The sub-controllers are identified by their acpi UID. + * + * GPIO numbering is _not_ ordered meaning that gpio # 0 in ACPI namespace does + * _not_ correspond to the first gpio register at controller's gpio base. + * There is no logic or pattern in mapping gpio numbers to registers (pads) so + * each sub-controller needs to have its own mapping table + */ + +/* score_pins[gpio_nr] = pad_nr */ + +static unsigned const score_pins[BYT_NGPIO_SCORE] = { + 85, 89, 93, 96, 99, 102, 98, 101, 34, 37, + 36, 38, 39, 35, 40, 84, 62, 61, 64, 59, + 54, 56, 60, 55, 63, 57, 51, 50, 53, 47, + 52, 49, 48, 43, 46, 41, 45, 42, 58, 44, + 95, 105, 70, 68, 67, 66, 69, 71, 65, 72, + 86, 90, 88, 92, 103, 77, 79, 83, 78, 81, + 80, 82, 13, 12, 15, 14, 17, 18, 19, 16, + 2, 1, 0, 4, 6, 7, 9, 8, 33, 32, + 31, 30, 29, 27, 25, 28, 26, 23, 21, 20, + 24, 22, 5, 3, 10, 11, 106, 87, 91, 104, + 97, 100, +}; + +static unsigned const ncore_pins[BYT_NGPIO_NCORE] = { + 19, 18, 17, 20, 21, 22, 24, 25, 23, 16, + 14, 15, 12, 26, 27, 1, 4, 8, 11, 0, + 3, 6, 10, 13, 2, 5, 9, 7, +}; + +static unsigned const sus_pins[BYT_NGPIO_SUS] = { + 29, 33, 30, 31, 32, 34, 36, 35, 38, 37, + 18, 7, 11, 20, 17, 1, 8, 10, 19, 12, + 0, 2, 23, 39, 28, 27, 22, 21, 24, 25, + 26, 51, 56, 54, 49, 55, 48, 57, 50, 58, + 52, 53, 59, 40, +}; + +static struct pinctrl_gpio_range byt_ranges[] = { + { + .name = "1", /* match with acpi _UID in probe */ + .npins = BYT_NGPIO_SCORE, + .pins = score_pins, + }, + { + .name = "2", + .npins = BYT_NGPIO_NCORE, + .pins = ncore_pins, + }, + { + .name = "3", + .npins = BYT_NGPIO_SUS, + .pins = sus_pins, + }, + { + }, +}; + +struct byt_gpio { + struct gpio_chip chip; + struct irq_domain *domain; + struct platform_device *pdev; + spinlock_t lock; + void __iomem *reg_base; + struct pinctrl_gpio_range *range; +}; + +static void __iomem *byt_gpio_reg(struct gpio_chip *chip, unsigned offset, + int reg) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + u32 reg_offset; + void __iomem *ptr; + + if (reg == BYT_INT_STAT_REG) + reg_offset = (offset / 32) * 4; + else + reg_offset = vg->range->pins[offset] * 16; + + ptr = (void __iomem *) (vg->reg_base + reg_offset + reg); + return ptr; +} + +static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + + pm_runtime_get(&vg->pdev->dev); + + return 0; +} + +static void byt_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); + u32 value; + + /* clear interrupt triggering */ + value = readl(reg); + value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + writel(value, reg); + + pm_runtime_put(&vg->pdev->dev); +} + +static int byt_irq_type(struct irq_data *d, unsigned type) +{ + struct byt_gpio *vg = irq_data_get_irq_chip_data(d); + u32 offset = irqd_to_hwirq(d); + u32 value; + unsigned long flags; + void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); + + if (offset >= vg->chip.ngpio) + return -EINVAL; + + spin_lock_irqsave(&vg->lock, flags); + value = readl(reg); + + /* For level trigges the BYT_TRIG_POS and BYT_TRIG_NEG bits + * are used to indicate high and low level triggering + */ + value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + value |= BYT_TRIG_LVL; + case IRQ_TYPE_EDGE_RISING: + value |= BYT_TRIG_POS; + break; + case IRQ_TYPE_LEVEL_LOW: + value |= BYT_TRIG_LVL; + case IRQ_TYPE_EDGE_FALLING: + value |= BYT_TRIG_NEG; + break; + case IRQ_TYPE_EDGE_BOTH: + value |= (BYT_TRIG_NEG | BYT_TRIG_POS); + break; + } + writel(value, reg); + + spin_unlock_irqrestore(&vg->lock, flags); + + return 0; +} + +static int byt_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); + return readl(reg) & BYT_LEVEL; +} + +static void byt_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); + unsigned long flags; + u32 old_val; + + spin_lock_irqsave(&vg->lock, flags); + + old_val = readl(reg); + + if (value) + writel(old_val | BYT_LEVEL, reg); + else + writel(old_val & ~BYT_LEVEL, reg); + + spin_unlock_irqrestore(&vg->lock, flags); +} + +static int byt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); + unsigned long flags; + u32 value; + + spin_lock_irqsave(&vg->lock, flags); + + value = readl(reg) | BYT_DIR_MASK; + value = value & (~BYT_INPUT_EN); /* active low */ + writel(value, reg); + + spin_unlock_irqrestore(&vg->lock, flags); + + return 0; +} + +static int byt_gpio_direction_output(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + void __iomem *reg = byt_gpio_reg(chip, gpio, BYT_VAL_REG); + unsigned long flags; + u32 reg_val; + + spin_lock_irqsave(&vg->lock, flags); + + reg_val = readl(reg) | (BYT_DIR_MASK | !!value); + reg_val &= ~(BYT_OUTPUT_EN | !value); + writel(reg_val, reg); + + spin_unlock_irqrestore(&vg->lock, flags); + + return 0; +} + +static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + int i; + unsigned long flags; + u32 conf0, val, offs; + + spin_lock_irqsave(&vg->lock, flags); + + for (i = 0; i < vg->chip.ngpio; i++) { + offs = vg->range->pins[i] * 16; + conf0 = readl(vg->reg_base + offs + BYT_CONF0_REG); + val = readl(vg->reg_base + offs + BYT_VAL_REG); + + seq_printf(s, + " gpio-%-3d %s %s %s pad-%-3d offset:0x%03x mux:%d %s%s%s\n", + i, + val & BYT_INPUT_EN ? " " : "in", + val & BYT_OUTPUT_EN ? " " : "out", + val & BYT_LEVEL ? "hi" : "lo", + vg->range->pins[i], offs, + conf0 & 0x7, + conf0 & BYT_TRIG_NEG ? "fall " : "", + conf0 & BYT_TRIG_POS ? "rise " : "", + conf0 & BYT_TRIG_LVL ? "lvl " : ""); + } + spin_unlock_irqrestore(&vg->lock, flags); +} + +static int byt_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct byt_gpio *vg = container_of(chip, struct byt_gpio, chip); + return irq_create_mapping(vg->domain, offset); +} + +static void byt_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_data *data = irq_desc_get_irq_data(desc); + struct byt_gpio *vg = irq_data_get_irq_handler_data(data); + struct irq_chip *chip = irq_data_get_irq_chip(data); + u32 base, pin, mask; + void __iomem *reg; + u32 pending; + unsigned virq; + int looplimit = 0; + + /* check from GPIO controller which pin triggered the interrupt */ + for (base = 0; base < vg->chip.ngpio; base += 32) { + + reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); + + while ((pending = readl(reg))) { + pin = __ffs(pending); + mask = BIT(pin); + /* Clear before handling so we can't lose an edge */ + writel(mask, reg); + + virq = irq_find_mapping(vg->domain, base + pin); + generic_handle_irq(virq); + + /* In case bios or user sets triggering incorretly a pin + * might remain in "interrupt triggered" state. + */ + if (looplimit++ > 32) { + dev_err(&vg->pdev->dev, + "Gpio %d interrupt flood, disabling\n", + base + pin); + + reg = byt_gpio_reg(&vg->chip, base + pin, + BYT_CONF0_REG); + mask = readl(reg); + mask &= ~(BYT_TRIG_NEG | BYT_TRIG_POS | + BYT_TRIG_LVL); + writel(mask, reg); + mask = readl(reg); /* flush */ + break; + } + } + } + chip->irq_eoi(data); +} + +static void byt_irq_unmask(struct irq_data *d) +{ +} + +static void byt_irq_mask(struct irq_data *d) +{ +} + +static struct irq_chip byt_irqchip = { + .name = "BYT-GPIO", + .irq_mask = byt_irq_mask, + .irq_unmask = byt_irq_unmask, + .irq_set_type = byt_irq_type, +}; + +static void byt_gpio_irq_init_hw(struct byt_gpio *vg) +{ + void __iomem *reg; + u32 base, value; + + /* clear interrupt status trigger registers */ + for (base = 0; base < vg->chip.ngpio; base += 32) { + reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); + writel(0xffffffff, reg); + /* make sure trigger bits are cleared, if not then a pin + might be misconfigured in bios */ + value = readl(reg); + if (value) + dev_err(&vg->pdev->dev, + "GPIO interrupt error, pins misconfigured\n"); + } +} + +static int byt_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + struct byt_gpio *vg = d->host_data; + + irq_set_chip_and_handler_name(virq, &byt_irqchip, handle_simple_irq, + "demux"); + irq_set_chip_data(virq, vg); + irq_set_irq_type(virq, IRQ_TYPE_NONE); + + return 0; +} + +static const struct irq_domain_ops byt_gpio_irq_ops = { + .map = byt_gpio_irq_map, +}; + +static int byt_gpio_probe(struct platform_device *pdev) +{ + struct byt_gpio *vg; + struct gpio_chip *gc; + struct resource *mem_rc, *irq_rc; + struct device *dev = &pdev->dev; + struct acpi_device *acpi_dev; + struct pinctrl_gpio_range *range; + acpi_handle handle = ACPI_HANDLE(dev); + unsigned hwirq; + int ret; + + if (acpi_bus_get_device(handle, &acpi_dev)) + return -ENODEV; + + vg = devm_kzalloc(dev, sizeof(struct byt_gpio), GFP_KERNEL); + if (!vg) { + dev_err(&pdev->dev, "can't allocate byt_gpio chip data\n"); + return -ENOMEM; + } + + for (range = byt_ranges; range->name; range++) { + if (!strcmp(acpi_dev->pnp.unique_id, range->name)) { + vg->chip.ngpio = range->npins; + vg->range = range; + break; + } + } + + if (!vg->chip.ngpio || !vg->range) + return -ENODEV; + + vg->pdev = pdev; + platform_set_drvdata(pdev, vg); + + mem_rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vg->reg_base = devm_ioremap_resource(dev, mem_rc); + if (IS_ERR(vg->reg_base)) + return PTR_ERR(vg->reg_base); + + spin_lock_init(&vg->lock); + + gc = &vg->chip; + gc->label = dev_name(&pdev->dev); + gc->owner = THIS_MODULE; + gc->request = byt_gpio_request; + gc->free = byt_gpio_free; + gc->direction_input = byt_gpio_direction_input; + gc->direction_output = byt_gpio_direction_output; + gc->get = byt_gpio_get; + gc->set = byt_gpio_set; + gc->dbg_show = byt_gpio_dbg_show; + gc->base = -1; + gc->can_sleep = 0; + gc->dev = dev; + + ret = gpiochip_add(gc); + if (ret) { + dev_err(&pdev->dev, "failed adding byt-gpio chip\n"); + return ret; + } + + /* set up interrupts */ + irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (irq_rc && irq_rc->start) { + hwirq = irq_rc->start; + gc->to_irq = byt_gpio_to_irq; + + vg->domain = irq_domain_add_linear(NULL, gc->ngpio, + &byt_gpio_irq_ops, vg); + if (!vg->domain) + return -ENXIO; + + byt_gpio_irq_init_hw(vg); + + irq_set_handler_data(hwirq, vg); + irq_set_chained_handler(hwirq, byt_gpio_irq_handler); + + /* Register interrupt handlers for gpio signaled acpi events */ + acpi_gpiochip_request_interrupts(gc); + } + + pm_runtime_enable(dev); + + return 0; +} + +static int byt_gpio_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int byt_gpio_runtime_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops byt_gpio_pm_ops = { + .runtime_suspend = byt_gpio_runtime_suspend, + .runtime_resume = byt_gpio_runtime_resume, +}; + +static const struct acpi_device_id byt_gpio_acpi_match[] = { + { "INT33B2", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, byt_gpio_acpi_match); + +static int byt_gpio_remove(struct platform_device *pdev) +{ + struct byt_gpio *vg = platform_get_drvdata(pdev); + int err; + pm_runtime_disable(&pdev->dev); + err = gpiochip_remove(&vg->chip); + if (err) + dev_warn(&pdev->dev, "failed to remove gpio_chip.\n"); + + return 0; +} + +static struct platform_driver byt_gpio_driver = { + .probe = byt_gpio_probe, + .remove = byt_gpio_remove, + .driver = { + .name = "byt_gpio", + .owner = THIS_MODULE, + .pm = &byt_gpio_pm_ops, + .acpi_match_table = ACPI_PTR(byt_gpio_acpi_match), + }, +}; + +static int __init byt_gpio_init(void) +{ + return platform_driver_register(&byt_gpio_driver); +} + +subsys_initcall(byt_gpio_init); diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c index c8f20a3d8f88..a1c88b30f71f 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -113,7 +113,7 @@ static struct lock_class_key gpio_lock_class; /* pins are just named GPIO0..GPIO53 */ #define BCM2835_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a) -struct pinctrl_pin_desc bcm2835_gpio_pins[] = { +static struct pinctrl_pin_desc bcm2835_gpio_pins[] = { BCM2835_GPIO_PIN(0), BCM2835_GPIO_PIN(1), BCM2835_GPIO_PIN(2), diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index eeff7f7fc920..f22a2193d949 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -853,7 +853,6 @@ static int __exit u300_gpio_remove(struct platform_device *pdev) } u300_gpio_free_ports(gpio); clk_disable_unprepare(gpio->clk); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 5f58cf0e96e2..a74b3cbd7451 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -50,37 +50,58 @@ static const struct of_device_id exynos_wkup_irq_ids[] = { { } }; -static void exynos_gpio_irq_unmask(struct irq_data *irqd) +static void exynos_gpio_irq_mask(struct irq_data *irqd) { struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); struct samsung_pinctrl_drv_data *d = bank->drvdata; unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset; unsigned long mask; + unsigned long flags; + + spin_lock_irqsave(&bank->slock, flags); mask = readl(d->virt_base + reg_mask); - mask &= ~(1 << irqd->hwirq); + mask |= 1 << irqd->hwirq; writel(mask, d->virt_base + reg_mask); + + spin_unlock_irqrestore(&bank->slock, flags); } -static void exynos_gpio_irq_mask(struct irq_data *irqd) +static void exynos_gpio_irq_ack(struct irq_data *irqd) { struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); struct samsung_pinctrl_drv_data *d = bank->drvdata; - unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset; - unsigned long mask; + unsigned long reg_pend = d->ctrl->geint_pend + bank->eint_offset; - mask = readl(d->virt_base + reg_mask); - mask |= 1 << irqd->hwirq; - writel(mask, d->virt_base + reg_mask); + writel(1 << irqd->hwirq, d->virt_base + reg_pend); } -static void exynos_gpio_irq_ack(struct irq_data *irqd) +static void exynos_gpio_irq_unmask(struct irq_data *irqd) { struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd); struct samsung_pinctrl_drv_data *d = bank->drvdata; - unsigned long reg_pend = d->ctrl->geint_pend + bank->eint_offset; + unsigned long reg_mask = d->ctrl->geint_mask + bank->eint_offset; + unsigned long mask; + unsigned long flags; - writel(1 << irqd->hwirq, d->virt_base + reg_pend); + /* + * Ack level interrupts right before unmask + * + * If we don't do this we'll get a double-interrupt. Level triggered + * interrupts must not fire an interrupt if the level is not + * _currently_ active, even if it was active while the interrupt was + * masked. + */ + if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK) + exynos_gpio_irq_ack(irqd); + + spin_lock_irqsave(&bank->slock, flags); + + mask = readl(d->virt_base + reg_mask); + mask &= ~(1 << irqd->hwirq); + writel(mask, d->virt_base + reg_mask); + + spin_unlock_irqrestore(&bank->slock, flags); } static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type) @@ -258,37 +279,58 @@ err_domains: return ret; } -static void exynos_wkup_irq_unmask(struct irq_data *irqd) +static void exynos_wkup_irq_mask(struct irq_data *irqd) { struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd); struct samsung_pinctrl_drv_data *d = b->drvdata; unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset; unsigned long mask; + unsigned long flags; + + spin_lock_irqsave(&b->slock, flags); mask = readl(d->virt_base + reg_mask); - mask &= ~(1 << irqd->hwirq); + mask |= 1 << irqd->hwirq; writel(mask, d->virt_base + reg_mask); + + spin_unlock_irqrestore(&b->slock, flags); } -static void exynos_wkup_irq_mask(struct irq_data *irqd) +static void exynos_wkup_irq_ack(struct irq_data *irqd) { struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd); struct samsung_pinctrl_drv_data *d = b->drvdata; - unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset; - unsigned long mask; + unsigned long pend = d->ctrl->weint_pend + b->eint_offset; - mask = readl(d->virt_base + reg_mask); - mask |= 1 << irqd->hwirq; - writel(mask, d->virt_base + reg_mask); + writel(1 << irqd->hwirq, d->virt_base + pend); } -static void exynos_wkup_irq_ack(struct irq_data *irqd) +static void exynos_wkup_irq_unmask(struct irq_data *irqd) { struct samsung_pin_bank *b = irq_data_get_irq_chip_data(irqd); struct samsung_pinctrl_drv_data *d = b->drvdata; - unsigned long pend = d->ctrl->weint_pend + b->eint_offset; + unsigned long reg_mask = d->ctrl->weint_mask + b->eint_offset; + unsigned long mask; + unsigned long flags; - writel(1 << irqd->hwirq, d->virt_base + pend); + /* + * Ack level interrupts right before unmask + * + * If we don't do this we'll get a double-interrupt. Level triggered + * interrupts must not fire an interrupt if the level is not + * _currently_ active, even if it was active while the interrupt was + * masked. + */ + if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK) + exynos_wkup_irq_ack(irqd); + + spin_lock_irqsave(&b->slock, flags); + + mask = readl(d->virt_base + reg_mask); + mask &= ~(1 << irqd->hwirq); + writel(mask, d->virt_base + reg_mask); + + spin_unlock_irqrestore(&b->slock, flags); } static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type) diff --git a/drivers/pinctrl/pinctrl-exynos5440.c b/drivers/pinctrl/pinctrl-exynos5440.c index 32a48f44f574..3b283fd898ff 100644 --- a/drivers/pinctrl/pinctrl-exynos5440.c +++ b/drivers/pinctrl/pinctrl-exynos5440.c @@ -220,7 +220,7 @@ static int exynos5440_dt_node_to_map(struct pinctrl_dev *pctldev, dev_err(dev, "failed to alloc memory for group name\n"); goto free_map; } - sprintf(gname, "%s%s", np->name, GROUP_SUFFIX); + snprintf(gname, strlen(np->name) + 4, "%s%s", np->name, GROUP_SUFFIX); /* * don't have config options? then skip over to creating function @@ -259,7 +259,8 @@ skip_cfgs: dev_err(dev, "failed to alloc memory for func name\n"); goto free_cfg; } - sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX); + snprintf(fname, strlen(np->name) + 4, "%s%s", np->name, + FUNCTION_SUFFIX); map[*nmaps].data.mux.group = gname; map[*nmaps].data.mux.function = fname; @@ -713,7 +714,8 @@ static int exynos5440_pinctrl_parse_dt(struct platform_device *pdev, dev_err(dev, "failed to alloc memory for group name\n"); return -ENOMEM; } - sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX); + snprintf(gname, strlen(cfg_np->name) + 4, "%s%s", cfg_np->name, + GROUP_SUFFIX); grp->name = gname; grp->pins = pin_list; @@ -733,7 +735,8 @@ skip_to_pin_function: dev_err(dev, "failed to alloc memory for func name\n"); return -ENOMEM; } - sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX); + snprintf(fname, strlen(cfg_np->name) + 4, "%s%s", cfg_np->name, + FUNCTION_SUFFIX); func->name = fname; func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL); @@ -806,7 +809,7 @@ static int exynos5440_pinctrl_register(struct platform_device *pdev, /* for each pin, set the name of the pin */ for (pin = 0; pin < ctrldesc->npins; pin++) { - sprintf(pin_names, "gpio%02d", pin); + snprintf(pin_names, 6, "gpio%02d", pin); pdesc = pindesc + pin; pdesc->name = pin_names; pin_names += PIN_NAME_LENGTH; diff --git a/drivers/pinctrl/pinctrl-imx.c b/drivers/pinctrl/pinctrl-imx.c index 4fcfff9243be..57a4eb0add2e 100644 --- a/drivers/pinctrl/pinctrl-imx.c +++ b/drivers/pinctrl/pinctrl-imx.c @@ -221,13 +221,21 @@ static int imx_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, pin_id = pins[i]; pin_reg = &info->pin_regs[pin_id]; - if (!pin_reg->mux_reg) { + if (!(info->flags & ZERO_OFFSET_VALID) && !pin_reg->mux_reg) { dev_err(ipctl->dev, "Pin(%s) does not support mux function\n", info->pins[pin_id].name); return -EINVAL; } - writel(mux[i], ipctl->base + pin_reg->mux_reg); + if (info->flags & SHARE_MUX_CONF_REG) { + u32 reg; + reg = readl(ipctl->base + pin_reg->mux_reg); + reg &= ~(0x7 << 20); + reg |= (mux[i] << 20); + writel(reg, ipctl->base + pin_reg->mux_reg); + } else { + writel(mux[i], ipctl->base + pin_reg->mux_reg); + } dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", pin_reg->mux_reg, mux[i]); @@ -287,7 +295,7 @@ static int imx_pinconf_get(struct pinctrl_dev *pctldev, const struct imx_pinctrl_soc_info *info = ipctl->info; const struct imx_pin_reg *pin_reg = &info->pin_regs[pin_id]; - if (!pin_reg->conf_reg) { + if (!(info->flags & ZERO_OFFSET_VALID) && !pin_reg->conf_reg) { dev_err(info->dev, "Pin(%s) does not support config function\n", info->pins[pin_id].name); return -EINVAL; @@ -295,6 +303,9 @@ static int imx_pinconf_get(struct pinctrl_dev *pctldev, *config = readl(ipctl->base + pin_reg->conf_reg); + if (info->flags & SHARE_MUX_CONF_REG) + *config &= 0xffff; + return 0; } @@ -305,7 +316,7 @@ static int imx_pinconf_set(struct pinctrl_dev *pctldev, const struct imx_pinctrl_soc_info *info = ipctl->info; const struct imx_pin_reg *pin_reg = &info->pin_regs[pin_id]; - if (!pin_reg->conf_reg) { + if (!(info->flags & ZERO_OFFSET_VALID) && !pin_reg->conf_reg) { dev_err(info->dev, "Pin(%s) does not support config function\n", info->pins[pin_id].name); return -EINVAL; @@ -314,7 +325,15 @@ static int imx_pinconf_set(struct pinctrl_dev *pctldev, dev_dbg(ipctl->dev, "pinconf set pin %s\n", info->pins[pin_id].name); - writel(config, ipctl->base + pin_reg->conf_reg); + if (info->flags & SHARE_MUX_CONF_REG) { + u32 reg; + reg = readl(ipctl->base + pin_reg->conf_reg); + reg &= ~0xffff; + reg |= config; + writel(reg, ipctl->base + pin_reg->conf_reg); + } else { + writel(config, ipctl->base + pin_reg->conf_reg); + } dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n", pin_reg->conf_reg, config); @@ -381,19 +400,24 @@ static struct pinctrl_desc imx_pinctrl_desc = { * 1 u32 CONFIG, so 24 types in total for each pin. */ #define FSL_PIN_SIZE 24 +#define SHARE_FSL_PIN_SIZE 20 static int imx_pinctrl_parse_groups(struct device_node *np, struct imx_pin_group *grp, struct imx_pinctrl_soc_info *info, u32 index) { - int size; + int size, pin_size; const __be32 *list; int i; u32 config; dev_dbg(info->dev, "group(%d): %s\n", index, np->name); + if (info->flags & SHARE_MUX_CONF_REG) + pin_size = SHARE_FSL_PIN_SIZE; + else + pin_size = FSL_PIN_SIZE; /* Initialise group */ grp->name = np->name; @@ -403,12 +427,12 @@ static int imx_pinctrl_parse_groups(struct device_node *np, */ list = of_get_property(np, "fsl,pins", &size); /* we do not check return since it's safe node passed down */ - if (!size || size % FSL_PIN_SIZE) { + if (!size || size % pin_size) { dev_err(info->dev, "Invalid fsl,pins property\n"); return -EINVAL; } - grp->npins = size / FSL_PIN_SIZE; + grp->npins = size / pin_size; grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), GFP_KERNEL); grp->mux_mode = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), @@ -421,10 +445,17 @@ static int imx_pinctrl_parse_groups(struct device_node *np, GFP_KERNEL); for (i = 0; i < grp->npins; i++) { u32 mux_reg = be32_to_cpu(*list++); - u32 conf_reg = be32_to_cpu(*list++); - unsigned int pin_id = mux_reg ? mux_reg / 4 : conf_reg / 4; - struct imx_pin_reg *pin_reg = &info->pin_regs[pin_id]; + u32 conf_reg; + unsigned int pin_id; + struct imx_pin_reg *pin_reg; + if (info->flags & SHARE_MUX_CONF_REG) + conf_reg = mux_reg; + else + conf_reg = be32_to_cpu(*list++); + + pin_id = mux_reg ? mux_reg / 4 : conf_reg / 4; + pin_reg = &info->pin_regs[pin_id]; grp->pins[i] = pin_id; pin_reg->mux_reg = mux_reg; pin_reg->conf_reg = conf_reg; diff --git a/drivers/pinctrl/pinctrl-imx.h b/drivers/pinctrl/pinctrl-imx.h index 607ef5497552..bcedd991c9f3 100644 --- a/drivers/pinctrl/pinctrl-imx.h +++ b/drivers/pinctrl/pinctrl-imx.h @@ -74,8 +74,12 @@ struct imx_pinctrl_soc_info { unsigned int ngroups; struct imx_pmx_func *functions; unsigned int nfunctions; + unsigned int flags; }; +#define ZERO_OFFSET_VALID 0x1 +#define SHARE_MUX_CONF_REG 0x2 + #define NO_MUX 0x0 #define NO_PAD 0x0 diff --git a/drivers/pinctrl/pinctrl-mxs.c b/drivers/pinctrl/pinctrl-mxs.c index b45c4eb35798..f5d56436ba7f 100644 --- a/drivers/pinctrl/pinctrl-mxs.c +++ b/drivers/pinctrl/pinctrl-mxs.c @@ -515,7 +515,6 @@ int mxs_pinctrl_probe(struct platform_device *pdev, return 0; err: - platform_set_drvdata(pdev, NULL); iounmap(d->base); return ret; } @@ -525,7 +524,6 @@ int mxs_pinctrl_remove(struct platform_device *pdev) { struct mxs_pinctrl_data *d = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); pinctrl_unregister(d->pctl); iounmap(d->base); diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 8a4f9c5c0b8e..4a1cfdce2232 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -1309,7 +1309,7 @@ static int nmk_gpio_irq_map(struct irq_domain *d, unsigned int irq, return 0; } -const struct irq_domain_ops nmk_gpio_irq_simple_ops = { +static const struct irq_domain_ops nmk_gpio_irq_simple_ops = { .map = nmk_gpio_irq_map, .xlate = irq_domain_xlate_twocell, }; @@ -1681,7 +1681,7 @@ static bool nmk_pinctrl_dt_get_config(struct device_node *np, return has_config; } -int nmk_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, +static int nmk_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned *reserved_maps, @@ -1740,7 +1740,7 @@ exit: return ret; } -int nmk_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, +static int nmk_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps) { diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c new file mode 100644 index 000000000000..1eb5a2e43b06 --- /dev/null +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -0,0 +1,1394 @@ +/* + * Pinctrl driver for Rockchip SoCs + * + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner <heiko@sntech.de> + * + * With some ideas taken from pinctrl-samsung: + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Copyright (c) 2012 Linaro Ltd + * http://www.linaro.org + * + * and pinctrl-at91: + * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.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. + * + * 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/module.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/bitops.h> +#include <linux/gpio.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/clk-provider.h> +#include <dt-bindings/pinctrl/rockchip.h> + +#include "core.h" +#include "pinconf.h" + +/* GPIO control registers */ +#define GPIO_SWPORT_DR 0x00 +#define GPIO_SWPORT_DDR 0x04 +#define GPIO_INTEN 0x30 +#define GPIO_INTMASK 0x34 +#define GPIO_INTTYPE_LEVEL 0x38 +#define GPIO_INT_POLARITY 0x3c +#define GPIO_INT_STATUS 0x40 +#define GPIO_INT_RAWSTATUS 0x44 +#define GPIO_DEBOUNCE 0x48 +#define GPIO_PORTS_EOI 0x4c +#define GPIO_EXT_PORT 0x50 +#define GPIO_LS_SYNC 0x60 + +/** + * @reg_base: register base of the gpio bank + * @clk: clock of the gpio bank + * @irq: interrupt of the gpio bank + * @pin_base: first pin number + * @nr_pins: number of pins in this bank + * @name: name of the bank + * @bank_num: number of the bank, to account for holes + * @valid: are all necessary informations present + * @of_node: dt node of this bank + * @drvdata: common pinctrl basedata + * @domain: irqdomain of the gpio bank + * @gpio_chip: gpiolib chip + * @grange: gpio range + * @slock: spinlock for the gpio bank + */ +struct rockchip_pin_bank { + void __iomem *reg_base; + struct clk *clk; + int irq; + u32 pin_base; + u8 nr_pins; + char *name; + u8 bank_num; + bool valid; + struct device_node *of_node; + struct rockchip_pinctrl *drvdata; + struct irq_domain *domain; + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range grange; + spinlock_t slock; + +}; + +#define PIN_BANK(id, pins, label) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + } + +/** + * @pull_auto: some SoCs don't allow pulls to be specified as up or down, but + * instead decide this automatically based on the pad-type. + */ +struct rockchip_pin_ctrl { + struct rockchip_pin_bank *pin_banks; + u32 nr_banks; + u32 nr_pins; + char *label; + int mux_offset; + int pull_offset; + bool pull_auto; + int pull_bank_stride; +}; + +struct rockchip_pin_config { + unsigned int func; + unsigned long *configs; + unsigned int nconfigs; +}; + +/** + * struct rockchip_pin_group: represent group of pins of a pinmux function. + * @name: name of the pin group, used to lookup the group. + * @pins: the pins included in this group. + * @npins: number of pins included in this group. + * @func: the mux function number to be programmed when selected. + * @configs: the config values to be set for each pin + * @nconfigs: number of configs for each pin + */ +struct rockchip_pin_group { + const char *name; + unsigned int npins; + unsigned int *pins; + struct rockchip_pin_config *data; +}; + +/** + * struct rockchip_pmx_func: represent a pin function. + * @name: name of the pin function, used to lookup the function. + * @groups: one or more names of pin groups that provide this function. + * @num_groups: number of groups included in @groups. + */ +struct rockchip_pmx_func { + const char *name; + const char **groups; + u8 ngroups; +}; + +struct rockchip_pinctrl { + void __iomem *reg_base; + struct device *dev; + struct rockchip_pin_ctrl *ctrl; + struct pinctrl_desc pctl; + struct pinctrl_dev *pctl_dev; + struct rockchip_pin_group *groups; + unsigned int ngroups; + struct rockchip_pmx_func *functions; + unsigned int nfunctions; +}; + +static inline struct rockchip_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) +{ + return container_of(gc, struct rockchip_pin_bank, gpio_chip); +} + +static const inline struct rockchip_pin_group *pinctrl_name_to_group( + const struct rockchip_pinctrl *info, + const char *name) +{ + const struct rockchip_pin_group *grp = NULL; + int i; + + for (i = 0; i < info->ngroups; i++) { + if (strcmp(info->groups[i].name, name)) + continue; + + grp = &info->groups[i]; + break; + } + + return grp; +} + +/* + * given a pin number that is local to a pin controller, find out the pin bank + * and the register base of the pin bank. + */ +static struct rockchip_pin_bank *pin_to_bank(struct rockchip_pinctrl *info, + unsigned pin) +{ + struct rockchip_pin_bank *b = info->ctrl->pin_banks; + + while ((pin >= b->pin_base) && + ((b->pin_base + b->nr_pins - 1) < pin)) + b++; + + return b; +} + +static struct rockchip_pin_bank *bank_num_to_bank( + struct rockchip_pinctrl *info, + unsigned num) +{ + struct rockchip_pin_bank *b = info->ctrl->pin_banks; + int i; + + for (i = 0; i < info->ctrl->nr_banks; i++) { + if (b->bank_num == num) + break; + + b++; + } + + if (b->bank_num != num) + return ERR_PTR(-EINVAL); + + return b; +} + +/* + * Pinctrl_ops handling + */ + +static int rockchip_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->ngroups; +} + +static const char *rockchip_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->groups[selector].name; +} + +static int rockchip_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, const unsigned **pins, + unsigned *npins) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + if (selector >= info->ngroups) + return -EINVAL; + + *pins = info->groups[selector].pins; + *npins = info->groups[selector].npins; + + return 0; +} + +static int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct rockchip_pin_group *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + int map_num = 1; + int i; + + /* + * first find the group of this node and check if we need to create + * config maps for pins + */ + grp = pinctrl_name_to_group(info, np->name); + if (!grp) { + dev_err(info->dev, "unable to find group for node %s\n", + np->name); + return -EINVAL; + } + + map_num += grp->npins; + new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + *map = new_map; + *num_maps = map_num; + + /* create mux map */ + parent = of_get_parent(np); + if (!parent) { + devm_kfree(pctldev->dev, new_map); + return -EINVAL; + } + new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[0].data.mux.function = parent->name; + new_map[0].data.mux.group = np->name; + of_node_put(parent); + + /* create config map */ + new_map++; + for (i = 0; i < grp->npins; i++) { + new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[i].data.configs.group_or_pin = + pin_get_name(pctldev, grp->pins[i]); + new_map[i].data.configs.configs = grp->data[i].configs; + new_map[i].data.configs.num_configs = grp->data[i].nconfigs; + } + + dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n", + (*map)->data.mux.function, (*map)->data.mux.group, map_num); + + return 0; +} + +static void rockchip_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ +} + +static const struct pinctrl_ops rockchip_pctrl_ops = { + .get_groups_count = rockchip_get_groups_count, + .get_group_name = rockchip_get_group_name, + .get_group_pins = rockchip_get_group_pins, + .dt_node_to_map = rockchip_dt_node_to_map, + .dt_free_map = rockchip_dt_free_map, +}; + +/* + * Hardware access + */ + +/* + * Set a new mux function for a pin. + * + * The register is divided into the upper and lower 16 bit. When changing + * a value, the previous register value is not read and changed. Instead + * it seems the changed bits are marked in the upper 16 bit, while the + * changed value gets set in the same offset in the lower 16 bit. + * All pin settings seem to be 2 bit wide in both the upper and lower + * parts. + * @bank: pin bank to change + * @pin: pin to change + * @mux: new mux function to set + */ +static void rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) +{ + struct rockchip_pinctrl *info = bank->drvdata; + void __iomem *reg = info->reg_base + info->ctrl->mux_offset; + unsigned long flags; + u8 bit; + u32 data; + + dev_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n", + bank->bank_num, pin, mux); + + /* get basic quadrupel of mux registers and the correct reg inside */ + reg += bank->bank_num * 0x10; + reg += (pin / 8) * 4; + bit = (pin % 8) * 2; + + spin_lock_irqsave(&bank->slock, flags); + + data = (3 << (bit + 16)); + data |= (mux & 3) << bit; + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); +} + +static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + void __iomem *reg; + u8 bit; + + /* rk3066b does support any pulls */ + if (!ctrl->pull_offset) + return PIN_CONFIG_BIAS_DISABLE; + + reg = info->reg_base + ctrl->pull_offset; + + if (ctrl->pull_auto) { + reg += bank->bank_num * ctrl->pull_bank_stride; + reg += (pin_num / 16) * 4; + bit = pin_num % 16; + + return !(readl_relaxed(reg) & BIT(bit)) + ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT + : PIN_CONFIG_BIAS_DISABLE; + } else { + dev_err(info->dev, "pull support for rk31xx not implemented\n"); + return -EIO; + } +} + +static int rockchip_set_pull(struct rockchip_pin_bank *bank, + int pin_num, int pull) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + void __iomem *reg; + unsigned long flags; + u8 bit; + u32 data; + + dev_dbg(info->dev, "setting pull of GPIO%d-%d to %d\n", + bank->bank_num, pin_num, pull); + + /* rk3066b does support any pulls */ + if (!ctrl->pull_offset) + return pull ? -EINVAL : 0; + + reg = info->reg_base + ctrl->pull_offset; + + if (ctrl->pull_auto) { + if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT && + pull != PIN_CONFIG_BIAS_DISABLE) { + dev_err(info->dev, "only PIN_DEFAULT and DISABLE allowed\n"); + return -EINVAL; + } + + reg += bank->bank_num * ctrl->pull_bank_stride; + reg += (pin_num / 16) * 4; + bit = pin_num % 16; + + spin_lock_irqsave(&bank->slock, flags); + + data = BIT(bit + 16); + if (pull == PIN_CONFIG_BIAS_DISABLE) + data |= BIT(bit); + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); + } else { + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) { + dev_err(info->dev, "pull direction (up/down) needs to be specified\n"); + return -EINVAL; + } + + dev_err(info->dev, "pull support for rk31xx not implemented\n"); + return -EIO; + } + + return 0; +} + +/* + * Pinmux_ops handling + */ + +static int rockchip_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->nfunctions; +} + +static const char *rockchip_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->functions[selector].name; +} + +static int rockchip_pmx_get_groups(struct pinctrl_dev *pctldev, + unsigned selector, const char * const **groups, + unsigned * const num_groups) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].ngroups; + + return 0; +} + +static int rockchip_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = info->groups[group].pins; + const struct rockchip_pin_config *data = info->groups[group].data; + struct rockchip_pin_bank *bank; + int cnt; + + dev_dbg(info->dev, "enable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + /* + * for each pin in the pin group selected, program the correspoding pin + * pin function number in the config register. + */ + for (cnt = 0; cnt < info->groups[group].npins; cnt++) { + bank = pin_to_bank(info, pins[cnt]); + rockchip_set_mux(bank, pins[cnt] - bank->pin_base, + data[cnt].func); + } + + return 0; +} + +static void rockchip_pmx_disable(struct pinctrl_dev *pctldev, + unsigned selector, unsigned group) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = info->groups[group].pins; + struct rockchip_pin_bank *bank; + int cnt; + + dev_dbg(info->dev, "disable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + for (cnt = 0; cnt < info->groups[group].npins; cnt++) { + bank = pin_to_bank(info, pins[cnt]); + rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); + } +} + +/* + * The calls to gpio_direction_output() and gpio_direction_input() + * leads to this function call (via the pinctrl_gpio_direction_{input|output}() + * function called from the gpiolib interface). + */ +static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, bool input) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank; + struct gpio_chip *chip; + int pin; + u32 data; + + chip = range->gc; + bank = gc_to_pin_bank(chip); + pin = offset - chip->base; + + dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n", + offset, range->name, pin, input ? "input" : "output"); + + rockchip_set_mux(bank, pin, RK_FUNC_GPIO); + + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + /* set bit to 1 for output, 0 for input */ + if (!input) + data |= BIT(pin); + else + data &= ~BIT(pin); + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + + return 0; +} + +static const struct pinmux_ops rockchip_pmx_ops = { + .get_functions_count = rockchip_pmx_get_funcs_count, + .get_function_name = rockchip_pmx_get_func_name, + .get_function_groups = rockchip_pmx_get_groups, + .enable = rockchip_pmx_enable, + .disable = rockchip_pmx_disable, + .gpio_set_direction = rockchip_pmx_gpio_set_direction, +}; + +/* + * Pinconf_ops handling + */ + +static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, + enum pin_config_param pull) +{ + /* rk3066b does support any pulls */ + if (!ctrl->pull_offset) + return pull ? false : true; + + if (ctrl->pull_auto) { + if (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT && + pull != PIN_CONFIG_BIAS_DISABLE) + return false; + } else { + if (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT) + return false; + } + + return true; +} + +/* set the pin config settings for a specified pin */ +static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long config) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank = pin_to_bank(info, pin); + enum pin_config_param param = pinconf_to_config_param(config); + u16 arg = pinconf_to_config_argument(config); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + return rockchip_set_pull(bank, pin - bank->pin_base, param); + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + if (!rockchip_pinconf_pull_valid(info->ctrl, param)) + return -ENOTSUPP; + + if (!arg) + return -EINVAL; + + return rockchip_set_pull(bank, pin - bank->pin_base, param); + break; + default: + return -ENOTSUPP; + break; + } + + return 0; +} + +/* get the pin config settings for a specified pin */ +static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank = pin_to_bank(info, pin); + enum pin_config_param param = pinconf_to_config_param(*config); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (rockchip_get_pull(bank, pin - bank->pin_base) != param) + return -EINVAL; + + *config = 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + if (!rockchip_pinconf_pull_valid(info->ctrl, param)) + return -ENOTSUPP; + + if (rockchip_get_pull(bank, pin - bank->pin_base) != param) + return -EINVAL; + + *config = 1; + break; + default: + return -ENOTSUPP; + break; + } + + return 0; +} + +static const struct pinconf_ops rockchip_pinconf_ops = { + .pin_config_get = rockchip_pinconf_get, + .pin_config_set = rockchip_pinconf_set, +}; + +static const char *gpio_compat = "rockchip,gpio-bank"; + +static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info, + struct device_node *np) +{ + struct device_node *child; + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, gpio_compat)) + continue; + + info->nfunctions++; + info->ngroups += of_get_child_count(child); + } +} + +static int rockchip_pinctrl_parse_groups(struct device_node *np, + struct rockchip_pin_group *grp, + struct rockchip_pinctrl *info, + u32 index) +{ + struct rockchip_pin_bank *bank; + int size; + const __be32 *list; + int num; + int i, j; + int ret; + + dev_dbg(info->dev, "group(%d): %s\n", index, np->name); + + /* Initialise group */ + grp->name = np->name; + + /* + * the binding format is rockchip,pins = <bank pin mux CONFIG>, + * do sanity check and calculate pins number + */ + list = of_get_property(np, "rockchip,pins", &size); + /* we do not check return since it's safe node passed down */ + size /= sizeof(*list); + if (!size || size % 4) { + dev_err(info->dev, "wrong pins number or pins and configs should be by 4\n"); + return -EINVAL; + } + + grp->npins = size / 4; + + grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), + GFP_KERNEL); + grp->data = devm_kzalloc(info->dev, grp->npins * + sizeof(struct rockchip_pin_config), + GFP_KERNEL); + if (!grp->pins || !grp->data) + return -ENOMEM; + + for (i = 0, j = 0; i < size; i += 4, j++) { + const __be32 *phandle; + struct device_node *np_config; + + num = be32_to_cpu(*list++); + bank = bank_num_to_bank(info, num); + if (IS_ERR(bank)) + return PTR_ERR(bank); + + grp->pins[j] = bank->pin_base + be32_to_cpu(*list++); + grp->data[j].func = be32_to_cpu(*list++); + + phandle = list++; + if (!phandle) + return -EINVAL; + + np_config = of_find_node_by_phandle(be32_to_cpup(phandle)); + ret = pinconf_generic_parse_dt_config(np_config, + &grp->data[j].configs, &grp->data[j].nconfigs); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_pinctrl_parse_functions(struct device_node *np, + struct rockchip_pinctrl *info, + u32 index) +{ + struct device_node *child; + struct rockchip_pmx_func *func; + struct rockchip_pin_group *grp; + int ret; + static u32 grp_index; + u32 i = 0; + + dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name); + + func = &info->functions[index]; + + /* Initialise function */ + func->name = np->name; + func->ngroups = of_get_child_count(np); + if (func->ngroups <= 0) + return 0; + + func->groups = devm_kzalloc(info->dev, + func->ngroups * sizeof(char *), GFP_KERNEL); + if (!func->groups) + return -ENOMEM; + + for_each_child_of_node(np, child) { + func->groups[i] = child->name; + grp = &info->groups[grp_index++]; + ret = rockchip_pinctrl_parse_groups(child, grp, info, i++); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_pinctrl_parse_dt(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + int ret; + int i; + + rockchip_pinctrl_child_count(info, np); + + dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); + + info->functions = devm_kzalloc(dev, info->nfunctions * + sizeof(struct rockchip_pmx_func), + GFP_KERNEL); + if (!info->functions) { + dev_err(dev, "failed to allocate memory for function list\n"); + return -EINVAL; + } + + info->groups = devm_kzalloc(dev, info->ngroups * + sizeof(struct rockchip_pin_group), + GFP_KERNEL); + if (!info->groups) { + dev_err(dev, "failed allocate memory for ping group list\n"); + return -EINVAL; + } + + i = 0; + + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, gpio_compat)) + continue; + ret = rockchip_pinctrl_parse_functions(child, info, i++); + if (ret) { + dev_err(&pdev->dev, "failed to parse function\n"); + return ret; + } + } + + return 0; +} + +static int rockchip_pinctrl_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct pinctrl_desc *ctrldesc = &info->pctl; + struct pinctrl_pin_desc *pindesc, *pdesc; + struct rockchip_pin_bank *pin_bank; + int pin, bank, ret; + int k; + + ctrldesc->name = "rockchip-pinctrl"; + ctrldesc->owner = THIS_MODULE; + ctrldesc->pctlops = &rockchip_pctrl_ops; + ctrldesc->pmxops = &rockchip_pmx_ops; + ctrldesc->confops = &rockchip_pinconf_ops; + + pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * + info->ctrl->nr_pins, GFP_KERNEL); + if (!pindesc) { + dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n"); + return -ENOMEM; + } + ctrldesc->pins = pindesc; + ctrldesc->npins = info->ctrl->nr_pins; + + pdesc = pindesc; + for (bank = 0 , k = 0; bank < info->ctrl->nr_banks; bank++) { + pin_bank = &info->ctrl->pin_banks[bank]; + for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) { + pdesc->number = k; + pdesc->name = kasprintf(GFP_KERNEL, "%s-%d", + pin_bank->name, pin); + pdesc++; + } + } + + info->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, info); + if (!info->pctl_dev) { + dev_err(&pdev->dev, "could not register pinctrl driver\n"); + return -EINVAL; + } + + for (bank = 0; bank < info->ctrl->nr_banks; ++bank) { + pin_bank = &info->ctrl->pin_banks[bank]; + pin_bank->grange.name = pin_bank->name; + pin_bank->grange.id = bank; + pin_bank->grange.pin_base = pin_bank->pin_base; + pin_bank->grange.base = pin_bank->gpio_chip.base; + pin_bank->grange.npins = pin_bank->gpio_chip.ngpio; + pin_bank->grange.gc = &pin_bank->gpio_chip; + pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange); + } + + ret = rockchip_pinctrl_parse_dt(pdev, info); + if (ret) { + pinctrl_unregister(info->pctl_dev); + return ret; + } + + return 0; +} + +/* + * GPIO handling + */ + +static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR; + unsigned long flags; + u32 data; + + spin_lock_irqsave(&bank->slock, flags); + + data = readl(reg); + data &= ~BIT(offset); + if (value) + data |= BIT(offset); + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); +} + +/* + * Returns the level of the pin for input direction and setting of the DR + * register for output gpios. + */ +static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + u32 data; + + data = readl(bank->reg_base + GPIO_EXT_PORT); + data >>= offset; + data &= 1; + return data; +} + +/* + * gpiolib gpio_direction_input callback function. The setting of the pin + * mux function as 'gpio input' will be handled by the pinctrl susbsystem + * interface. + */ +static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_gpio_direction_input(gc->base + offset); +} + +/* + * gpiolib gpio_direction_output callback function. The setting of the pin + * mux function as 'gpio output' will be handled by the pinctrl susbsystem + * interface. + */ +static int rockchip_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + rockchip_gpio_set(gc, offset, value); + return pinctrl_gpio_direction_output(gc->base + offset); +} + +/* + * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin + * and a virtual IRQ, if not already present. + */ +static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + unsigned int virq; + + if (!bank->domain) + return -ENXIO; + + virq = irq_create_mapping(bank->domain, offset); + + return (virq) ? : -ENXIO; +} + +static const struct gpio_chip rockchip_gpiolib_chip = { + .set = rockchip_gpio_set, + .get = rockchip_gpio_get, + .direction_input = rockchip_gpio_direction_input, + .direction_output = rockchip_gpio_direction_output, + .to_irq = rockchip_gpio_to_irq, + .owner = THIS_MODULE, +}; + +/* + * Interrupt handling + */ + +static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct rockchip_pin_bank *bank = irq_get_handler_data(irq); + u32 pend; + + dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name); + + chained_irq_enter(chip, desc); + + pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS); + + while (pend) { + unsigned int virq; + + irq = __ffs(pend); + pend &= ~BIT(irq); + virq = irq_linear_revmap(bank->domain, irq); + + if (!virq) { + dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq); + continue; + } + + dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq); + + generic_handle_irq(virq); + } + + chained_irq_exit(chip, desc); +} + +static int rockchip_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct rockchip_pin_bank *bank = gc->private; + u32 mask = BIT(d->hwirq); + u32 polarity; + u32 level; + u32 data; + + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(d->irq, handle_edge_irq); + else + __irq_set_handler_locked(d->irq, handle_level_irq); + + irq_gc_lock(gc); + + level = readl_relaxed(gc->reg_base + GPIO_INTTYPE_LEVEL); + polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + level |= mask; + polarity |= mask; + break; + case IRQ_TYPE_EDGE_FALLING: + level |= mask; + polarity &= ~mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + level &= ~mask; + polarity |= mask; + break; + case IRQ_TYPE_LEVEL_LOW: + level &= ~mask; + polarity &= ~mask; + break; + default: + irq_gc_unlock(gc); + return -EINVAL; + } + + writel_relaxed(level, gc->reg_base + GPIO_INTTYPE_LEVEL); + writel_relaxed(polarity, gc->reg_base + GPIO_INT_POLARITY); + + irq_gc_unlock(gc); + + /* make sure the pin is configured as gpio input */ + rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO); + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + data &= ~mask; + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + + return 0; +} + +static int rockchip_interrupts_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + struct irq_chip_generic *gc; + int ret; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) { + dev_warn(&pdev->dev, "bank %s is not valid\n", + bank->name); + continue; + } + + bank->domain = irq_domain_add_linear(bank->of_node, 32, + &irq_generic_chip_ops, NULL); + if (!bank->domain) { + dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n", + bank->name); + continue; + } + + ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1, + "rockchip_gpio_irq", handle_level_irq, + clr, 0, IRQ_GC_INIT_MASK_CACHE); + if (ret) { + dev_err(&pdev->dev, "could not alloc generic chips for bank %s\n", + bank->name); + irq_domain_remove(bank->domain); + continue; + } + + gc = irq_get_domain_generic_chip(bank->domain, 0); + gc->reg_base = bank->reg_base; + gc->private = bank; + gc->chip_types[0].regs.mask = GPIO_INTEN; + gc->chip_types[0].regs.ack = GPIO_PORTS_EOI; + gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; + gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; + gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type; + + irq_set_handler_data(bank->irq, bank); + irq_set_chained_handler(bank->irq, rockchip_irq_demux); + } + + return 0; +} + +static int rockchip_gpiolib_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + struct gpio_chip *gc; + int ret; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) { + dev_warn(&pdev->dev, "bank %s is not valid\n", + bank->name); + continue; + } + + bank->gpio_chip = rockchip_gpiolib_chip; + + gc = &bank->gpio_chip; + gc->base = bank->pin_base; + gc->ngpio = bank->nr_pins; + gc->dev = &pdev->dev; + gc->of_node = bank->of_node; + gc->label = bank->name; + + ret = gpiochip_add(gc); + if (ret) { + dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n", + gc->label, ret); + goto fail; + } + } + + rockchip_interrupts_register(pdev, info); + + return 0; + +fail: + for (--i, --bank; i >= 0; --i, --bank) { + if (!bank->valid) + continue; + + if (gpiochip_remove(&bank->gpio_chip)) + dev_err(&pdev->dev, "gpio chip %s remove failed\n", + bank->gpio_chip.label); + } + return ret; +} + +static int rockchip_gpiolib_unregister(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int ret = 0; + int i; + + for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) + continue; + + ret = gpiochip_remove(&bank->gpio_chip); + } + + if (ret) + dev_err(&pdev->dev, "gpio chip remove failed\n"); + + return ret; +} + +static int rockchip_get_bank_data(struct rockchip_pin_bank *bank, + struct device *dev) +{ + struct resource res; + + if (of_address_to_resource(bank->of_node, 0, &res)) { + dev_err(dev, "cannot find IO resource for bank\n"); + return -ENOENT; + } + + bank->reg_base = devm_ioremap_resource(dev, &res); + if (IS_ERR(bank->reg_base)) + return PTR_ERR(bank->reg_base); + + bank->irq = irq_of_parse_and_map(bank->of_node, 0); + + bank->clk = of_clk_get(bank->of_node, 0); + if (IS_ERR(bank->clk)) + return PTR_ERR(bank->clk); + + return clk_prepare_enable(bank->clk); +} + +static const struct of_device_id rockchip_pinctrl_dt_match[]; + +/* retrieve the soc specific data */ +static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( + struct rockchip_pinctrl *d, + struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device_node *node = pdev->dev.of_node; + struct device_node *np; + struct rockchip_pin_ctrl *ctrl; + struct rockchip_pin_bank *bank; + int i; + + match = of_match_node(rockchip_pinctrl_dt_match, node); + ctrl = (struct rockchip_pin_ctrl *)match->data; + + for_each_child_of_node(node, np) { + if (!of_find_property(np, "gpio-controller", NULL)) + continue; + + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!strcmp(bank->name, np->name)) { + bank->of_node = np; + + if (!rockchip_get_bank_data(bank, &pdev->dev)) + bank->valid = true; + + break; + } + } + } + + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + spin_lock_init(&bank->slock); + bank->drvdata = d; + bank->pin_base = ctrl->nr_pins; + ctrl->nr_pins += bank->nr_pins; + } + + return ctrl; +} + +static int rockchip_pinctrl_probe(struct platform_device *pdev) +{ + struct rockchip_pinctrl *info; + struct device *dev = &pdev->dev; + struct rockchip_pin_ctrl *ctrl; + struct resource *res; + int ret; + + if (!dev->of_node) { + dev_err(dev, "device tree node not found\n"); + return -ENODEV; + } + + info = devm_kzalloc(dev, sizeof(struct rockchip_pinctrl), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ctrl = rockchip_pinctrl_get_soc_data(info, pdev); + if (!ctrl) { + dev_err(dev, "driver data not available\n"); + return -EINVAL; + } + info->ctrl = ctrl; + info->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "cannot find IO resource\n"); + return -ENOENT; + } + + info->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->reg_base)) + return PTR_ERR(info->reg_base); + + ret = rockchip_gpiolib_register(pdev, info); + if (ret) + return ret; + + ret = rockchip_pinctrl_register(pdev, info); + if (ret) { + rockchip_gpiolib_unregister(pdev, info); + return ret; + } + + platform_set_drvdata(pdev, info); + + return 0; +} + +static struct rockchip_pin_bank rk2928_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk2928_pin_ctrl = { + .pin_banks = rk2928_pin_banks, + .nr_banks = ARRAY_SIZE(rk2928_pin_banks), + .label = "RK2928-GPIO", + .mux_offset = 0xa8, + .pull_offset = 0x118, + .pull_auto = 1, + .pull_bank_stride = 8, +}; + +static struct rockchip_pin_bank rk3066a_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), + PIN_BANK(4, 32, "gpio4"), + PIN_BANK(6, 16, "gpio6"), +}; + +static struct rockchip_pin_ctrl rk3066a_pin_ctrl = { + .pin_banks = rk3066a_pin_banks, + .nr_banks = ARRAY_SIZE(rk3066a_pin_banks), + .label = "RK3066a-GPIO", + .mux_offset = 0xa8, + .pull_offset = 0x118, + .pull_auto = 1, + .pull_bank_stride = 8, +}; + +static struct rockchip_pin_bank rk3066b_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk3066b_pin_ctrl = { + .pin_banks = rk3066b_pin_banks, + .nr_banks = ARRAY_SIZE(rk3066b_pin_banks), + .label = "RK3066b-GPIO", + .mux_offset = 0x60, + .pull_offset = -EINVAL, +}; + +static struct rockchip_pin_bank rk3188_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk3188_pin_ctrl = { + .pin_banks = rk3188_pin_banks, + .nr_banks = ARRAY_SIZE(rk3188_pin_banks), + .label = "RK3188-GPIO", + .mux_offset = 0x68, + .pull_offset = 0x164, + .pull_bank_stride = 16, +}; + +static const struct of_device_id rockchip_pinctrl_dt_match[] = { + { .compatible = "rockchip,rk2928-pinctrl", + .data = (void *)&rk2928_pin_ctrl }, + { .compatible = "rockchip,rk3066a-pinctrl", + .data = (void *)&rk3066a_pin_ctrl }, + { .compatible = "rockchip,rk3066b-pinctrl", + .data = (void *)&rk3066b_pin_ctrl }, + { .compatible = "rockchip,rk3188-pinctrl", + .data = (void *)&rk3188_pin_ctrl }, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match); + +static struct platform_driver rockchip_pinctrl_driver = { + .probe = rockchip_pinctrl_probe, + .driver = { + .name = "rockchip-pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rockchip_pinctrl_dt_match), + }, +}; + +static int __init rockchip_pinctrl_drv_register(void) +{ + return platform_driver_register(&rockchip_pinctrl_driver); +} +postcore_initcall(rockchip_pinctrl_drv_register); + +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_DESCRIPTION("Rockchip pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 0a6c720b6655..a7fa9e2d4751 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -50,7 +50,7 @@ static struct pin_config { }; /* Global list of devices (struct samsung_pinctrl_drv_data) */ -LIST_HEAD(drvdata_list); +static LIST_HEAD(drvdata_list); static unsigned int pin_base; diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index b9fa04618601..6866548fab31 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -30,7 +30,7 @@ #define DRIVER_NAME "pinctrl-single" #define PCS_MUX_PINS_NAME "pinctrl-single,pins" #define PCS_MUX_BITS_NAME "pinctrl-single,bits" -#define PCS_REG_NAME_LEN ((sizeof(unsigned long) * 2) + 1) +#define PCS_REG_NAME_LEN ((sizeof(unsigned long) * 2) + 3) #define PCS_OFF_DISABLED ~0U /** @@ -163,6 +163,7 @@ struct pcs_name { * @foff: value to turn mux off * @fmax: max number of functions in fmask * @is_pinconf: whether supports pinconf + * @bits_per_pin:number of bits per pin * @names: array of register names for pins * @pins: physical pins on the SoC * @pgtree: pingroup index radix tree @@ -190,6 +191,7 @@ struct pcs_device { unsigned fmax; bool bits_per_mux; bool is_pinconf; + unsigned bits_per_pin; struct pcs_name *names; struct pcs_data pins; struct radix_tree_root pgtree; @@ -431,10 +433,11 @@ static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, vals = &func->vals[i]; val = pcs->read(vals->reg); - if (!vals->mask) - mask = pcs->fmask; + + if (pcs->bits_per_mux) + mask = vals->mask; else - mask = pcs->fmask & vals->mask; + mask = pcs->fmask; val &= ~mask; val |= (vals->val & mask); @@ -741,7 +744,8 @@ static const struct pinconf_ops pcs_pinconf_ops = { * @pcs: pcs driver instance * @offset: register offset from base */ -static int pcs_add_pin(struct pcs_device *pcs, unsigned offset) +static int pcs_add_pin(struct pcs_device *pcs, unsigned offset, + unsigned pin_pos) { struct pinctrl_pin_desc *pin; struct pcs_name *pn; @@ -756,8 +760,8 @@ static int pcs_add_pin(struct pcs_device *pcs, unsigned offset) pin = &pcs->pins.pa[i]; pn = &pcs->names[i]; - sprintf(pn->name, "%lx", - (unsigned long)pcs->res->start + offset); + sprintf(pn->name, "%lx.%d", + (unsigned long)pcs->res->start + offset, pin_pos); pin->name = pn->name; pin->number = i; pcs->pins.cur++; @@ -777,9 +781,17 @@ static int pcs_add_pin(struct pcs_device *pcs, unsigned offset) static int pcs_allocate_pin_table(struct pcs_device *pcs) { int mux_bytes, nr_pins, i; + int num_pins_in_register = 0; mux_bytes = pcs->width / BITS_PER_BYTE; - nr_pins = pcs->size / mux_bytes; + + if (pcs->bits_per_mux) { + pcs->bits_per_pin = fls(pcs->fmask); + nr_pins = (pcs->size * BITS_PER_BYTE) / pcs->bits_per_pin; + num_pins_in_register = pcs->width / pcs->bits_per_pin; + } else { + nr_pins = pcs->size / mux_bytes; + } dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins); pcs->pins.pa = devm_kzalloc(pcs->dev, @@ -800,9 +812,17 @@ static int pcs_allocate_pin_table(struct pcs_device *pcs) for (i = 0; i < pcs->desc.npins; i++) { unsigned offset; int res; - - offset = i * mux_bytes; - res = pcs_add_pin(pcs, offset); + int byte_num; + int pin_pos = 0; + + if (pcs->bits_per_mux) { + byte_num = (pcs->bits_per_pin * i) / BITS_PER_BYTE; + offset = (byte_num / mux_bytes) * mux_bytes; + pin_pos = i % num_pins_in_register; + } else { + offset = i * mux_bytes; + } + res = pcs_add_pin(pcs, offset, pin_pos); if (res < 0) { dev_err(pcs->dev, "error adding pins: %i\n", res); return res; @@ -919,7 +939,10 @@ static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset) return -EINVAL; } - index = offset / (pcs->width / BITS_PER_BYTE); + if (pcs->bits_per_mux) + index = (offset * BITS_PER_BYTE) / pcs->bits_per_pin; + else + index = offset / (pcs->width / BITS_PER_BYTE); return index; } @@ -1097,29 +1120,18 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, { struct pcs_func_vals *vals; const __be32 *mux; - int size, params, rows, *pins, index = 0, found = 0, res = -ENOMEM; + int size, rows, *pins, index = 0, found = 0, res = -ENOMEM; struct pcs_function *function; - if (pcs->bits_per_mux) { - params = 3; - mux = of_get_property(np, PCS_MUX_BITS_NAME, &size); - } else { - params = 2; - mux = of_get_property(np, PCS_MUX_PINS_NAME, &size); - } - - if (!mux) { - dev_err(pcs->dev, "no valid property for %s\n", np->name); - return -EINVAL; - } - - if (size < (sizeof(*mux) * params)) { - dev_err(pcs->dev, "bad data for %s\n", np->name); + mux = of_get_property(np, PCS_MUX_PINS_NAME, &size); + if ((!mux) || (size < sizeof(*mux) * 2)) { + dev_err(pcs->dev, "bad data for mux %s\n", + np->name); return -EINVAL; } size /= sizeof(*mux); /* Number of elements in array */ - rows = size / params; + rows = size / 2; vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL); if (!vals) @@ -1137,10 +1149,6 @@ static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, val = be32_to_cpup(mux + index++); vals[found].reg = pcs->base + offset; vals[found].val = val; - if (params == 3) { - val = be32_to_cpup(mux + index++); - vals[found].mask = val; - } pin = pcs_get_pin_by_offset(pcs, offset); if (pin < 0) { @@ -1189,6 +1197,125 @@ free_vals: return res; } + +#define PARAMS_FOR_BITS_PER_MUX 3 + +static int pcs_parse_bits_in_pinctrl_entry(struct pcs_device *pcs, + struct device_node *np, + struct pinctrl_map **map, + unsigned *num_maps, + const char **pgnames) +{ + struct pcs_func_vals *vals; + const __be32 *mux; + int size, rows, *pins, index = 0, found = 0, res = -ENOMEM; + int npins_in_row; + struct pcs_function *function; + + mux = of_get_property(np, PCS_MUX_BITS_NAME, &size); + + if (!mux) { + dev_err(pcs->dev, "no valid property for %s\n", np->name); + return -EINVAL; + } + + if (size < (sizeof(*mux) * PARAMS_FOR_BITS_PER_MUX)) { + dev_err(pcs->dev, "bad data for %s\n", np->name); + return -EINVAL; + } + + /* Number of elements in array */ + size /= sizeof(*mux); + + rows = size / PARAMS_FOR_BITS_PER_MUX; + npins_in_row = pcs->width / pcs->bits_per_pin; + + vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows * npins_in_row, + GFP_KERNEL); + if (!vals) + return -ENOMEM; + + pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows * npins_in_row, + GFP_KERNEL); + if (!pins) + goto free_vals; + + while (index < size) { + unsigned offset, val; + unsigned mask, bit_pos, val_pos, mask_pos, submask; + unsigned pin_num_from_lsb; + int pin; + + offset = be32_to_cpup(mux + index++); + val = be32_to_cpup(mux + index++); + mask = be32_to_cpup(mux + index++); + + /* Parse pins in each row from LSB */ + while (mask) { + bit_pos = ffs(mask); + pin_num_from_lsb = bit_pos / pcs->bits_per_pin; + mask_pos = ((pcs->fmask) << (bit_pos - 1)); + val_pos = val & mask_pos; + submask = mask & mask_pos; + mask &= ~mask_pos; + + if (submask != mask_pos) { + dev_warn(pcs->dev, + "Invalid submask 0x%x for %s at 0x%x\n", + submask, np->name, offset); + continue; + } + + vals[found].mask = submask; + vals[found].reg = pcs->base + offset; + vals[found].val = val_pos; + + pin = pcs_get_pin_by_offset(pcs, offset); + if (pin < 0) { + dev_err(pcs->dev, + "could not add functions for %s %ux\n", + np->name, offset); + break; + } + pins[found++] = pin + pin_num_from_lsb; + } + } + + pgnames[0] = np->name; + function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1); + if (!function) + goto free_pins; + + res = pcs_add_pingroup(pcs, np, np->name, pins, found); + if (res < 0) + goto free_function; + + (*map)->type = PIN_MAP_TYPE_MUX_GROUP; + (*map)->data.mux.group = np->name; + (*map)->data.mux.function = np->name; + + if (pcs->is_pinconf) { + dev_err(pcs->dev, "pinconf not supported\n"); + goto free_pingroups; + } + + *num_maps = 1; + return 0; + +free_pingroups: + pcs_free_pingroups(pcs); + *num_maps = 1; +free_function: + pcs_remove_function(pcs, function); + +free_pins: + devm_kfree(pcs->dev, pins); + +free_vals: + devm_kfree(pcs->dev, vals); + + return res; +} /** * pcs_dt_node_to_map() - allocates and parses pinctrl maps * @pctldev: pinctrl instance @@ -1219,12 +1346,22 @@ static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, goto free_map; } - ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, num_maps, - pgnames); - if (ret < 0) { - dev_err(pcs->dev, "no pins entries for %s\n", - np_config->name); - goto free_pgnames; + if (pcs->bits_per_mux) { + ret = pcs_parse_bits_in_pinctrl_entry(pcs, np_config, map, + num_maps, pgnames); + if (ret < 0) { + dev_err(pcs->dev, "no pins entries for %s\n", + np_config->name); + goto free_pgnames; + } + } else { + ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, + num_maps, pgnames); + if (ret < 0) { + dev_err(pcs->dev, "no pins entries for %s\n", + np_config->name); + goto free_pgnames; + } } return 0; @@ -1346,6 +1483,29 @@ static int pcs_add_gpio_func(struct device_node *node, struct pcs_device *pcs) return ret; } +static int pinctrl_single_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct pcs_device *pcs; + + pcs = platform_get_drvdata(pdev); + if (!pcs) + return -EINVAL; + + return pinctrl_force_sleep(pcs->pctl); +} + +static int pinctrl_single_resume(struct platform_device *pdev) +{ + struct pcs_device *pcs; + + pcs = platform_get_drvdata(pdev); + if (!pcs) + return -EINVAL; + + return pinctrl_force_default(pcs->pctl); +} + static int pcs_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1494,6 +1654,10 @@ static struct platform_driver pcs_driver = { .name = DRIVER_NAME, .of_match_table = pcs_of_match, }, +#ifdef CONFIG_PM + .suspend = pinctrl_single_suspend, + .resume = pinctrl_single_resume, +#endif }; module_platform_driver(pcs_driver); diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c new file mode 100644 index 000000000000..04d4506ae18d --- /dev/null +++ b/drivers/pinctrl/pinctrl-st.c @@ -0,0 +1,1403 @@ +/* + * Copyright (C) 2013 STMicroelectronics (R&D) Limited. + * Authors: + * Srinivas Kandagatla <srinivas.kandagatla@st.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/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_address.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/platform_device.h> +#include "core.h" + +/* PIO Block registers */ +/* PIO output */ +#define REG_PIO_POUT 0x00 +/* Set bits of POUT */ +#define REG_PIO_SET_POUT 0x04 +/* Clear bits of POUT */ +#define REG_PIO_CLR_POUT 0x08 +/* PIO input */ +#define REG_PIO_PIN 0x10 +/* PIO configuration */ +#define REG_PIO_PC(n) (0x20 + (n) * 0x10) +/* Set bits of PC[2:0] */ +#define REG_PIO_SET_PC(n) (0x24 + (n) * 0x10) +/* Clear bits of PC[2:0] */ +#define REG_PIO_CLR_PC(n) (0x28 + (n) * 0x10) +/* PIO input comparison */ +#define REG_PIO_PCOMP 0x50 +/* Set bits of PCOMP */ +#define REG_PIO_SET_PCOMP 0x54 +/* Clear bits of PCOMP */ +#define REG_PIO_CLR_PCOMP 0x58 +/* PIO input comparison mask */ +#define REG_PIO_PMASK 0x60 +/* Set bits of PMASK */ +#define REG_PIO_SET_PMASK 0x64 +/* Clear bits of PMASK */ +#define REG_PIO_CLR_PMASK 0x68 + +#define ST_GPIO_DIRECTION_BIDIR 0x1 +#define ST_GPIO_DIRECTION_OUT 0x2 +#define ST_GPIO_DIRECTION_IN 0x4 + +/** + * Packed style retime configuration. + * There are two registers cfg0 and cfg1 in this style for each bank. + * Each field in this register is 8 bit corresponding to 8 pins in the bank. + */ +#define RT_P_CFGS_PER_BANK 2 +#define RT_P_CFG0_CLK1NOTCLK0_FIELD(reg) REG_FIELD(reg, 0, 7) +#define RT_P_CFG0_DELAY_0_FIELD(reg) REG_FIELD(reg, 16, 23) +#define RT_P_CFG0_DELAY_1_FIELD(reg) REG_FIELD(reg, 24, 31) +#define RT_P_CFG1_INVERTCLK_FIELD(reg) REG_FIELD(reg, 0, 7) +#define RT_P_CFG1_RETIME_FIELD(reg) REG_FIELD(reg, 8, 15) +#define RT_P_CFG1_CLKNOTDATA_FIELD(reg) REG_FIELD(reg, 16, 23) +#define RT_P_CFG1_DOUBLE_EDGE_FIELD(reg) REG_FIELD(reg, 24, 31) + +/** + * Dedicated style retime Configuration register + * each register is dedicated per pin. + */ +#define RT_D_CFGS_PER_BANK 8 +#define RT_D_CFG_CLK_SHIFT 0 +#define RT_D_CFG_CLK_MASK (0x3 << 0) +#define RT_D_CFG_CLKNOTDATA_SHIFT 2 +#define RT_D_CFG_CLKNOTDATA_MASK BIT(2) +#define RT_D_CFG_DELAY_SHIFT 3 +#define RT_D_CFG_DELAY_MASK (0xf << 3) +#define RT_D_CFG_DELAY_INNOTOUT_SHIFT 7 +#define RT_D_CFG_DELAY_INNOTOUT_MASK BIT(7) +#define RT_D_CFG_DOUBLE_EDGE_SHIFT 8 +#define RT_D_CFG_DOUBLE_EDGE_MASK BIT(8) +#define RT_D_CFG_INVERTCLK_SHIFT 9 +#define RT_D_CFG_INVERTCLK_MASK BIT(9) +#define RT_D_CFG_RETIME_SHIFT 10 +#define RT_D_CFG_RETIME_MASK BIT(10) + +/* + * Pinconf is represented in an opaque unsigned long variable. + * Below is the bit allocation details for each possible configuration. + * All the bit fields can be encapsulated into four variables + * (direction, retime-type, retime-clk, retime-delay) + * + * +----------------+ + *[31:28]| reserved-3 | + * +----------------+------------- + *[27] | oe | | + * +----------------+ v + *[26] | pu | [Direction ] + * +----------------+ ^ + *[25] | od | | + * +----------------+------------- + *[24] | reserved-2 | + * +----------------+------------- + *[23] | retime | | + * +----------------+ | + *[22] | retime-invclk | | + * +----------------+ v + *[21] |retime-clknotdat| [Retime-type ] + * +----------------+ ^ + *[20] | retime-de | | + * +----------------+------------- + *[19:18]| retime-clk |------>[Retime-Clk ] + * +----------------+ + *[17:16]| reserved-1 | + * +----------------+ + *[15..0]| retime-delay |------>[Retime Delay] + * +----------------+ + */ + +#define ST_PINCONF_UNPACK(conf, param)\ + ((conf >> ST_PINCONF_ ##param ##_SHIFT) \ + & ST_PINCONF_ ##param ##_MASK) + +#define ST_PINCONF_PACK(conf, val, param) (conf |=\ + ((val & ST_PINCONF_ ##param ##_MASK) << \ + ST_PINCONF_ ##param ##_SHIFT)) + +/* Output enable */ +#define ST_PINCONF_OE_MASK 0x1 +#define ST_PINCONF_OE_SHIFT 27 +#define ST_PINCONF_OE BIT(27) +#define ST_PINCONF_UNPACK_OE(conf) ST_PINCONF_UNPACK(conf, OE) +#define ST_PINCONF_PACK_OE(conf) ST_PINCONF_PACK(conf, 1, OE) + +/* Pull Up */ +#define ST_PINCONF_PU_MASK 0x1 +#define ST_PINCONF_PU_SHIFT 26 +#define ST_PINCONF_PU BIT(26) +#define ST_PINCONF_UNPACK_PU(conf) ST_PINCONF_UNPACK(conf, PU) +#define ST_PINCONF_PACK_PU(conf) ST_PINCONF_PACK(conf, 1, PU) + +/* Open Drain */ +#define ST_PINCONF_OD_MASK 0x1 +#define ST_PINCONF_OD_SHIFT 25 +#define ST_PINCONF_OD BIT(25) +#define ST_PINCONF_UNPACK_OD(conf) ST_PINCONF_UNPACK(conf, OD) +#define ST_PINCONF_PACK_OD(conf) ST_PINCONF_PACK(conf, 1, OD) + +#define ST_PINCONF_RT_MASK 0x1 +#define ST_PINCONF_RT_SHIFT 23 +#define ST_PINCONF_RT BIT(23) +#define ST_PINCONF_UNPACK_RT(conf) ST_PINCONF_UNPACK(conf, RT) +#define ST_PINCONF_PACK_RT(conf) ST_PINCONF_PACK(conf, 1, RT) + +#define ST_PINCONF_RT_INVERTCLK_MASK 0x1 +#define ST_PINCONF_RT_INVERTCLK_SHIFT 22 +#define ST_PINCONF_RT_INVERTCLK BIT(22) +#define ST_PINCONF_UNPACK_RT_INVERTCLK(conf) \ + ST_PINCONF_UNPACK(conf, RT_INVERTCLK) +#define ST_PINCONF_PACK_RT_INVERTCLK(conf) \ + ST_PINCONF_PACK(conf, 1, RT_INVERTCLK) + +#define ST_PINCONF_RT_CLKNOTDATA_MASK 0x1 +#define ST_PINCONF_RT_CLKNOTDATA_SHIFT 21 +#define ST_PINCONF_RT_CLKNOTDATA BIT(21) +#define ST_PINCONF_UNPACK_RT_CLKNOTDATA(conf) \ + ST_PINCONF_UNPACK(conf, RT_CLKNOTDATA) +#define ST_PINCONF_PACK_RT_CLKNOTDATA(conf) \ + ST_PINCONF_PACK(conf, 1, RT_CLKNOTDATA) + +#define ST_PINCONF_RT_DOUBLE_EDGE_MASK 0x1 +#define ST_PINCONF_RT_DOUBLE_EDGE_SHIFT 20 +#define ST_PINCONF_RT_DOUBLE_EDGE BIT(20) +#define ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(conf) \ + ST_PINCONF_UNPACK(conf, RT_DOUBLE_EDGE) +#define ST_PINCONF_PACK_RT_DOUBLE_EDGE(conf) \ + ST_PINCONF_PACK(conf, 1, RT_DOUBLE_EDGE) + +#define ST_PINCONF_RT_CLK_MASK 0x3 +#define ST_PINCONF_RT_CLK_SHIFT 18 +#define ST_PINCONF_RT_CLK BIT(18) +#define ST_PINCONF_UNPACK_RT_CLK(conf) ST_PINCONF_UNPACK(conf, RT_CLK) +#define ST_PINCONF_PACK_RT_CLK(conf, val) ST_PINCONF_PACK(conf, val, RT_CLK) + +/* RETIME_DELAY in Pico Secs */ +#define ST_PINCONF_RT_DELAY_MASK 0xffff +#define ST_PINCONF_RT_DELAY_SHIFT 0 +#define ST_PINCONF_UNPACK_RT_DELAY(conf) ST_PINCONF_UNPACK(conf, RT_DELAY) +#define ST_PINCONF_PACK_RT_DELAY(conf, val) \ + ST_PINCONF_PACK(conf, val, RT_DELAY) + +#define ST_GPIO_PINS_PER_BANK (8) +#define OF_GPIO_ARGS_MIN (4) +#define OF_RT_ARGS_MIN (2) + +#define gpio_range_to_bank(chip) \ + container_of(chip, struct st_gpio_bank, range) + +#define gpio_chip_to_bank(chip) \ + container_of(chip, struct st_gpio_bank, gpio_chip) + + +enum st_retime_style { + st_retime_style_none, + st_retime_style_packed, + st_retime_style_dedicated, +}; + +struct st_retime_dedicated { + struct regmap_field *rt[ST_GPIO_PINS_PER_BANK]; +}; + +struct st_retime_packed { + struct regmap_field *clk1notclk0; + struct regmap_field *delay_0; + struct regmap_field *delay_1; + struct regmap_field *invertclk; + struct regmap_field *retime; + struct regmap_field *clknotdata; + struct regmap_field *double_edge; +}; + +struct st_pio_control { + u32 rt_pin_mask; + struct regmap_field *alt, *oe, *pu, *od; + /* retiming */ + union { + struct st_retime_packed rt_p; + struct st_retime_dedicated rt_d; + } rt; +}; + +struct st_pctl_data { + enum st_retime_style rt_style; + unsigned int *input_delays; + int ninput_delays; + unsigned int *output_delays; + int noutput_delays; + /* register offset information */ + int alt, oe, pu, od, rt; +}; + +struct st_pinconf { + int pin; + const char *name; + unsigned long config; + int altfunc; +}; + +struct st_pmx_func { + const char *name; + const char **groups; + unsigned ngroups; +}; + +struct st_pctl_group { + const char *name; + unsigned int *pins; + unsigned npins; + struct st_pinconf *pin_conf; +}; + +struct st_gpio_bank { + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range range; + void __iomem *base; + struct st_pio_control pc; +}; + +struct st_pinctrl { + struct device *dev; + struct pinctrl_dev *pctl; + struct st_gpio_bank *banks; + int nbanks; + struct st_pmx_func *functions; + int nfunctions; + struct st_pctl_group *groups; + int ngroups; + struct regmap *regmap; + const struct st_pctl_data *data; +}; + +/* SOC specific data */ +/* STiH415 data */ +unsigned int stih415_input_delays[] = {0, 500, 1000, 1500}; +unsigned int stih415_output_delays[] = {0, 1000, 2000, 3000}; + +#define STIH415_PCTRL_COMMON_DATA \ + .rt_style = st_retime_style_packed, \ + .input_delays = stih415_input_delays, \ + .ninput_delays = 4, \ + .output_delays = stih415_output_delays, \ + .noutput_delays = 4 + +static const struct st_pctl_data stih415_sbc_data = { + STIH415_PCTRL_COMMON_DATA, + .alt = 0, .oe = 5, .pu = 7, .od = 9, .rt = 16, +}; + +static const struct st_pctl_data stih415_front_data = { + STIH415_PCTRL_COMMON_DATA, + .alt = 0, .oe = 8, .pu = 10, .od = 12, .rt = 16, +}; + +static const struct st_pctl_data stih415_rear_data = { + STIH415_PCTRL_COMMON_DATA, + .alt = 0, .oe = 6, .pu = 8, .od = 10, .rt = 38, +}; + +static const struct st_pctl_data stih415_left_data = { + STIH415_PCTRL_COMMON_DATA, + .alt = 0, .oe = 3, .pu = 4, .od = 5, .rt = 6, +}; + +static const struct st_pctl_data stih415_right_data = { + STIH415_PCTRL_COMMON_DATA, + .alt = 0, .oe = 5, .pu = 7, .od = 9, .rt = 11, +}; + +/* STiH416 data */ +unsigned int stih416_delays[] = {0, 300, 500, 750, 1000, 1250, 1500, + 1750, 2000, 2250, 2500, 2750, 3000, 3250 }; + +static const struct st_pctl_data stih416_data = { + .rt_style = st_retime_style_dedicated, + .input_delays = stih416_delays, + .ninput_delays = 14, + .output_delays = stih416_delays, + .noutput_delays = 14, + .alt = 0, .oe = 40, .pu = 50, .od = 60, .rt = 100, +}; + +/* Low level functions.. */ +static inline int st_gpio_bank(int gpio) +{ + return gpio/ST_GPIO_PINS_PER_BANK; +} + +static inline int st_gpio_pin(int gpio) +{ + return gpio%ST_GPIO_PINS_PER_BANK; +} + +static void st_pinconf_set_config(struct st_pio_control *pc, + int pin, unsigned long config) +{ + struct regmap_field *output_enable = pc->oe; + struct regmap_field *pull_up = pc->pu; + struct regmap_field *open_drain = pc->od; + unsigned int oe_value, pu_value, od_value; + unsigned long mask = BIT(pin); + + regmap_field_read(output_enable, &oe_value); + regmap_field_read(pull_up, &pu_value); + regmap_field_read(open_drain, &od_value); + + /* Clear old values */ + oe_value &= ~mask; + pu_value &= ~mask; + od_value &= ~mask; + + if (config & ST_PINCONF_OE) + oe_value |= mask; + if (config & ST_PINCONF_PU) + pu_value |= mask; + if (config & ST_PINCONF_OD) + od_value |= mask; + + regmap_field_write(output_enable, oe_value); + regmap_field_write(pull_up, pu_value); + regmap_field_write(open_drain, od_value); +} + +static void st_pctl_set_function(struct st_pio_control *pc, + int pin_id, int function) +{ + struct regmap_field *alt = pc->alt; + unsigned int val; + int pin = st_gpio_pin(pin_id); + int offset = pin * 4; + + regmap_field_read(alt, &val); + val &= ~(0xf << offset); + val |= function << offset; + regmap_field_write(alt, val); +} + +static unsigned long st_pinconf_delay_to_bit(unsigned int delay, + const struct st_pctl_data *data, unsigned long config) +{ + unsigned int *delay_times; + int num_delay_times, i, closest_index = -1; + unsigned int closest_divergence = UINT_MAX; + + if (ST_PINCONF_UNPACK_OE(config)) { + delay_times = data->output_delays; + num_delay_times = data->noutput_delays; + } else { + delay_times = data->input_delays; + num_delay_times = data->ninput_delays; + } + + for (i = 0; i < num_delay_times; i++) { + unsigned int divergence = abs(delay - delay_times[i]); + + if (divergence == 0) + return i; + + if (divergence < closest_divergence) { + closest_divergence = divergence; + closest_index = i; + } + } + + pr_warn("Attempt to set delay %d, closest available %d\n", + delay, delay_times[closest_index]); + + return closest_index; +} + +static unsigned long st_pinconf_bit_to_delay(unsigned int index, + const struct st_pctl_data *data, unsigned long output) +{ + unsigned int *delay_times; + int num_delay_times; + + if (output) { + delay_times = data->output_delays; + num_delay_times = data->noutput_delays; + } else { + delay_times = data->input_delays; + num_delay_times = data->ninput_delays; + } + + if (index < num_delay_times) { + return delay_times[index]; + } else { + pr_warn("Delay not found in/out delay list\n"); + return 0; + } +} + +static void st_regmap_field_bit_set_clear_pin(struct regmap_field *field, + int enable, int pin) +{ + unsigned int val = 0; + + regmap_field_read(field, &val); + if (enable) + val |= BIT(pin); + else + val &= ~BIT(pin); + regmap_field_write(field, val); +} + +static void st_pinconf_set_retime_packed(struct st_pinctrl *info, + struct st_pio_control *pc, unsigned long config, int pin) +{ + const struct st_pctl_data *data = info->data; + struct st_retime_packed *rt_p = &pc->rt.rt_p; + unsigned int delay; + + st_regmap_field_bit_set_clear_pin(rt_p->clk1notclk0, + ST_PINCONF_UNPACK_RT_CLK(config), pin); + + st_regmap_field_bit_set_clear_pin(rt_p->clknotdata, + ST_PINCONF_UNPACK_RT_CLKNOTDATA(config), pin); + + st_regmap_field_bit_set_clear_pin(rt_p->double_edge, + ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config), pin); + + st_regmap_field_bit_set_clear_pin(rt_p->invertclk, + ST_PINCONF_UNPACK_RT_INVERTCLK(config), pin); + + st_regmap_field_bit_set_clear_pin(rt_p->retime, + ST_PINCONF_UNPACK_RT(config), pin); + + delay = st_pinconf_delay_to_bit(ST_PINCONF_UNPACK_RT_DELAY(config), + data, config); + /* 2 bit delay, lsb */ + st_regmap_field_bit_set_clear_pin(rt_p->delay_0, delay & 0x1, pin); + /* 2 bit delay, msb */ + st_regmap_field_bit_set_clear_pin(rt_p->delay_1, delay & 0x2, pin); + +} + +static void st_pinconf_set_retime_dedicated(struct st_pinctrl *info, + struct st_pio_control *pc, unsigned long config, int pin) +{ + int input = ST_PINCONF_UNPACK_OE(config) ? 0 : 1; + int clk = ST_PINCONF_UNPACK_RT_CLK(config); + int clknotdata = ST_PINCONF_UNPACK_RT_CLKNOTDATA(config); + int double_edge = ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config); + int invertclk = ST_PINCONF_UNPACK_RT_INVERTCLK(config); + int retime = ST_PINCONF_UNPACK_RT(config); + + unsigned long delay = st_pinconf_delay_to_bit( + ST_PINCONF_UNPACK_RT_DELAY(config), + info->data, config); + struct st_retime_dedicated *rt_d = &pc->rt.rt_d; + + unsigned long retime_config = + ((clk) << RT_D_CFG_CLK_SHIFT) | + ((delay) << RT_D_CFG_DELAY_SHIFT) | + ((input) << RT_D_CFG_DELAY_INNOTOUT_SHIFT) | + ((retime) << RT_D_CFG_RETIME_SHIFT) | + ((clknotdata) << RT_D_CFG_CLKNOTDATA_SHIFT) | + ((invertclk) << RT_D_CFG_INVERTCLK_SHIFT) | + ((double_edge) << RT_D_CFG_DOUBLE_EDGE_SHIFT); + + regmap_field_write(rt_d->rt[pin], retime_config); +} + +static void st_pinconf_get_direction(struct st_pio_control *pc, + int pin, unsigned long *config) +{ + unsigned int oe_value, pu_value, od_value; + + regmap_field_read(pc->oe, &oe_value); + regmap_field_read(pc->pu, &pu_value); + regmap_field_read(pc->od, &od_value); + + if (oe_value & BIT(pin)) + ST_PINCONF_PACK_OE(*config); + if (pu_value & BIT(pin)) + ST_PINCONF_PACK_PU(*config); + if (od_value & BIT(pin)) + ST_PINCONF_PACK_OD(*config); + +} + +static int st_pinconf_get_retime_packed(struct st_pinctrl *info, + struct st_pio_control *pc, int pin, unsigned long *config) +{ + const struct st_pctl_data *data = info->data; + struct st_retime_packed *rt_p = &pc->rt.rt_p; + unsigned int delay_bits, delay, delay0, delay1, val; + int output = ST_PINCONF_UNPACK_OE(*config); + + if (!regmap_field_read(rt_p->retime, &val) && (val & BIT(pin))) + ST_PINCONF_PACK_RT(*config); + + if (!regmap_field_read(rt_p->clk1notclk0, &val) && (val & BIT(pin))) + ST_PINCONF_PACK_RT_CLK(*config, 1); + + if (!regmap_field_read(rt_p->clknotdata, &val) && (val & BIT(pin))) + ST_PINCONF_PACK_RT_CLKNOTDATA(*config); + + if (!regmap_field_read(rt_p->double_edge, &val) && (val & BIT(pin))) + ST_PINCONF_PACK_RT_DOUBLE_EDGE(*config); + + if (!regmap_field_read(rt_p->invertclk, &val) && (val & BIT(pin))) + ST_PINCONF_PACK_RT_INVERTCLK(*config); + + regmap_field_read(rt_p->delay_0, &delay0); + regmap_field_read(rt_p->delay_1, &delay1); + delay_bits = (((delay1 & BIT(pin)) ? 1 : 0) << 1) | + (((delay0 & BIT(pin)) ? 1 : 0)); + delay = st_pinconf_bit_to_delay(delay_bits, data, output); + ST_PINCONF_PACK_RT_DELAY(*config, delay); + + return 0; +} + +static int st_pinconf_get_retime_dedicated(struct st_pinctrl *info, + struct st_pio_control *pc, int pin, unsigned long *config) +{ + unsigned int value; + unsigned long delay_bits, delay, rt_clk; + int output = ST_PINCONF_UNPACK_OE(*config); + struct st_retime_dedicated *rt_d = &pc->rt.rt_d; + + regmap_field_read(rt_d->rt[pin], &value); + + rt_clk = (value & RT_D_CFG_CLK_MASK) >> RT_D_CFG_CLK_SHIFT; + ST_PINCONF_PACK_RT_CLK(*config, rt_clk); + + delay_bits = (value & RT_D_CFG_DELAY_MASK) >> RT_D_CFG_DELAY_SHIFT; + delay = st_pinconf_bit_to_delay(delay_bits, info->data, output); + ST_PINCONF_PACK_RT_DELAY(*config, delay); + + if (value & RT_D_CFG_CLKNOTDATA_MASK) + ST_PINCONF_PACK_RT_CLKNOTDATA(*config); + + if (value & RT_D_CFG_DOUBLE_EDGE_MASK) + ST_PINCONF_PACK_RT_DOUBLE_EDGE(*config); + + if (value & RT_D_CFG_INVERTCLK_MASK) + ST_PINCONF_PACK_RT_INVERTCLK(*config); + + if (value & RT_D_CFG_RETIME_MASK) + ST_PINCONF_PACK_RT(*config); + + return 0; +} + +/* GPIO related functions */ + +static inline void __st_gpio_set(struct st_gpio_bank *bank, + unsigned offset, int value) +{ + if (value) + writel(BIT(offset), bank->base + REG_PIO_SET_POUT); + else + writel(BIT(offset), bank->base + REG_PIO_CLR_POUT); +} + +static void st_gpio_direction(struct st_gpio_bank *bank, + unsigned int gpio, unsigned int direction) +{ + int offset = st_gpio_pin(gpio); + int i = 0; + /** + * There are three configuration registers (PIOn_PC0, PIOn_PC1 + * and PIOn_PC2) for each port. These are used to configure the + * PIO port pins. Each pin can be configured as an input, output, + * bidirectional, or alternative function pin. Three bits, one bit + * from each of the three registers, configure the corresponding bit of + * the port. Valid bit settings is: + * + * PC2 PC1 PC0 Direction. + * 0 0 0 [Input Weak pull-up] + * 0 0 or 1 1 [Bidirection] + * 0 1 0 [Output] + * 1 0 0 [Input] + * + * PIOn_SET_PC and PIOn_CLR_PC registers are used to set and clear bits + * individually. + */ + for (i = 0; i <= 2; i++) { + if (direction & BIT(i)) + writel(BIT(offset), bank->base + REG_PIO_SET_PC(i)); + else + writel(BIT(offset), bank->base + REG_PIO_CLR_PC(i)); + } +} + +static int st_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void st_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static int st_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct st_gpio_bank *bank = gpio_chip_to_bank(chip); + + return !!(readl(bank->base + REG_PIO_PIN) & BIT(offset)); +} + +static void st_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct st_gpio_bank *bank = gpio_chip_to_bank(chip); + __st_gpio_set(bank, offset, value); +} + +static int st_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_gpio_direction_input(chip->base + offset); + + return 0; +} + +static int st_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct st_gpio_bank *bank = gpio_chip_to_bank(chip); + + __st_gpio_set(bank, offset, value); + pinctrl_gpio_direction_output(chip->base + offset); + + return 0; +} + +static int st_gpio_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, u32 *flags) +{ + if (WARN_ON(gc->of_gpio_n_cells < 1)) + return -EINVAL; + + if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells)) + return -EINVAL; + + if (gpiospec->args[0] > gc->ngpio) + return -EINVAL; + + return gpiospec->args[0]; +} + +/* Pinctrl Groups */ +static int st_pctl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->ngroups; +} + +static const char *st_pctl_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->groups[selector].name; +} + +static int st_pctl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, const unsigned **pins, unsigned *npins) +{ + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + if (selector >= info->ngroups) + return -EINVAL; + + *pins = info->groups[selector].pins; + *npins = info->groups[selector].npins; + + return 0; +} + +static const inline struct st_pctl_group *st_pctl_find_group_by_name( + const struct st_pinctrl *info, const char *name) +{ + int i; + + for (i = 0; i < info->ngroups; i++) { + if (!strcmp(info->groups[i].name, name)) + return &info->groups[i]; + } + + return NULL; +} + +static int st_pctl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, struct pinctrl_map **map, unsigned *num_maps) +{ + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct st_pctl_group *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + int map_num, i; + + grp = st_pctl_find_group_by_name(info, np->name); + if (!grp) { + dev_err(info->dev, "unable to find group for node %s\n", + np->name); + return -EINVAL; + } + + map_num = grp->npins + 1; + new_map = devm_kzalloc(pctldev->dev, + sizeof(*new_map) * map_num, GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + parent = of_get_parent(np); + if (!parent) { + devm_kfree(pctldev->dev, new_map); + return -EINVAL; + } + + *map = new_map; + *num_maps = map_num; + new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[0].data.mux.function = parent->name; + new_map[0].data.mux.group = np->name; + of_node_put(parent); + + /* create config map per pin */ + new_map++; + for (i = 0; i < grp->npins; i++) { + new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[i].data.configs.group_or_pin = + pin_get_name(pctldev, grp->pins[i]); + new_map[i].data.configs.configs = &grp->pin_conf[i].config; + new_map[i].data.configs.num_configs = 1; + } + dev_info(pctldev->dev, "maps: function %s group %s num %d\n", + (*map)->data.mux.function, grp->name, map_num); + + return 0; +} + +static void st_pctl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ +} + +static struct pinctrl_ops st_pctlops = { + .get_groups_count = st_pctl_get_groups_count, + .get_group_pins = st_pctl_get_group_pins, + .get_group_name = st_pctl_get_group_name, + .dt_node_to_map = st_pctl_dt_node_to_map, + .dt_free_map = st_pctl_dt_free_map, +}; + +/* Pinmux */ +static int st_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->nfunctions; +} + +const char *st_pmx_get_fname(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->functions[selector].name; +} + +static int st_pmx_get_groups(struct pinctrl_dev *pctldev, + unsigned selector, const char * const **grps, unsigned * const ngrps) +{ + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + *grps = info->functions[selector].groups; + *ngrps = info->functions[selector].ngroups; + + return 0; +} + +static struct st_pio_control *st_get_pio_control( + struct pinctrl_dev *pctldev, int pin) +{ + struct pinctrl_gpio_range *range = + pinctrl_find_gpio_range_from_pin(pctldev, pin); + struct st_gpio_bank *bank = gpio_range_to_bank(range); + + return &bank->pc; +} + +static int st_pmx_enable(struct pinctrl_dev *pctldev, unsigned fselector, + unsigned group) +{ + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct st_pinconf *conf = info->groups[group].pin_conf; + struct st_pio_control *pc; + int i; + + for (i = 0; i < info->groups[group].npins; i++) { + pc = st_get_pio_control(pctldev, conf[i].pin); + st_pctl_set_function(pc, conf[i].pin, conf[i].altfunc); + } + + return 0; +} + +static void st_pmx_disable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ +} + +static int st_pmx_set_gpio_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned gpio, + bool input) +{ + struct st_gpio_bank *bank = gpio_range_to_bank(range); + /* + * When a PIO bank is used in its primary function mode (altfunc = 0) + * Output Enable (OE), Open Drain(OD), and Pull Up (PU) + * for the primary PIO functions are driven by the related PIO block + */ + st_pctl_set_function(&bank->pc, gpio, 0); + st_gpio_direction(bank, gpio, input ? + ST_GPIO_DIRECTION_IN : ST_GPIO_DIRECTION_OUT); + + return 0; +} + +static struct pinmux_ops st_pmxops = { + .get_functions_count = st_pmx_get_funcs_count, + .get_function_name = st_pmx_get_fname, + .get_function_groups = st_pmx_get_groups, + .enable = st_pmx_enable, + .disable = st_pmx_disable, + .gpio_set_direction = st_pmx_set_gpio_direction, +}; + +/* Pinconf */ +static void st_pinconf_get_retime(struct st_pinctrl *info, + struct st_pio_control *pc, int pin, unsigned long *config) +{ + if (info->data->rt_style == st_retime_style_packed) + st_pinconf_get_retime_packed(info, pc, pin, config); + else if (info->data->rt_style == st_retime_style_dedicated) + if ((BIT(pin) & pc->rt_pin_mask)) + st_pinconf_get_retime_dedicated(info, pc, + pin, config); +} + +static void st_pinconf_set_retime(struct st_pinctrl *info, + struct st_pio_control *pc, int pin, unsigned long config) +{ + if (info->data->rt_style == st_retime_style_packed) + st_pinconf_set_retime_packed(info, pc, config, pin); + else if (info->data->rt_style == st_retime_style_dedicated) + if ((BIT(pin) & pc->rt_pin_mask)) + st_pinconf_set_retime_dedicated(info, pc, + config, pin); +} + +static int st_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long config) +{ + int pin = st_gpio_pin(pin_id); + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct st_pio_control *pc = st_get_pio_control(pctldev, pin_id); + + st_pinconf_set_config(pc, pin, config); + st_pinconf_set_retime(info, pc, pin, config); + + return 0; +} + +static int st_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *config) +{ + int pin = st_gpio_pin(pin_id); + struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct st_pio_control *pc = st_get_pio_control(pctldev, pin_id); + + *config = 0; + st_pinconf_get_direction(pc, pin, config); + st_pinconf_get_retime(info, pc, pin, config); + + return 0; +} + +static void st_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin_id) +{ + unsigned long config; + st_pinconf_get(pctldev, pin_id, &config); + + seq_printf(s, "[OE:%ld,PU:%ld,OD:%ld]\n" + "\t\t[retime:%ld,invclk:%ld,clknotdat:%ld," + "de:%ld,rt-clk:%ld,rt-delay:%ld]", + ST_PINCONF_UNPACK_OE(config), + ST_PINCONF_UNPACK_PU(config), + ST_PINCONF_UNPACK_OD(config), + ST_PINCONF_UNPACK_RT(config), + ST_PINCONF_UNPACK_RT_INVERTCLK(config), + ST_PINCONF_UNPACK_RT_CLKNOTDATA(config), + ST_PINCONF_UNPACK_RT_DOUBLE_EDGE(config), + ST_PINCONF_UNPACK_RT_CLK(config), + ST_PINCONF_UNPACK_RT_DELAY(config)); +} + +static struct pinconf_ops st_confops = { + .pin_config_get = st_pinconf_get, + .pin_config_set = st_pinconf_set, + .pin_config_dbg_show = st_pinconf_dbg_show, +}; + +static void st_pctl_dt_child_count(struct st_pinctrl *info, + struct device_node *np) +{ + struct device_node *child; + for_each_child_of_node(np, child) { + if (of_property_read_bool(child, "gpio-controller")) { + info->nbanks++; + } else { + info->nfunctions++; + info->ngroups += of_get_child_count(child); + } + } +} + +static int st_pctl_dt_setup_retime_packed(struct st_pinctrl *info, + int bank, struct st_pio_control *pc) +{ + struct device *dev = info->dev; + struct regmap *rm = info->regmap; + const struct st_pctl_data *data = info->data; + /* 2 registers per bank */ + int reg = (data->rt + bank * RT_P_CFGS_PER_BANK) * 4; + struct st_retime_packed *rt_p = &pc->rt.rt_p; + /* cfg0 */ + struct reg_field clk1notclk0 = RT_P_CFG0_CLK1NOTCLK0_FIELD(reg); + struct reg_field delay_0 = RT_P_CFG0_DELAY_0_FIELD(reg); + struct reg_field delay_1 = RT_P_CFG0_DELAY_1_FIELD(reg); + /* cfg1 */ + struct reg_field invertclk = RT_P_CFG1_INVERTCLK_FIELD(reg + 4); + struct reg_field retime = RT_P_CFG1_RETIME_FIELD(reg + 4); + struct reg_field clknotdata = RT_P_CFG1_CLKNOTDATA_FIELD(reg + 4); + struct reg_field double_edge = RT_P_CFG1_DOUBLE_EDGE_FIELD(reg + 4); + + rt_p->clk1notclk0 = devm_regmap_field_alloc(dev, rm, clk1notclk0); + rt_p->delay_0 = devm_regmap_field_alloc(dev, rm, delay_0); + rt_p->delay_1 = devm_regmap_field_alloc(dev, rm, delay_1); + rt_p->invertclk = devm_regmap_field_alloc(dev, rm, invertclk); + rt_p->retime = devm_regmap_field_alloc(dev, rm, retime); + rt_p->clknotdata = devm_regmap_field_alloc(dev, rm, clknotdata); + rt_p->double_edge = devm_regmap_field_alloc(dev, rm, double_edge); + + if (IS_ERR(rt_p->clk1notclk0) || IS_ERR(rt_p->delay_0) || + IS_ERR(rt_p->delay_1) || IS_ERR(rt_p->invertclk) || + IS_ERR(rt_p->retime) || IS_ERR(rt_p->clknotdata) || + IS_ERR(rt_p->double_edge)) + return -EINVAL; + + return 0; +} + +static int st_pctl_dt_setup_retime_dedicated(struct st_pinctrl *info, + int bank, struct st_pio_control *pc) +{ + struct device *dev = info->dev; + struct regmap *rm = info->regmap; + const struct st_pctl_data *data = info->data; + /* 8 registers per bank */ + int reg_offset = (data->rt + bank * RT_D_CFGS_PER_BANK) * 4; + struct st_retime_dedicated *rt_d = &pc->rt.rt_d; + unsigned int j; + u32 pin_mask = pc->rt_pin_mask; + + for (j = 0; j < RT_D_CFGS_PER_BANK; j++) { + if (BIT(j) & pin_mask) { + struct reg_field reg = REG_FIELD(reg_offset, 0, 31); + rt_d->rt[j] = devm_regmap_field_alloc(dev, rm, reg); + if (IS_ERR(rt_d->rt[j])) + return -EINVAL; + reg_offset += 4; + } + } + return 0; +} + +static int st_pctl_dt_setup_retime(struct st_pinctrl *info, + int bank, struct st_pio_control *pc) +{ + const struct st_pctl_data *data = info->data; + if (data->rt_style == st_retime_style_packed) + return st_pctl_dt_setup_retime_packed(info, bank, pc); + else if (data->rt_style == st_retime_style_dedicated) + return st_pctl_dt_setup_retime_dedicated(info, bank, pc); + + return -EINVAL; +} + +static int st_parse_syscfgs(struct st_pinctrl *info, + int bank, struct device_node *np) +{ + const struct st_pctl_data *data = info->data; + /** + * For a given shared register like OE/PU/OD, there are 8 bits per bank + * 0:7 belongs to bank0, 8:15 belongs to bank1 ... + * So each register is shared across 4 banks. + */ + int lsb = (bank%4) * ST_GPIO_PINS_PER_BANK; + int msb = lsb + ST_GPIO_PINS_PER_BANK - 1; + struct reg_field alt_reg = REG_FIELD((data->alt + bank) * 4, 0, 31); + struct reg_field oe_reg = REG_FIELD((data->oe + bank/4) * 4, lsb, msb); + struct reg_field pu_reg = REG_FIELD((data->pu + bank/4) * 4, lsb, msb); + struct reg_field od_reg = REG_FIELD((data->od + bank/4) * 4, lsb, msb); + struct st_pio_control *pc = &info->banks[bank].pc; + struct device *dev = info->dev; + struct regmap *regmap = info->regmap; + + pc->alt = devm_regmap_field_alloc(dev, regmap, alt_reg); + pc->oe = devm_regmap_field_alloc(dev, regmap, oe_reg); + pc->pu = devm_regmap_field_alloc(dev, regmap, pu_reg); + pc->od = devm_regmap_field_alloc(dev, regmap, od_reg); + + if (IS_ERR(pc->alt) || IS_ERR(pc->oe) || + IS_ERR(pc->pu) || IS_ERR(pc->od)) + return -EINVAL; + + /* retime avaiable for all pins by default */ + pc->rt_pin_mask = 0xff; + of_property_read_u32(np, "st,retime-pin-mask", &pc->rt_pin_mask); + st_pctl_dt_setup_retime(info, bank, pc); + + return 0; +} + +/* + * Each pin is represented in of the below forms. + * <bank offset mux direction rt_type rt_delay rt_clk> + */ +static int st_pctl_dt_parse_groups(struct device_node *np, + struct st_pctl_group *grp, struct st_pinctrl *info, int idx) +{ + /* bank pad direction val altfunction */ + const __be32 *list; + struct property *pp; + struct st_pinconf *conf; + phandle phandle; + struct device_node *pins; + u32 pin; + int i = 0, npins = 0, nr_props; + + pins = of_get_child_by_name(np, "st,pins"); + if (!pins) + return -ENODATA; + + for_each_property_of_node(pins, pp) { + /* Skip those we do not want to proceed */ + if (!strcmp(pp->name, "name")) + continue; + + if (pp && (pp->length/sizeof(__be32)) >= OF_GPIO_ARGS_MIN) { + npins++; + } else { + pr_warn("Invalid st,pins in %s node\n", np->name); + return -EINVAL; + } + } + + grp->npins = npins; + grp->name = np->name; + grp->pins = devm_kzalloc(info->dev, npins * sizeof(u32), GFP_KERNEL); + grp->pin_conf = devm_kzalloc(info->dev, + npins * sizeof(*conf), GFP_KERNEL); + + if (!grp->pins || !grp->pin_conf) + return -ENOMEM; + + /* <bank offset mux direction rt_type rt_delay rt_clk> */ + for_each_property_of_node(pins, pp) { + if (!strcmp(pp->name, "name")) + continue; + nr_props = pp->length/sizeof(u32); + list = pp->value; + conf = &grp->pin_conf[i]; + + /* bank & offset */ + phandle = be32_to_cpup(list++); + pin = be32_to_cpup(list++); + conf->pin = of_get_named_gpio(pins, pp->name, 0); + conf->name = pp->name; + grp->pins[i] = conf->pin; + /* mux */ + conf->altfunc = be32_to_cpup(list++); + conf->config = 0; + /* direction */ + conf->config |= be32_to_cpup(list++); + /* rt_type rt_delay rt_clk */ + if (nr_props >= OF_GPIO_ARGS_MIN + OF_RT_ARGS_MIN) { + /* rt_type */ + conf->config |= be32_to_cpup(list++); + /* rt_delay */ + conf->config |= be32_to_cpup(list++); + /* rt_clk */ + if (nr_props > OF_GPIO_ARGS_MIN + OF_RT_ARGS_MIN) + conf->config |= be32_to_cpup(list++); + } + i++; + } + of_node_put(pins); + + return 0; +} + +static int st_pctl_parse_functions(struct device_node *np, + struct st_pinctrl *info, u32 index, int *grp_index) +{ + struct device_node *child; + struct st_pmx_func *func; + struct st_pctl_group *grp; + int ret, i; + + func = &info->functions[index]; + func->name = np->name; + func->ngroups = of_get_child_count(np); + if (func->ngroups <= 0) { + dev_err(info->dev, "No groups defined\n"); + return -EINVAL; + } + func->groups = devm_kzalloc(info->dev, + func->ngroups * sizeof(char *), GFP_KERNEL); + if (!func->groups) + return -ENOMEM; + + i = 0; + for_each_child_of_node(np, child) { + func->groups[i] = child->name; + grp = &info->groups[*grp_index]; + *grp_index += 1; + ret = st_pctl_dt_parse_groups(child, grp, info, i++); + if (ret) + return ret; + } + dev_info(info->dev, "Function[%d\t name:%s,\tgroups:%d]\n", + index, func->name, func->ngroups); + + return 0; +} + +static struct gpio_chip st_gpio_template = { + .request = st_gpio_request, + .free = st_gpio_free, + .get = st_gpio_get, + .set = st_gpio_set, + .direction_input = st_gpio_direction_input, + .direction_output = st_gpio_direction_output, + .ngpio = ST_GPIO_PINS_PER_BANK, + .of_gpio_n_cells = 1, + .of_xlate = st_gpio_xlate, +}; + +static int st_gpiolib_register_bank(struct st_pinctrl *info, + int bank_nr, struct device_node *np) +{ + struct st_gpio_bank *bank = &info->banks[bank_nr]; + struct pinctrl_gpio_range *range = &bank->range; + struct device *dev = info->dev; + int bank_num = of_alias_get_id(np, "gpio"); + struct resource res; + int err; + + if (of_address_to_resource(np, 0, &res)) + return -ENODEV; + + bank->base = devm_request_and_ioremap(dev, &res); + if (!bank->base) { + dev_err(dev, "Can't get IO memory mapping!\n"); + return -ENODEV; + } + + bank->gpio_chip = st_gpio_template; + bank->gpio_chip.base = bank_num * ST_GPIO_PINS_PER_BANK; + bank->gpio_chip.ngpio = ST_GPIO_PINS_PER_BANK; + bank->gpio_chip.of_node = np; + + of_property_read_string(np, "st,bank-name", &range->name); + bank->gpio_chip.label = range->name; + + range->id = bank_num; + range->pin_base = range->base = range->id * ST_GPIO_PINS_PER_BANK; + range->npins = bank->gpio_chip.ngpio; + range->gc = &bank->gpio_chip; + err = gpiochip_add(&bank->gpio_chip); + if (err) { + dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_num); + return err; + } + dev_info(dev, "%s bank added.\n", range->name); + + return 0; +} + +static struct of_device_id st_pctl_of_match[] = { + { .compatible = "st,stih415-sbc-pinctrl", .data = &stih415_sbc_data }, + { .compatible = "st,stih415-rear-pinctrl", .data = &stih415_rear_data }, + { .compatible = "st,stih415-left-pinctrl", .data = &stih415_left_data }, + { .compatible = "st,stih415-right-pinctrl", + .data = &stih415_right_data }, + { .compatible = "st,stih415-front-pinctrl", + .data = &stih415_front_data }, + { .compatible = "st,stih416-sbc-pinctrl", .data = &stih416_data}, + { .compatible = "st,stih416-front-pinctrl", .data = &stih416_data}, + { .compatible = "st,stih416-rear-pinctrl", .data = &stih416_data}, + { .compatible = "st,stih416-fvdp-fe-pinctrl", .data = &stih416_data}, + { .compatible = "st,stih416-fvdp-lite-pinctrl", .data = &stih416_data}, + { /* sentinel */ } +}; + +static int st_pctl_probe_dt(struct platform_device *pdev, + struct pinctrl_desc *pctl_desc, struct st_pinctrl *info) +{ + int ret = 0; + int i = 0, j = 0, k = 0, bank; + struct pinctrl_pin_desc *pdesc; + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + int grp_index = 0; + + st_pctl_dt_child_count(info, np); + if (!info->nbanks) { + dev_err(&pdev->dev, "you need atleast one gpio bank\n"); + return -EINVAL; + } + + dev_info(&pdev->dev, "nbanks = %d\n", info->nbanks); + dev_info(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_info(&pdev->dev, "ngroups = %d\n", info->ngroups); + + info->functions = devm_kzalloc(&pdev->dev, + info->nfunctions * sizeof(*info->functions), GFP_KERNEL); + + info->groups = devm_kzalloc(&pdev->dev, + info->ngroups * sizeof(*info->groups) , GFP_KERNEL); + + info->banks = devm_kzalloc(&pdev->dev, + info->nbanks * sizeof(*info->banks), GFP_KERNEL); + + if (!info->functions || !info->groups || !info->banks) + return -ENOMEM; + + info->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(info->regmap)) { + dev_err(info->dev, "No syscfg phandle specified\n"); + return PTR_ERR(info->regmap); + } + info->data = of_match_node(st_pctl_of_match, np)->data; + + pctl_desc->npins = info->nbanks * ST_GPIO_PINS_PER_BANK; + pdesc = devm_kzalloc(&pdev->dev, + sizeof(*pdesc) * pctl_desc->npins, GFP_KERNEL); + if (!pdesc) + return -ENOMEM; + + pctl_desc->pins = pdesc; + + bank = 0; + for_each_child_of_node(np, child) { + if (of_property_read_bool(child, "gpio-controller")) { + const char *bank_name = NULL; + ret = st_gpiolib_register_bank(info, bank, child); + if (ret) + return ret; + + k = info->banks[bank].range.pin_base; + bank_name = info->banks[bank].range.name; + for (j = 0; j < ST_GPIO_PINS_PER_BANK; j++, k++) { + pdesc->number = k; + pdesc->name = kasprintf(GFP_KERNEL, "%s[%d]", + bank_name, j); + pdesc++; + } + st_parse_syscfgs(info, bank, child); + bank++; + } else { + ret = st_pctl_parse_functions(child, info, + i++, &grp_index); + if (ret) { + dev_err(&pdev->dev, "No functions found.\n"); + return ret; + } + } + } + + return 0; +} + +static int st_pctl_probe(struct platform_device *pdev) +{ + struct st_pinctrl *info; + struct pinctrl_desc *pctl_desc; + int ret, i; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "device node not found.\n"); + return -EINVAL; + } + + pctl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctl_desc), GFP_KERNEL); + if (!pctl_desc) + return -ENOMEM; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + platform_set_drvdata(pdev, info); + ret = st_pctl_probe_dt(pdev, pctl_desc, info); + if (ret) + return ret; + + pctl_desc->owner = THIS_MODULE, + pctl_desc->pctlops = &st_pctlops, + pctl_desc->pmxops = &st_pmxops, + pctl_desc->confops = &st_confops, + pctl_desc->name = dev_name(&pdev->dev); + + info->pctl = pinctrl_register(pctl_desc, &pdev->dev, info); + if (!info->pctl) { + dev_err(&pdev->dev, "Failed pinctrl registration\n"); + return -EINVAL; + } + + for (i = 0; i < info->nbanks; i++) + pinctrl_add_gpio_range(info->pctl, &info->banks[i].range); + + return 0; +} + +static struct platform_driver st_pctl_driver = { + .driver = { + .name = "st-pinctrl", + .owner = THIS_MODULE, + .of_match_table = st_pctl_of_match, + }, + .probe = st_pctl_probe, +}; + +static int __init st_pctl_init(void) +{ + return platform_driver_register(&st_pctl_driver); +} +arch_initcall(st_pctl_init); diff --git a/drivers/pinctrl/pinctrl-sunxi-pins.h b/drivers/pinctrl/pinctrl-sunxi-pins.h new file mode 100644 index 000000000000..2eeae0c066c4 --- /dev/null +++ b/drivers/pinctrl/pinctrl-sunxi-pins.h @@ -0,0 +1,2023 @@ +/* + * Allwinner A1X SoCs pinctrl driver. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __PINCTRL_SUNXI_PINS_H +#define __PINCTRL_SUNXI_PINS_H + +#include "pinctrl-sunxi.h" + +static const struct sunxi_desc_pin sun4i_a10_pins[] = { + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD3 */ + SUNXI_FUNCTION(0x3, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x4, "uart2")), /* RTS */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD2 */ + SUNXI_FUNCTION(0x3, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x4, "uart2")), /* CTS */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD1 */ + SUNXI_FUNCTION(0x3, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x4, "uart2")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD0 */ + SUNXI_FUNCTION(0x3, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x4, "uart2")), /* RX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD3 */ + SUNXI_FUNCTION(0x3, "spi1")), /* CS1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD2 */ + SUNXI_FUNCTION(0x3, "spi3")), /* CS0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD1 */ + SUNXI_FUNCTION(0x3, "spi3")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD0 */ + SUNXI_FUNCTION(0x3, "spi3")), /* MOSI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXCK */ + SUNXI_FUNCTION(0x3, "spi3")), /* MISO */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXERR */ + SUNXI_FUNCTION(0x3, "spi3")), /* CS1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXDV */ + SUNXI_FUNCTION(0x4, "uart1")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* EMDC */ + SUNXI_FUNCTION(0x4, "uart1")), /* RX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* EMDIO */ + SUNXI_FUNCTION(0x3, "uart6"), /* TX */ + SUNXI_FUNCTION(0x4, "uart1")), /* RTS */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXEN */ + SUNXI_FUNCTION(0x3, "uart6"), /* RX */ + SUNXI_FUNCTION(0x4, "uart1")), /* CTS */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXCK */ + SUNXI_FUNCTION(0x3, "uart7"), /* TX */ + SUNXI_FUNCTION(0x4, "uart1")), /* DTR */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ECRS */ + SUNXI_FUNCTION(0x3, "uart7"), /* RX */ + SUNXI_FUNCTION(0x4, "uart1")), /* DSR */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ECOL */ + SUNXI_FUNCTION(0x3, "can"), /* TX */ + SUNXI_FUNCTION(0x4, "uart1")), /* DCD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXERR */ + SUNXI_FUNCTION(0x3, "can"), /* RX */ + SUNXI_FUNCTION(0x4, "uart1")), /* RING */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SDA */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm")), /* PWM0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0")), /* RX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* MCLK */ + SUNXI_FUNCTION(0x3, "ac97")), /* MCLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* BCLK */ + SUNXI_FUNCTION(0x3, "ac97")), /* BCLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* LRCK */ + SUNXI_FUNCTION(0x3, "ac97")), /* SYNC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* DO0 */ + SUNXI_FUNCTION(0x3, "ac97")), /* DO */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s")), /* DO1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s")), /* DO2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s")), /* DO3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* DI */ + SUNXI_FUNCTION(0x3, "ac97")), /* DI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2")), /* CS1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* CS0 */ + SUNXI_FUNCTION(0x3, "jtag")), /* MS0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* CLK */ + SUNXI_FUNCTION(0x3, "jtag")), /* CK0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* MOSI */ + SUNXI_FUNCTION(0x3, "jtag")), /* DO0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* MISO */ + SUNXI_FUNCTION(0x3, "jtag")), /* DI0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SDA */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB20, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB21, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SDA */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB22, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart0"), /* TX */ + SUNXI_FUNCTION(0x3, "ir1")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB23, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart0"), /* RX */ + SUNXI_FUNCTION(0x3, "ir1")), /* RX */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NWE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NALE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MISO */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCLE */ + SUNXI_FUNCTION(0x3, "spi0")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NRE# */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ2 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ3 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQ4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQ5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQ6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQ7 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NWP */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE4 */ + SUNXI_FUNCTION(0x3, "spi2")), /* CS0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC20, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE5 */ + SUNXI_FUNCTION(0x3, "spi2")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC21, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE6 */ + SUNXI_FUNCTION(0x3, "spi2")), /* MOSI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC22, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE7 */ + SUNXI_FUNCTION(0x3, "spi2")), /* MISO */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC23, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "spi0")), /* CS0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC24, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NDQS */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D0 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VP0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D1 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VN0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VP1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VN1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VP2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VN2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VPC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VNC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D8 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VP3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D9 */ + SUNXI_FUNCTION(0x3, "lvds0")), /* VM3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VP0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VN0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VP1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VN1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VP2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VN2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D16 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VPC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D17 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VNC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VP3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */ + SUNXI_FUNCTION(0x3, "lvds1")), /* VN3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD20, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */ + SUNXI_FUNCTION(0x3, "csi1")), /* MCLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD21, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */ + SUNXI_FUNCTION(0x3, "sim")), /* VPPEN */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD22, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */ + SUNXI_FUNCTION(0x3, "sim")), /* VPPPP */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD23, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */ + SUNXI_FUNCTION(0x3, "sim")), /* DET */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD24, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */ + SUNXI_FUNCTION(0x3, "sim")), /* VCCEN */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD25, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* DE */ + SUNXI_FUNCTION(0x3, "sim")), /* RST */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD26, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "sim")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD27, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "sim")), /* SDA */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* CLK */ + SUNXI_FUNCTION(0x3, "csi0")), /* PCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* ERR */ + SUNXI_FUNCTION(0x3, "csi0")), /* CK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* SYNC */ + SUNXI_FUNCTION(0x3, "csi0")), /* HSYNC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* DVLD */ + SUNXI_FUNCTION(0x3, "csi0")), /* VSYNC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D0 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D1 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D1 */ + SUNXI_FUNCTION(0x4, "sim")), /* VPPEN */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D2 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D3 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D4 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D5 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D6 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D7 */ + SUNXI_FUNCTION(0x3, "csi0")), /* D7 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */ + SUNXI_FUNCTION(0x4, "jtag")), /* MSI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */ + SUNXI_FUNCTION(0x4, "jtag")), /* DI1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */ + SUNXI_FUNCTION(0x4, "uart0")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */ + SUNXI_FUNCTION(0x4, "jtag")), /* DO1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */ + SUNXI_FUNCTION(0x4, "uart0")), /* RX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */ + SUNXI_FUNCTION(0x4, "jtag")), /* CK1 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* CLK */ + SUNXI_FUNCTION(0x3, "csi1"), /* PCK */ + SUNXI_FUNCTION(0x4, "mmc1")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* ERR */ + SUNXI_FUNCTION(0x3, "csi1"), /* CK */ + SUNXI_FUNCTION(0x4, "mmc1")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* SYNC */ + SUNXI_FUNCTION(0x3, "csi1"), /* HSYNC */ + SUNXI_FUNCTION(0x4, "mmc1")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* DVLD */ + SUNXI_FUNCTION(0x3, "csi1"), /* VSYNC */ + SUNXI_FUNCTION(0x4, "mmc1")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D0 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D0 */ + SUNXI_FUNCTION(0x4, "mmc1"), /* D2 */ + SUNXI_FUNCTION(0x5, "csi0")), /* D8 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D1 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D1 */ + SUNXI_FUNCTION(0x4, "mmc1"), /* D3 */ + SUNXI_FUNCTION(0x5, "csi0")), /* D9 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D2 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D2 */ + SUNXI_FUNCTION(0x4, "uart3"), /* TX */ + SUNXI_FUNCTION(0x5, "csi0")), /* D10 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D3 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D3 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RX */ + SUNXI_FUNCTION(0x5, "csi0")), /* D11 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D4 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D4 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RTS */ + SUNXI_FUNCTION(0x5, "csi0")), /* D12 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D5 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D5 */ + SUNXI_FUNCTION(0x4, "uart3"), /* CTS */ + SUNXI_FUNCTION(0x5, "csi0")), /* D13 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D6 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D6 */ + SUNXI_FUNCTION(0x4, "uart4"), /* TX */ + SUNXI_FUNCTION(0x5, "csi0")), /* D14 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts1"), /* D7 */ + SUNXI_FUNCTION(0x3, "csi1"), /* D7 */ + SUNXI_FUNCTION(0x4, "uart4"), /* RX */ + SUNXI_FUNCTION(0x5, "csi0")), /* D15 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D0 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAA0 */ + SUNXI_FUNCTION(0x4, "uart3"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 0), /* EINT0 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D1 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAA1 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 1), /* EINT1 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D2 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAA2 */ + SUNXI_FUNCTION(0x4, "uart3"), /* RTS */ + SUNXI_FUNCTION_IRQ(0x6, 2), /* EINT2 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D3 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAIRQ */ + SUNXI_FUNCTION(0x4, "uart3"), /* CTS */ + SUNXI_FUNCTION_IRQ(0x6, 3), /* EINT3 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D4 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD0 */ + SUNXI_FUNCTION(0x4, "uart4"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 4), /* EINT4 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D5 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD1 */ + SUNXI_FUNCTION(0x4, "uart4"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 5), /* EINT5 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D6 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD2 */ + SUNXI_FUNCTION(0x4, "uart5"), /* TX */ + SUNXI_FUNCTION(0x5, "ms"), /* BS */ + SUNXI_FUNCTION_IRQ(0x6, 6), /* EINT6 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D7 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD3 */ + SUNXI_FUNCTION(0x4, "uart5"), /* RX */ + SUNXI_FUNCTION(0x5, "ms"), /* CLK */ + SUNXI_FUNCTION_IRQ(0x6, 7), /* EINT7 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D7 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D8 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD4 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN0 */ + SUNXI_FUNCTION(0x5, "ms"), /* D0 */ + SUNXI_FUNCTION_IRQ(0x6, 8), /* EINT8 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D8 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D9 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD5 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN1 */ + SUNXI_FUNCTION(0x5, "ms"), /* D1 */ + SUNXI_FUNCTION_IRQ(0x6, 9), /* EINT9 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D9 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D10 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD6 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN2 */ + SUNXI_FUNCTION(0x5, "ms"), /* D2 */ + SUNXI_FUNCTION_IRQ(0x6, 10), /* EINT10 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D10 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D11 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD7 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN3 */ + SUNXI_FUNCTION(0x5, "ms"), /* D3 */ + SUNXI_FUNCTION_IRQ(0x6, 11), /* EINT11 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D11 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D12 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD8 */ + SUNXI_FUNCTION(0x4, "ps2"), /* SCK1 */ + SUNXI_FUNCTION_IRQ(0x6, 12), /* EINT12 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D12 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D13 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD9 */ + SUNXI_FUNCTION(0x4, "ps2"), /* SDA1 */ + SUNXI_FUNCTION(0x5, "sim"), /* RST */ + SUNXI_FUNCTION_IRQ(0x6, 13), /* EINT13 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D13 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D14 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD10 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN4 */ + SUNXI_FUNCTION(0x5, "sim"), /* VPPEN */ + SUNXI_FUNCTION_IRQ(0x6, 14), /* EINT14 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D14 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D15 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD11 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN5 */ + SUNXI_FUNCTION(0x5, "sim"), /* VPPPP */ + SUNXI_FUNCTION_IRQ(0x6, 15), /* EINT15 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D15 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D16 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD12 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN6 */ + SUNXI_FUNCTION_IRQ(0x6, 16), /* EINT16 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D16 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D17 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD13 */ + SUNXI_FUNCTION(0x4, "keypad"), /* IN7 */ + SUNXI_FUNCTION(0x5, "sim"), /* VCCEN */ + SUNXI_FUNCTION_IRQ(0x6, 17), /* EINT17 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D17 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D18 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD14 */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT0 */ + SUNXI_FUNCTION(0x5, "sim"), /* SCK */ + SUNXI_FUNCTION_IRQ(0x6, 18), /* EINT18 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D18 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D19 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAD15 */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT1 */ + SUNXI_FUNCTION(0x5, "sim"), /* SDA */ + SUNXI_FUNCTION_IRQ(0x6, 19), /* EINT19 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D19 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH20, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D20 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAOE */ + SUNXI_FUNCTION(0x4, "can"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 20), /* EINT20 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D20 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH21, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D21 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATADREQ */ + SUNXI_FUNCTION(0x4, "can"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 21), /* EINT21 */ + SUNXI_FUNCTION(0x7, "csi1")), /* D21 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH22, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D22 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATADACK */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT2 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* CMD */ + SUNXI_FUNCTION(0x7, "csi1")), /* D22 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH23, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* D23 */ + SUNXI_FUNCTION(0x3, "pata"), /* ATACS0 */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT3 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* CLK */ + SUNXI_FUNCTION(0x7, "csi1")), /* D23 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH24, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* CLK */ + SUNXI_FUNCTION(0x3, "pata"), /* ATACS1 */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT4 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* D0 */ + SUNXI_FUNCTION(0x7, "csi1")), /* PCLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH25, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* DE */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAIORDY */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT5 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* D1 */ + SUNXI_FUNCTION(0x7, "csi1")), /* FIELD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH26, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAIOR */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT6 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* D2 */ + SUNXI_FUNCTION(0x7, "csi1")), /* HSYNC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PH27, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd1"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "pata"), /* ATAIOW */ + SUNXI_FUNCTION(0x4, "keypad"), /* OUT7 */ + SUNXI_FUNCTION(0x5, "mmc1"), /* D3 */ + SUNXI_FUNCTION(0x7, "csi1")), /* VSYNC */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out")), + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm")), /* PWM1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc3")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CS0 */ + SUNXI_FUNCTION(0x3, "uart5"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 22)), /* EINT22 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart5"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 23)), /* EINT23 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* MOSI */ + SUNXI_FUNCTION(0x3, "uart6"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 24)), /* EINT24 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* MISO */ + SUNXI_FUNCTION(0x3, "uart6"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 25)), /* EINT25 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi0"), /* CS1 */ + SUNXI_FUNCTION(0x3, "ps2"), /* SCK1 */ + SUNXI_FUNCTION(0x4, "timer4"), /* TCLKIN0 */ + SUNXI_FUNCTION_IRQ(0x6, 26)), /* EINT26 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CS1 */ + SUNXI_FUNCTION(0x3, "ps2"), /* SDA1 */ + SUNXI_FUNCTION(0x4, "timer5"), /* TCLKIN1 */ + SUNXI_FUNCTION_IRQ(0x6, 27)), /* EINT27 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x3, "uart2"), /* RTS */ + SUNXI_FUNCTION_IRQ(0x6, 28)), /* EINT28 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart2"), /* CTS */ + SUNXI_FUNCTION_IRQ(0x6, 29)), /* EINT29 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x3, "uart2"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 30)), /* EINT30 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x3, "uart2"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 31)), /* EINT31 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI20, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ps2"), /* SCK0 */ + SUNXI_FUNCTION(0x3, "uart7"), /* TX */ + SUNXI_FUNCTION(0x4, "hdmi")), /* HSCL */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PI21, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ps2"), /* SDA0 */ + SUNXI_FUNCTION(0x3, "uart7"), /* RX */ + SUNXI_FUNCTION(0x4, "hdmi")), /* HSDA */ +}; + +static const struct sunxi_desc_pin sun5i_a10s_pins[] = { + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD3 */ + SUNXI_FUNCTION(0x3, "ts0"), /* CLK */ + SUNXI_FUNCTION(0x5, "keypad")), /* IN0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD2 */ + SUNXI_FUNCTION(0x3, "ts0"), /* ERR */ + SUNXI_FUNCTION(0x5, "keypad")), /* IN1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD1 */ + SUNXI_FUNCTION(0x3, "ts0"), /* SYNC */ + SUNXI_FUNCTION(0x5, "keypad")), /* IN2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXD0 */ + SUNXI_FUNCTION(0x3, "ts0"), /* DLVD */ + SUNXI_FUNCTION(0x5, "keypad")), /* IN3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD3 */ + SUNXI_FUNCTION(0x3, "ts0"), /* D0 */ + SUNXI_FUNCTION(0x5, "keypad")), /* IN4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD2 */ + SUNXI_FUNCTION(0x3, "ts0"), /* D1 */ + SUNXI_FUNCTION(0x5, "keypad")), /* IN5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD1 */ + SUNXI_FUNCTION(0x3, "ts0"), /* D2 */ + SUNXI_FUNCTION(0x5, "keypad")), /* IN6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXD0 */ + SUNXI_FUNCTION(0x3, "ts0"), /* D3 */ + SUNXI_FUNCTION(0x5, "keypad")), /* IN7 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXCK */ + SUNXI_FUNCTION(0x3, "ts0"), /* D4 */ + SUNXI_FUNCTION(0x4, "uart1"), /* DTR */ + SUNXI_FUNCTION(0x5, "keypad")), /* OUT0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXERR */ + SUNXI_FUNCTION(0x3, "ts0"), /* D5 */ + SUNXI_FUNCTION(0x4, "uart1"), /* DSR */ + SUNXI_FUNCTION(0x5, "keypad")), /* OUT1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ERXDV */ + SUNXI_FUNCTION(0x3, "ts0"), /* D6 */ + SUNXI_FUNCTION(0x4, "uart1"), /* DCD */ + SUNXI_FUNCTION(0x5, "keypad")), /* OUT2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* EMDC */ + SUNXI_FUNCTION(0x3, "ts0"), /* D7 */ + SUNXI_FUNCTION(0x4, "uart1"), /* RING */ + SUNXI_FUNCTION(0x5, "keypad")), /* OUT3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* EMDIO */ + SUNXI_FUNCTION(0x3, "uart1"), /* TX */ + SUNXI_FUNCTION(0x5, "keypad")), /* OUT4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXEN */ + SUNXI_FUNCTION(0x3, "uart1"), /* RX */ + SUNXI_FUNCTION(0x5, "keypad")), /* OUT5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXCK */ + SUNXI_FUNCTION(0x3, "uart1"), /* CTS */ + SUNXI_FUNCTION(0x4, "uart3"), /* TX */ + SUNXI_FUNCTION(0x5, "keypad")), /* OUT6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ECRS */ + SUNXI_FUNCTION(0x3, "uart1"), /* RTS */ + SUNXI_FUNCTION(0x4, "uart3"), /* RX */ + SUNXI_FUNCTION(0x5, "keypad")), /* OUT7 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ECOL */ + SUNXI_FUNCTION(0x3, "uart2")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PA17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "emac"), /* ETXERR */ + SUNXI_FUNCTION(0x3, "uart2"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 31)), /* EINT31 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SDA */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm"), /* PWM0 */ + SUNXI_FUNCTION_IRQ(0x6, 16)), /* EINT16 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 17)), /* EINT17 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 18)), /* EINT18 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* MCLK */ + SUNXI_FUNCTION_IRQ(0x6, 19)), /* EINT19 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* BCLK */ + SUNXI_FUNCTION_IRQ(0x6, 20)), /* EINT20 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* LRCK */ + SUNXI_FUNCTION_IRQ(0x6, 21)), /* EINT21 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* DO */ + SUNXI_FUNCTION_IRQ(0x6, 22)), /* EINT22 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2s"), /* DI */ + SUNXI_FUNCTION_IRQ(0x6, 23)), /* EINT23 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* CS1 */ + SUNXI_FUNCTION_IRQ(0x6, 24)), /* EINT24 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* CS0 */ + SUNXI_FUNCTION(0x3, "jtag"), /* MS0 */ + SUNXI_FUNCTION_IRQ(0x6, 25)), /* EINT25 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* CLK */ + SUNXI_FUNCTION(0x3, "jtag"), /* CK0 */ + SUNXI_FUNCTION_IRQ(0x6, 26)), /* EINT26 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* MOSI */ + SUNXI_FUNCTION(0x3, "jtag"), /* DO0 */ + SUNXI_FUNCTION_IRQ(0x6, 27)), /* EINT27 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* MISO */ + SUNXI_FUNCTION(0x3, "jtag"), /* DI0 */ + SUNXI_FUNCTION_IRQ(0x6, 28)), /* EINT28 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SDA */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SDA */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart0"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 29)), /* EINT29 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB20, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "uart0"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 30)), /* EINT30 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NWE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NALE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MISO */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCLE */ + SUNXI_FUNCTION(0x3, "spi0")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE1 */ + SUNXI_FUNCTION(0x3, "spi0")), /* CS0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NRE */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ2 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ3 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ4 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ5 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ6 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ7 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D7 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NWP */ + SUNXI_FUNCTION(0x4, "uart3")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE2 */ + SUNXI_FUNCTION(0x4, "uart3")), /* RX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE3 */ + SUNXI_FUNCTION(0x3, "uart2"), /* TX */ + SUNXI_FUNCTION(0x4, "uart3")), /* CTS */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE4 */ + SUNXI_FUNCTION(0x3, "uart2"), /* RX */ + SUNXI_FUNCTION(0x4, "uart3")), /* RTS */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */ + SUNXI_FUNCTION(0x3, "uart2")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */ + SUNXI_FUNCTION(0x3, "uart2")), /* RX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */ + SUNXI_FUNCTION(0x3, "uart2")), /* CTS */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */ + SUNXI_FUNCTION(0x3, "uart2")), /* RTS */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */ + SUNXI_FUNCTION(0x3, "emac")), /* ECRS */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */ + SUNXI_FUNCTION(0x3, "emac")), /* ECOL */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D8 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D9 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */ + SUNXI_FUNCTION(0x3, "emac")), /* ERXD0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */ + SUNXI_FUNCTION(0x3, "emac")), /* ERXD1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */ + SUNXI_FUNCTION(0x3, "emac")), /* ERXD2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */ + SUNXI_FUNCTION(0x3, "emac")), /* ERXD3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */ + SUNXI_FUNCTION(0x3, "emac")), /* ERXCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */ + SUNXI_FUNCTION(0x3, "emac")), /* ERXERR */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D16 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D17 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */ + SUNXI_FUNCTION(0x3, "emac")), /* ERXDV */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */ + SUNXI_FUNCTION(0x3, "emac")), /* ETXD0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD20, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */ + SUNXI_FUNCTION(0x3, "emac")), /* ETXD1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD21, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */ + SUNXI_FUNCTION(0x3, "emac")), /* ETXD2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD22, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */ + SUNXI_FUNCTION(0x3, "emac")), /* ETXD3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD23, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */ + SUNXI_FUNCTION(0x3, "emac")), /* ETXEN */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD24, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */ + SUNXI_FUNCTION(0x3, "emac")), /* ETXCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD25, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* DE */ + SUNXI_FUNCTION(0x3, "emac")), /* ETXERR */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD26, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */ + SUNXI_FUNCTION(0x3, "emac")), /* EMDC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD27, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */ + SUNXI_FUNCTION(0x3, "emac")), /* EMDIO */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x2, "ts0"), /* CLK */ + SUNXI_FUNCTION(0x3, "csi0"), /* PCK */ + SUNXI_FUNCTION(0x4, "spi2"), /* CS0 */ + SUNXI_FUNCTION_IRQ(0x6, 14)), /* EINT14 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x2, "ts0"), /* ERR */ + SUNXI_FUNCTION(0x3, "csi0"), /* CK */ + SUNXI_FUNCTION(0x4, "spi2"), /* CLK */ + SUNXI_FUNCTION_IRQ(0x6, 15)), /* EINT15 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x2, "ts0"), /* SYNC */ + SUNXI_FUNCTION(0x3, "csi0"), /* HSYNC */ + SUNXI_FUNCTION(0x4, "spi2")), /* MOSI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* DVLD */ + SUNXI_FUNCTION(0x3, "csi0"), /* VSYNC */ + SUNXI_FUNCTION(0x4, "spi2")), /* MISO */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D0 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D0 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D1 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D1 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D2 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D2 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D3 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D3 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D4 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D4 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D5 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D5 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D6 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D6 */ + SUNXI_FUNCTION(0x4, "uart1")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ts0"), /* D7 */ + SUNXI_FUNCTION(0x3, "csi0"), /* D7 */ + SUNXI_FUNCTION(0x4, "uart1")), /* RX */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */ + SUNXI_FUNCTION(0x4, "jtag")), /* MS1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */ + SUNXI_FUNCTION(0x4, "jtag")), /* DI1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */ + SUNXI_FUNCTION(0x4, "uart0")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */ + SUNXI_FUNCTION(0x4, "jtag")), /* DO1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */ + SUNXI_FUNCTION(0x4, "uart0")), /* RX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */ + SUNXI_FUNCTION(0x4, "jtag")), /* CK1 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x2, "gps"), /* CLK */ + SUNXI_FUNCTION_IRQ(0x6, 0)), /* EINT0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x2, "gps"), /* SIGN */ + SUNXI_FUNCTION_IRQ(0x6, 1)), /* EINT1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x2, "gps"), /* MAG */ + SUNXI_FUNCTION_IRQ(0x6, 2)), /* EINT2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */ + SUNXI_FUNCTION(0x4, "uart1"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 3)), /* EINT3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */ + SUNXI_FUNCTION(0x4, "uart1"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 4)), /* EINT4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* DO */ + SUNXI_FUNCTION(0x4, "uart1"), /* CTS */ + SUNXI_FUNCTION_IRQ(0x6, 5)), /* EINT5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D1 */ + SUNXI_FUNCTION(0x4, "uart1"), /* RTS */ + SUNXI_FUNCTION(0x5, "uart2"), /* RTS */ + SUNXI_FUNCTION_IRQ(0x6, 6)), /* EINT6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D2 */ + SUNXI_FUNCTION(0x5, "uart2"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 7)), /* EINT7 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* D3 */ + SUNXI_FUNCTION(0x5, "uart2"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 8)), /* EINT8 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x3, "uart3"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 9)), /* EINT9 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart3"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 10)), /* EINT10 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x3, "uart3"), /* CTS */ + SUNXI_FUNCTION_IRQ(0x6, 11)), /* EINT11 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x3, "uart3"), /* RTS */ + SUNXI_FUNCTION_IRQ(0x6, 12)), /* EINT12 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CS1 */ + SUNXI_FUNCTION(0x3, "uart3"), /* PWM1 */ + SUNXI_FUNCTION(0x5, "uart2"), /* CTS */ + SUNXI_FUNCTION_IRQ(0x6, 13)), /* EINT13 */ +}; + +static const struct sunxi_desc_pin sun5i_a13_pins[] = { + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c0")), /* SDA */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "pwm"), + SUNXI_FUNCTION_IRQ(0x6, 16)), /* EINT16 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 17)), /* EINT17 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "ir0"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 18)), /* EINT18 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi2"), /* CS1 */ + SUNXI_FUNCTION_IRQ(0x6, 24)), /* EINT24 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB16, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c1")), /* SDA */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB17, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SCK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PB18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "i2c2")), /* SDA */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NWE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NALE */ + SUNXI_FUNCTION(0x3, "spi0")), /* MISO */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCLE */ + SUNXI_FUNCTION(0x3, "spi0")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NCE1 */ + SUNXI_FUNCTION(0x3, "spi0")), /* CS0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NCE0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0")), /* NRE */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NRB1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ0 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ1 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ2 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ3 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ4 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ5 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ6 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQ7 */ + SUNXI_FUNCTION(0x3, "mmc2")), /* D7 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PC19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "nand0"), /* NDQS */ + SUNXI_FUNCTION(0x4, "uart3")), /* RTS */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D4 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D5 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D6 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D7 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D10 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D11 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D12 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD13, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D13 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD14, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D14 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD15, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D15 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD18, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D18 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD19, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D19 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD20, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D20 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD21, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D21 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD22, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D22 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD23, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* D23 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD24, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD25, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* DE */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD26, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* HSYNC */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PD27, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "lcd0")), /* VSYNC */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x3, "csi0"), /* PCLK */ + SUNXI_FUNCTION(0x4, "spi2"), /* CS0 */ + SUNXI_FUNCTION_IRQ(0x6, 14)), /* EINT14 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x3, "csi0"), /* MCLK */ + SUNXI_FUNCTION(0x4, "spi2"), /* CLK */ + SUNXI_FUNCTION_IRQ(0x6, 15)), /* EINT15 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x3, "csi0"), /* HSYNC */ + SUNXI_FUNCTION(0x4, "spi2")), /* MOSI */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* VSYNC */ + SUNXI_FUNCTION(0x4, "spi2")), /* MISO */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D0 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D1 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE6, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D2 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE7, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D3 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE8, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D4 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D5 */ + SUNXI_FUNCTION(0x4, "mmc2")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D6 */ + SUNXI_FUNCTION(0x4, "uart1")), /* TX */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PE11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x3, "csi0"), /* D7 */ + SUNXI_FUNCTION(0x4, "uart1")), /* RX */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* D1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* D0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* CLK */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* CMD */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* D3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x4, "mmc0")), /* D2 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION_IRQ(0x6, 0)), /* EINT0 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG1, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION_IRQ(0x6, 1)), /* EINT1 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG2, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION_IRQ(0x6, 2)), /* EINT2 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG3, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */ + SUNXI_FUNCTION(0x4, "uart1"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 3)), /* EINT3 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG4, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */ + SUNXI_FUNCTION(0x4, "uart1"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 4)), /* EINT4 */ + /* Hole */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG9, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CS0 */ + SUNXI_FUNCTION(0x3, "uart3"), /* TX */ + SUNXI_FUNCTION_IRQ(0x6, 9)), /* EINT9 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG10, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* CLK */ + SUNXI_FUNCTION(0x3, "uart3"), /* RX */ + SUNXI_FUNCTION_IRQ(0x6, 10)), /* EINT10 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG11, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */ + SUNXI_FUNCTION(0x3, "uart3"), /* CTS */ + SUNXI_FUNCTION_IRQ(0x6, 11)), /* EINT11 */ + SUNXI_PIN(SUNXI_PINCTRL_PIN_PG12, + SUNXI_FUNCTION(0x0, "gpio_in"), + SUNXI_FUNCTION(0x1, "gpio_out"), + SUNXI_FUNCTION(0x2, "spi1"), /* MISO */ + SUNXI_FUNCTION(0x3, "uart3"), /* RTS */ + SUNXI_FUNCTION_IRQ(0x6, 12)), /* EINT12 */ +}; + +static const struct sunxi_pinctrl_desc sun4i_a10_pinctrl_data = { + .pins = sun4i_a10_pins, + .npins = ARRAY_SIZE(sun4i_a10_pins), +}; + +static const struct sunxi_pinctrl_desc sun5i_a10s_pinctrl_data = { + .pins = sun5i_a10s_pins, + .npins = ARRAY_SIZE(sun5i_a10s_pins), +}; + +static const struct sunxi_pinctrl_desc sun5i_a13_pinctrl_data = { + .pins = sun5i_a13_pins, + .npins = ARRAY_SIZE(sun5i_a13_pins), +}; + +#endif /* __PINCTRL_SUNXI_PINS_H */ diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c index b7d8c890514c..c47fd1e5450b 100644 --- a/drivers/pinctrl/pinctrl-sunxi.c +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -13,10 +13,12 @@ #include <linux/io.h> #include <linux/clk.h> #include <linux/gpio.h> +#include <linux/irqdomain.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinctrl.h> @@ -27,1319 +29,7 @@ #include "core.h" #include "pinctrl-sunxi.h" - -static const struct sunxi_desc_pin sun4i_a10_pins[] = { - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ERXD3 */ - SUNXI_FUNCTION(0x3, "spi1"), /* CS0 */ - SUNXI_FUNCTION(0x4, "uart2")), /* RTS */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ERXD2 */ - SUNXI_FUNCTION(0x3, "spi1"), /* CLK */ - SUNXI_FUNCTION(0x4, "uart2")), /* CTS */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ERXD1 */ - SUNXI_FUNCTION(0x3, "spi1"), /* MOSI */ - SUNXI_FUNCTION(0x4, "uart2")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ERXD0 */ - SUNXI_FUNCTION(0x3, "spi1"), /* MISO */ - SUNXI_FUNCTION(0x4, "uart2")), /* RX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ETXD3 */ - SUNXI_FUNCTION(0x3, "spi1")), /* CS1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ETXD2 */ - SUNXI_FUNCTION(0x3, "spi3")), /* CS0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ETXD1 */ - SUNXI_FUNCTION(0x3, "spi3")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ETXD0 */ - SUNXI_FUNCTION(0x3, "spi3")), /* MOSI */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ERXCK */ - SUNXI_FUNCTION(0x3, "spi3")), /* MISO */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ERXERR */ - SUNXI_FUNCTION(0x3, "spi3")), /* CS1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ERXDV */ - SUNXI_FUNCTION(0x4, "uart1")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* EMDC */ - SUNXI_FUNCTION(0x4, "uart1")), /* RX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* EMDIO */ - SUNXI_FUNCTION(0x3, "uart6"), /* TX */ - SUNXI_FUNCTION(0x4, "uart1")), /* RTS */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA13, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ETXEN */ - SUNXI_FUNCTION(0x3, "uart6"), /* RX */ - SUNXI_FUNCTION(0x4, "uart1")), /* CTS */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA14, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ETXCK */ - SUNXI_FUNCTION(0x3, "uart7"), /* TX */ - SUNXI_FUNCTION(0x4, "uart1")), /* DTR */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ECRS */ - SUNXI_FUNCTION(0x3, "uart7"), /* RX */ - SUNXI_FUNCTION(0x4, "uart1")), /* DSR */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA16, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ECOL */ - SUNXI_FUNCTION(0x3, "can"), /* TX */ - SUNXI_FUNCTION(0x4, "uart1")), /* DCD */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PA17, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "emac"), /* ETXERR */ - SUNXI_FUNCTION(0x3, "can"), /* RX */ - SUNXI_FUNCTION(0x4, "uart1")), /* RING */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c0")), /* SCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c0")), /* SDA */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "pwm")), /* PWM0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ir0")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ir0")), /* RX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2s"), /* MCLK */ - SUNXI_FUNCTION(0x3, "ac97")), /* MCLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2s"), /* BCLK */ - SUNXI_FUNCTION(0x3, "ac97")), /* BCLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2s"), /* LRCK */ - SUNXI_FUNCTION(0x3, "ac97")), /* SYNC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2s"), /* DO0 */ - SUNXI_FUNCTION(0x3, "ac97")), /* DO */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2s")), /* DO1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2s")), /* DO2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2s")), /* DO3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2s"), /* DI */ - SUNXI_FUNCTION(0x3, "ac97")), /* DI */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB13, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi2")), /* CS1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB14, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi2"), /* CS0 */ - SUNXI_FUNCTION(0x3, "jtag")), /* MS0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi2"), /* CLK */ - SUNXI_FUNCTION(0x3, "jtag")), /* CK0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB16, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi2"), /* MOSI */ - SUNXI_FUNCTION(0x3, "jtag")), /* DO0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB17, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi2"), /* MISO */ - SUNXI_FUNCTION(0x3, "jtag")), /* DI0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB18, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c1")), /* SCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB19, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c1")), /* SDA */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB20, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c2")), /* SCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB21, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c2")), /* SDA */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB22, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "uart0"), /* TX */ - SUNXI_FUNCTION(0x3, "ir1")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB23, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "uart0"), /* RX */ - SUNXI_FUNCTION(0x3, "ir1")), /* RX */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NWE */ - SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NALE */ - SUNXI_FUNCTION(0x3, "spi0")), /* MISO */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NCLE */ - SUNXI_FUNCTION(0x3, "spi0")), /* SCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NCE1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NCE0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NRE# */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NRB0 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NRB1 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ0 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ1 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ2 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ3 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NDQ4 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC13, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NDQ5 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC14, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NDQ6 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NDQ7 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC16, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NWP */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC17, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NCE2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC18, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NCE3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC19, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NCE4 */ - SUNXI_FUNCTION(0x3, "spi2")), /* CS0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC20, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NCE5 */ - SUNXI_FUNCTION(0x3, "spi2")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC21, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NCE6 */ - SUNXI_FUNCTION(0x3, "spi2")), /* MOSI */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC22, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NCE7 */ - SUNXI_FUNCTION(0x3, "spi2")), /* MISO */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC23, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "spi0")), /* CS0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC24, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NDQS */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D0 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VP0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D1 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VN0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D2 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VP1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D3 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VN1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D4 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VP2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D5 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VN2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D6 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VPC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D7 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VNC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D8 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VP3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D9 */ - SUNXI_FUNCTION(0x3, "lvds0")), /* VM3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D10 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VP0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D11 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VN0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D12 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VP1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD13, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D13 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VN1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD14, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D14 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VP2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D15 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VN2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD16, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D16 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VPC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD17, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D17 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VNC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD18, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D18 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VP3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD19, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D19 */ - SUNXI_FUNCTION(0x3, "lvds1")), /* VN3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD20, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D20 */ - SUNXI_FUNCTION(0x3, "csi1")), /* MCLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD21, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D21 */ - SUNXI_FUNCTION(0x3, "sim")), /* VPPEN */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD22, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D22 */ - SUNXI_FUNCTION(0x3, "sim")), /* VPPPP */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD23, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* D23 */ - SUNXI_FUNCTION(0x3, "sim")), /* DET */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD24, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* CLK */ - SUNXI_FUNCTION(0x3, "sim")), /* VCCEN */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD25, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* DE */ - SUNXI_FUNCTION(0x3, "sim")), /* RST */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD26, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* HSYNC */ - SUNXI_FUNCTION(0x3, "sim")), /* SCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD27, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0"), /* VSYNC */ - SUNXI_FUNCTION(0x3, "sim")), /* SDA */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* CLK */ - SUNXI_FUNCTION(0x3, "csi0")), /* PCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* ERR */ - SUNXI_FUNCTION(0x3, "csi0")), /* CK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* SYNC */ - SUNXI_FUNCTION(0x3, "csi0")), /* HSYNC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* DVLD */ - SUNXI_FUNCTION(0x3, "csi0")), /* VSYNC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* D0 */ - SUNXI_FUNCTION(0x3, "csi0")), /* D0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* D1 */ - SUNXI_FUNCTION(0x3, "csi0"), /* D1 */ - SUNXI_FUNCTION(0x4, "sim")), /* VPPEN */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* D2 */ - SUNXI_FUNCTION(0x3, "csi0")), /* D2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* D3 */ - SUNXI_FUNCTION(0x3, "csi0")), /* D3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* D4 */ - SUNXI_FUNCTION(0x3, "csi0")), /* D4 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* D5 */ - SUNXI_FUNCTION(0x3, "csi0")), /* D5 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* D6 */ - SUNXI_FUNCTION(0x3, "csi0")), /* D6 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts0"), /* D7 */ - SUNXI_FUNCTION(0x3, "csi0")), /* D7 */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc0"), /* D1 */ - SUNXI_FUNCTION(0x4, "jtag")), /* MSI */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc0"), /* D0 */ - SUNXI_FUNCTION(0x4, "jtag")), /* DI1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc0"), /* CLK */ - SUNXI_FUNCTION(0x4, "uart0")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc0"), /* CMD */ - SUNXI_FUNCTION(0x4, "jtag")), /* DO1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc0"), /* D3 */ - SUNXI_FUNCTION(0x4, "uart0")), /* RX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc0"), /* D2 */ - SUNXI_FUNCTION(0x4, "jtag")), /* CK1 */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* CLK */ - SUNXI_FUNCTION(0x3, "csi1"), /* PCK */ - SUNXI_FUNCTION(0x4, "mmc1")), /* CMD */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* ERR */ - SUNXI_FUNCTION(0x3, "csi1"), /* CK */ - SUNXI_FUNCTION(0x4, "mmc1")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* SYNC */ - SUNXI_FUNCTION(0x3, "csi1"), /* HSYNC */ - SUNXI_FUNCTION(0x4, "mmc1")), /* D0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* DVLD */ - SUNXI_FUNCTION(0x3, "csi1"), /* VSYNC */ - SUNXI_FUNCTION(0x4, "mmc1")), /* D1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* D0 */ - SUNXI_FUNCTION(0x3, "csi1"), /* D0 */ - SUNXI_FUNCTION(0x4, "mmc1"), /* D2 */ - SUNXI_FUNCTION(0x5, "csi0")), /* D8 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* D1 */ - SUNXI_FUNCTION(0x3, "csi1"), /* D1 */ - SUNXI_FUNCTION(0x4, "mmc1"), /* D3 */ - SUNXI_FUNCTION(0x5, "csi0")), /* D9 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* D2 */ - SUNXI_FUNCTION(0x3, "csi1"), /* D2 */ - SUNXI_FUNCTION(0x4, "uart3"), /* TX */ - SUNXI_FUNCTION(0x5, "csi0")), /* D10 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* D3 */ - SUNXI_FUNCTION(0x3, "csi1"), /* D3 */ - SUNXI_FUNCTION(0x4, "uart3"), /* RX */ - SUNXI_FUNCTION(0x5, "csi0")), /* D11 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* D4 */ - SUNXI_FUNCTION(0x3, "csi1"), /* D4 */ - SUNXI_FUNCTION(0x4, "uart3"), /* RTS */ - SUNXI_FUNCTION(0x5, "csi0")), /* D12 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* D5 */ - SUNXI_FUNCTION(0x3, "csi1"), /* D5 */ - SUNXI_FUNCTION(0x4, "uart3"), /* CTS */ - SUNXI_FUNCTION(0x5, "csi0")), /* D13 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* D6 */ - SUNXI_FUNCTION(0x3, "csi1"), /* D6 */ - SUNXI_FUNCTION(0x4, "uart4"), /* TX */ - SUNXI_FUNCTION(0x5, "csi0")), /* D14 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ts1"), /* D7 */ - SUNXI_FUNCTION(0x3, "csi1"), /* D7 */ - SUNXI_FUNCTION(0x4, "uart4"), /* RX */ - SUNXI_FUNCTION(0x5, "csi0")), /* D15 */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D0 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAA0 */ - SUNXI_FUNCTION(0x4, "uart3"), /* TX */ - SUNXI_FUNCTION(0x7, "csi1")), /* D0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D1 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAA1 */ - SUNXI_FUNCTION(0x4, "uart3"), /* RX */ - SUNXI_FUNCTION(0x7, "csi1")), /* D1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D2 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAA2 */ - SUNXI_FUNCTION(0x4, "uart3"), /* RTS */ - SUNXI_FUNCTION(0x7, "csi1")), /* D2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D3 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAIRQ */ - SUNXI_FUNCTION(0x4, "uart3"), /* CTS */ - SUNXI_FUNCTION(0x7, "csi1")), /* D3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D4 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD0 */ - SUNXI_FUNCTION(0x4, "uart4"), /* TX */ - SUNXI_FUNCTION(0x7, "csi1")), /* D4 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D5 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD1 */ - SUNXI_FUNCTION(0x4, "uart4"), /* RX */ - SUNXI_FUNCTION(0x7, "csi1")), /* D5 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D6 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD2 */ - SUNXI_FUNCTION(0x4, "uart5"), /* TX */ - SUNXI_FUNCTION(0x5, "ms"), /* BS */ - SUNXI_FUNCTION(0x7, "csi1")), /* D6 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D7 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD3 */ - SUNXI_FUNCTION(0x4, "uart5"), /* RX */ - SUNXI_FUNCTION(0x5, "ms"), /* CLK */ - SUNXI_FUNCTION(0x7, "csi1")), /* D7 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D8 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD4 */ - SUNXI_FUNCTION(0x4, "keypad"), /* IN0 */ - SUNXI_FUNCTION(0x5, "ms"), /* D0 */ - SUNXI_FUNCTION(0x7, "csi1")), /* D8 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D9 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD5 */ - SUNXI_FUNCTION(0x4, "keypad"), /* IN1 */ - SUNXI_FUNCTION(0x5, "ms"), /* D1 */ - SUNXI_FUNCTION(0x7, "csi1")), /* D9 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D10 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD6 */ - SUNXI_FUNCTION(0x4, "keypad"), /* IN2 */ - SUNXI_FUNCTION(0x5, "ms"), /* D2 */ - SUNXI_FUNCTION(0x7, "csi1")), /* D10 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D11 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD7 */ - SUNXI_FUNCTION(0x4, "keypad"), /* IN3 */ - SUNXI_FUNCTION(0x5, "ms"), /* D3 */ - SUNXI_FUNCTION(0x7, "csi1")), /* D11 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D12 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD8 */ - SUNXI_FUNCTION(0x4, "ps2"), /* SCK1 */ - SUNXI_FUNCTION(0x7, "csi1")), /* D12 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH13, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D13 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD9 */ - SUNXI_FUNCTION(0x4, "ps2"), /* SDA1 */ - SUNXI_FUNCTION(0x5, "sim"), /* RST */ - SUNXI_FUNCTION(0x7, "csi1")), /* D13 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH14, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D14 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD10 */ - SUNXI_FUNCTION(0x4, "keypad"), /* IN4 */ - SUNXI_FUNCTION(0x5, "sim"), /* VPPEN */ - SUNXI_FUNCTION(0x7, "csi1")), /* D14 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D15 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD11 */ - SUNXI_FUNCTION(0x4, "keypad"), /* IN5 */ - SUNXI_FUNCTION(0x5, "sim"), /* VPPPP */ - SUNXI_FUNCTION(0x7, "csi1")), /* D15 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH16, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D16 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD12 */ - SUNXI_FUNCTION(0x4, "keypad"), /* IN6 */ - SUNXI_FUNCTION(0x7, "csi1")), /* D16 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH17, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D17 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD13 */ - SUNXI_FUNCTION(0x4, "keypad"), /* IN7 */ - SUNXI_FUNCTION(0x5, "sim"), /* VCCEN */ - SUNXI_FUNCTION(0x7, "csi1")), /* D17 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH18, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D18 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD14 */ - SUNXI_FUNCTION(0x4, "keypad"), /* OUT0 */ - SUNXI_FUNCTION(0x5, "sim"), /* SCK */ - SUNXI_FUNCTION(0x7, "csi1")), /* D18 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH19, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D19 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAD15 */ - SUNXI_FUNCTION(0x4, "keypad"), /* OUT1 */ - SUNXI_FUNCTION(0x5, "sim"), /* SDA */ - SUNXI_FUNCTION(0x7, "csi1")), /* D19 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH20, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D20 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAOE */ - SUNXI_FUNCTION(0x4, "can"), /* TX */ - SUNXI_FUNCTION(0x7, "csi1")), /* D20 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH21, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D21 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATADREQ */ - SUNXI_FUNCTION(0x4, "can"), /* RX */ - SUNXI_FUNCTION(0x7, "csi1")), /* D21 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH22, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D22 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATADACK */ - SUNXI_FUNCTION(0x4, "keypad"), /* OUT2 */ - SUNXI_FUNCTION(0x5, "mmc1"), /* CMD */ - SUNXI_FUNCTION(0x7, "csi1")), /* D22 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH23, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* D23 */ - SUNXI_FUNCTION(0x3, "pata"), /* ATACS0 */ - SUNXI_FUNCTION(0x4, "keypad"), /* OUT3 */ - SUNXI_FUNCTION(0x5, "mmc1"), /* CLK */ - SUNXI_FUNCTION(0x7, "csi1")), /* D23 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH24, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* CLK */ - SUNXI_FUNCTION(0x3, "pata"), /* ATACS1 */ - SUNXI_FUNCTION(0x4, "keypad"), /* OUT4 */ - SUNXI_FUNCTION(0x5, "mmc1"), /* D0 */ - SUNXI_FUNCTION(0x7, "csi1")), /* PCLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH25, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* DE */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAIORDY */ - SUNXI_FUNCTION(0x4, "keypad"), /* OUT5 */ - SUNXI_FUNCTION(0x5, "mmc1"), /* D1 */ - SUNXI_FUNCTION(0x7, "csi1")), /* FIELD */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH26, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* HSYNC */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAIOR */ - SUNXI_FUNCTION(0x4, "keypad"), /* OUT6 */ - SUNXI_FUNCTION(0x5, "mmc1"), /* D2 */ - SUNXI_FUNCTION(0x7, "csi1")), /* HSYNC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PH27, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd1"), /* VSYNC */ - SUNXI_FUNCTION(0x3, "pata"), /* ATAIOW */ - SUNXI_FUNCTION(0x4, "keypad"), /* OUT7 */ - SUNXI_FUNCTION(0x5, "mmc1"), /* D3 */ - SUNXI_FUNCTION(0x7, "csi1")), /* VSYNC */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "pwm")), /* PWM1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc3")), /* CMD */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc3")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc3")), /* D0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc3")), /* D1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc3")), /* D2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc3")), /* D3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi0"), /* CS0 */ - SUNXI_FUNCTION(0x3, "uart5")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi0"), /* CLK */ - SUNXI_FUNCTION(0x3, "uart5")), /* RX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi0"), /* MOSI */ - SUNXI_FUNCTION(0x3, "uart6")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI13, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi0"), /* MISO */ - SUNXI_FUNCTION(0x3, "uart6")), /* RX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI14, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi0"), /* CS1 */ - SUNXI_FUNCTION(0x3, "ps2"), /* SCK1 */ - SUNXI_FUNCTION(0x4, "timer4")), /* TCLKIN0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* CS1 */ - SUNXI_FUNCTION(0x3, "ps2"), /* SDA1 */ - SUNXI_FUNCTION(0x4, "timer5")), /* TCLKIN1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI16, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* CS0 */ - SUNXI_FUNCTION(0x3, "uart2")), /* RTS */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI17, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* CLK */ - SUNXI_FUNCTION(0x3, "uart2")), /* CTS */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI18, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */ - SUNXI_FUNCTION(0x3, "uart2")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI19, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* MISO */ - SUNXI_FUNCTION(0x3, "uart2")), /* RX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI20, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ps2"), /* SCK0 */ - SUNXI_FUNCTION(0x3, "uart7"), /* TX */ - SUNXI_FUNCTION(0x4, "hdmi")), /* HSCL */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PI21, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ps2"), /* SDA0 */ - SUNXI_FUNCTION(0x3, "uart7"), /* RX */ - SUNXI_FUNCTION(0x4, "hdmi")), /* HSDA */ -}; - -static const struct sunxi_desc_pin sun5i_a13_pins[] = { - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c0")), /* SCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c0")), /* SDA */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "pwm")), - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ir0")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "ir0")), /* RX */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi2")), /* CS1 */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c1")), /* SCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB16, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c1")), /* SDA */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB17, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c2")), /* SCK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PB18, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "i2c2")), /* SDA */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NWE */ - SUNXI_FUNCTION(0x3, "spi0")), /* MOSI */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NALE */ - SUNXI_FUNCTION(0x3, "spi0")), /* MISO */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NCLE */ - SUNXI_FUNCTION(0x3, "spi0")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NCE1 */ - SUNXI_FUNCTION(0x3, "spi0")), /* CS0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NCE0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0")), /* NRE */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NRB0 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* CMD */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NRB1 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ0 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ1 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ2 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ3 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ4 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D4 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC13, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ5 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D5 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC14, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ6 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D6 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQ7 */ - SUNXI_FUNCTION(0x3, "mmc2")), /* D7 */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PC19, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "nand0"), /* NDQS */ - SUNXI_FUNCTION(0x4, "uart3")), /* RTS */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D4 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D5 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D6 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D7 */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D10 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D11 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D12 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD13, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D13 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD14, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D14 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD15, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D15 */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD18, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D18 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD19, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D19 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD20, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D20 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD21, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D21 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD22, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D22 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD23, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* D23 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD24, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD25, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* DE */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD26, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* HSYNC */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PD27, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "lcd0")), /* VSYNC */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x3, "csi0"), /* PCLK */ - SUNXI_FUNCTION(0x4, "spi2")), /* CS0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x3, "csi0"), /* MCLK */ - SUNXI_FUNCTION(0x4, "spi2")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x3, "csi0"), /* HSYNC */ - SUNXI_FUNCTION(0x4, "spi2")), /* MOSI */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* VSYNC */ - SUNXI_FUNCTION(0x4, "spi2")), /* MISO */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* D0 */ - SUNXI_FUNCTION(0x4, "mmc2")), /* D0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* D1 */ - SUNXI_FUNCTION(0x4, "mmc2")), /* D1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE6, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* D2 */ - SUNXI_FUNCTION(0x4, "mmc2")), /* D2 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE7, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* D3 */ - SUNXI_FUNCTION(0x4, "mmc2")), /* D3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE8, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* D4 */ - SUNXI_FUNCTION(0x4, "mmc2")), /* CMD */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* D5 */ - SUNXI_FUNCTION(0x4, "mmc2")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* D6 */ - SUNXI_FUNCTION(0x4, "uart1")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PE11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x3, "csi0"), /* D7 */ - SUNXI_FUNCTION(0x4, "uart1")), /* RX */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* D1 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* D0 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* CLK */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* CMD */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* D3 */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PF5, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x4, "mmc0")), /* D2 */ - /* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG0, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG1, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG2, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out")), - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG3, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc1"), /* CMD */ - SUNXI_FUNCTION(0x4, "uart1")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG4, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "mmc1"), /* CLK */ - SUNXI_FUNCTION(0x4, "uart1")), /* RX */ -/* Hole */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG9, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* CS0 */ - SUNXI_FUNCTION(0x3, "uart3")), /* TX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG10, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* CLK */ - SUNXI_FUNCTION(0x3, "uart3")), /* RX */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG11, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* MOSI */ - SUNXI_FUNCTION(0x3, "uart3")), /* CTS */ - SUNXI_PIN(SUNXI_PINCTRL_PIN_PG12, - SUNXI_FUNCTION(0x0, "gpio_in"), - SUNXI_FUNCTION(0x1, "gpio_out"), - SUNXI_FUNCTION(0x2, "spi1"), /* MISO */ - SUNXI_FUNCTION(0x3, "uart3")), /* RTS */ -}; - -static const struct sunxi_pinctrl_desc sun4i_a10_pinctrl_data = { - .pins = sun4i_a10_pins, - .npins = ARRAY_SIZE(sun4i_a10_pins), -}; - -static const struct sunxi_pinctrl_desc sun5i_a13_pinctrl_data = { - .pins = sun5i_a13_pins, - .npins = ARRAY_SIZE(sun5i_a13_pins), -}; +#include "pinctrl-sunxi-pins.h" static struct sunxi_pinctrl_group * sunxi_pinctrl_find_group_by_name(struct sunxi_pinctrl *pctl, const char *group) @@ -1399,6 +89,31 @@ sunxi_pinctrl_desc_find_function_by_name(struct sunxi_pinctrl *pctl, return NULL; } +static struct sunxi_desc_function * +sunxi_pinctrl_desc_find_function_by_pin(struct sunxi_pinctrl *pctl, + const u16 pin_num, + const char *func_name) +{ + int i; + + for (i = 0; i < pctl->desc->npins; i++) { + const struct sunxi_desc_pin *pin = pctl->desc->pins + i; + + if (pin->pin.number == pin_num) { + struct sunxi_desc_function *func = pin->functions; + + while (func->name) { + if (!strcmp(func->name, func_name)) + return func; + + func++; + } + } + } + + return NULL; +} + static int sunxi_pctrl_get_groups_count(struct pinctrl_dev *pctldev) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); @@ -1680,37 +395,20 @@ sunxi_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sunxi_desc_function *desc; - char pin_name[SUNXI_PIN_NAME_MAX_LEN]; const char *func; - u8 bank, pin; - int ret; - - bank = (offset) / PINS_PER_BANK; - pin = (offset) % PINS_PER_BANK; - - ret = sprintf(pin_name, "P%c%d", 'A' + bank, pin); - if (!ret) - goto error; if (input) func = "gpio_in"; else func = "gpio_out"; - desc = sunxi_pinctrl_desc_find_function_by_name(pctl, - pin_name, - func); - if (!desc) { - ret = -EINVAL; - goto error; - } + desc = sunxi_pinctrl_desc_find_function_by_pin(pctl, offset, func); + if (!desc) + return -EINVAL; sunxi_pmx_set(pctldev, offset, desc->muxval); - ret = 0; - -error: - return ret; + return 0; } static const struct pinmux_ops sunxi_pmx_ops = { @@ -1788,6 +486,26 @@ static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc, return pin; } +static int sunxi_pinctrl_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev); + struct sunxi_desc_function *desc; + + if (offset > chip->ngpio) + return -ENXIO; + + desc = sunxi_pinctrl_desc_find_function_by_pin(pctl, offset, "irq"); + if (!desc) + return -EINVAL; + + pctl->irq_array[desc->irqnum] = offset; + + dev_dbg(chip->dev, "%s: request IRQ for GPIO %d, return %d\n", + chip->label, offset + chip->base, desc->irqnum); + + return irq_find_mapping(pctl->domain, desc->irqnum); +} + static struct gpio_chip sunxi_pinctrl_gpio_chip = { .owner = THIS_MODULE, .request = sunxi_pinctrl_gpio_request, @@ -1797,12 +515,121 @@ static struct gpio_chip sunxi_pinctrl_gpio_chip = { .get = sunxi_pinctrl_gpio_get, .set = sunxi_pinctrl_gpio_set, .of_xlate = sunxi_pinctrl_gpio_of_xlate, + .to_irq = sunxi_pinctrl_gpio_to_irq, .of_gpio_n_cells = 3, .can_sleep = 0, }; +static int sunxi_pinctrl_irq_set_type(struct irq_data *d, + unsigned int type) +{ + struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); + u32 reg = sunxi_irq_cfg_reg(d->hwirq); + u8 index = sunxi_irq_cfg_offset(d->hwirq); + u8 mode; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + mode = IRQ_EDGE_RISING; + break; + case IRQ_TYPE_EDGE_FALLING: + mode = IRQ_EDGE_FALLING; + break; + case IRQ_TYPE_EDGE_BOTH: + mode = IRQ_EDGE_BOTH; + break; + case IRQ_TYPE_LEVEL_HIGH: + mode = IRQ_LEVEL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + mode = IRQ_LEVEL_LOW; + break; + default: + return -EINVAL; + } + + writel((mode & IRQ_CFG_IRQ_MASK) << index, pctl->membase + reg); + + return 0; +} + +static void sunxi_pinctrl_irq_mask_ack(struct irq_data *d) +{ + struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); + u32 ctrl_reg = sunxi_irq_ctrl_reg(d->hwirq); + u8 ctrl_idx = sunxi_irq_ctrl_offset(d->hwirq); + u32 status_reg = sunxi_irq_status_reg(d->hwirq); + u8 status_idx = sunxi_irq_status_offset(d->hwirq); + u32 val; + + /* Mask the IRQ */ + val = readl(pctl->membase + ctrl_reg); + writel(val & ~(1 << ctrl_idx), pctl->membase + ctrl_reg); + + /* Clear the IRQ */ + writel(1 << status_idx, pctl->membase + status_reg); +} + +static void sunxi_pinctrl_irq_mask(struct irq_data *d) +{ + struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); + u32 reg = sunxi_irq_ctrl_reg(d->hwirq); + u8 idx = sunxi_irq_ctrl_offset(d->hwirq); + u32 val; + + /* Mask the IRQ */ + val = readl(pctl->membase + reg); + writel(val & ~(1 << idx), pctl->membase + reg); +} + +static void sunxi_pinctrl_irq_unmask(struct irq_data *d) +{ + struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); + struct sunxi_desc_function *func; + u32 reg = sunxi_irq_ctrl_reg(d->hwirq); + u8 idx = sunxi_irq_ctrl_offset(d->hwirq); + u32 val; + + func = sunxi_pinctrl_desc_find_function_by_pin(pctl, + pctl->irq_array[d->hwirq], + "irq"); + + /* Change muxing to INT mode */ + sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval); + + /* Unmask the IRQ */ + val = readl(pctl->membase + reg); + writel(val | (1 << idx), pctl->membase + reg); +} + +static struct irq_chip sunxi_pinctrl_irq_chip = { + .irq_mask = sunxi_pinctrl_irq_mask, + .irq_mask_ack = sunxi_pinctrl_irq_mask_ack, + .irq_unmask = sunxi_pinctrl_irq_unmask, + .irq_set_type = sunxi_pinctrl_irq_set_type, +}; + +static void sunxi_pinctrl_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct sunxi_pinctrl *pctl = irq_get_handler_data(irq); + const unsigned long reg = readl(pctl->membase + IRQ_STATUS_REG); + + /* Clear all interrupts */ + writel(reg, pctl->membase + IRQ_STATUS_REG); + + if (reg) { + int irqoffset; + + for_each_set_bit(irqoffset, ®, SUNXI_IRQ_NUMBER) { + int pin_irq = irq_find_mapping(pctl->domain, irqoffset); + generic_handle_irq(pin_irq); + } + } +} + static struct of_device_id sunxi_pinctrl_match[] = { { .compatible = "allwinner,sun4i-a10-pinctrl", .data = (void *)&sun4i_a10_pinctrl_data }, + { .compatible = "allwinner,sun5i-a10s-pinctrl", .data = (void *)&sun5i_a10s_pinctrl_data }, { .compatible = "allwinner,sun5i-a13-pinctrl", .data = (void *)&sun5i_a13_pinctrl_data }, {} }; @@ -1997,6 +824,31 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev) clk_prepare_enable(clk); + pctl->irq = irq_of_parse_and_map(node, 0); + if (!pctl->irq) { + ret = -EINVAL; + goto gpiochip_error; + } + + pctl->domain = irq_domain_add_linear(node, SUNXI_IRQ_NUMBER, + &irq_domain_simple_ops, NULL); + if (!pctl->domain) { + dev_err(&pdev->dev, "Couldn't register IRQ domain\n"); + ret = -ENOMEM; + goto gpiochip_error; + } + + for (i = 0; i < SUNXI_IRQ_NUMBER; i++) { + int irqno = irq_create_mapping(pctl->domain, i); + + irq_set_chip_and_handler(irqno, &sunxi_pinctrl_irq_chip, + handle_simple_irq); + irq_set_chip_data(irqno, pctl); + }; + + irq_set_chained_handler(pctl->irq, sunxi_pinctrl_irq_handler); + irq_set_handler_data(pctl->irq, pctl); + dev_info(&pdev->dev, "initialized sunXi PIO driver\n"); return 0; diff --git a/drivers/pinctrl/pinctrl-sunxi.h b/drivers/pinctrl/pinctrl-sunxi.h index e921621059ce..d68047d8f699 100644 --- a/drivers/pinctrl/pinctrl-sunxi.h +++ b/drivers/pinctrl/pinctrl-sunxi.h @@ -344,9 +344,31 @@ #define PULL_PINS_BITS 2 #define PULL_PINS_MASK 0x03 +#define SUNXI_IRQ_NUMBER 32 + +#define IRQ_CFG_REG 0x200 +#define IRQ_CFG_IRQ_PER_REG 8 +#define IRQ_CFG_IRQ_BITS 4 +#define IRQ_CFG_IRQ_MASK ((1 << IRQ_CFG_IRQ_BITS) - 1) +#define IRQ_CTRL_REG 0x210 +#define IRQ_CTRL_IRQ_PER_REG 32 +#define IRQ_CTRL_IRQ_BITS 1 +#define IRQ_CTRL_IRQ_MASK ((1 << IRQ_CTRL_IRQ_BITS) - 1) +#define IRQ_STATUS_REG 0x214 +#define IRQ_STATUS_IRQ_PER_REG 32 +#define IRQ_STATUS_IRQ_BITS 1 +#define IRQ_STATUS_IRQ_MASK ((1 << IRQ_STATUS_IRQ_BITS) - 1) + +#define IRQ_EDGE_RISING 0x00 +#define IRQ_EDGE_FALLING 0x01 +#define IRQ_LEVEL_HIGH 0x02 +#define IRQ_LEVEL_LOW 0x03 +#define IRQ_EDGE_BOTH 0x04 + struct sunxi_desc_function { const char *name; u8 muxval; + u8 irqnum; }; struct sunxi_desc_pin { @@ -378,10 +400,13 @@ struct sunxi_pinctrl { struct gpio_chip *chip; struct sunxi_pinctrl_desc *desc; struct device *dev; + struct irq_domain *domain; struct sunxi_pinctrl_function *functions; unsigned nfunctions; struct sunxi_pinctrl_group *groups; unsigned ngroups; + int irq; + int irq_array[SUNXI_IRQ_NUMBER]; struct pinctrl_dev *pctl_dev; }; @@ -398,6 +423,13 @@ struct sunxi_pinctrl { .muxval = _val, \ } +#define SUNXI_FUNCTION_IRQ(_val, _irq) \ + { \ + .name = "irq", \ + .muxval = _val, \ + .irqnum = _irq, \ + } + /* * The sunXi PIO registers are organized as is: * 0x00 - 0x0c Muxing values. @@ -475,4 +507,40 @@ static inline u32 sunxi_pull_offset(u16 pin) return pin_num * PULL_PINS_BITS; } +static inline u32 sunxi_irq_cfg_reg(u16 irq) +{ + u8 reg = irq / IRQ_CFG_IRQ_PER_REG; + return reg + IRQ_CFG_REG; +} + +static inline u32 sunxi_irq_cfg_offset(u16 irq) +{ + u32 irq_num = irq % IRQ_CFG_IRQ_PER_REG; + return irq_num * IRQ_CFG_IRQ_BITS; +} + +static inline u32 sunxi_irq_ctrl_reg(u16 irq) +{ + u8 reg = irq / IRQ_CTRL_IRQ_PER_REG; + return reg + IRQ_CTRL_REG; +} + +static inline u32 sunxi_irq_ctrl_offset(u16 irq) +{ + u32 irq_num = irq % IRQ_CTRL_IRQ_PER_REG; + return irq_num * IRQ_CTRL_IRQ_BITS; +} + +static inline u32 sunxi_irq_status_reg(u16 irq) +{ + u8 reg = irq / IRQ_STATUS_IRQ_PER_REG; + return reg + IRQ_STATUS_REG; +} + +static inline u32 sunxi_irq_status_offset(u16 irq) +{ + u32 irq_num = irq % IRQ_STATUS_IRQ_PER_REG; + return irq_num * IRQ_STATUS_IRQ_BITS; +} + #endif /* __PINCTRL_SUNXI_H */ diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c new file mode 100644 index 000000000000..d4f12cc556b4 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c @@ -0,0 +1,1024 @@ +/* + * Pinctrl driver for the Toumaz Xenif TZ1090 PowerDown Controller pins + * + * Copyright (c) 2013, Imagination Technologies Ltd. + * + * Derived from Tegra code: + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * + * Derived from code: + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 NVIDIA Corporation + * Copyright (C) 2009-2011 ST-Ericsson AB + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/bitops.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/slab.h> + +/* + * The registers may be shared with other threads/cores, so we need to use the + * metag global lock2 for atomicity. + */ +#include <asm/global_lock.h> + +#include "core.h" +#include "pinconf.h" + +/* Register offsets from bank base address */ +#define REG_GPIO_CONTROL0 0x00 +#define REG_GPIO_CONTROL2 0x08 + +/* Register field information */ +#define REG_GPIO_CONTROL2_PU_PD_S 16 +#define REG_GPIO_CONTROL2_PDC_POS_S 4 +#define REG_GPIO_CONTROL2_PDC_DR_S 2 +#define REG_GPIO_CONTROL2_PDC_SR_S 1 +#define REG_GPIO_CONTROL2_PDC_SCHMITT_S 0 + +/* PU_PD field values */ +#define REG_PU_PD_TRISTATE 0 +#define REG_PU_PD_UP 1 +#define REG_PU_PD_DOWN 2 +#define REG_PU_PD_REPEATER 3 + +/* DR field values */ +#define REG_DR_2mA 0 +#define REG_DR_4mA 1 +#define REG_DR_8mA 2 +#define REG_DR_12mA 3 + +/** + * struct tz1090_pdc_function - TZ1090 PDC pinctrl mux function + * @name: The name of the function, exported to pinctrl core. + * @groups: An array of pin groups that may select this function. + * @ngroups: The number of entries in @groups. + */ +struct tz1090_pdc_function { + const char *name; + const char * const *groups; + unsigned int ngroups; +}; + +/** + * struct tz1090_pdc_pingroup - TZ1090 PDC pin group + * @name: Name of pin group. + * @pins: Array of pin numbers in this pin group. + * @npins: Number of pins in this pin group. + * @func: Function enabled by the mux. + * @reg: Mux register offset. + * @bit: Mux register bit. + * @drv: Drive control supported, otherwise it's a mux. + * This means Schmitt, Slew, and Drive strength. + * + * A representation of a group of pins (possibly just one pin) in the TZ1090 + * PDC pin controller. Each group allows some parameter or parameters to be + * configured. The most common is mux function selection. + */ +struct tz1090_pdc_pingroup { + const char *name; + const unsigned int *pins; + unsigned int npins; + int func; + u16 reg; + u8 bit; + bool drv; +}; + +/* + * All PDC pins can be GPIOs. Define these first to match how the GPIO driver + * names/numbers its pins. + */ + +enum tz1090_pdc_pin { + TZ1090_PDC_PIN_GPIO0, + TZ1090_PDC_PIN_GPIO1, + TZ1090_PDC_PIN_SYS_WAKE0, + TZ1090_PDC_PIN_SYS_WAKE1, + TZ1090_PDC_PIN_SYS_WAKE2, + TZ1090_PDC_PIN_IR_DATA, + TZ1090_PDC_PIN_EXT_POWER, +}; + +/* Pin names */ + +static const struct pinctrl_pin_desc tz1090_pdc_pins[] = { + /* PDC GPIOs */ + PINCTRL_PIN(TZ1090_PDC_PIN_GPIO0, "gpio0"), + PINCTRL_PIN(TZ1090_PDC_PIN_GPIO1, "gpio1"), + PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE0, "sys_wake0"), + PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE1, "sys_wake1"), + PINCTRL_PIN(TZ1090_PDC_PIN_SYS_WAKE2, "sys_wake2"), + PINCTRL_PIN(TZ1090_PDC_PIN_IR_DATA, "ir_data"), + PINCTRL_PIN(TZ1090_PDC_PIN_EXT_POWER, "ext_power"), +}; + +/* Pin group pins */ + +static const unsigned int gpio0_pins[] = { + TZ1090_PDC_PIN_GPIO0, +}; + +static const unsigned int gpio1_pins[] = { + TZ1090_PDC_PIN_GPIO1, +}; + +static const unsigned int pdc_pins[] = { + TZ1090_PDC_PIN_GPIO0, + TZ1090_PDC_PIN_GPIO1, + TZ1090_PDC_PIN_SYS_WAKE0, + TZ1090_PDC_PIN_SYS_WAKE1, + TZ1090_PDC_PIN_SYS_WAKE2, + TZ1090_PDC_PIN_IR_DATA, + TZ1090_PDC_PIN_EXT_POWER, +}; + +/* Mux functions */ + +enum tz1090_pdc_mux { + /* PDC_GPIO0 mux */ + TZ1090_PDC_MUX_IR_MOD_STABLE_OUT, + /* PDC_GPIO1 mux */ + TZ1090_PDC_MUX_IR_MOD_POWER_OUT, +}; + +/* Pin groups a function can be muxed to */ + +static const char * const gpio0_groups[] = { + "gpio0", +}; + +static const char * const gpio1_groups[] = { + "gpio1", +}; + +#define FUNCTION(mux, fname, group) \ + [(TZ1090_PDC_MUX_ ## mux)] = { \ + .name = #fname, \ + .groups = group##_groups, \ + .ngroups = ARRAY_SIZE(group##_groups), \ + } + +/* Must correlate with enum tz1090_pdc_mux */ +static const struct tz1090_pdc_function tz1090_pdc_functions[] = { + /* MUX fn pingroups */ + FUNCTION(IR_MOD_STABLE_OUT, ir_mod_stable_out, gpio0), + FUNCTION(IR_MOD_POWER_OUT, ir_mod_power_out, gpio1), +}; + +/** + * MUX_PG() - Initialise a pin group with mux control + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + * @f0: Function 0 (TZ1090_PDC_MUX_ is prepended) + * @mux_r: Mux register (REG_PINCTRL_ is prepended) + * @mux_b: Bit number in register of mux field + */ +#define MUX_PG(pg_name, f0, mux_r, mux_b) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .func = TZ1090_PDC_MUX_ ## f0, \ + .reg = (REG_ ## mux_r), \ + .bit = (mux_b), \ + } + +/** + * DRV_PG() - Initialise a pin group with drive control + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + */ +#define DRV_PG(pg_name) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .drv = true, \ + } + +static const struct tz1090_pdc_pingroup tz1090_pdc_groups[] = { + /* Muxing pin groups */ + /* pg_name, f0, mux register, mux bit */ + MUX_PG(gpio0, IR_MOD_STABLE_OUT, GPIO_CONTROL0, 7), + MUX_PG(gpio1, IR_MOD_POWER_OUT, GPIO_CONTROL0, 6), + + /* Drive pin groups */ + /* pg_name */ + DRV_PG(pdc), +}; + +/** + * struct tz1090_pdc_pmx - Private pinctrl data + * @dev: Platform device + * @pctl: Pin control device + * @regs: Register region + * @lock: Lock protecting coherency of mux_en and gpio_en + * @mux_en: Muxes that have been enabled + * @gpio_en: Muxable GPIOs that have been enabled + */ +struct tz1090_pdc_pmx { + struct device *dev; + struct pinctrl_dev *pctl; + void __iomem *regs; + spinlock_t lock; + u32 mux_en; + u32 gpio_en; +}; + +static inline u32 pmx_read(struct tz1090_pdc_pmx *pmx, u32 reg) +{ + return ioread32(pmx->regs + reg); +} + +static inline void pmx_write(struct tz1090_pdc_pmx *pmx, u32 val, u32 reg) +{ + iowrite32(val, pmx->regs + reg); +} + +/* + * Pin control operations + */ + +static int tz1090_pdc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tz1090_pdc_groups); +} + +static const char *tz1090_pdc_pinctrl_get_group_name(struct pinctrl_dev *pctl, + unsigned int group) +{ + return tz1090_pdc_groups[group].name; +} + +static int tz1090_pdc_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int group, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = tz1090_pdc_groups[group].pins; + *num_pins = tz1090_pdc_groups[group].npins; + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void tz1090_pdc_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int offset) +{ + seq_printf(s, " %s", dev_name(pctldev->dev)); +} +#endif + +static int reserve_map(struct device *dev, struct pinctrl_map **map, + unsigned int *reserved_maps, unsigned int *num_maps, + unsigned int reserve) +{ + unsigned int old_num = *reserved_maps; + unsigned int new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); + if (!new_map) { + dev_err(dev, "krealloc(map) failed\n"); + return -ENOMEM; + } + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + + return 0; +} + +static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, + const char *function) +{ + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} + +/** + * get_group_selector() - returns the group selector for a group + * @pin_group: the pin group to look up + * + * This is the same as pinctrl_get_group_selector except it doesn't produce an + * error message if the group isn't found or debug messages. + */ +static int get_group_selector(const char *pin_group) +{ + unsigned int group; + + for (group = 0; group < ARRAY_SIZE(tz1090_pdc_groups); ++group) + if (!strcmp(tz1090_pdc_groups[group].name, pin_group)) + return group; + + return -EINVAL; +} + +static int add_map_configs(struct device *dev, + struct pinctrl_map **map, + unsigned int *reserved_maps, unsigned int *num_maps, + const char *group, unsigned long *configs, + unsigned int num_configs) +{ + unsigned long *dup_configs; + enum pinctrl_map_type type; + + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), + GFP_KERNEL); + if (!dup_configs) { + dev_err(dev, "kmemdup(configs) failed\n"); + return -ENOMEM; + } + + /* + * We support both pins and pin groups, but we need to figure out which + * one we have. + */ + if (get_group_selector(group) >= 0) + type = PIN_MAP_TYPE_CONFIGS_GROUP; + else + type = PIN_MAP_TYPE_CONFIGS_PIN; + (*map)[*num_maps].type = type; + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} + +static void tz1090_pdc_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, + unsigned int num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + kfree(map[i].data.configs.configs); + + kfree(map); +} + +static int tz1090_pdc_pinctrl_dt_subnode_to_map(struct device *dev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *reserved_maps, + unsigned int *num_maps) +{ + int ret; + const char *function; + unsigned long *configs = NULL; + unsigned int num_configs = 0; + unsigned int reserve; + struct property *prop; + const char *group; + + ret = of_property_read_string(np, "tz1090,function", &function); + if (ret < 0) { + /* EINVAL=missing, which is fine since it's optional */ + if (ret != -EINVAL) + dev_err(dev, + "could not parse property function\n"); + function = NULL; + } + + ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + if (ret) + return ret; + + reserve = 0; + if (function != NULL) + reserve++; + if (num_configs) + reserve++; + ret = of_property_count_strings(np, "tz1090,pins"); + if (ret < 0) { + dev_err(dev, "could not parse property pins\n"); + goto exit; + } + reserve *= ret; + + ret = reserve_map(dev, map, reserved_maps, num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "tz1090,pins", prop, group) { + if (function) { + ret = add_map_mux(map, reserved_maps, num_maps, + group, function); + if (ret < 0) + goto exit; + } + + if (num_configs) { + ret = add_map_configs(dev, map, reserved_maps, + num_maps, group, configs, + num_configs); + if (ret < 0) + goto exit; + } + } + + ret = 0; + +exit: + kfree(configs); + return ret; +} + +static int tz1090_pdc_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + unsigned int reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = tz1090_pdc_pinctrl_dt_subnode_to_map(pctldev->dev, np, + map, &reserved_maps, + num_maps); + if (ret < 0) { + tz1090_pdc_pinctrl_dt_free_map(pctldev, *map, + *num_maps); + return ret; + } + } + + return 0; +} + +static struct pinctrl_ops tz1090_pdc_pinctrl_ops = { + .get_groups_count = tz1090_pdc_pinctrl_get_groups_count, + .get_group_name = tz1090_pdc_pinctrl_get_group_name, + .get_group_pins = tz1090_pdc_pinctrl_get_group_pins, +#ifdef CONFIG_DEBUG_FS + .pin_dbg_show = tz1090_pdc_pinctrl_pin_dbg_show, +#endif + .dt_node_to_map = tz1090_pdc_pinctrl_dt_node_to_map, + .dt_free_map = tz1090_pdc_pinctrl_dt_free_map, +}; + +/* + * Pin mux operations + */ + +static int tz1090_pdc_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tz1090_pdc_functions); +} + +static const char *tz1090_pdc_pinctrl_get_func_name(struct pinctrl_dev *pctldev, + unsigned int function) +{ + return tz1090_pdc_functions[function].name; +} + +static int tz1090_pdc_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, + unsigned int function, + const char * const **groups, + unsigned int * const num_groups) +{ + *groups = tz1090_pdc_functions[function].groups; + *num_groups = tz1090_pdc_functions[function].ngroups; + + return 0; +} + +/** + * tz1090_pdc_pinctrl_mux() - update mux bit + * @pmx: Pinmux data + * @grp: Pin mux group + */ +static void tz1090_pdc_pinctrl_mux(struct tz1090_pdc_pmx *pmx, + const struct tz1090_pdc_pingroup *grp) +{ + u32 reg, select; + unsigned int pin_shift = grp->pins[0]; + unsigned long flags; + + /* select = mux && !gpio */ + select = ((pmx->mux_en & ~pmx->gpio_en) >> pin_shift) & 1; + + /* set up the mux */ + __global_lock2(flags); + reg = pmx_read(pmx, grp->reg); + reg &= ~BIT(grp->bit); + reg |= select << grp->bit; + pmx_write(pmx, reg, grp->reg); + __global_unlock2(flags); +} + +static int tz1090_pdc_pinctrl_enable(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *grp = &tz1090_pdc_groups[group]; + + dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n", + __func__, + function, tz1090_pdc_functions[function].name, + group, tz1090_pdc_groups[group].name); + + /* is it even a mux? */ + if (grp->drv) + return -EINVAL; + + /* does this group even control the function? */ + if (function != grp->func) + return -EINVAL; + + /* record the pin being muxed and update mux bit */ + spin_lock(&pmx->lock); + pmx->mux_en |= BIT(grp->pins[0]); + tz1090_pdc_pinctrl_mux(pmx, grp); + spin_unlock(&pmx->lock); + return 0; +} + +static void tz1090_pdc_pinctrl_disable(struct pinctrl_dev *pctldev, + unsigned int function, + unsigned int group) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *grp = &tz1090_pdc_groups[group]; + + dev_dbg(pctldev->dev, "%s(func=%u (%s), group=%u (%s))\n", + __func__, + function, tz1090_pdc_functions[function].name, + group, tz1090_pdc_groups[group].name); + + /* is it even a mux? */ + if (grp->drv) + return; + + /* does this group even control the function? */ + if (function != grp->func) + return; + + /* record the pin being unmuxed and update mux bit */ + spin_lock(&pmx->lock); + pmx->mux_en &= ~BIT(grp->pins[0]); + tz1090_pdc_pinctrl_mux(pmx, grp); + spin_unlock(&pmx->lock); +} + +static const struct tz1090_pdc_pingroup *find_mux_group( + struct tz1090_pdc_pmx *pmx, + unsigned int pin) +{ + const struct tz1090_pdc_pingroup *grp; + unsigned int group; + + grp = tz1090_pdc_groups; + for (group = 0; group < ARRAY_SIZE(tz1090_pdc_groups); ++group, ++grp) { + /* only match muxes */ + if (grp->drv) + continue; + + /* with a matching pin */ + if (grp->pins[0] == pin) + return grp; + } + + return NULL; +} + +static int tz1090_pdc_pinctrl_gpio_request_enable( + struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *grp = find_mux_group(pmx, pin); + + if (grp) { + /* record the pin in GPIO use and update mux bit */ + spin_lock(&pmx->lock); + pmx->gpio_en |= BIT(pin); + tz1090_pdc_pinctrl_mux(pmx, grp); + spin_unlock(&pmx->lock); + } + return 0; +} + +static void tz1090_pdc_pinctrl_gpio_disable_free( + struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *grp = find_mux_group(pmx, pin); + + if (grp) { + /* record the pin not in GPIO use and update mux bit */ + spin_lock(&pmx->lock); + pmx->gpio_en &= ~BIT(pin); + tz1090_pdc_pinctrl_mux(pmx, grp); + spin_unlock(&pmx->lock); + } +} + +static struct pinmux_ops tz1090_pdc_pinmux_ops = { + .get_functions_count = tz1090_pdc_pinctrl_get_funcs_count, + .get_function_name = tz1090_pdc_pinctrl_get_func_name, + .get_function_groups = tz1090_pdc_pinctrl_get_func_groups, + .enable = tz1090_pdc_pinctrl_enable, + .disable = tz1090_pdc_pinctrl_disable, + .gpio_request_enable = tz1090_pdc_pinctrl_gpio_request_enable, + .gpio_disable_free = tz1090_pdc_pinctrl_gpio_disable_free, +}; + +/* + * Pin config operations + */ + +static int tz1090_pdc_pinconf_reg(struct pinctrl_dev *pctldev, + unsigned int pin, + enum pin_config_param param, + bool report_err, + u32 *reg, u32 *width, u32 *mask, u32 *shift, + u32 *val) +{ + /* Find information about parameter's register */ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + *val = REG_PU_PD_TRISTATE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + *val = REG_PU_PD_UP; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + *val = REG_PU_PD_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + *val = REG_PU_PD_REPEATER; + break; + default: + return -ENOTSUPP; + }; + + /* Only input bias parameters supported */ + *reg = REG_GPIO_CONTROL2; + *shift = REG_GPIO_CONTROL2_PU_PD_S + pin*2; + *width = 2; + + /* Calculate field information */ + *mask = (BIT(*width) - 1) << *shift; + + return 0; +} + +static int tz1090_pdc_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + int ret; + u32 reg, width, mask, shift, val, tmp, arg; + + /* Get register information */ + ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; + + /* Extract field from register */ + tmp = pmx_read(pmx, reg); + arg = ((tmp & mask) >> shift) == val; + + /* Config not active */ + if (!arg) + return -EINVAL; + + /* And pack config */ + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tz1090_pdc_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long config) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(config); + unsigned int arg = pinconf_to_config_argument(config); + int ret; + u32 reg, width, mask, shift, val, tmp; + unsigned long flags; + + dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n", + __func__, tz1090_pdc_pins[pin].name, config); + + /* Get register information */ + ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; + + /* Unpack argument and range check it */ + if (arg > 1) { + dev_dbg(pctldev->dev, "%s: arg %u out of range\n", + __func__, arg); + return -EINVAL; + } + + /* Write register field */ + __global_lock2(flags); + tmp = pmx_read(pmx, reg); + tmp &= ~mask; + if (arg) + tmp |= val << shift; + pmx_write(pmx, tmp, reg); + __global_unlock2(flags); + + return 0; +} + +static const int tz1090_pdc_boolean_map[] = { + [0] = -EINVAL, + [1] = 1, +}; + +static const int tz1090_pdc_dr_map[] = { + [REG_DR_2mA] = 2, + [REG_DR_4mA] = 4, + [REG_DR_8mA] = 8, + [REG_DR_12mA] = 12, +}; + +static int tz1090_pdc_pinconf_group_reg(struct pinctrl_dev *pctldev, + const struct tz1090_pdc_pingroup *g, + enum pin_config_param param, + bool report_err, u32 *reg, u32 *width, + u32 *mask, u32 *shift, const int **map) +{ + /* Drive configuration applies in groups, but not to all groups. */ + if (!g->drv) { + if (report_err) + dev_dbg(pctldev->dev, + "%s: group %s has no drive control\n", + __func__, g->name); + return -ENOTSUPP; + } + + /* Find information about drive parameter's register */ + *reg = REG_GPIO_CONTROL2; + switch (param) { + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + *shift = REG_GPIO_CONTROL2_PDC_SCHMITT_S; + *width = 1; + *map = tz1090_pdc_boolean_map; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + *shift = REG_GPIO_CONTROL2_PDC_DR_S; + *width = 2; + *map = tz1090_pdc_dr_map; + break; + case PIN_CONFIG_LOW_POWER_MODE: + *shift = REG_GPIO_CONTROL2_PDC_POS_S; + *width = 1; + *map = tz1090_pdc_boolean_map; + break; + default: + return -ENOTSUPP; + }; + + /* Calculate field information */ + *mask = (BIT(*width) - 1) << *shift; + + return 0; +} + +static int tz1090_pdc_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *config) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group]; + enum pin_config_param param = pinconf_to_config_param(*config); + int ret, arg; + u32 reg, width, mask, shift, val; + const int *map; + + /* Get register information */ + ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, &map); + if (ret < 0) + return ret; + + /* Extract field from register */ + val = pmx_read(pmx, reg); + arg = map[(val & mask) >> shift]; + if (arg < 0) + return arg; + + /* And pack config */ + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tz1090_pdc_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long config) +{ + struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group]; + enum pin_config_param param = pinconf_to_config_param(config); + const unsigned int *pit; + unsigned int i; + int ret, arg; + u32 reg, width, mask, shift, val; + unsigned long flags; + const int *map; + + dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n", + __func__, g->name, config); + + /* Get register information */ + ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, &map); + if (ret < 0) { + /* + * Maybe we're trying to set a per-pin configuration of a group, + * so do the pins one by one. This is mainly as a convenience. + */ + for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) { + ret = tz1090_pdc_pinconf_set(pctldev, *pit, config); + if (ret) + return ret; + } + return 0; + } + + /* Unpack argument and map it to register value */ + arg = pinconf_to_config_argument(config); + for (i = 0; i < BIT(width); ++i) { + if (map[i] == arg || (map[i] == -EINVAL && !arg)) { + /* Write register field */ + __global_lock2(flags); + val = pmx_read(pmx, reg); + val &= ~mask; + val |= i << shift; + pmx_write(pmx, val, reg); + __global_unlock2(flags); + return 0; + } + } + + dev_dbg(pctldev->dev, "%s: arg %u not supported\n", + __func__, arg); + return 0; +} + +static struct pinconf_ops tz1090_pdc_pinconf_ops = { + .is_generic = true, + .pin_config_get = tz1090_pdc_pinconf_get, + .pin_config_set = tz1090_pdc_pinconf_set, + .pin_config_group_get = tz1090_pdc_pinconf_group_get, + .pin_config_group_set = tz1090_pdc_pinconf_group_set, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + +/* + * Pin control driver setup + */ + +static struct pinctrl_desc tz1090_pdc_pinctrl_desc = { + .pctlops = &tz1090_pdc_pinctrl_ops, + .pmxops = &tz1090_pdc_pinmux_ops, + .confops = &tz1090_pdc_pinconf_ops, + .owner = THIS_MODULE, +}; + +static int tz1090_pdc_pinctrl_probe(struct platform_device *pdev) +{ + struct tz1090_pdc_pmx *pmx; + struct resource *res; + + pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); + if (!pmx) { + dev_err(&pdev->dev, "Can't alloc tz1090_pdc_pmx\n"); + return -ENOMEM; + } + pmx->dev = &pdev->dev; + spin_lock_init(&pmx->lock); + + tz1090_pdc_pinctrl_desc.name = dev_name(&pdev->dev); + tz1090_pdc_pinctrl_desc.pins = tz1090_pdc_pins; + tz1090_pdc_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pdc_pins); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing MEM resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, + "Couldn't request MEM resource\n"); + return -ENODEV; + } + + pmx->regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!pmx->regs) { + dev_err(&pdev->dev, "Couldn't ioremap regs\n"); + return -ENODEV; + } + + pmx->pctl = pinctrl_register(&tz1090_pdc_pinctrl_desc, &pdev->dev, pmx); + if (!pmx->pctl) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, pmx); + + dev_info(&pdev->dev, "TZ1090 PDC pinctrl driver initialised\n"); + + return 0; +} + +static int tz1090_pdc_pinctrl_remove(struct platform_device *pdev) +{ + struct tz1090_pdc_pmx *pmx = platform_get_drvdata(pdev); + + pinctrl_unregister(pmx->pctl); + + return 0; +} + +static struct of_device_id tz1090_pdc_pinctrl_of_match[] = { + { .compatible = "img,tz1090-pdc-pinctrl", }, + { }, +}; + +static struct platform_driver tz1090_pdc_pinctrl_driver = { + .driver = { + .name = "tz1090-pdc-pinctrl", + .owner = THIS_MODULE, + .of_match_table = tz1090_pdc_pinctrl_of_match, + }, + .probe = tz1090_pdc_pinctrl_probe, + .remove = tz1090_pdc_pinctrl_remove, +}; + +static int __init tz1090_pdc_pinctrl_init(void) +{ + return platform_driver_register(&tz1090_pdc_pinctrl_driver); +} +arch_initcall(tz1090_pdc_pinctrl_init); + +static void __exit tz1090_pdc_pinctrl_exit(void) +{ + platform_driver_unregister(&tz1090_pdc_pinctrl_driver); +} +module_exit(tz1090_pdc_pinctrl_exit); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("Toumaz Xenif TZ1090 PDC pinctrl driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, tz1090_pdc_pinctrl_of_match); diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c new file mode 100644 index 000000000000..4edae08a0a61 --- /dev/null +++ b/drivers/pinctrl/pinctrl-tz1090.c @@ -0,0 +1,2072 @@ +/* + * Pinctrl driver for the Toumaz Xenif TZ1090 SoC + * + * Copyright (c) 2013, Imagination Technologies Ltd. + * + * Derived from Tegra code: + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. + * + * Derived from code: + * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2010 NVIDIA Corporation + * Copyright (C) 2009-2011 ST-Ericsson AB + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/bitops.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +/* + * The registers may be shared with other threads/cores, so we need to use the + * metag global lock2 for atomicity. + */ +#include <asm/global_lock.h> + +#include "core.h" +#include "pinconf.h" + +/* Register offsets from bank base address */ +#define REG_PINCTRL_SELECT 0x10 +#define REG_PINCTRL_SCHMITT 0x90 +#define REG_PINCTRL_PU_PD 0xa0 +#define REG_PINCTRL_SR 0xc0 +#define REG_PINCTRL_DR 0xd0 +#define REG_PINCTRL_IF_CTL 0xe0 + +/* REG_PINCTRL_PU_PD field values */ +#define REG_PU_PD_TRISTATE 0 +#define REG_PU_PD_UP 1 +#define REG_PU_PD_DOWN 2 +#define REG_PU_PD_REPEATER 3 + +/* REG_PINCTRL_DR field values */ +#define REG_DR_2mA 0 +#define REG_DR_4mA 1 +#define REG_DR_8mA 2 +#define REG_DR_12mA 3 + +/** + * struct tz1090_function - TZ1090 pinctrl mux function + * @name: The name of the function, exported to pinctrl core. + * @groups: An array of pin groups that may select this function. + * @ngroups: The number of entries in @groups. + */ +struct tz1090_function { + const char *name; + const char * const *groups; + unsigned int ngroups; +}; + +/** + * struct tz1090_muxdesc - TZ1090 individual mux description + * @funcs: Function for each mux value. + * @reg: Mux register offset. 0 if unsupported. + * @bit: Mux register bit. 0 if unsupported. + * @width: Mux field width. 0 if unsupported. + * + * A representation of a group of signals (possibly just one signal) in the + * TZ1090 which can be muxed to a set of functions or sub muxes. + */ +struct tz1090_muxdesc { + int funcs[5]; + u16 reg; + u8 bit; + u8 width; +}; + +/** + * struct tz1090_pingroup - TZ1090 pin group + * @name: Name of pin group. + * @pins: Array of pin numbers in this pin group. + * @npins: Number of pins in this pin group. + * @mux: Top level mux. + * @drv: Drive control supported, 0 if unsupported. + * This means Schmitt, Slew, and Drive strength. + * @slw_bit: Slew register bit. 0 if unsupported. + * The same bit is used for Schmitt, and Drive (*2). + * @func: Currently muxed function. + * @func_count: Number of pins using current mux function. + * + * A representation of a group of pins (possibly just one pin) in the TZ1090 + * pin controller. Each group allows some parameter or parameters to be + * configured. The most common is mux function selection. + */ +struct tz1090_pingroup { + const char *name; + const unsigned int *pins; + unsigned int npins; + struct tz1090_muxdesc mux; + + bool drv; + u8 slw_bit; + + int func; + unsigned int func_count; +}; + +/* + * Most pins affected by the pinmux can also be GPIOs. Define these first. + * These must match how the GPIO driver names/numbers its pins. + */ + +enum tz1090_pin { + /* GPIO pins */ + TZ1090_PIN_SDIO_CLK, + TZ1090_PIN_SDIO_CMD, + TZ1090_PIN_SDIO_D0, + TZ1090_PIN_SDIO_D1, + TZ1090_PIN_SDIO_D2, + TZ1090_PIN_SDIO_D3, + TZ1090_PIN_SDH_CD, + TZ1090_PIN_SDH_WP, + TZ1090_PIN_SPI0_MCLK, + TZ1090_PIN_SPI0_CS0, + TZ1090_PIN_SPI0_CS1, + TZ1090_PIN_SPI0_CS2, + TZ1090_PIN_SPI0_DOUT, + TZ1090_PIN_SPI0_DIN, + TZ1090_PIN_SPI1_MCLK, + TZ1090_PIN_SPI1_CS0, + TZ1090_PIN_SPI1_CS1, + TZ1090_PIN_SPI1_CS2, + TZ1090_PIN_SPI1_DOUT, + TZ1090_PIN_SPI1_DIN, + TZ1090_PIN_UART0_RXD, + TZ1090_PIN_UART0_TXD, + TZ1090_PIN_UART0_CTS, + TZ1090_PIN_UART0_RTS, + TZ1090_PIN_UART1_RXD, + TZ1090_PIN_UART1_TXD, + TZ1090_PIN_SCB0_SDAT, + TZ1090_PIN_SCB0_SCLK, + TZ1090_PIN_SCB1_SDAT, + TZ1090_PIN_SCB1_SCLK, + TZ1090_PIN_SCB2_SDAT, + TZ1090_PIN_SCB2_SCLK, + TZ1090_PIN_I2S_MCLK, + TZ1090_PIN_I2S_BCLK_OUT, + TZ1090_PIN_I2S_LRCLK_OUT, + TZ1090_PIN_I2S_DOUT0, + TZ1090_PIN_I2S_DOUT1, + TZ1090_PIN_I2S_DOUT2, + TZ1090_PIN_I2S_DIN, + TZ1090_PIN_PDM_A, + TZ1090_PIN_PDM_B, + TZ1090_PIN_PDM_C, + TZ1090_PIN_PDM_D, + TZ1090_PIN_TFT_RED0, + TZ1090_PIN_TFT_RED1, + TZ1090_PIN_TFT_RED2, + TZ1090_PIN_TFT_RED3, + TZ1090_PIN_TFT_RED4, + TZ1090_PIN_TFT_RED5, + TZ1090_PIN_TFT_RED6, + TZ1090_PIN_TFT_RED7, + TZ1090_PIN_TFT_GREEN0, + TZ1090_PIN_TFT_GREEN1, + TZ1090_PIN_TFT_GREEN2, + TZ1090_PIN_TFT_GREEN3, + TZ1090_PIN_TFT_GREEN4, + TZ1090_PIN_TFT_GREEN5, + TZ1090_PIN_TFT_GREEN6, + TZ1090_PIN_TFT_GREEN7, + TZ1090_PIN_TFT_BLUE0, + TZ1090_PIN_TFT_BLUE1, + TZ1090_PIN_TFT_BLUE2, + TZ1090_PIN_TFT_BLUE3, + TZ1090_PIN_TFT_BLUE4, + TZ1090_PIN_TFT_BLUE5, + TZ1090_PIN_TFT_BLUE6, + TZ1090_PIN_TFT_BLUE7, + TZ1090_PIN_TFT_VDDEN_GD, + TZ1090_PIN_TFT_PANELCLK, + TZ1090_PIN_TFT_BLANK_LS, + TZ1090_PIN_TFT_VSYNC_NS, + TZ1090_PIN_TFT_HSYNC_NR, + TZ1090_PIN_TFT_VD12ACB, + TZ1090_PIN_TFT_PWRSAVE, + TZ1090_PIN_TX_ON, + TZ1090_PIN_RX_ON, + TZ1090_PIN_PLL_ON, + TZ1090_PIN_PA_ON, + TZ1090_PIN_RX_HP, + TZ1090_PIN_GAIN0, + TZ1090_PIN_GAIN1, + TZ1090_PIN_GAIN2, + TZ1090_PIN_GAIN3, + TZ1090_PIN_GAIN4, + TZ1090_PIN_GAIN5, + TZ1090_PIN_GAIN6, + TZ1090_PIN_GAIN7, + TZ1090_PIN_ANT_SEL0, + TZ1090_PIN_ANT_SEL1, + TZ1090_PIN_SDH_CLK_IN, + + /* Non-GPIO pins */ + TZ1090_PIN_TCK, + TZ1090_PIN_TRST, + TZ1090_PIN_TDI, + TZ1090_PIN_TDO, + TZ1090_PIN_TMS, + TZ1090_PIN_CLK_OUT0, + TZ1090_PIN_CLK_OUT1, + + NUM_GPIOS = TZ1090_PIN_TCK, +}; + +/* Pin names */ + +static const struct pinctrl_pin_desc tz1090_pins[] = { + /* GPIO pins */ + PINCTRL_PIN(TZ1090_PIN_SDIO_CLK, "sdio_clk"), + PINCTRL_PIN(TZ1090_PIN_SDIO_CMD, "sdio_cmd"), + PINCTRL_PIN(TZ1090_PIN_SDIO_D0, "sdio_d0"), + PINCTRL_PIN(TZ1090_PIN_SDIO_D1, "sdio_d1"), + PINCTRL_PIN(TZ1090_PIN_SDIO_D2, "sdio_d2"), + PINCTRL_PIN(TZ1090_PIN_SDIO_D3, "sdio_d3"), + PINCTRL_PIN(TZ1090_PIN_SDH_CD, "sdh_cd"), + PINCTRL_PIN(TZ1090_PIN_SDH_WP, "sdh_wp"), + PINCTRL_PIN(TZ1090_PIN_SPI0_MCLK, "spi0_mclk"), + PINCTRL_PIN(TZ1090_PIN_SPI0_CS0, "spi0_cs0"), + PINCTRL_PIN(TZ1090_PIN_SPI0_CS1, "spi0_cs1"), + PINCTRL_PIN(TZ1090_PIN_SPI0_CS2, "spi0_cs2"), + PINCTRL_PIN(TZ1090_PIN_SPI0_DOUT, "spi0_dout"), + PINCTRL_PIN(TZ1090_PIN_SPI0_DIN, "spi0_din"), + PINCTRL_PIN(TZ1090_PIN_SPI1_MCLK, "spi1_mclk"), + PINCTRL_PIN(TZ1090_PIN_SPI1_CS0, "spi1_cs0"), + PINCTRL_PIN(TZ1090_PIN_SPI1_CS1, "spi1_cs1"), + PINCTRL_PIN(TZ1090_PIN_SPI1_CS2, "spi1_cs2"), + PINCTRL_PIN(TZ1090_PIN_SPI1_DOUT, "spi1_dout"), + PINCTRL_PIN(TZ1090_PIN_SPI1_DIN, "spi1_din"), + PINCTRL_PIN(TZ1090_PIN_UART0_RXD, "uart0_rxd"), + PINCTRL_PIN(TZ1090_PIN_UART0_TXD, "uart0_txd"), + PINCTRL_PIN(TZ1090_PIN_UART0_CTS, "uart0_cts"), + PINCTRL_PIN(TZ1090_PIN_UART0_RTS, "uart0_rts"), + PINCTRL_PIN(TZ1090_PIN_UART1_RXD, "uart1_rxd"), + PINCTRL_PIN(TZ1090_PIN_UART1_TXD, "uart1_txd"), + PINCTRL_PIN(TZ1090_PIN_SCB0_SDAT, "scb0_sdat"), + PINCTRL_PIN(TZ1090_PIN_SCB0_SCLK, "scb0_sclk"), + PINCTRL_PIN(TZ1090_PIN_SCB1_SDAT, "scb1_sdat"), + PINCTRL_PIN(TZ1090_PIN_SCB1_SCLK, "scb1_sclk"), + PINCTRL_PIN(TZ1090_PIN_SCB2_SDAT, "scb2_sdat"), + PINCTRL_PIN(TZ1090_PIN_SCB2_SCLK, "scb2_sclk"), + PINCTRL_PIN(TZ1090_PIN_I2S_MCLK, "i2s_mclk"), + PINCTRL_PIN(TZ1090_PIN_I2S_BCLK_OUT, "i2s_bclk_out"), + PINCTRL_PIN(TZ1090_PIN_I2S_LRCLK_OUT, "i2s_lrclk_out"), + PINCTRL_PIN(TZ1090_PIN_I2S_DOUT0, "i2s_dout0"), + PINCTRL_PIN(TZ1090_PIN_I2S_DOUT1, "i2s_dout1"), + PINCTRL_PIN(TZ1090_PIN_I2S_DOUT2, "i2s_dout2"), + PINCTRL_PIN(TZ1090_PIN_I2S_DIN, "i2s_din"), + PINCTRL_PIN(TZ1090_PIN_PDM_A, "pdm_a"), + PINCTRL_PIN(TZ1090_PIN_PDM_B, "pdm_b"), + PINCTRL_PIN(TZ1090_PIN_PDM_C, "pdm_c"), + PINCTRL_PIN(TZ1090_PIN_PDM_D, "pdm_d"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED0, "tft_red0"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED1, "tft_red1"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED2, "tft_red2"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED3, "tft_red3"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED4, "tft_red4"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED5, "tft_red5"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED6, "tft_red6"), + PINCTRL_PIN(TZ1090_PIN_TFT_RED7, "tft_red7"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN0, "tft_green0"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN1, "tft_green1"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN2, "tft_green2"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN3, "tft_green3"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN4, "tft_green4"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN5, "tft_green5"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN6, "tft_green6"), + PINCTRL_PIN(TZ1090_PIN_TFT_GREEN7, "tft_green7"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE0, "tft_blue0"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE1, "tft_blue1"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE2, "tft_blue2"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE3, "tft_blue3"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE4, "tft_blue4"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE5, "tft_blue5"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE6, "tft_blue6"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLUE7, "tft_blue7"), + PINCTRL_PIN(TZ1090_PIN_TFT_VDDEN_GD, "tft_vdden_gd"), + PINCTRL_PIN(TZ1090_PIN_TFT_PANELCLK, "tft_panelclk"), + PINCTRL_PIN(TZ1090_PIN_TFT_BLANK_LS, "tft_blank_ls"), + PINCTRL_PIN(TZ1090_PIN_TFT_VSYNC_NS, "tft_vsync_ns"), + PINCTRL_PIN(TZ1090_PIN_TFT_HSYNC_NR, "tft_hsync_nr"), + PINCTRL_PIN(TZ1090_PIN_TFT_VD12ACB, "tft_vd12acb"), + PINCTRL_PIN(TZ1090_PIN_TFT_PWRSAVE, "tft_pwrsave"), + PINCTRL_PIN(TZ1090_PIN_TX_ON, "tx_on"), + PINCTRL_PIN(TZ1090_PIN_RX_ON, "rx_on"), + PINCTRL_PIN(TZ1090_PIN_PLL_ON, "pll_on"), + PINCTRL_PIN(TZ1090_PIN_PA_ON, "pa_on"), + PINCTRL_PIN(TZ1090_PIN_RX_HP, "rx_hp"), + PINCTRL_PIN(TZ1090_PIN_GAIN0, "gain0"), + PINCTRL_PIN(TZ1090_PIN_GAIN1, "gain1"), + PINCTRL_PIN(TZ1090_PIN_GAIN2, "gain2"), + PINCTRL_PIN(TZ1090_PIN_GAIN3, "gain3"), + PINCTRL_PIN(TZ1090_PIN_GAIN4, "gain4"), + PINCTRL_PIN(TZ1090_PIN_GAIN5, "gain5"), + PINCTRL_PIN(TZ1090_PIN_GAIN6, "gain6"), + PINCTRL_PIN(TZ1090_PIN_GAIN7, "gain7"), + PINCTRL_PIN(TZ1090_PIN_ANT_SEL0, "ant_sel0"), + PINCTRL_PIN(TZ1090_PIN_ANT_SEL1, "ant_sel1"), + PINCTRL_PIN(TZ1090_PIN_SDH_CLK_IN, "sdh_clk_in"), + + /* Non-GPIO pins */ + PINCTRL_PIN(TZ1090_PIN_TCK, "tck"), + PINCTRL_PIN(TZ1090_PIN_TRST, "trst"), + PINCTRL_PIN(TZ1090_PIN_TDI, "tdi"), + PINCTRL_PIN(TZ1090_PIN_TDO, "tdo"), + PINCTRL_PIN(TZ1090_PIN_TMS, "tms"), + PINCTRL_PIN(TZ1090_PIN_CLK_OUT0, "clk_out0"), + PINCTRL_PIN(TZ1090_PIN_CLK_OUT1, "clk_out1"), +}; + +/* Pins in each pin group */ + +static const unsigned int spi1_cs2_pins[] = { + TZ1090_PIN_SPI1_CS2, +}; + +static const unsigned int pdm_d_pins[] = { + TZ1090_PIN_PDM_D, +}; + +static const unsigned int tft_pins[] = { + TZ1090_PIN_TFT_RED0, + TZ1090_PIN_TFT_RED1, + TZ1090_PIN_TFT_RED2, + TZ1090_PIN_TFT_RED3, + TZ1090_PIN_TFT_RED4, + TZ1090_PIN_TFT_RED5, + TZ1090_PIN_TFT_RED6, + TZ1090_PIN_TFT_RED7, + TZ1090_PIN_TFT_GREEN0, + TZ1090_PIN_TFT_GREEN1, + TZ1090_PIN_TFT_GREEN2, + TZ1090_PIN_TFT_GREEN3, + TZ1090_PIN_TFT_GREEN4, + TZ1090_PIN_TFT_GREEN5, + TZ1090_PIN_TFT_GREEN6, + TZ1090_PIN_TFT_GREEN7, + TZ1090_PIN_TFT_BLUE0, + TZ1090_PIN_TFT_BLUE1, + TZ1090_PIN_TFT_BLUE2, + TZ1090_PIN_TFT_BLUE3, + TZ1090_PIN_TFT_BLUE4, + TZ1090_PIN_TFT_BLUE5, + TZ1090_PIN_TFT_BLUE6, + TZ1090_PIN_TFT_BLUE7, + TZ1090_PIN_TFT_VDDEN_GD, + TZ1090_PIN_TFT_PANELCLK, + TZ1090_PIN_TFT_BLANK_LS, + TZ1090_PIN_TFT_VSYNC_NS, + TZ1090_PIN_TFT_HSYNC_NR, + TZ1090_PIN_TFT_VD12ACB, + TZ1090_PIN_TFT_PWRSAVE, +}; + +static const unsigned int afe_pins[] = { + TZ1090_PIN_TX_ON, + TZ1090_PIN_RX_ON, + TZ1090_PIN_PLL_ON, + TZ1090_PIN_PA_ON, + TZ1090_PIN_RX_HP, + TZ1090_PIN_ANT_SEL0, + TZ1090_PIN_ANT_SEL1, + TZ1090_PIN_GAIN0, + TZ1090_PIN_GAIN1, + TZ1090_PIN_GAIN2, + TZ1090_PIN_GAIN3, + TZ1090_PIN_GAIN4, + TZ1090_PIN_GAIN5, + TZ1090_PIN_GAIN6, + TZ1090_PIN_GAIN7, +}; + +static const unsigned int sdio_pins[] = { + TZ1090_PIN_SDIO_CLK, + TZ1090_PIN_SDIO_CMD, + TZ1090_PIN_SDIO_D0, + TZ1090_PIN_SDIO_D1, + TZ1090_PIN_SDIO_D2, + TZ1090_PIN_SDIO_D3, +}; + +static const unsigned int sdh_pins[] = { + TZ1090_PIN_SDH_CD, + TZ1090_PIN_SDH_WP, + TZ1090_PIN_SDH_CLK_IN, +}; + +static const unsigned int spi0_pins[] = { + TZ1090_PIN_SPI0_MCLK, + TZ1090_PIN_SPI0_CS0, + TZ1090_PIN_SPI0_CS1, + TZ1090_PIN_SPI0_CS2, + TZ1090_PIN_SPI0_DOUT, + TZ1090_PIN_SPI0_DIN, +}; + +static const unsigned int spi1_pins[] = { + TZ1090_PIN_SPI1_MCLK, + TZ1090_PIN_SPI1_CS0, + TZ1090_PIN_SPI1_CS1, + TZ1090_PIN_SPI1_CS2, + TZ1090_PIN_SPI1_DOUT, + TZ1090_PIN_SPI1_DIN, +}; + +static const unsigned int uart0_pins[] = { + TZ1090_PIN_UART0_RTS, + TZ1090_PIN_UART0_CTS, + TZ1090_PIN_UART0_TXD, + TZ1090_PIN_UART0_RXD, +}; + +static const unsigned int uart1_pins[] = { + TZ1090_PIN_UART1_TXD, + TZ1090_PIN_UART1_RXD, +}; + +static const unsigned int uart_pins[] = { + TZ1090_PIN_UART1_TXD, + TZ1090_PIN_UART1_RXD, + TZ1090_PIN_UART0_RTS, + TZ1090_PIN_UART0_CTS, + TZ1090_PIN_UART0_TXD, + TZ1090_PIN_UART0_RXD, +}; + +static const unsigned int scb0_pins[] = { + TZ1090_PIN_SCB0_SDAT, + TZ1090_PIN_SCB0_SCLK, +}; + +static const unsigned int scb1_pins[] = { + TZ1090_PIN_SCB1_SDAT, + TZ1090_PIN_SCB1_SCLK, +}; + +static const unsigned int scb2_pins[] = { + TZ1090_PIN_SCB2_SDAT, + TZ1090_PIN_SCB2_SCLK, +}; + +static const unsigned int i2s_pins[] = { + TZ1090_PIN_I2S_MCLK, + TZ1090_PIN_I2S_BCLK_OUT, + TZ1090_PIN_I2S_LRCLK_OUT, + TZ1090_PIN_I2S_DOUT0, + TZ1090_PIN_I2S_DOUT1, + TZ1090_PIN_I2S_DOUT2, + TZ1090_PIN_I2S_DIN, +}; + +static const unsigned int jtag_pins[] = { + TZ1090_PIN_TCK, + TZ1090_PIN_TRST, + TZ1090_PIN_TDI, + TZ1090_PIN_TDO, + TZ1090_PIN_TMS, +}; + +/* Pins in each drive pin group */ + +static const unsigned int drive_sdio_pins[] = { + TZ1090_PIN_SDIO_CLK, + TZ1090_PIN_SDIO_CMD, + TZ1090_PIN_SDIO_D0, + TZ1090_PIN_SDIO_D1, + TZ1090_PIN_SDIO_D2, + TZ1090_PIN_SDIO_D3, + TZ1090_PIN_SDH_WP, + TZ1090_PIN_SDH_CD, + TZ1090_PIN_SDH_CLK_IN, +}; + +static const unsigned int drive_i2s_pins[] = { + TZ1090_PIN_CLK_OUT1, + TZ1090_PIN_I2S_DIN, + TZ1090_PIN_I2S_DOUT0, + TZ1090_PIN_I2S_DOUT1, + TZ1090_PIN_I2S_DOUT2, + TZ1090_PIN_I2S_LRCLK_OUT, + TZ1090_PIN_I2S_BCLK_OUT, + TZ1090_PIN_I2S_MCLK, +}; + +static const unsigned int drive_scb0_pins[] = { + TZ1090_PIN_SCB0_SCLK, + TZ1090_PIN_SCB0_SDAT, + TZ1090_PIN_PDM_D, + TZ1090_PIN_PDM_C, +}; + +static const unsigned int drive_pdm_pins[] = { + TZ1090_PIN_CLK_OUT0, + TZ1090_PIN_PDM_B, + TZ1090_PIN_PDM_A, +}; + +/* Pin groups each function can be muxed to */ + +/* + * The magic "perip" function allows otherwise non-muxing pins to be enabled in + * peripheral mode. + */ +static const char * const perip_groups[] = { + /* non-muxing convenient gpio pingroups */ + "uart", + "uart0", + "uart1", + "spi0", + "spi1", + "scb0", + "scb1", + "scb2", + "i2s", + /* individual pins not part of a pin mux group */ + "spi0_mclk", + "spi0_cs0", + "spi0_cs1", + "spi0_cs2", + "spi0_dout", + "spi0_din", + "spi1_mclk", + "spi1_cs0", + "spi1_cs1", + "spi1_dout", + "spi1_din", + "uart0_rxd", + "uart0_txd", + "uart0_cts", + "uart0_rts", + "uart1_rxd", + "uart1_txd", + "scb0_sdat", + "scb0_sclk", + "scb1_sdat", + "scb1_sclk", + "scb2_sdat", + "scb2_sclk", + "i2s_mclk", + "i2s_bclk_out", + "i2s_lrclk_out", + "i2s_dout0", + "i2s_dout1", + "i2s_dout2", + "i2s_din", + "pdm_a", + "pdm_b", + "pdm_c", +}; + +static const char * const sdh_sdio_groups[] = { + "sdh", + "sdio", + /* sdh pins */ + "sdh_cd", + "sdh_wp", + "sdh_clk_in", + /* sdio pins */ + "sdio_clk", + "sdio_cmd", + "sdio_d0", + "sdio_d1", + "sdio_d2", + "sdio_d3", +}; + +static const char * const spi1_cs2_groups[] = { + "spi1_cs2", +}; + +static const char * const pdm_dac_groups[] = { + "pdm_d", +}; + +static const char * const usb_vbus_groups[] = { + "spi1_cs2", + "pdm_d", +}; + +static const char * const afe_groups[] = { + "afe", + /* afe pins */ + "tx_on", + "rx_on", + "pll_on", + "pa_on", + "rx_hp", + "ant_sel0", + "ant_sel1", + "gain0", + "gain1", + "gain2", + "gain3", + "gain4", + "gain5", + "gain6", + "gain7", +}; + +static const char * const tft_groups[] = { + "tft", + /* tft pins */ + "tft_red0", + "tft_red1", + "tft_red2", + "tft_red3", + "tft_red4", + "tft_red5", + "tft_red6", + "tft_red7", + "tft_green0", + "tft_green1", + "tft_green2", + "tft_green3", + "tft_green4", + "tft_green5", + "tft_green6", + "tft_green7", + "tft_blue0", + "tft_blue1", + "tft_blue2", + "tft_blue3", + "tft_blue4", + "tft_blue5", + "tft_blue6", + "tft_blue7", + "tft_vdden_gd", + "tft_panelclk", + "tft_blank_ls", + "tft_vsync_ns", + "tft_hsync_nr", + "tft_vd12acb", + "tft_pwrsave", +}; + +/* Mux functions that can be used by a mux */ + +enum tz1090_mux { + /* internal placeholder */ + TZ1090_MUX_NA = -1, + /* magic per-non-muxing-GPIO-pin peripheral mode mux */ + TZ1090_MUX_PERIP, + /* SDH/SDIO mux */ + TZ1090_MUX_SDH, + TZ1090_MUX_SDIO, + /* USB_VBUS muxes */ + TZ1090_MUX_SPI1_CS2, + TZ1090_MUX_PDM_DAC, + TZ1090_MUX_USB_VBUS, + /* AFE mux */ + TZ1090_MUX_AFE, + TZ1090_MUX_TS_OUT_0, + /* EXT_DAC mux */ + TZ1090_MUX_DAC, + TZ1090_MUX_NOT_IQADC_STB, + TZ1090_MUX_IQDAC_STB, + /* TFT mux */ + TZ1090_MUX_TFT, + TZ1090_MUX_EXT_DAC, + TZ1090_MUX_TS_OUT_1, + TZ1090_MUX_LCD_TRACE, + TZ1090_MUX_PHY_RINGOSC, +}; + +#define FUNCTION(mux, fname, group) \ + [(TZ1090_MUX_ ## mux)] = { \ + .name = #fname, \ + .groups = group##_groups, \ + .ngroups = ARRAY_SIZE(group##_groups), \ + } +/* For intermediate functions with submuxes */ +#define NULL_FUNCTION(mux, fname) \ + [(TZ1090_MUX_ ## mux)] = { \ + .name = #fname, \ + } + +/* Must correlate with enum tz1090_mux */ +static const struct tz1090_function tz1090_functions[] = { + /* FUNCTION function name pingroups */ + FUNCTION(PERIP, perip, perip), + FUNCTION(SDH, sdh, sdh_sdio), + FUNCTION(SDIO, sdio, sdh_sdio), + FUNCTION(SPI1_CS2, spi1_cs2, spi1_cs2), + FUNCTION(PDM_DAC, pdm_dac, pdm_dac), + FUNCTION(USB_VBUS, usb_vbus, usb_vbus), + FUNCTION(AFE, afe, afe), + FUNCTION(TS_OUT_0, ts_out_0, afe), + FUNCTION(DAC, ext_dac, tft), + FUNCTION(NOT_IQADC_STB, not_iqadc_stb, tft), + FUNCTION(IQDAC_STB, iqdac_stb, tft), + FUNCTION(TFT, tft, tft), + NULL_FUNCTION(EXT_DAC, _ext_dac), + FUNCTION(TS_OUT_1, ts_out_1, tft), + FUNCTION(LCD_TRACE, lcd_trace, tft), + FUNCTION(PHY_RINGOSC, phy_ringosc, tft), +}; + +/* Sub muxes */ + +/** + * MUX() - Initialise a mux description. + * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none) + * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none) + * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none) + * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none) + * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none) + * @mux_r: Mux register (REG_PINCTRL_ is prepended) + * @mux_b: Bit number in register that the mux field begins + * @mux_w: Width of mux field in register + */ +#define MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) \ + { \ + .funcs = { \ + TZ1090_MUX_ ## f0, \ + TZ1090_MUX_ ## f1, \ + TZ1090_MUX_ ## f2, \ + TZ1090_MUX_ ## f3, \ + TZ1090_MUX_ ## f4, \ + }, \ + .reg = (REG_PINCTRL_ ## mux_r), \ + .bit = (mux_b), \ + .width = (mux_w), \ + } + +/** + * DEFINE_SUBMUX() - Defines a submux description separate from a pin group. + * @mux: Mux name (_submux is appended) + * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none) + * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none) + * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none) + * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none) + * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none) + * @mux_r: Mux register (REG_PINCTRL_ is prepended) + * @mux_b: Bit number in register that the mux field begins + * @mux_w: Width of mux field in register + * + * A sub mux is a nested mux that can be bound to a magic function number used + * by another mux description. For example value 4 of the top level mux might + * correspond to a function which has a submux pointed to in tz1090_submux[]. + * The outer mux can then take on any function in the top level mux or the + * submux, and if a submux function is chosen both muxes are updated to route + * the signal from the submux. + * + * The submux can be defined with DEFINE_SUBMUX and pointed to from + * tz1090_submux[] using SUBMUX. + */ +#define DEFINE_SUBMUX(mux, f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) \ + static struct tz1090_muxdesc mux ## _submux = \ + MUX(f0, f1, f2, f3, f4, mux_r, mux_b, mux_w) + +/** + * SUBMUX() - Link a submux to a function number. + * @f: Function name (TZ1090_MUX_ is prepended) + * @submux: Submux name (_submux is appended) + * + * For use in tz1090_submux[] initialisation to link an intermediate function + * number to a particular submux description. It indicates that when the + * function is chosen the signal is connected to the submux. + */ +#define SUBMUX(f, submux) [(TZ1090_MUX_ ## f)] = &(submux ## _submux) + +/** + * MUX_PG() - Initialise a pin group with mux control + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + * @f0: Function 0 (TZ1090_MUX_ is prepended, NA for none) + * @f1: Function 1 (TZ1090_MUX_ is prepended, NA for none) + * @f2: Function 2 (TZ1090_MUX_ is prepended, NA for none) + * @f3: Function 3 (TZ1090_MUX_ is prepended, NA for none) + * @f4: Function 4 (TZ1090_MUX_ is prepended, NA for none) + * @mux_r: Mux register (REG_PINCTRL_ is prepended) + * @mux_b: Bit number in register that the mux field begins + * @mux_w: Width of mux field in register + */ +#define MUX_PG(pg_name, f0, f1, f2, f3, f4, \ + mux_r, mux_b, mux_w) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .mux = MUX(f0, f1, f2, f3, f4, \ + mux_r, mux_b, mux_w), \ + } + +/** + * SIMPLE_PG() - Initialise a simple convenience pin group + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + * + * A simple pin group is simply used for binding pins together so they can be + * referred to by a single name instead of having to list every pin + * individually. + */ +#define SIMPLE_PG(pg_name) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + } + +/** + * DRV_PG() - Initialise a pin group with drive control + * @pg_name: Pin group name (stringified, _pins appended to get pins array) + * @slw_b: Slew register bit. + * The same bit is used for Schmitt, and Drive (*2). + */ +#define DRV_PG(pg_name, slw_b) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .drv = true, \ + .slw_bit = (slw_b), \ + } + +/* + * Define main muxing pin groups + */ + +/* submuxes */ + +/* name f0, f1, f2, f3, f4, mux r/b/w */ +DEFINE_SUBMUX(ext_dac, DAC, NOT_IQADC_STB, IQDAC_STB, NA, NA, IF_CTL, 6, 2); + +/* bind submuxes to internal functions */ +static struct tz1090_muxdesc *tz1090_submux[] = { + SUBMUX(EXT_DAC, ext_dac), +}; + +/* + * These are the pin mux groups. Pin muxing can be enabled and disabled for each + * pin individually so these groups are internal. The mapping of pins to pin mux + * group is below (tz1090_mux_pins). + */ +static struct tz1090_pingroup tz1090_mux_groups[] = { + /* Muxing pin groups */ + /* pg_name, f0, f1, f2, f3, f4, mux r/b/w */ + MUX_PG(sdh, SDH, SDIO, NA, NA, NA, IF_CTL, 20, 2), + MUX_PG(sdio, SDIO, SDH, NA, NA, NA, IF_CTL, 16, 2), + MUX_PG(spi1_cs2, SPI1_CS2, USB_VBUS, NA, NA, NA, IF_CTL, 10, 2), + MUX_PG(pdm_d, PDM_DAC, USB_VBUS, NA, NA, NA, IF_CTL, 8, 2), + MUX_PG(afe, AFE, TS_OUT_0, NA, NA, NA, IF_CTL, 4, 2), + MUX_PG(tft, TFT, EXT_DAC, TS_OUT_1, LCD_TRACE, PHY_RINGOSC, IF_CTL, 0, 3), +}; + +/* + * This is the mapping from GPIO pins to pin mux groups in tz1090_mux_groups[]. + * Pins which aren't muxable to multiple peripherals are set to + * TZ1090_MUX_GROUP_MAX to enable the "perip" function to enable/disable + * peripheral control of the pin. + * + * This array is initialised in tz1090_init_mux_pins(). + */ +static u8 tz1090_mux_pins[NUM_GPIOS]; + +/* TZ1090_MUX_GROUP_MAX is used in tz1090_mux_pins[] for non-muxing pins */ +#define TZ1090_MUX_GROUP_MAX ARRAY_SIZE(tz1090_mux_groups) + +/** + * tz1090_init_mux_pins() - Initialise GPIO pin to mux group mapping. + * + * Initialises the tz1090_mux_pins[] array to be the inverse of the pin lists in + * each pin mux group in tz1090_mux_groups[]. + * + * It is assumed that no pin mux groups overlap (share pins). + */ +static void __init tz1090_init_mux_pins(void) +{ + unsigned int g, p; + const struct tz1090_pingroup *grp; + const unsigned int *pin; + + for (p = 0; p < NUM_GPIOS; ++p) + tz1090_mux_pins[p] = TZ1090_MUX_GROUP_MAX; + + grp = tz1090_mux_groups; + for (g = 0, grp = tz1090_mux_groups; + g < ARRAY_SIZE(tz1090_mux_groups); ++g, ++grp) + for (pin = grp->pins, p = 0; p < grp->npins; ++p, ++pin) + tz1090_mux_pins[*pin] = g; +} + +/* + * These are the externally visible pin groups. Some of them allow group control + * of drive configuration. Some are just simple convenience pingroups. All the + * internal pin mux groups in tz1090_mux_groups[] are mirrored here with the + * same pins. + * Pseudo pin groups follow in the group numbers after this array for each GPIO + * pin. Any group used for muxing must have all pins belonging to the same pin + * mux group. + */ +static struct tz1090_pingroup tz1090_groups[] = { + /* Pin groups with drive control (with no out of place pins) */ + /* pg_name, slw/schmitt/drv b */ + DRV_PG(jtag, 11 /* 11, 22 */), + DRV_PG(tft, 10 /* 10, 20 */), + DRV_PG(scb2, 9 /* 9, 18 */), + DRV_PG(spi0, 7 /* 7, 14 */), + DRV_PG(uart, 5 /* 5, 10 */), + DRV_PG(scb1, 4 /* 4, 8 */), + DRV_PG(spi1, 3 /* 3, 6 */), + DRV_PG(afe, 0 /* 0, 0 */), + + /* + * Drive specific pin groups (with odd combinations of pins which makes + * the pin group naming somewhat arbitrary) + */ + /* pg_name, slw/schmitt/drv b */ + DRV_PG(drive_sdio, 8 /* 8, 16 */), /* sdio_* + sdh_* */ + DRV_PG(drive_i2s, 6 /* 6, 12 */), /* i2s_* + clk_out1 */ + DRV_PG(drive_scb0, 2 /* 2, 4 */), /* scb0_* + pdm_{c,d} */ + DRV_PG(drive_pdm, 1 /* 1, 2 */), /* pdm_{a,b} + clk_out0 */ + + /* Convenience pin groups */ + /* pg_name */ + SIMPLE_PG(uart0), + SIMPLE_PG(uart1), + SIMPLE_PG(scb0), + SIMPLE_PG(i2s), + SIMPLE_PG(sdh), + SIMPLE_PG(sdio), + + /* pseudo-pingroups for each GPIO pin follow */ +}; + +/** + * struct tz1090_pmx - Private pinctrl data + * @dev: Platform device + * @pctl: Pin control device + * @regs: Register region + * @lock: Lock protecting coherency of pin_en, gpio_en, and SELECT regs + * @pin_en: Pins that have been enabled (32 pins packed into each element) + * @gpio_en: GPIOs that have been enabled (32 pins packed into each element) + */ +struct tz1090_pmx { + struct device *dev; + struct pinctrl_dev *pctl; + void __iomem *regs; + spinlock_t lock; + u32 pin_en[3]; + u32 gpio_en[3]; +}; + +static inline u32 pmx_read(struct tz1090_pmx *pmx, u32 reg) +{ + return ioread32(pmx->regs + reg); +} + +static inline void pmx_write(struct tz1090_pmx *pmx, u32 val, u32 reg) +{ + iowrite32(val, pmx->regs + reg); +} + +/* + * Pin control operations + */ + +/* each GPIO pin has it's own pseudo pingroup containing only itself */ + +static int tz1090_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tz1090_groups) + NUM_GPIOS; +} + +static const char *tz1090_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group) +{ + if (group < ARRAY_SIZE(tz1090_groups)) { + /* normal pingroup */ + return tz1090_groups[group].name; + } else { + /* individual gpio pin pseudo-pingroup */ + unsigned int pin = group - ARRAY_SIZE(tz1090_groups); + return tz1090_pins[pin].name; + } +} + +static int tz1090_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int group, + const unsigned int **pins, + unsigned int *num_pins) +{ + if (group < ARRAY_SIZE(tz1090_groups)) { + /* normal pingroup */ + *pins = tz1090_groups[group].pins; + *num_pins = tz1090_groups[group].npins; + } else { + /* individual gpio pin pseudo-pingroup */ + unsigned int pin = group - ARRAY_SIZE(tz1090_groups); + *pins = &tz1090_pins[pin].number; + *num_pins = 1; + } + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void tz1090_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int offset) +{ + seq_printf(s, " %s", dev_name(pctldev->dev)); +} +#endif + +static int reserve_map(struct device *dev, struct pinctrl_map **map, + unsigned int *reserved_maps, unsigned int *num_maps, + unsigned int reserve) +{ + unsigned int old_num = *reserved_maps; + unsigned int new_num = *num_maps + reserve; + struct pinctrl_map *new_map; + + if (old_num >= new_num) + return 0; + + new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); + if (!new_map) { + dev_err(dev, "krealloc(map) failed\n"); + return -ENOMEM; + } + + memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); + + *map = new_map; + *reserved_maps = new_num; + + return 0; +} + +static int add_map_mux(struct pinctrl_map **map, unsigned int *reserved_maps, + unsigned int *num_maps, const char *group, + const char *function) +{ + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = group; + (*map)[*num_maps].data.mux.function = function; + (*num_maps)++; + + return 0; +} + +static int add_map_configs(struct device *dev, + struct pinctrl_map **map, + unsigned int *reserved_maps, unsigned int *num_maps, + const char *group, unsigned long *configs, + unsigned int num_configs) +{ + unsigned long *dup_configs; + + if (WARN_ON(*num_maps == *reserved_maps)) + return -ENOSPC; + + dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), + GFP_KERNEL); + if (!dup_configs) { + dev_err(dev, "kmemdup(configs) failed\n"); + return -ENOMEM; + } + + (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)[*num_maps].data.configs.group_or_pin = group; + (*map)[*num_maps].data.configs.configs = dup_configs; + (*map)[*num_maps].data.configs.num_configs = num_configs; + (*num_maps)++; + + return 0; +} + +static void tz1090_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, + unsigned int num_maps) +{ + int i; + + for (i = 0; i < num_maps; i++) + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + kfree(map[i].data.configs.configs); + + kfree(map); +} + +static int tz1090_pinctrl_dt_subnode_to_map(struct device *dev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *reserved_maps, + unsigned int *num_maps) +{ + int ret; + const char *function; + unsigned long *configs = NULL; + unsigned int num_configs = 0; + unsigned int reserve; + struct property *prop; + const char *group; + + ret = of_property_read_string(np, "tz1090,function", &function); + if (ret < 0) { + /* EINVAL=missing, which is fine since it's optional */ + if (ret != -EINVAL) + dev_err(dev, "could not parse property function\n"); + function = NULL; + } + + ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + if (ret) + return ret; + + reserve = 0; + if (function != NULL) + reserve++; + if (num_configs) + reserve++; + ret = of_property_count_strings(np, "tz1090,pins"); + if (ret < 0) { + dev_err(dev, "could not parse property pins\n"); + goto exit; + } + reserve *= ret; + + ret = reserve_map(dev, map, reserved_maps, num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "tz1090,pins", prop, group) { + if (function) { + ret = add_map_mux(map, reserved_maps, num_maps, + group, function); + if (ret < 0) + goto exit; + } + + if (num_configs) { + ret = add_map_configs(dev, map, reserved_maps, + num_maps, group, configs, + num_configs); + if (ret < 0) + goto exit; + } + } + + ret = 0; + +exit: + kfree(configs); + return ret; +} + +static int tz1090_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + unsigned int reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = tz1090_pinctrl_dt_subnode_to_map(pctldev->dev, np, map, + &reserved_maps, + num_maps); + if (ret < 0) { + tz1090_pinctrl_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + + return 0; +} + +static struct pinctrl_ops tz1090_pinctrl_ops = { + .get_groups_count = tz1090_pinctrl_get_groups_count, + .get_group_name = tz1090_pinctrl_get_group_name, + .get_group_pins = tz1090_pinctrl_get_group_pins, +#ifdef CONFIG_DEBUG_FS + .pin_dbg_show = tz1090_pinctrl_pin_dbg_show, +#endif + .dt_node_to_map = tz1090_pinctrl_dt_node_to_map, + .dt_free_map = tz1090_pinctrl_dt_free_map, +}; + +/* + * Pin mux operations + */ + +static int tz1090_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(tz1090_functions); +} + +static const char *tz1090_pinctrl_get_func_name(struct pinctrl_dev *pctldev, + unsigned int function) +{ + return tz1090_functions[function].name; +} + +static int tz1090_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, + unsigned int function, + const char * const **groups, + unsigned int * const num_groups) +{ + /* pingroup functions */ + *groups = tz1090_functions[function].groups; + *num_groups = tz1090_functions[function].ngroups; + return 0; +} + +/** + * tz1090_pinctrl_select() - update bit in SELECT register + * @pmx: Pinmux data + * @pin: Pin number (must be within GPIO range) + */ +static void tz1090_pinctrl_select(struct tz1090_pmx *pmx, + unsigned int pin) +{ + u32 reg, reg_shift, select, val; + unsigned int pmx_index, pmx_shift; + unsigned long flags; + + /* uses base 32 instead of base 30 */ + pmx_index = pin >> 5; + pmx_shift = pin & 0x1f; + + /* select = !perip || gpio */ + select = ((~pmx->pin_en[pmx_index] | + pmx->gpio_en[pmx_index]) >> pmx_shift) & 1; + + /* find register and bit offset (base 30) */ + reg = REG_PINCTRL_SELECT + 4*(pin / 30); + reg_shift = pin % 30; + + /* modify gpio select bit */ + __global_lock2(flags); + val = pmx_read(pmx, reg); + val &= ~BIT(reg_shift); + val |= select << reg_shift; + pmx_write(pmx, val, reg); + __global_unlock2(flags); +} + +/** + * tz1090_pinctrl_gpio_select() - enable/disable GPIO usage for a pin + * @pmx: Pinmux data + * @pin: Pin number + * @gpio_select: true to enable pin as GPIO, + * false to leave control to whatever function is enabled + * + * Records that GPIO usage is enabled/disabled so that enabling a function + * doesn't override the SELECT register bit. + */ +static void tz1090_pinctrl_gpio_select(struct tz1090_pmx *pmx, + unsigned int pin, + bool gpio_select) +{ + unsigned int index, shift; + u32 gpio_en; + + if (pin >= NUM_GPIOS) + return; + + /* uses base 32 instead of base 30 */ + index = pin >> 5; + shift = pin & 0x1f; + + spin_lock(&pmx->lock); + + /* keep a record whether gpio is selected */ + gpio_en = pmx->gpio_en[index]; + gpio_en &= ~BIT(shift); + if (gpio_select) + gpio_en |= BIT(shift); + pmx->gpio_en[index] = gpio_en; + + /* update the select bit */ + tz1090_pinctrl_select(pmx, pin); + + spin_unlock(&pmx->lock); +} + +/** + * tz1090_pinctrl_perip_select() - enable/disable peripheral interface for a pin + * @pmx: Pinmux data + * @pin: Pin number + * @perip_select: true to enable peripheral interface when not GPIO, + * false to leave pin in GPIO mode + * + * Records that peripheral usage is enabled/disabled so that SELECT register can + * be set appropriately when GPIO is disabled. + */ +static void tz1090_pinctrl_perip_select(struct tz1090_pmx *pmx, + unsigned int pin, + bool perip_select) +{ + unsigned int index, shift; + u32 pin_en; + + if (pin >= NUM_GPIOS) + return; + + /* uses base 32 instead of base 30 */ + index = pin >> 5; + shift = pin & 0x1f; + + spin_lock(&pmx->lock); + + /* keep a record whether peripheral is selected */ + pin_en = pmx->pin_en[index]; + pin_en &= ~BIT(shift); + if (perip_select) + pin_en |= BIT(shift); + pmx->pin_en[index] = pin_en; + + /* update the select bit */ + tz1090_pinctrl_select(pmx, pin); + + spin_unlock(&pmx->lock); +} + +/** + * tz1090_pinctrl_enable_mux() - Switch a pin mux group to a function. + * @pmx: Pinmux data + * @desc: Pinmux description + * @function: Function to switch to + * + * Enable a particular function on a pin mux group. Since pin mux descriptions + * are nested this function is recursive. + */ +static int tz1090_pinctrl_enable_mux(struct tz1090_pmx *pmx, + const struct tz1090_muxdesc *desc, + unsigned int function) +{ + const int *fit; + unsigned long flags; + int mux; + unsigned int func, ret; + u32 reg, mask; + + /* find the mux value for this function, searching recursively */ + for (mux = 0, fit = desc->funcs; + mux < ARRAY_SIZE(desc->funcs); ++mux, ++fit) { + func = *fit; + if (func == function) + goto found_mux; + + /* maybe it's a sub-mux */ + if (func < ARRAY_SIZE(tz1090_submux) && tz1090_submux[func]) { + ret = tz1090_pinctrl_enable_mux(pmx, + tz1090_submux[func], + function); + if (!ret) + goto found_mux; + } + } + + return -EINVAL; +found_mux: + + /* Set up the mux */ + if (desc->width) { + mask = (BIT(desc->width) - 1) << desc->bit; + __global_lock2(flags); + reg = pmx_read(pmx, desc->reg); + reg &= ~mask; + reg |= (mux << desc->bit) & mask; + pmx_write(pmx, reg, desc->reg); + __global_unlock2(flags); + } + + return 0; +} + +/** + * tz1090_pinctrl_enable() - Enable a function on a pin group. + * @pctldev: Pin control data + * @function: Function index to enable + * @group: Group index to enable + * + * Enable a particular function on a group of pins. The per GPIO pin pseudo pin + * groups can be used (in which case the pin will be enabled in peripheral mode + * and if it belongs to a pin mux group the mux will be switched if it isn't + * already in use. Some convenience pin groups can also be used in which case + * the effect is the same as enabling the function on each individual pin in the + * group. + */ +static int tz1090_pinctrl_enable(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + struct tz1090_pingroup *grp; + int ret; + unsigned int pin_num, mux_group, i, npins; + const unsigned int *pins; + + /* group of pins? */ + if (group < ARRAY_SIZE(tz1090_groups)) { + grp = &tz1090_groups[group]; + npins = grp->npins; + pins = grp->pins; + /* + * All pins in the group must belong to the same mux group, + * which allows us to just use the mux group of the first pin. + * By explicitly listing permitted pingroups for each function + * the pinmux core should ensure this is always the case. + */ + } else { + pin_num = group - ARRAY_SIZE(tz1090_groups); + npins = 1; + pins = &pin_num; + } + mux_group = tz1090_mux_pins[*pins]; + + /* no mux group, but can still be individually muxed to peripheral */ + if (mux_group >= TZ1090_MUX_GROUP_MAX) { + if (function == TZ1090_MUX_PERIP) + goto mux_pins; + return -EINVAL; + } + + /* mux group already set to a different function? */ + grp = &tz1090_mux_groups[mux_group]; + if (grp->func_count && grp->func != function) { + dev_err(pctldev->dev, + "%s: can't mux pin(s) to '%s', group already muxed to '%s'\n", + __func__, tz1090_functions[function].name, + tz1090_functions[grp->func].name); + return -EBUSY; + } + + dev_dbg(pctldev->dev, "%s: muxing %u pin(s) in '%s' to '%s'\n", + __func__, npins, grp->name, tz1090_functions[function].name); + + /* if first pin in mux group to be enabled, enable the group mux */ + if (!grp->func_count) { + grp->func = function; + ret = tz1090_pinctrl_enable_mux(pmx, &grp->mux, function); + if (ret) + return ret; + } + /* add pins to ref count and mux individually to peripheral */ + grp->func_count += npins; +mux_pins: + for (i = 0; i < npins; ++i) + tz1090_pinctrl_perip_select(pmx, pins[i], true); + + return 0; +} + +/** + * tz1090_pinctrl_disable() - Disable a function on a pin group. + * @pctldev: Pin control data + * @function: Function index to disable + * @group: Group index to disable + * + * Disable a particular function on a group of pins. The per GPIO pin pseudo pin + * groups can be used (in which case the pin will be taken out of peripheral + * mode. Some convenience pin groups can also be used in which case the effect + * is the same as enabling the function on each individual pin in the group. + */ +static void tz1090_pinctrl_disable(struct pinctrl_dev *pctldev, + unsigned int function, unsigned int group) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + struct tz1090_pingroup *grp; + unsigned int pin_num, mux_group, i, npins; + const unsigned int *pins; + + /* group of pins? */ + if (group < ARRAY_SIZE(tz1090_groups)) { + grp = &tz1090_groups[group]; + npins = grp->npins; + pins = grp->pins; + /* + * All pins in the group must belong to the same mux group, + * which allows us to just use the mux group of the first pin. + * By explicitly listing permitted pingroups for each function + * the pinmux core should ensure this is always the case. + */ + } else { + pin_num = group - ARRAY_SIZE(tz1090_groups); + npins = 1; + pins = &pin_num; + } + mux_group = tz1090_mux_pins[*pins]; + + /* no mux group, but can still be individually muxed to peripheral */ + if (mux_group >= TZ1090_MUX_GROUP_MAX) { + if (function == TZ1090_MUX_PERIP) + goto unmux_pins; + return; + } + + /* mux group already set to a different function? */ + grp = &tz1090_mux_groups[mux_group]; + dev_dbg(pctldev->dev, "%s: unmuxing %u pin(s) in '%s' from '%s'\n", + __func__, npins, grp->name, tz1090_functions[function].name); + + /* subtract pins from ref count and unmux individually */ + WARN_ON(grp->func_count < npins); + grp->func_count -= npins; +unmux_pins: + for (i = 0; i < npins; ++i) + tz1090_pinctrl_perip_select(pmx, pins[i], false); +} + +/** + * tz1090_pinctrl_gpio_request_enable() - Put pin in GPIO mode. + * @pctldev: Pin control data + * @range: GPIO range + * @pin: Pin number + * + * Puts a particular pin into GPIO mode, disabling peripheral control until it's + * disabled again. + */ +static int tz1090_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + tz1090_pinctrl_gpio_select(pmx, pin, true); + return 0; +} + +/** + * tz1090_pinctrl_gpio_disable_free() - Take pin out of GPIO mode. + * @pctldev: Pin control data + * @range: GPIO range + * @pin: Pin number + * + * Take a particular pin out of GPIO mode. If the pin is enabled for a + * peripheral it will return to peripheral mode. + */ +static void tz1090_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + tz1090_pinctrl_gpio_select(pmx, pin, false); +} + +static struct pinmux_ops tz1090_pinmux_ops = { + .get_functions_count = tz1090_pinctrl_get_funcs_count, + .get_function_name = tz1090_pinctrl_get_func_name, + .get_function_groups = tz1090_pinctrl_get_func_groups, + .enable = tz1090_pinctrl_enable, + .disable = tz1090_pinctrl_disable, + .gpio_request_enable = tz1090_pinctrl_gpio_request_enable, + .gpio_disable_free = tz1090_pinctrl_gpio_disable_free, +}; + +/* + * Pin config operations + */ + +struct tz1090_pinconf_pullup { + unsigned char index; + unsigned char shift; +}; + +/* The mapping of pin to pull up/down register index and shift */ +static struct tz1090_pinconf_pullup tz1090_pinconf_pullup[] = { + {5, 22}, /* 0 - TZ1090_PIN_SDIO_CLK */ + {0, 14}, /* 1 - TZ1090_PIN_SDIO_CMD */ + {0, 6}, /* 2 - TZ1090_PIN_SDIO_D0 */ + {0, 8}, /* 3 - TZ1090_PIN_SDIO_D1 */ + {0, 10}, /* 4 - TZ1090_PIN_SDIO_D2 */ + {0, 12}, /* 5 - TZ1090_PIN_SDIO_D3 */ + {0, 2}, /* 6 - TZ1090_PIN_SDH_CD */ + {0, 4}, /* 7 - TZ1090_PIN_SDH_WP */ + {0, 16}, /* 8 - TZ1090_PIN_SPI0_MCLK */ + {0, 18}, /* 9 - TZ1090_PIN_SPI0_CS0 */ + {0, 20}, /* 10 - TZ1090_PIN_SPI0_CS1 */ + {0, 22}, /* 11 - TZ1090_PIN_SPI0_CS2 */ + {0, 24}, /* 12 - TZ1090_PIN_SPI0_DOUT */ + {0, 26}, /* 13 - TZ1090_PIN_SPI0_DIN */ + {0, 28}, /* 14 - TZ1090_PIN_SPI1_MCLK */ + {0, 30}, /* 15 - TZ1090_PIN_SPI1_CS0 */ + {1, 0}, /* 16 - TZ1090_PIN_SPI1_CS1 */ + {1, 2}, /* 17 - TZ1090_PIN_SPI1_CS2 */ + {1, 4}, /* 18 - TZ1090_PIN_SPI1_DOUT */ + {1, 6}, /* 19 - TZ1090_PIN_SPI1_DIN */ + {1, 8}, /* 20 - TZ1090_PIN_UART0_RXD */ + {1, 10}, /* 21 - TZ1090_PIN_UART0_TXD */ + {1, 12}, /* 22 - TZ1090_PIN_UART0_CTS */ + {1, 14}, /* 23 - TZ1090_PIN_UART0_RTS */ + {1, 16}, /* 24 - TZ1090_PIN_UART1_RXD */ + {1, 18}, /* 25 - TZ1090_PIN_UART1_TXD */ + {1, 20}, /* 26 - TZ1090_PIN_SCB0_SDAT */ + {1, 22}, /* 27 - TZ1090_PIN_SCB0_SCLK */ + {1, 24}, /* 28 - TZ1090_PIN_SCB1_SDAT */ + {1, 26}, /* 29 - TZ1090_PIN_SCB1_SCLK */ + + {1, 28}, /* 30 - TZ1090_PIN_SCB2_SDAT */ + {1, 30}, /* 31 - TZ1090_PIN_SCB2_SCLK */ + {2, 0}, /* 32 - TZ1090_PIN_I2S_MCLK */ + {2, 2}, /* 33 - TZ1090_PIN_I2S_BCLK_OUT */ + {2, 4}, /* 34 - TZ1090_PIN_I2S_LRCLK_OUT */ + {2, 6}, /* 35 - TZ1090_PIN_I2S_DOUT0 */ + {2, 8}, /* 36 - TZ1090_PIN_I2S_DOUT1 */ + {2, 10}, /* 37 - TZ1090_PIN_I2S_DOUT2 */ + {2, 12}, /* 38 - TZ1090_PIN_I2S_DIN */ + {4, 12}, /* 39 - TZ1090_PIN_PDM_A */ + {4, 14}, /* 40 - TZ1090_PIN_PDM_B */ + {4, 18}, /* 41 - TZ1090_PIN_PDM_C */ + {4, 20}, /* 42 - TZ1090_PIN_PDM_D */ + {2, 14}, /* 43 - TZ1090_PIN_TFT_RED0 */ + {2, 16}, /* 44 - TZ1090_PIN_TFT_RED1 */ + {2, 18}, /* 45 - TZ1090_PIN_TFT_RED2 */ + {2, 20}, /* 46 - TZ1090_PIN_TFT_RED3 */ + {2, 22}, /* 47 - TZ1090_PIN_TFT_RED4 */ + {2, 24}, /* 48 - TZ1090_PIN_TFT_RED5 */ + {2, 26}, /* 49 - TZ1090_PIN_TFT_RED6 */ + {2, 28}, /* 50 - TZ1090_PIN_TFT_RED7 */ + {2, 30}, /* 51 - TZ1090_PIN_TFT_GREEN0 */ + {3, 0}, /* 52 - TZ1090_PIN_TFT_GREEN1 */ + {3, 2}, /* 53 - TZ1090_PIN_TFT_GREEN2 */ + {3, 4}, /* 54 - TZ1090_PIN_TFT_GREEN3 */ + {3, 6}, /* 55 - TZ1090_PIN_TFT_GREEN4 */ + {3, 8}, /* 56 - TZ1090_PIN_TFT_GREEN5 */ + {3, 10}, /* 57 - TZ1090_PIN_TFT_GREEN6 */ + {3, 12}, /* 58 - TZ1090_PIN_TFT_GREEN7 */ + {3, 14}, /* 59 - TZ1090_PIN_TFT_BLUE0 */ + + {3, 16}, /* 60 - TZ1090_PIN_TFT_BLUE1 */ + {3, 18}, /* 61 - TZ1090_PIN_TFT_BLUE2 */ + {3, 20}, /* 62 - TZ1090_PIN_TFT_BLUE3 */ + {3, 22}, /* 63 - TZ1090_PIN_TFT_BLUE4 */ + {3, 24}, /* 64 - TZ1090_PIN_TFT_BLUE5 */ + {3, 26}, /* 65 - TZ1090_PIN_TFT_BLUE6 */ + {3, 28}, /* 66 - TZ1090_PIN_TFT_BLUE7 */ + {3, 30}, /* 67 - TZ1090_PIN_TFT_VDDEN_GD */ + {4, 0}, /* 68 - TZ1090_PIN_TFT_PANELCLK */ + {4, 2}, /* 69 - TZ1090_PIN_TFT_BLANK_LS */ + {4, 4}, /* 70 - TZ1090_PIN_TFT_VSYNC_NS */ + {4, 6}, /* 71 - TZ1090_PIN_TFT_HSYNC_NR */ + {4, 8}, /* 72 - TZ1090_PIN_TFT_VD12ACB */ + {4, 10}, /* 73 - TZ1090_PIN_TFT_PWRSAVE */ + {4, 24}, /* 74 - TZ1090_PIN_TX_ON */ + {4, 26}, /* 75 - TZ1090_PIN_RX_ON */ + {4, 28}, /* 76 - TZ1090_PIN_PLL_ON */ + {4, 30}, /* 77 - TZ1090_PIN_PA_ON */ + {5, 0}, /* 78 - TZ1090_PIN_RX_HP */ + {5, 6}, /* 79 - TZ1090_PIN_GAIN0 */ + {5, 8}, /* 80 - TZ1090_PIN_GAIN1 */ + {5, 10}, /* 81 - TZ1090_PIN_GAIN2 */ + {5, 12}, /* 82 - TZ1090_PIN_GAIN3 */ + {5, 14}, /* 83 - TZ1090_PIN_GAIN4 */ + {5, 16}, /* 84 - TZ1090_PIN_GAIN5 */ + {5, 18}, /* 85 - TZ1090_PIN_GAIN6 */ + {5, 20}, /* 86 - TZ1090_PIN_GAIN7 */ + {5, 2}, /* 87 - TZ1090_PIN_ANT_SEL0 */ + {5, 4}, /* 88 - TZ1090_PIN_ANT_SEL1 */ + {0, 0}, /* 89 - TZ1090_PIN_SDH_CLK_IN */ + + {5, 24}, /* 90 - TZ1090_PIN_TCK */ + {5, 26}, /* 91 - TZ1090_PIN_TRST */ + {5, 28}, /* 92 - TZ1090_PIN_TDI */ + {5, 30}, /* 93 - TZ1090_PIN_TDO */ + {6, 0}, /* 94 - TZ1090_PIN_TMS */ + {4, 16}, /* 95 - TZ1090_PIN_CLK_OUT0 */ + {4, 22}, /* 96 - TZ1090_PIN_CLK_OUT1 */ +}; + +static int tz1090_pinconf_reg(struct pinctrl_dev *pctldev, + unsigned int pin, + enum pin_config_param param, + bool report_err, + u32 *reg, u32 *width, u32 *mask, u32 *shift, + u32 *val) +{ + struct tz1090_pinconf_pullup *pu; + + /* All supported pins have controllable input bias */ + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + *val = REG_PU_PD_TRISTATE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + *val = REG_PU_PD_UP; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + *val = REG_PU_PD_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + *val = REG_PU_PD_REPEATER; + break; + default: + return -ENOTSUPP; + }; + + /* Only input bias parameters supported */ + pu = &tz1090_pinconf_pullup[pin]; + *reg = REG_PINCTRL_PU_PD + 4*pu->index; + *shift = pu->shift; + *width = 2; + + /* Calculate field information */ + *mask = (BIT(*width) - 1) << *shift; + + return 0; +} + +static int tz1090_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + int ret; + u32 reg, width, mask, shift, val, tmp, arg; + + /* Get register information */ + ret = tz1090_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; + + /* Extract field from register */ + tmp = pmx_read(pmx, reg); + arg = ((tmp & mask) >> shift) == val; + + /* Config not active */ + if (!arg) + return -EINVAL; + + /* And pack config */ + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tz1090_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long config) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(config); + unsigned int arg = pinconf_to_config_argument(config); + int ret; + u32 reg, width, mask, shift, val, tmp; + unsigned long flags; + + dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n", + __func__, tz1090_pins[pin].name, config); + + /* Get register information */ + ret = tz1090_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; + + /* Unpack argument and range check it */ + if (arg > 1) { + dev_dbg(pctldev->dev, "%s: arg %u out of range\n", + __func__, arg); + return -EINVAL; + } + + /* Write register field */ + __global_lock2(flags); + tmp = pmx_read(pmx, reg); + tmp &= ~mask; + if (arg) + tmp |= val << shift; + pmx_write(pmx, tmp, reg); + __global_unlock2(flags); + + return 0; +} + +static const int tz1090_boolean_map[] = { + [0] = -EINVAL, + [1] = 1, +}; + +static const int tz1090_dr_map[] = { + [REG_DR_2mA] = 2, + [REG_DR_4mA] = 4, + [REG_DR_8mA] = 8, + [REG_DR_12mA] = 12, +}; + +static int tz1090_pinconf_group_reg(struct pinctrl_dev *pctldev, + const struct tz1090_pingroup *g, + enum pin_config_param param, + bool report_err, + u32 *reg, u32 *width, u32 *mask, u32 *shift, + const int **map) +{ + /* Drive configuration applies in groups, but not to all groups. */ + if (!g->drv) { + if (report_err) + dev_dbg(pctldev->dev, + "%s: group %s has no drive control\n", + __func__, g->name); + return -ENOTSUPP; + } + + /* Find information about drive parameter's register */ + switch (param) { + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + *reg = REG_PINCTRL_SCHMITT; + *width = 1; + *map = tz1090_boolean_map; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + *reg = REG_PINCTRL_DR; + *width = 2; + *map = tz1090_dr_map; + break; + default: + return -ENOTSUPP; + }; + + /* Calculate field information */ + *shift = g->slw_bit * *width; + *mask = (BIT(*width) - 1) << *shift; + + return 0; +} + +static int tz1090_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *config) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pingroup *g; + enum pin_config_param param = pinconf_to_config_param(*config); + int ret, arg; + unsigned int pin; + u32 reg, width, mask, shift, val; + const int *map; + + if (group >= ARRAY_SIZE(tz1090_groups)) { + pin = group - ARRAY_SIZE(tz1090_groups); + return tz1090_pinconf_get(pctldev, pin, config); + } + + g = &tz1090_groups[group]; + if (g->npins == 1) { + pin = g->pins[0]; + ret = tz1090_pinconf_get(pctldev, pin, config); + if (ret != -ENOTSUPP) + return ret; + } + + /* Get register information */ + ret = tz1090_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, &map); + if (ret < 0) + return ret; + + /* Extract field from register */ + val = pmx_read(pmx, reg); + arg = map[(val & mask) >> shift]; + if (arg < 0) + return arg; + + /* And pack config */ + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int tz1090_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned int group, unsigned long config) +{ + struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tz1090_pingroup *g; + enum pin_config_param param = pinconf_to_config_param(config); + unsigned int arg, pin, i; + const unsigned int *pit; + int ret; + u32 reg, width, mask, shift, val; + unsigned long flags; + const int *map; + + if (group >= ARRAY_SIZE(tz1090_groups)) { + pin = group - ARRAY_SIZE(tz1090_groups); + return tz1090_pinconf_set(pctldev, pin, config); + } + + g = &tz1090_groups[group]; + if (g->npins == 1) { + pin = g->pins[0]; + ret = tz1090_pinconf_set(pctldev, pin, config); + if (ret != -ENOTSUPP) + return ret; + } + + dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n", + __func__, g->name, config); + + /* Get register information */ + ret = tz1090_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, &map); + if (ret < 0) { + /* + * Maybe we're trying to set a per-pin configuration of a group, + * so do the pins one by one. This is mainly as a convenience. + */ + for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) { + ret = tz1090_pinconf_set(pctldev, *pit, config); + if (ret) + return ret; + } + return 0; + } + + /* Unpack argument and map it to register value */ + arg = pinconf_to_config_argument(config); + for (i = 0; i < BIT(width); ++i) { + if (map[i] == arg || (map[i] == -EINVAL && !arg)) { + /* Write register field */ + __global_lock2(flags); + val = pmx_read(pmx, reg); + val &= ~mask; + val |= i << shift; + pmx_write(pmx, val, reg); + __global_unlock2(flags); + return 0; + } + } + + dev_dbg(pctldev->dev, "%s: arg %u not supported\n", + __func__, arg); + return -EINVAL; +} + +static struct pinconf_ops tz1090_pinconf_ops = { + .is_generic = true, + .pin_config_get = tz1090_pinconf_get, + .pin_config_set = tz1090_pinconf_set, + .pin_config_group_get = tz1090_pinconf_group_get, + .pin_config_group_set = tz1090_pinconf_group_set, + .pin_config_config_dbg_show = pinconf_generic_dump_config, +}; + +/* + * Pin control driver setup + */ + +static struct pinctrl_desc tz1090_pinctrl_desc = { + .pctlops = &tz1090_pinctrl_ops, + .pmxops = &tz1090_pinmux_ops, + .confops = &tz1090_pinconf_ops, + .owner = THIS_MODULE, +}; + +static int tz1090_pinctrl_probe(struct platform_device *pdev) +{ + struct tz1090_pmx *pmx; + struct resource *res; + + pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); + if (!pmx) { + dev_err(&pdev->dev, "Can't alloc tz1090_pmx\n"); + return -ENOMEM; + } + pmx->dev = &pdev->dev; + spin_lock_init(&pmx->lock); + + tz1090_pinctrl_desc.name = dev_name(&pdev->dev); + tz1090_pinctrl_desc.pins = tz1090_pins; + tz1090_pinctrl_desc.npins = ARRAY_SIZE(tz1090_pins); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing MEM resource\n"); + return -ENODEV; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, + "Couldn't request MEM resource\n"); + return -ENODEV; + } + + pmx->regs = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!pmx->regs) { + dev_err(&pdev->dev, "Couldn't ioremap regs\n"); + return -ENODEV; + } + + pmx->pctl = pinctrl_register(&tz1090_pinctrl_desc, &pdev->dev, pmx); + if (!pmx->pctl) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, pmx); + + dev_info(&pdev->dev, "TZ1090 pinctrl driver initialised\n"); + + return 0; +} + +static int tz1090_pinctrl_remove(struct platform_device *pdev) +{ + struct tz1090_pmx *pmx = platform_get_drvdata(pdev); + + pinctrl_unregister(pmx->pctl); + + return 0; +} + +static struct of_device_id tz1090_pinctrl_of_match[] = { + { .compatible = "img,tz1090-pinctrl", }, + { }, +}; + +static struct platform_driver tz1090_pinctrl_driver = { + .driver = { + .name = "tz1090-pinctrl", + .owner = THIS_MODULE, + .of_match_table = tz1090_pinctrl_of_match, + }, + .probe = tz1090_pinctrl_probe, + .remove = tz1090_pinctrl_remove, +}; + +static int __init tz1090_pinctrl_init(void) +{ + tz1090_init_mux_pins(); + return platform_driver_register(&tz1090_pinctrl_driver); +} +arch_initcall(tz1090_pinctrl_init); + +static void __exit tz1090_pinctrl_exit(void) +{ + platform_driver_unregister(&tz1090_pinctrl_driver); +} +module_exit(tz1090_pinctrl_exit); + +MODULE_AUTHOR("Imagination Technologies Ltd."); +MODULE_DESCRIPTION("Toumaz Xenif TZ1090 pinctrl driver"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, tz1090_pinctrl_of_match); diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 06bfa09bb15c..46a152d17355 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -1100,7 +1100,6 @@ static int u300_pmx_remove(struct platform_device *pdev) struct u300_pmx *upmx = platform_get_drvdata(pdev); pinctrl_unregister(upmx->pctl); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/pinctrl/pinctrl-vf610.c b/drivers/pinctrl/pinctrl-vf610.c new file mode 100644 index 000000000000..68a970b1dbcf --- /dev/null +++ b/drivers/pinctrl/pinctrl-vf610.c @@ -0,0 +1,338 @@ +/* + * VF610 pinctrl driver based on imx pinmux and pinconf core + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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. + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinctrl.h> + +#include "pinctrl-imx.h" + +enum vf610_pads { + VF610_PAD_PTA6 = 0, + VF610_PAD_PTA8 = 1, + VF610_PAD_PTA9 = 2, + VF610_PAD_PTA10 = 3, + VF610_PAD_PTA11 = 4, + VF610_PAD_PTA12 = 5, + VF610_PAD_PTA16 = 6, + VF610_PAD_PTA17 = 7, + VF610_PAD_PTA18 = 8, + VF610_PAD_PTA19 = 9, + VF610_PAD_PTA20 = 10, + VF610_PAD_PTA21 = 11, + VF610_PAD_PTA22 = 12, + VF610_PAD_PTA23 = 13, + VF610_PAD_PTA24 = 14, + VF610_PAD_PTA25 = 15, + VF610_PAD_PTA26 = 16, + VF610_PAD_PTA27 = 17, + VF610_PAD_PTA28 = 18, + VF610_PAD_PTA29 = 19, + VF610_PAD_PTA30 = 20, + VF610_PAD_PTA31 = 21, + VF610_PAD_PTB0 = 22, + VF610_PAD_PTB1 = 23, + VF610_PAD_PTB2 = 24, + VF610_PAD_PTB3 = 25, + VF610_PAD_PTB4 = 26, + VF610_PAD_PTB5 = 27, + VF610_PAD_PTB6 = 28, + VF610_PAD_PTB7 = 29, + VF610_PAD_PTB8 = 30, + VF610_PAD_PTB9 = 31, + VF610_PAD_PTB10 = 32, + VF610_PAD_PTB11 = 33, + VF610_PAD_PTB12 = 34, + VF610_PAD_PTB13 = 35, + VF610_PAD_PTB14 = 36, + VF610_PAD_PTB15 = 37, + VF610_PAD_PTB16 = 38, + VF610_PAD_PTB17 = 39, + VF610_PAD_PTB18 = 40, + VF610_PAD_PTB19 = 41, + VF610_PAD_PTB20 = 42, + VF610_PAD_PTB21 = 43, + VF610_PAD_PTB22 = 44, + VF610_PAD_PTC0 = 45, + VF610_PAD_PTC1 = 46, + VF610_PAD_PTC2 = 47, + VF610_PAD_PTC3 = 48, + VF610_PAD_PTC4 = 49, + VF610_PAD_PTC5 = 50, + VF610_PAD_PTC6 = 51, + VF610_PAD_PTC7 = 52, + VF610_PAD_PTC8 = 53, + VF610_PAD_PTC9 = 54, + VF610_PAD_PTC10 = 55, + VF610_PAD_PTC11 = 56, + VF610_PAD_PTC12 = 57, + VF610_PAD_PTC13 = 58, + VF610_PAD_PTC14 = 59, + VF610_PAD_PTC15 = 60, + VF610_PAD_PTC16 = 61, + VF610_PAD_PTC17 = 62, + VF610_PAD_PTD31 = 63, + VF610_PAD_PTD30 = 64, + VF610_PAD_PTD29 = 65, + VF610_PAD_PTD28 = 66, + VF610_PAD_PTD27 = 67, + VF610_PAD_PTD26 = 68, + VF610_PAD_PTD25 = 69, + VF610_PAD_PTD24 = 70, + VF610_PAD_PTD23 = 71, + VF610_PAD_PTD22 = 72, + VF610_PAD_PTD21 = 73, + VF610_PAD_PTD20 = 74, + VF610_PAD_PTD19 = 75, + VF610_PAD_PTD18 = 76, + VF610_PAD_PTD17 = 77, + VF610_PAD_PTD16 = 78, + VF610_PAD_PTD0 = 79, + VF610_PAD_PTD1 = 80, + VF610_PAD_PTD2 = 81, + VF610_PAD_PTD3 = 82, + VF610_PAD_PTD4 = 83, + VF610_PAD_PTD5 = 84, + VF610_PAD_PTD6 = 85, + VF610_PAD_PTD7 = 86, + VF610_PAD_PTD8 = 87, + VF610_PAD_PTD9 = 88, + VF610_PAD_PTD10 = 89, + VF610_PAD_PTD11 = 90, + VF610_PAD_PTD12 = 91, + VF610_PAD_PTD13 = 92, + VF610_PAD_PTB23 = 93, + VF610_PAD_PTB24 = 94, + VF610_PAD_PTB25 = 95, + VF610_PAD_PTB26 = 96, + VF610_PAD_PTB27 = 97, + VF610_PAD_PTB28 = 98, + VF610_PAD_PTC26 = 99, + VF610_PAD_PTC27 = 100, + VF610_PAD_PTC28 = 101, + VF610_PAD_PTC29 = 102, + VF610_PAD_PTC30 = 103, + VF610_PAD_PTC31 = 104, + VF610_PAD_PTE0 = 105, + VF610_PAD_PTE1 = 106, + VF610_PAD_PTE2 = 107, + VF610_PAD_PTE3 = 108, + VF610_PAD_PTE4 = 109, + VF610_PAD_PTE5 = 110, + VF610_PAD_PTE6 = 111, + VF610_PAD_PTE7 = 112, + VF610_PAD_PTE8 = 113, + VF610_PAD_PTE9 = 114, + VF610_PAD_PTE10 = 115, + VF610_PAD_PTE11 = 116, + VF610_PAD_PTE12 = 117, + VF610_PAD_PTE13 = 118, + VF610_PAD_PTE14 = 119, + VF610_PAD_PTE15 = 120, + VF610_PAD_PTE16 = 121, + VF610_PAD_PTE17 = 122, + VF610_PAD_PTE18 = 123, + VF610_PAD_PTE19 = 124, + VF610_PAD_PTE20 = 125, + VF610_PAD_PTE21 = 126, + VF610_PAD_PTE22 = 127, + VF610_PAD_PTE23 = 128, + VF610_PAD_PTE24 = 129, + VF610_PAD_PTE25 = 130, + VF610_PAD_PTE26 = 131, + VF610_PAD_PTE27 = 132, + VF610_PAD_PTE28 = 133, + VF610_PAD_PTA7 = 134, +}; + +/* Pad names for the pinmux subsystem */ +static const struct pinctrl_pin_desc vf610_pinctrl_pads[] = { + IMX_PINCTRL_PIN(VF610_PAD_PTA6), + IMX_PINCTRL_PIN(VF610_PAD_PTA8), + IMX_PINCTRL_PIN(VF610_PAD_PTA9), + IMX_PINCTRL_PIN(VF610_PAD_PTA10), + IMX_PINCTRL_PIN(VF610_PAD_PTA11), + IMX_PINCTRL_PIN(VF610_PAD_PTA12), + IMX_PINCTRL_PIN(VF610_PAD_PTA16), + IMX_PINCTRL_PIN(VF610_PAD_PTA17), + IMX_PINCTRL_PIN(VF610_PAD_PTA18), + IMX_PINCTRL_PIN(VF610_PAD_PTA19), + IMX_PINCTRL_PIN(VF610_PAD_PTA20), + IMX_PINCTRL_PIN(VF610_PAD_PTA21), + IMX_PINCTRL_PIN(VF610_PAD_PTA22), + IMX_PINCTRL_PIN(VF610_PAD_PTA23), + IMX_PINCTRL_PIN(VF610_PAD_PTA24), + IMX_PINCTRL_PIN(VF610_PAD_PTA25), + IMX_PINCTRL_PIN(VF610_PAD_PTA26), + IMX_PINCTRL_PIN(VF610_PAD_PTA27), + IMX_PINCTRL_PIN(VF610_PAD_PTA28), + IMX_PINCTRL_PIN(VF610_PAD_PTA29), + IMX_PINCTRL_PIN(VF610_PAD_PTA30), + IMX_PINCTRL_PIN(VF610_PAD_PTA31), + IMX_PINCTRL_PIN(VF610_PAD_PTB0), + IMX_PINCTRL_PIN(VF610_PAD_PTB1), + IMX_PINCTRL_PIN(VF610_PAD_PTB2), + IMX_PINCTRL_PIN(VF610_PAD_PTB3), + IMX_PINCTRL_PIN(VF610_PAD_PTB4), + IMX_PINCTRL_PIN(VF610_PAD_PTB5), + IMX_PINCTRL_PIN(VF610_PAD_PTB6), + IMX_PINCTRL_PIN(VF610_PAD_PTB7), + IMX_PINCTRL_PIN(VF610_PAD_PTB8), + IMX_PINCTRL_PIN(VF610_PAD_PTB9), + IMX_PINCTRL_PIN(VF610_PAD_PTB10), + IMX_PINCTRL_PIN(VF610_PAD_PTB11), + IMX_PINCTRL_PIN(VF610_PAD_PTB12), + IMX_PINCTRL_PIN(VF610_PAD_PTB13), + IMX_PINCTRL_PIN(VF610_PAD_PTB14), + IMX_PINCTRL_PIN(VF610_PAD_PTB15), + IMX_PINCTRL_PIN(VF610_PAD_PTB16), + IMX_PINCTRL_PIN(VF610_PAD_PTB17), + IMX_PINCTRL_PIN(VF610_PAD_PTB18), + IMX_PINCTRL_PIN(VF610_PAD_PTB19), + IMX_PINCTRL_PIN(VF610_PAD_PTB20), + IMX_PINCTRL_PIN(VF610_PAD_PTB21), + IMX_PINCTRL_PIN(VF610_PAD_PTB22), + IMX_PINCTRL_PIN(VF610_PAD_PTC0), + IMX_PINCTRL_PIN(VF610_PAD_PTC1), + IMX_PINCTRL_PIN(VF610_PAD_PTC2), + IMX_PINCTRL_PIN(VF610_PAD_PTC3), + IMX_PINCTRL_PIN(VF610_PAD_PTC4), + IMX_PINCTRL_PIN(VF610_PAD_PTC5), + IMX_PINCTRL_PIN(VF610_PAD_PTC6), + IMX_PINCTRL_PIN(VF610_PAD_PTC7), + IMX_PINCTRL_PIN(VF610_PAD_PTC8), + IMX_PINCTRL_PIN(VF610_PAD_PTC9), + IMX_PINCTRL_PIN(VF610_PAD_PTC10), + IMX_PINCTRL_PIN(VF610_PAD_PTC11), + IMX_PINCTRL_PIN(VF610_PAD_PTC12), + IMX_PINCTRL_PIN(VF610_PAD_PTC13), + IMX_PINCTRL_PIN(VF610_PAD_PTC14), + IMX_PINCTRL_PIN(VF610_PAD_PTC15), + IMX_PINCTRL_PIN(VF610_PAD_PTC16), + IMX_PINCTRL_PIN(VF610_PAD_PTC17), + IMX_PINCTRL_PIN(VF610_PAD_PTD31), + IMX_PINCTRL_PIN(VF610_PAD_PTD30), + IMX_PINCTRL_PIN(VF610_PAD_PTD29), + IMX_PINCTRL_PIN(VF610_PAD_PTD28), + IMX_PINCTRL_PIN(VF610_PAD_PTD27), + IMX_PINCTRL_PIN(VF610_PAD_PTD26), + IMX_PINCTRL_PIN(VF610_PAD_PTD25), + IMX_PINCTRL_PIN(VF610_PAD_PTD24), + IMX_PINCTRL_PIN(VF610_PAD_PTD23), + IMX_PINCTRL_PIN(VF610_PAD_PTD22), + IMX_PINCTRL_PIN(VF610_PAD_PTD21), + IMX_PINCTRL_PIN(VF610_PAD_PTD20), + IMX_PINCTRL_PIN(VF610_PAD_PTD19), + IMX_PINCTRL_PIN(VF610_PAD_PTD18), + IMX_PINCTRL_PIN(VF610_PAD_PTD17), + IMX_PINCTRL_PIN(VF610_PAD_PTD16), + IMX_PINCTRL_PIN(VF610_PAD_PTD0), + IMX_PINCTRL_PIN(VF610_PAD_PTD1), + IMX_PINCTRL_PIN(VF610_PAD_PTD2), + IMX_PINCTRL_PIN(VF610_PAD_PTD3), + IMX_PINCTRL_PIN(VF610_PAD_PTD4), + IMX_PINCTRL_PIN(VF610_PAD_PTD5), + IMX_PINCTRL_PIN(VF610_PAD_PTD6), + IMX_PINCTRL_PIN(VF610_PAD_PTD7), + IMX_PINCTRL_PIN(VF610_PAD_PTD8), + IMX_PINCTRL_PIN(VF610_PAD_PTD9), + IMX_PINCTRL_PIN(VF610_PAD_PTD10), + IMX_PINCTRL_PIN(VF610_PAD_PTD11), + IMX_PINCTRL_PIN(VF610_PAD_PTD12), + IMX_PINCTRL_PIN(VF610_PAD_PTD13), + IMX_PINCTRL_PIN(VF610_PAD_PTB23), + IMX_PINCTRL_PIN(VF610_PAD_PTB24), + IMX_PINCTRL_PIN(VF610_PAD_PTB25), + IMX_PINCTRL_PIN(VF610_PAD_PTB26), + IMX_PINCTRL_PIN(VF610_PAD_PTB27), + IMX_PINCTRL_PIN(VF610_PAD_PTB28), + IMX_PINCTRL_PIN(VF610_PAD_PTC26), + IMX_PINCTRL_PIN(VF610_PAD_PTC27), + IMX_PINCTRL_PIN(VF610_PAD_PTC28), + IMX_PINCTRL_PIN(VF610_PAD_PTC29), + IMX_PINCTRL_PIN(VF610_PAD_PTC30), + IMX_PINCTRL_PIN(VF610_PAD_PTC31), + IMX_PINCTRL_PIN(VF610_PAD_PTE0), + IMX_PINCTRL_PIN(VF610_PAD_PTE1), + IMX_PINCTRL_PIN(VF610_PAD_PTE2), + IMX_PINCTRL_PIN(VF610_PAD_PTE3), + IMX_PINCTRL_PIN(VF610_PAD_PTE4), + IMX_PINCTRL_PIN(VF610_PAD_PTE5), + IMX_PINCTRL_PIN(VF610_PAD_PTE6), + IMX_PINCTRL_PIN(VF610_PAD_PTE7), + IMX_PINCTRL_PIN(VF610_PAD_PTE8), + IMX_PINCTRL_PIN(VF610_PAD_PTE9), + IMX_PINCTRL_PIN(VF610_PAD_PTE10), + IMX_PINCTRL_PIN(VF610_PAD_PTE11), + IMX_PINCTRL_PIN(VF610_PAD_PTE12), + IMX_PINCTRL_PIN(VF610_PAD_PTE13), + IMX_PINCTRL_PIN(VF610_PAD_PTE14), + IMX_PINCTRL_PIN(VF610_PAD_PTE15), + IMX_PINCTRL_PIN(VF610_PAD_PTE16), + IMX_PINCTRL_PIN(VF610_PAD_PTE17), + IMX_PINCTRL_PIN(VF610_PAD_PTE18), + IMX_PINCTRL_PIN(VF610_PAD_PTE19), + IMX_PINCTRL_PIN(VF610_PAD_PTE20), + IMX_PINCTRL_PIN(VF610_PAD_PTE21), + IMX_PINCTRL_PIN(VF610_PAD_PTE22), + IMX_PINCTRL_PIN(VF610_PAD_PTE23), + IMX_PINCTRL_PIN(VF610_PAD_PTE24), + IMX_PINCTRL_PIN(VF610_PAD_PTE25), + IMX_PINCTRL_PIN(VF610_PAD_PTE26), + IMX_PINCTRL_PIN(VF610_PAD_PTE27), + IMX_PINCTRL_PIN(VF610_PAD_PTE28), + IMX_PINCTRL_PIN(VF610_PAD_PTA7), +}; + +static struct imx_pinctrl_soc_info vf610_pinctrl_info = { + .pins = vf610_pinctrl_pads, + .npins = ARRAY_SIZE(vf610_pinctrl_pads), + .flags = ZERO_OFFSET_VALID | SHARE_MUX_CONF_REG, +}; + +static struct of_device_id vf610_pinctrl_of_match[] = { + { .compatible = "fsl,vf610-iomuxc", }, + { /* sentinel */ } +}; + +static int vf610_pinctrl_probe(struct platform_device *pdev) +{ + return imx_pinctrl_probe(pdev, &vf610_pinctrl_info); +} + +static struct platform_driver vf610_pinctrl_driver = { + .driver = { + .name = "vf610-pinctrl", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(vf610_pinctrl_of_match), + }, + .probe = vf610_pinctrl_probe, + .remove = imx_pinctrl_remove, +}; + +static int __init vf610_pinctrl_init(void) +{ + return platform_driver_register(&vf610_pinctrl_driver); +} +arch_initcall(vf610_pinctrl_init); + +static void __exit vf610_pinctrl_exit(void) +{ + platform_driver_unregister(&vf610_pinctrl_driver); +} +module_exit(vf610_pinctrl_exit); + +MODULE_DESCRIPTION("Freescale VF610 pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index 3b2fd43ff294..f3fc66b24370 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -18,6 +18,8 @@ #include <linux/ioport.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/pinctrl/machine.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -348,14 +350,72 @@ int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id sh_pfc_of_table[] = { +#ifdef CONFIG_PINCTRL_PFC_R8A73A4 + { + .compatible = "renesas,pfc-r8a73a4", + .data = &r8a73a4_pinmux_info, + }, +#endif +#ifdef CONFIG_PINCTRL_PFC_R8A7740 + { + .compatible = "renesas,pfc-r8a7740", + .data = &r8a7740_pinmux_info, + }, +#endif +#ifdef CONFIG_PINCTRL_PFC_R8A7778 + { + .compatible = "renesas,pfc-r8a7778", + .data = &r8a7778_pinmux_info, + }, +#endif +#ifdef CONFIG_PINCTRL_PFC_R8A7779 + { + .compatible = "renesas,pfc-r8a7779", + .data = &r8a7779_pinmux_info, + }, +#endif +#ifdef CONFIG_PINCTRL_PFC_R8A7790 + { + .compatible = "renesas,pfc-r8a7790", + .data = &r8a7790_pinmux_info, + }, +#endif +#ifdef CONFIG_PINCTRL_PFC_SH7372 + { + .compatible = "renesas,pfc-sh7372", + .data = &sh7372_pinmux_info, + }, +#endif +#ifdef CONFIG_PINCTRL_PFC_SH73A0 + { + .compatible = "renesas,pfc-sh73a0", + .data = &sh73a0_pinmux_info, + }, +#endif + { }, +}; +MODULE_DEVICE_TABLE(of, sh_pfc_of_table); +#endif + static int sh_pfc_probe(struct platform_device *pdev) { + const struct platform_device_id *platid = platform_get_device_id(pdev); +#ifdef CONFIG_OF + struct device_node *np = pdev->dev.of_node; +#endif const struct sh_pfc_soc_info *info; struct sh_pfc *pfc; int ret; - info = pdev->id_entry->driver_data - ? (void *)pdev->id_entry->driver_data : pdev->dev.platform_data; +#ifdef CONFIG_OF + if (np) + info = of_match_device(sh_pfc_of_table, &pdev->dev)->data; + else +#endif + info = platid ? (const void *)platid->driver_data : NULL; + if (info == NULL) return -ENODEV; @@ -501,6 +561,7 @@ static struct platform_driver sh_pfc_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sh_pfc_of_table), }, }; diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 3492ec9a33b7..bc8b028bb5d2 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -14,7 +14,9 @@ #include <linux/err.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinctrl.h> @@ -72,11 +74,214 @@ static void sh_pfc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, seq_printf(s, "%s", DRV_NAME); } +#ifdef CONFIG_OF +static int sh_pfc_map_add_config(struct pinctrl_map *map, + const char *group_or_pin, + enum pinctrl_map_type type, + unsigned long *configs, + unsigned int num_configs) +{ + unsigned long *cfgs; + + cfgs = kmemdup(configs, num_configs * sizeof(*cfgs), + GFP_KERNEL); + if (cfgs == NULL) + return -ENOMEM; + + map->type = type; + map->data.configs.group_or_pin = group_or_pin; + map->data.configs.configs = cfgs; + map->data.configs.num_configs = num_configs; + + return 0; +} + +static int sh_pfc_dt_subnode_to_map(struct device *dev, struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps, unsigned int *index) +{ + struct pinctrl_map *maps = *map; + unsigned int nmaps = *num_maps; + unsigned int idx = *index; + unsigned int num_configs; + const char *function = NULL; + unsigned long *configs; + struct property *prop; + unsigned int num_groups; + unsigned int num_pins; + const char *group; + const char *pin; + int ret; + + /* Parse the function and configuration properties. At least a function + * or one configuration must be specified. + */ + ret = of_property_read_string(np, "renesas,function", &function); + if (ret < 0 && ret != -EINVAL) { + dev_err(dev, "Invalid function in DT\n"); + return ret; + } + + ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + if (ret < 0) + return ret; + + if (!function && num_configs == 0) { + dev_err(dev, + "DT node must contain at least a function or config\n"); + goto done; + } + + /* Count the number of pins and groups and reallocate mappings. */ + ret = of_property_count_strings(np, "renesas,pins"); + if (ret == -EINVAL) { + num_pins = 0; + } else if (ret < 0) { + dev_err(dev, "Invalid pins list in DT\n"); + goto done; + } else { + num_pins = ret; + } + + ret = of_property_count_strings(np, "renesas,groups"); + if (ret == -EINVAL) { + num_groups = 0; + } else if (ret < 0) { + dev_err(dev, "Invalid pin groups list in DT\n"); + goto done; + } else { + num_groups = ret; + } + + if (!num_pins && !num_groups) { + dev_err(dev, "No pin or group provided in DT node\n"); + ret = -ENODEV; + goto done; + } + + if (function) + nmaps += num_groups; + if (configs) + nmaps += num_pins + num_groups; + + maps = krealloc(maps, sizeof(*maps) * nmaps, GFP_KERNEL); + if (maps == NULL) { + ret = -ENOMEM; + goto done; + } + + *map = maps; + *num_maps = nmaps; + + /* Iterate over pins and groups and create the mappings. */ + of_property_for_each_string(np, "renesas,groups", prop, group) { + if (function) { + maps[idx].type = PIN_MAP_TYPE_MUX_GROUP; + maps[idx].data.mux.group = group; + maps[idx].data.mux.function = function; + idx++; + } + + if (configs) { + ret = sh_pfc_map_add_config(&maps[idx], group, + PIN_MAP_TYPE_CONFIGS_GROUP, + configs, num_configs); + if (ret < 0) + goto done; + + idx++; + } + } + + if (!configs) { + ret = 0; + goto done; + } + + of_property_for_each_string(np, "renesas,pins", prop, pin) { + ret = sh_pfc_map_add_config(&maps[idx], pin, + PIN_MAP_TYPE_CONFIGS_PIN, + configs, num_configs); + if (ret < 0) + goto done; + + idx++; + } + +done: + *index = idx; + kfree(configs); + return ret; +} + +static void sh_pfc_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + unsigned int i; + + if (map == NULL) + return; + + for (i = 0; i < num_maps; ++i) { + if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP || + map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(map[i].data.configs.configs); + } + + kfree(map); +} + +static int sh_pfc_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); + struct device *dev = pmx->pfc->dev; + struct device_node *child; + unsigned int index; + int ret; + + *map = NULL; + *num_maps = 0; + index = 0; + + for_each_child_of_node(np, child) { + ret = sh_pfc_dt_subnode_to_map(dev, child, map, num_maps, + &index); + if (ret < 0) + goto done; + } + + /* If no mapping has been found in child nodes try the config node. */ + if (*num_maps == 0) { + ret = sh_pfc_dt_subnode_to_map(dev, np, map, num_maps, &index); + if (ret < 0) + goto done; + } + + if (*num_maps) + return 0; + + dev_err(dev, "no mapping found in node %s\n", np->full_name); + ret = -EINVAL; + +done: + if (ret < 0) + sh_pfc_dt_free_map(pctldev, *map, *num_maps); + + return ret; +} +#endif /* CONFIG_OF */ + static const struct pinctrl_ops sh_pfc_pinctrl_ops = { .get_groups_count = sh_pfc_get_groups_count, .get_group_name = sh_pfc_get_group_name, .get_group_pins = sh_pfc_get_group_pins, .pin_dbg_show = sh_pfc_pin_dbg_show, +#ifdef CONFIG_OF + .dt_node_to_map = sh_pfc_dt_node_to_map, + .dt_free_map = sh_pfc_dt_free_map, +#endif }; static int sh_pfc_get_functions_count(struct pinctrl_dev *pctldev) diff --git a/drivers/pinctrl/sirf/Makefile b/drivers/pinctrl/sirf/Makefile new file mode 100644 index 000000000000..3ffc475ce40c --- /dev/null +++ b/drivers/pinctrl/sirf/Makefile @@ -0,0 +1,5 @@ +# CSR SiRFsoc pinmux support + +obj-y += pinctrl-sirf.o +obj-y += pinctrl-prima2.o +obj-y += pinctrl-atlas6.o diff --git a/drivers/pinctrl/sirf/pinctrl-atlas6.c b/drivers/pinctrl/sirf/pinctrl-atlas6.c new file mode 100644 index 000000000000..1fa39a444171 --- /dev/null +++ b/drivers/pinctrl/sirf/pinctrl-atlas6.c @@ -0,0 +1,947 @@ +/* + * pinctrl pads, groups, functions for CSR SiRFatlasVI + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/pinctrl/pinctrl.h> +#include <linux/bitops.h> + +#include "pinctrl-sirf.h" + +/* + * pad list for the pinmux subsystem + * refer to atlasVI_io_table_v0.93.xls + */ +static const struct pinctrl_pin_desc sirfsoc_pads[] = { + PINCTRL_PIN(0, "gpio0-0"), + PINCTRL_PIN(1, "gpio0-1"), + PINCTRL_PIN(2, "gpio0-2"), + PINCTRL_PIN(3, "gpio0-3"), + PINCTRL_PIN(4, "pwm0"), + PINCTRL_PIN(5, "pwm1"), + PINCTRL_PIN(6, "pwm2"), + PINCTRL_PIN(7, "pwm3"), + PINCTRL_PIN(8, "warm_rst_b"), + PINCTRL_PIN(9, "odo_0"), + PINCTRL_PIN(10, "odo_1"), + PINCTRL_PIN(11, "dr_dir"), + PINCTRL_PIN(12, "rts_0"), + PINCTRL_PIN(13, "scl_1"), + PINCTRL_PIN(14, "ntrst"), + PINCTRL_PIN(15, "sda_1"), + PINCTRL_PIN(16, "x_ldd[16]"), + PINCTRL_PIN(17, "x_ldd[17]"), + PINCTRL_PIN(18, "x_ldd[18]"), + PINCTRL_PIN(19, "x_ldd[19]"), + PINCTRL_PIN(20, "x_ldd[20]"), + PINCTRL_PIN(21, "x_ldd[21]"), + PINCTRL_PIN(22, "x_ldd[22]"), + PINCTRL_PIN(23, "x_ldd[23]"), + PINCTRL_PIN(24, "gps_sgn"), + PINCTRL_PIN(25, "gps_mag"), + PINCTRL_PIN(26, "gps_clk"), + PINCTRL_PIN(27, "sd_cd_b_2"), + PINCTRL_PIN(28, "sd_vcc_on_2"), + PINCTRL_PIN(29, "sd_wp_b_2"), + PINCTRL_PIN(30, "sd_clk_3"), + PINCTRL_PIN(31, "sd_cmd_3"), + + PINCTRL_PIN(32, "x_sd_dat_3[0]"), + PINCTRL_PIN(33, "x_sd_dat_3[1]"), + PINCTRL_PIN(34, "x_sd_dat_3[2]"), + PINCTRL_PIN(35, "x_sd_dat_3[3]"), + PINCTRL_PIN(36, "usb_clk"), + PINCTRL_PIN(37, "usb_dir"), + PINCTRL_PIN(38, "usb_nxt"), + PINCTRL_PIN(39, "usb_stp"), + PINCTRL_PIN(40, "usb_dat[7]"), + PINCTRL_PIN(41, "usb_dat[6]"), + PINCTRL_PIN(42, "x_cko_1"), + PINCTRL_PIN(43, "spi_clk_1"), + PINCTRL_PIN(44, "spi_dout_1"), + PINCTRL_PIN(45, "spi_din_1"), + PINCTRL_PIN(46, "spi_en_1"), + PINCTRL_PIN(47, "x_txd_1"), + PINCTRL_PIN(48, "x_txd_2"), + PINCTRL_PIN(49, "x_rxd_1"), + PINCTRL_PIN(50, "x_rxd_2"), + PINCTRL_PIN(51, "x_usclk_0"), + PINCTRL_PIN(52, "x_utxd_0"), + PINCTRL_PIN(53, "x_urxd_0"), + PINCTRL_PIN(54, "x_utfs_0"), + PINCTRL_PIN(55, "x_urfs_0"), + PINCTRL_PIN(56, "usb_dat5"), + PINCTRL_PIN(57, "usb_dat4"), + PINCTRL_PIN(58, "usb_dat3"), + PINCTRL_PIN(59, "usb_dat2"), + PINCTRL_PIN(60, "usb_dat1"), + PINCTRL_PIN(61, "usb_dat0"), + PINCTRL_PIN(62, "x_ldd[14]"), + PINCTRL_PIN(63, "x_ldd[15]"), + + PINCTRL_PIN(64, "x_gps_gpio"), + PINCTRL_PIN(65, "x_ldd[13]"), + PINCTRL_PIN(66, "x_df_we_b"), + PINCTRL_PIN(67, "x_df_re_b"), + PINCTRL_PIN(68, "x_txd_0"), + PINCTRL_PIN(69, "x_rxd_0"), + PINCTRL_PIN(70, "x_l_lck"), + PINCTRL_PIN(71, "x_l_fck"), + PINCTRL_PIN(72, "x_l_de"), + PINCTRL_PIN(73, "x_ldd[0]"), + PINCTRL_PIN(74, "x_ldd[1]"), + PINCTRL_PIN(75, "x_ldd[2]"), + PINCTRL_PIN(76, "x_ldd[3]"), + PINCTRL_PIN(77, "x_ldd[4]"), + PINCTRL_PIN(78, "x_cko_0"), + PINCTRL_PIN(79, "x_ldd[5]"), + PINCTRL_PIN(80, "x_ldd[6]"), + PINCTRL_PIN(81, "x_ldd[7]"), + PINCTRL_PIN(82, "x_ldd[8]"), + PINCTRL_PIN(83, "x_ldd[9]"), + PINCTRL_PIN(84, "x_ldd[10]"), + PINCTRL_PIN(85, "x_ldd[11]"), + PINCTRL_PIN(86, "x_ldd[12]"), + PINCTRL_PIN(87, "x_vip_vsync"), + PINCTRL_PIN(88, "x_vip_hsync"), + PINCTRL_PIN(89, "x_vip_pxclk"), + PINCTRL_PIN(90, "x_sda_0"), + PINCTRL_PIN(91, "x_scl_0"), + PINCTRL_PIN(92, "x_df_ry_by"), + PINCTRL_PIN(93, "x_df_cs_b[1]"), + PINCTRL_PIN(94, "x_df_cs_b[0]"), + PINCTRL_PIN(95, "x_l_pclk"), + + PINCTRL_PIN(96, "x_df_dqs"), + PINCTRL_PIN(97, "x_df_wp_b"), + PINCTRL_PIN(98, "ac97_sync"), + PINCTRL_PIN(99, "ac97_bit_clk "), + PINCTRL_PIN(100, "ac97_dout"), + PINCTRL_PIN(101, "ac97_din"), + PINCTRL_PIN(102, "x_rtc_io"), +}; + +static const struct sirfsoc_muxmask lcd_16bits_sirfsoc_muxmask[] = { + { + .group = 1, + .mask = BIT(30) | BIT(31), + }, { + .group = 2, + .mask = BIT(1) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | + BIT(12) | BIT(13) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | + BIT(20) | BIT(21) | BIT(22) | BIT(31), + }, +}; + +static const struct sirfsoc_padmux lcd_16bits_padmux = { + .muxmask_counts = ARRAY_SIZE(lcd_16bits_sirfsoc_muxmask), + .muxmask = lcd_16bits_sirfsoc_muxmask, + .funcmask = BIT(4), + .funcval = 0, +}; + +static const unsigned lcd_16bits_pins[] = { 62, 63, 65, 70, 71, 72, 73, 74, 75, 76, 77, 79, 80, 81, 82, 83, + 84, 85, 86, 95 }; + +static const struct sirfsoc_muxmask lcd_18bits_muxmask[] = { + { + .group = 2, + .mask = BIT(1) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | + BIT(12) | BIT(13) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | + BIT(20) | BIT(21) | BIT(22) | BIT(31), + }, { + .group = 1, + .mask = BIT(30) | BIT(31), + }, { + .group = 0, + .mask = BIT(16) | BIT(17), + }, +}; + +static const struct sirfsoc_padmux lcd_18bits_padmux = { + .muxmask_counts = ARRAY_SIZE(lcd_18bits_muxmask), + .muxmask = lcd_18bits_muxmask, + .funcmask = BIT(4) | BIT(15), + .funcval = 0, +}; + +static const unsigned lcd_18bits_pins[] = { 16, 17, 62, 63, 65, 70, 71, 72, 73, 74, 75, 76, 77, 79, 80, 81, 82, 83, + 84, 85, 86, 95 }; + +static const struct sirfsoc_muxmask lcd_24bits_muxmask[] = { + { + .group = 2, + .mask = BIT(1) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | + BIT(12) | BIT(13) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | + BIT(20) | BIT(21) | BIT(22) | BIT(31), + }, { + .group = 1, + .mask = BIT(30) | BIT(31), + }, { + .group = 0, + .mask = BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(21) | BIT(22) | BIT(23), + }, +}; + +static const struct sirfsoc_padmux lcd_24bits_padmux = { + .muxmask_counts = ARRAY_SIZE(lcd_24bits_muxmask), + .muxmask = lcd_24bits_muxmask, + .funcmask = BIT(4) | BIT(15), + .funcval = 0, +}; + +static const unsigned lcd_24bits_pins[] = { 16, 17, 18, 19, 20, 21, 22, 23, 62, 63, 65, 70, 71, 72, 73, 74, 75, 76, 77, 79, + 80, 81, 82, 83, 84, 85, 86, 95}; + +static const struct sirfsoc_muxmask lcdrom_muxmask[] = { + { + .group = 2, + .mask = BIT(1) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(10) | BIT(11) | + BIT(12) | BIT(13) | BIT(15) | BIT(16) | BIT(17) | BIT(18) | BIT(19) | + BIT(20) | BIT(21) | BIT(22) | BIT(31), + }, { + .group = 1, + .mask = BIT(30) | BIT(31), + }, { + .group = 0, + .mask = BIT(8), + }, +}; + +static const struct sirfsoc_padmux lcdrom_padmux = { + .muxmask_counts = ARRAY_SIZE(lcdrom_muxmask), + .muxmask = lcdrom_muxmask, + .funcmask = BIT(4), + .funcval = BIT(4), +}; + +static const unsigned lcdrom_pins[] = { 8, 62, 63, 65, 70, 71, 72, 73, 74, 75, 76, 77, 79, 80, 81, 82, 83, + 84, 85, 86, 95}; + +static const struct sirfsoc_muxmask uart0_muxmask[] = { + { + .group = 0, + .mask = BIT(12), + }, { + .group = 1, + .mask = BIT(23), + }, { + .group = 2, + .mask = BIT(4) | BIT(5), + }, +}; + +static const struct sirfsoc_padmux uart0_padmux = { + .muxmask_counts = ARRAY_SIZE(uart0_muxmask), + .muxmask = uart0_muxmask, + .funcmask = BIT(9), + .funcval = BIT(9), +}; + +static const unsigned uart0_pins[] = { 12, 55, 68, 69 }; + +static const struct sirfsoc_muxmask uart0_nostreamctrl_muxmask[] = { + { + .group = 2, + .mask = BIT(4) | BIT(5), + }, +}; + +static const struct sirfsoc_padmux uart0_nostreamctrl_padmux = { + .muxmask_counts = ARRAY_SIZE(uart0_nostreamctrl_muxmask), + .muxmask = uart0_nostreamctrl_muxmask, +}; + +static const unsigned uart0_nostreamctrl_pins[] = { 68, 69 }; + +static const struct sirfsoc_muxmask uart1_muxmask[] = { + { + .group = 1, + .mask = BIT(15) | BIT(17), + }, +}; + +static const struct sirfsoc_padmux uart1_padmux = { + .muxmask_counts = ARRAY_SIZE(uart1_muxmask), + .muxmask = uart1_muxmask, +}; + +static const unsigned uart1_pins[] = { 47, 49 }; + +static const struct sirfsoc_muxmask uart2_muxmask[] = { + { + .group = 0, + .mask = BIT(10) | BIT(14), + }, { + .group = 1, + .mask = BIT(16) | BIT(18), + }, +}; + +static const struct sirfsoc_padmux uart2_padmux = { + .muxmask_counts = ARRAY_SIZE(uart2_muxmask), + .muxmask = uart2_muxmask, + .funcmask = BIT(10), + .funcval = BIT(10), +}; + +static const unsigned uart2_pins[] = { 10, 14, 48, 50 }; + +static const struct sirfsoc_muxmask uart2_nostreamctrl_muxmask[] = { + { + .group = 1, + .mask = BIT(16) | BIT(18), + }, +}; + +static const struct sirfsoc_padmux uart2_nostreamctrl_padmux = { + .muxmask_counts = ARRAY_SIZE(uart2_nostreamctrl_muxmask), + .muxmask = uart2_nostreamctrl_muxmask, +}; + +static const unsigned uart2_nostreamctrl_pins[] = { 48, 50 }; + +static const struct sirfsoc_muxmask sdmmc3_muxmask[] = { + { + .group = 0, + .mask = BIT(30) | BIT(31), + }, { + .group = 1, + .mask = BIT(0) | BIT(1) | BIT(2) | BIT(3), + }, +}; + +static const struct sirfsoc_padmux sdmmc3_padmux = { + .muxmask_counts = ARRAY_SIZE(sdmmc3_muxmask), + .muxmask = sdmmc3_muxmask, + .funcmask = BIT(7), + .funcval = 0, +}; + +static const unsigned sdmmc3_pins[] = { 30, 31, 32, 33, 34, 35 }; + +static const struct sirfsoc_muxmask spi0_muxmask[] = { + { + .group = 0, + .mask = BIT(30), + }, { + .group = 1, + .mask = BIT(0) | BIT(2) | BIT(3), + }, +}; + +static const struct sirfsoc_padmux spi0_padmux = { + .muxmask_counts = ARRAY_SIZE(spi0_muxmask), + .muxmask = spi0_muxmask, + .funcmask = BIT(7), + .funcval = BIT(7), +}; + +static const unsigned spi0_pins[] = { 30, 32, 34, 35 }; + +static const struct sirfsoc_muxmask cko1_muxmask[] = { + { + .group = 1, + .mask = BIT(10), + }, +}; + +static const struct sirfsoc_padmux cko1_padmux = { + .muxmask_counts = ARRAY_SIZE(cko1_muxmask), + .muxmask = cko1_muxmask, + .funcmask = BIT(3), + .funcval = 0, +}; + +static const unsigned cko1_pins[] = { 42 }; + +static const struct sirfsoc_muxmask i2s_muxmask[] = { + { + .group = 1, + .mask = BIT(10), + }, { + .group = 3, + .mask = BIT(2) | BIT(3) | BIT(4) | BIT(5), + }, +}; + +static const struct sirfsoc_padmux i2s_padmux = { + .muxmask_counts = ARRAY_SIZE(i2s_muxmask), + .muxmask = i2s_muxmask, + .funcmask = BIT(3), + .funcval = BIT(3), +}; + +static const unsigned i2s_pins[] = { 42, 98, 99, 100, 101 }; + +static const struct sirfsoc_muxmask i2s_no_din_muxmask[] = { + { + .group = 1, + .mask = BIT(10), + }, { + .group = 3, + .mask = BIT(2) | BIT(3) | BIT(4), + }, +}; + +static const struct sirfsoc_padmux i2s_no_din_padmux = { + .muxmask_counts = ARRAY_SIZE(i2s_no_din_muxmask), + .muxmask = i2s_no_din_muxmask, + .funcmask = BIT(3), + .funcval = BIT(3), +}; + +static const unsigned i2s_no_din_pins[] = { 42, 98, 99, 100 }; + +static const struct sirfsoc_muxmask i2s_6chn_muxmask[] = { + { + .group = 1, + .mask = BIT(10) | BIT(20) | BIT(23), + }, { + .group = 3, + .mask = BIT(2) | BIT(3) | BIT(4) | BIT(5), + }, +}; + +static const struct sirfsoc_padmux i2s_6chn_padmux = { + .muxmask_counts = ARRAY_SIZE(i2s_6chn_muxmask), + .muxmask = i2s_6chn_muxmask, + .funcmask = BIT(1) | BIT(3) | BIT(9), + .funcval = BIT(1) | BIT(3) | BIT(9), +}; + +static const unsigned i2s_6chn_pins[] = { 42, 52, 55, 98, 99, 100, 101 }; + +static const struct sirfsoc_muxmask ac97_muxmask[] = { + { + .group = 3, + .mask = BIT(2) | BIT(3) | BIT(4) | BIT(5), + }, +}; + +static const struct sirfsoc_padmux ac97_padmux = { + .muxmask_counts = ARRAY_SIZE(ac97_muxmask), + .muxmask = ac97_muxmask, +}; + +static const unsigned ac97_pins[] = { 98, 99, 100, 101 }; + +static const struct sirfsoc_muxmask spi1_muxmask[] = { + { + .group = 1, + .mask = BIT(11) | BIT(12) | BIT(13) | BIT(14), + }, +}; + +static const struct sirfsoc_padmux spi1_padmux = { + .muxmask_counts = ARRAY_SIZE(spi1_muxmask), + .muxmask = spi1_muxmask, + .funcmask = BIT(16), + .funcval = 0, +}; + +static const unsigned spi1_pins[] = { 43, 44, 45, 46 }; + +static const struct sirfsoc_muxmask sdmmc1_muxmask[] = { + { + .group = 2, + .mask = BIT(2) | BIT(3), + }, +}; + +static const struct sirfsoc_padmux sdmmc1_padmux = { + .muxmask_counts = ARRAY_SIZE(sdmmc1_muxmask), + .muxmask = sdmmc1_muxmask, + .funcmask = BIT(5), + .funcval = BIT(5), +}; + +static const unsigned sdmmc1_pins[] = { 66, 67 }; + +static const struct sirfsoc_muxmask gps_muxmask[] = { + { + .group = 0, + .mask = BIT(24) | BIT(25) | BIT(26), + }, +}; + +static const struct sirfsoc_padmux gps_padmux = { + .muxmask_counts = ARRAY_SIZE(gps_muxmask), + .muxmask = gps_muxmask, + .funcmask = BIT(13), + .funcval = 0, +}; + +static const unsigned gps_pins[] = { 24, 25, 26 }; + +static const struct sirfsoc_muxmask sdmmc5_muxmask[] = { + { + .group = 0, + .mask = BIT(24) | BIT(25) | BIT(26), + }, +}; + +static const struct sirfsoc_padmux sdmmc5_padmux = { + .muxmask_counts = ARRAY_SIZE(sdmmc5_muxmask), + .muxmask = sdmmc5_muxmask, + .funcmask = BIT(13), + .funcval = BIT(13), +}; + +static const unsigned sdmmc5_pins[] = { 24, 25, 26 }; + +static const struct sirfsoc_muxmask usp0_muxmask[] = { + { + .group = 1, + .mask = BIT(19) | BIT(20) | BIT(21) | BIT(22), + }, +}; + +static const struct sirfsoc_padmux usp0_padmux = { + .muxmask_counts = ARRAY_SIZE(usp0_muxmask), + .muxmask = usp0_muxmask, + .funcmask = BIT(1) | BIT(2) | BIT(9), + .funcval = 0, +}; + +static const unsigned usp0_pins[] = { 51, 52, 53, 54 }; + +static const struct sirfsoc_muxmask usp1_muxmask[] = { + { + .group = 0, + .mask = BIT(15), + }, { + .group = 1, + .mask = BIT(11) | BIT(12) | BIT(13) | BIT(14), + }, +}; + +static const struct sirfsoc_padmux usp1_padmux = { + .muxmask_counts = ARRAY_SIZE(usp1_muxmask), + .muxmask = usp1_muxmask, + .funcmask = BIT(16), + .funcval = BIT(16), +}; + +static const unsigned usp1_pins[] = { 15, 43, 44, 45, 46 }; + +static const struct sirfsoc_muxmask nand_muxmask[] = { + { + .group = 2, + .mask = BIT(2) | BIT(3) | BIT(28) | BIT(29) | BIT(30), + }, { + .group = 3, + .mask = BIT(0) | BIT(1), + }, +}; + +static const struct sirfsoc_padmux nand_padmux = { + .muxmask_counts = ARRAY_SIZE(nand_muxmask), + .muxmask = nand_muxmask, + .funcmask = BIT(5) | BIT(19), + .funcval = 0, +}; + +static const unsigned nand_pins[] = { 66, 67, 92, 93, 94, 96, 97 }; + +static const struct sirfsoc_muxmask sdmmc0_muxmask[] = { + { + .group = 3, + .mask = BIT(1), + }, +}; + +static const struct sirfsoc_padmux sdmmc0_padmux = { + .muxmask_counts = ARRAY_SIZE(sdmmc0_muxmask), + .muxmask = sdmmc0_muxmask, + .funcmask = BIT(5) | BIT(19), + .funcval = BIT(19), +}; + +static const unsigned sdmmc0_pins[] = { 97 }; + +static const struct sirfsoc_muxmask sdmmc2_muxmask[] = { + { + .group = 0, + .mask = BIT(27) | BIT(28) | BIT(29), + }, +}; + +static const struct sirfsoc_padmux sdmmc2_padmux = { + .muxmask_counts = ARRAY_SIZE(sdmmc2_muxmask), + .muxmask = sdmmc2_muxmask, + .funcmask = BIT(11), + .funcval = 0, +}; + +static const unsigned sdmmc2_pins[] = { 27, 28, 29 }; + +static const struct sirfsoc_muxmask sdmmc2_nowp_muxmask[] = { + { + .group = 0, + .mask = BIT(27) | BIT(28), + }, +}; + +static const struct sirfsoc_padmux sdmmc2_nowp_padmux = { + .muxmask_counts = ARRAY_SIZE(sdmmc2_nowp_muxmask), + .muxmask = sdmmc2_nowp_muxmask, + .funcmask = BIT(11), + .funcval = 0, +}; + +static const unsigned sdmmc2_nowp_pins[] = { 27, 28 }; + +static const struct sirfsoc_muxmask cko0_muxmask[] = { + { + .group = 2, + .mask = BIT(14), + }, +}; + +static const struct sirfsoc_padmux cko0_padmux = { + .muxmask_counts = ARRAY_SIZE(cko0_muxmask), + .muxmask = cko0_muxmask, +}; + +static const unsigned cko0_pins[] = { 78 }; + +static const struct sirfsoc_muxmask vip_muxmask[] = { + { + .group = 1, + .mask = BIT(4) | BIT(5) | BIT(6) | BIT(8) | BIT(9) + | BIT(24) | BIT(25) | BIT(26) | BIT(27) | BIT(28) | + BIT(29), + }, +}; + +static const struct sirfsoc_padmux vip_padmux = { + .muxmask_counts = ARRAY_SIZE(vip_muxmask), + .muxmask = vip_muxmask, + .funcmask = BIT(18), + .funcval = BIT(18), +}; + +static const unsigned vip_pins[] = { 36, 37, 38, 40, 41, 56, 57, 58, 59, 60, 61 }; + +static const struct sirfsoc_muxmask vip_noupli_muxmask[] = { + { + .group = 0, + .mask = BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(20) + | BIT(21) | BIT(22) | BIT(23), + }, { + .group = 2, + .mask = BIT(23) | BIT(24) | BIT(25), + }, +}; + +static const struct sirfsoc_padmux vip_noupli_padmux = { + .muxmask_counts = ARRAY_SIZE(vip_noupli_muxmask), + .muxmask = vip_noupli_muxmask, + .funcmask = BIT(15), + .funcval = BIT(15), +}; + +static const unsigned vip_noupli_pins[] = { 16, 17, 18, 19, 20, 21, 22, 23, 87, 88, 89 }; + +static const struct sirfsoc_muxmask i2c0_muxmask[] = { + { + .group = 2, + .mask = BIT(26) | BIT(27), + }, +}; + +static const struct sirfsoc_padmux i2c0_padmux = { + .muxmask_counts = ARRAY_SIZE(i2c0_muxmask), + .muxmask = i2c0_muxmask, +}; + +static const unsigned i2c0_pins[] = { 90, 91 }; + +static const struct sirfsoc_muxmask i2c1_muxmask[] = { + { + .group = 0, + .mask = BIT(13) | BIT(15), + }, +}; + +static const struct sirfsoc_padmux i2c1_padmux = { + .muxmask_counts = ARRAY_SIZE(i2c1_muxmask), + .muxmask = i2c1_muxmask, + .funcmask = BIT(16), + .funcval = 0, +}; + +static const unsigned i2c1_pins[] = { 13, 15 }; + +static const struct sirfsoc_muxmask pwm0_muxmask[] = { + { + .group = 0, + .mask = BIT(4), + }, +}; + +static const struct sirfsoc_padmux pwm0_padmux = { + .muxmask_counts = ARRAY_SIZE(pwm0_muxmask), + .muxmask = pwm0_muxmask, + .funcmask = BIT(12), + .funcval = 0, +}; + +static const unsigned pwm0_pins[] = { 4 }; + +static const struct sirfsoc_muxmask pwm1_muxmask[] = { + { + .group = 0, + .mask = BIT(5), + }, +}; + +static const struct sirfsoc_padmux pwm1_padmux = { + .muxmask_counts = ARRAY_SIZE(pwm1_muxmask), + .muxmask = pwm1_muxmask, +}; + +static const unsigned pwm1_pins[] = { 5 }; + +static const struct sirfsoc_muxmask pwm2_muxmask[] = { + { + .group = 0, + .mask = BIT(6), + }, +}; + +static const struct sirfsoc_padmux pwm2_padmux = { + .muxmask_counts = ARRAY_SIZE(pwm2_muxmask), + .muxmask = pwm2_muxmask, +}; + +static const unsigned pwm2_pins[] = { 6 }; + +static const struct sirfsoc_muxmask pwm3_muxmask[] = { + { + .group = 0, + .mask = BIT(7), + }, +}; + +static const struct sirfsoc_padmux pwm3_padmux = { + .muxmask_counts = ARRAY_SIZE(pwm3_muxmask), + .muxmask = pwm3_muxmask, +}; + +static const unsigned pwm3_pins[] = { 7 }; + +static const struct sirfsoc_muxmask pwm4_muxmask[] = { + { + .group = 2, + .mask = BIT(14), + }, +}; + +static const struct sirfsoc_padmux pwm4_padmux = { + .muxmask_counts = ARRAY_SIZE(pwm4_muxmask), + .muxmask = pwm4_muxmask, +}; + +static const unsigned pwm4_pins[] = { 78 }; + +static const struct sirfsoc_muxmask warm_rst_muxmask[] = { + { + .group = 0, + .mask = BIT(8), + }, +}; + +static const struct sirfsoc_padmux warm_rst_padmux = { + .muxmask_counts = ARRAY_SIZE(warm_rst_muxmask), + .muxmask = warm_rst_muxmask, + .funcmask = BIT(4), + .funcval = 0, +}; + +static const unsigned warm_rst_pins[] = { 8 }; + +static const struct sirfsoc_muxmask usb0_upli_drvbus_muxmask[] = { + { + .group = 1, + .mask = BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) + | BIT(9) | BIT(24) | BIT(25) | BIT(26) | + BIT(27) | BIT(28) | BIT(29), + }, +}; +static const struct sirfsoc_padmux usb0_upli_drvbus_padmux = { + .muxmask_counts = ARRAY_SIZE(usb0_upli_drvbus_muxmask), + .muxmask = usb0_upli_drvbus_muxmask, + .funcmask = BIT(18), + .funcval = 0, +}; + +static const unsigned usb0_upli_drvbus_pins[] = { 36, 37, 38, 39, 40, 41, 56, 57, 58, 59, 60, 61 }; + +static const struct sirfsoc_muxmask usb1_utmi_drvbus_muxmask[] = { + { + .group = 0, + .mask = BIT(28), + }, +}; + +static const struct sirfsoc_padmux usb1_utmi_drvbus_padmux = { + .muxmask_counts = ARRAY_SIZE(usb1_utmi_drvbus_muxmask), + .muxmask = usb1_utmi_drvbus_muxmask, + .funcmask = BIT(11), + .funcval = BIT(11), /* refer to PAD_UTMI_DRVVBUS1_ENABLE */ +}; + +static const unsigned usb1_utmi_drvbus_pins[] = { 28 }; + +static const struct sirfsoc_muxmask pulse_count_muxmask[] = { + { + .group = 0, + .mask = BIT(9) | BIT(10) | BIT(11), + }, +}; + +static const struct sirfsoc_padmux pulse_count_padmux = { + .muxmask_counts = ARRAY_SIZE(pulse_count_muxmask), + .muxmask = pulse_count_muxmask, +}; + +static const unsigned pulse_count_pins[] = { 9, 10, 11 }; + +static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { + SIRFSOC_PIN_GROUP("lcd_16bitsgrp", lcd_16bits_pins), + SIRFSOC_PIN_GROUP("lcd_18bitsgrp", lcd_18bits_pins), + SIRFSOC_PIN_GROUP("lcd_24bitsgrp", lcd_24bits_pins), + SIRFSOC_PIN_GROUP("lcdrom_grp", lcdrom_pins), + SIRFSOC_PIN_GROUP("uart0grp", uart0_pins), + SIRFSOC_PIN_GROUP("uart1grp", uart1_pins), + SIRFSOC_PIN_GROUP("uart2grp", uart2_pins), + SIRFSOC_PIN_GROUP("uart2_nostreamctrlgrp", uart2_nostreamctrl_pins), + SIRFSOC_PIN_GROUP("usp0grp", usp0_pins), + SIRFSOC_PIN_GROUP("usp1grp", usp1_pins), + SIRFSOC_PIN_GROUP("i2c0grp", i2c0_pins), + SIRFSOC_PIN_GROUP("i2c1grp", i2c1_pins), + SIRFSOC_PIN_GROUP("pwm0grp", pwm0_pins), + SIRFSOC_PIN_GROUP("pwm1grp", pwm1_pins), + SIRFSOC_PIN_GROUP("pwm2grp", pwm2_pins), + SIRFSOC_PIN_GROUP("pwm3grp", pwm3_pins), + SIRFSOC_PIN_GROUP("pwm4grp", pwm4_pins), + SIRFSOC_PIN_GROUP("vipgrp", vip_pins), + SIRFSOC_PIN_GROUP("vip_noupligrp", vip_noupli_pins), + SIRFSOC_PIN_GROUP("warm_rstgrp", warm_rst_pins), + SIRFSOC_PIN_GROUP("cko0grp", cko0_pins), + SIRFSOC_PIN_GROUP("cko1grp", cko1_pins), + SIRFSOC_PIN_GROUP("sdmmc0grp", sdmmc0_pins), + SIRFSOC_PIN_GROUP("sdmmc1grp", sdmmc1_pins), + SIRFSOC_PIN_GROUP("sdmmc2grp", sdmmc2_pins), + SIRFSOC_PIN_GROUP("sdmmc2_nowpgrp", sdmmc2_nowp_pins), + SIRFSOC_PIN_GROUP("sdmmc3grp", sdmmc3_pins), + SIRFSOC_PIN_GROUP("sdmmc5grp", sdmmc5_pins), + SIRFSOC_PIN_GROUP("usb0_upli_drvbusgrp", usb0_upli_drvbus_pins), + SIRFSOC_PIN_GROUP("usb1_utmi_drvbusgrp", usb1_utmi_drvbus_pins), + SIRFSOC_PIN_GROUP("pulse_countgrp", pulse_count_pins), + SIRFSOC_PIN_GROUP("i2sgrp", i2s_pins), + SIRFSOC_PIN_GROUP("i2s_no_dingrp", i2s_no_din_pins), + SIRFSOC_PIN_GROUP("i2s_6chngrp", i2s_6chn_pins), + SIRFSOC_PIN_GROUP("ac97grp", ac97_pins), + SIRFSOC_PIN_GROUP("nandgrp", nand_pins), + SIRFSOC_PIN_GROUP("spi0grp", spi0_pins), + SIRFSOC_PIN_GROUP("spi1grp", spi1_pins), + SIRFSOC_PIN_GROUP("gpsgrp", gps_pins), +}; + +static const char * const lcd_16bitsgrp[] = { "lcd_16bitsgrp" }; +static const char * const lcd_18bitsgrp[] = { "lcd_18bitsgrp" }; +static const char * const lcd_24bitsgrp[] = { "lcd_24bitsgrp" }; +static const char * const lcdromgrp[] = { "lcdromgrp" }; +static const char * const uart0grp[] = { "uart0grp" }; +static const char * const uart1grp[] = { "uart1grp" }; +static const char * const uart2grp[] = { "uart2grp" }; +static const char * const uart2_nostreamctrlgrp[] = { "uart2_nostreamctrlgrp" }; +static const char * const usp0grp[] = { "usp0grp" }; +static const char * const usp1grp[] = { "usp1grp" }; +static const char * const i2c0grp[] = { "i2c0grp" }; +static const char * const i2c1grp[] = { "i2c1grp" }; +static const char * const pwm0grp[] = { "pwm0grp" }; +static const char * const pwm1grp[] = { "pwm1grp" }; +static const char * const pwm2grp[] = { "pwm2grp" }; +static const char * const pwm3grp[] = { "pwm3grp" }; +static const char * const pwm4grp[] = { "pwm4grp" }; +static const char * const vipgrp[] = { "vipgrp" }; +static const char * const vip_noupligrp[] = { "vip_noupligrp" }; +static const char * const warm_rstgrp[] = { "warm_rstgrp" }; +static const char * const cko0grp[] = { "cko0grp" }; +static const char * const cko1grp[] = { "cko1grp" }; +static const char * const sdmmc0grp[] = { "sdmmc0grp" }; +static const char * const sdmmc1grp[] = { "sdmmc1grp" }; +static const char * const sdmmc2grp[] = { "sdmmc2grp" }; +static const char * const sdmmc3grp[] = { "sdmmc3grp" }; +static const char * const sdmmc5grp[] = { "sdmmc5grp" }; +static const char * const sdmmc2_nowpgrp[] = { "sdmmc2_nowpgrp" }; +static const char * const usb0_upli_drvbusgrp[] = { "usb0_upli_drvbusgrp" }; +static const char * const usb1_utmi_drvbusgrp[] = { "usb1_utmi_drvbusgrp" }; +static const char * const pulse_countgrp[] = { "pulse_countgrp" }; +static const char * const i2sgrp[] = { "i2sgrp" }; +static const char * const i2s_no_dingrp[] = { "i2s_no_dingrp" }; +static const char * const i2s_6chngrp[] = { "i2s_6chngrp" }; +static const char * const ac97grp[] = { "ac97grp" }; +static const char * const nandgrp[] = { "nandgrp" }; +static const char * const spi0grp[] = { "spi0grp" }; +static const char * const spi1grp[] = { "spi1grp" }; +static const char * const gpsgrp[] = { "gpsgrp" }; + +static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = { + SIRFSOC_PMX_FUNCTION("lcd_16bits", lcd_16bitsgrp, lcd_16bits_padmux), + SIRFSOC_PMX_FUNCTION("lcd_18bits", lcd_18bitsgrp, lcd_18bits_padmux), + SIRFSOC_PMX_FUNCTION("lcd_24bits", lcd_24bitsgrp, lcd_24bits_padmux), + SIRFSOC_PMX_FUNCTION("lcdrom", lcdromgrp, lcdrom_padmux), + SIRFSOC_PMX_FUNCTION("uart0", uart0grp, uart0_padmux), + SIRFSOC_PMX_FUNCTION("uart1", uart1grp, uart1_padmux), + SIRFSOC_PMX_FUNCTION("uart2", uart2grp, uart2_padmux), + SIRFSOC_PMX_FUNCTION("uart2_nostreamctrl", uart2_nostreamctrlgrp, uart2_nostreamctrl_padmux), + SIRFSOC_PMX_FUNCTION("usp0", usp0grp, usp0_padmux), + SIRFSOC_PMX_FUNCTION("usp1", usp1grp, usp1_padmux), + SIRFSOC_PMX_FUNCTION("i2c0", i2c0grp, i2c0_padmux), + SIRFSOC_PMX_FUNCTION("i2c1", i2c1grp, i2c1_padmux), + SIRFSOC_PMX_FUNCTION("pwm0", pwm0grp, pwm0_padmux), + SIRFSOC_PMX_FUNCTION("pwm1", pwm1grp, pwm1_padmux), + SIRFSOC_PMX_FUNCTION("pwm2", pwm2grp, pwm2_padmux), + SIRFSOC_PMX_FUNCTION("pwm3", pwm3grp, pwm3_padmux), + SIRFSOC_PMX_FUNCTION("pwm4", pwm4grp, pwm4_padmux), + SIRFSOC_PMX_FUNCTION("vip", vipgrp, vip_padmux), + SIRFSOC_PMX_FUNCTION("vip_noupli", vip_noupligrp, vip_noupli_padmux), + SIRFSOC_PMX_FUNCTION("warm_rst", warm_rstgrp, warm_rst_padmux), + SIRFSOC_PMX_FUNCTION("cko0", cko0grp, cko0_padmux), + SIRFSOC_PMX_FUNCTION("cko1", cko1grp, cko1_padmux), + SIRFSOC_PMX_FUNCTION("sdmmc0", sdmmc0grp, sdmmc0_padmux), + SIRFSOC_PMX_FUNCTION("sdmmc1", sdmmc1grp, sdmmc1_padmux), + SIRFSOC_PMX_FUNCTION("sdmmc2", sdmmc2grp, sdmmc2_padmux), + SIRFSOC_PMX_FUNCTION("sdmmc3", sdmmc3grp, sdmmc3_padmux), + SIRFSOC_PMX_FUNCTION("sdmmc5", sdmmc5grp, sdmmc5_padmux), + SIRFSOC_PMX_FUNCTION("sdmmc2_nowp", sdmmc2_nowpgrp, sdmmc2_nowp_padmux), + SIRFSOC_PMX_FUNCTION("usb0_upli_drvbus", usb0_upli_drvbusgrp, usb0_upli_drvbus_padmux), + SIRFSOC_PMX_FUNCTION("usb1_utmi_drvbus", usb1_utmi_drvbusgrp, usb1_utmi_drvbus_padmux), + SIRFSOC_PMX_FUNCTION("pulse_count", pulse_countgrp, pulse_count_padmux), + SIRFSOC_PMX_FUNCTION("i2s", i2sgrp, i2s_padmux), + SIRFSOC_PMX_FUNCTION("i2s_no_din", i2s_no_dingrp, i2s_no_din_padmux), + SIRFSOC_PMX_FUNCTION("i2s_6chn", i2s_6chngrp, i2s_6chn_padmux), + SIRFSOC_PMX_FUNCTION("ac97", ac97grp, ac97_padmux), + SIRFSOC_PMX_FUNCTION("nand", nandgrp, nand_padmux), + SIRFSOC_PMX_FUNCTION("spi0", spi0grp, spi0_padmux), + SIRFSOC_PMX_FUNCTION("spi1", spi1grp, spi1_padmux), + SIRFSOC_PMX_FUNCTION("gps", gpsgrp, gps_padmux), +}; + +struct sirfsoc_pinctrl_data atlas6_pinctrl_data = { + (struct pinctrl_pin_desc *)sirfsoc_pads, + ARRAY_SIZE(sirfsoc_pads), + (struct sirfsoc_pin_group *)sirfsoc_pin_groups, + ARRAY_SIZE(sirfsoc_pin_groups), + (struct sirfsoc_pmx_func *)sirfsoc_pmx_functions, + ARRAY_SIZE(sirfsoc_pmx_functions), +}; + diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-prima2.c index bc9d1be27fb0..1f0ad1ef5a3a 100644 --- a/drivers/pinctrl/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-prima2.c @@ -1,70 +1,15 @@ /* - * pinmux driver for CSR SiRFprimaII + * pinctrl pads, groups, functions for CSR SiRFprimaII * * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. * * Licensed under GPLv2 or later. */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/irq.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/err.h> -#include <linux/irqdomain.h> -#include <linux/irqchip/chained_irq.h> #include <linux/pinctrl/pinctrl.h> -#include <linux/pinctrl/pinmux.h> -#include <linux/pinctrl/consumer.h> -#include <linux/pinctrl/machine.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_device.h> -#include <linux/of_platform.h> #include <linux/bitops.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> - -#define DRIVER_NAME "pinmux-sirf" - -#define SIRFSOC_NUM_PADS 622 -#define SIRFSOC_RSC_PIN_MUX 0x4 - -#define SIRFSOC_GPIO_PAD_EN(g) ((g)*0x100 + 0x84) -#define SIRFSOC_GPIO_PAD_EN_CLR(g) ((g)*0x100 + 0x90) -#define SIRFSOC_GPIO_CTRL(g, i) ((g)*0x100 + (i)*4) -#define SIRFSOC_GPIO_DSP_EN0 (0x80) -#define SIRFSOC_GPIO_INT_STATUS(g) ((g)*0x100 + 0x8C) - -#define SIRFSOC_GPIO_CTL_INTR_LOW_MASK 0x1 -#define SIRFSOC_GPIO_CTL_INTR_HIGH_MASK 0x2 -#define SIRFSOC_GPIO_CTL_INTR_TYPE_MASK 0x4 -#define SIRFSOC_GPIO_CTL_INTR_EN_MASK 0x8 -#define SIRFSOC_GPIO_CTL_INTR_STS_MASK 0x10 -#define SIRFSOC_GPIO_CTL_OUT_EN_MASK 0x20 -#define SIRFSOC_GPIO_CTL_DATAOUT_MASK 0x40 -#define SIRFSOC_GPIO_CTL_DATAIN_MASK 0x80 -#define SIRFSOC_GPIO_CTL_PULL_MASK 0x100 -#define SIRFSOC_GPIO_CTL_PULL_HIGH 0x200 -#define SIRFSOC_GPIO_CTL_DSP_INT 0x400 - -#define SIRFSOC_GPIO_NO_OF_BANKS 5 -#define SIRFSOC_GPIO_BANK_SIZE 32 -#define SIRFSOC_GPIO_NUM(bank, index) (((bank)*(32)) + (index)) - -struct sirfsoc_gpio_bank { - struct of_mm_gpio_chip chip; - struct irq_domain *domain; - int id; - int parent_irq; - spinlock_t lock; - bool is_marco; /* for marco, some registers are different with prima2 */ -}; - -static struct sirfsoc_gpio_bank sgpio_bank[SIRFSOC_GPIO_NO_OF_BANKS]; -static DEFINE_SPINLOCK(sgpio_lock); + +#include "pinctrl-sirf.h" /* * pad list for the pinmux subsystem @@ -183,46 +128,6 @@ static const struct pinctrl_pin_desc sirfsoc_pads[] = { PINCTRL_PIN(114, "x_ldd[15]"), }; -/** - * @dev: a pointer back to containing device - * @virtbase: the offset to the controller in virtual memory - */ -struct sirfsoc_pmx { - struct device *dev; - struct pinctrl_dev *pmx; - void __iomem *gpio_virtbase; - void __iomem *rsc_virtbase; - bool is_marco; -}; - -/* SIRFSOC_GPIO_PAD_EN set */ -struct sirfsoc_muxmask { - unsigned long group; - unsigned long mask; -}; - -struct sirfsoc_padmux { - unsigned long muxmask_counts; - const struct sirfsoc_muxmask *muxmask; - /* RSC_PIN_MUX set */ - unsigned long funcmask; - unsigned long funcval; -}; - - /** - * struct sirfsoc_pin_group - describes a SiRFprimaII pin group - * @name: the name of this specific pin group - * @pins: an array of discrete physical pins used in this group, taken - * from the driver-local pin enumeration space - * @num_pins: the number of pins in this group array, i.e. the number of - * elements in .pins so we can iterate over that array - */ -struct sirfsoc_pin_group { - const char *name; - const unsigned int *pins; - const unsigned num_pins; -}; - static const struct sirfsoc_muxmask lcd_16bits_sirfsoc_muxmask[] = { { .group = 3, @@ -351,7 +256,7 @@ static const struct sirfsoc_padmux uart0_nostreamctrl_padmux = { .muxmask = uart0_nostreamctrl_muxmask, }; -static const unsigned uart0_nostreamctrl_pins[] = { 68, 39 }; +static const unsigned uart0_nostreamctrl_pins[] = { 68, 69 }; static const struct sirfsoc_muxmask uart1_muxmask[] = { { @@ -853,13 +758,6 @@ static const struct sirfsoc_padmux pulse_count_padmux = { static const unsigned pulse_count_pins[] = { 9, 10, 11 }; -#define SIRFSOC_PIN_GROUP(n, p) \ - { \ - .name = n, \ - .pins = p, \ - .num_pins = ARRAY_SIZE(p), \ - } - static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("lcd_16bitsgrp", lcd_16bits_pins), SIRFSOC_PIN_GROUP("lcd_18bitsgrp", lcd_18bits_pins), @@ -881,8 +779,8 @@ static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("vipgrp", vip_pins), SIRFSOC_PIN_GROUP("vipromgrp", viprom_pins), SIRFSOC_PIN_GROUP("warm_rstgrp", warm_rst_pins), - SIRFSOC_PIN_GROUP("cko0_rstgrp", cko0_pins), - SIRFSOC_PIN_GROUP("cko1_rstgrp", cko1_pins), + SIRFSOC_PIN_GROUP("cko0grp", cko0_pins), + SIRFSOC_PIN_GROUP("cko1grp", cko1_pins), SIRFSOC_PIN_GROUP("sdmmc0grp", sdmmc0_pins), SIRFSOC_PIN_GROUP("sdmmc1grp", sdmmc1_pins), SIRFSOC_PIN_GROUP("sdmmc2grp", sdmmc2_pins), @@ -900,101 +798,6 @@ static const struct sirfsoc_pin_group sirfsoc_pin_groups[] = { SIRFSOC_PIN_GROUP("gpsgrp", gps_pins), }; -static int sirfsoc_get_groups_count(struct pinctrl_dev *pctldev) -{ - return ARRAY_SIZE(sirfsoc_pin_groups); -} - -static const char *sirfsoc_get_group_name(struct pinctrl_dev *pctldev, - unsigned selector) -{ - return sirfsoc_pin_groups[selector].name; -} - -static int sirfsoc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, - const unsigned **pins, - unsigned *num_pins) -{ - *pins = sirfsoc_pin_groups[selector].pins; - *num_pins = sirfsoc_pin_groups[selector].num_pins; - return 0; -} - -static void sirfsoc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, - unsigned offset) -{ - seq_printf(s, " " DRIVER_NAME); -} - -static int sirfsoc_dt_node_to_map(struct pinctrl_dev *pctldev, - struct device_node *np_config, - struct pinctrl_map **map, unsigned *num_maps) -{ - struct sirfsoc_pmx *spmx = pinctrl_dev_get_drvdata(pctldev); - struct device_node *np; - struct property *prop; - const char *function, *group; - int ret, index = 0, count = 0; - - /* calculate number of maps required */ - for_each_child_of_node(np_config, np) { - ret = of_property_read_string(np, "sirf,function", &function); - if (ret < 0) - return ret; - - ret = of_property_count_strings(np, "sirf,pins"); - if (ret < 0) - return ret; - - count += ret; - } - - if (!count) { - dev_err(spmx->dev, "No child nodes passed via DT\n"); - return -ENODEV; - } - - *map = kzalloc(sizeof(**map) * count, GFP_KERNEL); - if (!*map) - return -ENOMEM; - - for_each_child_of_node(np_config, np) { - of_property_read_string(np, "sirf,function", &function); - of_property_for_each_string(np, "sirf,pins", prop, group) { - (*map)[index].type = PIN_MAP_TYPE_MUX_GROUP; - (*map)[index].data.mux.group = group; - (*map)[index].data.mux.function = function; - index++; - } - } - - *num_maps = count; - - return 0; -} - -static void sirfsoc_dt_free_map(struct pinctrl_dev *pctldev, - struct pinctrl_map *map, unsigned num_maps) -{ - kfree(map); -} - -static const struct pinctrl_ops sirfsoc_pctrl_ops = { - .get_groups_count = sirfsoc_get_groups_count, - .get_group_name = sirfsoc_get_group_name, - .get_group_pins = sirfsoc_get_group_pins, - .pin_dbg_show = sirfsoc_pin_dbg_show, - .dt_node_to_map = sirfsoc_dt_node_to_map, - .dt_free_map = sirfsoc_dt_free_map, -}; - -struct sirfsoc_pmx_func { - const char *name; - const char * const *groups; - const unsigned num_groups; - const struct sirfsoc_padmux *padmux; -}; - static const char * const lcd_16bitsgrp[] = { "lcd_16bitsgrp" }; static const char * const lcd_18bitsgrp[] = { "lcd_18bitsgrp" }; static const char * const lcd_24bitsgrp[] = { "lcd_24bitsgrp" }; @@ -1033,14 +836,6 @@ static const char * const spi0grp[] = { "spi0grp" }; static const char * const spi1grp[] = { "spi1grp" }; static const char * const gpsgrp[] = { "gpsgrp" }; -#define SIRFSOC_PMX_FUNCTION(n, g, m) \ - { \ - .name = n, \ - .groups = g, \ - .num_groups = ARRAY_SIZE(g), \ - .padmux = &m, \ - } - static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = { SIRFSOC_PMX_FUNCTION("lcd_16bits", lcd_16bitsgrp, lcd_16bits_padmux), SIRFSOC_PMX_FUNCTION("lcd_18bits", lcd_18bitsgrp, lcd_18bits_padmux), @@ -1081,730 +876,12 @@ static const struct sirfsoc_pmx_func sirfsoc_pmx_functions[] = { SIRFSOC_PMX_FUNCTION("gps", gpsgrp, gps_padmux), }; -static void sirfsoc_pinmux_endisable(struct sirfsoc_pmx *spmx, unsigned selector, - bool enable) -{ - int i; - const struct sirfsoc_padmux *mux = sirfsoc_pmx_functions[selector].padmux; - const struct sirfsoc_muxmask *mask = mux->muxmask; - - for (i = 0; i < mux->muxmask_counts; i++) { - u32 muxval; - if (!spmx->is_marco) { - muxval = readl(spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(mask[i].group)); - if (enable) - muxval = muxval & ~mask[i].mask; - else - muxval = muxval | mask[i].mask; - writel(muxval, spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(mask[i].group)); - } else { - if (enable) - writel(mask[i].mask, spmx->gpio_virtbase + - SIRFSOC_GPIO_PAD_EN_CLR(mask[i].group)); - else - writel(mask[i].mask, spmx->gpio_virtbase + - SIRFSOC_GPIO_PAD_EN(mask[i].group)); - } - } - - if (mux->funcmask && enable) { - u32 func_en_val; - func_en_val = - readl(spmx->rsc_virtbase + SIRFSOC_RSC_PIN_MUX); - func_en_val = - (func_en_val & ~mux->funcmask) | (mux-> - funcval); - writel(func_en_val, spmx->rsc_virtbase + SIRFSOC_RSC_PIN_MUX); - } -} - -static int sirfsoc_pinmux_enable(struct pinctrl_dev *pmxdev, unsigned selector, - unsigned group) -{ - struct sirfsoc_pmx *spmx; - - spmx = pinctrl_dev_get_drvdata(pmxdev); - sirfsoc_pinmux_endisable(spmx, selector, true); - - return 0; -} - -static void sirfsoc_pinmux_disable(struct pinctrl_dev *pmxdev, unsigned selector, - unsigned group) -{ - struct sirfsoc_pmx *spmx; - - spmx = pinctrl_dev_get_drvdata(pmxdev); - sirfsoc_pinmux_endisable(spmx, selector, false); -} - -static int sirfsoc_pinmux_get_funcs_count(struct pinctrl_dev *pmxdev) -{ - return ARRAY_SIZE(sirfsoc_pmx_functions); -} - -static const char *sirfsoc_pinmux_get_func_name(struct pinctrl_dev *pctldev, - unsigned selector) -{ - return sirfsoc_pmx_functions[selector].name; -} - -static int sirfsoc_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned selector, - const char * const **groups, - unsigned * const num_groups) -{ - *groups = sirfsoc_pmx_functions[selector].groups; - *num_groups = sirfsoc_pmx_functions[selector].num_groups; - return 0; -} - -static int sirfsoc_pinmux_request_gpio(struct pinctrl_dev *pmxdev, - struct pinctrl_gpio_range *range, unsigned offset) -{ - struct sirfsoc_pmx *spmx; - - int group = range->id; - - u32 muxval; - - spmx = pinctrl_dev_get_drvdata(pmxdev); - - if (!spmx->is_marco) { - muxval = readl(spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(group)); - muxval = muxval | (1 << (offset - range->pin_base)); - writel(muxval, spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(group)); - } else { - writel(1 << (offset - range->pin_base), spmx->gpio_virtbase + - SIRFSOC_GPIO_PAD_EN(group)); - } - - return 0; -} - -static const struct pinmux_ops sirfsoc_pinmux_ops = { - .enable = sirfsoc_pinmux_enable, - .disable = sirfsoc_pinmux_disable, - .get_functions_count = sirfsoc_pinmux_get_funcs_count, - .get_function_name = sirfsoc_pinmux_get_func_name, - .get_function_groups = sirfsoc_pinmux_get_groups, - .gpio_request_enable = sirfsoc_pinmux_request_gpio, -}; - -static struct pinctrl_desc sirfsoc_pinmux_desc = { - .name = DRIVER_NAME, - .pins = sirfsoc_pads, - .npins = ARRAY_SIZE(sirfsoc_pads), - .pctlops = &sirfsoc_pctrl_ops, - .pmxops = &sirfsoc_pinmux_ops, - .owner = THIS_MODULE, -}; - -/* - * Todo: bind irq_chip to every pinctrl_gpio_range - */ -static struct pinctrl_gpio_range sirfsoc_gpio_ranges[] = { - { - .name = "sirfsoc-gpio*", - .id = 0, - .base = 0, - .pin_base = 0, - .npins = 32, - }, { - .name = "sirfsoc-gpio*", - .id = 1, - .base = 32, - .pin_base = 32, - .npins = 32, - }, { - .name = "sirfsoc-gpio*", - .id = 2, - .base = 64, - .pin_base = 64, - .npins = 32, - }, { - .name = "sirfsoc-gpio*", - .id = 3, - .base = 96, - .pin_base = 96, - .npins = 19, - }, -}; - -static void __iomem *sirfsoc_rsc_of_iomap(void) -{ - const struct of_device_id rsc_ids[] = { - { .compatible = "sirf,prima2-rsc" }, - { .compatible = "sirf,marco-rsc" }, - {} - }; - struct device_node *np; - - np = of_find_matching_node(NULL, rsc_ids); - if (!np) - panic("unable to find compatible rsc node in dtb\n"); - - return of_iomap(np, 0); -} - -static int sirfsoc_gpio_of_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, - u32 *flags) -{ - if (gpiospec->args[0] > SIRFSOC_GPIO_NO_OF_BANKS * SIRFSOC_GPIO_BANK_SIZE) - return -EINVAL; - - if (gc != &sgpio_bank[gpiospec->args[0] / SIRFSOC_GPIO_BANK_SIZE].chip.gc) - return -EINVAL; - - if (flags) - *flags = gpiospec->args[1]; - - return gpiospec->args[0] % SIRFSOC_GPIO_BANK_SIZE; -} - -static int sirfsoc_pinmux_probe(struct platform_device *pdev) -{ - int ret; - struct sirfsoc_pmx *spmx; - struct device_node *np = pdev->dev.of_node; - int i; - - /* Create state holders etc for this driver */ - spmx = devm_kzalloc(&pdev->dev, sizeof(*spmx), GFP_KERNEL); - if (!spmx) - return -ENOMEM; - - spmx->dev = &pdev->dev; - - platform_set_drvdata(pdev, spmx); - - spmx->gpio_virtbase = of_iomap(np, 0); - if (!spmx->gpio_virtbase) { - ret = -ENOMEM; - dev_err(&pdev->dev, "can't map gpio registers\n"); - goto out_no_gpio_remap; - } - - spmx->rsc_virtbase = sirfsoc_rsc_of_iomap(); - if (!spmx->rsc_virtbase) { - ret = -ENOMEM; - dev_err(&pdev->dev, "can't map rsc registers\n"); - goto out_no_rsc_remap; - } - - if (of_device_is_compatible(np, "sirf,marco-pinctrl")) - spmx->is_marco = 1; - - /* Now register the pin controller and all pins it handles */ - spmx->pmx = pinctrl_register(&sirfsoc_pinmux_desc, &pdev->dev, spmx); - if (!spmx->pmx) { - dev_err(&pdev->dev, "could not register SIRFSOC pinmux driver\n"); - ret = -EINVAL; - goto out_no_pmx; - } - - for (i = 0; i < ARRAY_SIZE(sirfsoc_gpio_ranges); i++) { - sirfsoc_gpio_ranges[i].gc = &sgpio_bank[i].chip.gc; - pinctrl_add_gpio_range(spmx->pmx, &sirfsoc_gpio_ranges[i]); - } - - dev_info(&pdev->dev, "initialized SIRFSOC pinmux driver\n"); - - return 0; - -out_no_pmx: - iounmap(spmx->rsc_virtbase); -out_no_rsc_remap: - iounmap(spmx->gpio_virtbase); -out_no_gpio_remap: - platform_set_drvdata(pdev, NULL); - return ret; -} - -static const struct of_device_id pinmux_ids[] = { - { .compatible = "sirf,prima2-pinctrl" }, - { .compatible = "sirf,marco-pinctrl" }, - {} -}; - -static struct platform_driver sirfsoc_pinmux_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = pinmux_ids, - }, - .probe = sirfsoc_pinmux_probe, +struct sirfsoc_pinctrl_data prima2_pinctrl_data = { + (struct pinctrl_pin_desc *)sirfsoc_pads, + ARRAY_SIZE(sirfsoc_pads), + (struct sirfsoc_pin_group *)sirfsoc_pin_groups, + ARRAY_SIZE(sirfsoc_pin_groups), + (struct sirfsoc_pmx_func *)sirfsoc_pmx_functions, + ARRAY_SIZE(sirfsoc_pmx_functions), }; -static int __init sirfsoc_pinmux_init(void) -{ - return platform_driver_register(&sirfsoc_pinmux_driver); -} -arch_initcall(sirfsoc_pinmux_init); - -static inline int sirfsoc_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct sirfsoc_gpio_bank *bank = container_of(to_of_mm_gpio_chip(chip), - struct sirfsoc_gpio_bank, chip); - - return irq_create_mapping(bank->domain, offset); -} - -static inline int sirfsoc_gpio_to_offset(unsigned int gpio) -{ - return gpio % SIRFSOC_GPIO_BANK_SIZE; -} - -static inline struct sirfsoc_gpio_bank *sirfsoc_gpio_to_bank(unsigned int gpio) -{ - return &sgpio_bank[gpio / SIRFSOC_GPIO_BANK_SIZE]; -} - -static inline struct sirfsoc_gpio_bank *sirfsoc_irqchip_to_bank(struct gpio_chip *chip) -{ - return container_of(to_of_mm_gpio_chip(chip), struct sirfsoc_gpio_bank, chip); -} - -static void sirfsoc_gpio_irq_ack(struct irq_data *d) -{ - struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); - int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; - u32 val, offset; - unsigned long flags; - - offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - - spin_lock_irqsave(&sgpio_lock, flags); - - val = readl(bank->chip.regs + offset); - - writel(val, bank->chip.regs + offset); - - spin_unlock_irqrestore(&sgpio_lock, flags); -} - -static void __sirfsoc_gpio_irq_mask(struct sirfsoc_gpio_bank *bank, int idx) -{ - u32 val, offset; - unsigned long flags; - - offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - - spin_lock_irqsave(&sgpio_lock, flags); - - val = readl(bank->chip.regs + offset); - val &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; - val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; - writel(val, bank->chip.regs + offset); - - spin_unlock_irqrestore(&sgpio_lock, flags); -} - -static void sirfsoc_gpio_irq_mask(struct irq_data *d) -{ - struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); - - __sirfsoc_gpio_irq_mask(bank, d->hwirq % SIRFSOC_GPIO_BANK_SIZE); -} - -static void sirfsoc_gpio_irq_unmask(struct irq_data *d) -{ - struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); - int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; - u32 val, offset; - unsigned long flags; - - offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - - spin_lock_irqsave(&sgpio_lock, flags); - - val = readl(bank->chip.regs + offset); - val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; - val |= SIRFSOC_GPIO_CTL_INTR_EN_MASK; - writel(val, bank->chip.regs + offset); - - spin_unlock_irqrestore(&sgpio_lock, flags); -} - -static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type) -{ - struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); - int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; - u32 val, offset; - unsigned long flags; - - offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - - spin_lock_irqsave(&sgpio_lock, flags); - - val = readl(bank->chip.regs + offset); - val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; - - switch (type) { - case IRQ_TYPE_NONE: - break; - case IRQ_TYPE_EDGE_RISING: - val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; - val &= ~SIRFSOC_GPIO_CTL_INTR_LOW_MASK; - break; - case IRQ_TYPE_EDGE_FALLING: - val &= ~SIRFSOC_GPIO_CTL_INTR_HIGH_MASK; - val |= SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; - break; - case IRQ_TYPE_EDGE_BOTH: - val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_LOW_MASK | - SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; - break; - case IRQ_TYPE_LEVEL_LOW: - val &= ~(SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); - val |= SIRFSOC_GPIO_CTL_INTR_LOW_MASK; - break; - case IRQ_TYPE_LEVEL_HIGH: - val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK; - val &= ~(SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); - break; - } - - writel(val, bank->chip.regs + offset); - - spin_unlock_irqrestore(&sgpio_lock, flags); - - return 0; -} - -static struct irq_chip sirfsoc_irq_chip = { - .name = "sirf-gpio-irq", - .irq_ack = sirfsoc_gpio_irq_ack, - .irq_mask = sirfsoc_gpio_irq_mask, - .irq_unmask = sirfsoc_gpio_irq_unmask, - .irq_set_type = sirfsoc_gpio_irq_type, -}; - -static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc) -{ - struct sirfsoc_gpio_bank *bank = irq_get_handler_data(irq); - u32 status, ctrl; - int idx = 0; - struct irq_chip *chip = irq_get_chip(irq); - - chained_irq_enter(chip, desc); - - status = readl(bank->chip.regs + SIRFSOC_GPIO_INT_STATUS(bank->id)); - if (!status) { - printk(KERN_WARNING - "%s: gpio id %d status %#x no interrupt is flaged\n", - __func__, bank->id, status); - handle_bad_irq(irq, desc); - return; - } - - while (status) { - ctrl = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, idx)); - - /* - * Here we must check whether the corresponding GPIO's interrupt - * has been enabled, otherwise just skip it - */ - if ((status & 0x1) && (ctrl & SIRFSOC_GPIO_CTL_INTR_EN_MASK)) { - pr_debug("%s: gpio id %d idx %d happens\n", - __func__, bank->id, idx); - generic_handle_irq(irq_find_mapping(bank->domain, idx)); - } - - idx++; - status = status >> 1; - } - - chained_irq_exit(chip, desc); -} - -static inline void sirfsoc_gpio_set_input(struct sirfsoc_gpio_bank *bank, unsigned ctrl_offset) -{ - u32 val; - - val = readl(bank->chip.regs + ctrl_offset); - val &= ~SIRFSOC_GPIO_CTL_OUT_EN_MASK; - writel(val, bank->chip.regs + ctrl_offset); -} - -static int sirfsoc_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); - unsigned long flags; - - if (pinctrl_request_gpio(chip->base + offset)) - return -ENODEV; - - spin_lock_irqsave(&bank->lock, flags); - - /* - * default status: - * set direction as input and mask irq - */ - sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->id, offset)); - __sirfsoc_gpio_irq_mask(bank, offset); - - spin_unlock_irqrestore(&bank->lock, flags); - - return 0; -} - -static void sirfsoc_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); - unsigned long flags; - - spin_lock_irqsave(&bank->lock, flags); - - __sirfsoc_gpio_irq_mask(bank, offset); - sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->id, offset)); - - spin_unlock_irqrestore(&bank->lock, flags); - - pinctrl_free_gpio(chip->base + offset); -} - -static int sirfsoc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) -{ - struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); - int idx = sirfsoc_gpio_to_offset(gpio); - unsigned long flags; - unsigned offset; - - offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - - spin_lock_irqsave(&bank->lock, flags); - - sirfsoc_gpio_set_input(bank, offset); - - spin_unlock_irqrestore(&bank->lock, flags); - - return 0; -} - -static inline void sirfsoc_gpio_set_output(struct sirfsoc_gpio_bank *bank, unsigned offset, - int value) -{ - u32 out_ctrl; - unsigned long flags; - - spin_lock_irqsave(&bank->lock, flags); - - out_ctrl = readl(bank->chip.regs + offset); - if (value) - out_ctrl |= SIRFSOC_GPIO_CTL_DATAOUT_MASK; - else - out_ctrl &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK; - - out_ctrl &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; - out_ctrl |= SIRFSOC_GPIO_CTL_OUT_EN_MASK; - writel(out_ctrl, bank->chip.regs + offset); - - spin_unlock_irqrestore(&bank->lock, flags); -} - -static int sirfsoc_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) -{ - struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); - int idx = sirfsoc_gpio_to_offset(gpio); - u32 offset; - unsigned long flags; - - offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - - spin_lock_irqsave(&sgpio_lock, flags); - - sirfsoc_gpio_set_output(bank, offset, value); - - spin_unlock_irqrestore(&sgpio_lock, flags); - - return 0; -} - -static int sirfsoc_gpio_get_value(struct gpio_chip *chip, unsigned offset) -{ - struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); - u32 val; - unsigned long flags; - - spin_lock_irqsave(&bank->lock, flags); - - val = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); - - spin_unlock_irqrestore(&bank->lock, flags); - - return !!(val & SIRFSOC_GPIO_CTL_DATAIN_MASK); -} - -static void sirfsoc_gpio_set_value(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); - u32 ctrl; - unsigned long flags; - - spin_lock_irqsave(&bank->lock, flags); - - ctrl = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); - if (value) - ctrl |= SIRFSOC_GPIO_CTL_DATAOUT_MASK; - else - ctrl &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK; - writel(ctrl, bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); - - spin_unlock_irqrestore(&bank->lock, flags); -} - -static int sirfsoc_gpio_irq_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct sirfsoc_gpio_bank *bank = d->host_data; - - if (!bank) - return -EINVAL; - - irq_set_chip(irq, &sirfsoc_irq_chip); - irq_set_handler(irq, handle_level_irq); - irq_set_chip_data(irq, bank); - set_irq_flags(irq, IRQF_VALID); - - return 0; -} - -const struct irq_domain_ops sirfsoc_gpio_irq_simple_ops = { - .map = sirfsoc_gpio_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - -static void sirfsoc_gpio_set_pullup(const u32 *pullups) -{ - int i, n; - const unsigned long *p = (const unsigned long *)pullups; - - for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { - for_each_set_bit(n, p + i, BITS_PER_LONG) { - u32 offset = SIRFSOC_GPIO_CTRL(i, n); - u32 val = readl(sgpio_bank[i].chip.regs + offset); - val |= SIRFSOC_GPIO_CTL_PULL_MASK; - val |= SIRFSOC_GPIO_CTL_PULL_HIGH; - writel(val, sgpio_bank[i].chip.regs + offset); - } - } -} - -static void sirfsoc_gpio_set_pulldown(const u32 *pulldowns) -{ - int i, n; - const unsigned long *p = (const unsigned long *)pulldowns; - - for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { - for_each_set_bit(n, p + i, BITS_PER_LONG) { - u32 offset = SIRFSOC_GPIO_CTRL(i, n); - u32 val = readl(sgpio_bank[i].chip.regs + offset); - val |= SIRFSOC_GPIO_CTL_PULL_MASK; - val &= ~SIRFSOC_GPIO_CTL_PULL_HIGH; - writel(val, sgpio_bank[i].chip.regs + offset); - } - } -} - -static int sirfsoc_gpio_probe(struct device_node *np) -{ - int i, err = 0; - struct sirfsoc_gpio_bank *bank; - void *regs; - struct platform_device *pdev; - bool is_marco = false; - - u32 pullups[SIRFSOC_GPIO_NO_OF_BANKS], pulldowns[SIRFSOC_GPIO_NO_OF_BANKS]; - - pdev = of_find_device_by_node(np); - if (!pdev) - return -ENODEV; - - regs = of_iomap(np, 0); - if (!regs) - return -ENOMEM; - - if (of_device_is_compatible(np, "sirf,marco-pinctrl")) - is_marco = 1; - - for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { - bank = &sgpio_bank[i]; - spin_lock_init(&bank->lock); - bank->chip.gc.request = sirfsoc_gpio_request; - bank->chip.gc.free = sirfsoc_gpio_free; - bank->chip.gc.direction_input = sirfsoc_gpio_direction_input; - bank->chip.gc.get = sirfsoc_gpio_get_value; - bank->chip.gc.direction_output = sirfsoc_gpio_direction_output; - bank->chip.gc.set = sirfsoc_gpio_set_value; - bank->chip.gc.to_irq = sirfsoc_gpio_to_irq; - bank->chip.gc.base = i * SIRFSOC_GPIO_BANK_SIZE; - bank->chip.gc.ngpio = SIRFSOC_GPIO_BANK_SIZE; - bank->chip.gc.label = kstrdup(np->full_name, GFP_KERNEL); - bank->chip.gc.of_node = np; - bank->chip.gc.of_xlate = sirfsoc_gpio_of_xlate; - bank->chip.gc.of_gpio_n_cells = 2; - bank->chip.regs = regs; - bank->id = i; - bank->is_marco = is_marco; - bank->parent_irq = platform_get_irq(pdev, i); - if (bank->parent_irq < 0) { - err = bank->parent_irq; - goto out; - } - - err = gpiochip_add(&bank->chip.gc); - if (err) { - pr_err("%s: error in probe function with status %d\n", - np->full_name, err); - goto out; - } - - bank->domain = irq_domain_add_linear(np, SIRFSOC_GPIO_BANK_SIZE, - &sirfsoc_gpio_irq_simple_ops, bank); - - if (!bank->domain) { - pr_err("%s: Failed to create irqdomain\n", np->full_name); - err = -ENOSYS; - goto out; - } - - irq_set_chained_handler(bank->parent_irq, sirfsoc_gpio_handle_irq); - irq_set_handler_data(bank->parent_irq, bank); - } - - if (!of_property_read_u32_array(np, "sirf,pullups", pullups, - SIRFSOC_GPIO_NO_OF_BANKS)) - sirfsoc_gpio_set_pullup(pullups); - - if (!of_property_read_u32_array(np, "sirf,pulldowns", pulldowns, - SIRFSOC_GPIO_NO_OF_BANKS)) - sirfsoc_gpio_set_pulldown(pulldowns); - - return 0; - -out: - iounmap(regs); - return err; -} - -static int __init sirfsoc_gpio_init(void) -{ - - struct device_node *np; - - np = of_find_matching_node(NULL, pinmux_ids); - - if (!np) - return -ENODEV; - - return sirfsoc_gpio_probe(np); -} -subsys_initcall(sirfsoc_gpio_init); - -MODULE_AUTHOR("Rongjun Ying <rongjun.ying@csr.com>, " - "Yuping Luo <yuping.luo@csr.com>, " - "Barry Song <baohua.song@csr.com>"); -MODULE_DESCRIPTION("SIRFSOC pin control driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c new file mode 100644 index 000000000000..0677e198db60 --- /dev/null +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -0,0 +1,929 @@ +/* + * pinmux driver for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/machine.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/bitops.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <asm/mach/irq.h> + +#include "pinctrl-sirf.h" + +#define DRIVER_NAME "pinmux-sirf" + +struct sirfsoc_gpio_bank { + struct of_mm_gpio_chip chip; + struct irq_domain *domain; + int id; + int parent_irq; + spinlock_t lock; + bool is_marco; /* for marco, some registers are different with prima2 */ +}; + +static struct sirfsoc_gpio_bank sgpio_bank[SIRFSOC_GPIO_NO_OF_BANKS]; +static DEFINE_SPINLOCK(sgpio_lock); + +static struct sirfsoc_pin_group *sirfsoc_pin_groups; +static int sirfsoc_pingrp_cnt; + +static int sirfsoc_get_groups_count(struct pinctrl_dev *pctldev) +{ + return sirfsoc_pingrp_cnt; +} + +static const char *sirfsoc_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return sirfsoc_pin_groups[selector].name; +} + +static int sirfsoc_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, + const unsigned **pins, + unsigned *num_pins) +{ + *pins = sirfsoc_pin_groups[selector].pins; + *num_pins = sirfsoc_pin_groups[selector].num_pins; + return 0; +} + +static void sirfsoc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned offset) +{ + seq_printf(s, " " DRIVER_NAME); +} + +static int sirfsoc_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct sirfsoc_pmx *spmx = pinctrl_dev_get_drvdata(pctldev); + struct device_node *np; + struct property *prop; + const char *function, *group; + int ret, index = 0, count = 0; + + /* calculate number of maps required */ + for_each_child_of_node(np_config, np) { + ret = of_property_read_string(np, "sirf,function", &function); + if (ret < 0) + return ret; + + ret = of_property_count_strings(np, "sirf,pins"); + if (ret < 0) + return ret; + + count += ret; + } + + if (!count) { + dev_err(spmx->dev, "No child nodes passed via DT\n"); + return -ENODEV; + } + + *map = kzalloc(sizeof(**map) * count, GFP_KERNEL); + if (!*map) + return -ENOMEM; + + for_each_child_of_node(np_config, np) { + of_property_read_string(np, "sirf,function", &function); + of_property_for_each_string(np, "sirf,pins", prop, group) { + (*map)[index].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[index].data.mux.group = group; + (*map)[index].data.mux.function = function; + index++; + } + } + + *num_maps = count; + + return 0; +} + +static void sirfsoc_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + kfree(map); +} + +static struct pinctrl_ops sirfsoc_pctrl_ops = { + .get_groups_count = sirfsoc_get_groups_count, + .get_group_name = sirfsoc_get_group_name, + .get_group_pins = sirfsoc_get_group_pins, + .pin_dbg_show = sirfsoc_pin_dbg_show, + .dt_node_to_map = sirfsoc_dt_node_to_map, + .dt_free_map = sirfsoc_dt_free_map, +}; + +static struct sirfsoc_pmx_func *sirfsoc_pmx_functions; +static int sirfsoc_pmxfunc_cnt; + +static void sirfsoc_pinmux_endisable(struct sirfsoc_pmx *spmx, unsigned selector, + bool enable) +{ + int i; + const struct sirfsoc_padmux *mux = sirfsoc_pmx_functions[selector].padmux; + const struct sirfsoc_muxmask *mask = mux->muxmask; + + for (i = 0; i < mux->muxmask_counts; i++) { + u32 muxval; + if (!spmx->is_marco) { + muxval = readl(spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(mask[i].group)); + if (enable) + muxval = muxval & ~mask[i].mask; + else + muxval = muxval | mask[i].mask; + writel(muxval, spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(mask[i].group)); + } else { + if (enable) + writel(mask[i].mask, spmx->gpio_virtbase + + SIRFSOC_GPIO_PAD_EN_CLR(mask[i].group)); + else + writel(mask[i].mask, spmx->gpio_virtbase + + SIRFSOC_GPIO_PAD_EN(mask[i].group)); + } + } + + if (mux->funcmask && enable) { + u32 func_en_val; + func_en_val = + readl(spmx->rsc_virtbase + SIRFSOC_RSC_PIN_MUX); + func_en_val = + (func_en_val & ~mux->funcmask) | (mux-> + funcval); + writel(func_en_val, spmx->rsc_virtbase + SIRFSOC_RSC_PIN_MUX); + } +} + +static int sirfsoc_pinmux_enable(struct pinctrl_dev *pmxdev, unsigned selector, + unsigned group) +{ + struct sirfsoc_pmx *spmx; + + spmx = pinctrl_dev_get_drvdata(pmxdev); + sirfsoc_pinmux_endisable(spmx, selector, true); + + return 0; +} + +static void sirfsoc_pinmux_disable(struct pinctrl_dev *pmxdev, unsigned selector, + unsigned group) +{ + struct sirfsoc_pmx *spmx; + + spmx = pinctrl_dev_get_drvdata(pmxdev); + sirfsoc_pinmux_endisable(spmx, selector, false); +} + +static int sirfsoc_pinmux_get_funcs_count(struct pinctrl_dev *pmxdev) +{ + return sirfsoc_pmxfunc_cnt; +} + +static const char *sirfsoc_pinmux_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return sirfsoc_pmx_functions[selector].name; +} + +static int sirfsoc_pinmux_get_groups(struct pinctrl_dev *pctldev, unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + *groups = sirfsoc_pmx_functions[selector].groups; + *num_groups = sirfsoc_pmx_functions[selector].num_groups; + return 0; +} + +static int sirfsoc_pinmux_request_gpio(struct pinctrl_dev *pmxdev, + struct pinctrl_gpio_range *range, unsigned offset) +{ + struct sirfsoc_pmx *spmx; + + int group = range->id; + + u32 muxval; + + spmx = pinctrl_dev_get_drvdata(pmxdev); + + if (!spmx->is_marco) { + muxval = readl(spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(group)); + muxval = muxval | (1 << (offset - range->pin_base)); + writel(muxval, spmx->gpio_virtbase + SIRFSOC_GPIO_PAD_EN(group)); + } else { + writel(1 << (offset - range->pin_base), spmx->gpio_virtbase + + SIRFSOC_GPIO_PAD_EN(group)); + } + + return 0; +} + +static struct pinmux_ops sirfsoc_pinmux_ops = { + .enable = sirfsoc_pinmux_enable, + .disable = sirfsoc_pinmux_disable, + .get_functions_count = sirfsoc_pinmux_get_funcs_count, + .get_function_name = sirfsoc_pinmux_get_func_name, + .get_function_groups = sirfsoc_pinmux_get_groups, + .gpio_request_enable = sirfsoc_pinmux_request_gpio, +}; + +static struct pinctrl_desc sirfsoc_pinmux_desc = { + .name = DRIVER_NAME, + .pctlops = &sirfsoc_pctrl_ops, + .pmxops = &sirfsoc_pinmux_ops, + .owner = THIS_MODULE, +}; + +/* + * Todo: bind irq_chip to every pinctrl_gpio_range + */ +static struct pinctrl_gpio_range sirfsoc_gpio_ranges[] = { + { + .name = "sirfsoc-gpio*", + .id = 0, + .base = 0, + .pin_base = 0, + .npins = 32, + }, { + .name = "sirfsoc-gpio*", + .id = 1, + .base = 32, + .pin_base = 32, + .npins = 32, + }, { + .name = "sirfsoc-gpio*", + .id = 2, + .base = 64, + .pin_base = 64, + .npins = 32, + }, { + .name = "sirfsoc-gpio*", + .id = 3, + .base = 96, + .pin_base = 96, + .npins = 19, + }, +}; + +static void __iomem *sirfsoc_rsc_of_iomap(void) +{ + const struct of_device_id rsc_ids[] = { + { .compatible = "sirf,prima2-rsc" }, + { .compatible = "sirf,marco-rsc" }, + {} + }; + struct device_node *np; + + np = of_find_matching_node(NULL, rsc_ids); + if (!np) + panic("unable to find compatible rsc node in dtb\n"); + + return of_iomap(np, 0); +} + +static int sirfsoc_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (gpiospec->args[0] > SIRFSOC_GPIO_NO_OF_BANKS * SIRFSOC_GPIO_BANK_SIZE) + return -EINVAL; + + if (gc != &sgpio_bank[gpiospec->args[0] / SIRFSOC_GPIO_BANK_SIZE].chip.gc) + return -EINVAL; + + if (flags) + *flags = gpiospec->args[1]; + + return gpiospec->args[0] % SIRFSOC_GPIO_BANK_SIZE; +} + +static const struct of_device_id pinmux_ids[] = { + { .compatible = "sirf,prima2-pinctrl", .data = &prima2_pinctrl_data, }, + { .compatible = "sirf,atlas6-pinctrl", .data = &atlas6_pinctrl_data, }, + { .compatible = "sirf,marco-pinctrl", .data = &prima2_pinctrl_data, }, + {} +}; + +static int sirfsoc_pinmux_probe(struct platform_device *pdev) +{ + int ret; + struct sirfsoc_pmx *spmx; + struct device_node *np = pdev->dev.of_node; + const struct sirfsoc_pinctrl_data *pdata; + int i; + + /* Create state holders etc for this driver */ + spmx = devm_kzalloc(&pdev->dev, sizeof(*spmx), GFP_KERNEL); + if (!spmx) + return -ENOMEM; + + spmx->dev = &pdev->dev; + + platform_set_drvdata(pdev, spmx); + + spmx->gpio_virtbase = of_iomap(np, 0); + if (!spmx->gpio_virtbase) { + dev_err(&pdev->dev, "can't map gpio registers\n"); + return -ENOMEM; + } + + spmx->rsc_virtbase = sirfsoc_rsc_of_iomap(); + if (!spmx->rsc_virtbase) { + ret = -ENOMEM; + dev_err(&pdev->dev, "can't map rsc registers\n"); + goto out_no_rsc_remap; + } + + if (of_device_is_compatible(np, "sirf,marco-pinctrl")) + spmx->is_marco = 1; + + pdata = of_match_node(pinmux_ids, np)->data; + sirfsoc_pin_groups = pdata->grps; + sirfsoc_pingrp_cnt = pdata->grps_cnt; + sirfsoc_pmx_functions = pdata->funcs; + sirfsoc_pmxfunc_cnt = pdata->funcs_cnt; + sirfsoc_pinmux_desc.pins = pdata->pads; + sirfsoc_pinmux_desc.npins = pdata->pads_cnt; + + + /* Now register the pin controller and all pins it handles */ + spmx->pmx = pinctrl_register(&sirfsoc_pinmux_desc, &pdev->dev, spmx); + if (!spmx->pmx) { + dev_err(&pdev->dev, "could not register SIRFSOC pinmux driver\n"); + ret = -EINVAL; + goto out_no_pmx; + } + + for (i = 0; i < ARRAY_SIZE(sirfsoc_gpio_ranges); i++) { + sirfsoc_gpio_ranges[i].gc = &sgpio_bank[i].chip.gc; + pinctrl_add_gpio_range(spmx->pmx, &sirfsoc_gpio_ranges[i]); + } + + dev_info(&pdev->dev, "initialized SIRFSOC pinmux driver\n"); + + return 0; + +out_no_pmx: + iounmap(spmx->rsc_virtbase); +out_no_rsc_remap: + iounmap(spmx->gpio_virtbase); + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int sirfsoc_pinmux_suspend_noirq(struct device *dev) +{ + int i, j; + struct sirfsoc_pmx *spmx = dev_get_drvdata(dev); + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + for (j = 0; j < SIRFSOC_GPIO_BANK_SIZE; j++) { + spmx->gpio_regs[i][j] = readl(spmx->gpio_virtbase + + SIRFSOC_GPIO_CTRL(i, j)); + } + spmx->ints_regs[i] = readl(spmx->gpio_virtbase + + SIRFSOC_GPIO_INT_STATUS(i)); + spmx->paden_regs[i] = readl(spmx->gpio_virtbase + + SIRFSOC_GPIO_PAD_EN(i)); + } + spmx->dspen_regs = readl(spmx->gpio_virtbase + SIRFSOC_GPIO_DSP_EN0); + + for (i = 0; i < 3; i++) + spmx->rsc_regs[i] = readl(spmx->rsc_virtbase + 4 * i); + + return 0; +} + +static int sirfsoc_pinmux_resume_noirq(struct device *dev) +{ + int i, j; + struct sirfsoc_pmx *spmx = dev_get_drvdata(dev); + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + for (j = 0; j < SIRFSOC_GPIO_BANK_SIZE; j++) { + writel(spmx->gpio_regs[i][j], spmx->gpio_virtbase + + SIRFSOC_GPIO_CTRL(i, j)); + } + writel(spmx->ints_regs[i], spmx->gpio_virtbase + + SIRFSOC_GPIO_INT_STATUS(i)); + writel(spmx->paden_regs[i], spmx->gpio_virtbase + + SIRFSOC_GPIO_PAD_EN(i)); + } + writel(spmx->dspen_regs, spmx->gpio_virtbase + SIRFSOC_GPIO_DSP_EN0); + + for (i = 0; i < 3; i++) + writel(spmx->rsc_regs[i], spmx->rsc_virtbase + 4 * i); + + return 0; +} + +static const struct dev_pm_ops sirfsoc_pinmux_pm_ops = { + .suspend_noirq = sirfsoc_pinmux_suspend_noirq, + .resume_noirq = sirfsoc_pinmux_resume_noirq, +}; +#endif + +static struct platform_driver sirfsoc_pinmux_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = pinmux_ids, +#ifdef CONFIG_PM_SLEEP + .pm = &sirfsoc_pinmux_pm_ops, +#endif + }, + .probe = sirfsoc_pinmux_probe, +}; + +static int __init sirfsoc_pinmux_init(void) +{ + return platform_driver_register(&sirfsoc_pinmux_driver); +} +arch_initcall(sirfsoc_pinmux_init); + +static inline int sirfsoc_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct sirfsoc_gpio_bank *bank = container_of(to_of_mm_gpio_chip(chip), + struct sirfsoc_gpio_bank, chip); + + return irq_create_mapping(bank->domain, offset); +} + +static inline int sirfsoc_gpio_to_offset(unsigned int gpio) +{ + return gpio % SIRFSOC_GPIO_BANK_SIZE; +} + +static inline struct sirfsoc_gpio_bank *sirfsoc_gpio_to_bank(unsigned int gpio) +{ + return &sgpio_bank[gpio / SIRFSOC_GPIO_BANK_SIZE]; +} + +static inline struct sirfsoc_gpio_bank *sirfsoc_irqchip_to_bank(struct gpio_chip *chip) +{ + return container_of(to_of_mm_gpio_chip(chip), struct sirfsoc_gpio_bank, chip); +} + +static void sirfsoc_gpio_irq_ack(struct irq_data *d) +{ + struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); +} + +static void __sirfsoc_gpio_irq_mask(struct sirfsoc_gpio_bank *bank, int idx) +{ + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + val &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; + val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); +} + +static void sirfsoc_gpio_irq_mask(struct irq_data *d) +{ + struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + __sirfsoc_gpio_irq_mask(bank, d->hwirq % SIRFSOC_GPIO_BANK_SIZE); +} + +static void sirfsoc_gpio_irq_unmask(struct irq_data *d) +{ + struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + val |= SIRFSOC_GPIO_CTL_INTR_EN_MASK; + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); +} + +static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + + switch (type) { + case IRQ_TYPE_NONE: + break; + case IRQ_TYPE_EDGE_RISING: + val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; + val &= ~SIRFSOC_GPIO_CTL_INTR_LOW_MASK; + break; + case IRQ_TYPE_EDGE_FALLING: + val &= ~SIRFSOC_GPIO_CTL_INTR_HIGH_MASK; + val |= SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; + break; + case IRQ_TYPE_EDGE_BOTH: + val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_LOW_MASK | + SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; + break; + case IRQ_TYPE_LEVEL_LOW: + val &= ~(SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + val |= SIRFSOC_GPIO_CTL_INTR_LOW_MASK; + break; + case IRQ_TYPE_LEVEL_HIGH: + val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK; + val &= ~(SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + break; + } + + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); + + return 0; +} + +static struct irq_chip sirfsoc_irq_chip = { + .name = "sirf-gpio-irq", + .irq_ack = sirfsoc_gpio_irq_ack, + .irq_mask = sirfsoc_gpio_irq_mask, + .irq_unmask = sirfsoc_gpio_irq_unmask, + .irq_set_type = sirfsoc_gpio_irq_type, +}; + +static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc) +{ + struct sirfsoc_gpio_bank *bank = irq_get_handler_data(irq); + u32 status, ctrl; + int idx = 0; + struct irq_chip *chip = irq_get_chip(irq); + + chained_irq_enter(chip, desc); + + status = readl(bank->chip.regs + SIRFSOC_GPIO_INT_STATUS(bank->id)); + if (!status) { + printk(KERN_WARNING + "%s: gpio id %d status %#x no interrupt is flaged\n", + __func__, bank->id, status); + handle_bad_irq(irq, desc); + return; + } + + while (status) { + ctrl = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, idx)); + + /* + * Here we must check whether the corresponding GPIO's interrupt + * has been enabled, otherwise just skip it + */ + if ((status & 0x1) && (ctrl & SIRFSOC_GPIO_CTL_INTR_EN_MASK)) { + pr_debug("%s: gpio id %d idx %d happens\n", + __func__, bank->id, idx); + generic_handle_irq(irq_find_mapping(bank->domain, idx)); + } + + idx++; + status = status >> 1; + } + + chained_irq_exit(chip, desc); +} + +static inline void sirfsoc_gpio_set_input(struct sirfsoc_gpio_bank *bank, unsigned ctrl_offset) +{ + u32 val; + + val = readl(bank->chip.regs + ctrl_offset); + val &= ~SIRFSOC_GPIO_CTL_OUT_EN_MASK; + writel(val, bank->chip.regs + ctrl_offset); +} + +static int sirfsoc_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + unsigned long flags; + + if (pinctrl_request_gpio(chip->base + offset)) + return -ENODEV; + + spin_lock_irqsave(&bank->lock, flags); + + /* + * default status: + * set direction as input and mask irq + */ + sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->id, offset)); + __sirfsoc_gpio_irq_mask(bank, offset); + + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static void sirfsoc_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + __sirfsoc_gpio_irq_mask(bank, offset); + sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->id, offset)); + + spin_unlock_irqrestore(&bank->lock, flags); + + pinctrl_free_gpio(chip->base + offset); +} + +static int sirfsoc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + int idx = sirfsoc_gpio_to_offset(gpio); + unsigned long flags; + unsigned offset; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&bank->lock, flags); + + sirfsoc_gpio_set_input(bank, offset); + + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static inline void sirfsoc_gpio_set_output(struct sirfsoc_gpio_bank *bank, unsigned offset, + int value) +{ + u32 out_ctrl; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + out_ctrl = readl(bank->chip.regs + offset); + if (value) + out_ctrl |= SIRFSOC_GPIO_CTL_DATAOUT_MASK; + else + out_ctrl &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK; + + out_ctrl &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; + out_ctrl |= SIRFSOC_GPIO_CTL_OUT_EN_MASK; + writel(out_ctrl, bank->chip.regs + offset); + + spin_unlock_irqrestore(&bank->lock, flags); +} + +static int sirfsoc_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + int idx = sirfsoc_gpio_to_offset(gpio); + u32 offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + sirfsoc_gpio_set_output(bank, offset, value); + + spin_unlock_irqrestore(&sgpio_lock, flags); + + return 0; +} + +static int sirfsoc_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + u32 val; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + val = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); + + spin_unlock_irqrestore(&bank->lock, flags); + + return !!(val & SIRFSOC_GPIO_CTL_DATAIN_MASK); +} + +static void sirfsoc_gpio_set_value(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + u32 ctrl; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + ctrl = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); + if (value) + ctrl |= SIRFSOC_GPIO_CTL_DATAOUT_MASK; + else + ctrl &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK; + writel(ctrl, bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); + + spin_unlock_irqrestore(&bank->lock, flags); +} + +static int sirfsoc_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct sirfsoc_gpio_bank *bank = d->host_data; + + if (!bank) + return -EINVAL; + + irq_set_chip(irq, &sirfsoc_irq_chip); + irq_set_handler(irq, handle_level_irq); + irq_set_chip_data(irq, bank); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +static const struct irq_domain_ops sirfsoc_gpio_irq_simple_ops = { + .map = sirfsoc_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static void sirfsoc_gpio_set_pullup(const u32 *pullups) +{ + int i, n; + const unsigned long *p = (const unsigned long *)pullups; + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + for_each_set_bit(n, p + i, BITS_PER_LONG) { + u32 offset = SIRFSOC_GPIO_CTRL(i, n); + u32 val = readl(sgpio_bank[i].chip.regs + offset); + val |= SIRFSOC_GPIO_CTL_PULL_MASK; + val |= SIRFSOC_GPIO_CTL_PULL_HIGH; + writel(val, sgpio_bank[i].chip.regs + offset); + } + } +} + +static void sirfsoc_gpio_set_pulldown(const u32 *pulldowns) +{ + int i, n; + const unsigned long *p = (const unsigned long *)pulldowns; + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + for_each_set_bit(n, p + i, BITS_PER_LONG) { + u32 offset = SIRFSOC_GPIO_CTRL(i, n); + u32 val = readl(sgpio_bank[i].chip.regs + offset); + val |= SIRFSOC_GPIO_CTL_PULL_MASK; + val &= ~SIRFSOC_GPIO_CTL_PULL_HIGH; + writel(val, sgpio_bank[i].chip.regs + offset); + } + } +} + +static int sirfsoc_gpio_probe(struct device_node *np) +{ + int i, err = 0; + struct sirfsoc_gpio_bank *bank; + void *regs; + struct platform_device *pdev; + bool is_marco = false; + + u32 pullups[SIRFSOC_GPIO_NO_OF_BANKS], pulldowns[SIRFSOC_GPIO_NO_OF_BANKS]; + + pdev = of_find_device_by_node(np); + if (!pdev) + return -ENODEV; + + regs = of_iomap(np, 0); + if (!regs) + return -ENOMEM; + + if (of_device_is_compatible(np, "sirf,marco-pinctrl")) + is_marco = 1; + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + bank = &sgpio_bank[i]; + spin_lock_init(&bank->lock); + bank->chip.gc.request = sirfsoc_gpio_request; + bank->chip.gc.free = sirfsoc_gpio_free; + bank->chip.gc.direction_input = sirfsoc_gpio_direction_input; + bank->chip.gc.get = sirfsoc_gpio_get_value; + bank->chip.gc.direction_output = sirfsoc_gpio_direction_output; + bank->chip.gc.set = sirfsoc_gpio_set_value; + bank->chip.gc.to_irq = sirfsoc_gpio_to_irq; + bank->chip.gc.base = i * SIRFSOC_GPIO_BANK_SIZE; + bank->chip.gc.ngpio = SIRFSOC_GPIO_BANK_SIZE; + bank->chip.gc.label = kstrdup(np->full_name, GFP_KERNEL); + bank->chip.gc.of_node = np; + bank->chip.gc.of_xlate = sirfsoc_gpio_of_xlate; + bank->chip.gc.of_gpio_n_cells = 2; + bank->chip.regs = regs; + bank->id = i; + bank->is_marco = is_marco; + bank->parent_irq = platform_get_irq(pdev, i); + if (bank->parent_irq < 0) { + err = bank->parent_irq; + goto out; + } + + err = gpiochip_add(&bank->chip.gc); + if (err) { + pr_err("%s: error in probe function with status %d\n", + np->full_name, err); + goto out; + } + + bank->domain = irq_domain_add_linear(np, SIRFSOC_GPIO_BANK_SIZE, + &sirfsoc_gpio_irq_simple_ops, bank); + + if (!bank->domain) { + pr_err("%s: Failed to create irqdomain\n", np->full_name); + err = -ENOSYS; + goto out; + } + + irq_set_chained_handler(bank->parent_irq, sirfsoc_gpio_handle_irq); + irq_set_handler_data(bank->parent_irq, bank); + } + + if (!of_property_read_u32_array(np, "sirf,pullups", pullups, + SIRFSOC_GPIO_NO_OF_BANKS)) + sirfsoc_gpio_set_pullup(pullups); + + if (!of_property_read_u32_array(np, "sirf,pulldowns", pulldowns, + SIRFSOC_GPIO_NO_OF_BANKS)) + sirfsoc_gpio_set_pulldown(pulldowns); + + return 0; + +out: + iounmap(regs); + return err; +} + +static int __init sirfsoc_gpio_init(void) +{ + + struct device_node *np; + + np = of_find_matching_node(NULL, pinmux_ids); + + if (!np) + return -ENODEV; + + return sirfsoc_gpio_probe(np); +} +subsys_initcall(sirfsoc_gpio_init); + +MODULE_AUTHOR("Rongjun Ying <rongjun.ying@csr.com>, " + "Yuping Luo <yuping.luo@csr.com>, " + "Barry Song <baohua.song@csr.com>"); +MODULE_DESCRIPTION("SIRFSOC pin control driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.h b/drivers/pinctrl/sirf/pinctrl-sirf.h new file mode 100644 index 000000000000..17cc108510ba --- /dev/null +++ b/drivers/pinctrl/sirf/pinctrl-sirf.h @@ -0,0 +1,116 @@ +/* + * pinmux driver shared headfile for CSR SiRFsoc + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __PINMUX_SIRF_H__ +#define __PINMUX_SIRF_H__ + +#define SIRFSOC_NUM_PADS 622 +#define SIRFSOC_RSC_PIN_MUX 0x4 + +#define SIRFSOC_GPIO_PAD_EN(g) ((g)*0x100 + 0x84) +#define SIRFSOC_GPIO_PAD_EN_CLR(g) ((g)*0x100 + 0x90) +#define SIRFSOC_GPIO_CTRL(g, i) ((g)*0x100 + (i)*4) +#define SIRFSOC_GPIO_DSP_EN0 (0x80) +#define SIRFSOC_GPIO_INT_STATUS(g) ((g)*0x100 + 0x8C) + +#define SIRFSOC_GPIO_CTL_INTR_LOW_MASK 0x1 +#define SIRFSOC_GPIO_CTL_INTR_HIGH_MASK 0x2 +#define SIRFSOC_GPIO_CTL_INTR_TYPE_MASK 0x4 +#define SIRFSOC_GPIO_CTL_INTR_EN_MASK 0x8 +#define SIRFSOC_GPIO_CTL_INTR_STS_MASK 0x10 +#define SIRFSOC_GPIO_CTL_OUT_EN_MASK 0x20 +#define SIRFSOC_GPIO_CTL_DATAOUT_MASK 0x40 +#define SIRFSOC_GPIO_CTL_DATAIN_MASK 0x80 +#define SIRFSOC_GPIO_CTL_PULL_MASK 0x100 +#define SIRFSOC_GPIO_CTL_PULL_HIGH 0x200 +#define SIRFSOC_GPIO_CTL_DSP_INT 0x400 + +#define SIRFSOC_GPIO_NO_OF_BANKS 5 +#define SIRFSOC_GPIO_BANK_SIZE 32 +#define SIRFSOC_GPIO_NUM(bank, index) (((bank)*(32)) + (index)) + +/** + * @dev: a pointer back to containing device + * @virtbase: the offset to the controller in virtual memory + */ +struct sirfsoc_pmx { + struct device *dev; + struct pinctrl_dev *pmx; + void __iomem *gpio_virtbase; + void __iomem *rsc_virtbase; + u32 gpio_regs[SIRFSOC_GPIO_NO_OF_BANKS][SIRFSOC_GPIO_BANK_SIZE]; + u32 ints_regs[SIRFSOC_GPIO_NO_OF_BANKS]; + u32 paden_regs[SIRFSOC_GPIO_NO_OF_BANKS]; + u32 dspen_regs; + u32 rsc_regs[3]; + bool is_marco; +}; + +/* SIRFSOC_GPIO_PAD_EN set */ +struct sirfsoc_muxmask { + unsigned long group; + unsigned long mask; +}; + +struct sirfsoc_padmux { + unsigned long muxmask_counts; + const struct sirfsoc_muxmask *muxmask; + /* RSC_PIN_MUX set */ + unsigned long funcmask; + unsigned long funcval; +}; + + /** + * struct sirfsoc_pin_group - describes a SiRFprimaII pin group + * @name: the name of this specific pin group + * @pins: an array of discrete physical pins used in this group, taken + * from the driver-local pin enumeration space + * @num_pins: the number of pins in this group array, i.e. the number of + * elements in .pins so we can iterate over that array + */ +struct sirfsoc_pin_group { + const char *name; + const unsigned int *pins; + const unsigned num_pins; +}; + +#define SIRFSOC_PIN_GROUP(n, p) \ + { \ + .name = n, \ + .pins = p, \ + .num_pins = ARRAY_SIZE(p), \ + } + +struct sirfsoc_pmx_func { + const char *name; + const char * const *groups; + const unsigned num_groups; + const struct sirfsoc_padmux *padmux; +}; + +#define SIRFSOC_PMX_FUNCTION(n, g, m) \ + { \ + .name = n, \ + .groups = g, \ + .num_groups = ARRAY_SIZE(g), \ + .padmux = &m, \ + } + +struct sirfsoc_pinctrl_data { + struct pinctrl_pin_desc *pads; + int pads_cnt; + struct sirfsoc_pin_group *grps; + int grps_cnt; + struct sirfsoc_pmx_func *funcs; + int funcs_cnt; +}; + +extern struct sirfsoc_pinctrl_data prima2_pinctrl_data; +extern struct sirfsoc_pinctrl_data atlas6_pinctrl_data; + +#endif diff --git a/drivers/pinctrl/spear/pinctrl-plgpio.c b/drivers/pinctrl/spear/pinctrl-plgpio.c index 3e5a887216b8..0a7f0bdbaa7d 100644 --- a/drivers/pinctrl/spear/pinctrl-plgpio.c +++ b/drivers/pinctrl/spear/pinctrl-plgpio.c @@ -441,7 +441,7 @@ static int spear310_p2o(int pin) return offset; } -int spear310_o2p(int offset) +static int spear310_o2p(int offset) { if (offset <= 3) return 101 - offset; @@ -528,18 +528,13 @@ static int plgpio_probe(struct platform_device *pdev) struct resource *res; int ret, irq, i; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "invalid IORESOURCE_MEM\n"); - return -EBUSY; - } - plgpio = devm_kzalloc(&pdev->dev, sizeof(*plgpio), GFP_KERNEL); if (!plgpio) { dev_err(&pdev->dev, "memory allocation fail\n"); return -ENOMEM; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); plgpio->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(plgpio->base)) return PTR_ERR(plgpio->base); diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.c b/drivers/pinctrl/vt8500/pinctrl-wmt.c index 70d986e04afb..0cc4335bc0f2 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wmt.c +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c @@ -569,11 +569,9 @@ int wmt_pinctrl_probe(struct platform_device *pdev, struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->base = devm_request_and_ioremap(&pdev->dev, res); - if (!data->base) { - dev_err(&pdev->dev, "failed to map memory resource\n"); - return -EBUSY; - } + data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); wmt_desc.pins = data->pins; wmt_desc.npins = data->npins; diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 95cebf0185de..9357aa779048 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -211,6 +211,12 @@ static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) res->start = -1; res->end = -1; + if (!rule->map) { + res->flags |= IORESOURCE_DISABLED; + pnp_dbg(&dev->dev, " dma %d disabled\n", idx); + goto __add; + } + for (i = 0; i < 8; i++) { if (rule->map & (1 << xtab[i])) { res->start = res->end = xtab[i]; @@ -218,11 +224,9 @@ static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) goto __add; } } -#ifdef MAX_DMA_CHANNELS - res->start = res->end = MAX_DMA_CHANNELS; -#endif - res->flags |= IORESOURCE_DISABLED; - pnp_dbg(&dev->dev, " disable dma %d\n", idx); + + pnp_dbg(&dev->dev, " couldn't assign dma %d\n", idx); + return -EBUSY; __add: pnp_add_dma_resource(dev, res->start, res->flags); diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index 6b2238bb6a81..db9973bb53f1 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -27,7 +27,8 @@ #include <linux/pm_runtime.h> #include <linux/power/smartreflex.h> -#define SMARTREFLEX_NAME_LEN 16 +#define DRIVER_NAME "smartreflex" +#define SMARTREFLEX_NAME_LEN 32 #define NVALUE_NAME_LEN 40 #define SR_DISABLE_TIMEOUT 200 @@ -207,12 +208,11 @@ static void sr_stop_vddautocomp(struct omap_sr *sr) static int sr_late_init(struct omap_sr *sr_info) { struct omap_sr_data *pdata = sr_info->pdev->dev.platform_data; - struct resource *mem; int ret = 0; if (sr_class->notify && sr_class->notify_flags && sr_info->irq) { - ret = request_irq(sr_info->irq, sr_interrupt, - 0, sr_info->name, sr_info); + ret = devm_request_irq(&sr_info->pdev->dev, sr_info->irq, + sr_interrupt, 0, sr_info->name, sr_info); if (ret) goto error; disable_irq(sr_info->irq); @@ -224,14 +224,10 @@ static int sr_late_init(struct omap_sr *sr_info) return ret; error: - iounmap(sr_info->base); - mem = platform_get_resource(sr_info->pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, resource_size(mem)); list_del(&sr_info->node); dev_err(&sr_info->pdev->dev, "%s: ERROR in registering" "interrupt handler. Smartreflex will" "not function as desired\n", __func__); - kfree(sr_info); return ret; } @@ -341,9 +337,9 @@ static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row( /* Public Functions */ /** - * sr_configure_errgen() - Configures the smrtreflex to perform AVS using the + * sr_configure_errgen() - Configures the SmartReflex to perform AVS using the * error generator module. - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * configure the error generator module inside the smartreflex module. @@ -352,17 +348,17 @@ static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row( * SR CLASS 2 can choose between ERROR module and MINMAXAVG * module. Returns 0 on success and error value in case of failure. */ -int sr_configure_errgen(struct voltagedomain *voltdm) +int sr_configure_errgen(struct omap_sr *sr) { u32 sr_config, sr_errconfig, errconfig_offs; u32 vpboundint_en, vpboundint_st; u32 senp_en = 0, senn_en = 0; u8 senp_shift, senn_shift; - struct omap_sr *sr = _sr_lookup(voltdm); - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); - return PTR_ERR(sr); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); + return -EINVAL; } if (!sr->clk_length) @@ -414,22 +410,22 @@ int sr_configure_errgen(struct voltagedomain *voltdm) /** * sr_disable_errgen() - Disables SmartReflex AVS module's errgen component - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * disable the error generator module inside the smartreflex module. * * Returns 0 on success and error value in case of failure. */ -int sr_disable_errgen(struct voltagedomain *voltdm) +int sr_disable_errgen(struct omap_sr *sr) { u32 errconfig_offs; u32 vpboundint_en, vpboundint_st; - struct omap_sr *sr = _sr_lookup(voltdm); - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); - return PTR_ERR(sr); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); + return -EINVAL; } switch (sr->ip_type) { @@ -449,19 +445,24 @@ int sr_disable_errgen(struct voltagedomain *voltdm) return -EINVAL; } - /* Disable the interrupts of ERROR module */ - sr_modify_reg(sr, errconfig_offs, vpboundint_en | vpboundint_st, 0); - /* Disable the Sensor and errorgen */ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN, 0); + /* + * Disable the interrupts of ERROR module + * NOTE: modify is a read, modify,write - an implicit OCP barrier + * which is required is present here - sequencing is critical + * at this point (after errgen is disabled, vpboundint disable) + */ + sr_modify_reg(sr, errconfig_offs, vpboundint_en | vpboundint_st, 0); + return 0; } /** - * sr_configure_minmax() - Configures the smrtreflex to perform AVS using the + * sr_configure_minmax() - Configures the SmartReflex to perform AVS using the * minmaxavg module. - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * configure the minmaxavg module inside the smartreflex module. @@ -470,16 +471,16 @@ int sr_disable_errgen(struct voltagedomain *voltdm) * SR CLASS 2 can choose between ERROR module and MINMAXAVG * module. Returns 0 on success and error value in case of failure. */ -int sr_configure_minmax(struct voltagedomain *voltdm) +int sr_configure_minmax(struct omap_sr *sr) { u32 sr_config, sr_avgwt; u32 senp_en = 0, senn_en = 0; u8 senp_shift, senn_shift; - struct omap_sr *sr = _sr_lookup(voltdm); - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); - return PTR_ERR(sr); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); + return -EINVAL; } if (!sr->clk_length) @@ -546,7 +547,7 @@ int sr_configure_minmax(struct voltagedomain *voltdm) /** * sr_enable() - Enables the smartreflex module. - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: pointer to which the SR module to be configured belongs to. * @volt: The voltage at which the Voltage domain associated with * the smartreflex module is operating at. * This is required only to program the correct Ntarget value. @@ -555,16 +556,16 @@ int sr_configure_minmax(struct voltagedomain *voltdm) * enable a smartreflex module. Returns 0 on success. Returns error * value if the voltage passed is wrong or if ntarget value is wrong. */ -int sr_enable(struct voltagedomain *voltdm, unsigned long volt) +int sr_enable(struct omap_sr *sr, unsigned long volt) { struct omap_volt_data *volt_data; - struct omap_sr *sr = _sr_lookup(voltdm); struct omap_sr_nvalue_table *nvalue_row; int ret; - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); - return PTR_ERR(sr); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); + return -EINVAL; } volt_data = omap_voltage_get_voltdata(sr->voltdm, volt); @@ -606,17 +607,16 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) /** * sr_disable() - Disables the smartreflex module. - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: pointer to which the SR module to be configured belongs to. * * This API is to be called from the smartreflex class driver to * disable a smartreflex module. */ -void sr_disable(struct voltagedomain *voltdm) +void sr_disable(struct omap_sr *sr) { - struct omap_sr *sr = _sr_lookup(voltdm); - - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); return; } @@ -847,34 +847,33 @@ static int __init omap_sr_probe(struct platform_device *pdev) struct dentry *nvalue_dir; int i, ret = 0; - sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL); + sr_info = devm_kzalloc(&pdev->dev, sizeof(struct omap_sr), GFP_KERNEL); if (!sr_info) { dev_err(&pdev->dev, "%s: unable to allocate sr_info\n", __func__); return -ENOMEM; } + sr_info->name = devm_kzalloc(&pdev->dev, + SMARTREFLEX_NAME_LEN, GFP_KERNEL); + if (!sr_info->name) { + dev_err(&pdev->dev, "%s: unable to allocate SR instance name\n", + __func__); + return -ENOMEM; + } + platform_set_drvdata(pdev, sr_info); if (!pdata) { dev_err(&pdev->dev, "%s: platform data missing\n", __func__); - ret = -EINVAL; - goto err_free_devinfo; + return -EINVAL; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "%s: no mem resource\n", __func__); - ret = -ENODEV; - goto err_free_devinfo; - } - - mem = request_mem_region(mem->start, resource_size(mem), - dev_name(&pdev->dev)); - if (!mem) { - dev_err(&pdev->dev, "%s: no mem region\n", __func__); - ret = -EBUSY; - goto err_free_devinfo; + sr_info->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(sr_info->base)) { + dev_err(&pdev->dev, "%s: ioremap fail\n", __func__); + return PTR_ERR(sr_info->base); } irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -882,13 +881,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_irq_safe(&pdev->dev); - sr_info->name = kasprintf(GFP_KERNEL, "%s", pdata->name); - if (!sr_info->name) { - dev_err(&pdev->dev, "%s: Unable to alloc SR instance name\n", - __func__); - ret = -ENOMEM; - goto err_release_region; - } + snprintf(sr_info->name, SMARTREFLEX_NAME_LEN, "%s", pdata->name); sr_info->pdev = pdev; sr_info->srid = pdev->id; @@ -905,13 +898,6 @@ static int __init omap_sr_probe(struct platform_device *pdev) sr_info->autocomp_active = false; sr_info->ip_type = pdata->ip_type; - sr_info->base = ioremap(mem->start, resource_size(mem)); - if (!sr_info->base) { - dev_err(&pdev->dev, "%s: ioremap fail\n", __func__); - ret = -ENOMEM; - goto err_free_name; - } - if (irq) sr_info->irq = irq->start; @@ -927,7 +913,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) ret = sr_late_init(sr_info); if (ret) { pr_warning("%s: Error in SR late init\n", __func__); - goto err_iounmap; + goto err_list_del; } } @@ -938,7 +924,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) ret = PTR_ERR(sr_dbg_dir); pr_err("%s:sr debugfs dir creation failed(%d)\n", __func__, ret); - goto err_iounmap; + goto err_list_del; } } @@ -991,16 +977,8 @@ static int __init omap_sr_probe(struct platform_device *pdev) err_debugfs: debugfs_remove_recursive(sr_info->dbg_dir); -err_iounmap: +err_list_del: list_del(&sr_info->node); - iounmap(sr_info->base); -err_free_name: - kfree(sr_info->name); -err_release_region: - release_mem_region(mem->start, resource_size(mem)); -err_free_devinfo: - kfree(sr_info); - return ret; } @@ -1008,7 +986,6 @@ static int omap_sr_remove(struct platform_device *pdev) { struct omap_sr_data *pdata = pdev->dev.platform_data; struct omap_sr *sr_info; - struct resource *mem; if (!pdata) { dev_err(&pdev->dev, "%s: platform data missing\n", __func__); @@ -1027,13 +1004,8 @@ static int omap_sr_remove(struct platform_device *pdev) if (sr_info->dbg_dir) debugfs_remove_recursive(sr_info->dbg_dir); + pm_runtime_disable(&pdev->dev); list_del(&sr_info->node); - iounmap(sr_info->base); - kfree(sr_info->name); - kfree(sr_info); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, resource_size(mem)); - return 0; } @@ -1064,7 +1036,7 @@ static struct platform_driver smartreflex_driver = { .remove = omap_sr_remove, .shutdown = omap_sr_shutdown, .driver = { - .name = "smartreflex", + .name = DRIVER_NAME, }, }; diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 493948a38fca..8a7cb1f43046 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -406,7 +406,6 @@ static int pm8607_regulator_remove(struct platform_device *pdev) { struct pm8607_regulator_info *info = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(info->regulator); return 0; } diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8bb26446037e..f1e6ad98eeba 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -250,6 +250,15 @@ config REGULATOR_MAX77686 via I2C bus. The provided regulator is suitable for Exynos-4 chips to control VARM and VINT voltages. +config REGULATOR_MAX77693 + tristate "Maxim MAX77693 regulator" + depends on MFD_MAX77693 + help + This driver controls a Maxim 77693 regulator via I2C bus. + The regulators include two LDOs, 'SAFEOUT1', 'SAFEOUT2' + and one current regulator 'CHARGER'. This is suitable for + Exynos-4x12 chips. + config REGULATOR_PCAP tristate "Motorola PCAP2 regulator driver" depends on EZX_PCAP @@ -472,6 +481,16 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_TI_ABB + bool "TI Adaptive Body Bias on-chip LDO" + depends on ARCH_OMAP + help + Select this option to support Texas Instruments' on-chip Adaptive Body + Bias (ABB) LDO regulators. It is recommended that this option be + enabled on required TI SoC. Certain Operating Performance Points + on TI SoCs may be unstable without enabling this as it provides + device specific optimized bias to allow/optimize functionality. + config REGULATOR_VEXPRESS tristate "Versatile Express regulators" depends on VEXPRESS_CONFIG diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 47a34ff88f98..ba4a3cf3afec 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o -obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o ab8500-ext.o +obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o @@ -41,6 +41,7 @@ obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o +obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o @@ -63,6 +64,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index b4d45472aae6..02ff691cdb8b 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -16,9 +16,11 @@ #include <linux/kernel.h> #include <linux/err.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/ab8500.h> @@ -229,6 +231,28 @@ static unsigned int ab8500_ext_regulator_get_mode(struct regulator_dev *rdev) return ret; } +static int ab8500_ext_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + struct regulation_constraints *regu_constraints = rdev->constraints; + + if (!regu_constraints) { + dev_err(rdev_get_dev(rdev), "No regulator constraints\n"); + return -EINVAL; + } + + if (regu_constraints->min_uV == min_uV && + regu_constraints->max_uV == max_uV) + return 0; + + dev_err(rdev_get_dev(rdev), + "Requested min %duV max %duV != constrained min %duV max %duV\n", + min_uV, max_uV, + regu_constraints->min_uV, regu_constraints->max_uV); + + return -EINVAL; +} + static int ab8500_ext_list_voltage(struct regulator_dev *rdev, unsigned selector) { @@ -252,6 +276,7 @@ static struct regulator_ops ab8500_ext_regulator_ops = { .is_enabled = ab8500_ext_regulator_is_enabled, .set_mode = ab8500_ext_regulator_set_mode, .get_mode = ab8500_ext_regulator_get_mode, + .set_voltage = ab8500_ext_set_voltage, .list_voltage = ab8500_ext_list_voltage, }; @@ -310,18 +335,37 @@ static struct ab8500_ext_regulator_info }, }; -int ab8500_ext_regulator_init(struct platform_device *pdev) +static struct of_regulator_match ab8500_ext_regulator_match[] = { + { .name = "ab8500_ext1", .driver_data = (void *) AB8500_EXT_SUPPLY1, }, + { .name = "ab8500_ext2", .driver_data = (void *) AB8500_EXT_SUPPLY2, }, + { .name = "ab8500_ext3", .driver_data = (void *) AB8500_EXT_SUPPLY3, }, +}; + +static int ab8500_ext_regulator_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); struct ab8500_platform_data *ppdata; struct ab8500_regulator_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; struct regulator_config config = { }; int i, err; + if (np) { + err = of_regulator_match(&pdev->dev, np, + ab8500_ext_regulator_match, + ARRAY_SIZE(ab8500_ext_regulator_match)); + if (err < 0) { + dev_err(&pdev->dev, + "Error parsing regulator init data: %d\n", err); + return err; + } + } + if (!ab8500) { dev_err(&pdev->dev, "null mfd parent\n"); return -EINVAL; } + ppdata = dev_get_platdata(ab8500->dev); if (!ppdata) { dev_err(&pdev->dev, "null parent pdata\n"); @@ -362,8 +406,11 @@ int ab8500_ext_regulator_init(struct platform_device *pdev) pdata->ext_regulator[i].driver_data; config.dev = &pdev->dev; - config.init_data = &pdata->ext_regulator[i]; config.driver_data = info; + config.of_node = ab8500_ext_regulator_match[i].of_node; + config.init_data = (np) ? + ab8500_ext_regulator_match[i].init_data : + &pdata->ext_regulator[i]; /* register regulator with framework */ info->rdev = regulator_register(&info->desc, &config); @@ -386,7 +433,7 @@ int ab8500_ext_regulator_init(struct platform_device *pdev) return 0; } -void ab8500_ext_regulator_exit(struct platform_device *pdev) +static int ab8500_ext_regulator_remove(struct platform_device *pdev) { int i; @@ -399,7 +446,36 @@ void ab8500_ext_regulator_exit(struct platform_device *pdev) regulator_unregister(info->rdev); } + + return 0; +} + +static struct platform_driver ab8500_ext_regulator_driver = { + .probe = ab8500_ext_regulator_probe, + .remove = ab8500_ext_regulator_remove, + .driver = { + .name = "ab8500-ext-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_ext_regulator_init(void) +{ + int ret; + + ret = platform_driver_register(&ab8500_ext_regulator_driver); + if (ret) + pr_err("Failed to register ab8500 ext regulator: %d\n", ret); + + return ret; +} +subsys_initcall(ab8500_ext_regulator_init); + +static void __exit ab8500_ext_regulator_exit(void) +{ + platform_driver_unregister(&ab8500_ext_regulator_driver); } +module_exit(ab8500_ext_regulator_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>"); diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index a19045ee0ec4..603f192e84f1 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -719,6 +719,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .volt_table = ldo_vauxn_voltages, .enable_time = 200, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, @@ -741,6 +742,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .volt_table = ldo_vauxn_voltages, .enable_time = 200, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, @@ -763,6 +765,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), .volt_table = ldo_vaux3_voltages, .enable_time = 450, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, @@ -3156,22 +3159,12 @@ static int ab8500_regulator_probe(struct platform_device *pdev) return err; } - if (!is_ab8505(ab8500)) { - /* register external regulators (before Vaux1, 2 and 3) */ - err = ab8500_ext_regulator_init(pdev); - if (err) - return err; - } - /* register all regulators */ for (i = 0; i < abx500_regulator.info_size; i++) { err = ab8500_regulator_register(pdev, &pdata->regulator[i], i, NULL); - if (err < 0) { - if (!is_ab8505(ab8500)) - ab8500_ext_regulator_exit(pdev); + if (err < 0) return err; - } } return 0; @@ -3180,7 +3173,6 @@ static int ab8500_regulator_probe(struct platform_device *pdev) static int ab8500_regulator_remove(struct platform_device *pdev) { int i, err; - struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); for (i = 0; i < abx500_regulator.info_size; i++) { struct ab8500_regulator_info *info = NULL; @@ -3192,10 +3184,6 @@ static int ab8500_regulator_remove(struct platform_device *pdev) regulator_unregister(info->regulator); } - /* remove external regulators (after Vaux1, 2 and 3) */ - if (!is_ab8505(ab8500)) - ab8500_ext_regulator_exit(pdev); - /* remove regulator debug */ err = ab8500_regulator_debug_exit(pdev); if (err) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 815d6df8bd5f..288c75abc190 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2138,6 +2138,21 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector) EXPORT_SYMBOL_GPL(regulator_list_voltage); /** + * regulator_get_linear_step - return the voltage step size between VSEL values + * @regulator: regulator source + * + * Returns the voltage step size between VSEL values for linear + * regulators, or return 0 if the regulator isn't a linear regulator. + */ +unsigned int regulator_get_linear_step(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + return rdev->desc->uV_step; +} +EXPORT_SYMBOL_GPL(regulator_get_linear_step); + +/** * regulator_is_supported_voltage - check if a voltage range can be supported * * @regulator: Regulator to check. diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index d1e5bee2a26b..b99c49b9aff0 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -130,7 +130,7 @@ static int isl6271a_probe(struct i2c_client *i2c, if (i == 0) config.init_data = init_data; else - config.init_data = 0; + config.init_data = NULL; config.driver_data = pmic; pmic->rdev[i] = regulator_register(&isl_rd[i], &config); diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index d8af9e773310..3809b4381606 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -434,7 +434,7 @@ static int lp3971_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); + lp3971 = devm_kzalloc(&i2c->dev, sizeof(struct lp3971), GFP_KERNEL); if (lp3971 == NULL) return -ENOMEM; @@ -449,19 +449,15 @@ static int lp3971_i2c_probe(struct i2c_client *i2c, ret = -ENODEV; if (ret < 0) { dev_err(&i2c->dev, "failed to detect device\n"); - goto err_detect; + return ret; } ret = setup_regulators(lp3971, pdata); if (ret < 0) - goto err_detect; + return ret; i2c_set_clientdata(i2c, lp3971); return 0; - -err_detect: - kfree(lp3971); - return ret; } static int lp3971_i2c_remove(struct i2c_client *i2c) @@ -473,7 +469,6 @@ static int lp3971_i2c_remove(struct i2c_client *i2c) regulator_unregister(lp3971->rdev[i]); kfree(lp3971->rdev); - kfree(lp3971); return 0; } diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c index 61e4cf9edf6e..573024039ca0 100644 --- a/drivers/regulator/lp3972.c +++ b/drivers/regulator/lp3972.c @@ -528,7 +528,7 @@ static int lp3972_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - lp3972 = kzalloc(sizeof(struct lp3972), GFP_KERNEL); + lp3972 = devm_kzalloc(&i2c->dev, sizeof(struct lp3972), GFP_KERNEL); if (!lp3972) return -ENOMEM; @@ -546,19 +546,15 @@ static int lp3972_i2c_probe(struct i2c_client *i2c, } if (ret < 0) { dev_err(&i2c->dev, "failed to detect device. ret = %d\n", ret); - goto err_detect; + return ret; } ret = setup_regulators(lp3972, pdata); if (ret < 0) - goto err_detect; + return ret; i2c_set_clientdata(i2c, lp3972); return 0; - -err_detect: - kfree(lp3972); - return ret; } static int lp3972_i2c_remove(struct i2c_client *i2c) @@ -569,7 +565,6 @@ static int lp3972_i2c_remove(struct i2c_client *i2c) for (i = 0; i < lp3972->num_regulators; i++) regulator_unregister(lp3972->rdev[i]); kfree(lp3972->rdev); - kfree(lp3972); return 0; } diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index f5fc4a142cdf..b16336bcd4d4 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -18,6 +18,9 @@ #include <linux/regulator/lp872x.h> #include <linux/regulator/driver.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regulator/of_regulator.h> /* Registers : LP8720/8725 shared */ #define LP872X_GENERAL_CFG 0x00 @@ -723,8 +726,8 @@ static int lp872x_init_dvs(struct lp872x *lp) gpio = dvs->gpio; if (!gpio_is_valid(gpio)) { - dev_err(lp->dev, "invalid gpio: %d\n", gpio); - return -EINVAL; + dev_warn(lp->dev, "invalid gpio: %d\n", gpio); + goto set_default_dvs_mode; } pinstate = dvs->init_state; @@ -829,6 +832,103 @@ static const struct regmap_config lp872x_regmap_config = { .max_register = MAX_REGISTERS, }; +#ifdef CONFIG_OF + +#define LP872X_VALID_OPMODE (REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL) + +static struct of_regulator_match lp8720_matches[] = { + { .name = "ldo1", .driver_data = (void *)LP8720_ID_LDO1, }, + { .name = "ldo2", .driver_data = (void *)LP8720_ID_LDO2, }, + { .name = "ldo3", .driver_data = (void *)LP8720_ID_LDO3, }, + { .name = "ldo4", .driver_data = (void *)LP8720_ID_LDO4, }, + { .name = "ldo5", .driver_data = (void *)LP8720_ID_LDO5, }, + { .name = "buck", .driver_data = (void *)LP8720_ID_BUCK, }, +}; + +static struct of_regulator_match lp8725_matches[] = { + { .name = "ldo1", .driver_data = (void *)LP8725_ID_LDO1, }, + { .name = "ldo2", .driver_data = (void *)LP8725_ID_LDO2, }, + { .name = "ldo3", .driver_data = (void *)LP8725_ID_LDO3, }, + { .name = "ldo4", .driver_data = (void *)LP8725_ID_LDO4, }, + { .name = "ldo5", .driver_data = (void *)LP8725_ID_LDO5, }, + { .name = "lilo1", .driver_data = (void *)LP8725_ID_LILO1, }, + { .name = "lilo2", .driver_data = (void *)LP8725_ID_LILO2, }, + { .name = "buck1", .driver_data = (void *)LP8725_ID_BUCK1, }, + { .name = "buck2", .driver_data = (void *)LP8725_ID_BUCK2, }, +}; + +static struct lp872x_platform_data +*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which) +{ + struct device_node *np = dev->of_node; + struct lp872x_platform_data *pdata; + struct of_regulator_match *match; + struct regulator_init_data *d; + int num_matches; + int count; + int i; + u8 dvs_state; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto out; + + of_property_read_u8(np, "ti,general-config", &pdata->general_config); + if (of_find_property(np, "ti,update-config", NULL)) + pdata->update_config = true; + + pdata->dvs = devm_kzalloc(dev, sizeof(struct lp872x_dvs), GFP_KERNEL); + if (!pdata->dvs) + goto out; + + pdata->dvs->gpio = of_get_named_gpio(np, "ti,dvs-gpio", 0); + of_property_read_u8(np, "ti,dvs-vsel", (u8 *)&pdata->dvs->vsel); + of_property_read_u8(np, "ti,dvs-state", &dvs_state); + pdata->dvs->init_state = dvs_state ? DVS_HIGH : DVS_LOW; + + if (of_get_child_count(np) == 0) + goto out; + + switch (which) { + case LP8720: + match = lp8720_matches; + num_matches = ARRAY_SIZE(lp8720_matches); + break; + case LP8725: + match = lp8725_matches; + num_matches = ARRAY_SIZE(lp8725_matches); + break; + default: + goto out; + } + + count = of_regulator_match(dev, np, match, num_matches); + if (count <= 0) + goto out; + + for (i = 0; i < num_matches; i++) { + pdata->regulator_data[i].id = (int)match[i].driver_data; + pdata->regulator_data[i].init_data = match[i].init_data; + + /* Operation mode configuration for buck/buck1/buck2 */ + if (strncmp(match[i].name, "buck", 4)) + continue; + + d = pdata->regulator_data[i].init_data; + d->constraints.valid_modes_mask |= LP872X_VALID_OPMODE; + d->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE; + } +out: + return pdata; +} +#else +static struct lp872x_platform_data +*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which) +{ + return NULL; +} +#endif + static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) { struct lp872x *lp; @@ -838,6 +938,10 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) [LP8725] = LP8725_NUM_REGULATORS, }; + if (cl->dev.of_node) + cl->dev.platform_data = lp872x_populate_pdata_from_dt(&cl->dev, + (enum lp872x_id)id->driver_data); + lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); if (!lp) goto err_mem; @@ -882,6 +986,13 @@ static int lp872x_remove(struct i2c_client *cl) return 0; } +static const struct of_device_id lp872x_dt_ids[] = { + { .compatible = "ti,lp8720", }, + { .compatible = "ti,lp8725", }, + { } +}; +MODULE_DEVICE_TABLE(of, lp872x_dt_ids); + static const struct i2c_device_id lp872x_ids[] = { {"lp8720", LP8720}, {"lp8725", LP8725}, @@ -893,6 +1004,7 @@ static struct i2c_driver lp872x_driver = { .driver = { .name = "lp872x", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(lp872x_dt_ids), }, .probe = lp872x_probe, .remove = lp872x_remove, diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c index f0f6ea05065b..d9e38b4c2adc 100644 --- a/drivers/regulator/lp8755.c +++ b/drivers/regulator/lp8755.c @@ -19,7 +19,6 @@ #include <linux/interrupt.h> #include <linux/gpio.h> #include <linux/regmap.h> -#include <linux/delay.h> #include <linux/uaccess.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c index eb1e1e88ae51..0b015f2a7fd9 100644 --- a/drivers/regulator/lp8788-buck.c +++ b/drivers/regulator/lp8788-buck.c @@ -533,7 +533,6 @@ static int lp8788_buck_remove(struct platform_device *pdev) { struct lp8788_buck *buck = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(buck->regulator); return 0; diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index 0ce2c4c194b3..0527d87c6dd5 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -561,7 +561,6 @@ static int lp8788_dldo_remove(struct platform_device *pdev) { struct lp8788_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(ldo->regulator); return 0; @@ -622,7 +621,6 @@ static int lp8788_aldo_remove(struct platform_device *pdev) { struct lp8788_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(ldo->regulator); return 0; diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 20935b1a6ed4..f563057e5690 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -24,7 +24,6 @@ #include <linux/kernel.h> #include <linux/bug.h> -#include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/slab.h> diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c new file mode 100644 index 000000000000..ce4b96c15eba --- /dev/null +++ b/drivers/regulator/max77693.c @@ -0,0 +1,322 @@ +/* + * max77693.c - Regulator driver for the Maxim 77693 + * + * Copyright (C) 2013 Samsung Electronics + * Jonghwa Lee <jonghwa3.lee@samsung.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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max77686.c + */ + +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-private.h> +#include <linux/regulator/of_regulator.h> + +#define CHGIN_ILIM_STEP_20mA 20000 + +struct max77693_pmic_dev { + struct device *dev; + struct max77693_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; +}; + +/* CHARGER regulator ops */ +/* CHARGER regulator uses two bits for enabling */ +static int max77693_chg_is_enabled(struct regulator_dev *rdev) +{ + int ret; + u8 val; + + ret = max77693_read_reg(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret) + return ret; + + return (val & rdev->desc->enable_mask) == rdev->desc->enable_mask; +} + +/* + * CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA + * 0x00, 0x01, 0x2, 0x03 = 60 mA + * 0x04 ~ 0x7E = (60 + (X - 3) * 20) mA + */ +static int max77693_chg_get_current_limit(struct regulator_dev *rdev) +{ + unsigned int chg_min_uA = rdev->constraints->min_uA; + unsigned int chg_max_uA = rdev->constraints->max_uA; + u8 reg, sel; + unsigned int val; + int ret; + + ret = max77693_read_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_09, ®); + if (ret < 0) + return ret; + + sel = reg & CHG_CNFG_09_CHGIN_ILIM_MASK; + + /* the first four codes for charger current are all 60mA */ + if (sel <= 3) + sel = 0; + else + sel -= 3; + + val = chg_min_uA + CHGIN_ILIM_STEP_20mA * sel; + if (val > chg_max_uA) + return -EINVAL; + + return val; +} + +static int max77693_chg_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + unsigned int chg_min_uA = rdev->constraints->min_uA; + int sel = 0; + + while (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel < min_uA) + sel++; + + if (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel > max_uA) + return -EINVAL; + + /* the first four codes for charger current are all 60mA */ + sel += 3; + + return max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_09, sel); +} +/* end of CHARGER regulator ops */ + +static const unsigned int max77693_safeout_table[] = { + 4850000, + 4900000, + 4950000, + 3300000, +}; + +static struct regulator_ops max77693_safeout_ops = { + .list_voltage = regulator_list_voltage_table, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static struct regulator_ops max77693_charger_ops = { + .is_enabled = max77693_chg_is_enabled, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_current_limit = max77693_chg_get_current_limit, + .set_current_limit = max77693_chg_set_current_limit, +}; + +#define regulator_desc_esafeout(_num) { \ + .name = "ESAFEOUT"#_num, \ + .id = MAX77693_ESAFEOUT##_num, \ + .n_voltages = 4, \ + .ops = &max77693_safeout_ops, \ + .type = REGULATOR_VOLTAGE, \ + .volt_table = max77693_safeout_table, \ + .vsel_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \ + .vsel_mask = SAFEOUT_CTRL_SAFEOUT##_num##_MASK, \ + .enable_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \ + .enable_mask = SAFEOUT_CTRL_ENSAFEOUT##_num##_MASK , \ +} + +static struct regulator_desc regulators[] = { + regulator_desc_esafeout(1), + regulator_desc_esafeout(2), + { + .name = "CHARGER", + .id = MAX77693_CHARGER, + .ops = &max77693_charger_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + .enable_reg = MAX77693_CHG_REG_CHG_CNFG_00, + .enable_mask = CHG_CNFG_00_CHG_MASK | + CHG_CNFG_00_BUCK_MASK, + }, +}; + +#ifdef CONFIG_OF +static int max77693_pmic_dt_parse_rdata(struct device *dev, + struct max77693_regulator_data **rdata) +{ + struct device_node *np; + struct of_regulator_match *rmatch; + struct max77693_regulator_data *tmp; + int i, matched = 0; + + np = of_find_node_by_name(dev->parent->of_node, "regulators"); + if (!np) + return -EINVAL; + + rmatch = devm_kzalloc(dev, + sizeof(*rmatch) * ARRAY_SIZE(regulators), GFP_KERNEL); + if (!rmatch) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(regulators); i++) + rmatch[i].name = regulators[i].name; + + matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(regulators)); + if (matched <= 0) + return matched; + *rdata = devm_kzalloc(dev, sizeof(**rdata) * matched, GFP_KERNEL); + if (!(*rdata)) + return -ENOMEM; + + tmp = *rdata; + + for (i = 0; i < matched; i++) { + tmp->initdata = rmatch[i].init_data; + tmp->of_node = rmatch[i].of_node; + tmp->id = regulators[i].id; + tmp++; + } + + return matched; +} +#else +static int max77693_pmic_dt_parse_rdata(struct device *dev, + struct max77693_regulator_data **rdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int max77693_pmic_init_rdata(struct device *dev, + struct max77693_regulator_data **rdata) +{ + struct max77693_platform_data *pdata; + int num_regulators = 0; + + pdata = dev_get_platdata(dev->parent); + if (pdata) { + *rdata = pdata->regulators; + num_regulators = pdata->num_regulators; + } + + if (!(*rdata) && dev->parent->of_node) + num_regulators = max77693_pmic_dt_parse_rdata(dev, rdata); + + return num_regulators; +} + +static int max77693_pmic_probe(struct platform_device *pdev) +{ + struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max77693_pmic_dev *max77693_pmic; + struct max77693_regulator_data *rdata = NULL; + int num_rdata, i, ret; + struct regulator_config config; + + num_rdata = max77693_pmic_init_rdata(&pdev->dev, &rdata); + if (!rdata || num_rdata <= 0) { + dev_err(&pdev->dev, "No init data supplied.\n"); + return -ENODEV; + } + + max77693_pmic = devm_kzalloc(&pdev->dev, + sizeof(struct max77693_pmic_dev), + GFP_KERNEL); + if (!max77693_pmic) + return -ENOMEM; + + max77693_pmic->rdev = devm_kzalloc(&pdev->dev, + sizeof(struct regulator_dev *) * num_rdata, + GFP_KERNEL); + if (!max77693_pmic->rdev) + return -ENOMEM; + + max77693_pmic->dev = &pdev->dev; + max77693_pmic->iodev = iodev; + max77693_pmic->num_regulators = num_rdata; + + config.dev = &pdev->dev; + config.regmap = iodev->regmap; + config.driver_data = max77693_pmic; + platform_set_drvdata(pdev, max77693_pmic); + + for (i = 0; i < max77693_pmic->num_regulators; i++) { + int id = rdata[i].id; + + config.init_data = rdata[i].initdata; + config.of_node = rdata[i].of_node; + + max77693_pmic->rdev[i] = regulator_register(®ulators[id], + &config); + if (IS_ERR(max77693_pmic->rdev[i])) { + ret = PTR_ERR(max77693_pmic->rdev[i]); + dev_err(max77693_pmic->dev, + "Failed to initialize regulator-%d\n", id); + max77693_pmic->rdev[i] = NULL; + goto err; + } + } + + return 0; + err: + while (--i >= 0) + regulator_unregister(max77693_pmic->rdev[i]); + + return ret; +} + +static int max77693_pmic_remove(struct platform_device *pdev) +{ + struct max77693_pmic_dev *max77693_pmic = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max77693_pmic->rdev; + int i; + + for (i = 0; i < max77693_pmic->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + return 0; +} + +static const struct platform_device_id max77693_pmic_id[] = { + {"max77693-pmic", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(platform, max77693_pmic_id); + +static struct platform_driver max77693_pmic_driver = { + .driver = { + .name = "max77693-pmic", + .owner = THIS_MODULE, + }, + .probe = max77693_pmic_probe, + .remove = max77693_pmic_remove, + .id_table = max77693_pmic_id, +}; + +module_platform_driver(max77693_pmic_driver); + +MODULE_DESCRIPTION("MAXIM MAX77693 regulator driver"); +MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index 3597da8f0dca..e6d54a546d36 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -327,7 +327,6 @@ static int max8925_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index adb1414e5e37..0c5195a842e2 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -26,10 +26,12 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/err.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/max8973-regulator.h> +#include <linux/regulator/of_regulator.h> #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/slab.h> @@ -100,6 +102,7 @@ struct max8973_chip { int curr_vout_reg; int curr_gpio_val; bool valid_dvs_gpio; + struct regulator_ops ops; }; /* @@ -240,7 +243,7 @@ static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev) REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; } -static struct regulator_ops max8973_dcdc_ops = { +static const struct regulator_ops max8973_dcdc_ops = { .get_voltage_sel = max8973_dcdc_get_voltage_sel, .set_voltage_sel = max8973_dcdc_set_voltage_sel, .list_voltage = regulator_list_voltage_linear, @@ -369,7 +372,8 @@ static int max8973_probe(struct i2c_client *client, int ret; pdata = client->dev.platform_data; - if (!pdata) { + + if (!pdata && !client->dev.of_node) { dev_err(&client->dev, "No Platform data"); return -EIO; } @@ -388,30 +392,36 @@ static int max8973_probe(struct i2c_client *client, } i2c_set_clientdata(client, max); + max->ops = max8973_dcdc_ops; max->dev = &client->dev; max->desc.name = id->name; max->desc.id = 0; - max->desc.ops = &max8973_dcdc_ops; + max->desc.ops = &max->ops; max->desc.type = REGULATOR_VOLTAGE; max->desc.owner = THIS_MODULE; max->desc.min_uV = MAX8973_MIN_VOLATGE; max->desc.uV_step = MAX8973_VOLATGE_STEP; max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE; - if (!pdata->enable_ext_control) { + if (!pdata || !pdata->enable_ext_control) { max->desc.enable_reg = MAX8973_VOUT; max->desc.enable_mask = MAX8973_VOUT_ENABLE; - max8973_dcdc_ops.enable = regulator_enable_regmap; - max8973_dcdc_ops.disable = regulator_disable_regmap; - max8973_dcdc_ops.is_enabled = regulator_is_enabled_regmap; + max->ops.enable = regulator_enable_regmap; + max->ops.disable = regulator_disable_regmap; + max->ops.is_enabled = regulator_is_enabled_regmap; + } + + if (pdata) { + max->dvs_gpio = pdata->dvs_gpio; + max->enable_external_control = pdata->enable_ext_control; + max->curr_gpio_val = pdata->dvs_def_state; + max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state; + } else { + max->dvs_gpio = -EINVAL; + max->curr_vout_reg = MAX8973_VOUT; } - max->enable_external_control = pdata->enable_ext_control; - max->dvs_gpio = pdata->dvs_gpio; - max->curr_gpio_val = pdata->dvs_def_state; - max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state; max->lru_index[0] = max->curr_vout_reg; - max->valid_dvs_gpio = false; if (gpio_is_valid(max->dvs_gpio)) { int gpio_flags; @@ -437,16 +447,21 @@ static int max8973_probe(struct i2c_client *client, max->lru_index[i] = i; max->lru_index[0] = max->curr_vout_reg; max->lru_index[max->curr_vout_reg] = 0; + } else { + max->valid_dvs_gpio = false; } - ret = max8973_init_dcdc(max, pdata); - if (ret < 0) { - dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret); - return ret; + if (pdata) { + ret = max8973_init_dcdc(max, pdata); + if (ret < 0) { + dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret); + return ret; + } } config.dev = &client->dev; - config.init_data = pdata->reg_init_data; + config.init_data = pdata ? pdata->reg_init_data : + of_get_regulator_init_data(&client->dev, client->dev.of_node); config.driver_data = max; config.of_node = client->dev.of_node; config.regmap = max->regmap; diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index fdf7f0a09090..5ff99d2703db 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -466,8 +466,6 @@ static int mc13783_regulator_remove(struct platform_device *pdev) struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); int i; - platform_set_drvdata(pdev, NULL); - for (i = 0; i < priv->num_regulators; i++) regulator_unregister(priv->regulators[i]); diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index b716283a8760..1037e07937cf 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -636,8 +636,6 @@ static int mc13892_regulator_remove(struct platform_device *pdev) struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); int i; - platform_set_drvdata(pdev, NULL); - for (i = 0; i < priv->num_regulators; i++) regulator_unregister(priv->regulators[i]); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 66ca769287ab..f3c8f8f9dc39 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -61,6 +61,9 @@ static void of_get_regulation_constraints(struct device_node *np, else /* status change should be possible if not always on. */ constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; + if (of_property_read_bool(np, "regulator-allow-bypass")) + constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS; + ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); if (ramp_delay) constraints->ramp_delay = be32_to_cpu(*ramp_delay); diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c index 4899342f1fc1..1a73a297fe73 100644 --- a/drivers/regulator/pcap-regulator.c +++ b/drivers/regulator/pcap-regulator.c @@ -260,7 +260,6 @@ static int pcap_regulator_remove(struct platform_device *pdev) struct regulator_dev *rdev = platform_get_drvdata(pdev); regulator_unregister(rdev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 534075e13d6d..54df9f7cb504 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -106,7 +106,6 @@ static int pcf50633_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index cd9ea2ea1826..c9f16e17920f 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -12,7 +12,6 @@ */ #include <linux/bug.h> -#include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/slab.h> diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c new file mode 100644 index 000000000000..3753ed05e719 --- /dev/null +++ b/drivers/regulator/ti-abb-regulator.c @@ -0,0 +1,910 @@ +/* + * Texas Instruments SoC Adaptive Body Bias(ABB) Regulator + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Mike Turquette <mturquette@ti.com> + * + * Copyright (C) 2012-2013 Texas Instruments, Inc. + * Andrii Tseglytskyi <andrii.tseglytskyi@ti.com> + * Nishanth Menon <nm@ti.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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +/* + * ABB LDO operating states: + * NOMINAL_OPP: bypasses the ABB LDO + * FAST_OPP: sets ABB LDO to Forward Body-Bias + * SLOW_OPP: sets ABB LDO to Reverse Body-Bias + */ +#define TI_ABB_NOMINAL_OPP 0 +#define TI_ABB_FAST_OPP 1 +#define TI_ABB_SLOW_OPP 3 + +/** + * struct ti_abb_info - ABB information per voltage setting + * @opp_sel: one of TI_ABB macro + * @vset: (optional) vset value that LDOVBB needs to be overriden with. + * + * Array of per voltage entries organized in the same order as regulator_desc's + * volt_table list. (selector is used to index from this array) + */ +struct ti_abb_info { + u32 opp_sel; + u32 vset; +}; + +/** + * struct ti_abb_reg - Register description for ABB block + * @setup_reg: setup register offset from base + * @control_reg: control register offset from base + * @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask + * @fbb_sel_mask: setup register- FBB sel mask + * @rbb_sel_mask: setup register- RBB sel mask + * @sr2_en_mask: setup register- enable mask + * @opp_change_mask: control register - mask to trigger LDOVBB change + * @opp_sel_mask: control register - mask for mode to operate + */ +struct ti_abb_reg { + u32 setup_reg; + u32 control_reg; + + /* Setup register fields */ + u32 sr2_wtcnt_value_mask; + u32 fbb_sel_mask; + u32 rbb_sel_mask; + u32 sr2_en_mask; + + /* Control register fields */ + u32 opp_change_mask; + u32 opp_sel_mask; +}; + +/** + * struct ti_abb - ABB instance data + * @rdesc: regulator descriptor + * @clk: clock(usually sysclk) supplying ABB block + * @base: base address of ABB block + * @int_base: interrupt register base address + * @efuse_base: (optional) efuse base address for ABB modes + * @ldo_base: (optional) LDOVBB vset override base address + * @regs: pointer to struct ti_abb_reg for ABB block + * @txdone_mask: mask on int_base for tranxdone interrupt + * @ldovbb_override_mask: mask to ldo_base for overriding default LDO VBB + * vset with value from efuse + * @ldovbb_vset_mask: mask to ldo_base for providing the VSET override + * @info: array to per voltage ABB configuration + * @current_info_idx: current index to info + * @settling_time: SoC specific settling time for LDO VBB + */ +struct ti_abb { + struct regulator_desc rdesc; + struct clk *clk; + void __iomem *base; + void __iomem *int_base; + void __iomem *efuse_base; + void __iomem *ldo_base; + + const struct ti_abb_reg *regs; + u32 txdone_mask; + u32 ldovbb_override_mask; + u32 ldovbb_vset_mask; + + struct ti_abb_info *info; + int current_info_idx; + + u32 settling_time; +}; + +/** + * ti_abb_rmw() - handy wrapper to set specific register bits + * @mask: mask for register field + * @value: value shifted to mask location and written + * @offset: offset of register + * @base: base address + * + * Return: final register value (may be unused) + */ +static inline u32 ti_abb_rmw(u32 mask, u32 value, u32 offset, + void __iomem *base) +{ + u32 val; + + val = readl(base + offset); + val &= ~mask; + val |= (value << __ffs(mask)) & mask; + writel(val, base + offset); + + return val; +} + +/** + * ti_abb_check_txdone() - handy wrapper to check ABB tranxdone status + * @abb: pointer to the abb instance + * + * Return: true or false + */ +static inline bool ti_abb_check_txdone(const struct ti_abb *abb) +{ + return !!(readl(abb->int_base) & abb->txdone_mask); +} + +/** + * ti_abb_clear_txdone() - handy wrapper to clear ABB tranxdone status + * @abb: pointer to the abb instance + */ +static inline void ti_abb_clear_txdone(const struct ti_abb *abb) +{ + writel(abb->txdone_mask, abb->int_base); +}; + +/** + * ti_abb_wait_tranx() - waits for ABB tranxdone event + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. + */ +static int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb) +{ + int timeout = 0; + bool status; + + while (timeout++ <= abb->settling_time) { + status = ti_abb_check_txdone(abb); + if (status) + break; + + udelay(1); + } + + if (timeout > abb->settling_time) { + dev_warn_ratelimited(dev, + "%s:TRANXDONE timeout(%duS) int=0x%08x\n", + __func__, timeout, readl(abb->int_base)); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * ti_abb_clear_all_txdone() - clears ABB tranxdone event + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. + */ +static int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb) +{ + int timeout = 0; + bool status; + + while (timeout++ <= abb->settling_time) { + ti_abb_clear_txdone(abb); + + status = ti_abb_check_txdone(abb); + if (!status) + break; + + udelay(1); + } + + if (timeout > abb->settling_time) { + dev_warn_ratelimited(dev, + "%s:TRANXDONE timeout(%duS) int=0x%08x\n", + __func__, timeout, readl(abb->int_base)); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * ti_abb_program_ldovbb() - program LDOVBB register for override value + * @dev: device + * @abb: pointer to the abb instance + * @info: ABB info to program + */ +static void ti_abb_program_ldovbb(struct device *dev, const struct ti_abb *abb, + struct ti_abb_info *info) +{ + u32 val; + + val = readl(abb->ldo_base); + /* clear up previous values */ + val &= ~(abb->ldovbb_override_mask | abb->ldovbb_vset_mask); + + switch (info->opp_sel) { + case TI_ABB_SLOW_OPP: + case TI_ABB_FAST_OPP: + val |= abb->ldovbb_override_mask; + val |= info->vset << __ffs(abb->ldovbb_vset_mask); + break; + } + + writel(val, abb->ldo_base); +} + +/** + * ti_abb_set_opp() - Setup ABB and LDO VBB for required bias + * @rdev: regulator device + * @abb: pointer to the abb instance + * @info: ABB info to program + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, + struct ti_abb_info *info) +{ + const struct ti_abb_reg *regs = abb->regs; + struct device *dev = &rdev->dev; + int ret; + + ret = ti_abb_clear_all_txdone(dev, abb); + if (ret) + goto out; + + ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, regs->setup_reg, + abb->base); + + switch (info->opp_sel) { + case TI_ABB_SLOW_OPP: + ti_abb_rmw(regs->rbb_sel_mask, 1, regs->setup_reg, abb->base); + break; + case TI_ABB_FAST_OPP: + ti_abb_rmw(regs->fbb_sel_mask, 1, regs->setup_reg, abb->base); + break; + } + + /* program next state of ABB ldo */ + ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg, + abb->base); + + /* program LDO VBB vset override if needed */ + if (abb->ldo_base) + ti_abb_program_ldovbb(dev, abb, info); + + /* Initiate ABB ldo change */ + ti_abb_rmw(regs->opp_change_mask, 1, regs->control_reg, abb->base); + + /* Wait for ABB LDO to complete transition to new Bias setting */ + ret = ti_abb_wait_txdone(dev, abb); + if (ret) + goto out; + + ret = ti_abb_clear_all_txdone(dev, abb); + if (ret) + goto out; + +out: + return ret; +} + +/** + * ti_abb_set_voltage_sel() - regulator accessor function to set ABB LDO + * @rdev: regulator device + * @sel: selector to index into required ABB LDO settings (maps to + * regulator descriptor's volt_table) + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) +{ + const struct regulator_desc *desc = rdev->desc; + struct ti_abb *abb = rdev_get_drvdata(rdev); + struct device *dev = &rdev->dev; + struct ti_abb_info *info, *oinfo; + int ret = 0; + + if (!abb) { + dev_err_ratelimited(dev, "%s: No regulator drvdata\n", + __func__); + return -ENODEV; + } + + if (!desc->n_voltages || !abb->info) { + dev_err_ratelimited(dev, + "%s: No valid voltage table entries?\n", + __func__); + return -EINVAL; + } + + if (sel >= desc->n_voltages) { + dev_err(dev, "%s: sel idx(%d) >= n_voltages(%d)\n", __func__, + sel, desc->n_voltages); + return -EINVAL; + } + + /* If we are in the same index as we were, nothing to do here! */ + if (sel == abb->current_info_idx) { + dev_dbg(dev, "%s: Already at sel=%d\n", __func__, sel); + return ret; + } + + /* If data is exactly the same, then just update index, no change */ + info = &abb->info[sel]; + oinfo = &abb->info[abb->current_info_idx]; + if (!memcmp(info, oinfo, sizeof(*info))) { + dev_dbg(dev, "%s: Same data new idx=%d, old idx=%d\n", __func__, + sel, abb->current_info_idx); + goto out; + } + + ret = ti_abb_set_opp(rdev, abb, info); + +out: + if (!ret) + abb->current_info_idx = sel; + else + dev_err_ratelimited(dev, + "%s: Volt[%d] idx[%d] mode[%d] Fail(%d)\n", + __func__, desc->volt_table[sel], sel, + info->opp_sel, ret); + return ret; +} + +/** + * ti_abb_get_voltage_sel() - Regulator accessor to get current ABB LDO setting + * @rdev: regulator device + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_get_voltage_sel(struct regulator_dev *rdev) +{ + const struct regulator_desc *desc = rdev->desc; + struct ti_abb *abb = rdev_get_drvdata(rdev); + struct device *dev = &rdev->dev; + + if (!abb) { + dev_err_ratelimited(dev, "%s: No regulator drvdata\n", + __func__); + return -ENODEV; + } + + if (!desc->n_voltages || !abb->info) { + dev_err_ratelimited(dev, + "%s: No valid voltage table entries?\n", + __func__); + return -EINVAL; + } + + if (abb->current_info_idx >= (int)desc->n_voltages) { + dev_err(dev, "%s: Corrupted data? idx(%d) >= n_voltages(%d)\n", + __func__, abb->current_info_idx, desc->n_voltages); + return -EINVAL; + } + + return abb->current_info_idx; +} + +/** + * ti_abb_init_timings() - setup ABB clock timing for the current platform + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 if timing is updated, else returns error result. + */ +static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) +{ + u32 clock_cycles; + u32 clk_rate, sr2_wt_cnt_val, cycle_rate; + const struct ti_abb_reg *regs = abb->regs; + int ret; + char *pname = "ti,settling-time"; + + /* read device tree properties */ + ret = of_property_read_u32(dev->of_node, pname, &abb->settling_time); + if (ret) { + dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret); + return ret; + } + + /* ABB LDO cannot be settle in 0 time */ + if (!abb->settling_time) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + return -EINVAL; + } + + pname = "ti,clock-cycles"; + ret = of_property_read_u32(dev->of_node, pname, &clock_cycles); + if (ret) { + dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret); + return ret; + } + /* ABB LDO cannot be settle in 0 clock cycles */ + if (!clock_cycles) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + return -EINVAL; + } + + abb->clk = devm_clk_get(dev, NULL); + if (IS_ERR(abb->clk)) { + ret = PTR_ERR(abb->clk); + dev_err(dev, "%s: Unable to get clk(%d)\n", __func__, ret); + return ret; + } + + /* + * SR2_WTCNT_VALUE is the settling time for the ABB ldo after a + * transition and must be programmed with the correct time at boot. + * The value programmed into the register is the number of SYS_CLK + * clock cycles that match a given wall time profiled for the ldo. + * This value depends on: + * settling time of ldo in micro-seconds (varies per OMAP family) + * # of clock cycles per SYS_CLK period (varies per OMAP family) + * the SYS_CLK frequency in MHz (varies per board) + * The formula is: + * + * ldo settling time (in micro-seconds) + * SR2_WTCNT_VALUE = ------------------------------------------ + * (# system clock cycles) * (sys_clk period) + * + * Put another way: + * + * SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate)) + * + * To avoid dividing by zero multiply both "# clock cycles" and + * "settling time" by 10 such that the final result is the one we want. + */ + + /* Convert SYS_CLK rate to MHz & prevent divide by zero */ + clk_rate = DIV_ROUND_CLOSEST(clk_get_rate(abb->clk), 1000000); + + /* Calculate cycle rate */ + cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate); + + /* Calulate SR2_WTCNT_VALUE */ + sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate); + + dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, + clk_get_rate(abb->clk), sr2_wt_cnt_val); + + ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, regs->setup_reg, + abb->base); + + return 0; +} + +/** + * ti_abb_init_table() - Initialize ABB table from device tree + * @dev: device + * @abb: pointer to the abb instance + * @rinit_data: regulator initdata + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, + struct regulator_init_data *rinit_data) +{ + struct ti_abb_info *info; + const struct property *prop; + const __be32 *abb_info; + const u32 num_values = 6; + char *pname = "ti,abb_info"; + u32 num_entries, i; + unsigned int *volt_table; + int min_uV = INT_MAX, max_uV = 0; + struct regulation_constraints *c = &rinit_data->constraints; + + prop = of_find_property(dev->of_node, pname, NULL); + if (!prop) { + dev_err(dev, "No '%s' property?\n", pname); + return -ENODEV; + } + + if (!prop->value) { + dev_err(dev, "Empty '%s' property?\n", pname); + return -ENODATA; + } + + /* + * Each abb_info is a set of n-tuple, where n is num_values, consisting + * of voltage and a set of detection logic for ABB information for that + * voltage to apply. + */ + num_entries = prop->length / sizeof(u32); + if (!num_entries || (num_entries % num_values)) { + dev_err(dev, "All '%s' list entries need %d vals\n", pname, + num_values); + return -EINVAL; + } + num_entries /= num_values; + + info = devm_kzalloc(dev, sizeof(*info) * num_entries, GFP_KERNEL); + if (!info) { + dev_err(dev, "Can't allocate info table for '%s' property\n", + pname); + return -ENOMEM; + } + abb->info = info; + + volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries, + GFP_KERNEL); + if (!volt_table) { + dev_err(dev, "Can't allocate voltage table for '%s' property\n", + pname); + return -ENOMEM; + } + + abb->rdesc.n_voltages = num_entries; + abb->rdesc.volt_table = volt_table; + /* We do not know where the OPP voltage is at the moment */ + abb->current_info_idx = -EINVAL; + + abb_info = prop->value; + for (i = 0; i < num_entries; i++, info++, volt_table++) { + u32 efuse_offset, rbb_mask, fbb_mask, vset_mask; + u32 efuse_val; + + /* NOTE: num_values should equal to entries picked up here */ + *volt_table = be32_to_cpup(abb_info++); + info->opp_sel = be32_to_cpup(abb_info++); + efuse_offset = be32_to_cpup(abb_info++); + rbb_mask = be32_to_cpup(abb_info++); + fbb_mask = be32_to_cpup(abb_info++); + vset_mask = be32_to_cpup(abb_info++); + + dev_dbg(dev, + "[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n", + i, *volt_table, info->opp_sel, efuse_offset, rbb_mask, + fbb_mask, vset_mask); + + /* Find min/max for voltage set */ + if (min_uV > *volt_table) + min_uV = *volt_table; + if (max_uV < *volt_table) + max_uV = *volt_table; + + if (!abb->efuse_base) { + /* Ignore invalid data, but warn to help cleanup */ + if (efuse_offset || rbb_mask || fbb_mask || vset_mask) + dev_err(dev, "prop '%s': v=%d,bad efuse/mask\n", + pname, *volt_table); + goto check_abb; + } + + efuse_val = readl(abb->efuse_base + efuse_offset); + + /* Use ABB recommendation from Efuse */ + if (efuse_val & rbb_mask) + info->opp_sel = TI_ABB_SLOW_OPP; + else if (efuse_val & fbb_mask) + info->opp_sel = TI_ABB_FAST_OPP; + else if (rbb_mask || fbb_mask) + info->opp_sel = TI_ABB_NOMINAL_OPP; + + dev_dbg(dev, + "[%d]v=%d efusev=0x%x final ABB=%d\n", + i, *volt_table, efuse_val, info->opp_sel); + + /* Use recommended Vset bits from Efuse */ + if (!abb->ldo_base) { + if (vset_mask) + dev_err(dev, "prop'%s':v=%d vst=%x LDO base?\n", + pname, *volt_table, vset_mask); + continue; + } + info->vset = efuse_val & vset_mask >> __ffs(vset_mask); + dev_dbg(dev, "[%d]v=%d vset=%x\n", i, *volt_table, info->vset); +check_abb: + switch (info->opp_sel) { + case TI_ABB_NOMINAL_OPP: + case TI_ABB_FAST_OPP: + case TI_ABB_SLOW_OPP: + /* Valid values */ + break; + default: + dev_err(dev, "%s:[%d]v=%d, ABB=%d is invalid! Abort!\n", + __func__, i, *volt_table, info->opp_sel); + return -EINVAL; + } + } + + /* Setup the min/max voltage constraints from the supported list */ + c->min_uV = min_uV; + c->max_uV = max_uV; + + return 0; +} + +static struct regulator_ops ti_abb_reg_ops = { + .list_voltage = regulator_list_voltage_table, + + .set_voltage_sel = ti_abb_set_voltage_sel, + .get_voltage_sel = ti_abb_get_voltage_sel, +}; + +/* Default ABB block offsets, IF this changes in future, create new one */ +static const struct ti_abb_reg abb_regs_v1 = { + /* WARNING: registers are wrongly documented in TRM */ + .setup_reg = 0x04, + .control_reg = 0x00, + + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + +static const struct ti_abb_reg abb_regs_v2 = { + .setup_reg = 0x00, + .control_reg = 0x04, + + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + +static const struct of_device_id ti_abb_of_match[] = { + {.compatible = "ti,abb-v1", .data = &abb_regs_v1}, + {.compatible = "ti,abb-v2", .data = &abb_regs_v2}, + { }, +}; + +MODULE_DEVICE_TABLE(of, ti_abb_of_match); + +/** + * ti_abb_probe() - Initialize an ABB ldo instance + * @pdev: ABB platform device + * + * Initializes an individual ABB LDO for required Body-Bias. ABB is used to + * addional bias supply to SoC modules for power savings or mandatory stability + * configuration at certain Operating Performance Points(OPPs). + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct resource *res; + struct ti_abb *abb; + struct regulator_init_data *initdata = NULL; + struct regulator_dev *rdev = NULL; + struct regulator_desc *desc; + struct regulation_constraints *c; + struct regulator_config config = { }; + char *pname; + int ret = 0; + + match = of_match_device(ti_abb_of_match, dev); + if (!match) { + /* We do not expect this to happen */ + ret = -ENODEV; + dev_err(dev, "%s: Unable to match device\n", __func__); + goto err; + } + if (!match->data) { + ret = -EINVAL; + dev_err(dev, "%s: Bad data in match\n", __func__); + goto err; + } + + abb = devm_kzalloc(dev, sizeof(struct ti_abb), GFP_KERNEL); + if (!abb) { + dev_err(dev, "%s: Unable to allocate ABB struct\n", __func__); + ret = -ENOMEM; + goto err; + } + abb->regs = match->data; + + /* Map ABB resources */ + pname = "base-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_err(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto err; + } + abb->base = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->base)) { + ret = PTR_ERR(abb->base); + goto err; + } + + pname = "int-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_err(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto err; + } + /* + * We may have shared interrupt register offsets which are + * write-1-to-clear between domains ensuring exclusivity. + */ + abb->int_base = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (!abb->int_base) { + dev_err(dev, "Unable to map '%s'\n", pname); + ret = -ENOMEM; + goto err; + } + + /* Map Optional resources */ + pname = "efuse-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_dbg(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto skip_opt; + } + + /* + * We may have shared efuse register offsets which are read-only + * between domains + */ + abb->efuse_base = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (!abb->efuse_base) { + dev_err(dev, "Unable to map '%s'\n", pname); + ret = -ENOMEM; + goto err; + } + + pname = "ldo-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_dbg(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto skip_opt; + } + abb->ldo_base = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->ldo_base)) { + ret = PTR_ERR(abb->ldo_base); + goto err; + } + + /* IF ldo_base is set, the following are mandatory */ + pname = "ti,ldovbb-override-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->ldovbb_override_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->ldovbb_override_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + + pname = "ti,ldovbb-vset-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->ldovbb_vset_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->ldovbb_vset_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + +skip_opt: + pname = "ti,tranxdone-status-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->txdone_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->txdone_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + + initdata = of_get_regulator_init_data(dev, pdev->dev.of_node); + if (!initdata) { + ret = -ENOMEM; + dev_err(dev, "%s: Unable to alloc regulator init data\n", + __func__); + goto err; + } + + /* init ABB opp_sel table */ + ret = ti_abb_init_table(dev, abb, initdata); + if (ret) + goto err; + + /* init ABB timing */ + ret = ti_abb_init_timings(dev, abb); + if (ret) + goto err; + + desc = &abb->rdesc; + desc->name = dev_name(dev); + desc->owner = THIS_MODULE; + desc->type = REGULATOR_VOLTAGE; + desc->ops = &ti_abb_reg_ops; + + c = &initdata->constraints; + if (desc->n_voltages > 1) + c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; + c->always_on = true; + + config.dev = dev; + config.init_data = initdata; + config.driver_data = abb; + config.of_node = pdev->dev.of_node; + + rdev = regulator_register(desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "%s: failed to register regulator(%d)\n", + __func__, ret); + goto err; + } + platform_set_drvdata(pdev, rdev); + + /* Enable the ldo if not already done by bootloader */ + ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base); + + return 0; + +err: + dev_err(dev, "%s: Failed to initialize(%d)\n", __func__, ret); + return ret; +} + +/** + * ti_abb_remove() - cleanups + * @pdev: ABB platform device + * + * Return: 0 + */ +static int ti_abb_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +MODULE_ALIAS("platform:ti_abb"); + +static struct platform_driver ti_abb_driver = { + .probe = ti_abb_probe, + .remove = ti_abb_remove, + .driver = { + .name = "ti_abb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ti_abb_of_match), + }, +}; +module_platform_driver(ti_abb_driver); + +MODULE_DESCRIPTION("Texas Instruments ABB LDO regulator driver"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index 612919c3081c..a490d5b749b2 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -351,7 +351,6 @@ static int tps62360_probe(struct i2c_client *client, int chip_id; pdata = client->dev.platform_data; - chip_id = id->driver_data; if (client->dev.of_node) { const struct of_device_id *match; @@ -364,6 +363,11 @@ static int tps62360_probe(struct i2c_client *client, chip_id = (int)match->data; if (!pdata) pdata = of_get_tps62360_platform_data(&client->dev); + } else if (id) { + chip_id = id->driver_data; + } else { + dev_err(&client->dev, "No device tree match or id table match found\n"); + return -ENODEV; } if (!pdata) { @@ -402,7 +406,7 @@ static int tps62360_probe(struct i2c_client *client, return -ENODEV; } - tps->desc.name = id->name; + tps->desc.name = client->name; tps->desc.id = 0; tps->desc.ops = &tps62360_dcdc_ops; tps->desc.type = REGULATOR_VOLTAGE; diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index df395187c063..2df4616621f5 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -405,8 +405,6 @@ static int tps65217_regulator_remove(struct platform_device *pdev) for (i = 0; i < TPS65217_NUM_REGULATOR; i++) regulator_unregister(tps->rdev[i]); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 01c66e9712a4..a9d4284ea007 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -330,8 +330,6 @@ static int regulator_virtual_remove(struct platform_device *pdev) if (drvdata->enabled) regulator_disable(drvdata->regulator); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 0af6898bcd79..46938cf162ad 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -567,8 +567,6 @@ static int wm831x_buckv_remove(struct platform_device *pdev) struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x *wm831x = dcdc->wm831x; - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")), dcdc); free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), @@ -714,8 +712,6 @@ static int wm831x_buckp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), dcdc); regulator_unregister(dcdc->regulator); @@ -849,8 +845,6 @@ static int wm831x_boostp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), dcdc); regulator_unregister(dcdc->regulator); @@ -940,7 +934,6 @@ static int wm831x_epe_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(dcdc->regulator); return 0; diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 68586ee3e1cb..16ebdf94d0a0 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -225,8 +225,6 @@ static int wm831x_isink_remove(struct platform_device *pdev) { struct wm831x_isink *isink = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink); regulator_unregister(isink->regulator); diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 1ec379a9a95c..9ff883f80878 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -338,8 +338,6 @@ static int wm831x_gp_ldo_remove(struct platform_device *pdev) { struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(ldo->wm831x, platform_get_irq_byname(pdev, "UV")), ldo); regulator_unregister(ldo->regulator); diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index c6a32ea80b9d..a09f03ee5506 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -250,7 +250,6 @@ static int wm8400_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index a612c356a697..8f2a8a7a3f99 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -185,8 +185,6 @@ static int wm8994_ldo_remove(struct platform_device *pdev) { struct wm8994_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - regulator_unregister(ldo->regulator); return 0; diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index d72a9216ee2e..17150a778984 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -38,9 +38,6 @@ */ #define DASD_CHANQ_MAX_SIZE 4 -#define DASD_SLEEPON_START_TAG (void *) 1 -#define DASD_SLEEPON_END_TAG (void *) 2 - /* * SECTION: exported variables of dasd.c */ @@ -1787,11 +1784,11 @@ static void __dasd_device_process_ccw_queue(struct dasd_device *device, list_for_each_safe(l, n, &device->ccw_queue) { cqr = list_entry(l, struct dasd_ccw_req, devlist); - /* Stop list processing at the first non-final request. */ + /* Skip any non-final request. */ if (cqr->status == DASD_CQR_QUEUED || cqr->status == DASD_CQR_IN_IO || cqr->status == DASD_CQR_CLEAR_PENDING) - break; + continue; if (cqr->status == DASD_CQR_ERROR) { __dasd_device_recovery(device, cqr); } @@ -2183,7 +2180,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(device))) { cqr->status = DASD_CQR_FAILED; - cqr->intrc = -EAGAIN; + cqr->intrc = -ENOLINK; continue; } /* Don't try to start requests if device is stopped */ @@ -2402,8 +2399,7 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) * Cancels a request that was started with dasd_sleep_on_req. * This is useful to timeout requests. The request will be * terminated if it is currently in i/o. - * Returns 1 if the request has been terminated. - * 0 if there was no need to terminate the request (not started yet) + * Returns 0 if request termination was successful * negative error code if termination failed * Cancellation of a request is an asynchronous operation! The calling * function has to wait until the request is properly returned via callback. @@ -2440,7 +2436,6 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr) return rc; } - /* * SECTION: Operations of the dasd_block layer. */ @@ -2537,6 +2532,16 @@ static void __dasd_process_request_queue(struct dasd_block *block) __blk_end_request_all(req, -EIO); continue; } + if (test_bit(DASD_FLAG_ABORTALL, &basedev->flags) && + (basedev->features & DASD_FEATURE_FAILFAST || + blk_noretry_request(req))) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "Rejecting failfast request %p", + req); + blk_start_request(req); + __blk_end_request_all(req, -ETIMEDOUT); + continue; + } cqr = basedev->discipline->build_cp(basedev, block, req); if (IS_ERR(cqr)) { if (PTR_ERR(cqr) == -EBUSY) @@ -2575,8 +2580,10 @@ static void __dasd_process_request_queue(struct dasd_block *block) */ cqr->callback_data = (void *) req; cqr->status = DASD_CQR_FILLED; + req->completion_data = cqr; blk_start_request(req); list_add_tail(&cqr->blocklist, &block->ccw_queue); + INIT_LIST_HEAD(&cqr->devlist); dasd_profile_start(block, cqr, req); } } @@ -2590,8 +2597,17 @@ static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr) req = (struct request *) cqr->callback_data; dasd_profile_end(cqr->block, cqr, req); status = cqr->block->base->discipline->free_cp(cqr, req); - if (status <= 0) - error = status ? status : -EIO; + if (status < 0) + error = status; + else if (status == 0) { + if (cqr->intrc == -EPERM) + error = -EBADE; + else if (cqr->intrc == -ENOLINK || + cqr->intrc == -ETIMEDOUT) + error = cqr->intrc; + else + error = -EIO; + } __blk_end_request_all(req, error); } @@ -2692,6 +2708,7 @@ static void __dasd_block_start_head(struct dasd_block *block) test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(block->base))) { cqr->status = DASD_CQR_FAILED; + cqr->intrc = -ENOLINK; dasd_schedule_block_bh(block); continue; } @@ -2864,6 +2881,82 @@ static void do_dasd_request(struct request_queue *queue) } /* + * Block timeout callback, called from the block layer + * + * request_queue lock is held on entry. + * + * Return values: + * BLK_EH_RESET_TIMER if the request should be left running + * BLK_EH_NOT_HANDLED if the request is handled or terminated + * by the driver. + */ +enum blk_eh_timer_return dasd_times_out(struct request *req) +{ + struct dasd_ccw_req *cqr = req->completion_data; + struct dasd_block *block = req->q->queuedata; + struct dasd_device *device; + int rc = 0; + + if (!cqr) + return BLK_EH_NOT_HANDLED; + + device = cqr->startdev ? cqr->startdev : block->base; + if (!device->blk_timeout) + return BLK_EH_RESET_TIMER; + DBF_DEV_EVENT(DBF_WARNING, device, + " dasd_times_out cqr %p status %x", + cqr, cqr->status); + + spin_lock(&block->queue_lock); + spin_lock(get_ccwdev_lock(device->cdev)); + cqr->retries = -1; + cqr->intrc = -ETIMEDOUT; + if (cqr->status >= DASD_CQR_QUEUED) { + spin_unlock(get_ccwdev_lock(device->cdev)); + rc = dasd_cancel_req(cqr); + } else if (cqr->status == DASD_CQR_FILLED || + cqr->status == DASD_CQR_NEED_ERP) { + cqr->status = DASD_CQR_TERMINATED; + spin_unlock(get_ccwdev_lock(device->cdev)); + } else if (cqr->status == DASD_CQR_IN_ERP) { + struct dasd_ccw_req *searchcqr, *nextcqr, *tmpcqr; + + list_for_each_entry_safe(searchcqr, nextcqr, + &block->ccw_queue, blocklist) { + tmpcqr = searchcqr; + while (tmpcqr->refers) + tmpcqr = tmpcqr->refers; + if (tmpcqr != cqr) + continue; + /* searchcqr is an ERP request for cqr */ + searchcqr->retries = -1; + searchcqr->intrc = -ETIMEDOUT; + if (searchcqr->status >= DASD_CQR_QUEUED) { + spin_unlock(get_ccwdev_lock(device->cdev)); + rc = dasd_cancel_req(searchcqr); + spin_lock(get_ccwdev_lock(device->cdev)); + } else if ((searchcqr->status == DASD_CQR_FILLED) || + (searchcqr->status == DASD_CQR_NEED_ERP)) { + searchcqr->status = DASD_CQR_TERMINATED; + rc = 0; + } else if (searchcqr->status == DASD_CQR_IN_ERP) { + /* + * Shouldn't happen; most recent ERP + * request is at the front of queue + */ + continue; + } + break; + } + spin_unlock(get_ccwdev_lock(device->cdev)); + } + dasd_schedule_block_bh(block); + spin_unlock(&block->queue_lock); + + return rc ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; +} + +/* * Allocate and initialize request queue and default I/O scheduler. */ static int dasd_alloc_queue(struct dasd_block *block) diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index a71bb8aaca1d..58bc6eb49de1 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1240,6 +1240,101 @@ dasd_expires_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store); +static ssize_t +dasd_retries_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_device *device; + int len; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + len = snprintf(buf, PAGE_SIZE, "%lu\n", device->default_retries); + dasd_put_device(device); + return len; +} + +static ssize_t +dasd_retries_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + unsigned long val; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + + if ((strict_strtoul(buf, 10, &val) != 0) || + (val > DASD_RETRIES_MAX)) { + dasd_put_device(device); + return -EINVAL; + } + + if (val) + device->default_retries = val; + + dasd_put_device(device); + return count; +} + +static DEVICE_ATTR(retries, 0644, dasd_retries_show, dasd_retries_store); + +static ssize_t +dasd_timeout_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dasd_device *device; + int len; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + len = snprintf(buf, PAGE_SIZE, "%lu\n", device->blk_timeout); + dasd_put_device(device); + return len; +} + +static ssize_t +dasd_timeout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + struct request_queue *q; + unsigned long val, flags; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device) || !device->block) + return -ENODEV; + + if ((strict_strtoul(buf, 10, &val) != 0) || + val > UINT_MAX / HZ) { + dasd_put_device(device); + return -EINVAL; + } + q = device->block->request_queue; + if (!q) { + dasd_put_device(device); + return -ENODEV; + } + spin_lock_irqsave(&device->block->request_queue_lock, flags); + if (!val) + blk_queue_rq_timed_out(q, NULL); + else + blk_queue_rq_timed_out(q, dasd_times_out); + + device->blk_timeout = val; + + blk_queue_rq_timeout(q, device->blk_timeout * HZ); + spin_unlock_irqrestore(&device->block->request_queue_lock, flags); + + dasd_put_device(device); + return count; +} + +static DEVICE_ATTR(timeout, 0644, + dasd_timeout_show, dasd_timeout_store); + static ssize_t dasd_reservation_policy_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1350,6 +1445,8 @@ static struct attribute * dasd_attrs[] = { &dev_attr_erplog.attr, &dev_attr_failfast.attr, &dev_attr_expires.attr, + &dev_attr_retries.attr, + &dev_attr_timeout.attr, &dev_attr_reservation_policy.attr, &dev_attr_last_known_reservation_state.attr, &dev_attr_safe_offline.attr, diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index cc0603358522..feca317b33de 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -359,6 +359,7 @@ dasd_diag_check_device(struct dasd_device *device) } device->default_expires = DIAG_TIMEOUT; + device->default_retries = DIAG_MAX_RETRIES; /* Figure out position of label block */ switch (private->rdc_data.vdev_class) { @@ -555,7 +556,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, recid++; } } - cqr->retries = DIAG_MAX_RETRIES; + cqr->retries = memdev->default_retries; cqr->buildclk = get_tod_clock(); if (blk_noretry_request(req) || block->base->features & DASD_FEATURE_FAILFAST) @@ -582,7 +583,10 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr) { - cqr->status = DASD_CQR_FILLED; + if (cqr->retries < 0) + cqr->status = DASD_CQR_FAILED; + else + cqr->status = DASD_CQR_FILLED; }; /* Fill in IOCTL data for device. */ diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 6a44b27623ed..e61a6deea3c0 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1682,6 +1682,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* set default timeout */ device->default_expires = DASD_EXPIRES; + /* set default retry count */ + device->default_retries = DASD_RETRIES; + if (private->gneq) { value = 1; for (i = 0; i < private->gneq->timeout.value; i++) @@ -2378,6 +2381,10 @@ sleep: static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) { + if (cqr->retries < 0) { + cqr->status = DASD_CQR_FAILED; + return; + } cqr->status = DASD_CQR_FILLED; if (cqr->block && (cqr->startdev != cqr->block->base)) { dasd_eckd_reset_ccw_to_base_io(cqr); @@ -2659,7 +2666,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -2834,7 +2841,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -2968,7 +2975,7 @@ static int prepare_itcw(struct itcw *itcw, dcw = itcw_add_dcw(itcw, pfx_cmd, 0, &pfxdata, sizeof(pfxdata), total_data_size); - return IS_ERR(dcw) ? PTR_ERR(dcw) : 0; + return PTR_RET(dcw); } static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( @@ -3127,7 +3134,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -3330,7 +3337,7 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, cqr->block = block; cqr->expires = startdev->default_expires * HZ; cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index 3250cb471f78..8d11f773a752 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -159,6 +159,14 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) struct dasd_device *device; device = cqr->startdev; + if (cqr->intrc == -ETIMEDOUT) { + dev_err(&device->cdev->dev, "cqr %p timeout error", cqr); + return; + } + if (cqr->intrc == -ENOLINK) { + dev_err(&device->cdev->dev, "cqr %p transport error", cqr); + return; + } /* dump sense data */ if (device->discipline && device->discipline->dump_sense) device->discipline->dump_sense(device, cqr, irb); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 4dd0e2f6047e..9cbc8c32ba59 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -29,6 +29,8 @@ #endif /* PRINTK_HEADER */ #define PRINTK_HEADER "dasd(fba):" +#define FBA_DEFAULT_RETRIES 32 + #define DASD_FBA_CCW_WRITE 0x41 #define DASD_FBA_CCW_READ 0x42 #define DASD_FBA_CCW_LOCATE 0x43 @@ -167,6 +169,7 @@ dasd_fba_check_characteristics(struct dasd_device *device) } device->default_expires = DASD_EXPIRES; + device->default_retries = FBA_DEFAULT_RETRIES; device->path_data.opm = LPM_ANYPATH; readonly = dasd_device_is_ro(device); @@ -369,7 +372,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, cqr->memdev = memdev; cqr->block = block; cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ - cqr->retries = 32; + cqr->retries = memdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -425,7 +428,10 @@ out: static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) { - cqr->status = DASD_CQR_FILLED; + if (cqr->retries < 0) + cqr->status = DASD_CQR_FAILED; + else + cqr->status = DASD_CQR_FILLED; }; static int diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 0785bd9bd5b6..690001af0d09 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -224,6 +224,8 @@ struct dasd_ccw_req { /* default expiration time*/ #define DASD_EXPIRES 300 #define DASD_EXPIRES_MAX 40000000 +#define DASD_RETRIES 256 +#define DASD_RETRIES_MAX 32768 /* per dasd_ccw_req flags */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ @@ -466,6 +468,9 @@ struct dasd_device { /* default expiration time in s */ unsigned long default_expires; + unsigned long default_retries; + + unsigned long blk_timeout; struct dentry *debugfs_dentry; struct dasd_profile profile; @@ -519,7 +524,10 @@ struct dasd_block { #define DASD_FLAG_SUSPENDED 9 /* The device was suspended */ #define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/ #define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */ +#define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */ +#define DASD_SLEEPON_START_TAG ((void *) 1) +#define DASD_SLEEPON_END_TAG ((void *) 2) void dasd_put_device_wake(struct dasd_device *); @@ -660,6 +668,8 @@ void dasd_free_device(struct dasd_device *); struct dasd_block *dasd_alloc_block(void); void dasd_free_block(struct dasd_block *); +enum blk_eh_timer_return dasd_times_out(struct request *req); + void dasd_enable_device(struct dasd_device *); void dasd_set_target_state(struct dasd_device *, int); void dasd_kick_device(struct dasd_device *); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 8be1b51e9311..25a0f2f8b0b9 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -141,6 +141,59 @@ static int dasd_ioctl_resume(struct dasd_block *block) } /* + * Abort all failfast I/O on a device. + */ +static int dasd_ioctl_abortio(struct dasd_block *block) +{ + unsigned long flags; + struct dasd_device *base; + struct dasd_ccw_req *cqr, *n; + + base = block->base; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (test_and_set_bit(DASD_FLAG_ABORTALL, &base->flags)) + return 0; + DBF_DEV_EVENT(DBF_NOTICE, base, "%s", "abortall flag set"); + + spin_lock_irqsave(&block->request_queue_lock, flags); + spin_lock(&block->queue_lock); + list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) { + if (test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + cqr->callback_data && + cqr->callback_data != DASD_SLEEPON_START_TAG && + cqr->callback_data != DASD_SLEEPON_END_TAG) { + spin_unlock(&block->queue_lock); + blk_abort_request(cqr->callback_data); + spin_lock(&block->queue_lock); + } + } + spin_unlock(&block->queue_lock); + spin_unlock_irqrestore(&block->request_queue_lock, flags); + + dasd_schedule_block_bh(block); + return 0; +} + +/* + * Allow I/O on a device + */ +static int dasd_ioctl_allowio(struct dasd_block *block) +{ + struct dasd_device *base; + + base = block->base; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (test_and_clear_bit(DASD_FLAG_ABORTALL, &base->flags)) + DBF_DEV_EVENT(DBF_NOTICE, base, "%s", "abortall flag unset"); + + return 0; +} + +/* * performs formatting of _device_ according to _fdata_ * Note: The discipline's format_function is assumed to deliver formatting * commands to format multiple units of the device. In terms of the ECKD @@ -458,6 +511,12 @@ int dasd_ioctl(struct block_device *bdev, fmode_t mode, case BIODASDRESUME: rc = dasd_ioctl_resume(block); break; + case BIODASDABORTIO: + rc = dasd_ioctl_abortio(block); + break; + case BIODASDALLOWIO: + rc = dasd_ioctl_allowio(block); + break; case BIODASDFMT: rc = dasd_ioctl_format(bdev, argp); break; diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index f3c325207445..17821a026c9c 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o + sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index bd6871bf545a..3e4fb4e858da 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -50,11 +50,42 @@ static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); /* Suspend request */ static DECLARE_COMPLETION(sclp_request_queue_flushed); +/* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */ +int sclp_console_pages = SCLP_CONSOLE_PAGES; +/* Flag to indicate if buffer pages are dropped on buffer full condition */ +int sclp_console_drop = 0; +/* Number of times the console dropped buffer pages */ +unsigned long sclp_console_full; + static void sclp_suspend_req_cb(struct sclp_req *req, void *data) { complete(&sclp_request_queue_flushed); } +static int __init sclp_setup_console_pages(char *str) +{ + int pages, rc; + + rc = kstrtoint(str, 0, &pages); + if (!rc && pages >= SCLP_CONSOLE_PAGES) + sclp_console_pages = pages; + return 1; +} + +__setup("sclp_con_pages=", sclp_setup_console_pages); + +static int __init sclp_setup_console_drop(char *str) +{ + int drop, rc; + + rc = kstrtoint(str, 0, &drop); + if (!rc && drop) + sclp_console_drop = 1; + return 1; +} + +__setup("sclp_con_drop=", sclp_setup_console_drop); + static struct sclp_req sclp_suspend_req; /* Timer for request retries. */ @@ -117,14 +148,19 @@ static int sclp_init(void); int sclp_service_call(sclp_cmdw_t command, void *sccb) { - int cc; + int cc = 4; /* Initialize for program check handling */ asm volatile( - " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ - " ipm %0\n" - " srl %0,28" - : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) + "0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ + "1: ipm %0\n" + " srl %0,28\n" + "2:\n" + EX_TABLE(0b, 2b) + EX_TABLE(1b, 2b) + : "+&d" (cc) : "d" (command), "a" (__pa(sccb)) : "cc", "memory"); + if (cc == 4) + return -EINVAL; if (cc == 3) return -EIO; if (cc == 2) @@ -1013,11 +1049,47 @@ static const struct dev_pm_ops sclp_pm_ops = { .restore = sclp_restore, }; +static ssize_t sclp_show_console_pages(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%i\n", sclp_console_pages); +} + +static DRIVER_ATTR(con_pages, S_IRUSR, sclp_show_console_pages, NULL); + +static ssize_t sclp_show_con_drop(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%i\n", sclp_console_drop); +} + +static DRIVER_ATTR(con_drop, S_IRUSR, sclp_show_con_drop, NULL); + +static ssize_t sclp_show_console_full(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%lu\n", sclp_console_full); +} + +static DRIVER_ATTR(con_full, S_IRUSR, sclp_show_console_full, NULL); + +static struct attribute *sclp_drv_attrs[] = { + &driver_attr_con_pages.attr, + &driver_attr_con_drop.attr, + &driver_attr_con_full.attr, + NULL, +}; +static struct attribute_group sclp_drv_attr_group = { + .attrs = sclp_drv_attrs, +}; +static const struct attribute_group *sclp_drv_attr_groups[] = { + &sclp_drv_attr_group, + NULL, +}; + static struct platform_driver sclp_pdrv = { .driver = { .name = "sclp", .owner = THIS_MODULE, .pm = &sclp_pm_ops, + .groups = sclp_drv_attr_groups, }, }; @@ -1096,10 +1168,12 @@ static __init int sclp_initcall(void) rc = platform_driver_register(&sclp_pdrv); if (rc) return rc; + sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0); - rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; + rc = PTR_RET(sclp_pdev); if (rc) goto fail_platform_driver_unregister; + rc = atomic_notifier_chain_register(&panic_notifier_list, &sclp_on_panic_nb); if (rc) diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 25bcd4c0ed82..40d1406289ed 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -15,7 +15,7 @@ /* maximum number of pages concerning our own memory management */ #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) -#define MAX_CONSOLE_PAGES 6 +#define SCLP_CONSOLE_PAGES 6 #define EVTYP_OPCMD 0x01 #define EVTYP_MSG 0x02 @@ -171,10 +171,15 @@ int sclp_remove_processed(struct sccb_header *sccb); int sclp_deactivate(void); int sclp_reactivate(void); int sclp_service_call(sclp_cmdw_t command, void *sccb); +int sclp_sync_request(sclp_cmdw_t command, void *sccb); int sclp_sdias_init(void); void sclp_sdias_exit(void); +extern int sclp_console_pages; +extern int sclp_console_drop; +extern unsigned long sclp_console_full; + /* useful inlines */ /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index bf07c3a188d4..8cd34bf644b3 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -195,7 +195,7 @@ static void sclp_sync_callback(struct sclp_req *req, void *data) complete(completion); } -static int do_sync_request(sclp_cmdw_t cmd, void *sccb) +int sclp_sync_request(sclp_cmdw_t cmd, void *sccb) { struct completion completion; struct sclp_req *request; @@ -270,7 +270,7 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); + rc = sclp_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); if (rc) goto out; if (sccb->header.response_code != 0x0010) { @@ -304,7 +304,7 @@ static int do_cpu_configure(sclp_cmdw_t cmd) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -374,7 +374,7 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn) return -ENOMEM; sccb->header.length = PAGE_SIZE; sccb->rn = rn; - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -429,7 +429,7 @@ static int sclp_attach_storage(u8 id) if (!sccb) return -ENOMEM; sccb->header.length = PAGE_SIZE; - rc = do_sync_request(0x00080001 | id << 8, sccb); + rc = sclp_sync_request(0x00080001 | id << 8, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -627,7 +627,7 @@ static int __init sclp_detect_standby_memory(void) for (id = 0; id <= sclp_max_storage_id; id++) { memset(sccb, 0, PAGE_SIZE); sccb->header.length = PAGE_SIZE; - rc = do_sync_request(0x00040001 | id << 8, sccb); + rc = sclp_sync_request(0x00040001 | id << 8, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -668,7 +668,7 @@ static int __init sclp_detect_standby_memory(void) if (rc) goto out; sclp_pdev = platform_device_register_simple("sclp_mem", -1, NULL, 0); - rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; + rc = PTR_RET(sclp_pdev); if (rc) goto out_driver; sclp_add_standby_memory(); @@ -714,7 +714,7 @@ static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) sccb->header.length = PAGE_SIZE; sccb->atype = SCLP_RECONFIG_PCI_ATPYE; sccb->aid = fid; - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -771,7 +771,7 @@ static int do_chp_configure(sclp_cmdw_t cmd) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -846,7 +846,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); + rc = sclp_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); if (rc) goto out; if (sccb->header.response_code != 0x0010) { diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index ecf45c54f8c4..5880def98fc1 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -130,6 +130,31 @@ sclp_console_timeout(unsigned long data) } /* + * Drop oldest console buffer if sclp_con_drop is set + */ +static int +sclp_console_drop_buffer(void) +{ + struct list_head *list; + struct sclp_buffer *buffer; + void *page; + + if (!sclp_console_drop) + return 0; + list = sclp_con_outqueue.next; + if (sclp_con_queue_running) + /* The first element is in I/O */ + list = list->next; + if (list == &sclp_con_outqueue) + return 0; + list_del(list); + buffer = list_entry(list, struct sclp_buffer, list); + page = sclp_unmake_buffer(buffer); + list_add_tail((struct list_head *) page, &sclp_con_pages); + return 1; +} + +/* * Writes the given message to S390 system console */ static void @@ -150,9 +175,13 @@ sclp_console_write(struct console *console, const char *message, do { /* make sure we have a console output buffer */ if (sclp_conbuf == NULL) { + if (list_empty(&sclp_con_pages)) + sclp_console_full++; while (list_empty(&sclp_con_pages)) { if (sclp_con_suspended) goto out; + if (sclp_console_drop_buffer()) + break; spin_unlock_irqrestore(&sclp_con_lock, flags); sclp_sync_wait(); spin_lock_irqsave(&sclp_con_lock, flags); @@ -297,7 +326,7 @@ sclp_console_init(void) return rc; /* Allocate pages for output buffering */ INIT_LIST_HEAD(&sclp_con_pages); - for (i = 0; i < MAX_CONSOLE_PAGES; i++) { + for (i = 0; i < sclp_console_pages; i++) { page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); list_add_tail(page, &sclp_con_pages); } diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c new file mode 100644 index 000000000000..648cb86afd42 --- /dev/null +++ b/drivers/s390/char/sclp_ctl.c @@ -0,0 +1,144 @@ +/* + * IOCTL interface for SCLP + * + * Copyright IBM Corp. 2012 + * + * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + +#include <linux/compat.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <asm/compat.h> +#include <asm/sclp_ctl.h> +#include <asm/sclp.h> + +#include "sclp.h" + +/* + * Supported command words + */ +static unsigned int sclp_ctl_sccb_wlist[] = { + 0x00400002, + 0x00410002, +}; + +/* + * Check if command word is supported + */ +static int sclp_ctl_cmdw_supported(unsigned int cmdw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) { + if (cmdw == sclp_ctl_sccb_wlist[i]) + return 1; + } + return 0; +} + +static void __user *u64_to_uptr(u64 value) +{ + if (is_compat_task()) + return compat_ptr(value); + else + return (void __user *)(unsigned long)value; +} + +/* + * Start SCLP request + */ +static int sclp_ctl_ioctl_sccb(void __user *user_area) +{ + struct sclp_ctl_sccb ctl_sccb; + struct sccb_header *sccb; + int rc; + + if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb))) + return -EFAULT; + if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw)) + return -EOPNOTSUPP; + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) { + rc = -EFAULT; + goto out_free; + } + if (sccb->length > PAGE_SIZE || sccb->length < 8) + return -EINVAL; + if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) { + rc = -EFAULT; + goto out_free; + } + rc = sclp_sync_request(ctl_sccb.cmdw, sccb); + if (rc) + goto out_free; + if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length)) + rc = -EFAULT; +out_free: + free_page((unsigned long) sccb); + return rc; +} + +/* + * SCLP SCCB ioctl function + */ +static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + void __user *argp; + + if (is_compat_task()) + argp = compat_ptr(arg); + else + argp = (void __user *) arg; + switch (cmd) { + case SCLP_CTL_SCCB: + return sclp_ctl_ioctl_sccb(argp); + default: /* unknown ioctl number */ + return -ENOTTY; + } +} + +/* + * File operations + */ +static const struct file_operations sclp_ctl_fops = { + .owner = THIS_MODULE, + .open = nonseekable_open, + .unlocked_ioctl = sclp_ctl_ioctl, + .compat_ioctl = sclp_ctl_ioctl, + .llseek = no_llseek, +}; + +/* + * Misc device definition + */ +static struct miscdevice sclp_ctl_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sclp", + .fops = &sclp_ctl_fops, +}; + +/* + * Register sclp_ctl misc device + */ +static int __init sclp_ctl_init(void) +{ + return misc_register(&sclp_ctl_device); +} +module_init(sclp_ctl_init); + +/* + * Deregister sclp_ctl misc device + */ +static void __exit sclp_ctl_exit(void) +{ + misc_deregister(&sclp_ctl_device); +} +module_exit(sclp_ctl_exit); diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 5aaaa2ec8df4..4eed38cd0af6 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -362,6 +362,31 @@ sclp_vt220_timeout(unsigned long data) #define BUFFER_MAX_DELAY HZ/20 +/* + * Drop oldest console buffer if sclp_con_drop is set + */ +static int +sclp_vt220_drop_buffer(void) +{ + struct list_head *list; + struct sclp_vt220_request *request; + void *page; + + if (!sclp_console_drop) + return 0; + list = sclp_vt220_outqueue.next; + if (sclp_vt220_queue_running) + /* The first element is in I/O */ + list = list->next; + if (list == &sclp_vt220_outqueue) + return 0; + list_del(list); + request = list_entry(list, struct sclp_vt220_request, list); + page = request->sclp_req.sccb; + list_add_tail((struct list_head *) page, &sclp_vt220_empty); + return 1; +} + /* * Internal implementation of the write function. Write COUNT bytes of data * from memory at BUF @@ -390,12 +415,16 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, do { /* Create an sclp output buffer if none exists yet */ if (sclp_vt220_current_request == NULL) { + if (list_empty(&sclp_vt220_empty)) + sclp_console_full++; while (list_empty(&sclp_vt220_empty)) { - spin_unlock_irqrestore(&sclp_vt220_lock, flags); if (may_fail || sclp_vt220_suspended) goto out; - else - sclp_sync_wait(); + if (sclp_vt220_drop_buffer()) + break; + spin_unlock_irqrestore(&sclp_vt220_lock, flags); + + sclp_sync_wait(); spin_lock_irqsave(&sclp_vt220_lock, flags); } page = (void *) sclp_vt220_empty.next; @@ -428,8 +457,8 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY; add_timer(&sclp_vt220_timer); } - spin_unlock_irqrestore(&sclp_vt220_lock, flags); out: + spin_unlock_irqrestore(&sclp_vt220_lock, flags); return overall_written; } @@ -803,7 +832,7 @@ sclp_vt220_con_init(void) if (!CONSOLE_IS_SCLP) return 0; - rc = __sclp_vt220_init(MAX_CONSOLE_PAGES); + rc = __sclp_vt220_init(sclp_console_pages); if (rc) return rc; /* Attach linux console */ diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c index 54b3c79203f5..91c3c642c76e 100644 --- a/drivers/s390/char/tape_class.c +++ b/drivers/s390/char/tape_class.c @@ -77,7 +77,7 @@ struct tape_class_device *register_tape_dev( tcd->class_device = device_create(tape_class, device, tcd->char_device->dev, NULL, "%s", tcd->device_name); - rc = IS_ERR(tcd->class_device) ? PTR_ERR(tcd->class_device) : 0; + rc = PTR_RET(tcd->class_device); if (rc) goto fail_with_cdev; rc = sysfs_create_link( diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index e9b72311e254..d5eac985976b 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -112,7 +112,8 @@ static int vmwdt_keepalive(void) static int vmwdt_disable(void) { - int ret = __diag288(wdt_cancel, 0, "", 0); + char cmd[] = {'\0'}; + int ret = __diag288(wdt_cancel, 0, cmd, 0); WARN_ON(ret != 0); clear_bit(VMWDT_RUNNING, &vmwdt_is_open); return ret; @@ -124,7 +125,7 @@ static int __init vmwdt_probe(void) * so we try initializing it with a NOP command ("BEGIN") * that won't cause any harm even if the following disable * fails for some reason */ - static char __initdata ebc_begin[] = { + char ebc_begin[] = { 194, 197, 199, 201, 213 }; if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index bc10220f6847..91edbd7ee806 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -9,142 +9,87 @@ */ #include <linux/init.h> +#include <linux/irq.h> +#include <linux/kernel_stat.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/rculist.h> #include <linux/slab.h> -#include <linux/rcupdate.h> #include <asm/airq.h> #include <asm/isc.h> #include "cio.h" #include "cio_debug.h" +#include "ioasm.h" -#define NR_AIRQS 32 -#define NR_AIRQS_PER_WORD sizeof(unsigned long) -#define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) - -union indicator_t { - unsigned long word[NR_AIRQ_WORDS]; - unsigned char byte[NR_AIRQS]; -} __attribute__((packed)); - -struct airq_t { - adapter_int_handler_t handler; - void *drv_data; -}; - -static union indicator_t indicators[MAX_ISC+1]; -static struct airq_t *airqs[MAX_ISC+1][NR_AIRQS]; - -static int register_airq(struct airq_t *airq, u8 isc) -{ - int i; - - for (i = 0; i < NR_AIRQS; i++) - if (!cmpxchg(&airqs[isc][i], NULL, airq)) - return i; - return -ENOMEM; -} +static DEFINE_SPINLOCK(airq_lists_lock); +static struct hlist_head airq_lists[MAX_ISC+1]; /** - * s390_register_adapter_interrupt() - register adapter interrupt handler - * @handler: adapter handler to be registered - * @drv_data: driver data passed with each call to the handler - * @isc: isc for which the handler should be called + * register_adapter_interrupt() - register adapter interrupt handler + * @airq: pointer to adapter interrupt descriptor * - * Returns: - * Pointer to the indicator to be used on success - * ERR_PTR() if registration failed + * Returns 0 on success, or -EINVAL. */ -void *s390_register_adapter_interrupt(adapter_int_handler_t handler, - void *drv_data, u8 isc) +int register_adapter_interrupt(struct airq_struct *airq) { - struct airq_t *airq; - char dbf_txt[16]; - int ret; - - if (isc > MAX_ISC) - return ERR_PTR(-EINVAL); - airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); - if (!airq) { - ret = -ENOMEM; - goto out; + char dbf_txt[32]; + + if (!airq->handler || airq->isc > MAX_ISC) + return -EINVAL; + if (!airq->lsi_ptr) { + airq->lsi_ptr = kzalloc(1, GFP_KERNEL); + if (!airq->lsi_ptr) + return -ENOMEM; + airq->flags |= AIRQ_PTR_ALLOCATED; } - airq->handler = handler; - airq->drv_data = drv_data; - - ret = register_airq(airq, isc); -out: - snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); + if (!airq->lsi_mask) + airq->lsi_mask = 0xff; + snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq); CIO_TRACE_EVENT(4, dbf_txt); - if (ret < 0) { - kfree(airq); - return ERR_PTR(ret); - } else - return &indicators[isc].byte[ret]; + isc_register(airq->isc); + spin_lock(&airq_lists_lock); + hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]); + spin_unlock(&airq_lists_lock); + return 0; } -EXPORT_SYMBOL(s390_register_adapter_interrupt); +EXPORT_SYMBOL(register_adapter_interrupt); /** - * s390_unregister_adapter_interrupt - unregister adapter interrupt handler - * @ind: indicator for which the handler is to be unregistered - * @isc: interruption subclass + * unregister_adapter_interrupt - unregister adapter interrupt handler + * @airq: pointer to adapter interrupt descriptor */ -void s390_unregister_adapter_interrupt(void *ind, u8 isc) +void unregister_adapter_interrupt(struct airq_struct *airq) { - struct airq_t *airq; - char dbf_txt[16]; - int i; + char dbf_txt[32]; - i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]); - snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); + if (hlist_unhashed(&airq->list)) + return; + snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq); CIO_TRACE_EVENT(4, dbf_txt); - indicators[isc].byte[i] = 0; - airq = xchg(&airqs[isc][i], NULL); - /* - * Allow interrupts to complete. This will ensure that the airq handle - * is no longer referenced by any interrupt handler. - */ - synchronize_sched(); - kfree(airq); + spin_lock(&airq_lists_lock); + hlist_del_rcu(&airq->list); + spin_unlock(&airq_lists_lock); + synchronize_rcu(); + isc_unregister(airq->isc); + if (airq->flags & AIRQ_PTR_ALLOCATED) { + kfree(airq->lsi_ptr); + airq->lsi_ptr = NULL; + airq->flags &= ~AIRQ_PTR_ALLOCATED; + } } -EXPORT_SYMBOL(s390_unregister_adapter_interrupt); - -#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) +EXPORT_SYMBOL(unregister_adapter_interrupt); void do_adapter_IO(u8 isc) { - int w; - int i; - unsigned long word; - struct airq_t *airq; - - /* - * Access indicator array in word-sized chunks to minimize storage - * fetch operations. - */ - for (w = 0; w < NR_AIRQ_WORDS; w++) { - word = indicators[isc].word[w]; - i = w * NR_AIRQS_PER_WORD; - /* - * Check bytes within word for active indicators. - */ - while (word) { - if (word & INDICATOR_MASK) { - airq = airqs[isc][i]; - /* Make sure gcc reads from airqs only once. */ - barrier(); - if (likely(airq)) - airq->handler(&indicators[isc].byte[i], - airq->drv_data); - else - /* - * Reset ill-behaved indicator. - */ - indicators[isc].byte[i] = 0; - } - word <<= 8; - i++; - } - } + struct airq_struct *airq; + struct hlist_head *head; + + head = &airq_lists[isc]; + rcu_read_lock(); + hlist_for_each_entry_rcu(airq, head, list) + if ((*airq->lsi_ptr & airq->lsi_mask) != 0) + airq->handler(airq); + rcu_read_unlock(); } diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 8ea7d9b2c671..13299f902676 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -20,6 +20,7 @@ #include <asm/chpid.h> #include <asm/chsc.h> #include <asm/crw.h> +#include <asm/isc.h> #include "css.h" #include "cio.h" @@ -144,6 +145,65 @@ out: return ret; } +/** + * chsc_ssqd() - store subchannel QDIO data (SSQD) + * @schid: id of the subchannel on which SSQD is performed + * @ssqd: request and response block for SSQD + * + * Returns 0 on success. + */ +int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd) +{ + memset(ssqd, 0, sizeof(*ssqd)); + ssqd->request.length = 0x0010; + ssqd->request.code = 0x0024; + ssqd->first_sch = schid.sch_no; + ssqd->last_sch = schid.sch_no; + ssqd->ssid = schid.ssid; + + if (chsc(ssqd)) + return -EIO; + + return chsc_error_from_response(ssqd->response.code); +} +EXPORT_SYMBOL_GPL(chsc_ssqd); + +/** + * chsc_sadc() - set adapter device controls (SADC) + * @schid: id of the subchannel on which SADC is performed + * @scssc: request and response block for SADC + * @summary_indicator_addr: summary indicator address + * @subchannel_indicator_addr: subchannel indicator address + * + * Returns 0 on success. + */ +int chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc, + u64 summary_indicator_addr, u64 subchannel_indicator_addr) +{ + memset(scssc, 0, sizeof(*scssc)); + scssc->request.length = 0x0fe0; + scssc->request.code = 0x0021; + scssc->operation_code = 0; + + scssc->summary_indicator_addr = summary_indicator_addr; + scssc->subchannel_indicator_addr = subchannel_indicator_addr; + + scssc->ks = PAGE_DEFAULT_KEY >> 4; + scssc->kc = PAGE_DEFAULT_KEY >> 4; + scssc->isc = QDIO_AIRQ_ISC; + scssc->schid = schid; + + /* enable the time delay disablement facility */ + if (css_general_characteristics.aif_tdd) + scssc->word_with_d_bit = 0x10000000; + + if (chsc(scssc)) + return -EIO; + + return chsc_error_from_response(scssc->response.code); +} +EXPORT_SYMBOL_GPL(chsc_sadc); + static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) { spin_lock_irq(sch->lock); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index e7ef2a683b8f..23d072e70eb2 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -7,14 +7,10 @@ #include <asm/chpid.h> #include <asm/chsc.h> #include <asm/schid.h> +#include <asm/qdio.h> #define CHSC_SDA_OC_MSS 0x2 -struct chsc_header { - u16 length; - u16 code; -} __attribute__ ((packed)); - #define NR_MEASUREMENT_CHARS 5 struct cmg_chars { u32 values[NR_MEASUREMENT_CHARS]; @@ -77,6 +73,40 @@ struct chsc_ssd_info { u16 fla[8]; }; +struct chsc_ssqd_area { + struct chsc_header request; + u16:10; + u8 ssid:2; + u8 fmt:4; + u16 first_sch; + u16:16; + u16 last_sch; + u32:32; + struct chsc_header response; + u32:32; + struct qdio_ssqd_desc qdio_ssqd; +} __packed; + +struct chsc_scssc_area { + struct chsc_header request; + u16 operation_code; + u16:16; + u32:32; + u32:32; + u64 summary_indicator_addr; + u64 subchannel_indicator_addr; + u32 ks:4; + u32 kc:4; + u32:21; + u32 isc:3; + u32 word_with_d_bit; + u32:32; + struct subchannel_id schid; + u32 reserved[1004]; + struct chsc_header response; + u32:32; +} __packed; + struct chsc_scpd { struct chsc_header request; u32:2; @@ -116,7 +146,9 @@ int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, void chsc_chp_online(struct chp_id chpid); void chsc_chp_offline(struct chp_id chpid); int chsc_get_channel_measurement_chars(struct channel_path *chp); - +int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd); +int chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc, + u64 summary_indicator_addr, u64 subchannel_indicator_addr); int chsc_error_from_response(int response); int chsc_siosl(struct subchannel_id schid); diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index facdf809113f..7b29d0be0ca3 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -29,6 +29,10 @@ static debug_info_t *chsc_debug_msg_id; static debug_info_t *chsc_debug_log_id; +static struct chsc_request *on_close_request; +static struct chsc_async_area *on_close_chsc_area; +static DEFINE_MUTEX(on_close_mutex); + #define CHSC_MSG(imp, args...) do { \ debug_sprintf_event(chsc_debug_msg_id, imp , ##args); \ } while (0) @@ -258,7 +262,7 @@ static int chsc_async(struct chsc_async_area *chsc_area, CHSC_LOG(2, "schid"); CHSC_LOG_HEX(2, &sch->schid, sizeof(sch->schid)); cc = chsc(chsc_area); - sprintf(dbf, "cc:%d", cc); + snprintf(dbf, sizeof(dbf), "cc:%d", cc); CHSC_LOG(2, dbf); switch (cc) { case 0: @@ -287,11 +291,11 @@ static int chsc_async(struct chsc_async_area *chsc_area, return ret; } -static void chsc_log_command(struct chsc_async_area *chsc_area) +static void chsc_log_command(void *chsc_area) { char dbf[10]; - sprintf(dbf, "CHSC:%x", chsc_area->header.code); + snprintf(dbf, sizeof(dbf), "CHSC:%x", ((uint16_t *)chsc_area)[1]); CHSC_LOG(0, dbf); CHSC_LOG_HEX(0, chsc_area, 32); } @@ -355,13 +359,106 @@ static int chsc_ioctl_start(void __user *user_area) if (copy_to_user(user_area, chsc_area, PAGE_SIZE)) ret = -EFAULT; out_free: - sprintf(dbf, "ret:%d", ret); + snprintf(dbf, sizeof(dbf), "ret:%d", ret); CHSC_LOG(0, dbf); kfree(request); free_page((unsigned long)chsc_area); return ret; } +static int chsc_ioctl_on_close_set(void __user *user_area) +{ + char dbf[13]; + int ret; + + mutex_lock(&on_close_mutex); + if (on_close_chsc_area) { + ret = -EBUSY; + goto out_unlock; + } + on_close_request = kzalloc(sizeof(*on_close_request), GFP_KERNEL); + if (!on_close_request) { + ret = -ENOMEM; + goto out_unlock; + } + on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); + if (!on_close_chsc_area) { + ret = -ENOMEM; + goto out_free_request; + } + if (copy_from_user(on_close_chsc_area, user_area, PAGE_SIZE)) { + ret = -EFAULT; + goto out_free_chsc; + } + ret = 0; + goto out_unlock; + +out_free_chsc: + free_page((unsigned long)on_close_chsc_area); + on_close_chsc_area = NULL; +out_free_request: + kfree(on_close_request); + on_close_request = NULL; +out_unlock: + mutex_unlock(&on_close_mutex); + snprintf(dbf, sizeof(dbf), "ocsret:%d", ret); + CHSC_LOG(0, dbf); + return ret; +} + +static int chsc_ioctl_on_close_remove(void) +{ + char dbf[13]; + int ret; + + mutex_lock(&on_close_mutex); + if (!on_close_chsc_area) { + ret = -ENOENT; + goto out_unlock; + } + free_page((unsigned long)on_close_chsc_area); + on_close_chsc_area = NULL; + kfree(on_close_request); + on_close_request = NULL; + ret = 0; +out_unlock: + mutex_unlock(&on_close_mutex); + snprintf(dbf, sizeof(dbf), "ocrret:%d", ret); + CHSC_LOG(0, dbf); + return ret; +} + +static int chsc_ioctl_start_sync(void __user *user_area) +{ + struct chsc_sync_area *chsc_area; + int ret, ccode; + + chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!chsc_area) + return -ENOMEM; + if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) { + ret = -EFAULT; + goto out_free; + } + if (chsc_area->header.code & 0x4000) { + ret = -EINVAL; + goto out_free; + } + chsc_log_command(chsc_area); + ccode = chsc(chsc_area); + if (ccode != 0) { + ret = -EIO; + goto out_free; + } + if (copy_to_user(user_area, chsc_area, PAGE_SIZE)) + ret = -EFAULT; + else + ret = 0; +out_free: + free_page((unsigned long)chsc_area); + return ret; +} + static int chsc_ioctl_info_channel_path(void __user *user_cd) { struct chsc_chp_cd *cd; @@ -795,6 +892,8 @@ static long chsc_ioctl(struct file *filp, unsigned int cmd, switch (cmd) { case CHSC_START: return chsc_ioctl_start(argp); + case CHSC_START_SYNC: + return chsc_ioctl_start_sync(argp); case CHSC_INFO_CHANNEL_PATH: return chsc_ioctl_info_channel_path(argp); case CHSC_INFO_CU: @@ -809,14 +908,60 @@ static long chsc_ioctl(struct file *filp, unsigned int cmd, return chsc_ioctl_chpd(argp); case CHSC_INFO_DCAL: return chsc_ioctl_dcal(argp); + case CHSC_ON_CLOSE_SET: + return chsc_ioctl_on_close_set(argp); + case CHSC_ON_CLOSE_REMOVE: + return chsc_ioctl_on_close_remove(); default: /* unknown ioctl number */ return -ENOIOCTLCMD; } } +static atomic_t chsc_ready_for_use = ATOMIC_INIT(1); + +static int chsc_open(struct inode *inode, struct file *file) +{ + if (!atomic_dec_and_test(&chsc_ready_for_use)) { + atomic_inc(&chsc_ready_for_use); + return -EBUSY; + } + return nonseekable_open(inode, file); +} + +static int chsc_release(struct inode *inode, struct file *filp) +{ + char dbf[13]; + int ret; + + mutex_lock(&on_close_mutex); + if (!on_close_chsc_area) + goto out_unlock; + init_completion(&on_close_request->completion); + CHSC_LOG(0, "on_close"); + chsc_log_command(on_close_chsc_area); + spin_lock_irq(&chsc_lock); + ret = chsc_async(on_close_chsc_area, on_close_request); + spin_unlock_irq(&chsc_lock); + if (ret == -EINPROGRESS) { + wait_for_completion(&on_close_request->completion); + ret = chsc_examine_irb(on_close_request); + } + snprintf(dbf, sizeof(dbf), "relret:%d", ret); + CHSC_LOG(0, dbf); + free_page((unsigned long)on_close_chsc_area); + on_close_chsc_area = NULL; + kfree(on_close_request); + on_close_request = NULL; +out_unlock: + mutex_unlock(&on_close_mutex); + atomic_inc(&chsc_ready_for_use); + return 0; +} + static const struct file_operations chsc_fops = { .owner = THIS_MODULE, - .open = nonseekable_open, + .open = chsc_open, + .release = chsc_release, .unlocked_ioctl = chsc_ioctl, .compat_ioctl = chsc_ioctl, .llseek = no_llseek, diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 935d80b4e9ce..4eeb4a6bf207 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -568,7 +568,7 @@ out: */ void __irq_entry do_IRQ(struct pt_regs *regs) { - struct tpi_info *tpi_info; + struct tpi_info *tpi_info = (struct tpi_info *) ®s->int_code; struct subchannel *sch; struct irb *irb; struct pt_regs *old_regs; @@ -579,46 +579,34 @@ void __irq_entry do_IRQ(struct pt_regs *regs) if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) /* Serve timer interrupts first. */ clock_comparator_work(); - /* - * Get interrupt information from lowcore - */ - tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id; - irb = (struct irb *)&S390_lowcore.irb; - do { - kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); - if (tpi_info->adapter_IO) { - do_adapter_IO(tpi_info->isc); - continue; - } - sch = (struct subchannel *)(unsigned long)tpi_info->intparm; - if (!sch) { - /* Clear pending interrupt condition. */ - inc_irq_stat(IRQIO_CIO); - tsch(tpi_info->schid, irb); - continue; - } - spin_lock(sch->lock); - /* Store interrupt response block to lowcore. */ - if (tsch(tpi_info->schid, irb) == 0) { - /* Keep subchannel information word up to date. */ - memcpy (&sch->schib.scsw, &irb->scsw, - sizeof (irb->scsw)); - /* Call interrupt handler if there is one. */ - if (sch->driver && sch->driver->irq) - sch->driver->irq(sch); - else - inc_irq_stat(IRQIO_CIO); - } else + + kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); + irb = (struct irb *) &S390_lowcore.irb; + if (tpi_info->adapter_IO) { + do_adapter_IO(tpi_info->isc); + goto out; + } + sch = (struct subchannel *)(unsigned long) tpi_info->intparm; + if (!sch) { + /* Clear pending interrupt condition. */ + inc_irq_stat(IRQIO_CIO); + tsch(tpi_info->schid, irb); + goto out; + } + spin_lock(sch->lock); + /* Store interrupt response block to lowcore. */ + if (tsch(tpi_info->schid, irb) == 0) { + /* Keep subchannel information word up to date. */ + memcpy (&sch->schib.scsw, &irb->scsw, sizeof (irb->scsw)); + /* Call interrupt handler if there is one. */ + if (sch->driver && sch->driver->irq) + sch->driver->irq(sch); + else inc_irq_stat(IRQIO_CIO); - spin_unlock(sch->lock); - /* - * Are more interrupts pending? - * If so, the tpi instruction will update the lowcore - * to hold the info for the next interrupt. - * We don't do this for VM because a tpi drops the cpu - * out of the sie which costs more cycles than it saves. - */ - } while (MACHINE_IS_LPAR && tpi(NULL) != 0); + } else + inc_irq_stat(IRQIO_CIO); + spin_unlock(sch->lock); +out: irq_exit(); set_irq_regs(old_regs); } diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 5132554d7917..8acaae18bd11 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -140,40 +140,6 @@ struct siga_flag { u8:3; } __attribute__ ((packed)); -struct chsc_ssqd_area { - struct chsc_header request; - u16:10; - u8 ssid:2; - u8 fmt:4; - u16 first_sch; - u16:16; - u16 last_sch; - u32:32; - struct chsc_header response; - u32:32; - struct qdio_ssqd_desc qdio_ssqd; -} __attribute__ ((packed)); - -struct scssc_area { - struct chsc_header request; - u16 operation_code; - u16:16; - u32:32; - u32:32; - u64 summary_indicator_addr; - u64 subchannel_indicator_addr; - u32 ks:4; - u32 kc:4; - u32:21; - u32 isc:3; - u32 word_with_d_bit; - u32:32; - struct subchannel_id schid; - u32 reserved[1004]; - struct chsc_header response; - u32:32; -} __attribute__ ((packed)); - struct qdio_dev_perf_stat { unsigned int adapter_int; unsigned int qdio_int; diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 843051bc20f1..fb1c1e0483ed 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -608,50 +608,6 @@ static inline int contains_aobs(struct qdio_q *q) return !q->is_input_q && q->u.out.use_cq; } -static inline void qdio_trace_aob(struct qdio_irq *irq, struct qdio_q *q, - int i, struct qaob *aob) -{ - int tmp; - - DBF_DEV_EVENT(DBF_INFO, irq, "AOB%d:%lx", i, - (unsigned long) virt_to_phys(aob)); - DBF_DEV_EVENT(DBF_INFO, irq, "RES00:%lx", - (unsigned long) aob->res0[0]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES01:%lx", - (unsigned long) aob->res0[1]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES02:%lx", - (unsigned long) aob->res0[2]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES03:%lx", - (unsigned long) aob->res0[3]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES04:%lx", - (unsigned long) aob->res0[4]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES05:%lx", - (unsigned long) aob->res0[5]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES1:%x", aob->res1); - DBF_DEV_EVENT(DBF_INFO, irq, "RES2:%x", aob->res2); - DBF_DEV_EVENT(DBF_INFO, irq, "RES3:%x", aob->res3); - DBF_DEV_EVENT(DBF_INFO, irq, "AORC:%u", aob->aorc); - DBF_DEV_EVENT(DBF_INFO, irq, "FLAGS:%u", aob->flags); - DBF_DEV_EVENT(DBF_INFO, irq, "CBTBS:%u", aob->cbtbs); - DBF_DEV_EVENT(DBF_INFO, irq, "SBC:%u", aob->sb_count); - for (tmp = 0; tmp < QDIO_MAX_ELEMENTS_PER_BUFFER; ++tmp) { - DBF_DEV_EVENT(DBF_INFO, irq, "SBA%d:%lx", tmp, - (unsigned long) aob->sba[tmp]); - DBF_DEV_EVENT(DBF_INFO, irq, "rSBA%d:%lx", tmp, - (unsigned long) q->sbal[i]->element[tmp].addr); - DBF_DEV_EVENT(DBF_INFO, irq, "DC%d:%u", tmp, aob->dcount[tmp]); - DBF_DEV_EVENT(DBF_INFO, irq, "rDC%d:%u", tmp, - q->sbal[i]->element[tmp].length); - } - DBF_DEV_EVENT(DBF_INFO, irq, "USER0:%lx", (unsigned long) aob->user0); - for (tmp = 0; tmp < 2; ++tmp) { - DBF_DEV_EVENT(DBF_INFO, irq, "RES4%d:%lx", tmp, - (unsigned long) aob->res4[tmp]); - } - DBF_DEV_EVENT(DBF_INFO, irq, "USER1:%lx", (unsigned long) aob->user1); - DBF_DEV_EVENT(DBF_INFO, irq, "USER2:%lx", (unsigned long) aob->user2); -} - static inline void qdio_handle_aobs(struct qdio_q *q, int start, int count) { unsigned char state = 0; diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 16ecd35b8e51..f5f4a91fab44 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -254,40 +254,31 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr, int rc; DBF_EVENT("getssqd:%4x", schid->sch_no); - if (irq_ptr != NULL) - ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page; - else + if (!irq_ptr) { ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL); - memset(ssqd, 0, PAGE_SIZE); - - ssqd->request = (struct chsc_header) { - .length = 0x0010, - .code = 0x0024, - }; - ssqd->first_sch = schid->sch_no; - ssqd->last_sch = schid->sch_no; - ssqd->ssid = schid->ssid; - - if (chsc(ssqd)) - return -EIO; - rc = chsc_error_from_response(ssqd->response.code); + if (!ssqd) + return -ENOMEM; + } else { + ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page; + } + + rc = chsc_ssqd(*schid, ssqd); if (rc) - return rc; + goto out; if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) || !(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) || (ssqd->qdio_ssqd.sch != schid->sch_no)) - return -EINVAL; - - if (irq_ptr != NULL) - memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd, - sizeof(struct qdio_ssqd_desc)); - else { - memcpy(data, &ssqd->qdio_ssqd, - sizeof(struct qdio_ssqd_desc)); + rc = -EINVAL; + + if (!rc) + memcpy(data, &ssqd->qdio_ssqd, sizeof(*data)); + +out: + if (!irq_ptr) free_page((unsigned long)ssqd); - } - return 0; + + return rc; } void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr) @@ -295,7 +286,7 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr) unsigned char qdioac; int rc; - rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, NULL); + rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, &irq_ptr->ssqd_desc); if (rc) { DBF_ERROR("%4x ssqd ERR", irq_ptr->schid.sch_no); DBF_ERROR("rc:%x", rc); diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index bde5255200dc..5d06253c2a7a 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -36,8 +36,13 @@ struct indicator_t { static LIST_HEAD(tiq_list); static DEFINE_MUTEX(tiq_list_lock); -/* adapter local summary indicator */ -static u8 *tiqdio_alsi; +/* Adapter interrupt definitions */ +static void tiqdio_thinint_handler(struct airq_struct *airq); + +static struct airq_struct tiqdio_airq = { + .handler = tiqdio_thinint_handler, + .isc = QDIO_AIRQ_ISC, +}; static struct indicator_t *q_indicators; @@ -176,7 +181,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) * @alsi: pointer to adapter local summary indicator * @data: NULL */ -static void tiqdio_thinint_handler(void *alsi, void *data) +static void tiqdio_thinint_handler(struct airq_struct *airq) { u32 si_used = clear_shared_ind(); struct qdio_q *q; @@ -208,51 +213,31 @@ static void tiqdio_thinint_handler(void *alsi, void *data) static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) { - struct scssc_area *scssc_area; + struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page; + u64 summary_indicator_addr, subchannel_indicator_addr; int rc; - scssc_area = (struct scssc_area *)irq_ptr->chsc_page; - memset(scssc_area, 0, PAGE_SIZE); - if (reset) { - scssc_area->summary_indicator_addr = 0; - scssc_area->subchannel_indicator_addr = 0; + summary_indicator_addr = 0; + subchannel_indicator_addr = 0; } else { - scssc_area->summary_indicator_addr = virt_to_phys(tiqdio_alsi); - scssc_area->subchannel_indicator_addr = - virt_to_phys(irq_ptr->dsci); + summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr); + subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci); } - scssc_area->request = (struct chsc_header) { - .length = 0x0fe0, - .code = 0x0021, - }; - scssc_area->operation_code = 0; - scssc_area->ks = PAGE_DEFAULT_KEY >> 4; - scssc_area->kc = PAGE_DEFAULT_KEY >> 4; - scssc_area->isc = QDIO_AIRQ_ISC; - scssc_area->schid = irq_ptr->schid; - - /* enable the time delay disablement facility */ - if (css_general_characteristics.aif_tdd) - scssc_area->word_with_d_bit = 0x10000000; - - rc = chsc(scssc_area); - if (rc) - return -EIO; - - rc = chsc_error_from_response(scssc_area->response.code); + rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr, + subchannel_indicator_addr); if (rc) { DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no, - scssc_area->response.code); - DBF_ERROR_HEX(&scssc_area->response, sizeof(void *)); - return rc; + scssc->response.code); + goto out; } DBF_EVENT("setscind"); - DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long)); - DBF_HEX(&scssc_area->subchannel_indicator_addr, sizeof(unsigned long)); - return 0; + DBF_HEX(&summary_indicator_addr, sizeof(summary_indicator_addr)); + DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr)); +out: + return rc; } /* allocate non-shared indicators and shared indicator */ @@ -272,14 +257,12 @@ void tiqdio_free_memory(void) int __init tiqdio_register_thinints(void) { - isc_register(QDIO_AIRQ_ISC); - tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler, - NULL, QDIO_AIRQ_ISC); - if (IS_ERR(tiqdio_alsi)) { - DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi)); - tiqdio_alsi = NULL; - isc_unregister(QDIO_AIRQ_ISC); - return -ENOMEM; + int rc; + + rc = register_adapter_interrupt(&tiqdio_airq); + if (rc) { + DBF_EVENT("RTI:%x", rc); + return rc; } return 0; } @@ -312,9 +295,5 @@ void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) void __exit tiqdio_unregister_thinints(void) { WARN_ON(!list_empty(&tiq_list)); - - if (tiqdio_alsi) { - s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC); - isc_unregister(QDIO_AIRQ_ISC); - } + unregister_adapter_interrupt(&tiqdio_airq); } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 9de41aa14896..f446a7705c3b 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -58,7 +58,7 @@ static inline void ap_schedule_poll_timer(void); static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags); static int ap_device_remove(struct device *dev); static int ap_device_probe(struct device *dev); -static void ap_interrupt_handler(void *unused1, void *unused2); +static void ap_interrupt_handler(struct airq_struct *airq); static void ap_reset(struct ap_device *ap_dev); static void ap_config_timeout(unsigned long ptr); static int ap_select_domain(void); @@ -106,7 +106,6 @@ static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); static struct task_struct *ap_poll_kthread = NULL; static DEFINE_MUTEX(ap_poll_thread_mutex); static DEFINE_SPINLOCK(ap_poll_timer_lock); -static void *ap_interrupt_indicator; static struct hrtimer ap_poll_timer; /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ @@ -120,13 +119,21 @@ static int ap_suspend_flag; static int user_set_domain = 0; static struct bus_type ap_bus_type; +/* Adapter interrupt definitions */ +static int ap_airq_flag; + +static struct airq_struct ap_airq = { + .handler = ap_interrupt_handler, + .isc = AP_ISC, +}; + /** * ap_using_interrupts() - Returns non-zero if interrupt support is * available. */ static inline int ap_using_interrupts(void) { - return ap_interrupt_indicator != NULL; + return ap_airq_flag; } /** @@ -588,7 +595,7 @@ static int ap_init_queue(ap_qid_t qid) } } if (rc == 0 && ap_using_interrupts()) { - rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator); + rc = ap_queue_enable_interruption(qid, ap_airq.lsi_ptr); /* If interruption mode is supported by the machine, * but an AP can not be enabled for interruption then * the AP will be discarded. */ @@ -821,13 +828,22 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state) static int ap_bus_resume(struct device *dev) { - int rc = 0; struct ap_device *ap_dev = to_ap_dev(dev); + int rc; if (ap_suspend_flag) { ap_suspend_flag = 0; - if (!ap_interrupts_available()) - ap_interrupt_indicator = NULL; + if (ap_interrupts_available()) { + if (!ap_using_interrupts()) { + rc = register_adapter_interrupt(&ap_airq); + ap_airq_flag = (rc == 0); + } + } else { + if (ap_using_interrupts()) { + unregister_adapter_interrupt(&ap_airq); + ap_airq_flag = 0; + } + } ap_query_configuration(); if (!user_set_domain) { ap_domain_index = -1; @@ -848,7 +864,10 @@ static int ap_bus_resume(struct device *dev) tasklet_schedule(&ap_tasklet); if (ap_thread_flag) rc = ap_poll_thread_start(); - } + else + rc = 0; + } else + rc = 0; if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) { spin_lock_bh(&ap_dev->lock); ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid), @@ -1266,7 +1285,7 @@ out: return rc; } -static void ap_interrupt_handler(void *unused1, void *unused2) +static void ap_interrupt_handler(struct airq_struct *airq) { inc_irq_stat(IRQIO_APB); tasklet_schedule(&ap_tasklet); @@ -1722,7 +1741,7 @@ static void ap_poll_all(unsigned long dummy) * important that no requests on any AP get lost. */ if (ap_using_interrupts()) - xchg((u8 *)ap_interrupt_indicator, 0); + xchg(ap_airq.lsi_ptr, 0); do { flags = 0; spin_lock(&ap_device_list_lock); @@ -1795,7 +1814,7 @@ static int ap_poll_thread_start(void) mutex_lock(&ap_poll_thread_mutex); if (!ap_poll_kthread) { ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); - rc = IS_ERR(ap_poll_kthread) ? PTR_ERR(ap_poll_kthread) : 0; + rc = PTR_RET(ap_poll_kthread); if (rc) ap_poll_kthread = NULL; } @@ -1881,13 +1900,8 @@ int __init ap_module_init(void) return -ENODEV; } if (ap_interrupts_available()) { - isc_register(AP_ISC); - ap_interrupt_indicator = s390_register_adapter_interrupt( - &ap_interrupt_handler, NULL, AP_ISC); - if (IS_ERR(ap_interrupt_indicator)) { - ap_interrupt_indicator = NULL; - isc_unregister(AP_ISC); - } + rc = register_adapter_interrupt(&ap_airq); + ap_airq_flag = (rc == 0); } register_reset_call(&ap_reset_call); @@ -1904,7 +1918,7 @@ int __init ap_module_init(void) /* Create /sys/devices/ap. */ ap_root_device = root_device_register("ap"); - rc = IS_ERR(ap_root_device) ? PTR_ERR(ap_root_device) : 0; + rc = PTR_RET(ap_root_device); if (rc) goto out_bus; @@ -1955,10 +1969,8 @@ out_bus: bus_unregister(&ap_bus_type); out: unregister_reset_call(&ap_reset_call); - if (ap_using_interrupts()) { - s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); - isc_unregister(AP_ISC); - } + if (ap_using_interrupts()) + unregister_adapter_interrupt(&ap_airq); return rc; } @@ -1994,10 +2006,8 @@ void ap_module_exit(void) bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); bus_unregister(&ap_bus_type); unregister_reset_call(&ap_reset_call); - if (ap_using_interrupts()) { - s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); - isc_unregister(AP_ISC); - } + if (ap_using_interrupts()) + unregister_adapter_interrupt(&ap_airq); } module_init(ap_module_init); diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 83bc9c5fa0c1..fd7b3bd80789 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -3348,7 +3348,7 @@ static int __init claw_init(void) } CLAW_DBF_TEXT(2, setup, "init_mod"); claw_root_dev = root_device_register("claw"); - ret = IS_ERR(claw_root_dev) ? PTR_ERR(claw_root_dev) : 0; + ret = PTR_RET(claw_root_dev); if (ret) goto register_err; ret = ccw_driver_register(&claw_ccw_driver); diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 676f12049a36..70b3a023100e 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1837,7 +1837,7 @@ static int __init ctcm_init(void) if (ret) goto out_err; ctcm_root_dev = root_device_register("ctcm"); - ret = IS_ERR(ctcm_root_dev) ? PTR_ERR(ctcm_root_dev) : 0; + ret = PTR_RET(ctcm_root_dev); if (ret) goto register_err; ret = ccw_driver_register(&ctcm_ccw_driver); diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index c645dc9e98af..f404f55b3191 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -2441,7 +2441,7 @@ __init lcs_init_module(void) if (rc) goto out_err; lcs_root_dev = root_device_register("lcs"); - rc = IS_ERR(lcs_root_dev) ? PTR_ERR(lcs_root_dev) : 0; + rc = PTR_RET(lcs_root_dev); if (rc) goto register_err; rc = ccw_driver_register(&lcs_ccw_driver); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 6cd0fc1b203a..70ce6b6fce3b 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5705,7 +5705,7 @@ static int __init qeth_core_init(void) if (rc) goto out_err; qeth_core_root_dev = root_device_register("qeth"); - rc = IS_ERR(qeth_core_root_dev) ? PTR_ERR(qeth_core_root_dev) : 0; + rc = PTR_RET(qeth_core_root_dev); if (rc) goto register_err; qeth_core_header_cache = kmem_cache_create("qeth_hdr", diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c index c487916a9d45..c0f4f4290dd6 100644 --- a/drivers/scsi/a3000.c +++ b/drivers/scsi/a3000.c @@ -280,18 +280,7 @@ static struct platform_driver amiga_a3000_scsi_driver = { }, }; -static int __init amiga_a3000_scsi_init(void) -{ - return platform_driver_probe(&amiga_a3000_scsi_driver, - amiga_a3000_scsi_probe); -} -module_init(amiga_a3000_scsi_init); - -static void __exit amiga_a3000_scsi_exit(void) -{ - platform_driver_unregister(&amiga_a3000_scsi_driver); -} -module_exit(amiga_a3000_scsi_exit); +module_platform_driver_probe(amiga_a3000_scsi_driver, amiga_a3000_scsi_probe); MODULE_DESCRIPTION("Amiga 3000 built-in SCSI"); MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/a4000t.c b/drivers/scsi/a4000t.c index 23c76f41883c..70c521f79f7c 100644 --- a/drivers/scsi/a4000t.c +++ b/drivers/scsi/a4000t.c @@ -116,20 +116,7 @@ static struct platform_driver amiga_a4000t_scsi_driver = { }, }; -static int __init amiga_a4000t_scsi_init(void) -{ - return platform_driver_probe(&amiga_a4000t_scsi_driver, - amiga_a4000t_scsi_probe); -} - -module_init(amiga_a4000t_scsi_init); - -static void __exit amiga_a4000t_scsi_exit(void) -{ - platform_driver_unregister(&amiga_a4000t_scsi_driver); -} - -module_exit(amiga_a4000t_scsi_exit); +module_platform_driver_probe(amiga_a4000t_scsi_driver, amiga_a4000t_scsi_probe); MODULE_AUTHOR("Alan Hourihane <alanh@fairlite.demon.co.uk> / " "Kars de Jong <jongk@linux-m68k.org>"); diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 846f475f62c1..90c95a3385d1 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -2026,7 +2026,7 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) static inline int make_local_pdev(adapter_t *adapter, struct pci_dev **pdev) { - *pdev = alloc_pci_dev(); + *pdev = pci_alloc_dev(NULL); if( *pdev == NULL ) return -1; diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index 42539ee2cb11..4c5aabe21755 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -229,8 +229,6 @@ static int scsi_runtime_resume(struct device *dev) static int scsi_runtime_idle(struct device *dev) { - int err; - dev_dbg(dev, "scsi_runtime_idle\n"); /* Insert hooks here for targets, hosts, and transport classes */ @@ -240,14 +238,11 @@ static int scsi_runtime_idle(struct device *dev) if (sdev->request_queue->dev) { pm_runtime_mark_last_busy(dev); - err = pm_runtime_autosuspend(dev); - } else { - err = pm_runtime_suspend(dev); + pm_runtime_autosuspend(dev); + return -EBUSY; } - } else { - err = pm_runtime_suspend(dev); } - return err; + return 0; } int scsi_autopm_get_device(struct scsi_device *sdev) diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index 7715de2629c1..74727851820d 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -63,12 +63,12 @@ void clk_rate_table_build(struct clk *clk, else freq = clk->parent->rate * mult / div; - freq_table[i].index = i; + freq_table[i].driver_data = i; freq_table[i].frequency = freq; } /* Termination entry */ - freq_table[i].index = i; + freq_table[i].driver_data = i; freq_table[i].frequency = CPUFREQ_TABLE_END; } diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c index afe9282629b9..8afa5a4589f2 100644 --- a/drivers/sh/pm_runtime.c +++ b/drivers/sh/pm_runtime.c @@ -25,7 +25,7 @@ static int default_platform_runtime_idle(struct device *dev) { /* suspend synchronously to disable clocks immediately */ - return pm_runtime_suspend(dev); + return 0; } static struct dev_pm_domain default_pm_domain = { diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index a537f8dffc09..8a6bb37910da 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -103,16 +103,6 @@ static void altera_spi_chipsel(struct spi_device *spi, int value) } } -static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) -{ - return 0; -} - -static int altera_spi_setup(struct spi_device *spi) -{ - return 0; -} - static inline unsigned int hw_txbyte(struct altera_spi *hw, int count) { if (hw->tx) { @@ -231,7 +221,6 @@ static int altera_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = 16; master->mode_bits = SPI_CS_HIGH; - master->setup = altera_spi_setup; hw = spi_master_get_devdata(master); platform_set_drvdata(pdev, hw); @@ -240,7 +229,6 @@ static int altera_spi_probe(struct platform_device *pdev) hw->bitbang.master = spi_master_get(master); if (!hw->bitbang.master) return err; - hw->bitbang.setup_transfer = altera_spi_setupxfer; hw->bitbang.chipselect = altera_spi_chipsel; hw->bitbang.txrx_bufs = altera_spi_txrx; @@ -285,7 +273,6 @@ static int altera_spi_probe(struct platform_device *pdev) exit_busy: err = -EBUSY; exit: - platform_set_drvdata(pdev, NULL); spi_master_put(master); return err; } @@ -296,7 +283,6 @@ static int altera_spi_remove(struct platform_device *dev) struct spi_master *master = hw->bitbang.master; spi_bitbang_stop(&hw->bitbang); - platform_set_drvdata(dev, NULL); spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index e504b7636058..0e06407a4670 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -155,9 +155,6 @@ static int ath79_spi_setup(struct spi_device *spi) { int status = 0; - if (spi->bits_per_word > 32) - return -EINVAL; - if (!spi->controller_state) { status = ath79_spi_setup_cs(spi); if (status) @@ -226,6 +223,7 @@ static int ath79_spi_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->setup = ath79_spi_setup; master->cleanup = ath79_spi_cleanup; if (pdata) { @@ -287,7 +285,6 @@ err_clk_put: err_unmap: iounmap(sp->base); err_put_master: - platform_set_drvdata(pdev, NULL); spi_master_put(sp->bitbang.master); return ret; @@ -302,7 +299,6 @@ static int ath79_spi_remove(struct platform_device *pdev) clk_disable(sp->clk); clk_put(sp->clk); iounmap(sp->base); - platform_set_drvdata(pdev, NULL); spi_master_put(sp->bitbang.master); return 0; diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 380387a47b1d..ea1ec009f44d 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -424,10 +424,15 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, return err; } -static bool filter(struct dma_chan *chan, void *slave) +static bool filter(struct dma_chan *chan, void *pdata) { - struct at_dma_slave *sl = slave; + struct atmel_spi_dma *sl_pdata = pdata; + struct at_dma_slave *sl; + if (!sl_pdata) + return false; + + sl = &sl_pdata->dma_slave; if (sl->dma_dev == chan->device->dev) { chan->private = sl; return true; @@ -438,24 +443,31 @@ static bool filter(struct dma_chan *chan, void *slave) static int atmel_spi_configure_dma(struct atmel_spi *as) { - struct at_dma_slave *sdata = &as->dma.dma_slave; struct dma_slave_config slave_config; + struct device *dev = &as->pdev->dev; int err; - if (sdata && sdata->dma_dev) { - dma_cap_mask_t mask; + dma_cap_mask_t mask; + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); - /* Try to grab two DMA channels */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - as->dma.chan_tx = dma_request_channel(mask, filter, sdata); - if (as->dma.chan_tx) - as->dma.chan_rx = - dma_request_channel(mask, filter, sdata); + as->dma.chan_tx = dma_request_slave_channel_compat(mask, filter, + &as->dma, + dev, "tx"); + if (!as->dma.chan_tx) { + dev_err(dev, + "DMA TX channel not available, SPI unable to use DMA\n"); + err = -EBUSY; + goto error; } - if (!as->dma.chan_rx || !as->dma.chan_tx) { - dev_err(&as->pdev->dev, - "DMA channel not available, SPI unable to use DMA\n"); + + as->dma.chan_rx = dma_request_slave_channel_compat(mask, filter, + &as->dma, + dev, "rx"); + + if (!as->dma.chan_rx) { + dev_err(dev, + "DMA RX channel not available, SPI unable to use DMA\n"); err = -EBUSY; goto error; } @@ -1268,13 +1280,6 @@ static int atmel_spi_setup(struct spi_device *spi) return -EINVAL; } - if (bits < 8 || bits > 16) { - dev_dbg(&spi->dev, - "setup: invalid bits_per_word %u (8 to 16)\n", - bits); - return -EINVAL; - } - /* see notes above re chipselect */ if (!atmel_spi_is_v2(as) && spi->chip_select == 0 @@ -1515,7 +1520,7 @@ static int atmel_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; master->num_chipselect = master->dev.of_node ? 0 : 4; diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 44dd34b6ad09..e1965553ab79 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -248,11 +248,6 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) hz = t->speed_hz; } - if (bpw < 4 || bpw > 24) { - dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n", - bpw); - return -EINVAL; - } if (hz > spi->max_speed_hz || hz > hw->freq_max || hz < hw->freq_min) { dev_err(&spi->dev, "setupxfer: clock rate=%d out of range\n", hz); @@ -296,12 +291,6 @@ static int au1550_spi_setup(struct spi_device *spi) { struct au1550_spi *hw = spi_master_get_devdata(spi->master); - if (spi->bits_per_word < 4 || spi->bits_per_word > 24) { - dev_err(&spi->dev, "setup: invalid bits_per_word=%d\n", - spi->bits_per_word); - return -EINVAL; - } - if (spi->max_speed_hz == 0) spi->max_speed_hz = hw->freq_max; if (spi->max_speed_hz > hw->freq_max @@ -782,6 +771,7 @@ static int au1550_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 24); hw = spi_master_get_devdata(master); @@ -987,8 +977,6 @@ static int au1550_spi_remove(struct platform_device *pdev) au1xxx_dbdma_chan_free(hw->dma_tx_ch); } - platform_set_drvdata(pdev, NULL); - spi_master_put(hw->master); return 0; } diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 89c0b5033114..a4185e492321 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -331,10 +331,9 @@ static int bcm2835_spi_probe(struct platform_device *pdev) goto out_master_put; } - bs->regs = devm_request_and_ioremap(&pdev->dev, res); - if (!bs->regs) { - dev_err(&pdev->dev, "could not request/map memory region\n"); - err = -ENODEV; + bs->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(bs->regs)) { + err = PTR_ERR(bs->regs); goto out_master_put; } diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index a4ec5f4ec817..9fd7a39b8029 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -124,17 +124,6 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, /* the spi->mode bits understood by this driver: */ #define MODEBITS (SPI_CPOL | SPI_CPHA) -static int bcm63xx_spi_setup(struct spi_device *spi) -{ - if (spi->bits_per_word != 8) { - dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, spi->bits_per_word); - return -EINVAL; - } - - return 0; -} - static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, unsigned int num_transfers) { @@ -277,13 +266,6 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master, * full-duplex transfers. */ list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->bits_per_word != 8) { - dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, t->bits_per_word); - status = -EINVAL; - goto exit; - } - if (!first) first = t; @@ -430,11 +412,11 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) master->bus_num = pdata->bus_num; master->num_chipselect = pdata->num_chipselect; - master->setup = bcm63xx_spi_setup; master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer; master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; master->transfer_one_message = bcm63xx_spi_transfer_one; master->mode_bits = MODEBITS; + master->bits_per_word_mask = SPI_BPW_MASK(8); bs->msg_type_shift = pdata->msg_type_shift; bs->msg_ctl_width = pdata->msg_ctl_width; bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); @@ -469,7 +451,6 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) out_clk_disable: clk_disable_unprepare(clk); out_err: - platform_set_drvdata(pdev, NULL); spi_master_put(master); out_clk: clk_put(clk); @@ -491,8 +472,6 @@ static int bcm63xx_spi_remove(struct platform_device *pdev) clk_disable_unprepare(bs->clk); clk_put(bs->clk); - platform_set_drvdata(pdev, 0); - spi_master_put(master); return 0; diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 39b0d1711b4e..07ec597f9732 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -417,7 +417,7 @@ bfin_sport_spi_pump_transfers(unsigned long data) /* Bits per word setup */ bits_per_word = transfer->bits_per_word; - if (bits_per_word % 16 == 0) + if (bits_per_word == 16) drv_data->ops = &bfin_sport_transfer_ops_u16; else drv_data->ops = &bfin_sport_transfer_ops_u8; @@ -600,13 +600,6 @@ bfin_sport_spi_setup(struct spi_device *spi) } } - if (spi->bits_per_word % 8) { - dev_err(&spi->dev, "%d bits_per_word is not supported\n", - spi->bits_per_word); - ret = -EINVAL; - goto error; - } - /* translate common spi framework into our register * following configure contents are same for tx and rx. */ @@ -778,6 +771,7 @@ static int bfin_sport_spi_probe(struct platform_device *pdev) drv_data->pin_req = platform_info->pin_req; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; master->cleanup = bfin_sport_spi_cleanup; @@ -882,9 +876,6 @@ static int bfin_sport_spi_remove(struct platform_device *pdev) peripheral_free_list(drv_data->pin_req); - /* Prevent double remove */ - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 317f564c899c..59a73424419c 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -643,21 +643,16 @@ static void bfin_spi_pump_transfers(unsigned long data) /* Bits per word setup */ bits_per_word = transfer->bits_per_word; - if (bits_per_word % 16 == 0) { + if (bits_per_word == 16) { drv_data->n_bytes = bits_per_word/8; drv_data->len = (transfer->len) >> 1; cr_width = BIT_CTL_WORDSIZE; drv_data->ops = &bfin_bfin_spi_transfer_ops_u16; - } else if (bits_per_word % 8 == 0) { + } else if (bits_per_word == 8) { drv_data->n_bytes = bits_per_word/8; drv_data->len = transfer->len; cr_width = 0; drv_data->ops = &bfin_bfin_spi_transfer_ops_u8; - } else { - dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n"); - message->status = -EINVAL; - bfin_spi_giveback(drv_data); - return; } cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE); cr |= cr_width; @@ -808,13 +803,13 @@ static void bfin_spi_pump_transfers(unsigned long data) bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val); else { int loop; - if (bits_per_word % 16 == 0) { + if (bits_per_word == 16) { u16 *buf = (u16 *)drv_data->tx; for (loop = 0; loop < bits_per_word / 16; loop++) { bfin_write(&drv_data->regs->tdbr, *buf++); } - } else if (bits_per_word % 8 == 0) { + } else if (bits_per_word == 8) { u8 *buf = (u8 *)drv_data->tx; for (loop = 0; loop < bits_per_word / 8; loop++) bfin_write(&drv_data->regs->tdbr, *buf++); @@ -1033,12 +1028,6 @@ static int bfin_spi_setup(struct spi_device *spi) chip->ctl_reg &= bfin_ctl_reg; } - if (spi->bits_per_word % 8) { - dev_err(&spi->dev, "%d bits_per_word is not supported\n", - spi->bits_per_word); - goto error; - } - /* translate common spi framework into our register */ if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) { dev_err(&spi->dev, "unsupported spi modes detected\n"); @@ -1299,7 +1288,7 @@ static int bfin_spi_probe(struct platform_device *pdev) /* the spi->mode bits supported by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; - + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; master->cleanup = bfin_spi_cleanup; @@ -1418,9 +1407,6 @@ static int bfin_spi_remove(struct platform_device *pdev) peripheral_free_list(drv_data->pin_req); - /* Prevent double remove */ - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index a11cbf02691a..17965fe225cc 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -42,12 +42,6 @@ static int spi_clps711x_setup(struct spi_device *spi) { struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master); - if (spi->bits_per_word != 8) { - dev_err(&spi->dev, "Unsupported master bus width %i\n", - spi->bits_per_word); - return -EINVAL; - } - /* We are expect that SPI-device is not selected */ gpio_direction_output(hw->chipselect[spi->chip_select], !(spi->mode & SPI_CS_HIGH)); @@ -190,6 +184,7 @@ static int spi_clps711x_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->mode_bits = SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_MASK(8); master->num_chipselect = pdata->num_chipselect; master->setup = spi_clps711x_setup; master->transfer_one_message = spi_clps711x_transfer_one_message; @@ -254,7 +249,6 @@ err_out: if (gpio_is_valid(hw->chipselect[i])) gpio_free(hw->chipselect[i]); - platform_set_drvdata(pdev, NULL); spi_master_put(master); kfree(master); @@ -274,7 +268,6 @@ static int spi_clps711x_remove(struct platform_device *pdev) gpio_free(hw->chipselect[i]); devm_clk_put(&pdev->dev, hw->spi_clk); - platform_set_drvdata(pdev, NULL); spi_unregister_master(master); kfree(master); diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 7b5cc9e4e94d..0631b9d4a5de 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -312,10 +312,7 @@ static int mcfqspi_transfer_one_message(struct spi_master *master, bool cs_high = spi->mode & SPI_CS_HIGH; u16 qmr = MCFQSPI_QMR_MSTR; - if (t->bits_per_word) - qmr |= t->bits_per_word << 10; - else - qmr |= spi->bits_per_word << 10; + qmr |= t->bits_per_word << 10; if (spi->mode & SPI_CPHA) qmr |= MCFQSPI_QMR_CPHA; if (spi->mode & SPI_CPOL) @@ -377,11 +374,6 @@ static int mcfqspi_unprepare_transfer_hw(struct spi_master *master) static int mcfqspi_setup(struct spi_device *spi) { - if ((spi->bits_per_word < 8) || (spi->bits_per_word > 16)) { - dev_dbg(&spi->dev, "%d bits per word is not supported\n", - spi->bits_per_word); - return -EINVAL; - } if (spi->chip_select >= spi->master->num_chipselect) { dev_dbg(&spi->dev, "%d chip select is out of range\n", spi->chip_select); @@ -408,6 +400,12 @@ static int mcfqspi_probe(struct platform_device *pdev) struct mcfqspi_platform_data *pdata; int status; + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_dbg(&pdev->dev, "platform data is missing\n"); + return -ENOENT; + } + master = spi_alloc_master(&pdev->dev, sizeof(*mcfqspi)); if (master == NULL) { dev_dbg(&pdev->dev, "spi_alloc_master failed\n"); @@ -458,11 +456,6 @@ static int mcfqspi_probe(struct platform_device *pdev) } clk_enable(mcfqspi->clk); - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_dbg(&pdev->dev, "platform data is missing\n"); - goto fail4; - } master->bus_num = pdata->bus_num; master->num_chipselect = pdata->num_chipselect; @@ -477,6 +470,7 @@ static int mcfqspi_probe(struct platform_device *pdev) mcfqspi->dev = &pdev->dev; master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); master->setup = mcfqspi_setup; master->transfer_one_message = mcfqspi_transfer_one_message; master->prepare_transfer_hardware = mcfqspi_prepare_transfer_hw; @@ -524,7 +518,6 @@ static int mcfqspi_remove(struct platform_device *pdev) /* disable the hardware (set the baud rate to 0) */ mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); - platform_set_drvdata(pdev, NULL); mcfqspi_cs_teardown(mcfqspi); clk_disable(mcfqspi->clk); clk_put(mcfqspi->clk); diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 50b13c9b1ab6..222d3e37fc28 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -299,16 +299,15 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, * Assign function pointer to appropriate transfer method * 8bit, 16bit or 32bit transfer */ - if (bits_per_word <= 8 && bits_per_word >= 2) { + if (bits_per_word <= 8) { dspi->get_rx = davinci_spi_rx_buf_u8; dspi->get_tx = davinci_spi_tx_buf_u8; dspi->bytes_per_word[spi->chip_select] = 1; - } else if (bits_per_word <= 16 && bits_per_word >= 2) { + } else { dspi->get_rx = davinci_spi_rx_buf_u16; dspi->get_tx = davinci_spi_tx_buf_u16; dspi->bytes_per_word[spi->chip_select] = 2; - } else - return -EINVAL; + } if (!hz) hz = spi->max_speed_hz; @@ -865,7 +864,7 @@ static int davinci_spi_probe(struct platform_device *pdev) goto err; } - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); dspi = spi_master_get_devdata(master); if (dspi == NULL) { @@ -933,6 +932,7 @@ static int davinci_spi_probe(struct platform_device *pdev) master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 16); master->setup = davinci_spi_setup; dspi->bitbang.chipselect = davinci_spi_chipselect; @@ -1044,7 +1044,7 @@ static int davinci_spi_remove(struct platform_device *pdev) struct spi_master *master; struct resource *r; - master = dev_get_drvdata(&pdev->dev); + master = platform_get_drvdata(pdev); dspi = spi_master_get_devdata(master); spi_bitbang_stop(&dspi->bitbang); diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 4a6d5c9057a4..4aa8be865cc0 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -111,8 +111,6 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev); struct resource *mem; - platform_set_drvdata(pdev, NULL); - clk_disable(dwsmmio->clk); clk_put(dwsmmio->clk); dwsmmio->clk = NULL; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index c1abc06899e7..79c958e49f61 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -457,19 +457,7 @@ static void pump_transfers(unsigned long data) } if (transfer->bits_per_word) { bits = transfer->bits_per_word; - - switch (bits) { - case 8: - case 16: - dws->n_bytes = dws->dma_width = bits >> 3; - break; - default: - printk(KERN_ERR "MRST SPI0: unsupported bits:" - "%db\n", bits); - message->status = -EIO; - goto early_exit; - } - + dws->n_bytes = dws->dma_width = bits >> 3; cr0 = (bits - 1) | (chip->type << SPI_FRF_OFFSET) | (spi->mode << SPI_MODE_OFFSET) @@ -629,9 +617,6 @@ static int dw_spi_setup(struct spi_device *spi) struct dw_spi_chip *chip_info = NULL; struct chip_data *chip; - if (spi->bits_per_word != 8 && spi->bits_per_word != 16) - return -EINVAL; - /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (!chip) { @@ -660,16 +645,12 @@ static int dw_spi_setup(struct spi_device *spi) chip->enable_dma = chip_info->enable_dma; } - if (spi->bits_per_word <= 8) { + if (spi->bits_per_word == 8) { chip->n_bytes = 1; chip->dma_width = 1; - } else if (spi->bits_per_word <= 16) { + } else if (spi->bits_per_word == 16) { chip->n_bytes = 2; chip->dma_width = 2; - } else { - /* Never take >16b case for MRST SPIC */ - dev_err(&spi->dev, "invalid wordsize\n"); - return -EINVAL; } chip->bits_per_word = spi->bits_per_word; @@ -824,6 +805,7 @@ int dw_spi_add_host(struct dw_spi *dws) } master->mode_bits = SPI_CPOL | SPI_CPHA; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->bus_num = dws->bus_num; master->num_chipselect = dws->num_cs; master->cleanup = dw_spi_cleanup; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index d7bac60253c9..cad30b8a1d71 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -296,12 +296,6 @@ static int ep93xx_spi_setup(struct spi_device *spi) struct ep93xx_spi *espi = spi_master_get_devdata(spi->master); struct ep93xx_spi_chip *chip; - if (spi->bits_per_word < 4 || spi->bits_per_word > 16) { - dev_err(&espi->pdev->dev, "invalid bits per word %d\n", - spi->bits_per_word); - return -EINVAL; - } - chip = spi_get_ctldata(spi); if (!chip) { dev_dbg(&espi->pdev->dev, "initial setup for %s\n", @@ -365,10 +359,6 @@ static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) /* first validate each transfer */ list_for_each_entry(t, &msg->transfers, transfer_list) { - if (t->bits_per_word) { - if (t->bits_per_word < 4 || t->bits_per_word > 16) - return -EINVAL; - } if (t->speed_hz && t->speed_hz < espi->min_rate) return -EINVAL; } @@ -1046,6 +1036,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = info->num_chipselect; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); platform_set_drvdata(pdev, master); @@ -1104,6 +1095,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) espi->wq = create_singlethread_workqueue("ep93xx_spid"); if (!espi->wq) { dev_err(&pdev->dev, "unable to create workqueue\n"); + error = -ENOMEM; goto fail_free_dma; } INIT_WORK(&espi->msg_work, ep93xx_spi_work); @@ -1132,7 +1124,6 @@ fail_put_clock: clk_put(espi->clk); fail_release_master: spi_master_put(master); - platform_set_drvdata(pdev, NULL); return error; } @@ -1167,7 +1158,6 @@ static int ep93xx_spi_remove(struct platform_device *pdev) ep93xx_spi_release_dma(espi); clk_put(espi->clk); - platform_set_drvdata(pdev, NULL); spi_unregister_master(master); return 0; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 24610ca8955d..6a74d7848d93 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -144,10 +144,6 @@ static int fsl_espi_setup_transfer(struct spi_device *spi, if (!bits_per_word) bits_per_word = spi->bits_per_word; - /* Make sure its a bit width we support [4..16] */ - if ((bits_per_word < 4) || (bits_per_word > 16)) - return -EINVAL; - if (!hz) hz = spi->max_speed_hz; @@ -157,12 +153,10 @@ static int fsl_espi_setup_transfer(struct spi_device *spi, cs->get_tx = mpc8xxx_spi_tx_buf_u32; if (bits_per_word <= 8) { cs->rx_shift = 8 - bits_per_word; - } else if (bits_per_word <= 16) { + } else { cs->rx_shift = 16 - bits_per_word; if (spi->mode & SPI_LSB_FIRST) cs->get_tx = fsl_espi_tx_buf_lsb; - } else { - return -EINVAL; } mpc8xxx_spi->rx_shift = cs->rx_shift; @@ -609,6 +603,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev, if (ret) goto err_probe; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); master->setup = fsl_espi_setup; mpc8xxx_spi = spi_master_get_devdata(master); diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index a91db0e57b23..e947f2d1b2f5 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -61,7 +61,7 @@ struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata) return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); } -void mpc8xxx_spi_work(struct work_struct *work) +static void mpc8xxx_spi_work(struct work_struct *work) { struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi, work); diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 14e202ee7036..41e89c3e3edc 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -853,7 +853,7 @@ err: static int of_fsl_spi_remove(struct platform_device *ofdev) { - struct spi_master *master = dev_get_drvdata(&ofdev->dev); + struct spi_master *master = platform_get_drvdata(ofdev); struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); int ret; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 0021fc4c45bc..a54524cf42cc 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -239,9 +239,6 @@ static int spi_gpio_setup(struct spi_device *spi) struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); struct device_node *np = spi->master->dev.of_node; - if (spi->bits_per_word > 32) - return -EINVAL; - if (np) { /* * In DT environments, the CS GPIOs have already been @@ -446,6 +443,7 @@ static int spi_gpio_probe(struct platform_device *pdev) if (pdata) spi_gpio->pdata = *pdata; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->flags = master_flags; master->bus_num = pdev->id; master->num_chipselect = SPI_N_CHIPSEL; @@ -514,8 +512,6 @@ static int spi_gpio_remove(struct platform_device *pdev) status = spi_bitbang_stop(&spi_gpio->bitbang); spi_master_put(spi_gpio->bitbang.master); - platform_set_drvdata(pdev, NULL); - if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 0befeeb522f4..7db4f43ee4d8 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -37,7 +37,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> -#include <linux/pinctrl/consumer.h> #include <linux/platform_data/spi-imx.h> @@ -698,11 +697,10 @@ static int spi_imx_setupxfer(struct spi_device *spi, } else if (config.bpw <= 16) { spi_imx->rx = spi_imx_buf_rx_u16; spi_imx->tx = spi_imx_buf_tx_u16; - } else if (config.bpw <= 32) { + } else { spi_imx->rx = spi_imx_buf_rx_u32; spi_imx->tx = spi_imx_buf_tx_u32; - } else - BUG(); + } spi_imx->devtype_data->config(spi_imx, &config); @@ -760,7 +758,6 @@ static int spi_imx_probe(struct platform_device *pdev) struct spi_master *master; struct spi_imx_data *spi_imx; struct resource *res; - struct pinctrl *pinctrl; int i, ret, num_cs; if (!np && !mxc_platform_info) { @@ -783,6 +780,7 @@ static int spi_imx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->bus_num = pdev->id; master->num_chipselect = num_cs; @@ -848,12 +846,6 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_iounmap; } - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto out_free_irq; - } - spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(spi_imx->clk_ipg)) { ret = PTR_ERR(spi_imx->clk_ipg); @@ -902,7 +894,6 @@ out_gpio_free: } spi_master_put(master); kfree(master); - platform_set_drvdata(pdev, NULL); return ret; } @@ -929,8 +920,6 @@ static int spi_imx_remove(struct platform_device *pdev) release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index dfddf336912d..29fce6af5145 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -21,7 +21,6 @@ #include <linux/interrupt.h> #include <linux/of_address.h> #include <linux/of_platform.h> -#include <linux/workqueue.h> #include <linux/completion.h> #include <linux/io.h> #include <linux/delay.h> @@ -33,24 +32,15 @@ struct mpc512x_psc_spi { void (*cs_control)(struct spi_device *spi, bool on); - u32 sysclk; /* driver internal data */ struct mpc52xx_psc __iomem *psc; struct mpc512x_psc_fifo __iomem *fifo; unsigned int irq; u8 bits_per_word; - u8 busy; u32 mclk; - u8 eofbyte; - struct workqueue_struct *workqueue; - struct work_struct work; - - struct list_head queue; - spinlock_t lock; /* Message queue lock */ - - struct completion done; + struct completion txisrdone; }; /* controller state */ @@ -136,145 +126,223 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi, struct spi_transfer *t) { struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master); - struct mpc52xx_psc __iomem *psc = mps->psc; struct mpc512x_psc_fifo __iomem *fifo = mps->fifo; - size_t len = t->len; + size_t tx_len = t->len; + size_t rx_len = t->len; u8 *tx_buf = (u8 *)t->tx_buf; u8 *rx_buf = (u8 *)t->rx_buf; if (!tx_buf && !rx_buf && t->len) return -EINVAL; - /* Zero MR2 */ - in_8(&psc->mode); - out_8(&psc->mode, 0x0); - - /* enable transmiter/receiver */ - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); - - while (len) { - int count; - int i; + while (rx_len || tx_len) { + size_t txcount; u8 data; size_t fifosz; - int rxcount; + size_t rxcount; + int rxtries; /* - * The number of bytes that can be sent at a time - * depends on the fifo size. + * send the TX bytes in as large a chunk as possible + * but neither exceed the TX nor the RX FIFOs */ fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->txsz)); - count = min(fifosz, len); - - for (i = count; i > 0; i--) { - data = tx_buf ? *tx_buf++ : 0; - if (len == EOFBYTE && t->cs_change) - setbits32(&fifo->txcmd, MPC512x_PSC_FIFO_EOF); - out_8(&fifo->txdata_8, data); - len--; + txcount = min(fifosz, tx_len); + fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->rxsz)); + fifosz -= in_be32(&fifo->rxcnt) + 1; + txcount = min(fifosz, txcount); + if (txcount) { + + /* fill the TX FIFO */ + while (txcount-- > 0) { + data = tx_buf ? *tx_buf++ : 0; + if (tx_len == EOFBYTE && t->cs_change) + setbits32(&fifo->txcmd, + MPC512x_PSC_FIFO_EOF); + out_8(&fifo->txdata_8, data); + tx_len--; + } + + /* have the ISR trigger when the TX FIFO is empty */ + INIT_COMPLETION(mps->txisrdone); + out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY); + out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY); + wait_for_completion(&mps->txisrdone); } - INIT_COMPLETION(mps->done); + /* + * consume as much RX data as the FIFO holds, while we + * iterate over the transfer's TX data length + * + * only insist in draining all the remaining RX bytes + * when the TX bytes were exhausted (that's at the very + * end of this transfer, not when still iterating over + * the transfer's chunks) + */ + rxtries = 50; + do { + + /* + * grab whatever was in the FIFO when we started + * looking, don't bother fetching what was added to + * the FIFO while we read from it -- we'll return + * here eventually and prefer sending out remaining + * TX data + */ + fifosz = in_be32(&fifo->rxcnt); + rxcount = min(fifosz, rx_len); + while (rxcount-- > 0) { + data = in_8(&fifo->rxdata_8); + if (rx_buf) + *rx_buf++ = data; + rx_len--; + } - /* interrupt on tx fifo empty */ - out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY); - out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY); + /* + * come back later if there still is TX data to send, + * bail out of the RX drain loop if all of the TX data + * was sent and all of the RX data was received (i.e. + * when the transmission has completed) + */ + if (tx_len) + break; + if (!rx_len) + break; - wait_for_completion(&mps->done); + /* + * TX data transmission has completed while RX data + * is still pending -- that's a transient situation + * which depends on wire speed and specific + * hardware implementation details (buffering) yet + * should resolve very quickly + * + * just yield for a moment to not hog the CPU for + * too long when running SPI at low speed + * + * the timeout range is rather arbitrary and tries + * to balance throughput against system load; the + * chosen values result in a minimal timeout of 50 + * times 10us and thus work at speeds as low as + * some 20kbps, while the maximum timeout at the + * transfer's end could be 5ms _if_ nothing else + * ticks in the system _and_ RX data still wasn't + * received, which only occurs in situations that + * are exceptional; removing the unpredictability + * of the timeout either decreases throughput + * (longer timeouts), or puts more load on the + * system (fixed short timeouts) or requires the + * use of a timeout API instead of a counter and an + * unknown inner delay + */ + usleep_range(10, 100); + + } while (--rxtries > 0); + if (!tx_len && rx_len && !rxtries) { + /* + * not enough RX bytes even after several retries + * and the resulting rather long timeout? + */ + rxcount = in_be32(&fifo->rxcnt); + dev_warn(&spi->dev, + "short xfer, missing %zd RX bytes, FIFO level %zd\n", + rx_len, rxcount); + } - mdelay(1); + /* + * drain and drop RX data which "should not be there" in + * the first place, for undisturbed transmission this turns + * into a NOP (except for the FIFO level fetch) + */ + if (!tx_len && !rx_len) { + while (in_be32(&fifo->rxcnt)) + in_8(&fifo->rxdata_8); + } - /* rx fifo should have count bytes in it */ - rxcount = in_be32(&fifo->rxcnt); - if (rxcount != count) - mdelay(1); + } + return 0; +} - rxcount = in_be32(&fifo->rxcnt); - if (rxcount != count) { - dev_warn(&spi->dev, "expected %d bytes in rx fifo " - "but got %d\n", count, rxcount); +static int mpc512x_psc_spi_msg_xfer(struct spi_master *master, + struct spi_message *m) +{ + struct spi_device *spi; + unsigned cs_change; + int status; + struct spi_transfer *t; + + spi = m->spi; + cs_change = 1; + status = 0; + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->bits_per_word || t->speed_hz) { + status = mpc512x_psc_spi_transfer_setup(spi, t); + if (status < 0) + break; } - rxcount = min(rxcount, count); - for (i = rxcount; i > 0; i--) { - data = in_8(&fifo->rxdata_8); - if (rx_buf) - *rx_buf++ = data; - } - while (in_be32(&fifo->rxcnt)) { - in_8(&fifo->rxdata_8); - } + if (cs_change) + mpc512x_psc_spi_activate_cs(spi); + cs_change = t->cs_change; + + status = mpc512x_psc_spi_transfer_rxtx(spi, t); + if (status) + break; + m->actual_length += t->len; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (cs_change) + mpc512x_psc_spi_deactivate_cs(spi); } - /* disable transmiter/receiver and fifo interrupt */ - out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); - out_be32(&fifo->tximr, 0); - return 0; + + m->status = status; + m->complete(m->context); + + if (status || !cs_change) + mpc512x_psc_spi_deactivate_cs(spi); + + mpc512x_psc_spi_transfer_setup(spi, NULL); + + spi_finalize_current_message(master); + return status; } -static void mpc512x_psc_spi_work(struct work_struct *work) +static int mpc512x_psc_spi_prep_xfer_hw(struct spi_master *master) { - struct mpc512x_psc_spi *mps = container_of(work, - struct mpc512x_psc_spi, - work); - - spin_lock_irq(&mps->lock); - mps->busy = 1; - while (!list_empty(&mps->queue)) { - struct spi_message *m; - struct spi_device *spi; - struct spi_transfer *t = NULL; - unsigned cs_change; - int status; - - m = container_of(mps->queue.next, struct spi_message, queue); - list_del_init(&m->queue); - spin_unlock_irq(&mps->lock); - - spi = m->spi; - cs_change = 1; - status = 0; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->bits_per_word || t->speed_hz) { - status = mpc512x_psc_spi_transfer_setup(spi, t); - if (status < 0) - break; - } + struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); + struct mpc52xx_psc __iomem *psc = mps->psc; - if (cs_change) - mpc512x_psc_spi_activate_cs(spi); - cs_change = t->cs_change; + dev_dbg(&master->dev, "%s()\n", __func__); - status = mpc512x_psc_spi_transfer_rxtx(spi, t); - if (status) - break; - m->actual_length += t->len; + /* Zero MR2 */ + in_8(&psc->mode); + out_8(&psc->mode, 0x0); - if (t->delay_usecs) - udelay(t->delay_usecs); + /* enable transmitter/receiver */ + out_8(&psc->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE); - if (cs_change) - mpc512x_psc_spi_deactivate_cs(spi); - } + return 0; +} - m->status = status; - m->complete(m->context); +static int mpc512x_psc_spi_unprep_xfer_hw(struct spi_master *master) +{ + struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); + struct mpc52xx_psc __iomem *psc = mps->psc; + struct mpc512x_psc_fifo __iomem *fifo = mps->fifo; - if (status || !cs_change) - mpc512x_psc_spi_deactivate_cs(spi); + dev_dbg(&master->dev, "%s()\n", __func__); - mpc512x_psc_spi_transfer_setup(spi, NULL); + /* disable transmitter/receiver and fifo interrupt */ + out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE); + out_be32(&fifo->tximr, 0); - spin_lock_irq(&mps->lock); - } - mps->busy = 0; - spin_unlock_irq(&mps->lock); + return 0; } static int mpc512x_psc_spi_setup(struct spi_device *spi) { - struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master); struct mpc512x_psc_spi_cs *cs = spi->controller_state; - unsigned long flags; int ret; if (spi->bits_per_word % 8) @@ -303,28 +371,6 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi) cs->bits_per_word = spi->bits_per_word; cs->speed_hz = spi->max_speed_hz; - spin_lock_irqsave(&mps->lock, flags); - if (!mps->busy) - mpc512x_psc_spi_deactivate_cs(spi); - spin_unlock_irqrestore(&mps->lock, flags); - - return 0; -} - -static int mpc512x_psc_spi_transfer(struct spi_device *spi, - struct spi_message *m) -{ - struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master); - unsigned long flags; - - m->actual_length = 0; - m->status = -EINPROGRESS; - - spin_lock_irqsave(&mps->lock, flags); - list_add_tail(&m->queue, &mps->queue); - queue_work(mps->workqueue, &mps->work); - spin_unlock_irqrestore(&mps->lock, flags); - return 0; } @@ -407,12 +453,12 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id) struct mpc512x_psc_spi *mps = (struct mpc512x_psc_spi *)dev_id; struct mpc512x_psc_fifo __iomem *fifo = mps->fifo; - /* clear interrupt and wake up the work queue */ + /* clear interrupt and wake up the rx/tx routine */ if (in_be32(&fifo->txisr) & in_be32(&fifo->tximr) & MPC512x_PSC_FIFO_EMPTY) { out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY); out_be32(&fifo->tximr, 0); - complete(&mps->done); + complete(&mps->txisrdone); return IRQ_HANDLED; } return IRQ_NONE; @@ -444,18 +490,18 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, if (pdata == NULL) { mps->cs_control = mpc512x_spi_cs_control; - mps->sysclk = 0; master->bus_num = bus_num; } else { mps->cs_control = pdata->cs_control; - mps->sysclk = pdata->sysclk; master->bus_num = pdata->bus_num; master->num_chipselect = pdata->max_chipselect; } master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; master->setup = mpc512x_psc_spi_setup; - master->transfer = mpc512x_psc_spi_transfer; + master->prepare_transfer_hardware = mpc512x_psc_spi_prep_xfer_hw; + master->transfer_one_message = mpc512x_psc_spi_msg_xfer; + master->unprepare_transfer_hardware = mpc512x_psc_spi_unprep_xfer_hw; master->cleanup = mpc512x_psc_spi_cleanup; master->dev.of_node = dev->of_node; @@ -473,31 +519,18 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr, "mpc512x-psc-spi", mps); if (ret) goto free_master; + init_completion(&mps->txisrdone); ret = mpc512x_psc_spi_port_config(master, mps); if (ret < 0) goto free_irq; - spin_lock_init(&mps->lock); - init_completion(&mps->done); - INIT_WORK(&mps->work, mpc512x_psc_spi_work); - INIT_LIST_HEAD(&mps->queue); - - mps->workqueue = - create_singlethread_workqueue(dev_name(master->dev.parent)); - if (mps->workqueue == NULL) { - ret = -EBUSY; - goto free_irq; - } - ret = spi_register_master(master); if (ret < 0) - goto unreg_master; + goto free_irq; return ret; -unreg_master: - destroy_workqueue(mps->workqueue); free_irq: free_irq(mps->irq, mps); free_master: @@ -513,8 +546,6 @@ static int mpc512x_psc_spi_do_remove(struct device *dev) struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); struct mpc512x_psc_spi *mps = spi_master_get_devdata(master); - flush_workqueue(mps->workqueue); - destroy_workqueue(mps->workqueue); spi_unregister_master(master); free_irq(mps->irq, mps); if (mps->psc) diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index 291120b37dbb..fed0571d4dec 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -481,7 +481,7 @@ static int mpc52xx_psc_spi_of_probe(struct platform_device *op) static int mpc52xx_psc_spi_of_remove(struct platform_device *op) { - struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); + struct spi_master *master = spi_master_get(platform_get_drvdata(op)); struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); flush_workqueue(mps->workqueue); diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 29f77056eedc..7c675fe83101 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -438,7 +438,7 @@ static int mpc52xx_spi_probe(struct platform_device *op) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; master->dev.of_node = op->dev.of_node; - dev_set_drvdata(&op->dev, master); + platform_set_drvdata(op, master); ms = spi_master_get_devdata(master); ms->master = master; @@ -529,7 +529,7 @@ static int mpc52xx_spi_probe(struct platform_device *op) static int mpc52xx_spi_remove(struct platform_device *op) { - struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); + struct spi_master *master = spi_master_get(platform_get_drvdata(op)); struct mpc52xx_spi *ms = spi_master_get_devdata(master); int i; diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 84982768cd10..424d38e59421 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -46,7 +46,6 @@ #include <linux/gpio.h> #include <linux/regulator/consumer.h> #include <linux/module.h> -#include <linux/pinctrl/consumer.h> #include <linux/stmp_device.h> #include <linux/spi/spi.h> #include <linux/spi/mxs-spi.h> @@ -75,12 +74,6 @@ static int mxs_spi_setup_transfer(struct spi_device *dev, if (t && t->bits_per_word) bits_per_word = t->bits_per_word; - if (bits_per_word != 8) { - dev_err(&dev->dev, "%s, unsupported bits_per_word=%d\n", - __func__, bits_per_word); - return -EINVAL; - } - hz = dev->max_speed_hz; if (t && t->speed_hz) hz = min(hz, t->speed_hz); @@ -506,7 +499,6 @@ static int mxs_spi_probe(struct platform_device *pdev) struct mxs_spi *spi; struct mxs_ssp *ssp; struct resource *iores; - struct pinctrl *pinctrl; struct clk *clk; void __iomem *base; int devid, clk_freq; @@ -528,10 +520,6 @@ static int mxs_spi_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - return PTR_ERR(pinctrl); - clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) return PTR_ERR(clk); @@ -548,6 +536,7 @@ static int mxs_spi_probe(struct platform_device *pdev) master->transfer_one_message = mxs_spi_transfer_one; master->setup = mxs_spi_setup; + master->bits_per_word_mask = SPI_BPW_MASK(8); master->mode_bits = SPI_CPOL | SPI_CPHA; master->num_chipselect = 3; master->dev.of_node = np; diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index b3f9ec83ef73..2ad3d74ac021 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -174,17 +174,6 @@ static void nuc900_spi_gobusy(struct nuc900_spi *hw) spin_unlock_irqrestore(&hw->lock, flags); } -static int nuc900_spi_setupxfer(struct spi_device *spi, - struct spi_transfer *t) -{ - return 0; -} - -static int nuc900_spi_setup(struct spi_device *spi) -{ - return 0; -} - static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count) { return hw->tx ? hw->tx[count] : 0; @@ -377,10 +366,8 @@ static int nuc900_spi_probe(struct platform_device *pdev) master->num_chipselect = hw->pdata->num_cs; master->bus_num = hw->pdata->bus_num; hw->bitbang.master = hw->master; - hw->bitbang.setup_transfer = nuc900_spi_setupxfer; hw->bitbang.chipselect = nuc900_spi_chipsel; hw->bitbang.txrx_bufs = nuc900_spi_txrx; - hw->bitbang.master->setup = nuc900_spi_setup; hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (hw->res == NULL) { @@ -459,8 +446,6 @@ static int nuc900_spi_remove(struct platform_device *dev) free_irq(hw->irq, hw); - platform_set_drvdata(dev, NULL); - spi_bitbang_stop(&hw->bitbang); clk_disable(hw->clk); diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index e60a776ed2d4..58deb79d046b 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -368,7 +368,6 @@ exit_gpio: exit_busy: err = -EBUSY; exit: - platform_set_drvdata(pdev, NULL); spi_master_put(master); return err; } @@ -382,7 +381,6 @@ static int tiny_spi_remove(struct platform_device *pdev) spi_bitbang_stop(&hw->bitbang); for (i = 0; i < hw->gpio_cs_count; i++) gpio_free(hw->gpio_cs[i]); - platform_set_drvdata(pdev, NULL); spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 78d29a18dcc4..ee25670f8cfd 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -298,12 +298,6 @@ static int omap1_spi100k_setup(struct spi_device *spi) struct omap1_spi100k *spi100k; struct omap1_spi100k_cs *cs = spi->controller_state; - if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { - dev_dbg(&spi->dev, "setup: unsupported %d bit words\n", - spi->bits_per_word); - return -EINVAL; - } - spi100k = spi_master_get_devdata(spi->master); if (!cs) { @@ -451,10 +445,7 @@ static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) unsigned len = t->len; if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ - || (len && !(rx_buf || tx_buf)) - || (t->bits_per_word && - ( t->bits_per_word < 4 - || t->bits_per_word > 32))) { + || (len && !(rx_buf || tx_buf))) { dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", t->speed_hz, len, @@ -509,8 +500,9 @@ static int omap1_spi100k_probe(struct platform_device *pdev) master->cleanup = NULL; master->num_chipselect = 2; master->mode_bits = MODEBITS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); spi100k = spi_master_get_devdata(master); spi100k->master = master; @@ -569,7 +561,7 @@ static int omap1_spi100k_remove(struct platform_device *pdev) unsigned long flags; int status = 0; - master = dev_get_drvdata(&pdev->dev); + master = platform_get_drvdata(pdev); spi100k = spi_master_get_devdata(master); spin_lock_irqsave(&spi100k->lock, flags); diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index 102b233b50c4..a6a8f0961750 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -495,7 +495,7 @@ static int uwire_probe(struct platform_device *pdev) return -ENOMEM; } - dev_set_drvdata(&pdev->dev, uwire); + platform_set_drvdata(pdev, uwire); uwire->ck = clk_get(&pdev->dev, "fck"); if (IS_ERR(uwire->ck)) { @@ -538,7 +538,7 @@ static int uwire_probe(struct platform_device *pdev) static int uwire_remove(struct platform_device *pdev) { - struct uwire_spi *uwire = dev_get_drvdata(&pdev->dev); + struct uwire_spi *uwire = platform_get_drvdata(pdev); int status; // FIXME remove all child devices, somewhere ... diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 86d2158946bb..5994039758de 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -38,13 +38,15 @@ #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> +#include <linux/gcd.h> #include <linux/spi/spi.h> #include <linux/platform_data/spi-omap2-mcspi.h> #define OMAP2_MCSPI_MAX_FREQ 48000000 +#define OMAP2_MCSPI_MAX_FIFODEPTH 64 +#define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF #define SPI_AUTOSUSPEND_TIMEOUT 2000 #define OMAP2_MCSPI_REVISION 0x00 @@ -54,6 +56,7 @@ #define OMAP2_MCSPI_WAKEUPENABLE 0x20 #define OMAP2_MCSPI_SYST 0x24 #define OMAP2_MCSPI_MODULCTRL 0x28 +#define OMAP2_MCSPI_XFERLEVEL 0x7c /* per-channel banks, 0x14 bytes each, first is: */ #define OMAP2_MCSPI_CHCONF0 0x2c @@ -63,6 +66,7 @@ #define OMAP2_MCSPI_RX0 0x3c /* per-register bitmasks: */ +#define OMAP2_MCSPI_IRQSTATUS_EOW BIT(17) #define OMAP2_MCSPI_MODULCTRL_SINGLE BIT(0) #define OMAP2_MCSPI_MODULCTRL_MS BIT(2) @@ -83,10 +87,13 @@ #define OMAP2_MCSPI_CHCONF_IS BIT(18) #define OMAP2_MCSPI_CHCONF_TURBO BIT(19) #define OMAP2_MCSPI_CHCONF_FORCE BIT(20) +#define OMAP2_MCSPI_CHCONF_FFET BIT(27) +#define OMAP2_MCSPI_CHCONF_FFER BIT(28) #define OMAP2_MCSPI_CHSTAT_RXS BIT(0) #define OMAP2_MCSPI_CHSTAT_TXS BIT(1) #define OMAP2_MCSPI_CHSTAT_EOT BIT(2) +#define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3) #define OMAP2_MCSPI_CHCTRL_EN BIT(0) @@ -102,6 +109,9 @@ struct omap2_mcspi_dma { struct completion dma_tx_completion; struct completion dma_rx_completion; + + char dma_rx_ch_name[14]; + char dma_tx_ch_name[14]; }; /* use PIO for small transfers, avoiding DMA setup/teardown overhead and @@ -129,6 +139,7 @@ struct omap2_mcspi { struct omap2_mcspi_dma *dma_channels; struct device *dev; struct omap2_mcspi_regs ctx; + int fifo_depth; unsigned int pin_dir:1; }; @@ -187,6 +198,16 @@ static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val) mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); } +static inline int mcspi_bytes_per_word(int word_len) +{ + if (word_len <= 8) + return 1; + else if (word_len <= 16) + return 2; + else /* word_len <= 32 */ + return 4; +} + static void omap2_mcspi_set_dma_req(const struct spi_device *spi, int is_read, int enable) { @@ -248,6 +269,58 @@ static void omap2_mcspi_set_master_mode(struct spi_master *master) ctx->modulctrl = l; } +static void omap2_mcspi_set_fifo(const struct spi_device *spi, + struct spi_transfer *t, int enable) +{ + struct spi_master *master = spi->master; + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi *mcspi; + unsigned int wcnt; + int fifo_depth, bytes_per_word; + u32 chconf, xferlevel; + + mcspi = spi_master_get_devdata(master); + + chconf = mcspi_cached_chconf0(spi); + if (enable) { + bytes_per_word = mcspi_bytes_per_word(cs->word_len); + if (t->len % bytes_per_word != 0) + goto disable_fifo; + + fifo_depth = gcd(t->len, OMAP2_MCSPI_MAX_FIFODEPTH); + if (fifo_depth < 2 || fifo_depth % bytes_per_word != 0) + goto disable_fifo; + + wcnt = t->len / bytes_per_word; + if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT) + goto disable_fifo; + + xferlevel = wcnt << 16; + if (t->rx_buf != NULL) { + chconf |= OMAP2_MCSPI_CHCONF_FFER; + xferlevel |= (fifo_depth - 1) << 8; + } else { + chconf |= OMAP2_MCSPI_CHCONF_FFET; + xferlevel |= fifo_depth - 1; + } + + mcspi_write_reg(master, OMAP2_MCSPI_XFERLEVEL, xferlevel); + mcspi_write_chconf0(spi, chconf); + mcspi->fifo_depth = fifo_depth; + + return; + } + +disable_fifo: + if (t->rx_buf != NULL) + chconf &= ~OMAP2_MCSPI_CHCONF_FFER; + else + chconf &= ~OMAP2_MCSPI_CHCONF_FFET; + + mcspi_write_chconf0(spi, chconf); + mcspi->fifo_depth = 0; +} + static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) { struct spi_master *spi_cntrl = mcspi->master; @@ -364,7 +437,7 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - unsigned int count; + unsigned int count, dma_count; u32 l; int elements = 0; int word_len, element_count; @@ -372,6 +445,11 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; count = xfer->len; + dma_count = xfer->len; + + if (mcspi->fifo_depth == 0) + dma_count -= es; + word_len = cs->word_len; l = mcspi_cached_chconf0(spi); @@ -385,16 +463,15 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, if (mcspi_dma->dma_rx) { struct dma_async_tx_descriptor *tx; struct scatterlist sg; - size_t len = xfer->len - es; dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); - if (l & OMAP2_MCSPI_CHCONF_TURBO) - len -= es; + if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0) + dma_count -= es; sg_init_table(&sg, 1); sg_dma_address(&sg) = xfer->rx_dma; - sg_dma_len(&sg) = len; + sg_dma_len(&sg) = dma_count; tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | @@ -414,6 +491,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, wait_for_completion(&mcspi_dma->dma_rx_completion); dma_unmap_single(mcspi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE); + + if (mcspi->fifo_depth > 0) + return count; + omap2_mcspi_set_enable(spi, 0); elements = element_count - 1; @@ -433,10 +514,9 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, else /* word_len <= 32 */ ((u32 *)xfer->rx_buf)[elements++] = w; } else { + int bytes_per_word = mcspi_bytes_per_word(word_len); dev_err(&spi->dev, "DMA RX penultimate word empty"); - count -= (word_len <= 8) ? 2 : - (word_len <= 16) ? 4 : - /* word_len <= 32 */ 8; + count -= (bytes_per_word << 1); omap2_mcspi_set_enable(spi, 1); return count; } @@ -454,9 +534,7 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, ((u32 *)xfer->rx_buf)[elements] = w; } else { dev_err(&spi->dev, "DMA RX last word empty"); - count -= (word_len <= 8) ? 1 : - (word_len <= 16) ? 2 : - /* word_len <= 32 */ 4; + count -= mcspi_bytes_per_word(word_len); } omap2_mcspi_set_enable(spi, 1); return count; @@ -475,7 +553,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) struct dma_slave_config cfg; enum dma_slave_buswidth width; unsigned es; + u32 burst; void __iomem *chstat_reg; + void __iomem *irqstat_reg; + int wait_res; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; @@ -493,19 +574,27 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) es = 4; } + count = xfer->len; + burst = 1; + + if (mcspi->fifo_depth > 0) { + if (count > mcspi->fifo_depth) + burst = mcspi->fifo_depth / es; + else + burst = count / es; + } + memset(&cfg, 0, sizeof(cfg)); cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; cfg.src_addr_width = width; cfg.dst_addr_width = width; - cfg.src_maxburst = 1; - cfg.dst_maxburst = 1; + cfg.src_maxburst = burst; + cfg.dst_maxburst = burst; rx = xfer->rx_buf; tx = xfer->tx_buf; - count = xfer->len; - if (tx != NULL) omap2_mcspi_tx_dma(spi, xfer, cfg); @@ -513,18 +602,38 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) count = omap2_mcspi_rx_dma(spi, xfer, cfg, es); if (tx != NULL) { - chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; wait_for_completion(&mcspi_dma->dma_tx_completion); dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len, DMA_TO_DEVICE); + if (mcspi->fifo_depth > 0) { + irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; + + if (mcspi_wait_for_reg_bit(irqstat_reg, + OMAP2_MCSPI_IRQSTATUS_EOW) < 0) + dev_err(&spi->dev, "EOW timed out\n"); + + mcspi_write_reg(mcspi->master, OMAP2_MCSPI_IRQSTATUS, + OMAP2_MCSPI_IRQSTATUS_EOW); + } + /* for TX_ONLY mode, be sure all words have shifted out */ if (rx == NULL) { - if (mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_TXS) < 0) - dev_err(&spi->dev, "TXS timed out\n"); - else if (mcspi_wait_for_reg_bit(chstat_reg, - OMAP2_MCSPI_CHSTAT_EOT) < 0) + chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; + if (mcspi->fifo_depth > 0) { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXFFE); + if (wait_res < 0) + dev_err(&spi->dev, "TXFFE timed out\n"); + } else { + wait_res = mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_TXS); + if (wait_res < 0) + dev_err(&spi->dev, "TXS timed out\n"); + } + if (wait_res >= 0 && + (mcspi_wait_for_reg_bit(chstat_reg, + OMAP2_MCSPI_CHSTAT_EOT) < 0)) dev_err(&spi->dev, "EOT timed out\n"); } } @@ -830,12 +939,20 @@ static int omap2_mcspi_request_dma(struct spi_device *spi) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); sig = mcspi_dma->dma_rx_sync_dev; - mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); + + mcspi_dma->dma_rx = + dma_request_slave_channel_compat(mask, omap_dma_filter_fn, + &sig, &master->dev, + mcspi_dma->dma_rx_ch_name); if (!mcspi_dma->dma_rx) goto no_dma; sig = mcspi_dma->dma_tx_sync_dev; - mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); + mcspi_dma->dma_tx = + dma_request_slave_channel_compat(mask, omap_dma_filter_fn, + &sig, &master->dev, + mcspi_dma->dma_tx_ch_name); + if (!mcspi_dma->dma_tx) { dma_release_channel(mcspi_dma->dma_rx); mcspi_dma->dma_rx = NULL; @@ -857,12 +974,6 @@ static int omap2_mcspi_setup(struct spi_device *spi) struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_cs *cs = spi->controller_state; - if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { - dev_dbg(&spi->dev, "setup: unsupported %d bit words\n", - spi->bits_per_word); - return -EINVAL; - } - mcspi_dma = &mcspi->dma_channels[spi->chip_select]; if (!cs) { @@ -951,7 +1062,7 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) cs = spi->controller_state; cd = spi->controller_data; - omap2_mcspi_set_enable(spi, 1); + omap2_mcspi_set_enable(spi, 0); list_for_each_entry(t, &m->transfers, transfer_list) { if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { status = -EINVAL; @@ -999,6 +1110,12 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) if (t->len) { unsigned count; + if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && + (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)) + omap2_mcspi_set_fifo(spi, t, 1); + + omap2_mcspi_set_enable(spi, 1); + /* RX_ONLY mode needs dummy data in TX reg */ if (t->tx_buf == NULL) __raw_writel(0, cs->base @@ -1025,6 +1142,11 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) omap2_mcspi_force_cs(spi, 0); cs_active = 0; } + + omap2_mcspi_set_enable(spi, 0); + + if (mcspi->fifo_depth > 0) + omap2_mcspi_set_fifo(spi, t, 0); } /* Restore defaults if they were overriden */ if (par_override) { @@ -1045,8 +1167,10 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) omap2_mcspi_set_enable(spi, 0); - m->status = status; + if (mcspi->fifo_depth > 0 && t) + omap2_mcspi_set_fifo(spi, t, 0); + m->status = status; } static int omap2_mcspi_transfer_one_message(struct spi_master *master, @@ -1072,10 +1196,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, unsigned len = t->len; if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ - || (len && !(rx_buf || tx_buf)) - || (t->bits_per_word && - ( t->bits_per_word < 4 - || t->bits_per_word > 32))) { + || (len && !(rx_buf || tx_buf))) { dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", t->speed_hz, len, @@ -1186,7 +1307,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev) static int bus_num = 1; struct device_node *node = pdev->dev.of_node; const struct of_device_id *match; - struct pinctrl *pinctrl; master = spi_alloc_master(&pdev->dev, sizeof *mcspi); if (master == NULL) { @@ -1196,7 +1316,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = omap2_mcspi_setup; master->prepare_transfer_hardware = omap2_prepare_transfer; master->unprepare_transfer_hardware = omap2_unprepare_transfer; @@ -1204,7 +1324,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); mcspi = spi_master_get_devdata(master); mcspi->master = master; @@ -1256,39 +1376,47 @@ static int omap2_mcspi_probe(struct platform_device *pdev) goto free_master; for (i = 0; i < master->num_chipselect; i++) { - char dma_ch_name[14]; + char *dma_rx_ch_name = mcspi->dma_channels[i].dma_rx_ch_name; + char *dma_tx_ch_name = mcspi->dma_channels[i].dma_tx_ch_name; struct resource *dma_res; - sprintf(dma_ch_name, "rx%d", i); - dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, - dma_ch_name); - if (!dma_res) { - dev_dbg(&pdev->dev, "cannot get DMA RX channel\n"); - status = -ENODEV; - break; - } + sprintf(dma_rx_ch_name, "rx%d", i); + if (!pdev->dev.of_node) { + dma_res = + platform_get_resource_byname(pdev, + IORESOURCE_DMA, + dma_rx_ch_name); + if (!dma_res) { + dev_dbg(&pdev->dev, + "cannot get DMA RX channel\n"); + status = -ENODEV; + break; + } - mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start; - sprintf(dma_ch_name, "tx%d", i); - dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, - dma_ch_name); - if (!dma_res) { - dev_dbg(&pdev->dev, "cannot get DMA TX channel\n"); - status = -ENODEV; - break; + mcspi->dma_channels[i].dma_rx_sync_dev = + dma_res->start; } + sprintf(dma_tx_ch_name, "tx%d", i); + if (!pdev->dev.of_node) { + dma_res = + platform_get_resource_byname(pdev, + IORESOURCE_DMA, + dma_tx_ch_name); + if (!dma_res) { + dev_dbg(&pdev->dev, + "cannot get DMA TX channel\n"); + status = -ENODEV; + break; + } - mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start; + mcspi->dma_channels[i].dma_tx_sync_dev = + dma_res->start; + } } if (status < 0) goto dma_chnl_free; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - dev_warn(&pdev->dev, - "pins are not configured from the driver\n"); - pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_enable(&pdev->dev); @@ -1318,7 +1446,7 @@ static int omap2_mcspi_remove(struct platform_device *pdev) struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *dma_channels; - master = dev_get_drvdata(&pdev->dev); + master = platform_get_drvdata(pdev); mcspi = spi_master_get_devdata(master); dma_channels = mcspi->dma_channels; diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 66a5f82cf138..5d90bebaa0fa 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -428,7 +428,7 @@ static int orion_spi_probe(struct platform_device *pdev) master->transfer_one_message = orion_spi_transfer_one_message; master->num_chipselect = ORION_NUM_CHIPSELECTS; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); spi = spi_master_get_devdata(master); spi->master = master; @@ -485,7 +485,7 @@ static int orion_spi_remove(struct platform_device *pdev) struct resource *r; struct orion_spi *spi; - master = dev_get_drvdata(&pdev->dev); + master = platform_get_drvdata(pdev); spi = spi_master_get_devdata(master); clk_disable_unprepare(spi->clk); diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 3b246543282f..abef061fb84a 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -368,11 +368,6 @@ struct pl022 { resource_size_t phybase; void __iomem *virtbase; struct clk *clk; - /* Two optional pin states - default & sleep */ - struct pinctrl *pinctrl; - struct pinctrl_state *pins_default; - struct pinctrl_state *pins_idle; - struct pinctrl_state *pins_sleep; struct spi_master *master; struct pl022_ssp_controller *master_info; /* Message per-transfer pump */ @@ -2134,32 +2129,7 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) pl022->chipselects = devm_kzalloc(dev, num_cs * sizeof(int), GFP_KERNEL); - pl022->pinctrl = devm_pinctrl_get(dev); - if (IS_ERR(pl022->pinctrl)) { - status = PTR_ERR(pl022->pinctrl); - goto err_no_pinctrl; - } - - pl022->pins_default = pinctrl_lookup_state(pl022->pinctrl, - PINCTRL_STATE_DEFAULT); - /* enable pins to be muxed in and configured */ - if (!IS_ERR(pl022->pins_default)) { - status = pinctrl_select_state(pl022->pinctrl, - pl022->pins_default); - if (status) - dev_err(dev, "could not set default pins\n"); - } else - dev_err(dev, "could not get default pinstate\n"); - - pl022->pins_idle = pinctrl_lookup_state(pl022->pinctrl, - PINCTRL_STATE_IDLE); - if (IS_ERR(pl022->pins_idle)) - dev_dbg(dev, "could not get idle pinstate\n"); - - pl022->pins_sleep = pinctrl_lookup_state(pl022->pinctrl, - PINCTRL_STATE_SLEEP); - if (IS_ERR(pl022->pins_sleep)) - dev_dbg(dev, "could not get sleep pinstate\n"); + pinctrl_pm_select_default_state(dev); /* * Bus Number Which has been Assigned to this SSP controller @@ -2309,7 +2279,6 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) amba_release_regions(adev); err_no_ioregion: err_no_gpio: - err_no_pinctrl: spi_master_put(master); return status; } @@ -2349,44 +2318,21 @@ pl022_remove(struct amba_device *adev) */ static void pl022_suspend_resources(struct pl022 *pl022, bool runtime) { - int ret; - struct pinctrl_state *pins_state; - clk_disable(pl022->clk); - pins_state = runtime ? pl022->pins_idle : pl022->pins_sleep; - /* Optionally let pins go into sleep states */ - if (!IS_ERR(pins_state)) { - ret = pinctrl_select_state(pl022->pinctrl, pins_state); - if (ret) - dev_err(&pl022->adev->dev, "could not set %s pins\n", - runtime ? "idle" : "sleep"); - } + if (runtime) + pinctrl_pm_select_idle_state(&pl022->adev->dev); + else + pinctrl_pm_select_sleep_state(&pl022->adev->dev); } static void pl022_resume_resources(struct pl022 *pl022, bool runtime) { - int ret; - - /* Optionaly enable pins to be muxed in and configured */ /* First go to the default state */ - if (!IS_ERR(pl022->pins_default)) { - ret = pinctrl_select_state(pl022->pinctrl, pl022->pins_default); - if (ret) - dev_err(&pl022->adev->dev, - "could not set default pins\n"); - } - - if (!runtime) { + pinctrl_pm_select_default_state(&pl022->adev->dev); + if (!runtime) /* Then let's idle the pins until the next transfer happens */ - if (!IS_ERR(pl022->pins_idle)) { - ret = pinctrl_select_state(pl022->pinctrl, - pl022->pins_idle); - if (ret) - dev_err(&pl022->adev->dev, - "could not set idle pins\n"); - } - } + pinctrl_pm_select_idle_state(&pl022->adev->dev); clk_enable(pl022->clk); } diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 357f183a4fb7..0ee53c25ba58 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -190,12 +190,6 @@ static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t) speed = min(t->speed_hz, spi->max_speed_hz); } - if (bits_per_word != 8) { - dev_err(&spi->dev, "invalid bits-per-word (%d)\n", - bits_per_word); - return -EINVAL; - } - if (!speed || (speed > spi->max_speed_hz)) { dev_err(&spi->dev, "invalid speed_hz (%d)\n", speed); return -EINVAL; @@ -229,12 +223,6 @@ static int spi_ppc4xx_setup(struct spi_device *spi) { struct spi_ppc4xx_cs *cs = spi->controller_state; - if (spi->bits_per_word != 8) { - dev_err(&spi->dev, "invalid bits-per-word (%d)\n", - spi->bits_per_word); - return -EINVAL; - } - if (!spi->max_speed_hz) { dev_err(&spi->dev, "invalid max_speed_hz (must be non-zero)\n"); return -EINVAL; @@ -406,7 +394,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op) if (master == NULL) return -ENOMEM; master->dev.of_node = np; - dev_set_drvdata(dev, master); + platform_set_drvdata(op, master); hw = spi_master_get_devdata(master); hw->master = spi_master_get(master); hw->dev = dev; @@ -465,6 +453,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op) bbp->use_dma = 0; bbp->master->setup = spi_ppc4xx_setup; bbp->master->cleanup = spi_ppc4xx_cleanup; + bbp->master->bits_per_word_mask = SPI_BPW_MASK(8); /* the spi->mode bits understood by this driver: */ bbp->master->mode_bits = @@ -553,7 +542,6 @@ request_mem_error: free_gpios: free_gpios(hw); free_master: - dev_set_drvdata(dev, NULL); spi_master_put(master); dev_err(dev, "initialization failed\n"); @@ -562,11 +550,10 @@ free_master: static int spi_ppc4xx_of_remove(struct platform_device *op) { - struct spi_master *master = dev_get_drvdata(&op->dev); + struct spi_master *master = platform_get_drvdata(op); struct ppc4xx_spi *hw = spi_master_get_devdata(master); spi_bitbang_stop(&hw->bitbang); - dev_set_drvdata(&op->dev, NULL); release_mem_region(hw->mapbase, hw->mapsize); free_irq(hw->irqnum, hw); iounmap(hw->regs); diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index 6427600b5bbe..3c0b55125f1e 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -327,22 +327,23 @@ void pxa2xx_spi_dma_start(struct driver_data *drv_data) int pxa2xx_spi_dma_setup(struct driver_data *drv_data) { struct pxa2xx_spi_master *pdata = drv_data->master_info; + struct device *dev = &drv_data->pdev->dev; dma_cap_mask_t mask; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - drv_data->dummy = devm_kzalloc(&drv_data->pdev->dev, SZ_2K, GFP_KERNEL); + drv_data->dummy = devm_kzalloc(dev, SZ_2K, GFP_KERNEL); if (!drv_data->dummy) return -ENOMEM; - drv_data->tx_chan = dma_request_channel(mask, pxa2xx_spi_dma_filter, - pdata); + drv_data->tx_chan = dma_request_slave_channel_compat(mask, + pxa2xx_spi_dma_filter, pdata, dev, "tx"); if (!drv_data->tx_chan) return -ENODEV; - drv_data->rx_chan = dma_request_channel(mask, pxa2xx_spi_dma_filter, - pdata); + drv_data->rx_chan = dma_request_slave_channel_compat(mask, + pxa2xx_spi_dma_filter, pdata, dev, "rx"); if (!drv_data->rx_chan) { dma_release_channel(drv_data->tx_chan); drv_data->tx_chan = NULL; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 48b396fced0a..f440dcee852b 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -881,21 +881,6 @@ static int setup(struct spi_device *spi) rx_thres = RX_THRESH_DFLT; } - if (!pxa25x_ssp_comp(drv_data) - && (spi->bits_per_word < 4 || spi->bits_per_word > 32)) { - dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d " - "b/w not 4-32 for type non-PXA25x_SSP\n", - drv_data->ssp_type, spi->bits_per_word); - return -EINVAL; - } else if (pxa25x_ssp_comp(drv_data) - && (spi->bits_per_word < 4 - || spi->bits_per_word > 16)) { - dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d " - "b/w not 4-16 for type PXA25x_SSP\n", - drv_data->ssp_type, spi->bits_per_word); - return -EINVAL; - } - /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (!chip) { @@ -1011,9 +996,6 @@ static int setup(struct spi_device *spi) chip->n_bytes = 4; chip->read = u32_reader; chip->write = u32_writer; - } else { - dev_err(&spi->dev, "invalid wordsize\n"); - return -ENODEV; } chip->bits_per_word = spi->bits_per_word; @@ -1040,32 +1022,10 @@ static void cleanup(struct spi_device *spi) } #ifdef CONFIG_ACPI -static int pxa2xx_spi_acpi_add_dma(struct acpi_resource *res, void *data) -{ - struct pxa2xx_spi_master *pdata = data; - - if (res->type == ACPI_RESOURCE_TYPE_FIXED_DMA) { - const struct acpi_resource_fixed_dma *dma; - - dma = &res->data.fixed_dma; - if (pdata->tx_slave_id < 0) { - pdata->tx_slave_id = dma->request_lines; - pdata->tx_chan_id = dma->channels; - } else if (pdata->rx_slave_id < 0) { - pdata->rx_slave_id = dma->request_lines; - pdata->rx_chan_id = dma->channels; - } - } - - /* Tell the ACPI core to skip this resource */ - return 1; -} - static struct pxa2xx_spi_master * pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) { struct pxa2xx_spi_master *pdata; - struct list_head resource_list; struct acpi_device *adev; struct ssp_device *ssp; struct resource *res; @@ -1091,7 +1051,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) ssp->phys_base = res->start; ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ssp->mmio_base)) - return PTR_ERR(ssp->mmio_base); + return NULL; ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); @@ -1103,15 +1063,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) ssp->port_id = devid; pdata->num_chipselect = 1; - pdata->rx_slave_id = -1; - pdata->tx_slave_id = -1; - - INIT_LIST_HEAD(&resource_list); - acpi_dev_get_resources(adev, &resource_list, pxa2xx_spi_acpi_add_dma, - pdata); - acpi_dev_free_resource_list(&resource_list); - - pdata->enable_dma = pdata->rx_slave_id >= 0 && pdata->tx_slave_id >= 0; + pdata->enable_dma = true; return pdata; } @@ -1119,6 +1071,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) static struct acpi_device_id pxa2xx_spi_acpi_match[] = { { "INT33C0", 0 }, { "INT33C1", 0 }, + { "80860F0E", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); @@ -1190,11 +1143,13 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) drv_data->ioaddr = ssp->mmio_base; drv_data->ssdr_physical = ssp->phys_base + SSDR; if (pxa25x_ssp_comp(drv_data)) { + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; drv_data->dma_cr1 = 0; drv_data->clear_sr = SSSR_ROR; drv_data->mask_sr = SSSR_RFS | SSSR_TFS | SSSR_ROR; } else { + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; drv_data->dma_cr1 = DEFAULT_DMA_CR1; drv_data->clear_sr = SSSR_ROR | SSSR_TINT; @@ -1214,7 +1169,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) if (platform_info->enable_dma) { status = pxa2xx_spi_dma_setup(drv_data); if (status) { - dev_warn(dev, "failed to setup DMA, using PIO\n"); + dev_dbg(dev, "no DMA channels available, using PIO\n"); platform_info->enable_dma = false; } } @@ -1299,9 +1254,6 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) /* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); - /* Prevent double remove */ - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 902f2fb902db..b44a6ac3cec9 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -719,7 +719,7 @@ static void rspi_release_dma(struct rspi_data *rspi) static int rspi_remove(struct platform_device *pdev) { - struct rspi_data *rspi = dev_get_drvdata(&pdev->dev); + struct rspi_data *rspi = platform_get_drvdata(pdev); spi_unregister_master(rspi->master); rspi_release_dma(rspi); @@ -759,7 +759,7 @@ static int rspi_probe(struct platform_device *pdev) } rspi = spi_master_get_devdata(master); - dev_set_drvdata(&pdev->dev, rspi); + platform_set_drvdata(pdev, rspi); rspi->master = master; rspi->addr = ioremap(res->start, resource_size(res)); diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 02d64603fcc5..68910b310152 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -667,8 +667,6 @@ static int s3c24xx_spi_remove(struct platform_device *dev) { struct s3c24xx_spi *hw = platform_get_drvdata(dev); - platform_set_drvdata(dev, NULL); - spi_bitbang_stop(&hw->bitbang); clk_disable(hw->clk); diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 71cc3e6ef47c..eb53df27e7ea 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -39,6 +39,7 @@ #endif #define MAX_SPI_PORTS 3 +#define S3C64XX_SPI_QUIRK_POLL (1 << 0) /* Registers and bit-fields */ @@ -130,6 +131,7 @@ #define S3C64XX_SPI_TRAILCNT S3C64XX_SPI_MAX_TRAILCNT #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) +#define is_polling(x) (x->port_conf->quirks & S3C64XX_SPI_QUIRK_POLL) #define RXBUSY (1<<2) #define TXBUSY (1<<3) @@ -158,6 +160,7 @@ struct s3c64xx_spi_port_config { int fifo_lvl_mask[MAX_SPI_PORTS]; int rx_lvl_offset; int tx_st_done; + int quirks; bool high_speed; bool clk_from_cmu; }; @@ -205,6 +208,7 @@ struct s3c64xx_spi_driver_data { struct s3c64xx_spi_port_config *port_conf; unsigned int port_id; unsigned long gpios[4]; + bool cs_gpio; }; static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) @@ -344,8 +348,12 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) { struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); - /* Acquire DMA channels */ - while (!acquire_dma(sdd)) + /* + * If DMA resource was not available during + * probe, no need to continue with dma requests + * else Acquire DMA channels + */ + while (!is_polling(sdd) && !acquire_dma(sdd)) usleep_range(10000, 11000); pm_runtime_get_sync(&sdd->pdev->dev); @@ -358,9 +366,12 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); /* Free DMA channels */ - sdd->ops->release((enum dma_ch)sdd->rx_dma.ch, &s3c64xx_spi_dma_client); - sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, &s3c64xx_spi_dma_client); - + if (!is_polling(sdd)) { + sdd->ops->release((enum dma_ch)sdd->rx_dma.ch, + &s3c64xx_spi_dma_client); + sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, + &s3c64xx_spi_dma_client); + } pm_runtime_put(&sdd->pdev->dev); return 0; @@ -464,8 +475,10 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi); /* Free DMA channels */ - dma_release_channel(sdd->rx_dma.ch); - dma_release_channel(sdd->tx_dma.ch); + if (!is_polling(sdd)) { + dma_release_channel(sdd->rx_dma.ch); + dma_release_channel(sdd->tx_dma.ch); + } pm_runtime_put(&sdd->pdev->dev); return 0; @@ -558,14 +571,40 @@ static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd, if (sdd->tgl_spi != spi) { /* if last mssg on diff device */ /* Deselect the last toggled device */ cs = sdd->tgl_spi->controller_data; - gpio_set_value(cs->line, - spi->mode & SPI_CS_HIGH ? 0 : 1); + if (sdd->cs_gpio) + gpio_set_value(cs->line, + spi->mode & SPI_CS_HIGH ? 0 : 1); } sdd->tgl_spi = NULL; } cs = spi->controller_data; - gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0); + if (sdd->cs_gpio) + gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0); + + /* Start the signals */ + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); +} + +static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, + int timeout_ms) +{ + void __iomem *regs = sdd->regs; + unsigned long val = 1; + u32 status; + + /* max fifo depth available */ + u32 max_fifo = (FIFO_LVL_MASK(sdd) >> 1) + 1; + + if (timeout_ms) + val = msecs_to_loops(timeout_ms); + + do { + status = readl(regs + S3C64XX_SPI_STATUS); + } while (RX_FIFO_LVL(status, sdd) < max_fifo && --val); + + /* return the actual received data length */ + return RX_FIFO_LVL(status, sdd); } static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, @@ -590,20 +629,19 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val); } - if (!val) - return -EIO; - if (dma_mode) { u32 status; /* + * If the previous xfer was completed within timeout, then + * proceed further else return -EIO. * DmaTx returns after simply writing data in the FIFO, * w/o waiting for real transmission on the bus to finish. * DmaRx returns only after Dma read data from FIFO which * needs bus transmission to finish, so we don't worry if * Xfer involved Rx(with or without Tx). */ - if (xfer->rx_buf == NULL) { + if (val && !xfer->rx_buf) { val = msecs_to_loops(10); status = readl(regs + S3C64XX_SPI_STATUS); while ((TX_FIFO_LVL(status, sdd) @@ -613,30 +651,54 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, status = readl(regs + S3C64XX_SPI_STATUS); } - if (!val) - return -EIO; } + + /* If timed out while checking rx/tx status return error */ + if (!val) + return -EIO; } else { + int loops; + u32 cpy_len; + u8 *buf; + /* If it was only Tx */ - if (xfer->rx_buf == NULL) { + if (!xfer->rx_buf) { sdd->state &= ~TXBUSY; return 0; } - switch (sdd->cur_bpw) { - case 32: - ioread32_rep(regs + S3C64XX_SPI_RX_DATA, - xfer->rx_buf, xfer->len / 4); - break; - case 16: - ioread16_rep(regs + S3C64XX_SPI_RX_DATA, - xfer->rx_buf, xfer->len / 2); - break; - default: - ioread8_rep(regs + S3C64XX_SPI_RX_DATA, - xfer->rx_buf, xfer->len); - break; - } + /* + * If the receive length is bigger than the controller fifo + * size, calculate the loops and read the fifo as many times. + * loops = length / max fifo size (calculated by using the + * fifo mask). + * For any size less than the fifo size the below code is + * executed atleast once. + */ + loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1); + buf = xfer->rx_buf; + do { + /* wait for data to be received in the fifo */ + cpy_len = s3c64xx_spi_wait_for_timeout(sdd, + (loops ? ms : 0)); + + switch (sdd->cur_bpw) { + case 32: + ioread32_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len / 4); + break; + case 16: + ioread16_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len / 2); + break; + default: + ioread8_rep(regs + S3C64XX_SPI_RX_DATA, + buf, cpy_len); + break; + } + + buf = buf + cpy_len; + } while (loops--); sdd->state &= ~RXBUSY; } @@ -651,7 +713,11 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, if (sdd->tgl_spi == spi) sdd->tgl_spi = NULL; - gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1); + if (sdd->cs_gpio) + gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1); + + /* Quiese the signals */ + writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); } static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) @@ -733,7 +799,7 @@ static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, struct device *dev = &sdd->pdev->dev; struct spi_transfer *xfer; - if (msg->is_dma_mapped) + if (is_polling(sdd) || msg->is_dma_mapped) return 0; /* First mark all xfer unmapped */ @@ -782,7 +848,7 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, struct device *dev = &sdd->pdev->dev; struct spi_transfer *xfer; - if (msg->is_dma_mapped) + if (is_polling(sdd) || msg->is_dma_mapped) return; list_for_each_entry(xfer, &msg->transfers, transfer_list) { @@ -861,8 +927,9 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, /* Polling method for xfers not bigger than FIFO capacity */ use_dma = 0; - if (sdd->rx_dma.ch && sdd->tx_dma.ch && - (xfer->len > ((FIFO_LVL_MASK(sdd) >> 1) + 1))) + if (!is_polling(sdd) && + (sdd->rx_dma.ch && sdd->tx_dma.ch && + (xfer->len > ((FIFO_LVL_MASK(sdd) >> 1) + 1)))) use_dma = 1; spin_lock_irqsave(&sdd->lock, flags); @@ -876,17 +943,10 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master, /* Slave Select */ enable_cs(sdd, spi); - /* Start the signals */ - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); - spin_unlock_irqrestore(&sdd->lock, flags); status = wait_for_xfer(sdd, xfer, use_dma); - /* Quiese the signals */ - writel(S3C64XX_SPI_SLAVE_SIG_INACT, - sdd->regs + S3C64XX_SPI_SLAVE_SEL); - if (status) { dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n", xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, @@ -942,8 +1002,10 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( { struct s3c64xx_spi_csinfo *cs; struct device_node *slave_np, *data_np = NULL; + struct s3c64xx_spi_driver_data *sdd; u32 fb_delay = 0; + sdd = spi_master_get_devdata(spi->master); slave_np = spi->dev.of_node; if (!slave_np) { dev_err(&spi->dev, "device node not found\n"); @@ -963,7 +1025,10 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( return ERR_PTR(-ENOMEM); } - cs->line = of_get_named_gpio(data_np, "cs-gpio", 0); + /* The CS line is asserted/deasserted by the gpio pin */ + if (sdd->cs_gpio) + cs->line = of_get_named_gpio(data_np, "cs-gpio", 0); + if (!gpio_is_valid(cs->line)) { dev_err(&spi->dev, "chip select gpio is not specified or invalid\n"); kfree(cs); @@ -1003,7 +1068,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) return -ENODEV; } - if (!spi_get_ctldata(spi)) { + /* Request gpio only if cs line is asserted by gpio pins */ + if (sdd->cs_gpio) { err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH, dev_name(&spi->dev)); if (err) { @@ -1012,9 +1078,11 @@ static int s3c64xx_spi_setup(struct spi_device *spi) cs->line, err); goto err_gpio_req; } - spi_set_ctldata(spi, cs); } + if (!spi_get_ctldata(spi)) + spi_set_ctldata(spi, cs); + sci = sdd->cntrlr_info; spin_lock_irqsave(&sdd->lock, flags); @@ -1092,8 +1160,10 @@ err_gpio_req: static void s3c64xx_spi_cleanup(struct spi_device *spi) { struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi); + struct s3c64xx_spi_driver_data *sdd; - if (cs) { + sdd = spi_master_get_devdata(spi->master); + if (cs && sdd->cs_gpio) { gpio_free(cs->line); if (spi->dev.of_node) kfree(cs); @@ -1270,7 +1340,11 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) sdd->cntrlr_info = sci; sdd->pdev = pdev; sdd->sfr_start = mem_res->start; + sdd->cs_gpio = true; if (pdev->dev.of_node) { + if (!of_find_property(pdev->dev.of_node, "cs-gpio", NULL)) + sdd->cs_gpio = false; + ret = of_alias_get_id(pdev->dev.of_node, "spi"); if (ret < 0) { dev_err(&pdev->dev, "failed to get alias id, errno %d\n", @@ -1287,19 +1361,19 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) if (!sdd->pdev->dev.of_node) { res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { - dev_err(&pdev->dev, "Unable to get SPI tx dma " - "resource\n"); - return -ENXIO; - } - sdd->tx_dma.dmach = res->start; + dev_warn(&pdev->dev, "Unable to get SPI tx dma " + "resource. Switching to poll mode\n"); + sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL; + } else + sdd->tx_dma.dmach = res->start; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { - dev_err(&pdev->dev, "Unable to get SPI rx dma " - "resource\n"); - return -ENXIO; - } - sdd->rx_dma.dmach = res->start; + dev_warn(&pdev->dev, "Unable to get SPI rx dma " + "resource. Switching to poll mode\n"); + sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL; + } else + sdd->rx_dma.dmach = res->start; } sdd->tx_dma.direction = DMA_MEM_TO_DEV; @@ -1314,7 +1388,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; master->num_chipselect = sci->num_cs; master->dma_alignment = 8; - master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(8); /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; @@ -1399,7 +1474,6 @@ err3: err2: clk_disable_unprepare(sdd->clk); err0: - platform_set_drvdata(pdev, NULL); spi_master_put(master); return ret; @@ -1420,7 +1494,6 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) clk_disable_unprepare(sdd->clk); - platform_set_drvdata(pdev, NULL); spi_master_put(master); return 0; @@ -1535,6 +1608,15 @@ static struct s3c64xx_spi_port_config exynos4_spi_port_config = { .clk_from_cmu = true, }; +static struct s3c64xx_spi_port_config exynos5440_spi_port_config = { + .fifo_lvl_mask = { 0x1ff }, + .rx_lvl_offset = 15, + .tx_st_done = 25, + .high_speed = true, + .clk_from_cmu = true, + .quirks = S3C64XX_SPI_QUIRK_POLL, +}; + static struct platform_device_id s3c64xx_spi_driver_ids[] = { { .name = "s3c2443-spi", @@ -1558,15 +1640,16 @@ static struct platform_device_id s3c64xx_spi_driver_ids[] = { { }, }; -#ifdef CONFIG_OF static const struct of_device_id s3c64xx_spi_dt_match[] = { { .compatible = "samsung,exynos4210-spi", .data = (void *)&exynos4_spi_port_config, }, + { .compatible = "samsung,exynos5440-spi", + .data = (void *)&exynos5440_spi_port_config, + }, { }, }; MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match); -#endif /* CONFIG_OF */ static struct platform_driver s3c64xx_spi_driver = { .driver = { diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index eab593eaaafa..716edf999538 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -297,7 +297,7 @@ static int hspi_probe(struct platform_device *pdev) } hspi = spi_master_get_devdata(master); - dev_set_drvdata(&pdev->dev, hspi); + platform_set_drvdata(pdev, hspi); /* init hspi */ hspi->master = master; @@ -341,7 +341,7 @@ static int hspi_probe(struct platform_device *pdev) static int hspi_remove(struct platform_device *pdev) { - struct hspi_priv *hspi = dev_get_drvdata(&pdev->dev); + struct hspi_priv *hspi = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index 3c3600a994bd..c120a70094f2 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -434,7 +434,7 @@ static irqreturn_t spi_sh_irq(int irq, void *_ss) static int spi_sh_remove(struct platform_device *pdev) { - struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev); + struct spi_sh_data *ss = platform_get_drvdata(pdev); spi_unregister_master(ss->master); destroy_workqueue(ss->workqueue); @@ -471,7 +471,7 @@ static int spi_sh_probe(struct platform_device *pdev) } ss = spi_master_get_devdata(master); - dev_set_drvdata(&pdev->dev, ss); + platform_set_drvdata(pdev, ss); switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { case IORESOURCE_MEM_8BIT: diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 0808cd56bf8d..fc20bcfd90c3 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -19,7 +19,6 @@ #include <linux/of_gpio.h> #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> -#include <linux/pinctrl/consumer.h> #define DRIVER_NAME "sirfsoc_spi" @@ -127,7 +126,6 @@ struct sirfsoc_spi { void __iomem *base; u32 ctrl_freq; /* SPI controller clock speed */ struct clk *clk; - struct pinctrl *p; /* rx & tx bufs from the spi_transfer */ const void *tx; @@ -142,9 +140,6 @@ struct sirfsoc_spi { unsigned int left_tx_cnt; unsigned int left_rx_cnt; - /* tasklet to push tx msg into FIFO */ - struct tasklet_struct tasklet_tx; - int chipselect[0]; }; @@ -236,17 +231,6 @@ static void spi_sirfsoc_tx_word_u32(struct sirfsoc_spi *sspi) sspi->left_tx_cnt--; } -static void spi_sirfsoc_tasklet_tx(unsigned long arg) -{ - struct sirfsoc_spi *sspi = (struct sirfsoc_spi *)arg; - - /* Fill Tx FIFO while there are left words to be transmitted */ - while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) & - SIRFSOC_SPI_FIFO_FULL)) && - sspi->left_tx_cnt) - sspi->tx_word(sspi); -} - static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) { struct sirfsoc_spi *sspi = dev_id; @@ -261,25 +245,25 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); } - if (spi_stat & SIRFSOC_SPI_FRM_END) { + if (spi_stat & (SIRFSOC_SPI_FRM_END + | SIRFSOC_SPI_RXFIFO_THD_REACH)) while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS) & SIRFSOC_SPI_FIFO_EMPTY)) && sspi->left_rx_cnt) sspi->rx_word(sspi); - /* Received all words */ - if ((sspi->left_rx_cnt == 0) && (sspi->left_tx_cnt == 0)) { - complete(&sspi->done); - writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); - } - } - - if (spi_stat & SIRFSOC_SPI_RXFIFO_THD_REACH || - spi_stat & SIRFSOC_SPI_TXFIFO_THD_REACH || - spi_stat & SIRFSOC_SPI_RX_FIFO_FULL || - spi_stat & SIRFSOC_SPI_TXFIFO_EMPTY) - tasklet_schedule(&sspi->tasklet_tx); + if (spi_stat & (SIRFSOC_SPI_FIFO_EMPTY + | SIRFSOC_SPI_TXFIFO_THD_REACH)) + while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) + & SIRFSOC_SPI_FIFO_FULL)) && + sspi->left_tx_cnt) + sspi->tx_word(sspi); + /* Received all words */ + if ((sspi->left_rx_cnt == 0) && (sspi->left_tx_cnt == 0)) { + complete(&sspi->done); + writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); + } return IRQ_HANDLED; } @@ -426,9 +410,7 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) SIRFSOC_SPI_FIFO_WIDTH_DWORD; break; default: - dev_err(&spi->dev, "Bits per word %d not supported\n", - bits_per_word); - return -EINVAL; + BUG(); } if (!(spi->mode & SPI_CS_HIGH)) @@ -556,26 +538,20 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer; sspi->bitbang.master->setup = spi_sirfsoc_setup; master->bus_num = pdev->id; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) | + SPI_BPW_MASK(16) | SPI_BPW_MASK(32); sspi->bitbang.master->dev.of_node = pdev->dev.of_node; - sspi->p = pinctrl_get_select_default(&pdev->dev); - ret = IS_ERR(sspi->p); - if (ret) - goto free_master; - sspi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(sspi->clk)) { ret = -EINVAL; - goto free_pin; + goto free_master; } clk_prepare_enable(sspi->clk); sspi->ctrl_freq = clk_get_rate(sspi->clk); init_completion(&sspi->done); - tasklet_init(&sspi->tasklet_tx, spi_sirfsoc_tasklet_tx, - (unsigned long)sspi); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); @@ -594,8 +570,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) free_clk: clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); -free_pin: - pinctrl_put(sspi->p); free_master: spi_master_put(master); err_cs: @@ -618,7 +592,6 @@ static int spi_sirfsoc_remove(struct platform_device *pdev) } clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); - pinctrl_put(sspi->p); spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 598eb45e8008..e8f542ab8935 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1041,7 +1041,7 @@ static int tegra_spi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "master allocation failed\n"); return -ENOMEM; } - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); tspi = spi_master_get_devdata(master); /* Parse DT */ @@ -1152,7 +1152,7 @@ exit_free_master: static int tegra_spi_remove(struct platform_device *pdev) { - struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct spi_master *master = platform_get_drvdata(pdev); struct tegra_spi_data *tspi = spi_master_get_devdata(master); free_irq(tspi->irq, tspi); diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 09df8e22dba0..c1d5d95e70ea 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -480,7 +480,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); tsd = spi_master_get_devdata(master); tsd->master = master; tsd->dev = &pdev->dev; @@ -555,7 +555,7 @@ exit_free_master: static int tegra_sflash_remove(struct platform_device *pdev) { - struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct spi_master *master = platform_get_drvdata(pdev); struct tegra_sflash_data *tsd = spi_master_get_devdata(master); free_irq(tsd->irq, tsd); diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 3faf88d003de..80490cc11ce5 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1089,7 +1089,7 @@ static int tegra_slink_probe(struct platform_device *pdev) master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); tspi = spi_master_get_devdata(master); tspi->master = master; tspi->dev = &pdev->dev; @@ -1193,7 +1193,7 @@ exit_free_master: static int tegra_slink_remove(struct platform_device *pdev) { - struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct spi_master *master = platform_get_drvdata(pdev); struct tegra_slink_data *tspi = spi_master_get_devdata(master); free_irq(tspi->irq, tspi); diff --git a/drivers/spi/spi-ti-ssp.c b/drivers/spi/spi-ti-ssp.c index 46992cab65f1..10606fcc6efc 100644 --- a/drivers/spi/spi-ti-ssp.c +++ b/drivers/spi/spi-ti-ssp.c @@ -237,14 +237,6 @@ static void ti_ssp_spi_work(struct work_struct *work) spin_unlock(&hw->lock); } -static int ti_ssp_spi_setup(struct spi_device *spi) -{ - if (spi->bits_per_word > 32) - return -EINVAL; - - return 0; -} - static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) { struct ti_ssp_spi *hw; @@ -269,12 +261,6 @@ static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) dev_err(&spi->dev, "invalid xfer, full duplex\n"); return -EINVAL; } - - if (t->bits_per_word > 32) { - dev_err(&spi->dev, "invalid xfer width %d\n", - t->bits_per_word); - return -EINVAL; - } } spin_lock(&hw->lock); @@ -337,8 +323,8 @@ static int ti_ssp_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = pdata->num_cs; master->mode_bits = MODE_BITS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->flags = SPI_MASTER_HALF_DUPLEX; - master->setup = ti_ssp_spi_setup; master->transfer = ti_ssp_spi_transfer; error = spi_register_master(master); diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 637d728fbeb5..dd55707a6aa5 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -367,7 +367,7 @@ static irqreturn_t pch_spi_handler(int irq, void *dev_id) if (reg_spsr_val & SPSR_ORF_BIT) { dev_err(&board_dat->pdev->dev, "%s Over run error\n", __func__); - if (data->current_msg->complete != 0) { + if (data->current_msg->complete) { data->transfer_complete = true; data->current_msg->status = -EIO; data->current_msg->complete(data->current_msg->context); @@ -472,11 +472,6 @@ static int pch_spi_setup(struct spi_device *pspi) dev_dbg(&pspi->dev, "%s 8 bits per word\n", __func__); } - if ((pspi->bits_per_word != 8) && (pspi->bits_per_word != 16)) { - dev_err(&pspi->dev, "%s Invalid bits per word\n", __func__); - return -EINVAL; - } - /* Check baud rate setting */ /* if baud rate of chip is greater than max we can support,return error */ @@ -537,17 +532,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) /* if baud rate has been specified validate the same */ if (transfer->speed_hz > PCH_MAX_BAUDRATE) transfer->speed_hz = PCH_MAX_BAUDRATE; - - /* if bits per word has been specified validate the same */ - if (transfer->bits_per_word) { - if ((transfer->bits_per_word != 8) - && (transfer->bits_per_word != 16)) { - retval = -EINVAL; - dev_err(&pspi->dev, - "%s Invalid bits per word\n", __func__); - goto err_return_spinlock; - } - } } spin_unlock_irqrestore(&data->lock, flags); @@ -659,7 +643,7 @@ static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw) list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { pmsg->status = -ENOMEM; - if (pmsg->complete != 0) + if (pmsg->complete) pmsg->complete(pmsg->context); /* delete from queue */ @@ -709,7 +693,7 @@ static void pch_spi_nomore_transfer(struct pch_spi_data *data) * [To the spi core..indicating end of transfer] */ data->current_msg->status = 0; - if (data->current_msg->complete != 0) { + if (data->current_msg->complete) { dev_dbg(&data->master->dev, "%s:Invoking callback of SPI core\n", __func__); data->current_msg->complete(data->current_msg->context); @@ -1202,7 +1186,7 @@ static void pch_spi_process_messages(struct work_struct *pwork) list_for_each_entry_safe(pmsg, tmp, data->queue.next, queue) { pmsg->status = -EIO; - if (pmsg->complete != 0) { + if (pmsg->complete) { spin_unlock(&data->lock); pmsg->complete(pmsg->context); spin_lock(&data->lock); @@ -1442,6 +1426,7 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev) master->setup = pch_spi_setup; master->transfer = pch_spi_transfer; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); data->board_dat = board_dat; data->plat_dev = plat_dev; diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index adb853047926..e9b7681ff6ac 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -116,17 +116,12 @@ static void txx9spi_cs_func(struct spi_device *spi, struct txx9spi *c, static int txx9spi_setup(struct spi_device *spi) { struct txx9spi *c = spi_master_get_devdata(spi->master); - u8 bits_per_word; if (!spi->max_speed_hz || spi->max_speed_hz > c->max_speed_hz || spi->max_speed_hz < c->min_speed_hz) return -EINVAL; - bits_per_word = spi->bits_per_word; - if (bits_per_word != 8 && bits_per_word != 16) - return -EINVAL; - if (gpio_direction_output(spi->chip_select, !(spi->mode & SPI_CS_HIGH))) { dev_err(&spi->dev, "Cannot setup GPIO for chipselect.\n"); @@ -319,8 +314,6 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) if (!t->tx_buf && !t->rx_buf && t->len) return -EINVAL; - if (bits_per_word != 8 && bits_per_word != 16) - return -EINVAL; if (t->len & ((bits_per_word >> 3) - 1)) return -EINVAL; if (speed_hz < c->min_speed_hz || speed_hz > c->max_speed_hz) @@ -411,6 +404,7 @@ static int txx9spi_probe(struct platform_device *dev) master->setup = txx9spi_setup; master->transfer = txx9spi_transfer; master->num_chipselect = (u16)UINT_MAX; /* any GPIO numbers */ + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); ret = spi_register_master(master); if (ret) @@ -425,7 +419,6 @@ exit: clk_disable(c->clk); clk_put(c->clk); } - platform_set_drvdata(dev, NULL); spi_master_put(master); return ret; } @@ -436,7 +429,6 @@ static int txx9spi_remove(struct platform_device *dev) struct txx9spi *c = spi_master_get_devdata(master); spi_unregister_master(master); - platform_set_drvdata(dev, NULL); destroy_workqueue(c->workqueue); clk_disable(c->clk); clk_put(c->clk); diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 4d3ec8b9f479..4258c712ad3c 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -76,7 +76,7 @@ static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm, { unsigned int speed; - if ((t->bits_per_word && t->bits_per_word != 8) || t->len > 62) + if (t->len > 62) return -EINVAL; speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; @@ -209,14 +209,6 @@ static int spi_xcomm_transfer_one(struct spi_master *master, return status; } -static int spi_xcomm_setup(struct spi_device *spi) -{ - if (spi->bits_per_word != 8) - return -EINVAL; - - return 0; -} - static int spi_xcomm_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -233,8 +225,8 @@ static int spi_xcomm_probe(struct i2c_client *i2c, master->num_chipselect = 16; master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_3WIRE; + master->bits_per_word_mask = SPI_BPW_MASK(8); master->flags = SPI_MASTER_HALF_DUPLEX; - master->setup = spi_xcomm_setup; master->transfer_one_message = spi_xcomm_transfer_one; master->dev.of_node = i2c->dev.of_node; i2c_set_clientdata(i2c, master); diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 34d18dcfa0db..fb56fcfdf65e 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -30,6 +30,7 @@ */ #define XSPI_CR_OFFSET 0x60 /* Control Register */ +#define XSPI_CR_LOOP 0x01 #define XSPI_CR_ENABLE 0x02 #define XSPI_CR_MASTER_MODE 0x04 #define XSPI_CR_CPOL 0x08 @@ -232,21 +233,6 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi, return 0; } -static int xilinx_spi_setup(struct spi_device *spi) -{ - /* always return 0, we can not check the number of bits. - * There are cases when SPI setup is called before any driver is - * there, in that case the SPI core defaults to 8 bits, which we - * do not support in some cases. But if we return an error, the - * SPI device would not be registered and no driver can get hold of it - * When the driver is there, it will call SPI setup again with the - * correct number of bits per transfer. - * If a driver setups with the wrong bit number, it will fail when - * it tries to do a transfer - */ - return 0; -} - static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) { u8 sr; @@ -315,7 +301,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) } /* See if there is more data to send */ - if (!xspi->remaining_bytes > 0) + if (xspi->remaining_bytes <= 0) break; } @@ -355,11 +341,12 @@ static const struct of_device_id xilinx_spi_of_match[] = { MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, - u32 irq, s16 bus_num, int num_cs, int little_endian, int bits_per_word) + u32 irq, s16 bus_num, int num_cs, int bits_per_word) { struct spi_master *master; struct xilinx_spi *xspi; int ret; + u32 tmp; master = spi_alloc_master(dev, sizeof(struct xilinx_spi)); if (!master) @@ -373,7 +360,6 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, xspi->bitbang.chipselect = xilinx_spi_chipselect; xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; - xspi->bitbang.master->setup = xilinx_spi_setup; init_completion(&xspi->done); if (!request_mem_region(mem->start, resource_size(mem), @@ -392,13 +378,25 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, xspi->mem = *mem; xspi->irq = irq; - if (little_endian) { - xspi->read_fn = xspi_read32; - xspi->write_fn = xspi_write32; - } else { + + /* + * Detect endianess on the IP via loop bit in CR. Detection + * must be done before reset is sent because incorrect reset + * value generates error interrupt. + * Setup little endian helper functions first and try to use them + * and check if bit was correctly setup or not. + */ + xspi->read_fn = xspi_read32; + xspi->write_fn = xspi_write32; + + xspi->write_fn(XSPI_CR_LOOP, xspi->regs + XSPI_CR_OFFSET); + tmp = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); + tmp &= XSPI_CR_LOOP; + if (tmp != XSPI_CR_LOOP) { xspi->read_fn = xspi_read32_be; xspi->write_fn = xspi_write32_be; } + xspi->bits_per_word = bits_per_word; if (xspi->bits_per_word == 8) { xspi->tx_fn = xspi_tx8; @@ -462,14 +460,13 @@ static int xilinx_spi_probe(struct platform_device *dev) { struct xspi_platform_data *pdata; struct resource *r; - int irq, num_cs = 0, little_endian = 0, bits_per_word = 8; + int irq, num_cs = 0, bits_per_word = 8; struct spi_master *master; u8 i; pdata = dev->dev.platform_data; if (pdata) { num_cs = pdata->num_chipselect; - little_endian = pdata->little_endian; bits_per_word = pdata->bits_per_word; } @@ -501,7 +498,7 @@ static int xilinx_spi_probe(struct platform_device *dev) return -ENXIO; master = xilinx_spi_init(&dev->dev, r, irq, dev->id, num_cs, - little_endian, bits_per_word); + bits_per_word); if (!master) return -ENODEV; @@ -517,7 +514,6 @@ static int xilinx_spi_probe(struct platform_device *dev) static int xilinx_spi_remove(struct platform_device *dev) { xilinx_spi_deinit(platform_get_drvdata(dev)); - platform_set_drvdata(dev, 0); return 0; } diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 085db8b2f2bc..978dda2c5239 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -223,7 +223,7 @@ static const struct dev_pm_ops spi_pm = { SET_RUNTIME_PM_OPS( pm_generic_runtime_suspend, pm_generic_runtime_resume, - pm_generic_runtime_idle + NULL ) }; diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index c6dc1846943f..119d486a5cf7 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -20,6 +20,7 @@ #include <asm/cacheflush.h> #include <linux/fdtable.h> #include <linux/file.h> +#include <linux/freezer.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/miscdevice.h> @@ -2140,13 +2141,13 @@ retry: if (!binder_has_proc_work(proc, thread)) ret = -EAGAIN; } else - ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread)); + ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); } else { if (non_block) { if (!binder_has_thread_work(thread)) ret = -EAGAIN; } else - ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread)); + ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); } binder_lock(__func__); diff --git a/drivers/staging/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig index 34f3b6d02d2a..9a4296c2e3ab 100644 --- a/drivers/staging/media/solo6x10/Kconfig +++ b/drivers/staging/media/solo6x10/Kconfig @@ -1,7 +1,8 @@ config SOLO6X10 tristate "Softlogic 6x10 MPEG codec cards" depends on PCI && VIDEO_DEV && SND && I2C - depends on FONTS + select FONT_SUPPORT + select FONT_8x16 select VIDEOBUF2_DMA_SG select VIDEOBUF2_DMA_CONTIG select SND_PCM diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index ad41319d1d9b..28b35ad9c6cd 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -150,10 +150,6 @@ struct pl011_dmatx_data { struct uart_amba_port { struct uart_port port; struct clk *clk; - /* Two optional pin states - default & sleep */ - struct pinctrl *pinctrl; - struct pinctrl_state *pins_default; - struct pinctrl_state *pins_sleep; const struct vendor_data *vendor; unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ @@ -1479,12 +1475,7 @@ static int pl011_hwinit(struct uart_port *port) int retval; /* Optionaly enable pins to be muxed in and configured */ - if (!IS_ERR(uap->pins_default)) { - retval = pinctrl_select_state(uap->pinctrl, uap->pins_default); - if (retval) - dev_err(port->dev, - "could not set default pins\n"); - } + pinctrl_pm_select_default_state(port->dev); /* * Try to enable the clock producer. @@ -1610,7 +1601,6 @@ static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int cr; - int retval; /* * disable all interrupts @@ -1653,13 +1643,7 @@ static void pl011_shutdown(struct uart_port *port) */ clk_disable_unprepare(uap->clk); /* Optionally let pins go into sleep states */ - if (!IS_ERR(uap->pins_sleep)) { - retval = pinctrl_select_state(uap->pinctrl, uap->pins_sleep); - if (retval) - dev_err(port->dev, - "could not set pins to sleep state\n"); - } - + pinctrl_pm_select_sleep_state(port->dev); if (uap->port.dev->platform_data) { struct amba_pl011_data *plat; @@ -2012,12 +1996,7 @@ static int __init pl011_console_setup(struct console *co, char *options) return -ENODEV; /* Allow pins to be muxed in and configured */ - if (!IS_ERR(uap->pins_default)) { - ret = pinctrl_select_state(uap->pinctrl, uap->pins_default); - if (ret) - dev_err(uap->port.dev, - "could not set default pins\n"); - } + pinctrl_pm_select_default_state(uap->port.dev); ret = clk_prepare(uap->clk); if (ret) @@ -2131,21 +2110,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto out; } - uap->pinctrl = devm_pinctrl_get(&dev->dev); - if (IS_ERR(uap->pinctrl)) { - ret = PTR_ERR(uap->pinctrl); - goto out; - } - uap->pins_default = pinctrl_lookup_state(uap->pinctrl, - PINCTRL_STATE_DEFAULT); - if (IS_ERR(uap->pins_default)) - dev_err(&dev->dev, "could not get default pinstate\n"); - - uap->pins_sleep = pinctrl_lookup_state(uap->pinctrl, - PINCTRL_STATE_SLEEP); - if (IS_ERR(uap->pins_sleep)) - dev_dbg(&dev->dev, "could not get sleep pinstate\n"); - uap->clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c index e266eca0ec76..4a82267af83f 100644 --- a/drivers/tty/serial/mfd.c +++ b/drivers/tty/serial/mfd.c @@ -1252,13 +1252,8 @@ static int serial_hsu_resume(struct pci_dev *pdev) #ifdef CONFIG_PM_RUNTIME static int serial_hsu_runtime_idle(struct device *dev) { - int err; - - err = pm_schedule_suspend(dev, 500); - if (err) - return -EBUSY; - - return 0; + pm_schedule_suspend(dev, 500); + return -EBUSY; } static int serial_hsu_runtime_suspend(struct device *dev) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 6eab440e1542..7609ac4aed1c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1765,7 +1765,8 @@ int usb_runtime_idle(struct device *dev) */ if (autosuspend_check(udev) == 0) pm_runtime_autosuspend(dev); - return 0; + /* Tell the core not to suspend it, though. */ + return -EBUSY; } int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 5fd3fee58f8b..d6b0fadf53e9 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -141,7 +141,6 @@ static const struct dev_pm_ops usb_port_pm_ops = { #ifdef CONFIG_PM_RUNTIME .runtime_suspend = usb_port_runtime_suspend, .runtime_resume = usb_port_runtime_resume, - .runtime_idle = pm_generic_runtime_idle, #endif }; diff --git a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig index 0d03a5200482..36bc28c884ad 100644 --- a/drivers/usb/misc/sisusbvga/Kconfig +++ b/drivers/usb/misc/sisusbvga/Kconfig @@ -2,6 +2,7 @@ config USB_SISUSBVGA tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)" depends on (USB_MUSB_HDRC || USB_EHCI_HCD) + select FONT_SUPPORT if USB_SISUSBVGA_CON ---help--- Say Y here if you intend to attach a USB2VGA dongle based on a Net2280 and a SiS315 chip. diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index bc922c47d046..8c30603e0a86 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -6,7 +6,9 @@ menu "Console display driver support" config VGA_CONSOLE bool "VGA text console" if EXPERT || !X86 - depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) + depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && \ + !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && !CRIS && \ + (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) default y help Saying Y here will allow you to use Linux in text mode through a @@ -62,6 +64,7 @@ config MDA_CONSOLE config SGI_NEWPORT_CONSOLE tristate "SGI Newport Console support" depends on SGI_IP22 + select FONT_SUPPORT help Say Y here if you want the console on the Newport aka XL graphics card of your Indy. Most people say Y here. @@ -91,6 +94,7 @@ config FRAMEBUFFER_CONSOLE tristate "Framebuffer Console support" depends on FB select CRC32 + select FONT_SUPPORT help Low-level framebuffer-based console driver. @@ -123,120 +127,12 @@ config FRAMEBUFFER_CONSOLE_ROTATION config STI_CONSOLE bool "STI text console" depends on PARISC + select FONT_SUPPORT default y help The STI console is the builtin display/keyboard on HP-PARISC machines. Say Y here to build support for it into your kernel. The alternative is to use your primary serial port as a console. -config FONTS - bool "Select compiled-in fonts" - depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE - help - Say Y here if you would like to use fonts other than the default - your frame buffer console usually use. - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about foreign fonts. - - If unsure, say N (the default choices are safe). - -config FONT_8x8 - bool "VGA 8x8 font" if FONTS - depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE - default y if !SPARC && !FONTS - help - This is the "high resolution" font for the VGA frame buffer (the one - provided by the text console 80x50 (and higher) modes). - - Note that this is a poor quality font. The VGA 8x16 font is quite a - lot more readable. - - Given the resolution provided by the frame buffer device, answer N - here is safe. - -config FONT_8x16 - bool "VGA 8x16 font" if FONTS - depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE || STI_CONSOLE || USB_SISUSBVGA_CON - default y if !SPARC && !FONTS - help - This is the "high resolution" font for the VGA frame buffer (the one - provided by the VGA text console 80x25 mode. - - If unsure, say Y. - -config FONT_6x11 - bool "Mac console 6x11 font (not supported by all drivers)" if FONTS - depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE - default y if !SPARC && !FONTS && MAC - help - Small console font with Macintosh-style high-half glyphs. Some Mac - framebuffer drivers don't support this one at all. - -config FONT_7x14 - bool "console 7x14 font (not supported by all drivers)" if FONTS - depends on FRAMEBUFFER_CONSOLE - help - Console font with characters just a bit smaller than the default. - If the standard 8x16 font is a little too big for you, say Y. - Otherwise, say N. - -config FONT_PEARL_8x8 - bool "Pearl (old m68k) console 8x8 font" if FONTS - depends on FRAMEBUFFER_CONSOLE - default y if !SPARC && !FONTS && AMIGA - help - Small console font with PC-style control-character and high-half - glyphs. - -config FONT_ACORN_8x8 - bool "Acorn console 8x8 font" if FONTS - depends on FRAMEBUFFER_CONSOLE - default y if !SPARC && !FONTS && ARM && ARCH_ACORN - help - Small console font with PC-style control characters and high-half - glyphs. - -config FONT_MINI_4x6 - bool "Mini 4x6 font" - depends on !SPARC && FONTS - -config FONT_SUN8x16 - bool "Sparc console 8x16 font" - depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC) - help - This is the high resolution console font for Sun machines. Say Y. - -config FONT_SUN12x22 - bool "Sparc console 12x22 font (not supported by all drivers)" - depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC) - help - This is the high resolution console font for Sun machines with very - big letters (like the letters used in the SPARC PROM). If the - standard font is unreadable for you, say Y, otherwise say N. - -config FONT_10x18 - bool "console 10x18 font (not supported by all drivers)" if FONTS - depends on FRAMEBUFFER_CONSOLE - help - This is a high resolution console font for machines with very - big letters. It fits between the sun 12x22 and the normal 8x16 font. - If other fonts are too big or too small for you, say Y, otherwise say N. - -config FONT_AUTOSELECT - def_bool y - depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE || STI_CONSOLE || USB_SISUSBVGA_CON - depends on !FONT_8x8 - depends on !FONT_6x11 - depends on !FONT_7x14 - depends on !FONT_PEARL_8x8 - depends on !FONT_ACORN_8x8 - depends on !FONT_MINI_4x6 - depends on !FONT_SUN8x16 - depends on !FONT_SUN12x22 - depends on !FONT_10x18 - select FONT_8x16 - endmenu diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile index 48da25c96cd3..43bfa485db96 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -2,32 +2,12 @@ # 5 Aug 1999, James Simmons, <mailto:jsimmons@users.sf.net> # Rewritten to use lists instead of if-statements. -# Font handling -font-objs := fonts.o - -font-objs-$(CONFIG_FONT_SUN8x16) += font_sun8x16.o -font-objs-$(CONFIG_FONT_SUN12x22) += font_sun12x22.o -font-objs-$(CONFIG_FONT_8x8) += font_8x8.o -font-objs-$(CONFIG_FONT_8x16) += font_8x16.o -font-objs-$(CONFIG_FONT_6x11) += font_6x11.o -font-objs-$(CONFIG_FONT_7x14) += font_7x14.o -font-objs-$(CONFIG_FONT_10x18) += font_10x18.o -font-objs-$(CONFIG_FONT_PEARL_8x8) += font_pearl_8x8.o -font-objs-$(CONFIG_FONT_ACORN_8x8) += font_acorn_8x8.o -font-objs-$(CONFIG_FONT_MINI_4x6) += font_mini_4x6.o - -font-objs += $(font-objs-y) - -obj-$(CONFIG_FONTS) += font.o - -# Each configuration option enables a list of files. - obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o -obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o font.o -obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o font.o +obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o +obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o obj-$(CONFIG_VGA_CONSOLE) += vgacon.o obj-$(CONFIG_MDA_CONSOLE) += mdacon.o -obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o font.o softcursor.o +obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o softcursor.o ifeq ($(CONFIG_FB_TILEBLITTING),y) obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += tileblit.o endif @@ -36,8 +16,4 @@ obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon_rotate.o fbcon_cw.o fbcon_ud.o \ fbcon_ccw.o endif -obj-$(CONFIG_FB_STI) += sticore.o font.o - -ifeq ($(CONFIG_USB_SISUSBVGA_CON),y) -obj-$(CONFIG_USB_SISUSBVGA) += font.o -endif +obj-$(CONFIG_FB_STI) += sticore.o diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 5217baf5528c..c70207478e57 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -296,37 +296,6 @@ add_head: } /** - * virtqueue_add_buf - expose buffer to other end - * @vq: the struct virtqueue we're talking about. - * @sg: the description of the buffer(s). - * @out_num: the number of sg readable by other side - * @in_num: the number of sg which are writable (after readable ones) - * @data: the token identifying the buffer. - * @gfp: how to do memory allocations (if necessary). - * - * Caller must ensure we don't call this with other virtqueue operations - * at the same time (except where noted). - * - * Returns zero or a negative error (ie. ENOSPC, ENOMEM). - */ -int virtqueue_add_buf(struct virtqueue *_vq, - struct scatterlist sg[], - unsigned int out, - unsigned int in, - void *data, - gfp_t gfp) -{ - struct scatterlist *sgs[2]; - - sgs[0] = sg; - sgs[1] = sg + out; - - return virtqueue_add(_vq, sgs, sg_next_arr, - out, in, out ? 1 : 0, in ? 1 : 0, data, gfp); -} -EXPORT_SYMBOL_GPL(virtqueue_add_buf); - -/** * virtqueue_add_sgs - expose buffers to other end * @vq: the struct virtqueue we're talking about. * @sgs: array of terminated scatterlists. @@ -473,7 +442,7 @@ EXPORT_SYMBOL_GPL(virtqueue_notify); * virtqueue_kick - update after add_buf * @vq: the struct virtqueue * - * After one or more virtqueue_add_buf calls, invoke this to kick + * After one or more virtqueue_add_* calls, invoke this to kick * the other side. * * Caller must ensure we don't call this with other virtqueue @@ -530,7 +499,7 @@ static inline bool more_used(const struct vring_virtqueue *vq) * operations at the same time (except where noted). * * Returns NULL if there are no used buffers, or the "data" token - * handed to virtqueue_add_buf(). + * handed to virtqueue_add_*(). */ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len) { @@ -685,7 +654,7 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed); * virtqueue_detach_unused_buf - detach first unused buffer * @vq: the struct virtqueue we're talking about. * - * Returns NULL or the "data" token handed to virtqueue_add_buf(). + * Returns NULL or the "data" token handed to virtqueue_add_*(). * This is not valid on an active queue; it is useful only for device * shutdown. */ diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index c8aab4e97833..2a2ef97697b2 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -36,6 +36,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/sched.h> #include <linux/errno.h> @@ -231,7 +233,7 @@ static enum bp_state reserve_additional_memory(long credit) rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT); if (rc) { - pr_info("xen_balloon: %s: add_memory() failed: %i\n", __func__, rc); + pr_info("%s: add_memory() failed: %i\n", __func__, rc); return BP_EAGAIN; } @@ -578,7 +580,7 @@ static int __init balloon_init(void) if (!xen_domain()) return -ENODEV; - pr_info("xen/balloon: Initialising balloon driver.\n"); + pr_info("Initialising balloon driver\n"); balloon_stats.current_pages = xen_pv_domain() ? min(xen_start_info->nr_pages - xen_released_pages, max_pfn) diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c index 084041d42c9a..cc6513a176b0 100644 --- a/drivers/xen/cpu_hotplug.c +++ b/drivers/xen/cpu_hotplug.c @@ -1,3 +1,5 @@ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/notifier.h> #include <xen/xen.h> @@ -31,7 +33,7 @@ static int vcpu_online(unsigned int cpu) err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state); if (err != 1) { if (!xen_initial_domain()) - printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); + pr_err("Unable to read cpu state\n"); return err; } @@ -40,7 +42,7 @@ static int vcpu_online(unsigned int cpu) else if (strcmp(state, "offline") == 0) return 0; - printk(KERN_ERR "XENBUS: unknown state(%s) on CPU%d\n", state, cpu); + pr_err("unknown state(%s) on CPU%d\n", state, cpu); return -EINVAL; } static void vcpu_hotplug(unsigned int cpu) diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 6a6bbe4ede92..a58ac435a9a4 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -21,6 +21,8 @@ * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/linkage.h> #include <linux/interrupt.h> #include <linux/irq.h> @@ -600,8 +602,7 @@ static unsigned int __startup_pirq(unsigned int irq) rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind_pirq); if (rc != 0) { if (!probing_irq(irq)) - printk(KERN_INFO "Failed to obtain physical IRQ %d\n", - irq); + pr_info("Failed to obtain physical IRQ %d\n", irq); return 0; } evtchn = bind_pirq.port; @@ -693,8 +694,8 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi, irq = xen_irq_from_gsi(gsi); if (irq != -1) { - printk(KERN_INFO "xen_map_pirq_gsi: returning irq %d for gsi %u\n", - irq, gsi); + pr_info("%s: returning irq %d for gsi %u\n", + __func__, irq, gsi); goto out; } @@ -812,10 +813,10 @@ int xen_destroy_irq(int irq) * (free_domain_pirqs). */ if ((rc == -ESRCH && info->u.pirq.domid != DOMID_SELF)) - printk(KERN_INFO "domain %d does not have %d anymore\n", + pr_info("domain %d does not have %d anymore\n", info->u.pirq.domid, info->u.pirq.pirq); else if (rc) { - printk(KERN_WARNING "unmap irq failed %d\n", rc); + pr_warn("unmap irq failed %d\n", rc); goto out; } } @@ -1621,8 +1622,8 @@ static void restore_pirqs(void) rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq); if (rc) { - printk(KERN_WARNING "xen map irq failed gsi=%d irq=%d pirq=%d rc=%d\n", - gsi, irq, pirq, rc); + pr_warn("xen map irq failed gsi=%d irq=%d pirq=%d rc=%d\n", + gsi, irq, pirq, rc); xen_free_irq(irq); continue; } @@ -1844,13 +1845,11 @@ void xen_callback_vector(void) callback_via = HVM_CALLBACK_VECTOR(HYPERVISOR_CALLBACK_VECTOR); rc = xen_set_callback_via(callback_via); if (rc) { - printk(KERN_ERR "Request for Xen HVM callback vector" - " failed.\n"); + pr_err("Request for Xen HVM callback vector failed\n"); xen_have_vector_callback = 0; return; } - printk(KERN_INFO "Xen HVM callback vector for event delivery is " - "enabled\n"); + pr_info("Xen HVM callback vector for event delivery is enabled\n"); /* in the restore case the vector has already been allocated */ if (!test_bit(HYPERVISOR_CALLBACK_VECTOR, used_vectors)) alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index 45c8efaa6b3e..8feecf01d55c 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -31,6 +31,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> @@ -547,11 +549,11 @@ static int __init evtchn_init(void) /* Create '/dev/xen/evtchn'. */ err = misc_register(&evtchn_miscdev); if (err != 0) { - printk(KERN_ERR "Could not register /dev/xen/evtchn\n"); + pr_err("Could not register /dev/xen/evtchn\n"); return err; } - printk(KERN_INFO "Event-channel device installed.\n"); + pr_info("Event-channel device installed\n"); return 0; } diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c index 4097987b330e..787d17945418 100644 --- a/drivers/xen/gntalloc.c +++ b/drivers/xen/gntalloc.c @@ -48,6 +48,8 @@ * grant operation. */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/atomic.h> #include <linux/module.h> #include <linux/miscdevice.h> @@ -507,7 +509,7 @@ static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma) int rv, i; if (!(vma->vm_flags & VM_SHARED)) { - printk(KERN_ERR "%s: Mapping must be shared.\n", __func__); + pr_err("%s: Mapping must be shared\n", __func__); return -EINVAL; } @@ -584,7 +586,7 @@ static int __init gntalloc_init(void) err = misc_register(&gntalloc_miscdev); if (err != 0) { - printk(KERN_ERR "Could not register misc gntalloc device\n"); + pr_err("Could not register misc gntalloc device\n"); return err; } diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 3c8803feba26..eab5427c75f5 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -19,6 +19,8 @@ #undef DEBUG +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> @@ -760,7 +762,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) if (use_ptemod && map->vma) goto unlock_out; if (use_ptemod && priv->mm != vma->vm_mm) { - printk(KERN_WARNING "Huh? Other mm?\n"); + pr_warn("Huh? Other mm?\n"); goto unlock_out; } @@ -795,7 +797,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) vma->vm_end - vma->vm_start, find_grant_ptes, map); if (err) { - printk(KERN_WARNING "find_grant_ptes() failure.\n"); + pr_warn("find_grant_ptes() failure.\n"); goto out_put_map; } } @@ -855,7 +857,7 @@ static int __init gntdev_init(void) err = misc_register(&gntdev_miscdev); if (err != 0) { - printk(KERN_ERR "Could not register gntdev device\n"); + pr_err("Could not register gntdev device\n"); return err; } return 0; diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 04c1b2d9b775..04cdeb8e3719 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -31,6 +31,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/sched.h> #include <linux/mm.h> @@ -508,8 +510,7 @@ static void gnttab_handle_deferred(unsigned long unused) entry = NULL; } else { if (!--entry->warn_delay) - pr_info("g.e. %#x still pending\n", - entry->ref); + pr_info("g.e. %#x still pending\n", entry->ref); if (!first) first = entry; } @@ -838,7 +839,7 @@ gnttab_retry_eagain_gop(unsigned int cmd, void *gop, int16_t *status, } while ((*status == GNTST_eagain) && (delay < MAX_DELAY)); if (delay >= MAX_DELAY) { - printk(KERN_ERR "%s: %s eagain grant\n", func, current->comm); + pr_err("%s: %s eagain grant\n", func, current->comm); *status = GNTST_bad_page; } } @@ -1048,8 +1049,8 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx) xatp.gpfn = (xen_hvm_resume_frames >> PAGE_SHIFT) + i; rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp); if (rc != 0) { - printk(KERN_WARNING - "grant table add_to_physmap failed, err=%d\n", rc); + pr_warn("grant table add_to_physmap failed, err=%d\n", + rc); break; } } while (i-- > start_idx); @@ -1131,8 +1132,7 @@ static void gnttab_request_version(void) grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1); gnttab_interface = &gnttab_v1_ops; } - printk(KERN_INFO "Grant tables using version %d layout.\n", - grant_table_version); + pr_info("Grant tables using version %d layout\n", grant_table_version); } static int gnttab_setup(void) @@ -1150,8 +1150,7 @@ static int gnttab_setup(void) gnttab_shared.addr = xen_remap(xen_hvm_resume_frames, PAGE_SIZE * max_nr_gframes); if (gnttab_shared.addr == NULL) { - printk(KERN_WARNING - "Failed to ioremap gnttab share frames!"); + pr_warn("Failed to ioremap gnttab share frames!\n"); return -ENOMEM; } } diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 412b96cc5305..9385b4524547 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -1,6 +1,9 @@ /* * Handle extern requests for shutdown, reboot and sysrq */ + +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/err.h> #include <linux/slab.h> @@ -43,6 +46,7 @@ struct suspend_info { void (*post)(int cancelled); }; +#ifdef CONFIG_HIBERNATE_CALLBACKS static void xen_hvm_post_suspend(int cancelled) { xen_arch_hvm_post_suspend(cancelled); @@ -63,7 +67,6 @@ static void xen_post_suspend(int cancelled) xen_mm_unpin_all(); } -#ifdef CONFIG_HIBERNATE_CALLBACKS static int xen_suspend(void *data) { struct suspend_info *si = data; @@ -73,8 +76,7 @@ static int xen_suspend(void *data) err = syscore_suspend(); if (err) { - printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n", - err); + pr_err("%s: system core suspend failed: %d\n", __func__, err); return err; } @@ -115,14 +117,14 @@ static void do_suspend(void) during suspend. */ err = freeze_processes(); if (err) { - printk(KERN_ERR "xen suspend: freeze failed %d\n", err); + pr_err("%s: freeze failed %d\n", __func__, err); goto out; } #endif err = dpm_suspend_start(PMSG_FREEZE); if (err) { - printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err); + pr_err("%s: dpm_suspend_start %d\n", __func__, err); goto out_thaw; } @@ -131,7 +133,7 @@ static void do_suspend(void) err = dpm_suspend_end(PMSG_FREEZE); if (err) { - printk(KERN_ERR "dpm_suspend_end failed: %d\n", err); + pr_err("dpm_suspend_end failed: %d\n", err); si.cancelled = 0; goto out_resume; } @@ -153,7 +155,7 @@ static void do_suspend(void) dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE); if (err) { - printk(KERN_ERR "failed to start xen_suspend: %d\n", err); + pr_err("failed to start xen_suspend: %d\n", err); si.cancelled = 1; } @@ -245,7 +247,7 @@ static void shutdown_handler(struct xenbus_watch *watch, if (handler->cb) { handler->cb(); } else { - printk(KERN_INFO "Ignoring shutdown request: %s\n", str); + pr_info("Ignoring shutdown request: %s\n", str); shutting_down = SHUTDOWN_INVALID; } @@ -265,8 +267,7 @@ static void sysrq_handler(struct xenbus_watch *watch, const char **vec, if (err) return; if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { - printk(KERN_ERR "Unable to read sysrq code in " - "control/sysrq\n"); + pr_err("Unable to read sysrq code in control/sysrq\n"); xenbus_transaction_end(xbt, 1); return; } @@ -299,14 +300,14 @@ static int setup_shutdown_watcher(void) err = register_xenbus_watch(&shutdown_watch); if (err) { - printk(KERN_ERR "Failed to set shutdown watcher\n"); + pr_err("Failed to set shutdown watcher\n"); return err; } #ifdef CONFIG_MAGIC_SYSRQ err = register_xenbus_watch(&sysrq_watch); if (err) { - printk(KERN_ERR "Failed to set sysrq watcher\n"); + pr_err("Failed to set sysrq watcher\n"); return err; } #endif diff --git a/drivers/xen/mcelog.c b/drivers/xen/mcelog.c index 8feee08bcb43..6ab6a79c38a5 100644 --- a/drivers/xen/mcelog.c +++ b/drivers/xen/mcelog.c @@ -32,6 +32,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen_mcelog: " fmt + #include <linux/init.h> #include <linux/types.h> #include <linux/kernel.h> @@ -51,8 +53,6 @@ #include <asm/xen/hypercall.h> #include <asm/xen/hypervisor.h> -#define XEN_MCELOG "xen_mcelog: " - static struct mc_info g_mi; static struct mcinfo_logical_cpu *g_physinfo; static uint32_t ncpus; @@ -227,7 +227,7 @@ static int convert_log(struct mc_info *mi) mic = NULL; x86_mcinfo_lookup(&mic, mi, MC_TYPE_GLOBAL); if (unlikely(!mic)) { - pr_warning(XEN_MCELOG "Failed to find global error info\n"); + pr_warn("Failed to find global error info\n"); return -ENODEV; } @@ -241,8 +241,7 @@ static int convert_log(struct mc_info *mi) if (g_physinfo[i].mc_apicid == m.apicid) break; if (unlikely(i == ncpus)) { - pr_warning(XEN_MCELOG "Failed to match cpu with apicid %d\n", - m.apicid); + pr_warn("Failed to match cpu with apicid %d\n", m.apicid); return -ENODEV; } @@ -254,7 +253,7 @@ static int convert_log(struct mc_info *mi) mic = NULL; x86_mcinfo_lookup(&mic, mi, MC_TYPE_BANK); if (unlikely(!mic)) { - pr_warning(XEN_MCELOG "Fail to find bank error info\n"); + pr_warn("Fail to find bank error info\n"); return -ENODEV; } @@ -295,9 +294,8 @@ static int mc_queue_handle(uint32_t flags) mc_op.u.mc_fetch.flags = flags; ret = HYPERVISOR_mca(&mc_op); if (ret) { - pr_err(XEN_MCELOG "Failed to fetch %s error log\n", - (flags == XEN_MC_URGENT) ? - "urgnet" : "nonurgent"); + pr_err("Failed to fetch %surgent error log\n", + flags == XEN_MC_URGENT ? "" : "non"); break; } @@ -307,15 +305,12 @@ static int mc_queue_handle(uint32_t flags) else { ret = convert_log(&g_mi); if (ret) - pr_warning(XEN_MCELOG - "Failed to convert this error log, " - "continue acking it anyway\n"); + pr_warn("Failed to convert this error log, continue acking it anyway\n"); mc_op.u.mc_fetch.flags = flags | XEN_MC_ACK; ret = HYPERVISOR_mca(&mc_op); if (ret) { - pr_err(XEN_MCELOG - "Failed to ack previous error log\n"); + pr_err("Failed to ack previous error log\n"); break; } } @@ -334,15 +329,12 @@ static void xen_mce_work_fn(struct work_struct *work) /* urgent mc_info */ err = mc_queue_handle(XEN_MC_URGENT); if (err) - pr_err(XEN_MCELOG - "Failed to handle urgent mc_info queue, " - "continue handling nonurgent mc_info queue anyway.\n"); + pr_err("Failed to handle urgent mc_info queue, continue handling nonurgent mc_info queue anyway\n"); /* nonurgent mc_info */ err = mc_queue_handle(XEN_MC_NONURGENT); if (err) - pr_err(XEN_MCELOG - "Failed to handle nonurgent mc_info queue.\n"); + pr_err("Failed to handle nonurgent mc_info queue\n"); /* wake processes polling /dev/mcelog */ wake_up_interruptible(&xen_mce_chrdev_wait); @@ -370,7 +362,7 @@ static int bind_virq_for_mce(void) set_xen_guest_handle(mc_op.u.mc_physcpuinfo.info, g_physinfo); ret = HYPERVISOR_mca(&mc_op); if (ret) { - pr_err(XEN_MCELOG "Failed to get CPU numbers\n"); + pr_err("Failed to get CPU numbers\n"); return ret; } @@ -383,7 +375,7 @@ static int bind_virq_for_mce(void) set_xen_guest_handle(mc_op.u.mc_physcpuinfo.info, g_physinfo); ret = HYPERVISOR_mca(&mc_op); if (ret) { - pr_err(XEN_MCELOG "Failed to get CPU info\n"); + pr_err("Failed to get CPU info\n"); kfree(g_physinfo); return ret; } @@ -391,7 +383,7 @@ static int bind_virq_for_mce(void) ret = bind_virq_to_irqhandler(VIRQ_MCA, 0, xen_mce_interrupt, 0, "mce", NULL); if (ret < 0) { - pr_err(XEN_MCELOG "Failed to bind virq\n"); + pr_err("Failed to bind virq\n"); kfree(g_physinfo); return ret; } diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c index 6536d5ab1697..79e1dff7ed4f 100644 --- a/drivers/xen/pcpu.c +++ b/drivers/xen/pcpu.c @@ -31,6 +31,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen_cpu: " fmt + #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/cpu.h> @@ -44,7 +46,6 @@ #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> -#define XEN_PCPU "xen_cpu: " /* * @cpu_id: Xen physical cpu logic number @@ -242,8 +243,7 @@ static struct pcpu *create_and_register_pcpu(struct xenpf_pcpuinfo *info) err = register_pcpu(pcpu); if (err) { - pr_warning(XEN_PCPU "Failed to register pcpu%u\n", - info->xen_cpuid); + pr_warn("Failed to register pcpu%u\n", info->xen_cpuid); return ERR_PTR(-ENOENT); } @@ -378,19 +378,19 @@ static int __init xen_pcpu_init(void) xen_pcpu_interrupt, 0, "xen-pcpu", NULL); if (irq < 0) { - pr_warning(XEN_PCPU "Failed to bind pcpu virq\n"); + pr_warn("Failed to bind pcpu virq\n"); return irq; } ret = subsys_system_register(&xen_pcpu_subsys, NULL); if (ret) { - pr_warning(XEN_PCPU "Failed to register pcpu subsys\n"); + pr_warn("Failed to register pcpu subsys\n"); goto err1; } ret = xen_sync_pcpus(); if (ret) { - pr_warning(XEN_PCPU "Failed to sync pcpu info\n"); + pr_warn("Failed to sync pcpu info\n"); goto err2; } diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 2cfc24d76fc5..f8e5dd701ecb 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -6,6 +6,8 @@ * Copyright (c) 2002-2004, K A Fraser, B Dragovic */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/sched.h> @@ -565,7 +567,7 @@ static int __init privcmd_init(void) err = misc_register(&privcmd_dev); if (err != 0) { - printk(KERN_ERR "Could not register Xen privcmd device\n"); + pr_err("Could not register Xen privcmd device\n"); return err; } return 0; diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 1d94316f0ea4..aadffcf7db9b 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -33,6 +33,8 @@ * */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/bootmem.h> #include <linux/dma-mapping.h> #include <linux/export.h> @@ -202,8 +204,8 @@ retry: order--; } if (order != get_order(bytes)) { - pr_warn("Warning: only able to allocate %ld MB " - "for software IO TLB\n", (PAGE_SIZE << order) >> 20); + pr_warn("Warning: only able to allocate %ld MB for software IO TLB\n", + (PAGE_SIZE << order) >> 20); xen_io_tlb_nslabs = SLABS_PER_PAGE << order; bytes = xen_io_tlb_nslabs << IO_TLB_SHIFT; } @@ -242,11 +244,11 @@ error: if (repeat--) { xen_io_tlb_nslabs = max(1024UL, /* Min is 2MB */ (xen_io_tlb_nslabs >> 1)); - printk(KERN_INFO "Xen-SWIOTLB: Lowering to %luMB\n", - (xen_io_tlb_nslabs << IO_TLB_SHIFT) >> 20); + pr_info("Lowering to %luMB\n", + (xen_io_tlb_nslabs << IO_TLB_SHIFT) >> 20); goto retry; } - pr_err("%s (rc:%d)", xen_swiotlb_error(m_ret), rc); + pr_err("%s (rc:%d)\n", xen_swiotlb_error(m_ret), rc); if (early) panic("%s (rc:%d)", xen_swiotlb_error(m_ret), rc); else diff --git a/drivers/xen/tmem.c b/drivers/xen/tmem.c index 0f0493c63371..83b5c53bec6b 100644 --- a/drivers/xen/tmem.c +++ b/drivers/xen/tmem.c @@ -5,6 +5,8 @@ * Author: Dan Magenheimer */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> @@ -388,8 +390,8 @@ static int xen_tmem_init(void) return PTR_ERR(old_ops); s = " (WARNING: frontswap_ops overridden)"; } - printk(KERN_INFO "frontswap enabled, RAM provided by " - "Xen Transcendent Memory%s\n", s); + pr_info("frontswap enabled, RAM provided by Xen Transcendent Memory%s\n", + s); } #endif #ifdef CONFIG_CLEANCACHE @@ -400,8 +402,8 @@ static int xen_tmem_init(void) cleancache_register_ops(&tmem_cleancache_ops); if (old_ops) s = " (WARNING: cleancache_ops overridden)"; - printk(KERN_INFO "cleancache enabled, RAM provided by " - "Xen Transcendent Memory%s\n", s); + pr_info("cleancache enabled, RAM provided by Xen Transcendent Memory%s\n", + s); } #endif #ifdef CONFIG_XEN_SELFBALLOONING diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c index 18c742bec91b..0caf4863be8c 100644 --- a/drivers/xen/xen-acpi-cpuhotplug.c +++ b/drivers/xen/xen-acpi-cpuhotplug.c @@ -15,6 +15,8 @@ * details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/xen/xen-acpi-memhotplug.c b/drivers/xen/xen-acpi-memhotplug.c index faef5b396051..9083f1e474f8 100644 --- a/drivers/xen/xen-acpi-memhotplug.c +++ b/drivers/xen/xen-acpi-memhotplug.c @@ -15,6 +15,8 @@ * details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c index c763479ed85e..59708fdd068b 100644 --- a/drivers/xen/xen-acpi-pad.c +++ b/drivers/xen/xen-acpi-pad.c @@ -14,6 +14,8 @@ * more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/types.h> #include <acpi/acpi_bus.h> diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 8abd7d579037..13bc6c31c060 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -17,6 +17,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/cpumask.h> #include <linux/cpufreq.h> #include <linux/freezer.h> @@ -34,8 +36,6 @@ #include <xen/interface/platform.h> #include <asm/xen/hypercall.h> -#define DRV_NAME "xen-acpi-processor: " - static int no_hypercall; MODULE_PARM_DESC(off, "Inhibit the hypercall."); module_param_named(off, no_hypercall, int, 0400); @@ -104,7 +104,7 @@ static int push_cxx_to_hypervisor(struct acpi_processor *_pr) set_xen_guest_handle(dst_cx->dp, NULL); } if (!ok) { - pr_debug(DRV_NAME "No _Cx for ACPI CPU %u\n", _pr->acpi_id); + pr_debug("No _Cx for ACPI CPU %u\n", _pr->acpi_id); kfree(dst_cx_states); return -EINVAL; } @@ -133,7 +133,7 @@ static int push_cxx_to_hypervisor(struct acpi_processor *_pr) /* EINVAL means the ACPI ID is incorrect - meaning the ACPI * table is referencing a non-existing CPU - which can happen * with broken ACPI tables. */ - pr_err(DRV_NAME "(CX): Hypervisor error (%d) for ACPI CPU%u\n", + pr_err("(CX): Hypervisor error (%d) for ACPI CPU%u\n", ret, _pr->acpi_id); kfree(dst_cx_states); @@ -239,7 +239,7 @@ static int push_pxx_to_hypervisor(struct acpi_processor *_pr) dst_perf->flags |= XEN_PX_PSD; if (dst_perf->flags != (XEN_PX_PSD | XEN_PX_PSS | XEN_PX_PCT | XEN_PX_PPC)) { - pr_warn(DRV_NAME "ACPI CPU%u missing some P-state data (%x), skipping.\n", + pr_warn("ACPI CPU%u missing some P-state data (%x), skipping\n", _pr->acpi_id, dst_perf->flags); ret = -ENODEV; goto err_free; @@ -265,8 +265,8 @@ static int push_pxx_to_hypervisor(struct acpi_processor *_pr) /* EINVAL means the ACPI ID is incorrect - meaning the ACPI * table is referencing a non-existing CPU - which can happen * with broken ACPI tables. */ - pr_warn(DRV_NAME "(_PXX): Hypervisor error (%d) for ACPI CPU%u\n", - ret, _pr->acpi_id); + pr_warn("(_PXX): Hypervisor error (%d) for ACPI CPU%u\n", + ret, _pr->acpi_id); err_free: if (!IS_ERR_OR_NULL(dst_states)) kfree(dst_states); @@ -318,7 +318,7 @@ static unsigned int __init get_max_acpi_id(void) max_acpi_id = max(info->acpi_id, max_acpi_id); } max_acpi_id *= 2; /* Slack for CPU hotplug support. */ - pr_debug(DRV_NAME "Max ACPI ID: %u\n", max_acpi_id); + pr_debug("Max ACPI ID: %u\n", max_acpi_id); return max_acpi_id; } /* @@ -365,15 +365,14 @@ read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv) /* There are more ACPI Processor objects than in x2APIC or MADT. * This can happen with incorrect ACPI SSDT declerations. */ if (acpi_id > nr_acpi_bits) { - pr_debug(DRV_NAME "We only have %u, trying to set %u\n", + pr_debug("We only have %u, trying to set %u\n", nr_acpi_bits, acpi_id); return AE_OK; } /* OK, There is a ACPI Processor object */ __set_bit(acpi_id, acpi_id_present); - pr_debug(DRV_NAME "ACPI CPU%u w/ PBLK:0x%lx\n", acpi_id, - (unsigned long)pblk); + pr_debug("ACPI CPU%u w/ PBLK:0x%lx\n", acpi_id, (unsigned long)pblk); status = acpi_evaluate_object(handle, "_CST", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -476,7 +475,7 @@ static int xen_upload_processor_pm_data(void) unsigned int i; int rc = 0; - pr_info(DRV_NAME "Uploading Xen processor PM info\n"); + pr_info("Uploading Xen processor PM info\n"); for_each_possible_cpu(i) { struct acpi_processor *_pr; @@ -523,7 +522,7 @@ static int __init xen_acpi_processor_init(void) acpi_perf_data = alloc_percpu(struct acpi_processor_performance); if (!acpi_perf_data) { - pr_debug(DRV_NAME "Memory allocation error for acpi_perf_data.\n"); + pr_debug("Memory allocation error for acpi_perf_data\n"); kfree(acpi_ids_done); return -ENOMEM; } diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c index 8f37e23f6d13..e555845d61fa 100644 --- a/drivers/xen/xen-balloon.c +++ b/drivers/xen/xen-balloon.c @@ -30,6 +30,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/capability.h> @@ -81,7 +83,7 @@ static int balloon_init_watcher(struct notifier_block *notifier, err = register_xenbus_watch(&target_watch); if (err) - printk(KERN_ERR "Failed to set balloon watcher\n"); + pr_err("Failed to set balloon watcher\n"); return NOTIFY_DONE; } @@ -95,7 +97,7 @@ static int __init balloon_init(void) if (!xen_domain()) return -ENODEV; - pr_info("xen-balloon: Initialising balloon driver.\n"); + pr_info("Initialising balloon driver\n"); register_balloon(&balloon_dev); diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c index 3daf862d739d..c5ee82587e8c 100644 --- a/drivers/xen/xen-pciback/conf_space_header.c +++ b/drivers/xen/xen-pciback/conf_space_header.c @@ -4,6 +4,8 @@ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/pci.h> #include "pciback.h" @@ -75,10 +77,8 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) pci_name(dev)); err = pci_set_mwi(dev); if (err) { - printk(KERN_WARNING - DRV_NAME ": %s: cannot enable " - "memory-write-invalidate (%d)\n", - pci_name(dev), err); + pr_warn("%s: cannot enable memory-write-invalidate (%d)\n", + pci_name(dev), err); value &= ~PCI_COMMAND_INVALIDATE; } } @@ -91,7 +91,7 @@ static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data) struct pci_bar_info *bar = data; if (unlikely(!bar)) { - printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n", + pr_warn(DRV_NAME ": driver data not found for %s\n", pci_name(dev)); return XEN_PCI_ERR_op_failed; } @@ -125,7 +125,7 @@ static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data) struct pci_bar_info *bar = data; if (unlikely(!bar)) { - printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n", + pr_warn(DRV_NAME ": driver data not found for %s\n", pci_name(dev)); return XEN_PCI_ERR_op_failed; } @@ -153,7 +153,7 @@ static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data) struct pci_bar_info *bar = data; if (unlikely(!bar)) { - printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n", + pr_warn(DRV_NAME ": driver data not found for %s\n", pci_name(dev)); return XEN_PCI_ERR_op_failed; } @@ -375,7 +375,7 @@ int xen_pcibk_config_header_add_fields(struct pci_dev *dev) default: err = -EINVAL; - printk(KERN_ERR DRV_NAME ": %s: Unsupported header type %d!\n", + pr_err("%s: Unsupported header type %d!\n", pci_name(dev), dev->hdr_type); break; } diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c index 4e8ba38aa0c9..62fcd485f0a7 100644 --- a/drivers/xen/xen-pciback/pci_stub.c +++ b/drivers/xen/xen-pciback/pci_stub.c @@ -4,6 +4,9 @@ * Ryan Wilson <hap9@epoch.ncsc.mil> * Chris Bookholt <hap10@epoch.ncsc.mil> */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/rwsem.h> @@ -425,8 +428,6 @@ static int __init pcistub_init_devices_late(void) unsigned long flags; int err = 0; - pr_debug(DRV_NAME ": pcistub_init_devices_late\n"); - spin_lock_irqsave(&pcistub_devices_lock, flags); while (!list_empty(&seized_devices)) { @@ -544,15 +545,11 @@ static void pcistub_remove(struct pci_dev *dev) found_psdev->pdev); if (found_psdev->pdev) { - printk(KERN_WARNING DRV_NAME ": ****** removing device " - "%s while still in-use! ******\n", + pr_warn("****** removing device %s while still in-use! ******\n", pci_name(found_psdev->dev)); - printk(KERN_WARNING DRV_NAME ": ****** driver domain may" - " still access this device's i/o resources!\n"); - printk(KERN_WARNING DRV_NAME ": ****** shutdown driver " - "domain before binding device\n"); - printk(KERN_WARNING DRV_NAME ": ****** to other drivers " - "or domains\n"); + pr_warn("****** driver domain may still access this device's i/o resources!\n"); + pr_warn("****** shutdown driver domain before binding device\n"); + pr_warn("****** to other drivers or domains\n"); xen_pcibk_release_pci_dev(found_psdev->pdev, found_psdev->dev); @@ -1018,7 +1015,7 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func) pci_dev_id->bus = bus; pci_dev_id->devfn = devfn; - pr_debug(DRV_NAME ": wants to seize %04x:%02x:%02x.%d\n", + pr_debug("wants to seize %04x:%02x:%02x.%d\n", domain, bus, slot, func); spin_lock_irqsave(&device_ids_lock, flags); @@ -1048,8 +1045,8 @@ static int pcistub_device_id_remove(int domain, int bus, int slot, int func) err = 0; - pr_debug(DRV_NAME ": removed %04x:%02x:%02x.%d from " - "seize list\n", domain, bus, slot, func); + pr_debug("removed %04x:%02x:%02x.%d from seize list\n", + domain, bus, slot, func); } } spin_unlock_irqrestore(&device_ids_lock, flags); @@ -1196,19 +1193,23 @@ static ssize_t pcistub_irq_handler_switch(struct device_driver *drv, struct pcistub_device *psdev; struct xen_pcibk_dev_data *dev_data; int domain, bus, slot, func; - int err = -ENOENT; + int err; err = str_to_slot(buf, &domain, &bus, &slot, &func); if (err) return err; psdev = pcistub_device_find(domain, bus, slot, func); - if (!psdev) + if (!psdev) { + err = -ENOENT; goto out; + } dev_data = pci_get_drvdata(psdev->dev); - if (!dev_data) + if (!dev_data) { + err = -ENOENT; goto out; + } dev_dbg(&psdev->dev->dev, "%s fake irq handler: %d->%d\n", dev_data->irq_name, dev_data->isr_on, @@ -1470,7 +1471,7 @@ out: return err; parse_error: - printk(KERN_ERR DRV_NAME ": Error parsing pci_devs_to_hide at \"%s\"\n", + pr_err("Error parsing pci_devs_to_hide at \"%s\"\n", pci_devs_to_hide + pos); return -EINVAL; } diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index b98cf0c35725..64eb0cd8b8af 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -3,6 +3,9 @@ * * Author: Ryan Wilson <hap9@epoch.ncsc.mil> */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/wait.h> #include <linux/bitops.h> @@ -144,7 +147,7 @@ int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev, status = pci_enable_msi(dev); if (status) { - pr_warn_ratelimited(DRV_NAME ": %s: error enabling MSI for guest %u: err %d\n", + pr_warn_ratelimited("%s: error enabling MSI for guest %u: err %d\n", pci_name(dev), pdev->xdev->otherend_id, status); op->value = 0; @@ -225,7 +228,7 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev, op->msix_entries[i].vector); } } else - pr_warn_ratelimited(DRV_NAME ": %s: error enabling MSI-X for guest %u: err %d!\n", + pr_warn_ratelimited("%s: error enabling MSI-X for guest %u: err %d!\n", pci_name(dev), pdev->xdev->otherend_id, result); kfree(entries); @@ -372,7 +375,7 @@ static irqreturn_t xen_pcibk_guest_interrupt(int irq, void *dev_id) dev_data->handled++; if ((dev_data->handled % 1000) == 0) { if (xen_test_irq_shared(irq)) { - printk(KERN_INFO "%s IRQ line is not shared " + pr_info("%s IRQ line is not shared " "with other domains. Turning ISR off\n", dev_data->irq_name); dev_data->ack_intr = 0; diff --git a/drivers/xen/xen-pciback/vpci.c b/drivers/xen/xen-pciback/vpci.c index 0f478ac483cd..3165ce361b00 100644 --- a/drivers/xen/xen-pciback/vpci.c +++ b/drivers/xen/xen-pciback/vpci.c @@ -5,6 +5,8 @@ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/list.h> #include <linux/slab.h> #include <linux/pci.h> @@ -102,8 +104,7 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, struct pci_dev_entry, list); if (match_slot(dev, t->dev)) { - pr_info(DRV_NAME ": vpci: %s: " - "assign to virtual slot %d func %d\n", + pr_info("vpci: %s: assign to virtual slot %d func %d\n", pci_name(dev), slot, PCI_FUNC(dev->devfn)); list_add_tail(&dev_entry->list, @@ -117,9 +118,8 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, /* Assign to a new slot on the virtual PCI bus */ for (slot = 0; slot < PCI_SLOT_MAX; slot++) { if (list_empty(&vpci_dev->dev_list[slot])) { - printk(KERN_INFO DRV_NAME - ": vpci: %s: assign to virtual slot %d\n", - pci_name(dev), slot); + pr_info("vpci: %s: assign to virtual slot %d\n", + pci_name(dev), slot); list_add_tail(&dev_entry->list, &vpci_dev->dev_list[slot]); func = dev->is_virtfn ? 0 : PCI_FUNC(dev->devfn); diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c index 64b11f99eacc..a9ed867afaba 100644 --- a/drivers/xen/xen-pciback/xenbus.c +++ b/drivers/xen/xen-pciback/xenbus.c @@ -3,6 +3,9 @@ * * Author: Ryan Wilson <hap9@epoch.ncsc.mil> */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/list.h> @@ -723,14 +726,13 @@ int __init xen_pcibk_xenbus_register(void) { xen_pcibk_wq = create_workqueue("xen_pciback_workqueue"); if (!xen_pcibk_wq) { - printk(KERN_ERR "%s: create" - "xen_pciback_workqueue failed\n", __func__); + pr_err("%s: create xen_pciback_workqueue failed\n", __func__); return -EFAULT; } xen_pcibk_backend = &xen_pcibk_vpci_backend; if (passthrough) xen_pcibk_backend = &xen_pcibk_passthrough_backend; - pr_info(DRV_NAME ": backend is %s\n", xen_pcibk_backend->name); + pr_info("backend is %s\n", xen_pcibk_backend->name); return xenbus_register_backend(&xen_pcibk_driver); } diff --git a/drivers/xen/xen-selfballoon.c b/drivers/xen/xen-selfballoon.c index f70984a892aa..02817a85f877 100644 --- a/drivers/xen/xen-selfballoon.c +++ b/drivers/xen/xen-selfballoon.c @@ -64,6 +64,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/bootmem.h> #include <linux/swap.h> @@ -510,22 +512,19 @@ int xen_selfballoon_init(bool use_selfballooning, bool use_frontswap_selfshrink) return -ENODEV; if (xen_initial_domain()) { - pr_info("xen/balloon: Xen selfballooning driver " - "disabled for domain0.\n"); + pr_info("Xen selfballooning driver disabled for domain0\n"); return -ENODEV; } xen_selfballooning_enabled = tmem_enabled && use_selfballooning; if (xen_selfballooning_enabled) { - pr_info("xen/balloon: Initializing Xen " - "selfballooning driver.\n"); + pr_info("Initializing Xen selfballooning driver\n"); enable = true; } #ifdef CONFIG_FRONTSWAP frontswap_selfshrinking = tmem_enabled && use_frontswap_selfshrink; if (frontswap_selfshrinking) { - pr_info("xen/balloon: Initializing frontswap " - "selfshrinking driver.\n"); + pr_info("Initializing frontswap selfshrinking driver\n"); enable = true; } #endif diff --git a/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c index c5aa55c5d371..fdb0f339d0a7 100644 --- a/drivers/xen/xenbus/xenbus_comms.c +++ b/drivers/xen/xenbus/xenbus_comms.c @@ -30,6 +30,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/wait.h> #include <linux/interrupt.h> #include <linux/sched.h> @@ -205,13 +207,12 @@ int xb_init_comms(void) struct xenstore_domain_interface *intf = xen_store_interface; if (intf->req_prod != intf->req_cons) - printk(KERN_ERR "XENBUS request ring is not quiescent " - "(%08x:%08x)!\n", intf->req_cons, intf->req_prod); + pr_err("request ring is not quiescent (%08x:%08x)!\n", + intf->req_cons, intf->req_prod); if (intf->rsp_prod != intf->rsp_cons) { - printk(KERN_WARNING "XENBUS response ring is not quiescent " - "(%08x:%08x): fixing up\n", - intf->rsp_cons, intf->rsp_prod); + pr_warn("response ring is not quiescent (%08x:%08x): fixing up\n", + intf->rsp_cons, intf->rsp_prod); /* breaks kdump */ if (!reset_devices) intf->rsp_cons = intf->rsp_prod; @@ -225,7 +226,7 @@ int xb_init_comms(void) err = bind_evtchn_to_irqhandler(xen_store_evtchn, wake_waiting, 0, "xenbus", &xb_waitq); if (err < 0) { - printk(KERN_ERR "XENBUS request irq failed %i\n", err); + pr_err("request irq failed %i\n", err); return err; } diff --git a/drivers/xen/xenbus/xenbus_dev_backend.c b/drivers/xen/xenbus/xenbus_dev_backend.c index a6f42fc01407..b17707ee07d4 100644 --- a/drivers/xen/xenbus/xenbus_dev_backend.c +++ b/drivers/xen/xenbus/xenbus_dev_backend.c @@ -1,3 +1,5 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/slab.h> #include <linux/types.h> #include <linux/mm.h> @@ -127,7 +129,7 @@ static int __init xenbus_backend_init(void) err = misc_register(&xenbus_backend_dev); if (err) - printk(KERN_ERR "Could not register xenbus backend device\n"); + pr_err("Could not register xenbus backend device\n"); return err; } diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c index ac727028e658..85534ea63555 100644 --- a/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -35,6 +35,8 @@ * Turned xenfs into a loadable module. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/errno.h> #include <linux/uio.h> @@ -616,7 +618,7 @@ static int __init xenbus_init(void) err = misc_register(&xenbus_dev); if (err) - printk(KERN_ERR "Could not register xenbus frontend device\n"); + pr_err("Could not register xenbus frontend device\n"); return err; } diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 8c9db16500e3..38e92b770e91 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -30,6 +30,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define DPRINTK(fmt, args...) \ pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ __func__, __LINE__, ##args) @@ -280,15 +282,15 @@ void xenbus_dev_shutdown(struct device *_dev) get_device(&dev->dev); if (dev->state != XenbusStateConnected) { - printk(KERN_INFO "%s: %s: %s != Connected, skipping\n", __func__, - dev->nodename, xenbus_strstate(dev->state)); + pr_info("%s: %s: %s != Connected, skipping\n", + __func__, dev->nodename, xenbus_strstate(dev->state)); goto out; } xenbus_switch_state(dev, XenbusStateClosing); timeout = wait_for_completion_timeout(&dev->down, timeout); if (!timeout) - printk(KERN_INFO "%s: %s timeout closing device\n", - __func__, dev->nodename); + pr_info("%s: %s timeout closing device\n", + __func__, dev->nodename); out: put_device(&dev->dev); } @@ -579,8 +581,7 @@ int xenbus_dev_suspend(struct device *dev) if (drv->suspend) err = drv->suspend(xdev); if (err) - printk(KERN_WARNING - "xenbus: suspend %s failed: %i\n", dev_name(dev), err); + pr_warn("suspend %s failed: %i\n", dev_name(dev), err); return 0; } EXPORT_SYMBOL_GPL(xenbus_dev_suspend); @@ -599,9 +600,8 @@ int xenbus_dev_resume(struct device *dev) drv = to_xenbus_driver(dev->driver); err = talk_to_otherend(xdev); if (err) { - printk(KERN_WARNING - "xenbus: resume (talk_to_otherend) %s failed: %i\n", - dev_name(dev), err); + pr_warn("resume (talk_to_otherend) %s failed: %i\n", + dev_name(dev), err); return err; } @@ -610,18 +610,15 @@ int xenbus_dev_resume(struct device *dev) if (drv->resume) { err = drv->resume(xdev); if (err) { - printk(KERN_WARNING - "xenbus: resume %s failed: %i\n", - dev_name(dev), err); + pr_warn("resume %s failed: %i\n", dev_name(dev), err); return err; } } err = watch_otherend(xdev); if (err) { - printk(KERN_WARNING - "xenbus_probe: resume (watch_otherend) %s failed: " - "%d.\n", dev_name(dev), err); + pr_warn("resume (watch_otherend) %s failed: %d.\n", + dev_name(dev), err); return err; } @@ -776,8 +773,7 @@ static int __init xenbus_init(void) /* Initialize the interface to xenstore. */ err = xs_init(); if (err) { - printk(KERN_WARNING - "XENBUS: Error initializing xenstore comms: %i\n", err); + pr_warn("Error initializing xenstore comms: %i\n", err); goto out_error; } diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c index 257be37d9091..998bbbab816b 100644 --- a/drivers/xen/xenbus/xenbus_probe_backend.c +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -31,9 +31,11 @@ * IN THE SOFTWARE. */ -#define DPRINTK(fmt, args...) \ - pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ - __func__, __LINE__, ##args) +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DPRINTK(fmt, ...) \ + pr_debug("(%s:%d) " fmt "\n", \ + __func__, __LINE__, ##__VA_ARGS__) #include <linux/kernel.h> #include <linux/err.h> diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index a7e25073de19..6ed8a9df4472 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -1,6 +1,8 @@ -#define DPRINTK(fmt, args...) \ - pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ - __func__, __LINE__, ##args) +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DPRINTK(fmt, ...) \ + pr_debug("(%s:%d) " fmt "\n", \ + __func__, __LINE__, ##__VA_ARGS__) #include <linux/kernel.h> #include <linux/err.h> @@ -36,13 +38,13 @@ static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) { nodename = strchr(nodename, '/'); if (!nodename || strlen(nodename + 1) >= XEN_BUS_ID_SIZE) { - printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename); + pr_warn("bad frontend %s\n", nodename); return -EINVAL; } strlcpy(bus_id, nodename + 1, XEN_BUS_ID_SIZE); if (!strchr(bus_id, '/')) { - printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id); + pr_warn("bus_id %s no slash\n", bus_id); return -EINVAL; } *strchr(bus_id, '/') = '-'; @@ -234,15 +236,13 @@ static int print_device_status(struct device *dev, void *data) if (!dev->driver) { /* Information only: is this too noisy? */ - printk(KERN_INFO "XENBUS: Device with no driver: %s\n", - xendev->nodename); + pr_info("Device with no driver: %s\n", xendev->nodename); } else if (xendev->state < XenbusStateConnected) { enum xenbus_state rstate = XenbusStateUnknown; if (xendev->otherend) rstate = xenbus_read_driver_state(xendev->otherend); - printk(KERN_WARNING "XENBUS: Timeout connecting " - "to device: %s (local state %d, remote state %d)\n", - xendev->nodename, xendev->state, rstate); + pr_warn("Timeout connecting to device: %s (local state %d, remote state %d)\n", + xendev->nodename, xendev->state, rstate); } return 0; @@ -256,12 +256,13 @@ static bool wait_loop(unsigned long start, unsigned int max_delay, { if (time_after(jiffies, start + (*seconds_waited+5)*HZ)) { if (!*seconds_waited) - printk(KERN_WARNING "XENBUS: Waiting for " - "devices to initialise: "); + pr_warn("Waiting for devices to initialise: "); *seconds_waited += 5; - printk("%us...", max_delay - *seconds_waited); - if (*seconds_waited == max_delay) + pr_cont("%us...", max_delay - *seconds_waited); + if (*seconds_waited == max_delay) { + pr_cont("\n"); return true; + } } schedule_timeout_interruptible(HZ/10); @@ -342,7 +343,7 @@ static void xenbus_reset_wait_for_backend(char *be, int expected) timeout = wait_event_interruptible_timeout(backend_state_wq, backend_state == expected, 5 * HZ); if (timeout <= 0) - printk(KERN_INFO "XENBUS: backend %s timed out.\n", be); + pr_info("backend %s timed out\n", be); } /* @@ -365,7 +366,7 @@ static void xenbus_reset_frontend(char *fe, char *be, int be_state) be_watch.callback = xenbus_reset_backend_state_changed; backend_state = XenbusStateUnknown; - printk(KERN_INFO "XENBUS: triggering reconnect on %s\n", be); + pr_info("triggering reconnect on %s\n", be); register_xenbus_watch(&be_watch); /* fall through to forward backend to state XenbusStateInitialising */ @@ -384,7 +385,7 @@ static void xenbus_reset_frontend(char *fe, char *be, int be_state) } unregister_xenbus_watch(&be_watch); - printk(KERN_INFO "XENBUS: reconnect done on %s\n", be); + pr_info("reconnect done on %s\n", be); kfree(be_watch.node); } diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 88e677b0de74..b6d5fff43d16 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -31,6 +31,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/unistd.h> #include <linux/errno.h> #include <linux/types.h> @@ -129,9 +131,8 @@ static int get_error(const char *errorstring) for (i = 0; strcmp(errorstring, xsd_errors[i].errstring) != 0; i++) { if (i == ARRAY_SIZE(xsd_errors) - 1) { - printk(KERN_WARNING - "XENBUS xen store gave: unknown error %s", - errorstring); + pr_warn("xen store gave: unknown error %s\n", + errorstring); return EINVAL; } } @@ -272,10 +273,8 @@ static void *xs_talkv(struct xenbus_transaction t, } if (msg.type != type) { - if (printk_ratelimit()) - printk(KERN_WARNING - "XENBUS unexpected type [%d], expected [%d]\n", - msg.type, type); + pr_warn_ratelimited("unexpected type [%d], expected [%d]\n", + msg.type, type); kfree(ret); return ERR_PTR(-EINVAL); } @@ -655,7 +654,7 @@ static void xs_reset_watches(void) err = xs_error(xs_single(XBT_NIL, XS_RESET_WATCHES, "", NULL)); if (err && err != -EEXIST) - printk(KERN_WARNING "xs_reset_watches failed: %d\n", err); + pr_warn("xs_reset_watches failed: %d\n", err); } /* Register callback to watch this node. */ @@ -705,9 +704,7 @@ void unregister_xenbus_watch(struct xenbus_watch *watch) err = xs_unwatch(watch->node, token); if (err) - printk(KERN_WARNING - "XENBUS Failed to release watch %s: %i\n", - watch->node, err); + pr_warn("Failed to release watch %s: %i\n", watch->node, err); up_read(&xs_state.watch_mutex); @@ -901,8 +898,7 @@ static int xenbus_thread(void *unused) for (;;) { err = process_msg(); if (err) - printk(KERN_WARNING "XENBUS error %d while reading " - "message\n", err); + pr_warn("error %d while reading message\n", err); if (kthread_should_stop()) break; } diff --git a/drivers/xen/xencomm.c b/drivers/xen/xencomm.c index b91f8ff50d05..4793fc594549 100644 --- a/drivers/xen/xencomm.c +++ b/drivers/xen/xencomm.c @@ -18,6 +18,8 @@ * Authors: Hollis Blanchard <hollisb@us.ibm.com> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/mm.h> #include <linux/slab.h> #include <asm/page.h> diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c index 71679875f056..06092e0fe8ce 100644 --- a/drivers/xen/xenfs/super.c +++ b/drivers/xen/xenfs/super.c @@ -7,6 +7,8 @@ * Turned xenfs into a loadable module. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/errno.h> #include <linux/module.h> @@ -82,7 +84,7 @@ static int __init xenfs_init(void) if (xen_domain()) return register_filesystem(&xenfs_type); - printk(KERN_INFO "XENFS: not registering filesystem on non-xen platform\n"); + pr_info("not registering filesystem on non-xen platform\n"); return 0; } diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 2906ee276408..603f18a65c12 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -10,6 +10,7 @@ config CIFS select CRYPTO_ECB select CRYPTO_DES select CRYPTO_SHA256 + select CRYPTO_CMAC help This is the client VFS module for the Common Internet File System (CIFS) protocol which is the successor to the Server Message Block diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index d59748346020..f3ac4154cbb6 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -213,7 +213,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) tcon->nativeFileSystem); } seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x" - "\nPathComponentMax: %d Status: 0x%d", + "\n\tPathComponentMax: %d Status: 0x%d", le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics), le32_to_cpu(tcon->fsAttrInfo.Attributes), le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength), @@ -224,6 +224,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) seq_puts(m, " type: CDROM "); else seq_printf(m, " type: %d ", dev_type); + if (server->ops->dump_share_caps) + server->ops->dump_share_caps(m, tcon); if (tcon->need_reconnect) seq_puts(m, "\tDISCONNECTED "); @@ -595,9 +597,36 @@ static int cifs_security_flags_proc_open(struct inode *inode, struct file *file) return single_open(file, cifs_security_flags_proc_show, NULL); } +/* + * Ensure that if someone sets a MUST flag, that we disable all other MAY + * flags except for the ones corresponding to the given MUST flag. If there are + * multiple MUST flags, then try to prefer more secure ones. + */ +static void +cifs_security_flags_handle_must_flags(unsigned int *flags) +{ + unsigned int signflags = *flags & CIFSSEC_MUST_SIGN; + + if ((*flags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) + *flags = CIFSSEC_MUST_KRB5; + else if ((*flags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) + *flags = CIFSSEC_MUST_NTLMSSP; + else if ((*flags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) + *flags = CIFSSEC_MUST_NTLMV2; + else if ((*flags & CIFSSEC_MUST_NTLM) == CIFSSEC_MUST_NTLM) + *flags = CIFSSEC_MUST_NTLM; + else if ((*flags & CIFSSEC_MUST_LANMAN) == CIFSSEC_MUST_LANMAN) + *flags = CIFSSEC_MUST_LANMAN; + else if ((*flags & CIFSSEC_MUST_PLNTXT) == CIFSSEC_MUST_PLNTXT) + *flags = CIFSSEC_MUST_PLNTXT; + + *flags |= signflags; +} + static ssize_t cifs_security_flags_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { + int rc; unsigned int flags; char flags_string[12]; char c; @@ -620,26 +649,35 @@ static ssize_t cifs_security_flags_proc_write(struct file *file, global_secflags = CIFSSEC_MAX; return count; } else if (!isdigit(c)) { - cifs_dbg(VFS, "invalid flag %c\n", c); + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", + flags_string); return -EINVAL; } } - /* else we have a number */ - flags = simple_strtoul(flags_string, NULL, 0); + /* else we have a number */ + rc = kstrtouint(flags_string, 0, &flags); + if (rc) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", + flags_string); + return rc; + } cifs_dbg(FYI, "sec flags 0x%x\n", flags); - if (flags <= 0) { - cifs_dbg(VFS, "invalid security flags %s\n", flags_string); + if (flags == 0) { + cifs_dbg(VFS, "Invalid SecurityFlags: %s\n", flags_string); return -EINVAL; } if (flags & ~CIFSSEC_MASK) { - cifs_dbg(VFS, "attempt to set unsupported security flags 0x%x\n", + cifs_dbg(VFS, "Unsupported security flags: 0x%x\n", flags & ~CIFSSEC_MASK); return -EINVAL; } + + cifs_security_flags_handle_must_flags(&flags); + /* flags look ok - update the global security flags for cifs module */ global_secflags = flags; if (global_secflags & CIFSSEC_MUST_SIGN) { diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h index 4fb097468e21..fe8d6276410a 100644 --- a/fs/cifs/cifs_unicode.h +++ b/fs/cifs/cifs_unicode.h @@ -327,14 +327,14 @@ UniToupper(register wchar_t uc) /* * UniStrupr: Upper case a unicode string */ -static inline wchar_t * -UniStrupr(register wchar_t *upin) +static inline __le16 * +UniStrupr(register __le16 *upin) { - register wchar_t *up; + register __le16 *up; up = upin; while (*up) { /* For all characters */ - *up = UniToupper(*up); + *up = cpu_to_le16(UniToupper(le16_to_cpu(*up))); up++; } return upin; /* Return input pointer */ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 71436d1fca13..3d8bf941d126 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -276,7 +276,6 @@ int calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, strncpy(password_with_pad, password, CIFS_ENCPWD_SIZE); if (!encrypt && global_secflags & CIFSSEC_MAY_PLNTXT) { - memset(lnm_session_key, 0, CIFS_SESS_KEY_SIZE); memcpy(lnm_session_key, password_with_pad, CIFS_ENCPWD_SIZE); return 0; @@ -414,7 +413,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, int rc = 0; int len; char nt_hash[CIFS_NTHASH_SIZE]; - wchar_t *user; + __le16 *user; wchar_t *domain; wchar_t *server; @@ -439,7 +438,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, return rc; } - /* convert ses->user_name to unicode and uppercase */ + /* convert ses->user_name to unicode */ len = ses->user_name ? strlen(ses->user_name) : 0; user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) { @@ -448,7 +447,7 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, } if (len) { - len = cifs_strtoUTF16((__le16 *)user, ses->user_name, len, nls_cp); + len = cifs_strtoUTF16(user, ses->user_name, len, nls_cp); UniStrupr(user); } else { memset(user, '\0', 2); @@ -536,7 +535,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) return rc; } - if (ses->server->secType == RawNTLMSSP) + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) memcpy(ses->auth_key.response + offset, ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); else @@ -568,7 +567,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) char ntlmv2_hash[16]; unsigned char *tiblob = NULL; /* target info blob */ - if (ses->server->secType == RawNTLMSSP) { + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) { if (!ses->domainName) { rc = find_domain_name(ses, nls_cp); if (rc) { @@ -706,6 +705,9 @@ calc_seckey(struct cifs_ses *ses) void cifs_crypto_shash_release(struct TCP_Server_Info *server) { + if (server->secmech.cmacaes) + crypto_free_shash(server->secmech.cmacaes); + if (server->secmech.hmacsha256) crypto_free_shash(server->secmech.hmacsha256); @@ -715,6 +717,8 @@ cifs_crypto_shash_release(struct TCP_Server_Info *server) if (server->secmech.hmacmd5) crypto_free_shash(server->secmech.hmacmd5); + kfree(server->secmech.sdesccmacaes); + kfree(server->secmech.sdeschmacsha256); kfree(server->secmech.sdeschmacmd5); @@ -748,6 +752,13 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server) goto crypto_allocate_hmacsha256_fail; } + server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0); + if (IS_ERR(server->secmech.cmacaes)) { + cifs_dbg(VFS, "could not allocate crypto cmac-aes"); + rc = PTR_ERR(server->secmech.cmacaes); + goto crypto_allocate_cmacaes_fail; + } + size = sizeof(struct shash_desc) + crypto_shash_descsize(server->secmech.hmacmd5); server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL); @@ -778,8 +789,22 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server) server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256; server->secmech.sdeschmacsha256->shash.flags = 0x0; + size = sizeof(struct shash_desc) + + crypto_shash_descsize(server->secmech.cmacaes); + server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL); + if (!server->secmech.sdesccmacaes) { + cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__); + rc = -ENOMEM; + goto crypto_allocate_cmacaes_sdesc_fail; + } + server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes; + server->secmech.sdesccmacaes->shash.flags = 0x0; + return 0; +crypto_allocate_cmacaes_sdesc_fail: + kfree(server->secmech.sdeschmacsha256); + crypto_allocate_hmacsha256_sdesc_fail: kfree(server->secmech.sdescmd5); @@ -787,6 +812,9 @@ crypto_allocate_md5_sdesc_fail: kfree(server->secmech.sdeschmacmd5); crypto_allocate_hmacmd5_sdesc_fail: + crypto_free_shash(server->secmech.cmacaes); + +crypto_allocate_cmacaes_fail: crypto_free_shash(server->secmech.hmacsha256); crypto_allocate_hmacsha256_fail: diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index a445e71746fa..4bdd547dbf6f 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -312,11 +312,14 @@ cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server) } static void -cifs_show_security(struct seq_file *s, struct TCP_Server_Info *server) +cifs_show_security(struct seq_file *s, struct cifs_ses *ses) { + if (ses->sectype == Unspecified) + return; + seq_printf(s, ",sec="); - switch (server->secType) { + switch (ses->sectype) { case LANMAN: seq_printf(s, "lanman"); break; @@ -338,7 +341,7 @@ cifs_show_security(struct seq_file *s, struct TCP_Server_Info *server) break; } - if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->sign) seq_printf(s, "i"); } @@ -369,7 +372,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; seq_printf(s, ",vers=%s", tcon->ses->server->vals->version_string); - cifs_show_security(s, tcon->ses->server); + cifs_show_security(s, tcon->ses); cifs_show_cache_flavor(s, cifs_sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index d05b3028e3b9..ea723a5e8226 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -132,5 +132,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION "2.0" +#define CIFS_VERSION "2.01" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 4f07f6fbe494..e66b08882548 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -101,20 +101,14 @@ enum statusEnum { }; enum securityEnum { - LANMAN = 0, /* Legacy LANMAN auth */ + Unspecified = 0, /* not specified */ + LANMAN, /* Legacy LANMAN auth */ NTLM, /* Legacy NTLM012 auth with NTLM hash */ NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ -/* NTLMSSP, */ /* can use rawNTLMSSP instead of NTLMSSP via SPNEGO */ Kerberos, /* Kerberos via SPNEGO */ }; -enum protocolEnum { - TCP = 0, - SCTP - /* Netbios frames protocol not supported at this time */ -}; - struct session_key { unsigned int len; char *response; @@ -131,9 +125,11 @@ struct cifs_secmech { struct crypto_shash *hmacmd5; /* hmac-md5 hash function */ struct crypto_shash *md5; /* md5 hash function */ struct crypto_shash *hmacsha256; /* hmac-sha256 hash function */ + struct crypto_shash *cmacaes; /* block-cipher based MAC function */ struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */ struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */ struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */ + struct sdesc *sdesccmacaes; /* ctxt to generate smb3 signature */ }; /* per smb session structure/fields */ @@ -181,6 +177,7 @@ enum smb_version { Smb_20, Smb_21, Smb_30, + Smb_302, }; struct mid_q_entry; @@ -228,6 +225,7 @@ struct smb_version_operations { void (*dump_detail)(void *); void (*clear_stats)(struct cifs_tcon *); void (*print_stats)(struct seq_file *m, struct cifs_tcon *); + void (*dump_share_caps)(struct seq_file *, struct cifs_tcon *); /* verify the message */ int (*check_message)(char *, unsigned int); bool (*is_oplock_break)(char *, struct TCP_Server_Info *); @@ -367,6 +365,8 @@ struct smb_version_operations { void (*set_lease_key)(struct inode *, struct cifs_fid *fid); /* generate new lease key */ void (*new_lease_key)(struct cifs_fid *fid); + /* The next two functions will need to be changed to per smb session */ + void (*generate_signingkey)(struct TCP_Server_Info *server); int (*calc_signature)(struct smb_rqst *rqst, struct TCP_Server_Info *server); }; @@ -387,6 +387,8 @@ struct smb_version_values { unsigned int cap_nt_find; unsigned int cap_large_files; unsigned int oplock_read; + __u16 signing_enabled; + __u16 signing_required; }; #define HEADER_SIZE(server) (server->vals->header_size) @@ -407,7 +409,8 @@ struct smb_vol { kgid_t backupgid; umode_t file_mode; umode_t dir_mode; - unsigned secFlg; + enum securityEnum sectype; /* sectype requested via mnt opts */ + bool sign; /* was signing requested via mnt opts? */ bool retry:1; bool intr:1; bool setuids:1; @@ -441,6 +444,7 @@ struct smb_vol { bool mfsymlinks:1; /* use Minshall+French Symlinks */ bool multiuser:1; bool rwpidforward:1; /* pid forward for read/write operations */ + bool nosharesock; unsigned int rsize; unsigned int wsize; bool sockopt_tcp_nodelay:1; @@ -514,6 +518,7 @@ struct TCP_Server_Info { struct task_struct *tsk; char server_GUID[16]; __u16 sec_mode; + bool sign; /* is signing enabled on this connection? */ bool session_estab; /* mark when very first sess is established */ #ifdef CONFIG_CIFS_SMB2 int echo_credits; /* echo reserved slots */ @@ -521,7 +526,6 @@ struct TCP_Server_Info { bool echoes:1; /* enable echoes */ #endif u16 dialect; /* dialect index that server chose */ - enum securityEnum secType; bool oplocks:1; /* enable oplocks */ unsigned int maxReq; /* Clients should submit no more */ /* than maxReq distinct unanswered SMBs to the server when using */ @@ -540,12 +544,17 @@ struct TCP_Server_Info { int timeAdj; /* Adjust for difference in server time zone in sec */ __u64 CurrentMid; /* multiplex id - rotating counter */ char cryptkey[CIFS_CRYPTO_KEY_SIZE]; /* used by ntlm, ntlmv2 etc */ + char smb3signingkey[SMB3_SIGN_KEY_SIZE]; /* for signing smb3 packets */ /* 16th byte of RFC1001 workstation name is always null */ char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; __u32 sequence_number; /* for signing, protected by srv_mutex */ struct session_key session_key; unsigned long lstrp; /* when we got last response from this server */ struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ +#define CIFS_NEGFLAVOR_LANMAN 0 /* wct == 13, LANMAN */ +#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */ +#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */ + char negflavor; /* NEGOTIATE response flavor */ /* extended security flavors that server supports */ bool sec_ntlmssp; /* supports NTLMSSP */ bool sec_kerberosu2u; /* supports U2U Kerberos */ @@ -697,7 +706,6 @@ struct cifs_ses { enum statusEnum status; unsigned overrideSecFlg; /* if non-zero override global sec flags */ __u16 ipc_tid; /* special tid for connection to IPC share */ - __u16 flags; __u16 vcnum; char *serverOS; /* name of operating system underlying server */ char *serverNOS; /* name of network operating system of server */ @@ -714,21 +722,14 @@ struct cifs_ses { char *password; struct session_key auth_key; struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ + enum securityEnum sectype; /* what security flavor was specified? */ + bool sign; /* is signing required? */ bool need_reconnect:1; /* connection reset, uid now invalid */ #ifdef CONFIG_CIFS_SMB2 __u16 session_flags; #endif /* CONFIG_CIFS_SMB2 */ }; -/* no more than one of the following three session flags may be set */ -#define CIFS_SES_NT4 1 -#define CIFS_SES_OS2 2 -#define CIFS_SES_W9X 4 -/* following flag is set for old servers such as OS2 (and Win95?) - which do not negotiate NTLM or POSIX dialects, but instead - negotiate one of the older LANMAN dialects */ -#define CIFS_SES_LANMAN 8 - static inline bool cap_unix(struct cifs_ses *ses) { @@ -816,7 +817,7 @@ struct cifs_tcon { #ifdef CONFIG_CIFS_SMB2 bool print:1; /* set if connection to printer share */ bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */ - __u32 capabilities; + __le32 capabilities; __u32 share_flags; __u32 maximal_access; __u32 vol_serial_number; @@ -1348,7 +1349,7 @@ require use of the stronger protocol */ #define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */ #define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */ -#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMSSP) +#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP) #define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2) #define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP) /* @@ -1494,4 +1495,7 @@ extern struct smb_version_values smb21_values; #define SMB30_VERSION_STRING "3.0" extern struct smb_version_operations smb30_operations; extern struct smb_version_values smb30_values; +#define SMB302_VERSION_STRING "3.02" +/*extern struct smb_version_operations smb302_operations;*/ /* not needed yet */ +extern struct smb_version_values smb302_values; #endif /* _CIFS_GLOB_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index e996ff6b26d1..11ca24a8e054 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -142,6 +142,11 @@ */ #define CIFS_SESS_KEY_SIZE (16) +/* + * Size of the smb3 signing key + */ +#define SMB3_SIGN_KEY_SIZE (16) + #define CIFS_CLIENT_CHALLENGE_SIZE (8) #define CIFS_SERVER_CHALLENGE_SIZE (8) #define CIFS_HMAC_MD5_HASH_SIZE (16) @@ -531,7 +536,7 @@ typedef struct lanman_neg_rsp { #define READ_RAW_ENABLE 1 #define WRITE_RAW_ENABLE 2 #define RAW_ENABLE (READ_RAW_ENABLE | WRITE_RAW_ENABLE) - +#define SMB1_CLIENT_GUID_SIZE (16) typedef struct negotiate_rsp { struct smb_hdr hdr; /* wct = 17 */ __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */ @@ -553,7 +558,7 @@ typedef struct negotiate_rsp { /* followed by 16 bytes of server GUID */ /* then security blob if cap_extended_security negotiated */ struct { - unsigned char GUID[16]; + unsigned char GUID[SMB1_CLIENT_GUID_SIZE]; unsigned char SecurityBlob[1]; } __attribute__((packed)) extended_response; } __attribute__((packed)) u; @@ -1315,6 +1320,14 @@ typedef struct smb_com_ntransact_rsp { /* parms and data follow */ } __attribute__((packed)) NTRANSACT_RSP; +/* See MS-SMB 2.2.7.2.1.1 */ +struct srv_copychunk { + __le64 SourceOffset; + __le64 DestinationOffset; + __le32 CopyLength; + __u32 Reserved; +} __packed; + typedef struct smb_com_transaction_ioctl_req { struct smb_hdr hdr; /* wct = 23 */ __u8 MaxSetupCount; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index dda188a94332..c8ff018fae68 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -118,6 +118,8 @@ extern void header_assemble(struct smb_hdr *, char /* command */ , extern int small_smb_init_no_tc(const int smb_cmd, const int wct, struct cifs_ses *ses, void **request_buf); +extern enum securityEnum select_sectype(struct TCP_Server_Info *server, + enum securityEnum requested); extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp); extern struct timespec cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); @@ -212,6 +214,7 @@ extern int cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses); extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, struct nls_table *nls_info); +extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); extern int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses); extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses, @@ -433,6 +436,7 @@ extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *); extern void cifs_crypto_shash_release(struct TCP_Server_Info *); extern int calc_seckey(struct cifs_ses *); +extern void generate_smb3signingkey(struct TCP_Server_Info *); #ifdef CONFIG_CIFS_WEAK_PW_HASH extern int calc_lanman_hash(const char *password, const char *cryptkey, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a58dc77cc443..a89c4cb4e6cf 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -367,6 +367,185 @@ vt2_err: return -EINVAL; } +static int +decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr) +{ + int rc = 0; + u16 count; + char *guid = pSMBr->u.extended_response.GUID; + struct TCP_Server_Info *server = ses->server; + + count = get_bcc(&pSMBr->hdr); + if (count < SMB1_CLIENT_GUID_SIZE) + return -EIO; + + spin_lock(&cifs_tcp_ses_lock); + if (server->srv_count > 1) { + spin_unlock(&cifs_tcp_ses_lock); + if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) { + cifs_dbg(FYI, "server UID changed\n"); + memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); + } + } else { + spin_unlock(&cifs_tcp_ses_lock); + memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE); + } + + if (count == SMB1_CLIENT_GUID_SIZE) { + server->sec_ntlmssp = true; + } else { + count -= SMB1_CLIENT_GUID_SIZE; + rc = decode_negTokenInit( + pSMBr->u.extended_response.SecurityBlob, count, server); + if (rc != 1) + return -EINVAL; + } + + return 0; +} + +int +cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required) +{ + bool srv_sign_required = server->sec_mode & server->vals->signing_required; + bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled; + bool mnt_sign_enabled = global_secflags & CIFSSEC_MAY_SIGN; + + /* + * Is signing required by mnt options? If not then check + * global_secflags to see if it is there. + */ + if (!mnt_sign_required) + mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) == + CIFSSEC_MUST_SIGN); + + /* + * If signing is required then it's automatically enabled too, + * otherwise, check to see if the secflags allow it. + */ + mnt_sign_enabled = mnt_sign_required ? mnt_sign_required : + (global_secflags & CIFSSEC_MAY_SIGN); + + /* If server requires signing, does client allow it? */ + if (srv_sign_required) { + if (!mnt_sign_enabled) { + cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!"); + return -ENOTSUPP; + } + server->sign = true; + } + + /* If client requires signing, does server allow it? */ + if (mnt_sign_required) { + if (!srv_sign_enabled) { + cifs_dbg(VFS, "Server does not support signing!"); + return -ENOTSUPP; + } + server->sign = true; + } + + return 0; +} + +#ifdef CONFIG_CIFS_WEAK_PW_HASH +static int +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) +{ + __s16 tmp; + struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; + + if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT) + return -EOPNOTSUPP; + + server->sec_mode = le16_to_cpu(rsp->SecurityMode); + server->maxReq = min_t(unsigned int, + le16_to_cpu(rsp->MaxMpxCount), + cifs_max_pending); + set_credits(server, server->maxReq); + server->maxBuf = le16_to_cpu(rsp->MaxBufSize); + server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); + /* even though we do not use raw we might as well set this + accurately, in case we ever find a need for it */ + if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { + server->max_rw = 0xFF00; + server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; + } else { + server->max_rw = 0;/* do not need to use raw anyway */ + server->capabilities = CAP_MPX_MODE; + } + tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); + if (tmp == -1) { + /* OS/2 often does not set timezone therefore + * we must use server time to calc time zone. + * Could deviate slightly from the right zone. + * Smallest defined timezone difference is 15 minutes + * (i.e. Nepal). Rounding up/down is done to match + * this requirement. + */ + int val, seconds, remain, result; + struct timespec ts, utc; + utc = CURRENT_TIME; + ts = cnvrtDosUnixTm(rsp->SrvTime.Date, + rsp->SrvTime.Time, 0); + cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n", + (int)ts.tv_sec, (int)utc.tv_sec, + (int)(utc.tv_sec - ts.tv_sec)); + val = (int)(utc.tv_sec - ts.tv_sec); + seconds = abs(val); + result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; + remain = seconds % MIN_TZ_ADJ; + if (remain >= (MIN_TZ_ADJ / 2)) + result += MIN_TZ_ADJ; + if (val < 0) + result = -result; + server->timeAdj = result; + } else { + server->timeAdj = (int)tmp; + server->timeAdj *= 60; /* also in seconds */ + } + cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj); + + + /* BB get server time for time conversions and add + code to use it and timezone since this is not UTC */ + + if (rsp->EncryptionKeyLength == + cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { + memcpy(server->cryptkey, rsp->EncryptionKey, + CIFS_CRYPTO_KEY_SIZE); + } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { + return -EIO; /* need cryptkey unless plain text */ + } + + cifs_dbg(FYI, "LANMAN negotiated\n"); + return 0; +} +#else +static inline int +decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr) +{ + cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n"); + return -EOPNOTSUPP; +} +#endif + +static bool +should_set_ext_sec_flag(enum securityEnum sectype) +{ + switch (sectype) { + case RawNTLMSSP: + case Kerberos: + return true; + case Unspecified: + if (global_secflags & + (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)) + return true; + /* Fallthrough */ + default: + return false; + } +} + int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) { @@ -375,41 +554,24 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) int rc = 0; int bytes_returned; int i; - struct TCP_Server_Info *server; + struct TCP_Server_Info *server = ses->server; u16 count; - unsigned int secFlags; - if (ses->server) - server = ses->server; - else { - rc = -EIO; - return rc; + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; } + rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ , (void **) &pSMB, (void **) &pSMBr); if (rc) return rc; - /* if any of auth flags (ie not sign or seal) are overriden use them */ - if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */ - else /* if override flags set only sign/seal OR them with global auth */ - secFlags = global_secflags | ses->overrideSecFlg; - - cifs_dbg(FYI, "secFlags 0x%x\n", secFlags); - pSMB->hdr.Mid = get_next_mid(server); pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS); - if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5) - pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) { - cifs_dbg(FYI, "Kerberos only mechanism, enable extended security\n"); - pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - } else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP) - pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; - else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) { - cifs_dbg(FYI, "NTLMSSP only mechanism, enable extended security\n"); + if (should_set_ext_sec_flag(ses->sectype)) { + cifs_dbg(FYI, "Requesting extended security."); pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC; } @@ -436,127 +598,21 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) could not negotiate a common dialect */ rc = -EOPNOTSUPP; goto neg_err_exit; -#ifdef CONFIG_CIFS_WEAK_PW_HASH - } else if ((pSMBr->hdr.WordCount == 13) - && ((server->dialect == LANMAN_PROT) - || (server->dialect == LANMAN2_PROT))) { - __s16 tmp; - struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr; - - if ((secFlags & CIFSSEC_MAY_LANMAN) || - (secFlags & CIFSSEC_MAY_PLNTXT)) - server->secType = LANMAN; - else { - cifs_dbg(VFS, "mount failed weak security disabled in /proc/fs/cifs/SecurityFlags\n"); - rc = -EOPNOTSUPP; - goto neg_err_exit; - } - server->sec_mode = le16_to_cpu(rsp->SecurityMode); - server->maxReq = min_t(unsigned int, - le16_to_cpu(rsp->MaxMpxCount), - cifs_max_pending); - set_credits(server, server->maxReq); - server->maxBuf = le16_to_cpu(rsp->MaxBufSize); - server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); - /* even though we do not use raw we might as well set this - accurately, in case we ever find a need for it */ - if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { - server->max_rw = 0xFF00; - server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE; - } else { - server->max_rw = 0;/* do not need to use raw anyway */ - server->capabilities = CAP_MPX_MODE; - } - tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone); - if (tmp == -1) { - /* OS/2 often does not set timezone therefore - * we must use server time to calc time zone. - * Could deviate slightly from the right zone. - * Smallest defined timezone difference is 15 minutes - * (i.e. Nepal). Rounding up/down is done to match - * this requirement. - */ - int val, seconds, remain, result; - struct timespec ts, utc; - utc = CURRENT_TIME; - ts = cnvrtDosUnixTm(rsp->SrvTime.Date, - rsp->SrvTime.Time, 0); - cifs_dbg(FYI, "SrvTime %d sec since 1970 (utc: %d) diff: %d\n", - (int)ts.tv_sec, (int)utc.tv_sec, - (int)(utc.tv_sec - ts.tv_sec)); - val = (int)(utc.tv_sec - ts.tv_sec); - seconds = abs(val); - result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ; - remain = seconds % MIN_TZ_ADJ; - if (remain >= (MIN_TZ_ADJ / 2)) - result += MIN_TZ_ADJ; - if (val < 0) - result = -result; - server->timeAdj = result; - } else { - server->timeAdj = (int)tmp; - server->timeAdj *= 60; /* also in seconds */ - } - cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj); - - - /* BB get server time for time conversions and add - code to use it and timezone since this is not UTC */ - - if (rsp->EncryptionKeyLength == - cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { - memcpy(ses->server->cryptkey, rsp->EncryptionKey, - CIFS_CRYPTO_KEY_SIZE); - } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { - rc = -EIO; /* need cryptkey unless plain text */ - goto neg_err_exit; - } - - cifs_dbg(FYI, "LANMAN negotiated\n"); - /* we will not end up setting signing flags - as no signing - was in LANMAN and server did not return the flags on */ - goto signing_check; -#else /* weak security disabled */ } else if (pSMBr->hdr.WordCount == 13) { - cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n"); - rc = -EOPNOTSUPP; -#endif /* WEAK_PW_HASH */ - goto neg_err_exit; + server->negflavor = CIFS_NEGFLAVOR_LANMAN; + rc = decode_lanman_negprot_rsp(server, pSMBr); + goto signing_check; } else if (pSMBr->hdr.WordCount != 17) { /* unknown wct */ rc = -EOPNOTSUPP; goto neg_err_exit; } - /* else wct == 17 NTLM */ + /* else wct == 17, NTLM or better */ + server->sec_mode = pSMBr->SecurityMode; if ((server->sec_mode & SECMODE_USER) == 0) cifs_dbg(FYI, "share mode security\n"); - if ((server->sec_mode & SECMODE_PW_ENCRYPT) == 0) -#ifdef CONFIG_CIFS_WEAK_PW_HASH - if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0) -#endif /* CIFS_WEAK_PW_HASH */ - cifs_dbg(VFS, "Server requests plain text password but client support disabled\n"); - - if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2) - server->secType = NTLMv2; - else if (secFlags & CIFSSEC_MAY_NTLM) - server->secType = NTLM; - else if (secFlags & CIFSSEC_MAY_NTLMV2) - server->secType = NTLMv2; - else if (secFlags & CIFSSEC_MAY_KRB5) - server->secType = Kerberos; - else if (secFlags & CIFSSEC_MAY_NTLMSSP) - server->secType = RawNTLMSSP; - else if (secFlags & CIFSSEC_MAY_LANMAN) - server->secType = LANMAN; - else { - rc = -EOPNOTSUPP; - cifs_dbg(VFS, "Invalid security type\n"); - goto neg_err_exit; - } - /* else ... any others ...? */ - /* one byte, so no need to convert this or EncryptionKeyLen from little endian */ server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount), @@ -569,90 +625,26 @@ CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses) server->capabilities = le32_to_cpu(pSMBr->Capabilities); server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); server->timeAdj *= 60; + if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { + server->negflavor = CIFS_NEGFLAVOR_UNENCAP; memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey, CIFS_CRYPTO_KEY_SIZE); } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC || server->capabilities & CAP_EXTENDED_SECURITY) && (pSMBr->EncryptionKeyLength == 0)) { - /* decode security blob */ - count = get_bcc(&pSMBr->hdr); - if (count < 16) { - rc = -EIO; - goto neg_err_exit; - } - spin_lock(&cifs_tcp_ses_lock); - if (server->srv_count > 1) { - spin_unlock(&cifs_tcp_ses_lock); - if (memcmp(server->server_GUID, - pSMBr->u.extended_response. - GUID, 16) != 0) { - cifs_dbg(FYI, "server UID changed\n"); - memcpy(server->server_GUID, - pSMBr->u.extended_response.GUID, - 16); - } - } else { - spin_unlock(&cifs_tcp_ses_lock); - memcpy(server->server_GUID, - pSMBr->u.extended_response.GUID, 16); - } - - if (count == 16) { - server->secType = RawNTLMSSP; - } else { - rc = decode_negTokenInit(pSMBr->u.extended_response. - SecurityBlob, count - 16, - server); - if (rc == 1) - rc = 0; - else - rc = -EINVAL; - if (server->secType == Kerberos) { - if (!server->sec_kerberos && - !server->sec_mskerberos) - rc = -EOPNOTSUPP; - } else if (server->secType == RawNTLMSSP) { - if (!server->sec_ntlmssp) - rc = -EOPNOTSUPP; - } else - rc = -EOPNOTSUPP; - } + server->negflavor = CIFS_NEGFLAVOR_EXTENDED; + rc = decode_ext_sec_blob(ses, pSMBr); } else if (server->sec_mode & SECMODE_PW_ENCRYPT) { rc = -EIO; /* no crypt key only if plain text pwd */ - goto neg_err_exit; - } else - server->capabilities &= ~CAP_EXTENDED_SECURITY; - -#ifdef CONFIG_CIFS_WEAK_PW_HASH -signing_check: -#endif - if ((secFlags & CIFSSEC_MAY_SIGN) == 0) { - /* MUST_SIGN already includes the MAY_SIGN FLAG - so if this is zero it means that signing is disabled */ - cifs_dbg(FYI, "Signing disabled\n"); - if (server->sec_mode & SECMODE_SIGN_REQUIRED) { - cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n"); - rc = -EOPNOTSUPP; - } - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) { - /* signing required */ - cifs_dbg(FYI, "Must sign - secFlags 0x%x\n", secFlags); - if ((server->sec_mode & - (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) { - cifs_dbg(VFS, "signing required but server lacks support\n"); - rc = -EOPNOTSUPP; - } else - server->sec_mode |= SECMODE_SIGN_REQUIRED; } else { - /* signing optional ie CIFSSEC_MAY_SIGN */ - if ((server->sec_mode & SECMODE_SIGN_REQUIRED) == 0) - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); + server->negflavor = CIFS_NEGFLAVOR_UNENCAP; + server->capabilities &= ~CAP_EXTENDED_SECURITY; } +signing_check: + if (!rc) + rc = cifs_enable_signing(server, ses->sign); neg_err_exit: cifs_buf_release(pSMB); @@ -777,9 +769,8 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) pSMB->hdr.Mid = get_next_mid(ses->server); - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + if (ses->server->sign) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->hdr.Uid = ses->Suid; @@ -1540,8 +1531,7 @@ cifs_readv_callback(struct mid_q_entry *mid) switch (mid->mid_state) { case MID_RESPONSE_RECEIVED: /* result already set, check signature */ - if (server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (server->sign) { int rc = 0; rc = cifs_verify_signature(&rqst, server, @@ -3940,6 +3930,7 @@ QFileInfoRetry: pSMB->Pad = 0; pSMB->Fid = netfid; inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); @@ -4108,6 +4099,7 @@ UnixQFileInfoRetry: pSMB->Pad = 0; pSMB->Fid = netfid; inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); @@ -4794,11 +4786,8 @@ getDFSRetry: strncpy(pSMB->RequestFileName, search_name, name_len); } - if (ses->server) { - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - } + if (ses->server && ses->server->sign) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->hdr.Uid = ses->Suid; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index e3bc39bb9d12..afcb8a1a33b7 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -85,7 +85,7 @@ enum { Opt_acl, Opt_noacl, Opt_locallease, Opt_sign, Opt_seal, Opt_noac, Opt_fsc, Opt_mfsymlinks, - Opt_multiuser, Opt_sloppy, + Opt_multiuser, Opt_sloppy, Opt_nosharesock, /* Mount options which take numeric value */ Opt_backupuid, Opt_backupgid, Opt_uid, @@ -165,6 +165,7 @@ static const match_table_t cifs_mount_option_tokens = { { Opt_mfsymlinks, "mfsymlinks" }, { Opt_multiuser, "multiuser" }, { Opt_sloppy, "sloppy" }, + { Opt_nosharesock, "nosharesock" }, { Opt_backupuid, "backupuid=%s" }, { Opt_backupgid, "backupgid=%s" }, @@ -275,6 +276,7 @@ static const match_table_t cifs_smb_version_tokens = { { Smb_20, SMB20_VERSION_STRING}, { Smb_21, SMB21_VERSION_STRING }, { Smb_30, SMB30_VERSION_STRING }, + { Smb_302, SMB302_VERSION_STRING }, }; static int ip_connect(struct TCP_Server_Info *server); @@ -1024,44 +1026,48 @@ static int cifs_parse_security_flavors(char *value, substring_t args[MAX_OPT_ARGS]; + /* + * With mount options, the last one should win. Reset any existing + * settings back to default. + */ + vol->sectype = Unspecified; + vol->sign = false; + switch (match_token(value, cifs_secflavor_tokens, args)) { - case Opt_sec_krb5: - vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_SIGN; - break; - case Opt_sec_krb5i: - vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN; - break; case Opt_sec_krb5p: - /* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */ - cifs_dbg(VFS, "Krb5 cifs privacy not supported\n"); - break; - case Opt_sec_ntlmssp: - vol->secFlg |= CIFSSEC_MAY_NTLMSSP; + cifs_dbg(VFS, "sec=krb5p is not supported!\n"); + return 1; + case Opt_sec_krb5i: + vol->sign = true; + /* Fallthrough */ + case Opt_sec_krb5: + vol->sectype = Kerberos; break; case Opt_sec_ntlmsspi: - vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN; - break; - case Opt_ntlm: - /* ntlm is default so can be turned off too */ - vol->secFlg |= CIFSSEC_MAY_NTLM; + vol->sign = true; + /* Fallthrough */ + case Opt_sec_ntlmssp: + vol->sectype = RawNTLMSSP; break; case Opt_sec_ntlmi: - vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN; - break; - case Opt_sec_ntlmv2: - vol->secFlg |= CIFSSEC_MAY_NTLMV2; + vol->sign = true; + /* Fallthrough */ + case Opt_ntlm: + vol->sectype = NTLM; break; case Opt_sec_ntlmv2i: - vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN; + vol->sign = true; + /* Fallthrough */ + case Opt_sec_ntlmv2: + vol->sectype = NTLMv2; break; #ifdef CONFIG_CIFS_WEAK_PW_HASH case Opt_sec_lanman: - vol->secFlg |= CIFSSEC_MAY_LANMAN; + vol->sectype = LANMAN; break; #endif case Opt_sec_none: vol->nullauth = 1; - vol->secFlg |= CIFSSEC_MAY_NTLM; break; default: cifs_dbg(VFS, "bad security option: %s\n", value); @@ -1119,6 +1125,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol) vol->ops = &smb30_operations; vol->vals = &smb30_values; break; + case Smb_302: + vol->ops = &smb30_operations; /* currently identical with 3.0 */ + vol->vals = &smb302_values; + break; #endif default: cifs_dbg(VFS, "Unknown vers= option specified: %s\n", value); @@ -1424,7 +1434,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, vol->local_lease = 1; break; case Opt_sign: - vol->secFlg |= CIFSSEC_MUST_SIGN; + vol->sign = true; break; case Opt_seal: /* we do not do the following in secFlags because seal @@ -1455,6 +1465,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, case Opt_sloppy: sloppy = true; break; + case Opt_nosharesock: + vol->nosharesock = true; + break; /* Numeric Values */ case Opt_backupuid: @@ -1978,47 +1991,21 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr, static bool match_security(struct TCP_Server_Info *server, struct smb_vol *vol) { - unsigned int secFlags; - - if (vol->secFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - secFlags = vol->secFlg; - else - secFlags = global_secflags | vol->secFlg; - - switch (server->secType) { - case LANMAN: - if (!(secFlags & (CIFSSEC_MAY_LANMAN|CIFSSEC_MAY_PLNTXT))) - return false; - break; - case NTLMv2: - if (!(secFlags & CIFSSEC_MAY_NTLMV2)) - return false; - break; - case NTLM: - if (!(secFlags & CIFSSEC_MAY_NTLM)) - return false; - break; - case Kerberos: - if (!(secFlags & CIFSSEC_MAY_KRB5)) - return false; - break; - case RawNTLMSSP: - if (!(secFlags & CIFSSEC_MAY_NTLMSSP)) - return false; - break; - default: - /* shouldn't happen */ + /* + * The select_sectype function should either return the vol->sectype + * that was specified, or "Unspecified" if that sectype was not + * compatible with the given NEGOTIATE request. + */ + if (select_sectype(server, vol->sectype) == Unspecified) return false; - } - /* now check if signing mode is acceptable */ - if ((secFlags & CIFSSEC_MAY_SIGN) == 0 && - (server->sec_mode & SECMODE_SIGN_REQUIRED)) - return false; - else if (((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) && - (server->sec_mode & - (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)) == 0) - return false; + /* + * Now check if signing mode is acceptable. No need to check + * global_secflags at this point since if MUST_SIGN is set then + * the server->sign had better be too. + */ + if (vol->sign && !server->sign) + return false; return true; } @@ -2027,6 +2014,9 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol) { struct sockaddr *addr = (struct sockaddr *)&vol->dstaddr; + if (vol->nosharesock) + return 0; + if ((server->vals != vol->vals) || (server->ops != vol->ops)) return 0; @@ -2216,7 +2206,11 @@ out_err: static int match_session(struct cifs_ses *ses, struct smb_vol *vol) { - switch (ses->server->secType) { + if (vol->sectype != Unspecified && + vol->sectype != ses->sectype) + return 0; + + switch (ses->sectype) { case Kerberos: if (!uid_eq(vol->cred_uid, ses->cred_uid)) return 0; @@ -2493,7 +2487,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) ses->cred_uid = volume_info->cred_uid; ses->linux_uid = volume_info->linux_uid; - ses->overrideSecFlg = volume_info->secFlg; + ses->sectype = volume_info->sectype; + ses->sign = volume_info->sign; mutex_lock(&ses->session_mutex); rc = cifs_negotiate_protocol(xid, ses); @@ -3656,7 +3651,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, NTLMv2 password here) */ #ifdef CONFIG_CIFS_WEAK_PW_HASH if ((global_secflags & CIFSSEC_MAY_LANMAN) && - (ses->server->secType == LANMAN)) + (ses->sectype == LANMAN)) calc_lanman_hash(tcon->password, ses->server->cryptkey, ses->server->sec_mode & SECMODE_PW_ENCRYPT ? true : false, @@ -3674,8 +3669,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, } } - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->server->sign) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_STATUS32) { @@ -3738,7 +3732,7 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, } bcc_ptr += length + 1; bytes_left -= (length + 1); - strncpy(tcon->treeName, tree, MAX_TREE_SIZE); + strlcpy(tcon->treeName, tree, sizeof(tcon->treeName)); /* mostly informational -- no need to fail on error here */ kfree(tcon->nativeFileSystem); @@ -3827,7 +3821,6 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, int rc = -ENOSYS; struct TCP_Server_Info *server = ses->server; - ses->flags = 0; ses->capabilities = server->capabilities; if (linuxExtEnabled == 0) ses->capabilities &= (~server->vals->cap_unix); @@ -3848,6 +3841,8 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, server->sequence_number = 0x2; server->session_estab = true; ses->auth_key.response = NULL; + if (server->ops->generate_signingkey) + server->ops->generate_signingkey(server); } mutex_unlock(&server->srv_mutex); @@ -3870,23 +3865,11 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, static int cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) { - switch (ses->server->secType) { - case Kerberos: - vol->secFlg = CIFSSEC_MUST_KRB5; + vol->sectype = ses->sectype; + + /* krb5 is special, since we don't need username or pw */ + if (vol->sectype == Kerberos) return 0; - case NTLMv2: - vol->secFlg = CIFSSEC_MUST_NTLMV2; - break; - case NTLM: - vol->secFlg = CIFSSEC_MUST_NTLM; - break; - case RawNTLMSSP: - vol->secFlg = CIFSSEC_MUST_NTLMSSP; - break; - case LANMAN: - vol->secFlg = CIFSSEC_MUST_LANMAN; - break; - } return cifs_set_cifscreds(vol, ses); } @@ -3912,6 +3895,8 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) vol_info->nocase = master_tcon->nocase; vol_info->local_lease = master_tcon->local_lease; vol_info->no_linux_ext = !master_tcon->unix_ext; + vol_info->sectype = master_tcon->ses->sectype; + vol_info->sign = master_tcon->ses->sign; rc = cifs_set_vol_auth(vol_info, master_tcon->ses); if (rc) { diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 1bec014779fd..f7d4b2285efe 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -267,8 +267,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , if (treeCon->nocase) buffer->Flags |= SMBFLG_CASELESS; if ((treeCon->ses) && (treeCon->ses->server)) - if (treeCon->ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (treeCon->ses->server->sign) buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index f1213799de1a..ab8778469394 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -126,6 +126,22 @@ out: dput(dentry); } +/* + * Is it possible that this directory might turn out to be a DFS referral + * once we go to try and use it? + */ +static bool +cifs_dfs_is_possible(struct cifs_sb_info *cifs_sb) +{ +#ifdef CONFIG_CIFS_DFS_UPCALL + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + + if (tcon->Flags & SMB_SHARE_IS_IN_DFS) + return true; +#endif + return false; +} + static void cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) { @@ -135,6 +151,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; fattr->cf_dtype = DT_DIR; + /* + * Windows CIFS servers generally make DFS referrals look + * like directories in FIND_* responses with the reparse + * attribute flag also set (since DFS junctions are + * reparse points). We must revalidate at least these + * directory inodes before trying to use them (if + * they are DFS we will get PATH_NOT_COVERED back + * when queried directly and can then try to connect + * to the DFS target) + */ + if (cifs_dfs_is_possible(cifs_sb) && + (fattr->cf_cifsattrs & ATTR_REPARSE)) + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; } else { fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; fattr->cf_dtype = DT_REG; diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index f230571a7ab3..79358e341fd2 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -138,8 +138,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB) capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (ses->server->sign) pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_UNICODE) { @@ -310,11 +309,10 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifs_ses *ses, return; } -static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, - struct cifs_ses *ses, - const struct nls_table *nls_cp) +static void decode_ascii_ssetup(char **pbcc_area, __u16 bleft, + struct cifs_ses *ses, + const struct nls_table *nls_cp) { - int rc = 0; int len; char *bcc_ptr = *pbcc_area; @@ -322,24 +320,22 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, len = strnlen(bcc_ptr, bleft); if (len >= bleft) - return rc; + return; kfree(ses->serverOS); ses->serverOS = kzalloc(len + 1, GFP_KERNEL); if (ses->serverOS) strncpy(ses->serverOS, bcc_ptr, len); - if (strncmp(ses->serverOS, "OS/2", 4) == 0) { + if (strncmp(ses->serverOS, "OS/2", 4) == 0) cifs_dbg(FYI, "OS/2 server\n"); - ses->flags |= CIFS_SES_OS2; - } bcc_ptr += len + 1; bleft -= len + 1; len = strnlen(bcc_ptr, bleft); if (len >= bleft) - return rc; + return; kfree(ses->serverNOS); @@ -352,7 +348,7 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, len = strnlen(bcc_ptr, bleft); if (len > bleft) - return rc; + return; /* No domain field in LANMAN case. Domain is returned by old servers in the SMB negprot response */ @@ -360,8 +356,6 @@ static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, but thus do return domain here we could add parsing for it later, but it is not very important */ cifs_dbg(FYI, "ascii: bytes left %d\n", bleft); - - return rc; } int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, @@ -432,8 +426,7 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC; - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (ses->server->sign) { flags |= NTLMSSP_NEGOTIATE_SIGN; if (!ses->server->session_estab) flags |= NTLMSSP_NEGOTIATE_KEY_XCH; @@ -471,8 +464,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC; - if (ses->server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (ses->server->sign) { flags |= NTLMSSP_NEGOTIATE_SIGN; if (!ses->server->session_estab) flags |= NTLMSSP_NEGOTIATE_KEY_XCH; @@ -558,6 +550,56 @@ setup_ntlmv2_ret: return rc; } +enum securityEnum +select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) +{ + switch (server->negflavor) { + case CIFS_NEGFLAVOR_EXTENDED: + switch (requested) { + case Kerberos: + case RawNTLMSSP: + return requested; + case Unspecified: + if (server->sec_ntlmssp && + (global_secflags & CIFSSEC_MAY_NTLMSSP)) + return RawNTLMSSP; + if ((server->sec_kerberos || server->sec_mskerberos) && + (global_secflags & CIFSSEC_MAY_KRB5)) + return Kerberos; + /* Fallthrough */ + default: + return Unspecified; + } + case CIFS_NEGFLAVOR_UNENCAP: + switch (requested) { + case NTLM: + case NTLMv2: + return requested; + case Unspecified: + if (global_secflags & CIFSSEC_MAY_NTLMV2) + return NTLMv2; + if (global_secflags & CIFSSEC_MAY_NTLM) + return NTLM; + /* Fallthrough */ + default: + return Unspecified; + } + case CIFS_NEGFLAVOR_LANMAN: + switch (requested) { + case LANMAN: + return requested; + case Unspecified: + if (global_secflags & CIFSSEC_MAY_LANMAN) + return LANMAN; + /* Fallthrough */ + default: + return Unspecified; + } + default: + return Unspecified; + } +} + int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *nls_cp) @@ -579,11 +621,18 @@ CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, u16 blob_len; char *ntlmsspblob = NULL; - if (ses == NULL) + if (ses == NULL) { + WARN(1, "%s: ses == NULL!", __func__); return -EINVAL; + } - type = ses->server->secType; + type = select_sectype(ses->server, ses->sectype); cifs_dbg(FYI, "sess setup type %d\n", type); + if (type == Unspecified) { + cifs_dbg(VFS, "Unable to select appropriate authentication method!"); + return -EINVAL; + } + if (type == RawNTLMSSP) { /* if memory allocation is successful, caller of this function * frees it. @@ -643,8 +692,6 @@ ssetup_ntlmssp_authenticate: } bcc_ptr = str_area; - ses->flags &= ~CIFS_SES_LANMAN; - iov[1].iov_base = NULL; iov[1].iov_len = 0; @@ -668,7 +715,6 @@ ssetup_ntlmssp_authenticate: ses->server->sec_mode & SECMODE_PW_ENCRYPT ? true : false, lnm_session_key); - ses->flags |= CIFS_SES_LANMAN; memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); bcc_ptr += CIFS_AUTH_RESP_SIZE; @@ -938,8 +984,7 @@ ssetup_ntlmssp_authenticate: } decode_unicode_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); } else { - rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, - ses, nls_cp); + decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses, nls_cp); } ssetup_exit: diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 3efdb9d5c0b8..e813f04511d8 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -449,8 +449,7 @@ cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info) * WRITEX header, not including the 4 byte RFC1001 length. */ if (!(server->capabilities & CAP_LARGE_WRITE_X) || - (!(server->capabilities & CAP_UNIX) && - (server->sec_mode & (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)))) + (!(server->capabilities & CAP_UNIX) && server->sign)) wsize = min_t(unsigned int, wsize, server->maxBuf - sizeof(WRITE_REQ) + 4); @@ -765,20 +764,14 @@ smb_set_file_info(struct inode *inode, const char *full_path, } tcon = tlink_tcon(tlink); - /* - * NT4 apparently returns success on this call, but it doesn't really - * work. - */ - if (!(tcon->ses->flags & CIFS_SES_NT4)) { - rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, - cifs_sb->local_nls, + rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (rc == 0) { - cinode->cifsAttrs = le32_to_cpu(buf->Attributes); - goto out; - } else if (rc != -EOPNOTSUPP && rc != -EINVAL) - goto out; + if (rc == 0) { + cinode->cifsAttrs = le32_to_cpu(buf->Attributes); + goto out; + } else if (rc != -EOPNOTSUPP && rc != -EINVAL) { + goto out; } cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); @@ -964,4 +957,6 @@ struct smb_version_values smb1_values = { .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, .cap_large_files = CAP_LARGE_FILES, .oplock_read = OPLOCK_READ, + .signing_enabled = SECMODE_SIGN_ENABLED, + .signing_required = SECMODE_SIGN_REQUIRED, }; diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h index 7c0e2143e775..c38350851b08 100644 --- a/fs/cifs/smb2glob.h +++ b/fs/cifs/smb2glob.h @@ -54,5 +54,7 @@ #define SMB2_SIGNATURE_SIZE (16) #define SMB2_NTLMV2_SESSKEY_SIZE (16) #define SMB2_HMACSHA256_SIZE (32) +#define SMB2_CMACAES_SIZE (16) +#define SMB3_SIGNKEY_SIZE (16) #endif /* _SMB2_GLOB_H */ diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 10383d8c015b..b0c43345cd98 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -266,6 +266,10 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr) ((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength); break; case SMB2_IOCTL: + *off = le32_to_cpu( + ((struct smb2_ioctl_rsp *)hdr)->OutputOffset); + *len = le32_to_cpu(((struct smb2_ioctl_rsp *)hdr)->OutputCount); + break; case SMB2_CHANGE_NOTIFY: default: /* BB FIXME for unimplemented cases above */ diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index f2e76f3b0c61..6d15cab95b99 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -281,6 +281,25 @@ smb2_clear_stats(struct cifs_tcon *tcon) } static void +smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon) +{ + seq_puts(m, "\n\tShare Capabilities:"); + if (tcon->capabilities & SMB2_SHARE_CAP_DFS) + seq_puts(m, " DFS,"); + if (tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) + seq_puts(m, " CONTINUOUS AVAILABILITY,"); + if (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT) + seq_puts(m, " SCALEOUT,"); + if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) + seq_puts(m, " CLUSTER,"); + if (tcon->capabilities & SMB2_SHARE_CAP_ASYMMETRIC) + seq_puts(m, " ASYMMETRIC,"); + if (tcon->capabilities == 0) + seq_puts(m, " None"); + seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); +} + +static void smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) { #ifdef CONFIG_CIFS_STATS @@ -292,7 +311,6 @@ smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon) seq_printf(m, "\nSessionSetups: %d sent %d failed", atomic_read(&sent[SMB2_SESSION_SETUP_HE]), atomic_read(&failed[SMB2_SESSION_SETUP_HE])); -#define SMB2LOGOFF 0x0002 /* trivial request/resp */ seq_printf(m, "\nLogoffs: %d sent %d failed", atomic_read(&sent[SMB2_LOGOFF_HE]), atomic_read(&failed[SMB2_LOGOFF_HE])); @@ -645,6 +663,7 @@ struct smb_version_operations smb30_operations = { .dump_detail = smb2_dump_detail, .clear_stats = smb2_clear_stats, .print_stats = smb2_print_stats, + .dump_share_caps = smb2_dump_share_caps, .is_oplock_break = smb2_is_valid_oplock_break, .need_neg = smb2_need_neg, .negotiate = smb2_negotiate, @@ -690,6 +709,7 @@ struct smb_version_operations smb30_operations = { .get_lease_key = smb2_get_lease_key, .set_lease_key = smb2_set_lease_key, .new_lease_key = smb2_new_lease_key, + .generate_signingkey = generate_smb3signingkey, .calc_signature = smb3_calc_signature, }; @@ -709,6 +729,8 @@ struct smb_version_values smb20_values = { .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, .oplock_read = SMB2_OPLOCK_LEVEL_II, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, }; struct smb_version_values smb21_values = { @@ -727,6 +749,8 @@ struct smb_version_values smb21_values = { .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, .oplock_read = SMB2_OPLOCK_LEVEL_II, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, }; struct smb_version_values smb30_values = { @@ -745,4 +769,26 @@ struct smb_version_values smb30_values = { .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, .oplock_read = SMB2_OPLOCK_LEVEL_II, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, +}; + +struct smb_version_values smb302_values = { + .version_string = SMB302_VERSION_STRING, + .protocol_id = SMB302_PROT_ID, + .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU, + .large_lock_type = 0, + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, + .header_size = sizeof(struct smb2_hdr), + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp) - 1, + .lock_cmd = SMB2_LOCK, + .cap_unix = 0, + .cap_nt_find = SMB2_NT_FIND, + .cap_large_files = SMB2_LARGE_FILES, + .oplock_read = SMB2_OPLOCK_LEVEL_II, + .signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED, + .signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED, }; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2b95ce2b54e8..2b312e4eeaa6 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1,7 +1,7 @@ /* * fs/cifs/smb2pdu.c * - * Copyright (C) International Business Machines Corp., 2009, 2012 + * Copyright (C) International Business Machines Corp., 2009, 2013 * Etersoft, 2012 * Author(s): Steve French (sfrench@us.ibm.com) * Pavel Shilovsky (pshilovsky@samba.org) 2012 @@ -108,19 +108,33 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ , if (!tcon) goto out; + /* BB FIXME when we do write > 64K add +1 for every 64K in req or rsp */ + /* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */ + /* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */ + if ((tcon->ses) && + (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU)) + hdr->CreditCharge = cpu_to_le16(1); + /* else CreditCharge MBZ */ + hdr->TreeId = tcon->tid; /* Uid is not converted */ if (tcon->ses) hdr->SessionId = tcon->ses->Suid; - /* BB check following DFS flags BB */ - /* BB do we have to add check for SHI1005_FLAGS_DFS_ROOT too? */ - if (tcon->share_flags & SHI1005_FLAGS_DFS) - hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; - /* BB how does SMB2 do case sensitive? */ - /* if (tcon->nocase) - hdr->Flags |= SMBFLG_CASELESS; */ - if (tcon->ses && tcon->ses->server && - (tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED)) + + /* + * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have + * to pass the path on the Open SMB prefixed by \\server\share. + * Not sure when we would need to do the augmented path (if ever) and + * setting this flag breaks the SMB2 open operation since it is + * illegal to send an empty path name (without \\server\share prefix) + * when the DFS flag is set in the SMB open header. We could + * consider setting the flag on all operations other than open + * but it is safer to net set it for now. + */ +/* if (tcon->share_flags & SHI1005_FLAGS_DFS) + hdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */ + + if (tcon->ses && tcon->ses->server && tcon->ses->server->sign) hdr->Flags |= SMB2_FLAGS_SIGNED; out: pdu->StructureSize2 = cpu_to_le16(parmsize); @@ -328,34 +342,22 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) struct kvec iov[1]; int rc = 0; int resp_buftype; - struct TCP_Server_Info *server; - unsigned int sec_flags; - u16 temp = 0; + struct TCP_Server_Info *server = ses->server; int blob_offset, blob_length; char *security_blob; int flags = CIFS_NEG_OP; cifs_dbg(FYI, "Negotiate protocol\n"); - if (ses->server) - server = ses->server; - else { - rc = -EIO; - return rc; + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; } rc = small_smb2_init(SMB2_NEGOTIATE, NULL, (void **) &req); if (rc) return rc; - /* if any of auth flags (ie not sign or seal) are overriden use them */ - if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - sec_flags = ses->overrideSecFlg; /* BB FIXME fix sign flags?*/ - else /* if override flags set only sign/seal OR them with global auth */ - sec_flags = global_secflags | ses->overrideSecFlg; - - cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags); - req->hdr.SessionId = 0; req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id); @@ -364,12 +366,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) inc_rfc1001_len(req, 2); /* only one of SMB2 signing flags may be set in SMB2 request */ - if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) - temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; - else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */ - temp = SMB2_NEGOTIATE_SIGNING_ENABLED; - - req->SecurityMode = cpu_to_le16(temp); + if (ses->sign) + req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); + else if (global_secflags & CIFSSEC_MAY_SIGN) + req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); + else + req->SecurityMode = 0; req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities); @@ -399,6 +401,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) cifs_dbg(FYI, "negotiated smb2.1 dialect\n"); else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID)) cifs_dbg(FYI, "negotiated smb3.0 dialect\n"); + else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID)) + cifs_dbg(FYI, "negotiated smb3.02 dialect\n"); else { cifs_dbg(VFS, "Illegal dialect returned by server %d\n", le16_to_cpu(rsp->DialectRevision)); @@ -407,6 +411,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) } server->dialect = le16_to_cpu(rsp->DialectRevision); + /* SMB2 only has an extended negflavor */ + server->negflavor = CIFS_NEGFLAVOR_EXTENDED; server->maxBuf = le32_to_cpu(rsp->MaxTransactSize); server->max_read = le32_to_cpu(rsp->MaxReadSize); server->max_write = le32_to_cpu(rsp->MaxWriteSize); @@ -418,44 +424,22 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, &rsp->hdr); - if (blob_length == 0) { - cifs_dbg(VFS, "missing security blob on negprot\n"); - rc = -EIO; - goto neg_exit; - } - - cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags); - if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) { - cifs_dbg(FYI, "Signing required\n"); - if (!(server->sec_mode & (SMB2_NEGOTIATE_SIGNING_REQUIRED | - SMB2_NEGOTIATE_SIGNING_ENABLED))) { - cifs_dbg(VFS, "signing required but server lacks support\n"); - rc = -EOPNOTSUPP; - goto neg_exit; - } - server->sec_mode |= SECMODE_SIGN_REQUIRED; - } else if (sec_flags & CIFSSEC_MAY_SIGN) { - cifs_dbg(FYI, "Signing optional\n"); - if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { - cifs_dbg(FYI, "Server requires signing\n"); - server->sec_mode |= SECMODE_SIGN_REQUIRED; - } else { - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } - } else { - cifs_dbg(FYI, "Signing disabled\n"); - if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { - cifs_dbg(VFS, "Server requires packet signing to be enabled in /proc/fs/cifs/SecurityFlags\n"); - rc = -EOPNOTSUPP; - goto neg_exit; - } - server->sec_mode &= - ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED); - } + /* + * See MS-SMB2 section 2.2.4: if no blob, client picks default which + * for us will be + * ses->sectype = RawNTLMSSP; + * but for time being this is our only auth choice so doesn't matter. + * We just found a server which sets blob length to zero expecting raw. + */ + if (blob_length == 0) + cifs_dbg(FYI, "missing security blob on negprot\n"); + rc = cifs_enable_signing(server, ses->sign); #ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */ - rc = decode_neg_token_init(security_blob, blob_length, + if (rc) + goto neg_exit; + if (blob_length) + rc = decode_neg_token_init(security_blob, blob_length, &server->sec_type); if (rc == 1) rc = 0; @@ -480,9 +464,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, int rc = 0; int resp_buftype; __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ - struct TCP_Server_Info *server; - unsigned int sec_flags; - u8 temp = 0; + struct TCP_Server_Info *server = ses->server; u16 blob_length = 0; char *security_blob; char *ntlmssp_blob = NULL; @@ -490,11 +472,9 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, cifs_dbg(FYI, "Session Setup\n"); - if (ses->server) - server = ses->server; - else { - rc = -EIO; - return rc; + if (!server) { + WARN(1, "%s: server is NULL!\n", __func__); + return -EIO; } /* @@ -505,7 +485,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, if (!ses->ntlmssp) return -ENOMEM; - ses->server->secType = RawNTLMSSP; + /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */ + ses->sectype = RawNTLMSSP; ssetup_ntlmssp_authenticate: if (phase == NtLmChallenge) @@ -515,28 +496,19 @@ ssetup_ntlmssp_authenticate: if (rc) return rc; - /* if any of auth flags (ie not sign or seal) are overriden use them */ - if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) - sec_flags = ses->overrideSecFlg; /* BB FIXME fix sign flags?*/ - else /* if override flags set only sign/seal OR them with global auth */ - sec_flags = global_secflags | ses->overrideSecFlg; - - cifs_dbg(FYI, "sec_flags 0x%x\n", sec_flags); - req->hdr.SessionId = 0; /* First session, not a reauthenticate */ req->VcNumber = 0; /* MBZ */ /* to enable echos and oplocks */ req->hdr.CreditRequest = cpu_to_le16(3); /* only one of SMB2 signing flags may be set in SMB2 request */ - if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) - temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; - else if (ses->server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) - temp = SMB2_NEGOTIATE_SIGNING_REQUIRED; - else if (sec_flags & CIFSSEC_MAY_SIGN) /* MAY_SIGN is a single flag */ - temp = SMB2_NEGOTIATE_SIGNING_ENABLED; - - req->SecurityMode = temp; + if (server->sign) + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED; + else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */ + req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED; + else + req->SecurityMode = 0; + req->Capabilities = 0; req->Channel = 0; /* MBZ */ @@ -679,7 +651,7 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) /* since no tcon, smb2_init can not do this, so do here */ req->hdr.SessionId = ses->Suid; - if (server->sec_mode & SECMODE_SIGN_REQUIRED) + if (server->sign) req->hdr.Flags |= SMB2_FLAGS_SIGNED; rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0); @@ -788,11 +760,12 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, } tcon->share_flags = le32_to_cpu(rsp->ShareFlags); + tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); tcon->tidStatus = CifsGood; tcon->need_reconnect = false; tcon->tid = rsp->hdr.TreeId; - strncpy(tcon->treeName, tree, MAX_TREE_SIZE); + strlcpy(tcon->treeName, tree, sizeof(tcon->treeName)); if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) @@ -1036,6 +1009,122 @@ creat_exit: return rc; } +/* + * SMB2 IOCTL is used for both IOCTLs and FSCTLs + */ +int +SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, + u64 volatile_fid, u32 opcode, bool is_fsctl, char *in_data, + u32 indatalen, char **out_data, u32 *plen /* returned data len */) +{ + struct smb2_ioctl_req *req; + struct smb2_ioctl_rsp *rsp; + struct TCP_Server_Info *server; + struct cifs_ses *ses = tcon->ses; + struct kvec iov[2]; + int resp_buftype; + int num_iovecs; + int rc = 0; + + cifs_dbg(FYI, "SMB2 IOCTL\n"); + + /* zero out returned data len, in case of error */ + if (plen) + *plen = 0; + + if (ses && (ses->server)) + server = ses->server; + else + return -EIO; + + rc = small_smb2_init(SMB2_IOCTL, tcon, (void **) &req); + if (rc) + return rc; + + req->CtlCode = cpu_to_le32(opcode); + req->PersistentFileId = persistent_fid; + req->VolatileFileId = volatile_fid; + + if (indatalen) { + req->InputCount = cpu_to_le32(indatalen); + /* do not set InputOffset if no input data */ + req->InputOffset = + cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer) - 4); + iov[1].iov_base = in_data; + iov[1].iov_len = indatalen; + num_iovecs = 2; + } else + num_iovecs = 1; + + req->OutputOffset = 0; + req->OutputCount = 0; /* MBZ */ + + /* + * Could increase MaxOutputResponse, but that would require more + * than one credit. Windows typically sets this smaller, but for some + * ioctls it may be useful to allow server to send more. No point + * limiting what the server can send as long as fits in one credit + */ + req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */ + + if (is_fsctl) + req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL); + else + req->Flags = 0; + + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field */ + iov[0].iov_len = get_rfc1002_length(req) + 4; + + if (indatalen) + inc_rfc1001_len(req, indatalen); + + rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); + rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; + + if (rc != 0) { + if (tcon) + cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); + goto ioctl_exit; + } + + /* check if caller wants to look at return data or just return rc */ + if ((plen == NULL) || (out_data == NULL)) + goto ioctl_exit; + + *plen = le32_to_cpu(rsp->OutputCount); + + /* We check for obvious errors in the output buffer length and offset */ + if (*plen == 0) + goto ioctl_exit; /* server returned no data */ + else if (*plen > 0xFF00) { + cifs_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen); + *plen = 0; + rc = -EIO; + goto ioctl_exit; + } + + if (get_rfc1002_length(rsp) < le32_to_cpu(rsp->OutputOffset) + *plen) { + cifs_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen, + le32_to_cpu(rsp->OutputOffset)); + *plen = 0; + rc = -EIO; + goto ioctl_exit; + } + + *out_data = kmalloc(*plen, GFP_KERNEL); + if (*out_data == NULL) { + rc = -ENOMEM; + goto ioctl_exit; + } + + memcpy(*out_data, rsp->hdr.ProtocolId + le32_to_cpu(rsp->OutputOffset), + *plen); +ioctl_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +} + int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid) @@ -1384,8 +1473,7 @@ smb2_readv_callback(struct mid_q_entry *mid) case MID_RESPONSE_RECEIVED: credits_received = le16_to_cpu(buf->CreditRequest); /* result already set, check signature */ - if (server->sec_mode & - (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (server->sign) { int rc; rc = smb2_verify_signature(&rqst, server); diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index 4cb4ced258cb..f31043b26bd3 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -1,7 +1,7 @@ /* * fs/cifs/smb2pdu.h * - * Copyright (c) International Business Machines Corp., 2009, 2010 + * Copyright (c) International Business Machines Corp., 2009, 2013 * Etersoft, 2012 * Author(s): Steve French (sfrench@us.ibm.com) * Pavel Shilovsky (pshilovsky@samba.org) 2012 @@ -170,6 +170,7 @@ struct smb2_negotiate_req { #define SMB20_PROT_ID 0x0202 #define SMB21_PROT_ID 0x0210 #define SMB30_PROT_ID 0x0300 +#define SMB302_PROT_ID 0x0302 #define BAD_PROT_ID 0xFFFF /* SecurityMode flags */ @@ -283,10 +284,17 @@ struct smb2_tree_connect_rsp { #define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 #define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 #define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 -#define SHI1005_FLAGS_ENABLE_HASH 0x00002000 +#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000 +#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000 +#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000 +#define SHI1005_FLAGS_ALL 0x0000FF33 /* Possible share capabilities */ -#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) +#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ +#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ +#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */ +#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */ +#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ struct smb2_tree_disconnect_req { struct smb2_hdr hdr; @@ -477,6 +485,75 @@ struct create_lease { struct lease_context lcontext; } __packed; +/* this goes in the ioctl buffer when doing a copychunk request */ +struct copychunk_ioctl { + char SourceKey[24]; + __le32 ChunkCount; /* we are only sending 1 */ + __le32 Reserved; + /* array will only be one chunk long for us */ + __le64 SourceOffset; + __le64 TargetOffset; + __le32 Length; /* how many bytes to copy */ + __u32 Reserved2; +} __packed; + +/* Response and Request are the same format */ +struct validate_negotiate_info { + __le32 Capabilities; + __u8 Guid[SMB2_CLIENT_GUID_SIZE]; + __le16 SecurityMode; + __le16 DialectCount; + __le16 Dialect[1]; +} __packed; + +#define RSS_CAPABLE 0x00000001 +#define RDMA_CAPABLE 0x00000002 + +struct network_interface_info_ioctl_rsp { + __le32 Next; /* next interface. zero if this is last one */ + __le32 IfIndex; + __le32 Capability; /* RSS or RDMA Capable */ + __le32 Reserved; + __le64 LinkSpeed; + char SockAddr_Storage[128]; +} __packed; + +#define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ + +struct smb2_ioctl_req { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u16 Reserved; + __le32 CtlCode; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 InputOffset; + __le32 InputCount; + __le32 MaxInputResponse; + __le32 OutputOffset; + __le32 OutputCount; + __le32 MaxOutputResponse; + __le32 Flags; + __u32 Reserved2; + char Buffer[0]; +} __packed; + +struct smb2_ioctl_rsp { + struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u16 Reserved; + __le32 CtlCode; + __u64 PersistentFileId; /* opaque endianness */ + __u64 VolatileFileId; /* opaque endianness */ + __le32 InputOffset; + __le32 InputCount; + __le32 OutputOffset; + __le32 OutputCount; + __le32 Flags; + __u32 Reserved2; + /* char * buffer[] */ +} __packed; + /* Currently defined values for close flags */ #define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) struct smb2_close_req { @@ -517,17 +594,25 @@ struct smb2_flush_rsp { __le16 Reserved; } __packed; +/* For read request Flags field below, following flag is defined for SMB3.02 */ +#define SMB2_READFLAG_READ_UNBUFFERED 0x01 + +/* Channel field for read and write: exactly one of following flags can be set*/ +#define SMB2_CHANNEL_NONE 0x00000000 +#define SMB2_CHANNEL_RDMA_V1 0x00000001 /* SMB3 or later */ +#define SMB2_CHANNEL_RDMA_V1_INVALIDATE 0x00000001 /* SMB3.02 or later */ + struct smb2_read_req { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 49 */ __u8 Padding; /* offset from start of SMB2 header to place read */ - __u8 Reserved; + __u8 Flags; /* MBZ unless SMB3.02 or later */ __le32 Length; __le64 Offset; __u64 PersistentFileId; /* opaque endianness */ __u64 VolatileFileId; /* opaque endianness */ __le32 MinimumCount; - __le32 Channel; /* Reserved MBZ */ + __le32 Channel; /* MBZ except for SMB3 or later */ __le32 RemainingBytes; __le16 ReadChannelInfoOffset; /* Reserved MBZ */ __le16 ReadChannelInfoLength; /* Reserved MBZ */ @@ -545,8 +630,9 @@ struct smb2_read_rsp { __u8 Buffer[1]; } __packed; -/* For write request Flags field below the following flag is defined: */ -#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 +/* For write request Flags field below the following flags are defined: */ +#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001 /* SMB2.1 or later */ +#define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002 /* SMB3.02 or later */ struct smb2_write_req { struct smb2_hdr hdr; diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 2aa3535e38ce..d4e1eb807457 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -111,6 +111,10 @@ extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __u32 desired_access, __u32 create_disposition, __u32 file_attributes, __u32 create_options, __u8 *oplock, struct smb2_file_all_info *buf); +extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid, u32 opcode, + bool is_fsctl, char *in_data, u32 indatalen, + char **out_data, u32 *plen /* returned data len */); extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_file_id, u64 volatile_file_id); extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 01f0ac800780..09b4fbaadeb6 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -116,11 +116,155 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) return rc; } +void +generate_smb3signingkey(struct TCP_Server_Info *server) +{ + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; + __u8 L[4] = {0, 0, 0, 128}; + int rc = 0; + unsigned char prfhash[SMB2_HMACSHA256_SIZE]; + unsigned char *hashptr = prfhash; + + memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); + memset(server->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE); + + rc = crypto_shash_setkey(server->secmech.hmacsha256, + server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set with session key\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash); + if (rc) { + cifs_dbg(VFS, "%s: Could not init sign hmac\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + i, 4); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with n\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + "SMB2AESCMAC", 12); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with label\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + &zero, 1); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with zero\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + "SmbSign", 8); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with context\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_update(&server->secmech.sdeschmacsha256->shash, + L, 4); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with L\n", __func__); + goto smb3signkey_ret; + } + + rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash, + hashptr); + if (rc) { + cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); + goto smb3signkey_ret; + } + + memcpy(server->smb3signingkey, hashptr, SMB3_SIGNKEY_SIZE); + +smb3signkey_ret: + return; +} + int smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) { - cifs_dbg(FYI, "smb3 signatures not supported yet\n"); - return -EOPNOTSUPP; + int i, rc; + unsigned char smb3_signature[SMB2_CMACAES_SIZE]; + unsigned char *sigptr = smb3_signature; + struct kvec *iov = rqst->rq_iov; + int n_vec = rqst->rq_nvec; + struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; + + memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE); + memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE); + + rc = crypto_shash_setkey(server->secmech.cmacaes, + server->smb3signingkey, SMB2_CMACAES_SIZE); + if (rc) { + cifs_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__); + return rc; + } + + rc = crypto_shash_init(&server->secmech.sdesccmacaes->shash); + if (rc) { + cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__); + return rc; + } + + for (i = 0; i < n_vec; i++) { + if (iov[i].iov_len == 0) + continue; + if (iov[i].iov_base == NULL) { + cifs_dbg(VFS, "null iovec entry"); + return -EIO; + } + /* + * The first entry includes a length field (which does not get + * signed that occupies the first 4 bytes before the header). + */ + if (i == 0) { + if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ + break; /* nothing to sign or corrupt header */ + rc = + crypto_shash_update( + &server->secmech.sdesccmacaes->shash, + iov[i].iov_base + 4, iov[i].iov_len - 4); + } else { + rc = + crypto_shash_update( + &server->secmech.sdesccmacaes->shash, + iov[i].iov_base, iov[i].iov_len); + } + if (rc) { + cifs_dbg(VFS, "%s: Couldn't update cmac aes with payload\n", + __func__); + return rc; + } + } + + /* now hash over the rq_pages array */ + for (i = 0; i < rqst->rq_npages; i++) { + struct kvec p_iov; + + cifs_rqst_page_to_kvec(rqst, i, &p_iov); + crypto_shash_update(&server->secmech.sdesccmacaes->shash, + p_iov.iov_base, p_iov.iov_len); + kunmap(rqst->rq_pages[i]); + } + + rc = crypto_shash_final(&server->secmech.sdesccmacaes->shash, + sigptr); + if (rc) + cifs_dbg(VFS, "%s: Could not generate cmac aes\n", __func__); + + memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); + + return rc; } /* must be called with server->srv_mutex held */ @@ -275,8 +419,7 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, dump_smb(mid->resp_buf, min_t(u32, 80, len)); /* convert the length into a more usable form */ - if ((len > 24) && - (server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) { + if (len > 24 && server->sign) { int rc; rc = smb2_verify_signature(&rqst, server); diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h index 7056b891e087..d952ee48f4dc 100644 --- a/fs/cifs/smbfsctl.h +++ b/fs/cifs/smbfsctl.h @@ -1,7 +1,7 @@ /* * fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions * - * Copyright (c) International Business Machines Corp., 2002,2009 + * Copyright (c) International Business Machines Corp., 2002,2013 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ /* IOCTL information */ /* * List of ioctl/fsctl function codes that are or could be useful in the - * future to remote clients like cifs or SMB2 client. There is probably + * future to remote clients like cifs or SMB2/SMB3 client. This is probably * a slightly larger set of fsctls that NTFS local filesystem could handle, * including the seven below that we do not have struct definitions for. * Even with protocol definitions for most of these now available, we still @@ -30,7 +30,13 @@ * remotely. Some of the following, such as the encryption/compression ones * could be invoked from tools via a specialized hook into the VFS rather * than via the standard vfs entry points + * + * See MS-SMB2 Section 2.2.31 (last checked June 2013, all of that list are + * below). Additional detail on less common ones can be found in MS-FSCC + * section 2.3. */ +#define FSCTL_DFS_GET_REFERRALS 0x00060194 +#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0 #define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000 #define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004 #define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008 @@ -71,14 +77,31 @@ #define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ #define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ #define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ +#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */ #define FSCTL_SIS_LINK_FILES 0x0009C104 #define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */ #define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */ /* strange that the number for this op is not sequential with previous op */ #define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */ +/* Enumerate previous versions of a file */ +#define FSCTL_SRV_ENUMERATE_SNAPSHOTS 0x00144064 +/* Retrieve an opaque file reference for server-side data movement ie copy */ +#define FSCTL_SRV_REQUEST_RESUME_KEY 0x00140078 +#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */ #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */ #define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */ +/* Perform server-side data movement */ +#define FSCTL_SRV_COPYCHUNK 0x001440F2 +#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2 +#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */ +#define FSCTL_SRV_READ_HASH 0x001441BB /* BB add struct */ #define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003 #define IO_REPARSE_TAG_HSM 0xC0000004 #define IO_REPARSE_TAG_SIS 0x80000007 + +/* fsctl flags */ +/* If Flags is set to this value, the request is an FSCTL not ioctl request */ +#define SMB2_0_IOCTL_IS_FSCTL 0x00000001 + diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index bfbf4700d160..6fdcb1b4a106 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -447,7 +447,7 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) { int error; - error = wait_event_freezekillable(server->response_q, + error = wait_event_freezekillable_unsafe(server->response_q, midQ->mid_state != MID_REQUEST_SUBMITTED); if (error < 0) return -ERESTARTSYS; @@ -463,7 +463,7 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) struct mid_q_entry *mid; /* enable signing if server requires it */ - if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if (server->sign) hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; mid = AllocMidQEntry(hdr, server); @@ -612,7 +612,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server, dump_smb(mid->resp_buf, min_t(u32, 92, len)); /* convert the length into a more usable form */ - if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) { + if (server->sign) { struct kvec iov; int rc = 0; struct smb_rqst rqst = { .rq_iov = &iov, diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 0f0f73624a88..9ad17b15b454 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -34,6 +34,7 @@ #include <linux/mutex.h> #include <linux/anon_inodes.h> #include <linux/device.h> +#include <linux/freezer.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/mman.h> @@ -1602,7 +1603,8 @@ fetch_events: } spin_unlock_irqrestore(&ep->lock, flags); - if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) + if (!freezable_schedule_hrtimeout_range(to, slack, + HRTIMER_MODE_ABS)) timed_out = 1; spin_lock_irqsave(&ep->lock, flags); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c1c7a9d78722..ce727047ee87 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -79,7 +79,7 @@ int nfs_wait_bit_killable(void *word) { if (fatal_signal_pending(current)) return -ERESTARTSYS; - freezable_schedule(); + freezable_schedule_unsafe(); return 0; } EXPORT_SYMBOL_GPL(nfs_wait_bit_killable); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 43ea96ced28c..ce90eb4775c2 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -33,7 +33,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) res = rpc_call_sync(clnt, msg, flags); if (res != -EJUKEBOX) break; - freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); + freezable_schedule_timeout_killable_unsafe(NFS_JUKEBOX_RETRY_TIME); res = -ERESTARTSYS; } while (!fatal_signal_pending(current)); return res; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d7ba5616989c..28241a42f363 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -268,7 +268,7 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout) *timeout = NFS4_POLL_RETRY_MIN; if (*timeout > NFS4_POLL_RETRY_MAX) *timeout = NFS4_POLL_RETRY_MAX; - freezable_schedule_timeout_killable(*timeout); + freezable_schedule_timeout_killable_unsafe(*timeout); if (fatal_signal_pending(current)) res = -ERESTARTSYS; *timeout <<= 1; @@ -4528,7 +4528,7 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4 static unsigned long nfs4_set_lock_task_retry(unsigned long timeout) { - freezable_schedule_timeout_killable(timeout); + freezable_schedule_timeout_killable_unsafe(timeout); timeout <<= 1; if (timeout > NFS4_LOCK_MAXTIMEOUT) return NFS4_LOCK_MAXTIMEOUT; diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index e4bcb2cf055a..bfd95bf38005 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -178,6 +178,8 @@ static int pstore_unlink(struct inode *dir, struct dentry *dentry) if (p->psi->erase) p->psi->erase(p->type, p->id, p->count, dentry->d_inode->i_ctime, p->psi); + else + return -EPERM; return simple_unlink(dir, dentry); } diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 86d1038b5a12..b7ffe2bcd9c4 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -239,17 +239,15 @@ int pstore_register(struct pstore_info *psi) { struct module *owner = psi->owner; + if (backend && strcmp(backend, psi->name)) + return -EPERM; + spin_lock(&pstore_lock); if (psinfo) { spin_unlock(&pstore_lock); return -EBUSY; } - if (backend && strcmp(backend, psi->name)) { - spin_unlock(&pstore_lock); - return -EINVAL; - } - if (!psi->write) psi->write = pstore_write_compat; psinfo = psi; @@ -274,6 +272,9 @@ int pstore_register(struct pstore_info *psi) add_timer(&pstore_timer); } + pr_info("pstore: Registered %s as persistent store backend\n", + psi->name); + return 0; } EXPORT_SYMBOL_GPL(pstore_register); diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 1376e5a8f0d6..43abee2c6cb9 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -399,8 +399,6 @@ static int ramoops_probe(struct platform_device *pdev) goto fail_out; } - if (!is_power_of_2(pdata->mem_size)) - pdata->mem_size = rounddown_pow_of_two(pdata->mem_size); if (!is_power_of_2(pdata->record_size)) pdata->record_size = rounddown_pow_of_two(pdata->record_size); if (!is_power_of_2(pdata->console_size)) diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 59337326e288..de272d426763 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -46,7 +46,7 @@ static inline size_t buffer_start(struct persistent_ram_zone *prz) } /* increase and wrap the start pointer, returning the old value */ -static inline size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) +static size_t buffer_start_add_atomic(struct persistent_ram_zone *prz, size_t a) { int old; int new; @@ -62,7 +62,7 @@ static inline size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) } /* increase the size counter until it hits the max size */ -static inline void buffer_size_add(struct persistent_ram_zone *prz, size_t a) +static void buffer_size_add_atomic(struct persistent_ram_zone *prz, size_t a) { size_t old; size_t new; @@ -78,6 +78,53 @@ static inline void buffer_size_add(struct persistent_ram_zone *prz, size_t a) } while (atomic_cmpxchg(&prz->buffer->size, old, new) != old); } +static DEFINE_RAW_SPINLOCK(buffer_lock); + +/* increase and wrap the start pointer, returning the old value */ +static size_t buffer_start_add_locked(struct persistent_ram_zone *prz, size_t a) +{ + int old; + int new; + unsigned long flags; + + raw_spin_lock_irqsave(&buffer_lock, flags); + + old = atomic_read(&prz->buffer->start); + new = old + a; + while (unlikely(new > prz->buffer_size)) + new -= prz->buffer_size; + atomic_set(&prz->buffer->start, new); + + raw_spin_unlock_irqrestore(&buffer_lock, flags); + + return old; +} + +/* increase the size counter until it hits the max size */ +static void buffer_size_add_locked(struct persistent_ram_zone *prz, size_t a) +{ + size_t old; + size_t new; + unsigned long flags; + + raw_spin_lock_irqsave(&buffer_lock, flags); + + old = atomic_read(&prz->buffer->size); + if (old == prz->buffer_size) + goto exit; + + new = old + a; + if (new > prz->buffer_size) + new = prz->buffer_size; + atomic_set(&prz->buffer->size, new); + +exit: + raw_spin_unlock_irqrestore(&buffer_lock, flags); +} + +static size_t (*buffer_start_add)(struct persistent_ram_zone *, size_t) = buffer_start_add_atomic; +static void (*buffer_size_add)(struct persistent_ram_zone *, size_t) = buffer_size_add_atomic; + static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz, uint8_t *data, size_t len, uint8_t *ecc) { @@ -372,6 +419,9 @@ static void *persistent_ram_iomap(phys_addr_t start, size_t size) return NULL; } + buffer_start_add = buffer_start_add_locked; + buffer_size_add = buffer_size_add_locked; + return ioremap(start, size); } diff --git a/fs/select.c b/fs/select.c index 8c1c96c27062..6b14dc7df3a4 100644 --- a/fs/select.c +++ b/fs/select.c @@ -27,6 +27,7 @@ #include <linux/rcupdate.h> #include <linux/hrtimer.h> #include <linux/sched/rt.h> +#include <linux/freezer.h> #include <asm/uaccess.h> @@ -236,7 +237,8 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state, set_current_state(state); if (!pwq->triggered) - rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS); + rc = freezable_schedule_hrtimeout_range(expires, slack, + HRTIMER_MODE_ABS); __set_current_state(TASK_RUNNING); /* diff --git a/include/acpi/acconfig.h b/include/acpi/acconfig.h index 14ceff788c40..1c16f821434f 100644 --- a/include/acpi/acconfig.h +++ b/include/acpi/acconfig.h @@ -219,8 +219,8 @@ * *****************************************************************************/ -#define ACPI_DEBUGGER_MAX_ARGS 8 /* Must be max method args + 1 */ -#define ACPI_DB_LINE_BUFFER_SIZE 512 +#define ACPI_DEBUGGER_MAX_ARGS ACPI_METHOD_NUM_ARGS + 4 /* Max command line arguments */ +#define ACPI_DB_LINE_BUFFER_SIZE 512 #define ACPI_DEBUGGER_COMMAND_PROMPT '-' #define ACPI_DEBUGGER_EXECUTE_PROMPT '%' diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h index 4f52ea795c7a..4607b027a657 100644 --- a/include/acpi/acoutput.h +++ b/include/acpi/acoutput.h @@ -428,27 +428,21 @@ * This is the non-debug case -- make everything go away, * leaving no executable debug code! */ -#define ACPI_FUNCTION_NAME(a) #define ACPI_DEBUG_PRINT(pl) #define ACPI_DEBUG_PRINT_RAW(pl) #define ACPI_DEBUG_EXEC(a) #define ACPI_DEBUG_ONLY_MEMBERS(a) +#define ACPI_FUNCTION_NAME(a) #define ACPI_FUNCTION_TRACE(a) #define ACPI_FUNCTION_TRACE_PTR(a, b) #define ACPI_FUNCTION_TRACE_U32(a, b) #define ACPI_FUNCTION_TRACE_STR(a, b) -#define ACPI_FUNCTION_EXIT -#define ACPI_FUNCTION_STATUS_EXIT(s) -#define ACPI_FUNCTION_VALUE_EXIT(s) #define ACPI_FUNCTION_ENTRY() #define ACPI_DUMP_STACK_ENTRY(a) #define ACPI_DUMP_OPERANDS(a, b, c) #define ACPI_DUMP_ENTRY(a, b) -#define ACPI_DUMP_TABLES(a, b) #define ACPI_DUMP_PATHNAME(a, b, c, d) #define ACPI_DUMP_BUFFER(a, b) -#define ACPI_DEBUG_PRINT(pl) -#define ACPI_DEBUG_PRINT_RAW(pl) #define ACPI_IS_DEBUG_ENABLED(level, component) 0 /* Return macros must have a return statement at the minimum */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c13c919ab99e..56e6b68c8d2f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -63,13 +63,6 @@ acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld #define ACPI_BUS_FILE_ROOT "acpi" extern struct proc_dir_entry *acpi_root_dir; -enum acpi_bus_removal_type { - ACPI_BUS_REMOVAL_NORMAL = 0, - ACPI_BUS_REMOVAL_EJECT, - ACPI_BUS_REMOVAL_SUPRISE, - ACPI_BUS_REMOVAL_TYPE_COUNT -}; - enum acpi_bus_device_type { ACPI_BUS_TYPE_DEVICE = 0, ACPI_BUS_TYPE_POWER, @@ -163,12 +156,10 @@ struct acpi_device_flags { u32 dynamic_status:1; u32 removable:1; u32 ejectable:1; - u32 suprise_removal_ok:1; u32 power_manageable:1; - u32 performance_manageable:1; u32 eject_pending:1; u32 match_driver:1; - u32 reserved:24; + u32 reserved:26; }; /* File System */ @@ -286,6 +277,7 @@ struct acpi_device_physical_node { u8 node_id; struct list_head node; struct device *dev; + bool put_online:1; }; /* set maximum of physical nodes to 32 for expansibility */ @@ -310,7 +302,6 @@ struct acpi_device { struct acpi_driver *driver; void *driver_data; struct device dev; - enum acpi_bus_removal_type removal_type; /* indicate for different removal type */ u8 physical_node_count; struct list_head physical_node_list; struct mutex physical_node_lock; @@ -443,7 +434,6 @@ int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); struct acpi_pci_root { - struct list_head node; struct acpi_device * device; struct pci_bus *bus; u16 segment; @@ -468,8 +458,6 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, acpi_notify_handler handler, void *context); acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, acpi_notify_handler handler); -int acpi_device_power_state(struct device *dev, struct acpi_device *adev, - u32 target_state, int d_max_in, int *d_min_p); int acpi_pm_device_sleep_state(struct device *, int *, int); void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev); void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev); @@ -485,23 +473,13 @@ static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, { return AE_SUPPORT; } -static inline int __acpi_device_power_state(int m, int *p) +static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) { if (p) *p = ACPI_STATE_D0; + return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3) ? m : ACPI_STATE_D0; } -static inline int acpi_device_power_state(struct device *dev, - struct acpi_device *adev, - u32 target_state, int d_max_in, - int *d_min_p) -{ - return __acpi_device_power_state(d_max_in, d_min_p); -} -static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) -{ - return __acpi_device_power_state(m, p); -} static inline void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev) {} static inline void acpi_dev_pm_remove_dependent(acpi_handle handle, diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 454881e6450a..1b09300810e6 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 0x20130328 +#define ACPI_CA_VERSION 0x20130517 #include <acpi/acconfig.h> #include <acpi/actypes.h> @@ -80,6 +80,7 @@ extern bool acpi_gbl_enable_aml_debug_object; extern u8 acpi_gbl_copy_dsdt_locally; extern u8 acpi_gbl_truncate_io_addresses; extern u8 acpi_gbl_disable_auto_repair; +extern u8 acpi_gbl_disable_ssdt_table_load; /* * Hardware-reduced prototypes. All interfaces that use these macros will diff --git a/include/acpi/processor.h b/include/acpi/processor.h index ea69367fdd3b..66096d06925e 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -6,6 +6,10 @@ #include <linux/thermal.h> #include <asm/acpi.h> +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_DEVICE_NAME "Processor" +#define ACPI_PROCESSOR_DEVICE_HID "ACPI0007" + #define ACPI_PROCESSOR_BUSY_METRIC 10 #define ACPI_PROCESSOR_MAX_POWER 8 @@ -207,6 +211,7 @@ struct acpi_processor { struct acpi_processor_throttling throttling; struct acpi_processor_limit limit; struct thermal_cooling_device *cdev; + struct device *dev; /* Processor device. */ }; struct acpi_processor_errata { diff --git a/include/dt-bindings/pinctrl/rockchip.h b/include/dt-bindings/pinctrl/rockchip.h new file mode 100644 index 000000000000..cd5788be82ce --- /dev/null +++ b/include/dt-bindings/pinctrl/rockchip.h @@ -0,0 +1,32 @@ +/* + * Header providing constants for Rockchip pinctrl bindings. + * + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner <heiko@sntech.de> + * + * 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. + */ + +#ifndef __DT_BINDINGS_ROCKCHIP_PINCTRL_H__ +#define __DT_BINDINGS_ROCKCHIP_PINCTRL_H__ + +#define RK_GPIO0 0 +#define RK_GPIO1 1 +#define RK_GPIO2 2 +#define RK_GPIO3 3 +#define RK_GPIO4 4 +#define RK_GPIO6 6 + +#define RK_FUNC_GPIO 0 +#define RK_FUNC_1 1 +#define RK_FUNC_2 2 + +#endif diff --git a/arch/arm/include/asm/kvm_arch_timer.h b/include/kvm/arm_arch_timer.h index 68cb9e1dfb81..6d9aeddc09bf 100644 --- a/arch/arm/include/asm/kvm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -61,6 +61,8 @@ struct arch_timer_cpu { #ifdef CONFIG_KVM_ARM_TIMER int kvm_timer_hyp_init(void); int kvm_timer_init(struct kvm *kvm); +void kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu, + const struct kvm_irq_level *irq); void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu); void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu); @@ -76,6 +78,8 @@ static inline int kvm_timer_init(struct kvm *kvm) return 0; } +static inline void kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu, + const struct kvm_irq_level *irq) {} static inline void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) {} static inline void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) {} static inline void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) {} diff --git a/arch/arm/include/asm/kvm_vgic.h b/include/kvm/arm_vgic.h index 343744e4809c..343744e4809c 100644 --- a/arch/arm/include/asm/kvm_vgic.h +++ b/include/kvm/arm_vgic.h diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 17b5b5967641..353ba256f368 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -352,8 +352,7 @@ extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, /* Enable _OST when all relevant hotplug operations are enabled */ #if defined(CONFIG_ACPI_HOTPLUG_CPU) && \ - (defined(CONFIG_ACPI_HOTPLUG_MEMORY) || \ - defined(CONFIG_ACPI_HOTPLUG_MEMORY_MODULE)) && \ + defined(CONFIG_ACPI_HOTPLUG_MEMORY) && \ defined(CONFIG_ACPI_CONTAINER) #define ACPI_HOTPLUG_OST #endif diff --git a/include/linux/aer.h b/include/linux/aer.h index 737f90ab4b62..4dbaa7081530 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -7,6 +7,10 @@ #ifndef _AER_H_ #define _AER_H_ +#define AER_NONFATAL 0 +#define AER_FATAL 1 +#define AER_CORRECTABLE 2 + struct aer_header_log_regs { unsigned int dw0; unsigned int dw1; @@ -31,9 +35,9 @@ struct aer_capability_regs { #if defined(CONFIG_PCIEAER) /* pci-e port driver needs this function to enable aer */ -extern int pci_enable_pcie_error_reporting(struct pci_dev *dev); -extern int pci_disable_pcie_error_reporting(struct pci_dev *dev); -extern int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); +int pci_enable_pcie_error_reporting(struct pci_dev *dev); +int pci_disable_pcie_error_reporting(struct pci_dev *dev); +int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); #else static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev) { @@ -49,11 +53,11 @@ static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) } #endif -extern void cper_print_aer(struct pci_dev *dev, - int cper_severity, struct aer_capability_regs *aer); -extern int cper_severity_to_aer(int cper_severity); -extern void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, - int severity, - struct aer_capability_regs *aer_regs); +void cper_print_aer(struct pci_dev *dev, int cper_severity, + struct aer_capability_regs *aer); +int cper_severity_to_aer(int cper_severity); +void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, + int severity, + struct aer_capability_regs *aer_regs); #endif //_AER_H_ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 11860985fecb..1ec14a732176 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -210,6 +210,10 @@ void of_fixed_clk_setup(struct device_node *np); * CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to * enable the clock. Setting this flag does the opposite: setting the bit * disable the clock and clearing it enables the clock + * CLK_GATE_HIWORD_MASK - The gate settings are only in lower 16-bit + * of this register, and mask of gate bits are in higher 16-bit of this + * register. While setting the gate bits, higher 16-bit should also be + * updated to indicate changing gate bits. */ struct clk_gate { struct clk_hw hw; @@ -220,6 +224,7 @@ struct clk_gate { }; #define CLK_GATE_SET_TO_DISABLE BIT(0) +#define CLK_GATE_HIWORD_MASK BIT(1) extern const struct clk_ops clk_gate_ops; struct clk *clk_register_gate(struct device *dev, const char *name, @@ -257,6 +262,10 @@ struct clk_div_table { * Some hardware implementations gracefully handle this case and allow a * zero divisor by not modifying their input clock * (divide by one / bypass). + * CLK_DIVIDER_HIWORD_MASK - The divider settings are only in lower 16-bit + * of this register, and mask of divider bits are in higher 16-bit of this + * register. While setting the divider bits, higher 16-bit should also be + * updated to indicate changing divider bits. */ struct clk_divider { struct clk_hw hw; @@ -271,6 +280,7 @@ struct clk_divider { #define CLK_DIVIDER_ONE_BASED BIT(0) #define CLK_DIVIDER_POWER_OF_TWO BIT(1) #define CLK_DIVIDER_ALLOW_ZERO BIT(2) +#define CLK_DIVIDER_HIWORD_MASK BIT(3) extern const struct clk_ops clk_divider_ops; struct clk *clk_register_divider(struct device *dev, const char *name, @@ -299,6 +309,10 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, * Flags: * CLK_MUX_INDEX_ONE - register index starts at 1, not 0 * CLK_MUX_INDEX_BIT - register index is a single bit (power of two) + * CLK_MUX_HIWORD_MASK - The mux settings are only in lower 16-bit of this + * register, and mask of mux bits are in higher 16-bit of this register. + * While setting the mux bits, higher 16-bit should also be updated to + * indicate changing mux bits. */ struct clk_mux { struct clk_hw hw; @@ -312,6 +326,7 @@ struct clk_mux { #define CLK_MUX_INDEX_ONE BIT(0) #define CLK_MUX_INDEX_BIT BIT(1) +#define CLK_MUX_HIWORD_MASK BIT(2) extern const struct clk_ops clk_mux_ops; @@ -423,6 +438,17 @@ struct of_device_id; typedef void (*of_clk_init_cb_t)(struct device_node *); +struct clk_onecell_data { + struct clk **clks; + unsigned int clk_num; +}; + +#define CLK_OF_DECLARE(name, compat, fn) \ + static const struct of_device_id __clk_of_table_##name \ + __used __section(__clk_of_table) \ + = { .compatible = compat, .data = fn }; + +#ifdef CONFIG_OF int of_clk_add_provider(struct device_node *np, struct clk *(*clk_src_get)(struct of_phandle_args *args, void *data), @@ -430,19 +456,39 @@ int of_clk_add_provider(struct device_node *np, void of_clk_del_provider(struct device_node *np); struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data); -struct clk_onecell_data { - struct clk **clks; - unsigned int clk_num; -}; struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data); const char *of_clk_get_parent_name(struct device_node *np, int index); void of_clk_init(const struct of_device_id *matches); -#define CLK_OF_DECLARE(name, compat, fn) \ - static const struct of_device_id __clk_of_table_##name \ - __used __section(__clk_of_table) \ - = { .compatible = compat, .data = fn }; +#else /* !CONFIG_OF */ +static inline int of_clk_add_provider(struct device_node *np, + struct clk *(*clk_src_get)(struct of_phandle_args *args, + void *data), + void *data) +{ + return 0; +} +#define of_clk_del_provider(np) \ + { while (0); } +static inline struct clk *of_clk_src_simple_get( + struct of_phandle_args *clkspec, void *data) +{ + return ERR_PTR(-ENOENT); +} +static inline struct clk *of_clk_src_onecell_get( + struct of_phandle_args *clkspec, void *data) +{ + return ERR_PTR(-ENOENT); +} +static inline const char *of_clk_get_parent_name(struct device_node *np, + int index) +{ + return NULL; +} +#define of_clk_init(matches) \ + { while (0); } +#endif /* CONFIG_OF */ #endif /* CONFIG_COMMON_CLK */ #endif /* CLK_PROVIDER_H */ diff --git a/include/linux/clk/tegra.h b/include/linux/clk/tegra.h index 642789baec74..23a0ceee831f 100644 --- a/include/linux/clk/tegra.h +++ b/include/linux/clk/tegra.h @@ -120,9 +120,13 @@ static inline void tegra_cpu_clock_resume(void) } #endif +#ifdef CONFIG_ARCH_TEGRA void tegra_periph_reset_deassert(struct clk *c); void tegra_periph_reset_assert(struct clk *c); -void tegra_clocks_init(void); +#else +static inline void tegra_periph_reset_deassert(struct clk *c) {} +static inline void tegra_periph_reset_assert(struct clk *c) {} +#endif void tegra_clocks_apply_init_table(void); #endif /* __LINUX_CLK_TEGRA_H_ */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 037d36ae63e5..4d7390bc1727 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -1,8 +1,8 @@ /* - * linux/include/linux/cpufreq.h + * linux/include/linux/cpufreq.h * - * Copyright (C) 2001 Russell King - * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2001 Russell King + * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * * 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 @@ -26,7 +26,6 @@ /* Print length for names. Extra 1 space for accomodating '\n' in prints */ #define CPUFREQ_NAME_PLEN (CPUFREQ_NAME_LEN + 1) - /********************************************************************* * CPUFREQ NOTIFIER INTERFACE * *********************************************************************/ @@ -71,6 +70,10 @@ struct cpufreq_governor; /* /sys/devices/system/cpu/cpufreq: entry point for global variables */ extern struct kobject *cpufreq_global_kobject; +int cpufreq_get_global_kobject(void); +void cpufreq_put_global_kobject(void); +int cpufreq_sysfs_create_file(const struct attribute *attr); +void cpufreq_sysfs_remove_file(const struct attribute *attr); #define CPUFREQ_ETERNAL (-1) struct cpufreq_cpuinfo { @@ -107,6 +110,7 @@ struct cpufreq_policy { unsigned int policy; /* see above */ struct cpufreq_governor *governor; /* see below */ void *governor_data; + bool governor_enabled; /* governor start/stop flag */ struct work_struct update; /* if update_policy() needs to be * called, but you're in IRQ context */ @@ -115,6 +119,7 @@ struct cpufreq_policy { struct kobject kobj; struct completion kobj_unregister; + bool transition_ongoing; /* Tracks transition status */ }; #define CPUFREQ_ADJUST (0) @@ -148,17 +153,18 @@ struct cpufreq_freqs { u8 flags; /* flags of cpufreq_driver, see below. */ }; - /** - * cpufreq_scale - "old * mult / div" calculation for large values (32-bit-arch safe) + * cpufreq_scale - "old * mult / div" calculation for large values (32-bit-arch + * safe) * @old: old value * @div: divisor * @mult: multiplier * * - * new = old * mult / div + * new = old * mult / div */ -static inline unsigned long cpufreq_scale(unsigned long old, u_int div, u_int mult) +static inline unsigned long cpufreq_scale(unsigned long old, u_int div, + u_int mult) { #if BITS_PER_LONG == 32 @@ -211,14 +217,12 @@ extern int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); - extern int __cpufreq_driver_getavg(struct cpufreq_policy *policy, unsigned int cpu); int cpufreq_register_governor(struct cpufreq_governor *governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor); - /********************************************************************* * CPUFREQ DRIVER INTERFACE * *********************************************************************/ @@ -229,7 +233,7 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor); struct freq_attr; struct cpufreq_driver { - struct module *owner; + struct module *owner; char name[CPUFREQ_NAME_LEN]; u8 flags; /* @@ -277,11 +281,11 @@ struct cpufreq_driver { int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); - void cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state); -static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min, unsigned int max) +static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, + unsigned int min, unsigned int max) { if (policy->min < min) policy->min = min; @@ -337,12 +341,16 @@ const char *cpufreq_get_current_driver(void); /********************************************************************* * CPUFREQ 2.6. INTERFACE * *********************************************************************/ +u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu); int cpufreq_update_policy(unsigned int cpu); bool have_governor_per_policy(void); +struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy); #ifdef CONFIG_CPU_FREQ -/* query the current CPU frequency (in kHz). If zero, cpufreq couldn't detect it */ +/* + * query the current CPU frequency (in kHz). If zero, cpufreq couldn't detect it + */ unsigned int cpufreq_get(unsigned int cpu); #else static inline unsigned int cpufreq_get(unsigned int cpu) @@ -351,7 +359,9 @@ static inline unsigned int cpufreq_get(unsigned int cpu) } #endif -/* query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it */ +/* + * query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it + */ #ifdef CONFIG_CPU_FREQ unsigned int cpufreq_quick_get(unsigned int cpu); unsigned int cpufreq_quick_get_max(unsigned int cpu); @@ -366,16 +376,14 @@ static inline unsigned int cpufreq_quick_get_max(unsigned int cpu) } #endif - /********************************************************************* * CPUFREQ DEFAULT GOVERNOR * *********************************************************************/ - /* - Performance governor is fallback governor if any other gov failed to - auto load due latency restrictions -*/ + * Performance governor is fallback governor if any other gov failed to auto + * load due latency restrictions + */ #ifdef CONFIG_CPU_FREQ_GOV_PERFORMANCE extern struct cpufreq_governor cpufreq_gov_performance; #endif @@ -395,7 +403,6 @@ extern struct cpufreq_governor cpufreq_gov_conservative; #define CPUFREQ_DEFAULT_GOVERNOR (&cpufreq_gov_conservative) #endif - /********************************************************************* * FREQUENCY TABLE HELPERS * *********************************************************************/ @@ -404,7 +411,7 @@ extern struct cpufreq_governor cpufreq_gov_conservative; #define CPUFREQ_TABLE_END ~1 struct cpufreq_frequency_table { - unsigned int index; /* any */ + unsigned int driver_data; /* driver specific data, not used by core */ unsigned int frequency; /* kHz - doesn't need to be in ascending * order */ }; @@ -432,4 +439,7 @@ void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy); void cpufreq_frequency_table_put_attr(unsigned int cpu); + +ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf); + #endif /* _LINUX_CPUFREQ_H */ diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 8f0406230a0a..0bc4b74668e9 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -111,6 +111,9 @@ struct cpuidle_driver { struct cpuidle_state states[CPUIDLE_STATE_MAX]; int state_count; int safe_state_index; + + /* the driver handles the cpus in cpumask */ + struct cpumask *cpumask; }; #ifdef CONFIG_CPU_IDLE @@ -135,9 +138,6 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_play_dead(void); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); -extern int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu); -extern void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu); - #else static inline void disable_cpuidle(void) { } static inline int cpuidle_idle_call(void) { return -ENODEV; } diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h index 21ca773f77bf..822c1354f3a6 100644 --- a/include/linux/debug_locks.h +++ b/include/linux/debug_locks.h @@ -51,7 +51,7 @@ struct task_struct; extern void debug_show_all_locks(void); extern void debug_show_held_locks(struct task_struct *task); extern void debug_check_no_locks_freed(const void *from, unsigned long len); -extern void debug_check_no_locks_held(struct task_struct *task); +extern void debug_check_no_locks_held(void); #else static inline void debug_show_all_locks(void) { @@ -67,7 +67,7 @@ debug_check_no_locks_freed(const void *from, unsigned long len) } static inline void -debug_check_no_locks_held(struct task_struct *task) +debug_check_no_locks_held(void) { } #endif diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index fe8c4476f7e4..5f1ab92107e6 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -181,6 +181,8 @@ extern struct devfreq *devfreq_add_device(struct device *dev, const char *governor_name, void *data); extern int devfreq_remove_device(struct devfreq *devfreq); + +/* Supposed to be called by PM_SLEEP/PM_RUNTIME callbacks */ extern int devfreq_suspend_device(struct devfreq *devfreq); extern int devfreq_resume_device(struct devfreq *devfreq); diff --git a/include/linux/device.h b/include/linux/device.h index 9d4835a8f8b8..bcf8c0d4cd98 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -71,6 +71,10 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * the specific driver's probe to initial the matched device. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. + * + * @online: Called to put the device back online (after offlining it). + * @offline: Called to put the device offline for hot-removal. May fail. + * * @suspend: Called when a device on this bus wants to go to sleep mode. * @resume: Called to bring a device on this bus out of sleep mode. * @pm: Power management operations of this bus, callback the specific @@ -105,6 +109,9 @@ struct bus_type { int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); + int (*online)(struct device *dev); + int (*offline)(struct device *dev); + int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); @@ -652,6 +659,9 @@ struct acpi_dev_node { * device (i.e. the bus driver that discovered the device). * @iommu_group: IOMMU group the device belongs to. * + * @offline_disabled: If set, the device is permanently online. + * @offline: Set after successful invocation of bus type's .offline(). + * * At the lowest level, every device in a Linux system is represented by an * instance of struct device. The device structure contains the information * that the device model core needs to model the system. Most subsystems, @@ -723,6 +733,9 @@ struct device { void (*release)(struct device *dev); struct iommu_group *iommu_group; + + bool offline_disabled:1; + bool offline:1; }; static inline struct device *kobj_to_dev(struct kobject *kobj) @@ -859,6 +872,15 @@ extern const char *device_get_devnode(struct device *dev, extern void *dev_get_drvdata(const struct device *dev); extern int dev_set_drvdata(struct device *dev, void *data); +static inline bool device_supports_offline(struct device *dev) +{ + return dev->bus && dev->bus->offline && dev->bus->online; +} + +extern void lock_device_hotplug(void); +extern void unlock_device_hotplug(void); +extern int device_offline(struct device *dev); +extern int device_online(struct device *dev); /* * Root device objects for grouping under /sys/devices */ diff --git a/include/linux/freezer.h b/include/linux/freezer.h index e70df40d84f6..7fd81b8c4897 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -3,6 +3,7 @@ #ifndef FREEZER_H_INCLUDED #define FREEZER_H_INCLUDED +#include <linux/debug_locks.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/atomic.h> @@ -46,7 +47,11 @@ extern int freeze_kernel_threads(void); extern void thaw_processes(void); extern void thaw_kernel_threads(void); -static inline bool try_to_freeze(void) +/* + * DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION + * If try_to_freeze causes a lockdep warning it means the caller may deadlock + */ +static inline bool try_to_freeze_unsafe(void) { might_sleep(); if (likely(!freezing(current))) @@ -54,6 +59,13 @@ static inline bool try_to_freeze(void) return __refrigerator(false); } +static inline bool try_to_freeze(void) +{ + if (!(current->flags & PF_NOFREEZE)) + debug_check_no_locks_held(); + return try_to_freeze_unsafe(); +} + extern bool freeze_task(struct task_struct *p); extern bool set_freezable(void); @@ -115,6 +127,14 @@ static inline void freezer_count(void) try_to_freeze(); } +/* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ +static inline void freezer_count_unsafe(void) +{ + current->flags &= ~PF_FREEZER_SKIP; + smp_mb(); + try_to_freeze_unsafe(); +} + /** * freezer_should_skip - whether to skip a task when determining frozen * state is reached @@ -139,28 +159,86 @@ static inline bool freezer_should_skip(struct task_struct *p) } /* - * These macros are intended to be used whenever you want allow a sleeping + * These functions are intended to be used whenever you want allow a sleeping * task to be frozen. Note that neither return any clear indication of * whether a freeze event happened while in this function. */ /* Like schedule(), but should not block the freezer. */ -#define freezable_schedule() \ -({ \ - freezer_do_not_count(); \ - schedule(); \ - freezer_count(); \ -}) +static inline void freezable_schedule(void) +{ + freezer_do_not_count(); + schedule(); + freezer_count(); +} + +/* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ +static inline void freezable_schedule_unsafe(void) +{ + freezer_do_not_count(); + schedule(); + freezer_count_unsafe(); +} + +/* + * Like freezable_schedule_timeout(), but should not block the freezer. Do not + * call this with locks held. + */ +static inline long freezable_schedule_timeout(long timeout) +{ + long __retval; + freezer_do_not_count(); + __retval = schedule_timeout(timeout); + freezer_count(); + return __retval; +} + +/* + * Like schedule_timeout_interruptible(), but should not block the freezer. Do not + * call this with locks held. + */ +static inline long freezable_schedule_timeout_interruptible(long timeout) +{ + long __retval; + freezer_do_not_count(); + __retval = schedule_timeout_interruptible(timeout); + freezer_count(); + return __retval; +} /* Like schedule_timeout_killable(), but should not block the freezer. */ -#define freezable_schedule_timeout_killable(timeout) \ -({ \ - long __retval; \ - freezer_do_not_count(); \ - __retval = schedule_timeout_killable(timeout); \ - freezer_count(); \ - __retval; \ -}) +static inline long freezable_schedule_timeout_killable(long timeout) +{ + long __retval; + freezer_do_not_count(); + __retval = schedule_timeout_killable(timeout); + freezer_count(); + return __retval; +} + +/* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ +static inline long freezable_schedule_timeout_killable_unsafe(long timeout) +{ + long __retval; + freezer_do_not_count(); + __retval = schedule_timeout_killable(timeout); + freezer_count_unsafe(); + return __retval; +} + +/* + * Like schedule_hrtimeout_range(), but should not block the freezer. Do not + * call this with locks held. + */ +static inline int freezable_schedule_hrtimeout_range(ktime_t *expires, + unsigned long delta, const enum hrtimer_mode mode) +{ + int __retval; + freezer_do_not_count(); + __retval = schedule_hrtimeout_range(expires, delta, mode); + freezer_count(); + return __retval; +} /* * Freezer-friendly wrappers around wait_event_interruptible(), @@ -177,33 +255,45 @@ static inline bool freezer_should_skip(struct task_struct *p) __retval; \ }) +/* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ +#define wait_event_freezekillable_unsafe(wq, condition) \ +({ \ + int __retval; \ + freezer_do_not_count(); \ + __retval = wait_event_killable(wq, (condition)); \ + freezer_count_unsafe(); \ + __retval; \ +}) + #define wait_event_freezable(wq, condition) \ ({ \ int __retval; \ - for (;;) { \ - __retval = wait_event_interruptible(wq, \ - (condition) || freezing(current)); \ - if (__retval || (condition)) \ - break; \ - try_to_freeze(); \ - } \ + freezer_do_not_count(); \ + __retval = wait_event_interruptible(wq, (condition)); \ + freezer_count(); \ __retval; \ }) #define wait_event_freezable_timeout(wq, condition, timeout) \ ({ \ long __retval = timeout; \ - for (;;) { \ - __retval = wait_event_interruptible_timeout(wq, \ - (condition) || freezing(current), \ - __retval); \ - if (__retval <= 0 || (condition)) \ - break; \ - try_to_freeze(); \ - } \ + freezer_do_not_count(); \ + __retval = wait_event_interruptible_timeout(wq, (condition), \ + __retval); \ + freezer_count(); \ __retval; \ }) +#define wait_event_freezable_exclusive(wq, condition) \ +({ \ + int __retval; \ + freezer_do_not_count(); \ + __retval = wait_event_interruptible_exclusive(wq, condition); \ + freezer_count(); \ + __retval; \ +}) + + #else /* !CONFIG_FREEZER */ static inline bool frozen(struct task_struct *p) { return false; } static inline bool freezing(struct task_struct *p) { return false; } @@ -225,18 +315,37 @@ static inline void set_freezable(void) {} #define freezable_schedule() schedule() +#define freezable_schedule_unsafe() schedule() + +#define freezable_schedule_timeout(timeout) schedule_timeout(timeout) + +#define freezable_schedule_timeout_interruptible(timeout) \ + schedule_timeout_interruptible(timeout) + #define freezable_schedule_timeout_killable(timeout) \ schedule_timeout_killable(timeout) +#define freezable_schedule_timeout_killable_unsafe(timeout) \ + schedule_timeout_killable(timeout) + +#define freezable_schedule_hrtimeout_range(expires, delta, mode) \ + schedule_hrtimeout_range(expires, delta, mode) + #define wait_event_freezable(wq, condition) \ wait_event_interruptible(wq, condition) #define wait_event_freezable_timeout(wq, condition, timeout) \ wait_event_interruptible_timeout(wq, condition, timeout) +#define wait_event_freezable_exclusive(wq, condition) \ + wait_event_interruptible_exclusive(wq, condition) + #define wait_event_freezekillable(wq, condition) \ wait_event_killable(wq, condition) +#define wait_event_freezekillable_unsafe(wq, condition) \ + wait_event_killable(wq, condition) + #endif /* !CONFIG_FREEZER */ #endif /* FREEZER_H_INCLUDED */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 8db53cfaccdb..a63d83ebd151 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -125,6 +125,7 @@ static inline bool is_error_page(struct page *page) #define KVM_REQ_MCLOCK_INPROGRESS 19 #define KVM_REQ_EPR_EXIT 20 #define KVM_REQ_SCAN_IOAPIC 21 +#define KVM_REQ_GLOBAL_CLOCK_UPDATE 22 #define KVM_USERSPACE_IRQ_SOURCE_ID 0 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1 @@ -145,7 +146,8 @@ struct kvm_io_range { #define NR_IOBUS_DEVS 1000 struct kvm_io_bus { - int dev_count; + int dev_count; + int ioeventfd_count; struct kvm_io_range range[]; }; diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 3e622c610925..dd38e62b84d2 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -234,6 +234,8 @@ static inline void unlock_memory_hotplug(void) {} extern int is_mem_section_removable(unsigned long pfn, unsigned long nr_pages); extern void try_offline_node(int nid); +extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages); +extern void remove_memory(int nid, u64 start, u64 size); #else static inline int is_mem_section_removable(unsigned long pfn, @@ -243,15 +245,23 @@ static inline int is_mem_section_removable(unsigned long pfn, } static inline void try_offline_node(int nid) {} + +static inline int offline_pages(unsigned long start_pfn, unsigned long nr_pages) +{ + return -EINVAL; +} + +static inline void remove_memory(int nid, u64 start, u64 size) {} #endif /* CONFIG_MEMORY_HOTREMOVE */ +extern int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, + void *arg, int (*func)(struct memory_block *, void *)); extern int mem_online_node(int nid); extern int add_memory(int nid, u64 start, u64 size); extern int arch_add_memory(int nid, u64 start, u64 size); extern int offline_pages(unsigned long start_pfn, unsigned long nr_pages); -extern int offline_memory_block(struct memory_block *mem); extern bool is_memblock_offlined(struct memory_block *mem); -extern int remove_memory(int nid, u64 start, u64 size); +extern void remove_memory(int nid, u64 start, u64 size); extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn, int nr_pages); extern void sparse_remove_one_section(struct zone *zone, struct mem_section *ms); diff --git a/include/linux/mfd/abx500/ab8500-sysctrl.h b/include/linux/mfd/abx500/ab8500-sysctrl.h index 990bc93f46e1..adba89d9c660 100644 --- a/include/linux/mfd/abx500/ab8500-sysctrl.h +++ b/include/linux/mfd/abx500/ab8500-sysctrl.h @@ -278,8 +278,8 @@ struct ab8500_sysctrl_platform_data { #define AB9540_SYSCLK12CONFCTRL_PLL26TO38ENA BIT(0) #define AB9540_SYSCLK12CONFCTRL_SYSCLK12USBMUXSEL BIT(1) -#define AB9540_SYSCLK12CONFCTRL_INT384MHZMUXSEL_MASK 0x0C -#define AB9540_SYSCLK12CONFCTRL_INT384MHZMUXSEL_SHIFT 2 +#define AB9540_SYSCLK12CONFCTRL_INT384MHZMUXSEL0 BIT(2) +#define AB9540_SYSCLK12CONFCTRL_INT384MHZMUXSEL1 BIT(3) #define AB9540_SYSCLK12CONFCTRL_SYSCLK12BUFMUX BIT(4) #define AB9540_SYSCLK12CONFCTRL_SYSCLK12PLLMUX BIT(5) #define AB9540_SYSCLK12CONFCTRL_SYSCLK2MUXVALID BIT(6) diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 689e6a0d9c99..ca0790fba2f5 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -134,6 +134,11 @@ enum prcmu_clock { PRCMU_SIACLK, PRCMU_SVACLK, PRCMU_ACLK, + PRCMU_HVACLK, /* Ux540 only */ + PRCMU_G1CLK, /* Ux540 only */ + PRCMU_SDMMCHCLK, + PRCMU_CAMCLK, + PRCMU_BML8580CLK, PRCMU_NUM_REG_CLOCKS, PRCMU_SYSCLK = PRCMU_NUM_REG_CLOCKS, PRCMU_CDCLK, @@ -148,6 +153,13 @@ enum prcmu_clock { PRCMU_DSI0ESCCLK, PRCMU_DSI1ESCCLK, PRCMU_DSI2ESCCLK, + /* LCD DSI PLL - Ux540 only */ + PRCMU_PLLDSI_LCD, + PRCMU_DSI0CLK_LCD, + PRCMU_DSI1CLK_LCD, + PRCMU_DSI0ESCCLK_LCD, + PRCMU_DSI1ESCCLK_LCD, + PRCMU_DSI2ESCCLK_LCD, }; /** diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 1aa4f13cdfa6..244fb0d51589 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -85,6 +85,19 @@ enum max77693_pmic_reg { MAX77693_PMIC_REG_END, }; +/* MAX77693 CHG_CNFG_00 register */ +#define CHG_CNFG_00_CHG_MASK 0x1 +#define CHG_CNFG_00_BUCK_MASK 0x4 + +/* MAX77693 CHG_CNFG_09 Register */ +#define CHG_CNFG_09_CHGIN_ILIM_MASK 0x7F + +/* MAX77693 CHG_CTRL Register */ +#define SAFEOUT_CTRL_SAFEOUT1_MASK 0x3 +#define SAFEOUT_CTRL_SAFEOUT2_MASK 0xC +#define SAFEOUT_CTRL_ENSAFEOUT1_MASK 0x40 +#define SAFEOUT_CTRL_ENSAFEOUT2_MASK 0x80 + /* Slave addr = 0x4A: MUIC */ enum max77693_muic_reg { MAX77693_MUIC_REG_ID = 0x00, diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h index 3109a6c5c948..676f0f388992 100644 --- a/include/linux/mfd/max77693.h +++ b/include/linux/mfd/max77693.h @@ -30,6 +30,20 @@ #ifndef __LINUX_MFD_MAX77693_H #define __LINUX_MFD_MAX77693_H +/* MAX77686 regulator IDs */ +enum max77693_regulators { + MAX77693_ESAFEOUT1 = 0, + MAX77693_ESAFEOUT2, + MAX77693_CHARGER, + MAX77693_REG_MAX, +}; + +struct max77693_regulator_data { + int id; + struct regulator_init_data *initdata; + struct device_node *of_node; +}; + struct max77693_reg_data { u8 addr; u8 data; @@ -52,6 +66,10 @@ struct max77693_muic_platform_data { struct max77693_platform_data { int wakeup; + /* regulator data */ + struct max77693_regulator_data *regulators; + int num_regulators; + /* muic data */ struct max77693_muic_platform_data *muic_data; }; diff --git a/include/linux/msi.h b/include/linux/msi.h index 20c2d6dd5d25..ee66f3a12fb6 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -35,6 +35,7 @@ struct msi_desc { u32 masked; /* mask bits */ unsigned int irq; + unsigned int nvec_used; /* number of messages */ struct list_head list; union { diff --git a/include/linux/pci.h b/include/linux/pci.h index 3a24e4ff3248..0fd1f1582fa1 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -364,7 +364,8 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev) return dev; } -struct pci_dev *alloc_pci_dev(void); +struct pci_dev *pci_alloc_dev(struct pci_bus *bus); +struct pci_dev * __deprecated alloc_pci_dev(void); #define to_pci_dev(n) container_of(n, struct pci_dev, dev) #define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) @@ -1018,6 +1019,8 @@ int pci_request_selected_regions_exclusive(struct pci_dev *, int, const char *); void pci_release_selected_regions(struct pci_dev *, int); /* drivers/pci/bus.c */ +struct pci_bus *pci_bus_get(struct pci_bus *bus); +void pci_bus_put(struct pci_bus *bus); void pci_add_resource(struct list_head *resources, struct resource *res); void pci_add_resource_offset(struct list_head *resources, struct resource *res, resource_size_t offset); @@ -1643,6 +1646,7 @@ void pcibios_set_master(struct pci_dev *dev); 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); #ifdef CONFIG_PCI_MMCONFIG void __init pci_mmcfg_early_init(void); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index c12916248469..3bed2e89611b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -556,7 +556,6 @@ #define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450 #define PCI_DEVICE_ID_AMD_8131_APIC 0x7451 #define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458 -#define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b #define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F #define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090 #define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091 @@ -568,8 +567,9 @@ #define PCI_DEVICE_ID_AMD_CS5536_IDE 0x209A #define PCI_DEVICE_ID_AMD_LX_VIDEO 0x2081 #define PCI_DEVICE_ID_AMD_LX_AES 0x2082 -#define PCI_DEVICE_ID_AMD_HUDSON2_IDE 0x780c #define PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE 0x7800 +#define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b +#define PCI_DEVICE_ID_AMD_HUDSON2_IDE 0x780c #define PCI_VENDOR_ID_TRIDENT 0x1023 #define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 @@ -2476,6 +2476,9 @@ #define PCI_VENDOR_ID_ASMEDIA 0x1b21 +#define PCI_VENDOR_ID_CIRCUITCO 0x1cc8 +#define PCI_SUBSYSTEM_ID_CIRCUITCO_MINNOWBOARD 0x0001 + #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 4aad3cea69ae..18eccefea06e 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -40,6 +40,25 @@ extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s); extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev); extern void devm_pinctrl_put(struct pinctrl *p); +#ifdef CONFIG_PM +extern int pinctrl_pm_select_default_state(struct device *dev); +extern int pinctrl_pm_select_sleep_state(struct device *dev); +extern int pinctrl_pm_select_idle_state(struct device *dev); +#else +static inline int pinctrl_pm_select_default_state(struct device *dev) +{ + return 0; +} +static inline int pinctrl_pm_select_sleep_state(struct device *dev) +{ + return 0; +} +static inline int pinctrl_pm_select_idle_state(struct device *dev) +{ + return 0; +} +#endif + #else /* !CONFIG_PINCTRL */ static inline int pinctrl_request_gpio(unsigned gpio) @@ -92,6 +111,21 @@ static inline void devm_pinctrl_put(struct pinctrl *p) { } +static inline int pinctrl_pm_select_default_state(struct device *dev) +{ + return 0; +} + +static inline int pinctrl_pm_select_sleep_state(struct device *dev) +{ + return 0; +} + +static inline int pinctrl_pm_select_idle_state(struct device *dev) +{ + return 0; +} + #endif /* CONFIG_PINCTRL */ static inline struct pinctrl * __must_check pinctrl_get_select( @@ -158,47 +192,4 @@ static inline struct pinctrl * __must_check devm_pinctrl_get_select_default( return devm_pinctrl_get_select(dev, PINCTRL_STATE_DEFAULT); } -#ifdef CONFIG_PINCONF - -extern int pin_config_get(const char *dev_name, const char *name, - unsigned long *config); -extern int pin_config_set(const char *dev_name, const char *name, - unsigned long config); -extern int pin_config_group_get(const char *dev_name, - const char *pin_group, - unsigned long *config); -extern int pin_config_group_set(const char *dev_name, - const char *pin_group, - unsigned long config); - -#else - -static inline int pin_config_get(const char *dev_name, const char *name, - unsigned long *config) -{ - return 0; -} - -static inline int pin_config_set(const char *dev_name, const char *name, - unsigned long config) -{ - return 0; -} - -static inline int pin_config_group_get(const char *dev_name, - const char *pin_group, - unsigned long *config) -{ - return 0; -} - -static inline int pin_config_group_set(const char *dev_name, - const char *pin_group, - unsigned long config) -{ - return 0; -} - -#endif - #endif /* __LINUX_PINCTRL_CONSUMER_H */ diff --git a/include/linux/pinctrl/devinfo.h b/include/linux/pinctrl/devinfo.h index 6e5f8a985ea7..281cb91ddcf5 100644 --- a/include/linux/pinctrl/devinfo.h +++ b/include/linux/pinctrl/devinfo.h @@ -28,6 +28,10 @@ struct dev_pin_info { struct pinctrl *p; struct pinctrl_state *default_state; +#ifdef CONFIG_PM + struct pinctrl_state *sleep_state; + struct pinctrl_state *idle_state; +#endif }; extern int pinctrl_bind_pins(struct device *dev); diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index 6aa238096622..bf7e989abcb5 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -29,12 +29,25 @@ * if for example some other pin is going to drive the signal connected * to it for a while. Pins used for input are usually always high * impedance. + * @PIN_CONFIG_BIAS_BUS_HOLD: the pin will be set to weakly latch so that it + * weakly drives the last value on a tristate bus, also known as a "bus + * holder", "bus keeper" or "repeater". This allows another device on the + * bus to change the value by driving the bus high or low and switching to + * tristate. The argument is ignored. * @PIN_CONFIG_BIAS_PULL_UP: the pin will be pulled up (usually with high * impedance to VDD). If the argument is != 0 pull-up is enabled, - * if it is 0, pull-up is disabled. + * if it is 0, pull-up is total, i.e. the pin is connected to VDD. * @PIN_CONFIG_BIAS_PULL_DOWN: the pin will be pulled down (usually with high * impedance to GROUND). If the argument is != 0 pull-down is enabled, - * if it is 0, pull-down is disabled. + * if it is 0, pull-down is total, i.e. the pin is connected to GROUND. + * @PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: the pin will be pulled up or down based + * on embedded knowledge of the controller hardware, like current mux + * function. The pull direction and possibly strength too will normally + * be decided completely inside the hardware block and not be readable + * from the kernel side. + * If the argument is != 0 pull up/down is enabled, if it is 0, the + * configuration is ignored. The proper way to disable it is to use + * @PIN_CONFIG_BIAS_DISABLE. * @PIN_CONFIG_DRIVE_PUSH_PULL: the pin will be driven actively high and * low, this is the most typical case and is typically achieved with two * active transistors on the output. Setting this config will enable @@ -57,14 +70,14 @@ * setting pins to this mode. * @PIN_CONFIG_INPUT_DEBOUNCE: this will configure the pin to debounce mode, * which means it will wait for signals to settle when reading inputs. The - * argument gives the debounce time on a custom format. Setting the + * argument gives the debounce time in usecs. Setting the * argument to zero turns debouncing off. * @PIN_CONFIG_POWER_SOURCE: if the pin can select between different power * supplies, the argument to this parameter (on a custom format) tells * the driver which alternative power source to use. * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to - * this parameter (on a custom format) tells the driver which alternative - * slew rate to use. + * this parameter (on a custom format) tells the driver which alternative + * slew rate to use. * @PIN_CONFIG_LOW_POWER_MODE: this will configure the pin for low power * operation, if several modes of operation are supported these can be * passed in the argument on a custom form, else just use argument 1 @@ -78,8 +91,10 @@ enum pin_config_param { PIN_CONFIG_BIAS_DISABLE, PIN_CONFIG_BIAS_HIGH_IMPEDANCE, + PIN_CONFIG_BIAS_BUS_HOLD, PIN_CONFIG_BIAS_PULL_UP, PIN_CONFIG_BIAS_PULL_DOWN, + PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, PIN_CONFIG_DRIVE_PUSH_PULL, PIN_CONFIG_DRIVE_OPEN_DRAIN, PIN_CONFIG_DRIVE_OPEN_SOURCE, diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h index 1ad4f31ef6b8..f6998692bdc9 100644 --- a/include/linux/pinctrl/pinconf.h +++ b/include/linux/pinctrl/pinconf.h @@ -30,7 +30,7 @@ struct seq_file; * @pin_config_set: configure an individual pin * @pin_config_group_get: get configurations for an entire pin group * @pin_config_group_set: configure all pins in a group - * @pin_config_group_dbg_set: optional debugfs to modify a pin configuration + * @pin_config_dbg_parse_modify: optional debugfs to modify a pin configuration * @pin_config_dbg_show: optional debugfs display hook that will provide * per-device info for a certain pin in debugfs * @pin_config_group_dbg_show: optional debugfs display hook that will provide diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 2c2a9e8d8578..5979147d2bda 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -49,7 +49,8 @@ struct pinctrl_pin_desc { * @name: a name for the chip in this range * @id: an ID number for the chip in this range * @base: base offset of the GPIO range - * @pin_base: base pin number of the GPIO range + * @pin_base: base pin number of the GPIO range if pins == NULL + * @pins: enumeration of pins in GPIO range or NULL * @npins: number of pins in the GPIO range, including the base number * @gc: an optional pointer to a gpio_chip */ @@ -59,6 +60,7 @@ struct pinctrl_gpio_range { unsigned int id; unsigned int base; unsigned int pin_base; + unsigned const *pins; unsigned int npins; struct gpio_chip *gc; }; diff --git a/include/linux/platform_data/clk-ux500.h b/include/linux/platform_data/clk-ux500.h index 320d9c39ea0a..9d98f3aaa16c 100644 --- a/include/linux/platform_data/clk-ux500.h +++ b/include/linux/platform_data/clk-ux500.h @@ -12,7 +12,9 @@ void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, u32 clkrst5_base, u32 clkrst6_base); -void u9540_clk_init(void); -void u8540_clk_init(void); +void u9540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base); +void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base); #endif /* __CLK_UX500_H */ diff --git a/include/linux/platform_data/si5351.h b/include/linux/platform_data/si5351.h index 92dabcaf6499..54334393ab92 100644 --- a/include/linux/platform_data/si5351.h +++ b/include/linux/platform_data/si5351.h @@ -79,6 +79,23 @@ enum si5351_drive_strength { }; /** + * enum si5351_disable_state - Si5351 clock output disable state + * @SI5351_DISABLE_DEFAULT: default, do not change eeprom config + * @SI5351_DISABLE_LOW: CLKx is set to a LOW state when disabled + * @SI5351_DISABLE_HIGH: CLKx is set to a HIGH state when disabled + * @SI5351_DISABLE_FLOATING: CLKx is set to a FLOATING state when + * disabled + * @SI5351_DISABLE_NEVER: CLKx is NEVER disabled + */ +enum si5351_disable_state { + SI5351_DISABLE_DEFAULT = 0, + SI5351_DISABLE_LOW, + SI5351_DISABLE_HIGH, + SI5351_DISABLE_FLOATING, + SI5351_DISABLE_NEVER, +}; + +/** * struct si5351_clkout_config - Si5351 clock output configuration * @clkout: clkout number * @multisynth_src: multisynth source clock @@ -91,6 +108,7 @@ struct si5351_clkout_config { enum si5351_multisynth_src multisynth_src; enum si5351_clkout_src clkout_src; enum si5351_drive_strength drive; + enum si5351_disable_state disable_state; bool pll_master; unsigned long rate; }; diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 7d7e09efff9b..6fa7cea25da9 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -37,7 +37,6 @@ extern void pm_runtime_enable(struct device *dev); extern void __pm_runtime_disable(struct device *dev, bool check_resume); extern void pm_runtime_allow(struct device *dev); extern void pm_runtime_forbid(struct device *dev); -extern int pm_generic_runtime_idle(struct device *dev); extern int pm_generic_runtime_suspend(struct device *dev); extern int pm_generic_runtime_resume(struct device *dev); extern void pm_runtime_no_callbacks(struct device *dev); @@ -143,7 +142,6 @@ static inline bool pm_runtime_active(struct device *dev) { return true; } static inline bool pm_runtime_status_suspended(struct device *dev) { return false; } static inline bool pm_runtime_enabled(struct device *dev) { return false; } -static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } static inline void pm_runtime_no_callbacks(struct device *dev) {} diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index c0f44c2b006d..d8b187c3925d 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -299,11 +299,11 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm); void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data); /* Smartreflex driver hooks to be called from Smartreflex class driver */ -int sr_enable(struct voltagedomain *voltdm, unsigned long volt); -void sr_disable(struct voltagedomain *voltdm); -int sr_configure_errgen(struct voltagedomain *voltdm); -int sr_disable_errgen(struct voltagedomain *voltdm); -int sr_configure_minmax(struct voltagedomain *voltdm); +int sr_enable(struct omap_sr *sr, unsigned long volt); +void sr_disable(struct omap_sr *sr); +int sr_configure_errgen(struct omap_sr *sr); +int sr_disable_errgen(struct omap_sr *sr); +int sr_configure_minmax(struct omap_sr *sr); /* API to register the smartreflex class driver with the smartreflex driver */ int sr_register_class(struct omap_sr_class_data *class_data); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 02d84e24b7c2..75981d0b57dc 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -23,6 +23,7 @@ struct irq_domain; struct spi_device; struct regmap; struct regmap_range_cfg; +struct regmap_field; /* An enum of all the supported cache types */ enum regcache_type { @@ -394,10 +395,15 @@ bool regmap_can_raw_write(struct regmap *map); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, unsigned int max); +int regcache_drop_region(struct regmap *map, unsigned int min, + unsigned int max); void regcache_cache_only(struct regmap *map, bool enable); void regcache_cache_bypass(struct regmap *map, bool enable); void regcache_mark_dirty(struct regmap *map); +bool regmap_check_range_table(struct regmap *map, unsigned int reg, + const struct regmap_access_table *table); + int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs); @@ -412,6 +418,36 @@ bool regmap_reg_in_ranges(unsigned int reg, unsigned int nranges); /** + * Description of an register field + * + * @reg: Offset of the register within the regmap bank + * @lsb: lsb of the register field. + * @reg: msb of the register field. + */ +struct reg_field { + unsigned int reg; + unsigned int lsb; + unsigned int msb; +}; + +#define REG_FIELD(_reg, _lsb, _msb) { \ + .reg = _reg, \ + .lsb = _lsb, \ + .msb = _msb, \ + } + +struct regmap_field *regmap_field_alloc(struct regmap *regmap, + struct reg_field reg_field); +void regmap_field_free(struct regmap_field *field); + +struct regmap_field *devm_regmap_field_alloc(struct device *dev, + struct regmap *regmap, struct reg_field reg_field); +void devm_regmap_field_free(struct device *dev, struct regmap_field *field); + +int regmap_field_read(struct regmap_field *field, unsigned int *val); +int regmap_field_write(struct regmap_field *field, unsigned int val); + +/** * Description of an IRQ for the generic regmap irq_chip. * * @reg_offset: Offset of the status/mask register within the bank @@ -562,6 +598,13 @@ static inline int regcache_sync_region(struct regmap *map, unsigned int min, return -EINVAL; } +static inline int regcache_drop_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline void regcache_cache_only(struct regmap *map, bool enable) { WARN_ONCE(1, "regmap API is disabled"); diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h index 7c5ff0c55773..75307447cef9 100644 --- a/include/linux/regulator/ab8500.h +++ b/include/linux/regulator/ab8500.h @@ -336,8 +336,4 @@ static inline int ab8500_regulator_debug_exit(struct platform_device *pdev) } #endif -/* AB8500 external regulator functions. */ -int ab8500_ext_regulator_init(struct platform_device *pdev); -void ab8500_ext_regulator_exit(struct platform_device *pdev); - #endif diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 145022a83085..3a76389c6aaa 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -165,6 +165,7 @@ int regulator_count_voltages(struct regulator *regulator); int regulator_list_voltage(struct regulator *regulator, unsigned selector); int regulator_is_supported_voltage(struct regulator *regulator, int min_uV, int max_uV); +unsigned int regulator_get_linear_step(struct regulator *regulator); int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV); int regulator_set_voltage_time(struct regulator *regulator, int old_uV, int new_uV); diff --git a/include/linux/security.h b/include/linux/security.h index 4686491852a7..40560f41e3d5 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1392,7 +1392,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @ctxlen contains the length of @ctx. * * @inode_getsecctx: - * Returns a string containing all relevant security context information + * On success, returns 0 and fills out @ctx and @ctxlen with the security + * context for the given @inode. * * @inode we wish to get the security context of. * @ctx is a pointer in which to place the allocated security context. diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 6ff26c8db7b9..28e440be1c07 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -308,6 +308,9 @@ struct spi_master { /* bitmask of supported bits_per_word for transfers */ u32 bits_per_word_mask; +#define SPI_BPW_MASK(bits) BIT((bits) - 1) +#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0UL : (BIT(bits) - 1)) +#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1)) /* other constraints relevant to this driver */ u16 flags; diff --git a/include/linux/spi/xilinx_spi.h b/include/linux/spi/xilinx_spi.h index 6f17278810b0..333ecdfee0d9 100644 --- a/include/linux/spi/xilinx_spi.h +++ b/include/linux/spi/xilinx_spi.h @@ -11,7 +11,6 @@ */ struct xspi_platform_data { u16 num_chipselect; - bool little_endian; u8 bits_per_word; struct spi_board_info *devices; u8 num_devices; diff --git a/include/linux/suspend.h b/include/linux/suspend.h index d4e3f16d5e89..f73cabf59012 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -363,6 +363,7 @@ extern bool pm_wakeup_pending(void); extern bool pm_get_wakeup_count(unsigned int *count, bool block); extern bool pm_save_wakeup_count(unsigned int count); extern void pm_wakep_autosleep_enabled(bool set); +extern void pm_print_active_wakeup_sources(void); static inline void lock_system_sleep(void) { diff --git a/include/linux/tpm.h b/include/linux/tpm.h index fcb627ff8d3e..9a9051bb1a03 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -22,6 +22,8 @@ #ifndef __LINUX_TPM_H__ #define __LINUX_TPM_H__ +#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */ + /* * Chip num is this value or a valid tpm idx */ diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 9ff8645b7e0b..e94c75ded111 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -34,13 +34,6 @@ struct virtqueue { void *priv; }; -int virtqueue_add_buf(struct virtqueue *vq, - struct scatterlist sg[], - unsigned int out_num, - unsigned int in_num, - void *data, - gfp_t gfp); - int virtqueue_add_outbuf(struct virtqueue *vq, struct scatterlist sg[], unsigned int num, void *data, diff --git a/include/trace/events/power.h b/include/trace/events/power.h index 427acab5d69a..8e42410bd159 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -5,6 +5,7 @@ #define _TRACE_POWER_H #include <linux/ktime.h> +#include <linux/pm_qos.h> #include <linux/tracepoint.h> DECLARE_EVENT_CLASS(cpu, @@ -177,6 +178,178 @@ DEFINE_EVENT(power_domain, power_domain_target, TP_ARGS(name, state, cpu_id) ); + +/* + * The pm qos events are used for pm qos update + */ +DECLARE_EVENT_CLASS(pm_qos_request, + + TP_PROTO(int pm_qos_class, s32 value), + + TP_ARGS(pm_qos_class, value), + + TP_STRUCT__entry( + __field( int, pm_qos_class ) + __field( s32, value ) + ), + + TP_fast_assign( + __entry->pm_qos_class = pm_qos_class; + __entry->value = value; + ), + + TP_printk("pm_qos_class=%s value=%d", + __print_symbolic(__entry->pm_qos_class, + { PM_QOS_CPU_DMA_LATENCY, "CPU_DMA_LATENCY" }, + { PM_QOS_NETWORK_LATENCY, "NETWORK_LATENCY" }, + { PM_QOS_NETWORK_THROUGHPUT, "NETWORK_THROUGHPUT" }), + __entry->value) +); + +DEFINE_EVENT(pm_qos_request, pm_qos_add_request, + + TP_PROTO(int pm_qos_class, s32 value), + + TP_ARGS(pm_qos_class, value) +); + +DEFINE_EVENT(pm_qos_request, pm_qos_update_request, + + TP_PROTO(int pm_qos_class, s32 value), + + TP_ARGS(pm_qos_class, value) +); + +DEFINE_EVENT(pm_qos_request, pm_qos_remove_request, + + TP_PROTO(int pm_qos_class, s32 value), + + TP_ARGS(pm_qos_class, value) +); + +TRACE_EVENT(pm_qos_update_request_timeout, + + TP_PROTO(int pm_qos_class, s32 value, unsigned long timeout_us), + + TP_ARGS(pm_qos_class, value, timeout_us), + + TP_STRUCT__entry( + __field( int, pm_qos_class ) + __field( s32, value ) + __field( unsigned long, timeout_us ) + ), + + TP_fast_assign( + __entry->pm_qos_class = pm_qos_class; + __entry->value = value; + __entry->timeout_us = timeout_us; + ), + + TP_printk("pm_qos_class=%s value=%d, timeout_us=%ld", + __print_symbolic(__entry->pm_qos_class, + { PM_QOS_CPU_DMA_LATENCY, "CPU_DMA_LATENCY" }, + { PM_QOS_NETWORK_LATENCY, "NETWORK_LATENCY" }, + { PM_QOS_NETWORK_THROUGHPUT, "NETWORK_THROUGHPUT" }), + __entry->value, __entry->timeout_us) +); + +DECLARE_EVENT_CLASS(pm_qos_update, + + TP_PROTO(enum pm_qos_req_action action, int prev_value, int curr_value), + + TP_ARGS(action, prev_value, curr_value), + + TP_STRUCT__entry( + __field( enum pm_qos_req_action, action ) + __field( int, prev_value ) + __field( int, curr_value ) + ), + + TP_fast_assign( + __entry->action = action; + __entry->prev_value = prev_value; + __entry->curr_value = curr_value; + ), + + TP_printk("action=%s prev_value=%d curr_value=%d", + __print_symbolic(__entry->action, + { PM_QOS_ADD_REQ, "ADD_REQ" }, + { PM_QOS_UPDATE_REQ, "UPDATE_REQ" }, + { PM_QOS_REMOVE_REQ, "REMOVE_REQ" }), + __entry->prev_value, __entry->curr_value) +); + +DEFINE_EVENT(pm_qos_update, pm_qos_update_target, + + TP_PROTO(enum pm_qos_req_action action, int prev_value, int curr_value), + + TP_ARGS(action, prev_value, curr_value) +); + +DEFINE_EVENT_PRINT(pm_qos_update, pm_qos_update_flags, + + TP_PROTO(enum pm_qos_req_action action, int prev_value, int curr_value), + + TP_ARGS(action, prev_value, curr_value), + + TP_printk("action=%s prev_value=0x%x curr_value=0x%x", + __print_symbolic(__entry->action, + { PM_QOS_ADD_REQ, "ADD_REQ" }, + { PM_QOS_UPDATE_REQ, "UPDATE_REQ" }, + { PM_QOS_REMOVE_REQ, "REMOVE_REQ" }), + __entry->prev_value, __entry->curr_value) +); + +DECLARE_EVENT_CLASS(dev_pm_qos_request, + + TP_PROTO(const char *name, enum dev_pm_qos_req_type type, + s32 new_value), + + TP_ARGS(name, type, new_value), + + TP_STRUCT__entry( + __string( name, name ) + __field( enum dev_pm_qos_req_type, type ) + __field( s32, new_value ) + ), + + TP_fast_assign( + __assign_str(name, name); + __entry->type = type; + __entry->new_value = new_value; + ), + + TP_printk("device=%s type=%s new_value=%d", + __get_str(name), + __print_symbolic(__entry->type, + { DEV_PM_QOS_LATENCY, "DEV_PM_QOS_LATENCY" }, + { DEV_PM_QOS_FLAGS, "DEV_PM_QOS_FLAGS" }), + __entry->new_value) +); + +DEFINE_EVENT(dev_pm_qos_request, dev_pm_qos_add_request, + + TP_PROTO(const char *name, enum dev_pm_qos_req_type type, + s32 new_value), + + TP_ARGS(name, type, new_value) +); + +DEFINE_EVENT(dev_pm_qos_request, dev_pm_qos_update_request, + + TP_PROTO(const char *name, enum dev_pm_qos_req_type type, + s32 new_value), + + TP_ARGS(name, type, new_value) +); + +DEFINE_EVENT(dev_pm_qos_request, dev_pm_qos_remove_request, + + TP_PROTO(const char *name, enum dev_pm_qos_req_type type, + s32 new_value), + + TP_ARGS(name, type, new_value) +); #endif /* _TRACE_POWER_H */ /* This part must be outside protection */ diff --git a/include/trace/events/regmap.h b/include/trace/events/regmap.h index a43a2f67bd8e..23d561512f64 100644 --- a/include/trace/events/regmap.h +++ b/include/trace/events/regmap.h @@ -223,6 +223,29 @@ DEFINE_EVENT(regmap_async, regmap_async_complete_done, ); +TRACE_EVENT(regcache_drop_region, + + TP_PROTO(struct device *dev, unsigned int from, + unsigned int to), + + TP_ARGS(dev, from, to), + + TP_STRUCT__entry( + __string( name, dev_name(dev) ) + __field( unsigned int, from ) + __field( unsigned int, to ) + ), + + TP_fast_assign( + __assign_str(name, dev_name(dev)); + __entry->from = from; + __entry->to = to; + ), + + TP_printk("%s %u-%u", __get_str(name), (unsigned int)__entry->from, + (unsigned int)__entry->to) +); + #endif /* _TRACE_REGMAP_H */ /* This part must be outside protection */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 864e324da80d..c3cc01d474b0 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -468,7 +468,7 @@ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ #define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */ -#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* Clock Power Management */ #define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */ #define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ #define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */ diff --git a/include/uapi/linux/virtio_console.h b/include/uapi/linux/virtio_console.h index c312f16bc4e7..ba260dd0b33a 100644 --- a/include/uapi/linux/virtio_console.h +++ b/include/uapi/linux/virtio_console.h @@ -38,6 +38,7 @@ /* Feature bits */ #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ #define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ +#define VIRTIO_CONSOLE_F_EMERG_WRITE 2 /* Does host support emergency write? */ #define VIRTIO_CONSOLE_BAD_ID (~(__u32)0) @@ -48,6 +49,8 @@ struct virtio_console_config { __u16 rows; /* max. number of ports this device can hold */ __u32 max_nr_ports; + /* emergency write register */ + __u32 emerg_wr; } __attribute__((packed)); /* diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index ea66f3f60d63..e5ec1caab82a 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -80,7 +80,9 @@ /* The remaining space is defined by each driver as the per-driver * configuration space */ -#define VIRTIO_PCI_CONFIG(dev) ((dev)->msix_enabled ? 24 : 20) +#define VIRTIO_PCI_CONFIG_OFF(msix_enabled) ((msix_enabled) ? 24 : 20) +/* Deprecated: please use VIRTIO_PCI_CONFIG_OFF instead */ +#define VIRTIO_PCI_CONFIG(dev) VIRTIO_PCI_CONFIG_OFF((dev)->msix_enabled) /* Virtio ABI version, this must match exactly */ #define VIRTIO_PCI_ABI_VERSION 0 diff --git a/include/xen/acpi.h b/include/xen/acpi.h index 68d73d09b770..46aa3d1c1654 100644 --- a/include/xen/acpi.h +++ b/include/xen/acpi.h @@ -78,11 +78,25 @@ static inline int xen_acpi_get_pxm(acpi_handle h) int xen_acpi_notify_hypervisor_state(u8 sleep_state, u32 pm1a_cnt, u32 pm1b_cnd); +static inline int xen_acpi_suspend_lowlevel(void) +{ + /* + * Xen will save and restore CPU context, so + * we can skip that and just go straight to + * the suspend. + */ + acpi_enter_sleep_state(ACPI_STATE_S3); + return 0; +} + static inline void xen_acpi_sleep_register(void) { - if (xen_initial_domain()) + if (xen_initial_domain()) { acpi_os_set_prepare_sleep( &xen_acpi_notify_hypervisor_state); + + acpi_suspend_lowlevel = xen_acpi_suspend_lowlevel; + } } #else static inline void xen_acpi_sleep_register(void) diff --git a/include/xen/hvm.h b/include/xen/hvm.h index 13e43e41637d..63917a8de3b0 100644 --- a/include/xen/hvm.h +++ b/include/xen/hvm.h @@ -44,8 +44,8 @@ static inline int hvm_get_parameter(int idx, uint64_t *value) xhv.index = idx; r = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv); if (r < 0) { - printk(KERN_ERR "Cannot get hvm parameter %s (%d): %d!\n", - param_name(idx), idx, r); + pr_err("Cannot get hvm parameter %s (%d): %d!\n", + param_name(idx), idx, r); return r; } *value = xhv.value; diff --git a/kernel/exit.c b/kernel/exit.c index 3a77cd9390a1..fafe75d9e6f6 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -824,7 +824,7 @@ void do_exit(long code) /* * Make sure we are holding no locks: */ - debug_check_no_locks_held(tsk); + debug_check_no_locks_held(); /* * We can do this unlocked here. The futex code uses this flag * just to verify whether the pi state cleanup has been done diff --git a/kernel/freezer.c b/kernel/freezer.c index c38893b0efba..8b2afc1c9df0 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c @@ -110,6 +110,18 @@ bool freeze_task(struct task_struct *p) { unsigned long flags; + /* + * This check can race with freezer_do_not_count, but worst case that + * will result in an extra wakeup being sent to the task. It does not + * race with freezer_count(), the barriers in freezer_count() and + * freezer_should_skip() ensure that either freezer_count() sees + * freezing == true in try_to_freeze() and freezes, or + * freezer_should_skip() sees !PF_FREEZE_SKIP and freezes the task + * normally. + */ + if (freezer_should_skip(p)) + return false; + spin_lock_irqsave(&freezer_lock, flags); if (!freezing(p) || frozen(p)) { spin_unlock_irqrestore(&freezer_lock, flags); diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index fd4b13b131f8..3ee4d06c6fc2 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -47,6 +47,7 @@ #include <linux/sched/sysctl.h> #include <linux/sched/rt.h> #include <linux/timer.h> +#include <linux/freezer.h> #include <asm/uaccess.h> @@ -1545,7 +1546,7 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod t->task = NULL; if (likely(t->task)) - schedule(); + freezable_schedule(); hrtimer_cancel(&t->timer); mode = HRTIMER_MODE_ABS; diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 1f3186b37fd5..e16c45b9ee77 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -4090,7 +4090,7 @@ void debug_check_no_locks_freed(const void *mem_from, unsigned long mem_len) } EXPORT_SYMBOL_GPL(debug_check_no_locks_freed); -static void print_held_locks_bug(struct task_struct *curr) +static void print_held_locks_bug(void) { if (!debug_locks_off()) return; @@ -4099,22 +4099,21 @@ static void print_held_locks_bug(struct task_struct *curr) printk("\n"); printk("=====================================\n"); - printk("[ BUG: lock held at task exit time! ]\n"); + printk("[ BUG: %s/%d still has locks held! ]\n", + current->comm, task_pid_nr(current)); print_kernel_ident(); printk("-------------------------------------\n"); - printk("%s/%d is exiting with locks still held!\n", - curr->comm, task_pid_nr(curr)); - lockdep_print_held_locks(curr); - + lockdep_print_held_locks(current); printk("\nstack backtrace:\n"); dump_stack(); } -void debug_check_no_locks_held(struct task_struct *task) +void debug_check_no_locks_held(void) { - if (unlikely(task->lockdep_depth > 0)) - print_held_locks_bug(task); + if (unlikely(current->lockdep_depth > 0)) + print_held_locks_bug(); } +EXPORT_SYMBOL_GPL(debug_check_no_locks_held); void debug_show_all_locks(void) { diff --git a/kernel/power/main.c b/kernel/power/main.c index d77663bfedeb..1d1bf630e6e9 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -424,6 +424,8 @@ static ssize_t wakeup_count_store(struct kobject *kobj, if (sscanf(buf, "%u", &val) == 1) { if (pm_save_wakeup_count(val)) error = n; + else + pm_print_active_wakeup_sources(); } out: @@ -528,6 +530,10 @@ pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, if (sscanf(buf, "%d", &val) == 1) { pm_trace_enabled = !!val; + if (pm_trace_enabled) { + pr_warn("PM: Enabling pm_trace changes system date and time during resume.\n" + "PM: Correct system time has to be restored manually after resume.\n"); + } return n; } return -EINVAL; diff --git a/kernel/power/process.c b/kernel/power/process.c index 98088e0e71e8..fc0df8486449 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -30,9 +30,10 @@ static int try_to_freeze_tasks(bool user_only) unsigned int todo; bool wq_busy = false; struct timeval start, end; - u64 elapsed_csecs64; - unsigned int elapsed_csecs; + u64 elapsed_msecs64; + unsigned int elapsed_msecs; bool wakeup = false; + int sleep_usecs = USEC_PER_MSEC; do_gettimeofday(&start); @@ -68,22 +69,25 @@ static int try_to_freeze_tasks(bool user_only) /* * We need to retry, but first give the freezing tasks some - * time to enter the refrigerator. + * time to enter the refrigerator. Start with an initial + * 1 ms sleep followed by exponential backoff until 8 ms. */ - msleep(10); + usleep_range(sleep_usecs / 2, sleep_usecs); + if (sleep_usecs < 8 * USEC_PER_MSEC) + sleep_usecs *= 2; } do_gettimeofday(&end); - elapsed_csecs64 = timeval_to_ns(&end) - timeval_to_ns(&start); - do_div(elapsed_csecs64, NSEC_PER_SEC / 100); - elapsed_csecs = elapsed_csecs64; + elapsed_msecs64 = timeval_to_ns(&end) - timeval_to_ns(&start); + do_div(elapsed_msecs64, NSEC_PER_MSEC); + elapsed_msecs = elapsed_msecs64; if (todo) { printk("\n"); - printk(KERN_ERR "Freezing of tasks %s after %d.%02d seconds " + printk(KERN_ERR "Freezing of tasks %s after %d.%03d seconds " "(%d tasks refusing to freeze, wq_busy=%d):\n", wakeup ? "aborted" : "failed", - elapsed_csecs / 100, elapsed_csecs % 100, + elapsed_msecs / 1000, elapsed_msecs % 1000, todo - wq_busy, wq_busy); if (!wakeup) { @@ -96,8 +100,8 @@ static int try_to_freeze_tasks(bool user_only) read_unlock(&tasklist_lock); } } else { - printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100, - elapsed_csecs % 100); + printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, + elapsed_msecs % 1000); } return todo ? -EBUSY : 0; diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 587dddeebf15..06fe28589e9c 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -44,6 +44,7 @@ #include <linux/uaccess.h> #include <linux/export.h> +#include <trace/events/power.h> /* * locking rule: all changes to constraints or notifiers lists @@ -202,6 +203,7 @@ int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, spin_unlock_irqrestore(&pm_qos_lock, flags); + trace_pm_qos_update_target(action, prev_value, curr_value); if (prev_value != curr_value) { blocking_notifier_call_chain(c->notifiers, (unsigned long)curr_value, @@ -272,6 +274,7 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, spin_unlock_irqrestore(&pm_qos_lock, irqflags); + trace_pm_qos_update_flags(action, prev_value, curr_value); return prev_value != curr_value; } @@ -333,6 +336,7 @@ void pm_qos_add_request(struct pm_qos_request *req, } req->pm_qos_class = pm_qos_class; INIT_DELAYED_WORK(&req->work, pm_qos_work_fn); + trace_pm_qos_add_request(pm_qos_class, value); pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, &req->node, PM_QOS_ADD_REQ, value); } @@ -361,6 +365,7 @@ void pm_qos_update_request(struct pm_qos_request *req, cancel_delayed_work_sync(&req->work); + trace_pm_qos_update_request(req->pm_qos_class, new_value); if (new_value != req->node.prio) pm_qos_update_target( pm_qos_array[req->pm_qos_class]->constraints, @@ -387,6 +392,8 @@ void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value, cancel_delayed_work_sync(&req->work); + trace_pm_qos_update_request_timeout(req->pm_qos_class, + new_value, timeout_us); if (new_value != req->node.prio) pm_qos_update_target( pm_qos_array[req->pm_qos_class]->constraints, @@ -416,6 +423,7 @@ void pm_qos_remove_request(struct pm_qos_request *req) cancel_delayed_work_sync(&req->work); + trace_pm_qos_remove_request(req->pm_qos_class, PM_QOS_DEFAULT_VALUE); pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints, &req->node, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); @@ -477,7 +485,7 @@ static int find_pm_qos_object_by_minor(int minor) { int pm_qos_class; - for (pm_qos_class = 0; + for (pm_qos_class = PM_QOS_CPU_DMA_LATENCY; pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) { if (minor == pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor) @@ -491,7 +499,7 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp) long pm_qos_class; pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); - if (pm_qos_class >= 0) { + if (pm_qos_class >= PM_QOS_CPU_DMA_LATENCY) { struct pm_qos_request *req = kzalloc(sizeof(*req), GFP_KERNEL); if (!req) return -ENOMEM; @@ -584,7 +592,7 @@ static int __init pm_qos_power_init(void) BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES); - for (i = 1; i < PM_QOS_NUM_CLASSES; i++) { + for (i = PM_QOS_CPU_DMA_LATENCY; i < PM_QOS_NUM_CLASSES; i++) { ret = register_pm_qos_misc(pm_qos_array[i]); if (ret < 0) { printk(KERN_ERR "pm_qos_param: %s setup failed\n", diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 8b5d1cd933f4..349587bb03e1 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -642,8 +642,9 @@ __register_nosave_region(unsigned long start_pfn, unsigned long end_pfn, region->end_pfn = end_pfn; list_add_tail(®ion->list, &nosave_regions); Report: - printk(KERN_INFO "PM: Registered nosave memory: %016lx - %016lx\n", - start_pfn << PAGE_SHIFT, end_pfn << PAGE_SHIFT); + printk(KERN_INFO "PM: Registered nosave memory: [mem %#010llx-%#010llx]\n", + (unsigned long long) start_pfn << PAGE_SHIFT, + ((unsigned long long) end_pfn << PAGE_SHIFT) - 1); } /* diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index bef86d121eb2..ece04223bb1e 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -269,7 +269,7 @@ int suspend_devices_and_enter(suspend_state_t state) suspend_test_start(); error = dpm_suspend_start(PMSG_SUSPEND); if (error) { - printk(KERN_ERR "PM: Some devices failed to suspend\n"); + pr_err("PM: Some devices failed to suspend, or early wake event detected\n"); goto Recover_platform; } suspend_test_finish("suspend devices"); diff --git a/kernel/signal.c b/kernel/signal.c index 113411bfe8b1..50e41075ac77 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2848,7 +2848,7 @@ int do_sigtimedwait(const sigset_t *which, siginfo_t *info, recalc_sigpending(); spin_unlock_irq(&tsk->sighand->siglock); - timeout = schedule_timeout_interruptible(timeout); + timeout = freezable_schedule_timeout_interruptible(timeout); spin_lock_irq(&tsk->sighand->siglock); __set_task_blocked(tsk, &tsk->real_blocked); diff --git a/lib/Kconfig b/lib/Kconfig index d246a3bbd6ef..5a5203ded0dd 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -410,4 +410,6 @@ config OID_REGISTRY config UCS2_STRING tristate +source "lib/fonts/Kconfig" + endmenu diff --git a/lib/Makefile b/lib/Makefile index 8f8d385187f2..c09e38eca87a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -153,6 +153,8 @@ interval_tree_test-objs := interval_tree_test_main.o interval_tree.o obj-$(CONFIG_ASN1) += asn1_decoder.o +obj-$(CONFIG_FONT_SUPPORT) += fonts/ + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/fonts/Kconfig b/lib/fonts/Kconfig new file mode 100644 index 000000000000..34fd931b54b5 --- /dev/null +++ b/lib/fonts/Kconfig @@ -0,0 +1,117 @@ +# +# Font configuration +# + +config FONT_SUPPORT + tristate + +if FONT_SUPPORT + +config FONTS + bool "Select compiled-in fonts" + depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE + help + Say Y here if you would like to use fonts other than the default + your frame buffer console usually use. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about foreign fonts. + + If unsure, say N (the default choices are safe). + +config FONT_8x8 + bool "VGA 8x8 font" if FONTS + depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE + default y if !SPARC && !FONTS + help + This is the "high resolution" font for the VGA frame buffer (the one + provided by the text console 80x50 (and higher) modes). + + Note that this is a poor quality font. The VGA 8x16 font is quite a + lot more readable. + + Given the resolution provided by the frame buffer device, answer N + here is safe. + +config FONT_8x16 + bool "VGA 8x16 font" if FONTS + default y if !SPARC && !FONTS + help + This is the "high resolution" font for the VGA frame buffer (the one + provided by the VGA text console 80x25 mode. + + If unsure, say Y. + +config FONT_6x11 + bool "Mac console 6x11 font (not supported by all drivers)" if FONTS + depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE + default y if !SPARC && !FONTS && MAC + help + Small console font with Macintosh-style high-half glyphs. Some Mac + framebuffer drivers don't support this one at all. + +config FONT_7x14 + bool "console 7x14 font (not supported by all drivers)" if FONTS + depends on FRAMEBUFFER_CONSOLE + help + Console font with characters just a bit smaller than the default. + If the standard 8x16 font is a little too big for you, say Y. + Otherwise, say N. + +config FONT_PEARL_8x8 + bool "Pearl (old m68k) console 8x8 font" if FONTS + depends on FRAMEBUFFER_CONSOLE + default y if !SPARC && !FONTS && AMIGA + help + Small console font with PC-style control-character and high-half + glyphs. + +config FONT_ACORN_8x8 + bool "Acorn console 8x8 font" if FONTS + depends on FRAMEBUFFER_CONSOLE + default y if !SPARC && !FONTS && ARM && ARCH_ACORN + help + Small console font with PC-style control characters and high-half + glyphs. + +config FONT_MINI_4x6 + bool "Mini 4x6 font" + depends on !SPARC && FONTS + +config FONT_SUN8x16 + bool "Sparc console 8x16 font" + depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC) + help + This is the high resolution console font for Sun machines. Say Y. + +config FONT_SUN12x22 + bool "Sparc console 12x22 font (not supported by all drivers)" + depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC) + help + This is the high resolution console font for Sun machines with very + big letters (like the letters used in the SPARC PROM). If the + standard font is unreadable for you, say Y, otherwise say N. + +config FONT_10x18 + bool "console 10x18 font (not supported by all drivers)" if FONTS + depends on FRAMEBUFFER_CONSOLE + help + This is a high resolution console font for machines with very + big letters. It fits between the sun 12x22 and the normal 8x16 font. + If other fonts are too big or too small for you, say Y, otherwise say N. + +config FONT_AUTOSELECT + def_bool y + depends on !FONT_8x8 + depends on !FONT_6x11 + depends on !FONT_7x14 + depends on !FONT_PEARL_8x8 + depends on !FONT_ACORN_8x8 + depends on !FONT_MINI_4x6 + depends on !FONT_SUN8x16 + depends on !FONT_SUN12x22 + depends on !FONT_10x18 + select FONT_8x16 + +endif # FONT_SUPPORT diff --git a/lib/fonts/Makefile b/lib/fonts/Makefile new file mode 100644 index 000000000000..2761560f3f15 --- /dev/null +++ b/lib/fonts/Makefile @@ -0,0 +1,18 @@ +# Font handling + +font-objs := fonts.o + +font-objs-$(CONFIG_FONT_SUN8x16) += font_sun8x16.o +font-objs-$(CONFIG_FONT_SUN12x22) += font_sun12x22.o +font-objs-$(CONFIG_FONT_8x8) += font_8x8.o +font-objs-$(CONFIG_FONT_8x16) += font_8x16.o +font-objs-$(CONFIG_FONT_6x11) += font_6x11.o +font-objs-$(CONFIG_FONT_7x14) += font_7x14.o +font-objs-$(CONFIG_FONT_10x18) += font_10x18.o +font-objs-$(CONFIG_FONT_PEARL_8x8) += font_pearl_8x8.o +font-objs-$(CONFIG_FONT_ACORN_8x8) += font_acorn_8x8.o +font-objs-$(CONFIG_FONT_MINI_4x6) += font_mini_4x6.o + +font-objs += $(font-objs-y) + +obj-$(CONFIG_FONT_SUPPORT) += font.o diff --git a/drivers/video/console/font_10x18.c b/lib/fonts/font_10x18.c index 6be72bb218ee..6be72bb218ee 100644 --- a/drivers/video/console/font_10x18.c +++ b/lib/fonts/font_10x18.c diff --git a/drivers/video/console/font_6x11.c b/lib/fonts/font_6x11.c index 46e86e67aa6a..46e86e67aa6a 100644 --- a/drivers/video/console/font_6x11.c +++ b/lib/fonts/font_6x11.c diff --git a/drivers/video/console/font_7x14.c b/lib/fonts/font_7x14.c index 3b7dbf9c060b..3b7dbf9c060b 100644 --- a/drivers/video/console/font_7x14.c +++ b/lib/fonts/font_7x14.c diff --git a/drivers/video/console/font_8x16.c b/lib/fonts/font_8x16.c index 00a0c67a5c7d..00a0c67a5c7d 100644 --- a/drivers/video/console/font_8x16.c +++ b/lib/fonts/font_8x16.c diff --git a/drivers/video/console/font_8x8.c b/lib/fonts/font_8x8.c index 9f56efe2cee7..9f56efe2cee7 100644 --- a/drivers/video/console/font_8x8.c +++ b/lib/fonts/font_8x8.c diff --git a/drivers/video/console/font_acorn_8x8.c b/lib/fonts/font_acorn_8x8.c index 639e31ae1100..639e31ae1100 100644 --- a/drivers/video/console/font_acorn_8x8.c +++ b/lib/fonts/font_acorn_8x8.c diff --git a/drivers/video/console/font_mini_4x6.c b/lib/fonts/font_mini_4x6.c index 838caa1cfef7..838caa1cfef7 100644 --- a/drivers/video/console/font_mini_4x6.c +++ b/lib/fonts/font_mini_4x6.c diff --git a/drivers/video/console/font_pearl_8x8.c b/lib/fonts/font_pearl_8x8.c index dc6ad539ca4e..dc6ad539ca4e 100644 --- a/drivers/video/console/font_pearl_8x8.c +++ b/lib/fonts/font_pearl_8x8.c diff --git a/drivers/video/console/font_sun12x22.c b/lib/fonts/font_sun12x22.c index d3643853c33a..d3643853c33a 100644 --- a/drivers/video/console/font_sun12x22.c +++ b/lib/fonts/font_sun12x22.c diff --git a/drivers/video/console/font_sun8x16.c b/lib/fonts/font_sun8x16.c index 268151325b83..268151325b83 100644 --- a/drivers/video/console/font_sun8x16.c +++ b/lib/fonts/font_sun8x16.c diff --git a/drivers/video/console/fonts.c b/lib/fonts/fonts.c index d0c03fd70871..f947189efe6d 100644 --- a/drivers/video/console/fonts.c +++ b/lib/fonts/fonts.c @@ -1,5 +1,5 @@ /* - * linux/drivers/video/fonts.c -- `Soft' font definitions + * `Soft' font definitions * * Created 1995 by Geert Uytterhoeven * Rewritten 1998 by Martin Mares <mj@ucw.cz> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 106602e5a70e..f5ba127b2051 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1605,6 +1605,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages) { return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ); } +#endif /* CONFIG_MEMORY_HOTREMOVE */ /** * walk_memory_range - walks through all mem sections in [start_pfn, end_pfn) @@ -1618,7 +1619,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages) * * Returns the return value of func. */ -static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, +int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, void *arg, int (*func)(struct memory_block *, void *)) { struct memory_block *mem = NULL; @@ -1655,24 +1656,7 @@ static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, return 0; } -/** - * offline_memory_block_cb - callback function for offlining memory block - * @mem: the memory block to be offlined - * @arg: buffer to hold error msg - * - * Always return 0, and put the error msg in arg if any. - */ -static int offline_memory_block_cb(struct memory_block *mem, void *arg) -{ - int *ret = arg; - int error = offline_memory_block(mem); - - if (error != 0 && *ret == 0) - *ret = error; - - return 0; -} - +#ifdef CONFIG_MEMORY_HOTREMOVE static int is_memblock_offlined_cb(struct memory_block *mem, void *arg) { int ret = !is_memblock_offlined(mem); @@ -1798,54 +1782,22 @@ void try_offline_node(int nid) } EXPORT_SYMBOL(try_offline_node); -int __ref remove_memory(int nid, u64 start, u64 size) +void __ref remove_memory(int nid, u64 start, u64 size) { - unsigned long start_pfn, end_pfn; - int ret = 0; - int retry = 1; - - start_pfn = PFN_DOWN(start); - end_pfn = PFN_UP(start + size - 1); - - /* - * When CONFIG_MEMCG is on, one memory block may be used by other - * blocks to store page cgroup when onlining pages. But we don't know - * in what order pages are onlined. So we iterate twice to offline - * memory: - * 1st iterate: offline every non primary memory block. - * 2nd iterate: offline primary (i.e. first added) memory block. - */ -repeat: - walk_memory_range(start_pfn, end_pfn, &ret, - offline_memory_block_cb); - if (ret) { - if (!retry) - return ret; - - retry = 0; - ret = 0; - goto repeat; - } + int ret; lock_memory_hotplug(); /* - * we have offlined all memory blocks like this: - * 1. lock memory hotplug - * 2. offline a memory block - * 3. unlock memory hotplug - * - * repeat step1-3 to offline the memory block. All memory blocks - * must be offlined before removing memory. But we don't hold the - * lock in the whole operation. So we should check whether all - * memory blocks are offlined. + * All memory blocks must be offlined before removing memory. Check + * whether all memory blocks in question are offline and trigger a BUG() + * if this is not the case. */ - - ret = walk_memory_range(start_pfn, end_pfn, NULL, + ret = walk_memory_range(PFN_DOWN(start), PFN_UP(start + size - 1), NULL, is_memblock_offlined_cb); if (ret) { unlock_memory_hotplug(); - return ret; + BUG(); } /* remove memmap entry */ @@ -1856,17 +1808,6 @@ repeat: try_offline_node(nid); unlock_memory_hotplug(); - - return 0; } -#else -int offline_pages(unsigned long start_pfn, unsigned long nr_pages) -{ - return -EINVAL; -} -int remove_memory(int nid, u64 start, u64 size) -{ - return -EINVAL; -} -#endif /* CONFIG_MEMORY_HOTREMOVE */ EXPORT_SYMBOL_GPL(remove_memory); +#endif /* CONFIG_MEMORY_HOTREMOVE */ diff --git a/mm/shmem.c b/mm/shmem.c index 118dfa4952f4..a87990cf9f94 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1936,6 +1936,13 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) inode = shmem_get_inode(dir->i_sb, dir, mode, dev, VM_NORESERVE); if (inode) { +#ifdef CONFIG_TMPFS_POSIX_ACL + error = generic_acl_init(inode, dir); + if (error) { + iput(inode); + return error; + } +#endif error = security_inode_init_security(inode, dir, &dentry->d_name, shmem_initxattrs, NULL); @@ -1945,15 +1952,8 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) return error; } } -#ifdef CONFIG_TMPFS_POSIX_ACL - error = generic_acl_init(inode, dir); - if (error) { - iput(inode); - return error; - } -#else + error = 0; -#endif dir->i_size += BOGO_DIRENT_SIZE; dir->i_ctime = dir->i_mtime = CURRENT_TIME; d_instantiate(dentry, inode); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 5356b120dbf8..77d251e02593 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -254,7 +254,7 @@ static int rpc_wait_bit_killable(void *word) { if (fatal_signal_pending(current)) return -ERESTARTSYS; - freezable_schedule(); + freezable_schedule_unsafe(); return 0; } diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 826e09938bff..c4ce243824bb 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -114,6 +114,7 @@ #include <linux/mount.h> #include <net/checksum.h> #include <linux/security.h> +#include <linux/freezer.h> struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE]; EXPORT_SYMBOL_GPL(unix_socket_table); @@ -1879,7 +1880,7 @@ static long unix_stream_data_wait(struct sock *sk, long timeo, set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); unix_state_unlock(sk); - timeo = schedule_timeout(timeo); + timeo = freezable_schedule_timeout(timeo); unix_state_lock(sk); clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); } diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index a4be8e112bb6..3d155dd27eb6 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -884,7 +884,7 @@ static void check_section(const char *modname, struct elf_info *elf, #define ALL_EXIT_SECTIONS EXIT_SECTIONS, ALL_XXXEXIT_SECTIONS #define DATA_SECTIONS ".data$", ".data.rel$" -#define TEXT_SECTIONS ".text$" +#define TEXT_SECTIONS ".text$", ".text.unlikely$" #define INIT_SECTIONS ".init.*" #define CPU_INIT_SECTIONS ".cpuinit.*" diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3ae28db5a64f..031d2d9dd695 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -88,7 +88,7 @@ static const char *const aa_audit_type[] = { "HINT", "STATUS", "ERROR", - "KILLED" + "KILLED", "AUTO" }; diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 8a9b5027c813..d5af1d15f26d 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -69,6 +69,23 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) } /** + * aa_get_task_profile - Get another task's profile + * @task: task to query (NOT NULL) + * + * Returns: counted reference to @task's profile + */ +struct aa_profile *aa_get_task_profile(struct task_struct *task) +{ + struct aa_profile *p; + + rcu_read_lock(); + p = aa_get_profile(__aa_task_profile(task)); + rcu_read_unlock(); + + return p; +} + +/** * aa_replace_current_profile - replace the current tasks profiles * @profile: new profile (NOT NULL) * @@ -76,7 +93,7 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) */ int aa_replace_current_profile(struct aa_profile *profile) { - struct aa_task_cxt *cxt = current_cred()->security; + struct aa_task_cxt *cxt = current_cxt(); struct cred *new; BUG_ON(!profile); @@ -87,17 +104,13 @@ int aa_replace_current_profile(struct aa_profile *profile) if (!new) return -ENOMEM; - cxt = new->security; - if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { + cxt = cred_cxt(new); + if (unconfined(profile) || (cxt->profile->ns != profile->ns)) /* if switching to unconfined or a different profile namespace * clear out context state */ - aa_put_profile(cxt->previous); - aa_put_profile(cxt->onexec); - cxt->previous = NULL; - cxt->onexec = NULL; - cxt->token = 0; - } + aa_clear_task_cxt_trans(cxt); + /* be careful switching cxt->profile, when racing replacement it * is possible that cxt->profile->replacedby is the reference keeping * @profile valid, so make sure to get its reference before dropping @@ -123,7 +136,7 @@ int aa_set_current_onexec(struct aa_profile *profile) if (!new) return -ENOMEM; - cxt = new->security; + cxt = cred_cxt(new); aa_get_profile(profile); aa_put_profile(cxt->onexec); cxt->onexec = profile; @@ -150,7 +163,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) return -ENOMEM; BUG_ON(!profile); - cxt = new->security; + cxt = cred_cxt(new); if (!cxt->previous) { /* transfer refcount */ cxt->previous = cxt->profile; @@ -187,7 +200,7 @@ int aa_restore_previous_profile(u64 token) if (!new) return -ENOMEM; - cxt = new->security; + cxt = cred_cxt(new); if (cxt->token != token) { abort_creds(new); return -EACCES; @@ -205,11 +218,10 @@ int aa_restore_previous_profile(u64 token) aa_get_profile(cxt->profile); aa_put_profile(cxt->previous); } - /* clear exec && prev information when restoring to previous context */ + /* ref has been transfered so avoid putting ref in clear_task_cxt */ cxt->previous = NULL; - cxt->token = 0; - aa_put_profile(cxt->onexec); - cxt->onexec = NULL; + /* clear exec && prev information when restoring to previous context */ + aa_clear_task_cxt_trans(cxt); commit_creds(new); return 0; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 859abdaac1ea..01b7bd669a88 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -62,17 +62,14 @@ static int may_change_ptraced_domain(struct task_struct *task, struct aa_profile *to_profile) { struct task_struct *tracer; - const struct cred *cred = NULL; struct aa_profile *tracerp = NULL; int error = 0; rcu_read_lock(); tracer = ptrace_parent(task); - if (tracer) { + if (tracer) /* released below */ - cred = get_task_cred(tracer); - tracerp = aa_cred_profile(cred); - } + tracerp = aa_get_task_profile(tracer); /* not ptraced */ if (!tracer || unconfined(tracerp)) @@ -82,8 +79,7 @@ static int may_change_ptraced_domain(struct task_struct *task, out: rcu_read_unlock(); - if (cred) - put_cred(cred); + aa_put_profile(tracerp); return error; } @@ -360,7 +356,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->cred_prepared) return 0; - cxt = bprm->cred->security; + cxt = cred_cxt(bprm->cred); BUG_ON(!cxt); profile = aa_get_profile(aa_newest_version(cxt->profile)); @@ -443,6 +439,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) } else { error = -ENOENT; info = "profile not found"; + /* remove MAY_EXEC to audit as failure */ + perms.allow &= ~MAY_EXEC; } } } else if (COMPLAIN_MODE(profile)) { @@ -514,11 +512,7 @@ x_clear: cxt->profile = new_profile; /* clear out all temporary/transitional state from the context */ - aa_put_profile(cxt->previous); - aa_put_profile(cxt->onexec); - cxt->previous = NULL; - cxt->onexec = NULL; - cxt->token = 0; + aa_clear_task_cxt_trans(cxt); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, @@ -557,7 +551,7 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm) void apparmor_bprm_committing_creds(struct linux_binprm *bprm) { struct aa_profile *profile = __aa_current_profile(); - struct aa_task_cxt *new_cxt = bprm->cred->security; + struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred); /* bail out if unconfined or not changing profile */ if ((new_cxt->profile == profile) || @@ -634,7 +628,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) /* released below */ cred = get_current_cred(); - cxt = cred->security; + cxt = cred_cxt(cred); profile = aa_cred_profile(cred); previous_profile = cxt->previous; @@ -750,7 +744,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, bool permtest) { const struct cred *cred; - struct aa_task_cxt *cxt; struct aa_profile *profile, *target = NULL; struct aa_namespace *ns = NULL; struct file_perms perms = {}; @@ -770,7 +763,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, } cred = get_current_cred(); - cxt = cred->security; profile = aa_cred_profile(cred); /* diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9f73ea..1ba2ca56a6ef 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -15,6 +15,7 @@ #ifndef __APPARMOR_H #define __APPARMOR_H +#include <linux/slab.h> #include <linux/fs.h> #include "match.h" @@ -64,9 +65,18 @@ extern int apparmor_initialized __initdata; /* fn's in lib */ char *aa_split_fqname(char *args, char **ns_name); void aa_info_message(const char *str); -void *kvmalloc(size_t size); +void *__aa_kvmalloc(size_t size, gfp_t flags); void kvfree(void *buffer); +static inline void *kvmalloc(size_t size) +{ + return __aa_kvmalloc(size, 0); +} + +static inline void *kvzalloc(size_t size) +{ + return __aa_kvmalloc(size, __GFP_ZERO); +} /** * aa_strneq - compare null terminated @str to a non null terminated substring diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index a9cbee4d9e48..d44ba5802e3d 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -21,6 +21,9 @@ #include "policy.h" +#define cred_cxt(X) (X)->security +#define current_cxt() cred_cxt(current_cred()) + /* struct aa_file_cxt - the AppArmor context the file was opened in * @perms: the permission the file was opened with * @@ -80,23 +83,8 @@ int aa_replace_current_profile(struct aa_profile *profile); int aa_set_current_onexec(struct aa_profile *profile); int aa_set_current_hat(struct aa_profile *profile, u64 token); int aa_restore_previous_profile(u64 cookie); +struct aa_profile *aa_get_task_profile(struct task_struct *task); -/** - * __aa_task_is_confined - determine if @task has any confinement - * @task: task to check confinement of (NOT NULL) - * - * If @task != current needs to be called in RCU safe critical section - */ -static inline bool __aa_task_is_confined(struct task_struct *task) -{ - struct aa_task_cxt *cxt = __task_cred(task)->security; - - BUG_ON(!cxt || !cxt->profile); - if (unconfined(aa_newest_version(cxt->profile))) - return 0; - - return 1; -} /** * aa_cred_profile - obtain cred's profiles @@ -108,12 +96,36 @@ static inline bool __aa_task_is_confined(struct task_struct *task) */ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) { - struct aa_task_cxt *cxt = cred->security; + struct aa_task_cxt *cxt = cred_cxt(cred); BUG_ON(!cxt || !cxt->profile); return aa_newest_version(cxt->profile); } /** + * __aa_task_profile - retrieve another task's profile + * @task: task to query (NOT NULL) + * + * Returns: @task's profile without incrementing its ref count + * + * If @task != current needs to be called in RCU safe critical section + */ +static inline struct aa_profile *__aa_task_profile(struct task_struct *task) +{ + return aa_cred_profile(__task_cred(task)); +} + +/** + * __aa_task_is_confined - determine if @task has any confinement + * @task: task to check confinement of (NOT NULL) + * + * If @task != current needs to be called in RCU safe critical section + */ +static inline bool __aa_task_is_confined(struct task_struct *task) +{ + return !unconfined(__aa_task_profile(task)); +} + +/** * __aa_current_profile - find the current tasks confining profile * * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) @@ -136,7 +148,7 @@ static inline struct aa_profile *__aa_current_profile(void) */ static inline struct aa_profile *aa_current_profile(void) { - const struct aa_task_cxt *cxt = current_cred()->security; + const struct aa_task_cxt *cxt = current_cxt(); struct aa_profile *profile; BUG_ON(!cxt || !cxt->profile); @@ -151,4 +163,17 @@ static inline struct aa_profile *aa_current_profile(void) return profile; } +/** + * aa_clear_task_cxt_trans - clear transition tracking info from the cxt + * @cxt: task context to clear (NOT NULL) + */ +static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt) +{ + aa_put_profile(cxt->previous); + aa_put_profile(cxt->onexec); + cxt->previous = NULL; + cxt->onexec = NULL; + cxt->token = 0; +} + #endif /* __AA_CONTEXT_H */ diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 967b2deda376..2c922b86bd44 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -186,11 +186,6 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules) aa_free_domain_entries(&rules->trans); } -#define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40)) - -/* from namei.c */ -#define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x)) - /** * aa_map_file_perms - map file flags to AppArmor permissions * @file: open file to map flags to AppArmor permissions @@ -199,8 +194,13 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules) */ static inline u32 aa_map_file_to_perms(struct file *file) { - int flags = MAP_OPEN_FLAGS(file->f_flags); - u32 perms = ACC_FMODE(file->f_mode); + int flags = file->f_flags; + u32 perms = 0; + + if (file->f_mode & FMODE_WRITE) + perms |= MAY_WRITE; + if (file->f_mode & FMODE_READ) + perms |= MAY_READ; if ((flags & O_APPEND) && (perms & MAY_WRITE)) perms = (perms & ~MAY_WRITE) | MAY_APPEND; diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 775843e7f984..001c43aa0406 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -4,7 +4,7 @@ * This file contains AppArmor policy dfa matching engine definitions. * * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2012 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -16,25 +16,30 @@ #define __AA_MATCH_H #include <linux/kref.h> -#include <linux/workqueue.h> #define DFA_NOMATCH 0 #define DFA_START 1 -#define DFA_VALID_PERM_MASK 0xffffffff -#define DFA_VALID_PERM2_MASK 0xffffffff /** * The format used for transition tables is based on the GNU flex table * file format (--tables-file option; see Table File Format in the flex * info pages and the flex sources for documentation). The magic number * used in the header is 0x1B5E783D instead of 0xF13C57B1 though, because - * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used - * slightly differently (see the apparmor-parser package). + * new tables have been defined and others YY_ID_CHK (check) and YY_ID_DEF + * (default) tables are used slightly differently (see the apparmor-parser + * package). + * + * + * The data in the packed dfa is stored in network byte order, and the tables + * are arranged for flexibility. We convert the table data to host native + * byte order. + * + * The dfa begins with a table set header, and is followed by the actual + * tables. */ #define YYTH_MAGIC 0x1B5E783D -#define YYTH_DEF_RECURSE 0x1 /* DEF Table is recursive */ struct table_set_header { u32 th_magic; /* YYTH_MAGIC */ @@ -63,7 +68,7 @@ struct table_set_header { #define YYTD_DATA32 4 #define YYTD_DATA64 8 -/* Each ACCEPT2 table gets 6 dedicated flags, YYTD_DATAX define the +/* ACCEPT & ACCEPT2 tables gets 6 dedicated flags, YYTD_DATAX define the * first flags */ #define ACCEPT1_FLAGS(X) ((X) & 0x3f) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569fdd83..b25491a3046a 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -32,13 +32,13 @@ extern const char *const profile_mode_names[]; #define APPARMOR_NAMES_MAX_INDEX 3 -#define COMPLAIN_MODE(_profile) \ - ((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ - ((_profile)->mode == APPARMOR_COMPLAIN)) +#define PROFILE_MODE(_profile, _mode) \ + ((aa_g_profile_mode == (_mode)) || \ + ((_profile)->mode == (_mode))) -#define KILL_MODE(_profile) \ - ((aa_g_profile_mode == APPARMOR_KILL) || \ - ((_profile)->mode == APPARMOR_KILL)) +#define COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN) + +#define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL) #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) @@ -105,6 +105,7 @@ struct aa_ns_acct { * @acct: accounting for the namespace * @unconfined: special unconfined profile for the namespace * @sub_ns: list of namespaces under the current namespace. + * @uniq_null: uniq value used for null learning profiles * * An aa_namespace defines the set profiles that are searched to determine * which profile to attach to a task. Profiles can not be shared between @@ -127,6 +128,7 @@ struct aa_namespace { struct aa_ns_acct acct; struct aa_profile *unconfined; struct list_head sub_ns; + atomic_t uniq_null; }; /* struct aa_policydb - match engine for a policy @@ -148,7 +150,6 @@ struct aa_policydb { * @rename: optional profile name that this profile renamed * @xmatch: optional extended matching for unconfined executables names * @xmatch_len: xmatch prefix len, used to determine xmatch priority - * @sid: the unique security id number of this profile * @audit: the auditing mode of the profile * @mode: the enforcement mode of the profile * @flags: flags controlling profile behavior @@ -184,7 +185,6 @@ struct aa_profile { struct aa_dfa *xmatch; int xmatch_len; - u32 sid; enum audit_mode audit; enum profile_mode mode; u32 flags; diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h index 544aa6b766a4..6bd5f33d9533 100644 --- a/security/apparmor/include/procattr.h +++ b/security/apparmor/include/procattr.h @@ -21,6 +21,5 @@ int aa_getprocattr(struct aa_profile *profile, char **string); int aa_setprocattr_changehat(char *args, size_t size, int test); int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); -int aa_setprocattr_permipc(char *fqname); #endif /* __AA_PROCATTR_H */ diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h index 020db35c3010..513ca0e48965 100644 --- a/security/apparmor/include/sid.h +++ b/security/apparmor/include/sid.h @@ -16,7 +16,9 @@ #include <linux/types.h> -struct aa_profile; +/* sid value that will not be allocated */ +#define AA_SID_INVALID 0 +#define AA_SID_ALLOC AA_SID_INVALID u32 aa_alloc_sid(void); void aa_free_sid(u32 sid); diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index cf1071b14232..c51d2266587e 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -95,23 +95,18 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, * - tracer profile has CAP_SYS_PTRACE */ - struct aa_profile *tracer_p; - /* cred released below */ - const struct cred *cred = get_task_cred(tracer); + struct aa_profile *tracer_p = aa_get_task_profile(tracer); int error = 0; - tracer_p = aa_cred_profile(cred); if (!unconfined(tracer_p)) { - /* lcred released below */ - const struct cred *lcred = get_task_cred(tracee); - struct aa_profile *tracee_p = aa_cred_profile(lcred); + struct aa_profile *tracee_p = aa_get_task_profile(tracee); error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); error = aa_audit_ptrace(tracer_p, tracee_p, error); - put_cred(lcred); + aa_put_profile(tracee_p); } - put_cred(cred); + aa_put_profile(tracer_p); return error; } diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 7430298116d6..fcfe0233574c 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -45,8 +45,10 @@ char *aa_split_fqname(char *fqname, char **ns_name) *ns_name = skip_spaces(&name[1]); if (split) { /* overwrite ':' with \0 */ - *split = 0; - name = skip_spaces(split + 1); + *split++ = 0; + if (strncmp(split, "//", 2) == 0) + split += 2; + name = skip_spaces(split); } else /* a ns name without a following profile is allowed */ name = NULL; @@ -75,15 +77,16 @@ void aa_info_message(const char *str) } /** - * kvmalloc - do allocation preferring kmalloc but falling back to vmalloc - * @size: size of allocation + * __aa_kvmalloc - do allocation preferring kmalloc but falling back to vmalloc + * @size: how many bytes of memory are required + * @flags: the type of memory to allocate (see kmalloc). * * Return: allocated buffer or NULL if failed * * It is possible that policy being loaded from the user is larger than * what can be allocated by kmalloc, in those cases fall back to vmalloc. */ -void *kvmalloc(size_t size) +void *__aa_kvmalloc(size_t size, gfp_t flags) { void *buffer = NULL; @@ -92,32 +95,22 @@ void *kvmalloc(size_t size) /* do not attempt kmalloc if we need more than 16 pages at once */ if (size <= (16*PAGE_SIZE)) - buffer = kmalloc(size, GFP_NOIO | __GFP_NOWARN); + buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); if (!buffer) { /* see kvfree for why size must be at least work_struct size * when allocated via vmalloc */ if (size < sizeof(struct work_struct)) size = sizeof(struct work_struct); - buffer = vmalloc(size); + if (flags & __GFP_ZERO) + buffer = vzalloc(size); + else + buffer = vmalloc(size); } return buffer; } /** - * do_vfree - workqueue routine for freeing vmalloced memory - * @work: data to be freed - * - * The work_struct is overlaid to the data being freed, as at the point - * the work is scheduled the data is no longer valid, be its freeing - * needs to be delayed until safe. - */ -static void do_vfree(struct work_struct *work) -{ - vfree(work); -} - -/** * kvfree - free an allocation do by kvmalloc * @buffer: buffer to free (MAYBE_NULL) * @@ -125,13 +118,8 @@ static void do_vfree(struct work_struct *work) */ void kvfree(void *buffer) { - if (is_vmalloc_addr(buffer)) { - /* Data is no longer valid so just use the allocated space - * as the work_struct - */ - struct work_struct *work = (struct work_struct *) buffer; - INIT_WORK(work, do_vfree); - schedule_work(work); - } else + if (is_vmalloc_addr(buffer)) + vfree(buffer); + else kfree(buffer); } diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b21830eced41..2e2a0dd4a73f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -48,8 +48,8 @@ int apparmor_initialized __initdata; */ static void apparmor_cred_free(struct cred *cred) { - aa_free_task_context(cred->security); - cred->security = NULL; + aa_free_task_context(cred_cxt(cred)); + cred_cxt(cred) = NULL; } /* @@ -62,7 +62,7 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) if (!cxt) return -ENOMEM; - cred->security = cxt; + cred_cxt(cred) = cxt; return 0; } @@ -77,8 +77,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, if (!cxt) return -ENOMEM; - aa_dup_task_context(cxt, old->security); - new->security = cxt; + aa_dup_task_context(cxt, cred_cxt(old)); + cred_cxt(new) = cxt; return 0; } @@ -87,8 +87,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, */ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) { - const struct aa_task_cxt *old_cxt = old->security; - struct aa_task_cxt *new_cxt = new->security; + const struct aa_task_cxt *old_cxt = cred_cxt(old); + struct aa_task_cxt *new_cxt = cred_cxt(new); aa_dup_task_context(new_cxt, old_cxt); } @@ -469,7 +469,6 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd) static int common_mmap(int op, struct file *file, unsigned long prot, unsigned long flags) { - struct dentry *dentry; int mask = 0; if (!file || !file->f_security) @@ -486,7 +485,6 @@ static int common_mmap(int op, struct file *file, unsigned long prot, if (prot & PROT_EXEC) mask |= AA_EXEC_MMAP; - dentry = file->f_path.dentry; return common_file_perm(op, file, mask); } @@ -507,11 +505,9 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { int error = -ENOENT; - struct aa_profile *profile; /* released below */ const struct cred *cred = get_task_cred(task); - struct aa_task_cxt *cxt = cred->security; - profile = aa_cred_profile(cred); + struct aa_task_cxt *cxt = cred_cxt(cred); if (strcmp(name, "current") == 0) error = aa_getprocattr(aa_newest_version(cxt->profile), @@ -533,6 +529,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, static int apparmor_setprocattr(struct task_struct *task, char *name, void *value, size_t size) { + struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; char *command, *args = value; size_t arg_size; int error; @@ -576,30 +574,31 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, } else if (strcmp(command, "permprofile") == 0) { error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, AA_DO_TEST); - } else if (strcmp(command, "permipc") == 0) { - error = aa_setprocattr_permipc(args); - } else { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.op = OP_SETPROCATTR; - aad.info = name; - aad.error = -EINVAL; - return aa_audit(AUDIT_APPARMOR_DENIED, - __aa_current_profile(), GFP_KERNEL, - &sa, NULL); - } + } else + goto fail; } else if (strcmp(name, "exec") == 0) { - error = aa_setprocattr_changeprofile(args, AA_ONEXEC, - !AA_DO_TEST); - } else { + if (strcmp(command, "exec") == 0) + error = aa_setprocattr_changeprofile(args, AA_ONEXEC, + !AA_DO_TEST); + else + goto fail; + } else /* only support the "current" and "exec" process attributes */ return -EINVAL; - } + if (!error) error = size; return error; + +fail: + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + aad.profile = aa_current_profile(); + aad.op = OP_SETPROCATTR; + aad.info = name; + aad.error = -EINVAL; + aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); + return -EINVAL; } static int apparmor_task_setrlimit(struct task_struct *task, @@ -886,7 +885,7 @@ static int __init set_init_cxt(void) return -ENOMEM; cxt->profile = aa_get_profile(root_ns->unconfined); - cred->security = cxt; + cred_cxt(cred) = cxt; return 0; } @@ -915,8 +914,11 @@ static int __init apparmor_init(void) error = register_security(&apparmor_ops); if (error) { + struct cred *cred = (struct cred *)current->real_cred; + aa_free_task_context(cred_cxt(cred)); + cred_cxt(cred) = NULL; AA_ERROR("Unable to register AppArmor\n"); - goto set_init_cxt_out; + goto register_security_out; } /* Report that AppArmor successfully initialized */ @@ -930,9 +932,6 @@ static int __init apparmor_init(void) return error; -set_init_cxt_out: - aa_free_task_context(current->real_cred->security); - register_security_out: aa_free_root_ns(); diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 90971a8c3789..727eb4200d5c 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -4,7 +4,7 @@ * This file contains AppArmor dfa based regular expression matching engine * * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2012 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -23,6 +23,8 @@ #include "include/apparmor.h" #include "include/match.h" +#define base_idx(X) ((X) & 0xffffff) + /** * unpack_table - unpack a dfa table (one of accept, default, base, next check) * @blob: data to unpack (NOT NULL) @@ -30,7 +32,7 @@ * * Returns: pointer to table else NULL on failure * - * NOTE: must be freed by kvfree (not kmalloc) + * NOTE: must be freed by kvfree (not kfree) */ static struct table_header *unpack_table(char *blob, size_t bsize) { @@ -57,7 +59,7 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; - table = kvmalloc(tsize); + table = kvzalloc(tsize); if (table) { *table = th; if (th.td_flags == YYTD_DATA8) @@ -137,8 +139,7 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; - /* TODO: do check that DEF state recursion terminates */ - if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; @@ -314,7 +315,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, u8 *equiv = EQUIV_TABLE(dfa); /* default is direct to next state */ for (; len; len--) { - pos = base[state] + equiv[(u8) *str++]; + pos = base_idx(base[state]) + equiv[(u8) *str++]; if (check[pos] == state) state = next[pos]; else @@ -323,7 +324,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, } else { /* default is direct to next state */ for (; len; len--) { - pos = base[state] + (u8) *str++; + pos = base_idx(base[state]) + (u8) *str++; if (check[pos] == state) state = next[pos]; else @@ -364,7 +365,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, u8 *equiv = EQUIV_TABLE(dfa); /* default is direct to next state */ while (*str) { - pos = base[state] + equiv[(u8) *str++]; + pos = base_idx(base[state]) + equiv[(u8) *str++]; if (check[pos] == state) state = next[pos]; else @@ -373,7 +374,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, } else { /* default is direct to next state */ while (*str) { - pos = base[state] + (u8) *str++; + pos = base_idx(base[state]) + (u8) *str++; if (check[pos] == state) state = next[pos]; else @@ -409,14 +410,14 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, u8 *equiv = EQUIV_TABLE(dfa); /* default is direct to next state */ - pos = base[state] + equiv[(u8) c]; + pos = base_idx(base[state]) + equiv[(u8) c]; if (check[pos] == state) state = next[pos]; else state = def[state]; } else { /* default is direct to next state */ - pos = base[state] + (u8) c; + pos = base_idx(base[state]) + (u8) c; if (check[pos] == state) state = next[pos]; else diff --git a/security/apparmor/path.c b/security/apparmor/path.c index e91ffee80162..35b394a75d76 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -174,7 +174,7 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, if (info && error) { if (error == -ENOENT) *info = "Failed name lookup - deleted entry"; - else if (error == -ESTALE) + else if (error == -EACCES) *info = "Failed name lookup - disconnected path"; else if (error == -ENAMETOOLONG) *info = "Failed name lookup - name too long"; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 813200384d97..0f345c4dee5f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -87,7 +87,6 @@ #include "include/policy.h" #include "include/policy_unpack.h" #include "include/resource.h" -#include "include/sid.h" /* root profile namespace */ @@ -292,7 +291,6 @@ static struct aa_namespace *alloc_namespace(const char *prefix, if (!ns->unconfined) goto fail_unconfined; - ns->unconfined->sid = aa_alloc_sid(); ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | PFLAG_IMMUTABLE; @@ -303,6 +301,8 @@ static struct aa_namespace *alloc_namespace(const char *prefix, */ ns->unconfined->ns = aa_get_namespace(ns); + atomic_set(&ns->uniq_null, 0); + return ns; fail_unconfined: @@ -497,7 +497,6 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new) /* released when @new is freed */ new->parent = aa_get_profile(old->parent); new->ns = aa_get_namespace(old->ns); - new->sid = old->sid; __list_add_profile(&policy->profiles, new); /* inherit children */ list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { @@ -636,83 +635,6 @@ void __init aa_free_root_ns(void) } /** - * aa_alloc_profile - allocate, initialize and return a new profile - * @hname: name of the profile (NOT NULL) - * - * Returns: refcount profile or NULL on failure - */ -struct aa_profile *aa_alloc_profile(const char *hname) -{ - struct aa_profile *profile; - - /* freed by free_profile - usually through aa_put_profile */ - profile = kzalloc(sizeof(*profile), GFP_KERNEL); - if (!profile) - return NULL; - - if (!policy_init(&profile->base, NULL, hname)) { - kzfree(profile); - return NULL; - } - - /* refcount released by caller */ - return profile; -} - -/** - * aa_new_null_profile - create a new null-X learning profile - * @parent: profile that caused this profile to be created (NOT NULL) - * @hat: true if the null- learning profile is a hat - * - * Create a null- complain mode profile used in learning mode. The name of - * the profile is unique and follows the format of parent//null-sid. - * - * null profiles are added to the profile list but the list does not - * hold a count on them so that they are automatically released when - * not in use. - * - * Returns: new refcounted profile else NULL on failure - */ -struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) -{ - struct aa_profile *profile = NULL; - char *name; - u32 sid = aa_alloc_sid(); - - /* freed below */ - name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); - if (!name) - goto fail; - sprintf(name, "%s//null-%x", parent->base.hname, sid); - - profile = aa_alloc_profile(name); - kfree(name); - if (!profile) - goto fail; - - profile->sid = sid; - profile->mode = APPARMOR_COMPLAIN; - profile->flags = PFLAG_NULL; - if (hat) - profile->flags |= PFLAG_HAT; - - /* released on free_profile */ - profile->parent = aa_get_profile(parent); - profile->ns = aa_get_namespace(parent->ns); - - write_lock(&profile->ns->lock); - __list_add_profile(&parent->base.profiles, profile); - write_unlock(&profile->ns->lock); - - /* refcount released by caller */ - return profile; - -fail: - aa_free_sid(sid); - return NULL; -} - -/** * free_profile - free a profile * @profile: the profile to free (MAYBE NULL) * @@ -749,7 +671,6 @@ static void free_profile(struct aa_profile *profile) aa_free_cap_rules(&profile->caps); aa_free_rlimit_rules(&profile->rlimits); - aa_free_sid(profile->sid); aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); @@ -790,6 +711,81 @@ void aa_free_profile_kref(struct kref *kref) free_profile(p); } +/** + * aa_alloc_profile - allocate, initialize and return a new profile + * @hname: name of the profile (NOT NULL) + * + * Returns: refcount profile or NULL on failure + */ +struct aa_profile *aa_alloc_profile(const char *hname) +{ + struct aa_profile *profile; + + /* freed by free_profile - usually through aa_put_profile */ + profile = kzalloc(sizeof(*profile), GFP_KERNEL); + if (!profile) + return NULL; + + if (!policy_init(&profile->base, NULL, hname)) { + kzfree(profile); + return NULL; + } + + /* refcount released by caller */ + return profile; +} + +/** + * aa_new_null_profile - create a new null-X learning profile + * @parent: profile that caused this profile to be created (NOT NULL) + * @hat: true if the null- learning profile is a hat + * + * Create a null- complain mode profile used in learning mode. The name of + * the profile is unique and follows the format of parent//null-<uniq>. + * + * null profiles are added to the profile list but the list does not + * hold a count on them so that they are automatically released when + * not in use. + * + * Returns: new refcounted profile else NULL on failure + */ +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) +{ + struct aa_profile *profile = NULL; + char *name; + int uniq = atomic_inc_return(&parent->ns->uniq_null); + + /* freed below */ + name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); + if (!name) + goto fail; + sprintf(name, "%s//null-%x", parent->base.hname, uniq); + + profile = aa_alloc_profile(name); + kfree(name); + if (!profile) + goto fail; + + profile->mode = APPARMOR_COMPLAIN; + profile->flags = PFLAG_NULL; + if (hat) + profile->flags |= PFLAG_HAT; + + /* released on free_profile */ + profile->parent = aa_get_profile(parent); + profile->ns = aa_get_namespace(parent->ns); + + write_lock(&profile->ns->lock); + __list_add_profile(&parent->base.profiles, profile); + write_unlock(&profile->ns->lock); + + /* refcount released by caller */ + return profile; + +fail: + return NULL; +} + /* TODO: profile accounting - setup in remove */ /** @@ -972,7 +968,6 @@ static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, profile->parent = aa_get_profile((struct aa_profile *) policy); __list_add_profile(&policy->profiles, profile); /* released on free_profile */ - profile->sid = aa_alloc_sid(); profile->ns = aa_get_namespace(ns); } @@ -1110,14 +1105,8 @@ audit: if (!error) { if (rename_profile) __replace_profile(rename_profile, new_profile); - if (old_profile) { - /* when there are both rename and old profiles - * inherit old profiles sid - */ - if (rename_profile) - aa_free_sid(new_profile->sid); + if (old_profile) __replace_profile(old_profile, new_profile); - } if (!(old_profile || rename_profile)) __add_new_profile(ns, policy, new_profile); } @@ -1167,14 +1156,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) if (fqname[0] == ':') { char *ns_name; name = aa_split_fqname(fqname, &ns_name); - if (ns_name) { - /* released below */ - ns = aa_find_namespace(root, ns_name); - if (!ns) { - info = "namespace does not exist"; - error = -ENOENT; - goto fail; - } + /* released below */ + ns = aa_find_namespace(root, ns_name); + if (!ns) { + info = "namespace does not exist"; + error = -ENOENT; + goto fail; } } else /* released below */ diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 329b1fd30749..6dac7d77cb4d 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -27,7 +27,6 @@ #include "include/match.h" #include "include/policy.h" #include "include/policy_unpack.h" -#include "include/sid.h" /* * The AppArmor interface treats data as a type byte followed by the @@ -290,6 +289,9 @@ static int unpack_strdup(struct aa_ext *e, char **string, const char *name) return res; } +#define DFA_VALID_PERM_MASK 0xffffffff +#define DFA_VALID_PERM2_MASK 0xffffffff + /** * verify_accept - verify the accept tables of a dfa * @dfa: dfa to verify accept tables of (NOT NULL) diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 1b41c542d376..6c9390179b89 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -163,9 +163,3 @@ int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) name = aa_split_fqname(fqname, &ns_name); return aa_change_profile(ns_name, name, onexec, test); } - -int aa_setprocattr_permipc(char *fqname) -{ - /* TODO: add ipc permission querying */ - return -ENOTSUPP; -} diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index e1f3d7ef2c54..748bf0ca6c9f 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -15,6 +15,7 @@ #include <linux/audit.h> #include "include/audit.h" +#include "include/context.h" #include "include/resource.h" #include "include/policy.h" @@ -90,17 +91,25 @@ int aa_map_resource(int resource) int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, unsigned int resource, struct rlimit *new_rlim) { + struct aa_profile *task_profile; int error = 0; + rcu_read_lock(); + task_profile = aa_get_profile(aa_cred_profile(__task_cred(task))); + rcu_read_unlock(); + /* TODO: extend resource control to handle other (non current) - * processes. AppArmor rules currently have the implicit assumption - * that the task is setting the resource of the current process + * profiles. AppArmor rules currently have the implicit assumption + * that the task is setting the resource of a task confined with + * the same profile. */ - if ((task != current->group_leader) || + if (profile != task_profile || (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) error = -EACCES; + aa_put_profile(task_profile); + return audit_resource(profile, resource, new_rlim->rlim_max, error); } diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 4bb3a775a996..245c6d92065b 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -17,6 +17,21 @@ config INTEGRITY_SIGNATURE This is useful for evm and module keyrings, when keys are usually only added from initramfs. +config INTEGRITY_AUDIT + bool "Enables integrity auditing support " + depends on INTEGRITY && AUDIT + default y + help + In addition to enabling integrity auditing support, this + option adds a kernel parameter 'integrity_audit', which + controls the level of integrity auditing messages. + 0 - basic integrity auditing messages (default) + 1 - additional integrity auditing messages + + Additional informational integrity auditing messages would + be enabled by specifying 'integrity_audit=1' on the kernel + command line. + config INTEGRITY_ASYMMETRIC_KEYS boolean "Enable asymmetric keys support" depends on INTEGRITY_SIGNATURE diff --git a/security/integrity/Makefile b/security/integrity/Makefile index ebb6409b3fcb..0f9cffb1f9ad 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_INTEGRITY) += integrity.o +obj-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index cdbde1762189..df0fa451a871 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/crypto.h> +#include <linux/audit.h> #include <linux/xattr.h> #include <linux/integrity.h> #include <linux/evm.h> @@ -24,6 +25,9 @@ int evm_initialized; +static char *integrity_status_msg[] = { + "pass", "fail", "no_label", "no_xattrs", "unknown" +}; char *evm_hmac = "hmac(sha1)"; char *evm_hash = "sha1"; int evm_hmac_version = CONFIG_EVM_HMAC_VERSION; @@ -262,9 +266,15 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, if ((evm_status == INTEGRITY_PASS) || (evm_status == INTEGRITY_NOXATTRS)) return 0; - return -EPERM; + goto out; } evm_status = evm_verify_current_integrity(dentry); +out: + if (evm_status != INTEGRITY_PASS) + integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode, + dentry->d_name.name, "appraise_metadata", + integrity_status_msg[evm_status], + -EPERM, 0); return evm_status == INTEGRITY_PASS ? 0 : -EPERM; } @@ -357,6 +367,9 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) if ((evm_status == INTEGRITY_PASS) || (evm_status == INTEGRITY_NOXATTRS)) return 0; + integrity_audit_msg(AUDIT_INTEGRITY_METADATA, dentry->d_inode, + dentry->d_name.name, "appraise_metadata", + integrity_status_msg[evm_status], -EPERM, 0); return -EPERM; } diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index d232c73647ae..39196abaff0d 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -38,18 +38,6 @@ config IMA_MEASURE_PCR_IDX that IMA uses to maintain the integrity aggregate of the measurement list. If unsure, use the default 10. -config IMA_AUDIT - bool "Enables auditing support" - depends on IMA - depends on AUDIT - default y - help - This option adds a kernel parameter 'ima_audit', which - allows informational auditing messages to be enabled - at boot. If this option is selected, informational integrity - auditing messages can be enabled with 'ima_audit=1' on - the kernel command line. - config IMA_LSM_RULES bool depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK) diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 3f2ca6bdc384..56dfee7cbf61 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -7,5 +7,4 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o -ima-$(CONFIG_IMA_AUDIT) += ima_audit.o ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index a41c9c18e5e0..b3dd616560f7 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -62,20 +62,6 @@ struct ima_queue_entry { }; extern struct list_head ima_measurements; /* list of all measurements */ -#ifdef CONFIG_IMA_AUDIT -/* declarations */ -void integrity_audit_msg(int audit_msgno, struct inode *inode, - const unsigned char *fname, const char *op, - const char *cause, int result, int info); -#else -static inline void integrity_audit_msg(int audit_msgno, struct inode *inode, - const unsigned char *fname, - const char *op, const char *cause, - int result, int info) -{ -} -#endif - /* Internal IMA function definitions */ int ima_init(void); void ima_cleanup(void); diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 84c37c4db914..c42fb7a70dee 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -113,5 +113,19 @@ static inline int asymmetric_verify(struct key *keyring, const char *sig, } #endif +#ifdef CONFIG_INTEGRITY_AUDIT +/* declarations */ +void integrity_audit_msg(int audit_msgno, struct inode *inode, + const unsigned char *fname, const char *op, + const char *cause, int result, int info); +#else +static inline void integrity_audit_msg(int audit_msgno, struct inode *inode, + const unsigned char *fname, + const char *op, const char *cause, + int result, int info) +{ +} +#endif + /* set during initialization */ extern int iint_initialized; diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/integrity_audit.c index c586faae8fd6..d7efb30404aa 100644 --- a/security/integrity/ima/ima_audit.c +++ b/security/integrity/integrity_audit.c @@ -13,20 +13,20 @@ #include <linux/fs.h> #include <linux/gfp.h> #include <linux/audit.h> -#include "ima.h" +#include "integrity.h" -static int ima_audit; +static int integrity_audit_info; /* ima_audit_setup - enable informational auditing messages */ -static int __init ima_audit_setup(char *str) +static int __init integrity_audit_setup(char *str) { unsigned long audit; if (!strict_strtoul(str, 0, &audit)) - ima_audit = audit ? 1 : 0; + integrity_audit_info = audit ? 1 : 0; return 1; } -__setup("ima_audit=", ima_audit_setup); +__setup("integrity_audit=", integrity_audit_setup); void integrity_audit_msg(int audit_msgno, struct inode *inode, const unsigned char *fname, const char *op, @@ -34,7 +34,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode, { struct audit_buffer *ab; - if (!ima_audit && audit_info == 1) /* Skip informational messages */ + if (!integrity_audit_info && audit_info == 1) /* Skip info messages */ return; ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno); diff --git a/security/smack/smack.h b/security/smack/smack.h index 8ad30955e15d..339614c76e63 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -29,6 +29,38 @@ #define SMK_LONGLABEL 256 /* + * This is the repository for labels seen so that it is + * not necessary to keep allocating tiny chuncks of memory + * and so that they can be shared. + * + * Labels are never modified in place. Anytime a label + * is imported (e.g. xattrset on a file) the list is checked + * for it and it is added if it doesn't exist. The address + * is passed out in either case. Entries are added, but + * never deleted. + * + * Since labels are hanging around anyway it doesn't + * hurt to maintain a secid for those awkward situations + * where kernel components that ought to use LSM independent + * interfaces don't. The secid should go away when all of + * these components have been repaired. + * + * The cipso value associated with the label gets stored here, too. + * + * Keep the access rules for this subject label here so that + * the entire set of rules does not need to be examined every + * time. + */ +struct smack_known { + struct list_head list; + char *smk_known; + u32 smk_secid; + struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ + struct list_head smk_rules; /* access rules */ + struct mutex smk_rules_lock; /* lock for rules */ +}; + +/* * Maximum number of bytes for the levels in a CIPSO IP option. * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is * bigger than can be used, and 24 is the next lower multiple @@ -46,25 +78,25 @@ struct superblock_smack { }; struct socket_smack { - char *smk_out; /* outbound label */ - char *smk_in; /* inbound label */ - char *smk_packet; /* TCP peer label */ + struct smack_known *smk_out; /* outbound label */ + char *smk_in; /* inbound label */ + char *smk_packet; /* TCP peer label */ }; /* * Inode smack data */ struct inode_smack { - char *smk_inode; /* label of the fso */ - char *smk_task; /* label of the task */ - char *smk_mmap; /* label of the mmap domain */ - struct mutex smk_lock; /* initialization lock */ - int smk_flags; /* smack inode flags */ + char *smk_inode; /* label of the fso */ + struct smack_known *smk_task; /* label of the task */ + struct smack_known *smk_mmap; /* label of the mmap domain */ + struct mutex smk_lock; /* initialization lock */ + int smk_flags; /* smack inode flags */ }; struct task_smack { - char *smk_task; /* label for access control */ - char *smk_forked; /* label when forked */ + struct smack_known *smk_task; /* label for access control */ + struct smack_known *smk_forked; /* label when forked */ struct list_head smk_rules; /* per task access rules */ struct mutex smk_rules_lock; /* lock for the rules */ }; @@ -78,7 +110,7 @@ struct task_smack { */ struct smack_rule { struct list_head list; - char *smk_subject; + struct smack_known *smk_subject; char *smk_object; int smk_access; }; @@ -94,35 +126,14 @@ struct smk_netlbladdr { }; /* - * This is the repository for labels seen so that it is - * not necessary to keep allocating tiny chuncks of memory - * and so that they can be shared. - * - * Labels are never modified in place. Anytime a label - * is imported (e.g. xattrset on a file) the list is checked - * for it and it is added if it doesn't exist. The address - * is passed out in either case. Entries are added, but - * never deleted. - * - * Since labels are hanging around anyway it doesn't - * hurt to maintain a secid for those awkward situations - * where kernel components that ought to use LSM independent - * interfaces don't. The secid should go away when all of - * these components have been repaired. - * - * The cipso value associated with the label gets stored here, too. - * - * Keep the access rules for this subject label here so that - * the entire set of rules does not need to be examined every - * time. + * An entry in the table identifying ports. */ -struct smack_known { - struct list_head list; - char *smk_known; - u32 smk_secid; - struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ - struct list_head smk_rules; /* access rules */ - struct mutex smk_rules_lock; /* lock for rules */ +struct smk_port_label { + struct list_head list; + struct sock *smk_sock; /* socket initialized on */ + unsigned short smk_port; /* the port number */ + char *smk_in; /* incoming label */ + struct smack_known *smk_out; /* outgoing label */ }; /* @@ -132,6 +143,7 @@ struct smack_known { #define SMK_FSFLOOR "smackfsfloor=" #define SMK_FSHAT "smackfshat=" #define SMK_FSROOT "smackfsroot=" +#define SMK_FSTRANS "smackfstransmute=" #define SMACK_CIPSO_OPTION "-CIPSO" @@ -203,9 +215,9 @@ struct inode_smack *new_inode_smack(char *); * These functions are in smack_access.c */ int smk_access_entry(char *, char *, struct list_head *); -int smk_access(char *, char *, int, struct smk_audit_info *); +int smk_access(struct smack_known *, char *, int, struct smk_audit_info *); int smk_curacc(char *, u32, struct smk_audit_info *); -char *smack_from_secid(const u32); +struct smack_known *smack_from_secid(const u32); char *smk_parse_smack(const char *string, int len); int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); char *smk_import(const char *, int); @@ -218,7 +230,7 @@ u32 smack_to_secid(const char *); */ extern int smack_cipso_direct; extern int smack_cipso_mapped; -extern char *smack_net_ambient; +extern struct smack_known *smack_net_ambient; extern char *smack_onlycap; extern const char *smack_cipso_option; @@ -254,17 +266,17 @@ static inline char *smk_of_inode(const struct inode *isp) } /* - * Present a pointer to the smack label in an task blob. + * Present a pointer to the smack label entry in an task blob. */ -static inline char *smk_of_task(const struct task_smack *tsp) +static inline struct smack_known *smk_of_task(const struct task_smack *tsp) { return tsp->smk_task; } /* - * Present a pointer to the forked smack label in an task blob. + * Present a pointer to the forked smack label entry in an task blob. */ -static inline char *smk_of_forked(const struct task_smack *tsp) +static inline struct smack_known *smk_of_forked(const struct task_smack *tsp) { return tsp->smk_forked; } @@ -272,7 +284,7 @@ static inline char *smk_of_forked(const struct task_smack *tsp) /* * Present a pointer to the smack label in the current task blob. */ -static inline char *smk_of_current(void) +static inline struct smack_known *smk_of_current(void) { return smk_of_task(current_security()); } @@ -283,9 +295,11 @@ static inline char *smk_of_current(void) */ static inline int smack_privileged(int cap) { + struct smack_known *skp = smk_of_current(); + if (!capable(cap)) return 0; - if (smack_onlycap == NULL || smack_onlycap == smk_of_current()) + if (smack_onlycap == NULL || smack_onlycap == skp->smk_known) return 1; return 0; } diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 2e397a88d410..6a0377f38620 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -93,7 +93,7 @@ int smk_access_entry(char *subject_label, char *object_label, list_for_each_entry_rcu(srp, rule_list, list) { if (srp->smk_object == object_label && - srp->smk_subject == subject_label) { + srp->smk_subject->smk_known == subject_label) { may = srp->smk_access; break; } @@ -104,7 +104,7 @@ int smk_access_entry(char *subject_label, char *object_label, /** * smk_access - determine if a subject has a specific access to an object - * @subject_label: a pointer to the subject's Smack label + * @subject_known: a pointer to the subject's Smack label entry * @object_label: a pointer to the object's Smack label * @request: the access requested, in "MAY" format * @a : a pointer to the audit data @@ -115,10 +115,9 @@ int smk_access_entry(char *subject_label, char *object_label, * * Smack labels are shared on smack_list */ -int smk_access(char *subject_label, char *object_label, int request, - struct smk_audit_info *a) +int smk_access(struct smack_known *subject_known, char *object_label, + int request, struct smk_audit_info *a) { - struct smack_known *skp; int may = MAY_NOT; int rc = 0; @@ -127,7 +126,7 @@ int smk_access(char *subject_label, char *object_label, int request, * * A star subject can't access any object. */ - if (subject_label == smack_known_star.smk_known) { + if (subject_known == &smack_known_star) { rc = -EACCES; goto out_audit; } @@ -137,7 +136,7 @@ int smk_access(char *subject_label, char *object_label, int request, * An internet subject can access any object. */ if (object_label == smack_known_web.smk_known || - subject_label == smack_known_web.smk_known) + subject_known == &smack_known_web) goto out_audit; /* * A star object can be accessed by any subject. @@ -148,7 +147,7 @@ int smk_access(char *subject_label, char *object_label, int request, * An object can be accessed in any way by a subject * with the same label. */ - if (subject_label == object_label) + if (subject_known->smk_known == object_label) goto out_audit; /* * A hat subject can read any object. @@ -157,7 +156,7 @@ int smk_access(char *subject_label, char *object_label, int request, if ((request & MAY_ANYREAD) == request) { if (object_label == smack_known_floor.smk_known) goto out_audit; - if (subject_label == smack_known_hat.smk_known) + if (subject_known == &smack_known_hat) goto out_audit; } /* @@ -167,9 +166,9 @@ int smk_access(char *subject_label, char *object_label, int request, * good. A negative response from smk_access_entry() * indicates there is no entry for this pair. */ - skp = smk_find_entry(subject_label); rcu_read_lock(); - may = smk_access_entry(subject_label, object_label, &skp->smk_rules); + may = smk_access_entry(subject_known->smk_known, object_label, + &subject_known->smk_rules); rcu_read_unlock(); if (may > 0 && (request & may) == request) @@ -179,7 +178,8 @@ int smk_access(char *subject_label, char *object_label, int request, out_audit: #ifdef CONFIG_AUDIT if (a) - smack_log(subject_label, object_label, request, rc, a); + smack_log(subject_known->smk_known, object_label, request, + rc, a); #endif return rc; } @@ -198,20 +198,21 @@ out_audit: int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) { struct task_smack *tsp = current_security(); - char *sp = smk_of_task(tsp); + struct smack_known *skp = smk_of_task(tsp); int may; int rc; /* * Check the global rule list */ - rc = smk_access(sp, obj_label, mode, NULL); + rc = smk_access(skp, obj_label, mode, NULL); if (rc == 0) { /* * If there is an entry in the task's rule list * it can further restrict access. */ - may = smk_access_entry(sp, obj_label, &tsp->smk_rules); + may = smk_access_entry(skp->smk_known, obj_label, + &tsp->smk_rules); if (may < 0) goto out_audit; if ((mode & may) == mode) @@ -228,7 +229,7 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) out_audit: #ifdef CONFIG_AUDIT if (a) - smack_log(sp, obj_label, mode, rc, a); + smack_log(skp->smk_known, obj_label, mode, rc, a); #endif return rc; } @@ -402,6 +403,8 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, sap->flags |= NETLBL_SECATTR_MLS_CAT; sap->attr.mls.lvl = level; sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (!sap->attr.mls.cat) + return -ENOMEM; sap->attr.mls.cat->startbit = 0; for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++) @@ -513,10 +516,10 @@ char *smk_import(const char *string, int len) * smack_from_secid - find the Smack label associated with a secid * @secid: an integer that might be associated with a Smack label * - * Returns a pointer to the appropriate Smack label if there is one, + * Returns a pointer to the appropriate Smack label entry if there is one, * otherwise a pointer to the invalid Smack label. */ -char *smack_from_secid(const u32 secid) +struct smack_known *smack_from_secid(const u32 secid) { struct smack_known *skp; @@ -524,7 +527,7 @@ char *smack_from_secid(const u32 secid) list_for_each_entry_rcu(skp, &smack_known_list, list) { if (skp->smk_secid == secid) { rcu_read_unlock(); - return skp->smk_known; + return skp; } } @@ -533,7 +536,7 @@ char *smack_from_secid(const u32 secid) * of a secid that is not on the list. */ rcu_read_unlock(); - return smack_known_invalid.smk_known; + return &smack_known_invalid; } /** diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index d52c780bdb78..6a083303501d 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -27,10 +27,13 @@ #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> +#include <linux/dccp.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/pipe_fs_i.h> #include <net/cipso_ipv4.h> +#include <net/ip.h> +#include <net/ipv6.h> #include <linux/audit.h> #include <linux/magic.h> #include <linux/dcache.h> @@ -45,6 +48,12 @@ #define TRANS_TRUE "TRUE" #define TRANS_TRUE_SIZE 4 +#define SMK_CONNECTING 0 +#define SMK_RECEIVING 1 +#define SMK_SENDING 2 + +LIST_HEAD(smk_ipv6_port_list); + /** * smk_fetch - Fetch the smack label from a file. * @ip: a pointer to the inode @@ -53,11 +62,12 @@ * Returns a pointer to the master list entry for the Smack label * or NULL if there was no label to fetch. */ -static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp) +static struct smack_known *smk_fetch(const char *name, struct inode *ip, + struct dentry *dp) { int rc; char *buffer; - char *result = NULL; + struct smack_known *skp = NULL; if (ip->i_op->getxattr == NULL) return NULL; @@ -68,11 +78,11 @@ static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp) rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL); if (rc > 0) - result = smk_import(buffer, rc); + skp = smk_import_entry(buffer, rc); kfree(buffer); - return result; + return skp; } /** @@ -102,7 +112,8 @@ struct inode_smack *new_inode_smack(char *smack) * * Returns the new blob or NULL if there's no memory available */ -static struct task_smack *new_task_smack(char *task, char *forked, gfp_t gfp) +static struct task_smack *new_task_smack(struct smack_known *task, + struct smack_known *forked, gfp_t gfp) { struct task_smack *tsp; @@ -164,17 +175,17 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { int rc; struct smk_audit_info ad; - char *tsp; + struct smack_known *skp; rc = cap_ptrace_access_check(ctp, mode); if (rc != 0) return rc; - tsp = smk_of_task(task_security(ctp)); + skp = smk_of_task(task_security(ctp)); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, ctp); - rc = smk_curacc(tsp, MAY_READWRITE, &ad); + rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad); return rc; } @@ -190,17 +201,17 @@ static int smack_ptrace_traceme(struct task_struct *ptp) { int rc; struct smk_audit_info ad; - char *tsp; + struct smack_known *skp; rc = cap_ptrace_traceme(ptp); if (rc != 0) return rc; - tsp = smk_of_task(task_security(ptp)); + skp = smk_of_task(task_security(ptp)); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, ptp); - rc = smk_curacc(tsp, MAY_READWRITE, &ad); + rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad); return rc; } @@ -215,12 +226,12 @@ static int smack_ptrace_traceme(struct task_struct *ptp) static int smack_syslog(int typefrom_file) { int rc = 0; - char *sp = smk_of_current(); + struct smack_known *skp = smk_of_current(); if (smack_privileged(CAP_MAC_OVERRIDE)) return 0; - if (sp != smack_known_floor.smk_known) + if (skp != &smack_known_floor) rc = -EACCES; return rc; @@ -250,8 +261,9 @@ static int smack_sb_alloc_security(struct super_block *sb) sbsp->smk_default = smack_known_floor.smk_known; sbsp->smk_floor = smack_known_floor.smk_known; sbsp->smk_hat = smack_known_hat.smk_known; - sbsp->smk_initialized = 0; - + /* + * smk_initialized will be zero from kzalloc. + */ sb->s_security = sbsp; return 0; @@ -295,6 +307,8 @@ static int smack_sb_copy_data(char *orig, char *smackopts) dp = smackopts; else if (strstr(cp, SMK_FSROOT) == cp) dp = smackopts; + else if (strstr(cp, SMK_FSTRANS) == cp) + dp = smackopts; else dp = otheropts; @@ -330,8 +344,9 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) char *op; char *commap; char *nsp; + int transmute = 0; - if (sp->smk_initialized != 0) + if (sp->smk_initialized) return 0; sp->smk_initialized = 1; @@ -362,6 +377,13 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) nsp = smk_import(op, 0); if (nsp != NULL) sp->smk_root = nsp; + } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) { + op += strlen(SMK_FSTRANS); + nsp = smk_import(op, 0); + if (nsp != NULL) { + sp->smk_root = nsp; + transmute = 1; + } } } @@ -369,11 +391,15 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) * Initialize the root inode. */ isp = inode->i_security; - if (isp == NULL) + if (inode->i_security == NULL) { inode->i_security = new_inode_smack(sp->smk_root); - else + isp = inode->i_security; + } else isp->smk_inode = sp->smk_root; + if (transmute) + isp->smk_flags |= SMK_INODE_TRANSMUTE; + return 0; } @@ -524,7 +550,9 @@ static int smack_bprm_secureexec(struct linux_binprm *bprm) */ static int smack_inode_alloc_security(struct inode *inode) { - inode->i_security = new_inode_smack(smk_of_current()); + struct smack_known *skp = smk_of_current(); + + inode->i_security = new_inode_smack(skp->smk_known); if (inode->i_security == NULL) return -ENOMEM; return 0; @@ -557,9 +585,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, char **name, void **value, size_t *len) { - struct smack_known *skp; struct inode_smack *issp = inode->i_security; - char *csp = smk_of_current(); + struct smack_known *skp = smk_of_current(); char *isp = smk_of_inode(inode); char *dsp = smk_of_inode(dir); int may; @@ -571,9 +598,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, } if (value) { - skp = smk_find_entry(csp); rcu_read_lock(); - may = smk_access_entry(csp, dsp, &skp->smk_rules); + may = smk_access_entry(skp->smk_known, dsp, &skp->smk_rules); rcu_read_unlock(); /* @@ -862,29 +888,31 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { - char *nsp; + struct smack_known *skp; struct inode_smack *isp = dentry->d_inode->i_security; + if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { + isp->smk_flags |= SMK_INODE_TRANSMUTE; + return; + } + + skp = smk_import_entry(value, size); if (strcmp(name, XATTR_NAME_SMACK) == 0) { - nsp = smk_import(value, size); - if (nsp != NULL) - isp->smk_inode = nsp; + if (skp != NULL) + isp->smk_inode = skp->smk_known; else isp->smk_inode = smack_known_invalid.smk_known; } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { - nsp = smk_import(value, size); - if (nsp != NULL) - isp->smk_task = nsp; + if (skp != NULL) + isp->smk_task = skp; else - isp->smk_task = smack_known_invalid.smk_known; + isp->smk_task = &smack_known_invalid; } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { - nsp = smk_import(value, size); - if (nsp != NULL) - isp->smk_mmap = nsp; + if (skp != NULL) + isp->smk_mmap = skp; else - isp->smk_mmap = smack_known_invalid.smk_known; - } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) - isp->smk_flags |= SMK_INODE_TRANSMUTE; + isp->smk_mmap = &smack_known_invalid; + } return; } @@ -990,7 +1018,7 @@ static int smack_inode_getsecurity(const struct inode *inode, if (strcmp(name, XATTR_SMACK_IPIN) == 0) isp = ssp->smk_in; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) - isp = ssp->smk_out; + isp = ssp->smk_out->smk_known; else return -EOPNOTSUPP; @@ -1070,7 +1098,9 @@ static int smack_file_permission(struct file *file, int mask) */ static int smack_file_alloc_security(struct file *file) { - file->f_security = smk_of_current(); + struct smack_known *skp = smk_of_current(); + + file->f_security = skp->smk_known; return 0; } @@ -1181,10 +1211,9 @@ static int smack_mmap_file(struct file *file, unsigned long flags) { struct smack_known *skp; + struct smack_known *mkp; struct smack_rule *srp; struct task_smack *tsp; - char *sp; - char *msmack; char *osmack; struct inode_smack *isp; int may; @@ -1198,11 +1227,10 @@ static int smack_mmap_file(struct file *file, isp = file_inode(file)->i_security; if (isp->smk_mmap == NULL) return 0; - msmack = isp->smk_mmap; + mkp = isp->smk_mmap; tsp = current_security(); - sp = smk_of_current(); - skp = smk_find_entry(sp); + skp = smk_of_current(); rc = 0; rcu_read_lock(); @@ -1216,13 +1244,13 @@ static int smack_mmap_file(struct file *file, /* * Matching labels always allows access. */ - if (msmack == osmack) + if (mkp->smk_known == osmack) continue; /* * If there is a matching local rule take * that into account as well. */ - may = smk_access_entry(srp->smk_subject, osmack, + may = smk_access_entry(srp->smk_subject->smk_known, osmack, &tsp->smk_rules); if (may == -ENOENT) may = srp->smk_access; @@ -1240,8 +1268,8 @@ static int smack_mmap_file(struct file *file, * If there isn't one a SMACK64MMAP subject * can't have as much access as current. */ - skp = smk_find_entry(msmack); - mmay = smk_access_entry(msmack, osmack, &skp->smk_rules); + mmay = smk_access_entry(mkp->smk_known, osmack, + &mkp->smk_rules); if (mmay == -ENOENT) { rc = -EACCES; break; @@ -1250,7 +1278,8 @@ static int smack_mmap_file(struct file *file, * If there is a local entry it modifies the * potential access, too. */ - tmay = smk_access_entry(msmack, osmack, &tsp->smk_rules); + tmay = smk_access_entry(mkp->smk_known, osmack, + &tsp->smk_rules); if (tmay != -ENOENT) mmay &= tmay; @@ -1279,7 +1308,9 @@ static int smack_mmap_file(struct file *file, */ static int smack_file_set_fowner(struct file *file) { - file->f_security = smk_of_current(); + struct smack_known *skp = smk_of_current(); + + file->f_security = skp->smk_known; return 0; } @@ -1297,9 +1328,10 @@ static int smack_file_set_fowner(struct file *file) static int smack_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { + struct smack_known *skp; + struct smack_known *tkp = smk_of_task(tsk->cred->security); struct file *file; int rc; - char *tsp = smk_of_task(tsk->cred->security); struct smk_audit_info ad; /* @@ -1308,13 +1340,14 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, file = container_of(fown, struct file, f_owner); /* we don't log here as rc can be overriden */ - rc = smk_access(file->f_security, tsp, MAY_WRITE, NULL); + skp = smk_find_entry(file->f_security); + rc = smk_access(skp, tkp->smk_known, MAY_WRITE, NULL); if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) rc = 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, tsk); - smack_log(file->f_security, tsp, MAY_WRITE, rc, &ad); + smack_log(file->f_security, tkp->smk_known, MAY_WRITE, rc, &ad); return rc; } @@ -1469,12 +1502,12 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old) static int smack_kernel_act_as(struct cred *new, u32 secid) { struct task_smack *new_tsp = new->security; - char *smack = smack_from_secid(secid); + struct smack_known *skp = smack_from_secid(secid); - if (smack == NULL) + if (skp == NULL) return -EINVAL; - new_tsp->smk_task = smack; + new_tsp->smk_task = skp; return 0; } @@ -1492,8 +1525,8 @@ static int smack_kernel_create_files_as(struct cred *new, struct inode_smack *isp = inode->i_security; struct task_smack *tsp = new->security; - tsp->smk_forked = isp->smk_inode; - tsp->smk_task = isp->smk_inode; + tsp->smk_forked = smk_find_entry(isp->smk_inode); + tsp->smk_task = tsp->smk_forked; return 0; } @@ -1509,10 +1542,11 @@ static int smk_curacc_on_task(struct task_struct *p, int access, const char *caller) { struct smk_audit_info ad; + struct smack_known *skp = smk_of_task(task_security(p)); smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); - return smk_curacc(smk_of_task(task_security(p)), access, &ad); + return smk_curacc(skp->smk_known, access, &ad); } /** @@ -1558,7 +1592,9 @@ static int smack_task_getsid(struct task_struct *p) */ static void smack_task_getsecid(struct task_struct *p, u32 *secid) { - *secid = smack_to_secid(smk_of_task(task_security(p))); + struct smack_known *skp = smk_of_task(task_security(p)); + + *secid = skp->smk_secid; } /** @@ -1662,6 +1698,8 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, int sig, u32 secid) { struct smk_audit_info ad; + struct smack_known *skp; + struct smack_known *tkp = smk_of_task(task_security(p)); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); @@ -1670,15 +1708,14 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * can write the receiver. */ if (secid == 0) - return smk_curacc(smk_of_task(task_security(p)), MAY_WRITE, - &ad); + return smk_curacc(tkp->smk_known, MAY_WRITE, &ad); /* * If the secid isn't 0 we're dealing with some USB IO * specific behavior. This is not clean. For one thing * we can't take privilege into account. */ - return smk_access(smack_from_secid(secid), - smk_of_task(task_security(p)), MAY_WRITE, &ad); + skp = smack_from_secid(secid); + return smk_access(skp, tkp->smk_known, MAY_WRITE, &ad); } /** @@ -1710,7 +1747,9 @@ static int smack_task_wait(struct task_struct *p) static void smack_task_to_inode(struct task_struct *p, struct inode *inode) { struct inode_smack *isp = inode->i_security; - isp->smk_inode = smk_of_task(task_security(p)); + struct smack_known *skp = smk_of_task(task_security(p)); + + isp->smk_inode = skp->smk_known; } /* @@ -1729,15 +1768,15 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) */ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) { - char *csp = smk_of_current(); + struct smack_known *skp = smk_of_current(); struct socket_smack *ssp; ssp = kzalloc(sizeof(struct socket_smack), gfp_flags); if (ssp == NULL) return -ENOMEM; - ssp->smk_in = csp; - ssp->smk_out = csp; + ssp->smk_in = skp->smk_known; + ssp->smk_out = skp; ssp->smk_packet = NULL; sk->sk_security = ssp; @@ -1824,7 +1863,7 @@ static int smack_netlabel(struct sock *sk, int labeled) labeled == SMACK_UNLABELED_SOCKET) netlbl_sock_delattr(sk); else { - skp = smk_find_entry(ssp->smk_out); + skp = ssp->smk_out; rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel); } @@ -1847,6 +1886,7 @@ static int smack_netlabel(struct sock *sk, int labeled) */ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) { + struct smack_known *skp; int rc; int sk_lbl; char *hostsp; @@ -1865,7 +1905,8 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) ad.a.u.net->v4info.daddr = sap->sin_addr.s_addr; #endif sk_lbl = SMACK_UNLABELED_SOCKET; - rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE, &ad); + skp = ssp->smk_out; + rc = smk_access(skp, hostsp, MAY_WRITE, &ad); } else { sk_lbl = SMACK_CIPSO_SOCKET; rc = 0; @@ -1878,6 +1919,155 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) } /** + * smk_ipv6_port_label - Smack port access table management + * @sock: socket + * @address: address + * + * Create or update the port list entry + */ +static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) +{ + struct sock *sk = sock->sk; + struct sockaddr_in6 *addr6; + struct socket_smack *ssp = sock->sk->sk_security; + struct smk_port_label *spp; + unsigned short port = 0; + + if (address == NULL) { + /* + * This operation is changing the Smack information + * on the bound socket. Take the changes to the port + * as well. + */ + list_for_each_entry(spp, &smk_ipv6_port_list, list) { + if (sk != spp->smk_sock) + continue; + spp->smk_in = ssp->smk_in; + spp->smk_out = ssp->smk_out; + return; + } + /* + * A NULL address is only used for updating existing + * bound entries. If there isn't one, it's OK. + */ + return; + } + + addr6 = (struct sockaddr_in6 *)address; + port = ntohs(addr6->sin6_port); + /* + * This is a special case that is safely ignored. + */ + if (port == 0) + return; + + /* + * Look for an existing port list entry. + * This is an indication that a port is getting reused. + */ + list_for_each_entry(spp, &smk_ipv6_port_list, list) { + if (spp->smk_port != port) + continue; + spp->smk_port = port; + spp->smk_sock = sk; + spp->smk_in = ssp->smk_in; + spp->smk_out = ssp->smk_out; + return; + } + + /* + * A new port entry is required. + */ + spp = kzalloc(sizeof(*spp), GFP_KERNEL); + if (spp == NULL) + return; + + spp->smk_port = port; + spp->smk_sock = sk; + spp->smk_in = ssp->smk_in; + spp->smk_out = ssp->smk_out; + + list_add(&spp->list, &smk_ipv6_port_list); + return; +} + +/** + * smk_ipv6_port_check - check Smack port access + * @sock: socket + * @address: address + * + * Create or update the port list entry + */ +static int smk_ipv6_port_check(struct sock *sk, struct sockaddr *address, + int act) +{ + __be16 *bep; + __be32 *be32p; + struct sockaddr_in6 *addr6; + struct smk_port_label *spp; + struct socket_smack *ssp = sk->sk_security; + struct smack_known *skp; + unsigned short port = 0; + char *object; + struct smk_audit_info ad; +#ifdef CONFIG_AUDIT + struct lsm_network_audit net; +#endif + + if (act == SMK_RECEIVING) { + skp = smack_net_ambient; + object = ssp->smk_in; + } else { + skp = ssp->smk_out; + object = smack_net_ambient->smk_known; + } + + /* + * Get the IP address and port from the address. + */ + addr6 = (struct sockaddr_in6 *)address; + port = ntohs(addr6->sin6_port); + bep = (__be16 *)(&addr6->sin6_addr); + be32p = (__be32 *)(&addr6->sin6_addr); + + /* + * It's remote, so port lookup does no good. + */ + if (be32p[0] || be32p[1] || be32p[2] || bep[6] || ntohs(bep[7]) != 1) + goto auditout; + + /* + * It's local so the send check has to have passed. + */ + if (act == SMK_RECEIVING) { + skp = &smack_known_web; + goto auditout; + } + + list_for_each_entry(spp, &smk_ipv6_port_list, list) { + if (spp->smk_port != port) + continue; + object = spp->smk_in; + if (act == SMK_CONNECTING) + ssp->smk_packet = spp->smk_out->smk_known; + break; + } + +auditout: + +#ifdef CONFIG_AUDIT + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); + ad.a.u.net->family = sk->sk_family; + ad.a.u.net->dport = port; + if (act == SMK_RECEIVING) + ad.a.u.net->v6info.saddr = addr6->sin6_addr; + else + ad.a.u.net->v6info.daddr = addr6->sin6_addr; +#endif + return smk_access(skp, object, MAY_WRITE, &ad); +} + +/** * smack_inode_setsecurity - set smack xattrs * @inode: the object * @name: attribute name @@ -1892,7 +2082,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) static int smack_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - char *sp; + struct smack_known *skp; struct inode_smack *nsp = inode->i_security; struct socket_smack *ssp; struct socket *sock; @@ -1901,12 +2091,12 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, if (value == NULL || size > SMK_LONGLABEL || size == 0) return -EACCES; - sp = smk_import(value, size); - if (sp == NULL) + skp = smk_import_entry(value, size); + if (skp == NULL) return -EINVAL; if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { - nsp->smk_inode = sp; + nsp->smk_inode = skp->smk_known; nsp->smk_flags |= SMK_INODE_INSTANT; return 0; } @@ -1923,10 +2113,10 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, ssp = sock->sk->sk_security; if (strcmp(name, XATTR_SMACK_IPIN) == 0) - ssp->smk_in = sp; + ssp->smk_in = skp->smk_known; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) { - ssp->smk_out = sp; - if (sock->sk->sk_family != PF_UNIX) { + ssp->smk_out = skp; + if (sock->sk->sk_family == PF_INET) { rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); if (rc != 0) printk(KERN_WARNING @@ -1936,6 +2126,9 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, } else return -EOPNOTSUPP; + if (sock->sk->sk_family == PF_INET6) + smk_ipv6_port_label(sock, NULL); + return 0; } @@ -1963,6 +2156,25 @@ static int smack_socket_post_create(struct socket *sock, int family, } /** + * smack_socket_bind - record port binding information. + * @sock: the socket + * @address: the port address + * @addrlen: size of the address + * + * Records the label bound to a port. + * + * Returns 0 + */ +static int smack_socket_bind(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + if (sock->sk != NULL && sock->sk->sk_family == PF_INET6) + smk_ipv6_port_label(sock, address); + + return 0; +} + +/** * smack_socket_connect - connect access check * @sock: the socket * @sap: the other end @@ -1975,12 +2187,24 @@ static int smack_socket_post_create(struct socket *sock, int family, static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, int addrlen) { - if (sock->sk == NULL || sock->sk->sk_family != PF_INET) + int rc = 0; + + if (sock->sk == NULL) return 0; - if (addrlen < sizeof(struct sockaddr_in)) - return -EINVAL; - return smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap); + switch (sock->sk->sk_family) { + case PF_INET: + if (addrlen < sizeof(struct sockaddr_in)) + return -EINVAL; + rc = smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap); + break; + case PF_INET6: + if (addrlen < sizeof(struct sockaddr_in6)) + return -EINVAL; + rc = smk_ipv6_port_check(sock->sk, sap, SMK_CONNECTING); + break; + } + return rc; } /** @@ -2011,7 +2235,9 @@ static int smack_flags_to_may(int flags) */ static int smack_msg_msg_alloc_security(struct msg_msg *msg) { - msg->security = smk_of_current(); + struct smack_known *skp = smk_of_current(); + + msg->security = skp->smk_known; return 0; } @@ -2046,8 +2272,9 @@ static char *smack_of_shm(struct shmid_kernel *shp) static int smack_shm_alloc_security(struct shmid_kernel *shp) { struct kern_ipc_perm *isp = &shp->shm_perm; + struct smack_known *skp = smk_of_current(); - isp->security = smk_of_current(); + isp->security = skp->smk_known; return 0; } @@ -2169,8 +2396,9 @@ static char *smack_of_sem(struct sem_array *sma) static int smack_sem_alloc_security(struct sem_array *sma) { struct kern_ipc_perm *isp = &sma->sem_perm; + struct smack_known *skp = smk_of_current(); - isp->security = smk_of_current(); + isp->security = skp->smk_known; return 0; } @@ -2287,8 +2515,9 @@ static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops, static int smack_msg_queue_alloc_security(struct msg_queue *msq) { struct kern_ipc_perm *kisp = &msq->q_perm; + struct smack_known *skp = smk_of_current(); - kisp->security = smk_of_current(); + kisp->security = skp->smk_known; return 0; } @@ -2460,8 +2689,8 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) struct super_block *sbp; struct superblock_smack *sbsp; struct inode_smack *isp; - char *csp = smk_of_current(); - char *fetched; + struct smack_known *skp; + struct smack_known *ckp = smk_of_current(); char *final; char trattr[TRANS_TRUE_SIZE]; int transflag = 0; @@ -2528,7 +2757,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * Programs that change smack have to treat the * pty with respect. */ - final = csp; + final = ckp->smk_known; break; case SOCKFS_MAGIC: /* @@ -2583,9 +2812,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * Get the dentry for xattr. */ dp = dget(opt_dentry); - fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp); - if (fetched != NULL) - final = fetched; + skp = smk_fetch(XATTR_NAME_SMACK, inode, dp); + if (skp != NULL) + final = skp->smk_known; /* * Transmuting directory @@ -2625,7 +2854,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) } if (final == NULL) - isp->smk_inode = csp; + isp->smk_inode = ckp->smk_known; else isp->smk_inode = final; @@ -2648,13 +2877,14 @@ unlockandout: */ static int smack_getprocattr(struct task_struct *p, char *name, char **value) { + struct smack_known *skp = smk_of_task(task_security(p)); char *cp; int slen; if (strcmp(name, "current") != 0) return -EINVAL; - cp = kstrdup(smk_of_task(task_security(p)), GFP_KERNEL); + cp = kstrdup(skp->smk_known, GFP_KERNEL); if (cp == NULL) return -ENOMEM; @@ -2680,7 +2910,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, { struct task_smack *tsp; struct cred *new; - char *newsmack; + struct smack_known *skp; /* * Changing another process' Smack value is too dangerous @@ -2698,14 +2928,14 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (strcmp(name, "current") != 0) return -EINVAL; - newsmack = smk_import(value, size); - if (newsmack == NULL) + skp = smk_import_entry(value, size); + if (skp == NULL) return -EINVAL; /* * No process is ever allowed the web ("@") label. */ - if (newsmack == smack_known_web.smk_known) + if (skp == &smack_known_web) return -EPERM; new = prepare_creds(); @@ -2713,7 +2943,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, return -ENOMEM; tsp = new->security; - tsp->smk_task = newsmack; + tsp->smk_task = skp; commit_creds(new); return size; @@ -2731,6 +2961,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, static int smack_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk) { + struct smack_known *skp; struct socket_smack *ssp = sock->sk_security; struct socket_smack *osp = other->sk_security; struct socket_smack *nsp = newsk->sk_security; @@ -2744,15 +2975,17 @@ static int smack_unix_stream_connect(struct sock *sock, smk_ad_setfield_u_net_sk(&ad, other); #endif - if (!smack_privileged(CAP_MAC_OVERRIDE)) - rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); + if (!smack_privileged(CAP_MAC_OVERRIDE)) { + skp = ssp->smk_out; + rc = smk_access(skp, osp->smk_in, MAY_WRITE, &ad); + } /* * Cross reference the peer labels for SO_PEERSEC. */ if (rc == 0) { - nsp->smk_packet = ssp->smk_out; - ssp->smk_packet = osp->smk_out; + nsp->smk_packet = ssp->smk_out->smk_known; + ssp->smk_packet = osp->smk_out->smk_known; } return rc; @@ -2770,8 +3003,8 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) { struct socket_smack *ssp = sock->sk->sk_security; struct socket_smack *osp = other->sk->sk_security; + struct smack_known *skp; struct smk_audit_info ad; - int rc = 0; #ifdef CONFIG_AUDIT struct lsm_network_audit net; @@ -2780,10 +3013,11 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) smk_ad_setfield_u_net_sk(&ad, other->sk); #endif - if (!smack_privileged(CAP_MAC_OVERRIDE)) - rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); + if (smack_privileged(CAP_MAC_OVERRIDE)) + return 0; - return rc; + skp = ssp->smk_out; + return smk_access(skp, osp->smk_in, MAY_WRITE, &ad); } /** @@ -2792,22 +3026,32 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) * @msg: the message * @size: the size of the message * - * Return 0 if the current subject can write to the destination - * host. This is only a question if the destination is a single - * label host. + * Return 0 if the current subject can write to the destination host. + * For IPv4 this is only a question if the destination is a single label host. + * For IPv6 this is a check against the label of the port. */ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; + struct sockaddr *sap = (struct sockaddr *) msg->msg_name; + int rc = 0; /* * Perfectly reasonable for this to be NULL */ - if (sip == NULL || sip->sin_family != AF_INET) + if (sip == NULL) return 0; - return smack_netlabel_send(sock->sk, sip); + switch (sip->sin_family) { + case AF_INET: + rc = smack_netlabel_send(sock->sk, sip); + break; + case AF_INET6: + rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING); + break; + } + return rc; } /** @@ -2815,13 +3059,12 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, * @sap: netlabel secattr * @ssp: socket security information * - * Returns a pointer to a Smack label found on the label list. + * Returns a pointer to a Smack label entry found on the label list. */ -static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, - struct socket_smack *ssp) +static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, + struct socket_smack *ssp) { - struct smack_known *kp; - char *sp; + struct smack_known *skp; int found = 0; if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { @@ -2836,11 +3079,11 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, * ambient value. */ rcu_read_lock(); - list_for_each_entry(kp, &smack_known_list, list) { - if (sap->attr.mls.lvl != kp->smk_netlabel.attr.mls.lvl) + list_for_each_entry(skp, &smack_known_list, list) { + if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) continue; if (memcmp(sap->attr.mls.cat, - kp->smk_netlabel.attr.mls.cat, + skp->smk_netlabel.attr.mls.cat, SMK_CIPSOLEN) != 0) continue; found = 1; @@ -2849,17 +3092,17 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, rcu_read_unlock(); if (found) - return kp->smk_known; + return skp; if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known) - return smack_known_web.smk_known; - return smack_known_star.smk_known; + return &smack_known_web; + return &smack_known_star; } if ((sap->flags & NETLBL_SECATTR_SECID) != 0) { /* * Looks like a fallback, which gives us a secid. */ - sp = smack_from_secid(sap->attr.secid); + skp = smack_from_secid(sap->attr.secid); /* * This has got to be a bug because it is * impossible to specify a fallback without @@ -2867,8 +3110,8 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, * it has a secid, and the only way to get a * secid is from a fallback. */ - BUG_ON(sp == NULL); - return sp; + BUG_ON(skp == NULL); + return skp; } /* * Without guidance regarding the smack value @@ -2878,6 +3121,54 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, return smack_net_ambient; } +static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr *sap) +{ + struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap; + u8 nexthdr; + int offset; + int proto = -EINVAL; + struct ipv6hdr _ipv6h; + struct ipv6hdr *ip6; + __be16 frag_off; + struct tcphdr _tcph, *th; + struct udphdr _udph, *uh; + struct dccp_hdr _dccph, *dh; + + sip->sin6_port = 0; + + offset = skb_network_offset(skb); + ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h); + if (ip6 == NULL) + return -EINVAL; + sip->sin6_addr = ip6->saddr; + + nexthdr = ip6->nexthdr; + offset += sizeof(_ipv6h); + offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off); + if (offset < 0) + return -EINVAL; + + proto = nexthdr; + switch (proto) { + case IPPROTO_TCP: + th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); + if (th != NULL) + sip->sin6_port = th->source; + break; + case IPPROTO_UDP: + uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); + if (uh != NULL) + sip->sin6_port = uh->source; + break; + case IPPROTO_DCCP: + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh != NULL) + sip->sin6_port = dh->dccph_sport; + break; + } + return proto; +} + /** * smack_socket_sock_rcv_skb - Smack packet delivery access check * @sk: socket @@ -2889,43 +3180,52 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { struct netlbl_lsm_secattr secattr; struct socket_smack *ssp = sk->sk_security; - char *csp; - int rc; + struct smack_known *skp; + struct sockaddr sadd; + int rc = 0; struct smk_audit_info ad; #ifdef CONFIG_AUDIT struct lsm_network_audit net; #endif - if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) - return 0; - - /* - * Translate what netlabel gave us. - */ - netlbl_secattr_init(&secattr); + switch (sk->sk_family) { + case PF_INET: + /* + * Translate what netlabel gave us. + */ + netlbl_secattr_init(&secattr); - rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); - if (rc == 0) - csp = smack_from_secattr(&secattr, ssp); - else - csp = smack_net_ambient; + rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); + if (rc == 0) + skp = smack_from_secattr(&secattr, ssp); + else + skp = smack_net_ambient; - netlbl_secattr_destroy(&secattr); + netlbl_secattr_destroy(&secattr); #ifdef CONFIG_AUDIT - smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); - ad.a.u.net->family = sk->sk_family; - ad.a.u.net->netif = skb->skb_iif; - ipv4_skb_to_auditdata(skb, &ad.a, NULL); + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); + ad.a.u.net->family = sk->sk_family; + ad.a.u.net->netif = skb->skb_iif; + ipv4_skb_to_auditdata(skb, &ad.a, NULL); #endif - /* - * Receiving a packet requires that the other end - * be able to write here. Read access is not required. - * This is the simplist possible security model - * for networking. - */ - rc = smk_access(csp, ssp->smk_in, MAY_WRITE, &ad); - if (rc != 0) - netlbl_skbuff_err(skb, rc, 0); + /* + * Receiving a packet requires that the other end + * be able to write here. Read access is not required. + * This is the simplist possible security model + * for networking. + */ + rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); + if (rc != 0) + netlbl_skbuff_err(skb, rc, 0); + break; + case PF_INET6: + rc = smk_skb_to_addr_ipv6(skb, &sadd); + if (rc == IPPROTO_UDP || rc == IPPROTO_TCP) + rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING); + else + rc = 0; + break; + } return rc; } @@ -2979,7 +3279,7 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, { struct netlbl_lsm_secattr secattr; struct socket_smack *ssp = NULL; - char *sp; + struct smack_known *skp; int family = PF_UNSPEC; u32 s = 0; /* 0 is the invalid secid */ int rc; @@ -2995,7 +3295,7 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, if (family == PF_UNIX) { ssp = sock->sk->sk_security; - s = smack_to_secid(ssp->smk_out); + s = ssp->smk_out->smk_secid; } else if (family == PF_INET || family == PF_INET6) { /* * Translate what netlabel gave us. @@ -3005,8 +3305,8 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) { - sp = smack_from_secattr(&secattr, ssp); - s = smack_to_secid(sp); + skp = smack_from_secattr(&secattr, ssp); + s = skp->smk_secid; } netlbl_secattr_destroy(&secattr); } @@ -3027,13 +3327,15 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, static void smack_sock_graft(struct sock *sk, struct socket *parent) { struct socket_smack *ssp; + struct smack_known *skp = smk_of_current(); if (sk == NULL || (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)) return; ssp = sk->sk_security; - ssp->smk_in = ssp->smk_out = smk_of_current(); + ssp->smk_in = skp->smk_known; + ssp->smk_out = skp; /* cssp->smk_packet is already set in smack_inet_csk_clone() */ } @@ -3055,7 +3357,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct netlbl_lsm_secattr secattr; struct sockaddr_in addr; struct iphdr *hdr; - char *sp; char *hsp; int rc; struct smk_audit_info ad; @@ -3063,16 +3364,24 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct lsm_network_audit net; #endif - /* handle mapped IPv4 packets arriving via IPv6 sockets */ - if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) - family = PF_INET; + if (family == PF_INET6) { + /* + * Handle mapped IPv4 packets arriving + * via IPv6 sockets. Don't set up netlabel + * processing on IPv6. + */ + if (skb->protocol == htons(ETH_P_IP)) + family = PF_INET; + else + return 0; + } netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) - sp = smack_from_secattr(&secattr, ssp); + skp = smack_from_secattr(&secattr, ssp); else - sp = smack_known_huh.smk_known; + skp = &smack_known_huh; netlbl_secattr_destroy(&secattr); #ifdef CONFIG_AUDIT @@ -3085,7 +3394,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * Receiving a packet requires that the other end be able to write * here. Read access is not required. */ - rc = smk_access(sp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); if (rc != 0) return rc; @@ -3093,7 +3402,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * Save the peer's label in the request_sock so we can later setup * smk_packet in the child socket so that SO_PEERCRED can report it. */ - req->peer_secid = smack_to_secid(sp); + req->peer_secid = skp->smk_secid; /* * We need to decide if we want to label the incoming connection here @@ -3106,10 +3415,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, hsp = smack_host_label(&addr); rcu_read_unlock(); - if (hsp == NULL) { - skp = smk_find_entry(sp); + if (hsp == NULL) rc = netlbl_req_setattr(req, &skp->smk_netlabel); - } else + else netlbl_req_delattr(req); return rc; @@ -3126,10 +3434,12 @@ static void smack_inet_csk_clone(struct sock *sk, const struct request_sock *req) { struct socket_smack *ssp = sk->sk_security; + struct smack_known *skp; - if (req->peer_secid != 0) - ssp->smk_packet = smack_from_secid(req->peer_secid); - else + if (req->peer_secid != 0) { + skp = smack_from_secid(req->peer_secid); + ssp->smk_packet = skp->smk_known; + } else ssp->smk_packet = NULL; } @@ -3155,7 +3465,9 @@ static void smack_inet_csk_clone(struct sock *sk, static int smack_key_alloc(struct key *key, const struct cred *cred, unsigned long flags) { - key->security = smk_of_task(cred->security); + struct smack_known *skp = smk_of_task(cred->security); + + key->security = skp->smk_known; return 0; } @@ -3184,7 +3496,7 @@ static int smack_key_permission(key_ref_t key_ref, { struct key *keyp; struct smk_audit_info ad; - char *tsp = smk_of_task(cred->security); + struct smack_known *tkp = smk_of_task(cred->security); keyp = key_ref_to_ptr(key_ref); if (keyp == NULL) @@ -3198,15 +3510,14 @@ static int smack_key_permission(key_ref_t key_ref, /* * This should not occur */ - if (tsp == NULL) + if (tkp == NULL) return -EACCES; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY); ad.a.u.key_struct.key = keyp->serial; ad.a.u.key_struct.key_desc = keyp->description; #endif - return smk_access(tsp, keyp->security, - MAY_READWRITE, &ad); + return smk_access(tkp, keyp->security, MAY_READWRITE, &ad); } #endif /* CONFIG_KEYS */ @@ -3288,7 +3599,7 @@ static int smack_audit_rule_known(struct audit_krule *krule) static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, struct audit_context *actx) { - char *smack; + struct smack_known *skp; char *rule = vrule; if (!rule) { @@ -3300,7 +3611,7 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER) return 0; - smack = smack_from_secid(secid); + skp = smack_from_secid(secid); /* * No need to do string comparisons. If a match occurs, @@ -3308,9 +3619,9 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, * label. */ if (op == Audit_equal) - return (rule == smack); + return (rule == skp->smk_known); if (op == Audit_not_equal) - return (rule != smack); + return (rule != skp->smk_known); return 0; } @@ -3338,11 +3649,11 @@ static void smack_audit_rule_free(void *vrule) */ static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { - char *sp = smack_from_secid(secid); + struct smack_known *skp = smack_from_secid(secid); if (secdata) - *secdata = sp; - *seclen = strlen(sp); + *secdata = skp->smk_known; + *seclen = strlen(skp->smk_known); return 0; } @@ -3498,6 +3809,7 @@ struct security_operations smack_ops = { .unix_may_send = smack_unix_may_send, .socket_post_create = smack_socket_post_create, + .socket_bind = smack_socket_bind, .socket_connect = smack_socket_connect, .socket_sendmsg = smack_socket_sendmsg, .socket_sock_rcv_skb = smack_socket_sock_rcv_skb, @@ -3577,8 +3889,8 @@ static __init int smack_init(void) if (!security_module_enable(&smack_ops)) return 0; - tsp = new_task_smack(smack_known_floor.smk_known, - smack_known_floor.smk_known, GFP_KERNEL); + tsp = new_task_smack(&smack_known_floor, &smack_known_floor, + GFP_KERNEL); if (tsp == NULL) return -ENOMEM; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 53a08b85bda4..ab167037b2dd 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -66,7 +66,7 @@ static DEFINE_MUTEX(smk_netlbladdr_lock); * If it isn't somehow marked, use this. * It can be reset via smackfs/ambient */ -char *smack_net_ambient; +struct smack_known *smack_net_ambient; /* * This is the level in a CIPSO header that indicates a @@ -112,7 +112,7 @@ struct smack_master_list { LIST_HEAD(smack_rule_list); struct smack_parsed_rule { - char *smk_subject; + struct smack_known *smk_subject; char *smk_object; int smk_access1; int smk_access2; @@ -163,9 +163,11 @@ static inline void smack_catset_bit(unsigned int cat, char *catsetp) */ static void smk_netlabel_audit_set(struct netlbl_audit *nap) { + struct smack_known *skp = smk_of_current(); + nap->loginuid = audit_get_loginuid(current); nap->sessionid = audit_get_sessionid(current); - nap->secid = smack_to_secid(smk_of_current()); + nap->secid = skp->smk_secid; } /* @@ -306,7 +308,7 @@ static int smk_fill_rule(const char *subject, const char *object, struct smack_known *skp; if (import) { - rule->smk_subject = smk_import(subject, len); + rule->smk_subject = smk_import_entry(subject, len); if (rule->smk_subject == NULL) return -1; @@ -321,7 +323,7 @@ static int smk_fill_rule(const char *subject, const char *object, kfree(cp); if (skp == NULL) return -1; - rule->smk_subject = skp->smk_known; + rule->smk_subject = skp; cp = smk_parse_smack(object, len); if (cp == NULL) @@ -445,7 +447,6 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, struct list_head *rule_list, struct mutex *rule_lock, int format) { - struct smack_known *skp; struct smack_parsed_rule *rule; char *data; int datalen; @@ -505,12 +506,10 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, goto out_free_rule; } - if (rule_list == NULL) { load = 1; - skp = smk_find_entry(rule->smk_subject); - rule_list = &skp->smk_rules; - rule_lock = &skp->smk_rules_lock; + rule_list = &rule->smk_subject->smk_rules; + rule_lock = &rule->smk_subject->smk_rules_lock; } rc = smk_set_access(rule, rule_list, rule_lock, load); @@ -579,13 +578,14 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) * because you should expect to be able to write * anything you read back. */ - if (strlen(srp->smk_subject) >= max || strlen(srp->smk_object) >= max) + if (strlen(srp->smk_subject->smk_known) >= max || + strlen(srp->smk_object) >= max) return; if (srp->smk_access == 0) return; - seq_printf(s, "%s %s", srp->smk_subject, srp->smk_object); + seq_printf(s, "%s %s", srp->smk_subject->smk_known, srp->smk_object); seq_putc(s, ' '); @@ -738,9 +738,9 @@ static void smk_unlbl_ambient(char *oldambient) __func__, __LINE__, rc); } if (smack_net_ambient == NULL) - smack_net_ambient = smack_known_floor.smk_known; + smack_net_ambient = &smack_known_floor; - rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET, + rc = netlbl_cfg_unlbl_map_add(smack_net_ambient->smk_known, PF_INET, NULL, NULL, &nai); if (rc != 0) printk(KERN_WARNING "%s:%d add rc = %d\n", @@ -881,7 +881,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, if (format == SMK_FIXED24_FMT) rule += SMK_LABELLEN; else - rule += strlen(skp->smk_known); + rule += strlen(skp->smk_known) + 1; ret = sscanf(rule, "%d", &maplevel); if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL) @@ -1535,11 +1535,12 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, */ mutex_lock(&smack_ambient_lock); - asize = strlen(smack_net_ambient) + 1; + asize = strlen(smack_net_ambient->smk_known) + 1; if (cn >= asize) rc = simple_read_from_buffer(buf, cn, ppos, - smack_net_ambient, asize); + smack_net_ambient->smk_known, + asize); else rc = -EINVAL; @@ -1560,8 +1561,8 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, static ssize_t smk_write_ambient(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct smack_known *skp; char *oldambient; - char *smack = NULL; char *data; int rc = count; @@ -1577,16 +1578,16 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, goto out; } - smack = smk_import(data, count); - if (smack == NULL) { + skp = smk_import_entry(data, count); + if (skp == NULL) { rc = -EINVAL; goto out; } mutex_lock(&smack_ambient_lock); - oldambient = smack_net_ambient; - smack_net_ambient = smack; + oldambient = smack_net_ambient->smk_known; + smack_net_ambient = skp; smk_unlbl_ambient(oldambient); mutex_unlock(&smack_ambient_lock); @@ -1645,7 +1646,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char *data; - char *sp = smk_of_task(current->cred->security); + struct smack_known *skp = smk_of_task(current->cred->security); int rc = count; if (!smack_privileged(CAP_MAC_ADMIN)) @@ -1656,7 +1657,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, * explicitly for clarity. The smk_access() implementation * would use smk_access(smack_onlycap, MAY_WRITE) */ - if (smack_onlycap != NULL && smack_onlycap != sp) + if (smack_onlycap != NULL && smack_onlycap != skp->smk_known) return -EPERM; data = kzalloc(count, GFP_KERNEL); @@ -1866,8 +1867,8 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, if (res) return -EINVAL; - res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access1, - NULL); + res = smk_access(rule.smk_subject, rule.smk_object, + rule.smk_access1, NULL); data[0] = res == 0 ? '1' : '0'; data[1] = '\0'; diff --git a/arch/arm/kvm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 49a7516d81c7..c2e1ef4604e8 100644 --- a/arch/arm/kvm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -25,14 +25,12 @@ #include <clocksource/arm_arch_timer.h> #include <asm/arch_timer.h> -#include <asm/kvm_vgic.h> -#include <asm/kvm_arch_timer.h> +#include <kvm/arm_vgic.h> +#include <kvm/arm_arch_timer.h> static struct timecounter *timecounter; static struct workqueue_struct *wqueue; -static struct kvm_irq_level timer_irq = { - .level = 1, -}; +static unsigned int host_vtimer_irq; static cycle_t kvm_phys_timer_read(void) { @@ -67,8 +65,8 @@ static void kvm_timer_inject_irq(struct kvm_vcpu *vcpu) timer->cntv_ctl |= ARCH_TIMER_CTRL_IT_MASK; kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, - vcpu->arch.timer_cpu.irq->irq, - vcpu->arch.timer_cpu.irq->level); + timer->irq->irq, + timer->irq->level); } static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) @@ -156,6 +154,20 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) timer_arm(timer, ns); } +void kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu, + const struct kvm_irq_level *irq) +{ + struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + + /* + * The vcpu timer irq number cannot be determined in + * kvm_timer_vcpu_init() because it is called much before + * kvm_vcpu_set_target(). To handle this, we determine + * vcpu timer irq number when the vcpu is reset. + */ + timer->irq = irq; +} + void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; @@ -163,12 +175,11 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) INIT_WORK(&timer->expired, kvm_timer_inject_irq_work); hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); timer->timer.function = kvm_timer_expire; - timer->irq = &timer_irq; } static void kvm_timer_init_interrupt(void *info) { - enable_percpu_irq(timer_irq.irq, 0); + enable_percpu_irq(host_vtimer_irq, 0); } @@ -182,7 +193,7 @@ static int kvm_timer_cpu_notify(struct notifier_block *self, break; case CPU_DYING: case CPU_DYING_FROZEN: - disable_percpu_irq(timer_irq.irq); + disable_percpu_irq(host_vtimer_irq); break; } @@ -230,7 +241,7 @@ int kvm_timer_hyp_init(void) goto out; } - timer_irq.irq = ppi; + host_vtimer_irq = ppi; err = register_cpu_notifier(&kvm_timer_cpu_nb); if (err) { diff --git a/arch/arm/kvm/vgic.c b/virt/kvm/arm/vgic.c index 17c5ac7d10ed..17c5ac7d10ed 100644 --- a/arch/arm/kvm/vgic.c +++ b/virt/kvm/arm/vgic.c diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 64ee720b75c7..1550637d1b10 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c @@ -753,6 +753,7 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) if (ret < 0) goto unlock_fail; + kvm->buses[bus_idx]->ioeventfd_count++; list_add_tail(&p->list, &kvm->ioeventfds); mutex_unlock(&kvm->slots_lock); @@ -798,6 +799,7 @@ kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) continue; kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev); + kvm->buses[bus_idx]->ioeventfd_count--; ioeventfd_release(p); ret = 0; break; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 302681c4aa44..1580dd4ace4e 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2926,7 +2926,8 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, struct kvm_io_bus *new_bus, *bus; bus = kvm->buses[bus_idx]; - if (bus->dev_count > NR_IOBUS_DEVS - 1) + /* exclude ioeventfd which is limited by maximum fd */ + if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1) return -ENOSPC; new_bus = kzalloc(sizeof(*bus) + ((bus->dev_count + 1) * @@ -3181,6 +3182,7 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, out_undebugfs: unregister_syscore_ops(&kvm_syscore_ops); + misc_deregister(&kvm_dev); out_unreg: kvm_async_pf_deinit(); out_free: |