Pull power management and ACPI updates from Rafael Wysocki:
"This time the total number of ACPI commits is slightly greater than
the number of cpufreq commits, but Viresh Kumar (who works on cpufreq)
remains the most active patch submitter.
To me, the most significant change is the addition of offline/online
device operations to the driver core (with the Greg's blessing) and
the related modifications of the ACPI core hotplug code. Next are the
freezer updates from Colin Cross that should make the freezing of
tasks a bit less heavy weight.
We also have a couple of regression fixes, a number of fixes for
issues that have not been identified as regressions, two new drivers
and a bunch of cleanups all over.
Highlights:
- Hotplug changes to support graceful hot-removal failures.
It sometimes is necessary to fail device hot-removal operations
gracefully if they cannot be carried out completely. For example,
if memory from a memory module being hot-removed has been allocated
for the kernel's own use and cannot be moved elsewhere, it's
desirable to fail the hot-removal operation in a graceful way
rather than to crash the kernel, but currenty a success or a kernel
crash are the only possible outcomes of an attempted memory
hot-removal. Needless to say, that is not a very attractive
alternative and it had to be addressed.
However, in order to make it work for memory, I first had to make
it work for CPUs and for this purpose I needed to modify the ACPI
processor driver. It's been split into two parts, a resident one
handling the low-level initialization/cleanup and a modular one
playing the actual driver's role (but it binds to the CPU system
device objects rather than to the ACPI device objects representing
processors). That's been sort of like a live brain surgery on a
patient who's riding a bike.
So this is a little scary, but since we found and fixed a couple of
regressions it caused to happen during the early linux-next testing
(a month ago), nobody has complained.
As a bonus we remove some duplicated ACPI hotplug code, because the
ACPI-based CPU hotplug is now going to use the common ACPI hotplug
code.
- Lighter weight freezing of tasks.
These changes from Colin Cross and Mandeep Singh Baines are
targeted at making the freezing of tasks a bit less heavy weight
operation. They reduce the number of tasks woken up every time
during the freezing, by using the observation that the freezer
simply doesn't need to wake up some of them and wait for them all
to call refrigerator(). The time needed for the freezer to decide
to report a failure is reduced too.
Also reintroduced is the check causing a lockdep warining to
trigger when try_to_freeze() is called with locks held (which is
generally unsafe and shouldn't happen).
- cpufreq updates
First off, a commit from Srivatsa S Bhat fixes a resume regression
introduced during the 3.10 cycle causing some cpufreq sysfs
attributes to return wrong values to user space after resume. The
fix is kind of fresh, but also it's pretty obvious once Srivatsa
has identified the root cause.
Second, we have a new freqdomain_cpus sysfs attribute for the
acpi-cpufreq driver to provide information previously available via
related_cpus. From Lan Tianyu.
Finally, we fix a number of issues, mostly related to the
CPUFREQ_POSTCHANGE notifier and cpufreq Kconfig options and clean
up some code. The majority of changes from Viresh Kumar with bits
from Jacob Shin, Heiko Stübner, Xiaoguang Chen, Ezequiel Garcia,
Arnd Bergmann, and Tang Yuantian.
- ACPICA update
A usual bunch of updates from the ACPICA upstream.
During the 3.4 cycle we introduced support for ACPI 5 extended
sleep registers, but they are only supposed to be used if the
HW-reduced mode bit is set in the FADT flags and the code attempted
to use them without checking that bit. That caused suspend/resume
regressions to happen on some systems. Fix from Lv Zheng causes
those registers to be used only if the HW-reduced mode bit is set.
Apart from this some other ACPICA bugs are fixed and code cleanups
are made by Bob Moore, Tomasz Nowicki, Lv Zheng, Chao Guan, and
Zhang Rui.
- cpuidle updates
New driver for Xilinx Zynq processors is added by Michal Simek.
Multidriver support simplification, addition of some missing
kerneldoc comments and Kconfig-related fixes come from Daniel
Lezcano.
- ACPI power management updates
Changes to make suspend/resume work correctly in Xen guests from
Konrad Rzeszutek Wilk, sparse warning fix from Fengguang Wu and
cleanups and fixes of the ACPI device power state selection
routine.
- ACPI documentation updates
Some previously missing pieces of ACPI documentation are added by
Lv Zheng and Aaron Lu (hopefully, that will help people to
uderstand how the ACPI subsystem works) and one outdated doc is
updated by Hanjun Guo.
- Assorted ACPI updates
We finally nailed down the IA-64 issue that was the reason for
reverting commit
9f29ab11ddbf ("ACPI / scan: do not match drivers
against objects having scan handlers"), so we can fix it and move
the ACPI scan handler check added to the ACPI video driver back to
the core.
A mechanism for adding CMOS RTC address space handlers is
introduced by Lan Tianyu to allow some EC-related breakage to be
fixed on some systems.
A spec-compliant implementation of acpi_os_get_timer() is added by
Mika Westerberg.
The evaluation of _STA is added to do_acpi_find_child() to avoid
situations in which a pointer to a disabled device object is
returned instead of an enabled one with the same _ADR value. From
Jeff Wu.
Intel BayTrail PCH (Platform Controller Hub) support is added to
the ACPI driver for Intel Low-Power Subsystems (LPSS) and that
driver is modified to work around a couple of known BIOS issues.
Changes from Mika Westerberg and Heikki Krogerus.
The EC driver is fixed by Vasiliy Kulikov to use get_user() and
put_user() instead of dereferencing user space pointers blindly.
Code cleanups are made by Bjorn Helgaas, Nicholas Mazzuca and Toshi
Kani.
- Assorted power management updates
The "runtime idle" helper routine is changed to take the return
values of the callbacks executed by it into account and to call
rpm_suspend() if they return 0, which allows us to reduce the
overall code bloat a bit (by dropping some code that's not
necessary any more after that modification).
The runtime PM documentation is updated by Alan Stern (to reflect
the "runtime idle" behavior change).
New trace points for PM QoS are added by Sahara
(<
[email protected]>).
PM QoS documentation is updated by Lan Tianyu.
Code cleanups are made and minor issues are addressed by Bernie
Thompson, Bjorn Helgaas, Julius Werner, and Shuah Khan.
- devfreq updates
New driver for the Exynos5-bus device from Abhilash Kesavan.
Minor cleanups, fixes and MAINTAINERS update from MyungJoo Ham,
Abhilash Kesavan, Paul Bolle, Rajagopal Venkat, and Wei Yongjun.
- OMAP power management updates
Adaptive Voltage Scaling (AVS) SmartReflex voltage control driver
updates from Andrii Tseglytskyi and Nishanth Menon."
* tag 'pm+acpi-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (162 commits)
cpufreq: Fix cpufreq regression after suspend/resume
ACPI / PM: Fix possible NULL pointer deref in acpi_pm_device_sleep_state()
PM / Sleep: Warn about system time after resume with pm_trace
cpufreq: don't leave stale policy pointer in cdbs->cur_policy
acpi-cpufreq: Add new sysfs attribute freqdomain_cpus
cpufreq: make sure frequency transitions are serialized
ACPI: implement acpi_os_get_timer() according the spec
ACPI / EC: Add HP Folio 13 to ec_dmi_table in order to skip DSDT scan
ACPI: Add CMOS RTC Operation Region handler support
ACPI / processor: Drop unused variable from processor_perflib.c
cpufreq: tegra: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: s3c64xx: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: omap: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: imx6q: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: exynos: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: dbx500: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: davinci: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: arm-big-little: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: powernow-k8: call CPUFREQ_POSTCHANGE notfier in error cases
cpufreq: pcc: call CPUFREQ_POSTCHANGE notfier in error cases
...
"Processor type and Features" -> Support for Hotpluggable CPUs
-Make sure that you have CONFIG_HOTPLUG, and CONFIG_SMP turned on as well.
+Make sure that you have CONFIG_SMP turned on as well.
You would need to enable CONFIG_HOTPLUG_CPU for SMP suspend/resume support
as well.
CPUs in MADT as hotpluggable CPUS. In the case there are no disabled CPUS
we assume 1/2 the number of CPUs currently present can be hotplugged.
- Caveat: Today's ACPI MADT can only provide 256 entries since the apicid field
- in MADT is only 8 bits.
+ Caveat: ACPI MADT can only provide 256 entries in systems with only ACPI 2.0c
+ or earlier ACPI version supported, because the apicid field in MADT is only
+ 8 bits. From ACPI 3.0, this limitation was removed since the apicid field
+ was extended to 32 bits with x2APIC introduced.
User Space Notification
The builtin appraise policy appraises all files
owned by uid=0.
- ima_audit= [IMA]
- Format: { "0" | "1" }
- 0 -- integrity auditing messages. (Default)
- 1 -- enable informational integrity auditing messages.
-
ima_hash= [IMA]
Format: { "sha1" | "md5" }
default: "sha1"
inport.irq= [HW] Inport (ATI XL and Microsoft) busmouse driver
Format: <irq>
+ int_pln_enable [x86] Enable power limit notification interrupt
+
+ integrity_audit=[IMA]
+ Format: { "0" | "1" }
+ 0 -- basic integrity auditing messages. (Default)
+ 1 -- additional integrity auditing messages.
+
intel_iommu= [DMAR] Intel IOMMU driver (DMAR) option
on
Enable intel iommu driver.
video= [FB] Frame buffer configuration
See Documentation/fb/modedb.txt.
+ video.brightness_switch_enabled= [0,1]
+ If set to 1, on receiving an ACPI notify event
+ generated by hotkey, video driver will adjust brightness
+ level and then send out the event to user space through
+ the allocated input device; If set to 0, video driver
+ will only send out the event without touching backlight
+ brightness level.
+ default: 1
+
virtio_mmio.device=
[VMMIO] Memory mapped virtio (platform) device.
that this also can be controlled per-workqueue for
workqueues visible under /sys/bus/workqueue/.
+ workqueue.power_efficient
+ Per-cpu workqueues are generally preferred because
+ they show better performance thanks to cache
+ locality; unfortunately, per-cpu workqueues tend to
+ be more power hungry than unbound workqueues.
+
+ Enabling this makes the per-cpu workqueues which
+ were observed to contribute significantly to power
+ consumption unbound, leading to measurably lower
+ power usage at the cost of small performance
+ overhead.
+
+ The default value of this parameter is determined by
+ the config option CONFIG_WQ_POWER_EFFICIENT_DEFAULT.
+
x2apic_phys [X86-64,APIC] Use x2apic physical mode instead of
default x2apic cluster mode on platforms
supporting x2apic.
F: drivers/pnp/pnpacpi/
F: include/linux/acpi.h
F: include/acpi/
+ F: Documentation/acpi
+ F: Documentation/ABI/testing/sysfs-bus-acpi
ACPI FAN DRIVER
ARM/CSR SIRFPRIMA2 MACHINE SUPPORT
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/baohua/linux.git
S: Maintained
F: arch/arm/mach-prima2/
F: drivers/dma/sirf-dma.c
S: Maintained
F: arch/arm/mach-s5p*/
F: arch/arm/mach-exynos*/
+N: exynos
ARM/SAMSUNG MOBILE MACHINE SUPPORT
S: Maintained
F: drivers/clk/socfpga/
+ARM/STI ARCHITECTURE
+W: http://www.stlinux.com
+S: Maintained
+F: arch/arm/mach-sti/
+
ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
T: git git://git.xilinx.com/linux-xlnx.git
S: Supported
F: arch/arm/mach-zynq/
+ F: drivers/cpuidle/cpuidle-zynq.c
ARM64 PORT (AARCH64 ARCHITECTURE)
T: git git://git.linaro.org/people/mturquette/linux.git
S: Maintained
-F: drivers/clk/clk.c
-F: drivers/clk/clk-*
+F: drivers/clk/
+X: drivers/clk/clkdev.c
F: include/linux/clk-pr*
+F: include/linux/clk/
COMMON INTERNET FILE SYSTEM (CIFS)
S: Maintained
- T: git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
+ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
+ T: git git://git.linaro.org/people/vireshk/linux.git (For ARM Updates)
F: drivers/cpufreq/
F: include/linux/cpufreq.h
DEVICE FREQUENCY (DEVFREQ)
- L: linux-kernel@vger.kernel.org
+ L: linux-pm@vger.kernel.org
S: Maintained
F: drivers/devfreq/
FCOE SUBSYSTEM (libfc, libfcoe, fcoe)
W: www.Open-FCoE.org
S: Supported
F: drivers/scsi/libfc/
S: Odd fixes
F: drivers/block/floppy.c
+FMC SUBSYSTEM
+W: http://www.ohwr.org/projects/fmc-bus
+S: Supported
+F: drivers/fmc/
+F: include/linux/fmc*.h
+F: include/linux/ipmi-fru.h
+K: fmc_d.*register
+
FPU EMULATOR
W: http://floatingpoint.sourceforge.net/emulator/index.html
S: Maintained
T: git git://git.secretlab.ca/git/linux-2.6.git
F: Documentation/gpio.txt
F: drivers/gpio/
F: arch/ia64/
IBM Power in-Nest Crypto Acceleration
S: Supported
F: drivers/crypto/nx/
INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
S: Supported
F: security/integrity/ima/
F: include/linux/jbd2.h
JSM Neo PCI based serial card
-M: Lucas Tavares <lucaskt@linux.vnet.ibm.com>
+M: Thadeu Lima de Souza Cascardo <cascardo@linux.vnet.ibm.com>
S: Maintained
F: drivers/tty/serial/jsm/
F: drivers/s390/kvm/
KERNEL VIRTUAL MACHINE (KVM) FOR ARM
W: http://systems.cs.columbia.edu/projects/kvm-arm
-S: Maintained
+S: Supported
F: arch/arm/include/uapi/asm/kvm*
F: arch/arm/include/asm/kvm*
F: arch/arm/kvm/
+KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64)
+S: Maintained
+F: arch/arm64/include/uapi/asm/kvm*
+F: arch/arm64/include/asm/kvm*
+F: arch/arm64/kvm/
+
KEXEC
W: http://kernel.org/pub/linux/utils/kernel/kexec/
S: Maintained
F: drivers/pinctrl/pinctrl-at91.c
+PIN CONTROLLER - SAMSUNG
+S: Maintained
+F: drivers/pinctrl/pinctrl-exynos.*
+F: drivers/pinctrl/pinctrl-s3c*
+F: drivers/pinctrl/pinctrl-samsung.*
+
PIN CONTROLLER - ST SPEAR
S: Supported
+F: Documentation/stable_kernel_rules.txt
STAGING SUBSYSTEM
STAGING - SPEAKUP CONSOLE SPEECH DRIVER
-M: Kirk Reiser <kirk@braille.uwo.ca>
+M: Kirk Reiser <kirk@reisers.ca>
W: http://www.linux-speakup.org/
F: drivers/media/usb/tm6000/
TPM DEVICE DRIVER
W: http://tpmdd.sourceforge.net
F: arch/arm/xen/
F: arch/arm/include/asm/xen/
+XEN HYPERVISOR ARM64
+S: Supported
+F: arch/arm64/xen/
+F: arch/arm64/include/asm/xen/
+
XEN NETWORK BACKEND DRIVER
r->name = dev_name(&pdev->dev);
}
- if (of_get_property(node, "ti,no_idle_on_suspend", NULL))
- omap_device_disable_idle_on_suspend(pdev);
-
pdev->dev.pm_domain = &omap_device_pm_domain;
odbfd_exit1:
return ret;
}
- static int _od_runtime_idle(struct device *dev)
- {
- return pm_generic_runtime_idle(dev);
- }
-
static int _od_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
if (!ret && !pm_runtime_status_suspended(dev)) {
if (pm_generic_runtime_suspend(dev) == 0) {
- if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND))
- omap_device_idle(pdev);
+ omap_device_idle(pdev);
od->flags |= OMAP_DEVICE_SUSPENDED;
}
}
if ((od->flags & OMAP_DEVICE_SUSPENDED) &&
!pm_runtime_status_suspended(dev)) {
od->flags &= ~OMAP_DEVICE_SUSPENDED;
- if (!(od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND))
- omap_device_enable(pdev);
+ omap_device_enable(pdev);
pm_generic_runtime_resume(dev);
}
struct dev_pm_domain omap_device_pm_domain = {
.ops = {
SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
- _od_runtime_idle)
+ NULL)
USE_PLATFORM_PM_SLEEP_OPS
.suspend_noirq = _od_suspend_noirq,
.resume_noirq = _od_resume_noirq,
select ARM_ERRATA_754327 if SMP
select ARM_ERRATA_764369 if SMP
select ARM_GIC
- select CPU_FREQ_TABLE if CPU_FREQ
select CPU_V7
select PINCTRL
select PINCTRL_TEGRA20
select ARM_ERRATA_754322
select ARM_ERRATA_764369 if SMP
select ARM_GIC
- select CPU_FREQ_TABLE if CPU_FREQ
select CPU_V7
select PINCTRL
select PINCTRL_TEGRA30
config ARCH_TEGRA_114_SOC
bool "Enable support for Tegra114 family"
- select ARM_ARCH_TIMER
+ select HAVE_ARM_ARCH_TIMER
select ARM_GIC
select ARM_L1_CACHE_SHIFT_6
- select CPU_FREQ_TABLE if CPU_FREQ
select CPU_V7
select PINCTRL
select PINCTRL_TEGRA114
extern struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void);
extern struct s3c_iotimings *s3c_cpufreq_getiotimings(void);
-#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS
#define s3c_cpufreq_debugfs_call(x) x
#else
#define s3c_cpufreq_debugfs_call(x) NULL
#define s3c2412_iotiming_set NULL
#endif /* CONFIG_S3C2412_IOTIMING */
-#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUG
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ_DEBUG
#define s3c_freq_dbg(x...) printk(KERN_INFO x)
#else
#define s3c_freq_dbg(x...) do { if (0) printk(x); } while (0)
-#endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUG */
+#endif /* CONFIG_ARM_S3C24XX_CPUFREQ_DEBUG */
-#ifdef CONFIG_CPU_FREQ_S3C24XX_IODEBUG
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ_IODEBUG
#define s3c_freq_iodbg(x...) printk(KERN_INFO x)
#else
#define s3c_freq_iodbg(x...) do { if (0) printk(x); } while (0)
-#endif /* CONFIG_CPU_FREQ_S3C24XX_IODEBUG */
+#endif /* CONFIG_ARM_S3C24XX_CPUFREQ_IODEBUG */
static inline int s3c_cpufreq_addfreq(struct cpufreq_frequency_table *table,
int index, size_t table_size,
s3c_freq_dbg("%s: { %d = %u kHz }\n",
__func__, index, freq);
- table[index].index = index;
+ table[index].driver_data = index;
table[index].frequency = freq;
}
config ETRAXFS
bool "ETRAX-FS-V32"
+ select CPU_FREQ_TABLE if CPU_FREQ
help
Support CRIS V32.
config CRIS_MACH_ARTPEC3
bool "ARTPEC-3"
+ select CPU_FREQ_TABLE if CPU_FREQ
help
Support Axis ARTPEC-3.
endmenu
-source "drivers/base/Kconfig"
-
-# standard linux drivers
-source "drivers/mtd/Kconfig"
-
-source "drivers/parport/Kconfig"
-
-source "drivers/pnp/Kconfig"
-
-source "drivers/block/Kconfig"
-
-source "drivers/ide/Kconfig"
-
-source "drivers/net/Kconfig"
-
-source "drivers/i2c/Kconfig"
-
-source "drivers/rtc/Kconfig"
-
-#
-# input before char - char/joystick depends on it. As does USB.
-#
-source "drivers/input/Kconfig"
-
-source "drivers/char/Kconfig"
+source "drivers/Kconfig"
source "fs/Kconfig"
-source "drivers/usb/Kconfig"
-
-source "drivers/uwb/Kconfig"
-
-source "drivers/staging/Kconfig"
-
source "arch/cris/Kconfig.debug"
source "security/Kconfig"
EXPORT_SYMBOL_GPL(device_show_bool);
/**
- * device_release - free device structure.
- * @kobj: device's kobject.
+ * device_release - free device structure.
+ * @kobj: device's kobject.
*
- * This is called once the reference count for the object
- * reaches 0. We forward the call to the device's release
- * method, which should handle actually freeing the structure.
+ * This is called once the reference count for the object
+ * reaches 0. We forward the call to the device's release
+ * method, which should handle actually freeing the structure.
*/
static void device_release(struct kobject *kobj)
{
static struct device_attribute uevent_attr =
__ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
+ static ssize_t show_online(struct device *dev, struct device_attribute *attr,
+ char *buf)
+ {
+ bool val;
+
+ lock_device_hotplug();
+ val = !dev->offline;
+ unlock_device_hotplug();
+ return sprintf(buf, "%u\n", val);
+ }
+
+ static ssize_t store_online(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+ {
+ bool val;
+ int ret;
+
+ ret = strtobool(buf, &val);
+ if (ret < 0)
+ return ret;
+
+ lock_device_hotplug();
+ ret = val ? device_online(dev) : device_offline(dev);
+ unlock_device_hotplug();
+ return ret < 0 ? ret : count;
+ }
+
+ static struct device_attribute online_attr =
+ __ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online);
+
static int device_add_attributes(struct device *dev,
struct device_attribute *attrs)
{
if (error)
goto err_remove_type_groups;
+ if (device_supports_offline(dev) && !dev->offline_disabled) {
+ error = device_create_file(dev, &online_attr);
+ if (error)
+ goto err_remove_type_groups;
+ }
+
return 0;
err_remove_type_groups:
struct class *class = dev->class;
const struct device_type *type = dev->type;
+ device_remove_file(dev, &online_attr);
device_remove_groups(dev, dev->groups);
if (type)
/**
* device_for_each_child - device child iterator.
* @parent: parent struct device.
- * @data: data for the callback.
* @fn: function to be called for each device.
+ * @data: data for the callback.
*
* Iterate over @parent's child devices, and call @fn for each,
* passing it @data.
/**
* device_find_child - device iterator for locating a particular device.
* @parent: parent struct device
- * @data: Data to pass to match function
* @match: Callback function to check device
+ * @data: Data to pass to match function
*
* This is similar to the device_for_each_child() function above, but it
* returns a reference to a device that is 'found' for later use, as
* if it does. If the callback returns non-zero and a reference to the
* current device can be obtained, this function will return to the caller
* and not iterate over any more devices.
+ *
+ * NOTE: you will need to drop the reference with put_device() after use.
*/
struct device *device_find_child(struct device *parent, void *data,
int (*match)(struct device *dev, void *data))
EXPORT_SYMBOL_GPL(device_create_file);
EXPORT_SYMBOL_GPL(device_remove_file);
+ static DEFINE_MUTEX(device_hotplug_lock);
+
+ void lock_device_hotplug(void)
+ {
+ mutex_lock(&device_hotplug_lock);
+ }
+
+ void unlock_device_hotplug(void)
+ {
+ mutex_unlock(&device_hotplug_lock);
+ }
+
+ static int device_check_offline(struct device *dev, void *not_used)
+ {
+ int ret;
+
+ ret = device_for_each_child(dev, NULL, device_check_offline);
+ if (ret)
+ return ret;
+
+ return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0;
+ }
+
+ /**
+ * device_offline - Prepare the device for hot-removal.
+ * @dev: Device to be put offline.
+ *
+ * Execute the device bus type's .offline() callback, if present, to prepare
+ * the device for a subsequent hot-removal. If that succeeds, the device must
+ * not be used until either it is removed or its bus type's .online() callback
+ * is executed.
+ *
+ * Call under device_hotplug_lock.
+ */
+ int device_offline(struct device *dev)
+ {
+ int ret;
+
+ if (dev->offline_disabled)
+ return -EPERM;
+
+ ret = device_for_each_child(dev, NULL, device_check_offline);
+ if (ret)
+ return ret;
+
+ device_lock(dev);
+ if (device_supports_offline(dev)) {
+ if (dev->offline) {
+ ret = 1;
+ } else {
+ ret = dev->bus->offline(dev);
+ if (!ret) {
+ kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
+ dev->offline = true;
+ }
+ }
+ }
+ device_unlock(dev);
+
+ return ret;
+ }
+
+ /**
+ * device_online - Put the device back online after successful device_offline().
+ * @dev: Device to be put back online.
+ *
+ * If device_offline() has been successfully executed for @dev, but the device
+ * has not been removed subsequently, execute its bus type's .online() callback
+ * to indicate that the device can be used again.
+ *
+ * Call under device_hotplug_lock.
+ */
+ int device_online(struct device *dev)
+ {
+ int ret = 0;
+
+ device_lock(dev);
+ if (device_supports_offline(dev)) {
+ if (dev->offline) {
+ ret = dev->bus->online(dev);
+ if (!ret) {
+ kobject_uevent(&dev->kobj, KOBJ_ONLINE);
+ dev->offline = false;
+ }
+ } else {
+ ret = 1;
+ }
+ }
+ device_unlock(dev);
+
+ return ret;
+ }
+
struct root_device {
struct device dev;
struct module *owner;
#include <linux/gfp.h>
#include <linux/slab.h>
#include <linux/percpu.h>
+ #include <linux/acpi.h>
#include "base.h"
- struct bus_type cpu_subsys = {
- .name = "cpu",
- .dev_name = "cpu",
- };
- EXPORT_SYMBOL_GPL(cpu_subsys);
-
static DEFINE_PER_CPU(struct device *, cpu_sys_devices);
+ static int cpu_subsys_match(struct device *dev, struct device_driver *drv)
+ {
+ /* ACPI style match is the only one that may succeed. */
+ if (acpi_driver_match_device(dev, drv))
+ return 1;
+
+ return 0;
+ }
+
#ifdef CONFIG_HOTPLUG_CPU
static void change_cpu_under_node(struct cpu *cpu,
unsigned int from_nid, unsigned int to_nid)
cpu->node_id = to_nid;
}
- static ssize_t show_online(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+ static int __ref cpu_subsys_online(struct device *dev)
{
struct cpu *cpu = container_of(dev, struct cpu, dev);
+ int cpuid = dev->id;
+ int from_nid, to_nid;
+ int ret;
+
+ cpu_hotplug_driver_lock();
- return sprintf(buf, "%u\n", !!cpu_online(cpu->dev.id));
+ from_nid = cpu_to_node(cpuid);
+ ret = cpu_up(cpuid);
+ /*
+ * When hot adding memory to memoryless node and enabling a cpu
+ * on the node, node number of the cpu may internally change.
+ */
+ to_nid = cpu_to_node(cpuid);
+ if (from_nid != to_nid)
+ change_cpu_under_node(cpu, from_nid, to_nid);
+
+ cpu_hotplug_driver_unlock();
+ return ret;
}
- static ssize_t __ref store_online(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+ static int cpu_subsys_offline(struct device *dev)
{
- struct cpu *cpu = container_of(dev, struct cpu, dev);
- int cpuid = cpu->dev.id;
- int from_nid, to_nid;
- ssize_t ret;
+ int ret;
cpu_hotplug_driver_lock();
- switch (buf[0]) {
- case '0':
- ret = cpu_down(cpuid);
- if (!ret)
- kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
- break;
- case '1':
- from_nid = cpu_to_node(cpuid);
- ret = cpu_up(cpuid);
-
- /*
- * When hot adding memory to memoryless node and enabling a cpu
- * on the node, node number of the cpu may internally change.
- */
- to_nid = cpu_to_node(cpuid);
- if (from_nid != to_nid)
- change_cpu_under_node(cpu, from_nid, to_nid);
-
- if (!ret)
- kobject_uevent(&dev->kobj, KOBJ_ONLINE);
- break;
- default:
- ret = -EINVAL;
- }
+ ret = cpu_down(dev->id);
cpu_hotplug_driver_unlock();
-
- if (ret >= 0)
- ret = count;
return ret;
}
- static DEVICE_ATTR(online, 0644, show_online, store_online);
-
- static struct attribute *hotplug_cpu_attrs[] = {
- &dev_attr_online.attr,
- NULL
- };
-
- static struct attribute_group hotplug_cpu_attr_group = {
- .attrs = hotplug_cpu_attrs,
- };
void unregister_cpu(struct cpu *cpu)
{
static DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store);
static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store);
#endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */
-
#endif /* CONFIG_HOTPLUG_CPU */
+ struct bus_type cpu_subsys = {
+ .name = "cpu",
+ .dev_name = "cpu",
+ .match = cpu_subsys_match,
+ #ifdef CONFIG_HOTPLUG_CPU
+ .online = cpu_subsys_online,
+ .offline = cpu_subsys_offline,
+ #endif
+ };
+ EXPORT_SYMBOL_GPL(cpu_subsys);
+
#ifdef CONFIG_KEXEC
#include <linux/kexec.h>
return rc;
}
static DEVICE_ATTR(crash_notes_size, 0400, show_crash_notes_size, NULL);
+
+static struct attribute *crash_note_cpu_attrs[] = {
+ &dev_attr_crash_notes.attr,
+ &dev_attr_crash_notes_size.attr,
+ NULL
+};
+
+static struct attribute_group crash_note_cpu_attr_group = {
+ .attrs = crash_note_cpu_attrs,
+};
#endif
- #endif
- #ifdef CONFIG_HOTPLUG_CPU
- &hotplug_cpu_attr_group,
+static const struct attribute_group *common_cpu_attr_groups[] = {
+#ifdef CONFIG_KEXEC
+ &crash_note_cpu_attr_group,
+#endif
+ NULL
+};
+
+static const struct attribute_group *hotplugable_cpu_attr_groups[] = {
+#ifdef CONFIG_KEXEC
+ &crash_note_cpu_attr_group,
+#endif
+ NULL
+};
+
/*
* Print cpu online, possible, present, and system maps
*/
cpu->dev.id = num;
cpu->dev.bus = &cpu_subsys;
cpu->dev.release = cpu_device_release;
+ cpu->dev.offline_disabled = !cpu->hotpluggable;
+ cpu->dev.offline = !cpu_online(num);
#ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE
cpu->dev.bus->uevent = arch_cpu_uevent;
#endif
+ cpu->dev.groups = common_cpu_attr_groups;
+ if (cpu->hotpluggable)
+ cpu->dev.groups = hotplugable_cpu_attr_groups;
error = device_register(&cpu->dev);
if (!error)
per_cpu(cpu_sys_devices, num) = &cpu->dev;
if (!error)
register_cpu_under_node(num, cpu_to_node(num));
-#ifdef CONFIG_KEXEC
- if (!error)
- error = device_create_file(&cpu->dev, &dev_attr_crash_notes);
- if (!error)
- error = device_create_file(&cpu->dev,
- &dev_attr_crash_notes_size);
-#endif
return error;
}
return section_nr / sections_per_block;
}
+ static int memory_subsys_online(struct device *dev);
+ static int memory_subsys_offline(struct device *dev);
+
static struct bus_type memory_subsys = {
.name = MEMORY_CLASS_NAME,
.dev_name = MEMORY_CLASS_NAME,
+ .online = memory_subsys_online,
+ .offline = memory_subsys_offline,
};
static BLOCKING_NOTIFIER_HEAD(memory_chain);
kfree(mem);
}
-/*
- * register_memory - Setup a sysfs device for a memory block
- */
-static
-int register_memory(struct memory_block *memory)
-{
- int error;
-
- memory->dev.bus = &memory_subsys;
- memory->dev.id = memory->start_section_nr / sections_per_block;
- memory->dev.release = memory_block_release;
- memory->dev.offline = memory->state == MEM_OFFLINE;
-
- error = device_register(&memory->dev);
- return error;
-}
-
unsigned long __weak memory_block_size_bytes(void)
{
return MIN_MEMORY_BLOCK_SIZE;
{
int ret = 0;
- if (mem->state != from_state_req) {
- ret = -EINVAL;
- goto out;
- }
+ if (mem->state != from_state_req)
+ return -EINVAL;
if (to_state == MEM_OFFLINE)
mem->state = MEM_GOING_OFFLINE;
ret = memory_block_action(mem->start_section_nr, to_state, online_type);
+ mem->state = ret ? from_state_req : to_state;
+ return ret;
+ }
- if (ret) {
- mem->state = from_state_req;
- goto out;
- }
+ static int memory_subsys_online(struct device *dev)
+ {
+ struct memory_block *mem = container_of(dev, struct memory_block, dev);
+ int ret;
- mem->state = to_state;
- switch (mem->state) {
- case MEM_OFFLINE:
- kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE);
- break;
- case MEM_ONLINE:
- kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE);
- break;
- default:
- break;
+ mutex_lock(&mem->state_mutex);
+
+ ret = mem->state == MEM_ONLINE ? 0 :
+ __memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE,
+ ONLINE_KEEP);
+
+ mutex_unlock(&mem->state_mutex);
+ return ret;
+ }
+
+ static int memory_subsys_offline(struct device *dev)
+ {
+ struct memory_block *mem = container_of(dev, struct memory_block, dev);
+ int ret;
+
+ mutex_lock(&mem->state_mutex);
+
+ ret = mem->state == MEM_OFFLINE ? 0 :
+ __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1);
+
+ mutex_unlock(&mem->state_mutex);
+ return ret;
+ }
+
+ static int __memory_block_change_state_uevent(struct memory_block *mem,
+ unsigned long to_state, unsigned long from_state_req,
+ int online_type)
+ {
+ int ret = __memory_block_change_state(mem, to_state, from_state_req,
+ online_type);
+ if (!ret) {
+ switch (mem->state) {
+ case MEM_OFFLINE:
+ kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE);
+ break;
+ case MEM_ONLINE:
+ kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE);
+ break;
+ default:
+ break;
+ }
}
- out:
return ret;
}
int ret;
mutex_lock(&mem->state_mutex);
- ret = __memory_block_change_state(mem, to_state, from_state_req,
- online_type);
+ ret = __memory_block_change_state_uevent(mem, to_state, from_state_req,
+ online_type);
mutex_unlock(&mem->state_mutex);
return ret;
struct device_attribute *attr, const char *buf, size_t count)
{
struct memory_block *mem;
+ bool offline;
int ret = -EINVAL;
mem = container_of(dev, struct memory_block, dev);
- if (!strncmp(buf, "online_kernel", min_t(int, count, 13)))
+ lock_device_hotplug();
+
+ if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) {
+ offline = false;
ret = memory_block_change_state(mem, MEM_ONLINE,
MEM_OFFLINE, ONLINE_KERNEL);
- else if (!strncmp(buf, "online_movable", min_t(int, count, 14)))
+ } else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) {
+ offline = false;
ret = memory_block_change_state(mem, MEM_ONLINE,
MEM_OFFLINE, ONLINE_MOVABLE);
- else if (!strncmp(buf, "online", min_t(int, count, 6)))
+ } else if (!strncmp(buf, "online", min_t(int, count, 6))) {
+ offline = false;
ret = memory_block_change_state(mem, MEM_ONLINE,
MEM_OFFLINE, ONLINE_KEEP);
- else if(!strncmp(buf, "offline", min_t(int, count, 7)))
+ } else if(!strncmp(buf, "offline", min_t(int, count, 7))) {
+ offline = true;
ret = memory_block_change_state(mem, MEM_OFFLINE,
MEM_ONLINE, -1);
+ }
+ if (!ret)
+ dev->offline = offline;
+
+ unlock_device_hotplug();
if (ret)
return ret;
static DEVICE_ATTR(phys_device, 0444, show_phys_device, NULL);
static DEVICE_ATTR(removable, 0444, show_mem_removable, NULL);
-#define mem_create_simple_file(mem, attr_name) \
- device_create_file(&mem->dev, &dev_attr_##attr_name)
-#define mem_remove_simple_file(mem, attr_name) \
- device_remove_file(&mem->dev, &dev_attr_##attr_name)
-
/*
* Block size attribute stuff
*/
static DEVICE_ATTR(block_size_bytes, 0444, print_block_size, NULL);
-static int block_size_init(void)
-{
- return device_create_file(memory_subsys.dev_root,
- &dev_attr_block_size_bytes);
-}
-
/*
* Some architectures will have custom drivers to do this, and
* will not need to do it from userspace. The fake hot-add code
out:
return ret;
}
-static DEVICE_ATTR(probe, S_IWUSR, NULL, memory_probe_store);
-static int memory_probe_init(void)
-{
- return device_create_file(memory_subsys.dev_root, &dev_attr_probe);
-}
-#else
-static inline int memory_probe_init(void)
-{
- return 0;
-}
+static DEVICE_ATTR(probe, S_IWUSR, NULL, memory_probe_store);
#endif
#ifdef CONFIG_MEMORY_FAILURE
static DEVICE_ATTR(soft_offline_page, S_IWUSR, NULL, store_soft_offline_page);
static DEVICE_ATTR(hard_offline_page, S_IWUSR, NULL, store_hard_offline_page);
-
-static __init int memory_fail_init(void)
-{
- int err;
-
- err = device_create_file(memory_subsys.dev_root,
- &dev_attr_soft_offline_page);
- if (!err)
- err = device_create_file(memory_subsys.dev_root,
- &dev_attr_hard_offline_page);
- return err;
-}
-#else
-static inline int memory_fail_init(void)
-{
- return 0;
-}
#endif
/*
return find_memory_block_hinted(section, NULL);
}
+static struct attribute *memory_memblk_attrs[] = {
+ &dev_attr_phys_index.attr,
+ &dev_attr_end_phys_index.attr,
+ &dev_attr_state.attr,
+ &dev_attr_phys_device.attr,
+ &dev_attr_removable.attr,
+ NULL
+};
+
+static struct attribute_group memory_memblk_attr_group = {
+ .attrs = memory_memblk_attrs,
+};
+
+static const struct attribute_group *memory_memblk_attr_groups[] = {
+ &memory_memblk_attr_group,
+ NULL,
+};
+
+/*
+ * register_memory - Setup a sysfs device for a memory block
+ */
+static
+int register_memory(struct memory_block *memory)
+{
+ int error;
+
+ memory->dev.bus = &memory_subsys;
+ memory->dev.id = memory->start_section_nr / sections_per_block;
+ memory->dev.release = memory_block_release;
+ memory->dev.groups = memory_memblk_attr_groups;
++ memory->dev.offline = memory->state == MEM_OFFLINE;
+
+ error = device_register(&memory->dev);
+ return error;
+}
+
static int init_memory_block(struct memory_block **memory,
struct mem_section *section, unsigned long state)
{
mem->phys_device = arch_get_memory_phys_device(start_pfn);
ret = register_memory(mem);
- if (!ret)
- ret = mem_create_simple_file(mem, phys_index);
- if (!ret)
- ret = mem_create_simple_file(mem, end_phys_index);
- if (!ret)
- ret = mem_create_simple_file(mem, state);
- if (!ret)
- ret = mem_create_simple_file(mem, phys_device);
- if (!ret)
- ret = mem_create_simple_file(mem, removable);
*memory = mem;
return ret;
unregister_mem_sect_under_nodes(mem, __section_nr(section));
mem->section_count--;
- if (mem->section_count == 0) {
- mem_remove_simple_file(mem, phys_index);
- mem_remove_simple_file(mem, end_phys_index);
- mem_remove_simple_file(mem, state);
- mem_remove_simple_file(mem, phys_device);
- mem_remove_simple_file(mem, removable);
+ if (mem->section_count == 0)
unregister_memory(mem);
- } else
+ else
kobject_put(&mem->dev.kobj);
mutex_unlock(&mem_sysfs_mutex);
}
#endif /* CONFIG_MEMORY_HOTREMOVE */
- /*
- * offline one memory block. If the memory block has been offlined, do nothing.
- */
- int offline_memory_block(struct memory_block *mem)
- {
- int ret = 0;
-
- mutex_lock(&mem->state_mutex);
- if (mem->state != MEM_OFFLINE)
- ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1);
- mutex_unlock(&mem->state_mutex);
-
- return ret;
- }
-
/* return true if the memory block is offlined, otherwise, return false */
bool is_memblock_offlined(struct memory_block *mem)
{
return mem->state == MEM_OFFLINE;
}
+static struct attribute *memory_root_attrs[] = {
+#ifdef CONFIG_ARCH_MEMORY_PROBE
+ &dev_attr_probe.attr,
+#endif
+
+#ifdef CONFIG_MEMORY_FAILURE
+ &dev_attr_soft_offline_page.attr,
+ &dev_attr_hard_offline_page.attr,
+#endif
+
+ &dev_attr_block_size_bytes.attr,
+ NULL
+};
+
+static struct attribute_group memory_root_attr_group = {
+ .attrs = memory_root_attrs,
+};
+
+static const struct attribute_group *memory_root_attr_groups[] = {
+ &memory_root_attr_group,
+ NULL,
+};
+
/*
* Initialize the sysfs support for memory devices...
*/
unsigned long block_sz;
struct memory_block *mem = NULL;
- ret = subsys_system_register(&memory_subsys, NULL);
+ ret = subsys_system_register(&memory_subsys, memory_root_attr_groups);
if (ret)
goto out;
ret = err;
}
- err = memory_probe_init();
- if (!ret)
- ret = err;
- err = memory_fail_init();
- if (!ret)
- ret = err;
- err = block_size_init();
- if (!ret)
- ret = err;
out:
if (ret)
printk(KERN_ERR "%s() failed: %d\n", __func__, ret);
}
/**
- * platform_driver_register - register a driver for platform-level devices
+ * __platform_driver_register - register a driver for platform-level devices
* @drv: platform driver structure
*/
-int platform_driver_register(struct platform_driver *drv)
+int __platform_driver_register(struct platform_driver *drv,
+ struct module *owner)
{
+ drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
return driver_register(&drv->driver);
}
-EXPORT_SYMBOL_GPL(platform_driver_register);
+EXPORT_SYMBOL_GPL(__platform_driver_register);
/**
* platform_driver_unregister - unregister a driver for platform-level devices
static const struct dev_pm_ops platform_dev_pm_ops = {
.runtime_suspend = pm_generic_runtime_suspend,
.runtime_resume = pm_generic_runtime_resume,
- .runtime_idle = pm_generic_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
};
config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver"
depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK
+ select CPU_FREQ_TABLE
help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
config ARM_EXYNOS_CPUFREQ
bool "SAMSUNG EXYNOS SoCs"
depends on ARCH_EXYNOS
+ select CPU_FREQ_TABLE
default y
help
This adds the CPUFreq driver common part for Samsung
config ARM_EXYNOS5440_CPUFREQ
def_bool SOC_EXYNOS5440
depends on HAVE_CLK && PM_OPP && OF
+ select CPU_FREQ_TABLE
help
This adds the CPUFreq driver for Samsung EXYNOS5440
SoC. The nature of exynos5440 clock controller is
config ARM_HIGHBANK_CPUFREQ
tristate "Calxeda Highbank-based"
depends on ARCH_HIGHBANK
- select CPU_FREQ_TABLE
select GENERIC_CPUFREQ_CPU0
select PM_OPP
select REGULATOR
tristate "Freescale i.MX6Q cpufreq support"
depends on SOC_IMX6Q
depends on REGULATOR_ANATOP
+ select CPU_FREQ_TABLE
help
This adds cpufreq driver support for Freescale i.MX6Q SOC.
config ARM_KIRKWOOD_CPUFREQ
def_bool ARCH_KIRKWOOD && OF
+ select CPU_FREQ_TABLE
help
This adds the CPUFreq driver for Marvell Kirkwood
SoCs.
default ARCH_OMAP2PLUS
select CPU_FREQ_TABLE
+config ARM_S3C_CPUFREQ
+ bool
+ help
+ Internal configuration node for common cpufreq on Samsung SoC
+
+config ARM_S3C24XX_CPUFREQ
+ bool "CPUfreq driver for Samsung S3C24XX series CPUs (EXPERIMENTAL)"
+ depends on ARCH_S3C24XX
+ select ARM_S3C_CPUFREQ
+ help
+ This enables the CPUfreq driver for the Samsung S3C24XX family
+ of CPUs.
+
+ For details, take a look at <file:Documentation/cpu-freq>.
+
+ If in doubt, say N.
+
+config ARM_S3C24XX_CPUFREQ_DEBUG
+ bool "Debug CPUfreq Samsung driver core"
+ depends on ARM_S3C24XX_CPUFREQ
+ help
+ Enable s3c_freq_dbg for the Samsung S3C CPUfreq core
+
+config ARM_S3C24XX_CPUFREQ_IODEBUG
+ bool "Debug CPUfreq Samsung driver IO timing"
+ depends on ARM_S3C24XX_CPUFREQ
+ help
+ Enable s3c_freq_iodbg for the Samsung S3C CPUfreq core
+
+config ARM_S3C24XX_CPUFREQ_DEBUGFS
+ bool "Export debugfs for CPUFreq"
+ depends on ARM_S3C24XX_CPUFREQ && DEBUG_FS
+ help
+ Export status information via debugfs.
+
+config ARM_S3C2410_CPUFREQ
+ bool
+ depends on ARM_S3C24XX_CPUFREQ && CPU_S3C2410
+ select S3C2410_CPUFREQ_UTILS
+ help
+ CPU Frequency scaling support for S3C2410
+
+config ARM_S3C2412_CPUFREQ
+ bool
+ depends on ARM_S3C24XX_CPUFREQ && CPU_S3C2412
+ default y
+ select S3C2412_IOTIMING
+ help
+ CPU Frequency scaling support for S3C2412 and S3C2413 SoC CPUs.
+
config ARM_S3C2416_CPUFREQ
bool "S3C2416 CPU Frequency scaling support"
depends on CPU_S3C2416
+ select CPU_FREQ_TABLE
help
This adds the CPUFreq driver for the Samsung S3C2416 and
S3C2450 SoC. The S3C2416 supports changing the rate of the
If in doubt, say N.
+config ARM_S3C2440_CPUFREQ
+ bool "S3C2440/S3C2442 CPU Frequency scaling support"
+ depends on ARM_S3C24XX_CPUFREQ && (CPU_S3C2440 || CPU_S3C2442)
+ select S3C2410_CPUFREQ_UTILS
+ default y
+ help
+ CPU Frequency scaling support for S3C2440 and S3C2442 SoC CPUs.
+
config ARM_S3C64XX_CPUFREQ
bool "Samsung S3C64XX"
depends on CPU_S3C6410
+ select CPU_FREQ_TABLE
default y
help
This adds the CPUFreq driver for Samsung S3C6410 SoC.
config ARM_SPEAR_CPUFREQ
bool "SPEAr CPUFreq support"
depends on PLAT_SPEAR
+ select CPU_FREQ_TABLE
default y
help
This adds the CPUFreq driver support for SPEAr SOCs.
+
+ config ARM_TEGRA_CPUFREQ
+ bool "TEGRA CPUFreq support"
+ depends on ARCH_TEGRA
+ select CPU_FREQ_TABLE
+ default y
+ help
+ This adds the CPUFreq driver support for TEGRA SOCs.
obj-$(CONFIG_PXA25x) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA27x) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
+obj-$(CONFIG_ARM_S3C24XX_CPUFREQ) += s3c24xx-cpufreq.o
+obj-$(CONFIG_ARM_S3C24XX_CPUFREQ_DEBUGFS) += s3c24xx-cpufreq-debugfs.o
+obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
+obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
+obj-$(CONFIG_ARM_S3C2440_CPUFREQ) += s3c2440-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o
obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
- obj-$(CONFIG_ARCH_TEGRA) += tegra-cpufreq.o
+ obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o
##################################################################################
# PowerPC platform drivers
ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o
obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o
obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o
+ obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o
+ obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o
+ obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o
+ obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o
##################################################################################
# Other platform drivers
obj-$(CONFIG_AVR32_AT32AP_CPUFREQ) += at32ap-cpufreq.o
- obj-$(CONFIG_BLACKFIN) += blackfin-cpufreq.o
+ obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o
obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o
obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
--- /dev/null
- cfg->pll.index = __raw_readl(S3C2410_MPLLCON);
+/*
+ * Copyright (c) 2006-2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C24XX CPU Frequency scaling
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <plat/cpu.h>
+#include <plat/clock.h>
+#include <plat/cpu-freq-core.h>
+
+#include <mach/regs-clock.h>
+
+/* note, cpufreq support deals in kHz, no Hz */
+
+static struct cpufreq_driver s3c24xx_driver;
+static struct s3c_cpufreq_config cpu_cur;
+static struct s3c_iotimings s3c24xx_iotiming;
+static struct cpufreq_frequency_table *pll_reg;
+static unsigned int last_target = ~0;
+static unsigned int ftab_size;
+static struct cpufreq_frequency_table *ftab;
+
+static struct clk *_clk_mpll;
+static struct clk *_clk_xtal;
+static struct clk *clk_fclk;
+static struct clk *clk_hclk;
+static struct clk *clk_pclk;
+static struct clk *clk_arm;
+
+#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
+struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void)
+{
+ return &cpu_cur;
+}
+
+struct s3c_iotimings *s3c_cpufreq_getiotimings(void)
+{
+ return &s3c24xx_iotiming;
+}
+#endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUGFS */
+
+static void s3c_cpufreq_getcur(struct s3c_cpufreq_config *cfg)
+{
+ unsigned long fclk, pclk, hclk, armclk;
+
+ cfg->freq.fclk = fclk = clk_get_rate(clk_fclk);
+ cfg->freq.hclk = hclk = clk_get_rate(clk_hclk);
+ cfg->freq.pclk = pclk = clk_get_rate(clk_pclk);
+ cfg->freq.armclk = armclk = clk_get_rate(clk_arm);
+
- suspend_pll.index = __raw_readl(S3C2410_MPLLCON);
++ cfg->pll.driver_data = __raw_readl(S3C2410_MPLLCON);
+ cfg->pll.frequency = fclk;
+
+ cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10);
+
+ cfg->divs.h_divisor = fclk / hclk;
+ cfg->divs.p_divisor = fclk / pclk;
+}
+
+static inline void s3c_cpufreq_calc(struct s3c_cpufreq_config *cfg)
+{
+ unsigned long pll = cfg->pll.frequency;
+
+ cfg->freq.fclk = pll;
+ cfg->freq.hclk = pll / cfg->divs.h_divisor;
+ cfg->freq.pclk = pll / cfg->divs.p_divisor;
+
+ /* convert hclk into 10ths of nanoseconds for io calcs */
+ cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10);
+}
+
+static inline int closer(unsigned int target, unsigned int n, unsigned int c)
+{
+ int diff_cur = abs(target - c);
+ int diff_new = abs(target - n);
+
+ return (diff_new < diff_cur);
+}
+
+static void s3c_cpufreq_show(const char *pfx,
+ struct s3c_cpufreq_config *cfg)
+{
+ s3c_freq_dbg("%s: Fvco=%u, F=%lu, A=%lu, H=%lu (%u), P=%lu (%u)\n",
+ pfx, cfg->pll.frequency, cfg->freq.fclk, cfg->freq.armclk,
+ cfg->freq.hclk, cfg->divs.h_divisor,
+ cfg->freq.pclk, cfg->divs.p_divisor);
+}
+
+/* functions to wrapper the driver info calls to do the cpu specific work */
+
+static void s3c_cpufreq_setio(struct s3c_cpufreq_config *cfg)
+{
+ if (cfg->info->set_iotiming)
+ (cfg->info->set_iotiming)(cfg, &s3c24xx_iotiming);
+}
+
+static int s3c_cpufreq_calcio(struct s3c_cpufreq_config *cfg)
+{
+ if (cfg->info->calc_iotiming)
+ return (cfg->info->calc_iotiming)(cfg, &s3c24xx_iotiming);
+
+ return 0;
+}
+
+static void s3c_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
+{
+ (cfg->info->set_refresh)(cfg);
+}
+
+static void s3c_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
+{
+ (cfg->info->set_divs)(cfg);
+}
+
+static int s3c_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
+{
+ return (cfg->info->calc_divs)(cfg);
+}
+
+static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg)
+{
+ (cfg->info->set_fvco)(cfg);
+}
+
+static inline void s3c_cpufreq_resume_clocks(void)
+{
+ cpu_cur.info->resume_clocks();
+}
+
+static inline void s3c_cpufreq_updateclk(struct clk *clk,
+ unsigned int freq)
+{
+ clk_set_rate(clk, freq);
+}
+
+static int s3c_cpufreq_settarget(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ struct cpufreq_frequency_table *pll)
+{
+ struct s3c_cpufreq_freqs freqs;
+ struct s3c_cpufreq_config cpu_new;
+ unsigned long flags;
+
+ cpu_new = cpu_cur; /* copy new from current */
+
+ s3c_cpufreq_show("cur", &cpu_cur);
+
+ /* TODO - check for DMA currently outstanding */
+
+ cpu_new.pll = pll ? *pll : cpu_cur.pll;
+
+ if (pll)
+ freqs.pll_changing = 1;
+
+ /* update our frequencies */
+
+ cpu_new.freq.armclk = target_freq;
+ cpu_new.freq.fclk = cpu_new.pll.frequency;
+
+ if (s3c_cpufreq_calcdivs(&cpu_new) < 0) {
+ printk(KERN_ERR "no divisors for %d\n", target_freq);
+ goto err_notpossible;
+ }
+
+ s3c_freq_dbg("%s: got divs\n", __func__);
+
+ s3c_cpufreq_calc(&cpu_new);
+
+ s3c_freq_dbg("%s: calculated frequencies for new\n", __func__);
+
+ if (cpu_new.freq.hclk != cpu_cur.freq.hclk) {
+ if (s3c_cpufreq_calcio(&cpu_new) < 0) {
+ printk(KERN_ERR "%s: no IO timings\n", __func__);
+ goto err_notpossible;
+ }
+ }
+
+ s3c_cpufreq_show("new", &cpu_new);
+
+ /* setup our cpufreq parameters */
+
+ freqs.old = cpu_cur.freq;
+ freqs.new = cpu_new.freq;
+
+ freqs.freqs.old = cpu_cur.freq.armclk / 1000;
+ freqs.freqs.new = cpu_new.freq.armclk / 1000;
+
+ /* update f/h/p clock settings before we issue the change
+ * notification, so that drivers do not need to do anything
+ * special if they want to recalculate on CPUFREQ_PRECHANGE. */
+
+ s3c_cpufreq_updateclk(_clk_mpll, cpu_new.pll.frequency);
+ s3c_cpufreq_updateclk(clk_fclk, cpu_new.freq.fclk);
+ s3c_cpufreq_updateclk(clk_hclk, cpu_new.freq.hclk);
+ s3c_cpufreq_updateclk(clk_pclk, cpu_new.freq.pclk);
+
+ /* start the frequency change */
+ cpufreq_notify_transition(policy, &freqs.freqs, CPUFREQ_PRECHANGE);
+
+ /* If hclk is staying the same, then we do not need to
+ * re-write the IO or the refresh timings whilst we are changing
+ * speed. */
+
+ local_irq_save(flags);
+
+ /* is our memory clock slowing down? */
+ if (cpu_new.freq.hclk < cpu_cur.freq.hclk) {
+ s3c_cpufreq_setrefresh(&cpu_new);
+ s3c_cpufreq_setio(&cpu_new);
+ }
+
+ if (cpu_new.freq.fclk == cpu_cur.freq.fclk) {
+ /* not changing PLL, just set the divisors */
+
+ s3c_cpufreq_setdivs(&cpu_new);
+ } else {
+ if (cpu_new.freq.fclk < cpu_cur.freq.fclk) {
+ /* slow the cpu down, then set divisors */
+
+ s3c_cpufreq_setfvco(&cpu_new);
+ s3c_cpufreq_setdivs(&cpu_new);
+ } else {
+ /* set the divisors, then speed up */
+
+ s3c_cpufreq_setdivs(&cpu_new);
+ s3c_cpufreq_setfvco(&cpu_new);
+ }
+ }
+
+ /* did our memory clock speed up */
+ if (cpu_new.freq.hclk > cpu_cur.freq.hclk) {
+ s3c_cpufreq_setrefresh(&cpu_new);
+ s3c_cpufreq_setio(&cpu_new);
+ }
+
+ /* update our current settings */
+ cpu_cur = cpu_new;
+
+ local_irq_restore(flags);
+
+ /* notify everyone we've done this */
+ cpufreq_notify_transition(policy, &freqs.freqs, CPUFREQ_POSTCHANGE);
+
+ s3c_freq_dbg("%s: finished\n", __func__);
+ return 0;
+
+ err_notpossible:
+ printk(KERN_ERR "no compatible settings for %d\n", target_freq);
+ return -EINVAL;
+}
+
+/* s3c_cpufreq_target
+ *
+ * called by the cpufreq core to adjust the frequency that the CPU
+ * is currently running at.
+ */
+
+static int s3c_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct cpufreq_frequency_table *pll;
+ unsigned int index;
+
+ /* avoid repeated calls which cause a needless amout of duplicated
+ * logging output (and CPU time as the calculation process is
+ * done) */
+ if (target_freq == last_target)
+ return 0;
+
+ last_target = target_freq;
+
+ s3c_freq_dbg("%s: policy %p, target %u, relation %u\n",
+ __func__, policy, target_freq, relation);
+
+ if (ftab) {
+ if (cpufreq_frequency_table_target(policy, ftab,
+ target_freq, relation,
+ &index)) {
+ s3c_freq_dbg("%s: table failed\n", __func__);
+ return -EINVAL;
+ }
+
+ s3c_freq_dbg("%s: adjust %d to entry %d (%u)\n", __func__,
+ target_freq, index, ftab[index].frequency);
+ target_freq = ftab[index].frequency;
+ }
+
+ target_freq *= 1000; /* convert target to Hz */
+
+ /* find the settings for our new frequency */
+
+ if (!pll_reg || cpu_cur.lock_pll) {
+ /* either we've not got any PLL values, or we've locked
+ * to the current one. */
+ pll = NULL;
+ } else {
+ struct cpufreq_policy tmp_policy;
+ int ret;
+
+ /* we keep the cpu pll table in Hz, to ensure we get an
+ * accurate value for the PLL output. */
+
+ tmp_policy.min = policy->min * 1000;
+ tmp_policy.max = policy->max * 1000;
+ tmp_policy.cpu = policy->cpu;
+
+ /* cpufreq_frequency_table_target uses a pointer to 'index'
+ * which is the number of the table entry, not the value of
+ * the table entry's index field. */
+
+ ret = cpufreq_frequency_table_target(&tmp_policy, pll_reg,
+ target_freq, relation,
+ &index);
+
+ if (ret < 0) {
+ printk(KERN_ERR "%s: no PLL available\n", __func__);
+ goto err_notpossible;
+ }
+
+ pll = pll_reg + index;
+
+ s3c_freq_dbg("%s: target %u => %u\n",
+ __func__, target_freq, pll->frequency);
+
+ target_freq = pll->frequency;
+ }
+
+ return s3c_cpufreq_settarget(policy, target_freq, pll);
+
+ err_notpossible:
+ printk(KERN_ERR "no compatible settings for %d\n", target_freq);
+ return -EINVAL;
+}
+
+static unsigned int s3c_cpufreq_get(unsigned int cpu)
+{
+ return clk_get_rate(clk_arm) / 1000;
+}
+
+struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, name);
+ if (IS_ERR(clk))
+ printk(KERN_ERR "cpufreq: failed to get clock '%s'\n", name);
+
+ return clk;
+}
+
+static int s3c_cpufreq_init(struct cpufreq_policy *policy)
+{
+ printk(KERN_INFO "%s: initialising policy %p\n", __func__, policy);
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ policy->cur = s3c_cpufreq_get(0);
+ policy->min = policy->cpuinfo.min_freq = 0;
+ policy->max = policy->cpuinfo.max_freq = cpu_cur.info->max.fclk / 1000;
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+ /* feed the latency information from the cpu driver */
+ policy->cpuinfo.transition_latency = cpu_cur.info->latency;
+
+ if (ftab)
+ cpufreq_frequency_table_cpuinfo(policy, ftab);
+
+ return 0;
+}
+
+static __init int s3c_cpufreq_initclks(void)
+{
+ _clk_mpll = s3c_cpufreq_clk_get(NULL, "mpll");
+ _clk_xtal = s3c_cpufreq_clk_get(NULL, "xtal");
+ clk_fclk = s3c_cpufreq_clk_get(NULL, "fclk");
+ clk_hclk = s3c_cpufreq_clk_get(NULL, "hclk");
+ clk_pclk = s3c_cpufreq_clk_get(NULL, "pclk");
+ clk_arm = s3c_cpufreq_clk_get(NULL, "armclk");
+
+ if (IS_ERR(clk_fclk) || IS_ERR(clk_hclk) || IS_ERR(clk_pclk) ||
+ IS_ERR(_clk_mpll) || IS_ERR(clk_arm) || IS_ERR(_clk_xtal)) {
+ printk(KERN_ERR "%s: could not get clock(s)\n", __func__);
+ return -ENOENT;
+ }
+
+ printk(KERN_INFO "%s: clocks f=%lu,h=%lu,p=%lu,a=%lu\n", __func__,
+ clk_get_rate(clk_fclk) / 1000,
+ clk_get_rate(clk_hclk) / 1000,
+ clk_get_rate(clk_pclk) / 1000,
+ clk_get_rate(clk_arm) / 1000);
+
+ return 0;
+}
+
+static int s3c_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static struct cpufreq_frequency_table suspend_pll;
+static unsigned int suspend_freq;
+
+static int s3c_cpufreq_suspend(struct cpufreq_policy *policy)
+{
+ suspend_pll.frequency = clk_get_rate(_clk_mpll);
++ suspend_pll.driver_data = __raw_readl(S3C2410_MPLLCON);
+ suspend_freq = s3c_cpufreq_get(0) * 1000;
+
+ return 0;
+}
+
+static int s3c_cpufreq_resume(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ s3c_freq_dbg("%s: resuming with policy %p\n", __func__, policy);
+
+ last_target = ~0; /* invalidate last_target setting */
+
+ /* first, find out what speed we resumed at. */
+ s3c_cpufreq_resume_clocks();
+
+ /* whilst we will be called later on, we try and re-set the
+ * cpu frequencies as soon as possible so that we do not end
+ * up resuming devices and then immediately having to re-set
+ * a number of settings once these devices have restarted.
+ *
+ * as a note, it is expected devices are not used until they
+ * have been un-suspended and at that time they should have
+ * used the updated clock settings.
+ */
+
+ ret = s3c_cpufreq_settarget(NULL, suspend_freq, &suspend_pll);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to reset pll/freq\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+#else
+#define s3c_cpufreq_resume NULL
+#define s3c_cpufreq_suspend NULL
+#endif
+
+static struct cpufreq_driver s3c24xx_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = s3c_cpufreq_verify,
+ .target = s3c_cpufreq_target,
+ .get = s3c_cpufreq_get,
+ .init = s3c_cpufreq_init,
+ .suspend = s3c_cpufreq_suspend,
+ .resume = s3c_cpufreq_resume,
+ .name = "s3c24xx",
+};
+
+
+int __init s3c_cpufreq_register(struct s3c_cpufreq_info *info)
+{
+ if (!info || !info->name) {
+ printk(KERN_ERR "%s: failed to pass valid information\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "S3C24XX CPU Frequency driver, %s cpu support\n",
+ info->name);
+
+ /* check our driver info has valid data */
+
+ BUG_ON(info->set_refresh == NULL);
+ BUG_ON(info->set_divs == NULL);
+ BUG_ON(info->calc_divs == NULL);
+
+ /* info->set_fvco is optional, depending on whether there
+ * is a need to set the clock code. */
+
+ cpu_cur.info = info;
+
+ /* Note, driver registering should probably update locktime */
+
+ return 0;
+}
+
+int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
+{
+ struct s3c_cpufreq_board *ours;
+
+ if (!board) {
+ printk(KERN_INFO "%s: no board data\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Copy the board information so that each board can make this
+ * initdata. */
+
+ ours = kzalloc(sizeof(struct s3c_cpufreq_board), GFP_KERNEL);
+ if (ours == NULL) {
+ printk(KERN_ERR "%s: no memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ *ours = *board;
+ cpu_cur.board = ours;
+
+ return 0;
+}
+
+int __init s3c_cpufreq_auto_io(void)
+{
+ int ret;
+
+ if (!cpu_cur.info->get_iotiming) {
+ printk(KERN_ERR "%s: get_iotiming undefined\n", __func__);
+ return -ENOENT;
+ }
+
+ printk(KERN_INFO "%s: working out IO settings\n", __func__);
+
+ ret = (cpu_cur.info->get_iotiming)(&cpu_cur, &s3c24xx_iotiming);
+ if (ret)
+ printk(KERN_ERR "%s: failed to get timings\n", __func__);
+
+ return ret;
+}
+
+/* if one or is zero, then return the other, otherwise return the min */
+#define do_min(_a, _b) ((_a) == 0 ? (_b) : (_b) == 0 ? (_a) : min(_a, _b))
+
+/**
+ * s3c_cpufreq_freq_min - find the minimum settings for the given freq.
+ * @dst: The destination structure
+ * @a: One argument.
+ * @b: The other argument.
+ *
+ * Create a minimum of each frequency entry in the 'struct s3c_freq',
+ * unless the entry is zero when it is ignored and the non-zero argument
+ * used.
+ */
+static void s3c_cpufreq_freq_min(struct s3c_freq *dst,
+ struct s3c_freq *a, struct s3c_freq *b)
+{
+ dst->fclk = do_min(a->fclk, b->fclk);
+ dst->hclk = do_min(a->hclk, b->hclk);
+ dst->pclk = do_min(a->pclk, b->pclk);
+ dst->armclk = do_min(a->armclk, b->armclk);
+}
+
+static inline u32 calc_locktime(u32 freq, u32 time_us)
+{
+ u32 result;
+
+ result = freq * time_us;
+ result = DIV_ROUND_UP(result, 1000 * 1000);
+
+ return result;
+}
+
+static void s3c_cpufreq_update_loctkime(void)
+{
+ unsigned int bits = cpu_cur.info->locktime_bits;
+ u32 rate = (u32)clk_get_rate(_clk_xtal);
+ u32 val;
+
+ if (bits == 0) {
+ WARN_ON(1);
+ return;
+ }
+
+ val = calc_locktime(rate, cpu_cur.info->locktime_u) << bits;
+ val |= calc_locktime(rate, cpu_cur.info->locktime_m);
+
+ printk(KERN_INFO "%s: new locktime is 0x%08x\n", __func__, val);
+ __raw_writel(val, S3C2410_LOCKTIME);
+}
+
+static int s3c_cpufreq_build_freq(void)
+{
+ int size, ret;
+
+ if (!cpu_cur.info->calc_freqtable)
+ return -EINVAL;
+
+ kfree(ftab);
+ ftab = NULL;
+
+ size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0);
+ size++;
+
+ ftab = kmalloc(sizeof(struct cpufreq_frequency_table) * size, GFP_KERNEL);
+ if (!ftab) {
+ printk(KERN_ERR "%s: no memory for tables\n", __func__);
+ return -ENOMEM;
+ }
+
+ ftab_size = size;
+
+ ret = cpu_cur.info->calc_freqtable(&cpu_cur, ftab, size);
+ s3c_cpufreq_addfreq(ftab, ret, size, CPUFREQ_TABLE_END);
+
+ return 0;
+}
+
+static int __init s3c_cpufreq_initcall(void)
+{
+ int ret = 0;
+
+ if (cpu_cur.info && cpu_cur.board) {
+ ret = s3c_cpufreq_initclks();
+ if (ret)
+ goto out;
+
+ /* get current settings */
+ s3c_cpufreq_getcur(&cpu_cur);
+ s3c_cpufreq_show("cur", &cpu_cur);
+
+ if (cpu_cur.board->auto_io) {
+ ret = s3c_cpufreq_auto_io();
+ if (ret) {
+ printk(KERN_ERR "%s: failed to get io timing\n",
+ __func__);
+ goto out;
+ }
+ }
+
+ if (cpu_cur.board->need_io && !cpu_cur.info->set_iotiming) {
+ printk(KERN_ERR "%s: no IO support registered\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!cpu_cur.info->need_pll)
+ cpu_cur.lock_pll = 1;
+
+ s3c_cpufreq_update_loctkime();
+
+ s3c_cpufreq_freq_min(&cpu_cur.max, &cpu_cur.board->max,
+ &cpu_cur.info->max);
+
+ if (cpu_cur.info->calc_freqtable)
+ s3c_cpufreq_build_freq();
+
+ ret = cpufreq_register_driver(&s3c24xx_driver);
+ }
+
+ out:
+ return ret;
+}
+
+late_initcall(s3c_cpufreq_initcall);
+
+/**
+ * s3c_plltab_register - register CPU PLL table.
+ * @plls: The list of PLL entries.
+ * @plls_no: The size of the PLL entries @plls.
+ *
+ * Register the given set of PLLs with the system.
+ */
+int __init s3c_plltab_register(struct cpufreq_frequency_table *plls,
+ unsigned int plls_no)
+{
+ struct cpufreq_frequency_table *vals;
+ unsigned int size;
+
+ size = sizeof(struct cpufreq_frequency_table) * (plls_no + 1);
+
+ vals = kmalloc(size, GFP_KERNEL);
+ if (vals) {
+ memcpy(vals, plls, size);
+ pll_reg = vals;
+
+ /* write a terminating entry, we don't store it in the
+ * table that is stored in the kernel */
+ vals += plls_no;
+ vals->frequency = CPUFREQ_TABLE_END;
+
+ printk(KERN_INFO "cpufreq: %d PLL entries\n", plls_no);
+ } else
+ printk(KERN_ERR "cpufreq: no memory for PLL tables\n");
+
+ return vals ? 0 : -ENOMEM;
+}
- config CPU_IDLE
+ menuconfig CPU_IDLE
bool "CPU idle PM support"
default y if ACPI || PPC_PSERIES
+ select CPU_IDLE_GOV_LADDER if (!NO_HZ && !NO_HZ_IDLE)
+ select CPU_IDLE_GOV_MENU if (NO_HZ || NO_HZ_IDLE)
help
CPU idle is a generic framework for supporting software-controlled
idle processor power management. It includes modular cross-platform
If you're using an ACPI-enabled platform, you should say Y here.
+ if CPU_IDLE
+
config CPU_IDLE_MULTIPLE_DRIVERS
bool "Support multiple cpuidle drivers"
- depends on CPU_IDLE
default n
help
Allows the cpuidle framework to use different drivers for each CPU.
states. If unsure say N.
config CPU_IDLE_GOV_LADDER
- bool
- depends on CPU_IDLE
+ bool "Ladder governor (for periodic timer tick)"
default y
config CPU_IDLE_GOV_MENU
- bool
- depends on CPU_IDLE && NO_HZ
+ bool "Menu governor (for tickless system)"
default y
- config ARCH_NEEDS_CPU_IDLE_COUPLED
- def_bool n
-
- if CPU_IDLE
-
config CPU_IDLE_CALXEDA
bool "CPU Idle Driver for Calxeda processors"
depends on ARCH_HIGHBANK
+ select ARM_CPU_SUSPEND
help
Select this to enable cpuidle on Calxeda processors.
+ config CPU_IDLE_ZYNQ
+ bool "CPU Idle Driver for Xilinx Zynq processors"
+ depends on ARCH_ZYNQ
+ help
+ Select this to enable cpuidle on Xilinx Zynq processors.
+
endif
+
+ config ARCH_NEEDS_CPU_IDLE_COUPLED
+ def_bool n
/*
* Moorestown platform Langwell chip GPIO driver
*
- * Copyright (c) 2008 - 2009, Intel Corporation.
+ * Copyright (c) 2008, 2009, 2013, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
/* Supports:
* Moorestown platform Langwell chip.
* Medfield platform Penwell chip.
- * Whitney point.
*/
#include <linux/module.h>
struct lnw_gpio {
struct gpio_chip chip;
- void *reg_base;
+ void __iomem *reg_base;
spinlock_t lock;
struct pci_dev *pdev;
struct irq_domain *domain;
#define to_lnw_priv(chip) container_of(chip, struct lnw_gpio, chip)
static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
- enum GPIO_REG reg_type)
+ enum GPIO_REG reg_type)
{
struct lnw_gpio *lnw = to_lnw_priv(chip);
unsigned nreg = chip->ngpio / 32;
u8 reg = offset / 32;
- void __iomem *ptr;
- ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4);
- return ptr;
+ return lnw->reg_base + reg_type * nreg * 4 + reg * 4;
}
static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset,
struct lnw_gpio *lnw = to_lnw_priv(chip);
unsigned nreg = chip->ngpio / 32;
u8 reg = offset / 16;
- void __iomem *ptr;
- ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4);
- return ptr;
+ return lnw->reg_base + reg_type * nreg * 4 + reg * 4;
}
static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset)
static int lnw_gpio_runtime_idle(struct device *dev)
{
- int err = pm_schedule_suspend(dev, 500);
-
- if (!err)
- return 0;
-
+ pm_schedule_suspend(dev, 500);
return -EBUSY;
}
};
static int lnw_gpio_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
+ const struct pci_device_id *id)
{
- void *base;
- resource_size_t start, len;
+ void __iomem *base;
struct lnw_gpio *lnw;
u32 gpio_base;
u32 irq_base;
int retval;
int ngpio = id->driver_data;
- retval = pci_enable_device(pdev);
+ retval = pcim_enable_device(pdev);
if (retval)
return retval;
- retval = pci_request_regions(pdev, "langwell_gpio");
+ retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev));
if (retval) {
- dev_err(&pdev->dev, "error requesting resources\n");
- goto err_pci_req_region;
- }
- /* get the gpio_base from bar1 */
- start = pci_resource_start(pdev, 1);
- len = pci_resource_len(pdev, 1);
- base = ioremap_nocache(start, len);
- if (!base) {
- dev_err(&pdev->dev, "error mapping bar1\n");
- retval = -EFAULT;
- goto err_ioremap;
+ dev_err(&pdev->dev, "I/O memory mapping error\n");
+ return retval;
}
- irq_base = *(u32 *)base;
- gpio_base = *((u32 *)base + 1);
+
+ base = pcim_iomap_table(pdev)[1];
+
+ irq_base = readl(base);
+ gpio_base = readl(sizeof(u32) + base);
+
/* release the IO mapping, since we already get the info from bar1 */
- iounmap(base);
- /* get the register base from bar0 */
- start = pci_resource_start(pdev, 0);
- len = pci_resource_len(pdev, 0);
- base = devm_ioremap_nocache(&pdev->dev, start, len);
- if (!base) {
- dev_err(&pdev->dev, "error mapping bar0\n");
- retval = -EFAULT;
- goto err_ioremap;
- }
+ pcim_iounmap_regions(pdev, 1 << 1);
lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL);
if (!lnw) {
- dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n");
- retval = -ENOMEM;
- goto err_ioremap;
+ dev_err(&pdev->dev, "can't allocate chip data\n");
+ return -ENOMEM;
}
- lnw->reg_base = base;
+ lnw->reg_base = pcim_iomap_table(pdev)[0];
lnw->chip.label = dev_name(&pdev->dev);
lnw->chip.request = lnw_gpio_request;
lnw->chip.direction_input = lnw_gpio_direction_input;
lnw->chip.can_sleep = 0;
lnw->pdev = pdev;
+ spin_lock_init(&lnw->lock);
+
lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ngpio, irq_base,
&lnw_gpio_irq_ops, lnw);
- if (!lnw->domain) {
- retval = -ENOMEM;
- goto err_ioremap;
- }
+ if (!lnw->domain)
+ return -ENOMEM;
pci_set_drvdata(pdev, lnw);
retval = gpiochip_add(&lnw->chip);
if (retval) {
- dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval);
- goto err_ioremap;
+ dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
+ return retval;
}
lnw_irq_init_hw(lnw);
irq_set_handler_data(pdev->irq, lnw);
irq_set_chained_handler(pdev->irq, lnw_irq_handler);
- spin_lock_init(&lnw->lock);
-
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_allow(&pdev->dev);
return 0;
-
-err_ioremap:
- pci_release_regions(pdev);
-err_pci_req_region:
- pci_disable_device(pdev);
- return retval;
}
static struct pci_driver lnw_gpio_driver = {
},
};
-
-static int wp_gpio_probe(struct platform_device *pdev)
-{
- struct lnw_gpio *lnw;
- struct gpio_chip *gc;
- struct resource *rc;
- int retval = 0;
-
- rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!rc)
- return -EINVAL;
-
- lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL);
- if (!lnw) {
- dev_err(&pdev->dev,
- "can't allocate whitneypoint_gpio chip data\n");
- return -ENOMEM;
- }
- lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc));
- if (lnw->reg_base == NULL) {
- retval = -EINVAL;
- goto err_kmalloc;
- }
- spin_lock_init(&lnw->lock);
- gc = &lnw->chip;
- gc->label = dev_name(&pdev->dev);
- gc->owner = THIS_MODULE;
- gc->direction_input = lnw_gpio_direction_input;
- gc->direction_output = lnw_gpio_direction_output;
- gc->get = lnw_gpio_get;
- gc->set = lnw_gpio_set;
- gc->to_irq = NULL;
- gc->base = 0;
- gc->ngpio = 64;
- gc->can_sleep = 0;
- retval = gpiochip_add(gc);
- if (retval) {
- dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n",
- retval);
- goto err_ioremap;
- }
- platform_set_drvdata(pdev, lnw);
- return 0;
-err_ioremap:
- iounmap(lnw->reg_base);
-err_kmalloc:
- kfree(lnw);
- return retval;
-}
-
-static int wp_gpio_remove(struct platform_device *pdev)
-{
- struct lnw_gpio *lnw = platform_get_drvdata(pdev);
- int err;
- err = gpiochip_remove(&lnw->chip);
- if (err)
- dev_err(&pdev->dev, "failed to remove gpio_chip.\n");
- iounmap(lnw->reg_base);
- kfree(lnw);
- platform_set_drvdata(pdev, NULL);
- return 0;
-}
-
-static struct platform_driver wp_gpio_driver = {
- .probe = wp_gpio_probe,
- .remove = wp_gpio_remove,
- .driver = {
- .name = "wp_gpio",
- .owner = THIS_MODULE,
- },
-};
-
static int __init lnw_gpio_init(void)
{
- int ret;
- ret = pci_register_driver(&lnw_gpio_driver);
- if (ret < 0)
- return ret;
- ret = platform_driver_register(&wp_gpio_driver);
- if (ret < 0)
- pci_unregister_driver(&lnw_gpio_driver);
- return ret;
+ return pci_register_driver(&lnw_gpio_driver);
}
device_initcall(lnw_gpio_init);
CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true),
CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true),
CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true),
+ CLK_MGT_ENTRY(BML8580CLK, PLL_DIV, true),
CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true),
CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true),
CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true),
/* CPU FREQ table, may be changed due to if MAX_OPP is supported. */
static struct cpufreq_frequency_table db8500_cpufreq_table[] = {
- { .frequency = 200000, .index = ARM_EXTCLK,},
- { .frequency = 400000, .index = ARM_50_OPP,},
- { .frequency = 800000, .index = ARM_100_OPP,},
+ { .frequency = 200000, .driver_data = ARM_EXTCLK,},
+ { .frequency = 400000, .driver_data = ARM_50_OPP,},
+ { .frequency = 800000, .driver_data = ARM_100_OPP,},
{ .frequency = CPUFREQ_TABLE_END,}, /* To be used for MAX_OPP. */
{ .frequency = CPUFREQ_TABLE_END,},
};
return -EINVAL;
/* Set the new arm opp. */
- return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].index);
+ return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].driver_data);
}
static int set_plldsi_rate(unsigned long rate)
{
if (prcmu_has_arm_maxopp()) {
db8500_cpufreq_table[3].frequency = 1000000;
- db8500_cpufreq_table[3].index = ARM_MAX_OPP;
+ db8500_cpufreq_table[3].driver_data = ARM_MAX_OPP;
}
}
#include <asm/cacheflush.h>
#include <linux/fdtable.h>
#include <linux/file.h>
+ #include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
list_del(&buffer->entry);
if (free_page_start || free_page_end) {
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %p do not share page%s%s with with %p or %p\n",
+ "%d: merge free, buffer %p do not share page%s%s with %p or %p\n",
proc->pid, buffer, free_page_start ? "" : " end",
free_page_end ? "" : " start", prev, next);
binder_update_page_range(proc, 0, free_page_start ?
if (!binder_has_proc_work(proc, thread))
ret = -EAGAIN;
} else
- ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
+ ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
} else {
if (non_block) {
if (!binder_has_thread_work(thread))
ret = -EAGAIN;
} else
- ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
+ ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
}
binder_lock(__func__);
* be triggered
*/
+#if defined(CONFIG_SERIAL_MFD_HSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/console.h>
#ifdef CONFIG_PM_RUNTIME
static int serial_hsu_runtime_idle(struct device *dev)
{
- int err;
-
- err = pm_schedule_suspend(dev, 500);
- if (err)
- return -EBUSY;
-
- return 0;
+ pm_schedule_suspend(dev, 500);
+ return -EBUSY;
}
static int serial_hsu_runtime_suspend(struct device *dev)
usb_autopm_get_interface(intf);
set_bit(port1, hub->busy_bits);
- retval = usb_hub_set_port_power(hdev, port1, true);
+ retval = usb_hub_set_port_power(hdev, hub, port1, true);
if (port_dev->child && !retval) {
/*
* Wait for usb hub port to be reconnected in order to make
usb_autopm_get_interface(intf);
set_bit(port1, hub->busy_bits);
- retval = usb_hub_set_port_power(hdev, port1, false);
+ retval = usb_hub_set_port_power(hdev, hub, port1, false);
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION);
usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE);
clear_bit(port1, hub->busy_bits);
#ifdef CONFIG_PM_RUNTIME
.runtime_suspend = usb_port_runtime_suspend,
.runtime_resume = usb_port_runtime_resume,
- .runtime_idle = pm_generic_runtime_idle,
#endif
};
{
int error;
- error = wait_event_freezekillable(server->response_q,
+ error = wait_event_freezekillable_unsafe(server->response_q,
midQ->mid_state != MID_REQUEST_SUBMITTED);
if (error < 0)
return -ERESTARTSYS;
struct mid_q_entry *mid;
/* enable signing if server requires it */
- if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ if (server->sign)
hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
mid = AllocMidQEntry(hdr, server);
dump_smb(mid->resp_buf, min_t(u32, 92, len));
/* convert the length into a more usable form */
- if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
+ if (server->sign) {
struct kvec iov;
int rc = 0;
struct smb_rqst rqst = { .rq_iov = &iov,
* the specific driver's probe to initial the matched device.
* @remove: Called when a device removed from this bus.
* @shutdown: Called at shut-down time to quiesce the device.
+ *
+ * @online: Called to put the device back online (after offlining it).
+ * @offline: Called to put the device offline for hot-removal. May fail.
+ *
* @suspend: Called when a device on this bus wants to go to sleep mode.
* @resume: Called to bring a device on this bus out of sleep mode.
* @pm: Power management operations of this bus, callback the specific
* bus-specific setup
* @p: The private data of the driver core, only the driver core can
* touch this.
+ * @lock_key: Lock class key for use by the lock validator
*
* A bus is a channel between the processor and one or more devices. For the
* purposes of the device model, all devices are connected via a bus, even if
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
+ int (*online)(struct device *dev);
+ int (*offline)(struct device *dev);
+
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
* segment limitations.
* @dma_pools: Dma pools (if dma'ble device).
* @dma_mem: Internal for coherent mem override.
+ * @cma_area: Contiguous memory area for dma allocations
* @archdata: For arch-specific additions.
* @of_node: Associated device tree node.
* @acpi_node: Associated ACPI device node.
* @release: Callback to free the device after all references have
* gone away. This should be set by the allocator of the
* device (i.e. the bus driver that discovered the device).
+ * @iommu_group: IOMMU group the device belongs to.
+ *
+ * @offline_disabled: If set, the device is permanently online.
+ * @offline: Set after successful invocation of bus type's .offline().
+ *
* At the lowest level, every device in a Linux system is represented by an
* instance of struct device. The device structure contains the information
* that the device model core needs to model the system. Most subsystems,
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
+
+ bool offline_disabled:1;
+ bool offline:1;
};
static inline struct device *kobj_to_dev(struct kobject *kobj)
extern void *dev_get_drvdata(const struct device *dev);
extern int dev_set_drvdata(struct device *dev, void *data);
+ static inline bool device_supports_offline(struct device *dev)
+ {
+ return dev->bus && dev->bus->offline && dev->bus->online;
+ }
+
+ extern void lock_device_hotplug(void);
+ extern void unlock_device_hotplug(void);
+ extern int device_offline(struct device *dev);
+ extern int device_online(struct device *dev);
/*
* Root device objects for grouping under /sys/devices
*/