Power management and ACPI updates for v4.1-rc1

- Generic PM domains support update including new PM domain
    callbacks to handle device initialization better (Russell King,
    Rafael J Wysocki, Kevin Hilman).
 
  - Unified device properties API update including a new mechanism
    for accessing data provided by platform initialization code
    (Rafael J Wysocki, Adrian Hunter).
 
  - ARM cpuidle update including ARM32/ARM64 handling consolidation
    (Daniel Lezcano).
 
  - intel_idle update including support for the Silvermont Core in
    the Baytrail SOC and for the Airmont Core in the Cherrytrail and
    Braswell SOCs (Len Brown, Mathias Krause).
 
  - New cpufreq driver for Hisilicon ACPU (Leo Yan).
 
  - intel_pstate update including support for the Knights Landing
    chip (Dasaratharaman Chandramouli, Kristen Carlson Accardi).
 
  - QorIQ cpufreq driver update (Tang Yuantian, Arnd Bergmann).
 
  - powernv cpufreq driver update (Shilpasri G Bhat).
 
  - devfreq update including Tegra support changes (Tomeu Vizoso,
    MyungJoo Ham, Chanwoo Choi).
 
  - powercap RAPL (Running-Average Power Limit) driver update
    including support for Intel Broadwell server chips (Jacob Pan,
    Mathias Krause).
 
  - ACPI device enumeration update related to the handling of the
    special PRP0001 device ID allowing DT-style 'compatible' property
    to be used for ACPI device identification (Rafael J Wysocki).
 
  - ACPI EC driver update including limited _DEP support (Lan Tianyu,
    Lv Zheng).
 
  - ACPI backlight driver update including a new mechanism to allow
    native backlight handling to be forced on non-Windows 8 systems
    and a new quirk for Lenovo Ideapad Z570 (Aaron Lu, Hans de Goede).
 
  - New Windows Vista compatibility quirk for Sony VGN-SR19XN (Chen Yu).
 
  - Assorted ACPI fixes and cleanups (Aaron Lu, Martin Kepplinger,
    Masanari Iida, Mika Westerberg, Nan Li, Rafael J Wysocki).
 
  - Fixes related to suspend-to-idle for the iTCO watchdog driver and
    the ACPI core system suspend/resume code (Rafael J Wysocki, Chen Yu).
 
  - PM tracing support for the suspend phase of system suspend/resume
    transitions (Zhonghui Fu).
 
  - Configurable delay for the system suspend/resume testing facility
    (Brian Norris).
 
  - PNP subsystem cleanups (Peter Huewe, Rafael J Wysocki).
 
 /
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABCAAGBQJVLbO+AAoJEILEb/54YlRx5N4QAJXsmEW1FL2l6mMAyTQkEsVj
 nbqjF9I6aJgYM9+i8GKaZJxpN17SAZ7Ii7aCAXjPwX8AvjT70+gcZr+KDWtPir61
 B75VNVEcUYOR4vOF5Z6rQcQMlhGPkfMOJYXFMahpOG6DdPbVh1x2/tuawfc6IC0V
 a6S/fln6WqHrXQ+8swDSv1KuZsav6+8AQaTlNUQkkuXdY9b3k/3xiy5C2K26APP8
 x1B39iAF810qX6ipnK0gEOC3Vs29dl7hvNmgOVmmkBGVS7+pqTuy5n1/9M12cDRz
 78IQ7DXB0NcSwr5tdrmGVUyH0Q6H9lnD3vO7MJkYwKDh5a/2MiBr2GZc4KHDKDWn
 E1sS27f1Pdn9qnpWLzTcY+yYNV3EEyre56L2fc+sh+Xq9sNOjUah+Y/eAej/IxYD
 XYRf+GAj768yCJgNP+Y3PJES/PRh+0IZ/dn5k0Qq2iYvc8mcObyG6zdQIvCucv/i
 70uV1Z2GWEb31cI9TUV8o5GrMW3D0KI9EsCEEpiFFUnhjNog3AWcerGgFQMHxu7X
 ZnNSzudvek+XJ3NtpbPgTiJAmnMz8bDvBQm3G1LUO2TQdjYTU6YMUHsfzXs8DL6c
 aIMWO4stkVuDtWrlT/hfzIXepliccyXmSP6sbH+zNNCepulXe5C4M2SftaDi4l/B
 uIctXWznvHoGys+EFL+v
 =erd3
 -----END PGP SIGNATURE-----

Merge tag 'pm+acpi-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management and ACPI updates from Rafael Wysocki:
 "These are mostly fixes and cleanups all over, although there are a few
  items that sort of fall into the new feature category.

  First off, we have new callbacks for PM domains that should help us to
  handle some issues related to device initialization in a better way.

  There also is some consolidation in the unified device properties API
  area allowing us to use that inferface for accessing data coming from
  platform initialization code in addition to firmware-provided data.

  We have some new device/CPU IDs in a few drivers, support for new
  chips and a new cpufreq driver too.

  Specifics:

   - Generic PM domains support update including new PM domain callbacks
     to handle device initialization better (Russell King, Rafael J
     Wysocki, Kevin Hilman)

   - Unified device properties API update including a new mechanism for
     accessing data provided by platform initialization code (Rafael J
     Wysocki, Adrian Hunter)

   - ARM cpuidle update including ARM32/ARM64 handling consolidation
     (Daniel Lezcano)

   - intel_idle update including support for the Silvermont Core in the
     Baytrail SOC and for the Airmont Core in the Cherrytrail and
     Braswell SOCs (Len Brown, Mathias Krause)

   - New cpufreq driver for Hisilicon ACPU (Leo Yan)

   - intel_pstate update including support for the Knights Landing chip
     (Dasaratharaman Chandramouli, Kristen Carlson Accardi)

   - QorIQ cpufreq driver update (Tang Yuantian, Arnd Bergmann)

   - powernv cpufreq driver update (Shilpasri G Bhat)

   - devfreq update including Tegra support changes (Tomeu Vizoso,
     MyungJoo Ham, Chanwoo Choi)

   - powercap RAPL (Running-Average Power Limit) driver update including
     support for Intel Broadwell server chips (Jacob Pan, Mathias Krause)

   - ACPI device enumeration update related to the handling of the
     special PRP0001 device ID allowing DT-style 'compatible' property
     to be used for ACPI device identification (Rafael J Wysocki)

   - ACPI EC driver update including limited _DEP support (Lan Tianyu,
     Lv Zheng)

   - ACPI backlight driver update including a new mechanism to allow
     native backlight handling to be forced on non-Windows 8 systems and
     a new quirk for Lenovo Ideapad Z570 (Aaron Lu, Hans de Goede)

   - New Windows Vista compatibility quirk for Sony VGN-SR19XN (Chen Yu)

   - Assorted ACPI fixes and cleanups (Aaron Lu, Martin Kepplinger,
     Masanari Iida, Mika Westerberg, Nan Li, Rafael J Wysocki)

   - Fixes related to suspend-to-idle for the iTCO watchdog driver and
     the ACPI core system suspend/resume code (Rafael J Wysocki, Chen Yu)

   - PM tracing support for the suspend phase of system suspend/resume
     transitions (Zhonghui Fu)

   - Configurable delay for the system suspend/resume testing facility
     (Brian Norris)

   - PNP subsystem cleanups (Peter Huewe, Rafael J Wysocki)"

* tag 'pm+acpi-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (74 commits)
  ACPI / scan: Fix NULL pointer dereference in acpi_companion_match()
  ACPI / scan: Rework modalias creation when "compatible" is present
  intel_idle: mark cpu id array as __initconst
  powercap / RAPL: mark rapl_ids array as __initconst
  powercap / RAPL: add ID for Broadwell server
  intel_pstate: Knights Landing support
  intel_pstate: remove MSR test
  cpufreq: fix qoriq uniprocessor build
  ACPI / scan: Take the PRP0001 position in the list of IDs into account
  ACPI / scan: Simplify acpi_match_device()
  ACPI / scan: Generalize of_compatible matching
  device property: Introduce firmware node type for platform data
  device property: Make it possible to use secondary firmware nodes
  PM / watchdog: iTCO: stop watchdog during system suspend
  cpufreq: hisilicon: add acpu driver
  ACPI / EC: Call acpi_walk_dep_device_list() after installing EC opregion handler
  cpufreq: powernv: Report cpu frequency throttling
  intel_idle: Add support for the Airmont Core in the Cherrytrail and Braswell SOCs
  intel_idle: Update support for Silvermont Core in Baytrail SOC
  PM / devfreq: tegra: Register governor on module init
  ...
This commit is contained in:
Linus Torvalds 2015-04-14 20:21:54 -07:00
commit 2481bc7528
96 changed files with 1802 additions and 933 deletions

View File

@ -254,8 +254,13 @@ GPIO support
~~~~~~~~~~~~ ~~~~~~~~~~~~
ACPI 5 introduced two new resources to describe GPIO connections: GpioIo ACPI 5 introduced two new resources to describe GPIO connections: GpioIo
and GpioInt. These resources are used be used to pass GPIO numbers used by and GpioInt. These resources are used be used to pass GPIO numbers used by
the device to the driver. For example: the device to the driver. ACPI 5.1 extended this with _DSD (Device
Specific Data) which made it possible to name the GPIOs among other things.
For example:
Device (DEV)
{
Method (_CRS, 0, NotSerialized) Method (_CRS, 0, NotSerialized)
{ {
Name (SBUF, ResourceTemplate() Name (SBUF, ResourceTemplate()
@ -285,6 +290,18 @@ the device to the driver. For example:
Return (SBUF) Return (SBUF)
} }
// ACPI 5.1 _DSD used for naming the GPIOs
Name (_DSD, Package ()
{
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package ()
{
Package () {"power-gpios", Package() {^DEV, 0, 0, 0 }},
Package () {"irq-gpios", Package() {^DEV, 1, 0, 0 }},
}
})
...
These GPIO numbers are controller relative and path "\\_SB.PCI0.GPI0" These GPIO numbers are controller relative and path "\\_SB.PCI0.GPI0"
specifies the path to the controller. In order to use these GPIOs in Linux specifies the path to the controller. In order to use these GPIOs in Linux
we need to translate them to the corresponding Linux GPIO descriptors. we need to translate them to the corresponding Linux GPIO descriptors.
@ -300,11 +317,11 @@ a code like this:
struct gpio_desc *irq_desc, *power_desc; struct gpio_desc *irq_desc, *power_desc;
irq_desc = gpiod_get_index(dev, NULL, 1); irq_desc = gpiod_get(dev, "irq");
if (IS_ERR(irq_desc)) if (IS_ERR(irq_desc))
/* handle error */ /* handle error */
power_desc = gpiod_get_index(dev, NULL, 0); power_desc = gpiod_get(dev, "power");
if (IS_ERR(power_desc)) if (IS_ERR(power_desc))
/* handle error */ /* handle error */
@ -313,6 +330,9 @@ a code like this:
There are also devm_* versions of these functions which release the There are also devm_* versions of these functions which release the
descriptors once the device is released. descriptors once the device is released.
See Documentation/acpi/gpio-properties.txt for more information about the
_DSD binding related to GPIOs.
MFD devices MFD devices
~~~~~~~~~~~ ~~~~~~~~~~~
The MFD devices register their children as platform devices. For the child The MFD devices register their children as platform devices. For the child

View File

@ -3477,6 +3477,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
improve throughput, but will also increase the improve throughput, but will also increase the
amount of memory reserved for use by the client. amount of memory reserved for use by the client.
suspend.pm_test_delay=
[SUSPEND]
Sets the number of seconds to remain in a suspend test
mode before resuming the system (see
/sys/power/pm_test). Only available when CONFIG_PM_DEBUG
is set. Default value is 5.
swapaccount=[0|1] swapaccount=[0|1]
[KNL] Enable accounting of swap in memory resource [KNL] Enable accounting of swap in memory resource
controller if no parameter or 1 is given or disable controller if no parameter or 1 is given or disable

View File

@ -75,12 +75,14 @@ you should do the following:
# echo platform > /sys/power/disk # echo platform > /sys/power/disk
# echo disk > /sys/power/state # echo disk > /sys/power/state
Then, the kernel will try to freeze processes, suspend devices, wait 5 seconds, Then, the kernel will try to freeze processes, suspend devices, wait a few
resume devices and thaw processes. If "platform" is written to seconds (5 by default, but configurable by the suspend.pm_test_delay module
parameter), resume devices and thaw processes. If "platform" is written to
/sys/power/pm_test , then after suspending devices the kernel will additionally /sys/power/pm_test , then after suspending devices the kernel will additionally
invoke the global control methods (eg. ACPI global control methods) used to invoke the global control methods (eg. ACPI global control methods) used to
prepare the platform firmware for hibernation. Next, it will wait 5 seconds and prepare the platform firmware for hibernation. Next, it will wait a
invoke the platform (eg. ACPI) global methods used to cancel hibernation etc. configurable number of seconds and invoke the platform (eg. ACPI) global
methods used to cancel hibernation etc.
Writing "none" to /sys/power/pm_test causes the kernel to switch to the normal Writing "none" to /sys/power/pm_test causes the kernel to switch to the normal
hibernation/suspend operations. Also, when open for reading, /sys/power/pm_test hibernation/suspend operations. Also, when open for reading, /sys/power/pm_test

View File

@ -4331,6 +4331,15 @@ S: Supported
F: drivers/phy/ F: drivers/phy/
F: include/linux/phy/ F: include/linux/phy/
GENERIC PM DOMAINS
M: "Rafael J. Wysocki" <rjw@rjwysocki.net>
M: Kevin Hilman <khilman@kernel.org>
M: Ulf Hansson <ulf.hansson@linaro.org>
L: linux-pm@vger.kernel.org
S: Supported
F: drivers/base/power/domain*.c
F: include/linux/pm_domain.h
GENERIC UIO DRIVER FOR PCI DEVICES GENERIC UIO DRIVER FOR PCI DEVICES
M: "Michael S. Tsirkin" <mst@redhat.com> M: "Michael S. Tsirkin" <mst@redhat.com>
L: kvm@vger.kernel.org L: kvm@vger.kernel.org

View File

@ -1,6 +1,8 @@
#ifndef __ASM_ARM_CPUIDLE_H #ifndef __ASM_ARM_CPUIDLE_H
#define __ASM_ARM_CPUIDLE_H #define __ASM_ARM_CPUIDLE_H
#include <asm/proc-fns.h>
#ifdef CONFIG_CPU_IDLE #ifdef CONFIG_CPU_IDLE
extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev, extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index); struct cpuidle_driver *drv, int index);
@ -25,4 +27,25 @@ static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
*/ */
#define ARM_CPUIDLE_WFI_STATE ARM_CPUIDLE_WFI_STATE_PWR(UINT_MAX) #define ARM_CPUIDLE_WFI_STATE ARM_CPUIDLE_WFI_STATE_PWR(UINT_MAX)
struct device_node;
struct cpuidle_ops {
int (*suspend)(int cpu, unsigned long arg);
int (*init)(struct device_node *, int cpu);
};
struct of_cpuidle_method {
const char *method;
struct cpuidle_ops *ops;
};
#define CPUIDLE_METHOD_OF_DECLARE(name, _method, _ops) \
static const struct of_cpuidle_method __cpuidle_method_of_table_##name \
__used __section(__cpuidle_method_of_table) \
= { .method = _method, .ops = _ops }
extern int arm_cpuidle_suspend(int index);
extern int arm_cpuidle_init(int cpu);
#endif #endif

View File

@ -10,8 +10,28 @@
*/ */
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <asm/proc-fns.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <asm/cpuidle.h>
extern struct of_cpuidle_method __cpuidle_method_of_table[];
static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel
__used __section(__cpuidle_method_of_table_end);
static struct cpuidle_ops cpuidle_ops[NR_CPUS];
/**
* arm_cpuidle_simple_enter() - a wrapper to cpu_do_idle()
* @dev: not used
* @drv: not used
* @index: not used
*
* A trivial wrapper to allow the cpu_do_idle function to be assigned as a
* cpuidle callback by matching the function signature.
*
* Returns the index passed as parameter
*/
int arm_cpuidle_simple_enter(struct cpuidle_device *dev, int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index) struct cpuidle_driver *drv, int index)
{ {
@ -19,3 +39,114 @@ int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
return index; return index;
} }
/**
* arm_cpuidle_suspend() - function to enter low power idle states
* @index: an integer used as an identifier for the low level PM callbacks
*
* This function calls the underlying arch specific low level PM code as
* registered at the init time.
*
* Returns -EOPNOTSUPP if no suspend callback is defined, the result of the
* callback otherwise.
*/
int arm_cpuidle_suspend(int index)
{
int ret = -EOPNOTSUPP;
int cpu = smp_processor_id();
if (cpuidle_ops[cpu].suspend)
ret = cpuidle_ops[cpu].suspend(cpu, index);
return ret;
}
/**
* arm_cpuidle_get_ops() - find a registered cpuidle_ops by name
* @method: the method name
*
* Search in the __cpuidle_method_of_table array the cpuidle ops matching the
* method name.
*
* Returns a struct cpuidle_ops pointer, NULL if not found.
*/
static struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method)
{
struct of_cpuidle_method *m = __cpuidle_method_of_table;
for (; m->method; m++)
if (!strcmp(m->method, method))
return m->ops;
return NULL;
}
/**
* arm_cpuidle_read_ops() - Initialize the cpuidle ops with the device tree
* @dn: a pointer to a struct device node corresponding to a cpu node
* @cpu: the cpu identifier
*
* Get the method name defined in the 'enable-method' property, retrieve the
* associated cpuidle_ops and do a struct copy. This copy is needed because all
* cpuidle_ops are tagged __initdata and will be unloaded after the init
* process.
*
* Return 0 on sucess, -ENOENT if no 'enable-method' is defined, -EOPNOTSUPP if
* no cpuidle_ops is registered for the 'enable-method'.
*/
static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu)
{
const char *enable_method;
struct cpuidle_ops *ops;
enable_method = of_get_property(dn, "enable-method", NULL);
if (!enable_method)
return -ENOENT;
ops = arm_cpuidle_get_ops(enable_method);
if (!ops) {
pr_warn("%s: unsupported enable-method property: %s\n",
dn->full_name, enable_method);
return -EOPNOTSUPP;
}
cpuidle_ops[cpu] = *ops; /* structure copy */
pr_notice("cpuidle: enable-method property '%s'"
" found operations\n", enable_method);
return 0;
}
/**
* arm_cpuidle_init() - Initialize cpuidle_ops for a specific cpu
* @cpu: the cpu to be initialized
*
* Initialize the cpuidle ops with the device for the cpu and then call
* the cpu's idle initialization callback. This may fail if the underlying HW
* is not operational.
*
* Returns:
* 0 on success,
* -ENODEV if it fails to find the cpu node in the device tree,
* -EOPNOTSUPP if it does not find a registered cpuidle_ops for this cpu,
* -ENOENT if it fails to find an 'enable-method' property,
* -ENXIO if the HW reports a failure or a misconfiguration,
* -ENOMEM if the HW report an memory allocation failure
*/
int __init arm_cpuidle_init(int cpu)
{
struct device_node *cpu_node = of_cpu_device_node_get(cpu);
int ret;
if (!cpu_node)
return -ENODEV;
ret = arm_cpuidle_read_ops(cpu_node, cpu);
if (!ret && cpuidle_ops[cpu].init)
ret = cpuidle_ops[cpu].init(cpu_node, cpu);
of_node_put(cpu_node);
return ret;
}

View File

@ -17,7 +17,6 @@
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/export.h> #include <linux/export.h>
#include <asm/proc-fns.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <mach/cpuidle.h> #include <mach/cpuidle.h>

View File

@ -9,7 +9,6 @@
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/proc-fns.h>
#include "common.h" #include "common.h"
#include "cpuidle.h" #include "cpuidle.h"

View File

@ -9,7 +9,6 @@
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/proc-fns.h>
#include "common.h" #include "common.h"
#include "cpuidle.h" #include "cpuidle.h"

View File

@ -10,7 +10,6 @@
#include <linux/cpu_pm.h> #include <linux/cpu_pm.h>
#include <linux/module.h> #include <linux/module.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/proc-fns.h>
#include <asm/suspend.h> #include <asm/suspend.h>
#include "common.h" #include "common.h"

View File

@ -17,7 +17,6 @@
#include <linux/tick.h> #include <linux/tick.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/proc-fns.h>
#include "common.h" #include "common.h"
#include "pm.h" #include "pm.h"

View File

@ -16,7 +16,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/time.h> #include <linux/time.h>
#include <asm/proc-fns.h> #include <asm/cpuidle.h>
#include <mach/map.h> #include <mach/map.h>

View File

@ -27,7 +27,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/proc-fns.h>
#include <asm/smp_plat.h> #include <asm/smp_plat.h>
#include <asm/suspend.h> #include <asm/suspend.h>

View File

@ -27,7 +27,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/proc-fns.h>
#include <asm/smp_plat.h> #include <asm/smp_plat.h>
#include <asm/suspend.h> #include <asm/suspend.h>

View File

@ -48,7 +48,7 @@ CONFIG_CMDLINE="console=ttyAMA0"
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_COMPAT=y CONFIG_COMPAT=y
CONFIG_CPU_IDLE=y CONFIG_CPU_IDLE=y
CONFIG_ARM64_CPUIDLE=y CONFIG_ARM_CPUIDLE=y
CONFIG_NET=y CONFIG_NET=y
CONFIG_PACKET=y CONFIG_PACKET=y
CONFIG_UNIX=y CONFIG_UNIX=y

View File

@ -4,10 +4,10 @@
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
#ifdef CONFIG_CPU_IDLE #ifdef CONFIG_CPU_IDLE
extern int cpu_init_idle(unsigned int cpu); extern int arm_cpuidle_init(unsigned int cpu);
extern int cpu_suspend(unsigned long arg); extern int cpu_suspend(unsigned long arg);
#else #else
static inline int cpu_init_idle(unsigned int cpu) static inline int arm_cpuidle_init(unsigned int cpu)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
@ -17,5 +17,8 @@ static inline int cpu_suspend(unsigned long arg)
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
#endif #endif
static inline int arm_cpuidle_suspend(int index)
{
return cpu_suspend(index);
}
#endif #endif

View File

@ -15,7 +15,7 @@
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/cpu_ops.h> #include <asm/cpu_ops.h>
int cpu_init_idle(unsigned int cpu) int arm_cpuidle_init(unsigned int cpu)
{ {
int ret = -EOPNOTSUPP; int ret = -EOPNOTSUPP;
struct device_node *cpu_node = of_cpu_device_node_get(cpu); struct device_node *cpu_node = of_cpu_device_node_get(cpu);

View File

@ -1,5 +1,5 @@
#ifndef _ASM_X86_RESUME_TRACE_H #ifndef _ASM_X86_PM_TRACE_H
#define _ASM_X86_RESUME_TRACE_H #define _ASM_X86_PM_TRACE_H
#include <asm/asm.h> #include <asm/asm.h>
@ -14,8 +14,10 @@ do { \
".previous" \ ".previous" \
:"=r" (tracedata) \ :"=r" (tracedata) \
: "i" (__LINE__), "i" (__FILE__)); \ : "i" (__LINE__), "i" (__FILE__)); \
generate_resume_trace(tracedata, user); \ generate_pm_trace(tracedata, user); \
} \ } \
} while (0) } while (0)
#endif /* _ASM_X86_RESUME_TRACE_H */ #define TRACE_SUSPEND(user) TRACE_RESUME(user)
#endif /* _ASM_X86_PM_TRACE_H */

View File

@ -102,7 +102,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
pdevinfo.id = -1; pdevinfo.id = -1;
pdevinfo.res = resources; pdevinfo.res = resources;
pdevinfo.num_res = count; pdevinfo.num_res = count;
pdevinfo.acpi_node.companion = adev; pdevinfo.fwnode = acpi_fwnode_handle(adev);
pdevinfo.dma_mask = DMA_BIT_MASK(32); pdevinfo.dma_mask = DMA_BIT_MASK(32);
pdev = platform_device_register_full(&pdevinfo); pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev)) if (IS_ERR(pdev))

View File

@ -531,8 +531,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN && battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN &&
(s16)(battery->rate_now) < 0) { (s16)(battery->rate_now) < 0) {
battery->rate_now = abs((s16)battery->rate_now); battery->rate_now = abs((s16)battery->rate_now);
printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate" printk_once(KERN_WARNING FW_BUG
" invalid.\n"); "battery: (dis)charge rate invalid.\n");
} }
if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags) if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags)

View File

@ -215,6 +215,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
}, },
{ {
.callback = dmi_disable_osi_vista, .callback = dmi_disable_osi_vista,
.ident = "VGN-SR19XN",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"),
},
},
{
.callback = dmi_disable_osi_vista,
.ident = "Toshiba Satellite L355", .ident = "Toshiba Satellite L355",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),

View File

@ -615,7 +615,7 @@ void acpi_dock_add(struct acpi_device *adev)
memset(&pdevinfo, 0, sizeof(pdevinfo)); memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.name = "dock"; pdevinfo.name = "dock";
pdevinfo.id = dock_station_count; pdevinfo.id = dock_station_count;
pdevinfo.acpi_node.companion = adev; pdevinfo.fwnode = acpi_fwnode_handle(adev);
pdevinfo.data = &ds; pdevinfo.data = &ds;
pdevinfo.size_data = sizeof(ds); pdevinfo.size_data = sizeof(ds);
dd = platform_device_register_full(&pdevinfo); dd = platform_device_register_full(&pdevinfo);

View File

@ -136,6 +136,50 @@ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */
static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */
/* --------------------------------------------------------------------------
* Logging/Debugging
* -------------------------------------------------------------------------- */
/*
* Splitters used by the developers to track the boundary of the EC
* handling processes.
*/
#ifdef DEBUG
#define EC_DBG_SEP " "
#define EC_DBG_DRV "+++++"
#define EC_DBG_STM "====="
#define EC_DBG_REQ "*****"
#define EC_DBG_EVT "#####"
#else
#define EC_DBG_SEP ""
#define EC_DBG_DRV
#define EC_DBG_STM
#define EC_DBG_REQ
#define EC_DBG_EVT
#endif
#define ec_log_raw(fmt, ...) \
pr_info(fmt "\n", ##__VA_ARGS__)
#define ec_dbg_raw(fmt, ...) \
pr_debug(fmt "\n", ##__VA_ARGS__)
#define ec_log(filter, fmt, ...) \
ec_log_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__)
#define ec_dbg(filter, fmt, ...) \
ec_dbg_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__)
#define ec_log_drv(fmt, ...) \
ec_log(EC_DBG_DRV, fmt, ##__VA_ARGS__)
#define ec_dbg_drv(fmt, ...) \
ec_dbg(EC_DBG_DRV, fmt, ##__VA_ARGS__)
#define ec_dbg_stm(fmt, ...) \
ec_dbg(EC_DBG_STM, fmt, ##__VA_ARGS__)
#define ec_dbg_req(fmt, ...) \
ec_dbg(EC_DBG_REQ, fmt, ##__VA_ARGS__)
#define ec_dbg_evt(fmt, ...) \
ec_dbg(EC_DBG_EVT, fmt, ##__VA_ARGS__)
#define ec_dbg_ref(ec, fmt, ...) \
ec_dbg_raw("%lu: " fmt, ec->reference_count, ## __VA_ARGS__)
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
* Device Flags * Device Flags
* -------------------------------------------------------------------------- */ * -------------------------------------------------------------------------- */
@ -159,14 +203,14 @@ static inline u8 acpi_ec_read_status(struct acpi_ec *ec)
{ {
u8 x = inb(ec->command_addr); u8 x = inb(ec->command_addr);
pr_debug("EC_SC(R) = 0x%2.2x " ec_dbg_raw("EC_SC(R) = 0x%2.2x "
"SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d\n", "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d",
x, x,
!!(x & ACPI_EC_FLAG_SCI), !!(x & ACPI_EC_FLAG_SCI),
!!(x & ACPI_EC_FLAG_BURST), !!(x & ACPI_EC_FLAG_BURST),
!!(x & ACPI_EC_FLAG_CMD), !!(x & ACPI_EC_FLAG_CMD),
!!(x & ACPI_EC_FLAG_IBF), !!(x & ACPI_EC_FLAG_IBF),
!!(x & ACPI_EC_FLAG_OBF)); !!(x & ACPI_EC_FLAG_OBF));
return x; return x;
} }
@ -175,20 +219,20 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec)
u8 x = inb(ec->data_addr); u8 x = inb(ec->data_addr);
ec->curr->timestamp = jiffies; ec->curr->timestamp = jiffies;
pr_debug("EC_DATA(R) = 0x%2.2x\n", x); ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x);
return x; return x;
} }
static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command)
{ {
pr_debug("EC_SC(W) = 0x%2.2x\n", command); ec_dbg_raw("EC_SC(W) = 0x%2.2x", command);
outb(command, ec->command_addr); outb(command, ec->command_addr);
ec->curr->timestamp = jiffies; ec->curr->timestamp = jiffies;
} }
static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data)
{ {
pr_debug("EC_DATA(W) = 0x%2.2x\n", data); ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data);
outb(data, ec->data_addr); outb(data, ec->data_addr);
ec->curr->timestamp = jiffies; ec->curr->timestamp = jiffies;
} }
@ -240,7 +284,7 @@ static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open)
* software need to manually trigger a pseudo GPE event on * software need to manually trigger a pseudo GPE event on
* EN=1 writes. * EN=1 writes.
*/ */
pr_debug("***** Polling quirk *****\n"); ec_dbg_raw("Polling quirk");
advance_transaction(ec); advance_transaction(ec);
} }
} }
@ -299,7 +343,7 @@ static void acpi_ec_set_storm(struct acpi_ec *ec, u8 flag)
{ {
if (!test_bit(flag, &ec->flags)) { if (!test_bit(flag, &ec->flags)) {
acpi_ec_disable_gpe(ec, false); acpi_ec_disable_gpe(ec, false);
pr_debug("+++++ Polling enabled +++++\n"); ec_dbg_drv("Polling enabled");
set_bit(flag, &ec->flags); set_bit(flag, &ec->flags);
} }
} }
@ -309,7 +353,7 @@ static void acpi_ec_clear_storm(struct acpi_ec *ec, u8 flag)
if (test_bit(flag, &ec->flags)) { if (test_bit(flag, &ec->flags)) {
clear_bit(flag, &ec->flags); clear_bit(flag, &ec->flags);
acpi_ec_enable_gpe(ec, false); acpi_ec_enable_gpe(ec, false);
pr_debug("+++++ Polling disabled +++++\n"); ec_dbg_drv("Polling disabled");
} }
} }
@ -335,7 +379,7 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)
static void acpi_ec_submit_query(struct acpi_ec *ec) static void acpi_ec_submit_query(struct acpi_ec *ec)
{ {
if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
pr_debug("***** Event started *****\n"); ec_dbg_req("Event started");
schedule_work(&ec->work); schedule_work(&ec->work);
} }
} }
@ -344,7 +388,7 @@ static void acpi_ec_complete_query(struct acpi_ec *ec)
{ {
if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { if (ec->curr->command == ACPI_EC_COMMAND_QUERY) {
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
pr_debug("***** Event stopped *****\n"); ec_dbg_req("Event stopped");
} }
} }
@ -366,8 +410,8 @@ static void advance_transaction(struct acpi_ec *ec)
u8 status; u8 status;
bool wakeup = false; bool wakeup = false;
pr_debug("===== %s (%d) =====\n", ec_dbg_stm("%s (%d)", in_interrupt() ? "IRQ" : "TASK",
in_interrupt() ? "IRQ" : "TASK", smp_processor_id()); smp_processor_id());
/* /*
* By always clearing STS before handling all indications, we can * By always clearing STS before handling all indications, we can
* ensure a hardware STS 0->1 change after this clearing can always * ensure a hardware STS 0->1 change after this clearing can always
@ -390,8 +434,8 @@ static void advance_transaction(struct acpi_ec *ec)
if (t->rlen == t->ri) { if (t->rlen == t->ri) {
t->flags |= ACPI_EC_COMMAND_COMPLETE; t->flags |= ACPI_EC_COMMAND_COMPLETE;
if (t->command == ACPI_EC_COMMAND_QUERY) if (t->command == ACPI_EC_COMMAND_QUERY)
pr_debug("***** Command(%s) hardware completion *****\n", ec_dbg_req("Command(%s) hardware completion",
acpi_ec_cmd_string(t->command)); acpi_ec_cmd_string(t->command));
wakeup = true; wakeup = true;
} }
} else } else
@ -410,8 +454,8 @@ static void advance_transaction(struct acpi_ec *ec)
acpi_ec_complete_query(ec); acpi_ec_complete_query(ec);
t->rdata[t->ri++] = 0x00; t->rdata[t->ri++] = 0x00;
t->flags |= ACPI_EC_COMMAND_COMPLETE; t->flags |= ACPI_EC_COMMAND_COMPLETE;
pr_debug("***** Command(%s) software completion *****\n", ec_dbg_req("Command(%s) software completion",
acpi_ec_cmd_string(t->command)); acpi_ec_cmd_string(t->command));
wakeup = true; wakeup = true;
} else if ((status & ACPI_EC_FLAG_IBF) == 0) { } else if ((status & ACPI_EC_FLAG_IBF) == 0) {
acpi_ec_write_cmd(ec, t->command); acpi_ec_write_cmd(ec, t->command);
@ -502,21 +546,21 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
ret = -EINVAL; ret = -EINVAL;
goto unlock; goto unlock;
} }
ec_dbg_ref(ec, "Increase command");
/* following two actions should be kept atomic */ /* following two actions should be kept atomic */
ec->curr = t; ec->curr = t;
pr_debug("***** Command(%s) started *****\n", ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command));
acpi_ec_cmd_string(t->command));
start_transaction(ec); start_transaction(ec);
spin_unlock_irqrestore(&ec->lock, tmp); spin_unlock_irqrestore(&ec->lock, tmp);
ret = ec_poll(ec); ret = ec_poll(ec);
spin_lock_irqsave(&ec->lock, tmp); spin_lock_irqsave(&ec->lock, tmp);
if (t->irq_count == ec_storm_threshold) if (t->irq_count == ec_storm_threshold)
acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM); acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM);
pr_debug("***** Command(%s) stopped *****\n", ec_dbg_req("Command(%s) stopped", acpi_ec_cmd_string(t->command));
acpi_ec_cmd_string(t->command));
ec->curr = NULL; ec->curr = NULL;
/* Disable GPE for command processing (IBF=0/OBF=1) */ /* Disable GPE for command processing (IBF=0/OBF=1) */
acpi_ec_complete_request(ec); acpi_ec_complete_request(ec);
ec_dbg_ref(ec, "Decrease command");
unlock: unlock:
spin_unlock_irqrestore(&ec->lock, tmp); spin_unlock_irqrestore(&ec->lock, tmp);
return ret; return ret;
@ -676,11 +720,13 @@ static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
spin_lock_irqsave(&ec->lock, flags); spin_lock_irqsave(&ec->lock, flags);
if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) {
pr_debug("+++++ Starting EC +++++\n"); ec_dbg_drv("Starting EC");
/* Enable GPE for event processing (SCI_EVT=1) */ /* Enable GPE for event processing (SCI_EVT=1) */
if (!resuming) if (!resuming) {
acpi_ec_submit_request(ec); acpi_ec_submit_request(ec);
pr_debug("EC started\n"); ec_dbg_ref(ec, "Increase driver");
}
ec_log_drv("EC started");
} }
spin_unlock_irqrestore(&ec->lock, flags); spin_unlock_irqrestore(&ec->lock, flags);
} }
@ -702,17 +748,19 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
spin_lock_irqsave(&ec->lock, flags); spin_lock_irqsave(&ec->lock, flags);
if (acpi_ec_started(ec)) { if (acpi_ec_started(ec)) {
pr_debug("+++++ Stopping EC +++++\n"); ec_dbg_drv("Stopping EC");
set_bit(EC_FLAGS_STOPPED, &ec->flags); set_bit(EC_FLAGS_STOPPED, &ec->flags);
spin_unlock_irqrestore(&ec->lock, flags); spin_unlock_irqrestore(&ec->lock, flags);
wait_event(ec->wait, acpi_ec_stopped(ec)); wait_event(ec->wait, acpi_ec_stopped(ec));
spin_lock_irqsave(&ec->lock, flags); spin_lock_irqsave(&ec->lock, flags);
/* Disable GPE for event processing (SCI_EVT=1) */ /* Disable GPE for event processing (SCI_EVT=1) */
if (!suspending) if (!suspending) {
acpi_ec_complete_request(ec); acpi_ec_complete_request(ec);
ec_dbg_ref(ec, "Decrease driver");
}
clear_bit(EC_FLAGS_STARTED, &ec->flags); clear_bit(EC_FLAGS_STARTED, &ec->flags);
clear_bit(EC_FLAGS_STOPPED, &ec->flags); clear_bit(EC_FLAGS_STOPPED, &ec->flags);
pr_debug("EC stopped\n"); ec_log_drv("EC stopped");
} }
spin_unlock_irqrestore(&ec->lock, flags); spin_unlock_irqrestore(&ec->lock, flags);
} }
@ -824,12 +872,12 @@ static void acpi_ec_run(void *cxt)
if (!handler) if (!handler)
return; return;
pr_debug("##### Query(0x%02x) started #####\n", handler->query_bit); ec_dbg_evt("Query(0x%02x) started", handler->query_bit);
if (handler->func) if (handler->func)
handler->func(handler->data); handler->func(handler->data);
else if (handler->handle) else if (handler->handle)
acpi_evaluate_object(handler->handle, NULL, NULL, NULL); acpi_evaluate_object(handler->handle, NULL, NULL, NULL);
pr_debug("##### Query(0x%02x) stopped #####\n", handler->query_bit); ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit);
acpi_ec_put_query_handler(handler); acpi_ec_put_query_handler(handler);
} }
@ -861,8 +909,8 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
if (value == handler->query_bit) { if (value == handler->query_bit) {
/* have custom handler for this bit */ /* have custom handler for this bit */
handler = acpi_ec_get_query_handler(handler); handler = acpi_ec_get_query_handler(handler);
pr_debug("##### Query(0x%02x) scheduled #####\n", ec_dbg_evt("Query(0x%02x) scheduled",
handler->query_bit); handler->query_bit);
status = acpi_os_execute((handler->func) ? status = acpi_os_execute((handler->func) ?
OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER,
acpi_ec_run, handler); acpi_ec_run, handler);
@ -1099,6 +1147,9 @@ static int acpi_ec_add(struct acpi_device *device)
ret = ec_install_handlers(ec); ret = ec_install_handlers(ec);
/* Reprobe devices depending on the EC */
acpi_walk_dep_device_list(ec->handle);
/* EC is fully operational, allow queries */ /* EC is fully operational, allow queries */
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);

View File

@ -168,7 +168,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
unsigned int node_id; unsigned int node_id;
int retval = -EINVAL; int retval = -EINVAL;
if (ACPI_COMPANION(dev)) { if (has_acpi_companion(dev)) {
if (acpi_dev) { if (acpi_dev) {
dev_warn(dev, "ACPI companion already set\n"); dev_warn(dev, "ACPI companion already set\n");
return -EINVAL; return -EINVAL;
@ -220,7 +220,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
list_add(&physical_node->node, physnode_list); list_add(&physical_node->node, physnode_list);
acpi_dev->physical_node_count++; acpi_dev->physical_node_count++;
if (!ACPI_COMPANION(dev)) if (!has_acpi_companion(dev))
ACPI_COMPANION_SET(dev, acpi_dev); ACPI_COMPANION_SET(dev, acpi_dev);
acpi_physnode_link_name(physical_node_name, node_id); acpi_physnode_link_name(physical_node_name, node_id);

View File

@ -207,5 +207,5 @@ static int __init intel_crc_pmic_opregion_driver_init(void)
} }
module_init(intel_crc_pmic_opregion_driver_init); module_init(intel_crc_pmic_opregion_driver_init);
MODULE_DESCRIPTION("CrystalCove ACPI opration region driver"); MODULE_DESCRIPTION("CrystalCove ACPI operation region driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -114,7 +114,12 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
return 0; return 0;
} }
/* /**
* create_pnp_modalias - Create hid/cid(s) string for modalias and uevent
* @acpi_dev: ACPI device object.
* @modalias: Buffer to print into.
* @size: Size of the buffer.
*
* Creates hid/cid(s) string needed for modalias and uevent * Creates hid/cid(s) string needed for modalias and uevent
* e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get:
* char *modalias: "acpi:IBM0001:ACPI0001" * char *modalias: "acpi:IBM0001:ACPI0001"
@ -122,68 +127,98 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
* -EINVAL: output error * -EINVAL: output error
* -ENOMEM: output is truncated * -ENOMEM: output is truncated
*/ */
static int create_modalias(struct acpi_device *acpi_dev, char *modalias, static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias,
int size) int size)
{ {
int len; int len;
int count; int count;
struct acpi_hardware_id *id; struct acpi_hardware_id *id;
if (list_empty(&acpi_dev->pnp.ids)) /*
* Since we skip PRP0001 from the modalias below, 0 should be returned
* if PRP0001 is the only ACPI/PNP ID in the device's list.
*/
count = 0;
list_for_each_entry(id, &acpi_dev->pnp.ids, list)
if (strcmp(id->id, "PRP0001"))
count++;
if (!count)
return 0; return 0;
/* len = snprintf(modalias, size, "acpi:");
* If the device has PRP0001 we expose DT compatible modalias if (len <= 0)
* instead in form of of:NnameTCcompatible. return len;
*/
if (acpi_dev->data.of_compatible) {
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
const union acpi_object *of_compatible, *obj;
int i, nval;
char *c;
acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); size -= len;
/* DT strings are all in lower case */
for (c = buf.pointer; *c != '\0'; c++)
*c = tolower(*c);
len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
ACPI_FREE(buf.pointer); if (!strcmp(id->id, "PRP0001"))
continue;
of_compatible = acpi_dev->data.of_compatible; count = snprintf(&modalias[len], size, "%s:", id->id);
if (of_compatible->type == ACPI_TYPE_PACKAGE) { if (count < 0)
nval = of_compatible->package.count; return -EINVAL;
obj = of_compatible->package.elements;
} else { /* Must be ACPI_TYPE_STRING. */
nval = 1;
obj = of_compatible;
}
for (i = 0; i < nval; i++, obj++) {
count = snprintf(&modalias[len], size, "C%s",
obj->string.pointer);
if (count < 0)
return -EINVAL;
if (count >= size)
return -ENOMEM;
len += count; if (count >= size)
size -= count; return -ENOMEM;
}
} else {
len = snprintf(modalias, size, "acpi:");
size -= len;
list_for_each_entry(id, &acpi_dev->pnp.ids, list) { len += count;
count = snprintf(&modalias[len], size, "%s:", id->id); size -= count;
if (count < 0)
return -EINVAL;
if (count >= size)
return -ENOMEM;
len += count;
size -= count;
}
} }
modalias[len] = '\0';
return len;
}
/**
* create_of_modalias - Creates DT compatible string for modalias and uevent
* @acpi_dev: ACPI device object.
* @modalias: Buffer to print into.
* @size: Size of the buffer.
*
* Expose DT compatible modalias as of:NnameTCcompatible. This function should
* only be called for devices having PRP0001 in their list of ACPI/PNP IDs.
*/
static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias,
int size)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
const union acpi_object *of_compatible, *obj;
int len, count;
int i, nval;
char *c;
acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
/* DT strings are all in lower case */
for (c = buf.pointer; *c != '\0'; c++)
*c = tolower(*c);
len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);
ACPI_FREE(buf.pointer);
if (len <= 0)
return len;
of_compatible = acpi_dev->data.of_compatible;
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
nval = of_compatible->package.count;
obj = of_compatible->package.elements;
} else { /* Must be ACPI_TYPE_STRING. */
nval = 1;
obj = of_compatible;
}
for (i = 0; i < nval; i++, obj++) {
count = snprintf(&modalias[len], size, "C%s",
obj->string.pointer);
if (count < 0)
return -EINVAL;
if (count >= size)
return -ENOMEM;
len += count;
size -= count;
}
modalias[len] = '\0'; modalias[len] = '\0';
return len; return len;
} }
@ -194,7 +229,8 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
* *
* Check if the given device has an ACPI companion and if that companion has * Check if the given device has an ACPI companion and if that companion has
* a valid list of PNP IDs, and if the device is the first (primary) physical * a valid list of PNP IDs, and if the device is the first (primary) physical
* device associated with it. * device associated with it. Return the companion pointer if that's the case
* or NULL otherwise.
* *
* If multiple physical devices are attached to a single ACPI companion, we need * If multiple physical devices are attached to a single ACPI companion, we need
* to be careful. The usage scenario for this kind of relationship is that all * to be careful. The usage scenario for this kind of relationship is that all
@ -208,31 +244,69 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
* resources available from it but they will be matched normally using functions * resources available from it but they will be matched normally using functions
* provided by their bus types (and analogously for their modalias). * provided by their bus types (and analogously for their modalias).
*/ */
static bool acpi_companion_match(const struct device *dev) static struct acpi_device *acpi_companion_match(const struct device *dev)
{ {
struct acpi_device *adev; struct acpi_device *adev;
bool ret; struct mutex *physical_node_lock;
adev = ACPI_COMPANION(dev); adev = ACPI_COMPANION(dev);
if (!adev) if (!adev)
return false; return NULL;
if (list_empty(&adev->pnp.ids)) if (list_empty(&adev->pnp.ids))
return false; return NULL;
mutex_lock(&adev->physical_node_lock); physical_node_lock = &adev->physical_node_lock;
mutex_lock(physical_node_lock);
if (list_empty(&adev->physical_node_list)) { if (list_empty(&adev->physical_node_list)) {
ret = false; adev = NULL;
} else { } else {
const struct acpi_device_physical_node *node; const struct acpi_device_physical_node *node;
node = list_first_entry(&adev->physical_node_list, node = list_first_entry(&adev->physical_node_list,
struct acpi_device_physical_node, node); struct acpi_device_physical_node, node);
ret = node->dev == dev; if (node->dev != dev)
adev = NULL;
} }
mutex_unlock(&adev->physical_node_lock); mutex_unlock(physical_node_lock);
return ret; return adev;
}
static int __acpi_device_uevent_modalias(struct acpi_device *adev,
struct kobj_uevent_env *env)
{
int len;
if (!adev)
return -ENODEV;
if (list_empty(&adev->pnp.ids))
return 0;
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_pnp_modalias(adev, &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len < 0)
return len;
env->buflen += len;
if (!adev->data.of_compatible)
return 0;
if (len > 0 && add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_of_modalias(adev, &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len < 0)
return len;
env->buflen += len;
return 0;
} }
/* /*
@ -243,22 +317,41 @@ static bool acpi_companion_match(const struct device *dev)
*/ */
int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
{ {
int len; return __acpi_device_uevent_modalias(acpi_companion_match(dev), env);
if (!acpi_companion_match(dev))
return -ENODEV;
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_modalias(ACPI_COMPANION(dev), &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len <= 0)
return len;
env->buflen += len;
return 0;
} }
EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);
static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size)
{
int len, count;
if (!adev)
return -ENODEV;
if (list_empty(&adev->pnp.ids))
return 0;
len = create_pnp_modalias(adev, buf, size - 1);
if (len < 0) {
return len;
} else if (len > 0) {
buf[len++] = '\n';
size -= len;
}
if (!adev->data.of_compatible)
return len;
count = create_of_modalias(adev, buf + len, size - 1);
if (count < 0) {
return count;
} else if (count > 0) {
len += count;
buf[len++] = '\n';
}
return len;
}
/* /*
* Creates modalias sysfs attribute for ACPI enumerated devices. * Creates modalias sysfs attribute for ACPI enumerated devices.
* Because the other buses does not support ACPI HIDs & CIDs. * Because the other buses does not support ACPI HIDs & CIDs.
@ -267,29 +360,13 @@ EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias);
*/ */
int acpi_device_modalias(struct device *dev, char *buf, int size) int acpi_device_modalias(struct device *dev, char *buf, int size)
{ {
int len; return __acpi_device_modalias(acpi_companion_match(dev), buf, size);
if (!acpi_companion_match(dev))
return -ENODEV;
len = create_modalias(ACPI_COMPANION(dev), buf, size -1);
if (len <= 0)
return len;
buf[len++] = '\n';
return len;
} }
EXPORT_SYMBOL_GPL(acpi_device_modalias); EXPORT_SYMBOL_GPL(acpi_device_modalias);
static ssize_t static ssize_t
acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) {
struct acpi_device *acpi_dev = to_acpi_device(dev); return __acpi_device_modalias(to_acpi_device(dev), buf, 1024);
int len;
len = create_modalias(acpi_dev, buf, 1024);
if (len <= 0)
return len;
buf[len++] = '\n';
return len;
} }
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
@ -894,8 +971,51 @@ static void acpi_device_remove_files(struct acpi_device *dev)
ACPI Bus operations ACPI Bus operations
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
/**
* acpi_of_match_device - Match device object using the "compatible" property.
* @adev: ACPI device object to match.
* @of_match_table: List of device IDs to match against.
*
* If @dev has an ACPI companion which has the special PRP0001 device ID in its
* list of identifiers and a _DSD object with the "compatible" property, use
* that property to match against the given list of identifiers.
*/
static bool acpi_of_match_device(struct acpi_device *adev,
const struct of_device_id *of_match_table)
{
const union acpi_object *of_compatible, *obj;
int i, nval;
if (!adev)
return false;
of_compatible = adev->data.of_compatible;
if (!of_match_table || !of_compatible)
return false;
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
nval = of_compatible->package.count;
obj = of_compatible->package.elements;
} else { /* Must be ACPI_TYPE_STRING. */
nval = 1;
obj = of_compatible;
}
/* Now we can look for the driver DT compatible strings */
for (i = 0; i < nval; i++, obj++) {
const struct of_device_id *id;
for (id = of_match_table; id->compatible[0]; id++)
if (!strcasecmp(obj->string.pointer, id->compatible))
return true;
}
return false;
}
static const struct acpi_device_id *__acpi_match_device( static const struct acpi_device_id *__acpi_match_device(
struct acpi_device *device, const struct acpi_device_id *ids) struct acpi_device *device,
const struct acpi_device_id *ids,
const struct of_device_id *of_ids)
{ {
const struct acpi_device_id *id; const struct acpi_device_id *id;
struct acpi_hardware_id *hwid; struct acpi_hardware_id *hwid;
@ -904,14 +1024,27 @@ static const struct acpi_device_id *__acpi_match_device(
* If the device is not present, it is unnecessary to load device * If the device is not present, it is unnecessary to load device
* driver for it. * driver for it.
*/ */
if (!device->status.present) if (!device || !device->status.present)
return NULL; return NULL;
for (id = ids; id->id[0]; id++) list_for_each_entry(hwid, &device->pnp.ids, list) {
list_for_each_entry(hwid, &device->pnp.ids, list) /* First, check the ACPI/PNP IDs provided by the caller. */
for (id = ids; id->id[0]; id++)
if (!strcmp((char *) id->id, hwid->id)) if (!strcmp((char *) id->id, hwid->id))
return id; return id;
/*
* Next, check the special "PRP0001" ID and try to match the
* "compatible" property if found.
*
* The id returned by the below is not valid, but the only
* caller passing non-NULL of_ids here is only interested in
* whether or not the return value is NULL.
*/
if (!strcmp("PRP0001", hwid->id)
&& acpi_of_match_device(device, of_ids))
return id;
}
return NULL; return NULL;
} }
@ -929,68 +1062,26 @@ static const struct acpi_device_id *__acpi_match_device(
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
const struct device *dev) const struct device *dev)
{ {
struct acpi_device *adev; return __acpi_match_device(acpi_companion_match(dev), ids, NULL);
acpi_handle handle = ACPI_HANDLE(dev);
if (!ids || !handle || acpi_bus_get_device(handle, &adev))
return NULL;
if (!acpi_companion_match(dev))
return NULL;
return __acpi_match_device(adev, ids);
} }
EXPORT_SYMBOL_GPL(acpi_match_device); EXPORT_SYMBOL_GPL(acpi_match_device);
int acpi_match_device_ids(struct acpi_device *device, int acpi_match_device_ids(struct acpi_device *device,
const struct acpi_device_id *ids) const struct acpi_device_id *ids)
{ {
return __acpi_match_device(device, ids) ? 0 : -ENOENT; return __acpi_match_device(device, ids, NULL) ? 0 : -ENOENT;
} }
EXPORT_SYMBOL(acpi_match_device_ids); EXPORT_SYMBOL(acpi_match_device_ids);
/* Performs match against special "PRP0001" shoehorn ACPI ID */
static bool acpi_of_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
const union acpi_object *of_compatible, *obj;
struct acpi_device *adev;
int i, nval;
adev = ACPI_COMPANION(dev);
if (!adev)
return false;
of_compatible = adev->data.of_compatible;
if (!drv->of_match_table || !of_compatible)
return false;
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
nval = of_compatible->package.count;
obj = of_compatible->package.elements;
} else { /* Must be ACPI_TYPE_STRING. */
nval = 1;
obj = of_compatible;
}
/* Now we can look for the driver DT compatible strings */
for (i = 0; i < nval; i++, obj++) {
const struct of_device_id *id;
for (id = drv->of_match_table; id->compatible[0]; id++)
if (!strcasecmp(obj->string.pointer, id->compatible))
return true;
}
return false;
}
bool acpi_driver_match_device(struct device *dev, bool acpi_driver_match_device(struct device *dev,
const struct device_driver *drv) const struct device_driver *drv)
{ {
if (!drv->acpi_match_table) if (!drv->acpi_match_table)
return acpi_of_driver_match_device(dev, drv); return acpi_of_match_device(ACPI_COMPANION(dev),
drv->of_match_table);
return !!acpi_match_device(drv->acpi_match_table, dev); return !!__acpi_match_device(acpi_companion_match(dev),
drv->acpi_match_table, drv->of_match_table);
} }
EXPORT_SYMBOL_GPL(acpi_driver_match_device); EXPORT_SYMBOL_GPL(acpi_driver_match_device);
@ -1031,20 +1122,7 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv)
static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{ {
struct acpi_device *acpi_dev = to_acpi_device(dev); return __acpi_device_uevent_modalias(to_acpi_device(dev), env);
int len;
if (list_empty(&acpi_dev->pnp.ids))
return 0;
if (add_uevent_var(env, "MODALIAS="))
return -ENOMEM;
len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
sizeof(env->buf) - env->buflen);
if (len <= 0)
return len;
env->buflen += len;
return 0;
} }
static void acpi_device_notify(acpi_handle handle, u32 event, void *data) static void acpi_device_notify(acpi_handle handle, u32 event, void *data)
@ -1062,10 +1140,10 @@ static void acpi_device_notify_fixed(void *data)
acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device); acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device);
} }
static acpi_status acpi_device_fixed_event(void *data) static u32 acpi_device_fixed_event(void *data)
{ {
acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data); acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data);
return AE_OK; return ACPI_INTERRUPT_HANDLED;
} }
static int acpi_device_install_notify_handler(struct acpi_device *device) static int acpi_device_install_notify_handler(struct acpi_device *device)

View File

@ -629,6 +629,7 @@ static int acpi_freeze_begin(void)
static int acpi_freeze_prepare(void) static int acpi_freeze_prepare(void)
{ {
acpi_enable_wakeup_devices(ACPI_STATE_S0);
acpi_enable_all_wakeup_gpes(); acpi_enable_all_wakeup_gpes();
acpi_os_wait_events_complete(); acpi_os_wait_events_complete();
enable_irq_wake(acpi_gbl_FADT.sci_interrupt); enable_irq_wake(acpi_gbl_FADT.sci_interrupt);
@ -637,6 +638,7 @@ static int acpi_freeze_prepare(void)
static void acpi_freeze_restore(void) static void acpi_freeze_restore(void)
{ {
acpi_disable_wakeup_devices(ACPI_STATE_S0);
disable_irq_wake(acpi_gbl_FADT.sci_interrupt); disable_irq_wake(acpi_gbl_FADT.sci_interrupt);
acpi_enable_all_runtime_gpes(); acpi_enable_all_runtime_gpes();
} }
@ -806,21 +808,6 @@ static void acpi_sleep_hibernate_setup(void)
static inline void acpi_sleep_hibernate_setup(void) {} static inline void acpi_sleep_hibernate_setup(void) {}
#endif /* !CONFIG_HIBERNATION */ #endif /* !CONFIG_HIBERNATION */
int acpi_suspend(u32 acpi_state)
{
suspend_state_t states[] = {
[1] = PM_SUSPEND_STANDBY,
[3] = PM_SUSPEND_MEM,
[5] = PM_SUSPEND_MAX
};
if (acpi_state < 6 && states[acpi_state])
return pm_suspend(states[acpi_state]);
if (acpi_state == 4)
return hibernate();
return -EINVAL;
}
static void acpi_power_off_prepare(void) static void acpi_power_off_prepare(void)
{ {
/* Prepare to power off the system */ /* Prepare to power off the system */

View File

@ -1,6 +1,4 @@
extern int acpi_suspend(u32 state);
extern void acpi_enable_wakeup_devices(u8 sleep_state); extern void acpi_enable_wakeup_devices(u8 sleep_state);
extern void acpi_disable_wakeup_devices(u8 sleep_state); extern void acpi_disable_wakeup_devices(u8 sleep_state);

View File

@ -527,7 +527,7 @@ static ssize_t counter_show(struct kobject *kobj,
acpi_irq_not_handled; acpi_irq_not_handled;
all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count = all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count =
acpi_gpe_count; acpi_gpe_count;
size = sprintf(buf, "%8d", all_counters[index].count); size = sprintf(buf, "%8u", all_counters[index].count);
/* "gpe_all" or "sci" */ /* "gpe_all" or "sci" */
if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS)

View File

@ -82,9 +82,15 @@ module_param(allow_duplicates, bool, 0644);
* For Windows 8 systems: used to decide if video module * For Windows 8 systems: used to decide if video module
* should skip registering backlight interface of its own. * should skip registering backlight interface of its own.
*/ */
static int use_native_backlight_param = -1; enum {
NATIVE_BACKLIGHT_NOT_SET = -1,
NATIVE_BACKLIGHT_OFF,
NATIVE_BACKLIGHT_ON,
};
static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET;
module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); module_param_named(use_native_backlight, use_native_backlight_param, int, 0444);
static bool use_native_backlight_dmi = true; static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET;
static int register_count; static int register_count;
static struct mutex video_list_lock; static struct mutex video_list_lock;
@ -237,15 +243,16 @@ static void acpi_video_switch_brightness(struct work_struct *work);
static bool acpi_video_use_native_backlight(void) static bool acpi_video_use_native_backlight(void)
{ {
if (use_native_backlight_param != -1) if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET)
return use_native_backlight_param; return use_native_backlight_param;
else else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET)
return use_native_backlight_dmi; return use_native_backlight_dmi;
return acpi_osi_is_win8();
} }
bool acpi_video_verify_backlight_support(void) bool acpi_video_verify_backlight_support(void)
{ {
if (acpi_osi_is_win8() && acpi_video_use_native_backlight() && if (acpi_video_use_native_backlight() &&
backlight_device_registered(BACKLIGHT_RAW)) backlight_device_registered(BACKLIGHT_RAW))
return false; return false;
return acpi_video_backlight_support(); return acpi_video_backlight_support();
@ -414,7 +421,13 @@ static int __init video_set_bqc_offset(const struct dmi_system_id *d)
static int __init video_disable_native_backlight(const struct dmi_system_id *d) static int __init video_disable_native_backlight(const struct dmi_system_id *d)
{ {
use_native_backlight_dmi = false; use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF;
return 0;
}
static int __init video_enable_native_backlight(const struct dmi_system_id *d)
{
use_native_backlight_dmi = NATIVE_BACKLIGHT_ON;
return 0; return 0;
} }
@ -559,6 +572,17 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"),
}, },
}, },
/* Non win8 machines which need native backlight nevertheless */
{
/* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */
.callback = video_enable_native_backlight,
.ident = "Lenovo Ideapad Z570",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "102434U"),
},
},
{} {}
}; };

View File

@ -174,14 +174,6 @@ static struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"), DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"),
}, },
}, },
{
.callback = video_detect_force_vendor,
.ident = "Lenovo IdeaPad Z570",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Ideapad Z570"),
},
},
{ }, { },
}; };

View File

@ -128,20 +128,8 @@ static struct pnp_driver isapnp_driver = {
.remove = isapnp_remove_one, .remove = isapnp_remove_one,
}; };
static int __init isapnp_init(void) module_pnp_driver(isapnp_driver);
{
return pnp_register_driver(&isapnp_driver);
}
static void __exit isapnp_exit(void)
{
pnp_unregister_driver(&isapnp_driver);
}
MODULE_AUTHOR("Alan Cox"); MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("low-level driver for ISA PnP ATA"); MODULE_DESCRIPTION("low-level driver for ISA PnP ATA");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION); MODULE_VERSION(DRV_VERSION);
module_init(isapnp_init);
module_exit(isapnp_exit);

View File

@ -12,6 +12,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/fwnode.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -2144,3 +2145,53 @@ define_dev_printk_level(dev_notice, KERN_NOTICE);
define_dev_printk_level(_dev_info, KERN_INFO); define_dev_printk_level(_dev_info, KERN_INFO);
#endif #endif
static inline bool fwnode_is_primary(struct fwnode_handle *fwnode)
{
return fwnode && !IS_ERR(fwnode->secondary);
}
/**
* set_primary_fwnode - Change the primary firmware node of a given device.
* @dev: Device to handle.
* @fwnode: New primary firmware node of the device.
*
* Set the device's firmware node pointer to @fwnode, but if a secondary
* firmware node of the device is present, preserve it.
*/
void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
{
if (fwnode) {
struct fwnode_handle *fn = dev->fwnode;
if (fwnode_is_primary(fn))
fn = fn->secondary;
fwnode->secondary = fn;
dev->fwnode = fwnode;
} else {
dev->fwnode = fwnode_is_primary(dev->fwnode) ?
dev->fwnode->secondary : NULL;
}
}
EXPORT_SYMBOL_GPL(set_primary_fwnode);
/**
* set_secondary_fwnode - Change the secondary firmware node of a given device.
* @dev: Device to handle.
* @fwnode: New secondary firmware node of the device.
*
* If a primary firmware node of the device is present, set its secondary
* pointer to @fwnode. Otherwise, set the device's firmware node pointer to
* @fwnode.
*/
void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
{
if (fwnode)
fwnode->secondary = ERR_PTR(-ENODEV);
if (fwnode_is_primary(dev->fwnode))
dev->fwnode->secondary = fwnode;
else
dev->fwnode = fwnode;
}

View File

@ -298,6 +298,12 @@ static int really_probe(struct device *dev, struct device_driver *drv)
goto probe_failed; goto probe_failed;
} }
if (dev->pm_domain && dev->pm_domain->activate) {
ret = dev->pm_domain->activate(dev);
if (ret)
goto probe_failed;
}
if (dev->bus->probe) { if (dev->bus->probe) {
ret = dev->bus->probe(dev); ret = dev->bus->probe(dev);
if (ret) if (ret)
@ -308,6 +314,9 @@ static int really_probe(struct device *dev, struct device_driver *drv)
goto probe_failed; goto probe_failed;
} }
if (dev->pm_domain && dev->pm_domain->sync)
dev->pm_domain->sync(dev);
driver_bound(dev); driver_bound(dev);
ret = 1; ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n", pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
@ -319,6 +328,8 @@ probe_failed:
driver_sysfs_remove(dev); driver_sysfs_remove(dev);
dev->driver = NULL; dev->driver = NULL;
dev_set_drvdata(dev, NULL); dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
switch (ret) { switch (ret) {
case -EPROBE_DEFER: case -EPROBE_DEFER:
@ -529,6 +540,9 @@ static void __device_release_driver(struct device *dev)
devres_release_all(dev); devres_release_all(dev);
dev->driver = NULL; dev->driver = NULL;
dev_set_drvdata(dev, NULL); dev_set_drvdata(dev, NULL);
if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev);
klist_remove(&dev->p->knode_driver); klist_remove(&dev->p->knode_driver);
if (dev->bus) if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier, blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

View File

@ -463,7 +463,7 @@ struct platform_device *platform_device_register_full(
goto err_alloc; goto err_alloc;
pdev->dev.parent = pdevinfo->parent; pdev->dev.parent = pdevinfo->parent;
ACPI_COMPANION_SET(&pdev->dev, pdevinfo->acpi_node.companion); pdev->dev.fwnode = pdevinfo->fwnode;
if (pdevinfo->dma_mask) { if (pdevinfo->dma_mask) {
/* /*

View File

@ -68,7 +68,36 @@ static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
return genpd; return genpd;
} }
struct generic_pm_domain *dev_to_genpd(struct device *dev) /*
* Get the generic PM domain for a particular struct device.
* This validates the struct device pointer, the PM domain pointer,
* and checks that the PM domain pointer is a real generic PM domain.
* Any failure results in NULL being returned.
*/
struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev)
{
struct generic_pm_domain *genpd = NULL, *gpd;
if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain))
return NULL;
mutex_lock(&gpd_list_lock);
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
if (&gpd->domain == dev->pm_domain) {
genpd = gpd;
break;
}
}
mutex_unlock(&gpd_list_lock);
return genpd;
}
/*
* This should only be used where we are certain that the pm_domain
* attached to the device is a genpd domain.
*/
static struct generic_pm_domain *dev_to_genpd(struct device *dev)
{ {
if (IS_ERR_OR_NULL(dev->pm_domain)) if (IS_ERR_OR_NULL(dev->pm_domain))
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
@ -173,8 +202,8 @@ static int genpd_power_on(struct generic_pm_domain *genpd)
genpd->power_on_latency_ns = elapsed_ns; genpd->power_on_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true; genpd->max_off_time_changed = true;
genpd_recalc_cpu_exit_latency(genpd); genpd_recalc_cpu_exit_latency(genpd);
pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "on", elapsed_ns); genpd->name, "on", elapsed_ns);
return ret; return ret;
} }
@ -199,8 +228,8 @@ static int genpd_power_off(struct generic_pm_domain *genpd)
genpd->power_off_latency_ns = elapsed_ns; genpd->power_off_latency_ns = elapsed_ns;
genpd->max_off_time_changed = true; genpd->max_off_time_changed = true;
pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
genpd->name, "off", elapsed_ns); genpd->name, "off", elapsed_ns);
return ret; return ret;
} }
@ -1513,9 +1542,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) if (!genpd || genpd != pm_genpd_lookup_dev(dev))
|| IS_ERR_OR_NULL(dev->pm_domain)
|| pd_to_genpd(dev->pm_domain) != genpd)
return -EINVAL; return -EINVAL;
/* The above validation also means we have existing domain_data. */ /* The above validation also means we have existing domain_data. */
@ -2093,21 +2120,10 @@ EXPORT_SYMBOL_GPL(of_genpd_get_from_provider);
*/ */
static void genpd_dev_pm_detach(struct device *dev, bool power_off) static void genpd_dev_pm_detach(struct device *dev, bool power_off)
{ {
struct generic_pm_domain *pd = NULL, *gpd; struct generic_pm_domain *pd;
int ret = 0; int ret = 0;
if (!dev->pm_domain) pd = pm_genpd_lookup_dev(dev);
return;
mutex_lock(&gpd_list_lock);
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
if (&gpd->domain == dev->pm_domain) {
pd = gpd;
break;
}
}
mutex_unlock(&gpd_list_lock);
if (!pd) if (!pd)
return; return;
@ -2130,6 +2146,17 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
genpd_queue_power_off_work(pd); genpd_queue_power_off_work(pd);
} }
static void genpd_dev_pm_sync(struct device *dev)
{
struct generic_pm_domain *pd;
pd = dev_to_genpd(dev);
if (IS_ERR(pd))
return;
genpd_queue_power_off_work(pd);
}
/** /**
* genpd_dev_pm_attach - Attach a device to its PM domain using DT. * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
* @dev: Device to attach. * @dev: Device to attach.
@ -2196,6 +2223,7 @@ int genpd_dev_pm_attach(struct device *dev)
} }
dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->detach = genpd_dev_pm_detach;
dev->pm_domain->sync = genpd_dev_pm_sync;
pm_genpd_poweron(pd); pm_genpd_poweron(pd);
return 0; return 0;

View File

@ -23,7 +23,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/async.h> #include <linux/async.h>
@ -1017,6 +1017,9 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
char *info = NULL; char *info = NULL;
int error = 0; int error = 0;
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
if (async_error) if (async_error)
goto Complete; goto Complete;
@ -1057,6 +1060,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
Complete: Complete:
complete_all(&dev->power.completion); complete_all(&dev->power.completion);
TRACE_SUSPEND(error);
return error; return error;
} }
@ -1078,7 +1082,7 @@ static int device_suspend_noirq(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend_noirq, dev); async_schedule(async_suspend_noirq, dev);
return 0; return 0;
@ -1157,6 +1161,9 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
char *info = NULL; char *info = NULL;
int error = 0; int error = 0;
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
__pm_runtime_disable(dev, false); __pm_runtime_disable(dev, false);
if (async_error) if (async_error)
@ -1198,6 +1205,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
async_error = error; async_error = error;
Complete: Complete:
TRACE_SUSPEND(error);
complete_all(&dev->power.completion); complete_all(&dev->power.completion);
return error; return error;
} }
@ -1219,7 +1227,7 @@ static int device_suspend_late(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend_late, dev); async_schedule(async_suspend_late, dev);
return 0; return 0;
@ -1338,6 +1346,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
int error = 0; int error = 0;
DECLARE_DPM_WATCHDOG_ON_STACK(wd); DECLARE_DPM_WATCHDOG_ON_STACK(wd);
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
dpm_wait_for_children(dev, async); dpm_wait_for_children(dev, async);
if (async_error) if (async_error)
@ -1444,6 +1455,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
if (error) if (error)
async_error = error; async_error = error;
TRACE_SUSPEND(error);
return error; return error;
} }
@ -1465,7 +1477,7 @@ static int device_suspend(struct device *dev)
{ {
reinit_completion(&dev->power.completion); reinit_completion(&dev->power.completion);
if (pm_async_enabled && dev->power.async_suspend) { if (is_async(dev)) {
get_device(dev); get_device(dev);
async_schedule(async_suspend, dev); async_schedule(async_suspend, dev);
return 0; return 0;

View File

@ -7,7 +7,7 @@
* devices may be working. * devices may be working.
*/ */
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/rtc.h> #include <linux/rtc.h>
@ -154,7 +154,7 @@ EXPORT_SYMBOL(set_trace_device);
* it's not any guarantee, but it's a high _likelihood_ that * it's not any guarantee, but it's a high _likelihood_ that
* the match is valid). * the match is valid).
*/ */
void generate_resume_trace(const void *tracedata, unsigned int user) void generate_pm_trace(const void *tracedata, unsigned int user)
{ {
unsigned short lineno = *(unsigned short *)tracedata; unsigned short lineno = *(unsigned short *)tracedata;
const char *file = *(const char **)(tracedata + 2); const char *file = *(const char **)(tracedata + 2);
@ -164,7 +164,7 @@ void generate_resume_trace(const void *tracedata, unsigned int user)
file_hash_value = hash_string(lineno, file, FILEHASH); file_hash_value = hash_string(lineno, file, FILEHASH);
set_magic_time(user_hash_value, file_hash_value, dev_hash_value); set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
} }
EXPORT_SYMBOL(generate_resume_trace); EXPORT_SYMBOL(generate_pm_trace);
extern char __tracedata_start, __tracedata_end; extern char __tracedata_start, __tracedata_end;
static int show_file_hash(unsigned int value) static int show_file_hash(unsigned int value)

View File

@ -10,10 +10,102 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/property.h>
#include <linux/export.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/property.h>
/**
* device_add_property_set - Add a collection of properties to a device object.
* @dev: Device to add properties to.
* @pset: Collection of properties to add.
*
* Associate a collection of device properties represented by @pset with @dev
* as its secondary firmware node.
*/
void device_add_property_set(struct device *dev, struct property_set *pset)
{
if (pset)
pset->fwnode.type = FWNODE_PDATA;
set_secondary_fwnode(dev, &pset->fwnode);
}
EXPORT_SYMBOL_GPL(device_add_property_set);
static inline bool is_pset(struct fwnode_handle *fwnode)
{
return fwnode && fwnode->type == FWNODE_PDATA;
}
static inline struct property_set *to_pset(struct fwnode_handle *fwnode)
{
return is_pset(fwnode) ?
container_of(fwnode, struct property_set, fwnode) : NULL;
}
static struct property_entry *pset_prop_get(struct property_set *pset,
const char *name)
{
struct property_entry *prop;
if (!pset || !pset->properties)
return NULL;
for (prop = pset->properties; prop->name; prop++)
if (!strcmp(name, prop->name))
return prop;
return NULL;
}
static int pset_prop_read_array(struct property_set *pset, const char *name,
enum dev_prop_type type, void *val, size_t nval)
{
struct property_entry *prop;
unsigned int item_size;
prop = pset_prop_get(pset, name);
if (!prop)
return -ENODATA;
if (prop->type != type)
return -EPROTO;
if (!val)
return prop->nval;
if (prop->nval < nval)
return -EOVERFLOW;
switch (type) {
case DEV_PROP_U8:
item_size = sizeof(u8);
break;
case DEV_PROP_U16:
item_size = sizeof(u16);
break;
case DEV_PROP_U32:
item_size = sizeof(u32);
break;
case DEV_PROP_U64:
item_size = sizeof(u64);
break;
case DEV_PROP_STRING:
item_size = sizeof(const char *);
break;
default:
return -EINVAL;
}
memcpy(val, prop->value.raw_data, nval * item_size);
return 0;
}
static inline struct fwnode_handle *dev_fwnode(struct device *dev)
{
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
&dev->of_node->fwnode : dev->fwnode;
}
/** /**
* device_property_present - check if a property of a device is present * device_property_present - check if a property of a device is present
@ -24,10 +116,7 @@
*/ */
bool device_property_present(struct device *dev, const char *propname) bool device_property_present(struct device *dev, const char *propname)
{ {
if (IS_ENABLED(CONFIG_OF) && dev->of_node) return fwnode_property_present(dev_fwnode(dev), propname);
return of_property_read_bool(dev->of_node, propname);
return !acpi_dev_prop_get(ACPI_COMPANION(dev), propname, NULL);
} }
EXPORT_SYMBOL_GPL(device_property_present); EXPORT_SYMBOL_GPL(device_property_present);
@ -43,32 +132,22 @@ bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname)
else if (is_acpi_node(fwnode)) else if (is_acpi_node(fwnode))
return !acpi_dev_prop_get(acpi_node(fwnode), propname, NULL); return !acpi_dev_prop_get(acpi_node(fwnode), propname, NULL);
return false; return !!pset_prop_get(to_pset(fwnode), propname);
} }
EXPORT_SYMBOL_GPL(fwnode_property_present); EXPORT_SYMBOL_GPL(fwnode_property_present);
#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
(val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
: of_property_count_elems_of_size((node), (propname), sizeof(type))
#define DEV_PROP_READ_ARRAY(_dev_, _propname_, _type_, _proptype_, _val_, _nval_) \
IS_ENABLED(CONFIG_OF) && _dev_->of_node ? \
(OF_DEV_PROP_READ_ARRAY(_dev_->of_node, _propname_, _type_, \
_val_, _nval_)) : \
acpi_dev_prop_read(ACPI_COMPANION(_dev_), _propname_, \
_proptype_, _val_, _nval_)
/** /**
* device_property_read_u8_array - return a u8 array property of a device * device_property_read_u8_array - return a u8 array property of a device
* @dev: Device to get the property of * @dev: Device to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Function reads an array of u8 properties with @propname from the device * Function reads an array of u8 properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of numbers, * %-EPROTO if the property is not an array of numbers,
@ -77,7 +156,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_present);
int device_property_read_u8_array(struct device *dev, const char *propname, int device_property_read_u8_array(struct device *dev, const char *propname,
u8 *val, size_t nval) u8 *val, size_t nval)
{ {
return DEV_PROP_READ_ARRAY(dev, propname, u8, DEV_PROP_U8, val, nval); return fwnode_property_read_u8_array(dev_fwnode(dev), propname, val, nval);
} }
EXPORT_SYMBOL_GPL(device_property_read_u8_array); EXPORT_SYMBOL_GPL(device_property_read_u8_array);
@ -85,13 +164,14 @@ EXPORT_SYMBOL_GPL(device_property_read_u8_array);
* device_property_read_u16_array - return a u16 array property of a device * device_property_read_u16_array - return a u16 array property of a device
* @dev: Device to get the property of * @dev: Device to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Function reads an array of u16 properties with @propname from the device * Function reads an array of u16 properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of numbers, * %-EPROTO if the property is not an array of numbers,
@ -100,7 +180,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u8_array);
int device_property_read_u16_array(struct device *dev, const char *propname, int device_property_read_u16_array(struct device *dev, const char *propname,
u16 *val, size_t nval) u16 *val, size_t nval)
{ {
return DEV_PROP_READ_ARRAY(dev, propname, u16, DEV_PROP_U16, val, nval); return fwnode_property_read_u16_array(dev_fwnode(dev), propname, val, nval);
} }
EXPORT_SYMBOL_GPL(device_property_read_u16_array); EXPORT_SYMBOL_GPL(device_property_read_u16_array);
@ -108,13 +188,14 @@ EXPORT_SYMBOL_GPL(device_property_read_u16_array);
* device_property_read_u32_array - return a u32 array property of a device * device_property_read_u32_array - return a u32 array property of a device
* @dev: Device to get the property of * @dev: Device to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Function reads an array of u32 properties with @propname from the device * Function reads an array of u32 properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of numbers, * %-EPROTO if the property is not an array of numbers,
@ -123,7 +204,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u16_array);
int device_property_read_u32_array(struct device *dev, const char *propname, int device_property_read_u32_array(struct device *dev, const char *propname,
u32 *val, size_t nval) u32 *val, size_t nval)
{ {
return DEV_PROP_READ_ARRAY(dev, propname, u32, DEV_PROP_U32, val, nval); return fwnode_property_read_u32_array(dev_fwnode(dev), propname, val, nval);
} }
EXPORT_SYMBOL_GPL(device_property_read_u32_array); EXPORT_SYMBOL_GPL(device_property_read_u32_array);
@ -131,13 +212,14 @@ EXPORT_SYMBOL_GPL(device_property_read_u32_array);
* device_property_read_u64_array - return a u64 array property of a device * device_property_read_u64_array - return a u64 array property of a device
* @dev: Device to get the property of * @dev: Device to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Function reads an array of u64 properties with @propname from the device * Function reads an array of u64 properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of numbers, * %-EPROTO if the property is not an array of numbers,
@ -146,7 +228,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u32_array);
int device_property_read_u64_array(struct device *dev, const char *propname, int device_property_read_u64_array(struct device *dev, const char *propname,
u64 *val, size_t nval) u64 *val, size_t nval)
{ {
return DEV_PROP_READ_ARRAY(dev, propname, u64, DEV_PROP_U64, val, nval); return fwnode_property_read_u64_array(dev_fwnode(dev), propname, val, nval);
} }
EXPORT_SYMBOL_GPL(device_property_read_u64_array); EXPORT_SYMBOL_GPL(device_property_read_u64_array);
@ -154,13 +236,14 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array);
* device_property_read_string_array - return a string array property of device * device_property_read_string_array - return a string array property of device
* @dev: Device to get the property of * @dev: Device to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Function reads an array of string properties with @propname from the device * Function reads an array of string properties with @propname from the device
* firmware description and stores them to @val if found. * firmware description and stores them to @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO or %-EILSEQ if the property is not an array of strings, * %-EPROTO or %-EILSEQ if the property is not an array of strings,
@ -169,10 +252,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array);
int device_property_read_string_array(struct device *dev, const char *propname, int device_property_read_string_array(struct device *dev, const char *propname,
const char **val, size_t nval) const char **val, size_t nval)
{ {
return IS_ENABLED(CONFIG_OF) && dev->of_node ? return fwnode_property_read_string_array(dev_fwnode(dev), propname, val, nval);
of_property_read_string_array(dev->of_node, propname, val, nval) :
acpi_dev_prop_read(ACPI_COMPANION(dev), propname,
DEV_PROP_STRING, val, nval);
} }
EXPORT_SYMBOL_GPL(device_property_read_string_array); EXPORT_SYMBOL_GPL(device_property_read_string_array);
@ -193,13 +273,14 @@ EXPORT_SYMBOL_GPL(device_property_read_string_array);
int device_property_read_string(struct device *dev, const char *propname, int device_property_read_string(struct device *dev, const char *propname,
const char **val) const char **val)
{ {
return IS_ENABLED(CONFIG_OF) && dev->of_node ? return fwnode_property_read_string(dev_fwnode(dev), propname, val);
of_property_read_string(dev->of_node, propname, val) :
acpi_dev_prop_read(ACPI_COMPANION(dev), propname,
DEV_PROP_STRING, val, 1);
} }
EXPORT_SYMBOL_GPL(device_property_read_string); EXPORT_SYMBOL_GPL(device_property_read_string);
#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
(val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
: of_property_count_elems_of_size((node), (propname), sizeof(type))
#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \ #define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
({ \ ({ \
int _ret_; \ int _ret_; \
@ -210,7 +291,8 @@ EXPORT_SYMBOL_GPL(device_property_read_string);
_ret_ = acpi_dev_prop_read(acpi_node(_fwnode_), _propname_, \ _ret_ = acpi_dev_prop_read(acpi_node(_fwnode_), _propname_, \
_proptype_, _val_, _nval_); \ _proptype_, _val_, _nval_); \
else \ else \
_ret_ = -ENXIO; \ _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \
_proptype_, _val_, _nval_); \
_ret_; \ _ret_; \
}) })
@ -218,13 +300,14 @@ EXPORT_SYMBOL_GPL(device_property_read_string);
* fwnode_property_read_u8_array - return a u8 array property of firmware node * fwnode_property_read_u8_array - return a u8 array property of firmware node
* @fwnode: Firmware node to get the property of * @fwnode: Firmware node to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Read an array of u8 properties with @propname from @fwnode and stores them to * Read an array of u8 properties with @propname from @fwnode and stores them to
* @val if found. * @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of numbers, * %-EPROTO if the property is not an array of numbers,
@ -243,13 +326,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u8_array);
* fwnode_property_read_u16_array - return a u16 array property of firmware node * fwnode_property_read_u16_array - return a u16 array property of firmware node
* @fwnode: Firmware node to get the property of * @fwnode: Firmware node to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Read an array of u16 properties with @propname from @fwnode and store them to * Read an array of u16 properties with @propname from @fwnode and store them to
* @val if found. * @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of numbers, * %-EPROTO if the property is not an array of numbers,
@ -268,13 +352,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u16_array);
* fwnode_property_read_u32_array - return a u32 array property of firmware node * fwnode_property_read_u32_array - return a u32 array property of firmware node
* @fwnode: Firmware node to get the property of * @fwnode: Firmware node to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Read an array of u32 properties with @propname from @fwnode store them to * Read an array of u32 properties with @propname from @fwnode store them to
* @val if found. * @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of numbers, * %-EPROTO if the property is not an array of numbers,
@ -293,13 +378,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u32_array);
* fwnode_property_read_u64_array - return a u64 array property firmware node * fwnode_property_read_u64_array - return a u64 array property firmware node
* @fwnode: Firmware node to get the property of * @fwnode: Firmware node to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Read an array of u64 properties with @propname from @fwnode and store them to * Read an array of u64 properties with @propname from @fwnode and store them to
* @val if found. * @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of numbers, * %-EPROTO if the property is not an array of numbers,
@ -318,13 +404,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array);
* fwnode_property_read_string_array - return string array property of a node * fwnode_property_read_string_array - return string array property of a node
* @fwnode: Firmware node to get the property of * @fwnode: Firmware node to get the property of
* @propname: Name of the property * @propname: Name of the property
* @val: The values are stored here * @val: The values are stored here or %NULL to return the number of values
* @nval: Size of the @val array * @nval: Size of the @val array
* *
* Read an string list property @propname from the given firmware node and store * Read an string list property @propname from the given firmware node and store
* them to @val if found. * them to @val if found.
* *
* Return: %0 if the property was found (success), * Return: number of values if @val was %NULL,
* %0 if the property was found (success),
* %-EINVAL if given arguments are not valid, * %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value, * %-ENODATA if the property does not have a value,
* %-EPROTO if the property is not an array of strings, * %-EPROTO if the property is not an array of strings,
@ -336,13 +423,16 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
size_t nval) size_t nval)
{ {
if (is_of_node(fwnode)) if (is_of_node(fwnode))
return of_property_read_string_array(of_node(fwnode), propname, return val ?
val, nval); of_property_read_string_array(of_node(fwnode), propname,
val, nval) :
of_property_count_strings(of_node(fwnode), propname);
else if (is_acpi_node(fwnode)) else if (is_acpi_node(fwnode))
return acpi_dev_prop_read(acpi_node(fwnode), propname, return acpi_dev_prop_read(acpi_node(fwnode), propname,
DEV_PROP_STRING, val, nval); DEV_PROP_STRING, val, nval);
return -ENXIO; return pset_prop_read_array(to_pset(fwnode), propname,
DEV_PROP_STRING, val, nval);
} }
EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);

View File

@ -637,18 +637,7 @@ static struct pnp_driver tpm_inf_pnp_driver = {
.remove = tpm_inf_pnp_remove .remove = tpm_inf_pnp_remove
}; };
static int __init init_inf(void) module_pnp_driver(tpm_inf_pnp_driver);
{
return pnp_register_driver(&tpm_inf_pnp_driver);
}
static void __exit cleanup_inf(void)
{
pnp_unregister_driver(&tpm_inf_pnp_driver);
}
module_init(init_inf);
module_exit(cleanup_inf);
MODULE_AUTHOR("Marcel Selhorst <tpmdd@sirrix.com>"); MODULE_AUTHOR("Marcel Selhorst <tpmdd@sirrix.com>");
MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2");

View File

@ -293,5 +293,13 @@ config SH_CPU_FREQ
If unsure, say N. If unsure, say N.
endif endif
config QORIQ_CPUFREQ
tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
depends on OF && COMMON_CLK && (PPC_E500MC || ARM)
select CLK_QORIQ
help
This adds the CPUFreq driver support for Freescale QorIQ SoCs
which are capable of changing the CPU's frequency dynamically.
endif endif
endmenu endmenu

View File

@ -108,6 +108,15 @@ config ARM_HIGHBANK_CPUFREQ
If in doubt, say N. If in doubt, say N.
config ARM_HISI_ACPU_CPUFREQ
tristate "Hisilicon ACPU CPUfreq driver"
depends on ARCH_HISI && CPUFREQ_DT
select PM_OPP
help
This enables the hisilicon ACPU CPUfreq driver.
If in doubt, say N.
config ARM_IMX6Q_CPUFREQ config ARM_IMX6Q_CPUFREQ
tristate "Freescale i.MX6 cpufreq support" tristate "Freescale i.MX6 cpufreq support"
depends on ARCH_MXC depends on ARCH_MXC

View File

@ -23,15 +23,6 @@ config CPU_FREQ_MAPLE
This adds support for frequency switching on Maple 970FX This adds support for frequency switching on Maple 970FX
Evaluation Board and compatible boards (IBM JS2x blades). 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 CLK_QORIQ
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 config CPU_FREQ_PMAC
bool "Support for Apple PowerBooks" bool "Support for Apple PowerBooks"
depends on ADB_PMU && PPC32 depends on ADB_PMU && PPC32

View File

@ -59,6 +59,7 @@ arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o
obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o
obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o
obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
@ -85,7 +86,7 @@ obj-$(CONFIG_CPU_FREQ_CBE) += ppc-cbe-cpufreq.o
ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o 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_CBE_PMI) += ppc_cbe_cpufreq_pmi.o
obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o
obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o obj-$(CONFIG_QORIQ_CPUFREQ) += qoriq-cpufreq.o
obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o
obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o
obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o

View File

@ -0,0 +1,42 @@
/*
* Hisilicon Platforms Using ACPU CPUFreq Support
*
* Copyright (c) 2015 Hisilicon Limited.
* Copyright (c) 2015 Linaro Limited.
*
* Leo Yan <leo.yan@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
static int __init hisi_acpu_cpufreq_driver_init(void)
{
struct platform_device *pdev;
if (!of_machine_is_compatible("hisilicon,hi6220"))
return -ENODEV;
pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
return PTR_ERR_OR_ZERO(pdev);
}
module_init(hisi_acpu_cpufreq_driver_init);
MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
MODULE_DESCRIPTION("Hisilicon acpu cpufreq driver");
MODULE_LICENSE("GPL v2");

View File

@ -614,6 +614,19 @@ static void core_set_pstate(struct cpudata *cpudata, int pstate)
wrmsrl_on_cpu(cpudata->cpu, MSR_IA32_PERF_CTL, val); wrmsrl_on_cpu(cpudata->cpu, MSR_IA32_PERF_CTL, val);
} }
static int knl_get_turbo_pstate(void)
{
u64 value;
int nont, ret;
rdmsrl(MSR_NHM_TURBO_RATIO_LIMIT, value);
nont = core_get_max_pstate();
ret = (((value) >> 8) & 0xFF);
if (ret <= nont)
ret = nont;
return ret;
}
static struct cpu_defaults core_params = { static struct cpu_defaults core_params = {
.pid_policy = { .pid_policy = {
.sample_rate_ms = 10, .sample_rate_ms = 10,
@ -651,6 +664,23 @@ static struct cpu_defaults byt_params = {
}, },
}; };
static struct cpu_defaults knl_params = {
.pid_policy = {
.sample_rate_ms = 10,
.deadband = 0,
.setpoint = 97,
.p_gain_pct = 20,
.d_gain_pct = 0,
.i_gain_pct = 0,
},
.funcs = {
.get_max = core_get_max_pstate,
.get_min = core_get_min_pstate,
.get_turbo = knl_get_turbo_pstate,
.set = core_set_pstate,
},
};
static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max) static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max)
{ {
int max_perf = cpu->pstate.turbo_pstate; int max_perf = cpu->pstate.turbo_pstate;
@ -865,6 +895,7 @@ static const struct x86_cpu_id intel_pstate_cpu_ids[] = {
ICPU(0x4e, core_params), ICPU(0x4e, core_params),
ICPU(0x4f, core_params), ICPU(0x4f, core_params),
ICPU(0x56, core_params), ICPU(0x56, core_params),
ICPU(0x57, knl_params),
{} {}
}; };
MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids); MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids);
@ -1024,25 +1055,11 @@ static unsigned int force_load;
static int intel_pstate_msrs_not_valid(void) static int intel_pstate_msrs_not_valid(void)
{ {
/* Check that all the msr's we are using are valid. */
u64 aperf, mperf, tmp;
rdmsrl(MSR_IA32_APERF, aperf);
rdmsrl(MSR_IA32_MPERF, mperf);
if (!pstate_funcs.get_max() || if (!pstate_funcs.get_max() ||
!pstate_funcs.get_min() || !pstate_funcs.get_min() ||
!pstate_funcs.get_turbo()) !pstate_funcs.get_turbo())
return -ENODEV; return -ENODEV;
rdmsrl(MSR_IA32_APERF, tmp);
if (!(tmp - aperf))
return -ENODEV;
rdmsrl(MSR_IA32_MPERF, tmp);
if (!(tmp - mperf))
return -ENODEV;
return 0; return 0;
} }

View File

@ -34,9 +34,13 @@
#include <asm/smp.h> /* Required for cpu_sibling_mask() in UP configs */ #include <asm/smp.h> /* Required for cpu_sibling_mask() in UP configs */
#define POWERNV_MAX_PSTATES 256 #define POWERNV_MAX_PSTATES 256
#define PMSR_PSAFE_ENABLE (1UL << 30)
#define PMSR_SPR_EM_DISABLE (1UL << 31)
#define PMSR_MAX(x) ((x >> 32) & 0xFF)
#define PMSR_LP(x) ((x >> 48) & 0xFF)
static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1];
static bool rebooting; static bool rebooting, throttled;
/* /*
* Note: The set of pstates consists of contiguous integers, the * Note: The set of pstates consists of contiguous integers, the
@ -294,6 +298,44 @@ static inline unsigned int get_nominal_index(void)
return powernv_pstate_info.max - powernv_pstate_info.nominal; return powernv_pstate_info.max - powernv_pstate_info.nominal;
} }
static void powernv_cpufreq_throttle_check(unsigned int cpu)
{
unsigned long pmsr;
int pmsr_pmax, pmsr_lp;
pmsr = get_pmspr(SPRN_PMSR);
/* Check for Pmax Capping */
pmsr_pmax = (s8)PMSR_MAX(pmsr);
if (pmsr_pmax != powernv_pstate_info.max) {
throttled = true;
pr_info("CPU %d Pmax is reduced to %d\n", cpu, pmsr_pmax);
pr_info("Max allowed Pstate is capped\n");
}
/*
* Check for Psafe by reading LocalPstate
* or check if Psafe_mode_active is set in PMSR.
*/
pmsr_lp = (s8)PMSR_LP(pmsr);
if ((pmsr_lp < powernv_pstate_info.min) ||
(pmsr & PMSR_PSAFE_ENABLE)) {
throttled = true;
pr_info("Pstate set to safe frequency\n");
}
/* Check if SPR_EM_DISABLE is set in PMSR */
if (pmsr & PMSR_SPR_EM_DISABLE) {
throttled = true;
pr_info("Frequency Control disabled from OS\n");
}
if (throttled) {
pr_info("PMSR = %16lx\n", pmsr);
pr_crit("CPU Frequency could be throttled\n");
}
}
/* /*
* powernv_cpufreq_target_index: Sets the frequency corresponding to * powernv_cpufreq_target_index: Sets the frequency corresponding to
* the cpufreq table entry indexed by new_index on the cpus in the * the cpufreq table entry indexed by new_index on the cpus in the
@ -307,6 +349,9 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy,
if (unlikely(rebooting) && new_index != get_nominal_index()) if (unlikely(rebooting) && new_index != get_nominal_index())
return 0; return 0;
if (!throttled)
powernv_cpufreq_throttle_check(smp_processor_id());
freq_data.pstate_id = powernv_freqs[new_index].driver_data; freq_data.pstate_id = powernv_freqs[new_index].driver_data;
/* /*

View File

@ -1,7 +1,7 @@
/* /*
* Copyright 2013 Freescale Semiconductor, Inc. * Copyright 2013 Freescale Semiconductor, Inc.
* *
* CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs. * CPU Frequency Scaling driver for Freescale QorIQ SoCs.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
@ -20,12 +20,13 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <sysdev/fsl_soc.h>
#if !defined(CONFIG_ARM)
#include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */ #include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */
#endif
/** /**
* struct cpu_data - per CPU data struct * struct cpu_data
* @parent: the parent node of cpu clock * @parent: the parent node of cpu clock
* @table: frequency table * @table: frequency table
*/ */
@ -69,18 +70,79 @@ static const struct soc_data sdata[] = {
static u32 min_cpufreq; static u32 min_cpufreq;
static const u32 *fmask; static const u32 *fmask;
static DEFINE_PER_CPU(struct cpu_data *, cpu_data); #if defined(CONFIG_ARM)
static int get_cpu_physical_id(int cpu)
/* 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); return topology_core_id(cpu);
}
#else
static int get_cpu_physical_id(int cpu)
{
return get_hard_smp_processor_id(cpu);
} }
#endif #endif
static u32 get_bus_freq(void)
{
struct device_node *soc;
u32 sysfreq;
soc = of_find_node_by_type(NULL, "soc");
if (!soc)
return 0;
if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
sysfreq = 0;
of_node_put(soc);
return sysfreq;
}
static struct device_node *cpu_to_clk_node(int cpu)
{
struct device_node *np, *clk_np;
if (!cpu_present(cpu))
return NULL;
np = of_get_cpu_node(cpu, NULL);
if (!np)
return NULL;
clk_np = of_parse_phandle(np, "clocks", 0);
if (!clk_np)
return NULL;
of_node_put(np);
return clk_np;
}
/* traverse cpu nodes to get cpu mask of sharing clock wire */
static void set_affected_cpus(struct cpufreq_policy *policy)
{
struct device_node *np, *clk_np;
struct cpumask *dstp = policy->cpus;
int i;
np = cpu_to_clk_node(policy->cpu);
if (!np)
return;
for_each_present_cpu(i) {
clk_np = cpu_to_clk_node(i);
if (!clk_np)
continue;
if (clk_np == np)
cpumask_set_cpu(i, dstp);
of_node_put(clk_np);
}
of_node_put(np);
}
/* reduce the duplicated frequencies in frequency table */ /* reduce the duplicated frequencies in frequency table */
static void freq_table_redup(struct cpufreq_frequency_table *freq_table, static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
int count) int count)
@ -107,6 +169,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
int i, j, ind; int i, j, ind;
unsigned int freq, max_freq; unsigned int freq, max_freq;
struct cpufreq_frequency_table table; struct cpufreq_frequency_table table;
for (i = 0; i < count - 1; i++) { for (i = 0; i < count - 1; i++) {
max_freq = freq_table[i].frequency; max_freq = freq_table[i].frequency;
ind = i; ind = i;
@ -131,7 +194,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
} }
} }
static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
{ {
struct device_node *np; struct device_node *np;
int i, count, ret; int i, count, ret;
@ -147,10 +210,8 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
return -ENODEV; return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL); data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) { if (!data)
pr_err("%s: no memory\n", __func__);
goto err_np; goto err_np;
}
policy->clk = of_clk_get(np, 0); policy->clk = of_clk_get(np, 0);
if (IS_ERR(policy->clk)) { if (IS_ERR(policy->clk)) {
@ -172,7 +233,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
} }
if (fmask) if (fmask)
mask = fmask[get_hard_smp_processor_id(cpu)]; mask = fmask[get_cpu_physical_id(cpu)];
else else
mask = 0x0; mask = 0x0;
@ -203,13 +264,12 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
data->table = table; data->table = table;
/* update ->cpus if we have cluster, no harm if not */ /* update ->cpus if we have cluster, no harm if not */
cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu)); set_affected_cpus(policy);
for_each_cpu(i, per_cpu(cpu_mask, cpu)) policy->driver_data = data;
per_cpu(cpu_data, i) = data;
/* Minimum transition latency is 12 platform clocks */ /* Minimum transition latency is 12 platform clocks */
u64temp = 12ULL * NSEC_PER_SEC; u64temp = 12ULL * NSEC_PER_SEC;
do_div(u64temp, fsl_get_sys_freq()); do_div(u64temp, get_bus_freq());
policy->cpuinfo.transition_latency = u64temp + 1; policy->cpuinfo.transition_latency = u64temp + 1;
of_node_put(np); of_node_put(np);
@ -221,7 +281,7 @@ err_nomem1:
err_node: err_node:
of_node_put(data->parent); of_node_put(data->parent);
err_nomem2: err_nomem2:
per_cpu(cpu_data, cpu) = NULL; policy->driver_data = NULL;
kfree(data); kfree(data);
err_np: err_np:
of_node_put(np); of_node_put(np);
@ -229,43 +289,40 @@ err_np:
return -ENODEV; return -ENODEV;
} }
static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy) static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{ {
struct cpu_data *data = per_cpu(cpu_data, policy->cpu); struct cpu_data *data = policy->driver_data;
unsigned int cpu;
of_node_put(data->parent); of_node_put(data->parent);
kfree(data->table); kfree(data->table);
kfree(data); kfree(data);
policy->driver_data = NULL;
for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu))
per_cpu(cpu_data, cpu) = NULL;
return 0; return 0;
} }
static int corenet_cpufreq_target(struct cpufreq_policy *policy, static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
unsigned int index) unsigned int index)
{ {
struct clk *parent; struct clk *parent;
struct cpu_data *data = per_cpu(cpu_data, policy->cpu); struct cpu_data *data = policy->driver_data;
parent = of_clk_get(data->parent, data->table[index].driver_data); parent = of_clk_get(data->parent, data->table[index].driver_data);
return clk_set_parent(policy->clk, parent); return clk_set_parent(policy->clk, parent);
} }
static struct cpufreq_driver ppc_corenet_cpufreq_driver = { static struct cpufreq_driver qoriq_cpufreq_driver = {
.name = "ppc_cpufreq", .name = "qoriq_cpufreq",
.flags = CPUFREQ_CONST_LOOPS, .flags = CPUFREQ_CONST_LOOPS,
.init = corenet_cpufreq_cpu_init, .init = qoriq_cpufreq_cpu_init,
.exit = __exit_p(corenet_cpufreq_cpu_exit), .exit = __exit_p(qoriq_cpufreq_cpu_exit),
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = corenet_cpufreq_target, .target_index = qoriq_cpufreq_target,
.get = cpufreq_generic_get, .get = cpufreq_generic_get,
.attr = cpufreq_generic_attr, .attr = cpufreq_generic_attr,
}; };
static const struct of_device_id node_matches[] __initdata = { static const struct of_device_id node_matches[] __initconst = {
{ .compatible = "fsl,p2041-clockgen", .data = &sdata[0], }, { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
{ .compatible = "fsl,p3041-clockgen", .data = &sdata[0], }, { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
{ .compatible = "fsl,p5020-clockgen", .data = &sdata[1], }, { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
@ -275,61 +332,43 @@ static const struct of_device_id node_matches[] __initdata = {
{} {}
}; };
static int __init ppc_corenet_cpufreq_init(void) static int __init qoriq_cpufreq_init(void)
{ {
int ret; int ret;
struct device_node *np; struct device_node *np;
const struct of_device_id *match; const struct of_device_id *match;
const struct soc_data *data; const struct soc_data *data;
unsigned int cpu;
np = of_find_matching_node(NULL, node_matches); np = of_find_matching_node(NULL, node_matches);
if (!np) if (!np)
return -ENODEV; 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); match = of_match_node(node_matches, np);
data = match->data; data = match->data;
if (data) { if (data) {
if (data->flag) if (data->flag)
fmask = data->freq_mask; fmask = data->freq_mask;
min_cpufreq = fsl_get_sys_freq(); min_cpufreq = get_bus_freq();
} else { } else {
min_cpufreq = fsl_get_sys_freq() / 2; min_cpufreq = get_bus_freq() / 2;
} }
of_node_put(np); of_node_put(np);
ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver); ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
if (!ret) if (!ret)
pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n"); pr_info("Freescale QorIQ CPU frequency scaling driver\n");
return ret; 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); module_init(qoriq_cpufreq_init);
static void __exit ppc_corenet_cpufreq_exit(void) static void __exit qoriq_cpufreq_exit(void)
{ {
unsigned int cpu; cpufreq_unregister_driver(&qoriq_cpufreq_driver);
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_exit(qoriq_cpufreq_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>"); MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs"); MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs");

View File

@ -29,15 +29,10 @@ config DT_IDLE_STATES
bool bool
menu "ARM CPU Idle Drivers" menu "ARM CPU Idle Drivers"
depends on ARM depends on ARM || ARM64
source "drivers/cpuidle/Kconfig.arm" source "drivers/cpuidle/Kconfig.arm"
endmenu endmenu
menu "ARM64 CPU Idle Drivers"
depends on ARM64
source "drivers/cpuidle/Kconfig.arm64"
endmenu
menu "MIPS CPU Idle Drivers" menu "MIPS CPU Idle Drivers"
depends on MIPS depends on MIPS
source "drivers/cpuidle/Kconfig.mips" source "drivers/cpuidle/Kconfig.mips"

View File

@ -1,10 +1,20 @@
# #
# ARM CPU Idle drivers # ARM CPU Idle drivers
# #
config ARM_CPUIDLE
bool "Generic ARM/ARM64 CPU idle Driver"
select DT_IDLE_STATES
help
Select this to enable generic cpuidle driver for ARM.
It provides a generic idle driver whose idle states are configured
at run-time through DT nodes. The CPUidle suspend backend is
initialized by calling the CPU operations init idle hook
provided by architecture code.
config ARM_BIG_LITTLE_CPUIDLE config ARM_BIG_LITTLE_CPUIDLE
bool "Support for ARM big.LITTLE processors" bool "Support for ARM big.LITTLE processors"
depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS
depends on MCPM depends on MCPM && !ARM64
select ARM_CPU_SUSPEND select ARM_CPU_SUSPEND
select CPU_IDLE_MULTIPLE_DRIVERS select CPU_IDLE_MULTIPLE_DRIVERS
select DT_IDLE_STATES select DT_IDLE_STATES
@ -16,51 +26,51 @@ config ARM_BIG_LITTLE_CPUIDLE
config ARM_CLPS711X_CPUIDLE config ARM_CLPS711X_CPUIDLE
bool "CPU Idle Driver for CLPS711X processors" bool "CPU Idle Driver for CLPS711X processors"
depends on ARCH_CLPS711X || COMPILE_TEST depends on ARCH_CLPS711X && !ARM64 || COMPILE_TEST
help help
Select this to enable cpuidle on Cirrus Logic CLPS711X SOCs. Select this to enable cpuidle on Cirrus Logic CLPS711X SOCs.
config ARM_HIGHBANK_CPUIDLE config ARM_HIGHBANK_CPUIDLE
bool "CPU Idle Driver for Calxeda processors" bool "CPU Idle Driver for Calxeda processors"
depends on ARM_PSCI depends on ARM_PSCI && !ARM64
select ARM_CPU_SUSPEND select ARM_CPU_SUSPEND
help help
Select this to enable cpuidle on Calxeda processors. Select this to enable cpuidle on Calxeda processors.
config ARM_KIRKWOOD_CPUIDLE config ARM_KIRKWOOD_CPUIDLE
bool "CPU Idle Driver for Marvell Kirkwood SoCs" bool "CPU Idle Driver for Marvell Kirkwood SoCs"
depends on MACH_KIRKWOOD depends on MACH_KIRKWOOD && !ARM64
help help
This adds the CPU Idle driver for Marvell Kirkwood SoCs. This adds the CPU Idle driver for Marvell Kirkwood SoCs.
config ARM_ZYNQ_CPUIDLE config ARM_ZYNQ_CPUIDLE
bool "CPU Idle Driver for Xilinx Zynq processors" bool "CPU Idle Driver for Xilinx Zynq processors"
depends on ARCH_ZYNQ depends on ARCH_ZYNQ && !ARM64
help help
Select this to enable cpuidle on Xilinx Zynq processors. Select this to enable cpuidle on Xilinx Zynq processors.
config ARM_U8500_CPUIDLE config ARM_U8500_CPUIDLE
bool "Cpu Idle Driver for the ST-E u8500 processors" bool "Cpu Idle Driver for the ST-E u8500 processors"
depends on ARCH_U8500 depends on ARCH_U8500 && !ARM64
help help
Select this to enable cpuidle for ST-E u8500 processors Select this to enable cpuidle for ST-E u8500 processors
config ARM_AT91_CPUIDLE config ARM_AT91_CPUIDLE
bool "Cpu Idle Driver for the AT91 processors" bool "Cpu Idle Driver for the AT91 processors"
default y default y
depends on ARCH_AT91 depends on ARCH_AT91 && !ARM64
help help
Select this to enable cpuidle for AT91 processors Select this to enable cpuidle for AT91 processors
config ARM_EXYNOS_CPUIDLE config ARM_EXYNOS_CPUIDLE
bool "Cpu Idle Driver for the Exynos processors" bool "Cpu Idle Driver for the Exynos processors"
depends on ARCH_EXYNOS depends on ARCH_EXYNOS && !ARM64
select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
help help
Select this to enable cpuidle for Exynos processors Select this to enable cpuidle for Exynos processors
config ARM_MVEBU_V7_CPUIDLE config ARM_MVEBU_V7_CPUIDLE
bool "CPU Idle Driver for mvebu v7 family processors" bool "CPU Idle Driver for mvebu v7 family processors"
depends on ARCH_MVEBU depends on ARCH_MVEBU && !ARM64
help help
Select this to enable cpuidle on Armada 370, 38x and XP processors. Select this to enable cpuidle on Armada 370, 38x and XP processors.

View File

@ -1,13 +0,0 @@
#
# ARM64 CPU Idle drivers
#
config ARM64_CPUIDLE
bool "Generic ARM64 CPU idle Driver"
select DT_IDLE_STATES
help
Select this to enable generic cpuidle driver for ARM64.
It provides a generic idle driver whose idle states are configured
at run-time through DT nodes. The CPUidle suspend backend is
initialized by calling the CPU operations init idle hook
provided by architecture code.

View File

@ -17,15 +17,12 @@ obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o
obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o
obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o
obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o
obj-$(CONFIG_ARM_CPUIDLE) += cpuidle-arm.o
############################################################################### ###############################################################################
# MIPS drivers # MIPS drivers
obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o
###############################################################################
# ARM64 drivers
obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o
############################################################################### ###############################################################################
# POWERPC drivers # POWERPC drivers
obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o

View File

@ -1,5 +1,5 @@
/* /*
* ARM64 generic CPU idle driver. * ARM/ARM64 generic CPU idle driver.
* *
* Copyright (C) 2014 ARM Ltd. * Copyright (C) 2014 ARM Ltd.
* Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
@ -9,7 +9,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#define pr_fmt(fmt) "CPUidle arm64: " fmt #define pr_fmt(fmt) "CPUidle arm: " fmt
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
@ -17,13 +17,14 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/slab.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include "dt_idle_states.h" #include "dt_idle_states.h"
/* /*
* arm64_enter_idle_state - Programs CPU to enter the specified state * arm_enter_idle_state - Programs CPU to enter the specified state
* *
* dev: cpuidle device * dev: cpuidle device
* drv: cpuidle driver * drv: cpuidle driver
@ -32,8 +33,8 @@
* Called from the CPUidle framework to program the device to the * Called from the CPUidle framework to program the device to the
* specified target state selected by the governor. * specified target state selected by the governor.
*/ */
static int arm64_enter_idle_state(struct cpuidle_device *dev, static int arm_enter_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx) struct cpuidle_driver *drv, int idx)
{ {
int ret; int ret;
@ -49,7 +50,7 @@ static int arm64_enter_idle_state(struct cpuidle_device *dev,
* call the CPU ops suspend protocol with idle index as a * call the CPU ops suspend protocol with idle index as a
* parameter. * parameter.
*/ */
ret = cpu_suspend(idx); arm_cpuidle_suspend(idx);
cpu_pm_exit(); cpu_pm_exit();
} }
@ -57,8 +58,8 @@ static int arm64_enter_idle_state(struct cpuidle_device *dev,
return ret ? -1 : idx; return ret ? -1 : idx;
} }
static struct cpuidle_driver arm64_idle_driver = { static struct cpuidle_driver arm_idle_driver = {
.name = "arm64_idle", .name = "arm_idle",
.owner = THIS_MODULE, .owner = THIS_MODULE,
/* /*
* State at index 0 is standby wfi and considered standard * State at index 0 is standby wfi and considered standard
@ -68,32 +69,33 @@ static struct cpuidle_driver arm64_idle_driver = {
* handler for idle state index 0. * handler for idle state index 0.
*/ */
.states[0] = { .states[0] = {
.enter = arm64_enter_idle_state, .enter = arm_enter_idle_state,
.exit_latency = 1, .exit_latency = 1,
.target_residency = 1, .target_residency = 1,
.power_usage = UINT_MAX, .power_usage = UINT_MAX,
.name = "WFI", .name = "WFI",
.desc = "ARM64 WFI", .desc = "ARM WFI",
} }
}; };
static const struct of_device_id arm64_idle_state_match[] __initconst = { static const struct of_device_id arm_idle_state_match[] __initconst = {
{ .compatible = "arm,idle-state", { .compatible = "arm,idle-state",
.data = arm64_enter_idle_state }, .data = arm_enter_idle_state },
{ }, { },
}; };
/* /*
* arm64_idle_init * arm_idle_init
* *
* Registers the arm64 specific cpuidle driver with the cpuidle * Registers the arm specific cpuidle driver with the cpuidle
* framework. It relies on core code to parse the idle states * framework. It relies on core code to parse the idle states
* and initialize them using driver data structures accordingly. * and initialize them using driver data structures accordingly.
*/ */
static int __init arm64_idle_init(void) static int __init arm_idle_init(void)
{ {
int cpu, ret; int cpu, ret;
struct cpuidle_driver *drv = &arm64_idle_driver; struct cpuidle_driver *drv = &arm_idle_driver;
struct cpuidle_device *dev;
/* /*
* Initialize idle states data, starting at index 1. * Initialize idle states data, starting at index 1.
@ -101,22 +103,61 @@ static int __init arm64_idle_init(void)
* let the driver initialization fail accordingly since there is no * let the driver initialization fail accordingly since there is no
* reason to initialize the idle driver if only wfi is supported. * reason to initialize the idle driver if only wfi is supported.
*/ */
ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1); ret = dt_init_idle_driver(drv, arm_idle_state_match, 1);
if (ret <= 0) if (ret <= 0)
return ret ? : -ENODEV; return ret ? : -ENODEV;
ret = cpuidle_register_driver(drv);
if (ret) {
pr_err("Failed to register cpuidle driver\n");
return ret;
}
/* /*
* Call arch CPU operations in order to initialize * Call arch CPU operations in order to initialize
* idle states suspend back-end specific data * idle states suspend back-end specific data
*/ */
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
ret = cpu_init_idle(cpu); ret = arm_cpuidle_init(cpu);
/*
* Skip the cpuidle device initialization if the reported
* failure is a HW misconfiguration/breakage (-ENXIO).
*/
if (ret == -ENXIO)
continue;
if (ret) { if (ret) {
pr_err("CPU %d failed to init idle CPU ops\n", cpu); pr_err("CPU %d failed to init idle CPU ops\n", cpu);
return ret; goto out_fail;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
pr_err("Failed to allocate cpuidle device\n");
goto out_fail;
}
dev->cpu = cpu;
ret = cpuidle_register_device(dev);
if (ret) {
pr_err("Failed to register cpuidle device for CPU %d\n",
cpu);
kfree(dev);
goto out_fail;
} }
} }
return cpuidle_register(drv, NULL); return 0;
out_fail:
while (--cpu >= 0) {
dev = per_cpu(cpuidle_devices, cpu);
cpuidle_unregister_device(dev);
kfree(dev);
}
cpuidle_unregister_driver(drv);
return ret;
} }
device_initcall(arm64_idle_init); device_initcall(arm_idle_init);

View File

@ -19,7 +19,6 @@
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/export.h> #include <linux/export.h>
#include <asm/proc-fns.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#define AT91_MAX_STATES 2 #define AT91_MAX_STATES 2

View File

@ -19,7 +19,6 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_data/cpuidle-exynos.h> #include <linux/platform_data/cpuidle-exynos.h>
#include <asm/proc-fns.h>
#include <asm/suspend.h> #include <asm/suspend.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>

View File

@ -21,7 +21,6 @@
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/export.h> #include <linux/export.h>
#include <asm/proc-fns.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#define KIRKWOOD_MAX_STATES 2 #define KIRKWOOD_MAX_STATES 2

View File

@ -19,7 +19,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/proc-fns.h>
static atomic_t master = ATOMIC_INIT(0); static atomic_t master = ATOMIC_INIT(0);
static DEFINE_SPINLOCK(master_lock); static DEFINE_SPINLOCK(master_lock);

View File

@ -28,7 +28,6 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <asm/proc-fns.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#define ZYNQ_MAX_STATES 2 #define ZYNQ_MAX_STATES 2

View File

@ -392,7 +392,6 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
/** /**
* _remove_devfreq() - Remove devfreq from the list and release its resources. * _remove_devfreq() - Remove devfreq from the list and release its resources.
* @devfreq: the devfreq struct * @devfreq: the devfreq struct
* @skip: skip calling device_unregister().
*/ */
static void _remove_devfreq(struct devfreq *devfreq) static void _remove_devfreq(struct devfreq *devfreq)
{ {

View File

@ -194,7 +194,7 @@ static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
return 0; return 0;
} }
static struct devfreq_event_ops exynos_ppmu_ops = { static const struct devfreq_event_ops exynos_ppmu_ops = {
.disable = exynos_ppmu_disable, .disable = exynos_ppmu_disable,
.set_event = exynos_ppmu_set_event, .set_event = exynos_ppmu_set_event,
.get_event = exynos_ppmu_get_event, .get_event = exynos_ppmu_get_event,

View File

@ -62,7 +62,8 @@
#define ACTMON_BELOW_WMARK_WINDOW 3 #define ACTMON_BELOW_WMARK_WINDOW 3
#define ACTMON_BOOST_FREQ_STEP 16000 #define ACTMON_BOOST_FREQ_STEP 16000
/* activity counter is incremented every 256 memory transactions, and each /*
* Activity counter is incremented every 256 memory transactions, and each
* transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is * transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is
* 4 * 256 = 1024. * 4 * 256 = 1024.
*/ */
@ -85,16 +86,25 @@
* struct tegra_devfreq_device_config - configuration specific to an ACTMON * struct tegra_devfreq_device_config - configuration specific to an ACTMON
* device * device
* *
* Coefficients and thresholds are in % * Coefficients and thresholds are percentages unless otherwise noted
*/ */
struct tegra_devfreq_device_config { struct tegra_devfreq_device_config {
u32 offset; u32 offset;
u32 irq_mask; u32 irq_mask;
/* Factors applied to boost_freq every consecutive watermark breach */
unsigned int boost_up_coeff; unsigned int boost_up_coeff;
unsigned int boost_down_coeff; unsigned int boost_down_coeff;
/* Define the watermark bounds when applied to the current avg */
unsigned int boost_up_threshold; unsigned int boost_up_threshold;
unsigned int boost_down_threshold; unsigned int boost_down_threshold;
/*
* Threshold of activity (cycles) below which the CPU frequency isn't
* to be taken into account. This is to avoid increasing the EMC
* frequency when the CPU is very busy but not accessing the bus often.
*/
u32 avg_dependency_threshold; u32 avg_dependency_threshold;
}; };
@ -105,7 +115,7 @@ enum tegra_actmon_device {
static struct tegra_devfreq_device_config actmon_device_configs[] = { static struct tegra_devfreq_device_config actmon_device_configs[] = {
{ {
/* MCALL */ /* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0, .offset = 0x1c0,
.irq_mask = 1 << 26, .irq_mask = 1 << 26,
.boost_up_coeff = 200, .boost_up_coeff = 200,
@ -114,7 +124,7 @@ static struct tegra_devfreq_device_config actmon_device_configs[] = {
.boost_down_threshold = 40, .boost_down_threshold = 40,
}, },
{ {
/* MCCPU */ /* MCCPU: memory accesses from the CPUs */
.offset = 0x200, .offset = 0x200,
.irq_mask = 1 << 25, .irq_mask = 1 << 25,
.boost_up_coeff = 800, .boost_up_coeff = 800,
@ -132,25 +142,29 @@ static struct tegra_devfreq_device_config actmon_device_configs[] = {
*/ */
struct tegra_devfreq_device { struct tegra_devfreq_device {
const struct tegra_devfreq_device_config *config; const struct tegra_devfreq_device_config *config;
void __iomem *regs;
spinlock_t lock;
void __iomem *regs; /* Average event count sampled in the last interrupt */
u32 avg_band_freq; u32 avg_count;
u32 avg_count;
unsigned long target_freq; /*
unsigned long boost_freq; * Extra frequency to increase the target by due to consecutive
* watermark breaches.
*/
unsigned long boost_freq;
/* Optimal frequency calculated from the stats for this device */
unsigned long target_freq;
}; };
struct tegra_devfreq { struct tegra_devfreq {
struct devfreq *devfreq; struct devfreq *devfreq;
struct platform_device *pdev;
struct reset_control *reset; struct reset_control *reset;
struct clk *clock; struct clk *clock;
void __iomem *regs; void __iomem *regs;
spinlock_t lock;
struct clk *emc_clock; struct clk *emc_clock;
unsigned long max_freq; unsigned long max_freq;
unsigned long cur_freq; unsigned long cur_freq;
@ -174,19 +188,43 @@ static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = {
{ 250000, 100000 }, { 250000, 100000 },
}; };
static u32 actmon_readl(struct tegra_devfreq *tegra, u32 offset)
{
return readl(tegra->regs + offset);
}
static void actmon_writel(struct tegra_devfreq *tegra, u32 val, u32 offset)
{
writel(val, tegra->regs + offset);
}
static u32 device_readl(struct tegra_devfreq_device *dev, u32 offset)
{
return readl(dev->regs + offset);
}
static void device_writel(struct tegra_devfreq_device *dev, u32 val,
u32 offset)
{
writel(val, dev->regs + offset);
}
static unsigned long do_percent(unsigned long val, unsigned int pct) static unsigned long do_percent(unsigned long val, unsigned int pct)
{ {
return val * pct / 100; return val * pct / 100;
} }
static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq_device *dev) static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{ {
u32 avg = dev->avg_count; u32 avg = dev->avg_count;
u32 band = dev->avg_band_freq * ACTMON_SAMPLING_PERIOD; u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ;
u32 band = avg_band_freq * ACTMON_SAMPLING_PERIOD;
writel(avg + band, dev->regs + ACTMON_DEV_AVG_UPPER_WMARK); device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
avg = max(avg, band);
writel(avg - band, dev->regs + ACTMON_DEV_AVG_LOWER_WMARK); avg = max(dev->avg_count, band);
device_writel(dev, avg - band, ACTMON_DEV_AVG_LOWER_WMARK);
} }
static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra, static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
@ -194,96 +232,96 @@ static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
{ {
u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD;
writel(do_percent(val, dev->config->boost_up_threshold), device_writel(dev, do_percent(val, dev->config->boost_up_threshold),
dev->regs + ACTMON_DEV_UPPER_WMARK); ACTMON_DEV_UPPER_WMARK);
writel(do_percent(val, dev->config->boost_down_threshold), device_writel(dev, do_percent(val, dev->config->boost_down_threshold),
dev->regs + ACTMON_DEV_LOWER_WMARK); ACTMON_DEV_LOWER_WMARK);
} }
static void actmon_write_barrier(struct tegra_devfreq *tegra) static void actmon_write_barrier(struct tegra_devfreq *tegra)
{ {
/* ensure the update has reached the ACTMON */ /* ensure the update has reached the ACTMON */
wmb(); wmb();
readl(tegra->regs + ACTMON_GLB_STATUS); actmon_readl(tegra, ACTMON_GLB_STATUS);
} }
static irqreturn_t actmon_isr(int irq, void *data) static void actmon_isr_device(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{ {
struct tegra_devfreq *tegra = data;
struct tegra_devfreq_device *dev = NULL;
unsigned long flags; unsigned long flags;
u32 val; u32 intr_status, dev_ctrl;
unsigned int i;
val = readl(tegra->regs + ACTMON_GLB_STATUS); spin_lock_irqsave(&dev->lock, flags);
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { dev->avg_count = device_readl(dev, ACTMON_DEV_AVG_COUNT);
if (val & tegra->devices[i].config->irq_mask) { tegra_devfreq_update_avg_wmark(tegra, dev);
dev = tegra->devices + i;
break;
}
}
if (!dev) intr_status = device_readl(dev, ACTMON_DEV_INTR_STATUS);
return IRQ_NONE; dev_ctrl = device_readl(dev, ACTMON_DEV_CTRL);
spin_lock_irqsave(&tegra->lock, flags);
dev->avg_count = readl(dev->regs + ACTMON_DEV_AVG_COUNT);
tegra_devfreq_update_avg_wmark(dev);
val = readl(dev->regs + ACTMON_DEV_INTR_STATUS);
if (val & ACTMON_DEV_INTR_CONSECUTIVE_UPPER) {
val = readl(dev->regs + ACTMON_DEV_CTRL) |
ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN |
ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_UPPER) {
/* /*
* new_boost = min(old_boost * up_coef + step, max_freq) * new_boost = min(old_boost * up_coef + step, max_freq)
*/ */
dev->boost_freq = do_percent(dev->boost_freq, dev->boost_freq = do_percent(dev->boost_freq,
dev->config->boost_up_coeff); dev->config->boost_up_coeff);
dev->boost_freq += ACTMON_BOOST_FREQ_STEP; dev->boost_freq += ACTMON_BOOST_FREQ_STEP;
if (dev->boost_freq >= tegra->max_freq) {
dev->boost_freq = tegra->max_freq;
val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
}
writel(val, dev->regs + ACTMON_DEV_CTRL);
} else if (val & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) {
val = readl(dev->regs + ACTMON_DEV_CTRL) |
ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN |
ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
if (dev->boost_freq >= tegra->max_freq)
dev->boost_freq = tegra->max_freq;
else
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
} else if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) {
/* /*
* new_boost = old_boost * down_coef * new_boost = old_boost * down_coef
* or 0 if (old_boost * down_coef < step / 2) * or 0 if (old_boost * down_coef < step / 2)
*/ */
dev->boost_freq = do_percent(dev->boost_freq, dev->boost_freq = do_percent(dev->boost_freq,
dev->config->boost_down_coeff); dev->config->boost_down_coeff);
if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1)) {
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1))
dev->boost_freq = 0; dev->boost_freq = 0;
val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; else
} dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
writel(val, dev->regs + ACTMON_DEV_CTRL);
} }
if (dev->config->avg_dependency_threshold) { if (dev->config->avg_dependency_threshold) {
val = readl(dev->regs + ACTMON_DEV_CTRL);
if (dev->avg_count >= dev->config->avg_dependency_threshold) if (dev->avg_count >= dev->config->avg_dependency_threshold)
val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
else if (dev->boost_freq == 0) else if (dev->boost_freq == 0)
val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
writel(val, dev->regs + ACTMON_DEV_CTRL);
} }
writel(ACTMON_INTR_STATUS_CLEAR, dev->regs + ACTMON_DEV_INTR_STATUS); device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
actmon_write_barrier(tegra); actmon_write_barrier(tegra);
spin_unlock_irqrestore(&tegra->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
}
return IRQ_WAKE_THREAD; static irqreturn_t actmon_isr(int irq, void *data)
{
struct tegra_devfreq *tegra = data;
bool handled = false;
unsigned int i;
u32 val;
val = actmon_readl(tegra, ACTMON_GLB_STATUS);
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
if (val & tegra->devices[i].config->irq_mask) {
actmon_isr_device(tegra, tegra->devices + i);
handled = true;
}
}
return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
} }
static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra, static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,
@ -317,7 +355,7 @@ static void actmon_update_target(struct tegra_devfreq *tegra,
static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq); static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq);
} }
spin_lock_irqsave(&tegra->lock, flags); spin_lock_irqsave(&dev->lock, flags);
dev->target_freq = dev->avg_count / ACTMON_SAMPLING_PERIOD; dev->target_freq = dev->avg_count / ACTMON_SAMPLING_PERIOD;
avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold; avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold;
@ -327,7 +365,7 @@ static void actmon_update_target(struct tegra_devfreq *tegra,
if (dev->avg_count >= dev->config->avg_dependency_threshold) if (dev->avg_count >= dev->config->avg_dependency_threshold)
dev->target_freq = max(dev->target_freq, static_cpu_emc_freq); dev->target_freq = max(dev->target_freq, static_cpu_emc_freq);
spin_unlock_irqrestore(&tegra->lock, flags); spin_unlock_irqrestore(&dev->lock, flags);
} }
static irqreturn_t actmon_thread_isr(int irq, void *data) static irqreturn_t actmon_thread_isr(int irq, void *data)
@ -345,131 +383,110 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,
unsigned long action, void *ptr) unsigned long action, void *ptr)
{ {
struct clk_notifier_data *data = ptr; struct clk_notifier_data *data = ptr;
struct tegra_devfreq *tegra = container_of(nb, struct tegra_devfreq, struct tegra_devfreq *tegra;
rate_change_nb); struct tegra_devfreq_device *dev;
unsigned int i; unsigned int i;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&tegra->lock, flags); if (action != POST_RATE_CHANGE)
return NOTIFY_OK;
switch (action) { tegra = container_of(nb, struct tegra_devfreq, rate_change_nb);
case POST_RATE_CHANGE:
tegra->cur_freq = data->new_rate / KHZ;
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) tegra->cur_freq = data->new_rate / KHZ;
tegra_devfreq_update_wmark(tegra, tegra->devices + i);
actmon_write_barrier(tegra); for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
break; dev = &tegra->devices[i];
case PRE_RATE_CHANGE:
/* fall through */
case ABORT_RATE_CHANGE:
break;
};
spin_unlock_irqrestore(&tegra->lock, flags); spin_lock_irqsave(&dev->lock, flags);
tegra_devfreq_update_wmark(tegra, dev);
spin_unlock_irqrestore(&dev->lock, flags);
}
actmon_write_barrier(tegra);
return NOTIFY_OK; return NOTIFY_OK;
} }
static void tegra_actmon_enable_interrupts(struct tegra_devfreq *tegra)
{
struct tegra_devfreq_device *dev;
u32 val;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = &tegra->devices[i];
val = device_readl(dev, ACTMON_DEV_CTRL);
val |= ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN;
val |= ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN;
val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
val |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
device_writel(dev, val, ACTMON_DEV_CTRL);
}
actmon_write_barrier(tegra);
}
static void tegra_actmon_disable_interrupts(struct tegra_devfreq *tegra)
{
struct tegra_devfreq_device *dev;
u32 val;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = &tegra->devices[i];
val = device_readl(dev, ACTMON_DEV_CTRL);
val &= ~ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN;
val &= ~ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN;
val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
device_writel(dev, val, ACTMON_DEV_CTRL);
}
actmon_write_barrier(tegra);
}
static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev) struct tegra_devfreq_device *dev)
{ {
u32 val; u32 val = 0;
dev->avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ;
dev->target_freq = tegra->cur_freq; dev->target_freq = tegra->cur_freq;
dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD;
writel(dev->avg_count, dev->regs + ACTMON_DEV_INIT_AVG); device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG);
tegra_devfreq_update_avg_wmark(dev); tegra_devfreq_update_avg_wmark(tegra, dev);
tegra_devfreq_update_wmark(tegra, dev); tegra_devfreq_update_wmark(tegra, dev);
writel(ACTMON_COUNT_WEIGHT, dev->regs + ACTMON_DEV_COUNT_WEIGHT); device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT);
writel(ACTMON_INTR_STATUS_CLEAR, dev->regs + ACTMON_DEV_INTR_STATUS); device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
val = 0; val |= ACTMON_DEV_CTRL_ENB_PERIODIC;
val |= ACTMON_DEV_CTRL_ENB_PERIODIC |
ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN |
ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN;
val |= (ACTMON_AVERAGE_WINDOW_LOG2 - 1) val |= (ACTMON_AVERAGE_WINDOW_LOG2 - 1)
<< ACTMON_DEV_CTRL_K_VAL_SHIFT; << ACTMON_DEV_CTRL_K_VAL_SHIFT;
val |= (ACTMON_BELOW_WMARK_WINDOW - 1) val |= (ACTMON_BELOW_WMARK_WINDOW - 1)
<< ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT; << ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT;
val |= (ACTMON_ABOVE_WMARK_WINDOW - 1) val |= (ACTMON_ABOVE_WMARK_WINDOW - 1)
<< ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT; << ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT;
val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN |
ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
writel(val, dev->regs + ACTMON_DEV_CTRL);
actmon_write_barrier(tegra);
val = readl(dev->regs + ACTMON_DEV_CTRL);
val |= ACTMON_DEV_CTRL_ENB; val |= ACTMON_DEV_CTRL_ENB;
writel(val, dev->regs + ACTMON_DEV_CTRL);
device_writel(dev, val, ACTMON_DEV_CTRL);
actmon_write_barrier(tegra); actmon_write_barrier(tegra);
} }
static int tegra_devfreq_suspend(struct device *dev)
{
struct platform_device *pdev;
struct tegra_devfreq *tegra;
struct tegra_devfreq_device *actmon_dev;
unsigned int i;
u32 val;
pdev = container_of(dev, struct platform_device, dev);
tegra = platform_get_drvdata(pdev);
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
actmon_dev = &tegra->devices[i];
val = readl(actmon_dev->regs + ACTMON_DEV_CTRL);
val &= ~ACTMON_DEV_CTRL_ENB;
writel(val, actmon_dev->regs + ACTMON_DEV_CTRL);
writel(ACTMON_INTR_STATUS_CLEAR,
actmon_dev->regs + ACTMON_DEV_INTR_STATUS);
actmon_write_barrier(tegra);
}
return 0;
}
static int tegra_devfreq_resume(struct device *dev)
{
struct platform_device *pdev;
struct tegra_devfreq *tegra;
struct tegra_devfreq_device *actmon_dev;
unsigned int i;
pdev = container_of(dev, struct platform_device, dev);
tegra = platform_get_drvdata(pdev);
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
actmon_dev = &tegra->devices[i];
tegra_actmon_configure_device(tegra, actmon_dev);
}
return 0;
}
static int tegra_devfreq_target(struct device *dev, unsigned long *freq, static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags) u32 flags)
{ {
struct platform_device *pdev; struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct tegra_devfreq *tegra;
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
unsigned long rate = *freq * KHZ; unsigned long rate = *freq * KHZ;
pdev = container_of(dev, struct platform_device, dev);
tegra = platform_get_drvdata(pdev);
rcu_read_lock(); rcu_read_lock();
opp = devfreq_recommended_opp(dev, &rate, flags); opp = devfreq_recommended_opp(dev, &rate, flags);
if (IS_ERR(opp)) { if (IS_ERR(opp)) {
@ -480,10 +497,8 @@ static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
rate = dev_pm_opp_get_freq(opp); rate = dev_pm_opp_get_freq(opp);
rcu_read_unlock(); rcu_read_unlock();
/* TODO: Once we have per-user clk constraints, set a floor */ clk_set_min_rate(tegra->emc_clock, rate);
clk_set_rate(tegra->emc_clock, rate); clk_set_rate(tegra->emc_clock, 0);
/* TODO: Set voltage as well */
return 0; return 0;
} }
@ -491,13 +506,9 @@ static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
static int tegra_devfreq_get_dev_status(struct device *dev, static int tegra_devfreq_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat) struct devfreq_dev_status *stat)
{ {
struct platform_device *pdev; struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct tegra_devfreq *tegra;
struct tegra_devfreq_device *actmon_dev; struct tegra_devfreq_device *actmon_dev;
pdev = container_of(dev, struct platform_device, dev);
tegra = platform_get_drvdata(pdev);
stat->current_frequency = tegra->cur_freq; stat->current_frequency = tegra->cur_freq;
/* To be used by the tegra governor */ /* To be used by the tegra governor */
@ -508,7 +519,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
actmon_dev = &tegra->devices[MCALL]; actmon_dev = &tegra->devices[MCALL];
/* Number of cycles spent on memory access */ /* Number of cycles spent on memory access */
stat->busy_time = actmon_dev->avg_count; stat->busy_time = device_readl(actmon_dev, ACTMON_DEV_AVG_COUNT);
/* The bus can be considered to be saturated way before 100% */ /* The bus can be considered to be saturated way before 100% */
stat->busy_time *= 100 / BUS_SATURATION_RATIO; stat->busy_time *= 100 / BUS_SATURATION_RATIO;
@ -516,11 +527,19 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
/* Number of cycles in a sampling period */ /* Number of cycles in a sampling period */
stat->total_time = ACTMON_SAMPLING_PERIOD * tegra->cur_freq; stat->total_time = ACTMON_SAMPLING_PERIOD * tegra->cur_freq;
stat->busy_time = min(stat->busy_time, stat->total_time);
return 0; return 0;
} }
static int tegra_devfreq_get_target(struct devfreq *devfreq, static struct devfreq_dev_profile tegra_devfreq_profile = {
unsigned long *freq) .polling_ms = 0,
.target = tegra_devfreq_target,
.get_dev_status = tegra_devfreq_get_dev_status,
};
static int tegra_governor_get_target(struct devfreq *devfreq,
unsigned long *freq)
{ {
struct devfreq_dev_status stat; struct devfreq_dev_status stat;
struct tegra_devfreq *tegra; struct tegra_devfreq *tegra;
@ -548,22 +567,43 @@ static int tegra_devfreq_get_target(struct devfreq *devfreq,
return 0; return 0;
} }
static int tegra_devfreq_event_handler(struct devfreq *devfreq, static int tegra_governor_event_handler(struct devfreq *devfreq,
unsigned int event, void *data) unsigned int event, void *data)
{ {
return 0; struct tegra_devfreq *tegra;
int ret = 0;
tegra = dev_get_drvdata(devfreq->dev.parent);
switch (event) {
case DEVFREQ_GOV_START:
devfreq_monitor_start(devfreq);
tegra_actmon_enable_interrupts(tegra);
break;
case DEVFREQ_GOV_STOP:
tegra_actmon_disable_interrupts(tegra);
devfreq_monitor_stop(devfreq);
break;
case DEVFREQ_GOV_SUSPEND:
tegra_actmon_disable_interrupts(tegra);
devfreq_monitor_suspend(devfreq);
break;
case DEVFREQ_GOV_RESUME:
devfreq_monitor_resume(devfreq);
tegra_actmon_enable_interrupts(tegra);
break;
}
return ret;
} }
static struct devfreq_governor tegra_devfreq_governor = { static struct devfreq_governor tegra_devfreq_governor = {
.name = "tegra", .name = "tegra_actmon",
.get_target_freq = tegra_devfreq_get_target, .get_target_freq = tegra_governor_get_target,
.event_handler = tegra_devfreq_event_handler, .event_handler = tegra_governor_event_handler,
};
static struct devfreq_dev_profile tegra_devfreq_profile = {
.polling_ms = 0,
.target = tegra_devfreq_target,
.get_dev_status = tegra_devfreq_get_dev_status,
}; };
static int tegra_devfreq_probe(struct platform_device *pdev) static int tegra_devfreq_probe(struct platform_device *pdev)
@ -571,8 +611,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
struct tegra_devfreq *tegra; struct tegra_devfreq *tegra;
struct tegra_devfreq_device *dev; struct tegra_devfreq_device *dev;
struct resource *res; struct resource *res;
unsigned long max_freq;
unsigned int i; unsigned int i;
unsigned long rate;
int irq; int irq;
int err; int err;
@ -580,19 +620,11 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
if (!tegra) if (!tegra)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&tegra->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Failed to get regs resource\n");
return -ENODEV;
}
tegra->regs = devm_ioremap_resource(&pdev->dev, res); tegra->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tegra->regs)) { if (IS_ERR(tegra->regs))
dev_err(&pdev->dev, "Failed to get IO memory\n");
return PTR_ERR(tegra->regs); return PTR_ERR(tegra->regs);
}
tegra->reset = devm_reset_control_get(&pdev->dev, "actmon"); tegra->reset = devm_reset_control_get(&pdev->dev, "actmon");
if (IS_ERR(tegra->reset)) { if (IS_ERR(tegra->reset)) {
@ -612,11 +644,7 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return PTR_ERR(tegra->emc_clock); return PTR_ERR(tegra->emc_clock);
} }
err = of_init_opp_table(&pdev->dev); clk_set_rate(tegra->emc_clock, ULONG_MAX);
if (err) {
dev_err(&pdev->dev, "Failed to init operating point table\n");
return err;
}
tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb; tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb;
err = clk_notifier_register(tegra->emc_clock, &tegra->rate_change_nb); err = clk_notifier_register(tegra->emc_clock, &tegra->rate_change_nb);
@ -630,43 +658,41 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
err = clk_prepare_enable(tegra->clock); err = clk_prepare_enable(tegra->clock);
if (err) { if (err) {
reset_control_deassert(tegra->reset); dev_err(&pdev->dev,
"Failed to prepare and enable ACTMON clock\n");
return err; return err;
} }
reset_control_deassert(tegra->reset); reset_control_deassert(tegra->reset);
max_freq = clk_round_rate(tegra->emc_clock, ULONG_MAX); tegra->max_freq = clk_round_rate(tegra->emc_clock, ULONG_MAX) / KHZ;
tegra->max_freq = max_freq / KHZ;
clk_set_rate(tegra->emc_clock, max_freq);
tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ; tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ;
writel(ACTMON_SAMPLING_PERIOD - 1, actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1,
tegra->regs + ACTMON_GLB_PERIOD_CTRL); ACTMON_GLB_PERIOD_CTRL);
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) { for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
dev = tegra->devices + i; dev = tegra->devices + i;
dev->config = actmon_device_configs + i; dev->config = actmon_device_configs + i;
dev->regs = tegra->regs + dev->config->offset; dev->regs = tegra->regs + dev->config->offset;
spin_lock_init(&dev->lock);
tegra_actmon_configure_device(tegra, tegra->devices + i); tegra_actmon_configure_device(tegra, dev);
} }
err = devfreq_add_governor(&tegra_devfreq_governor); for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
if (err) { rate = clk_round_rate(tegra->emc_clock, rate);
dev_err(&pdev->dev, "Failed to add governor\n"); dev_pm_opp_add(&pdev->dev, rate, 0);
return err;
} }
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
tegra->devfreq = devm_devfreq_add_device(&pdev->dev,
&tegra_devfreq_profile,
"tegra",
NULL);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(&pdev->dev, "Failed to get IRQ\n");
return -ENODEV;
}
platform_set_drvdata(pdev, tegra);
err = devm_request_threaded_irq(&pdev->dev, irq, actmon_isr, err = devm_request_threaded_irq(&pdev->dev, irq, actmon_isr,
actmon_thread_isr, IRQF_SHARED, actmon_thread_isr, IRQF_SHARED,
"tegra-devfreq", tegra); "tegra-devfreq", tegra);
@ -675,7 +701,11 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return err; return err;
} }
platform_set_drvdata(pdev, tegra); tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
tegra->devfreq = devm_devfreq_add_device(&pdev->dev,
&tegra_devfreq_profile,
"tegra_actmon",
NULL);
return 0; return 0;
} }
@ -683,6 +713,19 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
static int tegra_devfreq_remove(struct platform_device *pdev) static int tegra_devfreq_remove(struct platform_device *pdev)
{ {
struct tegra_devfreq *tegra = platform_get_drvdata(pdev); struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
u32 val;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
val = device_readl(&tegra->devices[i], ACTMON_DEV_CTRL);
val &= ~ACTMON_DEV_CTRL_ENB;
device_writel(&tegra->devices[i], val, ACTMON_DEV_CTRL);
}
actmon_write_barrier(tegra);
devm_free_irq(&pdev->dev, irq, tegra);
clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb); clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb);
@ -691,28 +734,52 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
return 0; return 0;
} }
static SIMPLE_DEV_PM_OPS(tegra_devfreq_pm_ops, static const struct of_device_id tegra_devfreq_of_match[] = {
tegra_devfreq_suspend,
tegra_devfreq_resume);
static struct of_device_id tegra_devfreq_of_match[] = {
{ .compatible = "nvidia,tegra124-actmon" }, { .compatible = "nvidia,tegra124-actmon" },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match);
static struct platform_driver tegra_devfreq_driver = { static struct platform_driver tegra_devfreq_driver = {
.probe = tegra_devfreq_probe, .probe = tegra_devfreq_probe,
.remove = tegra_devfreq_remove, .remove = tegra_devfreq_remove,
.driver = { .driver = {
.name = "tegra-devfreq", .name = "tegra-devfreq",
.owner = THIS_MODULE,
.of_match_table = tegra_devfreq_of_match, .of_match_table = tegra_devfreq_of_match,
.pm = &tegra_devfreq_pm_ops,
}, },
}; };
module_platform_driver(tegra_devfreq_driver);
MODULE_LICENSE("GPL"); static int __init tegra_devfreq_init(void)
{
int ret = 0;
ret = devfreq_add_governor(&tegra_devfreq_governor);
if (ret) {
pr_err("%s: failed to add governor: %d\n", __func__, ret);
return ret;
}
ret = platform_driver_register(&tegra_devfreq_driver);
if (ret)
devfreq_remove_governor(&tegra_devfreq_governor);
return ret;
}
module_init(tegra_devfreq_init)
static void __exit tegra_devfreq_exit(void)
{
int ret = 0;
platform_driver_unregister(&tegra_devfreq_driver);
ret = devfreq_remove_governor(&tegra_devfreq_governor);
if (ret)
pr_err("%s: failed to remove governor: %d\n", __func__, ret);
}
module_exit(tegra_devfreq_exit)
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Tegra devfreq driver"); MODULE_DESCRIPTION("Tegra devfreq driver");
MODULE_AUTHOR("Tomeu Vizoso <tomeu.vizoso@collabora.com>"); MODULE_AUTHOR("Tomeu Vizoso <tomeu.vizoso@collabora.com>");
MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match);

View File

@ -17,6 +17,8 @@
enum of_gpio_flags; enum of_gpio_flags;
struct acpi_device;
/** /**
* struct acpi_gpio_info - ACPI GPIO specific information * struct acpi_gpio_info - ACPI GPIO specific information
* @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo

View File

@ -164,7 +164,7 @@ static int dw_i2c_probe(struct platform_device *pdev)
/* fast mode by default because of legacy reasons */ /* fast mode by default because of legacy reasons */
clk_freq = 400000; clk_freq = 400000;
if (ACPI_COMPANION(&pdev->dev)) { if (has_acpi_companion(&pdev->dev)) {
dw_i2c_acpi_configure(pdev); dw_i2c_acpi_configure(pdev);
} else if (pdev->dev.of_node) { } else if (pdev->dev.of_node) {
of_property_read_u32(pdev->dev.of_node, of_property_read_u32(pdev->dev.of_node,
@ -284,7 +284,7 @@ static int dw_i2c_remove(struct platform_device *pdev)
pm_runtime_put(&pdev->dev); pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
if (ACPI_COMPANION(&pdev->dev)) if (has_acpi_companion(&pdev->dev))
dw_i2c_acpi_unconfigure(pdev); dw_i2c_acpi_unconfigure(pdev);
return 0; return 0;

View File

@ -133,7 +133,7 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
return AE_OK; return AE_OK;
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
info.acpi_node.companion = adev; info.fwnode = acpi_fwnode_handle(adev);
info.irq = -1; info.irq = -1;
INIT_LIST_HEAD(&resource_list); INIT_LIST_HEAD(&resource_list);
@ -971,7 +971,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
client->dev.bus = &i2c_bus_type; client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type; client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node; client->dev.of_node = info->of_node;
ACPI_COMPANION_SET(&client->dev, info->acpi_node.companion); client->dev.fwnode = info->fwnode;
i2c_dev_set_name(adap, client); i2c_dev_set_name(adap, client);
status = device_register(&client->dev); status = device_register(&client->dev);

View File

@ -96,17 +96,5 @@ static struct pnp_driver idepnp_driver = {
.remove = idepnp_remove, .remove = idepnp_remove,
}; };
static int __init pnpide_init(void) module_pnp_driver(idepnp_driver);
{
return pnp_register_driver(&idepnp_driver);
}
static void __exit pnpide_exit(void)
{
pnp_unregister_driver(&idepnp_driver);
}
module_init(pnpide_init);
module_exit(pnpide_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -217,19 +217,11 @@ static struct cpuidle_state byt_cstates[] = {
.target_residency = 1, .target_residency = 1,
.enter = &intel_idle, .enter = &intel_idle,
.enter_freeze = intel_idle_freeze, }, .enter_freeze = intel_idle_freeze, },
{
.name = "C1E-BYT",
.desc = "MWAIT 0x01",
.flags = MWAIT2flg(0x01),
.exit_latency = 15,
.target_residency = 30,
.enter = &intel_idle,
.enter_freeze = intel_idle_freeze, },
{ {
.name = "C6N-BYT", .name = "C6N-BYT",
.desc = "MWAIT 0x58", .desc = "MWAIT 0x58",
.flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TLB_FLUSHED, .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 40, .exit_latency = 300,
.target_residency = 275, .target_residency = 275,
.enter = &intel_idle, .enter = &intel_idle,
.enter_freeze = intel_idle_freeze, }, .enter_freeze = intel_idle_freeze, },
@ -237,7 +229,7 @@ static struct cpuidle_state byt_cstates[] = {
.name = "C6S-BYT", .name = "C6S-BYT",
.desc = "MWAIT 0x52", .desc = "MWAIT 0x52",
.flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 140, .exit_latency = 500,
.target_residency = 560, .target_residency = 560,
.enter = &intel_idle, .enter = &intel_idle,
.enter_freeze = intel_idle_freeze, }, .enter_freeze = intel_idle_freeze, },
@ -246,7 +238,7 @@ static struct cpuidle_state byt_cstates[] = {
.desc = "MWAIT 0x60", .desc = "MWAIT 0x60",
.flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 1200, .exit_latency = 1200,
.target_residency = 1500, .target_residency = 4000,
.enter = &intel_idle, .enter = &intel_idle,
.enter_freeze = intel_idle_freeze, }, .enter_freeze = intel_idle_freeze, },
{ {
@ -261,6 +253,51 @@ static struct cpuidle_state byt_cstates[] = {
.enter = NULL } .enter = NULL }
}; };
static struct cpuidle_state cht_cstates[] = {
{
.name = "C1-CHT",
.desc = "MWAIT 0x00",
.flags = MWAIT2flg(0x00),
.exit_latency = 1,
.target_residency = 1,
.enter = &intel_idle,
.enter_freeze = intel_idle_freeze, },
{
.name = "C6N-CHT",
.desc = "MWAIT 0x58",
.flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 80,
.target_residency = 275,
.enter = &intel_idle,
.enter_freeze = intel_idle_freeze, },
{
.name = "C6S-CHT",
.desc = "MWAIT 0x52",
.flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 200,
.target_residency = 560,
.enter = &intel_idle,
.enter_freeze = intel_idle_freeze, },
{
.name = "C7-CHT",
.desc = "MWAIT 0x60",
.flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 1200,
.target_residency = 4000,
.enter = &intel_idle,
.enter_freeze = intel_idle_freeze, },
{
.name = "C7S-CHT",
.desc = "MWAIT 0x64",
.flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TLB_FLUSHED,
.exit_latency = 10000,
.target_residency = 20000,
.enter = &intel_idle,
.enter_freeze = intel_idle_freeze, },
{
.enter = NULL }
};
static struct cpuidle_state ivb_cstates[] = { static struct cpuidle_state ivb_cstates[] = {
{ {
.name = "C1-IVB", .name = "C1-IVB",
@ -747,6 +784,12 @@ static const struct idle_cpu idle_cpu_byt = {
.byt_auto_demotion_disable_flag = true, .byt_auto_demotion_disable_flag = true,
}; };
static const struct idle_cpu idle_cpu_cht = {
.state_table = cht_cstates,
.disable_promotion_to_c1e = true,
.byt_auto_demotion_disable_flag = true,
};
static const struct idle_cpu idle_cpu_ivb = { static const struct idle_cpu idle_cpu_ivb = {
.state_table = ivb_cstates, .state_table = ivb_cstates,
.disable_promotion_to_c1e = true, .disable_promotion_to_c1e = true,
@ -775,7 +818,7 @@ static const struct idle_cpu idle_cpu_avn = {
#define ICPU(model, cpu) \ #define ICPU(model, cpu) \
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu } { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu }
static const struct x86_cpu_id intel_idle_ids[] = { static const struct x86_cpu_id intel_idle_ids[] __initconst = {
ICPU(0x1a, idle_cpu_nehalem), ICPU(0x1a, idle_cpu_nehalem),
ICPU(0x1e, idle_cpu_nehalem), ICPU(0x1e, idle_cpu_nehalem),
ICPU(0x1f, idle_cpu_nehalem), ICPU(0x1f, idle_cpu_nehalem),
@ -789,6 +832,7 @@ static const struct x86_cpu_id intel_idle_ids[] = {
ICPU(0x2d, idle_cpu_snb), ICPU(0x2d, idle_cpu_snb),
ICPU(0x36, idle_cpu_atom), ICPU(0x36, idle_cpu_atom),
ICPU(0x37, idle_cpu_byt), ICPU(0x37, idle_cpu_byt),
ICPU(0x4c, idle_cpu_cht),
ICPU(0x3a, idle_cpu_ivb), ICPU(0x3a, idle_cpu_ivb),
ICPU(0x3e, idle_cpu_ivt), ICPU(0x3e, idle_cpu_ivt),
ICPU(0x3c, idle_cpu_hsw), ICPU(0x3c, idle_cpu_hsw),

View File

@ -684,7 +684,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
if (dev_is_pci(dev)) { if (dev_is_pci(dev)) {
pdev = to_pci_dev(dev); pdev = to_pci_dev(dev);
segment = pci_domain_nr(pdev->bus); segment = pci_domain_nr(pdev->bus);
} else if (ACPI_COMPANION(dev)) } else if (has_acpi_companion(dev))
dev = &ACPI_COMPANION(dev)->dev; dev = &ACPI_COMPANION(dev)->dev;
rcu_read_lock(); rcu_read_lock();

View File

@ -1195,16 +1195,6 @@ static struct pnp_driver ene_driver = {
.shutdown = ene_shutdown, .shutdown = ene_shutdown,
}; };
static int __init ene_init(void)
{
return pnp_register_driver(&ene_driver);
}
static void ene_exit(void)
{
pnp_unregister_driver(&ene_driver);
}
module_param(sample_period, int, S_IRUGO); module_param(sample_period, int, S_IRUGO);
MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)"); MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)");
@ -1226,5 +1216,4 @@ MODULE_DESCRIPTION
MODULE_AUTHOR("Maxim Levitsky"); MODULE_AUTHOR("Maxim Levitsky");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(ene_init); module_pnp_driver(ene_driver);
module_exit(ene_exit);

View File

@ -684,16 +684,6 @@ static struct pnp_driver fintek_driver = {
.shutdown = fintek_shutdown, .shutdown = fintek_shutdown,
}; };
static int __init fintek_init(void)
{
return pnp_register_driver(&fintek_driver);
}
static void __exit fintek_exit(void)
{
pnp_unregister_driver(&fintek_driver);
}
module_param(debug, int, S_IRUGO | S_IWUSR); module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable debugging output"); MODULE_PARM_DESC(debug, "Enable debugging output");
@ -703,5 +693,4 @@ MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver");
MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(fintek_init); module_pnp_driver(fintek_driver);
module_exit(fintek_exit);

View File

@ -1708,21 +1708,10 @@ static struct pnp_driver ite_driver = {
.shutdown = ite_shutdown, .shutdown = ite_shutdown,
}; };
static int __init ite_init(void)
{
return pnp_register_driver(&ite_driver);
}
static void __exit ite_exit(void)
{
pnp_unregister_driver(&ite_driver);
}
MODULE_DEVICE_TABLE(pnp, ite_ids); MODULE_DEVICE_TABLE(pnp, ite_ids);
MODULE_DESCRIPTION("ITE Tech Inc. IT8712F/ITE8512F CIR driver"); MODULE_DESCRIPTION("ITE Tech Inc. IT8712F/ITE8512F CIR driver");
MODULE_AUTHOR("Juan J. Garcia de Soria <skandalfo@gmail.com>"); MODULE_AUTHOR("Juan J. Garcia de Soria <skandalfo@gmail.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(ite_init); module_pnp_driver(ite_driver);
module_exit(ite_exit);

View File

@ -1219,16 +1219,6 @@ static struct pnp_driver nvt_driver = {
.shutdown = nvt_shutdown, .shutdown = nvt_shutdown,
}; };
static int __init nvt_init(void)
{
return pnp_register_driver(&nvt_driver);
}
static void __exit nvt_exit(void)
{
pnp_unregister_driver(&nvt_driver);
}
module_param(debug, int, S_IRUGO | S_IWUSR); module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Enable debugging output"); MODULE_PARM_DESC(debug, "Enable debugging output");
@ -1238,5 +1228,4 @@ MODULE_DESCRIPTION("Nuvoton W83667HG-A & W83677HG-I CIR driver");
MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(nvt_init); module_pnp_driver(nvt_driver);
module_exit(nvt_exit);

View File

@ -1175,17 +1175,4 @@ MODULE_AUTHOR("Franco Venturi <fventuri@mediaone.net>");
MODULE_DESCRIPTION("General Instruments SB1000 driver"); MODULE_DESCRIPTION("General Instruments SB1000 driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int __init module_pnp_driver(sb1000_driver);
sb1000_init(void)
{
return pnp_register_driver(&sb1000_driver);
}
static void __exit
sb1000_exit(void)
{
pnp_unregister_driver(&sb1000_driver);
}
module_init(sb1000_init);
module_exit(sb1000_exit);

View File

@ -624,19 +624,7 @@ static struct pnp_driver gmux_pnp_driver = {
}, },
}; };
static int __init apple_gmux_init(void) module_pnp_driver(gmux_pnp_driver);
{
return pnp_register_driver(&gmux_pnp_driver);
}
static void __exit apple_gmux_exit(void)
{
pnp_unregister_driver(&gmux_pnp_driver);
}
module_init(apple_gmux_init);
module_exit(apple_gmux_exit);
MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>"); MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
MODULE_DESCRIPTION("Apple Gmux Driver"); MODULE_DESCRIPTION("Apple Gmux Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -3,7 +3,7 @@
* Bjorn Helgaas <bjorn.helgaas@hp.com> * Bjorn Helgaas <bjorn.helgaas@hp.com>
*/ */
extern spinlock_t pnp_lock; extern struct mutex pnp_lock;
extern const struct attribute_group *pnp_dev_groups[]; extern const struct attribute_group *pnp_dev_groups[];
void *pnp_alloc(long size); void *pnp_alloc(long size);

View File

@ -5,6 +5,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pnp.h> #include <linux/pnp.h>
@ -244,10 +245,10 @@ int pnp_add_card(struct pnp_card *card)
} }
pnp_interface_attach_card(card); pnp_interface_attach_card(card);
spin_lock(&pnp_lock); mutex_lock(&pnp_lock);
list_add_tail(&card->global_list, &pnp_cards); list_add_tail(&card->global_list, &pnp_cards);
list_add_tail(&card->protocol_list, &card->protocol->cards); list_add_tail(&card->protocol_list, &card->protocol->cards);
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
/* we wait until now to add devices in order to ensure the drivers /* we wait until now to add devices in order to ensure the drivers
* will be able to use all of the related devices on the card * will be able to use all of the related devices on the card
@ -276,10 +277,10 @@ void pnp_remove_card(struct pnp_card *card)
struct list_head *pos, *temp; struct list_head *pos, *temp;
device_unregister(&card->dev); device_unregister(&card->dev);
spin_lock(&pnp_lock); mutex_lock(&pnp_lock);
list_del(&card->global_list); list_del(&card->global_list);
list_del(&card->protocol_list); list_del(&card->protocol_list);
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
list_for_each_safe(pos, temp, &card->devices) { list_for_each_safe(pos, temp, &card->devices) {
struct pnp_dev *dev = card_to_pnp_dev(pos); struct pnp_dev *dev = card_to_pnp_dev(pos);
pnp_remove_card_device(dev); pnp_remove_card_device(dev);
@ -297,10 +298,10 @@ int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev)
dev->card_link = NULL; dev->card_link = NULL;
dev_set_name(&dev->dev, "%02x:%02x.%02x", dev_set_name(&dev->dev, "%02x:%02x.%02x",
dev->protocol->number, card->number, dev->number); dev->protocol->number, card->number, dev->number);
spin_lock(&pnp_lock); mutex_lock(&pnp_lock);
dev->card = card; dev->card = card;
list_add_tail(&dev->card_list, &card->devices); list_add_tail(&dev->card_list, &card->devices);
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
return 0; return 0;
} }
@ -310,10 +311,10 @@ int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev)
*/ */
void pnp_remove_card_device(struct pnp_dev *dev) void pnp_remove_card_device(struct pnp_dev *dev)
{ {
spin_lock(&pnp_lock); mutex_lock(&pnp_lock);
dev->card = NULL; dev->card = NULL;
list_del(&dev->card_list); list_del(&dev->card_list);
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
__pnp_remove_device(dev); __pnp_remove_device(dev);
} }
@ -426,9 +427,9 @@ int pnp_register_card_driver(struct pnp_card_driver *drv)
if (error < 0) if (error < 0)
return error; return error;
spin_lock(&pnp_lock); mutex_lock(&pnp_lock);
list_add_tail(&drv->global_list, &pnp_card_drivers); list_add_tail(&drv->global_list, &pnp_card_drivers);
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
list_for_each_safe(pos, temp, &pnp_cards) { list_for_each_safe(pos, temp, &pnp_cards) {
struct pnp_card *card = struct pnp_card *card =
@ -444,9 +445,9 @@ int pnp_register_card_driver(struct pnp_card_driver *drv)
*/ */
void pnp_unregister_card_driver(struct pnp_card_driver *drv) void pnp_unregister_card_driver(struct pnp_card_driver *drv)
{ {
spin_lock(&pnp_lock); mutex_lock(&pnp_lock);
list_del(&drv->global_list); list_del(&drv->global_list);
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
pnp_unregister_driver(&drv->link); pnp_unregister_driver(&drv->link);
} }

View File

@ -9,6 +9,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -19,7 +20,7 @@
static LIST_HEAD(pnp_protocols); static LIST_HEAD(pnp_protocols);
LIST_HEAD(pnp_global); LIST_HEAD(pnp_global);
DEFINE_SPINLOCK(pnp_lock); DEFINE_MUTEX(pnp_lock);
/* /*
* ACPI or PNPBIOS should tell us about all platform devices, so we can * ACPI or PNPBIOS should tell us about all platform devices, so we can
@ -41,6 +42,13 @@ void *pnp_alloc(long size)
return result; return result;
} }
static void pnp_remove_protocol(struct pnp_protocol *protocol)
{
mutex_lock(&pnp_lock);
list_del(&protocol->protocol_list);
mutex_unlock(&pnp_lock);
}
/** /**
* pnp_protocol_register - adds a pnp protocol to the pnp layer * pnp_protocol_register - adds a pnp protocol to the pnp layer
* @protocol: pointer to the corresponding pnp_protocol structure * @protocol: pointer to the corresponding pnp_protocol structure
@ -49,13 +57,14 @@ void *pnp_alloc(long size)
*/ */
int pnp_register_protocol(struct pnp_protocol *protocol) int pnp_register_protocol(struct pnp_protocol *protocol)
{ {
int nodenum;
struct list_head *pos; struct list_head *pos;
int nodenum, ret;
INIT_LIST_HEAD(&protocol->devices); INIT_LIST_HEAD(&protocol->devices);
INIT_LIST_HEAD(&protocol->cards); INIT_LIST_HEAD(&protocol->cards);
nodenum = 0; nodenum = 0;
spin_lock(&pnp_lock);
mutex_lock(&pnp_lock);
/* assign the lowest unused number */ /* assign the lowest unused number */
list_for_each(pos, &pnp_protocols) { list_for_each(pos, &pnp_protocols) {
@ -66,12 +75,18 @@ int pnp_register_protocol(struct pnp_protocol *protocol)
} }
} }
list_add_tail(&protocol->protocol_list, &pnp_protocols);
spin_unlock(&pnp_lock);
protocol->number = nodenum; protocol->number = nodenum;
dev_set_name(&protocol->dev, "pnp%d", nodenum); dev_set_name(&protocol->dev, "pnp%d", nodenum);
return device_register(&protocol->dev);
list_add_tail(&protocol->protocol_list, &pnp_protocols);
mutex_unlock(&pnp_lock);
ret = device_register(&protocol->dev);
if (ret)
pnp_remove_protocol(protocol);
return ret;
} }
/** /**
@ -80,9 +95,7 @@ int pnp_register_protocol(struct pnp_protocol *protocol)
*/ */
void pnp_unregister_protocol(struct pnp_protocol *protocol) void pnp_unregister_protocol(struct pnp_protocol *protocol)
{ {
spin_lock(&pnp_lock); pnp_remove_protocol(protocol);
list_del(&protocol->protocol_list);
spin_unlock(&pnp_lock);
device_unregister(&protocol->dev); device_unregister(&protocol->dev);
} }
@ -157,18 +170,36 @@ struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id,
return dev; return dev;
} }
static void pnp_delist_device(struct pnp_dev *dev)
{
mutex_lock(&pnp_lock);
list_del(&dev->global_list);
list_del(&dev->protocol_list);
mutex_unlock(&pnp_lock);
}
int __pnp_add_device(struct pnp_dev *dev) int __pnp_add_device(struct pnp_dev *dev)
{ {
int ret;
pnp_fixup_device(dev); pnp_fixup_device(dev);
dev->status = PNP_READY; dev->status = PNP_READY;
spin_lock(&pnp_lock);
mutex_lock(&pnp_lock);
list_add_tail(&dev->global_list, &pnp_global); list_add_tail(&dev->global_list, &pnp_global);
list_add_tail(&dev->protocol_list, &dev->protocol->devices); list_add_tail(&dev->protocol_list, &dev->protocol->devices);
spin_unlock(&pnp_lock);
if (dev->protocol->can_wakeup) mutex_unlock(&pnp_lock);
ret = device_register(&dev->dev);
if (ret)
pnp_delist_device(dev);
else if (dev->protocol->can_wakeup)
device_set_wakeup_capable(&dev->dev, device_set_wakeup_capable(&dev->dev,
dev->protocol->can_wakeup(dev)); dev->protocol->can_wakeup(dev));
return device_register(&dev->dev);
return ret;
} }
/* /*
@ -203,10 +234,7 @@ int pnp_add_device(struct pnp_dev *dev)
void __pnp_remove_device(struct pnp_dev *dev) void __pnp_remove_device(struct pnp_dev *dev)
{ {
spin_lock(&pnp_lock); pnp_delist_device(dev);
list_del(&dev->global_list);
list_del(&dev->protocol_list);
spin_unlock(&pnp_lock);
device_unregister(&dev->dev); device_unregister(&dev->dev);
} }

View File

@ -58,22 +58,22 @@ static const struct pnp_device_id *match_device(struct pnp_driver *drv,
int pnp_device_attach(struct pnp_dev *pnp_dev) int pnp_device_attach(struct pnp_dev *pnp_dev)
{ {
spin_lock(&pnp_lock); mutex_lock(&pnp_lock);
if (pnp_dev->status != PNP_READY) { if (pnp_dev->status != PNP_READY) {
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
return -EBUSY; return -EBUSY;
} }
pnp_dev->status = PNP_ATTACHED; pnp_dev->status = PNP_ATTACHED;
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
return 0; return 0;
} }
void pnp_device_detach(struct pnp_dev *pnp_dev) void pnp_device_detach(struct pnp_dev *pnp_dev)
{ {
spin_lock(&pnp_lock); mutex_lock(&pnp_lock);
if (pnp_dev->status == PNP_ATTACHED) if (pnp_dev->status == PNP_ATTACHED)
pnp_dev->status = PNP_READY; pnp_dev->status = PNP_READY;
spin_unlock(&pnp_lock); mutex_unlock(&pnp_lock);
pnp_disable_dev(pnp_dev); pnp_disable_dev(pnp_dev);
} }

View File

@ -248,6 +248,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
ACPI_COMPANION_SET(&dev->dev, device);
dev->data = device; dev->data = device;
/* .enabled means the device can decode the resources */ /* .enabled means the device can decode the resources */
dev->active = device->status.enabled; dev->active = device->status.enabled;
@ -290,11 +291,9 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
return error; return error;
} }
error = acpi_bind_one(&dev->dev, device);
num++; num++;
return error; return 0;
} }
static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,

View File

@ -1054,7 +1054,7 @@ static const struct rapl_defaults rapl_defaults_atom = {
.driver_data = (kernel_ulong_t)&_ops, \ .driver_data = (kernel_ulong_t)&_ops, \
} }
static const struct x86_cpu_id rapl_ids[] = { static const struct x86_cpu_id rapl_ids[] __initconst = {
RAPL_CPU(0x2a, rapl_defaults_core),/* Sandy Bridge */ RAPL_CPU(0x2a, rapl_defaults_core),/* Sandy Bridge */
RAPL_CPU(0x2d, rapl_defaults_core),/* Sandy Bridge EP */ RAPL_CPU(0x2d, rapl_defaults_core),/* Sandy Bridge EP */
RAPL_CPU(0x37, rapl_defaults_atom),/* Valleyview */ RAPL_CPU(0x37, rapl_defaults_atom),/* Valleyview */
@ -1062,6 +1062,7 @@ static const struct x86_cpu_id rapl_ids[] = {
RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */ RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */
RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */ RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */
RAPL_CPU(0x3f, rapl_defaults_hsw_server),/* Haswell servers */ RAPL_CPU(0x3f, rapl_defaults_hsw_server),/* Haswell servers */
RAPL_CPU(0x4f, rapl_defaults_hsw_server),/* Broadwell servers */
RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */ RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */
RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */ RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */
RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */ RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */

View File

@ -234,18 +234,7 @@ static struct pnp_driver fintek_8250_driver = {
.id_table = fintek_dev_table, .id_table = fintek_dev_table,
}; };
static int fintek_8250_init(void) module_pnp_driver(fintek_8250_driver);
{
return pnp_register_driver(&fintek_8250_driver);
}
module_init(fintek_8250_init);
static void fintek_8250_exit(void)
{
pnp_unregister_driver(&fintek_8250_driver);
}
module_exit(fintek_8250_exit);
MODULE_DESCRIPTION("Fintek F812164 module"); MODULE_DESCRIPTION("Fintek F812164 module");
MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>"); MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -51,6 +51,7 @@
#define DRV_VERSION "1.11" #define DRV_VERSION "1.11"
/* Includes */ /* Includes */
#include <linux/acpi.h> /* For ACPI support */
#include <linux/module.h> /* For module specific items */ #include <linux/module.h> /* For module specific items */
#include <linux/moduleparam.h> /* For new moduleparam's */ #include <linux/moduleparam.h> /* For new moduleparam's */
#include <linux/types.h> /* For standard types (like size_t) */ #include <linux/types.h> /* For standard types (like size_t) */
@ -103,6 +104,8 @@ static struct { /* this is private data for the iTCO_wdt device */
struct platform_device *dev; struct platform_device *dev;
/* the PCI-device */ /* the PCI-device */
struct pci_dev *pdev; struct pci_dev *pdev;
/* whether or not the watchdog has been suspended */
bool suspended;
} iTCO_wdt_private; } iTCO_wdt_private;
/* module parameters */ /* module parameters */
@ -571,12 +574,60 @@ static void iTCO_wdt_shutdown(struct platform_device *dev)
iTCO_wdt_stop(NULL); iTCO_wdt_stop(NULL);
} }
#ifdef CONFIG_PM_SLEEP
/*
* Suspend-to-idle requires this, because it stops the ticks and timekeeping, so
* the watchdog cannot be pinged while in that state. In ACPI sleep states the
* watchdog is stopped by the platform firmware.
*/
#ifdef CONFIG_ACPI
static inline bool need_suspend(void)
{
return acpi_target_system_state() == ACPI_STATE_S0;
}
#else
static inline bool need_suspend(void) { return true; }
#endif
static int iTCO_wdt_suspend_noirq(struct device *dev)
{
int ret = 0;
iTCO_wdt_private.suspended = false;
if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) {
ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev);
if (!ret)
iTCO_wdt_private.suspended = true;
}
return ret;
}
static int iTCO_wdt_resume_noirq(struct device *dev)
{
if (iTCO_wdt_private.suspended)
iTCO_wdt_start(&iTCO_wdt_watchdog_dev);
return 0;
}
static struct dev_pm_ops iTCO_wdt_pm = {
.suspend_noirq = iTCO_wdt_suspend_noirq,
.resume_noirq = iTCO_wdt_resume_noirq,
};
#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm)
#else
#define ITCO_WDT_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
static struct platform_driver iTCO_wdt_driver = { static struct platform_driver iTCO_wdt_driver = {
.probe = iTCO_wdt_probe, .probe = iTCO_wdt_probe,
.remove = iTCO_wdt_remove, .remove = iTCO_wdt_remove,
.shutdown = iTCO_wdt_shutdown, .shutdown = iTCO_wdt_shutdown,
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.pm = ITCO_WDT_PM_OPS,
}, },
}; };

View File

@ -252,6 +252,7 @@ struct acpi_device_pnp {
#define acpi_device_bid(d) ((d)->pnp.bus_id) #define acpi_device_bid(d) ((d)->pnp.bus_id)
#define acpi_device_adr(d) ((d)->pnp.bus_address) #define acpi_device_adr(d) ((d)->pnp.bus_address)
const char *acpi_device_hid(struct acpi_device *device); const char *acpi_device_hid(struct acpi_device *device);
#define acpi_device_uid(d) ((d)->pnp.unique_id)
#define acpi_device_name(d) ((d)->pnp.device_name) #define acpi_device_name(d) ((d)->pnp.device_name)
#define acpi_device_class(d) ((d)->pnp.device_class) #define acpi_device_class(d) ((d)->pnp.device_class)
@ -386,7 +387,8 @@ static inline bool is_acpi_node(struct fwnode_handle *fwnode)
static inline struct acpi_device *acpi_node(struct fwnode_handle *fwnode) static inline struct acpi_device *acpi_node(struct fwnode_handle *fwnode)
{ {
return fwnode ? container_of(fwnode, struct acpi_device, fwnode) : NULL; return is_acpi_node(fwnode) ?
container_of(fwnode, struct acpi_device, fwnode) : NULL;
} }
static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)

View File

@ -170,6 +170,7 @@
#define IOMMU_OF_TABLES() OF_TABLE(CONFIG_OF_IOMMU, iommu) #define IOMMU_OF_TABLES() OF_TABLE(CONFIG_OF_IOMMU, iommu)
#define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem)
#define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) #define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method)
#define CPUIDLE_METHOD_OF_TABLES() OF_TABLE(CONFIG_CPU_IDLE, cpuidle_method)
#define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) #define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon)
#define KERNEL_DTB() \ #define KERNEL_DTB() \
@ -504,6 +505,7 @@
CLKSRC_OF_TABLES() \ CLKSRC_OF_TABLES() \
IOMMU_OF_TABLES() \ IOMMU_OF_TABLES() \
CPU_METHOD_OF_TABLES() \ CPU_METHOD_OF_TABLES() \
CPUIDLE_METHOD_OF_TABLES() \
KERNEL_DTB() \ KERNEL_DTB() \
IRQCHIP_OF_MATCH_TABLE() \ IRQCHIP_OF_MATCH_TABLE() \
EARLYCON_OF_TABLES() EARLYCON_OF_TABLES()

View File

@ -53,10 +53,16 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
return adev ? adev->handle : NULL; return adev ? adev->handle : NULL;
} }
#define ACPI_COMPANION(dev) ((dev)->acpi_node.companion) #define ACPI_COMPANION(dev) acpi_node((dev)->fwnode)
#define ACPI_COMPANION_SET(dev, adev) ACPI_COMPANION(dev) = (adev) #define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \
acpi_fwnode_handle(adev) : NULL)
#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev))
static inline bool has_acpi_companion(struct device *dev)
{
return is_acpi_node(dev->fwnode);
}
static inline void acpi_preset_companion(struct device *dev, static inline void acpi_preset_companion(struct device *dev,
struct acpi_device *parent, u64 addr) struct acpi_device *parent, u64 addr)
{ {
@ -471,6 +477,11 @@ static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
return NULL; return NULL;
} }
static inline bool has_acpi_companion(struct device *dev)
{
return false;
}
static inline const char *acpi_dev_name(struct acpi_device *adev) static inline const char *acpi_dev_name(struct acpi_device *adev)
{ {
return NULL; return NULL;

View File

@ -91,7 +91,7 @@ struct devfreq_event_desc {
const char *name; const char *name;
void *driver_data; void *driver_data;
struct devfreq_event_ops *ops; const struct devfreq_event_ops *ops;
}; };
#if defined(CONFIG_PM_DEVFREQ_EVENT) #if defined(CONFIG_PM_DEVFREQ_EVENT)

View File

@ -38,6 +38,7 @@ struct class;
struct subsys_private; struct subsys_private;
struct bus_type; struct bus_type;
struct device_node; struct device_node;
struct fwnode_handle;
struct iommu_ops; struct iommu_ops;
struct iommu_group; struct iommu_group;
@ -650,14 +651,6 @@ struct device_dma_parameters {
unsigned long segment_boundary_mask; unsigned long segment_boundary_mask;
}; };
struct acpi_device;
struct acpi_dev_node {
#ifdef CONFIG_ACPI
struct acpi_device *companion;
#endif
};
/** /**
* struct device - The basic device structure * struct device - The basic device structure
* @parent: The device's "parent" device, the device to which it is attached. * @parent: The device's "parent" device, the device to which it is attached.
@ -703,7 +696,7 @@ struct acpi_dev_node {
* @cma_area: Contiguous memory area for dma allocations * @cma_area: Contiguous memory area for dma allocations
* @archdata: For arch-specific additions. * @archdata: For arch-specific additions.
* @of_node: Associated device tree node. * @of_node: Associated device tree node.
* @acpi_node: Associated ACPI device node. * @fwnode: Associated device node supplied by platform firmware.
* @devt: For creating the sysfs "dev". * @devt: For creating the sysfs "dev".
* @id: device instance * @id: device instance
* @devres_lock: Spinlock to protect the resource of the device. * @devres_lock: Spinlock to protect the resource of the device.
@ -779,7 +772,7 @@ struct device {
struct dev_archdata archdata; struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */ struct device_node *of_node; /* associated device tree node */
struct acpi_dev_node acpi_node; /* associated ACPI device node */ struct fwnode_handle *fwnode; /* firmware device node */
dev_t devt; /* dev_t, creates the sysfs "dev" */ dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */ u32 id; /* device instance */
@ -954,6 +947,9 @@ extern void unlock_device_hotplug(void);
extern int lock_device_hotplug_sysfs(void); extern int lock_device_hotplug_sysfs(void);
extern int device_offline(struct device *dev); extern int device_offline(struct device *dev);
extern int device_online(struct device *dev); extern int device_online(struct device *dev);
extern void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
extern void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
/* /*
* Root device objects for grouping under /sys/devices * Root device objects for grouping under /sys/devices
*/ */

27
include/linux/fwnode.h Normal file
View File

@ -0,0 +1,27 @@
/*
* fwnode.h - Firmware device node object handle type definition.
*
* Copyright (C) 2015, Intel Corporation
* Author: 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.
*/
#ifndef _LINUX_FWNODE_H_
#define _LINUX_FWNODE_H_
enum fwnode_type {
FWNODE_INVALID = 0,
FWNODE_OF,
FWNODE_ACPI,
FWNODE_PDATA,
};
struct fwnode_handle {
enum fwnode_type type;
struct fwnode_handle *secondary;
};
#endif

View File

@ -278,7 +278,7 @@ static inline int i2c_slave_event(struct i2c_client *client,
* @platform_data: stored in i2c_client.dev.platform_data * @platform_data: stored in i2c_client.dev.platform_data
* @archdata: copied into i2c_client.dev.archdata * @archdata: copied into i2c_client.dev.archdata
* @of_node: pointer to OpenFirmware device node * @of_node: pointer to OpenFirmware device node
* @acpi_node: ACPI device node * @fwnode: device node supplied by the platform firmware
* @irq: stored in i2c_client.irq * @irq: stored in i2c_client.irq
* *
* I2C doesn't actually support hardware probing, although controllers and * I2C doesn't actually support hardware probing, although controllers and
@ -299,7 +299,7 @@ struct i2c_board_info {
void *platform_data; void *platform_data;
struct dev_archdata *archdata; struct dev_archdata *archdata;
struct device_node *of_node; struct device_node *of_node;
struct acpi_dev_node acpi_node; struct fwnode_handle *fwnode;
int irq; int irq;
}; };

View File

@ -59,7 +59,7 @@ extern int platform_add_devices(struct platform_device **, int);
struct platform_device_info { struct platform_device_info {
struct device *parent; struct device *parent;
struct acpi_dev_node acpi_node; struct fwnode_handle *fwnode;
const char *name; const char *name;
int id; int id;

View File

@ -1,8 +1,8 @@
#ifndef RESUME_TRACE_H #ifndef PM_TRACE_H
#define RESUME_TRACE_H #define PM_TRACE_H
#ifdef CONFIG_PM_TRACE #ifdef CONFIG_PM_TRACE
#include <asm/resume-trace.h> #include <asm/pm-trace.h>
#include <linux/types.h> #include <linux/types.h>
extern int pm_trace_enabled; extern int pm_trace_enabled;
@ -14,7 +14,7 @@ static inline int pm_trace_is_enabled(void)
struct device; struct device;
extern void set_trace_device(struct device *); extern void set_trace_device(struct device *);
extern void generate_resume_trace(const void *tracedata, unsigned int user); extern void generate_pm_trace(const void *tracedata, unsigned int user);
extern int show_trace_dev_match(char *buf, size_t size); extern int show_trace_dev_match(char *buf, size_t size);
#define TRACE_DEVICE(dev) do { \ #define TRACE_DEVICE(dev) do { \
@ -28,6 +28,7 @@ static inline int pm_trace_is_enabled(void) { return 0; }
#define TRACE_DEVICE(dev) do { } while (0) #define TRACE_DEVICE(dev) do { } while (0)
#define TRACE_RESUME(dev) do { } while (0) #define TRACE_RESUME(dev) do { } while (0)
#define TRACE_SUSPEND(dev) do { } while (0)
#endif #endif

View File

@ -603,10 +603,18 @@ extern void dev_pm_put_subsys_data(struct device *dev);
* Power domains provide callbacks that are executed during system suspend, * Power domains provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions along with * hibernation, system resume and during runtime PM transitions along with
* subsystem-level and driver-level callbacks. * subsystem-level and driver-level callbacks.
*
* @detach: Called when removing a device from the domain.
* @activate: Called before executing probe routines for bus types and drivers.
* @sync: Called after successful driver probe.
* @dismiss: Called after unsuccessful driver probe and after driver removal.
*/ */
struct dev_pm_domain { struct dev_pm_domain {
struct dev_pm_ops ops; struct dev_pm_ops ops;
void (*detach)(struct device *dev, bool power_off); void (*detach)(struct device *dev, bool power_off);
int (*activate)(struct device *dev);
void (*sync)(struct device *dev);
void (*dismiss)(struct device *dev);
}; };
/* /*

View File

@ -127,7 +127,7 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
return to_gpd_data(dev->power.subsys_data->domain_data); return to_gpd_data(dev->power.subsys_data->domain_data);
} }
extern struct generic_pm_domain *dev_to_genpd(struct device *dev); extern struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev);
extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev, struct device *dev,
struct gpd_timing_data *td); struct gpd_timing_data *td);
@ -163,9 +163,9 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
{ {
return ERR_PTR(-ENOSYS); return ERR_PTR(-ENOSYS);
} }
static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) static inline struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev)
{ {
return ERR_PTR(-ENOSYS); return NULL;
} }
static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev, struct device *dev,

View File

@ -510,4 +510,16 @@ static inline void pnp_unregister_driver(struct pnp_driver *drv) { }
#endif /* CONFIG_PNP */ #endif /* CONFIG_PNP */
/**
* module_pnp_driver() - Helper macro for registering a PnP driver
* @__pnp_driver: pnp_driver struct
*
* Helper macro for PnP drivers which do not do anything special in module
* init/exit. This eliminates a lot of boilerplate. Each module may only
* use this macro once, and calling it replaces module_init() and module_exit()
*/
#define module_pnp_driver(__pnp_driver) \
module_driver(__pnp_driver, pnp_register_driver, \
pnp_unregister_driver)
#endif /* _LINUX_PNP_H */ #endif /* _LINUX_PNP_H */

View File

@ -13,6 +13,7 @@
#ifndef _LINUX_PROPERTY_H_ #ifndef _LINUX_PROPERTY_H_
#define _LINUX_PROPERTY_H_ #define _LINUX_PROPERTY_H_
#include <linux/fwnode.h>
#include <linux/types.h> #include <linux/types.h>
struct device; struct device;
@ -40,16 +41,6 @@ int device_property_read_string_array(struct device *dev, const char *propname,
int device_property_read_string(struct device *dev, const char *propname, int device_property_read_string(struct device *dev, const char *propname,
const char **val); const char **val);
enum fwnode_type {
FWNODE_INVALID = 0,
FWNODE_OF,
FWNODE_ACPI,
};
struct fwnode_handle {
enum fwnode_type type;
};
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname); bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname);
int fwnode_property_read_u8_array(struct fwnode_handle *fwnode, int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
const char *propname, u8 *val, const char *propname, u8 *val,
@ -140,4 +131,37 @@ static inline int fwnode_property_read_u64(struct fwnode_handle *fwnode,
return fwnode_property_read_u64_array(fwnode, propname, val, 1); return fwnode_property_read_u64_array(fwnode, propname, val, 1);
} }
/**
* struct property_entry - "Built-in" device property representation.
* @name: Name of the property.
* @type: Type of the property.
* @nval: Number of items of type @type making up the value.
* @value: Value of the property (an array of @nval items of type @type).
*/
struct property_entry {
const char *name;
enum dev_prop_type type;
size_t nval;
union {
void *raw_data;
u8 *u8_data;
u16 *u16_data;
u32 *u32_data;
u64 *u64_data;
const char **str;
} value;
};
/**
* struct property_set - Collection of "built-in" device properties.
* @fwnode: Handle to be pointed to by the fwnode field of struct device.
* @properties: Array of properties terminated with a null entry.
*/
struct property_set {
struct fwnode_handle fwnode;
struct property_entry *properties;
};
void device_add_property_set(struct device *dev, struct property_set *pset);
#endif /* _LINUX_PROPERTY_H_ */ #endif /* _LINUX_PROPERTY_H_ */

View File

@ -11,7 +11,7 @@
#include <linux/export.h> #include <linux/export.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/resume-trace.h> #include <linux/pm-trace.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>

View File

@ -28,6 +28,7 @@
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/moduleparam.h>
#include "power.h" #include "power.h"
@ -233,12 +234,20 @@ static bool platform_suspend_again(suspend_state_t state)
suspend_ops->suspend_again() : false; suspend_ops->suspend_again() : false;
} }
#ifdef CONFIG_PM_DEBUG
static unsigned int pm_test_delay = 5;
module_param(pm_test_delay, uint, 0644);
MODULE_PARM_DESC(pm_test_delay,
"Number of seconds to wait before resuming from suspend test");
#endif
static int suspend_test(int level) static int suspend_test(int level)
{ {
#ifdef CONFIG_PM_DEBUG #ifdef CONFIG_PM_DEBUG
if (pm_test_level == level) { if (pm_test_level == level) {
printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); printk(KERN_INFO "suspend debug: Waiting for %d second(s).\n",
mdelay(5000); pm_test_delay);
mdelay(pm_test_delay * 1000);
return 1; return 1;
} }
#endif /* !CONFIG_PM_DEBUG */ #endif /* !CONFIG_PM_DEBUG */