From 1d4a9c17d4d204a159139361e8d4db7f9f267879 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Sun, 22 Feb 2015 21:16:49 -0800 Subject: PM / sleep: add configurable delay for pm_test When CONFIG_PM_DEBUG=y, we provide a sysfs file (/sys/power/pm_test) for selecting one of a few suspend test modes, where rather than entering a full suspend state, the kernel will perform some subset of suspend steps, wait 5 seconds, and then resume back to normal operation. This mode is useful for (among other things) observing the state of the system just before entering a sleep mode, for debugging or analysis purposes. However, a constant 5 second wait is not sufficient for some sorts of analysis; for example, on an SoC, one might want to use external tools to probe the power states of various on-chip controllers or clocks. This patch turns this 5 second delay into a configurable module parameter, so users can determine how long to wait in this pseudo-suspend state before resuming the system. Example (wait 30 seconds); # echo 30 > /sys/module/suspend/parameters/pm_test_delay # echo core > /sys/power/pm_test # time echo mem > /sys/power/state ... [ 17.583625] suspend debug: Waiting for 30 second(s). ... real 0m30.381s user 0m0.017s sys 0m0.080s Signed-off-by: Brian Norris Acked-by: Pavel Machek Reviewed-by: Kevin Cernekee Acked-by: Florian Fainelli Signed-off-by: Rafael J. Wysocki --- Documentation/kernel-parameters.txt | 7 +++++++ Documentation/power/basic-pm-debugging.txt | 10 ++++++---- kernel/power/suspend.c | 13 +++++++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index bfcb1a62a7b4..8b1fa5e129ac 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -3462,6 +3462,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. improve throughput, but will also increase the amount of memory reserved for use by the client. + suspend.pm_test_delay= + [SUSPEND] + Sets the number of seconds to remain in a suspend test + mode before resuming the system (see + /sys/power/pm_test). Only available when CONFIG_PM_DEBUG + is set. Default value is 5. + swapaccount=[0|1] [KNL] Enable accounting of swap in memory resource controller if no parameter or 1 is given or disable diff --git a/Documentation/power/basic-pm-debugging.txt b/Documentation/power/basic-pm-debugging.txt index edeecd447d23..b96098ccfe69 100644 --- a/Documentation/power/basic-pm-debugging.txt +++ b/Documentation/power/basic-pm-debugging.txt @@ -75,12 +75,14 @@ you should do the following: # echo platform > /sys/power/disk # echo disk > /sys/power/state -Then, the kernel will try to freeze processes, suspend devices, wait 5 seconds, -resume devices and thaw processes. If "platform" is written to +Then, the kernel will try to freeze processes, suspend devices, wait a few +seconds (5 by default, but configurable by the suspend.pm_test_delay module +parameter), resume devices and thaw processes. If "platform" is written to /sys/power/pm_test , then after suspending devices the kernel will additionally invoke the global control methods (eg. ACPI global control methods) used to -prepare the platform firmware for hibernation. Next, it will wait 5 seconds and -invoke the platform (eg. ACPI) global methods used to cancel hibernation etc. +prepare the platform firmware for hibernation. Next, it will wait a +configurable number of seconds and invoke the platform (eg. ACPI) global +methods used to cancel hibernation etc. Writing "none" to /sys/power/pm_test causes the kernel to switch to the normal hibernation/suspend operations. Also, when open for reading, /sys/power/pm_test diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index b7d6b3a721b1..8d7a1ef72758 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "power.h" @@ -233,12 +234,20 @@ static bool platform_suspend_again(suspend_state_t state) suspend_ops->suspend_again() : false; } +#ifdef CONFIG_PM_DEBUG +static unsigned int pm_test_delay = 5; +module_param(pm_test_delay, uint, 0644); +MODULE_PARM_DESC(pm_test_delay, + "Number of seconds to wait before resuming from suspend test"); +#endif + static int suspend_test(int level) { #ifdef CONFIG_PM_DEBUG if (pm_test_level == level) { - printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n"); - mdelay(5000); + printk(KERN_INFO "suspend debug: Waiting for %d second(s).\n", + pm_test_delay); + mdelay(pm_test_delay * 1000); return 1; } #endif /* !CONFIG_PM_DEBUG */ -- cgit From 3535a3c126651616a111491726c241e801fd9418 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 27 Feb 2015 14:48:15 +0800 Subject: ACPI / EC: Cleanup logging/debugging splitter support. This patch refines logging/debugging splitter support so that when DEBUG is disabled, splitters won't be visible in the kernel logs while they are still available for developers when DEBUG is enabled. This patch also refines the splitters to mark the following handling process boundaries: +++++: boundary of driver starting/stopping boundary of IRQ storming =====: boundary of transaction advancement *****: boundary of EC command boundary of EC query #####: boundary of EC _Qxx evaluation The following 2 log entries are originally logged using pr_info() in order to be used as the boot/suspend/resume log entries for the EC device, this patch also restores them to pr_info() logging level: ACPI : EC: EC started ACPI : EC: EC stopped In this patch, one log entry around "Polling quirk" is converted into ec_dbg_raw() which doesn't contain the boundary marker. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 108 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 34 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a8dd2f763382..07426c8c255b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -136,6 +136,48 @@ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ +/* -------------------------------------------------------------------------- + * Logging/Debugging + * -------------------------------------------------------------------------- */ + +/* + * Splitters used by the developers to track the boundary of the EC + * handling processes. + */ +#ifdef DEBUG +#define EC_DBG_SEP " " +#define EC_DBG_DRV "+++++" +#define EC_DBG_STM "=====" +#define EC_DBG_REQ "*****" +#define EC_DBG_EVT "#####" +#else +#define EC_DBG_SEP "" +#define EC_DBG_DRV +#define EC_DBG_STM +#define EC_DBG_REQ +#define EC_DBG_EVT +#endif + +#define ec_log_raw(fmt, ...) \ + pr_info(fmt "\n", ##__VA_ARGS__) +#define ec_dbg_raw(fmt, ...) \ + pr_debug(fmt "\n", ##__VA_ARGS__) +#define ec_log(filter, fmt, ...) \ + ec_log_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__) +#define ec_dbg(filter, fmt, ...) \ + ec_dbg_raw(filter EC_DBG_SEP fmt EC_DBG_SEP filter, ##__VA_ARGS__) + +#define ec_log_drv(fmt, ...) \ + ec_log(EC_DBG_DRV, fmt, ##__VA_ARGS__) +#define ec_dbg_drv(fmt, ...) \ + ec_dbg(EC_DBG_DRV, fmt, ##__VA_ARGS__) +#define ec_dbg_stm(fmt, ...) \ + ec_dbg(EC_DBG_STM, fmt, ##__VA_ARGS__) +#define ec_dbg_req(fmt, ...) \ + ec_dbg(EC_DBG_REQ, fmt, ##__VA_ARGS__) +#define ec_dbg_evt(fmt, ...) \ + ec_dbg(EC_DBG_EVT, fmt, ##__VA_ARGS__) + /* -------------------------------------------------------------------------- * Device Flags * -------------------------------------------------------------------------- */ @@ -159,14 +201,14 @@ static inline u8 acpi_ec_read_status(struct acpi_ec *ec) { u8 x = inb(ec->command_addr); - pr_debug("EC_SC(R) = 0x%2.2x " - "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d\n", - x, - !!(x & ACPI_EC_FLAG_SCI), - !!(x & ACPI_EC_FLAG_BURST), - !!(x & ACPI_EC_FLAG_CMD), - !!(x & ACPI_EC_FLAG_IBF), - !!(x & ACPI_EC_FLAG_OBF)); + ec_dbg_raw("EC_SC(R) = 0x%2.2x " + "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d", + x, + !!(x & ACPI_EC_FLAG_SCI), + !!(x & ACPI_EC_FLAG_BURST), + !!(x & ACPI_EC_FLAG_CMD), + !!(x & ACPI_EC_FLAG_IBF), + !!(x & ACPI_EC_FLAG_OBF)); return x; } @@ -175,20 +217,20 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec) u8 x = inb(ec->data_addr); ec->curr->timestamp = jiffies; - pr_debug("EC_DATA(R) = 0x%2.2x\n", x); + ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x); return x; } static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) { - pr_debug("EC_SC(W) = 0x%2.2x\n", command); + ec_dbg_raw("EC_SC(W) = 0x%2.2x", command); outb(command, ec->command_addr); ec->curr->timestamp = jiffies; } static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) { - pr_debug("EC_DATA(W) = 0x%2.2x\n", data); + ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data); outb(data, ec->data_addr); ec->curr->timestamp = jiffies; } @@ -240,7 +282,7 @@ static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open) * software need to manually trigger a pseudo GPE event on * EN=1 writes. */ - pr_debug("***** Polling quirk *****\n"); + ec_dbg_raw("Polling quirk"); advance_transaction(ec); } } @@ -299,7 +341,7 @@ static void acpi_ec_set_storm(struct acpi_ec *ec, u8 flag) { if (!test_bit(flag, &ec->flags)) { acpi_ec_disable_gpe(ec, false); - pr_debug("+++++ Polling enabled +++++\n"); + ec_dbg_drv("Polling enabled"); set_bit(flag, &ec->flags); } } @@ -309,7 +351,7 @@ static void acpi_ec_clear_storm(struct acpi_ec *ec, u8 flag) if (test_bit(flag, &ec->flags)) { clear_bit(flag, &ec->flags); acpi_ec_enable_gpe(ec, false); - pr_debug("+++++ Polling disabled +++++\n"); + ec_dbg_drv("Polling disabled"); } } @@ -335,7 +377,7 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec) static void acpi_ec_submit_query(struct acpi_ec *ec) { if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { - pr_debug("***** Event started *****\n"); + ec_dbg_req("Event started"); schedule_work(&ec->work); } } @@ -344,7 +386,7 @@ static void acpi_ec_complete_query(struct acpi_ec *ec) { if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); - pr_debug("***** Event stopped *****\n"); + ec_dbg_req("Event stopped"); } } @@ -366,8 +408,8 @@ static void advance_transaction(struct acpi_ec *ec) u8 status; bool wakeup = false; - pr_debug("===== %s (%d) =====\n", - in_interrupt() ? "IRQ" : "TASK", smp_processor_id()); + ec_dbg_stm("%s (%d)", in_interrupt() ? "IRQ" : "TASK", + smp_processor_id()); /* * By always clearing STS before handling all indications, we can * ensure a hardware STS 0->1 change after this clearing can always @@ -390,8 +432,8 @@ static void advance_transaction(struct acpi_ec *ec) if (t->rlen == t->ri) { t->flags |= ACPI_EC_COMMAND_COMPLETE; if (t->command == ACPI_EC_COMMAND_QUERY) - pr_debug("***** Command(%s) hardware completion *****\n", - acpi_ec_cmd_string(t->command)); + ec_dbg_req("Command(%s) hardware completion", + acpi_ec_cmd_string(t->command)); wakeup = true; } } else @@ -410,8 +452,8 @@ static void advance_transaction(struct acpi_ec *ec) acpi_ec_complete_query(ec); t->rdata[t->ri++] = 0x00; t->flags |= ACPI_EC_COMMAND_COMPLETE; - pr_debug("***** Command(%s) software completion *****\n", - acpi_ec_cmd_string(t->command)); + ec_dbg_req("Command(%s) software completion", + acpi_ec_cmd_string(t->command)); wakeup = true; } else if ((status & ACPI_EC_FLAG_IBF) == 0) { acpi_ec_write_cmd(ec, t->command); @@ -504,16 +546,14 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, } /* following two actions should be kept atomic */ ec->curr = t; - pr_debug("***** Command(%s) started *****\n", - acpi_ec_cmd_string(t->command)); + ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command)); start_transaction(ec); spin_unlock_irqrestore(&ec->lock, tmp); ret = ec_poll(ec); spin_lock_irqsave(&ec->lock, tmp); if (t->irq_count == ec_storm_threshold) acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM); - pr_debug("***** Command(%s) stopped *****\n", - acpi_ec_cmd_string(t->command)); + ec_dbg_req("Command(%s) stopped", acpi_ec_cmd_string(t->command)); ec->curr = NULL; /* Disable GPE for command processing (IBF=0/OBF=1) */ acpi_ec_complete_request(ec); @@ -676,11 +716,11 @@ static void acpi_ec_start(struct acpi_ec *ec, bool resuming) spin_lock_irqsave(&ec->lock, flags); if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { - pr_debug("+++++ Starting EC +++++\n"); + ec_dbg_drv("Starting EC"); /* Enable GPE for event processing (SCI_EVT=1) */ if (!resuming) acpi_ec_submit_request(ec); - pr_debug("EC started\n"); + ec_log_drv("EC started"); } spin_unlock_irqrestore(&ec->lock, flags); } @@ -702,7 +742,7 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) spin_lock_irqsave(&ec->lock, flags); if (acpi_ec_started(ec)) { - pr_debug("+++++ Stopping EC +++++\n"); + ec_dbg_drv("Stopping EC"); set_bit(EC_FLAGS_STOPPED, &ec->flags); spin_unlock_irqrestore(&ec->lock, flags); wait_event(ec->wait, acpi_ec_stopped(ec)); @@ -712,7 +752,7 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) acpi_ec_complete_request(ec); clear_bit(EC_FLAGS_STARTED, &ec->flags); clear_bit(EC_FLAGS_STOPPED, &ec->flags); - pr_debug("EC stopped\n"); + ec_log_drv("EC stopped"); } spin_unlock_irqrestore(&ec->lock, flags); } @@ -824,12 +864,12 @@ static void acpi_ec_run(void *cxt) if (!handler) return; - pr_debug("##### Query(0x%02x) started #####\n", handler->query_bit); + ec_dbg_evt("Query(0x%02x) started", handler->query_bit); if (handler->func) handler->func(handler->data); else if (handler->handle) acpi_evaluate_object(handler->handle, NULL, NULL, NULL); - pr_debug("##### Query(0x%02x) stopped #####\n", handler->query_bit); + ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit); acpi_ec_put_query_handler(handler); } @@ -861,8 +901,8 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) if (value == handler->query_bit) { /* have custom handler for this bit */ handler = acpi_ec_get_query_handler(handler); - pr_debug("##### Query(0x%02x) scheduled #####\n", - handler->query_bit); + ec_dbg_evt("Query(0x%02x) scheduled", + handler->query_bit); status = acpi_os_execute((handler->func) ? OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, acpi_ec_run, handler); -- cgit From 770970f0b40a7c303765f0593acd4ceeb54831f7 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 27 Feb 2015 14:48:24 +0800 Subject: ACPI / EC: Add GPE reference counting debugging messages. This patch enhances debugging with the GPE reference count messages added. This kind of log entries can be used by the platform validators to validate if there is an EC transaction broken because of firmware/driver bugs. No functional changes. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 07426c8c255b..a362f20c6c8b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -177,6 +177,8 @@ static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ ec_dbg(EC_DBG_REQ, fmt, ##__VA_ARGS__) #define ec_dbg_evt(fmt, ...) \ ec_dbg(EC_DBG_EVT, fmt, ##__VA_ARGS__) +#define ec_dbg_ref(ec, fmt, ...) \ + ec_dbg_raw("%lu: " fmt, ec->reference_count, ## __VA_ARGS__) /* -------------------------------------------------------------------------- * Device Flags @@ -544,6 +546,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, ret = -EINVAL; goto unlock; } + ec_dbg_ref(ec, "Increase command"); /* following two actions should be kept atomic */ ec->curr = t; ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command)); @@ -557,6 +560,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, ec->curr = NULL; /* Disable GPE for command processing (IBF=0/OBF=1) */ acpi_ec_complete_request(ec); + ec_dbg_ref(ec, "Decrease command"); unlock: spin_unlock_irqrestore(&ec->lock, tmp); return ret; @@ -718,8 +722,10 @@ static void acpi_ec_start(struct acpi_ec *ec, bool resuming) if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { ec_dbg_drv("Starting EC"); /* Enable GPE for event processing (SCI_EVT=1) */ - if (!resuming) + if (!resuming) { acpi_ec_submit_request(ec); + ec_dbg_ref(ec, "Increase driver"); + } ec_log_drv("EC started"); } spin_unlock_irqrestore(&ec->lock, flags); @@ -748,8 +754,10 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) wait_event(ec->wait, acpi_ec_stopped(ec)); spin_lock_irqsave(&ec->lock, flags); /* Disable GPE for event processing (SCI_EVT=1) */ - if (!suspending) + if (!suspending) { acpi_ec_complete_request(ec); + ec_dbg_ref(ec, "Decrease driver"); + } clear_bit(EC_FLAGS_STARTED, &ec->flags); clear_bit(EC_FLAGS_STOPPED, &ec->flags); ec_log_drv("EC stopped"); -- cgit From 7b1a13228b321ea27bd53070bcd332417069ace8 Mon Sep 17 00:00:00 2001 From: Nan Li Date: Wed, 4 Mar 2015 18:48:35 +0800 Subject: ACPI / sysfs: Treat the count field of counter_show() as unsigned The count field is an unsigned 32bit value, and the counter_show() function should also treat it as a unsigned value. Otherwise the counter may show negative number as we found on a machine: ... gpe23: 0 invalid gpe24: -2071733 enabled gpe25: 0 invalid ... gpe_all: -2070980 sci: -2070949 Signed-off-by: Nan Li Signed-off-by: Lee, Chun-Yi Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 13e577c80201..0876d77b3206 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -527,7 +527,7 @@ static ssize_t counter_show(struct kobject *kobj, acpi_irq_not_handled; all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count = acpi_gpe_count; - size = sprintf(buf, "%8d", all_counters[index].count); + size = sprintf(buf, "%8u", all_counters[index].count); /* "gpe_all" or "sci" */ if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) -- cgit From eea97aed815ac25b9435a556fce345d257f47405 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Thu, 12 Mar 2015 10:33:46 -0700 Subject: MAINTAINERS: add entry for Generic PM domains (genpd) Add entry for genpd with Rafael, myself and Ulf as co-maintainers. Signed-off-by: Kevin Hilman Acked-by: Geert Uytterhoeven Signed-off-by: Rafael J. Wysocki --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6239a305dff0..4c4a03f230f3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4294,6 +4294,15 @@ S: Supported F: drivers/phy/ F: include/linux/phy/ +GENERIC PM DOMAINS +M: "Rafael J. Wysocki" +M: Kevin Hilman +M: Ulf Hansson +L: linux-pm@vger.kernel.org +S: Supported +F: drivers/base/power/domain*.c +F: include/linux/pm_domain.h + GENERIC UIO DRIVER FOR PCI DEVICES M: "Michael S. Tsirkin" L: kvm@vger.kernel.org -- cgit From 8a5de52afc5f9a20a356e49351292d098cc455b2 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Mon, 16 Mar 2015 17:35:18 +0800 Subject: ACPI / blacklist: Disable Vista compatibility for Sony VGN-SR19XN. Sony VGN-SR19XN laptop needs to disable windows vista compatibility, or else it freezes when plugging/unplugging the VGA connector. Link: https://bugzilla.kernel.org/show_bug.cgi?id=66771 Tested-by: Lionel Duriez Reviewed-by: Zhang Rui Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/blacklist.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 9b693d54c743..1d1791935c31 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -215,6 +215,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { }, { .callback = dmi_disable_osi_vista, + .ident = "VGN-SR19XN", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"), + }, + }, + { + .callback = dmi_disable_osi_vista, .ident = "Toshiba Satellite L355", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), -- cgit From 2eb1eb02dda368fb224bf5a379d2448c742b71db Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 13 Mar 2015 01:45:49 +0100 Subject: PNP / ACPI: Use ACPI_COMPANION_SET() during initialization pnpacpi_add_device() calls acpi_bind_one() on an already registered device, which is a mistake, but it can initialize the ACPI companion field of the struct device to be registered using ACPI_COMPANION_SET() instead, so make it do that. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg --- drivers/pnp/pnpacpi/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index d2b780aade89..5153d1d69aee 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -248,6 +248,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device) if (!dev) return -ENOMEM; + ACPI_COMPANION_SET(&dev->dev, device); dev->data = device; /* .enabled means the device can decode the resources */ dev->active = device->status.enabled; @@ -290,11 +291,9 @@ static int __init pnpacpi_add_device(struct acpi_device *device) return error; } - error = acpi_bind_one(&dev->dev, device); - num++; - return error; + return 0; } static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, -- cgit From ce793486e23e0162a732c605189c8028e0910e86 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 16 Mar 2015 23:49:03 +0100 Subject: driver core / ACPI: Represent ACPI companions using fwnode_handle Now that we have struct fwnode_handle, we can use that to point to ACPI companions from struct device objects instead of pointing to struct acpi_device directly. There are two benefits from that. First, the somewhat ugly and hackish struct acpi_dev_node can be dropped and, second, the same struct fwnode_handle pointer can be used in the future to point to other (non-ACPI) firmware device node types. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Acked-by: Grant Likely --- drivers/acpi/acpi_platform.c | 2 +- drivers/acpi/dock.c | 2 +- drivers/base/platform.c | 2 +- drivers/gpio/gpiolib.h | 2 ++ drivers/i2c/i2c-core.c | 4 ++-- include/acpi/acpi_bus.h | 3 ++- include/linux/acpi.h | 5 +++-- include/linux/device.h | 13 +++---------- include/linux/fwnode.h | 25 +++++++++++++++++++++++++ include/linux/i2c.h | 4 ++-- include/linux/platform_device.h | 2 +- include/linux/property.h | 11 +---------- 12 files changed, 44 insertions(+), 31 deletions(-) create mode 100644 include/linux/fwnode.h diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 1284138e42ab..4bf75597f732 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -102,7 +102,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) pdevinfo.id = -1; pdevinfo.res = resources; pdevinfo.num_res = count; - pdevinfo.acpi_node.companion = adev; + pdevinfo.fwnode = acpi_fwnode_handle(adev); pdevinfo.dma_mask = DMA_BIT_MASK(32); pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index d9339b442a4e..a688aa243f6c 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -615,7 +615,7 @@ void acpi_dock_add(struct acpi_device *adev) memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.name = "dock"; pdevinfo.id = dock_station_count; - pdevinfo.acpi_node.companion = adev; + pdevinfo.fwnode = acpi_fwnode_handle(adev); pdevinfo.data = &ds; pdevinfo.size_data = sizeof(ds); dd = platform_device_register_full(&pdevinfo); diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 9421fed40905..17f0204fabef 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -454,7 +454,7 @@ struct platform_device *platform_device_register_full( goto err_alloc; pdev->dev.parent = pdevinfo->parent; - ACPI_COMPANION_SET(&pdev->dev, pdevinfo->acpi_node.companion); + pdev->dev.fwnode = pdevinfo->fwnode; if (pdevinfo->dma_mask) { /* diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 550a5eafbd38..ab892be26dc2 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -17,6 +17,8 @@ enum of_gpio_flags; +struct acpi_device; + /** * struct acpi_gpio_info - ACPI GPIO specific information * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index edf274cabe81..c87c31387e2d 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -133,7 +133,7 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, return AE_OK; memset(&info, 0, sizeof(info)); - info.acpi_node.companion = adev; + info.fwnode = acpi_fwnode_handle(adev); info.irq = -1; INIT_LIST_HEAD(&resource_list); @@ -971,7 +971,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; client->dev.of_node = info->of_node; - ACPI_COMPANION_SET(&client->dev, info->acpi_node.companion); + client->dev.fwnode = info->fwnode; i2c_dev_set_name(adap, client); status = device_register(&client->dev); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 61e32ec1fc4d..dae2a16fe93f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -386,7 +386,8 @@ static inline bool is_acpi_node(struct fwnode_handle *fwnode) static inline struct acpi_device *acpi_node(struct fwnode_handle *fwnode) { - return fwnode ? container_of(fwnode, struct acpi_device, fwnode) : NULL; + return is_acpi_node(fwnode) ? + container_of(fwnode, struct acpi_device, fwnode) : NULL; } static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 24c7aa8b1d20..402ddbdc2da1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -53,8 +53,9 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) return adev ? adev->handle : NULL; } -#define ACPI_COMPANION(dev) ((dev)->acpi_node.companion) -#define ACPI_COMPANION_SET(dev, adev) ACPI_COMPANION(dev) = (adev) +#define ACPI_COMPANION(dev) acpi_node((dev)->fwnode) +#define ACPI_COMPANION_SET(dev, adev) (dev)->fwnode = (adev) ? \ + acpi_fwnode_handle(adev) : NULL #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) static inline void acpi_preset_companion(struct device *dev, diff --git a/include/linux/device.h b/include/linux/device.h index 0eb8ee2dc6d1..badef20b876a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -38,6 +38,7 @@ struct class; struct subsys_private; struct bus_type; struct device_node; +struct fwnode_handle; struct iommu_ops; struct iommu_group; @@ -650,14 +651,6 @@ struct device_dma_parameters { unsigned long segment_boundary_mask; }; -struct acpi_device; - -struct acpi_dev_node { -#ifdef CONFIG_ACPI - struct acpi_device *companion; -#endif -}; - /** * struct device - The basic device structure * @parent: The device's "parent" device, the device to which it is attached. @@ -703,7 +696,7 @@ struct acpi_dev_node { * @cma_area: Contiguous memory area for dma allocations * @archdata: For arch-specific additions. * @of_node: Associated device tree node. - * @acpi_node: Associated ACPI device node. + * @fwnode: Associated device node supplied by platform firmware. * @devt: For creating the sysfs "dev". * @id: device instance * @devres_lock: Spinlock to protect the resource of the device. @@ -779,7 +772,7 @@ struct device { struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ - struct acpi_dev_node acpi_node; /* associated ACPI device node */ + struct fwnode_handle *fwnode; /* firmware device node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h new file mode 100644 index 000000000000..17bb5f039509 --- /dev/null +++ b/include/linux/fwnode.h @@ -0,0 +1,25 @@ +/* + * fwnode.h - Firmware device node object handle type definition. + * + * Copyright (C) 2015, Intel Corporation + * Author: Rafael J. Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_FWNODE_H_ +#define _LINUX_FWNODE_H_ + +enum fwnode_type { + FWNODE_INVALID = 0, + FWNODE_OF, + FWNODE_ACPI, +}; + +struct fwnode_handle { + enum fwnode_type type; +}; + +#endif diff --git a/include/linux/i2c.h b/include/linux/i2c.h index f17da50402a4..6d89575361a8 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -278,7 +278,7 @@ static inline int i2c_slave_event(struct i2c_client *client, * @platform_data: stored in i2c_client.dev.platform_data * @archdata: copied into i2c_client.dev.archdata * @of_node: pointer to OpenFirmware device node - * @acpi_node: ACPI device node + * @fwnode: device node supplied by the platform firmware * @irq: stored in i2c_client.irq * * I2C doesn't actually support hardware probing, although controllers and @@ -299,7 +299,7 @@ struct i2c_board_info { void *platform_data; struct dev_archdata *archdata; struct device_node *of_node; - struct acpi_dev_node acpi_node; + struct fwnode_handle *fwnode; int irq; }; diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index ae4882ca4a64..58f1e75ba105 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -59,7 +59,7 @@ extern int platform_add_devices(struct platform_device **, int); struct platform_device_info { struct device *parent; - struct acpi_dev_node acpi_node; + struct fwnode_handle *fwnode; const char *name; int id; diff --git a/include/linux/property.h b/include/linux/property.h index a6a3d98bd7e9..31dfd3db35d6 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -13,6 +13,7 @@ #ifndef _LINUX_PROPERTY_H_ #define _LINUX_PROPERTY_H_ +#include #include struct device; @@ -40,16 +41,6 @@ int device_property_read_string_array(struct device *dev, const char *propname, int device_property_read_string(struct device *dev, const char *propname, const char **val); -enum fwnode_type { - FWNODE_INVALID = 0, - FWNODE_OF, - FWNODE_ACPI, -}; - -struct fwnode_handle { - enum fwnode_type type; -}; - bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname); int fwnode_property_read_u8_array(struct fwnode_handle *fwnode, const char *propname, u8 *val, -- cgit From ca5b74d2675a44f54aacb919c1cf022463e2f738 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 16 Mar 2015 23:49:08 +0100 Subject: ACPI: Introduce has_acpi_companion() Now that the ACPI companions of devices are represented by pointers to struct fwnode_handle, it is not quite efficient to check whether or not an ACPI companion of a device is present by evaluating the ACPI_COMPANION() macro. For this reason, introduce a special static inline routine for that, has_acpi_companion(), and update the code to use it where applicable. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/glue.c | 4 ++-- drivers/i2c/busses/i2c-designware-platdrv.c | 4 ++-- drivers/iommu/intel-iommu.c | 2 +- include/linux/acpi.h | 10 ++++++++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index f774c65ecb8b..39c485b0c25c 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -168,7 +168,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) unsigned int node_id; int retval = -EINVAL; - if (ACPI_COMPANION(dev)) { + if (has_acpi_companion(dev)) { if (acpi_dev) { dev_warn(dev, "ACPI companion already set\n"); return -EINVAL; @@ -220,7 +220,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) list_add(&physical_node->node, physnode_list); acpi_dev->physical_node_count++; - if (!ACPI_COMPANION(dev)) + if (!has_acpi_companion(dev)) ACPI_COMPANION_SET(dev, acpi_dev); acpi_physnode_link_name(physical_node_name, node_id); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index c270f5f9a8f9..538d6910b550 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -166,7 +166,7 @@ static int dw_i2c_probe(struct platform_device *pdev) /* fast mode by default because of legacy reasons */ clk_freq = 400000; - if (ACPI_COMPANION(&pdev->dev)) { + if (has_acpi_companion(&pdev->dev)) { dw_i2c_acpi_configure(pdev); } else if (pdev->dev.of_node) { of_property_read_u32(pdev->dev.of_node, @@ -286,7 +286,7 @@ static int dw_i2c_remove(struct platform_device *pdev) pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - if (ACPI_COMPANION(&pdev->dev)) + if (has_acpi_companion(&pdev->dev)) dw_i2c_acpi_unconfigure(pdev); return 0; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ae4c1a854e57..591b84331315 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -684,7 +684,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf if (dev_is_pci(dev)) { pdev = to_pci_dev(dev); segment = pci_domain_nr(pdev->bus); - } else if (ACPI_COMPANION(dev)) + } else if (has_acpi_companion(dev)) dev = &ACPI_COMPANION(dev)->dev; rcu_read_lock(); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 402ddbdc2da1..ec488d03b518 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -58,6 +58,11 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) acpi_fwnode_handle(adev) : NULL #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) +static inline bool has_acpi_companion(struct device *dev) +{ + return is_acpi_node(dev->fwnode); +} + static inline void acpi_preset_companion(struct device *dev, struct acpi_device *parent, u64 addr) { @@ -472,6 +477,11 @@ static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev) return NULL; } +static inline bool has_acpi_companion(struct device *dev) +{ + return false; +} + static inline const char *acpi_dev_name(struct acpi_device *adev) { return NULL; -- cgit From 56b858dfad9b97aea88f9fe87b1468ddaf154c94 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 17 Mar 2015 12:40:12 +0200 Subject: ACPI: Update GPIO documentation to mention _DSD Make sure that the ACPI enumeration.txt provides latest information on how to describe and retrieve GPIOs now that we can take advantage of _DSD device properties. Signed-off-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- Documentation/acpi/enumeration.txt | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index 9b121a569ab4..750401f91341 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -254,8 +254,13 @@ GPIO support ~~~~~~~~~~~~ ACPI 5 introduced two new resources to describe GPIO connections: GpioIo and GpioInt. These resources are used be used to pass GPIO numbers used by -the device to the driver. For example: +the device to the driver. ACPI 5.1 extended this with _DSD (Device +Specific Data) which made it possible to name the GPIOs among other things. +For example: + +Device (DEV) +{ Method (_CRS, 0, NotSerialized) { Name (SBUF, ResourceTemplate() @@ -285,6 +290,18 @@ the device to the driver. For example: Return (SBUF) } + // ACPI 5.1 _DSD used for naming the GPIOs + Name (_DSD, Package () + { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () + { + Package () {"power-gpios", Package() {^DEV, 0, 0, 0 }}, + Package () {"irq-gpios", Package() {^DEV, 1, 0, 0 }}, + } + }) + ... + These GPIO numbers are controller relative and path "\\_SB.PCI0.GPI0" specifies the path to the controller. In order to use these GPIOs in Linux we need to translate them to the corresponding Linux GPIO descriptors. @@ -300,11 +317,11 @@ a code like this: struct gpio_desc *irq_desc, *power_desc; - irq_desc = gpiod_get_index(dev, NULL, 1); + irq_desc = gpiod_get(dev, "irq"); if (IS_ERR(irq_desc)) /* handle error */ - power_desc = gpiod_get_index(dev, NULL, 0); + power_desc = gpiod_get(dev, "power"); if (IS_ERR(power_desc)) /* handle error */ @@ -313,6 +330,9 @@ a code like this: There are also devm_* versions of these functions which release the descriptors once the device is released. +See Documentation/acpi/gpio-properties.txt for more information about the +_DSD binding related to GPIOs. + MFD devices ~~~~~~~~~~~ The MFD devices register their children as platform devices. For the child -- cgit From 359597cb374b7935bf804cde590ac55f9cb275b7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 17 Mar 2015 11:48:30 +0200 Subject: ACPI: Add acpi_device_uid() for convenience Add a nicer way to get the ACPI _UID. Signed-off-by: Adrian Hunter Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 61e32ec1fc4d..f8009d99190a 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -252,6 +252,7 @@ struct acpi_device_pnp { #define acpi_device_bid(d) ((d)->pnp.bus_id) #define acpi_device_adr(d) ((d)->pnp.bus_address) const char *acpi_device_hid(struct acpi_device *device); +#define acpi_device_uid(d) ((d)->pnp.unique_id) #define acpi_device_name(d) ((d)->pnp.device_name) #define acpi_device_class(d) ((d)->pnp.device_class) -- cgit From fd9caef4e222a2c5e7ed63050780d215a6a263f3 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Fri, 13 Mar 2015 13:52:50 +0800 Subject: ACPI / scan: fix fixed event handler return value The fixed event handler should return a value that is either 0 or 1 meanning if the event is handled or not, instead of an acpi_status to mean if the handler runs well or not. Suggested-by: Lv Zheng Signed-off-by: Aaron Lu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index bbca7830e18a..3e5a2768c3b4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1062,10 +1062,10 @@ static void acpi_device_notify_fixed(void *data) acpi_device_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, device); } -static acpi_status acpi_device_fixed_event(void *data) +static u32 acpi_device_fixed_event(void *data) { acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_device_notify_fixed, data); - return AE_OK; + return ACPI_INTERRUPT_HANDLED; } static int acpi_device_install_notify_handler(struct acpi_device *device) -- cgit From 9237516c1b96553040e95588347e2f6d337531ae Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Fri, 13 Mar 2015 00:48:17 +0100 Subject: ACPI / battery: make warning greppable Signed-off-by: Martin Kepplinger Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index d98ba4355819..baebd1a9b811 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -530,8 +530,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery) battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN && (s16)(battery->rate_now) < 0) { battery->rate_now = abs((s16)battery->rate_now); - printk_once(KERN_WARNING FW_BUG "battery: (dis)charge rate" - " invalid.\n"); + printk_once(KERN_WARNING FW_BUG + "battery: (dis)charge rate invalid.\n"); } if (test_bit(ACPI_BATTERY_QUIRK_PERCENTAGE_CAPACITY, &battery->flags) -- cgit From b8ee29786f6c0a887a65240fe0289b7ffc227a55 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Mar 2015 15:29:23 +0100 Subject: ACPI / sleep: Drop acpi_suspend() which is not used The acpi_suspend() function has no callers, so drop it. Signed-off-by: Rafael J. Wysocki Acked-by: Lorenzo Pieralisi Reviewed-by: Hanjun Guo --- drivers/acpi/sleep.c | 15 --------------- drivers/acpi/sleep.h | 2 -- 2 files changed, 17 deletions(-) diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 7f251dd1a687..24a830b39ec3 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -806,21 +806,6 @@ static void acpi_sleep_hibernate_setup(void) static inline void acpi_sleep_hibernate_setup(void) {} #endif /* !CONFIG_HIBERNATION */ -int acpi_suspend(u32 acpi_state) -{ - suspend_state_t states[] = { - [1] = PM_SUSPEND_STANDBY, - [3] = PM_SUSPEND_MEM, - [5] = PM_SUSPEND_MAX - }; - - if (acpi_state < 6 && states[acpi_state]) - return pm_suspend(states[acpi_state]); - if (acpi_state == 4) - return hibernate(); - return -EINVAL; -} - static void acpi_power_off_prepare(void) { /* Prepare to power off the system */ diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h index 0143540a2519..c797ffa568d5 100644 --- a/drivers/acpi/sleep.h +++ b/drivers/acpi/sleep.h @@ -1,6 +1,4 @@ -extern int acpi_suspend(u32 state); - extern void acpi_enable_wakeup_devices(u8 sleep_state); extern void acpi_disable_wakeup_devices(u8 sleep_state); -- cgit From 5c0acf3b4f96abbd1fad40af7fdf0ed94cf523b7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 17 Mar 2015 09:58:58 +0200 Subject: driver core: Add comments about returning array counts The "read array" variants of the device property functions can be used to return the number of values in an array. Update the comments to reflect that. Signed-off-by: Adrian Hunter Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- drivers/base/property.c | 53 +++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index c45845874d4f..b524a4e42a83 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -62,13 +62,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_present); * device_property_read_u8_array - return a u8 array property of a device * @dev: Device to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Function reads an array of u8 properties with @propname from the device * firmware description and stores them to @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, @@ -85,13 +86,14 @@ EXPORT_SYMBOL_GPL(device_property_read_u8_array); * device_property_read_u16_array - return a u16 array property of a device * @dev: Device to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Function reads an array of u16 properties with @propname from the device * firmware description and stores them to @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, @@ -108,13 +110,14 @@ EXPORT_SYMBOL_GPL(device_property_read_u16_array); * device_property_read_u32_array - return a u32 array property of a device * @dev: Device to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Function reads an array of u32 properties with @propname from the device * firmware description and stores them to @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, @@ -131,13 +134,14 @@ EXPORT_SYMBOL_GPL(device_property_read_u32_array); * device_property_read_u64_array - return a u64 array property of a device * @dev: Device to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Function reads an array of u64 properties with @propname from the device * firmware description and stores them to @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, @@ -154,13 +158,14 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array); * device_property_read_string_array - return a string array property of device * @dev: Device to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Function reads an array of string properties with @propname from the device * firmware description and stores them to @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO or %-EILSEQ if the property is not an array of strings, @@ -170,7 +175,8 @@ int device_property_read_string_array(struct device *dev, const char *propname, const char **val, size_t nval) { return IS_ENABLED(CONFIG_OF) && dev->of_node ? - of_property_read_string_array(dev->of_node, propname, val, nval) : + (val ? of_property_read_string_array(dev->of_node, propname, val, nval) + : of_property_count_strings(dev->of_node, propname)) : acpi_dev_prop_read(ACPI_COMPANION(dev), propname, DEV_PROP_STRING, val, nval); } @@ -218,13 +224,14 @@ EXPORT_SYMBOL_GPL(device_property_read_string); * fwnode_property_read_u8_array - return a u8 array property of firmware node * @fwnode: Firmware node to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Read an array of u8 properties with @propname from @fwnode and stores them to * @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, @@ -243,13 +250,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u8_array); * fwnode_property_read_u16_array - return a u16 array property of firmware node * @fwnode: Firmware node to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Read an array of u16 properties with @propname from @fwnode and store them to * @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, @@ -268,13 +276,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u16_array); * fwnode_property_read_u32_array - return a u32 array property of firmware node * @fwnode: Firmware node to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Read an array of u32 properties with @propname from @fwnode store them to * @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, @@ -293,13 +302,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u32_array); * fwnode_property_read_u64_array - return a u64 array property firmware node * @fwnode: Firmware node to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Read an array of u64 properties with @propname from @fwnode and store them to * @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of numbers, @@ -318,13 +328,14 @@ EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array); * fwnode_property_read_string_array - return string array property of a node * @fwnode: Firmware node to get the property of * @propname: Name of the property - * @val: The values are stored here + * @val: The values are stored here or %NULL to return the number of values * @nval: Size of the @val array * * Read an string list property @propname from the given firmware node and store * them to @val if found. * - * Return: %0 if the property was found (success), + * Return: number of values if @val was %NULL, + * %0 if the property was found (success), * %-EINVAL if given arguments are not valid, * %-ENODATA if the property does not have a value, * %-EPROTO if the property is not an array of strings, -- cgit From 431d452af13720463dda498999b2e9a08729c03a Mon Sep 17 00:00:00 2001 From: Zhonghui Fu Date: Wed, 18 Mar 2015 15:54:27 +0100 Subject: PM / sleep: add pm-trace support for suspending phase Occasionally, the system can't come back up after suspend/resume due to problems of device suspending phase. This patch make PM_TRACE infrastructure cover device suspending phase of suspend/resume process, and the information in RTC can tell developers which device suspending function make system hang. Signed-off-by: Zhonghui Fu Signed-off-by: Rafael J. Wysocki --- arch/x86/include/asm/pm-trace.h | 23 +++++++++++++++++++++++ arch/x86/include/asm/resume-trace.h | 21 --------------------- drivers/base/power/main.c | 20 ++++++++++++++++---- drivers/base/power/trace.c | 6 +++--- include/linux/pm-trace.h | 35 +++++++++++++++++++++++++++++++++++ include/linux/resume-trace.h | 34 ---------------------------------- kernel/power/main.c | 2 +- 7 files changed, 78 insertions(+), 63 deletions(-) create mode 100644 arch/x86/include/asm/pm-trace.h delete mode 100644 arch/x86/include/asm/resume-trace.h create mode 100644 include/linux/pm-trace.h delete mode 100644 include/linux/resume-trace.h diff --git a/arch/x86/include/asm/pm-trace.h b/arch/x86/include/asm/pm-trace.h new file mode 100644 index 000000000000..7b7ac42c3661 --- /dev/null +++ b/arch/x86/include/asm/pm-trace.h @@ -0,0 +1,23 @@ +#ifndef _ASM_X86_PM_TRACE_H +#define _ASM_X86_PM_TRACE_H + +#include + +#define TRACE_RESUME(user) \ +do { \ + if (pm_trace_enabled) { \ + const void *tracedata; \ + asm volatile(_ASM_MOV " $1f,%0\n" \ + ".section .tracedata,\"a\"\n" \ + "1:\t.word %c1\n\t" \ + _ASM_PTR " %c2\n" \ + ".previous" \ + :"=r" (tracedata) \ + : "i" (__LINE__), "i" (__FILE__)); \ + generate_pm_trace(tracedata, user); \ + } \ +} while (0) + +#define TRACE_SUSPEND(user) TRACE_RESUME(user) + +#endif /* _ASM_X86_PM_TRACE_H */ diff --git a/arch/x86/include/asm/resume-trace.h b/arch/x86/include/asm/resume-trace.h deleted file mode 100644 index 3ff1c2cb1da5..000000000000 --- a/arch/x86/include/asm/resume-trace.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _ASM_X86_RESUME_TRACE_H -#define _ASM_X86_RESUME_TRACE_H - -#include - -#define TRACE_RESUME(user) \ -do { \ - if (pm_trace_enabled) { \ - const void *tracedata; \ - asm volatile(_ASM_MOV " $1f,%0\n" \ - ".section .tracedata,\"a\"\n" \ - "1:\t.word %c1\n\t" \ - _ASM_PTR " %c2\n" \ - ".previous" \ - :"=r" (tracedata) \ - : "i" (__LINE__), "i" (__FILE__)); \ - generate_resume_trace(tracedata, user); \ - } \ -} while (0) - -#endif /* _ASM_X86_RESUME_TRACE_H */ diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9717d5f20139..3d874eca7104 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -1017,6 +1017,9 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a char *info = NULL; int error = 0; + TRACE_DEVICE(dev); + TRACE_SUSPEND(0); + if (async_error) goto Complete; @@ -1057,6 +1060,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a Complete: complete_all(&dev->power.completion); + TRACE_SUSPEND(error); return error; } @@ -1078,7 +1082,7 @@ static int device_suspend_noirq(struct device *dev) { reinit_completion(&dev->power.completion); - if (pm_async_enabled && dev->power.async_suspend) { + if (is_async(dev)) { get_device(dev); async_schedule(async_suspend_noirq, dev); return 0; @@ -1157,6 +1161,9 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as char *info = NULL; int error = 0; + TRACE_DEVICE(dev); + TRACE_SUSPEND(0); + __pm_runtime_disable(dev, false); if (async_error) @@ -1198,6 +1205,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as async_error = error; Complete: + TRACE_SUSPEND(error); complete_all(&dev->power.completion); return error; } @@ -1219,7 +1227,7 @@ static int device_suspend_late(struct device *dev) { reinit_completion(&dev->power.completion); - if (pm_async_enabled && dev->power.async_suspend) { + if (is_async(dev)) { get_device(dev); async_schedule(async_suspend_late, dev); return 0; @@ -1338,6 +1346,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) int error = 0; DECLARE_DPM_WATCHDOG_ON_STACK(wd); + TRACE_DEVICE(dev); + TRACE_SUSPEND(0); + dpm_wait_for_children(dev, async); if (async_error) @@ -1444,6 +1455,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (error) async_error = error; + TRACE_SUSPEND(error); return error; } @@ -1465,7 +1477,7 @@ static int device_suspend(struct device *dev) { reinit_completion(&dev->power.completion); - if (pm_async_enabled && dev->power.async_suspend) { + if (is_async(dev)) { get_device(dev); async_schedule(async_suspend, dev); return 0; diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index d94a1f5121cf..a311cfa4c5bd 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -7,7 +7,7 @@ * devices may be working. */ -#include +#include #include #include @@ -154,7 +154,7 @@ EXPORT_SYMBOL(set_trace_device); * it's not any guarantee, but it's a high _likelihood_ that * the match is valid). */ -void generate_resume_trace(const void *tracedata, unsigned int user) +void generate_pm_trace(const void *tracedata, unsigned int user) { unsigned short lineno = *(unsigned short *)tracedata; const char *file = *(const char **)(tracedata + 2); @@ -164,7 +164,7 @@ void generate_resume_trace(const void *tracedata, unsigned int user) file_hash_value = hash_string(lineno, file, FILEHASH); set_magic_time(user_hash_value, file_hash_value, dev_hash_value); } -EXPORT_SYMBOL(generate_resume_trace); +EXPORT_SYMBOL(generate_pm_trace); extern char __tracedata_start, __tracedata_end; static int show_file_hash(unsigned int value) diff --git a/include/linux/pm-trace.h b/include/linux/pm-trace.h new file mode 100644 index 000000000000..ecbde7a5548e --- /dev/null +++ b/include/linux/pm-trace.h @@ -0,0 +1,35 @@ +#ifndef PM_TRACE_H +#define PM_TRACE_H + +#ifdef CONFIG_PM_TRACE +#include +#include + +extern int pm_trace_enabled; + +static inline int pm_trace_is_enabled(void) +{ + return pm_trace_enabled; +} + +struct device; +extern void set_trace_device(struct device *); +extern void generate_pm_trace(const void *tracedata, unsigned int user); +extern int show_trace_dev_match(char *buf, size_t size); + +#define TRACE_DEVICE(dev) do { \ + if (pm_trace_enabled) \ + set_trace_device(dev); \ + } while(0) + +#else + +static inline int pm_trace_is_enabled(void) { return 0; } + +#define TRACE_DEVICE(dev) do { } while (0) +#define TRACE_RESUME(dev) do { } while (0) +#define TRACE_SUSPEND(dev) do { } while (0) + +#endif + +#endif diff --git a/include/linux/resume-trace.h b/include/linux/resume-trace.h deleted file mode 100644 index f31db2368782..000000000000 --- a/include/linux/resume-trace.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef RESUME_TRACE_H -#define RESUME_TRACE_H - -#ifdef CONFIG_PM_TRACE -#include -#include - -extern int pm_trace_enabled; - -static inline int pm_trace_is_enabled(void) -{ - return pm_trace_enabled; -} - -struct device; -extern void set_trace_device(struct device *); -extern void generate_resume_trace(const void *tracedata, unsigned int user); -extern int show_trace_dev_match(char *buf, size_t size); - -#define TRACE_DEVICE(dev) do { \ - if (pm_trace_enabled) \ - set_trace_device(dev); \ - } while(0) - -#else - -static inline int pm_trace_is_enabled(void) { return 0; } - -#define TRACE_DEVICE(dev) do { } while (0) -#define TRACE_RESUME(dev) do { } while (0) - -#endif - -#endif diff --git a/kernel/power/main.c b/kernel/power/main.c index 9a59d042ea84..86e8157a450f 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include -- cgit From a4f207428b53975069d12d49a509b440c707d691 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Fri, 13 Mar 2015 12:39:01 +0800 Subject: cpufreq: qoriq: Make the driver usable on all QorIQ platforms Freescale introduced new ARM core-based SoCs which support dynamic frequency switch feature. DFS on new SoCs are compatible with current PowerPC CoreNet platforms. In order to support those new platforms, this driver needs to be updated. The main changes include: 1. Changed the names of functions in driver. 2. Added two new functions get_cpu_physical_id() and get_bus_freq(). 3. Used a new way to get the CPU mask which share clock wire. Signed-off-by: Tang Yuantian Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/ppc-corenet-cpufreq.c | 161 +++++++++++++++++++++------------- 1 file changed, 99 insertions(+), 62 deletions(-) diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c index 7cb4b766cf94..9fd51c860308 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c @@ -1,7 +1,7 @@ /* * Copyright 2013 Freescale Semiconductor, Inc. * - * CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs. + * CPU Frequency Scaling driver for Freescale QorIQ SoCs. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -20,12 +20,11 @@ #include #include #include -#include #include /* for get_hard_smp_processor_id() in UP configs */ /** - * struct cpu_data - per CPU data struct + * struct cpu_data * @parent: the parent node of cpu clock * @table: frequency table */ @@ -69,17 +68,78 @@ static const struct soc_data sdata[] = { static u32 min_cpufreq; static const u32 *fmask; -static DEFINE_PER_CPU(struct cpu_data *, cpu_data); +#if defined(CONFIG_ARM) +static int get_cpu_physical_id(int cpu) +{ + return topology_core_id(cpu); +} +#else +static int get_cpu_physical_id(int cpu) +{ + return get_hard_smp_processor_id(cpu); +} +#endif + +static u32 get_bus_freq(void) +{ + struct device_node *soc; + u32 sysfreq; + + soc = of_find_node_by_type(NULL, "soc"); + if (!soc) + return 0; + + if (of_property_read_u32(soc, "bus-frequency", &sysfreq)) + sysfreq = 0; + + of_node_put(soc); -/* cpumask in a cluster */ -static DEFINE_PER_CPU(cpumask_var_t, cpu_mask); + return sysfreq; +} -#ifndef CONFIG_SMP -static inline const struct cpumask *cpu_core_mask(int cpu) +static struct device_node *cpu_to_clk_node(int cpu) { - return cpumask_of(0); + struct device_node *np, *clk_np; + + if (!cpu_present(cpu)) + return NULL; + + np = of_get_cpu_node(cpu, NULL); + if (!np) + return NULL; + + clk_np = of_parse_phandle(np, "clocks", 0); + if (!clk_np) + return NULL; + + of_node_put(np); + + return clk_np; +} + +/* traverse cpu nodes to get cpu mask of sharing clock wire */ +static void set_affected_cpus(struct cpufreq_policy *policy) +{ + struct device_node *np, *clk_np; + struct cpumask *dstp = policy->cpus; + int i; + + np = cpu_to_clk_node(policy->cpu); + if (!np) + return; + + for_each_present_cpu(i) { + clk_np = cpu_to_clk_node(i); + if (!clk_np) + continue; + + if (clk_np == np) + cpumask_set_cpu(i, dstp); + + of_node_put(clk_np); + } + of_node_put(np); } -#endif /* reduce the duplicated frequencies in frequency table */ static void freq_table_redup(struct cpufreq_frequency_table *freq_table, @@ -107,6 +167,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table, int i, j, ind; unsigned int freq, max_freq; struct cpufreq_frequency_table table; + for (i = 0; i < count - 1; i++) { max_freq = freq_table[i].frequency; ind = i; @@ -131,7 +192,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table, } } -static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) +static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) { struct device_node *np; int i, count, ret; @@ -147,10 +208,8 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) return -ENODEV; data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - pr_err("%s: no memory\n", __func__); + if (!data) goto err_np; - } policy->clk = of_clk_get(np, 0); if (IS_ERR(policy->clk)) { @@ -172,7 +231,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) } if (fmask) - mask = fmask[get_hard_smp_processor_id(cpu)]; + mask = fmask[get_cpu_physical_id(cpu)]; else mask = 0x0; @@ -203,13 +262,12 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) data->table = table; /* update ->cpus if we have cluster, no harm if not */ - cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu)); - for_each_cpu(i, per_cpu(cpu_mask, cpu)) - per_cpu(cpu_data, i) = data; + set_affected_cpus(policy); + policy->driver_data = data; /* Minimum transition latency is 12 platform clocks */ u64temp = 12ULL * NSEC_PER_SEC; - do_div(u64temp, fsl_get_sys_freq()); + do_div(u64temp, get_bus_freq()); policy->cpuinfo.transition_latency = u64temp + 1; of_node_put(np); @@ -221,7 +279,7 @@ err_nomem1: err_node: of_node_put(data->parent); err_nomem2: - per_cpu(cpu_data, cpu) = NULL; + policy->driver_data = NULL; kfree(data); err_np: of_node_put(np); @@ -229,43 +287,40 @@ err_np: return -ENODEV; } -static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy) +static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy) { - struct cpu_data *data = per_cpu(cpu_data, policy->cpu); - unsigned int cpu; + struct cpu_data *data = policy->driver_data; of_node_put(data->parent); kfree(data->table); kfree(data); - - for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu)) - per_cpu(cpu_data, cpu) = NULL; + policy->driver_data = NULL; return 0; } -static int corenet_cpufreq_target(struct cpufreq_policy *policy, +static int qoriq_cpufreq_target(struct cpufreq_policy *policy, unsigned int index) { struct clk *parent; - struct cpu_data *data = per_cpu(cpu_data, policy->cpu); + struct cpu_data *data = policy->driver_data; parent = of_clk_get(data->parent, data->table[index].driver_data); return clk_set_parent(policy->clk, parent); } -static struct cpufreq_driver ppc_corenet_cpufreq_driver = { - .name = "ppc_cpufreq", +static struct cpufreq_driver qoriq_cpufreq_driver = { + .name = "qoriq_cpufreq", .flags = CPUFREQ_CONST_LOOPS, - .init = corenet_cpufreq_cpu_init, - .exit = __exit_p(corenet_cpufreq_cpu_exit), + .init = qoriq_cpufreq_cpu_init, + .exit = __exit_p(qoriq_cpufreq_cpu_exit), .verify = cpufreq_generic_frequency_table_verify, - .target_index = corenet_cpufreq_target, + .target_index = qoriq_cpufreq_target, .get = cpufreq_generic_get, .attr = cpufreq_generic_attr, }; -static const struct of_device_id node_matches[] __initdata = { +static const struct of_device_id node_matches[] __initconst = { { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], }, { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], }, { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], }, @@ -275,61 +330,43 @@ static const struct of_device_id node_matches[] __initdata = { {} }; -static int __init ppc_corenet_cpufreq_init(void) +static int __init qoriq_cpufreq_init(void) { int ret; struct device_node *np; const struct of_device_id *match; const struct soc_data *data; - unsigned int cpu; np = of_find_matching_node(NULL, node_matches); if (!np) return -ENODEV; - for_each_possible_cpu(cpu) { - if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL)) - goto err_mask; - cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu)); - } - match = of_match_node(node_matches, np); data = match->data; if (data) { if (data->flag) fmask = data->freq_mask; - min_cpufreq = fsl_get_sys_freq(); + min_cpufreq = get_bus_freq(); } else { - min_cpufreq = fsl_get_sys_freq() / 2; + min_cpufreq = get_bus_freq() / 2; } of_node_put(np); - ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver); + ret = cpufreq_register_driver(&qoriq_cpufreq_driver); if (!ret) - pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n"); + pr_info("Freescale QorIQ CPU frequency scaling driver\n"); return ret; - -err_mask: - for_each_possible_cpu(cpu) - free_cpumask_var(per_cpu(cpu_mask, cpu)); - - return -ENOMEM; } -module_init(ppc_corenet_cpufreq_init); +module_init(qoriq_cpufreq_init); -static void __exit ppc_corenet_cpufreq_exit(void) +static void __exit qoriq_cpufreq_exit(void) { - unsigned int cpu; - - for_each_possible_cpu(cpu) - free_cpumask_var(per_cpu(cpu_mask, cpu)); - - cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver); + cpufreq_unregister_driver(&qoriq_cpufreq_driver); } -module_exit(ppc_corenet_cpufreq_exit); +module_exit(qoriq_cpufreq_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tang Yuantian "); -MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs"); +MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs"); -- cgit From 2f249358eedaf81cede7fb0927ed0bd9c1ae2de7 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Fri, 13 Mar 2015 12:39:02 +0800 Subject: cpufreq: qoriq: rename the driver This driver works on all QorIQ platforms which include ARM-based cores and PPC-based cores. Rename it in order to represent better. Signed-off-by: Tang Yuantian Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig | 8 + drivers/cpufreq/Kconfig.powerpc | 9 - drivers/cpufreq/Makefile | 2 +- drivers/cpufreq/ppc-corenet-cpufreq.c | 372 ---------------------------------- drivers/cpufreq/qoriq-cpufreq.c | 372 ++++++++++++++++++++++++++++++++++ 5 files changed, 381 insertions(+), 382 deletions(-) delete mode 100644 drivers/cpufreq/ppc-corenet-cpufreq.c create mode 100644 drivers/cpufreq/qoriq-cpufreq.c diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index a171fef2c2b6..659879a56dba 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -293,5 +293,13 @@ config SH_CPU_FREQ If unsure, say N. endif +config QORIQ_CPUFREQ + tristate "CPU frequency scaling driver for Freescale QorIQ SoCs" + depends on OF && COMMON_CLK && (PPC_E500MC || ARM) + select CLK_QORIQ + help + This adds the CPUFreq driver support for Freescale QorIQ SoCs + which are capable of changing the CPU's frequency dynamically. + endif endmenu diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc index 7ea24413cee6..3a0595b41eab 100644 --- a/drivers/cpufreq/Kconfig.powerpc +++ b/drivers/cpufreq/Kconfig.powerpc @@ -23,15 +23,6 @@ config CPU_FREQ_MAPLE This adds support for frequency switching on Maple 970FX Evaluation Board and compatible boards (IBM JS2x blades). -config PPC_CORENET_CPUFREQ - tristate "CPU frequency scaling driver for Freescale E500MC SoCs" - depends on PPC_E500MC && OF && COMMON_CLK - select CLK_QORIQ - help - This adds the CPUFreq driver support for Freescale e500mc, - e5500 and e6500 series SoCs which are capable of changing - the CPU's frequency dynamically. - config CPU_FREQ_PMAC bool "Support for Apple PowerBooks" depends on ADB_PMU && PPC32 diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 82a1821471fd..26df0ad00e32 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -85,7 +85,7 @@ obj-$(CONFIG_CPU_FREQ_CBE) += ppc-cbe-cpufreq.o ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o -obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o +obj-$(CONFIG_QORIQ_CPUFREQ) += qoriq-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 diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c deleted file mode 100644 index 9fd51c860308..000000000000 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright 2013 Freescale Semiconductor, Inc. - * - * CPU Frequency Scaling driver for Freescale QorIQ SoCs. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* for get_hard_smp_processor_id() in UP configs */ - -/** - * struct cpu_data - * @parent: the parent node of cpu clock - * @table: frequency table - */ -struct cpu_data { - struct device_node *parent; - struct cpufreq_frequency_table *table; -}; - -/** - * struct soc_data - SoC specific data - * @freq_mask: mask the disallowed frequencies - * @flag: unique flags - */ -struct soc_data { - u32 freq_mask[4]; - u32 flag; -}; - -#define FREQ_MASK 1 -/* see hardware specification for the allowed frqeuencies */ -static const struct soc_data sdata[] = { - { /* used by p2041 and p3041 */ - .freq_mask = {0x8, 0x8, 0x2, 0x2}, - .flag = FREQ_MASK, - }, - { /* used by p5020 */ - .freq_mask = {0x8, 0x2}, - .flag = FREQ_MASK, - }, - { /* used by p4080, p5040 */ - .freq_mask = {0}, - .flag = 0, - }, -}; - -/* - * the minimum allowed core frequency, in Hz - * for chassis v1.0, >= platform frequency - * for chassis v2.0, >= platform frequency / 2 - */ -static u32 min_cpufreq; -static const u32 *fmask; - -#if defined(CONFIG_ARM) -static int get_cpu_physical_id(int cpu) -{ - return topology_core_id(cpu); -} -#else -static int get_cpu_physical_id(int cpu) -{ - return get_hard_smp_processor_id(cpu); -} -#endif - -static u32 get_bus_freq(void) -{ - struct device_node *soc; - u32 sysfreq; - - soc = of_find_node_by_type(NULL, "soc"); - if (!soc) - return 0; - - if (of_property_read_u32(soc, "bus-frequency", &sysfreq)) - sysfreq = 0; - - of_node_put(soc); - - return sysfreq; -} - -static struct device_node *cpu_to_clk_node(int cpu) -{ - struct device_node *np, *clk_np; - - if (!cpu_present(cpu)) - return NULL; - - np = of_get_cpu_node(cpu, NULL); - if (!np) - return NULL; - - clk_np = of_parse_phandle(np, "clocks", 0); - if (!clk_np) - return NULL; - - of_node_put(np); - - return clk_np; -} - -/* traverse cpu nodes to get cpu mask of sharing clock wire */ -static void set_affected_cpus(struct cpufreq_policy *policy) -{ - struct device_node *np, *clk_np; - struct cpumask *dstp = policy->cpus; - int i; - - np = cpu_to_clk_node(policy->cpu); - if (!np) - return; - - for_each_present_cpu(i) { - clk_np = cpu_to_clk_node(i); - if (!clk_np) - continue; - - if (clk_np == np) - cpumask_set_cpu(i, dstp); - - of_node_put(clk_np); - } - of_node_put(np); -} - -/* reduce the duplicated frequencies in frequency table */ -static void freq_table_redup(struct cpufreq_frequency_table *freq_table, - int count) -{ - int i, j; - - for (i = 1; i < count; i++) { - for (j = 0; j < i; j++) { - if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID || - freq_table[j].frequency != - freq_table[i].frequency) - continue; - - freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; - break; - } - } -} - -/* sort the frequencies in frequency table in descenting order */ -static void freq_table_sort(struct cpufreq_frequency_table *freq_table, - int count) -{ - int i, j, ind; - unsigned int freq, max_freq; - struct cpufreq_frequency_table table; - - for (i = 0; i < count - 1; i++) { - max_freq = freq_table[i].frequency; - ind = i; - for (j = i + 1; j < count; j++) { - freq = freq_table[j].frequency; - if (freq == CPUFREQ_ENTRY_INVALID || - freq <= max_freq) - continue; - ind = j; - max_freq = freq; - } - - if (ind != i) { - /* exchange the frequencies */ - table.driver_data = freq_table[i].driver_data; - table.frequency = freq_table[i].frequency; - freq_table[i].driver_data = freq_table[ind].driver_data; - freq_table[i].frequency = freq_table[ind].frequency; - freq_table[ind].driver_data = table.driver_data; - freq_table[ind].frequency = table.frequency; - } - } -} - -static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) -{ - struct device_node *np; - int i, count, ret; - u32 freq, mask; - struct clk *clk; - struct cpufreq_frequency_table *table; - struct cpu_data *data; - unsigned int cpu = policy->cpu; - u64 u64temp; - - np = of_get_cpu_node(cpu, NULL); - if (!np) - return -ENODEV; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - goto err_np; - - policy->clk = of_clk_get(np, 0); - if (IS_ERR(policy->clk)) { - pr_err("%s: no clock information\n", __func__); - goto err_nomem2; - } - - data->parent = of_parse_phandle(np, "clocks", 0); - if (!data->parent) { - pr_err("%s: could not get clock information\n", __func__); - goto err_nomem2; - } - - count = of_property_count_strings(data->parent, "clock-names"); - table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL); - if (!table) { - pr_err("%s: no memory\n", __func__); - goto err_node; - } - - if (fmask) - mask = fmask[get_cpu_physical_id(cpu)]; - else - mask = 0x0; - - for (i = 0; i < count; i++) { - clk = of_clk_get(data->parent, i); - freq = clk_get_rate(clk); - /* - * the clock is valid if its frequency is not masked - * and large than minimum allowed frequency. - */ - if (freq < min_cpufreq || (mask & (1 << i))) - table[i].frequency = CPUFREQ_ENTRY_INVALID; - else - table[i].frequency = freq / 1000; - table[i].driver_data = i; - } - freq_table_redup(table, count); - freq_table_sort(table, count); - table[i].frequency = CPUFREQ_TABLE_END; - - /* set the min and max frequency properly */ - ret = cpufreq_table_validate_and_show(policy, table); - if (ret) { - pr_err("invalid frequency table: %d\n", ret); - goto err_nomem1; - } - - data->table = table; - - /* update ->cpus if we have cluster, no harm if not */ - set_affected_cpus(policy); - policy->driver_data = data; - - /* Minimum transition latency is 12 platform clocks */ - u64temp = 12ULL * NSEC_PER_SEC; - do_div(u64temp, get_bus_freq()); - policy->cpuinfo.transition_latency = u64temp + 1; - - of_node_put(np); - - return 0; - -err_nomem1: - kfree(table); -err_node: - of_node_put(data->parent); -err_nomem2: - policy->driver_data = NULL; - kfree(data); -err_np: - of_node_put(np); - - return -ENODEV; -} - -static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy) -{ - struct cpu_data *data = policy->driver_data; - - of_node_put(data->parent); - kfree(data->table); - kfree(data); - policy->driver_data = NULL; - - return 0; -} - -static int qoriq_cpufreq_target(struct cpufreq_policy *policy, - unsigned int index) -{ - struct clk *parent; - struct cpu_data *data = policy->driver_data; - - parent = of_clk_get(data->parent, data->table[index].driver_data); - return clk_set_parent(policy->clk, parent); -} - -static struct cpufreq_driver qoriq_cpufreq_driver = { - .name = "qoriq_cpufreq", - .flags = CPUFREQ_CONST_LOOPS, - .init = qoriq_cpufreq_cpu_init, - .exit = __exit_p(qoriq_cpufreq_cpu_exit), - .verify = cpufreq_generic_frequency_table_verify, - .target_index = qoriq_cpufreq_target, - .get = cpufreq_generic_get, - .attr = cpufreq_generic_attr, -}; - -static const struct of_device_id node_matches[] __initconst = { - { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], }, - { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], }, - { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], }, - { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], }, - { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], }, - { .compatible = "fsl,qoriq-clockgen-2.0", }, - {} -}; - -static int __init qoriq_cpufreq_init(void) -{ - int ret; - struct device_node *np; - const struct of_device_id *match; - const struct soc_data *data; - - np = of_find_matching_node(NULL, node_matches); - if (!np) - return -ENODEV; - - match = of_match_node(node_matches, np); - data = match->data; - if (data) { - if (data->flag) - fmask = data->freq_mask; - min_cpufreq = get_bus_freq(); - } else { - min_cpufreq = get_bus_freq() / 2; - } - - of_node_put(np); - - ret = cpufreq_register_driver(&qoriq_cpufreq_driver); - if (!ret) - pr_info("Freescale QorIQ CPU frequency scaling driver\n"); - - return ret; -} -module_init(qoriq_cpufreq_init); - -static void __exit qoriq_cpufreq_exit(void) -{ - cpufreq_unregister_driver(&qoriq_cpufreq_driver); -} -module_exit(qoriq_cpufreq_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Tang Yuantian "); -MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs"); diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c new file mode 100644 index 000000000000..9fd51c860308 --- /dev/null +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -0,0 +1,372 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * CPU Frequency Scaling driver for Freescale QorIQ SoCs. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* for get_hard_smp_processor_id() in UP configs */ + +/** + * struct cpu_data + * @parent: the parent node of cpu clock + * @table: frequency table + */ +struct cpu_data { + struct device_node *parent; + struct cpufreq_frequency_table *table; +}; + +/** + * struct soc_data - SoC specific data + * @freq_mask: mask the disallowed frequencies + * @flag: unique flags + */ +struct soc_data { + u32 freq_mask[4]; + u32 flag; +}; + +#define FREQ_MASK 1 +/* see hardware specification for the allowed frqeuencies */ +static const struct soc_data sdata[] = { + { /* used by p2041 and p3041 */ + .freq_mask = {0x8, 0x8, 0x2, 0x2}, + .flag = FREQ_MASK, + }, + { /* used by p5020 */ + .freq_mask = {0x8, 0x2}, + .flag = FREQ_MASK, + }, + { /* used by p4080, p5040 */ + .freq_mask = {0}, + .flag = 0, + }, +}; + +/* + * the minimum allowed core frequency, in Hz + * for chassis v1.0, >= platform frequency + * for chassis v2.0, >= platform frequency / 2 + */ +static u32 min_cpufreq; +static const u32 *fmask; + +#if defined(CONFIG_ARM) +static int get_cpu_physical_id(int cpu) +{ + return topology_core_id(cpu); +} +#else +static int get_cpu_physical_id(int cpu) +{ + return get_hard_smp_processor_id(cpu); +} +#endif + +static u32 get_bus_freq(void) +{ + struct device_node *soc; + u32 sysfreq; + + soc = of_find_node_by_type(NULL, "soc"); + if (!soc) + return 0; + + if (of_property_read_u32(soc, "bus-frequency", &sysfreq)) + sysfreq = 0; + + of_node_put(soc); + + return sysfreq; +} + +static struct device_node *cpu_to_clk_node(int cpu) +{ + struct device_node *np, *clk_np; + + if (!cpu_present(cpu)) + return NULL; + + np = of_get_cpu_node(cpu, NULL); + if (!np) + return NULL; + + clk_np = of_parse_phandle(np, "clocks", 0); + if (!clk_np) + return NULL; + + of_node_put(np); + + return clk_np; +} + +/* traverse cpu nodes to get cpu mask of sharing clock wire */ +static void set_affected_cpus(struct cpufreq_policy *policy) +{ + struct device_node *np, *clk_np; + struct cpumask *dstp = policy->cpus; + int i; + + np = cpu_to_clk_node(policy->cpu); + if (!np) + return; + + for_each_present_cpu(i) { + clk_np = cpu_to_clk_node(i); + if (!clk_np) + continue; + + if (clk_np == np) + cpumask_set_cpu(i, dstp); + + of_node_put(clk_np); + } + of_node_put(np); +} + +/* reduce the duplicated frequencies in frequency table */ +static void freq_table_redup(struct cpufreq_frequency_table *freq_table, + int count) +{ + int i, j; + + for (i = 1; i < count; i++) { + for (j = 0; j < i; j++) { + if (freq_table[j].frequency == CPUFREQ_ENTRY_INVALID || + freq_table[j].frequency != + freq_table[i].frequency) + continue; + + freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; + break; + } + } +} + +/* sort the frequencies in frequency table in descenting order */ +static void freq_table_sort(struct cpufreq_frequency_table *freq_table, + int count) +{ + int i, j, ind; + unsigned int freq, max_freq; + struct cpufreq_frequency_table table; + + for (i = 0; i < count - 1; i++) { + max_freq = freq_table[i].frequency; + ind = i; + for (j = i + 1; j < count; j++) { + freq = freq_table[j].frequency; + if (freq == CPUFREQ_ENTRY_INVALID || + freq <= max_freq) + continue; + ind = j; + max_freq = freq; + } + + if (ind != i) { + /* exchange the frequencies */ + table.driver_data = freq_table[i].driver_data; + table.frequency = freq_table[i].frequency; + freq_table[i].driver_data = freq_table[ind].driver_data; + freq_table[i].frequency = freq_table[ind].frequency; + freq_table[ind].driver_data = table.driver_data; + freq_table[ind].frequency = table.frequency; + } + } +} + +static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + struct device_node *np; + int i, count, ret; + u32 freq, mask; + struct clk *clk; + struct cpufreq_frequency_table *table; + struct cpu_data *data; + unsigned int cpu = policy->cpu; + u64 u64temp; + + np = of_get_cpu_node(cpu, NULL); + if (!np) + return -ENODEV; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto err_np; + + policy->clk = of_clk_get(np, 0); + if (IS_ERR(policy->clk)) { + pr_err("%s: no clock information\n", __func__); + goto err_nomem2; + } + + data->parent = of_parse_phandle(np, "clocks", 0); + if (!data->parent) { + pr_err("%s: could not get clock information\n", __func__); + goto err_nomem2; + } + + count = of_property_count_strings(data->parent, "clock-names"); + table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL); + if (!table) { + pr_err("%s: no memory\n", __func__); + goto err_node; + } + + if (fmask) + mask = fmask[get_cpu_physical_id(cpu)]; + else + mask = 0x0; + + for (i = 0; i < count; i++) { + clk = of_clk_get(data->parent, i); + freq = clk_get_rate(clk); + /* + * the clock is valid if its frequency is not masked + * and large than minimum allowed frequency. + */ + if (freq < min_cpufreq || (mask & (1 << i))) + table[i].frequency = CPUFREQ_ENTRY_INVALID; + else + table[i].frequency = freq / 1000; + table[i].driver_data = i; + } + freq_table_redup(table, count); + freq_table_sort(table, count); + table[i].frequency = CPUFREQ_TABLE_END; + + /* set the min and max frequency properly */ + ret = cpufreq_table_validate_and_show(policy, table); + if (ret) { + pr_err("invalid frequency table: %d\n", ret); + goto err_nomem1; + } + + data->table = table; + + /* update ->cpus if we have cluster, no harm if not */ + set_affected_cpus(policy); + policy->driver_data = data; + + /* Minimum transition latency is 12 platform clocks */ + u64temp = 12ULL * NSEC_PER_SEC; + do_div(u64temp, get_bus_freq()); + policy->cpuinfo.transition_latency = u64temp + 1; + + of_node_put(np); + + return 0; + +err_nomem1: + kfree(table); +err_node: + of_node_put(data->parent); +err_nomem2: + policy->driver_data = NULL; + kfree(data); +err_np: + of_node_put(np); + + return -ENODEV; +} + +static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + struct cpu_data *data = policy->driver_data; + + of_node_put(data->parent); + kfree(data->table); + kfree(data); + policy->driver_data = NULL; + + return 0; +} + +static int qoriq_cpufreq_target(struct cpufreq_policy *policy, + unsigned int index) +{ + struct clk *parent; + struct cpu_data *data = policy->driver_data; + + parent = of_clk_get(data->parent, data->table[index].driver_data); + return clk_set_parent(policy->clk, parent); +} + +static struct cpufreq_driver qoriq_cpufreq_driver = { + .name = "qoriq_cpufreq", + .flags = CPUFREQ_CONST_LOOPS, + .init = qoriq_cpufreq_cpu_init, + .exit = __exit_p(qoriq_cpufreq_cpu_exit), + .verify = cpufreq_generic_frequency_table_verify, + .target_index = qoriq_cpufreq_target, + .get = cpufreq_generic_get, + .attr = cpufreq_generic_attr, +}; + +static const struct of_device_id node_matches[] __initconst = { + { .compatible = "fsl,p2041-clockgen", .data = &sdata[0], }, + { .compatible = "fsl,p3041-clockgen", .data = &sdata[0], }, + { .compatible = "fsl,p5020-clockgen", .data = &sdata[1], }, + { .compatible = "fsl,p4080-clockgen", .data = &sdata[2], }, + { .compatible = "fsl,p5040-clockgen", .data = &sdata[2], }, + { .compatible = "fsl,qoriq-clockgen-2.0", }, + {} +}; + +static int __init qoriq_cpufreq_init(void) +{ + int ret; + struct device_node *np; + const struct of_device_id *match; + const struct soc_data *data; + + np = of_find_matching_node(NULL, node_matches); + if (!np) + return -ENODEV; + + match = of_match_node(node_matches, np); + data = match->data; + if (data) { + if (data->flag) + fmask = data->freq_mask; + min_cpufreq = get_bus_freq(); + } else { + min_cpufreq = get_bus_freq() / 2; + } + + of_node_put(np); + + ret = cpufreq_register_driver(&qoriq_cpufreq_driver); + if (!ret) + pr_info("Freescale QorIQ CPU frequency scaling driver\n"); + + return ret; +} +module_init(qoriq_cpufreq_init); + +static void __exit qoriq_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&qoriq_cpufreq_driver); +} +module_exit(qoriq_cpufreq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tang Yuantian "); +MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs"); -- cgit From 95c0fd457b03099b5ce385f3982e06d01ad42c5c Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:30 +0100 Subject: PNP: Add helper macro for pnp_register_driver boilerplate This patch introduces the module_pnp_driver macro which is a convenience macro for PNP driver modules similar to module_pci_driver. It is intended to be used by drivers which init/exit section does nothing but register/unregister the PNP driver. By using this macro it is possible to eliminate a few lines of boilerplate code per PNP driver. Based on work done by Lars-Peter Clausen for other busses (i2c and spi) and Greg KH for PCI. Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- include/linux/pnp.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 6512e9cbc6d5..5df733b8f704 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -510,4 +510,16 @@ static inline void pnp_unregister_driver(struct pnp_driver *drv) { } #endif /* CONFIG_PNP */ +/** + * module_pnp_driver() - Helper macro for registering a PnP driver + * @__pnp_driver: pnp_driver struct + * + * Helper macro for PnP drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_pnp_driver(__pnp_driver) \ + module_driver(__pnp_driver, pnp_register_driver, \ + pnp_unregister_driver) + #endif /* _LINUX_PNP_H */ -- cgit From 1551660369d00a7e8cdfa12e9af132053eb67140 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:31 +0100 Subject: PNP: tpm/tpm_infineon: Use module_pnp_driver to register driver Removing some boilerplate by using module_pnp_driver instead of calling register and unregister in the otherwise empty init/exit functions Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- drivers/char/tpm/tpm_infineon.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 6d492132ad2b..29ba520ac24d 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -637,18 +637,7 @@ static struct pnp_driver tpm_inf_pnp_driver = { .remove = tpm_inf_pnp_remove }; -static int __init init_inf(void) -{ - return pnp_register_driver(&tpm_inf_pnp_driver); -} - -static void __exit cleanup_inf(void) -{ - pnp_unregister_driver(&tpm_inf_pnp_driver); -} - -module_init(init_inf); -module_exit(cleanup_inf); +module_pnp_driver(tpm_inf_pnp_driver); MODULE_AUTHOR("Marcel Selhorst "); MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); -- cgit From 99c876bec30d064679c07d53b44a4e99da10f576 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:32 +0100 Subject: PNP: ata/pata_isapnp: Use module_pnp_driver to register driver Removing some boilerplate by using module_pnp_driver instead of calling register and unregister in the otherwise empty init/exit functions Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- drivers/ata/pata_isapnp.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c index b33d1f99b3a4..994f168b54a8 100644 --- a/drivers/ata/pata_isapnp.c +++ b/drivers/ata/pata_isapnp.c @@ -128,20 +128,8 @@ static struct pnp_driver isapnp_driver = { .remove = isapnp_remove_one, }; -static int __init isapnp_init(void) -{ - return pnp_register_driver(&isapnp_driver); -} - -static void __exit isapnp_exit(void) -{ - pnp_unregister_driver(&isapnp_driver); -} - +module_pnp_driver(isapnp_driver); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("low-level driver for ISA PnP ATA"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(isapnp_init); -module_exit(isapnp_exit); -- cgit From 6a5333092e4674c1e1f7de05ce6a167273f5afdb Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:33 +0100 Subject: PNP: ide/ide-pnp: Use module_pnp_driver to register driver Removing some boilerplate by using module_pnp_driver instead of calling register and unregister in the otherwise empty init/exit functions Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- drivers/ide/ide-pnp.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index e5f3db831373..f5f2b62471da 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -96,17 +96,5 @@ static struct pnp_driver idepnp_driver = { .remove = idepnp_remove, }; -static int __init pnpide_init(void) -{ - return pnp_register_driver(&idepnp_driver); -} - -static void __exit pnpide_exit(void) -{ - pnp_unregister_driver(&idepnp_driver); -} - -module_init(pnpide_init); -module_exit(pnpide_exit); - +module_pnp_driver(idepnp_driver); MODULE_LICENSE("GPL"); -- cgit From af638a04070f54c22c2fe7523e742d06b5f980bf Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:34 +0100 Subject: PNP: media/rc: Use module_pnp_driver to register driver Removing some boilerplate by using module_pnp_driver instead of calling register and unregister in the otherwise empty init/exit functions Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- drivers/media/rc/ene_ir.c | 13 +------------ drivers/media/rc/fintek-cir.c | 13 +------------ drivers/media/rc/ite-cir.c | 13 +------------ drivers/media/rc/nuvoton-cir.c | 13 +------------ 4 files changed, 4 insertions(+), 48 deletions(-) diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index e80f2c6c5f1a..8d77e1c4a141 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1195,16 +1195,6 @@ static struct pnp_driver ene_driver = { .shutdown = ene_shutdown, }; -static int __init ene_init(void) -{ - return pnp_register_driver(&ene_driver); -} - -static void ene_exit(void) -{ - pnp_unregister_driver(&ene_driver); -} - module_param(sample_period, int, S_IRUGO); MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)"); @@ -1226,5 +1216,4 @@ MODULE_DESCRIPTION MODULE_AUTHOR("Maxim Levitsky"); MODULE_LICENSE("GPL"); -module_init(ene_init); -module_exit(ene_exit); +module_pnp_driver(ene_driver); diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index b5167573240e..5c63c2ec6183 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -684,16 +684,6 @@ static struct pnp_driver fintek_driver = { .shutdown = fintek_shutdown, }; -static int __init fintek_init(void) -{ - return pnp_register_driver(&fintek_driver); -} - -static void __exit fintek_exit(void) -{ - pnp_unregister_driver(&fintek_driver); -} - module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Enable debugging output"); @@ -703,5 +693,4 @@ MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver"); MODULE_AUTHOR("Jarod Wilson "); MODULE_LICENSE("GPL"); -module_init(fintek_init); -module_exit(fintek_exit); +module_pnp_driver(fintek_driver); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 56abf9120cc2..0f301903aa6f 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1708,21 +1708,10 @@ static struct pnp_driver ite_driver = { .shutdown = ite_shutdown, }; -static int __init ite_init(void) -{ - return pnp_register_driver(&ite_driver); -} - -static void __exit ite_exit(void) -{ - pnp_unregister_driver(&ite_driver); -} - MODULE_DEVICE_TABLE(pnp, ite_ids); MODULE_DESCRIPTION("ITE Tech Inc. IT8712F/ITE8512F CIR driver"); MODULE_AUTHOR("Juan J. Garcia de Soria "); MODULE_LICENSE("GPL"); -module_init(ite_init); -module_exit(ite_exit); +module_pnp_driver(ite_driver); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 9c2c8635ff33..85af7a869167 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1219,16 +1219,6 @@ static struct pnp_driver nvt_driver = { .shutdown = nvt_shutdown, }; -static int __init nvt_init(void) -{ - return pnp_register_driver(&nvt_driver); -} - -static void __exit nvt_exit(void) -{ - pnp_unregister_driver(&nvt_driver); -} - module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Enable debugging output"); @@ -1238,5 +1228,4 @@ MODULE_DESCRIPTION("Nuvoton W83667HG-A & W83677HG-I CIR driver"); MODULE_AUTHOR("Jarod Wilson "); MODULE_LICENSE("GPL"); -module_init(nvt_init); -module_exit(nvt_exit); +module_pnp_driver(nvt_driver); -- cgit From ed03538aa8d5f03432ddd5ed73fc0ba2725af93d Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:35 +0100 Subject: PNP: net/sb1000: Use module_pnp_driver to register driver Removing some boilerplate by using module_pnp_driver instead of calling register and unregister in the otherwise empty init/exit functions Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- drivers/net/sb1000.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index 66c2f1a01963..aad0b59d41e3 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -1175,17 +1175,4 @@ MODULE_AUTHOR("Franco Venturi "); MODULE_DESCRIPTION("General Instruments SB1000 driver"); MODULE_LICENSE("GPL"); -static int __init -sb1000_init(void) -{ - return pnp_register_driver(&sb1000_driver); -} - -static void __exit -sb1000_exit(void) -{ - pnp_unregister_driver(&sb1000_driver); -} - -module_init(sb1000_init); -module_exit(sb1000_exit); +module_pnp_driver(sb1000_driver); -- cgit From 99f74f12e87426be0f83b87b5e46c1ec8b00bd0f Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:36 +0100 Subject: PNP: platform/x86/apple-gmux: Use module_pnp_driver to register driver Removing some boilerplate by using module_pnp_driver instead of calling register and unregister in the otherwise empty init/exit functions Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/apple-gmux.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index b9429fbf1cd8..66d6d22c239c 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -624,19 +624,7 @@ static struct pnp_driver gmux_pnp_driver = { }, }; -static int __init apple_gmux_init(void) -{ - return pnp_register_driver(&gmux_pnp_driver); -} - -static void __exit apple_gmux_exit(void) -{ - pnp_unregister_driver(&gmux_pnp_driver); -} - -module_init(apple_gmux_init); -module_exit(apple_gmux_exit); - +module_pnp_driver(gmux_pnp_driver); MODULE_AUTHOR("Seth Forshee "); MODULE_DESCRIPTION("Apple Gmux Driver"); MODULE_LICENSE("GPL"); -- cgit From aee94467d9858b5441c278ffa39dcd19bef548cf Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 16 Mar 2015 21:46:37 +0100 Subject: PNP: tty/serial/8250/8250_fintek: Use module_pnp_driver to register driver Removing some boilerplate by using module_pnp_driver instead of calling register and unregister in the otherwise empty init/exit functions Signed-off-by: Peter Huewe Signed-off-by: Rafael J. Wysocki --- drivers/tty/serial/8250/8250_fintek.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 1e6899bc9429..5815e81b5fc6 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -234,18 +234,7 @@ static struct pnp_driver fintek_8250_driver = { .id_table = fintek_dev_table, }; -static int fintek_8250_init(void) -{ - return pnp_register_driver(&fintek_8250_driver); -} -module_init(fintek_8250_init); - -static void fintek_8250_exit(void) -{ - pnp_unregister_driver(&fintek_8250_driver); -} -module_exit(fintek_8250_exit); - +module_pnp_driver(fintek_8250_driver); MODULE_DESCRIPTION("Fintek F812164 module"); MODULE_AUTHOR("Ricardo Ribalda "); MODULE_LICENSE("GPL"); -- cgit From 38f6b38dbb0896511c509fbb6ceabbedbee8e87d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 18 Mar 2015 22:39:55 +0100 Subject: PNP: Convert pnp_lock into a mutex pnp_lock is a spinlock, but it is only acquired from process context, so it may be a mutex just fine. Signed-off-by: Rafael J. Wysocki --- drivers/pnp/base.h | 2 +- drivers/pnp/card.c | 25 +++++++++++++------------ drivers/pnp/core.c | 19 ++++++++++--------- drivers/pnp/driver.c | 10 +++++----- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index c8873b0ca551..3151fd164614 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -3,7 +3,7 @@ * Bjorn Helgaas */ -extern spinlock_t pnp_lock; +extern struct mutex pnp_lock; extern const struct attribute_group *pnp_dev_groups[]; void *pnp_alloc(long size); diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index 874c236ac1a7..31ad9fc3f701 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -244,10 +245,10 @@ int pnp_add_card(struct pnp_card *card) } pnp_interface_attach_card(card); - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); list_add_tail(&card->global_list, &pnp_cards); list_add_tail(&card->protocol_list, &card->protocol->cards); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); /* we wait until now to add devices in order to ensure the drivers * will be able to use all of the related devices on the card @@ -276,10 +277,10 @@ void pnp_remove_card(struct pnp_card *card) struct list_head *pos, *temp; device_unregister(&card->dev); - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); list_del(&card->global_list); list_del(&card->protocol_list); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); list_for_each_safe(pos, temp, &card->devices) { struct pnp_dev *dev = card_to_pnp_dev(pos); pnp_remove_card_device(dev); @@ -297,10 +298,10 @@ int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev) dev->card_link = NULL; dev_set_name(&dev->dev, "%02x:%02x.%02x", dev->protocol->number, card->number, dev->number); - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); dev->card = card; list_add_tail(&dev->card_list, &card->devices); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); return 0; } @@ -310,10 +311,10 @@ int pnp_add_card_device(struct pnp_card *card, struct pnp_dev *dev) */ void pnp_remove_card_device(struct pnp_dev *dev) { - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); dev->card = NULL; list_del(&dev->card_list); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); __pnp_remove_device(dev); } @@ -426,9 +427,9 @@ int pnp_register_card_driver(struct pnp_card_driver *drv) if (error < 0) return error; - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); list_add_tail(&drv->global_list, &pnp_card_drivers); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); list_for_each_safe(pos, temp, &pnp_cards) { struct pnp_card *card = @@ -444,9 +445,9 @@ int pnp_register_card_driver(struct pnp_card_driver *drv) */ void pnp_unregister_card_driver(struct pnp_card_driver *drv) { - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); list_del(&drv->global_list); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); pnp_unregister_driver(&drv->link); } diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index cb6ce42f8e77..ef2f59c4e57c 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,7 @@ static LIST_HEAD(pnp_protocols); LIST_HEAD(pnp_global); -DEFINE_SPINLOCK(pnp_lock); +DEFINE_MUTEX(pnp_lock); /* * ACPI or PNPBIOS should tell us about all platform devices, so we can @@ -55,7 +56,7 @@ int pnp_register_protocol(struct pnp_protocol *protocol) INIT_LIST_HEAD(&protocol->devices); INIT_LIST_HEAD(&protocol->cards); nodenum = 0; - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); /* assign the lowest unused number */ list_for_each(pos, &pnp_protocols) { @@ -67,7 +68,7 @@ int pnp_register_protocol(struct pnp_protocol *protocol) } list_add_tail(&protocol->protocol_list, &pnp_protocols); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); protocol->number = nodenum; dev_set_name(&protocol->dev, "pnp%d", nodenum); @@ -80,9 +81,9 @@ int pnp_register_protocol(struct pnp_protocol *protocol) */ void pnp_unregister_protocol(struct pnp_protocol *protocol) { - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); list_del(&protocol->protocol_list); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); device_unregister(&protocol->dev); } @@ -161,10 +162,10 @@ int __pnp_add_device(struct pnp_dev *dev) { pnp_fixup_device(dev); dev->status = PNP_READY; - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); list_add_tail(&dev->global_list, &pnp_global); list_add_tail(&dev->protocol_list, &dev->protocol->devices); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); if (dev->protocol->can_wakeup) device_set_wakeup_capable(&dev->dev, dev->protocol->can_wakeup(dev)); @@ -203,10 +204,10 @@ int pnp_add_device(struct pnp_dev *dev) void __pnp_remove_device(struct pnp_dev *dev) { - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); list_del(&dev->global_list); list_del(&dev->protocol_list); - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); device_unregister(&dev->dev); } diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c index 4e57d3370368..153a493b5413 100644 --- a/drivers/pnp/driver.c +++ b/drivers/pnp/driver.c @@ -58,22 +58,22 @@ static const struct pnp_device_id *match_device(struct pnp_driver *drv, int pnp_device_attach(struct pnp_dev *pnp_dev) { - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); if (pnp_dev->status != PNP_READY) { - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); return -EBUSY; } pnp_dev->status = PNP_ATTACHED; - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); return 0; } void pnp_device_detach(struct pnp_dev *pnp_dev) { - spin_lock(&pnp_lock); + mutex_lock(&pnp_lock); if (pnp_dev->status == PNP_ATTACHED) pnp_dev->status = PNP_READY; - spin_unlock(&pnp_lock); + mutex_unlock(&pnp_lock); pnp_disable_dev(pnp_dev); } -- cgit From 71150d226564686ef32d15d29edfd18346f6e929 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 18 Mar 2015 22:40:04 +0100 Subject: PNP: Avoid leaving unregistered device objects in lists pnp_register_protocol() and __pnp_add_device() both have a problem that if device_register() fails, the objects they create will be left in the lists they have been put one beforehand. Unfortunately, that is not handled by the callers of those routines either, so in case of a device registration errors the PNP bus type's data structures will end up in an inconsistent state. Make pnp_register_protocol() and __pnp_add_device() remove the objects from the lists if device registration fails. Signed-off-by: Rafael J. Wysocki --- drivers/pnp/core.c | 53 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index ef2f59c4e57c..b54620e53830 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -42,6 +42,13 @@ void *pnp_alloc(long size) return result; } +static void pnp_remove_protocol(struct pnp_protocol *protocol) +{ + mutex_lock(&pnp_lock); + list_del(&protocol->protocol_list); + mutex_unlock(&pnp_lock); +} + /** * pnp_protocol_register - adds a pnp protocol to the pnp layer * @protocol: pointer to the corresponding pnp_protocol structure @@ -50,12 +57,13 @@ void *pnp_alloc(long size) */ int pnp_register_protocol(struct pnp_protocol *protocol) { - int nodenum; struct list_head *pos; + int nodenum, ret; INIT_LIST_HEAD(&protocol->devices); INIT_LIST_HEAD(&protocol->cards); nodenum = 0; + mutex_lock(&pnp_lock); /* assign the lowest unused number */ @@ -67,12 +75,18 @@ int pnp_register_protocol(struct pnp_protocol *protocol) } } + protocol->number = nodenum; + dev_set_name(&protocol->dev, "pnp%d", nodenum); + list_add_tail(&protocol->protocol_list, &pnp_protocols); + mutex_unlock(&pnp_lock); - protocol->number = nodenum; - dev_set_name(&protocol->dev, "pnp%d", nodenum); - return device_register(&protocol->dev); + ret = device_register(&protocol->dev); + if (ret) + pnp_remove_protocol(protocol); + + return ret; } /** @@ -81,9 +95,7 @@ int pnp_register_protocol(struct pnp_protocol *protocol) */ void pnp_unregister_protocol(struct pnp_protocol *protocol) { - mutex_lock(&pnp_lock); - list_del(&protocol->protocol_list); - mutex_unlock(&pnp_lock); + pnp_remove_protocol(protocol); device_unregister(&protocol->dev); } @@ -158,18 +170,36 @@ struct pnp_dev *pnp_alloc_dev(struct pnp_protocol *protocol, int id, return dev; } +static void pnp_delist_device(struct pnp_dev *dev) +{ + mutex_lock(&pnp_lock); + list_del(&dev->global_list); + list_del(&dev->protocol_list); + mutex_unlock(&pnp_lock); +} + int __pnp_add_device(struct pnp_dev *dev) { + int ret; + pnp_fixup_device(dev); dev->status = PNP_READY; + mutex_lock(&pnp_lock); + list_add_tail(&dev->global_list, &pnp_global); list_add_tail(&dev->protocol_list, &dev->protocol->devices); + mutex_unlock(&pnp_lock); - if (dev->protocol->can_wakeup) + + ret = device_register(&dev->dev); + if (ret) + pnp_delist_device(dev); + else if (dev->protocol->can_wakeup) device_set_wakeup_capable(&dev->dev, dev->protocol->can_wakeup(dev)); - return device_register(&dev->dev); + + return ret; } /* @@ -204,10 +234,7 @@ int pnp_add_device(struct pnp_dev *dev) void __pnp_remove_device(struct pnp_dev *dev) { - mutex_lock(&pnp_lock); - list_del(&dev->global_list); - list_del(&dev->protocol_list); - mutex_unlock(&pnp_lock); + pnp_delist_device(dev); device_unregister(&dev->dev); } -- cgit From ff92cfe7cbc05c6c62f5b06c7d9090f4c853cd51 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 11 Mar 2015 22:09:57 +0100 Subject: ACPI / video: Allow forcing native backlight on non win8 machines The native backlight behavior (so not registering both the acpi-video and the vendor backlight driver) can be useful on some non win8 machines too, so change the behavior of the video.use_native_backlight=1 or 0 kernel cmdline option to be: if user has set video.use_native_backlight=1 or 0, use that no matter if it is a win8 system or not. Also, we will put some known systems into the DMI table to make them either use native backlight interface or not, and the use_native_backlight_dmi is used to reflect that. Original-by: Hans de Goede Acked-by: Hans de Goede Signed-off-by: Aaron Lu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/video.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 26eb70c8f518..2f45dca31724 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -82,9 +82,15 @@ module_param(allow_duplicates, bool, 0644); * For Windows 8 systems: used to decide if video module * should skip registering backlight interface of its own. */ -static int use_native_backlight_param = -1; +enum { + NATIVE_BACKLIGHT_NOT_SET = -1, + NATIVE_BACKLIGHT_OFF, + NATIVE_BACKLIGHT_ON, +}; + +static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET; module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); -static bool use_native_backlight_dmi = true; +static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET; static int register_count; static struct mutex video_list_lock; @@ -237,15 +243,16 @@ static void acpi_video_switch_brightness(struct work_struct *work); static bool acpi_video_use_native_backlight(void) { - if (use_native_backlight_param != -1) + if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET) return use_native_backlight_param; - else + else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET) return use_native_backlight_dmi; + return acpi_osi_is_win8(); } bool acpi_video_verify_backlight_support(void) { - if (acpi_osi_is_win8() && acpi_video_use_native_backlight() && + if (acpi_video_use_native_backlight() && backlight_device_registered(BACKLIGHT_RAW)) return false; return acpi_video_backlight_support(); @@ -414,7 +421,7 @@ static int __init video_set_bqc_offset(const struct dmi_system_id *d) static int __init video_disable_native_backlight(const struct dmi_system_id *d) { - use_native_backlight_dmi = false; + use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF; return 0; } -- cgit From 64491882d399011bb0a4603e8cf481b65f0d3c5d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 20 Mar 2015 10:00:00 +0100 Subject: ACPI / video: Add force native backlight quirk for Lenovo Ideapad Z570 The Lenovo Ideapad Z570 (which is an Acer in disguise like some other Ideapads) has a broken acpi_video interface, this was fixed in commmit a11d342fb8 ("ACPI / video: force vendor backlight on Lenovo Ideapad Z570"). Which stops acpi_video from registering a backlight interface, but this is only a partial fix, because for people who have the ideapad-laptop module installed that module will now register a backlight interface, which also does not work, so we need to use the native intel_backlight interface. The Lenovo Ideapad 570 is a pre-win8 laptop / too old for the acpi-video code to automatically prefer the native backlight interface, so add a quirk for it. This commit also removes the previous incomplete fix. Link: https://bugzilla.redhat.com/show_bug.cgi?id=1187004 Tested-by: Be Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- drivers/acpi/video.c | 17 +++++++++++++++++ drivers/acpi/video_detect.c | 8 -------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 2f45dca31724..cc79d3fedfb2 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -425,6 +425,12 @@ static int __init video_disable_native_backlight(const struct dmi_system_id *d) return 0; } +static int __init video_enable_native_backlight(const struct dmi_system_id *d) +{ + use_native_backlight_dmi = NATIVE_BACKLIGHT_ON; + return 0; +} + static struct dmi_system_id video_dmi_table[] __initdata = { /* * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 @@ -566,6 +572,17 @@ static struct dmi_system_id video_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), }, }, + + /* Non win8 machines which need native backlight nevertheless */ + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ + .callback = video_enable_native_backlight, + .ident = "Lenovo Ideapad Z570", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), + }, + }, {} }; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 27c43499977a..c42feb2bacd0 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -174,14 +174,6 @@ 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"), - }, - }, { }, }; -- cgit From e90d5532773e2bcccc538dd346b9fc3482cd700c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 20 Mar 2015 13:59:27 +0100 Subject: driver core / PM: Add PM domain callbacks for device setup/cleanup If PM domains are in use, it may be necessary to prepare the code handling a PM domain for driver probing. For example, in some cases device drivers rely on the ability to power on the devices with the help of the IO runtime PM framework and the PM domain code needs to be ready for that. Also, if that code has not been fully initialized yet, the driver probing should be deferred. Moreover, after the probing is complete, it may be necessary to put the PM domain in question into the state reflecting the current needs of the devices in it, for example, so that power is not drawn in vain. The same should be done after removing a driver from a device, as the PM domain state may need to be changed to reflect the new situation. For these reasons, introduce new PM domain callbacks, ->activate, ->sync and ->dismiss called, respectively, before probing for a device driver, after the probing has completed successfully and if the probing has failed or the driver has been removed. Signed-off-by: Rafael J. Wysocki Acked-by: Ulf Hansson Reviewed-by: Kevin Hilman Acked-by: Greg Kroah-Hartman --- drivers/base/dd.c | 14 ++++++++++++++ include/linux/pm.h | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index cdc779cf79a3..aeb744891e44 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -298,6 +298,12 @@ static int really_probe(struct device *dev, struct device_driver *drv) goto probe_failed; } + if (dev->pm_domain && dev->pm_domain->activate) { + ret = dev->pm_domain->activate(dev); + if (ret) + goto probe_failed; + } + if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) @@ -308,6 +314,9 @@ static int really_probe(struct device *dev, struct device_driver *drv) goto probe_failed; } + if (dev->pm_domain && dev->pm_domain->sync) + dev->pm_domain->sync(dev); + driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", @@ -319,6 +328,8 @@ probe_failed: driver_sysfs_remove(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); + if (dev->pm_domain && dev->pm_domain->dismiss) + dev->pm_domain->dismiss(dev); if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ @@ -525,6 +536,9 @@ static void __device_release_driver(struct device *dev) devres_release_all(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); + if (dev->pm_domain && dev->pm_domain->dismiss) + dev->pm_domain->dismiss(dev); + klist_remove(&dev->p->knode_driver); if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, diff --git a/include/linux/pm.h b/include/linux/pm.h index e2f1be6dd9dd..2d29c64f8fb1 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -603,10 +603,18 @@ extern void dev_pm_put_subsys_data(struct device *dev); * Power domains provide callbacks that are executed during system suspend, * hibernation, system resume and during runtime PM transitions along with * subsystem-level and driver-level callbacks. + * + * @detach: Called when removing a device from the domain. + * @activate: Called before executing probe routines for bus types and drivers. + * @sync: Called after successful driver probe. + * @dismiss: Called after unsuccessful driver probe and after driver removal. */ struct dev_pm_domain { struct dev_pm_ops ops; void (*detach)(struct device *dev, bool power_off); + int (*activate)(struct device *dev); + void (*sync)(struct device *dev); + void (*dismiss)(struct device *dev); }; /* -- cgit From 632f7ce3f9b65039ac6c4f14ef91a0a1f3bdff59 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Mar 2015 15:55:12 +0100 Subject: PM / Domains: Sync runtime PM status with genpd after probe Buses which currently supports attaching devices to their PM domains, will invoke the dev_pm_domain_attach() API from their ->probe() callbacks. During the attach procedure, genpd power up the PM domain. In those scenarios where the bus/driver don't need to access its device during probe, it may leave it in runtime PM suspended state since that's also the default state. In that way, no notifications through the runtime PM callbacks will reach the PM domain during probe. For genpd, the consequence from the above scenario means the PM domain will remain powered. Therefore, implement the struct dev_pm_domain's ->sync() callback, which is invoked from driver core after the bus/driver has probed the device. It allows genpd to power off the PM domain if it's unused. Signed-off-by: Russell King [ Ulf: Updated patch according to updates in driver core ] Signed-off-by: Ulf Hansson Acked-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 45937f88e77c..295ff7189504 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2130,6 +2130,17 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off) genpd_queue_power_off_work(pd); } +static void genpd_dev_pm_sync(struct device *dev) +{ + struct generic_pm_domain *pd; + + pd = dev_to_genpd(dev); + if (IS_ERR(pd)) + return; + + genpd_queue_power_off_work(pd); +} + /** * genpd_dev_pm_attach - Attach a device to its PM domain using DT. * @dev: Device to attach. @@ -2196,6 +2207,7 @@ int genpd_dev_pm_attach(struct device *dev) } dev->pm_domain->detach = genpd_dev_pm_detach; + dev->pm_domain->sync = genpd_dev_pm_sync; pm_genpd_poweron(pd); return 0; -- cgit From 6d7d5c3266aa946b2049d9fed02186c1a378621b Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Mar 2015 17:20:28 +0000 Subject: PM / domains: quieten down generic pm domains PM domains are rather noisy; scheduling behaviour can cause callbacks to take longer, which causes them to spit out a warning-level message each time a callback takes a little longer than the previous time. There really isn't a need for this, except when debugging. Acked-by: Ulf Hansson Acked-by: Kevin Hilman Signed-off-by: Russell King Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 295ff7189504..2e2e6af1f3fe 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -173,8 +173,8 @@ static int genpd_power_on(struct generic_pm_domain *genpd) genpd->power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; genpd_recalc_cpu_exit_latency(genpd); - pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", - genpd->name, "on", elapsed_ns); + pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", + genpd->name, "on", elapsed_ns); return ret; } @@ -199,8 +199,8 @@ static int genpd_power_off(struct generic_pm_domain *genpd) genpd->power_off_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; - pr_warn("%s: Power-%s latency exceeded, new value %lld ns\n", - genpd->name, "off", elapsed_ns); + pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n", + genpd->name, "off", elapsed_ns); return ret; } -- cgit From 446d999c1c92cec996e759dc3c03110596e626f5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Mar 2015 17:20:33 +0000 Subject: PM / domains: factor out code to get the generic PM domain from a struct device The PM domain code contains two methods to get the generic PM domain for a struct device. One is dev_to_genpd() which is only safe when we know for certain that the device has a generic PM domain attached. The other is coded into genpd_dev_pm_detach() which ensures that the PM domain in the struct device is a generic PM domain (and so is safer). This commit factors out the safer version, documents it, and hides the unsafe dev_to_genpd(). Signed-off-by: Russell King Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 46 +++++++++++++++++++++++++++++++-------------- include/linux/pm_domain.h | 6 +++--- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 2e2e6af1f3fe..da25c0630789 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -68,7 +68,36 @@ static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name) return genpd; } -struct generic_pm_domain *dev_to_genpd(struct device *dev) +/* + * Get the generic PM domain for a particular struct device. + * This validates the struct device pointer, the PM domain pointer, + * and checks that the PM domain pointer is a real generic PM domain. + * Any failure results in NULL being returned. + */ +struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) +{ + struct generic_pm_domain *genpd = NULL, *gpd; + + if (IS_ERR_OR_NULL(dev) || IS_ERR_OR_NULL(dev->pm_domain)) + return NULL; + + mutex_lock(&gpd_list_lock); + list_for_each_entry(gpd, &gpd_list, gpd_list_node) { + if (&gpd->domain == dev->pm_domain) { + genpd = gpd; + break; + } + } + mutex_unlock(&gpd_list_lock); + + return genpd; +} + +/* + * This should only be used where we are certain that the pm_domain + * attached to the device is a genpd domain. + */ +static struct generic_pm_domain *dev_to_genpd(struct device *dev) { if (IS_ERR_OR_NULL(dev->pm_domain)) return ERR_PTR(-EINVAL); @@ -2093,21 +2122,10 @@ EXPORT_SYMBOL_GPL(of_genpd_get_from_provider); */ static void genpd_dev_pm_detach(struct device *dev, bool power_off) { - struct generic_pm_domain *pd = NULL, *gpd; + struct generic_pm_domain *pd; int ret = 0; - if (!dev->pm_domain) - return; - - mutex_lock(&gpd_list_lock); - list_for_each_entry(gpd, &gpd_list, gpd_list_node) { - if (&gpd->domain == dev->pm_domain) { - pd = gpd; - break; - } - } - mutex_unlock(&gpd_list_lock); - + pd = pm_genpd_lookup_dev(dev); if (!pd) return; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 080e778118ba..681ccb053f72 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -127,7 +127,7 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) return to_gpd_data(dev->power.subsys_data->domain_data); } -extern struct generic_pm_domain *dev_to_genpd(struct device *dev); +extern struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev); extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td); @@ -163,9 +163,9 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev) { return ERR_PTR(-ENOSYS); } -static inline struct generic_pm_domain *dev_to_genpd(struct device *dev) +static inline struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) { - return ERR_PTR(-ENOSYS); + return NULL; } static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, -- cgit From eeebc3bb4d5d7edb56cb594e8f0ec2cfb10c2518 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Feb 2015 16:32:46 +0100 Subject: ARM: cpuidle: Remove duplicate header inclusion The cpu_do_idle() function is always used by the cpuidle drivers. That led to have each driver including cpuidle.h and proc-fns.h, they are always paired. That makes a lot of duplicate headers inclusion. Instead of including both in each .c file, move the proc-fns.h header inclusion in the cpuidle.h header file directly, so we can save some line of code. Signed-off-by: Daniel Lezcano Acked-by: Kevin Hilman Acked-by: Lorenzo Pieralisi Tested-by: Lorenzo Pieralisi --- arch/arm/include/asm/cpuidle.h | 2 ++ arch/arm/kernel/cpuidle.c | 2 +- arch/arm/mach-davinci/cpuidle.c | 1 - arch/arm/mach-imx/cpuidle-imx6q.c | 1 - arch/arm/mach-imx/cpuidle-imx6sl.c | 1 - arch/arm/mach-imx/cpuidle-imx6sx.c | 1 - arch/arm/mach-omap2/cpuidle44xx.c | 1 - arch/arm/mach-s3c64xx/cpuidle.c | 2 +- arch/arm/mach-tegra/cpuidle-tegra20.c | 1 - arch/arm/mach-tegra/cpuidle-tegra30.c | 1 - drivers/cpuidle/cpuidle-at91.c | 1 - drivers/cpuidle/cpuidle-exynos.c | 1 - drivers/cpuidle/cpuidle-kirkwood.c | 1 - drivers/cpuidle/cpuidle-ux500.c | 1 - drivers/cpuidle/cpuidle-zynq.c | 1 - 15 files changed, 4 insertions(+), 14 deletions(-) diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h index af319ac4960c..348dc817b9f3 100644 --- a/arch/arm/include/asm/cpuidle.h +++ b/arch/arm/include/asm/cpuidle.h @@ -1,6 +1,8 @@ #ifndef __ASM_ARM_CPUIDLE_H #define __ASM_ARM_CPUIDLE_H +#include + #ifdef CONFIG_CPU_IDLE extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c index 89545f6c8403..45969f89f05c 100644 --- a/arch/arm/kernel/cpuidle.c +++ b/arch/arm/kernel/cpuidle.c @@ -10,7 +10,7 @@ */ #include -#include +#include int arm_cpuidle_simple_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c index e365c1bb1265..306ebc51599a 100644 --- a/arch/arm/mach-davinci/cpuidle.c +++ b/arch/arm/mach-davinci/cpuidle.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/arch/arm/mach-imx/cpuidle-imx6q.c b/arch/arm/mach-imx/cpuidle-imx6q.c index d76d08623f9f..8e21ccc1eda2 100644 --- a/arch/arm/mach-imx/cpuidle-imx6q.c +++ b/arch/arm/mach-imx/cpuidle-imx6q.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "common.h" #include "cpuidle.h" diff --git a/arch/arm/mach-imx/cpuidle-imx6sl.c b/arch/arm/mach-imx/cpuidle-imx6sl.c index 7d92e6584551..5742a9fd1ef2 100644 --- a/arch/arm/mach-imx/cpuidle-imx6sl.c +++ b/arch/arm/mach-imx/cpuidle-imx6sl.c @@ -9,7 +9,6 @@ #include #include #include -#include #include "common.h" #include "cpuidle.h" diff --git a/arch/arm/mach-imx/cpuidle-imx6sx.c b/arch/arm/mach-imx/cpuidle-imx6sx.c index 5a36722b089d..2c9f1a8bf245 100644 --- a/arch/arm/mach-imx/cpuidle-imx6sx.c +++ b/arch/arm/mach-imx/cpuidle-imx6sx.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include "common.h" diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c index 01e398a868bc..7622dbb05083 100644 --- a/arch/arm/mach-omap2/cpuidle44xx.c +++ b/arch/arm/mach-omap2/cpuidle44xx.c @@ -17,7 +17,6 @@ #include #include -#include #include "common.h" #include "pm.h" diff --git a/arch/arm/mach-s3c64xx/cpuidle.c b/arch/arm/mach-s3c64xx/cpuidle.c index 2eb072440dfa..93aa8cb70195 100644 --- a/arch/arm/mach-s3c64xx/cpuidle.c +++ b/arch/arm/mach-s3c64xx/cpuidle.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 4f25a7c7ca0f..e22b0d9fdc88 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -27,7 +27,6 @@ #include #include -#include #include #include diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index f8815ed65d9d..a2400ab44daa 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -27,7 +27,6 @@ #include #include -#include #include #include diff --git a/drivers/cpuidle/cpuidle-at91.c b/drivers/cpuidle/cpuidle-at91.c index aae7bfc1ea36..f2446c78d87c 100644 --- a/drivers/cpuidle/cpuidle-at91.c +++ b/drivers/cpuidle/cpuidle-at91.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #define AT91_MAX_STATES 2 diff --git a/drivers/cpuidle/cpuidle-exynos.c b/drivers/cpuidle/cpuidle-exynos.c index 26f5f29fdb03..0c06ea2f50bb 100644 --- a/drivers/cpuidle/cpuidle-exynos.c +++ b/drivers/cpuidle/cpuidle-exynos.c @@ -19,7 +19,6 @@ #include #include -#include #include #include diff --git a/drivers/cpuidle/cpuidle-kirkwood.c b/drivers/cpuidle/cpuidle-kirkwood.c index cea0a6c4b1db..d23d8f468c12 100644 --- a/drivers/cpuidle/cpuidle-kirkwood.c +++ b/drivers/cpuidle/cpuidle-kirkwood.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #define KIRKWOOD_MAX_STATES 2 diff --git a/drivers/cpuidle/cpuidle-ux500.c b/drivers/cpuidle/cpuidle-ux500.c index 66f81e410f0d..8bf895c0017d 100644 --- a/drivers/cpuidle/cpuidle-ux500.c +++ b/drivers/cpuidle/cpuidle-ux500.c @@ -19,7 +19,6 @@ #include #include -#include static atomic_t master = ATOMIC_INIT(0); static DEFINE_SPINLOCK(master_lock); diff --git a/drivers/cpuidle/cpuidle-zynq.c b/drivers/cpuidle/cpuidle-zynq.c index 002b8c9f98f5..543292b1d38e 100644 --- a/drivers/cpuidle/cpuidle-zynq.c +++ b/drivers/cpuidle/cpuidle-zynq.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #define ZYNQ_MAX_STATES 2 -- cgit From df6a0d6f633d684ef62bd92038c01a1781894f85 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 20 Mar 2015 17:20:38 +0000 Subject: PM / domains: avoid potential oops in pm_genpd_remove_device() pm_genpd_remove_device() tries hard to validate the generic PM domain passed to it, but the validation is not complete. dev->pm_domain contains a struct dev_pm_domain, which is the "base class" of generic PM domains. Other users of dev_pm_domains include stuff like vga_switheroo. Hence, a device could have a generic PM domain or a vga_switcheroo PM domain in dev->pm_domain. We need ot be certain that the PM domain is actually valid before we try to remove it. We can do this easily as we have a way to get the current validated generic PM domain for a struct device. This must match the generic PM domain being requested for removal. Convert the code to use this alternative validation method instead. Signed-off-by: Russell King Acked-by: Kevin Hilman Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index da25c0630789..2327613d4539 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1542,9 +1542,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, dev_dbg(dev, "%s()\n", __func__); - if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev) - || IS_ERR_OR_NULL(dev->pm_domain) - || pd_to_genpd(dev->pm_domain) != genpd) + if (!genpd || genpd != pm_genpd_lookup_dev(dev)) return -EINVAL; /* The above validation also means we have existing domain_data. */ -- cgit From 449e056c76cc8c777f3f5c3fb51c197ba2300c0c Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Feb 2015 16:32:45 +0100 Subject: ARM: cpuidle: Add a cpuidle ops structure to be used for DT The current state of the different cpuidle drivers is the different PM operations are passed via the platform_data using the platform driver paradigm. This approach allowed to split the low level PM code from the arch specific and the generic cpuidle code. Unfortunately there are complaints about this approach as, in the context of the single kernel image, we have multiple drivers loaded in memory for nothing and the platform driver is not adequate for cpuidle. This patch provides a common interface via cpuidle ops for all new cpuidle driver and a definition for the device tree. It will allow with the next patches to a have a common definition with ARM64 and share the same cpuidle driver. The code is optimized to use the __init section intensively in order to reduce the memory footprint after the driver is initialized and unify the function names with ARM64. Signed-off-by: Daniel Lezcano Acked-by: Kevin Hilman Acked-by: Rob Herring Acked-by: Catalin Marinas Tested-by: Lorenzo Pieralisi --- arch/arm/include/asm/cpuidle.h | 21 ++++++++++++ arch/arm/kernel/cpuidle.c | 72 +++++++++++++++++++++++++++++++++++++++ arch/arm64/include/asm/cpuidle.h | 5 ++- include/asm-generic/vmlinux.lds.h | 2 ++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h index 348dc817b9f3..0f8424924902 100644 --- a/arch/arm/include/asm/cpuidle.h +++ b/arch/arm/include/asm/cpuidle.h @@ -27,4 +27,25 @@ static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev, */ #define ARM_CPUIDLE_WFI_STATE ARM_CPUIDLE_WFI_STATE_PWR(UINT_MAX) +struct device_node; + +struct cpuidle_ops { + int (*suspend)(int cpu, unsigned long arg); + int (*init)(struct device_node *, int cpu); +}; + +struct of_cpuidle_method { + const char *method; + struct cpuidle_ops *ops; +}; + +#define CPUIDLE_METHOD_OF_DECLARE(name, _method, _ops) \ + static const struct of_cpuidle_method __cpuidle_method_of_table_##name \ + __used __section(__cpuidle_method_of_table) \ + = { .method = _method, .ops = _ops } + +extern int arm_cpuidle_suspend(int index); + +extern int arm_cpuidle_init(int cpu); + #endif diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c index 45969f89f05c..2b0dae3cd058 100644 --- a/arch/arm/kernel/cpuidle.c +++ b/arch/arm/kernel/cpuidle.c @@ -10,8 +10,17 @@ */ #include +#include +#include #include +extern struct of_cpuidle_method __cpuidle_method_of_table[]; + +static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel + __used __section(__cpuidle_method_of_table_end); + +static struct cpuidle_ops cpuidle_ops[NR_CPUS]; + int arm_cpuidle_simple_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { @@ -19,3 +28,66 @@ int arm_cpuidle_simple_enter(struct cpuidle_device *dev, return index; } + +int arm_cpuidle_suspend(int index) +{ + int ret = -EOPNOTSUPP; + int cpu = smp_processor_id(); + + if (cpuidle_ops[cpu].suspend) + ret = cpuidle_ops[cpu].suspend(cpu, index); + + return ret; +} + +static struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method) +{ + struct of_cpuidle_method *m = __cpuidle_method_of_table; + + for (; m->method; m++) + if (!strcmp(m->method, method)) + return m->ops; + + return NULL; +} + +static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu) +{ + const char *enable_method; + struct cpuidle_ops *ops; + + enable_method = of_get_property(dn, "enable-method", NULL); + if (!enable_method) + return -ENOENT; + + ops = arm_cpuidle_get_ops(enable_method); + if (!ops) { + pr_warn("%s: unsupported enable-method property: %s\n", + dn->full_name, enable_method); + return -EOPNOTSUPP; + } + + cpuidle_ops[cpu] = *ops; /* structure copy */ + + pr_notice("cpuidle: enable-method property '%s'" + " found operations\n", enable_method); + + return 0; +} + +int __init arm_cpuidle_init(int cpu) +{ + struct device_node *cpu_node = of_cpu_device_node_get(cpu); + int ret; + + if (!cpu_node) + return -ENODEV; + + ret = arm_cpuidle_read_ops(cpu_node, cpu); + if (!ret && cpuidle_ops[cpu].init) + ret = cpuidle_ops[cpu].init(cpu_node, cpu); + + of_node_put(cpu_node); + + return ret; +} diff --git a/arch/arm64/include/asm/cpuidle.h b/arch/arm64/include/asm/cpuidle.h index c60643f14cda..460a38bb84b9 100644 --- a/arch/arm64/include/asm/cpuidle.h +++ b/arch/arm64/include/asm/cpuidle.h @@ -17,5 +17,8 @@ static inline int cpu_suspend(unsigned long arg) return -EOPNOTSUPP; } #endif - +static inline int arm_cpuidle_suspend(int index) +{ + return cpu_suspend(index); +} #endif diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416..91c09305106d 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -167,6 +167,7 @@ #define IOMMU_OF_TABLES() OF_TABLE(CONFIG_OF_IOMMU, iommu) #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) #define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) +#define CPUIDLE_METHOD_OF_TABLES() OF_TABLE(CONFIG_CPU_IDLE, cpuidle_method) #define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) #define KERNEL_DTB() \ @@ -501,6 +502,7 @@ CLKSRC_OF_TABLES() \ IOMMU_OF_TABLES() \ CPU_METHOD_OF_TABLES() \ + CPUIDLE_METHOD_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() \ EARLYCON_OF_TABLES() -- cgit From 191de17aa3c139f58e1c75af5b4fe0e166e618da Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Feb 2015 16:32:45 +0100 Subject: ARM64: cpuidle: Replace cpu_suspend by the common ARM/ARM64 function Call the common ARM/ARM64 'arm_cpuidle_suspend' instead of cpu_suspend function which is specific to ARM64. Signed-off-by: Daniel Lezcano Acked-by: Kevin Hilman Acked-by: Rob Herring Acked-by: Lorenzo Pieralisi Tested-by: Lorenzo Pieralisi --- drivers/cpuidle/cpuidle-arm64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c index 39a2c62716c3..0cea24410d89 100644 --- a/drivers/cpuidle/cpuidle-arm64.c +++ b/drivers/cpuidle/cpuidle-arm64.c @@ -49,7 +49,7 @@ static int arm64_enter_idle_state(struct cpuidle_device *dev, * call the CPU ops suspend protocol with idle index as a * parameter. */ - ret = cpu_suspend(idx); + arm_cpuidle_suspend(idx); cpu_pm_exit(); } -- cgit From c9d62161490e2b74e51bcaf2acea07e27ce833eb Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Feb 2015 16:32:46 +0100 Subject: ARM64: cpuidle: Rename cpu_init_idle to a common function name With this change the cpuidle-arm64.c file calls the same function name for both ARM and ARM64. Signed-off-by: Daniel Lezcano Acked-by: Kevin Hilman Acked-by: Rob Herring Acked-by: Catalin Marinas Tested-by: Lorenzo Pieralisi --- arch/arm64/include/asm/cpuidle.h | 4 ++-- arch/arm64/kernel/cpuidle.c | 2 +- drivers/cpuidle/cpuidle-arm64.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/cpuidle.h b/arch/arm64/include/asm/cpuidle.h index 460a38bb84b9..141b2fcabaa6 100644 --- a/arch/arm64/include/asm/cpuidle.h +++ b/arch/arm64/include/asm/cpuidle.h @@ -4,10 +4,10 @@ #include #ifdef CONFIG_CPU_IDLE -extern int cpu_init_idle(unsigned int cpu); +extern int arm_cpuidle_init(unsigned int cpu); extern int cpu_suspend(unsigned long arg); #else -static inline int cpu_init_idle(unsigned int cpu) +static inline int arm_cpuidle_init(unsigned int cpu) { return -EOPNOTSUPP; } diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c index 5c0896647fd1..a78143a5c99f 100644 --- a/arch/arm64/kernel/cpuidle.c +++ b/arch/arm64/kernel/cpuidle.c @@ -15,7 +15,7 @@ #include #include -int cpu_init_idle(unsigned int cpu) +int arm_cpuidle_init(unsigned int cpu) { int ret = -EOPNOTSUPP; struct device_node *cpu_node = of_cpu_device_node_get(cpu); diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c index 0cea24410d89..6ef291c7046f 100644 --- a/drivers/cpuidle/cpuidle-arm64.c +++ b/drivers/cpuidle/cpuidle-arm64.c @@ -110,7 +110,7 @@ static int __init arm64_idle_init(void) * idle states suspend back-end specific data */ for_each_possible_cpu(cpu) { - ret = cpu_init_idle(cpu); + ret = arm_cpuidle_init(cpu); if (ret) { pr_err("CPU %d failed to init idle CPU ops\n", cpu); return ret; -- cgit From 69e6cb3d2f356a5c88abf18a8d266c31a74cfc08 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Feb 2015 16:32:46 +0100 Subject: ARM64: cpuidle: Remove arm64 reference In the next patch, this driver will be common across ARM/ARM64. Remove all refs to ARM64 as it will be shared with ARM32. Signed-off-by: Daniel Lezcano Acked-by: Kevin Hilman Acked-by: Rob Herring Acked-by: Lorenzo Pieralisi Tested-by: Lorenzo Pieralisi --- drivers/cpuidle/cpuidle-arm64.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c index 6ef291c7046f..1c94b88a7a7d 100644 --- a/drivers/cpuidle/cpuidle-arm64.c +++ b/drivers/cpuidle/cpuidle-arm64.c @@ -1,5 +1,5 @@ /* - * ARM64 generic CPU idle driver. + * ARM/ARM64 generic CPU idle driver. * * Copyright (C) 2014 ARM Ltd. * Author: Lorenzo Pieralisi @@ -9,7 +9,7 @@ * published by the Free Software Foundation. */ -#define pr_fmt(fmt) "CPUidle arm64: " fmt +#define pr_fmt(fmt) "CPUidle arm: " fmt #include #include @@ -23,7 +23,7 @@ #include "dt_idle_states.h" /* - * arm64_enter_idle_state - Programs CPU to enter the specified state + * arm_enter_idle_state - Programs CPU to enter the specified state * * dev: cpuidle device * drv: cpuidle driver @@ -32,8 +32,8 @@ * 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) +static int arm_enter_idle_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx) { int ret; @@ -57,8 +57,8 @@ static int arm64_enter_idle_state(struct cpuidle_device *dev, return ret ? -1 : idx; } -static struct cpuidle_driver arm64_idle_driver = { - .name = "arm64_idle", +static struct cpuidle_driver arm_idle_driver = { + .name = "arm_idle", .owner = THIS_MODULE, /* * State at index 0 is standby wfi and considered standard @@ -68,32 +68,32 @@ static struct cpuidle_driver arm64_idle_driver = { * handler for idle state index 0. */ .states[0] = { - .enter = arm64_enter_idle_state, + .enter = arm_enter_idle_state, .exit_latency = 1, .target_residency = 1, .power_usage = UINT_MAX, .name = "WFI", - .desc = "ARM64 WFI", + .desc = "ARM WFI", } }; -static const struct of_device_id arm64_idle_state_match[] __initconst = { +static const struct of_device_id arm_idle_state_match[] __initconst = { { .compatible = "arm,idle-state", - .data = arm64_enter_idle_state }, + .data = arm_enter_idle_state }, { }, }; /* - * arm64_idle_init + * arm_idle_init * - * Registers the arm64 specific cpuidle driver with the cpuidle + * Registers the arm specific cpuidle driver with the cpuidle * framework. It relies on core code to parse the idle states * and initialize them using driver data structures accordingly. */ -static int __init arm64_idle_init(void) +static int __init arm_idle_init(void) { int cpu, ret; - struct cpuidle_driver *drv = &arm64_idle_driver; + struct cpuidle_driver *drv = &arm_idle_driver; /* * Initialize idle states data, starting at index 1. @@ -101,7 +101,7 @@ static int __init arm64_idle_init(void) * 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); + ret = dt_init_idle_driver(drv, arm_idle_state_match, 1); if (ret <= 0) return ret ? : -ENODEV; @@ -119,4 +119,4 @@ static int __init arm64_idle_init(void) return cpuidle_register(drv, NULL); } -device_initcall(arm64_idle_init); +device_initcall(arm_idle_init); -- cgit From 0e0870448aa134e91fafe3c39ae270561b495459 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Mon, 2 Feb 2015 16:32:46 +0100 Subject: ARM: cpuidle: Enable the ARM64 driver for both ARM32/ARM64 ARM32 and ARM64 have the same DT definitions and the same approaches. The generic ARM cpuidle driver can be put in common for those two architectures. Signed-off-by: Daniel Lezcano Acked-by: Kevin Hilman Acked-by: Rob Herring Acked-by: Lorenzo Pieralisi Tested-by: Lorenzo Pieralisi --- arch/arm64/configs/defconfig | 2 +- drivers/cpuidle/Kconfig | 7 +-- drivers/cpuidle/Kconfig.arm | 28 ++++++--- drivers/cpuidle/Kconfig.arm64 | 13 ----- drivers/cpuidle/Makefile | 5 +- drivers/cpuidle/cpuidle-arm.c | 122 ++++++++++++++++++++++++++++++++++++++++ drivers/cpuidle/cpuidle-arm64.c | 122 ---------------------------------------- 7 files changed, 144 insertions(+), 155 deletions(-) delete mode 100644 drivers/cpuidle/Kconfig.arm64 create mode 100644 drivers/cpuidle/cpuidle-arm.c delete mode 100644 drivers/cpuidle/cpuidle-arm64.c diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index be1f12a5a5f0..af6a452b1aac 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -48,7 +48,7 @@ CONFIG_CMDLINE="console=ttyAMA0" # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_COMPAT=y CONFIG_CPU_IDLE=y -CONFIG_ARM64_CPUIDLE=y +CONFIG_ARM_CPUIDLE=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index c5029c1209b4..8c7930b5a65f 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -29,15 +29,10 @@ config DT_IDLE_STATES bool menu "ARM CPU Idle Drivers" -depends on ARM +depends on ARM || ARM64 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" diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 8e07c9419153..21340e0be73e 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -1,10 +1,20 @@ # # ARM CPU Idle drivers # +config ARM_CPUIDLE + bool "Generic ARM/ARM64 CPU idle Driver" + select DT_IDLE_STATES + help + Select this to enable generic cpuidle driver for ARM. + It provides a generic idle driver whose idle states are configured + at run-time through DT nodes. The CPUidle suspend backend is + initialized by calling the CPU operations init idle hook + provided by architecture code. + config ARM_BIG_LITTLE_CPUIDLE bool "Support for ARM big.LITTLE processors" depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS - depends on MCPM + depends on MCPM && !ARM64 select ARM_CPU_SUSPEND select CPU_IDLE_MULTIPLE_DRIVERS select DT_IDLE_STATES @@ -16,51 +26,51 @@ config ARM_BIG_LITTLE_CPUIDLE config ARM_CLPS711X_CPUIDLE bool "CPU Idle Driver for CLPS711X processors" - depends on ARCH_CLPS711X || COMPILE_TEST + depends on ARCH_CLPS711X && !ARM64 || COMPILE_TEST help Select this to enable cpuidle on Cirrus Logic CLPS711X SOCs. config ARM_HIGHBANK_CPUIDLE bool "CPU Idle Driver for Calxeda processors" - depends on ARM_PSCI + depends on ARM_PSCI && !ARM64 select ARM_CPU_SUSPEND help Select this to enable cpuidle on Calxeda processors. config ARM_KIRKWOOD_CPUIDLE bool "CPU Idle Driver for Marvell Kirkwood SoCs" - depends on MACH_KIRKWOOD + depends on MACH_KIRKWOOD && !ARM64 help This adds the CPU Idle driver for Marvell Kirkwood SoCs. config ARM_ZYNQ_CPUIDLE bool "CPU Idle Driver for Xilinx Zynq processors" - depends on ARCH_ZYNQ + depends on ARCH_ZYNQ && !ARM64 help Select this to enable cpuidle on Xilinx Zynq processors. config ARM_U8500_CPUIDLE bool "Cpu Idle Driver for the ST-E u8500 processors" - depends on ARCH_U8500 + depends on ARCH_U8500 && !ARM64 help Select this to enable cpuidle for ST-E u8500 processors config ARM_AT91_CPUIDLE bool "Cpu Idle Driver for the AT91 processors" default y - depends on ARCH_AT91 + depends on ARCH_AT91 && !ARM64 help Select this to enable cpuidle for AT91 processors config ARM_EXYNOS_CPUIDLE bool "Cpu Idle Driver for the Exynos processors" - depends on ARCH_EXYNOS + depends on ARCH_EXYNOS && !ARM64 select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP help Select this to enable cpuidle for Exynos processors config ARM_MVEBU_V7_CPUIDLE bool "CPU Idle Driver for mvebu v7 family processors" - depends on ARCH_MVEBU + depends on ARCH_MVEBU && !ARM64 help Select this to enable cpuidle on Armada 370, 38x and XP processors. diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64 deleted file mode 100644 index 6effb3656735..000000000000 --- a/drivers/cpuidle/Kconfig.arm64 +++ /dev/null @@ -1,13 +0,0 @@ -# -# ARM64 CPU Idle drivers -# - -config ARM64_CPUIDLE - bool "Generic ARM64 CPU idle Driver" - select DT_IDLE_STATES - help - Select this to enable generic cpuidle driver for ARM64. - It provides a generic idle driver whose idle states are configured - at run-time through DT nodes. The CPUidle suspend backend is - initialized by calling the CPU operations init idle hook - provided by architecture code. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 4d177b916f75..3ba81b1dffad 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -17,15 +17,12 @@ obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o +obj-$(CONFIG_ARM_CPUIDLE) += cpuidle-arm.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 diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c new file mode 100644 index 000000000000..1c94b88a7a7d --- /dev/null +++ b/drivers/cpuidle/cpuidle-arm.c @@ -0,0 +1,122 @@ +/* + * ARM/ARM64 generic CPU idle driver. + * + * Copyright (C) 2014 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * 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 arm: " fmt + +#include +#include +#include +#include +#include +#include + +#include + +#include "dt_idle_states.h" + +/* + * arm_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 arm_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. + */ + arm_cpuidle_suspend(idx); + + cpu_pm_exit(); + } + + return ret ? -1 : idx; +} + +static struct cpuidle_driver arm_idle_driver = { + .name = "arm_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 = arm_enter_idle_state, + .exit_latency = 1, + .target_residency = 1, + .power_usage = UINT_MAX, + .name = "WFI", + .desc = "ARM WFI", + } +}; + +static const struct of_device_id arm_idle_state_match[] __initconst = { + { .compatible = "arm,idle-state", + .data = arm_enter_idle_state }, + { }, +}; + +/* + * arm_idle_init + * + * Registers the arm 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 arm_idle_init(void) +{ + int cpu, ret; + struct cpuidle_driver *drv = &arm_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, arm_idle_state_match, 1); + if (ret <= 0) + return ret ? : -ENODEV; + + /* + * Call arch CPU operations in order to initialize + * idle states suspend back-end specific data + */ + for_each_possible_cpu(cpu) { + ret = arm_cpuidle_init(cpu); + if (ret) { + pr_err("CPU %d failed to init idle CPU ops\n", cpu); + return ret; + } + } + + return cpuidle_register(drv, NULL); +} +device_initcall(arm_idle_init); diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c deleted file mode 100644 index 1c94b88a7a7d..000000000000 --- a/drivers/cpuidle/cpuidle-arm64.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * ARM/ARM64 generic CPU idle driver. - * - * Copyright (C) 2014 ARM Ltd. - * Author: Lorenzo Pieralisi - * - * 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 arm: " fmt - -#include -#include -#include -#include -#include -#include - -#include - -#include "dt_idle_states.h" - -/* - * arm_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 arm_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. - */ - arm_cpuidle_suspend(idx); - - cpu_pm_exit(); - } - - return ret ? -1 : idx; -} - -static struct cpuidle_driver arm_idle_driver = { - .name = "arm_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 = arm_enter_idle_state, - .exit_latency = 1, - .target_residency = 1, - .power_usage = UINT_MAX, - .name = "WFI", - .desc = "ARM WFI", - } -}; - -static const struct of_device_id arm_idle_state_match[] __initconst = { - { .compatible = "arm,idle-state", - .data = arm_enter_idle_state }, - { }, -}; - -/* - * arm_idle_init - * - * Registers the arm 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 arm_idle_init(void) -{ - int cpu, ret; - struct cpuidle_driver *drv = &arm_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, arm_idle_state_match, 1); - if (ret <= 0) - return ret ? : -ENODEV; - - /* - * Call arch CPU operations in order to initialize - * idle states suspend back-end specific data - */ - for_each_possible_cpu(cpu) { - ret = arm_cpuidle_init(cpu); - if (ret) { - pr_err("CPU %d failed to init idle CPU ops\n", cpu); - return ret; - } - } - - return cpuidle_register(drv, NULL); -} -device_initcall(arm_idle_init); -- cgit From a0d46a3dfdc3f3d639b3fa84b84a58e116e4bf2c Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 5 Mar 2015 16:44:42 +0100 Subject: ARM: cpuidle: Register per cpuidle device If the cpuidle init cpu operation returns -ENXIO, therefore reporting HW failure or misconfiguration, the CPUidle driver skips the respective cpuidle device initialization because the associated platform back-end HW is not operational. That prevents the system to crash and allows to handle the error gracefully. For example, on Qcom's platform, each core has a SPM. The device associated with this SPM is initialized before the cpuidle framework. If there is an error in the initialization (eg. error in the DT), the system continues to boot but in degraded mode as some SPM may not be correctly initialized. Signed-off-by: Daniel Lezcano Acked-by: Lorenzo Pieralisi --- drivers/cpuidle/cpuidle-arm.c | 45 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c index 1c94b88a7a7d..545069d5fdfb 100644 --- a/drivers/cpuidle/cpuidle-arm.c +++ b/drivers/cpuidle/cpuidle-arm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -94,6 +95,7 @@ static int __init arm_idle_init(void) { int cpu, ret; struct cpuidle_driver *drv = &arm_idle_driver; + struct cpuidle_device *dev; /* * Initialize idle states data, starting at index 1. @@ -105,18 +107,57 @@ static int __init arm_idle_init(void) if (ret <= 0) return ret ? : -ENODEV; + ret = cpuidle_register_driver(drv); + if (ret) { + pr_err("Failed to register cpuidle driver\n"); + return ret; + } + /* * Call arch CPU operations in order to initialize * idle states suspend back-end specific data */ for_each_possible_cpu(cpu) { ret = arm_cpuidle_init(cpu); + + /* + * Skip the cpuidle device initialization if the reported + * failure is a HW misconfiguration/breakage (-ENXIO). + */ + if (ret == -ENXIO) + continue; + if (ret) { pr_err("CPU %d failed to init idle CPU ops\n", cpu); - return ret; + goto out_fail; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + pr_err("Failed to allocate cpuidle device\n"); + goto out_fail; + } + dev->cpu = cpu; + + ret = cpuidle_register_device(dev); + if (ret) { + pr_err("Failed to register cpuidle device for CPU %d\n", + cpu); + kfree(dev); + goto out_fail; } } - return cpuidle_register(drv, NULL); + return 0; +out_fail: + while (--cpu >= 0) { + dev = per_cpu(cpuidle_devices, cpu); + cpuidle_unregister_device(dev); + kfree(dev); + } + + cpuidle_unregister_driver(drv); + + return ret; } device_initcall(arm_idle_init); -- cgit From 9a309d6fd213911321acbfe839e0bdb3a7a9f4bf Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 24 Mar 2015 10:49:55 +0100 Subject: ARM: cpuidle: Document the code Add kernel-doc format documentation in the code. Signed-off-by: Daniel Lezcano --- arch/arm/kernel/cpuidle.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c index 2b0dae3cd058..318da33465f4 100644 --- a/arch/arm/kernel/cpuidle.c +++ b/arch/arm/kernel/cpuidle.c @@ -21,6 +21,17 @@ static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel static struct cpuidle_ops cpuidle_ops[NR_CPUS]; +/** + * arm_cpuidle_simple_enter() - a wrapper to cpu_do_idle() + * @dev: not used + * @drv: not used + * @index: not used + * + * A trivial wrapper to allow the cpu_do_idle function to be assigned as a + * cpuidle callback by matching the function signature. + * + * Returns the index passed as parameter + */ int arm_cpuidle_simple_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { @@ -29,6 +40,16 @@ int arm_cpuidle_simple_enter(struct cpuidle_device *dev, return index; } +/** + * arm_cpuidle_suspend() - function to enter low power idle states + * @index: an integer used as an identifier for the low level PM callbacks + * + * This function calls the underlying arch specific low level PM code as + * registered at the init time. + * + * Returns -EOPNOTSUPP if no suspend callback is defined, the result of the + * callback otherwise. + */ int arm_cpuidle_suspend(int index) { int ret = -EOPNOTSUPP; @@ -40,6 +61,15 @@ int arm_cpuidle_suspend(int index) return ret; } +/** + * arm_cpuidle_get_ops() - find a registered cpuidle_ops by name + * @method: the method name + * + * Search in the __cpuidle_method_of_table array the cpuidle ops matching the + * method name. + * + * Returns a struct cpuidle_ops pointer, NULL if not found. + */ static struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method) { struct of_cpuidle_method *m = __cpuidle_method_of_table; @@ -51,6 +81,19 @@ static struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method) return NULL; } +/** + * arm_cpuidle_read_ops() - Initialize the cpuidle ops with the device tree + * @dn: a pointer to a struct device node corresponding to a cpu node + * @cpu: the cpu identifier + * + * Get the method name defined in the 'enable-method' property, retrieve the + * associated cpuidle_ops and do a struct copy. This copy is needed because all + * cpuidle_ops are tagged __initdata and will be unloaded after the init + * process. + * + * Return 0 on sucess, -ENOENT if no 'enable-method' is defined, -EOPNOTSUPP if + * no cpuidle_ops is registered for the 'enable-method'. + */ static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu) { const char *enable_method; @@ -75,6 +118,22 @@ static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu) return 0; } +/** + * arm_cpuidle_init() - Initialize cpuidle_ops for a specific cpu + * @cpu: the cpu to be initialized + * + * Initialize the cpuidle ops with the device for the cpu and then call + * the cpu's idle initialization callback. This may fail if the underlying HW + * is not operational. + * + * Returns: + * 0 on success, + * -ENODEV if it fails to find the cpu node in the device tree, + * -EOPNOTSUPP if it does not find a registered cpuidle_ops for this cpu, + * -ENOENT if it fails to find an 'enable-method' property, + * -ENXIO if the HW reports a failure or a misconfiguration, + * -ENOMEM if the HW report an memory allocation failure + */ int __init arm_cpuidle_init(int cpu) { struct device_node *cpu_node = of_cpu_device_node_get(cpu); -- cgit From f42712a92e660fbee2193c5dde672470d099f63f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2015 00:18:05 +0100 Subject: driver core: property: Update fwnode_property_read_string_array() Commit 5c0acf3b4f96 (driver core: Add comments about returning array counts) forgot to update fwnode_property_read_string_array() along the lines of device_property_read_string_array(), although it did change the kerneldoc comment of it. Fix that. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- drivers/base/property.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index b524a4e42a83..cf3b2160e34e 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -347,8 +347,10 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, size_t nval) { if (is_of_node(fwnode)) - return of_property_read_string_array(of_node(fwnode), propname, - val, nval); + return val ? + of_property_read_string_array(of_node(fwnode), propname, + val, nval) : + of_property_count_strings(of_node(fwnode), propname); else if (is_acpi_node(fwnode)) return acpi_dev_prop_read(acpi_node(fwnode), propname, DEV_PROP_STRING, val, nval); -- cgit From 9017f25254e474f2cc05df489e4f83b972d3d6fd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2015 00:24:16 +0100 Subject: driver core: Implement device property accessors through fwnode ones Now that the ACPI companions of devices are pointed to by the fwnode field in struct device, the device_property_*() accessor functions can be modified to use their fwnode_property_*() counterparts internally with minimum extra overhead in the IS_ENABLED(CONFIG_OF) case, so make those changes. This allows us to get rid of the rather ugly DEV_PROP_READ_ARRAY() macro among other things. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- drivers/base/property.c | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index cf3b2160e34e..62787bc89a1d 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -15,6 +15,12 @@ #include #include +static inline struct fwnode_handle *dev_fwnode(struct device *dev) +{ + return IS_ENABLED(CONFIG_OF) && dev->of_node ? + &dev->of_node->fwnode : dev->fwnode; +} + /** * device_property_present - check if a property of a device is present * @dev: Device whose property is being checked @@ -24,10 +30,7 @@ */ bool device_property_present(struct device *dev, const char *propname) { - if (IS_ENABLED(CONFIG_OF) && dev->of_node) - return of_property_read_bool(dev->of_node, propname); - - return !acpi_dev_prop_get(ACPI_COMPANION(dev), propname, NULL); + return fwnode_property_present(dev_fwnode(dev), propname); } EXPORT_SYMBOL_GPL(device_property_present); @@ -47,17 +50,6 @@ bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) } EXPORT_SYMBOL_GPL(fwnode_property_present); -#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \ - (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \ - : of_property_count_elems_of_size((node), (propname), sizeof(type)) - -#define DEV_PROP_READ_ARRAY(_dev_, _propname_, _type_, _proptype_, _val_, _nval_) \ - IS_ENABLED(CONFIG_OF) && _dev_->of_node ? \ - (OF_DEV_PROP_READ_ARRAY(_dev_->of_node, _propname_, _type_, \ - _val_, _nval_)) : \ - acpi_dev_prop_read(ACPI_COMPANION(_dev_), _propname_, \ - _proptype_, _val_, _nval_) - /** * device_property_read_u8_array - return a u8 array property of a device * @dev: Device to get the property of @@ -78,7 +70,7 @@ EXPORT_SYMBOL_GPL(fwnode_property_present); int device_property_read_u8_array(struct device *dev, const char *propname, u8 *val, size_t nval) { - return DEV_PROP_READ_ARRAY(dev, propname, u8, DEV_PROP_U8, val, nval); + return fwnode_property_read_u8_array(dev_fwnode(dev), propname, val, nval); } EXPORT_SYMBOL_GPL(device_property_read_u8_array); @@ -102,7 +94,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u8_array); int device_property_read_u16_array(struct device *dev, const char *propname, u16 *val, size_t nval) { - return DEV_PROP_READ_ARRAY(dev, propname, u16, DEV_PROP_U16, val, nval); + return fwnode_property_read_u16_array(dev_fwnode(dev), propname, val, nval); } EXPORT_SYMBOL_GPL(device_property_read_u16_array); @@ -126,7 +118,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u16_array); int device_property_read_u32_array(struct device *dev, const char *propname, u32 *val, size_t nval) { - return DEV_PROP_READ_ARRAY(dev, propname, u32, DEV_PROP_U32, val, nval); + return fwnode_property_read_u32_array(dev_fwnode(dev), propname, val, nval); } EXPORT_SYMBOL_GPL(device_property_read_u32_array); @@ -150,7 +142,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u32_array); int device_property_read_u64_array(struct device *dev, const char *propname, u64 *val, size_t nval) { - return DEV_PROP_READ_ARRAY(dev, propname, u64, DEV_PROP_U64, val, nval); + return fwnode_property_read_u64_array(dev_fwnode(dev), propname, val, nval); } EXPORT_SYMBOL_GPL(device_property_read_u64_array); @@ -174,11 +166,7 @@ EXPORT_SYMBOL_GPL(device_property_read_u64_array); int device_property_read_string_array(struct device *dev, const char *propname, const char **val, size_t nval) { - return IS_ENABLED(CONFIG_OF) && dev->of_node ? - (val ? of_property_read_string_array(dev->of_node, propname, val, nval) - : of_property_count_strings(dev->of_node, propname)) : - acpi_dev_prop_read(ACPI_COMPANION(dev), propname, - DEV_PROP_STRING, val, nval); + return fwnode_property_read_string_array(dev_fwnode(dev), propname, val, nval); } EXPORT_SYMBOL_GPL(device_property_read_string_array); @@ -199,13 +187,14 @@ EXPORT_SYMBOL_GPL(device_property_read_string_array); int device_property_read_string(struct device *dev, const char *propname, const char **val) { - return IS_ENABLED(CONFIG_OF) && dev->of_node ? - of_property_read_string(dev->of_node, propname, val) : - acpi_dev_prop_read(ACPI_COMPANION(dev), propname, - DEV_PROP_STRING, val, 1); + return fwnode_property_read_string(dev_fwnode(dev), propname, val); } EXPORT_SYMBOL_GPL(device_property_read_string); +#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \ + (val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \ + : of_property_count_elems_of_size((node), (propname), sizeof(type)) + #define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \ ({ \ int _ret_; \ -- cgit From dff1eb047ff649c3fc3adc42776039b8b0c5a869 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 25 Mar 2015 23:13:08 +0900 Subject: ACPI/PMIC: Fix typo in MODULE_DESCRIPTION in intel_pmic_crc.c This patch fix a spelling typo in MODULE_DESCRIPTION within intel_pmic_crc.c Signed-off-by: Masanari Iida Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pmic/intel_pmic_crc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c index ef7d8ff95abe..42df46a86c25 100644 --- a/drivers/acpi/pmic/intel_pmic_crc.c +++ b/drivers/acpi/pmic/intel_pmic_crc.c @@ -207,5 +207,5 @@ static int __init intel_crc_pmic_opregion_driver_init(void) } module_init(intel_crc_pmic_opregion_driver_init); -MODULE_DESCRIPTION("CrystalCove ACPI opration region driver"); +MODULE_DESCRIPTION("CrystalCove ACPI operation region driver"); MODULE_LICENSE("GPL"); -- cgit From 7b02ccea2e70e5e3e9fccf36123b77240323b8d6 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Fri, 27 Mar 2015 18:08:48 +0800 Subject: ACPI / PM: Enable all wakeup GPEs in suspend-to-idle Currently, in suspend-to-idle, wakeup GPE for PCI devices are handled properly because acpi_pci_sleep_wake() invokes acpi_enable_gpe() to enable the wakeup GPE directly. But for the other wakeup-capable devices in ACPI bus, acpi_enable_wakeup_devices() should be invoked to update enable_for_wake mask in gpe_register_info structure, thus acpi_enable_all_wakeup_gpes() can enable the wakeup GPE referred in _PRW methods. And acpi_disable_wakeup_devices() will be called before disable_irq_wake() in acpi_freeze_restore() to restore the mask. This patch fixes a power button wakeup problem on Surface Pro 3, on which platform power button uses EC to deliver event (EC GPE is referred in _PRW). Note: enabling EC GPE during freeze state may bring some risks because EC events are expected to fire more frequently than others. Thus it may bring the system out of freeze state unnecessarily. (We already have comments about this in bugzilla) Link: https://bugzilla.kernel.org/show_bug.cgi?id=84651 Reported-and-tested-by: Ethan Schoonover Tested-by: Peter Amidon Tested-by: Yani Ioadnnou Tested-by: Mister Wardrop Tested-by: Anton Anikin Tested-by: Keith McClelland Reviewed-by: Zhang Rui Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 24a830b39ec3..2f0d4db40a9e 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -629,6 +629,7 @@ static int acpi_freeze_begin(void) static int acpi_freeze_prepare(void) { + acpi_enable_wakeup_devices(ACPI_STATE_S0); acpi_enable_all_wakeup_gpes(); acpi_os_wait_events_complete(); enable_irq_wake(acpi_gbl_FADT.sci_interrupt); @@ -637,6 +638,7 @@ static int acpi_freeze_prepare(void) static void acpi_freeze_restore(void) { + acpi_disable_wakeup_devices(ACPI_STATE_S0); disable_irq_wake(acpi_gbl_FADT.sci_interrupt); acpi_enable_all_runtime_gpes(); } -- cgit From 6f240fbc474018872f6d9664a3bd350bd22cc8d9 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Tue, 10 Mar 2015 09:27:35 +0900 Subject: PM / devfreq: event: Add const keyword for devfreq_event_ops structure This patch adds the const keyword for devfreq_event_ops structure because the ops of devfreq_event_desc structure should not be changed after initialization. Cc: Myungjoo Ham Cc: Kyungmin Park Signed-off-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/event/exynos-ppmu.c | 2 +- include/linux/devfreq-event.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c index ad8347385f53..7d99d13bacd8 100644 --- a/drivers/devfreq/event/exynos-ppmu.c +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -194,7 +194,7 @@ static int exynos_ppmu_get_event(struct devfreq_event_dev *edev, return 0; } -static struct devfreq_event_ops exynos_ppmu_ops = { +static const struct devfreq_event_ops exynos_ppmu_ops = { .disable = exynos_ppmu_disable, .set_event = exynos_ppmu_set_event, .get_event = exynos_ppmu_get_event, diff --git a/include/linux/devfreq-event.h b/include/linux/devfreq-event.h index 602fbbfcfeed..0a83a1e648b0 100644 --- a/include/linux/devfreq-event.h +++ b/include/linux/devfreq-event.h @@ -91,7 +91,7 @@ struct devfreq_event_desc { const char *name; void *driver_data; - struct devfreq_event_ops *ops; + const struct devfreq_event_ops *ops; }; #if defined(CONFIG_PM_DEVFREQ_EVENT) -- cgit From 4b524f12188c20c64c602ff09ba26a5d39d16b1c Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 18 Mar 2015 13:35:09 +0900 Subject: PM / devfreq: correct misleading comment _remove_devfreq() does not have @skip anymore after 3.16. The comment for _remove_devfreq() has been updated correspondingly. Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 30b538d8cc90..ca1b362d77e2 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -392,7 +392,6 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, /** * _remove_devfreq() - Remove devfreq from the list and release its resources. * @devfreq: the devfreq struct - * @skip: skip calling device_unregister(). */ static void _remove_devfreq(struct devfreq *devfreq) { -- cgit From 11573e9132ae40239195fe5162d48caf2b7d85bb Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Tue, 17 Mar 2015 10:36:12 +0100 Subject: PM / devfreq: tegra: Update to v5 of the submitted patches There seemed to be some miscommunication and an old version of the submitted patches was merged. This commit updates the driver to v5, which had this changelog: * Clarify the units of avg_dependency_threshold * Remove unused references to platform_device * Enable and disable interrupts on governor events * Make sure we handle all interrupts for any of the devices we are sampling * Move locking to be per-actmon-device Signed-off-by: Tomeu Vizoso CC: Alex Frid CC: Mikko Perttunen [Added const to device ID by MyungJoo] Signed-off-by: MyungJoo Ham --- drivers/devfreq/tegra-devfreq.c | 457 ++++++++++++++++++++++------------------ 1 file changed, 253 insertions(+), 204 deletions(-) diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c index 34790961af5a..d2f4d5ecaefd 100644 --- a/drivers/devfreq/tegra-devfreq.c +++ b/drivers/devfreq/tegra-devfreq.c @@ -62,7 +62,8 @@ #define ACTMON_BELOW_WMARK_WINDOW 3 #define ACTMON_BOOST_FREQ_STEP 16000 -/* activity counter is incremented every 256 memory transactions, and each +/* + * Activity counter is incremented every 256 memory transactions, and each * transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is * 4 * 256 = 1024. */ @@ -85,16 +86,25 @@ * struct tegra_devfreq_device_config - configuration specific to an ACTMON * device * - * Coefficients and thresholds are in % + * Coefficients and thresholds are percentages unless otherwise noted */ struct tegra_devfreq_device_config { u32 offset; u32 irq_mask; + /* Factors applied to boost_freq every consecutive watermark breach */ unsigned int boost_up_coeff; unsigned int boost_down_coeff; + + /* Define the watermark bounds when applied to the current avg */ unsigned int boost_up_threshold; unsigned int boost_down_threshold; + + /* + * Threshold of activity (cycles) below which the CPU frequency isn't + * to be taken into account. This is to avoid increasing the EMC + * frequency when the CPU is very busy but not accessing the bus often. + */ u32 avg_dependency_threshold; }; @@ -105,7 +115,7 @@ enum tegra_actmon_device { static struct tegra_devfreq_device_config actmon_device_configs[] = { { - /* MCALL */ + /* MCALL: All memory accesses (including from the CPUs) */ .offset = 0x1c0, .irq_mask = 1 << 26, .boost_up_coeff = 200, @@ -114,7 +124,7 @@ static struct tegra_devfreq_device_config actmon_device_configs[] = { .boost_down_threshold = 40, }, { - /* MCCPU */ + /* MCCPU: memory accesses from the CPUs */ .offset = 0x200, .irq_mask = 1 << 25, .boost_up_coeff = 800, @@ -132,25 +142,29 @@ static struct tegra_devfreq_device_config actmon_device_configs[] = { */ struct tegra_devfreq_device { const struct tegra_devfreq_device_config *config; + void __iomem *regs; + spinlock_t lock; - void __iomem *regs; - u32 avg_band_freq; - u32 avg_count; + /* Average event count sampled in the last interrupt */ + u32 avg_count; - unsigned long target_freq; - unsigned long boost_freq; + /* + * Extra frequency to increase the target by due to consecutive + * watermark breaches. + */ + unsigned long boost_freq; + + /* Optimal frequency calculated from the stats for this device */ + unsigned long target_freq; }; struct tegra_devfreq { struct devfreq *devfreq; - struct platform_device *pdev; struct reset_control *reset; struct clk *clock; void __iomem *regs; - spinlock_t lock; - struct clk *emc_clock; unsigned long max_freq; unsigned long cur_freq; @@ -174,19 +188,43 @@ static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = { { 250000, 100000 }, }; +static u32 actmon_readl(struct tegra_devfreq *tegra, u32 offset) +{ + return readl(tegra->regs + offset); +} + +static void actmon_writel(struct tegra_devfreq *tegra, u32 val, u32 offset) +{ + writel(val, tegra->regs + offset); +} + +static u32 device_readl(struct tegra_devfreq_device *dev, u32 offset) +{ + return readl(dev->regs + offset); +} + +static void device_writel(struct tegra_devfreq_device *dev, u32 val, + u32 offset) +{ + writel(val, dev->regs + offset); +} + static unsigned long do_percent(unsigned long val, unsigned int pct) { return val * pct / 100; } -static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq_device *dev) +static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra, + struct tegra_devfreq_device *dev) { u32 avg = dev->avg_count; - u32 band = dev->avg_band_freq * ACTMON_SAMPLING_PERIOD; + u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ; + u32 band = avg_band_freq * ACTMON_SAMPLING_PERIOD; + + device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK); - writel(avg + band, dev->regs + ACTMON_DEV_AVG_UPPER_WMARK); - avg = max(avg, band); - writel(avg - band, dev->regs + ACTMON_DEV_AVG_LOWER_WMARK); + avg = max(dev->avg_count, band); + device_writel(dev, avg - band, ACTMON_DEV_AVG_LOWER_WMARK); } static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra, @@ -194,96 +232,96 @@ static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra, { u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; - writel(do_percent(val, dev->config->boost_up_threshold), - dev->regs + ACTMON_DEV_UPPER_WMARK); + device_writel(dev, do_percent(val, dev->config->boost_up_threshold), + ACTMON_DEV_UPPER_WMARK); - writel(do_percent(val, dev->config->boost_down_threshold), - dev->regs + ACTMON_DEV_LOWER_WMARK); + device_writel(dev, do_percent(val, dev->config->boost_down_threshold), + ACTMON_DEV_LOWER_WMARK); } static void actmon_write_barrier(struct tegra_devfreq *tegra) { /* ensure the update has reached the ACTMON */ wmb(); - readl(tegra->regs + ACTMON_GLB_STATUS); + actmon_readl(tegra, ACTMON_GLB_STATUS); } -static irqreturn_t actmon_isr(int irq, void *data) +static void actmon_isr_device(struct tegra_devfreq *tegra, + struct tegra_devfreq_device *dev) { - struct tegra_devfreq *tegra = data; - struct tegra_devfreq_device *dev = NULL; unsigned long flags; - u32 val; - unsigned int i; + u32 intr_status, dev_ctrl; - val = readl(tegra->regs + ACTMON_GLB_STATUS); + spin_lock_irqsave(&dev->lock, flags); - for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { - if (val & tegra->devices[i].config->irq_mask) { - dev = tegra->devices + i; - break; - } - } - - if (!dev) - return IRQ_NONE; - - spin_lock_irqsave(&tegra->lock, flags); - - dev->avg_count = readl(dev->regs + ACTMON_DEV_AVG_COUNT); - tegra_devfreq_update_avg_wmark(dev); + dev->avg_count = device_readl(dev, ACTMON_DEV_AVG_COUNT); + tegra_devfreq_update_avg_wmark(tegra, dev); - val = readl(dev->regs + ACTMON_DEV_INTR_STATUS); - if (val & ACTMON_DEV_INTR_CONSECUTIVE_UPPER) { - val = readl(dev->regs + ACTMON_DEV_CTRL) | - ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN | - ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + intr_status = device_readl(dev, ACTMON_DEV_INTR_STATUS); + dev_ctrl = device_readl(dev, ACTMON_DEV_CTRL); + if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_UPPER) { /* * new_boost = min(old_boost * up_coef + step, max_freq) */ dev->boost_freq = do_percent(dev->boost_freq, dev->config->boost_up_coeff); dev->boost_freq += ACTMON_BOOST_FREQ_STEP; - if (dev->boost_freq >= tegra->max_freq) { - dev->boost_freq = tegra->max_freq; - val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; - } - writel(val, dev->regs + ACTMON_DEV_CTRL); - } else if (val & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) { - val = readl(dev->regs + ACTMON_DEV_CTRL) | - ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN | - ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + + if (dev->boost_freq >= tegra->max_freq) + dev->boost_freq = tegra->max_freq; + else + dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; + } else if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) { /* * new_boost = old_boost * down_coef * or 0 if (old_boost * down_coef < step / 2) */ dev->boost_freq = do_percent(dev->boost_freq, dev->config->boost_down_coeff); - if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1)) { + + dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; + + if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1)) dev->boost_freq = 0; - val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; - } - writel(val, dev->regs + ACTMON_DEV_CTRL); + else + dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; } if (dev->config->avg_dependency_threshold) { - val = readl(dev->regs + ACTMON_DEV_CTRL); if (dev->avg_count >= dev->config->avg_dependency_threshold) - val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; else if (dev->boost_freq == 0) - val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; - writel(val, dev->regs + ACTMON_DEV_CTRL); + dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; } - writel(ACTMON_INTR_STATUS_CLEAR, dev->regs + ACTMON_DEV_INTR_STATUS); + device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL); + + device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS); actmon_write_barrier(tegra); - spin_unlock_irqrestore(&tegra->lock, flags); + spin_unlock_irqrestore(&dev->lock, flags); +} - return IRQ_WAKE_THREAD; +static irqreturn_t actmon_isr(int irq, void *data) +{ + struct tegra_devfreq *tegra = data; + bool handled = false; + unsigned int i; + u32 val; + + val = actmon_readl(tegra, ACTMON_GLB_STATUS); + for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { + if (val & tegra->devices[i].config->irq_mask) { + actmon_isr_device(tegra, tegra->devices + i); + handled = true; + } + } + + return handled ? IRQ_WAKE_THREAD : IRQ_NONE; } static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra, @@ -317,7 +355,7 @@ static void actmon_update_target(struct tegra_devfreq *tegra, static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq); } - spin_lock_irqsave(&tegra->lock, flags); + spin_lock_irqsave(&dev->lock, flags); dev->target_freq = dev->avg_count / ACTMON_SAMPLING_PERIOD; avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold; @@ -327,7 +365,7 @@ static void actmon_update_target(struct tegra_devfreq *tegra, if (dev->avg_count >= dev->config->avg_dependency_threshold) dev->target_freq = max(dev->target_freq, static_cpu_emc_freq); - spin_unlock_irqrestore(&tegra->lock, flags); + spin_unlock_irqrestore(&dev->lock, flags); } static irqreturn_t actmon_thread_isr(int irq, void *data) @@ -345,131 +383,110 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb, unsigned long action, void *ptr) { struct clk_notifier_data *data = ptr; - struct tegra_devfreq *tegra = container_of(nb, struct tegra_devfreq, - rate_change_nb); + struct tegra_devfreq *tegra; + struct tegra_devfreq_device *dev; unsigned int i; unsigned long flags; - spin_lock_irqsave(&tegra->lock, flags); + if (action != POST_RATE_CHANGE) + return NOTIFY_OK; - switch (action) { - case POST_RATE_CHANGE: - tegra->cur_freq = data->new_rate / KHZ; + tegra = container_of(nb, struct tegra_devfreq, rate_change_nb); - for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) - tegra_devfreq_update_wmark(tegra, tegra->devices + i); + tegra->cur_freq = data->new_rate / KHZ; - actmon_write_barrier(tegra); - break; - case PRE_RATE_CHANGE: - /* fall through */ - case ABORT_RATE_CHANGE: - break; - }; + for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { + dev = &tegra->devices[i]; + + spin_lock_irqsave(&dev->lock, flags); + tegra_devfreq_update_wmark(tegra, dev); + spin_unlock_irqrestore(&dev->lock, flags); + } - spin_unlock_irqrestore(&tegra->lock, flags); + actmon_write_barrier(tegra); return NOTIFY_OK; } -static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, - struct tegra_devfreq_device *dev) +static void tegra_actmon_enable_interrupts(struct tegra_devfreq *tegra) { + struct tegra_devfreq_device *dev; u32 val; + unsigned int i; - dev->avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ; - dev->target_freq = tegra->cur_freq; - - dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; - writel(dev->avg_count, dev->regs + ACTMON_DEV_INIT_AVG); - - tegra_devfreq_update_avg_wmark(dev); - tegra_devfreq_update_wmark(tegra, dev); - - writel(ACTMON_COUNT_WEIGHT, dev->regs + ACTMON_DEV_COUNT_WEIGHT); - writel(ACTMON_INTR_STATUS_CLEAR, dev->regs + ACTMON_DEV_INTR_STATUS); - - val = 0; - val |= ACTMON_DEV_CTRL_ENB_PERIODIC | - ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN | - ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN; - val |= (ACTMON_AVERAGE_WINDOW_LOG2 - 1) - << ACTMON_DEV_CTRL_K_VAL_SHIFT; - val |= (ACTMON_BELOW_WMARK_WINDOW - 1) - << ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT; - val |= (ACTMON_ABOVE_WMARK_WINDOW - 1) - << ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT; - val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN | - ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; - - writel(val, dev->regs + ACTMON_DEV_CTRL); + for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { + dev = &tegra->devices[i]; - actmon_write_barrier(tegra); + val = device_readl(dev, ACTMON_DEV_CTRL); + val |= ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN; + val |= ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN; + val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + val |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; - val = readl(dev->regs + ACTMON_DEV_CTRL); - val |= ACTMON_DEV_CTRL_ENB; - writel(val, dev->regs + ACTMON_DEV_CTRL); + device_writel(dev, val, ACTMON_DEV_CTRL); + } actmon_write_barrier(tegra); } -static int tegra_devfreq_suspend(struct device *dev) +static void tegra_actmon_disable_interrupts(struct tegra_devfreq *tegra) { - struct platform_device *pdev; - struct tegra_devfreq *tegra; - struct tegra_devfreq_device *actmon_dev; - unsigned int i; + struct tegra_devfreq_device *dev; u32 val; - - pdev = container_of(dev, struct platform_device, dev); - tegra = platform_get_drvdata(pdev); + unsigned int i; for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { - actmon_dev = &tegra->devices[i]; - - val = readl(actmon_dev->regs + ACTMON_DEV_CTRL); - val &= ~ACTMON_DEV_CTRL_ENB; - writel(val, actmon_dev->regs + ACTMON_DEV_CTRL); + dev = &tegra->devices[i]; - writel(ACTMON_INTR_STATUS_CLEAR, - actmon_dev->regs + ACTMON_DEV_INTR_STATUS); + val = device_readl(dev, ACTMON_DEV_CTRL); + val &= ~ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN; + val &= ~ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN; + val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; - actmon_write_barrier(tegra); + device_writel(dev, val, ACTMON_DEV_CTRL); } - return 0; + actmon_write_barrier(tegra); } -static int tegra_devfreq_resume(struct device *dev) +static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, + struct tegra_devfreq_device *dev) { - struct platform_device *pdev; - struct tegra_devfreq *tegra; - struct tegra_devfreq_device *actmon_dev; - unsigned int i; + u32 val = 0; - pdev = container_of(dev, struct platform_device, dev); - tegra = platform_get_drvdata(pdev); + dev->target_freq = tegra->cur_freq; - for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { - actmon_dev = &tegra->devices[i]; + dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; + device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG); - tegra_actmon_configure_device(tegra, actmon_dev); - } + tegra_devfreq_update_avg_wmark(tegra, dev); + tegra_devfreq_update_wmark(tegra, dev); - return 0; + device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT); + device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS); + + val |= ACTMON_DEV_CTRL_ENB_PERIODIC; + val |= (ACTMON_AVERAGE_WINDOW_LOG2 - 1) + << ACTMON_DEV_CTRL_K_VAL_SHIFT; + val |= (ACTMON_BELOW_WMARK_WINDOW - 1) + << ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT; + val |= (ACTMON_ABOVE_WMARK_WINDOW - 1) + << ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT; + val |= ACTMON_DEV_CTRL_ENB; + + device_writel(dev, val, ACTMON_DEV_CTRL); + + actmon_write_barrier(tegra); } static int tegra_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) { - struct platform_device *pdev; - struct tegra_devfreq *tegra; + struct tegra_devfreq *tegra = dev_get_drvdata(dev); struct dev_pm_opp *opp; unsigned long rate = *freq * KHZ; - pdev = container_of(dev, struct platform_device, dev); - tegra = platform_get_drvdata(pdev); - rcu_read_lock(); opp = devfreq_recommended_opp(dev, &rate, flags); if (IS_ERR(opp)) { @@ -491,13 +508,9 @@ static int tegra_devfreq_target(struct device *dev, unsigned long *freq, static int tegra_devfreq_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { - struct platform_device *pdev; - struct tegra_devfreq *tegra; + struct tegra_devfreq *tegra = dev_get_drvdata(dev); struct tegra_devfreq_device *actmon_dev; - pdev = container_of(dev, struct platform_device, dev); - tegra = platform_get_drvdata(pdev); - stat->current_frequency = tegra->cur_freq; /* To be used by the tegra governor */ @@ -508,7 +521,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev, actmon_dev = &tegra->devices[MCALL]; /* Number of cycles spent on memory access */ - stat->busy_time = actmon_dev->avg_count; + stat->busy_time = device_readl(actmon_dev, ACTMON_DEV_AVG_COUNT); /* The bus can be considered to be saturated way before 100% */ stat->busy_time *= 100 / BUS_SATURATION_RATIO; @@ -516,11 +529,19 @@ static int tegra_devfreq_get_dev_status(struct device *dev, /* Number of cycles in a sampling period */ stat->total_time = ACTMON_SAMPLING_PERIOD * tegra->cur_freq; + stat->busy_time = min(stat->busy_time, stat->total_time); + return 0; } -static int tegra_devfreq_get_target(struct devfreq *devfreq, - unsigned long *freq) +static struct devfreq_dev_profile tegra_devfreq_profile = { + .polling_ms = 0, + .target = tegra_devfreq_target, + .get_dev_status = tegra_devfreq_get_dev_status, +}; + +static int tegra_governor_get_target(struct devfreq *devfreq, + unsigned long *freq) { struct devfreq_dev_status stat; struct tegra_devfreq *tegra; @@ -548,23 +569,50 @@ static int tegra_devfreq_get_target(struct devfreq *devfreq, return 0; } -static int tegra_devfreq_event_handler(struct devfreq *devfreq, - unsigned int event, void *data) +static int tegra_governor_event_handler(struct devfreq *devfreq, + unsigned int event, void *data) { - return 0; + struct tegra_devfreq *tegra; + int ret = 0; + + tegra = dev_get_drvdata(devfreq->dev.parent); + + switch (event) { + case DEVFREQ_GOV_START: + tegra_actmon_enable_interrupts(tegra); + devfreq_monitor_start(devfreq); + break; + + case DEVFREQ_GOV_STOP: + tegra_actmon_disable_interrupts(tegra); + devfreq_monitor_stop(devfreq); + break; + + case DEVFREQ_GOV_SUSPEND: + tegra_actmon_disable_interrupts(tegra); + devfreq_monitor_suspend(devfreq); + break; + + case DEVFREQ_GOV_RESUME: + tegra_actmon_enable_interrupts(tegra); + devfreq_monitor_resume(devfreq); + break; + } + + return ret; } static struct devfreq_governor tegra_devfreq_governor = { - .name = "tegra", - .get_target_freq = tegra_devfreq_get_target, - .event_handler = tegra_devfreq_event_handler, + .name = "tegra_actmon", + .get_target_freq = tegra_governor_get_target, + .event_handler = tegra_governor_event_handler, }; -static struct devfreq_dev_profile tegra_devfreq_profile = { - .polling_ms = 0, - .target = tegra_devfreq_target, - .get_dev_status = tegra_devfreq_get_dev_status, -}; +static int __init tegra_governor_init(void) +{ + return devfreq_add_governor(&tegra_devfreq_governor); +} +subsys_initcall(tegra_governor_init); static int tegra_devfreq_probe(struct platform_device *pdev) { @@ -580,19 +628,11 @@ static int tegra_devfreq_probe(struct platform_device *pdev) if (!tegra) return -ENOMEM; - spin_lock_init(&tegra->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Failed to get regs resource\n"); - return -ENODEV; - } tegra->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(tegra->regs)) { - dev_err(&pdev->dev, "Failed to get IO memory\n"); + if (IS_ERR(tegra->regs)) return PTR_ERR(tegra->regs); - } tegra->reset = devm_reset_control_get(&pdev->dev, "actmon"); if (IS_ERR(tegra->reset)) { @@ -630,7 +670,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev) err = clk_prepare_enable(tegra->clock); if (err) { - reset_control_deassert(tegra->reset); + dev_err(&pdev->dev, + "Failed to prepare and enable ACTMON clock\n"); return err; } @@ -643,30 +684,24 @@ static int tegra_devfreq_probe(struct platform_device *pdev) tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ; - writel(ACTMON_SAMPLING_PERIOD - 1, - tegra->regs + ACTMON_GLB_PERIOD_CTRL); + actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1, + ACTMON_GLB_PERIOD_CTRL); for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) { dev = tegra->devices + i; dev->config = actmon_device_configs + i; dev->regs = tegra->regs + dev->config->offset; + spin_lock_init(&dev->lock); - tegra_actmon_configure_device(tegra, tegra->devices + i); + tegra_actmon_configure_device(tegra, dev); } - err = devfreq_add_governor(&tegra_devfreq_governor); - if (err) { - dev_err(&pdev->dev, "Failed to add governor\n"); - return err; + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, "Failed to get IRQ\n"); + return -ENODEV; } - tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock); - tegra->devfreq = devm_devfreq_add_device(&pdev->dev, - &tegra_devfreq_profile, - "tegra", - NULL); - - irq = platform_get_irq(pdev, 0); err = devm_request_threaded_irq(&pdev->dev, irq, actmon_isr, actmon_thread_isr, IRQF_SHARED, "tegra-devfreq", tegra); @@ -677,12 +712,31 @@ static int tegra_devfreq_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tegra); + tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock); + tegra->devfreq = devm_devfreq_add_device(&pdev->dev, + &tegra_devfreq_profile, + "tegra_actmon", + NULL); + return 0; } static int tegra_devfreq_remove(struct platform_device *pdev) { struct tegra_devfreq *tegra = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + u32 val; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) { + val = device_readl(&tegra->devices[i], ACTMON_DEV_CTRL); + val &= ~ACTMON_DEV_CTRL_ENB; + device_writel(&tegra->devices[i], val, ACTMON_DEV_CTRL); + } + + actmon_write_barrier(tegra); + + devm_free_irq(&pdev->dev, irq, tegra); clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb); @@ -691,28 +745,23 @@ static int tegra_devfreq_remove(struct platform_device *pdev) return 0; } -static SIMPLE_DEV_PM_OPS(tegra_devfreq_pm_ops, - tegra_devfreq_suspend, - tegra_devfreq_resume); - -static struct of_device_id tegra_devfreq_of_match[] = { +static const struct of_device_id tegra_devfreq_of_match[] = { { .compatible = "nvidia,tegra124-actmon" }, { }, }; +MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match); + static struct platform_driver tegra_devfreq_driver = { .probe = tegra_devfreq_probe, .remove = tegra_devfreq_remove, .driver = { - .name = "tegra-devfreq", - .owner = THIS_MODULE, + .name = "tegra-devfreq", .of_match_table = tegra_devfreq_of_match, - .pm = &tegra_devfreq_pm_ops, }, }; module_platform_driver(tegra_devfreq_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Tegra devfreq driver"); MODULE_AUTHOR("Tomeu Vizoso "); -MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match); -- cgit From c70eea739f5e765b5d6aadf9621cbdeb024dfbd6 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Tue, 17 Mar 2015 10:36:14 +0100 Subject: PM / devfreq: tegra: Use clock rate constraints Now that we have per-user clocks and the possibility to set constraints in a clock, set a floor constraint on the EMC clock. Signed-off-by: Tomeu Vizoso Acked-by: MyungJoo Ham --- drivers/devfreq/tegra-devfreq.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c index d2f4d5ecaefd..ea6158bb76fa 100644 --- a/drivers/devfreq/tegra-devfreq.c +++ b/drivers/devfreq/tegra-devfreq.c @@ -497,10 +497,8 @@ static int tegra_devfreq_target(struct device *dev, unsigned long *freq, rate = dev_pm_opp_get_freq(opp); rcu_read_unlock(); - /* TODO: Once we have per-user clk constraints, set a floor */ - clk_set_rate(tegra->emc_clock, rate); - - /* TODO: Set voltage as well */ + clk_set_min_rate(tegra->emc_clock, rate); + clk_set_rate(tegra->emc_clock, 0); return 0; } @@ -619,7 +617,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev) struct tegra_devfreq *tegra; struct tegra_devfreq_device *dev; struct resource *res; - unsigned long max_freq; unsigned int i; int irq; int err; @@ -658,6 +655,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev) return err; } + clk_set_rate(tegra->emc_clock, ULONG_MAX); + tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb; err = clk_notifier_register(tegra->emc_clock, &tegra->rate_change_nb); if (err) { @@ -677,11 +676,7 @@ static int tegra_devfreq_probe(struct platform_device *pdev) reset_control_deassert(tegra->reset); - max_freq = clk_round_rate(tegra->emc_clock, ULONG_MAX); - tegra->max_freq = max_freq / KHZ; - - clk_set_rate(tegra->emc_clock, max_freq); - + tegra->max_freq = clk_round_rate(tegra->emc_clock, ULONG_MAX) / KHZ; tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ; actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1, -- cgit From 5d498b463a3cdef51e6d4cc7066ac89c360e7a98 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Tue, 17 Mar 2015 10:36:15 +0100 Subject: PM / devfreq: tegra: remove operating-points As the DT bindings don't have an operating-points property any more, build the OPP table from the frequencies supported by the EMC clock. Signed-off-by: Tomeu Vizoso Acked-by: MyungJoo Ham --- drivers/devfreq/tegra-devfreq.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c index ea6158bb76fa..5ba35cc2ea04 100644 --- a/drivers/devfreq/tegra-devfreq.c +++ b/drivers/devfreq/tegra-devfreq.c @@ -618,6 +618,7 @@ static int tegra_devfreq_probe(struct platform_device *pdev) struct tegra_devfreq_device *dev; struct resource *res; unsigned int i; + unsigned long rate; int irq; int err; @@ -649,12 +650,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev) return PTR_ERR(tegra->emc_clock); } - err = of_init_opp_table(&pdev->dev); - if (err) { - dev_err(&pdev->dev, "Failed to init operating point table\n"); - return err; - } - clk_set_rate(tegra->emc_clock, ULONG_MAX); tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb; @@ -691,6 +686,11 @@ static int tegra_devfreq_probe(struct platform_device *pdev) tegra_actmon_configure_device(tegra, dev); } + for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) { + rate = clk_round_rate(tegra->emc_clock, rate); + dev_pm_opp_add(&pdev->dev, rate, 0); + } + irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "Failed to get IRQ\n"); -- cgit From 2da19b1a4d4fadf8d84a5cb6a953749a559a29fb Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Tue, 17 Mar 2015 10:36:16 +0100 Subject: PM / devfreq: tegra: Set drvdata before enabling the irq To avoid a race in which the interrupt will be handled before the drvdata has been set up. Signed-off-by: Tomeu Vizoso Acked-by: MyungJoo Ham --- drivers/devfreq/tegra-devfreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c index 5ba35cc2ea04..7fa1bd4c431b 100644 --- a/drivers/devfreq/tegra-devfreq.c +++ b/drivers/devfreq/tegra-devfreq.c @@ -697,6 +697,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev) return -ENODEV; } + platform_set_drvdata(pdev, tegra); + err = devm_request_threaded_irq(&pdev->dev, irq, actmon_isr, actmon_thread_isr, IRQF_SHARED, "tegra-devfreq", tegra); @@ -705,8 +707,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev) return err; } - platform_set_drvdata(pdev, tegra); - tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock); tegra->devfreq = devm_devfreq_add_device(&pdev->dev, &tegra_devfreq_profile, -- cgit From 34ed504008b2d8d3578191424fc5cddcb76d69f5 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Tue, 17 Mar 2015 10:36:17 +0100 Subject: PM / devfreq: tegra: Enable interrupts after resuming the devfreq monitor Signed-off-by: Tomeu Vizoso Acked-by: MyungJoo Ham --- drivers/devfreq/tegra-devfreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c index 7fa1bd4c431b..8e633a6d775d 100644 --- a/drivers/devfreq/tegra-devfreq.c +++ b/drivers/devfreq/tegra-devfreq.c @@ -577,8 +577,8 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, switch (event) { case DEVFREQ_GOV_START: - tegra_actmon_enable_interrupts(tegra); devfreq_monitor_start(devfreq); + tegra_actmon_enable_interrupts(tegra); break; case DEVFREQ_GOV_STOP: @@ -592,8 +592,8 @@ static int tegra_governor_event_handler(struct devfreq *devfreq, break; case DEVFREQ_GOV_RESUME: - tegra_actmon_enable_interrupts(tegra); devfreq_monitor_resume(devfreq); + tegra_actmon_enable_interrupts(tegra); break; } -- cgit From 358b615f5812e28dd763d2aa1297563927a2f89d Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Mon, 30 Mar 2015 17:33:23 +0200 Subject: PM / devfreq: tegra: Register governor on module init So this driver builds as a module. Signed-off-by: Tomeu Vizoso Signed-off-by: MyungJoo Ham --- drivers/devfreq/tegra-devfreq.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c index 8e633a6d775d..13a1a6e8108c 100644 --- a/drivers/devfreq/tegra-devfreq.c +++ b/drivers/devfreq/tegra-devfreq.c @@ -606,12 +606,6 @@ static struct devfreq_governor tegra_devfreq_governor = { .event_handler = tegra_governor_event_handler, }; -static int __init tegra_governor_init(void) -{ - return devfreq_add_governor(&tegra_devfreq_governor); -} -subsys_initcall(tegra_governor_init); - static int tegra_devfreq_probe(struct platform_device *pdev) { struct tegra_devfreq *tegra; @@ -755,7 +749,36 @@ static struct platform_driver tegra_devfreq_driver = { .of_match_table = tegra_devfreq_of_match, }, }; -module_platform_driver(tegra_devfreq_driver); + +static int __init tegra_devfreq_init(void) +{ + int ret = 0; + + ret = devfreq_add_governor(&tegra_devfreq_governor); + if (ret) { + pr_err("%s: failed to add governor: %d\n", __func__, ret); + return ret; + } + + ret = platform_driver_register(&tegra_devfreq_driver); + if (ret) + devfreq_remove_governor(&tegra_devfreq_governor); + + return ret; +} +module_init(tegra_devfreq_init) + +static void __exit tegra_devfreq_exit(void) +{ + int ret = 0; + + platform_driver_unregister(&tegra_devfreq_driver); + + ret = devfreq_remove_governor(&tegra_devfreq_governor); + if (ret) + pr_err("%s: failed to remove governor: %d\n", __func__, ret); +} +module_exit(tegra_devfreq_exit) MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Tegra devfreq driver"); -- cgit From d7ef76717322c8e2df7d4360b33faa9466cb1a0d Mon Sep 17 00:00:00 2001 From: Len Brown Date: Tue, 24 Mar 2015 23:23:20 -0400 Subject: intel_idle: Update support for Silvermont Core in Baytrail SOC On some Silvermont-Core/Baytrail-SOC systems, C1E latency is higher than original specifications. Although C1E is still enumerated in CPUID.MWAIT.EDX, we delete the state from intel_idle to avoid latency impact. Under some conditions, the latency of the C6N-BYT and C6S-BYT states may exceed the specified values of 40 and 140 usec, respectively. Increase those values to 300 and 500 usec; to assure that the hardware does not violate constraints that may be set by the Linux PM_QOS sub-system. Also increase the C7-BYT target residency to 4.0 ms from 1.5 ms. Signed-off-by: Len Brown Cc: Kumar P Mahesh Cc: Alan Cox Cc: Mika Westerberg Cc: --- drivers/idle/intel_idle.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index b0e58522780d..44d1d7920202 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -217,19 +217,11 @@ static struct cpuidle_state byt_cstates[] = { .target_residency = 1, .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, - { - .name = "C1E-BYT", - .desc = "MWAIT 0x01", - .flags = MWAIT2flg(0x01), - .exit_latency = 15, - .target_residency = 30, - .enter = &intel_idle, - .enter_freeze = intel_idle_freeze, }, { .name = "C6N-BYT", .desc = "MWAIT 0x58", .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TLB_FLUSHED, - .exit_latency = 40, + .exit_latency = 300, .target_residency = 275, .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, @@ -237,7 +229,7 @@ static struct cpuidle_state byt_cstates[] = { .name = "C6S-BYT", .desc = "MWAIT 0x52", .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, - .exit_latency = 140, + .exit_latency = 500, .target_residency = 560, .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, @@ -246,7 +238,7 @@ static struct cpuidle_state byt_cstates[] = { .desc = "MWAIT 0x60", .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, .exit_latency = 1200, - .target_residency = 1500, + .target_residency = 4000, .enter = &intel_idle, .enter_freeze = intel_idle_freeze, }, { -- cgit From cab07a5652d1d124b505c2b7ed21c6823295c5d7 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 27 Mar 2015 20:54:01 -0400 Subject: intel_idle: Add support for the Airmont Core in the Cherrytrail and Braswell SOCs Support C-states for the Airmont core in the Cherrytrail and Braswell SOCs. The states are similar to those of Silvermont in Baytrail, except both flavors of C6 states are faster. Signed-off-by: Len Brown Cc: Kumar P Mahesh Cc: Alan Cox Cc: Mika Westerberg --- drivers/idle/intel_idle.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 44d1d7920202..2ec8618c376f 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -253,6 +253,51 @@ static struct cpuidle_state byt_cstates[] = { .enter = NULL } }; +static struct cpuidle_state cht_cstates[] = { + { + .name = "C1-CHT", + .desc = "MWAIT 0x00", + .flags = MWAIT2flg(0x00), + .exit_latency = 1, + .target_residency = 1, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C6N-CHT", + .desc = "MWAIT 0x58", + .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 80, + .target_residency = 275, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C6S-CHT", + .desc = "MWAIT 0x52", + .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 200, + .target_residency = 560, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C7-CHT", + .desc = "MWAIT 0x60", + .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 1200, + .target_residency = 4000, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .name = "C7S-CHT", + .desc = "MWAIT 0x64", + .flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 10000, + .target_residency = 20000, + .enter = &intel_idle, + .enter_freeze = intel_idle_freeze, }, + { + .enter = NULL } +}; + static struct cpuidle_state ivb_cstates[] = { { .name = "C1-IVB", @@ -740,6 +785,12 @@ static const struct idle_cpu idle_cpu_byt = { .byt_auto_demotion_disable_flag = true, }; +static const struct idle_cpu idle_cpu_cht = { + .state_table = cht_cstates, + .disable_promotion_to_c1e = true, + .byt_auto_demotion_disable_flag = true, +}; + static const struct idle_cpu idle_cpu_ivb = { .state_table = ivb_cstates, .disable_promotion_to_c1e = true, @@ -782,6 +833,7 @@ static const struct x86_cpu_id intel_idle_ids[] = { ICPU(0x2d, idle_cpu_snb), ICPU(0x36, idle_cpu_atom), ICPU(0x37, idle_cpu_byt), + ICPU(0x4c, idle_cpu_cht), ICPU(0x3a, idle_cpu_ivb), ICPU(0x3e, idle_cpu_ivt), ICPU(0x3c, idle_cpu_hsw), -- cgit From 09a972d1620934142d30cfda455ffe429af751c4 Mon Sep 17 00:00:00 2001 From: Shilpasri G Bhat Date: Wed, 1 Apr 2015 15:16:34 +0530 Subject: cpufreq: powernv: Report cpu frequency throttling The power and thermal safety of the system is taken care by an On-Chip-Controller (OCC) which is real-time subsystem embedded within the POWER8 processor. OCC continuously monitors the memory and core temperature, the total system power, state of power supply and fan. The cpu frequency can be throttled by OCC for the following reasons: 1)If a processor crosses its power and temperature limit then OCC will lower its Pmax to reduce the frequency and voltage. 2)If OCC crashes then the system is forced to Psafe frequency. 3)If OCC fails to recover then the kernel is not allowed to do any further frequency changes and the chip will remain in Psafe. The user can see a drop in performance when frequency is throttled and is unaware of throttling. So detect and report such a condition, so the user can check the OCC status to reboot the system or check for power supply or fan failures. The current status of the core is read from Power Management Status Register(PMSR) to check if any of the throttling condition is occurred and the appropriate throttling message is reported. Signed-off-by: Shilpasri G Bhat Reviewed-by: Preeti U Murthy Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/powernv-cpufreq.c | 47 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c index 2dfd4fdb5a52..ebef0d8279c7 100644 --- a/drivers/cpufreq/powernv-cpufreq.c +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -34,9 +34,13 @@ #include /* Required for cpu_sibling_mask() in UP configs */ #define POWERNV_MAX_PSTATES 256 +#define PMSR_PSAFE_ENABLE (1UL << 30) +#define PMSR_SPR_EM_DISABLE (1UL << 31) +#define PMSR_MAX(x) ((x >> 32) & 0xFF) +#define PMSR_LP(x) ((x >> 48) & 0xFF) static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; -static bool rebooting; +static bool rebooting, throttled; /* * Note: The set of pstates consists of contiguous integers, the @@ -294,6 +298,44 @@ static inline unsigned int get_nominal_index(void) return powernv_pstate_info.max - powernv_pstate_info.nominal; } +static void powernv_cpufreq_throttle_check(unsigned int cpu) +{ + unsigned long pmsr; + int pmsr_pmax, pmsr_lp; + + pmsr = get_pmspr(SPRN_PMSR); + + /* Check for Pmax Capping */ + pmsr_pmax = (s8)PMSR_MAX(pmsr); + if (pmsr_pmax != powernv_pstate_info.max) { + throttled = true; + pr_info("CPU %d Pmax is reduced to %d\n", cpu, pmsr_pmax); + pr_info("Max allowed Pstate is capped\n"); + } + + /* + * Check for Psafe by reading LocalPstate + * or check if Psafe_mode_active is set in PMSR. + */ + pmsr_lp = (s8)PMSR_LP(pmsr); + if ((pmsr_lp < powernv_pstate_info.min) || + (pmsr & PMSR_PSAFE_ENABLE)) { + throttled = true; + pr_info("Pstate set to safe frequency\n"); + } + + /* Check if SPR_EM_DISABLE is set in PMSR */ + if (pmsr & PMSR_SPR_EM_DISABLE) { + throttled = true; + pr_info("Frequency Control disabled from OS\n"); + } + + if (throttled) { + pr_info("PMSR = %16lx\n", pmsr); + pr_crit("CPU Frequency could be throttled\n"); + } +} + /* * powernv_cpufreq_target_index: Sets the frequency corresponding to * the cpufreq table entry indexed by new_index on the cpus in the @@ -307,6 +349,9 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, if (unlikely(rebooting) && new_index != get_nominal_index()) return 0; + if (!throttled) + powernv_cpufreq_throttle_check(smp_processor_id()); + freq_data.pstate_id = powernv_freqs[new_index].driver_data; /* -- cgit From 1c832b3e85b6ac35e5f113fa4204eb725c54794e Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Wed, 1 Apr 2015 09:47:18 +0800 Subject: ACPI / EC: Call acpi_walk_dep_device_list() after installing EC opregion handler On some machines(E,G Mircosoft surface 3), ACPI battery depends on the EC operation region and it has _DEP method which contains EC. Current code doesn't support such devices whose dep_unmet will be not be decreased after EC opregion handler being installed. This blocks battery device to be attached with its driver. This patch is to fix the issue. Link: https://bugzilla.kernel.org/show_bug.cgi?id=90161 Reported-and-tested-by: Lompik Tested-by: Valentin Lab Signed-off-by: Lan Tianyu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a362f20c6c8b..220d6406c9e9 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1147,6 +1147,9 @@ static int acpi_ec_add(struct acpi_device *device) ret = ec_install_handlers(ec); + /* Reprobe devices depending on the EC */ + acpi_walk_dep_device_list(ec->handle); + /* EC is fully operational, allow queries */ clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); -- cgit From 5acb972fed8bcfe6c38a4bdef4e74148eaad04b2 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 30 Mar 2015 13:26:37 +0800 Subject: cpufreq: hisilicon: add acpu driver Add acpu driver for hisilicon SoC, acpu is application processor subsystem. Currently the acpu has the coupled clock domain for two clusters, so this driver will directly use cpufreq-dt driver as backend. Signed-off-by: Leo Yan Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig.arm | 9 ++++++++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/hisi-acpu-cpufreq.c | 42 +++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 drivers/cpufreq/hisi-acpu-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 1b06fc4640e2..4f3dbc8cf729 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -108,6 +108,15 @@ config ARM_HIGHBANK_CPUFREQ If in doubt, say N. +config ARM_HISI_ACPU_CPUFREQ + tristate "Hisilicon ACPU CPUfreq driver" + depends on ARCH_HISI && CPUFREQ_DT + select PM_OPP + help + This enables the hisilicon ACPU CPUfreq driver. + + If in doubt, say N. + config ARM_IMX6Q_CPUFREQ tristate "Freescale i.MX6 cpufreq support" depends on ARCH_MXC diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 26df0ad00e32..cdce92ae2e8b 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -59,6 +59,7 @@ arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o +obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o diff --git a/drivers/cpufreq/hisi-acpu-cpufreq.c b/drivers/cpufreq/hisi-acpu-cpufreq.c new file mode 100644 index 000000000000..026d5b2224de --- /dev/null +++ b/drivers/cpufreq/hisi-acpu-cpufreq.c @@ -0,0 +1,42 @@ +/* + * Hisilicon Platforms Using ACPU CPUFreq Support + * + * Copyright (c) 2015 Hisilicon Limited. + * Copyright (c) 2015 Linaro Limited. + * + * Leo Yan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +static int __init hisi_acpu_cpufreq_driver_init(void) +{ + struct platform_device *pdev; + + if (!of_machine_is_compatible("hisilicon,hi6220")) + return -ENODEV; + + pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); + return PTR_ERR_OR_ZERO(pdev); +} +module_init(hisi_acpu_cpufreq_driver_init); + +MODULE_AUTHOR("Leo Yan "); +MODULE_DESCRIPTION("Hisilicon acpu cpufreq driver"); +MODULE_LICENSE("GPL v2"); -- cgit From f321c9cbf3bbf86f6e6153419eaf93ad085e3d74 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Apr 2015 15:25:04 +0200 Subject: PM / watchdog: iTCO: stop watchdog during system suspend If the target sleep state of the system is not an ACPI sleep state (S1, S2 or S3), the TCO watchdog needs to be stopped during system suspend, because it may not be possible to ping it any more after timekeeping has been suspended (suspend-to-idle does that for one example). For this reason, provide ->suspend_noirq and ->resume_noirq callbacks for the iTCO watchdog driver and use them to stop and restart the watchdog during system suspend and resume, respectively, if the system is not going to enter an ACPI sleep state (in which case the watchdog will be stopped by the platform firmware before the state is entered). Reported-and-tested-by: Borun Fu Signed-off-by: Rafael J. Wysocki Reviewed-by: Guenter Roeck --- drivers/watchdog/iTCO_wdt.c | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 05ee0bf88ce9..3c3fd417ddeb 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -51,6 +51,7 @@ #define DRV_VERSION "1.11" /* Includes */ +#include /* For ACPI support */ #include /* For module specific items */ #include /* For new moduleparam's */ #include /* For standard types (like size_t) */ @@ -103,6 +104,8 @@ static struct { /* this is private data for the iTCO_wdt device */ struct platform_device *dev; /* the PCI-device */ struct pci_dev *pdev; + /* whether or not the watchdog has been suspended */ + bool suspended; } iTCO_wdt_private; /* module parameters */ @@ -571,12 +574,60 @@ static void iTCO_wdt_shutdown(struct platform_device *dev) iTCO_wdt_stop(NULL); } +#ifdef CONFIG_PM_SLEEP +/* + * Suspend-to-idle requires this, because it stops the ticks and timekeeping, so + * the watchdog cannot be pinged while in that state. In ACPI sleep states the + * watchdog is stopped by the platform firmware. + */ + +#ifdef CONFIG_ACPI +static inline bool need_suspend(void) +{ + return acpi_target_system_state() == ACPI_STATE_S0; +} +#else +static inline bool need_suspend(void) { return true; } +#endif + +static int iTCO_wdt_suspend_noirq(struct device *dev) +{ + int ret = 0; + + iTCO_wdt_private.suspended = false; + if (watchdog_active(&iTCO_wdt_watchdog_dev) && need_suspend()) { + ret = iTCO_wdt_stop(&iTCO_wdt_watchdog_dev); + if (!ret) + iTCO_wdt_private.suspended = true; + } + return ret; +} + +static int iTCO_wdt_resume_noirq(struct device *dev) +{ + if (iTCO_wdt_private.suspended) + iTCO_wdt_start(&iTCO_wdt_watchdog_dev); + + return 0; +} + +static struct dev_pm_ops iTCO_wdt_pm = { + .suspend_noirq = iTCO_wdt_suspend_noirq, + .resume_noirq = iTCO_wdt_resume_noirq, +}; + +#define ITCO_WDT_PM_OPS (&iTCO_wdt_pm) +#else +#define ITCO_WDT_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + static struct platform_driver iTCO_wdt_driver = { .probe = iTCO_wdt_probe, .remove = iTCO_wdt_remove, .shutdown = iTCO_wdt_shutdown, .driver = { .name = DRV_NAME, + .pm = ITCO_WDT_PM_OPS, }, }; -- cgit From 97badf873ab60e841243b66133ff9eff2a46ef29 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Apr 2015 23:23:37 +0200 Subject: device property: Make it possible to use secondary firmware nodes Add a secondary pointer to struct fwnode_handle so as to make it possible for a device to have two firmware nodes associated with it at the same time, for example, an ACPI node and a node with a set of properties provided by platform initialization code. In the future that will allow device property lookup to fall back from the primary firmware node to the secondary one if the given property is not present there to make it easier to provide defaults for device properties used by device drivers. Introduce two helper routines, set_primary_fwnode() and set_secondary_fwnode() allowing callers to add a primary/secondary firmware node to the given device in such a way that (1) If there's only one firmware node for that device, it will be pointed to by the device's firmware node pointer. (2) If both the primary and secondary firmware nodes are present, the primary one will be pointed to by the device's firmware node pointer, while the secondary one will be pointed to by the primary node's secondary pointer. (3) If one of these nodes is removed (by calling one of the new nelpers with NULL as the second argument), the other one will be preserved. Make ACPI use set_primary_fwnode() for attaching its firmware nodes to devices. Signed-off-by: Rafael J. Wysocki Tested-by: Heikki Krogerus Acked-by: Greg Kroah-Hartman --- drivers/base/core.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 4 ++-- include/linux/device.h | 3 +++ include/linux/fwnode.h | 1 + 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 07304a3b9ee2..c7e2a9a70865 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -2133,3 +2134,53 @@ define_dev_printk_level(dev_notice, KERN_NOTICE); define_dev_printk_level(_dev_info, KERN_INFO); #endif + +static inline bool fwnode_is_primary(struct fwnode_handle *fwnode) +{ + return fwnode && !IS_ERR(fwnode->secondary); +} + +/** + * set_primary_fwnode - Change the primary firmware node of a given device. + * @dev: Device to handle. + * @fwnode: New primary firmware node of the device. + * + * Set the device's firmware node pointer to @fwnode, but if a secondary + * firmware node of the device is present, preserve it. + */ +void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) +{ + if (fwnode) { + struct fwnode_handle *fn = dev->fwnode; + + if (fwnode_is_primary(fn)) + fn = fn->secondary; + + fwnode->secondary = fn; + dev->fwnode = fwnode; + } else { + dev->fwnode = fwnode_is_primary(dev->fwnode) ? + dev->fwnode->secondary : NULL; + } +} +EXPORT_SYMBOL_GPL(set_primary_fwnode); + +/** + * set_secondary_fwnode - Change the secondary firmware node of a given device. + * @dev: Device to handle. + * @fwnode: New secondary firmware node of the device. + * + * If a primary firmware node of the device is present, set its secondary + * pointer to @fwnode. Otherwise, set the device's firmware node pointer to + * @fwnode. + */ +void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode) +{ + if (fwnode) + fwnode->secondary = ERR_PTR(-ENODEV); + + if (fwnode_is_primary(dev->fwnode)) + dev->fwnode->secondary = fwnode; + else + dev->fwnode = fwnode; +} diff --git a/include/linux/acpi.h b/include/linux/acpi.h index ec488d03b518..dd12127f171c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -54,8 +54,8 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) } #define ACPI_COMPANION(dev) acpi_node((dev)->fwnode) -#define ACPI_COMPANION_SET(dev, adev) (dev)->fwnode = (adev) ? \ - acpi_fwnode_handle(adev) : NULL +#define ACPI_COMPANION_SET(dev, adev) set_primary_fwnode(dev, (adev) ? \ + acpi_fwnode_handle(adev) : NULL) #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) static inline bool has_acpi_companion(struct device *dev) diff --git a/include/linux/device.h b/include/linux/device.h index badef20b876a..324d02add7b4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -940,6 +940,9 @@ extern void unlock_device_hotplug(void); extern int lock_device_hotplug_sysfs(void); extern int device_offline(struct device *dev); extern int device_online(struct device *dev); +extern void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode); +extern void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode); + /* * Root device objects for grouping under /sys/devices */ diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 17bb5f039509..fc30b84b532a 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -20,6 +20,7 @@ enum fwnode_type { struct fwnode_handle { enum fwnode_type type; + struct fwnode_handle *secondary; }; #endif -- cgit From 16ba08d5c9ec44f89ec03c67ecf7a9c5e2d204fd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Apr 2015 16:05:11 +0200 Subject: device property: Introduce firmware node type for platform data Introduce data structures and code allowing "built-in" properties to be associated with devices in such a way that they will be used by the device_property_* API if no proper firmware node (neither DT nor ACPI) is present for the given device. Each property is to be represented by a property_entry structure. An array of property_entry structures (terminated with a null entry) can be pointed to by the properties field of struct property_set that can be added as a firmware node to a struct device using device_add_property_set(). That will cause the device_property_* API to use that property_set as the source of properties if the given device does not have a DT node or an ACPI companion device object associated with it. Signed-off-by: Rafael J. Wysocki Tested-by: Heikki Krogerus Acked-by: Greg Kroah-Hartman --- drivers/base/property.c | 98 +++++++++++++++++++++++++++++++++++++++++++++--- include/linux/fwnode.h | 1 + include/linux/property.h | 33 ++++++++++++++++ 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 62787bc89a1d..6a3f7d8af341 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -10,10 +10,96 @@ * published by the Free Software Foundation. */ -#include -#include #include +#include +#include #include +#include + +/** + * device_add_property_set - Add a collection of properties to a device object. + * @dev: Device to add properties to. + * @pset: Collection of properties to add. + * + * Associate a collection of device properties represented by @pset with @dev + * as its secondary firmware node. + */ +void device_add_property_set(struct device *dev, struct property_set *pset) +{ + if (pset) + pset->fwnode.type = FWNODE_PDATA; + + set_secondary_fwnode(dev, &pset->fwnode); +} +EXPORT_SYMBOL_GPL(device_add_property_set); + +static inline bool is_pset(struct fwnode_handle *fwnode) +{ + return fwnode && fwnode->type == FWNODE_PDATA; +} + +static inline struct property_set *to_pset(struct fwnode_handle *fwnode) +{ + return is_pset(fwnode) ? + container_of(fwnode, struct property_set, fwnode) : NULL; +} + +static struct property_entry *pset_prop_get(struct property_set *pset, + const char *name) +{ + struct property_entry *prop; + + if (!pset || !pset->properties) + return NULL; + + for (prop = pset->properties; prop->name; prop++) + if (!strcmp(name, prop->name)) + return prop; + + return NULL; +} + +static int pset_prop_read_array(struct property_set *pset, const char *name, + enum dev_prop_type type, void *val, size_t nval) +{ + struct property_entry *prop; + unsigned int item_size; + + prop = pset_prop_get(pset, name); + if (!prop) + return -ENODATA; + + if (prop->type != type) + return -EPROTO; + + if (!val) + return prop->nval; + + if (prop->nval < nval) + return -EOVERFLOW; + + switch (type) { + case DEV_PROP_U8: + item_size = sizeof(u8); + break; + case DEV_PROP_U16: + item_size = sizeof(u16); + break; + case DEV_PROP_U32: + item_size = sizeof(u32); + break; + case DEV_PROP_U64: + item_size = sizeof(u64); + break; + case DEV_PROP_STRING: + item_size = sizeof(const char *); + break; + default: + return -EINVAL; + } + memcpy(val, prop->value.raw_data, nval * item_size); + return 0; +} static inline struct fwnode_handle *dev_fwnode(struct device *dev) { @@ -46,7 +132,7 @@ bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname) else if (is_acpi_node(fwnode)) return !acpi_dev_prop_get(acpi_node(fwnode), propname, NULL); - return false; + return !!pset_prop_get(to_pset(fwnode), propname); } EXPORT_SYMBOL_GPL(fwnode_property_present); @@ -205,7 +291,8 @@ EXPORT_SYMBOL_GPL(device_property_read_string); _ret_ = acpi_dev_prop_read(acpi_node(_fwnode_), _propname_, \ _proptype_, _val_, _nval_); \ else \ - _ret_ = -ENXIO; \ + _ret_ = pset_prop_read_array(to_pset(_fwnode_), _propname_, \ + _proptype_, _val_, _nval_); \ _ret_; \ }) @@ -344,7 +431,8 @@ int fwnode_property_read_string_array(struct fwnode_handle *fwnode, return acpi_dev_prop_read(acpi_node(fwnode), propname, DEV_PROP_STRING, val, nval); - return -ENXIO; + return pset_prop_read_array(to_pset(fwnode), propname, + DEV_PROP_STRING, val, nval); } EXPORT_SYMBOL_GPL(fwnode_property_read_string_array); diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index fc30b84b532a..0408545bce42 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -16,6 +16,7 @@ enum fwnode_type { FWNODE_INVALID = 0, FWNODE_OF, FWNODE_ACPI, + FWNODE_PDATA, }; struct fwnode_handle { diff --git a/include/linux/property.h b/include/linux/property.h index 31dfd3db35d6..de8bdf417a35 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -131,4 +131,37 @@ static inline int fwnode_property_read_u64(struct fwnode_handle *fwnode, return fwnode_property_read_u64_array(fwnode, propname, val, 1); } +/** + * struct property_entry - "Built-in" device property representation. + * @name: Name of the property. + * @type: Type of the property. + * @nval: Number of items of type @type making up the value. + * @value: Value of the property (an array of @nval items of type @type). + */ +struct property_entry { + const char *name; + enum dev_prop_type type; + size_t nval; + union { + void *raw_data; + u8 *u8_data; + u16 *u16_data; + u32 *u32_data; + u64 *u64_data; + const char **str; + } value; +}; + +/** + * struct property_set - Collection of "built-in" device properties. + * @fwnode: Handle to be pointed to by the fwnode field of struct device. + * @properties: Array of properties terminated with a null entry. + */ +struct property_set { + struct fwnode_handle fwnode; + struct property_entry *properties; +}; + +void device_add_property_set(struct device *dev, struct property_set *pset); + #endif /* _LINUX_PROPERTY_H_ */ -- cgit From 54fe9ce385d499fe5a2bb0c68f092c97dad9365c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Apr 2015 16:07:30 +0200 Subject: ACPI / scan: Generalize of_compatible matching Redefine the function used for matching the device's "compatible" property against a given list of "compatible" strings to take a pointer to that list instead of a driver object pointer to make it more general. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/scan.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 3e5a2768c3b4..99f97ac64aa4 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -949,9 +949,17 @@ int acpi_match_device_ids(struct acpi_device *device, } EXPORT_SYMBOL(acpi_match_device_ids); -/* Performs match against special "PRP0001" shoehorn ACPI ID */ -static bool acpi_of_driver_match_device(struct device *dev, - const struct device_driver *drv) +/** + * acpi_of_match_device - Match device using the "compatible" property. + * @dev: Device to match. + * @of_match_table: List of device IDs to match against. + * + * If @dev has an ACPI companion which has the special PRP0001 device ID in its + * list of identifiers and a _DSD object with the "compatible" property, use + * that property to match against the given list of identifiers. + */ +static bool acpi_of_match_device(struct device *dev, + const struct of_device_id *of_match_table) { const union acpi_object *of_compatible, *obj; struct acpi_device *adev; @@ -962,7 +970,7 @@ static bool acpi_of_driver_match_device(struct device *dev, return false; of_compatible = adev->data.of_compatible; - if (!drv->of_match_table || !of_compatible) + if (!of_match_table || !of_compatible) return false; if (of_compatible->type == ACPI_TYPE_PACKAGE) { @@ -976,7 +984,7 @@ static bool acpi_of_driver_match_device(struct device *dev, for (i = 0; i < nval; i++, obj++) { const struct of_device_id *id; - for (id = drv->of_match_table; id->compatible[0]; id++) + for (id = of_match_table; id->compatible[0]; id++) if (!strcasecmp(obj->string.pointer, id->compatible)) return true; } @@ -988,7 +996,7 @@ bool acpi_driver_match_device(struct device *dev, const struct device_driver *drv) { if (!drv->acpi_match_table) - return acpi_of_driver_match_device(dev, drv); + return acpi_of_match_device(dev, drv->of_match_table); return !!acpi_match_device(drv->acpi_match_table, dev); } -- cgit From e1acdeb0e7707f4220aa14ad6739102ba26e07c0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Apr 2015 16:07:38 +0200 Subject: ACPI / scan: Simplify acpi_match_device() Redefine acpi_companion_match() to return an ACPI device object pointer instead of a bool and use it to remove some redundant code from acpi_match_device(). Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/scan.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 99f97ac64aa4..b4d5549265ed 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -194,7 +194,8 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, * * Check if the given device has an ACPI companion and if that companion has * a valid list of PNP IDs, and if the device is the first (primary) physical - * device associated with it. + * device associated with it. Return the companion pointer if that's the case + * or NULL otherwise. * * If multiple physical devices are attached to a single ACPI companion, we need * to be careful. The usage scenario for this kind of relationship is that all @@ -208,31 +209,31 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, * resources available from it but they will be matched normally using functions * provided by their bus types (and analogously for their modalias). */ -static bool acpi_companion_match(const struct device *dev) +static struct acpi_device *acpi_companion_match(const struct device *dev) { struct acpi_device *adev; - bool ret; adev = ACPI_COMPANION(dev); if (!adev) - return false; + return NULL; if (list_empty(&adev->pnp.ids)) - return false; + return NULL; mutex_lock(&adev->physical_node_lock); if (list_empty(&adev->physical_node_list)) { - ret = false; + adev = NULL; } else { const struct acpi_device_physical_node *node; node = list_first_entry(&adev->physical_node_list, struct acpi_device_physical_node, node); - ret = node->dev == dev; + if (node->dev != dev) + adev = NULL; } mutex_unlock(&adev->physical_node_lock); - return ret; + return adev; } /* @@ -904,7 +905,7 @@ static const struct acpi_device_id *__acpi_match_device( * If the device is not present, it is unnecessary to load device * driver for it. */ - if (!device->status.present) + if (!device || !device->status.present) return NULL; for (id = ids; id->id[0]; id++) @@ -929,16 +930,7 @@ static const struct acpi_device_id *__acpi_match_device( const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, const struct device *dev) { - struct acpi_device *adev; - acpi_handle handle = ACPI_HANDLE(dev); - - if (!ids || !handle || acpi_bus_get_device(handle, &adev)) - return NULL; - - if (!acpi_companion_match(dev)) - return NULL; - - return __acpi_match_device(adev, ids); + return __acpi_match_device(acpi_companion_match(dev), ids); } EXPORT_SYMBOL_GPL(acpi_match_device); -- cgit From 2b9c698efa58bf7d9a0d3d3b28115cf9e55ca818 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 10 Apr 2015 16:07:51 +0200 Subject: ACPI / scan: Take the PRP0001 position in the list of IDs into account If the special PRP0001 device ID is present in a device's _CID list, it should not prevent any ACPI/PNP IDs preceding it in the device's list of identifiers from being matched first. That is, only if none of the IDs preceding PRP0001 in the device's PNP/ACPI IDs list matches the IDs recognized by the driver, the driver's list of "compatible" IDs should be matched against the device's "compatible" property, if present. In addition to that, drivers can provide both acpi_match_table and of_match_table at the same time and the of_compatible matching should be used in that case too if PRP0001 is present in the device's list of identifiers. To make that happen, rework acpi_driver_match_device() to do the "compatible" property check in addition to matching the driver's list of ACPI IDs against the device's one. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/scan.c | 115 +++++++++++++++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 50 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b4d5549265ed..8f3adf924e83 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -895,8 +895,51 @@ static void acpi_device_remove_files(struct acpi_device *dev) ACPI Bus operations -------------------------------------------------------------------------- */ +/** + * acpi_of_match_device - Match device object using the "compatible" property. + * @adev: ACPI device object to match. + * @of_match_table: List of device IDs to match against. + * + * If @dev has an ACPI companion which has the special PRP0001 device ID in its + * list of identifiers and a _DSD object with the "compatible" property, use + * that property to match against the given list of identifiers. + */ +static bool acpi_of_match_device(struct acpi_device *adev, + const struct of_device_id *of_match_table) +{ + const union acpi_object *of_compatible, *obj; + int i, nval; + + if (!adev) + return false; + + of_compatible = adev->data.of_compatible; + if (!of_match_table || !of_compatible) + return false; + + if (of_compatible->type == ACPI_TYPE_PACKAGE) { + nval = of_compatible->package.count; + obj = of_compatible->package.elements; + } else { /* Must be ACPI_TYPE_STRING. */ + nval = 1; + obj = of_compatible; + } + /* Now we can look for the driver DT compatible strings */ + for (i = 0; i < nval; i++, obj++) { + const struct of_device_id *id; + + for (id = of_match_table; id->compatible[0]; id++) + if (!strcasecmp(obj->string.pointer, id->compatible)) + return true; + } + + return false; +} + static const struct acpi_device_id *__acpi_match_device( - struct acpi_device *device, const struct acpi_device_id *ids) + struct acpi_device *device, + const struct acpi_device_id *ids, + const struct of_device_id *of_ids) { const struct acpi_device_id *id; struct acpi_hardware_id *hwid; @@ -908,11 +951,24 @@ static const struct acpi_device_id *__acpi_match_device( if (!device || !device->status.present) return NULL; - for (id = ids; id->id[0]; id++) - list_for_each_entry(hwid, &device->pnp.ids, list) + list_for_each_entry(hwid, &device->pnp.ids, list) { + /* First, check the ACPI/PNP IDs provided by the caller. */ + for (id = ids; id->id[0]; id++) if (!strcmp((char *) id->id, hwid->id)) return id; + /* + * Next, check the special "PRP0001" ID and try to match the + * "compatible" property if found. + * + * The id returned by the below is not valid, but the only + * caller passing non-NULL of_ids here is only interested in + * whether or not the return value is NULL. + */ + if (!strcmp("PRP0001", hwid->id) + && acpi_of_match_device(device, of_ids)) + return id; + } return NULL; } @@ -930,67 +986,26 @@ static const struct acpi_device_id *__acpi_match_device( const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, const struct device *dev) { - return __acpi_match_device(acpi_companion_match(dev), ids); + return __acpi_match_device(acpi_companion_match(dev), ids, NULL); } EXPORT_SYMBOL_GPL(acpi_match_device); int acpi_match_device_ids(struct acpi_device *device, const struct acpi_device_id *ids) { - return __acpi_match_device(device, ids) ? 0 : -ENOENT; + return __acpi_match_device(device, ids, NULL) ? 0 : -ENOENT; } EXPORT_SYMBOL(acpi_match_device_ids); -/** - * acpi_of_match_device - Match device using the "compatible" property. - * @dev: Device to match. - * @of_match_table: List of device IDs to match against. - * - * If @dev has an ACPI companion which has the special PRP0001 device ID in its - * list of identifiers and a _DSD object with the "compatible" property, use - * that property to match against the given list of identifiers. - */ -static bool acpi_of_match_device(struct device *dev, - const struct of_device_id *of_match_table) -{ - const union acpi_object *of_compatible, *obj; - struct acpi_device *adev; - int i, nval; - - adev = ACPI_COMPANION(dev); - if (!adev) - return false; - - of_compatible = adev->data.of_compatible; - if (!of_match_table || !of_compatible) - return false; - - if (of_compatible->type == ACPI_TYPE_PACKAGE) { - nval = of_compatible->package.count; - obj = of_compatible->package.elements; - } else { /* Must be ACPI_TYPE_STRING. */ - nval = 1; - obj = of_compatible; - } - /* Now we can look for the driver DT compatible strings */ - for (i = 0; i < nval; i++, obj++) { - const struct of_device_id *id; - - for (id = of_match_table; id->compatible[0]; id++) - if (!strcasecmp(obj->string.pointer, id->compatible)) - return true; - } - - return false; -} - bool acpi_driver_match_device(struct device *dev, const struct device_driver *drv) { if (!drv->acpi_match_table) - return acpi_of_match_device(dev, drv->of_match_table); + return acpi_of_match_device(ACPI_COMPANION(dev), + drv->of_match_table); - return !!acpi_match_device(drv->acpi_match_table, dev); + return !!__acpi_match_device(acpi_companion_match(dev), + drv->acpi_match_table, drv->of_match_table); } EXPORT_SYMBOL_GPL(acpi_driver_match_device); -- cgit From 4492d1a2e9566495858d25b35093e11509707407 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 11 Apr 2015 00:17:11 +0200 Subject: cpufreq: fix qoriq uniprocessor build The qoriq-cpufreq driver contains a hack for powerpc to include asm/smp.h on uniprocessor builds so it can get the hardware CPU number. On ARM, it does not require this hack, but instead gets a compile error: In file included from drivers/cpufreq/qoriq-cpufreq.c:24:0: arch/arm/include/asm/smp.h:18:3: error: #error " included in non-SMP build" arch/arm/include/asm/smp.h:21:0: warning: "raw_smp_processor_id" redefined This adds an #ifdef to mirror the one in its get_cpu_physical_id() function. Signed-off-by: Arnd Bergmann Fixes: 2f249358eedaf ("cpufreq: qoriq: rename the driver") Cc: Tang Yuantian Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/qoriq-cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index 9fd51c860308..88b21ae0d6b0 100644 --- a/drivers/cpufreq/qoriq-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -21,7 +21,9 @@ #include #include +#if !defined(CONFIG_ARM) #include /* for get_hard_smp_processor_id() in UP configs */ +#endif /** * struct cpu_data -- cgit From 5f97899d780787a8bbb42dee3be91ab5d8003b7f Mon Sep 17 00:00:00 2001 From: Kristen Carlson Accardi Date: Fri, 10 Apr 2015 10:21:09 -0700 Subject: intel_pstate: remove MSR test x86_match_cpu will not match our cpuid unless APERF/MPERF flag is set, so there is no need to do the manual check for this MSR. Signed-off-by: Kristen Carlson Accardi Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 872c5772c5d3..8006d86da397 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -1024,25 +1024,11 @@ static unsigned int force_load; static int intel_pstate_msrs_not_valid(void) { - /* Check that all the msr's we are using are valid. */ - u64 aperf, mperf, tmp; - - rdmsrl(MSR_IA32_APERF, aperf); - rdmsrl(MSR_IA32_MPERF, mperf); - if (!pstate_funcs.get_max() || !pstate_funcs.get_min() || !pstate_funcs.get_turbo()) return -ENODEV; - rdmsrl(MSR_IA32_APERF, tmp); - if (!(tmp - aperf)) - return -ENODEV; - - rdmsrl(MSR_IA32_MPERF, tmp); - if (!(tmp - mperf)) - return -ENODEV; - return 0; } -- cgit From b34ef932d79ac2a36e1d7ba2742e5d4ba54efa3c Mon Sep 17 00:00:00 2001 From: Dasaratharaman Chandramouli Date: Fri, 10 Apr 2015 10:22:18 -0700 Subject: intel_pstate: Knights Landing support 1. Add Knights Landing (KNL) CPUID to the list of CPUIDs supported by the intel_pstate driver. 2. Add a new cpu_default structure for KNL since KNL has a slightly different mechanism to get turbo pstates from MSRs. Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Kristen Carlson Accardi [ rjw: Subject and changelog ] Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 8006d86da397..c5b81beccc8e 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -614,6 +614,19 @@ static void core_set_pstate(struct cpudata *cpudata, int pstate) wrmsrl_on_cpu(cpudata->cpu, MSR_IA32_PERF_CTL, val); } +static int knl_get_turbo_pstate(void) +{ + u64 value; + int nont, ret; + + rdmsrl(MSR_NHM_TURBO_RATIO_LIMIT, value); + nont = core_get_max_pstate(); + ret = (((value) >> 8) & 0xFF); + if (ret <= nont) + ret = nont; + return ret; +} + static struct cpu_defaults core_params = { .pid_policy = { .sample_rate_ms = 10, @@ -651,6 +664,23 @@ static struct cpu_defaults byt_params = { }, }; +static struct cpu_defaults knl_params = { + .pid_policy = { + .sample_rate_ms = 10, + .deadband = 0, + .setpoint = 97, + .p_gain_pct = 20, + .d_gain_pct = 0, + .i_gain_pct = 0, + }, + .funcs = { + .get_max = core_get_max_pstate, + .get_min = core_get_min_pstate, + .get_turbo = knl_get_turbo_pstate, + .set = core_set_pstate, + }, +}; + static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max) { int max_perf = cpu->pstate.turbo_pstate; @@ -865,6 +895,7 @@ static const struct x86_cpu_id intel_pstate_cpu_ids[] = { ICPU(0x4e, core_params), ICPU(0x4f, core_params), ICPU(0x56, core_params), + ICPU(0x57, knl_params), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_pstate_cpu_ids); -- cgit From 34dfa36c04c5883c325e8d62905c0ca5551fcf27 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Tue, 7 Apr 2015 05:50:35 -0700 Subject: powercap / RAPL: add ID for Broadwell server Validated RAPL functions on this platform. Add support to driver. Signed-off-by: Jacob Pan Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index 63d4033eb683..678ac8c178b2 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -1062,6 +1062,7 @@ static const struct x86_cpu_id rapl_ids[] = { RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */ RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */ RAPL_CPU(0x3f, rapl_defaults_hsw_server),/* Haswell servers */ + RAPL_CPU(0x4f, rapl_defaults_hsw_server),/* Broadwell servers */ RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */ RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */ RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */ -- cgit From ea85dbcaec0ff29f2c3f84be4c8a01a94e8bd449 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Wed, 25 Mar 2015 22:15:52 +0100 Subject: powercap / RAPL: mark rapl_ids array as __initconst The RAPL ids are only tested in rapl_init() which is itself an __init function. For the MODULE_DEVICE_TABLE() file2alias doesn't care about the section, just about the symbol name. Therefore it's safe to mark the rapl_ids[] array as __initconst so its memory can be released after initialization is done. Signed-off-by: Mathias Krause Acked-by: Jacob Pan Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index 678ac8c178b2..e03877c4b195 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -1054,7 +1054,7 @@ static const struct rapl_defaults rapl_defaults_atom = { .driver_data = (kernel_ulong_t)&_ops, \ } -static const struct x86_cpu_id rapl_ids[] = { +static const struct x86_cpu_id rapl_ids[] __initconst = { RAPL_CPU(0x2a, rapl_defaults_core),/* Sandy Bridge */ RAPL_CPU(0x2d, rapl_defaults_core),/* Sandy Bridge EP */ RAPL_CPU(0x37, rapl_defaults_atom),/* Valleyview */ -- cgit From d5cdc3c4c22fce5a187b51736f3d0dc3e6cbe282 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Wed, 25 Mar 2015 22:15:14 +0100 Subject: intel_idle: mark cpu id array as __initconst The CPU ids are only tested in intel_idle_probe() which is itself an __init function. For the MODULE_DEVICE_TABLE() file2alias doesn't care about the section, just about the symbol name. So it's safe to mark the cpu id array as __initconst so its memory can be released after initialization is done. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index b0e58522780d..f5b7fc56fa4a 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -776,7 +776,7 @@ static const struct idle_cpu idle_cpu_avn = { #define ICPU(model, cpu) \ { X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu } -static const struct x86_cpu_id intel_idle_ids[] = { +static const struct x86_cpu_id intel_idle_ids[] __initconst = { ICPU(0x1a, idle_cpu_nehalem), ICPU(0x1e, idle_cpu_nehalem), ICPU(0x1f, idle_cpu_nehalem), -- cgit From 8765c5ba19490c9167fe0c4e05c2dfdcc39873a3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 13 Apr 2015 00:29:36 +0200 Subject: ACPI / scan: Rework modalias creation when "compatible" is present Currently, the ACPI modalias creation covers two mutually exclusive cases: If the PRP0001 device ID is present in the device's list of ACPI/PNP IDs and the "compatible" property is present in _DSD, the created modalias will follow the OF rules of modalias creation. Otherwise, ACPI rules are used. However, that is not really desirable, because the presence of PRP0001 in the list of device IDs generally does not preclude using other ACPI/PNP IDs with that device and those other IDs may be of higher priority. In those cases, the other IDs should take preference over PRP0001 and therefore they also should be present in the modalias. For this reason, rework the modalias creation for ACPI so that it shows both the ACPI-style and OF-style modalias strings if the device has a non-empty list of ACPI/PNP IDs (other than PRP0001) and a valid "compatible" property at the same time. Signed-off-by: Rafael J. Wysocki Tested-by: Mika Westerberg --- drivers/acpi/scan.c | 249 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 155 insertions(+), 94 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8f3adf924e83..d2e3c3e3f9c9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -114,7 +114,12 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, return 0; } -/* +/** + * create_pnp_modalias - Create hid/cid(s) string for modalias and uevent + * @acpi_dev: ACPI device object. + * @modalias: Buffer to print into. + * @size: Size of the buffer. + * * Creates hid/cid(s) string needed for modalias and uevent * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: * char *modalias: "acpi:IBM0001:ACPI0001" @@ -122,68 +127,98 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, * -EINVAL: output error * -ENOMEM: output is truncated */ -static int create_modalias(struct acpi_device *acpi_dev, char *modalias, - int size) +static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, + int size) { int len; int count; struct acpi_hardware_id *id; - if (list_empty(&acpi_dev->pnp.ids)) - return 0; - /* - * If the device has PRP0001 we expose DT compatible modalias - * instead in form of of:NnameTCcompatible. + * Since we skip PRP0001 from the modalias below, 0 should be returned + * if PRP0001 is the only ACPI/PNP ID in the device's list. */ - if (acpi_dev->data.of_compatible) { - struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; - const union acpi_object *of_compatible, *obj; - int i, nval; - char *c; - - acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); - /* DT strings are all in lower case */ - for (c = buf.pointer; *c != '\0'; c++) - *c = tolower(*c); - - len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); - ACPI_FREE(buf.pointer); - - of_compatible = acpi_dev->data.of_compatible; - if (of_compatible->type == ACPI_TYPE_PACKAGE) { - nval = of_compatible->package.count; - obj = of_compatible->package.elements; - } else { /* Must be ACPI_TYPE_STRING. */ - nval = 1; - obj = of_compatible; - } - for (i = 0; i < nval; i++, obj++) { - count = snprintf(&modalias[len], size, "C%s", - obj->string.pointer); - if (count < 0) - return -EINVAL; - if (count >= size) - return -ENOMEM; - - len += count; - size -= count; - } - } else { - len = snprintf(modalias, size, "acpi:"); - size -= len; + count = 0; + list_for_each_entry(id, &acpi_dev->pnp.ids, list) + if (strcmp(id->id, "PRP0001")) + count++; - list_for_each_entry(id, &acpi_dev->pnp.ids, list) { - count = snprintf(&modalias[len], size, "%s:", id->id); - if (count < 0) - return -EINVAL; - if (count >= size) - return -ENOMEM; - len += count; - size -= count; - } + if (!count) + return 0; + + len = snprintf(modalias, size, "acpi:"); + if (len <= 0) + return len; + + size -= len; + + list_for_each_entry(id, &acpi_dev->pnp.ids, list) { + if (!strcmp(id->id, "PRP0001")) + continue; + + count = snprintf(&modalias[len], size, "%s:", id->id); + if (count < 0) + return -EINVAL; + + if (count >= size) + return -ENOMEM; + + len += count; + size -= count; } + modalias[len] = '\0'; + return len; +} + +/** + * create_of_modalias - Creates DT compatible string for modalias and uevent + * @acpi_dev: ACPI device object. + * @modalias: Buffer to print into. + * @size: Size of the buffer. + * + * Expose DT compatible modalias as of:NnameTCcompatible. This function should + * only be called for devices having PRP0001 in their list of ACPI/PNP IDs. + */ +static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, + int size) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + const union acpi_object *of_compatible, *obj; + int len, count; + int i, nval; + char *c; + acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf); + /* DT strings are all in lower case */ + for (c = buf.pointer; *c != '\0'; c++) + *c = tolower(*c); + + len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer); + ACPI_FREE(buf.pointer); + + if (len <= 0) + return len; + + of_compatible = acpi_dev->data.of_compatible; + if (of_compatible->type == ACPI_TYPE_PACKAGE) { + nval = of_compatible->package.count; + obj = of_compatible->package.elements; + } else { /* Must be ACPI_TYPE_STRING. */ + nval = 1; + obj = of_compatible; + } + for (i = 0; i < nval; i++, obj++) { + count = snprintf(&modalias[len], size, "C%s", + obj->string.pointer); + if (count < 0) + return -EINVAL; + + if (count >= size) + return -ENOMEM; + + len += count; + size -= count; + } modalias[len] = '\0'; return len; } @@ -236,61 +271,100 @@ static struct acpi_device *acpi_companion_match(const struct device *dev) return adev; } -/* - * Creates uevent modalias field for ACPI enumerated devices. - * Because the other buses does not support ACPI HIDs & CIDs. - * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: - * "acpi:IBM0001:ACPI0001" - */ -int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) +static int __acpi_device_uevent_modalias(struct acpi_device *adev, + struct kobj_uevent_env *env) { int len; - if (!acpi_companion_match(dev)) + if (!adev) return -ENODEV; + if (list_empty(&adev->pnp.ids)) + return 0; + if (add_uevent_var(env, "MODALIAS=")) return -ENOMEM; - len = create_modalias(ACPI_COMPANION(dev), &env->buf[env->buflen - 1], - sizeof(env->buf) - env->buflen); - if (len <= 0) + + len = create_pnp_modalias(adev, &env->buf[env->buflen - 1], + sizeof(env->buf) - env->buflen); + if (len < 0) return len; + + env->buflen += len; + if (!adev->data.of_compatible) + return 0; + + if (len > 0 && add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + + len = create_of_modalias(adev, &env->buf[env->buflen - 1], + sizeof(env->buf) - env->buflen); + if (len < 0) + return len; + env->buflen += len; + return 0; } -EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); /* - * Creates modalias sysfs attribute for ACPI enumerated devices. + * Creates uevent modalias field for ACPI enumerated devices. * Because the other buses does not support ACPI HIDs & CIDs. * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: * "acpi:IBM0001:ACPI0001" */ -int acpi_device_modalias(struct device *dev, char *buf, int size) +int acpi_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) { - int len; + return __acpi_device_uevent_modalias(acpi_companion_match(dev), env); +} +EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); + +static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size) +{ + int len, count; - if (!acpi_companion_match(dev)) + if (!adev) return -ENODEV; - len = create_modalias(ACPI_COMPANION(dev), buf, size -1); - if (len <= 0) + if (list_empty(&adev->pnp.ids)) + return 0; + + len = create_pnp_modalias(adev, buf, size - 1); + if (len < 0) { return len; - buf[len++] = '\n'; + } else if (len > 0) { + buf[len++] = '\n'; + size -= len; + } + if (!adev->data.of_compatible) + return len; + + count = create_of_modalias(adev, buf + len, size - 1); + if (count < 0) { + return count; + } else if (count > 0) { + len += count; + buf[len++] = '\n'; + } + return len; } + +/* + * Creates modalias sysfs attribute for ACPI enumerated devices. + * Because the other buses does not support ACPI HIDs & CIDs. + * e.g. for a device with hid:IBM0001 and cid:ACPI0001 you get: + * "acpi:IBM0001:ACPI0001" + */ +int acpi_device_modalias(struct device *dev, char *buf, int size) +{ + return __acpi_device_modalias(acpi_companion_match(dev), buf, size); +} EXPORT_SYMBOL_GPL(acpi_device_modalias); static ssize_t acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct acpi_device *acpi_dev = to_acpi_device(dev); - int len; - - len = create_modalias(acpi_dev, buf, 1024); - if (len <= 0) - return len; - buf[len++] = '\n'; - return len; + return __acpi_device_modalias(to_acpi_device(dev), buf, 1024); } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); @@ -1046,20 +1120,7 @@ static int acpi_bus_match(struct device *dev, struct device_driver *drv) static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env) { - struct acpi_device *acpi_dev = to_acpi_device(dev); - int len; - - if (list_empty(&acpi_dev->pnp.ids)) - return 0; - - if (add_uevent_var(env, "MODALIAS=")) - return -ENOMEM; - len = create_modalias(acpi_dev, &env->buf[env->buflen - 1], - sizeof(env->buf) - env->buflen); - if (len <= 0) - return len; - env->buflen += len; - return 0; + return __acpi_device_uevent_modalias(to_acpi_device(dev), env); } static void acpi_device_notify(acpi_handle handle, u32 event, void *data) -- cgit From 5f2e3274e527ec52413b43b5a9b30422eda4cdd2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 13 Apr 2015 21:35:51 +0200 Subject: ACPI / scan: Fix NULL pointer dereference in acpi_companion_match() Commit e1acdeb0e770 "ACPI / scan: Simplify acpi_match_device()" introduced code that may lead to a NULL pointer dereference when trying to unlock a mutex. Fix that. Fixes: e1acdeb0e770 "ACPI / scan: Simplify acpi_match_device()" Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d2e3c3e3f9c9..69bc0d888c01 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -247,6 +247,7 @@ static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, static struct acpi_device *acpi_companion_match(const struct device *dev) { struct acpi_device *adev; + struct mutex *physical_node_lock; adev = ACPI_COMPANION(dev); if (!adev) @@ -255,7 +256,8 @@ static struct acpi_device *acpi_companion_match(const struct device *dev) if (list_empty(&adev->pnp.ids)) return NULL; - mutex_lock(&adev->physical_node_lock); + physical_node_lock = &adev->physical_node_lock; + mutex_lock(physical_node_lock); if (list_empty(&adev->physical_node_list)) { adev = NULL; } else { @@ -266,7 +268,7 @@ static struct acpi_device *acpi_companion_match(const struct device *dev) if (node->dev != dev) adev = NULL; } - mutex_unlock(&adev->physical_node_lock); + mutex_unlock(physical_node_lock); return adev; } -- cgit