forked from luck/tmp_suning_uos_patched
ACPI and power management updates for 3.18-rc1
- Rework the handling of wakeup IRQs by the IRQ core such that all of them will be switched over to "wakeup" mode in suspend_device_irqs() and in that mode the first interrupt will abort system suspend in progress or wake up the system if already in suspend-to-idle (or equivalent) without executing any interrupt handlers. Among other things that eliminates the wakeup-related motivation to use the IRQF_NO_SUSPEND interrupt flag with interrupts which don't really need it and should not use it (Thomas Gleixner and Rafael J Wysocki). - Switch over ACPI to handling wakeup interrupts with the help of the new mechanism introduced by the above IRQ core rework (Rafael J Wysocki). - Rework the core generic PM domains code to eliminate code that's not used, add DT support and add a generic mechanism by which devices can be added to PM domains automatically during enumeration (Ulf Hansson, Geert Uytterhoeven and Tomasz Figa). - Add debugfs-based mechanics for debugging generic PM domains (Maciej Matraszek). - ACPICA update to upstream version 20140828. Included are updates related to the SRAT and GTDT tables and the _PSx methods are in the METHOD_NAME list now (Bob Moore and Hanjun Guo). - Add _OSI("Darwin") support to the ACPI core (unfortunately, that can't really be done in a straightforward way) to prevent Thunderbolt from being turned off on Apple systems after boot (or after resume from system suspend) and rework the ACPI Smart Battery Subsystem (SBS) driver to work correctly with Apple platforms (Matthew Garrett and Andreas Noever). - ACPI LPSS (Low-Power Subsystem) driver update cleaning up the code, adding support for 133MHz I2C source clock on Intel Baytrail to it and making it avoid using UART RTS override with Auto Flow Control (Heikki Krogerus). - ACPI backlight updates removing the video_set_use_native_backlight quirk which is not necessary any more, making the code check the list of output devices returned by the _DOD method to avoid creating acpi_video interfaces that won't work and adding a quirk for Lenovo Ideapad Z570 (Hans de Goede, Aaron Lu and Stepan Bujnak). - New Win8 ACPI OSI quirks for some Dell laptops (Edward Lin). - Assorted ACPI code cleanups (Fabian Frederick, Rasmus Villemoes, Sudip Mukherjee, Yijing Wang, and Zhang Rui). - cpufreq core updates and cleanups (Viresh Kumar, Preeti U Murthy, Rasmus Villemoes). - cpufreq driver updates: cpufreq-cpu0/cpufreq-dt (driver name change among other things), ppc-corenet, powernv (Viresh Kumar, Preeti U Murthy, Shilpasri G Bhat, Lucas Stach). - cpuidle support for DT-based idle states infrastructure, new ARM64 cpuidle driver, cpuidle core cleanups (Lorenzo Pieralisi, Rasmus Villemoes). - ARM big.LITTLE cpuidle driver updates: support for DT-based initialization and Exynos5800 compatible string (Lorenzo Pieralisi, Kevin Hilman). - Rework of the test_suspend kernel command line argument and a new trace event for console resume (Srinivas Pandruvada, Todd E Brandt). - Second attempt to optimize swsusp_free() (hibernation core) to make it avoid going through all PFNs which may be way too slow on some systems (Joerg Roedel). - devfreq updates (Paul Bolle, Punit Agrawal, Ãrjan Eide). - rockchip-io Adaptive Voltage Scaling (AVS) driver and AVS entry update in MAINTAINERS (Heiko Stübner, Kevin Hilman). - PM core fix related to clock management (Geert Uytterhoeven). - PM core's sysfs code cleanup (Johannes Berg). / -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABCAAGBQJUNbJoAAoJEILEb/54YlRxRp8QAJyGIPdx+f03oBir+7vvEwhY svxd+V9xXK0UgWNGkCvlMk/1RIVy0qqtXliUrDaE+9tcHACA9+iAxMmNmDsjLOiO gpazuz5kgeznrmp1eNwQnYTt+OCReQIcyCsj4q4fNo9bbETTyr2bRz226LEuZekC TAiKdphYoOszFBgTVg5gfu+lqjHyXjgXPnwMTlRYn1y4YL2adDIgxj9cFedykTTW Eu593TY2dH6ovERJ6q3qxZbRuWuxtww95J07b3t2/2Eb3e/R/zlX0/XJ/C88f/m2 DkqngbOYqCdw+zJeN6k8631foyfUwAcTd0sJ1+5nsm5H4NE5NqObjbxOk5/yNht6 HgvgISGHWLerEw+A/Dk6o0oZOtR1G/TAQ5qQk5nUfKT/sSoU+9/USsXtWhXwZCia XccnJgW6ZtPrJJP3zDnkrxe3gndmLic11QXArw2IhWTsq0sZlAyMgtauBXLdDiQa H/AMiYrUNmIABef1cirBLTtgXN4Zbsai9vIrxMmV7OgBrclrh52NTjzr05P5Hnl2 fRK56mb6mP59LymI7n8fyXL8tHnbNwFvTaxuvrZmzcYbzL0l9DuPocJrrTHRSfhm GFfzfvLj0R66ZM4PthRSwz4H2v1FnlRcCkj5k/QjtBPlyzxtOnJveqve5umbrnb9 T5mRmlAs4iYwLuKCVVNT =sIv/ -----END PGP SIGNATURE----- Merge tag 'pm+acpi-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm Pull ACPI and power management updates from Rafael Wysocki: "Features-wise, to me the most important this time is a rework of wakeup interrupts handling in the core that makes them work consistently across all of the available sleep states, including suspend-to-idle. Many thanks to Thomas Gleixner for his help with this work. Second is an update of the generic PM domains code that has been in need of some care for quite a while. Unused code is being removed, DT support is being added and domains are now going to be attached to devices in bus type code in analogy with the ACPI PM domain. The majority of work here was done by Ulf Hansson who also has been the most active developer this time. Apart from this we have a traditional ACPICA update, this time to upstream version 20140828 and a few ACPI wakeup interrupts handling patches on top of the general rework mentioned above. There also are several cpufreq commits including renaming the cpufreq-cpu0 driver to cpufreq-dt, as this is what implements generic DT-based cpufreq support, and a new DT-based idle states infrastructure for cpuidle. In addition to that, the ACPI LPSS driver is updated, ACPI support for Apple machines is improved, a few bugs are fixed and a few cleanups are made all over. Finally, the Adaptive Voltage Scaling (AVS) subsystem now has a tree maintained by Kevin Hilman that will be merged through the PM tree. Numbers-wise, the generic PM domains update takes the lead this time with 32 non-merge commits, second is cpufreq (15 commits) and the 3rd place goes to the wakeup interrupts handling rework (13 commits). Specifics: - Rework the handling of wakeup IRQs by the IRQ core such that all of them will be switched over to "wakeup" mode in suspend_device_irqs() and in that mode the first interrupt will abort system suspend in progress or wake up the system if already in suspend-to-idle (or equivalent) without executing any interrupt handlers. Among other things that eliminates the wakeup-related motivation to use the IRQF_NO_SUSPEND interrupt flag with interrupts which don't really need it and should not use it (Thomas Gleixner and Rafael Wysocki) - Switch over ACPI to handling wakeup interrupts with the help of the new mechanism introduced by the above IRQ core rework (Rafael Wysocki) - Rework the core generic PM domains code to eliminate code that's not used, add DT support and add a generic mechanism by which devices can be added to PM domains automatically during enumeration (Ulf Hansson, Geert Uytterhoeven and Tomasz Figa). - Add debugfs-based mechanics for debugging generic PM domains (Maciej Matraszek). - ACPICA update to upstream version 20140828. Included are updates related to the SRAT and GTDT tables and the _PSx methods are in the METHOD_NAME list now (Bob Moore and Hanjun Guo). - Add _OSI("Darwin") support to the ACPI core (unfortunately, that can't really be done in a straightforward way) to prevent Thunderbolt from being turned off on Apple systems after boot (or after resume from system suspend) and rework the ACPI Smart Battery Subsystem (SBS) driver to work correctly with Apple platforms (Matthew Garrett and Andreas Noever). - ACPI LPSS (Low-Power Subsystem) driver update cleaning up the code, adding support for 133MHz I2C source clock on Intel Baytrail to it and making it avoid using UART RTS override with Auto Flow Control (Heikki Krogerus). - ACPI backlight updates removing the video_set_use_native_backlight quirk which is not necessary any more, making the code check the list of output devices returned by the _DOD method to avoid creating acpi_video interfaces that won't work and adding a quirk for Lenovo Ideapad Z570 (Hans de Goede, Aaron Lu and Stepan Bujnak) - New Win8 ACPI OSI quirks for some Dell laptops (Edward Lin) - Assorted ACPI code cleanups (Fabian Frederick, Rasmus Villemoes, Sudip Mukherjee, Yijing Wang, and Zhang Rui) - cpufreq core updates and cleanups (Viresh Kumar, Preeti U Murthy, Rasmus Villemoes) - cpufreq driver updates: cpufreq-cpu0/cpufreq-dt (driver name change among other things), ppc-corenet, powernv (Viresh Kumar, Preeti U Murthy, Shilpasri G Bhat, Lucas Stach) - cpuidle support for DT-based idle states infrastructure, new ARM64 cpuidle driver, cpuidle core cleanups (Lorenzo Pieralisi, Rasmus Villemoes) - ARM big.LITTLE cpuidle driver updates: support for DT-based initialization and Exynos5800 compatible string (Lorenzo Pieralisi, Kevin Hilman) - Rework of the test_suspend kernel command line argument and a new trace event for console resume (Srinivas Pandruvada, Todd E Brandt) - Second attempt to optimize swsusp_free() (hibernation core) to make it avoid going through all PFNs which may be way too slow on some systems (Joerg Roedel) - devfreq updates (Paul Bolle, Punit Agrawal, Ãrjan Eide). - rockchip-io Adaptive Voltage Scaling (AVS) driver and AVS entry update in MAINTAINERS (Heiko Stübner, Kevin Hilman) - PM core fix related to clock management (Geert Uytterhoeven) - PM core's sysfs code cleanup (Johannes Berg)" * tag 'pm+acpi-3.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (105 commits) ACPI / fan: printk replacement PM / clk: Fix crash in clocks management code if !CONFIG_PM_RUNTIME PM / Domains: Rename cpu_data to cpuidle_data cpufreq: cpufreq-dt: fix potential double put of cpu OF node cpufreq: cpu0: rename driver and internals to 'cpufreq_dt' PM / hibernate: Iterate over set bits instead of PFNs in swsusp_free() cpufreq: ppc-corenet: remove duplicate update of cpu_data ACPI / sleep: Rework the handling of ACPI GPE wakeup from suspend-to-idle PM / sleep: Rename platform suspend/resume functions in suspend.c PM / sleep: Export dpm_suspend_late/noirq() and dpm_resume_early/noirq() ACPICA: Introduce acpi_enable_all_wakeup_gpes() ACPICA: Clear all non-wakeup GPEs in acpi_hw_enable_wakeup_gpe_block() ACPI / video: check _DOD list when creating backlight devices PM / Domains: Move dev_pm_domain_attach|detach() to pm_domain.h cpufreq: Replace strnicmp with strncasecmp cpufreq: powernv: Set the cpus to nominal frequency during reboot/kexec cpufreq: powernv: Set the pstate of the last hotplugged out cpu in policy->cpus to minimum cpufreq: Allow stop CPU callback to be used by all cpufreq drivers PM / devfreq: exynos: Enable building exynos PPMU as module PM / devfreq: Export helper functions for drivers ...
This commit is contained in:
commit
b528392669
|
@ -8,6 +8,8 @@ Required Properties:
|
|||
* samsung,exynos4210-pd - for exynos4210 type power domain.
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- #power-domain-cells: number of cells in power domain specifier;
|
||||
must be 0.
|
||||
|
||||
Optional Properties:
|
||||
- clocks: List of clock handles. The parent clocks of the input clocks to the
|
||||
|
@ -29,6 +31,7 @@ Example:
|
|||
lcd0: power-domain-lcd0 {
|
||||
compatible = "samsung,exynos4210-pd";
|
||||
reg = <0x10023C00 0x10>;
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
|
||||
mfc_pd: power-domain@10044060 {
|
||||
|
@ -37,12 +40,8 @@ Example:
|
|||
clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>,
|
||||
<&clock CLK_MOUT_USER_ACLK333>;
|
||||
clock-names = "oscclk", "pclk0", "clk0";
|
||||
#power-domain-cells = <0>;
|
||||
};
|
||||
|
||||
Example of the node using power domain:
|
||||
|
||||
node {
|
||||
/* ... */
|
||||
samsung,power-domain = <&lcd0>;
|
||||
/* ... */
|
||||
};
|
||||
See Documentation/devicetree/bindings/power/power_domain.txt for description
|
||||
of consumer-side bindings.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
Generic CPU0 cpufreq driver
|
||||
Generic cpufreq driver
|
||||
|
||||
It is a generic cpufreq driver for CPU0 frequency management. It
|
||||
supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
|
||||
systems which share clock and voltage across all CPUs.
|
||||
It is a generic DT based cpufreq driver for frequency management. It supports
|
||||
both uniprocessor (UP) and symmetric multiprocessor (SMP) systems which share
|
||||
clock and voltage across all CPUs.
|
||||
|
||||
Both required and optional properties listed below must be defined
|
||||
under node /cpus/cpu@0.
|
49
Documentation/devicetree/bindings/power/power_domain.txt
Normal file
49
Documentation/devicetree/bindings/power/power_domain.txt
Normal file
|
@ -0,0 +1,49 @@
|
|||
* Generic PM domains
|
||||
|
||||
System on chip designs are often divided into multiple PM domains that can be
|
||||
used for power gating of selected IP blocks for power saving by reduced leakage
|
||||
current.
|
||||
|
||||
This device tree binding can be used to bind PM domain consumer devices with
|
||||
their PM domains provided by PM domain providers. A PM domain provider can be
|
||||
represented by any node in the device tree and can provide one or more PM
|
||||
domains. A consumer node can refer to the provider by a phandle and a set of
|
||||
phandle arguments (so called PM domain specifiers) of length specified by the
|
||||
#power-domain-cells property in the PM domain provider node.
|
||||
|
||||
==PM domain providers==
|
||||
|
||||
Required properties:
|
||||
- #power-domain-cells : Number of cells in a PM domain specifier;
|
||||
Typically 0 for nodes representing a single PM domain and 1 for nodes
|
||||
providing multiple PM domains (e.g. power controllers), but can be any value
|
||||
as specified by device tree binding documentation of particular provider.
|
||||
|
||||
Example:
|
||||
|
||||
power: power-controller@12340000 {
|
||||
compatible = "foo,power-controller";
|
||||
reg = <0x12340000 0x1000>;
|
||||
#power-domain-cells = <1>;
|
||||
};
|
||||
|
||||
The node above defines a power controller that is a PM domain provider and
|
||||
expects one cell as its phandle argument.
|
||||
|
||||
==PM domain consumers==
|
||||
|
||||
Required properties:
|
||||
- power-domains : A phandle and PM domain specifier as defined by bindings of
|
||||
the power controller specified by phandle.
|
||||
|
||||
Example:
|
||||
|
||||
leaky-device@12350000 {
|
||||
compatible = "foo,i-leak-current";
|
||||
reg = <0x12350000 0x1000>;
|
||||
power-domains = <&power 0>;
|
||||
};
|
||||
|
||||
The node above defines a typical PM domain consumer device, which is located
|
||||
inside a PM domain with index 0 of a power controller represented by a node
|
||||
with the label "power".
|
|
@ -0,0 +1,83 @@
|
|||
Rockchip SRAM for IO Voltage Domains:
|
||||
-------------------------------------
|
||||
|
||||
IO domain voltages on some Rockchip SoCs are variable but need to be
|
||||
kept in sync between the regulators and the SoC using a special
|
||||
register.
|
||||
|
||||
A specific example using rk3288:
|
||||
- If the regulator hooked up to a pin like SDMMC0_VDD is 3.3V then
|
||||
bit 7 of GRF_IO_VSEL needs to be 0. If the regulator hooked up to
|
||||
that same pin is 1.8V then bit 7 of GRF_IO_VSEL needs to be 1.
|
||||
|
||||
Said another way, this driver simply handles keeping bits in the SoC's
|
||||
general register file (GRF) in sync with the actual value of a voltage
|
||||
hooked up to the pins.
|
||||
|
||||
Note that this driver specifically doesn't include:
|
||||
- any logic for deciding what voltage we should set regulators to
|
||||
- any logic for deciding whether regulators (or internal SoC blocks)
|
||||
should have power or not have power
|
||||
|
||||
If there were some other software that had the smarts of making
|
||||
decisions about regulators, it would work in conjunction with this
|
||||
driver. When that other software adjusted a regulator's voltage then
|
||||
this driver would handle telling the SoC about it. A good example is
|
||||
vqmmc for SD. In that case the dw_mmc driver simply is told about a
|
||||
regulator. It changes the regulator between 3.3V and 1.8V at the
|
||||
right time. This driver notices the change and makes sure that the
|
||||
SoC is on the same page.
|
||||
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of:
|
||||
- "rockchip,rk3188-io-voltage-domain" for rk3188
|
||||
- "rockchip,rk3288-io-voltage-domain" for rk3288
|
||||
- rockchip,grf: phandle to the syscon managing the "general register files"
|
||||
|
||||
|
||||
You specify supplies using the standard regulator bindings by including
|
||||
a phandle the the relevant regulator. All specified supplies must be able
|
||||
to report their voltage. The IO Voltage Domain for any non-specified
|
||||
supplies will be not be touched.
|
||||
|
||||
Possible supplies for rk3188:
|
||||
- ap0-supply: The supply connected to AP0_VCC.
|
||||
- ap1-supply: The supply connected to AP1_VCC.
|
||||
- cif-supply: The supply connected to CIF_VCC.
|
||||
- flash-supply: The supply connected to FLASH_VCC.
|
||||
- lcdc0-supply: The supply connected to LCD0_VCC.
|
||||
- lcdc1-supply: The supply connected to LCD1_VCC.
|
||||
- vccio0-supply: The supply connected to VCCIO0.
|
||||
- vccio1-supply: The supply connected to VCCIO1.
|
||||
Sometimes also labeled VCCIO1 and VCCIO2.
|
||||
|
||||
Possible supplies for rk3288:
|
||||
- audio-supply: The supply connected to APIO4_VDD.
|
||||
- bb-supply: The supply connected to APIO5_VDD.
|
||||
- dvp-supply: The supply connected to DVPIO_VDD.
|
||||
- flash0-supply: The supply connected to FLASH0_VDD. Typically for eMMC
|
||||
- flash1-supply: The supply connected to FLASH1_VDD. Also known as SDIO1.
|
||||
- gpio30-supply: The supply connected to APIO1_VDD.
|
||||
- gpio1830 The supply connected to APIO2_VDD.
|
||||
- lcdc-supply: The supply connected to LCDC_VDD.
|
||||
- sdcard-supply: The supply connected to SDMMC0_VDD.
|
||||
- wifi-supply: The supply connected to APIO3_VDD. Also known as SDIO0.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
io-domains {
|
||||
compatible = "rockchip,rk3288-io-voltage-domain";
|
||||
rockchip,grf = <&grf>;
|
||||
|
||||
audio-supply = <&vcc18_codec>;
|
||||
bb-supply = <&vcc33_io>;
|
||||
dvp-supply = <&vcc_18>;
|
||||
flash0-supply = <&vcc18_flashio>;
|
||||
gpio1830-supply = <&vcc33_io>;
|
||||
gpio30-supply = <&vcc33_pmuio>;
|
||||
lcdc-supply = <&vcc33_lcd>;
|
||||
sdcard-supply = <&vccio_sd>;
|
||||
wifi-supply = <&vcc18_wl>;
|
||||
};
|
|
@ -3321,11 +3321,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|||
|
||||
tdfx= [HW,DRM]
|
||||
|
||||
test_suspend= [SUSPEND]
|
||||
test_suspend= [SUSPEND][,N]
|
||||
Specify "mem" (for Suspend-to-RAM) or "standby" (for
|
||||
standby suspend) as the system sleep state to briefly
|
||||
enter during system startup. The system is woken from
|
||||
this state using a wakeup-capable RTC alarm.
|
||||
standby suspend) or "freeze" (for suspend type freeze)
|
||||
as the system sleep state during system startup with
|
||||
the optional capability to repeat N number of times.
|
||||
The system is woken from this state using a
|
||||
wakeup-capable RTC alarm.
|
||||
|
||||
thash_entries= [KNL,NET]
|
||||
Set number of hash buckets for TCP connection
|
||||
|
|
123
Documentation/power/suspend-and-interrupts.txt
Normal file
123
Documentation/power/suspend-and-interrupts.txt
Normal file
|
@ -0,0 +1,123 @@
|
|||
System Suspend and Device Interrupts
|
||||
|
||||
Copyright (C) 2014 Intel Corp.
|
||||
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
|
||||
|
||||
Suspending and Resuming Device IRQs
|
||||
-----------------------------------
|
||||
|
||||
Device interrupt request lines (IRQs) are generally disabled during system
|
||||
suspend after the "late" phase of suspending devices (that is, after all of the
|
||||
->prepare, ->suspend and ->suspend_late callbacks have been executed for all
|
||||
devices). That is done by suspend_device_irqs().
|
||||
|
||||
The rationale for doing so is that after the "late" phase of device suspend
|
||||
there is no legitimate reason why any interrupts from suspended devices should
|
||||
trigger and if any devices have not been suspended properly yet, it is better to
|
||||
block interrupts from them anyway. Also, in the past we had problems with
|
||||
interrupt handlers for shared IRQs that device drivers implementing them were
|
||||
not prepared for interrupts triggering after their devices had been suspended.
|
||||
In some cases they would attempt to access, for example, memory address spaces
|
||||
of suspended devices and cause unpredictable behavior to ensue as a result.
|
||||
Unfortunately, such problems are very difficult to debug and the introduction
|
||||
of suspend_device_irqs(), along with the "noirq" phase of device suspend and
|
||||
resume, was the only practical way to mitigate them.
|
||||
|
||||
Device IRQs are re-enabled during system resume, right before the "early" phase
|
||||
of resuming devices (that is, before starting to execute ->resume_early
|
||||
callbacks for devices). The function doing that is resume_device_irqs().
|
||||
|
||||
|
||||
The IRQF_NO_SUSPEND Flag
|
||||
------------------------
|
||||
|
||||
There are interrupts that can legitimately trigger during the entire system
|
||||
suspend-resume cycle, including the "noirq" phases of suspending and resuming
|
||||
devices as well as during the time when nonboot CPUs are taken offline and
|
||||
brought back online. That applies to timer interrupts in the first place,
|
||||
but also to IPIs and to some other special-purpose interrupts.
|
||||
|
||||
The IRQF_NO_SUSPEND flag is used to indicate that to the IRQ subsystem when
|
||||
requesting a special-purpose interrupt. It causes suspend_device_irqs() to
|
||||
leave the corresponding IRQ enabled so as to allow the interrupt to work all
|
||||
the time as expected.
|
||||
|
||||
Note that the IRQF_NO_SUSPEND flag affects the entire IRQ and not just one
|
||||
user of it. Thus, if the IRQ is shared, all of the interrupt handlers installed
|
||||
for it will be executed as usual after suspend_device_irqs(), even if the
|
||||
IRQF_NO_SUSPEND flag was not passed to request_irq() (or equivalent) by some of
|
||||
the IRQ's users. For this reason, using IRQF_NO_SUSPEND and IRQF_SHARED at the
|
||||
same time should be avoided.
|
||||
|
||||
|
||||
System Wakeup Interrupts, enable_irq_wake() and disable_irq_wake()
|
||||
------------------------------------------------------------------
|
||||
|
||||
System wakeup interrupts generally need to be configured to wake up the system
|
||||
from sleep states, especially if they are used for different purposes (e.g. as
|
||||
I/O interrupts) in the working state.
|
||||
|
||||
That may involve turning on a special signal handling logic within the platform
|
||||
(such as an SoC) so that signals from a given line are routed in a different way
|
||||
during system sleep so as to trigger a system wakeup when needed. For example,
|
||||
the platform may include a dedicated interrupt controller used specifically for
|
||||
handling system wakeup events. Then, if a given interrupt line is supposed to
|
||||
wake up the system from sleep sates, the corresponding input of that interrupt
|
||||
controller needs to be enabled to receive signals from the line in question.
|
||||
After wakeup, it generally is better to disable that input to prevent the
|
||||
dedicated controller from triggering interrupts unnecessarily.
|
||||
|
||||
The IRQ subsystem provides two helper functions to be used by device drivers for
|
||||
those purposes. Namely, enable_irq_wake() turns on the platform's logic for
|
||||
handling the given IRQ as a system wakeup interrupt line and disable_irq_wake()
|
||||
turns that logic off.
|
||||
|
||||
Calling enable_irq_wake() causes suspend_device_irqs() to treat the given IRQ
|
||||
in a special way. Namely, the IRQ remains enabled, by on the first interrupt
|
||||
it will be disabled, marked as pending and "suspended" so that it will be
|
||||
re-enabled by resume_device_irqs() during the subsequent system resume. Also
|
||||
the PM core is notified about the event which casues the system suspend in
|
||||
progress to be aborted (that doesn't have to happen immediately, but at one
|
||||
of the points where the suspend thread looks for pending wakeup events).
|
||||
|
||||
This way every interrupt from a wakeup interrupt source will either cause the
|
||||
system suspend currently in progress to be aborted or wake up the system if
|
||||
already suspended. However, after suspend_device_irqs() interrupt handlers are
|
||||
not executed for system wakeup IRQs. They are only executed for IRQF_NO_SUSPEND
|
||||
IRQs at that time, but those IRQs should not be configured for system wakeup
|
||||
using enable_irq_wake().
|
||||
|
||||
|
||||
Interrupts and Suspend-to-Idle
|
||||
------------------------------
|
||||
|
||||
Suspend-to-idle (also known as the "freeze" sleep state) is a relatively new
|
||||
system sleep state that works by idling all of the processors and waiting for
|
||||
interrupts right after the "noirq" phase of suspending devices.
|
||||
|
||||
Of course, this means that all of the interrupts with the IRQF_NO_SUSPEND flag
|
||||
set will bring CPUs out of idle while in that state, but they will not cause the
|
||||
IRQ subsystem to trigger a system wakeup.
|
||||
|
||||
System wakeup interrupts, in turn, will trigger wakeup from suspend-to-idle in
|
||||
analogy with what they do in the full system suspend case. The only difference
|
||||
is that the wakeup from suspend-to-idle is signaled using the usual working
|
||||
state interrupt delivery mechanisms and doesn't require the platform to use
|
||||
any special interrupt handling logic for it to work.
|
||||
|
||||
|
||||
IRQF_NO_SUSPEND and enable_irq_wake()
|
||||
-------------------------------------
|
||||
|
||||
There are no valid reasons to use both enable_irq_wake() and the IRQF_NO_SUSPEND
|
||||
flag on the same IRQ.
|
||||
|
||||
First of all, if the IRQ is not shared, the rules for handling IRQF_NO_SUSPEND
|
||||
interrupts (interrupt handlers are invoked after suspend_device_irqs()) are
|
||||
directly at odds with the rules for handling system wakeup interrupts (interrupt
|
||||
handlers are not invoked after suspend_device_irqs()).
|
||||
|
||||
Second, both enable_irq_wake() and IRQF_NO_SUSPEND apply to entire IRQs and not
|
||||
to individual interrupt handlers, so sharing an IRQ between a system wakeup
|
||||
interrupt source and an IRQF_NO_SUSPEND interrupt source does not make sense.
|
|
@ -8490,11 +8490,11 @@ S: Maintained
|
|||
F: Documentation/security/Smack.txt
|
||||
F: security/smack/
|
||||
|
||||
SMARTREFLEX DRIVERS FOR ADAPTIVE VOLTAGE SCALING (AVS)
|
||||
DRIVERS FOR ADAPTIVE VOLTAGE SCALING (AVS)
|
||||
M: Kevin Hilman <khilman@kernel.org>
|
||||
M: Nishanth Menon <nm@ti.com>
|
||||
S: Maintained
|
||||
F: drivers/power/avs/smartreflex.c
|
||||
F: drivers/power/avs/
|
||||
F: include/linux/power/smartreflex.h
|
||||
L: linux-pm@vger.kernel.org
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ cpu0: cpu@0 {
|
|||
compatible = "arm,cortex-a15";
|
||||
reg = <0>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
|
||||
};
|
||||
|
||||
cpu1: cpu@1 {
|
||||
|
@ -45,6 +46,7 @@ cpu1: cpu@1 {
|
|||
compatible = "arm,cortex-a15";
|
||||
reg = <1>;
|
||||
cci-control-port = <&cci_control1>;
|
||||
cpu-idle-states = <&CLUSTER_SLEEP_BIG>;
|
||||
};
|
||||
|
||||
cpu2: cpu@2 {
|
||||
|
@ -52,6 +54,7 @@ cpu2: cpu@2 {
|
|||
compatible = "arm,cortex-a7";
|
||||
reg = <0x100>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
|
||||
};
|
||||
|
||||
cpu3: cpu@3 {
|
||||
|
@ -59,6 +62,7 @@ cpu3: cpu@3 {
|
|||
compatible = "arm,cortex-a7";
|
||||
reg = <0x101>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
|
||||
};
|
||||
|
||||
cpu4: cpu@4 {
|
||||
|
@ -66,6 +70,25 @@ cpu4: cpu@4 {
|
|||
compatible = "arm,cortex-a7";
|
||||
reg = <0x102>;
|
||||
cci-control-port = <&cci_control2>;
|
||||
cpu-idle-states = <&CLUSTER_SLEEP_LITTLE>;
|
||||
};
|
||||
|
||||
idle-states {
|
||||
CLUSTER_SLEEP_BIG: cluster-sleep-big {
|
||||
compatible = "arm,idle-state";
|
||||
local-timer-stop;
|
||||
entry-latency-us = <1000>;
|
||||
exit-latency-us = <700>;
|
||||
min-residency-us = <2000>;
|
||||
};
|
||||
|
||||
CLUSTER_SLEEP_LITTLE: cluster-sleep-little {
|
||||
compatible = "arm,idle-state";
|
||||
local-timer-stop;
|
||||
entry-latency-us = <1000>;
|
||||
exit-latency-us = <500>;
|
||||
min-residency-us = <2500>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -193,7 +193,6 @@ static void __init exynos_init_late(void)
|
|||
/* to be supported later */
|
||||
return;
|
||||
|
||||
pm_genpd_poweroff_unused();
|
||||
exynos_pm_init();
|
||||
}
|
||||
|
||||
|
|
|
@ -105,78 +105,6 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain)
|
|||
return exynos_pd_power(domain, false);
|
||||
}
|
||||
|
||||
static void exynos_add_device_to_domain(struct exynos_pm_domain *pd,
|
||||
struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "adding to power domain %s\n", pd->pd.name);
|
||||
|
||||
while (1) {
|
||||
ret = pm_genpd_add_device(&pd->pd, dev);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
pm_genpd_dev_need_restore(dev, true);
|
||||
}
|
||||
|
||||
static void exynos_remove_device_from_domain(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain *genpd = dev_to_genpd(dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "removing from power domain %s\n", genpd->name);
|
||||
|
||||
while (1) {
|
||||
ret = pm_genpd_remove_device(genpd, dev);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
static void exynos_read_domain_from_dt(struct device *dev)
|
||||
{
|
||||
struct platform_device *pd_pdev;
|
||||
struct exynos_pm_domain *pd;
|
||||
struct device_node *node;
|
||||
|
||||
node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0);
|
||||
if (!node)
|
||||
return;
|
||||
pd_pdev = of_find_device_by_node(node);
|
||||
if (!pd_pdev)
|
||||
return;
|
||||
pd = platform_get_drvdata(pd_pdev);
|
||||
exynos_add_device_to_domain(pd, dev);
|
||||
}
|
||||
|
||||
static int exynos_pm_notifier_call(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
||||
switch (event) {
|
||||
case BUS_NOTIFY_BIND_DRIVER:
|
||||
if (dev->of_node)
|
||||
exynos_read_domain_from_dt(dev);
|
||||
|
||||
break;
|
||||
|
||||
case BUS_NOTIFY_UNBOUND_DRIVER:
|
||||
exynos_remove_device_from_domain(dev);
|
||||
|
||||
break;
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block platform_nb = {
|
||||
.notifier_call = exynos_pm_notifier_call,
|
||||
};
|
||||
|
||||
static __init int exynos4_pm_init_power_domain(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
@ -202,7 +130,6 @@ static __init int exynos4_pm_init_power_domain(void)
|
|||
pd->base = of_iomap(np, 0);
|
||||
pd->pd.power_off = exynos_pd_power_off;
|
||||
pd->pd.power_on = exynos_pd_power_on;
|
||||
pd->pd.of_node = np;
|
||||
|
||||
pd->oscclk = clk_get(dev, "oscclk");
|
||||
if (IS_ERR(pd->oscclk))
|
||||
|
@ -228,15 +155,12 @@ static __init int exynos4_pm_init_power_domain(void)
|
|||
clk_put(pd->oscclk);
|
||||
|
||||
no_clk:
|
||||
platform_set_drvdata(pdev, pd);
|
||||
|
||||
on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN;
|
||||
|
||||
pm_genpd_init(&pd->pd, NULL, !on);
|
||||
of_genpd_add_provider_simple(np, &pd->pd);
|
||||
}
|
||||
|
||||
bus_register_notifier(&platform_bus_type, &platform_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(exynos4_pm_init_power_domain);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
static void __init imx27_dt_init(void)
|
||||
{
|
||||
struct platform_device_info devinfo = { .name = "cpufreq-cpu0", };
|
||||
struct platform_device_info devinfo = { .name = "cpufreq-dt", };
|
||||
|
||||
mxc_arch_reset_init_dt();
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ static void __init imx51_ipu_mipi_setup(void)
|
|||
|
||||
static void __init imx51_dt_init(void)
|
||||
{
|
||||
struct platform_device_info devinfo = { .name = "cpufreq-cpu0", };
|
||||
struct platform_device_info devinfo = { .name = "cpufreq-dt", };
|
||||
|
||||
mxc_arch_reset_init_dt();
|
||||
imx51_ipu_mipi_setup();
|
||||
|
|
|
@ -644,7 +644,7 @@ static int __init armada_xp_pmsu_cpufreq_init(void)
|
|||
}
|
||||
}
|
||||
|
||||
platform_device_register_simple("cpufreq-generic", -1, NULL, 0);
|
||||
platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ static inline void omap_init_cpufreq(void)
|
|||
if (!of_have_populated_dt())
|
||||
devinfo.name = "omap-cpufreq";
|
||||
else
|
||||
devinfo.name = "cpufreq-cpu0";
|
||||
devinfo.name = "cpufreq-dt";
|
||||
platform_device_register_full(&devinfo);
|
||||
}
|
||||
|
||||
|
|
|
@ -440,8 +440,3 @@ void s3c64xx_restart(enum reboot_mode mode, const char *cmd)
|
|||
/* if all else fails, or mode was for soft, jump to 0 */
|
||||
soft_restart(0);
|
||||
}
|
||||
|
||||
void __init s3c64xx_init_late(void)
|
||||
{
|
||||
s3c64xx_pm_late_initcall();
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ void s3c64xx_init_irq(u32 vic0, u32 vic1);
|
|||
void s3c64xx_init_io(struct map_desc *mach_desc, int size);
|
||||
|
||||
void s3c64xx_restart(enum reboot_mode mode, const char *cmd);
|
||||
void s3c64xx_init_late(void);
|
||||
|
||||
void s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
|
||||
unsigned long xusbxti_f, bool is_s3c6400, void __iomem *reg_base);
|
||||
|
@ -52,12 +51,6 @@ extern void s3c6410_map_io(void);
|
|||
#define s3c6410_init NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int __init s3c64xx_pm_late_initcall(void);
|
||||
#else
|
||||
static inline int s3c64xx_pm_late_initcall(void) { return 0; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_S3C64XX_PL080
|
||||
extern struct pl08x_platform_data s3c64xx_dma0_plat_data;
|
||||
extern struct pl08x_platform_data s3c64xx_dma1_plat_data;
|
||||
|
|
|
@ -233,7 +233,6 @@ MACHINE_START(ANW6410, "A&W6410")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = anw6410_map_io,
|
||||
.init_machine = anw6410_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -857,7 +857,6 @@ MACHINE_START(WLF_CRAGG_6410, "Wolfson Cragganmore 6410")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = crag6410_map_io,
|
||||
.init_machine = crag6410_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -277,7 +277,6 @@ MACHINE_START(HMT, "Airgoo-HMT")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = hmt_map_io,
|
||||
.init_machine = hmt_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -366,7 +366,6 @@ MACHINE_START(MINI6410, "MINI6410")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = mini6410_map_io,
|
||||
.init_machine = mini6410_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -103,7 +103,6 @@ MACHINE_START(NCP, "NCP")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = ncp_map_io,
|
||||
.init_machine = ncp_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -335,7 +335,6 @@ MACHINE_START(REAL6410, "REAL6410")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = real6410_map_io,
|
||||
.init_machine = real6410_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -156,7 +156,6 @@ MACHINE_START(SMARTQ5, "SmartQ 5")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = smartq_map_io,
|
||||
.init_machine = smartq5_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -172,7 +172,6 @@ MACHINE_START(SMARTQ7, "SmartQ 7")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = smartq_map_io,
|
||||
.init_machine = smartq7_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -92,7 +92,6 @@ MACHINE_START(SMDK6400, "SMDK6400")
|
|||
.init_irq = s3c6400_init_irq,
|
||||
.map_io = smdk6400_map_io,
|
||||
.init_machine = smdk6400_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -705,7 +705,6 @@ MACHINE_START(SMDK6410, "SMDK6410")
|
|||
.init_irq = s3c6410_init_irq,
|
||||
.map_io = smdk6410_map_io,
|
||||
.init_machine = smdk6410_machine_init,
|
||||
.init_late = s3c64xx_init_late,
|
||||
.init_time = samsung_timer_init,
|
||||
.restart = s3c64xx_restart,
|
||||
MACHINE_END
|
||||
|
|
|
@ -347,10 +347,3 @@ static __init int s3c64xx_pm_initcall(void)
|
|||
return 0;
|
||||
}
|
||||
arch_initcall(s3c64xx_pm_initcall);
|
||||
|
||||
int __init s3c64xx_pm_late_initcall(void)
|
||||
{
|
||||
pm_genpd_poweroff_unused();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,6 @@
|
|||
|
||||
int __init shmobile_cpufreq_init(void)
|
||||
{
|
||||
platform_device_register_simple("cpufreq-cpu0", -1, NULL, 0);
|
||||
platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,6 @@ static void r8a7779_init_pm_domain(struct r8a7779_pm_domain *r8a7779_pd)
|
|||
genpd->dev_ops.stop = pm_clk_suspend;
|
||||
genpd->dev_ops.start = pm_clk_resume;
|
||||
genpd->dev_ops.active_wakeup = pd_active_wakeup;
|
||||
genpd->dev_irq_safe = true;
|
||||
genpd->power_off = pd_power_down;
|
||||
genpd->power_on = pd_power_up;
|
||||
|
||||
|
|
|
@ -110,7 +110,6 @@ static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
|
|||
genpd->dev_ops.stop = pm_clk_suspend;
|
||||
genpd->dev_ops.start = pm_clk_resume;
|
||||
genpd->dev_ops.active_wakeup = rmobile_pd_active_wakeup;
|
||||
genpd->dev_irq_safe = true;
|
||||
genpd->power_off = rmobile_pd_power_down;
|
||||
genpd->power_on = rmobile_pd_power_up;
|
||||
__rmobile_pd_power_up(rmobile_pd, false);
|
||||
|
|
|
@ -110,7 +110,7 @@ static void __init zynq_init_late(void)
|
|||
*/
|
||||
static void __init zynq_init_machine(void)
|
||||
{
|
||||
struct platform_device_info devinfo = { .name = "cpufreq-cpu0", };
|
||||
struct platform_device_info devinfo = { .name = "cpufreq-dt", };
|
||||
struct soc_device_attribute *soc_dev_attr;
|
||||
struct soc_device *soc_dev;
|
||||
struct device *parent = NULL;
|
||||
|
|
|
@ -2623,6 +2623,7 @@ static struct irq_chip ioapic_chip __read_mostly = {
|
|||
.irq_eoi = ack_apic_level,
|
||||
.irq_set_affinity = native_ioapic_set_affinity,
|
||||
.irq_retrigger = ioapic_retrigger_irq,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static inline void init_IO_APIC_traps(void)
|
||||
|
@ -3173,6 +3174,7 @@ static struct irq_chip msi_chip = {
|
|||
.irq_ack = ack_apic_edge,
|
||||
.irq_set_affinity = msi_set_affinity,
|
||||
.irq_retrigger = ioapic_retrigger_irq,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
|
||||
|
@ -3271,6 +3273,7 @@ static struct irq_chip dmar_msi_type = {
|
|||
.irq_ack = ack_apic_edge,
|
||||
.irq_set_affinity = dmar_msi_set_affinity,
|
||||
.irq_retrigger = ioapic_retrigger_irq,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
int arch_setup_dmar_msi(unsigned int irq)
|
||||
|
@ -3321,6 +3324,7 @@ static struct irq_chip hpet_msi_type = {
|
|||
.irq_ack = ack_apic_edge,
|
||||
.irq_set_affinity = hpet_msi_set_affinity,
|
||||
.irq_retrigger = ioapic_retrigger_irq,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
int default_setup_hpet_msi(unsigned int irq, unsigned int id)
|
||||
|
@ -3384,6 +3388,7 @@ static struct irq_chip ht_irq_chip = {
|
|||
.irq_ack = ack_apic_edge,
|
||||
.irq_set_affinity = ht_set_affinity,
|
||||
.irq_retrigger = ioapic_retrigger_irq,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
|
||||
|
|
|
@ -54,55 +54,58 @@ ACPI_MODULE_NAME("acpi_lpss");
|
|||
|
||||
#define LPSS_PRV_REG_COUNT 9
|
||||
|
||||
struct lpss_shared_clock {
|
||||
const char *name;
|
||||
unsigned long rate;
|
||||
struct clk *clk;
|
||||
};
|
||||
/* LPSS Flags */
|
||||
#define LPSS_CLK BIT(0)
|
||||
#define LPSS_CLK_GATE BIT(1)
|
||||
#define LPSS_CLK_DIVIDER BIT(2)
|
||||
#define LPSS_LTR BIT(3)
|
||||
#define LPSS_SAVE_CTX BIT(4)
|
||||
|
||||
struct lpss_private_data;
|
||||
|
||||
struct lpss_device_desc {
|
||||
bool clk_required;
|
||||
const char *clkdev_name;
|
||||
bool ltr_required;
|
||||
unsigned int flags;
|
||||
unsigned int prv_offset;
|
||||
size_t prv_size_override;
|
||||
bool clk_divider;
|
||||
bool clk_gate;
|
||||
bool save_ctx;
|
||||
struct lpss_shared_clock *shared_clock;
|
||||
void (*setup)(struct lpss_private_data *pdata);
|
||||
};
|
||||
|
||||
static struct lpss_device_desc lpss_dma_desc = {
|
||||
.clk_required = true,
|
||||
.clkdev_name = "hclk",
|
||||
.flags = LPSS_CLK,
|
||||
};
|
||||
|
||||
struct lpss_private_data {
|
||||
void __iomem *mmio_base;
|
||||
resource_size_t mmio_size;
|
||||
unsigned int fixed_clk_rate;
|
||||
struct clk *clk;
|
||||
const struct lpss_device_desc *dev_desc;
|
||||
u32 prv_reg_ctx[LPSS_PRV_REG_COUNT];
|
||||
};
|
||||
|
||||
/* UART Component Parameter Register */
|
||||
#define LPSS_UART_CPR 0xF4
|
||||
#define LPSS_UART_CPR_AFCE BIT(4)
|
||||
|
||||
static void lpss_uart_setup(struct lpss_private_data *pdata)
|
||||
{
|
||||
unsigned int offset;
|
||||
u32 reg;
|
||||
u32 val;
|
||||
|
||||
offset = pdata->dev_desc->prv_offset + LPSS_TX_INT;
|
||||
reg = readl(pdata->mmio_base + offset);
|
||||
writel(reg | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
|
||||
val = readl(pdata->mmio_base + offset);
|
||||
writel(val | LPSS_TX_INT_MASK, pdata->mmio_base + offset);
|
||||
|
||||
offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
|
||||
reg = readl(pdata->mmio_base + offset);
|
||||
writel(reg | LPSS_GENERAL_UART_RTS_OVRD, pdata->mmio_base + offset);
|
||||
val = readl(pdata->mmio_base + LPSS_UART_CPR);
|
||||
if (!(val & LPSS_UART_CPR_AFCE)) {
|
||||
offset = pdata->dev_desc->prv_offset + LPSS_GENERAL;
|
||||
val = readl(pdata->mmio_base + offset);
|
||||
val |= LPSS_GENERAL_UART_RTS_OVRD;
|
||||
writel(val, pdata->mmio_base + offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void lpss_i2c_setup(struct lpss_private_data *pdata)
|
||||
static void byt_i2c_setup(struct lpss_private_data *pdata)
|
||||
{
|
||||
unsigned int offset;
|
||||
u32 val;
|
||||
|
@ -111,100 +114,56 @@ static void lpss_i2c_setup(struct lpss_private_data *pdata)
|
|||
val = readl(pdata->mmio_base + offset);
|
||||
val |= LPSS_RESETS_RESET_APB | LPSS_RESETS_RESET_FUNC;
|
||||
writel(val, pdata->mmio_base + offset);
|
||||
|
||||
if (readl(pdata->mmio_base + pdata->dev_desc->prv_offset))
|
||||
pdata->fixed_clk_rate = 133000000;
|
||||
}
|
||||
|
||||
static struct lpss_device_desc wpt_dev_desc = {
|
||||
.clk_required = true,
|
||||
.prv_offset = 0x800,
|
||||
.ltr_required = true,
|
||||
.clk_divider = true,
|
||||
.clk_gate = true,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc lpt_dev_desc = {
|
||||
.clk_required = true,
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
|
||||
.prv_offset = 0x800,
|
||||
.ltr_required = true,
|
||||
.clk_divider = true,
|
||||
.clk_gate = true,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc lpt_i2c_dev_desc = {
|
||||
.clk_required = true,
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR,
|
||||
.prv_offset = 0x800,
|
||||
.ltr_required = true,
|
||||
.clk_gate = true,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc lpt_uart_dev_desc = {
|
||||
.clk_required = true,
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
|
||||
.prv_offset = 0x800,
|
||||
.ltr_required = true,
|
||||
.clk_divider = true,
|
||||
.clk_gate = true,
|
||||
.setup = lpss_uart_setup,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc lpt_sdio_dev_desc = {
|
||||
.flags = LPSS_LTR,
|
||||
.prv_offset = 0x1000,
|
||||
.prv_size_override = 0x1018,
|
||||
.ltr_required = true,
|
||||
};
|
||||
|
||||
static struct lpss_shared_clock pwm_clock = {
|
||||
.name = "pwm_clk",
|
||||
.rate = 25000000,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_pwm_dev_desc = {
|
||||
.clk_required = true,
|
||||
.save_ctx = true,
|
||||
.shared_clock = &pwm_clock,
|
||||
.flags = LPSS_SAVE_CTX,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_uart_dev_desc = {
|
||||
.clk_required = true,
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
|
||||
.prv_offset = 0x800,
|
||||
.clk_divider = true,
|
||||
.clk_gate = true,
|
||||
.save_ctx = true,
|
||||
.setup = lpss_uart_setup,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_spi_dev_desc = {
|
||||
.clk_required = true,
|
||||
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
|
||||
.prv_offset = 0x400,
|
||||
.clk_divider = true,
|
||||
.clk_gate = true,
|
||||
.save_ctx = true,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_sdio_dev_desc = {
|
||||
.clk_required = true,
|
||||
};
|
||||
|
||||
static struct lpss_shared_clock i2c_clock = {
|
||||
.name = "i2c_clk",
|
||||
.rate = 100000000,
|
||||
.flags = LPSS_CLK,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc byt_i2c_dev_desc = {
|
||||
.clk_required = true,
|
||||
.flags = LPSS_CLK | LPSS_SAVE_CTX,
|
||||
.prv_offset = 0x800,
|
||||
.save_ctx = true,
|
||||
.shared_clock = &i2c_clock,
|
||||
.setup = lpss_i2c_setup,
|
||||
};
|
||||
|
||||
static struct lpss_shared_clock bsw_pwm_clock = {
|
||||
.name = "pwm_clk",
|
||||
.rate = 19200000,
|
||||
};
|
||||
|
||||
static struct lpss_device_desc bsw_pwm_dev_desc = {
|
||||
.clk_required = true,
|
||||
.save_ctx = true,
|
||||
.shared_clock = &bsw_pwm_clock,
|
||||
.setup = byt_i2c_setup,
|
||||
};
|
||||
|
||||
#else
|
||||
|
@ -237,7 +196,7 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
|
|||
{ "INT33FC", },
|
||||
|
||||
/* Braswell LPSS devices */
|
||||
{ "80862288", LPSS_ADDR(bsw_pwm_dev_desc) },
|
||||
{ "80862288", LPSS_ADDR(byt_pwm_dev_desc) },
|
||||
{ "8086228A", LPSS_ADDR(byt_uart_dev_desc) },
|
||||
{ "8086228E", LPSS_ADDR(byt_spi_dev_desc) },
|
||||
{ "808622C1", LPSS_ADDR(byt_i2c_dev_desc) },
|
||||
|
@ -251,7 +210,8 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
|
|||
{ "INT3436", LPSS_ADDR(lpt_sdio_dev_desc) },
|
||||
{ "INT3437", },
|
||||
|
||||
{ "INT3438", LPSS_ADDR(wpt_dev_desc) },
|
||||
/* Wildcat Point LPSS devices */
|
||||
{ "INT3438", LPSS_ADDR(lpt_dev_desc) },
|
||||
|
||||
{ }
|
||||
};
|
||||
|
@ -276,7 +236,6 @@ static int register_device_clock(struct acpi_device *adev,
|
|||
struct lpss_private_data *pdata)
|
||||
{
|
||||
const struct lpss_device_desc *dev_desc = pdata->dev_desc;
|
||||
struct lpss_shared_clock *shared_clock = dev_desc->shared_clock;
|
||||
const char *devname = dev_name(&adev->dev);
|
||||
struct clk *clk = ERR_PTR(-ENODEV);
|
||||
struct lpss_clk_data *clk_data;
|
||||
|
@ -289,12 +248,7 @@ static int register_device_clock(struct acpi_device *adev,
|
|||
clk_data = platform_get_drvdata(lpss_clk_dev);
|
||||
if (!clk_data)
|
||||
return -ENODEV;
|
||||
|
||||
if (dev_desc->clkdev_name) {
|
||||
clk_register_clkdev(clk_data->clk, dev_desc->clkdev_name,
|
||||
devname);
|
||||
return 0;
|
||||
}
|
||||
clk = clk_data->clk;
|
||||
|
||||
if (!pdata->mmio_base
|
||||
|| pdata->mmio_size < dev_desc->prv_offset + LPSS_CLK_SIZE)
|
||||
|
@ -303,24 +257,19 @@ static int register_device_clock(struct acpi_device *adev,
|
|||
parent = clk_data->name;
|
||||
prv_base = pdata->mmio_base + dev_desc->prv_offset;
|
||||
|
||||
if (shared_clock) {
|
||||
clk = shared_clock->clk;
|
||||
if (!clk) {
|
||||
clk = clk_register_fixed_rate(NULL, shared_clock->name,
|
||||
"lpss_clk", 0,
|
||||
shared_clock->rate);
|
||||
shared_clock->clk = clk;
|
||||
}
|
||||
parent = shared_clock->name;
|
||||
if (pdata->fixed_clk_rate) {
|
||||
clk = clk_register_fixed_rate(NULL, devname, parent, 0,
|
||||
pdata->fixed_clk_rate);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev_desc->clk_gate) {
|
||||
if (dev_desc->flags & LPSS_CLK_GATE) {
|
||||
clk = clk_register_gate(NULL, devname, parent, 0,
|
||||
prv_base, 0, 0, NULL);
|
||||
parent = devname;
|
||||
}
|
||||
|
||||
if (dev_desc->clk_divider) {
|
||||
if (dev_desc->flags & LPSS_CLK_DIVIDER) {
|
||||
/* Prevent division by zero */
|
||||
if (!readl(prv_base))
|
||||
writel(LPSS_CLK_DIVIDER_DEF_MASK, prv_base);
|
||||
|
@ -344,7 +293,7 @@ static int register_device_clock(struct acpi_device *adev,
|
|||
kfree(parent);
|
||||
kfree(clk_name);
|
||||
}
|
||||
|
||||
out:
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
|
@ -392,7 +341,10 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
|
|||
|
||||
pdata->dev_desc = dev_desc;
|
||||
|
||||
if (dev_desc->clk_required) {
|
||||
if (dev_desc->setup)
|
||||
dev_desc->setup(pdata);
|
||||
|
||||
if (dev_desc->flags & LPSS_CLK) {
|
||||
ret = register_device_clock(adev, pdata);
|
||||
if (ret) {
|
||||
/* Skip the device, but continue the namespace scan. */
|
||||
|
@ -413,9 +365,6 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
|
|||
goto err_out;
|
||||
}
|
||||
|
||||
if (dev_desc->setup)
|
||||
dev_desc->setup(pdata);
|
||||
|
||||
adev->driver_data = pdata;
|
||||
pdev = acpi_create_platform_device(adev);
|
||||
if (!IS_ERR_OR_NULL(pdev)) {
|
||||
|
@ -692,19 +641,19 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb,
|
|||
|
||||
switch (action) {
|
||||
case BUS_NOTIFY_BOUND_DRIVER:
|
||||
if (pdata->dev_desc->save_ctx)
|
||||
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
|
||||
pdev->dev.pm_domain = &acpi_lpss_pm_domain;
|
||||
break;
|
||||
case BUS_NOTIFY_UNBOUND_DRIVER:
|
||||
if (pdata->dev_desc->save_ctx)
|
||||
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
|
||||
pdev->dev.pm_domain = NULL;
|
||||
break;
|
||||
case BUS_NOTIFY_ADD_DEVICE:
|
||||
if (pdata->dev_desc->ltr_required)
|
||||
if (pdata->dev_desc->flags & LPSS_LTR)
|
||||
return sysfs_create_group(&pdev->dev.kobj,
|
||||
&lpss_attr_group);
|
||||
case BUS_NOTIFY_DEL_DEVICE:
|
||||
if (pdata->dev_desc->ltr_required)
|
||||
if (pdata->dev_desc->flags & LPSS_LTR)
|
||||
sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group);
|
||||
default:
|
||||
break;
|
||||
|
@ -721,7 +670,7 @@ static void acpi_lpss_bind(struct device *dev)
|
|||
{
|
||||
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
|
||||
|
||||
if (!pdata || !pdata->mmio_base || !pdata->dev_desc->ltr_required)
|
||||
if (!pdata || !pdata->mmio_base || !(pdata->dev_desc->flags & LPSS_LTR))
|
||||
return;
|
||||
|
||||
if (pdata->mmio_size >= pdata->dev_desc->prv_offset + LPSS_LTR_SIZE)
|
||||
|
|
|
@ -130,10 +130,6 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = {
|
|||
{"PNP0401"}, /* ECP Printer Port */
|
||||
/* apple-gmux */
|
||||
{"APP000B"},
|
||||
/* fujitsu-laptop.c */
|
||||
{"FUJ02bf"},
|
||||
{"FUJ02B1"},
|
||||
{"FUJ02E3"},
|
||||
/* system */
|
||||
{"PNP0c02"}, /* General ID for reserving resources */
|
||||
{"PNP0c01"}, /* memory controller */
|
||||
|
|
|
@ -596,6 +596,38 @@ acpi_status acpi_enable_all_runtime_gpes(void)
|
|||
|
||||
ACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes)
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* FUNCTION: acpi_enable_all_wakeup_gpes
|
||||
*
|
||||
* PARAMETERS: None
|
||||
*
|
||||
* RETURN: Status
|
||||
*
|
||||
* DESCRIPTION: Enable all "wakeup" GPEs and disable all of the other GPEs, in
|
||||
* all GPE blocks.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
acpi_status acpi_enable_all_wakeup_gpes(void)
|
||||
{
|
||||
acpi_status status;
|
||||
|
||||
ACPI_FUNCTION_TRACE(acpi_enable_all_wakeup_gpes);
|
||||
|
||||
status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
status = acpi_hw_enable_all_wakeup_gpes();
|
||||
(void)acpi_ut_release_mutex(ACPI_MTX_EVENTS);
|
||||
|
||||
return_ACPI_STATUS(status);
|
||||
}
|
||||
|
||||
ACPI_EXPORT_SYMBOL(acpi_enable_all_wakeup_gpes)
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* FUNCTION: acpi_install_gpe_block
|
||||
|
|
|
@ -396,11 +396,11 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
|
|||
/* Examine each GPE Register within the block */
|
||||
|
||||
for (i = 0; i < gpe_block->register_count; i++) {
|
||||
if (!gpe_block->register_info[i].enable_for_wake) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Enable all "wake" GPEs in this register */
|
||||
/*
|
||||
* Enable all "wake" GPEs in this register and disable the
|
||||
* remaining ones.
|
||||
*/
|
||||
|
||||
status =
|
||||
acpi_hw_write(gpe_block->register_info[i].enable_for_wake,
|
||||
|
|
|
@ -87,7 +87,9 @@ const char *acpi_gbl_io_decode[] = {
|
|||
|
||||
const char *acpi_gbl_ll_decode[] = {
|
||||
"ActiveHigh",
|
||||
"ActiveLow"
|
||||
"ActiveLow",
|
||||
"ActiveBoth",
|
||||
"Reserved"
|
||||
};
|
||||
|
||||
const char *acpi_gbl_max_decode[] = {
|
||||
|
|
|
@ -695,7 +695,7 @@ static void acpi_battery_quirks(struct acpi_battery *battery)
|
|||
if (battery->power_unit && dmi_name_in_vendors("LENOVO")) {
|
||||
const char *s;
|
||||
s = dmi_get_system_info(DMI_PRODUCT_VERSION);
|
||||
if (s && !strnicmp(s, "ThinkPad", 8)) {
|
||||
if (s && !strncasecmp(s, "ThinkPad", 8)) {
|
||||
dmi_walk(find_battery, battery);
|
||||
if (test_bit(ACPI_BATTERY_QUIRK_THINKPAD_MAH,
|
||||
&battery->flags) &&
|
||||
|
|
|
@ -247,8 +247,8 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
|
|||
},
|
||||
|
||||
/*
|
||||
* These machines will power on immediately after shutdown when
|
||||
* reporting the Windows 2012 OSI.
|
||||
* The wireless hotkey does not work on those machines when
|
||||
* returning true for _OSI("Windows 2012")
|
||||
*/
|
||||
{
|
||||
.callback = dmi_disable_osi_win8,
|
||||
|
@ -258,6 +258,38 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = dmi_disable_osi_win8,
|
||||
.ident = "Dell Inspiron 7537",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = dmi_disable_osi_win8,
|
||||
.ident = "Dell Inspiron 5437",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = dmi_disable_osi_win8,
|
||||
.ident = "Dell Inspiron 3437",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = dmi_disable_osi_win8,
|
||||
.ident = "Dell Vostro 3446",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
|
||||
},
|
||||
},
|
||||
|
||||
/*
|
||||
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
|
||||
|
|
|
@ -1040,6 +1040,40 @@ static struct dev_pm_domain acpi_general_pm_domain = {
|
|||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* acpi_dev_pm_detach - Remove ACPI power management from the device.
|
||||
* @dev: Device to take care of.
|
||||
* @power_off: Whether or not to try to remove power from the device.
|
||||
*
|
||||
* Remove the device from the general ACPI PM domain and remove its wakeup
|
||||
* notifier. If @power_off is set, additionally remove power from the device if
|
||||
* possible.
|
||||
*
|
||||
* Callers must ensure proper synchronization of this function with power
|
||||
* management callbacks.
|
||||
*/
|
||||
static void acpi_dev_pm_detach(struct device *dev, bool power_off)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
|
||||
if (adev && dev->pm_domain == &acpi_general_pm_domain) {
|
||||
dev->pm_domain = NULL;
|
||||
acpi_remove_pm_notifier(adev);
|
||||
if (power_off) {
|
||||
/*
|
||||
* If the device's PM QoS resume latency limit or flags
|
||||
* have been exposed to user space, they have to be
|
||||
* hidden at this point, so that they don't affect the
|
||||
* choice of the low-power state to put the device into.
|
||||
*/
|
||||
dev_pm_qos_hide_latency_limit(dev);
|
||||
dev_pm_qos_hide_flags(dev);
|
||||
acpi_device_wakeup(adev, ACPI_STATE_S0, false);
|
||||
acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_dev_pm_attach - Prepare device for ACPI power management.
|
||||
* @dev: Device to prepare.
|
||||
|
@ -1072,42 +1106,9 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
|
|||
acpi_dev_pm_full_power(adev);
|
||||
acpi_device_wakeup(adev, ACPI_STATE_S0, false);
|
||||
}
|
||||
|
||||
dev->pm_domain->detach = acpi_dev_pm_detach;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);
|
||||
|
||||
/**
|
||||
* acpi_dev_pm_detach - Remove ACPI power management from the device.
|
||||
* @dev: Device to take care of.
|
||||
* @power_off: Whether or not to try to remove power from the device.
|
||||
*
|
||||
* Remove the device from the general ACPI PM domain and remove its wakeup
|
||||
* notifier. If @power_off is set, additionally remove power from the device if
|
||||
* possible.
|
||||
*
|
||||
* Callers must ensure proper synchronization of this function with power
|
||||
* management callbacks.
|
||||
*/
|
||||
void acpi_dev_pm_detach(struct device *dev, bool power_off)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
|
||||
if (adev && dev->pm_domain == &acpi_general_pm_domain) {
|
||||
dev->pm_domain = NULL;
|
||||
acpi_remove_pm_notifier(adev);
|
||||
if (power_off) {
|
||||
/*
|
||||
* If the device's PM QoS resume latency limit or flags
|
||||
* have been exposed to user space, they have to be
|
||||
* hidden at this point, so that they don't affect the
|
||||
* choice of the low-power state to put the device into.
|
||||
*/
|
||||
dev_pm_qos_hide_latency_limit(dev);
|
||||
dev_pm_qos_hide_flags(dev);
|
||||
acpi_device_wakeup(adev, ACPI_STATE_S0, false);
|
||||
acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_pm_detach);
|
||||
#endif /* CONFIG_PM */
|
||||
|
|
|
@ -27,12 +27,10 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define PREFIX "ACPI: "
|
||||
|
||||
#define ACPI_FAN_CLASS "fan"
|
||||
#define ACPI_FAN_FILE_STATE "state"
|
||||
|
||||
|
@ -127,8 +125,9 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = {
|
|||
};
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Driver Interface
|
||||
-------------------------------------------------------------------------- */
|
||||
* Driver Interface
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int acpi_fan_add(struct acpi_device *device)
|
||||
{
|
||||
|
@ -143,7 +142,7 @@ static int acpi_fan_add(struct acpi_device *device)
|
|||
|
||||
result = acpi_bus_update_power(device->handle, NULL);
|
||||
if (result) {
|
||||
printk(KERN_ERR PREFIX "Setting initial power state\n");
|
||||
dev_err(&device->dev, "Setting initial power state\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -168,10 +167,9 @@ static int acpi_fan_add(struct acpi_device *device)
|
|||
&device->dev.kobj,
|
||||
"device");
|
||||
if (result)
|
||||
dev_err(&device->dev, "Failed to create sysfs link "
|
||||
"'device'\n");
|
||||
dev_err(&device->dev, "Failed to create sysfs link 'device'\n");
|
||||
|
||||
printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
|
||||
dev_info(&device->dev, "ACPI: %s [%s] (%s)\n",
|
||||
acpi_device_name(device), acpi_device_bid(device),
|
||||
!device->power.state ? "on" : "off");
|
||||
|
||||
|
@ -217,7 +215,7 @@ static int acpi_fan_resume(struct device *dev)
|
|||
|
||||
result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL);
|
||||
if (result)
|
||||
printk(KERN_ERR PREFIX "Error updating fan power state\n");
|
||||
dev_err(dev, "Error updating fan power state\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -152,6 +152,16 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported)
|
|||
osi_linux.dmi ? " via DMI" : "");
|
||||
}
|
||||
|
||||
if (!strcmp("Darwin", interface)) {
|
||||
/*
|
||||
* Apple firmware will behave poorly if it receives positive
|
||||
* answers to "Darwin" and any other OS. Respond positively
|
||||
* to Darwin and then disable all other vendor strings.
|
||||
*/
|
||||
acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS);
|
||||
supported = ACPI_UINT32_MAX;
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
|
@ -825,7 +835,7 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
|
|||
|
||||
acpi_irq_handler = handler;
|
||||
acpi_irq_context = context;
|
||||
if (request_irq(irq, acpi_irq, IRQF_SHARED | IRQF_NO_SUSPEND, "acpi", acpi_irq)) {
|
||||
if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
|
||||
printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq);
|
||||
acpi_irq_handler = NULL;
|
||||
return AE_NOT_ACQUIRED;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/pci-aspm.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <acpi/apei.h> /* for acpi_hest_init() */
|
||||
|
||||
#include "internal.h"
|
||||
|
@ -429,6 +430,19 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm,
|
|||
struct acpi_device *device = root->device;
|
||||
acpi_handle handle = device->handle;
|
||||
|
||||
/*
|
||||
* Apple always return failure on _OSC calls when _OSI("Darwin") has
|
||||
* been called successfully. We know the feature set supported by the
|
||||
* platform, so avoid calling _OSC at all
|
||||
*/
|
||||
|
||||
if (dmi_match(DMI_SYS_VENDOR, "Apple Inc.")) {
|
||||
root->osc_control_set = ~OSC_PCI_EXPRESS_PME_CONTROL;
|
||||
decode_osc_control(root, "OS assumes control of",
|
||||
root->osc_control_set);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* All supported architectures that use ACPI have support for
|
||||
* PCI domains, so we indicate this in _OSC support capabilities.
|
||||
|
|
|
@ -16,7 +16,7 @@ static int map_lapic_id(struct acpi_subtable_header *entry,
|
|||
u32 acpi_id, int *apic_id)
|
||||
{
|
||||
struct acpi_madt_local_apic *lapic =
|
||||
(struct acpi_madt_local_apic *)entry;
|
||||
container_of(entry, struct acpi_madt_local_apic, header);
|
||||
|
||||
if (!(lapic->lapic_flags & ACPI_MADT_ENABLED))
|
||||
return -ENODEV;
|
||||
|
@ -32,7 +32,7 @@ static int map_x2apic_id(struct acpi_subtable_header *entry,
|
|||
int device_declaration, u32 acpi_id, int *apic_id)
|
||||
{
|
||||
struct acpi_madt_local_x2apic *apic =
|
||||
(struct acpi_madt_local_x2apic *)entry;
|
||||
container_of(entry, struct acpi_madt_local_x2apic, header);
|
||||
|
||||
if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
|
||||
return -ENODEV;
|
||||
|
@ -49,7 +49,7 @@ static int map_lsapic_id(struct acpi_subtable_header *entry,
|
|||
int device_declaration, u32 acpi_id, int *apic_id)
|
||||
{
|
||||
struct acpi_madt_local_sapic *lsapic =
|
||||
(struct acpi_madt_local_sapic *)entry;
|
||||
container_of(entry, struct acpi_madt_local_sapic, header);
|
||||
|
||||
if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))
|
||||
return -ENODEV;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/jiffies.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
#include "sbshc.h"
|
||||
#include "battery.h"
|
||||
|
@ -61,6 +62,8 @@ static unsigned int cache_time = 1000;
|
|||
module_param(cache_time, uint, 0644);
|
||||
MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
|
||||
|
||||
static bool sbs_manager_broken;
|
||||
|
||||
#define MAX_SBS_BAT 4
|
||||
#define ACPI_SBS_BLOCK_MAX 32
|
||||
|
||||
|
@ -109,6 +112,7 @@ struct acpi_sbs {
|
|||
u8 batteries_supported:4;
|
||||
u8 manager_present:1;
|
||||
u8 charger_present:1;
|
||||
u8 charger_exists:1;
|
||||
};
|
||||
|
||||
#define to_acpi_sbs(x) container_of(x, struct acpi_sbs, charger)
|
||||
|
@ -429,9 +433,19 @@ static int acpi_ac_get_present(struct acpi_sbs *sbs)
|
|||
|
||||
result = acpi_smbus_read(sbs->hc, SMBUS_READ_WORD, ACPI_SBS_CHARGER,
|
||||
0x13, (u8 *) & status);
|
||||
if (!result)
|
||||
sbs->charger_present = (status >> 15) & 0x1;
|
||||
return result;
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/*
|
||||
* The spec requires that bit 4 always be 1. If it's not set, assume
|
||||
* that the implementation doesn't support an SBS charger
|
||||
*/
|
||||
if (!((status >> 4) & 0x1))
|
||||
return -ENODEV;
|
||||
|
||||
sbs->charger_present = (status >> 15) & 0x1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t acpi_battery_alarm_show(struct device *dev,
|
||||
|
@ -483,16 +497,21 @@ static int acpi_battery_read(struct acpi_battery *battery)
|
|||
ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2);
|
||||
} else if (battery->id == 0)
|
||||
battery->present = 1;
|
||||
|
||||
if (result || !battery->present)
|
||||
return result;
|
||||
|
||||
if (saved_present != battery->present) {
|
||||
battery->update_time = 0;
|
||||
result = acpi_battery_get_info(battery);
|
||||
if (result)
|
||||
if (result) {
|
||||
battery->present = 0;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = acpi_battery_get_state(battery);
|
||||
if (result)
|
||||
battery->present = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -524,6 +543,7 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id)
|
|||
result = power_supply_register(&sbs->device->dev, &battery->bat);
|
||||
if (result)
|
||||
goto end;
|
||||
|
||||
result = device_create_file(battery->bat.dev, &alarm_attr);
|
||||
if (result)
|
||||
goto end;
|
||||
|
@ -554,6 +574,7 @@ static int acpi_charger_add(struct acpi_sbs *sbs)
|
|||
if (result)
|
||||
goto end;
|
||||
|
||||
sbs->charger_exists = 1;
|
||||
sbs->charger.name = "sbs-charger";
|
||||
sbs->charger.type = POWER_SUPPLY_TYPE_MAINS;
|
||||
sbs->charger.properties = sbs_ac_props;
|
||||
|
@ -580,9 +601,12 @@ static void acpi_sbs_callback(void *context)
|
|||
struct acpi_battery *bat;
|
||||
u8 saved_charger_state = sbs->charger_present;
|
||||
u8 saved_battery_state;
|
||||
acpi_ac_get_present(sbs);
|
||||
if (sbs->charger_present != saved_charger_state)
|
||||
kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE);
|
||||
|
||||
if (sbs->charger_exists) {
|
||||
acpi_ac_get_present(sbs);
|
||||
if (sbs->charger_present != saved_charger_state)
|
||||
kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE);
|
||||
}
|
||||
|
||||
if (sbs->manager_present) {
|
||||
for (id = 0; id < MAX_SBS_BAT; ++id) {
|
||||
|
@ -598,12 +622,31 @@ static void acpi_sbs_callback(void *context)
|
|||
}
|
||||
}
|
||||
|
||||
static int disable_sbs_manager(const struct dmi_system_id *d)
|
||||
{
|
||||
sbs_manager_broken = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dmi_system_id acpi_sbs_dmi_table[] = {
|
||||
{
|
||||
.callback = disable_sbs_manager,
|
||||
.ident = "Apple",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc.")
|
||||
},
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static int acpi_sbs_add(struct acpi_device *device)
|
||||
{
|
||||
struct acpi_sbs *sbs;
|
||||
int result = 0;
|
||||
int id;
|
||||
|
||||
dmi_check_system(acpi_sbs_dmi_table);
|
||||
|
||||
sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL);
|
||||
if (!sbs) {
|
||||
result = -ENOMEM;
|
||||
|
@ -619,17 +662,24 @@ static int acpi_sbs_add(struct acpi_device *device)
|
|||
device->driver_data = sbs;
|
||||
|
||||
result = acpi_charger_add(sbs);
|
||||
if (result)
|
||||
if (result && result != -ENODEV)
|
||||
goto end;
|
||||
|
||||
result = acpi_manager_get_info(sbs);
|
||||
if (!result) {
|
||||
sbs->manager_present = 1;
|
||||
for (id = 0; id < MAX_SBS_BAT; ++id)
|
||||
if ((sbs->batteries_supported & (1 << id)))
|
||||
acpi_battery_add(sbs, id);
|
||||
} else
|
||||
result = 0;
|
||||
|
||||
if (!sbs_manager_broken) {
|
||||
result = acpi_manager_get_info(sbs);
|
||||
if (!result) {
|
||||
sbs->manager_present = 0;
|
||||
for (id = 0; id < MAX_SBS_BAT; ++id)
|
||||
if ((sbs->batteries_supported & (1 << id)))
|
||||
acpi_battery_add(sbs, id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!sbs->manager_present)
|
||||
acpi_battery_add(sbs, 0);
|
||||
|
||||
acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs);
|
||||
end:
|
||||
if (result)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/irq.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/acpi.h>
|
||||
|
@ -626,6 +627,19 @@ static int acpi_freeze_begin(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int acpi_freeze_prepare(void)
|
||||
{
|
||||
acpi_enable_all_wakeup_gpes();
|
||||
enable_irq_wake(acpi_gbl_FADT.sci_interrupt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_freeze_restore(void)
|
||||
{
|
||||
disable_irq_wake(acpi_gbl_FADT.sci_interrupt);
|
||||
acpi_enable_all_runtime_gpes();
|
||||
}
|
||||
|
||||
static void acpi_freeze_end(void)
|
||||
{
|
||||
acpi_scan_lock_release();
|
||||
|
@ -633,6 +647,8 @@ static void acpi_freeze_end(void)
|
|||
|
||||
static const struct platform_freeze_ops acpi_freeze_ops = {
|
||||
.begin = acpi_freeze_begin,
|
||||
.prepare = acpi_freeze_prepare,
|
||||
.restore = acpi_freeze_restore,
|
||||
.end = acpi_freeze_end,
|
||||
};
|
||||
|
||||
|
|
|
@ -661,7 +661,6 @@ EXPORT_SYMBOL(acpi_evaluate_dsm);
|
|||
* @uuid: UUID of requested functions, should be 16 bytes at least
|
||||
* @rev: revision number of requested functions
|
||||
* @funcs: bitmap of requested functions
|
||||
* @exclude: excluding special value, used to support i915 and nouveau
|
||||
*
|
||||
* Evaluate device's _DSM method to check whether it supports requested
|
||||
* functions. Currently only support 64 functions at maximum, should be
|
||||
|
|
|
@ -411,12 +411,6 @@ static int __init video_set_bqc_offset(const struct dmi_system_id *d)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init video_set_use_native_backlight(const struct dmi_system_id *d)
|
||||
{
|
||||
use_native_backlight_dmi = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init video_disable_native_backlight(const struct dmi_system_id *d)
|
||||
{
|
||||
use_native_backlight_dmi = false;
|
||||
|
@ -467,265 +461,6 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad X230",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X230"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad T430 and T430s",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T430"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad T430",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "2349D15"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad T431s",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "20AACTO1WW"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad Edge E530",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "3259A2G"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad Edge E530",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "3259CTO"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad Edge E530",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "3259HJG"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad W530",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W530"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ThinkPad X1 Carbon",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X1 Carbon"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Lenovo Yoga 13",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Yoga 13"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Lenovo Yoga 2 11",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Thinkpad Helix",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Dell Inspiron 7520",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Acer Aspire 5733Z",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5733Z"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Acer Aspire 5742G",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5742G"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Acer Aspire V5-171",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "V5-171"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Acer Aspire V5-431",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-431"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Acer Aspire V5-471G",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-471G"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Acer TravelMate B113",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate B113"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Acer Aspire V5-572G",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer Aspire"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "V5-572G/Dazzle_CX"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "Acer Aspire V5-573G",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer Aspire"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "V5-573G/Dazzle_HW"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "ASUS Zenbook Prime UX31A",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "UX31A"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP ProBook 4340s",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "HP ProBook 4340s"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP ProBook 4540s",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "HP ProBook 4540s"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP ProBook 2013 models",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook "),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, " G1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP EliteBook 2013 models",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook "),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, " G1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP EliteBook 2014 models",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook "),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, " G2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP ZBook 14",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 14"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP ZBook 15",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 15"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP ZBook 17",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 17"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP EliteBook 8470p",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8470p"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = video_set_use_native_backlight,
|
||||
.ident = "HP EliteBook 8780w",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8780w"),
|
||||
},
|
||||
},
|
||||
|
||||
/*
|
||||
* These models have a working acpi_video backlight control, and using
|
||||
|
@ -1419,6 +1154,23 @@ acpi_video_device_bind(struct acpi_video_bus *video,
|
|||
}
|
||||
}
|
||||
|
||||
static bool acpi_video_device_in_dod(struct acpi_video_device *device)
|
||||
{
|
||||
struct acpi_video_bus *video = device->video;
|
||||
int i;
|
||||
|
||||
/* If we have a broken _DOD, no need to test */
|
||||
if (!video->attached_count)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < video->attached_count; i++) {
|
||||
if (video->attached_array[i].bind_info == device)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arg:
|
||||
* video : video bus device
|
||||
|
@ -1858,6 +1610,15 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
|
|||
static int count;
|
||||
char *name;
|
||||
|
||||
/*
|
||||
* Do not create backlight device for video output
|
||||
* device that is not in the enumerated list.
|
||||
*/
|
||||
if (!acpi_video_device_in_dod(device)) {
|
||||
dev_dbg(&device->dev->dev, "not in _DOD list, ignore\n");
|
||||
return;
|
||||
}
|
||||
|
||||
result = acpi_video_init_brightness(device);
|
||||
if (result)
|
||||
return;
|
||||
|
|
|
@ -174,6 +174,14 @@ static struct dmi_system_id video_detect_dmi_table[] = {
|
|||
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"),
|
||||
},
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
|
@ -182,10 +183,16 @@ static int amba_probe(struct device *dev)
|
|||
int ret;
|
||||
|
||||
do {
|
||||
ret = amba_get_enable_pclk(pcdev);
|
||||
if (ret)
|
||||
ret = dev_pm_domain_attach(dev, true);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
break;
|
||||
|
||||
ret = amba_get_enable_pclk(pcdev);
|
||||
if (ret) {
|
||||
dev_pm_domain_detach(dev, true);
|
||||
break;
|
||||
}
|
||||
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
@ -199,6 +206,7 @@ static int amba_probe(struct device *dev)
|
|||
pm_runtime_put_noidle(dev);
|
||||
|
||||
amba_put_disable_pclk(pcdev);
|
||||
dev_pm_domain_detach(dev, true);
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
|
@ -220,6 +228,7 @@ static int amba_remove(struct device *dev)
|
|||
pm_runtime_put_noidle(dev);
|
||||
|
||||
amba_put_disable_pclk(pcdev);
|
||||
dev_pm_domain_detach(dev, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk/clk-conf.h>
|
||||
|
@ -506,11 +507,12 @@ static int platform_drv_probe(struct device *_dev)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
acpi_dev_pm_attach(_dev, true);
|
||||
|
||||
ret = drv->probe(dev);
|
||||
if (ret)
|
||||
acpi_dev_pm_detach(_dev, true);
|
||||
ret = dev_pm_domain_attach(_dev, true);
|
||||
if (ret != -EPROBE_DEFER) {
|
||||
ret = drv->probe(dev);
|
||||
if (ret)
|
||||
dev_pm_domain_detach(_dev, true);
|
||||
}
|
||||
|
||||
if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
|
||||
dev_warn(_dev, "probe deferral not supported\n");
|
||||
|
@ -532,7 +534,7 @@ static int platform_drv_remove(struct device *_dev)
|
|||
int ret;
|
||||
|
||||
ret = drv->remove(dev);
|
||||
acpi_dev_pm_detach(_dev, true);
|
||||
dev_pm_domain_detach(_dev, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -543,7 +545,7 @@ static void platform_drv_shutdown(struct device *_dev)
|
|||
struct platform_device *dev = to_platform_device(_dev);
|
||||
|
||||
drv->shutdown(dev);
|
||||
acpi_dev_pm_detach(_dev, true);
|
||||
dev_pm_domain_detach(_dev, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -368,8 +368,13 @@ int pm_clk_suspend(struct device *dev)
|
|||
|
||||
spin_lock_irqsave(&psd->lock, flags);
|
||||
|
||||
list_for_each_entry_reverse(ce, &psd->clock_list, node)
|
||||
clk_disable(ce->clk);
|
||||
list_for_each_entry_reverse(ce, &psd->clock_list, node) {
|
||||
if (ce->status < PCE_STATUS_ERROR) {
|
||||
if (ce->status == PCE_STATUS_ENABLED)
|
||||
clk_disable(ce->clk);
|
||||
ce->status = PCE_STATUS_ACQUIRED;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&psd->lock, flags);
|
||||
|
||||
|
@ -385,6 +390,7 @@ int pm_clk_resume(struct device *dev)
|
|||
struct pm_subsys_data *psd = dev_to_psd(dev);
|
||||
struct pm_clock_entry *ce;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
|
@ -394,8 +400,13 @@ int pm_clk_resume(struct device *dev)
|
|||
|
||||
spin_lock_irqsave(&psd->lock, flags);
|
||||
|
||||
list_for_each_entry(ce, &psd->clock_list, node)
|
||||
__pm_clk_enable(dev, ce->clk);
|
||||
list_for_each_entry(ce, &psd->clock_list, node) {
|
||||
if (ce->status < PCE_STATUS_ERROR) {
|
||||
ret = __pm_clk_enable(dev, ce->clk);
|
||||
if (!ret)
|
||||
ce->status = PCE_STATUS_ENABLED;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&psd->lock, flags);
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_clock.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pm_domain.h>
|
||||
|
||||
/**
|
||||
* dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
|
||||
|
@ -82,3 +84,53 @@ int dev_pm_put_subsys_data(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
|
||||
|
||||
/**
|
||||
* dev_pm_domain_attach - Attach a device to its PM domain.
|
||||
* @dev: Device to attach.
|
||||
* @power_on: Used to indicate whether we should power on the device.
|
||||
*
|
||||
* The @dev may only be attached to a single PM domain. By iterating through
|
||||
* the available alternatives we try to find a valid PM domain for the device.
|
||||
* As attachment succeeds, the ->detach() callback in the struct dev_pm_domain
|
||||
* should be assigned by the corresponding attach function.
|
||||
*
|
||||
* This function should typically be invoked from subsystem level code during
|
||||
* the probe phase. Especially for those that holds devices which requires
|
||||
* power management through PM domains.
|
||||
*
|
||||
* Callers must ensure proper synchronization of this function with power
|
||||
* management callbacks.
|
||||
*
|
||||
* Returns 0 on successfully attached PM domain or negative error code.
|
||||
*/
|
||||
int dev_pm_domain_attach(struct device *dev, bool power_on)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = acpi_dev_pm_attach(dev, power_on);
|
||||
if (ret)
|
||||
ret = genpd_dev_pm_attach(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_domain_attach);
|
||||
|
||||
/**
|
||||
* dev_pm_domain_detach - Detach a device from its PM domain.
|
||||
* @dev: Device to attach.
|
||||
* @power_off: Used to indicate whether we should power off the device.
|
||||
*
|
||||
* This functions will reverse the actions from dev_pm_domain_attach() and thus
|
||||
* try to detach the @dev from its PM domain. Typically it should be invoked
|
||||
* from subsystem level code during the remove phase.
|
||||
*
|
||||
* Callers must ensure proper synchronization of this function with power
|
||||
* management callbacks.
|
||||
*/
|
||||
void dev_pm_domain_detach(struct device *dev, bool power_off)
|
||||
{
|
||||
if (dev->pm_domain && dev->pm_domain->detach)
|
||||
dev->pm_domain->detach(dev, power_off);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_domain_detach);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -42,7 +42,7 @@ static int dev_update_qos_constraint(struct device *dev, void *data)
|
|||
* default_stop_ok - Default PM domain governor routine for stopping devices.
|
||||
* @dev: Device to check.
|
||||
*/
|
||||
bool default_stop_ok(struct device *dev)
|
||||
static bool default_stop_ok(struct device *dev)
|
||||
{
|
||||
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
|
||||
unsigned long flags;
|
||||
|
@ -229,10 +229,7 @@ static bool always_on_power_down_ok(struct dev_pm_domain *domain)
|
|||
|
||||
#else /* !CONFIG_PM_RUNTIME */
|
||||
|
||||
bool default_stop_ok(struct device *dev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline bool default_stop_ok(struct device *dev) { return false; }
|
||||
|
||||
#define default_power_down_ok NULL
|
||||
#define always_on_power_down_ok NULL
|
||||
|
|
|
@ -540,7 +540,7 @@ static void async_resume_noirq(void *data, async_cookie_t cookie)
|
|||
* Call the "noirq" resume handlers for all devices in dpm_noirq_list and
|
||||
* enable device drivers to receive interrupts.
|
||||
*/
|
||||
static void dpm_resume_noirq(pm_message_t state)
|
||||
void dpm_resume_noirq(pm_message_t state)
|
||||
{
|
||||
struct device *dev;
|
||||
ktime_t starttime = ktime_get();
|
||||
|
@ -662,7 +662,7 @@ static void async_resume_early(void *data, async_cookie_t cookie)
|
|||
* dpm_resume_early - Execute "early resume" callbacks for all devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static void dpm_resume_early(pm_message_t state)
|
||||
void dpm_resume_early(pm_message_t state)
|
||||
{
|
||||
struct device *dev;
|
||||
ktime_t starttime = ktime_get();
|
||||
|
@ -1093,7 +1093,7 @@ static int device_suspend_noirq(struct device *dev)
|
|||
* Prevent device drivers from receiving interrupts and call the "noirq" suspend
|
||||
* handlers for all non-sysdev devices.
|
||||
*/
|
||||
static int dpm_suspend_noirq(pm_message_t state)
|
||||
int dpm_suspend_noirq(pm_message_t state)
|
||||
{
|
||||
ktime_t starttime = ktime_get();
|
||||
int error = 0;
|
||||
|
@ -1232,7 +1232,7 @@ static int device_suspend_late(struct device *dev)
|
|||
* dpm_suspend_late - Execute "late suspend" callbacks for all devices.
|
||||
* @state: PM transition of the system being carried out.
|
||||
*/
|
||||
static int dpm_suspend_late(pm_message_t state)
|
||||
int dpm_suspend_late(pm_message_t state)
|
||||
{
|
||||
ktime_t starttime = ktime_get();
|
||||
int error = 0;
|
||||
|
|
|
@ -92,9 +92,6 @@
|
|||
* wakeup_count - Report the number of wakeup events related to the device
|
||||
*/
|
||||
|
||||
static const char enabled[] = "enabled";
|
||||
static const char disabled[] = "disabled";
|
||||
|
||||
const char power_group_name[] = "power";
|
||||
EXPORT_SYMBOL_GPL(power_group_name);
|
||||
|
||||
|
@ -336,11 +333,14 @@ static DEVICE_ATTR(pm_qos_remote_wakeup, 0644,
|
|||
#endif /* CONFIG_PM_RUNTIME */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static const char _enabled[] = "enabled";
|
||||
static const char _disabled[] = "disabled";
|
||||
|
||||
static ssize_t
|
||||
wake_show(struct device * dev, struct device_attribute *attr, char * buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n", device_can_wakeup(dev)
|
||||
? (device_may_wakeup(dev) ? enabled : disabled)
|
||||
? (device_may_wakeup(dev) ? _enabled : _disabled)
|
||||
: "");
|
||||
}
|
||||
|
||||
|
@ -357,11 +357,11 @@ wake_store(struct device * dev, struct device_attribute *attr,
|
|||
cp = memchr(buf, '\n', n);
|
||||
if (cp)
|
||||
len = cp - buf;
|
||||
if (len == sizeof enabled - 1
|
||||
&& strncmp(buf, enabled, sizeof enabled - 1) == 0)
|
||||
if (len == sizeof _enabled - 1
|
||||
&& strncmp(buf, _enabled, sizeof _enabled - 1) == 0)
|
||||
device_set_wakeup_enable(dev, 1);
|
||||
else if (len == sizeof disabled - 1
|
||||
&& strncmp(buf, disabled, sizeof disabled - 1) == 0)
|
||||
else if (len == sizeof _disabled - 1
|
||||
&& strncmp(buf, _disabled, sizeof _disabled - 1) == 0)
|
||||
device_set_wakeup_enable(dev, 0);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
@ -570,7 +570,8 @@ static ssize_t async_show(struct device *dev, struct device_attribute *attr,
|
|||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%s\n",
|
||||
device_async_suspend_enabled(dev) ? enabled : disabled);
|
||||
device_async_suspend_enabled(dev) ?
|
||||
_enabled : _disabled);
|
||||
}
|
||||
|
||||
static ssize_t async_store(struct device *dev, struct device_attribute *attr,
|
||||
|
@ -582,9 +583,10 @@ static ssize_t async_store(struct device *dev, struct device_attribute *attr,
|
|||
cp = memchr(buf, '\n', n);
|
||||
if (cp)
|
||||
len = cp - buf;
|
||||
if (len == sizeof enabled - 1 && strncmp(buf, enabled, len) == 0)
|
||||
if (len == sizeof _enabled - 1 && strncmp(buf, _enabled, len) == 0)
|
||||
device_enable_async_suspend(dev);
|
||||
else if (len == sizeof disabled - 1 && strncmp(buf, disabled, len) == 0)
|
||||
else if (len == sizeof _disabled - 1 &&
|
||||
strncmp(buf, _disabled, len) == 0)
|
||||
device_disable_async_suspend(dev);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
*/
|
||||
bool events_check_enabled __read_mostly;
|
||||
|
||||
/* If set and the system is suspending, terminate the suspend. */
|
||||
static bool pm_abort_suspend __read_mostly;
|
||||
|
||||
/*
|
||||
* Combined counters of registered wakeup events and wakeup events in progress.
|
||||
* They need to be modified together atomically, so it's better to use one
|
||||
|
@ -719,7 +722,18 @@ bool pm_wakeup_pending(void)
|
|||
pm_print_active_wakeup_sources();
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret || pm_abort_suspend;
|
||||
}
|
||||
|
||||
void pm_system_wakeup(void)
|
||||
{
|
||||
pm_abort_suspend = true;
|
||||
freeze_wake();
|
||||
}
|
||||
|
||||
void pm_wakeup_clear(void)
|
||||
{
|
||||
pm_abort_suspend = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <linux/syscore_ops.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <trace/events/power.h>
|
||||
|
||||
static LIST_HEAD(syscore_ops_list);
|
||||
|
@ -54,9 +54,8 @@ int syscore_suspend(void)
|
|||
pr_debug("Checking wakeup interrupts\n");
|
||||
|
||||
/* Return error code if there are any wakeup interrupts pending. */
|
||||
ret = check_wakeup_irqs();
|
||||
if (ret)
|
||||
return ret;
|
||||
if (pm_wakeup_pending())
|
||||
return -EBUSY;
|
||||
|
||||
WARN_ONCE(!irqs_disabled(),
|
||||
"Interrupts enabled before system core suspend.\n");
|
||||
|
|
|
@ -183,14 +183,14 @@ config CPU_FREQ_GOV_CONSERVATIVE
|
|||
|
||||
If in doubt, say N.
|
||||
|
||||
config GENERIC_CPUFREQ_CPU0
|
||||
tristate "Generic CPU0 cpufreq driver"
|
||||
config CPUFREQ_DT
|
||||
tristate "Generic DT based cpufreq driver"
|
||||
depends on HAVE_CLK && OF
|
||||
# if CPU_THERMAL is on and THERMAL=m, CPU0 cannot be =y:
|
||||
# if CPU_THERMAL is on and THERMAL=m, CPUFREQ_DT cannot be =y:
|
||||
depends on !CPU_THERMAL || THERMAL
|
||||
select PM_OPP
|
||||
help
|
||||
This adds a generic cpufreq driver for CPU0 frequency management.
|
||||
This adds a generic DT based cpufreq driver for frequency management.
|
||||
It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
|
||||
systems which share clock and voltage across all CPUs.
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ config ARM_EXYNOS_CPU_FREQ_BOOST_SW
|
|||
|
||||
config ARM_HIGHBANK_CPUFREQ
|
||||
tristate "Calxeda Highbank-based"
|
||||
depends on ARCH_HIGHBANK && GENERIC_CPUFREQ_CPU0 && REGULATOR
|
||||
depends on ARCH_HIGHBANK && CPUFREQ_DT && REGULATOR
|
||||
default m
|
||||
help
|
||||
This adds the CPUFreq driver for Calxeda Highbank SoC
|
||||
|
|
|
@ -13,7 +13,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o
|
|||
obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
|
||||
obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
|
||||
|
||||
obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o
|
||||
obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o
|
||||
|
||||
##################################################################################
|
||||
# x86 drivers.
|
||||
|
|
|
@ -1,248 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The OPP code in function cpu0_set_target() is reused from
|
||||
* drivers/cpufreq/omap-cpufreq.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
static unsigned int transition_latency;
|
||||
static unsigned int voltage_tolerance; /* in percentage */
|
||||
|
||||
static struct device *cpu_dev;
|
||||
static struct clk *cpu_clk;
|
||||
static struct regulator *cpu_reg;
|
||||
static struct cpufreq_frequency_table *freq_table;
|
||||
static struct thermal_cooling_device *cdev;
|
||||
|
||||
static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long volt = 0, volt_old = 0, tol = 0;
|
||||
unsigned int old_freq, new_freq;
|
||||
long freq_Hz, freq_exact;
|
||||
int ret;
|
||||
|
||||
freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
|
||||
if (freq_Hz <= 0)
|
||||
freq_Hz = freq_table[index].frequency * 1000;
|
||||
|
||||
freq_exact = freq_Hz;
|
||||
new_freq = freq_Hz / 1000;
|
||||
old_freq = clk_get_rate(cpu_clk) / 1000;
|
||||
|
||||
if (!IS_ERR(cpu_reg)) {
|
||||
rcu_read_lock();
|
||||
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
|
||||
if (IS_ERR(opp)) {
|
||||
rcu_read_unlock();
|
||||
pr_err("failed to find OPP for %ld\n", freq_Hz);
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
volt = dev_pm_opp_get_voltage(opp);
|
||||
rcu_read_unlock();
|
||||
tol = volt * voltage_tolerance / 100;
|
||||
volt_old = regulator_get_voltage(cpu_reg);
|
||||
}
|
||||
|
||||
pr_debug("%u MHz, %ld mV --> %u MHz, %ld mV\n",
|
||||
old_freq / 1000, volt_old ? volt_old / 1000 : -1,
|
||||
new_freq / 1000, volt ? volt / 1000 : -1);
|
||||
|
||||
/* scaling up? scale voltage before frequency */
|
||||
if (!IS_ERR(cpu_reg) && new_freq > old_freq) {
|
||||
ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
|
||||
if (ret) {
|
||||
pr_err("failed to scale voltage up: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_set_rate(cpu_clk, freq_exact);
|
||||
if (ret) {
|
||||
pr_err("failed to set clock rate: %d\n", ret);
|
||||
if (!IS_ERR(cpu_reg))
|
||||
regulator_set_voltage_tol(cpu_reg, volt_old, tol);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* scaling down? scale voltage after frequency */
|
||||
if (!IS_ERR(cpu_reg) && new_freq < old_freq) {
|
||||
ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
|
||||
if (ret) {
|
||||
pr_err("failed to scale voltage down: %d\n", ret);
|
||||
clk_set_rate(cpu_clk, old_freq * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpu0_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
policy->clk = cpu_clk;
|
||||
return cpufreq_generic_init(policy, freq_table, transition_latency);
|
||||
}
|
||||
|
||||
static struct cpufreq_driver cpu0_cpufreq_driver = {
|
||||
.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
|
||||
.verify = cpufreq_generic_frequency_table_verify,
|
||||
.target_index = cpu0_set_target,
|
||||
.get = cpufreq_generic_get,
|
||||
.init = cpu0_cpufreq_init,
|
||||
.name = "generic_cpu0",
|
||||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
||||
static int cpu0_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
cpu_dev = get_cpu_device(0);
|
||||
if (!cpu_dev) {
|
||||
pr_err("failed to get cpu0 device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
np = of_node_get(cpu_dev->of_node);
|
||||
if (!np) {
|
||||
pr_err("failed to find cpu0 node\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
cpu_reg = regulator_get_optional(cpu_dev, "cpu0");
|
||||
if (IS_ERR(cpu_reg)) {
|
||||
/*
|
||||
* If cpu0 regulator supply node is present, but regulator is
|
||||
* not yet registered, we should try defering probe.
|
||||
*/
|
||||
if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) {
|
||||
dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n");
|
||||
ret = -EPROBE_DEFER;
|
||||
goto out_put_node;
|
||||
}
|
||||
pr_warn("failed to get cpu0 regulator: %ld\n",
|
||||
PTR_ERR(cpu_reg));
|
||||
}
|
||||
|
||||
cpu_clk = clk_get(cpu_dev, NULL);
|
||||
if (IS_ERR(cpu_clk)) {
|
||||
ret = PTR_ERR(cpu_clk);
|
||||
pr_err("failed to get cpu0 clock: %d\n", ret);
|
||||
goto out_put_reg;
|
||||
}
|
||||
|
||||
/* OPPs might be populated at runtime, don't check for error here */
|
||||
of_init_opp_table(cpu_dev);
|
||||
|
||||
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
|
||||
if (ret) {
|
||||
pr_err("failed to init cpufreq table: %d\n", ret);
|
||||
goto out_put_clk;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "voltage-tolerance", &voltage_tolerance);
|
||||
|
||||
if (of_property_read_u32(np, "clock-latency", &transition_latency))
|
||||
transition_latency = CPUFREQ_ETERNAL;
|
||||
|
||||
if (!IS_ERR(cpu_reg)) {
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long min_uV, max_uV;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* OPP is maintained in order of increasing frequency, and
|
||||
* freq_table initialised from OPP is therefore sorted in the
|
||||
* same order.
|
||||
*/
|
||||
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
|
||||
;
|
||||
rcu_read_lock();
|
||||
opp = dev_pm_opp_find_freq_exact(cpu_dev,
|
||||
freq_table[0].frequency * 1000, true);
|
||||
min_uV = dev_pm_opp_get_voltage(opp);
|
||||
opp = dev_pm_opp_find_freq_exact(cpu_dev,
|
||||
freq_table[i-1].frequency * 1000, true);
|
||||
max_uV = dev_pm_opp_get_voltage(opp);
|
||||
rcu_read_unlock();
|
||||
ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
|
||||
if (ret > 0)
|
||||
transition_latency += ret * 1000;
|
||||
}
|
||||
|
||||
ret = cpufreq_register_driver(&cpu0_cpufreq_driver);
|
||||
if (ret) {
|
||||
pr_err("failed register driver: %d\n", ret);
|
||||
goto out_free_table;
|
||||
}
|
||||
|
||||
/*
|
||||
* For now, just loading the cooling device;
|
||||
* thermal DT code takes care of matching them.
|
||||
*/
|
||||
if (of_find_property(np, "#cooling-cells", NULL)) {
|
||||
cdev = of_cpufreq_cooling_register(np, cpu_present_mask);
|
||||
if (IS_ERR(cdev))
|
||||
pr_err("running cpufreq without cooling device: %ld\n",
|
||||
PTR_ERR(cdev));
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
return 0;
|
||||
|
||||
out_free_table:
|
||||
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
||||
out_put_clk:
|
||||
if (!IS_ERR(cpu_clk))
|
||||
clk_put(cpu_clk);
|
||||
out_put_reg:
|
||||
if (!IS_ERR(cpu_reg))
|
||||
regulator_put(cpu_reg);
|
||||
out_put_node:
|
||||
of_node_put(np);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpu0_cpufreq_remove(struct platform_device *pdev)
|
||||
{
|
||||
cpufreq_cooling_unregister(cdev);
|
||||
cpufreq_unregister_driver(&cpu0_cpufreq_driver);
|
||||
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver cpu0_cpufreq_platdrv = {
|
||||
.driver = {
|
||||
.name = "cpufreq-cpu0",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = cpu0_cpufreq_probe,
|
||||
.remove = cpu0_cpufreq_remove,
|
||||
};
|
||||
module_platform_driver(cpu0_cpufreq_platdrv);
|
||||
|
||||
MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
|
||||
MODULE_DESCRIPTION("Generic CPU0 cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
364
drivers/cpufreq/cpufreq-dt.c
Normal file
364
drivers/cpufreq/cpufreq-dt.c
Normal file
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* Copyright (C) 2014 Linaro.
|
||||
* Viresh Kumar <viresh.kumar@linaro.org>
|
||||
*
|
||||
* The OPP code in function set_target() is reused from
|
||||
* drivers/cpufreq/omap-cpufreq.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
struct private_data {
|
||||
struct device *cpu_dev;
|
||||
struct regulator *cpu_reg;
|
||||
struct thermal_cooling_device *cdev;
|
||||
unsigned int voltage_tolerance; /* in percentage */
|
||||
};
|
||||
|
||||
static int set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
struct dev_pm_opp *opp;
|
||||
struct cpufreq_frequency_table *freq_table = policy->freq_table;
|
||||
struct clk *cpu_clk = policy->clk;
|
||||
struct private_data *priv = policy->driver_data;
|
||||
struct device *cpu_dev = priv->cpu_dev;
|
||||
struct regulator *cpu_reg = priv->cpu_reg;
|
||||
unsigned long volt = 0, volt_old = 0, tol = 0;
|
||||
unsigned int old_freq, new_freq;
|
||||
long freq_Hz, freq_exact;
|
||||
int ret;
|
||||
|
||||
freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
|
||||
if (freq_Hz <= 0)
|
||||
freq_Hz = freq_table[index].frequency * 1000;
|
||||
|
||||
freq_exact = freq_Hz;
|
||||
new_freq = freq_Hz / 1000;
|
||||
old_freq = clk_get_rate(cpu_clk) / 1000;
|
||||
|
||||
if (!IS_ERR(cpu_reg)) {
|
||||
rcu_read_lock();
|
||||
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
|
||||
if (IS_ERR(opp)) {
|
||||
rcu_read_unlock();
|
||||
dev_err(cpu_dev, "failed to find OPP for %ld\n",
|
||||
freq_Hz);
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
volt = dev_pm_opp_get_voltage(opp);
|
||||
rcu_read_unlock();
|
||||
tol = volt * priv->voltage_tolerance / 100;
|
||||
volt_old = regulator_get_voltage(cpu_reg);
|
||||
}
|
||||
|
||||
dev_dbg(cpu_dev, "%u MHz, %ld mV --> %u MHz, %ld mV\n",
|
||||
old_freq / 1000, volt_old ? volt_old / 1000 : -1,
|
||||
new_freq / 1000, volt ? volt / 1000 : -1);
|
||||
|
||||
/* scaling up? scale voltage before frequency */
|
||||
if (!IS_ERR(cpu_reg) && new_freq > old_freq) {
|
||||
ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "failed to scale voltage up: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = clk_set_rate(cpu_clk, freq_exact);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "failed to set clock rate: %d\n", ret);
|
||||
if (!IS_ERR(cpu_reg))
|
||||
regulator_set_voltage_tol(cpu_reg, volt_old, tol);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* scaling down? scale voltage after frequency */
|
||||
if (!IS_ERR(cpu_reg) && new_freq < old_freq) {
|
||||
ret = regulator_set_voltage_tol(cpu_reg, volt, tol);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "failed to scale voltage down: %d\n",
|
||||
ret);
|
||||
clk_set_rate(cpu_clk, old_freq * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int allocate_resources(int cpu, struct device **cdev,
|
||||
struct regulator **creg, struct clk **cclk)
|
||||
{
|
||||
struct device *cpu_dev;
|
||||
struct regulator *cpu_reg;
|
||||
struct clk *cpu_clk;
|
||||
int ret = 0;
|
||||
char *reg_cpu0 = "cpu0", *reg_cpu = "cpu", *reg;
|
||||
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
if (!cpu_dev) {
|
||||
pr_err("failed to get cpu%d device\n", cpu);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Try "cpu0" for older DTs */
|
||||
if (!cpu)
|
||||
reg = reg_cpu0;
|
||||
else
|
||||
reg = reg_cpu;
|
||||
|
||||
try_again:
|
||||
cpu_reg = regulator_get_optional(cpu_dev, reg);
|
||||
if (IS_ERR(cpu_reg)) {
|
||||
/*
|
||||
* If cpu's regulator supply node is present, but regulator is
|
||||
* not yet registered, we should try defering probe.
|
||||
*/
|
||||
if (PTR_ERR(cpu_reg) == -EPROBE_DEFER) {
|
||||
dev_dbg(cpu_dev, "cpu%d regulator not ready, retry\n",
|
||||
cpu);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
/* Try with "cpu-supply" */
|
||||
if (reg == reg_cpu0) {
|
||||
reg = reg_cpu;
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
dev_warn(cpu_dev, "failed to get cpu%d regulator: %ld\n",
|
||||
cpu, PTR_ERR(cpu_reg));
|
||||
}
|
||||
|
||||
cpu_clk = clk_get(cpu_dev, NULL);
|
||||
if (IS_ERR(cpu_clk)) {
|
||||
/* put regulator */
|
||||
if (!IS_ERR(cpu_reg))
|
||||
regulator_put(cpu_reg);
|
||||
|
||||
ret = PTR_ERR(cpu_clk);
|
||||
|
||||
/*
|
||||
* If cpu's clk node is present, but clock is not yet
|
||||
* registered, we should try defering probe.
|
||||
*/
|
||||
if (ret == -EPROBE_DEFER)
|
||||
dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu);
|
||||
else
|
||||
dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret,
|
||||
cpu);
|
||||
} else {
|
||||
*cdev = cpu_dev;
|
||||
*creg = cpu_reg;
|
||||
*cclk = cpu_clk;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct device_node *np;
|
||||
struct private_data *priv;
|
||||
struct device *cpu_dev;
|
||||
struct regulator *cpu_reg;
|
||||
struct clk *cpu_clk;
|
||||
unsigned int transition_latency;
|
||||
int ret;
|
||||
|
||||
ret = allocate_resources(policy->cpu, &cpu_dev, &cpu_reg, &cpu_clk);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to allocate resources\n: %d", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
np = of_node_get(cpu_dev->of_node);
|
||||
if (!np) {
|
||||
dev_err(cpu_dev, "failed to find cpu%d node\n", policy->cpu);
|
||||
ret = -ENOENT;
|
||||
goto out_put_reg_clk;
|
||||
}
|
||||
|
||||
/* OPPs might be populated at runtime, don't check for error here */
|
||||
of_init_opp_table(cpu_dev);
|
||||
|
||||
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
|
||||
goto out_put_node;
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_table;
|
||||
}
|
||||
|
||||
of_property_read_u32(np, "voltage-tolerance", &priv->voltage_tolerance);
|
||||
|
||||
if (of_property_read_u32(np, "clock-latency", &transition_latency))
|
||||
transition_latency = CPUFREQ_ETERNAL;
|
||||
|
||||
if (!IS_ERR(cpu_reg)) {
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned long min_uV, max_uV;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* OPP is maintained in order of increasing frequency, and
|
||||
* freq_table initialised from OPP is therefore sorted in the
|
||||
* same order.
|
||||
*/
|
||||
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
|
||||
;
|
||||
rcu_read_lock();
|
||||
opp = dev_pm_opp_find_freq_exact(cpu_dev,
|
||||
freq_table[0].frequency * 1000, true);
|
||||
min_uV = dev_pm_opp_get_voltage(opp);
|
||||
opp = dev_pm_opp_find_freq_exact(cpu_dev,
|
||||
freq_table[i-1].frequency * 1000, true);
|
||||
max_uV = dev_pm_opp_get_voltage(opp);
|
||||
rcu_read_unlock();
|
||||
ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
|
||||
if (ret > 0)
|
||||
transition_latency += ret * 1000;
|
||||
}
|
||||
|
||||
/*
|
||||
* For now, just loading the cooling device;
|
||||
* thermal DT code takes care of matching them.
|
||||
*/
|
||||
if (of_find_property(np, "#cooling-cells", NULL)) {
|
||||
cdev = of_cpufreq_cooling_register(np, cpu_present_mask);
|
||||
if (IS_ERR(cdev))
|
||||
dev_err(cpu_dev,
|
||||
"running cpufreq without cooling device: %ld\n",
|
||||
PTR_ERR(cdev));
|
||||
else
|
||||
priv->cdev = cdev;
|
||||
}
|
||||
|
||||
priv->cpu_dev = cpu_dev;
|
||||
priv->cpu_reg = cpu_reg;
|
||||
policy->driver_data = priv;
|
||||
|
||||
policy->clk = cpu_clk;
|
||||
ret = cpufreq_generic_init(policy, freq_table, transition_latency);
|
||||
if (ret)
|
||||
goto out_cooling_unregister;
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
return 0;
|
||||
|
||||
out_cooling_unregister:
|
||||
cpufreq_cooling_unregister(priv->cdev);
|
||||
kfree(priv);
|
||||
out_free_table:
|
||||
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
|
||||
out_put_node:
|
||||
of_node_put(np);
|
||||
out_put_reg_clk:
|
||||
clk_put(cpu_clk);
|
||||
if (!IS_ERR(cpu_reg))
|
||||
regulator_put(cpu_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cpufreq_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct private_data *priv = policy->driver_data;
|
||||
|
||||
cpufreq_cooling_unregister(priv->cdev);
|
||||
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
|
||||
clk_put(policy->clk);
|
||||
if (!IS_ERR(priv->cpu_reg))
|
||||
regulator_put(priv->cpu_reg);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cpufreq_driver dt_cpufreq_driver = {
|
||||
.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
|
||||
.verify = cpufreq_generic_frequency_table_verify,
|
||||
.target_index = set_target,
|
||||
.get = cpufreq_generic_get,
|
||||
.init = cpufreq_init,
|
||||
.exit = cpufreq_exit,
|
||||
.name = "cpufreq-dt",
|
||||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
||||
static int dt_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *cpu_dev;
|
||||
struct regulator *cpu_reg;
|
||||
struct clk *cpu_clk;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* All per-cluster (CPUs sharing clock/voltages) initialization is done
|
||||
* from ->init(). In probe(), we just need to make sure that clk and
|
||||
* regulators are available. Else defer probe and retry.
|
||||
*
|
||||
* FIXME: Is checking this only for CPU0 sufficient ?
|
||||
*/
|
||||
ret = allocate_resources(0, &cpu_dev, &cpu_reg, &cpu_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_put(cpu_clk);
|
||||
if (!IS_ERR(cpu_reg))
|
||||
regulator_put(cpu_reg);
|
||||
|
||||
ret = cpufreq_register_driver(&dt_cpufreq_driver);
|
||||
if (ret)
|
||||
dev_err(cpu_dev, "failed register driver: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dt_cpufreq_remove(struct platform_device *pdev)
|
||||
{
|
||||
cpufreq_unregister_driver(&dt_cpufreq_driver);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dt_cpufreq_platdrv = {
|
||||
.driver = {
|
||||
.name = "cpufreq-dt",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = dt_cpufreq_probe,
|
||||
.remove = dt_cpufreq_remove,
|
||||
};
|
||||
module_platform_driver(dt_cpufreq_platdrv);
|
||||
|
||||
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
|
||||
MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
|
||||
MODULE_DESCRIPTION("Generic cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -437,7 +437,7 @@ static struct cpufreq_governor *__find_governor(const char *str_governor)
|
|||
struct cpufreq_governor *t;
|
||||
|
||||
list_for_each_entry(t, &cpufreq_governor_list, governor_list)
|
||||
if (!strnicmp(str_governor, t->name, CPUFREQ_NAME_LEN))
|
||||
if (!strncasecmp(str_governor, t->name, CPUFREQ_NAME_LEN))
|
||||
return t;
|
||||
|
||||
return NULL;
|
||||
|
@ -455,10 +455,10 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy,
|
|||
goto out;
|
||||
|
||||
if (cpufreq_driver->setpolicy) {
|
||||
if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
|
||||
if (!strncasecmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {
|
||||
*policy = CPUFREQ_POLICY_PERFORMANCE;
|
||||
err = 0;
|
||||
} else if (!strnicmp(str_governor, "powersave",
|
||||
} else if (!strncasecmp(str_governor, "powersave",
|
||||
CPUFREQ_NAME_LEN)) {
|
||||
*policy = CPUFREQ_POLICY_POWERSAVE;
|
||||
err = 0;
|
||||
|
@ -1382,7 +1382,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev,
|
|||
if (!cpufreq_suspended)
|
||||
pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
|
||||
__func__, new_cpu, cpu);
|
||||
} else if (cpufreq_driver->stop_cpu && cpufreq_driver->setpolicy) {
|
||||
} else if (cpufreq_driver->stop_cpu) {
|
||||
cpufreq_driver->stop_cpu(policy);
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info)
|
|||
* dependencies on platform headers. It is necessary to enable
|
||||
* Exynos multi-platform support and will be removed together with
|
||||
* this whole driver as soon as Exynos gets migrated to use
|
||||
* cpufreq-cpu0 driver.
|
||||
* cpufreq-dt driver.
|
||||
*/
|
||||
np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock");
|
||||
if (!np) {
|
||||
|
|
|
@ -174,7 +174,7 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
|
|||
* dependencies on platform headers. It is necessary to enable
|
||||
* Exynos multi-platform support and will be removed together with
|
||||
* this whole driver as soon as Exynos gets migrated to use
|
||||
* cpufreq-cpu0 driver.
|
||||
* cpufreq-dt driver.
|
||||
*/
|
||||
np = of_find_compatible_node(NULL, NULL, "samsung,exynos4412-clock");
|
||||
if (!np) {
|
||||
|
|
|
@ -153,7 +153,7 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
|
|||
* dependencies on platform headers. It is necessary to enable
|
||||
* Exynos multi-platform support and will be removed together with
|
||||
* this whole driver as soon as Exynos gets migrated to use
|
||||
* cpufreq-cpu0 driver.
|
||||
* cpufreq-dt driver.
|
||||
*/
|
||||
np = of_find_compatible_node(NULL, NULL, "samsung,exynos5250-clock");
|
||||
if (!np) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This driver provides the clk notifier callbacks that are used when
|
||||
* the cpufreq-cpu0 driver changes to frequency to alert the highbank
|
||||
* the cpufreq-dt driver changes to frequency to alert the highbank
|
||||
* EnergyCore Management Engine (ECME) about the need to change
|
||||
* voltage. The ECME interfaces with the actual voltage regulators.
|
||||
*/
|
||||
|
@ -60,7 +60,7 @@ static struct notifier_block hb_cpufreq_clk_nb = {
|
|||
|
||||
static int hb_cpufreq_driver_init(void)
|
||||
{
|
||||
struct platform_device_info devinfo = { .name = "cpufreq-cpu0", };
|
||||
struct platform_device_info devinfo = { .name = "cpufreq-dt", };
|
||||
struct device *cpu_dev;
|
||||
struct clk *cpu_clk;
|
||||
struct device_node *np;
|
||||
|
@ -95,7 +95,7 @@ static int hb_cpufreq_driver_init(void)
|
|||
goto out_put_node;
|
||||
}
|
||||
|
||||
/* Instantiate cpufreq-cpu0 */
|
||||
/* Instantiate cpufreq-dt */
|
||||
platform_device_register_full(&devinfo);
|
||||
|
||||
out_put_node:
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/cpufreq.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#include <asm/cputhreads.h>
|
||||
#include <asm/firmware.h>
|
||||
|
@ -35,6 +36,7 @@
|
|||
#define POWERNV_MAX_PSTATES 256
|
||||
|
||||
static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1];
|
||||
static bool rebooting;
|
||||
|
||||
/*
|
||||
* Note: The set of pstates consists of contiguous integers, the
|
||||
|
@ -283,6 +285,15 @@ static void set_pstate(void *freq_data)
|
|||
set_pmspr(SPRN_PMCR, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_nominal_index: Returns the index corresponding to the nominal
|
||||
* pstate in the cpufreq table
|
||||
*/
|
||||
static inline unsigned int get_nominal_index(void)
|
||||
{
|
||||
return powernv_pstate_info.max - powernv_pstate_info.nominal;
|
||||
}
|
||||
|
||||
/*
|
||||
* powernv_cpufreq_target_index: Sets the frequency corresponding to
|
||||
* the cpufreq table entry indexed by new_index on the cpus in the
|
||||
|
@ -293,6 +304,9 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy,
|
|||
{
|
||||
struct powernv_smp_call_data freq_data;
|
||||
|
||||
if (unlikely(rebooting) && new_index != get_nominal_index())
|
||||
return 0;
|
||||
|
||||
freq_data.pstate_id = powernv_freqs[new_index].driver_data;
|
||||
|
||||
/*
|
||||
|
@ -317,6 +331,33 @@ static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||
return cpufreq_table_validate_and_show(policy, powernv_freqs);
|
||||
}
|
||||
|
||||
static int powernv_cpufreq_reboot_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *unused)
|
||||
{
|
||||
int cpu;
|
||||
struct cpufreq_policy cpu_policy;
|
||||
|
||||
rebooting = true;
|
||||
for_each_online_cpu(cpu) {
|
||||
cpufreq_get_policy(&cpu_policy, cpu);
|
||||
powernv_cpufreq_target_index(&cpu_policy, get_nominal_index());
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block powernv_cpufreq_reboot_nb = {
|
||||
.notifier_call = powernv_cpufreq_reboot_notifier,
|
||||
};
|
||||
|
||||
static void powernv_cpufreq_stop_cpu(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct powernv_smp_call_data freq_data;
|
||||
|
||||
freq_data.pstate_id = powernv_pstate_info.min;
|
||||
smp_call_function_single(policy->cpu, set_pstate, &freq_data, 1);
|
||||
}
|
||||
|
||||
static struct cpufreq_driver powernv_cpufreq_driver = {
|
||||
.name = "powernv-cpufreq",
|
||||
.flags = CPUFREQ_CONST_LOOPS,
|
||||
|
@ -324,6 +365,7 @@ static struct cpufreq_driver powernv_cpufreq_driver = {
|
|||
.verify = cpufreq_generic_frequency_table_verify,
|
||||
.target_index = powernv_cpufreq_target_index,
|
||||
.get = powernv_cpufreq_get,
|
||||
.stop_cpu = powernv_cpufreq_stop_cpu,
|
||||
.attr = powernv_cpu_freq_attr,
|
||||
};
|
||||
|
||||
|
@ -342,12 +384,14 @@ static int __init powernv_cpufreq_init(void)
|
|||
return rc;
|
||||
}
|
||||
|
||||
register_reboot_notifier(&powernv_cpufreq_reboot_nb);
|
||||
return cpufreq_register_driver(&powernv_cpufreq_driver);
|
||||
}
|
||||
module_init(powernv_cpufreq_init);
|
||||
|
||||
static void __exit powernv_cpufreq_exit(void)
|
||||
{
|
||||
unregister_reboot_notifier(&powernv_cpufreq_reboot_nb);
|
||||
cpufreq_unregister_driver(&powernv_cpufreq_driver);
|
||||
}
|
||||
module_exit(powernv_cpufreq_exit);
|
||||
|
|
|
@ -199,7 +199,6 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||
}
|
||||
|
||||
data->table = table;
|
||||
per_cpu(cpu_data, cpu) = data;
|
||||
|
||||
/* update ->cpus if we have cluster, no harm if not */
|
||||
cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu));
|
||||
|
|
|
@ -597,7 +597,7 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev)
|
|||
* and dependencies on platform headers. It is necessary to enable
|
||||
* S5PV210 multi-platform support and will be removed together with
|
||||
* this whole driver as soon as S5PV210 gets migrated to use
|
||||
* cpufreq-cpu0 driver.
|
||||
* cpufreq-dt driver.
|
||||
*/
|
||||
np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock");
|
||||
if (!np) {
|
||||
|
|
|
@ -25,11 +25,19 @@ config CPU_IDLE_GOV_MENU
|
|||
bool "Menu governor (for tickless system)"
|
||||
default y
|
||||
|
||||
config DT_IDLE_STATES
|
||||
bool
|
||||
|
||||
menu "ARM CPU Idle Drivers"
|
||||
depends on ARM
|
||||
source "drivers/cpuidle/Kconfig.arm"
|
||||
endmenu
|
||||
|
||||
menu "ARM64 CPU Idle Drivers"
|
||||
depends on ARM64
|
||||
source "drivers/cpuidle/Kconfig.arm64"
|
||||
endmenu
|
||||
|
||||
menu "MIPS CPU Idle Drivers"
|
||||
depends on MIPS
|
||||
source "drivers/cpuidle/Kconfig.mips"
|
||||
|
|
|
@ -7,6 +7,7 @@ config ARM_BIG_LITTLE_CPUIDLE
|
|||
depends on MCPM
|
||||
select ARM_CPU_SUSPEND
|
||||
select CPU_IDLE_MULTIPLE_DRIVERS
|
||||
select DT_IDLE_STATES
|
||||
help
|
||||
Select this option to enable CPU idle driver for big.LITTLE based
|
||||
ARM systems. Driver manages CPUs coordination through MCPM and
|
||||
|
|
14
drivers/cpuidle/Kconfig.arm64
Normal file
14
drivers/cpuidle/Kconfig.arm64
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# ARM64 CPU Idle drivers
|
||||
#
|
||||
|
||||
config ARM64_CPUIDLE
|
||||
bool "Generic ARM64 CPU idle Driver"
|
||||
select ARM64_CPU_SUSPEND
|
||||
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.
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
|
||||
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
|
||||
obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o
|
||||
|
||||
##################################################################################
|
||||
# ARM SoC drivers
|
||||
|
@ -21,6 +22,10 @@ obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o
|
|||
# MIPS drivers
|
||||
obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o
|
||||
|
||||
###############################################################################
|
||||
# ARM64 drivers
|
||||
obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o
|
||||
|
||||
###############################################################################
|
||||
# POWERPC drivers
|
||||
obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
|
||||
|
|
133
drivers/cpuidle/cpuidle-arm64.c
Normal file
133
drivers/cpuidle/cpuidle-arm64.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* ARM64 generic CPU idle driver.
|
||||
*
|
||||
* Copyright (C) 2014 ARM Ltd.
|
||||
* Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "CPUidle arm64: " fmt
|
||||
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include "dt_idle_states.h"
|
||||
|
||||
/*
|
||||
* arm64_enter_idle_state - Programs CPU to enter the specified state
|
||||
*
|
||||
* dev: cpuidle device
|
||||
* drv: cpuidle driver
|
||||
* idx: state index
|
||||
*
|
||||
* Called from the CPUidle framework to program the device to the
|
||||
* specified target state selected by the governor.
|
||||
*/
|
||||
static int arm64_enter_idle_state(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!idx) {
|
||||
cpu_do_idle();
|
||||
return idx;
|
||||
}
|
||||
|
||||
ret = cpu_pm_enter();
|
||||
if (!ret) {
|
||||
/*
|
||||
* Pass idle state index to cpu_suspend which in turn will
|
||||
* call the CPU ops suspend protocol with idle index as a
|
||||
* parameter.
|
||||
*/
|
||||
ret = cpu_suspend(idx);
|
||||
|
||||
cpu_pm_exit();
|
||||
}
|
||||
|
||||
return ret ? -1 : idx;
|
||||
}
|
||||
|
||||
static struct cpuidle_driver arm64_idle_driver = {
|
||||
.name = "arm64_idle",
|
||||
.owner = THIS_MODULE,
|
||||
/*
|
||||
* State at index 0 is standby wfi and considered standard
|
||||
* on all ARM platforms. If in some platforms simple wfi
|
||||
* can't be used as "state 0", DT bindings must be implemented
|
||||
* to work around this issue and allow installing a special
|
||||
* handler for idle state index 0.
|
||||
*/
|
||||
.states[0] = {
|
||||
.enter = arm64_enter_idle_state,
|
||||
.exit_latency = 1,
|
||||
.target_residency = 1,
|
||||
.power_usage = UINT_MAX,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "WFI",
|
||||
.desc = "ARM64 WFI",
|
||||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id arm64_idle_state_match[] __initconst = {
|
||||
{ .compatible = "arm,idle-state",
|
||||
.data = arm64_enter_idle_state },
|
||||
{ },
|
||||
};
|
||||
|
||||
/*
|
||||
* arm64_idle_init
|
||||
*
|
||||
* Registers the arm64 specific cpuidle driver with the cpuidle
|
||||
* framework. It relies on core code to parse the idle states
|
||||
* and initialize them using driver data structures accordingly.
|
||||
*/
|
||||
static int __init arm64_idle_init(void)
|
||||
{
|
||||
int cpu, ret;
|
||||
struct cpuidle_driver *drv = &arm64_idle_driver;
|
||||
|
||||
/*
|
||||
* Initialize idle states data, starting at index 1.
|
||||
* This driver is DT only, if no DT idle states are detected (ret == 0)
|
||||
* let the driver initialization fail accordingly since there is no
|
||||
* reason to initialize the idle driver if only wfi is supported.
|
||||
*/
|
||||
ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1);
|
||||
if (ret <= 0) {
|
||||
if (ret)
|
||||
pr_err("failed to initialize idle states\n");
|
||||
return ret ? : -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call arch CPU operations in order to initialize
|
||||
* idle states suspend back-end specific data
|
||||
*/
|
||||
for_each_possible_cpu(cpu) {
|
||||
ret = cpu_init_idle(cpu);
|
||||
if (ret) {
|
||||
pr_err("CPU %d failed to init idle CPU ops\n", cpu);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = cpuidle_register(drv, NULL);
|
||||
if (ret) {
|
||||
pr_err("failed to register cpuidle driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(arm64_idle_init);
|
|
@ -24,6 +24,8 @@
|
|||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include "dt_idle_states.h"
|
||||
|
||||
static int bl_enter_powerdown(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx);
|
||||
|
||||
|
@ -73,6 +75,12 @@ static struct cpuidle_driver bl_idle_little_driver = {
|
|||
.state_count = 2,
|
||||
};
|
||||
|
||||
static const struct of_device_id bl_idle_state_match[] __initconst = {
|
||||
{ .compatible = "arm,idle-state",
|
||||
.data = bl_enter_powerdown },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct cpuidle_driver bl_idle_big_driver = {
|
||||
.name = "big_idle",
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -159,6 +167,7 @@ static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int part_id)
|
|||
static const struct of_device_id compatible_machine_match[] = {
|
||||
{ .compatible = "arm,vexpress,v2p-ca15_a7" },
|
||||
{ .compatible = "samsung,exynos5420" },
|
||||
{ .compatible = "samsung,exynos5800" },
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -190,6 +199,17 @@ static int __init bl_idle_init(void)
|
|||
if (ret)
|
||||
goto out_uninit_little;
|
||||
|
||||
/* Start at index 1, index 0 standard WFI */
|
||||
ret = dt_init_idle_driver(&bl_idle_big_driver, bl_idle_state_match, 1);
|
||||
if (ret < 0)
|
||||
goto out_uninit_big;
|
||||
|
||||
/* Start at index 1, index 0 standard WFI */
|
||||
ret = dt_init_idle_driver(&bl_idle_little_driver,
|
||||
bl_idle_state_match, 1);
|
||||
if (ret < 0)
|
||||
goto out_uninit_big;
|
||||
|
||||
ret = cpuidle_register(&bl_idle_little_driver, NULL);
|
||||
if (ret)
|
||||
goto out_uninit_big;
|
||||
|
|
213
drivers/cpuidle/dt_idle_states.c
Normal file
213
drivers/cpuidle/dt_idle_states.c
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* DT idle states parsing code.
|
||||
*
|
||||
* Copyright (C) 2014 ARM Ltd.
|
||||
* Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "DT idle-states: " fmt
|
||||
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "dt_idle_states.h"
|
||||
|
||||
static int init_state_node(struct cpuidle_state *idle_state,
|
||||
const struct of_device_id *matches,
|
||||
struct device_node *state_node)
|
||||
{
|
||||
int err;
|
||||
const struct of_device_id *match_id;
|
||||
|
||||
match_id = of_match_node(matches, state_node);
|
||||
if (!match_id)
|
||||
return -ENODEV;
|
||||
/*
|
||||
* CPUidle drivers are expected to initialize the const void *data
|
||||
* pointer of the passed in struct of_device_id array to the idle
|
||||
* state enter function.
|
||||
*/
|
||||
idle_state->enter = match_id->data;
|
||||
|
||||
err = of_property_read_u32(state_node, "wakeup-latency-us",
|
||||
&idle_state->exit_latency);
|
||||
if (err) {
|
||||
u32 entry_latency, exit_latency;
|
||||
|
||||
err = of_property_read_u32(state_node, "entry-latency-us",
|
||||
&entry_latency);
|
||||
if (err) {
|
||||
pr_debug(" * %s missing entry-latency-us property\n",
|
||||
state_node->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = of_property_read_u32(state_node, "exit-latency-us",
|
||||
&exit_latency);
|
||||
if (err) {
|
||||
pr_debug(" * %s missing exit-latency-us property\n",
|
||||
state_node->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* If wakeup-latency-us is missing, default to entry+exit
|
||||
* latencies as defined in idle states bindings
|
||||
*/
|
||||
idle_state->exit_latency = entry_latency + exit_latency;
|
||||
}
|
||||
|
||||
err = of_property_read_u32(state_node, "min-residency-us",
|
||||
&idle_state->target_residency);
|
||||
if (err) {
|
||||
pr_debug(" * %s missing min-residency-us property\n",
|
||||
state_node->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
idle_state->flags = CPUIDLE_FLAG_TIME_VALID;
|
||||
if (of_property_read_bool(state_node, "local-timer-stop"))
|
||||
idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
|
||||
/*
|
||||
* TODO:
|
||||
* replace with kstrdup and pointer assignment when name
|
||||
* and desc become string pointers
|
||||
*/
|
||||
strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1);
|
||||
strncpy(idle_state->desc, state_node->name, CPUIDLE_DESC_LEN - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the idle state is uniform across all CPUs in the CPUidle driver
|
||||
* cpumask
|
||||
*/
|
||||
static bool idle_state_valid(struct device_node *state_node, unsigned int idx,
|
||||
const cpumask_t *cpumask)
|
||||
{
|
||||
int cpu;
|
||||
struct device_node *cpu_node, *curr_state_node;
|
||||
bool valid = true;
|
||||
|
||||
/*
|
||||
* Compare idle state phandles for index idx on all CPUs in the
|
||||
* CPUidle driver cpumask. Start from next logical cpu following
|
||||
* cpumask_first(cpumask) since that's the CPU state_node was
|
||||
* retrieved from. If a mismatch is found bail out straight
|
||||
* away since we certainly hit a firmware misconfiguration.
|
||||
*/
|
||||
for (cpu = cpumask_next(cpumask_first(cpumask), cpumask);
|
||||
cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) {
|
||||
cpu_node = of_cpu_device_node_get(cpu);
|
||||
curr_state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
|
||||
idx);
|
||||
if (state_node != curr_state_node)
|
||||
valid = false;
|
||||
|
||||
of_node_put(curr_state_node);
|
||||
of_node_put(cpu_node);
|
||||
if (!valid)
|
||||
break;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* dt_init_idle_driver() - Parse the DT idle states and initialize the
|
||||
* idle driver states array
|
||||
* @drv: Pointer to CPU idle driver to be initialized
|
||||
* @matches: Array of of_device_id match structures to search in for
|
||||
* compatible idle state nodes. The data pointer for each valid
|
||||
* struct of_device_id entry in the matches array must point to
|
||||
* a function with the following signature, that corresponds to
|
||||
* the CPUidle state enter function signature:
|
||||
*
|
||||
* int (*)(struct cpuidle_device *dev,
|
||||
* struct cpuidle_driver *drv,
|
||||
* int index);
|
||||
*
|
||||
* @start_idx: First idle state index to be initialized
|
||||
*
|
||||
* If DT idle states are detected and are valid the state count and states
|
||||
* array entries in the cpuidle driver are initialized accordingly starting
|
||||
* from index start_idx.
|
||||
*
|
||||
* Return: number of valid DT idle states parsed, <0 on failure
|
||||
*/
|
||||
int dt_init_idle_driver(struct cpuidle_driver *drv,
|
||||
const struct of_device_id *matches,
|
||||
unsigned int start_idx)
|
||||
{
|
||||
struct cpuidle_state *idle_state;
|
||||
struct device_node *state_node, *cpu_node;
|
||||
int i, err = 0;
|
||||
const cpumask_t *cpumask;
|
||||
unsigned int state_idx = start_idx;
|
||||
|
||||
if (state_idx >= CPUIDLE_STATE_MAX)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* We get the idle states for the first logical cpu in the
|
||||
* driver mask (or cpu_possible_mask if the driver cpumask is not set)
|
||||
* and we check through idle_state_valid() if they are uniform
|
||||
* across CPUs, otherwise we hit a firmware misconfiguration.
|
||||
*/
|
||||
cpumask = drv->cpumask ? : cpu_possible_mask;
|
||||
cpu_node = of_cpu_device_node_get(cpumask_first(cpumask));
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
|
||||
if (!state_node)
|
||||
break;
|
||||
|
||||
if (!idle_state_valid(state_node, i, cpumask)) {
|
||||
pr_warn("%s idle state not valid, bailing out\n",
|
||||
state_node->full_name);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (state_idx == CPUIDLE_STATE_MAX) {
|
||||
pr_warn("State index reached static CPU idle driver states array size\n");
|
||||
break;
|
||||
}
|
||||
|
||||
idle_state = &drv->states[state_idx++];
|
||||
err = init_state_node(idle_state, matches, state_node);
|
||||
if (err) {
|
||||
pr_err("Parsing idle state node %s failed with err %d\n",
|
||||
state_node->full_name, err);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
of_node_put(state_node);
|
||||
}
|
||||
|
||||
of_node_put(state_node);
|
||||
of_node_put(cpu_node);
|
||||
if (err)
|
||||
return err;
|
||||
/*
|
||||
* Update the driver state count only if some valid DT idle states
|
||||
* were detected
|
||||
*/
|
||||
if (i)
|
||||
drv->state_count = state_idx;
|
||||
|
||||
/*
|
||||
* Return the number of present and valid DT idle states, which can
|
||||
* also be 0 on platforms with missing DT idle states or legacy DT
|
||||
* configuration predating the DT idle states bindings.
|
||||
*/
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dt_init_idle_driver);
|
7
drivers/cpuidle/dt_idle_states.h
Normal file
7
drivers/cpuidle/dt_idle_states.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#ifndef __DT_IDLE_STATES
|
||||
#define __DT_IDLE_STATES
|
||||
|
||||
int dt_init_idle_driver(struct cpuidle_driver *drv,
|
||||
const struct of_device_id *matches,
|
||||
unsigned int start_idx);
|
||||
#endif
|
|
@ -28,7 +28,7 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
|
|||
struct cpuidle_governor *gov;
|
||||
|
||||
list_for_each_entry(gov, &cpuidle_governors, governor_list)
|
||||
if (!strnicmp(str, gov->name, CPUIDLE_NAME_LEN))
|
||||
if (!strncasecmp(str, gov->name, CPUIDLE_NAME_LEN))
|
||||
return gov;
|
||||
|
||||
return NULL;
|
||||
|
|
|
@ -78,9 +78,8 @@ config ARM_EXYNOS4_BUS_DEVFREQ
|
|||
This does not yet operate with optimal voltages.
|
||||
|
||||
config ARM_EXYNOS5_BUS_DEVFREQ
|
||||
bool "ARM Exynos5250 Bus DEVFREQ Driver"
|
||||
tristate "ARM Exynos5250 Bus DEVFREQ Driver"
|
||||
depends on SOC_EXYNOS5250
|
||||
select ARCH_HAS_OPP
|
||||
select DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
select PM_OPP
|
||||
help
|
||||
|
|
|
@ -1119,6 +1119,7 @@ struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
|
|||
|
||||
return opp;
|
||||
}
|
||||
EXPORT_SYMBOL(devfreq_recommended_opp);
|
||||
|
||||
/**
|
||||
* devfreq_register_opp_notifier() - Helper function to get devfreq notified
|
||||
|
@ -1142,6 +1143,7 @@ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(devfreq_register_opp_notifier);
|
||||
|
||||
/**
|
||||
* devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq
|
||||
|
@ -1168,6 +1170,7 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(devfreq_unregister_opp_notifier);
|
||||
|
||||
static void devm_devfreq_opp_release(struct device *dev, void *res)
|
||||
{
|
||||
|
|
|
@ -73,6 +73,7 @@ void busfreq_mon_reset(struct busfreq_ppmu_data *ppmu_data)
|
|||
exynos_ppmu_start(ppmu_base);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(busfreq_mon_reset);
|
||||
|
||||
void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data)
|
||||
{
|
||||
|
@ -97,6 +98,7 @@ void exynos_read_ppmu(struct busfreq_ppmu_data *ppmu_data)
|
|||
|
||||
busfreq_mon_reset(ppmu_data);
|
||||
}
|
||||
EXPORT_SYMBOL(exynos_read_ppmu);
|
||||
|
||||
int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data)
|
||||
{
|
||||
|
@ -114,3 +116,4 @@ int exynos_get_busier_ppmu(struct busfreq_ppmu_data *ppmu_data)
|
|||
|
||||
return busy;
|
||||
}
|
||||
EXPORT_SYMBOL(exynos_get_busier_ppmu);
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include <linux/irqflags.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
@ -643,10 +644,13 @@ static int i2c_device_probe(struct device *dev)
|
|||
if (status < 0)
|
||||
return status;
|
||||
|
||||
acpi_dev_pm_attach(&client->dev, true);
|
||||
status = driver->probe(client, i2c_match_id(driver->id_table, client));
|
||||
if (status)
|
||||
acpi_dev_pm_detach(&client->dev, true);
|
||||
status = dev_pm_domain_attach(&client->dev, true);
|
||||
if (status != -EPROBE_DEFER) {
|
||||
status = driver->probe(client, i2c_match_id(driver->id_table,
|
||||
client));
|
||||
if (status)
|
||||
dev_pm_domain_detach(&client->dev, true);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -666,7 +670,7 @@ static int i2c_device_remove(struct device *dev)
|
|||
status = driver->remove(client);
|
||||
}
|
||||
|
||||
acpi_dev_pm_detach(&client->dev, true);
|
||||
dev_pm_domain_detach(&client->dev, true);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/mmc/card.h>
|
||||
|
@ -315,7 +316,7 @@ int sdio_add_func(struct sdio_func *func)
|
|||
ret = device_add(&func->dev);
|
||||
if (ret == 0) {
|
||||
sdio_func_set_present(func);
|
||||
acpi_dev_pm_attach(&func->dev, false);
|
||||
dev_pm_domain_attach(&func->dev, false);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -332,7 +333,7 @@ void sdio_remove_func(struct sdio_func *func)
|
|||
if (!sdio_func_present(func))
|
||||
return;
|
||||
|
||||
acpi_dev_pm_detach(&func->dev, false);
|
||||
dev_pm_domain_detach(&func->dev, false);
|
||||
device_del(&func->dev);
|
||||
put_device(&func->dev);
|
||||
}
|
||||
|
|
|
@ -41,11 +41,17 @@ static int __init pcie_pme_setup(char *str)
|
|||
}
|
||||
__setup("pcie_pme=", pcie_pme_setup);
|
||||
|
||||
enum pme_suspend_level {
|
||||
PME_SUSPEND_NONE = 0,
|
||||
PME_SUSPEND_WAKEUP,
|
||||
PME_SUSPEND_NOIRQ,
|
||||
};
|
||||
|
||||
struct pcie_pme_service_data {
|
||||
spinlock_t lock;
|
||||
struct pcie_device *srv;
|
||||
struct work_struct work;
|
||||
bool noirq; /* Don't enable the PME interrupt used by this service. */
|
||||
enum pme_suspend_level suspend_level;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -223,7 +229,7 @@ static void pcie_pme_work_fn(struct work_struct *work)
|
|||
spin_lock_irq(&data->lock);
|
||||
|
||||
for (;;) {
|
||||
if (data->noirq)
|
||||
if (data->suspend_level != PME_SUSPEND_NONE)
|
||||
break;
|
||||
|
||||
pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
|
||||
|
@ -250,7 +256,7 @@ static void pcie_pme_work_fn(struct work_struct *work)
|
|||
spin_lock_irq(&data->lock);
|
||||
}
|
||||
|
||||
if (!data->noirq)
|
||||
if (data->suspend_level == PME_SUSPEND_NONE)
|
||||
pcie_pme_interrupt_enable(port, true);
|
||||
|
||||
spin_unlock_irq(&data->lock);
|
||||
|
@ -367,6 +373,21 @@ static int pcie_pme_probe(struct pcie_device *srv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool pcie_pme_check_wakeup(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
|
||||
if (!bus)
|
||||
return false;
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list)
|
||||
if (device_may_wakeup(&dev->dev)
|
||||
|| pcie_pme_check_wakeup(dev->subordinate))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_pme_suspend - Suspend PCIe PME service device.
|
||||
* @srv: PCIe service device to suspend.
|
||||
|
@ -375,11 +396,26 @@ static int pcie_pme_suspend(struct pcie_device *srv)
|
|||
{
|
||||
struct pcie_pme_service_data *data = get_service_data(srv);
|
||||
struct pci_dev *port = srv->port;
|
||||
bool wakeup;
|
||||
|
||||
if (device_may_wakeup(&port->dev)) {
|
||||
wakeup = true;
|
||||
} else {
|
||||
down_read(&pci_bus_sem);
|
||||
wakeup = pcie_pme_check_wakeup(port->subordinate);
|
||||
up_read(&pci_bus_sem);
|
||||
}
|
||||
spin_lock_irq(&data->lock);
|
||||
pcie_pme_interrupt_enable(port, false);
|
||||
pcie_clear_root_pme_status(port);
|
||||
data->noirq = true;
|
||||
if (wakeup) {
|
||||
enable_irq_wake(srv->irq);
|
||||
data->suspend_level = PME_SUSPEND_WAKEUP;
|
||||
} else {
|
||||
struct pci_dev *port = srv->port;
|
||||
|
||||
pcie_pme_interrupt_enable(port, false);
|
||||
pcie_clear_root_pme_status(port);
|
||||
data->suspend_level = PME_SUSPEND_NOIRQ;
|
||||
}
|
||||
spin_unlock_irq(&data->lock);
|
||||
|
||||
synchronize_irq(srv->irq);
|
||||
|
@ -394,12 +430,17 @@ static int pcie_pme_suspend(struct pcie_device *srv)
|
|||
static int pcie_pme_resume(struct pcie_device *srv)
|
||||
{
|
||||
struct pcie_pme_service_data *data = get_service_data(srv);
|
||||
struct pci_dev *port = srv->port;
|
||||
|
||||
spin_lock_irq(&data->lock);
|
||||
data->noirq = false;
|
||||
pcie_clear_root_pme_status(port);
|
||||
pcie_pme_interrupt_enable(port, true);
|
||||
if (data->suspend_level == PME_SUSPEND_NOIRQ) {
|
||||
struct pci_dev *port = srv->port;
|
||||
|
||||
pcie_clear_root_pme_status(port);
|
||||
pcie_pme_interrupt_enable(port, true);
|
||||
} else {
|
||||
disable_irq_wake(srv->irq);
|
||||
}
|
||||
data->suspend_level = PME_SUSPEND_NONE;
|
||||
spin_unlock_irq(&data->lock);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1050,6 +1050,13 @@ static struct acpi_driver acpi_fujitsu_hotkey_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct acpi_device_id fujitsu_ids[] __used = {
|
||||
{ACPI_FUJITSU_HID, 0},
|
||||
{ACPI_FUJITSU_HOTKEY_HID, 0},
|
||||
{"", 0}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
|
||||
|
||||
static int __init fujitsu_init(void)
|
||||
{
|
||||
int ret, result, max_brightness;
|
||||
|
@ -1208,12 +1215,3 @@ MODULE_LICENSE("GPL");
|
|||
MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
|
||||
MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
|
||||
MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
|
||||
|
||||
static struct pnp_device_id pnp_ids[] __used = {
|
||||
{.id = "FUJ02bf"},
|
||||
{.id = "FUJ02B1"},
|
||||
{.id = "FUJ02E3"},
|
||||
{.id = ""}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pnp, pnp_ids);
|
||||
|
|
|
@ -10,3 +10,11 @@ menuconfig POWER_AVS
|
|||
AVS is also called SmartReflex on OMAP devices.
|
||||
|
||||
Say Y here to enable Adaptive Voltage Scaling class support.
|
||||
|
||||
config ROCKCHIP_IODOMAIN
|
||||
tristate "Rockchip IO domain support"
|
||||
depends on ARCH_ROCKCHIP && OF
|
||||
help
|
||||
Say y here to enable support io domains on Rockchip SoCs. It is
|
||||
necessary for the io domain setting of the SoC to match the
|
||||
voltage supplied by the regulators.
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o
|
||||
obj-$(CONFIG_ROCKCHIP_IODOMAIN) += rockchip-io-domain.o
|
||||
|
|
351
drivers/power/avs/rockchip-io-domain.c
Normal file
351
drivers/power/avs/rockchip-io-domain.c
Normal file
|
@ -0,0 +1,351 @@
|
|||
/*
|
||||
* Rockchip IO Voltage Domain driver
|
||||
*
|
||||
* Copyright 2014 MundoReader S.L.
|
||||
* Copyright 2014 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MAX_SUPPLIES 16
|
||||
|
||||
/*
|
||||
* The max voltage for 1.8V and 3.3V come from the Rockchip datasheet under
|
||||
* "Recommended Operating Conditions" for "Digital GPIO". When the typical
|
||||
* is 3.3V the max is 3.6V. When the typical is 1.8V the max is 1.98V.
|
||||
*
|
||||
* They are used like this:
|
||||
* - If the voltage on a rail is above the "1.8" voltage (1.98V) we'll tell the
|
||||
* SoC we're at 3.3.
|
||||
* - If the voltage on a rail is above the "3.3" voltage (3.6V) we'll consider
|
||||
* that to be an error.
|
||||
*/
|
||||
#define MAX_VOLTAGE_1_8 1980000
|
||||
#define MAX_VOLTAGE_3_3 3600000
|
||||
|
||||
#define RK3288_SOC_CON2 0x24c
|
||||
#define RK3288_SOC_CON2_FLASH0 BIT(7)
|
||||
#define RK3288_SOC_FLASH_SUPPLY_NUM 2
|
||||
|
||||
struct rockchip_iodomain;
|
||||
|
||||
/**
|
||||
* @supplies: voltage settings matching the register bits.
|
||||
*/
|
||||
struct rockchip_iodomain_soc_data {
|
||||
int grf_offset;
|
||||
const char *supply_names[MAX_SUPPLIES];
|
||||
void (*init)(struct rockchip_iodomain *iod);
|
||||
};
|
||||
|
||||
struct rockchip_iodomain_supply {
|
||||
struct rockchip_iodomain *iod;
|
||||
struct regulator *reg;
|
||||
struct notifier_block nb;
|
||||
int idx;
|
||||
};
|
||||
|
||||
struct rockchip_iodomain {
|
||||
struct device *dev;
|
||||
struct regmap *grf;
|
||||
struct rockchip_iodomain_soc_data *soc_data;
|
||||
struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
|
||||
};
|
||||
|
||||
static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
|
||||
int uV)
|
||||
{
|
||||
struct rockchip_iodomain *iod = supply->iod;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* set value bit */
|
||||
val = (uV > MAX_VOLTAGE_1_8) ? 0 : 1;
|
||||
val <<= supply->idx;
|
||||
|
||||
/* apply hiword-mask */
|
||||
val |= (BIT(supply->idx) << 16);
|
||||
|
||||
ret = regmap_write(iod->grf, iod->soc_data->grf_offset, val);
|
||||
if (ret)
|
||||
dev_err(iod->dev, "Couldn't write to GRF\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_iodomain_notify(struct notifier_block *nb,
|
||||
unsigned long event,
|
||||
void *data)
|
||||
{
|
||||
struct rockchip_iodomain_supply *supply =
|
||||
container_of(nb, struct rockchip_iodomain_supply, nb);
|
||||
int uV;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* According to Rockchip it's important to keep the SoC IO domain
|
||||
* higher than (or equal to) the external voltage. That means we need
|
||||
* to change it before external voltage changes happen in the case
|
||||
* of an increase.
|
||||
*
|
||||
* Note that in the "pre" change we pick the max possible voltage that
|
||||
* the regulator might end up at (the client requests a range and we
|
||||
* don't know for certain the exact voltage). Right now we rely on the
|
||||
* slop in MAX_VOLTAGE_1_8 and MAX_VOLTAGE_3_3 to save us if clients
|
||||
* request something like a max of 3.6V when they really want 3.3V.
|
||||
* We could attempt to come up with better rules if this fails.
|
||||
*/
|
||||
if (event & REGULATOR_EVENT_PRE_VOLTAGE_CHANGE) {
|
||||
struct pre_voltage_change_data *pvc_data = data;
|
||||
|
||||
uV = max_t(unsigned long, pvc_data->old_uV, pvc_data->max_uV);
|
||||
} else if (event & (REGULATOR_EVENT_VOLTAGE_CHANGE |
|
||||
REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE)) {
|
||||
uV = (unsigned long)data;
|
||||
} else {
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
dev_dbg(supply->iod->dev, "Setting to %d\n", uV);
|
||||
|
||||
if (uV > MAX_VOLTAGE_3_3) {
|
||||
dev_err(supply->iod->dev, "Voltage too high: %d\n", uV);
|
||||
|
||||
if (event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
|
||||
return NOTIFY_BAD;
|
||||
}
|
||||
|
||||
ret = rockchip_iodomain_write(supply, uV);
|
||||
if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
|
||||
return NOTIFY_BAD;
|
||||
|
||||
dev_info(supply->iod->dev, "Setting to %d done\n", uV);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void rk3288_iodomain_init(struct rockchip_iodomain *iod)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
/* if no flash supply we should leave things alone */
|
||||
if (!iod->supplies[RK3288_SOC_FLASH_SUPPLY_NUM].reg)
|
||||
return;
|
||||
|
||||
/*
|
||||
* set flash0 iodomain to also use this framework
|
||||
* instead of a special gpio.
|
||||
*/
|
||||
val = RK3288_SOC_CON2_FLASH0 | (RK3288_SOC_CON2_FLASH0 << 16);
|
||||
ret = regmap_write(iod->grf, RK3288_SOC_CON2, val);
|
||||
if (ret < 0)
|
||||
dev_warn(iod->dev, "couldn't update flash0 ctrl\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* On the rk3188 the io-domains are handled by a shared register with the
|
||||
* lower 8 bits being still being continuing drive-strength settings.
|
||||
*/
|
||||
static const struct rockchip_iodomain_soc_data soc_data_rk3188 = {
|
||||
.grf_offset = 0x104,
|
||||
.supply_names = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
"ap0",
|
||||
"ap1",
|
||||
"cif",
|
||||
"flash",
|
||||
"vccio0",
|
||||
"vccio1",
|
||||
"lcdc0",
|
||||
"lcdc1",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rockchip_iodomain_soc_data soc_data_rk3288 = {
|
||||
.grf_offset = 0x380,
|
||||
.supply_names = {
|
||||
"lcdc", /* LCDC_VDD */
|
||||
"dvp", /* DVPIO_VDD */
|
||||
"flash0", /* FLASH0_VDD (emmc) */
|
||||
"flash1", /* FLASH1_VDD (sdio1) */
|
||||
"wifi", /* APIO3_VDD (sdio0) */
|
||||
"bb", /* APIO5_VDD */
|
||||
"audio", /* APIO4_VDD */
|
||||
"sdcard", /* SDMMC0_VDD (sdmmc) */
|
||||
"gpio30", /* APIO1_VDD */
|
||||
"gpio1830", /* APIO2_VDD */
|
||||
},
|
||||
.init = rk3288_iodomain_init,
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_iodomain_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3188-io-voltage-domain",
|
||||
.data = (void *)&soc_data_rk3188
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3288-io-voltage-domain",
|
||||
.data = (void *)&soc_data_rk3288
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static int rockchip_iodomain_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
const struct of_device_id *match;
|
||||
struct rockchip_iodomain *iod;
|
||||
int i, ret = 0;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
iod = devm_kzalloc(&pdev->dev, sizeof(*iod), GFP_KERNEL);
|
||||
if (!iod)
|
||||
return -ENOMEM;
|
||||
|
||||
iod->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, iod);
|
||||
|
||||
match = of_match_node(rockchip_iodomain_match, np);
|
||||
iod->soc_data = (struct rockchip_iodomain_soc_data *)match->data;
|
||||
|
||||
iod->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
|
||||
if (IS_ERR(iod->grf)) {
|
||||
dev_err(&pdev->dev, "couldn't find grf regmap\n");
|
||||
return PTR_ERR(iod->grf);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_SUPPLIES; i++) {
|
||||
const char *supply_name = iod->soc_data->supply_names[i];
|
||||
struct rockchip_iodomain_supply *supply = &iod->supplies[i];
|
||||
struct regulator *reg;
|
||||
int uV;
|
||||
|
||||
if (!supply_name)
|
||||
continue;
|
||||
|
||||
reg = devm_regulator_get_optional(iod->dev, supply_name);
|
||||
if (IS_ERR(reg)) {
|
||||
ret = PTR_ERR(reg);
|
||||
|
||||
/* If a supply wasn't specified, that's OK */
|
||||
if (ret == -ENODEV)
|
||||
continue;
|
||||
else if (ret != -EPROBE_DEFER)
|
||||
dev_err(iod->dev, "couldn't get regulator %s\n",
|
||||
supply_name);
|
||||
goto unreg_notify;
|
||||
}
|
||||
|
||||
/* set initial correct value */
|
||||
uV = regulator_get_voltage(reg);
|
||||
|
||||
/* must be a regulator we can get the voltage of */
|
||||
if (uV < 0) {
|
||||
dev_err(iod->dev, "Can't determine voltage: %s\n",
|
||||
supply_name);
|
||||
goto unreg_notify;
|
||||
}
|
||||
|
||||
if (uV > MAX_VOLTAGE_3_3) {
|
||||
dev_crit(iod->dev,
|
||||
"%d uV is too high. May damage SoC!\n",
|
||||
uV);
|
||||
ret = -EINVAL;
|
||||
goto unreg_notify;
|
||||
}
|
||||
|
||||
/* setup our supply */
|
||||
supply->idx = i;
|
||||
supply->iod = iod;
|
||||
supply->reg = reg;
|
||||
supply->nb.notifier_call = rockchip_iodomain_notify;
|
||||
|
||||
ret = rockchip_iodomain_write(supply, uV);
|
||||
if (ret) {
|
||||
supply->reg = NULL;
|
||||
goto unreg_notify;
|
||||
}
|
||||
|
||||
/* register regulator notifier */
|
||||
ret = regulator_register_notifier(reg, &supply->nb);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"regulator notifier request failed\n");
|
||||
supply->reg = NULL;
|
||||
goto unreg_notify;
|
||||
}
|
||||
}
|
||||
|
||||
if (iod->soc_data->init)
|
||||
iod->soc_data->init(iod);
|
||||
|
||||
return 0;
|
||||
|
||||
unreg_notify:
|
||||
for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
|
||||
struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
|
||||
|
||||
if (io_supply->reg)
|
||||
regulator_unregister_notifier(io_supply->reg,
|
||||
&io_supply->nb);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_iodomain_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rockchip_iodomain *iod = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
for (i = MAX_SUPPLIES - 1; i >= 0; i--) {
|
||||
struct rockchip_iodomain_supply *io_supply = &iod->supplies[i];
|
||||
|
||||
if (io_supply->reg)
|
||||
regulator_unregister_notifier(io_supply->reg,
|
||||
&io_supply->nb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rockchip_iodomain_driver = {
|
||||
.probe = rockchip_iodomain_probe,
|
||||
.remove = rockchip_iodomain_remove,
|
||||
.driver = {
|
||||
.name = "rockchip-iodomain",
|
||||
.of_match_table = rockchip_iodomain_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rockchip_iodomain_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Rockchip IO-domain driver");
|
||||
MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
|
||||
MODULE_AUTHOR("Doug Anderson <dianders@chromium.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -75,8 +75,6 @@ static struct pm_clk_notifier_block platform_bus_notifier = {
|
|||
.con_ids = { NULL, },
|
||||
};
|
||||
|
||||
static bool default_pm_on;
|
||||
|
||||
static int __init sh_pm_runtime_init(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ARCH_SHMOBILE_MULTI)) {
|
||||
|
@ -96,16 +94,7 @@ static int __init sh_pm_runtime_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
default_pm_on = true;
|
||||
pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
|
||||
return 0;
|
||||
}
|
||||
core_initcall(sh_pm_runtime_init);
|
||||
|
||||
static int __init sh_pm_runtime_late_init(void)
|
||||
{
|
||||
if (default_pm_on)
|
||||
pm_genpd_poweroff_unused();
|
||||
return 0;
|
||||
}
|
||||
late_initcall(sh_pm_runtime_late_init);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/spi/spi.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/sched/rt.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -264,10 +265,12 @@ static int spi_drv_probe(struct device *dev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
acpi_dev_pm_attach(dev, true);
|
||||
ret = sdrv->probe(to_spi_device(dev));
|
||||
if (ret)
|
||||
acpi_dev_pm_detach(dev, true);
|
||||
ret = dev_pm_domain_attach(dev, true);
|
||||
if (ret != -EPROBE_DEFER) {
|
||||
ret = sdrv->probe(to_spi_device(dev));
|
||||
if (ret)
|
||||
dev_pm_domain_detach(dev, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -278,7 +281,7 @@ static int spi_drv_remove(struct device *dev)
|
|||
int ret;
|
||||
|
||||
ret = sdrv->remove(to_spi_device(dev));
|
||||
acpi_dev_pm_detach(dev, true);
|
||||
dev_pm_domain_detach(dev, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,10 @@
|
|||
#define METHOD_NAME__PRS "_PRS"
|
||||
#define METHOD_NAME__PRT "_PRT"
|
||||
#define METHOD_NAME__PRW "_PRW"
|
||||
#define METHOD_NAME__PS0 "_PS0"
|
||||
#define METHOD_NAME__PS1 "_PS1"
|
||||
#define METHOD_NAME__PS2 "_PS2"
|
||||
#define METHOD_NAME__PS3 "_PS3"
|
||||
#define METHOD_NAME__REG "_REG"
|
||||
#define METHOD_NAME__SB_ "_SB_"
|
||||
#define METHOD_NAME__SEG "_SEG"
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
/* Current ACPICA subsystem version in YYYYMMDD format */
|
||||
|
||||
#define ACPI_CA_VERSION 0x20140724
|
||||
#define ACPI_CA_VERSION 0x20140828
|
||||
|
||||
#include <acpi/acconfig.h>
|
||||
#include <acpi/actypes.h>
|
||||
|
@ -692,6 +692,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
|
|||
*event_status))
|
||||
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void))
|
||||
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void))
|
||||
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void))
|
||||
|
||||
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
|
||||
acpi_get_gpe_device(u32 gpe_index,
|
||||
|
|
|
@ -952,7 +952,8 @@ enum acpi_srat_type {
|
|||
ACPI_SRAT_TYPE_CPU_AFFINITY = 0,
|
||||
ACPI_SRAT_TYPE_MEMORY_AFFINITY = 1,
|
||||
ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY = 2,
|
||||
ACPI_SRAT_TYPE_RESERVED = 3 /* 3 and greater are reserved */
|
||||
ACPI_SRAT_TYPE_GICC_AFFINITY = 3,
|
||||
ACPI_SRAT_TYPE_RESERVED = 4 /* 4 and greater are reserved */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -968,7 +969,7 @@ struct acpi_srat_cpu_affinity {
|
|||
u32 flags;
|
||||
u8 local_sapic_eid;
|
||||
u8 proximity_domain_hi[3];
|
||||
u32 reserved; /* Reserved, must be zero */
|
||||
u32 clock_domain;
|
||||
};
|
||||
|
||||
/* Flags */
|
||||
|
@ -1010,6 +1011,20 @@ struct acpi_srat_x2apic_cpu_affinity {
|
|||
|
||||
#define ACPI_SRAT_CPU_ENABLED (1) /* 00: Use affinity structure */
|
||||
|
||||
/* 3: GICC Affinity (ACPI 5.1) */
|
||||
|
||||
struct acpi_srat_gicc_affinity {
|
||||
struct acpi_subtable_header header;
|
||||
u32 proximity_domain;
|
||||
u32 acpi_processor_uid;
|
||||
u32 flags;
|
||||
u32 clock_domain;
|
||||
};
|
||||
|
||||
/* Flags for struct acpi_srat_gicc_affinity */
|
||||
|
||||
#define ACPI_SRAT_GICC_ENABLED (1) /* 00: Use affinity structure */
|
||||
|
||||
/* Reset to default packing */
|
||||
|
||||
#pragma pack()
|
||||
|
|
|
@ -310,10 +310,15 @@ struct acpi_gtdt_timer_entry {
|
|||
u32 common_flags;
|
||||
};
|
||||
|
||||
/* Flag Definitions: timer_flags and virtual_timer_flags above */
|
||||
|
||||
#define ACPI_GTDT_GT_IRQ_MODE (1)
|
||||
#define ACPI_GTDT_GT_IRQ_POLARITY (1<<1)
|
||||
|
||||
/* Flag Definitions: common_flags above */
|
||||
|
||||
#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
|
||||
#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
|
||||
#define ACPI_GTDT_GT_IS_SECURE_TIMER (1)
|
||||
#define ACPI_GTDT_GT_ALWAYS_ON (1<<1)
|
||||
|
||||
/* 1: SBSA Generic Watchdog Structure */
|
||||
|
||||
|
|
|
@ -587,7 +587,6 @@ static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
|
|||
#if defined(CONFIG_ACPI) && defined(CONFIG_PM)
|
||||
struct acpi_device *acpi_dev_pm_get_node(struct device *dev);
|
||||
int acpi_dev_pm_attach(struct device *dev, bool power_on);
|
||||
void acpi_dev_pm_detach(struct device *dev, bool power_off);
|
||||
#else
|
||||
static inline struct acpi_device *acpi_dev_pm_get_node(struct device *dev)
|
||||
{
|
||||
|
@ -597,7 +596,6 @@ static inline int acpi_dev_pm_attach(struct device *dev, bool power_on)
|
|||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline void acpi_dev_pm_detach(struct device *dev, bool power_off) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
|
|
@ -112,6 +112,9 @@ struct cpufreq_policy {
|
|||
spinlock_t transition_lock;
|
||||
wait_queue_head_t transition_wait;
|
||||
struct task_struct *transition_task; /* Task which is doing the transition */
|
||||
|
||||
/* For cpufreq driver's internal use */
|
||||
void *driver_data;
|
||||
};
|
||||
|
||||
/* Only for ACPI */
|
||||
|
|
|
@ -193,11 +193,6 @@ extern void irq_wake_thread(unsigned int irq, void *dev_id);
|
|||
/* The following three functions are for the core kernel use only. */
|
||||
extern void suspend_device_irqs(void);
|
||||
extern void resume_device_irqs(void);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
extern int check_wakeup_irqs(void);
|
||||
#else
|
||||
static inline int check_wakeup_irqs(void) { return 0; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct irq_affinity_notify - context for notification of IRQ affinity changes
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user