Commit f4fe74cc authored by Linus Torvalds's avatar Linus Torvalds

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

Pull ACPI updates from Rafael Wysocki:
 "These update the ACPICA code in the kernel to the 20180508 upstream
  revision and make it support the RT patch, add CPPC v3 support to the
  ACPI CPPC library, add a WDAT-based watchdog quirk to prevent clashes
  with the RTC, add quirks to the ACPI AC and battery drivers, and
  update the ACPI SoC drivers.

  Specifics:

   - Update the ACPICA code in the kernel to the 20180508 upstream
     revision including:
       * iASL -tc option enhancement (Bob Moore).
       * Debugger improvements (Bob Moore).
       * Support for tables larger than 1 MB in acpidump/acpixtract (Bob
         Moore).
       * Minor fixes and cleanups (Colin Ian King, Toomas Soome).

   - Make the ACPICA code in the kernel support the RT patch (Sebastian
     Andrzej Siewior, Steven Rostedt).

   - Add a kmemleak annotation to the ACPICA code (Larry Finger).

   - Add CPPC v3 support to the ACPI CPPC library and fix two issues
     related to CPPC (Prashanth Prakash, Al Stone).

   - Add an ACPI WDAT-based watchdog quirk to prefer iTCO_wdt on systems
     where WDAT clashes with the RTC SRAM (Mika Westerberg).

   - Add some quirks to the ACPI AC and battery drivers (Carlo Caione,
     Hans de Goede).

   - Update the ACPI SoC drivers for Intel (LPSS) and AMD (APD)
     platforms (Akshu Agrawal, Hans de Goede).

   - Fix up some assorted minor issues (Al Stone, Laszlo Toth, Mathieu
     Malaterre)"

* tag 'acpi-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (32 commits)
  ACPICA: Mark acpi_ut_create_internal_object_dbg() memory allocations as non-leaks
  ACPI / watchdog: Prefer iTCO_wdt always when WDAT table uses RTC SRAM
  mailbox: PCC: erroneous error message when parsing ACPI PCCT
  ACPICA: Update version to 20180508
  ACPICA: acpidump/acpixtract: Support for tables larger than 1MB
  ACPI: APD: Add AMD misc clock handler support
  clk: x86: Add ST oscout platform clock
  ACPICA: Update version to 20180427
  ACPICA: Debugger: Removed direct support for EC address space in "Test Objects"
  ACPICA: Debugger: Add Package support for "test objects" command
  ACPICA: Improve error messages for the namespace root node
  ACPICA: Fix potential infinite loop in acpi_rs_dump_byte_list
  ACPICA: vsnprintf: this statement may fall through
  ACPICA: Tables: Fix spelling mistake in comment
  ACPICA: iASL: Enhance the -tc option (create AML hex file in C)
  ACPI: Add missing prototype_for arch_post_acpi_subsys_init()
  ACPI / tables: improve comments regarding acpi_parse_entries_array()
  ACPICA: Convert acpi_gbl_hardware lock back to an acpi_raw_spinlock
  ACPICA: provide abstraction for raw_spinlock_t
  ACPI / CPPC: Fix invalid PCC channel status errors
  ...
parents 3c89adb0 2448d139
Collaborative Processor Performance Control (CPPC)
CPPC defined in the ACPI spec describes a mechanism for the OS to manage the
performance of a logical processor on a contigious and abstract performance
scale. CPPC exposes a set of registers to describe abstract performance scale,
to request performance levels and to measure per-cpu delivered performance.
For more details on CPPC please refer to the ACPI specification at:
http://uefi.org/specifications
Some of the CPPC registers are exposed via sysfs under:
/sys/devices/system/cpu/cpuX/acpi_cppc/
for each cpu X
--------------------------------------------------------------------------------
$ ls -lR /sys/devices/system/cpu/cpu0/acpi_cppc/
/sys/devices/system/cpu/cpu0/acpi_cppc/:
total 0
-r--r--r-- 1 root root 65536 Mar 5 19:38 feedback_ctrs
-r--r--r-- 1 root root 65536 Mar 5 19:38 highest_perf
-r--r--r-- 1 root root 65536 Mar 5 19:38 lowest_freq
-r--r--r-- 1 root root 65536 Mar 5 19:38 lowest_nonlinear_perf
-r--r--r-- 1 root root 65536 Mar 5 19:38 lowest_perf
-r--r--r-- 1 root root 65536 Mar 5 19:38 nominal_freq
-r--r--r-- 1 root root 65536 Mar 5 19:38 nominal_perf
-r--r--r-- 1 root root 65536 Mar 5 19:38 reference_perf
-r--r--r-- 1 root root 65536 Mar 5 19:38 wraparound_time
--------------------------------------------------------------------------------
* highest_perf : Highest performance of this processor (abstract scale).
* nominal_perf : Highest sustained performance of this processor (abstract scale).
* lowest_nonlinear_perf : Lowest performance of this processor with nonlinear
power savings (abstract scale).
* lowest_perf : Lowest performance of this processor (abstract scale).
* lowest_freq : CPU frequency corresponding to lowest_perf (in MHz).
* nominal_freq : CPU frequency corresponding to nominal_perf (in MHz).
The above frequencies should only be used to report processor performance in
freqency instead of abstract scale. These values should not be used for any
functional decisions.
* feedback_ctrs : Includes both Reference and delivered performance counter.
Reference counter ticks up proportional to processor's reference performance.
Delivered counter ticks up proportional to processor's delivered performance.
* wraparound_time: Minimum time for the feedback counters to wraparound (seconds).
* reference_perf : Performance level at which reference performance counter
accumulates (abstract scale).
--------------------------------------------------------------------------------
Computing Average Delivered Performance
Below describes the steps to compute the average performance delivered by taking
two different snapshots of feedback counters at time T1 and T2.
T1: Read feedback_ctrs as fbc_t1
Wait or run some workload
T2: Read feedback_ctrs as fbc_t2
delivered_counter_delta = fbc_t2[del] - fbc_t1[del]
reference_counter_delta = fbc_t2[ref] - fbc_t1[ref]
delivered_perf = (refernce_perf x delivered_counter_delta) / reference_counter_delta
...@@ -86,6 +86,7 @@ extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); ...@@ -86,6 +86,7 @@ extern void *acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir);
static int ac_sleep_before_get_state_ms; static int ac_sleep_before_get_state_ms;
static int ac_check_pmic = 1;
static struct acpi_driver acpi_ac_driver = { static struct acpi_driver acpi_ac_driver = {
.name = "ac", .name = "ac",
...@@ -293,21 +294,43 @@ static int acpi_ac_battery_notify(struct notifier_block *nb, ...@@ -293,21 +294,43 @@ static int acpi_ac_battery_notify(struct notifier_block *nb,
return NOTIFY_OK; return NOTIFY_OK;
} }
static int thinkpad_e530_quirk(const struct dmi_system_id *d) static int __init thinkpad_e530_quirk(const struct dmi_system_id *d)
{ {
ac_sleep_before_get_state_ms = 1000; ac_sleep_before_get_state_ms = 1000;
return 0; return 0;
} }
static const struct dmi_system_id ac_dmi_table[] = { static int __init ac_do_not_check_pmic_quirk(const struct dmi_system_id *d)
{
ac_check_pmic = 0;
return 0;
}
static const struct dmi_system_id ac_dmi_table[] __initconst = {
{ {
/* Thinkpad e530 */
.callback = thinkpad_e530_quirk, .callback = thinkpad_e530_quirk,
.ident = "thinkpad e530",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"), DMI_MATCH(DMI_PRODUCT_NAME, "32597CG"),
}, },
}, },
{
/* ECS EF20EA */
.callback = ac_do_not_check_pmic_quirk,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
},
},
{
/* Lenovo Ideapad Miix 320 */
.callback = ac_do_not_check_pmic_quirk,
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
},
},
{}, {},
}; };
...@@ -367,7 +390,6 @@ static int acpi_ac_add(struct acpi_device *device) ...@@ -367,7 +390,6 @@ static int acpi_ac_add(struct acpi_device *device)
kfree(ac); kfree(ac);
} }
dmi_check_system(ac_dmi_table);
return result; return result;
} }
...@@ -425,13 +447,17 @@ static int __init acpi_ac_init(void) ...@@ -425,13 +447,17 @@ static int __init acpi_ac_init(void)
if (acpi_disabled) if (acpi_disabled)
return -ENODEV; return -ENODEV;
for (i = 0; i < ARRAY_SIZE(acpi_ac_blacklist); i++) dmi_check_system(ac_dmi_table);
if (acpi_dev_present(acpi_ac_blacklist[i].hid, "1",
acpi_ac_blacklist[i].hrv)) { if (ac_check_pmic) {
pr_info(PREFIX "AC: found native %s PMIC, not loading\n", for (i = 0; i < ARRAY_SIZE(acpi_ac_blacklist); i++)
acpi_ac_blacklist[i].hid); if (acpi_dev_present(acpi_ac_blacklist[i].hid, "1",
return -ENODEV; acpi_ac_blacklist[i].hrv)) {
} pr_info(PREFIX "AC: found native %s PMIC, not loading\n",
acpi_ac_blacklist[i].hid);
return -ENODEV;
}
}
#ifdef CONFIG_ACPI_PROCFS_POWER #ifdef CONFIG_ACPI_PROCFS_POWER
acpi_ac_dir = acpi_lock_ac_dir(); acpi_ac_dir = acpi_lock_ac_dir();
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
*/ */
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/platform_data/clk-st.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/clkdev.h> #include <linux/clkdev.h>
...@@ -72,6 +73,47 @@ static int acpi_apd_setup(struct apd_private_data *pdata) ...@@ -72,6 +73,47 @@ static int acpi_apd_setup(struct apd_private_data *pdata)
} }
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
static int misc_check_res(struct acpi_resource *ares, void *data)
{
struct resource res;
return !acpi_dev_resource_memory(ares, &res);
}
static int st_misc_setup(struct apd_private_data *pdata)
{
struct acpi_device *adev = pdata->adev;
struct platform_device *clkdev;
struct st_clk_data *clk_data;
struct resource_entry *rentry;
struct list_head resource_list;
int ret;
clk_data = devm_kzalloc(&adev->dev, sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
INIT_LIST_HEAD(&resource_list);
ret = acpi_dev_get_resources(adev, &resource_list, misc_check_res,
NULL);
if (ret < 0)
return -ENOENT;
list_for_each_entry(rentry, &resource_list, node) {
clk_data->base = devm_ioremap(&adev->dev, rentry->res->start,
resource_size(rentry->res));
break;
}
acpi_dev_free_resource_list(&resource_list);
clkdev = platform_device_register_data(&adev->dev, "clk-st",
PLATFORM_DEVID_NONE, clk_data,
sizeof(*clk_data));
return PTR_ERR_OR_ZERO(clkdev);
}
static const struct apd_device_desc cz_i2c_desc = { static const struct apd_device_desc cz_i2c_desc = {
.setup = acpi_apd_setup, .setup = acpi_apd_setup,
.fixed_clk_rate = 133000000, .fixed_clk_rate = 133000000,
...@@ -94,6 +136,10 @@ static const struct apd_device_desc cz_uart_desc = { ...@@ -94,6 +136,10 @@ static const struct apd_device_desc cz_uart_desc = {
.fixed_clk_rate = 48000000, .fixed_clk_rate = 48000000,
.properties = uart_properties, .properties = uart_properties,
}; };
static const struct apd_device_desc st_misc_desc = {
.setup = st_misc_setup,
};
#endif #endif
#ifdef CONFIG_ARM64 #ifdef CONFIG_ARM64
...@@ -179,6 +225,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { ...@@ -179,6 +225,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
{ "AMD0020", APD_ADDR(cz_uart_desc) }, { "AMD0020", APD_ADDR(cz_uart_desc) },
{ "AMDI0020", APD_ADDR(cz_uart_desc) }, { "AMDI0020", APD_ADDR(cz_uart_desc) },
{ "AMD0030", }, { "AMD0030", },
{ "AMD0040", APD_ADDR(st_misc_desc)},
#endif #endif
#ifdef CONFIG_ARM64 #ifdef CONFIG_ARM64
{ "APMC0D0F", APD_ADDR(xgene_i2c_desc) }, { "APMC0D0F", APD_ADDR(xgene_i2c_desc) },
......
...@@ -69,6 +69,10 @@ ACPI_MODULE_NAME("acpi_lpss"); ...@@ -69,6 +69,10 @@ ACPI_MODULE_NAME("acpi_lpss");
#define LPSS_SAVE_CTX BIT(4) #define LPSS_SAVE_CTX BIT(4)
#define LPSS_NO_D3_DELAY BIT(5) #define LPSS_NO_D3_DELAY BIT(5)
/* Crystal Cove PMIC shares same ACPI ID between different platforms */
#define BYT_CRC_HRV 2
#define CHT_CRC_HRV 3
struct lpss_private_data; struct lpss_private_data;
struct lpss_device_desc { struct lpss_device_desc {
...@@ -162,7 +166,7 @@ static void byt_pwm_setup(struct lpss_private_data *pdata) ...@@ -162,7 +166,7 @@ static void byt_pwm_setup(struct lpss_private_data *pdata)
if (!adev->pnp.unique_id || strcmp(adev->pnp.unique_id, "1")) if (!adev->pnp.unique_id || strcmp(adev->pnp.unique_id, "1"))
return; return;
if (!acpi_dev_present("INT33FD", NULL, -1)) if (!acpi_dev_present("INT33FD", NULL, BYT_CRC_HRV))
pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup)); pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup));
} }
......
...@@ -12,35 +12,51 @@ ...@@ -12,35 +12,51 @@
#define pr_fmt(fmt) "ACPI: watchdog: " fmt #define pr_fmt(fmt) "ACPI: watchdog: " fmt
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include "internal.h" #include "internal.h"
static const struct dmi_system_id acpi_watchdog_skip[] = { #ifdef CONFIG_RTC_MC146818_LIB
{ #include <linux/mc146818rtc.h>
/*
* On Lenovo Z50-70 there are two issues with the WDAT /*
* table. First some of the instructions use RTC SRAM * There are several systems where the WDAT table is accessing RTC SRAM to
* to store persistent information. This does not work well * store persistent information. This does not work well with the Linux RTC
* with Linux RTC driver. Second, more important thing is * driver so on those systems we skip WDAT driver and prefer iTCO_wdt
* that the instructions do not actually reset the system. * instead.
* *
* On this particular system iTCO_wdt seems to work just * See also https://bugzilla.kernel.org/show_bug.cgi?id=199033.
* fine so we prefer that over WDAT for now. */
* static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat)
* See also https://bugzilla.kernel.org/show_bug.cgi?id=199033. {
*/ const struct acpi_wdat_entry *entries;
.ident = "Lenovo Z50-70", int i;
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), entries = (struct acpi_wdat_entry *)(wdat + 1);
DMI_MATCH(DMI_PRODUCT_NAME, "20354"), for (i = 0; i < wdat->entries; i++) {
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Z50-70"), const struct acpi_generic_address *gas;
},
}, gas = &entries[i].register_region;
{} if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
}; switch (gas->address) {
case RTC_PORT(0):
case RTC_PORT(1):
case RTC_PORT(2):
case RTC_PORT(3):
return true;
}
}
}
return false;
}
#else
static bool acpi_watchdog_uses_rtc(const struct acpi_table_wdat *wdat)
{
return false;
}
#endif
static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void) static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
{ {
...@@ -50,9 +66,6 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void) ...@@ -50,9 +66,6 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
if (acpi_disabled) if (acpi_disabled)
return NULL; return NULL;
if (dmi_check_system(acpi_watchdog_skip))
return NULL;
status = acpi_get_table(ACPI_SIG_WDAT, 0, status = acpi_get_table(ACPI_SIG_WDAT, 0,
(struct acpi_table_header **)&wdat); (struct acpi_table_header **)&wdat);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
...@@ -60,6 +73,11 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void) ...@@ -60,6 +73,11 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
return NULL; return NULL;
} }
if (acpi_watchdog_uses_rtc(wdat)) {
pr_info("Skipping WDAT on this system because it uses RTC SRAM\n");
return NULL;
}
return wdat; return wdat;
} }
......
...@@ -143,6 +143,8 @@ acpi_status ...@@ -143,6 +143,8 @@ acpi_status
fl_split_input_pathname(char *input_path, fl_split_input_pathname(char *input_path,
char **out_directory_path, char **out_filename); char **out_directory_path, char **out_filename);
char *fl_get_file_basename(char *file_pathname);
char *ad_generate_filename(char *prefix, char *table_id); char *ad_generate_filename(char *prefix, char *table_id);
void void
......
...@@ -82,7 +82,7 @@ ACPI_GLOBAL(u8, acpi_gbl_global_lock_pending); ...@@ -82,7 +82,7 @@ ACPI_GLOBAL(u8, acpi_gbl_global_lock_pending);
* interrupt level * interrupt level
*/ */
ACPI_GLOBAL(acpi_spinlock, acpi_gbl_gpe_lock); /* For GPE data structs and registers */ ACPI_GLOBAL(acpi_spinlock, acpi_gbl_gpe_lock); /* For GPE data structs and registers */
ACPI_GLOBAL(acpi_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */ ACPI_GLOBAL(acpi_raw_spinlock, acpi_gbl_hardware_lock); /* For ACPI H/W except GPE registers */
ACPI_GLOBAL(acpi_spinlock, acpi_gbl_reference_count_lock); ACPI_GLOBAL(acpi_spinlock, acpi_gbl_reference_count_lock);
/* Mutex for _OSI support */ /* Mutex for _OSI support */
......
...@@ -189,9 +189,15 @@ void acpi_db_dump_namespace(char *start_arg, char *depth_arg) ...@@ -189,9 +189,15 @@ void acpi_db_dump_namespace(char *start_arg, char *depth_arg)
} }
acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT); acpi_db_set_output_destination(ACPI_DB_DUPLICATE_OUTPUT);
acpi_os_printf("ACPI Namespace (from %4.4s (%p) subtree):\n",
((struct acpi_namespace_node *)subtree_entry)->name. if (((struct acpi_namespace_node *)subtree_entry)->parent) {
ascii, subtree_entry); acpi_os_printf("ACPI Namespace (from %4.4s (%p) subtree):\n",
((struct acpi_namespace_node *)subtree_entry)->
name.ascii, subtree_entry);
} else {
acpi_os_printf("ACPI Namespace (from %s):\n",
ACPI_NAMESPACE_ROOT);
}
/* Display the subtree */ /* Display the subtree */
......
...@@ -30,6 +30,8 @@ acpi_db_test_buffer_type(struct acpi_namespace_node *node, u32 bit_length); ...@@ -30,6 +30,8 @@ acpi_db_test_buffer_type(struct acpi_namespace_node *node, u32 bit_length);
static acpi_status static acpi_status
acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length); acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length);
static acpi_status acpi_db_test_package_type(struct acpi_namespace_node *node);
static acpi_status static acpi_status
acpi_db_read_from_object(struct acpi_namespace_node *node, acpi_db_read_from_object(struct acpi_namespace_node *node,
acpi_object_type expected_type, acpi_object_type expected_type,
...@@ -273,6 +275,11 @@ acpi_db_test_one_object(acpi_handle obj_handle, ...@@ -273,6 +275,11 @@ acpi_db_test_one_object(acpi_handle obj_handle,
bit_length = byte_length * 8; bit_length = byte_length * 8;
break; break;
case ACPI_TYPE_PACKAGE:
local_type = ACPI_TYPE_PACKAGE;
break;
case ACPI_TYPE_FIELD_UNIT: case ACPI_TYPE_FIELD_UNIT:
case ACPI_TYPE_BUFFER_FIELD: case ACPI_TYPE_BUFFER_FIELD:
case ACPI_TYPE_LOCAL_REGION_FIELD: case ACPI_TYPE_LOCAL_REGION_FIELD:
...@@ -305,6 +312,7 @@ acpi_db_test_one_object(acpi_handle obj_handle, ...@@ -305,6 +312,7 @@ acpi_db_test_one_object(acpi_handle obj_handle,
acpi_os_printf("%14s: %4.4s", acpi_os_printf("%14s: %4.4s",
acpi_ut_get_type_name(node->type), node->name.ascii); acpi_ut_get_type_name(node->type), node->name.ascii);
if (!obj_desc) { if (!obj_desc) {
acpi_os_printf(" Ignoring, no attached object\n"); acpi_os_printf(" Ignoring, no attached object\n");
return (AE_OK); return (AE_OK);
...@@ -322,14 +330,13 @@ acpi_db_test_one_object(acpi_handle obj_handle, ...@@ -322,14 +330,13 @@ acpi_db_test_one_object(acpi_handle obj_handle,
case ACPI_ADR_SPACE_SYSTEM_MEMORY: case ACPI_ADR_SPACE_SYSTEM_MEMORY:
case ACPI_ADR_SPACE_SYSTEM_IO: case ACPI_ADR_SPACE_SYSTEM_IO:
case ACPI_ADR_SPACE_PCI_CONFIG: case ACPI_ADR_SPACE_PCI_CONFIG:
case ACPI_ADR_SPACE_EC:
break; break;
default: default:
acpi_os_printf acpi_os_printf
(" %s space is not supported [%4.4s]\n", (" %s space is not supported in this command [%4.4s]\n",
acpi_ut_get_region_name(region_obj->region. acpi_ut_get_region_name(region_obj->region.
space_id), space_id),
region_obj->region.node->name.ascii); region_obj->region.node->name.ascii);
...@@ -359,6 +366,11 @@ acpi_db_test_one_object(acpi_handle obj_handle, ...@@ -359,6 +366,11 @@ acpi_db_test_one_object(acpi_handle obj_handle,
status = acpi_db_test_buffer_type(node, bit_length); status = acpi_db_test_buffer_type(node, bit_length);
break; break;
case ACPI_TYPE_PACKAGE:
status = acpi_db_test_package_type(node);
break;
default: default:
acpi_os_printf(" Ignoring, type not implemented (%2.2X)", acpi_os_printf(" Ignoring, type not implemented (%2.2X)",
...@@ -366,6 +378,13 @@ acpi_db_test_one_object(acpi_handle obj_handle, ...@@ -366,6 +378,13 @@ acpi_db_test_one_object(acpi_handle obj_handle,
break; break;
} }
/* Exit on error, but don't abort the namespace walk */
if (ACPI_FAILURE(status)) {
status = AE_OK;
goto exit;
}
switch (node->type) { switch (node->type) {
case ACPI_TYPE_LOCAL_REGION_FIELD: case ACPI_TYPE_LOCAL_REGION_FIELD:
...@@ -373,12 +392,14 @@ acpi_db_test_one_object(acpi_handle obj_handle, ...@@ -373,12 +392,14 @@ acpi_db_test_one_object(acpi_handle obj_handle,
acpi_os_printf(" (%s)", acpi_os_printf(" (%s)",
acpi_ut_get_region_name(region_obj->region. acpi_ut_get_region_name(region_obj->region.
space_id)); space_id));
break; break;
default: default:
break; break;
} }
exit:
acpi_os_printf("\n"); acpi_os_printf("\n");
return (status); return (status);
} }
...@@ -431,7 +452,6 @@ acpi_db_test_integer_type(struct acpi_namespace_node *node, u32 bit_length) ...@@ -431,7 +452,6 @@ acpi_db_test_integer_type(struct acpi_namespace_node *node, u32 bit_length)
if (temp1->integer.value == value_to_write) { if (temp1->integer.value == value_to_write) {
value_to_write = 0; value_to_write = 0;
} }
/* Write a new value */ /* Write a new value */
write_value.type = ACPI_TYPE_INTEGER; write_value.type = ACPI_TYPE_INTEGER;
...@@ -706,6 +726,35 @@ acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length) ...@@ -706,6 +726,35 @@ acpi_db_test_string_type(struct acpi_namespace_node *node, u32 byte_length)
return (status); return (status);
} }
/*******************************************************************************
*
* FUNCTION: acpi_db_test_package_type
*
* PARAMETERS: node - Parent NS node for the object
*
* RETURN: Status
*
* DESCRIPTION: Test read for a Package object.
*
******************************************************************************/
static acpi_status acpi_db_test_package_type(struct acpi_namespace_node *node)
{
union acpi_object *temp1 = NULL;
acpi_status status;
/* Read the original value */
status = acpi_db_read_from_object(node, ACPI_TYPE_PACKAGE, &temp1);
if (ACPI_FAILURE(status)) {
return (status);
}
acpi_os_printf(" %8.8X Elements", temp1->package.count);
acpi_os_free(temp1);
return (status);
}
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_db_read_from_object * FUNCTION: acpi_db_read_from_object
...@@ -746,8 +795,8 @@ acpi_db_read_from_object(struct acpi_namespace_node *node, ...@@ -746,8 +795,8 @@ acpi_db_read_from_object(struct acpi_namespace_node *node,
acpi_gbl_method_executing = TRUE; acpi_gbl_method_executing = TRUE;
status = acpi_evaluate_object(read_handle, NULL, status = acpi_evaluate_object(read_handle, NULL,
&param_objects, &return_obj); &param_objects, &return_obj);
acpi_gbl_method_executing = FALSE;
acpi_gbl_method_executing = FALSE;
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
acpi_os_printf("Could not read from object, %s", acpi_os_printf("Could not read from object, %s",
acpi_format_exception(status)); acpi_format_exception(status));
...@@ -760,6 +809,7 @@ acpi_db_read_from_object(struct acpi_namespace_node *node, ...@@ -760,6 +809,7 @@ acpi_db_read_from_object(struct acpi_namespace_node *node,
case ACPI_TYPE_INTEGER: case ACPI_TYPE_INTEGER:
case ACPI_TYPE_BUFFER: case ACPI_TYPE_BUFFER:
case ACPI_TYPE_STRING: case ACPI_TYPE_STRING:
case ACPI_TYPE_PACKAGE:
/* /*
* Did we receive the type we wanted? Most important for the * Did we receive the type we wanted? Most important for the
* Integer/Buffer case (when a field is larger than an Integer, * Integer/Buffer case (when a field is larger than an Integer,
...@@ -771,6 +821,7 @@ acpi_db_read_from_object(struct acpi_namespace_node *node, ...@@ -771,6 +821,7 @@ acpi_db_read_from_object(struct acpi_namespace_node *node,
acpi_ut_get_type_name(expected_type), acpi_ut_get_type_name(expected_type),
acpi_ut_get_type_name(ret_value->type)); acpi_ut_get_type_name(ret_value->type));
acpi_os_free(return_obj.pointer);
return (AE_TYPE); return (AE_TYPE);
} }
......
...@@ -115,7 +115,7 @@ acpi_ds_scope_stack_push(struct acpi_namespace_node *node, ...@@ -115,7 +115,7 @@ acpi_ds_scope_stack_push(struct acpi_namespace_node *node,
acpi_ut_get_type_name(old_scope_info-> acpi_ut_get_type_name(old_scope_info->
common.value))); common.value)));
} else { } else {
ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "[\\___] (%s)", "ROOT")); ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, ACPI_NAMESPACE_ROOT));
} }
ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC,
...@@ -166,14 +166,14 @@ acpi_status acpi_ds_scope_stack_pop(struct acpi_walk_state *walk_state) ...@@ -166,14 +166,14 @@ acpi_status acpi_ds_scope_stack_pop(struct acpi_walk_state *walk_state)
new_scope_info = walk_state->scope_info; new_scope_info = walk_state->scope_info;
if (new_scope_info) { if (new_scope_info) {
ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "[%4.4s] (%s)\n",
"[%4.4s] (%s)\n",
acpi_ut_get_node_name(new_scope_info-> acpi_ut_get_node_name(new_scope_info->
scope.node), scope.node),
acpi_ut_get_type_name(new_scope_info-> acpi_ut_get_type_name(new_scope_info->
common.value))); common.value)));
} else { } else {
ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "[\\___] (ROOT)\n")); ACPI_DEBUG_PRINT_RAW((ACPI_DB_EXEC, "%s\n",
ACPI_NAMESPACE_ROOT));
} }
acpi_ut_delete_generic_state(scope_info); acpi_ut_delete_generic_state(scope_info);
......
...@@ -390,14 +390,14 @@ acpi_status acpi_hw_clear_acpi_status(void) ...@@ -390,14 +390,14 @@ acpi_status acpi_hw_clear_acpi_status(void)
ACPI_BITMASK_ALL_FIXED_STATUS, ACPI_BITMASK_ALL_FIXED_STATUS,
ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address))); ACPI_FORMAT_UINT64(acpi_gbl_xpm1a_status.address)));
lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); lock_flags = acpi_os_acquire_raw_lock(acpi_gbl_hardware_lock);
/* Clear the fixed events in PM1 A/B */ /* Clear the fixed events in PM1 A/B */
status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS,
ACPI_BITMASK_ALL_FIXED_STATUS); ACPI_BITMASK_ALL_FIXED_STATUS);
acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); acpi_os_release_raw_lock(acpi_gbl_hardware_lock, lock_flags);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
goto exit; goto exit;
......
...@@ -227,7 +227,7 @@ acpi_status acpi_write_bit_register(u32 register_id, u32 value) ...@@ -227,7 +227,7 @@ acpi_status acpi_write_bit_register(u32 register_id, u32 value)
return_ACPI_STATUS(AE_BAD_PARAMETER); return_ACPI_STATUS(AE_BAD_PARAMETER);
} }
lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); lock_flags = acpi_os_acquire_raw_lock(acpi_gbl_hardware_lock);
/* /*
* At this point, we know that the parent register is one of the * At this point, we know that the parent register is one of the
...@@ -288,7 +288,7 @@ acpi_status acpi_write_bit_register(u32 register_id, u32 value) ...@@ -288,7 +288,7 @@ acpi_status acpi_write_bit_register(u32 register_id, u32 value)
unlock_and_exit: unlock_and_exit:
acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); acpi_os_release_raw_lock(acpi_gbl_hardware_lock, lock_flags);
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
......
...@@ -539,7 +539,7 @@ static void acpi_rs_out_title(const char *title) ...@@ -539,7 +539,7 @@ static void acpi_rs_out_title(const char *title)
static void acpi_rs_dump_byte_list(u16 length, u8 * data) static void acpi_rs_dump_byte_list(u16 length, u8 * data)
{ {
u8 i; u16 i;
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
acpi_os_printf("%25s%2.2X : %2.2X\n", "Byte", i, data[i]); acpi_os_printf("%25s%2.2X : %2.2X\n", "Byte", i, data[i]);
......
...@@ -88,7 +88,7 @@ acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc, ...@@ -88,7 +88,7 @@ acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
* DESCRIPTION: This function is called to verify and install an ACPI table. * DESCRIPTION: This function is called to verify and install an ACPI table.
* When this function is called by "Load" or "LoadTable" opcodes, * When this function is called by "Load" or "LoadTable" opcodes,
* or by acpi_load_table() API, the "Reload" parameter is set. * or by acpi_load_table() API, the "Reload" parameter is set.
* After sucessfully returning from this function, table is * After successfully returning from this function, table is
* "INSTALLED" but not "VALIDATED". * "INSTALLED" but not "VALIDATED".
* *
******************************************************************************/ ******************************************************************************/
......
...@@ -53,7 +53,7 @@ void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 base_offset) ...@@ -53,7 +53,7 @@ void acpi_ut_dump_buffer(u8 *buffer, u32 count, u32 display, u32 base_offset)
/* Print current offset */ /* Print current offset */
acpi_os_printf("%6.4X: ", (base_offset + i)); acpi_os_printf("%8.4X: ", (base_offset + i));
/* Print 16 hex chars */ /* Print 16 hex chars */
...@@ -219,7 +219,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file, ...@@ -219,7 +219,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
/* Print current offset */ /* Print current offset */
fprintf(file, "%6.4X: ", (base_offset + i)); fprintf(file, "%8.4X: ", (base_offset + i));
/* Print 16 hex chars */ /* Print 16 hex chars */
......
...@@ -52,7 +52,7 @@ acpi_status acpi_ut_mutex_initialize(void) ...@@ -52,7 +52,7 @@ acpi_status acpi_ut_mutex_initialize(void)
return_ACPI_STATUS (status); return_ACPI_STATUS (status);
} }
status = acpi_os_create_lock (&acpi_gbl_hardware_lock); status = acpi_os_create_raw_lock(&acpi_gbl_hardware_lock);
if (ACPI_FAILURE (status)) { if (ACPI_FAILURE (status)) {
return_ACPI_STATUS (status); return_ACPI_STATUS (status);
} }
...@@ -109,7 +109,7 @@ void acpi_ut_mutex_terminate(void) ...@@ -109,7 +109,7 @@ void acpi_ut_mutex_terminate(void)
/* Delete the spinlocks */ /* Delete the spinlocks */
acpi_os_delete_lock(acpi_gbl_gpe_lock); acpi_os_delete_lock(acpi_gbl_gpe_lock);
acpi_os_delete_lock(acpi_gbl_hardware_lock); acpi_os_delete_raw_lock(acpi_gbl_hardware_lock);
acpi_os_delete_lock(acpi_gbl_reference_count_lock); acpi_os_delete_lock(acpi_gbl_reference_count_lock);
/* Delete the reader/writer lock */ /* Delete the reader/writer lock */
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
*****************************************************************************/ *****************************************************************************/
#include <acpi/acpi.h> #include <acpi/acpi.h>
#include <linux/kmemleak.h>
#include "accommon.h" #include "accommon.h"
#include "acnamesp.h" #include "acnamesp.h"
...@@ -70,6 +71,7 @@ union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char ...@@ -70,6 +71,7 @@ union acpi_operand_object *acpi_ut_create_internal_object_dbg(const char
if (!object) { if (!object) {
return_PTR(NULL); return_PTR(NULL);
} }
kmemleak_not_leak(object);
switch (type) { switch (type) {
case ACPI_TYPE_REGION: case ACPI_TYPE_REGION:
......
...@@ -470,6 +470,7 @@ int vsnprintf(char *string, acpi_size size, const char *format, va_list args) ...@@ -470,6 +470,7 @@ int vsnprintf(char *string, acpi_size size, const char *format, va_list args)
case 'X': case 'X':
type |= ACPI_FORMAT_UPPER; type |= ACPI_FORMAT_UPPER;
/* FALLTHROUGH */
case 'x': case 'x':
......
...@@ -141,7 +141,7 @@ void acpi_ut_repair_name(char *name) ...@@ -141,7 +141,7 @@ void acpi_ut_repair_name(char *name)
* Special case for the root node. This can happen if we get an * Special case for the root node. This can happen if we get an
* error during the execution of module-level code. * error during the execution of module-level code.
*/ */
if (ACPI_COMPARE_NAME(name, "\\___")) { if (ACPI_COMPARE_NAME(name, ACPI_ROOT_PATHNAME)) {
return; return;
} }
......
...@@ -74,6 +74,8 @@ static async_cookie_t async_cookie; ...@@ -74,6 +74,8 @@ static async_cookie_t async_cookie;
static bool battery_driver_registered; static bool battery_driver_registered;
static int battery_bix_broken_package; static int battery_bix_broken_package;
static int battery_notification_delay_ms; static int battery_notification_delay_ms;
static int battery_ac_is_broken;
static int battery_check_pmic = 1;
static unsigned int cache_time = 1000; static unsigned int cache_time = 1000;
module_param(cache_time, uint, 0644); module_param(cache_time, uint, 0644);
MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
...@@ -207,6 +209,20 @@ static bool acpi_battery_is_degraded(struct acpi_battery *battery) ...@@ -207,6 +209,20 @@ static bool acpi_battery_is_degraded(struct acpi_battery *battery)
battery->full_charge_capacity < battery->design_capacity; battery->full_charge_capacity < battery->design_capacity;
} }
static int acpi_battery_handle_discharging(struct acpi_battery *battery)
{
/*
* Some devices wrongly report discharging if the battery's charge level
* was above the device's start charging threshold atm the AC adapter
* was plugged in and the device thus did not start a new charge cycle.
*/
if ((battery_ac_is_broken || power_supply_is_system_supplied()) &&
battery->rate_now == 0)
return POWER_SUPPLY_STATUS_NOT_CHARGING;
return POWER_SUPPLY_STATUS_DISCHARGING;
}
static int acpi_battery_get_property(struct power_supply *psy, static int acpi_battery_get_property(struct power_supply *psy,
enum power_supply_property psp, enum power_supply_property psp,
union power_supply_propval *val) union power_supply_propval *val)
...@@ -222,7 +238,7 @@ static int acpi_battery_get_property(struct power_supply *psy, ...@@ -222,7 +238,7 @@ static int acpi_battery_get_property(struct power_supply *psy,
switch (psp) { switch (psp) {
case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_STATUS:
if (battery->state & ACPI_BATTERY_STATE_DISCHARGING) if (battery->state & ACPI_BATTERY_STATE_DISCHARGING)
val->intval = POWER_SUPPLY_STATUS_DISCHARGING; val->intval = acpi_battery_handle_discharging(battery);
else if (battery->state & ACPI_BATTERY_STATE_CHARGING) else if (battery->state & ACPI_BATTERY_STATE_CHARGING)
val->intval = POWER_SUPPLY_STATUS_CHARGING; val->intval = POWER_SUPPLY_STATUS_CHARGING;
else if (acpi_battery_is_charged(battery)) else if (acpi_battery_is_charged(battery))
...@@ -1263,23 +1279,64 @@ battery_notification_delay_quirk(const struct dmi_system_id *d) ...@@ -1263,23 +1279,64 @@ battery_notification_delay_quirk(const struct dmi_system_id *d)
return 0; return 0;
} }
static int __init
battery_ac_is_broken_quirk(const struct dmi_system_id *d)
{
battery_ac_is_broken = 1;
return 0;
}
static int __init
battery_do_not_check_pmic_quirk(const struct dmi_system_id *d)
{
battery_check_pmic = 0;
return 0;
}
static const struct dmi_system_id bat_dmi_table[] __initconst = { static const struct dmi_system_id bat_dmi_table[] __initconst = {
{ {
/* NEC LZ750/LS */
.callback = battery_bix_broken_package_quirk, .callback = battery_bix_broken_package_quirk,
.ident = "NEC LZ750/LS",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "NEC"), DMI_MATCH(DMI_SYS_VENDOR, "NEC"),
DMI_MATCH(DMI_PRODUCT_NAME, "PC-LZ750LS"), DMI_MATCH(DMI_PRODUCT_NAME, "PC-LZ750LS"),
}, },
}, },
{ {
/* Acer Aspire V5-573G */
.callback = battery_notification_delay_quirk, .callback = battery_notification_delay_quirk,
.ident = "Acer Aspire V5-573G",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-573G"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-573G"),
}, },
}, },
{
/* Point of View mobii wintab p800w */
.callback = battery_ac_is_broken_quirk,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
DMI_MATCH(DMI_BIOS_VERSION, "3BAIR1013"),
/* Above matches are too generic, add bios-date match */
DMI_MATCH(DMI_BIOS_DATE, "08/22/2014"),
},
},
{
/* ECS EF20EA */
.callback = battery_do_not_check_pmic_quirk,
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
},
},
{
/* Lenovo Ideapad Miix 320 */
.callback = battery_do_not_check_pmic_quirk,
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "80XF"),
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo MIIX 320-10ICR"),
},
},
{}, {},
}; };
...@@ -1419,16 +1476,18 @@ static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) ...@@ -1419,16 +1476,18 @@ static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
unsigned int i; unsigned int i;
int result; int result;
for (i = 0; i < ARRAY_SIZE(acpi_battery_blacklist); i++)
if (acpi_dev_present(acpi_battery_blacklist[i], "1", -1)) {
pr_info(PREFIX ACPI_BATTERY_DEVICE_NAME
": found native %s PMIC, not loading\n",
acpi_battery_blacklist[i]);
return;
}
dmi_check_system(bat_dmi_table); dmi_check_system(bat_dmi_table);
if (battery_check_pmic) {
for (i = 0; i < ARRAY_SIZE(acpi_battery_blacklist); i++)
if (acpi_dev_present(acpi_battery_blacklist[i], "1", -1)) {
pr_info(PREFIX ACPI_BATTERY_DEVICE_NAME
": found native %s PMIC, not loading\n",
acpi_battery_blacklist[i]);
return;
}
}
#ifdef CONFIG_ACPI_PROCFS_POWER #ifdef CONFIG_ACPI_PROCFS_POWER
acpi_battery_dir = acpi_lock_battery_dir(); acpi_battery_dir = acpi_lock_battery_dir();
if (!acpi_battery_dir) if (!acpi_battery_dir)
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/rwsem.h> #include <linux/rwsem.h>
#include <linux/wait.h> #include <linux/wait.h>
...@@ -49,7 +50,7 @@ struct cppc_pcc_data { ...@@ -49,7 +50,7 @@ struct cppc_pcc_data {
struct mbox_chan *pcc_channel; struct mbox_chan *pcc_channel;
void __iomem *pcc_comm_addr; void __iomem *pcc_comm_addr;
bool pcc_channel_acquired; bool pcc_channel_acquired;
ktime_t deadline; unsigned int deadline_us;
unsigned int pcc_mpar, pcc_mrtt, pcc_nominal; unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */ bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */
...@@ -156,6 +157,9 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, highest_perf); ...@@ -156,6 +157,9 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, highest_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_perf); show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_perf); show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_nonlinear_perf); show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_nonlinear_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_freq);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq);
show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf);
show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time); show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
...@@ -183,6 +187,8 @@ static struct attribute *cppc_attrs[] = { ...@@ -183,6 +187,8 @@ static struct attribute *cppc_attrs[] = {
&lowest_perf.attr, &lowest_perf.attr,
&lowest_nonlinear_perf.attr, &lowest_nonlinear_perf.attr,
&nominal_perf.attr, &nominal_perf.attr,
&nominal_freq.attr,
&lowest_freq.attr,
NULL NULL
}; };
...@@ -193,42 +199,31 @@ static struct kobj_type cppc_ktype = { ...@@ -193,42 +199,31 @@ static struct kobj_type cppc_ktype = {
static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit) static int check_pcc_chan(int pcc_ss_id, bool chk_err_bit)
{ {
int ret = -EIO, status = 0; int ret, status;
struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id]; struct cppc_pcc_data *pcc_ss_data = pcc_data[pcc_ss_id];
struct acpi_pcct_shared_memory __iomem *generic_comm_base = struct acpi_pcct_shared_memory __iomem *generic_comm_base =
pcc_ss_data->pcc_comm_addr; pcc_ss_data->pcc_comm_addr;
ktime_t next_deadline = ktime_add(ktime_get(),
pcc_ss_data->deadline);
if (!pcc_ss_data->platform_owns_pcc) if (!pcc_ss_data->platform_owns_pcc)
return 0; return 0;
/* Retry in case the remote processor was too slow to catch up. */ /*
while (!ktime_after(ktime_get(), next_deadline)) { * Poll PCC status register every 3us(delay_us) for maximum of
/* * deadline_us(timeout_us) until PCC command complete bit is set(cond)
* Per spec, prior to boot the PCC space wil be initialized by */
* platform and should have set the command completion bit when ret = readw_relaxed_poll_timeout(&generic_comm_base->status, status,
* PCC can be used by OSPM status & PCC_CMD_COMPLETE_MASK, 3,
*/ pcc_ss_data->deadline_us);
status = readw_relaxed(&generic_comm_base->status);
if (status & PCC_CMD_COMPLETE_MASK) {
ret = 0;
if (chk_err_bit && (status & PCC_ERROR_MASK))
ret = -EIO;
break;
}
/*
* Reducing the bus traffic in case this loop takes longer than
* a few retries.
*/
udelay(3);
}
if (likely(!ret)) if (likely(!ret)) {
pcc_ss_data->platform_owns_pcc = false; pcc_ss_data->platform_owns_pcc = false;
else if (chk_err_bit && (status & PCC_ERROR_MASK))
pr_err("PCC check channel failed for ss: %d. Status=%x\n", ret = -EIO;
pcc_ss_id, status); }
if (unlikely(ret))
pr_err("PCC check channel failed for ss: %d. ret=%d\n",
pcc_ss_id, ret);
return ret; return ret;
} }
...@@ -580,7 +575,7 @@ static int register_pcc_channel(int pcc_ss_idx) ...@@ -580,7 +575,7 @@ static int register_pcc_channel(int pcc_ss_idx)
* So add an arbitrary amount of wait on top of Nominal. * So add an arbitrary amount of wait on top of Nominal.
*/ */
usecs_lat = NUM_RETRIES * cppc_ss->latency; usecs_lat = NUM_RETRIES * cppc_ss->latency;
pcc_data[pcc_ss_idx]->deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC); pcc_data[pcc_ss_idx]->deadline_us = usecs_lat;
pcc_data[pcc_ss_idx]->pcc_mrtt = cppc_ss->min_turnaround_time; pcc_data[pcc_ss_idx]->pcc_mrtt = cppc_ss->min_turnaround_time;
pcc_data[pcc_ss_idx]->pcc_mpar = cppc_ss->max_access_rate; pcc_data[pcc_ss_idx]->pcc_mpar = cppc_ss->max_access_rate;
pcc_data[pcc_ss_idx]->pcc_nominal = cppc_ss->latency; pcc_data[pcc_ss_idx]->pcc_nominal = cppc_ss->latency;
...@@ -613,7 +608,6 @@ bool __weak cpc_ffh_supported(void) ...@@ -613,7 +608,6 @@ bool __weak cpc_ffh_supported(void)
return false; return false;
} }
/** /**
* pcc_data_alloc() - Allocate the pcc_data memory for pcc subspace * pcc_data_alloc() - Allocate the pcc_data memory for pcc subspace
* *
...@@ -641,6 +635,34 @@ int pcc_data_alloc(int pcc_ss_id) ...@@ -641,6 +635,34 @@ int pcc_data_alloc(int pcc_ss_id)
return 0; return 0;
} }
/* Check if CPPC revision + num_ent combination is supported */
static bool is_cppc_supported(int revision, int num_ent)
{
int expected_num_ent;
switch (revision) {
case CPPC_V2_REV:
expected_num_ent = CPPC_V2_NUM_ENT;
break;
case CPPC_V3_REV:
expected_num_ent = CPPC_V3_NUM_ENT;
break;
default:
pr_debug("Firmware exports unsupported CPPC revision: %d\n",
revision);
return false;
}
if (expected_num_ent != num_ent) {
pr_debug("Firmware exports %d entries. Expected: %d for CPPC rev:%d\n",
num_ent, expected_num_ent, revision);
return false;
}
return true;
}
/* /*
* An example CPC table looks like the following. * An example CPC table looks like the following.
* *
...@@ -731,14 +753,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) ...@@ -731,14 +753,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
cpc_obj->type); cpc_obj->type);
goto out_free; goto out_free;
} }
/* Only support CPPCv2. Bail otherwise. */
if (num_ent != CPPC_NUM_ENT) {
pr_debug("Firmware exports %d entries. Expected: %d\n",
num_ent, CPPC_NUM_ENT);
goto out_free;
}
cpc_ptr->num_entries = num_ent; cpc_ptr->num_entries = num_ent;
/* Second entry should be revision. */ /* Second entry should be revision. */
...@@ -750,12 +764,10 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) ...@@ -750,12 +764,10 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
cpc_obj->type); cpc_obj->type);
goto out_free; goto out_free;
} }
cpc_ptr->version = cpc_rev;
if (cpc_rev != CPPC_REV) { if (!is_cppc_supported(cpc_rev, num_ent))
pr_debug("Firmware exports revision:%d. Expected:%d\n",
cpc_rev, CPPC_REV);
goto out_free; goto out_free;
}
/* Iterate through remaining entries in _CPC */ /* Iterate through remaining entries in _CPC */
for (i = 2; i < num_ent; i++) { for (i = 2; i < num_ent; i++) {
...@@ -808,6 +820,18 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) ...@@ -808,6 +820,18 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
} }
} }
per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id; per_cpu(cpu_pcc_subspace_idx, pr->id) = pcc_subspace_id;
/*
* Initialize the remaining cpc_regs as unsupported.
* Example: In case FW exposes CPPC v2, the below loop will initialize
* LOWEST_FREQ and NOMINAL_FREQ regs as unsupported
*/
for (i = num_ent - 2; i < MAX_CPC_REG_ENT; i++) {
cpc_ptr->cpc_regs[i].type = ACPI_TYPE_INTEGER;
cpc_ptr->cpc_regs[i].cpc_entry.int_value = 0;
}
/* Store CPU Logical ID */ /* Store CPU Logical ID */
cpc_ptr->cpu_id = pr->id; cpc_ptr->cpu_id = pr->id;
...@@ -1037,26 +1061,34 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) ...@@ -1037,26 +1061,34 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
{ {
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
struct cpc_register_resource *highest_reg, *lowest_reg, struct cpc_register_resource *highest_reg, *lowest_reg,
*lowest_non_linear_reg, *nominal_reg; *lowest_non_linear_reg, *nominal_reg,
u64 high, low, nom, min_nonlinear; *low_freq_reg = NULL, *nom_freq_reg = NULL;
u64 high, low, nom, min_nonlinear, low_f = 0, nom_f = 0;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
struct cppc_pcc_data *pcc_ss_data; struct cppc_pcc_data *pcc_ss_data = NULL;
int ret = 0, regs_in_pcc = 0; int ret = 0, regs_in_pcc = 0;
if (!cpc_desc || pcc_ss_id < 0) { if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpunum); pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
return -ENODEV; return -ENODEV;
} }
pcc_ss_data = pcc_data[pcc_ss_id];
highest_reg = &cpc_desc->cpc_regs[HIGHEST_PERF]; highest_reg = &cpc_desc->cpc_regs[HIGHEST_PERF];
lowest_reg = &cpc_desc->cpc_regs[LOWEST_PERF]; lowest_reg = &cpc_desc->cpc_regs[LOWEST_PERF];
lowest_non_linear_reg = &cpc_desc->cpc_regs[LOW_NON_LINEAR_PERF]; lowest_non_linear_reg = &cpc_desc->cpc_regs[LOW_NON_LINEAR_PERF];
nominal_reg = &cpc_desc->cpc_regs[NOMINAL_PERF]; nominal_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
low_freq_reg = &cpc_desc->cpc_regs[LOWEST_FREQ];
nom_freq_reg = &cpc_desc->cpc_regs[NOMINAL_FREQ];
/* Are any of the regs PCC ?*/ /* Are any of the regs PCC ?*/
if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) || if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg)) { CPC_IN_PCC(lowest_non_linear_reg) || CPC_IN_PCC(nominal_reg) ||
CPC_IN_PCC(low_freq_reg) || CPC_IN_PCC(nom_freq_reg)) {
if (pcc_ss_id < 0) {
pr_debug("Invalid pcc_ss_id\n");
return -ENODEV;
}
pcc_ss_data = pcc_data[pcc_ss_id];
regs_in_pcc = 1; regs_in_pcc = 1;
down_write(&pcc_ss_data->pcc_lock); down_write(&pcc_ss_data->pcc_lock);
/* Ring doorbell once to update PCC subspace */ /* Ring doorbell once to update PCC subspace */
...@@ -1081,6 +1113,17 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps) ...@@ -1081,6 +1113,17 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
if (!high || !low || !nom || !min_nonlinear) if (!high || !low || !nom || !min_nonlinear)
ret = -EFAULT; ret = -EFAULT;
/* Read optional lowest and nominal frequencies if present */
if (CPC_SUPPORTED(low_freq_reg))
cpc_read(cpunum, low_freq_reg, &low_f);
if (CPC_SUPPORTED(nom_freq_reg))
cpc_read(cpunum, nom_freq_reg, &nom_f);
perf_caps->lowest_freq = low_f;
perf_caps->nominal_freq = nom_f;
out_err: out_err:
if (regs_in_pcc) if (regs_in_pcc)
up_write(&pcc_ss_data->pcc_lock); up_write(&pcc_ss_data->pcc_lock);
...@@ -1101,16 +1144,15 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) ...@@ -1101,16 +1144,15 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
struct cpc_register_resource *delivered_reg, *reference_reg, struct cpc_register_resource *delivered_reg, *reference_reg,
*ref_perf_reg, *ctr_wrap_reg; *ref_perf_reg, *ctr_wrap_reg;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum); int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpunum);
struct cppc_pcc_data *pcc_ss_data; struct cppc_pcc_data *pcc_ss_data = NULL;
u64 delivered, reference, ref_perf, ctr_wrap_time; u64 delivered, reference, ref_perf, ctr_wrap_time;
int ret = 0, regs_in_pcc = 0; int ret = 0, regs_in_pcc = 0;
if (!cpc_desc || pcc_ss_id < 0) { if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpunum); pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
return -ENODEV; return -ENODEV;
} }
pcc_ss_data = pcc_data[pcc_ss_id];
delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR]; delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR];
reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR]; reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR];
ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF]; ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
...@@ -1126,6 +1168,11 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) ...@@ -1126,6 +1168,11 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
/* Are any of the regs PCC ?*/ /* Are any of the regs PCC ?*/
if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) || if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) ||
CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) { CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) {
if (pcc_ss_id < 0) {
pr_debug("Invalid pcc_ss_id\n");
return -ENODEV;
}
pcc_ss_data = pcc_data[pcc_ss_id];
down_write(&pcc_ss_data->pcc_lock); down_write(&pcc_ss_data->pcc_lock);
regs_in_pcc = 1; regs_in_pcc = 1;
/* Ring doorbell once to update PCC subspace */ /* Ring doorbell once to update PCC subspace */
...@@ -1176,15 +1223,14 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) ...@@ -1176,15 +1223,14 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu); struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpu);
struct cpc_register_resource *desired_reg; struct cpc_register_resource *desired_reg;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu); int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
struct cppc_pcc_data *pcc_ss_data; struct cppc_pcc_data *pcc_ss_data = NULL;
int ret = 0; int ret = 0;
if (!cpc_desc || pcc_ss_id < 0) { if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpu); pr_debug("No CPC descriptor for CPU:%d\n", cpu);
return -ENODEV; return -ENODEV;
} }
pcc_ss_data = pcc_data[pcc_ss_id];
desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
/* /*
...@@ -1195,6 +1241,11 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) ...@@ -1195,6 +1241,11 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
* achieve that goal here * achieve that goal here
*/ */
if (CPC_IN_PCC(desired_reg)) { if (CPC_IN_PCC(desired_reg)) {
if (pcc_ss_id < 0) {
pr_debug("Invalid pcc_ss_id\n");
return -ENODEV;
}
pcc_ss_data = pcc_data[pcc_ss_id];
down_read(&pcc_ss_data->pcc_lock); /* BEGIN Phase-I */ down_read(&pcc_ss_data->pcc_lock); /* BEGIN Phase-I */
if (pcc_ss_data->platform_owns_pcc) { if (pcc_ss_data->platform_owns_pcc) {
ret = check_pcc_chan(pcc_ss_id, false); ret = check_pcc_chan(pcc_ss_id, false);
......
...@@ -8,8 +8,8 @@ void acpi_reboot(void) ...@@ -8,8 +8,8 @@ void acpi_reboot(void)
{ {
struct acpi_generic_address *rr; struct acpi_generic_address *rr;
struct pci_bus *bus0; struct pci_bus *bus0;
u8 reset_value;
unsigned int devfn; unsigned int devfn;
u8 reset_value;
if (acpi_disabled) if (acpi_disabled)
return; return;
...@@ -40,7 +40,7 @@ void acpi_reboot(void) ...@@ -40,7 +40,7 @@ void acpi_reboot(void)
/* Form PCI device/function pair. */ /* Form PCI device/function pair. */
devfn = PCI_DEVFN((rr->address >> 32) & 0xffff, devfn = PCI_DEVFN((rr->address >> 32) & 0xffff,
(rr->address >> 16) & 0xffff); (rr->address >> 16) & 0xffff);
printk(KERN_DEBUG "Resetting with ACPI PCI RESET_REG."); printk(KERN_DEBUG "Resetting with ACPI PCI RESET_REG.\n");
/* Write the value that resets us. */ /* Write the value that resets us. */
pci_bus_write_config_byte(bus0, devfn, pci_bus_write_config_byte(bus0, devfn,
(rr->address & 0xffff), reset_value); (rr->address & 0xffff), reset_value);
......
...@@ -222,7 +222,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) ...@@ -222,7 +222,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
* acpi_parse_entries_array - for each proc_num find a suitable subtable * acpi_parse_entries_array - for each proc_num find a suitable subtable
* *
* @id: table id (for debugging purposes) * @id: table id (for debugging purposes)
* @table_size: single entry size * @table_size: size of the root table
* @table_header: where does the table start? * @table_header: where does the table start?
* @proc: array of acpi_subtable_proc struct containing entry id * @proc: array of acpi_subtable_proc struct containing entry id
* and associated handler with it * and associated handler with it
...@@ -233,6 +233,11 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) ...@@ -233,6 +233,11 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
* on it. Assumption is that there's only single handler for particular * on it. Assumption is that there's only single handler for particular
* entry id. * entry id.
* *
* The table_size is not the size of the complete ACPI table (the length
* field in the header struct), but only the size of the root table; i.e.,
* the offset from the very first byte of the complete ACPI table, to the
* first byte of the very first subtable.
*
* On success returns sum of all matching entries for all proc handlers. * On success returns sum of all matching entries for all proc handlers.
* Otherwise, -ENODEV or -EINVAL is returned. * Otherwise, -ENODEV or -EINVAL is returned.
*/ */
...@@ -400,7 +405,7 @@ int __init acpi_table_parse(char *id, acpi_tbl_table_handler handler) ...@@ -400,7 +405,7 @@ int __init acpi_table_parse(char *id, acpi_tbl_table_handler handler)
return -ENODEV; return -ENODEV;
} }
/* /*
* The BIOS is supposed to supply a single APIC/MADT, * The BIOS is supposed to supply a single APIC/MADT,
* but some report two. Provide a knob to use either. * but some report two. Provide a knob to use either.
* (don't you wish instance 0 and 1 were not the same?) * (don't you wish instance 0 and 1 were not the same?)
......
obj-$(CONFIG_PMC_ATOM) += clk-pmc-atom.o
obj-$(CONFIG_X86_AMD_PLATFORM_DEVICE) += clk-st.o
clk-x86-lpss-objs := clk-lpt.o clk-x86-lpss-objs := clk-lpt.o
obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o
obj-$(CONFIG_PMC_ATOM) += clk-pmc-atom.o
// SPDX-License-Identifier: MIT
/*
* clock framework for AMD Stoney based clocks
*
* Copyright 2018 Advanced Micro Devices, Inc.
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/platform_data/clk-st.h>
#include <linux/platform_device.h>
/* Clock Driving Strength 2 register */
#define CLKDRVSTR2 0x28
/* Clock Control 1 register */
#define MISCCLKCNTL1 0x40
/* Auxiliary clock1 enable bit */
#define OSCCLKENB 2
/* 25Mhz auxiliary output clock freq bit */
#define OSCOUT1CLK25MHZ 16
#define ST_CLK_48M 0
#define ST_CLK_25M 1
#define ST_CLK_MUX 2
#define ST_CLK_GATE 3
#define ST_MAX_CLKS 4
static const char * const clk_oscout1_parents[] = { "clk48MHz", "clk25MHz" };
static struct clk_hw *hws[ST_MAX_CLKS];
static int st_clk_probe(struct platform_device *pdev)
{
struct st_clk_data *st_data;
st_data = dev_get_platdata(&pdev->dev);
if (!st_data || !st_data->base)
return -EINVAL;
hws[ST_CLK_48M] = clk_hw_register_fixed_rate(NULL, "clk48MHz", NULL, 0,
48000000);
hws[ST_CLK_25M] = clk_hw_register_fixed_rate(NULL, "clk25MHz", NULL, 0,
25000000);
hws[ST_CLK_MUX] = clk_hw_register_mux(NULL, "oscout1_mux",
clk_oscout1_parents, ARRAY_SIZE(clk_oscout1_parents),
0, st_data->base + CLKDRVSTR2, OSCOUT1CLK25MHZ, 3, 0, NULL);
clk_set_parent(hws[ST_CLK_MUX]->clk, hws[ST_CLK_25M]->clk);
hws[ST_CLK_GATE] = clk_hw_register_gate(NULL, "oscout1", "oscout1_mux",
0, st_data->base + MISCCLKCNTL1, OSCCLKENB,
CLK_GATE_SET_TO_DISABLE, NULL);
clk_hw_register_clkdev(hws[ST_CLK_GATE], "oscout1", NULL);
return 0;
}
static int st_clk_remove(struct platform_device *pdev)
{
int i;
for (i = 0; i < ST_MAX_CLKS; i++)
clk_hw_unregister(hws[i]);
return 0;
}
static struct platform_driver st_clk_driver = {
.driver = {
.name = "clk-st",
.suppress_bind_attrs = true,
},
.probe = st_clk_probe,
.remove = st_clk_remove,
};
builtin_platform_driver(st_clk_driver);
...@@ -42,9 +42,6 @@ ...@@ -42,9 +42,6 @@
*/ */
static struct cppc_cpudata **all_cpu_data; static struct cppc_cpudata **all_cpu_data;
/* Capture the max KHz from DMI */
static u64 cppc_dmi_max_khz;
/* Callback function used to retrieve the max frequency from DMI */ /* Callback function used to retrieve the max frequency from DMI */
static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
{ {
...@@ -75,6 +72,64 @@ static u64 cppc_get_dmi_max_khz(void) ...@@ -75,6 +72,64 @@ static u64 cppc_get_dmi_max_khz(void)
return (1000 * mhz); return (1000 * mhz);
} }
/*
* If CPPC lowest_freq and nominal_freq registers are exposed then we can
* use them to convert perf to freq and vice versa
*
* If the perf/freq point lies between Nominal and Lowest, we can treat
* (Low perf, Low freq) and (Nom Perf, Nom freq) as 2D co-ordinates of a line
* and extrapolate the rest
* For perf/freq > Nominal, we use the ratio perf:freq at Nominal for conversion
*/
static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu,
unsigned int perf)
{
static u64 max_khz;
struct cppc_perf_caps *caps = &cpu->perf_caps;
u64 mul, div;
if (caps->lowest_freq && caps->nominal_freq) {
if (perf >= caps->nominal_perf) {
mul = caps->nominal_freq;
div = caps->nominal_perf;
} else {
mul = caps->nominal_freq - caps->lowest_freq;
div = caps->nominal_perf - caps->lowest_perf;
}
} else {
if (!max_khz)
max_khz = cppc_get_dmi_max_khz();
mul = max_khz;
div = cpu->perf_caps.highest_perf;
}
return (u64)perf * mul / div;
}
static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu,
unsigned int freq)
{
static u64 max_khz;
struct cppc_perf_caps *caps = &cpu->perf_caps;
u64 mul, div;
if (caps->lowest_freq && caps->nominal_freq) {
if (freq >= caps->nominal_freq) {
mul = caps->nominal_perf;
div = caps->nominal_freq;
} else {
mul = caps->lowest_perf;
div = caps->lowest_freq;
}
} else {
if (!max_khz)
max_khz = cppc_get_dmi_max_khz();
mul = cpu->perf_caps.highest_perf;
div = max_khz;
}
return (u64)freq * mul / div;
}
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation) unsigned int relation)
...@@ -86,7 +141,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, ...@@ -86,7 +141,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
cpu = all_cpu_data[policy->cpu]; cpu = all_cpu_data[policy->cpu];
desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz; desired_perf = cppc_cpufreq_khz_to_perf(cpu, target_freq);
/* Return if it is exactly the same perf */ /* Return if it is exactly the same perf */
if (desired_perf == cpu->perf_ctrls.desired_perf) if (desired_perf == cpu->perf_ctrls.desired_perf)
return ret; return ret;
...@@ -186,24 +241,24 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -186,24 +241,24 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
return ret; return ret;
} }
cppc_dmi_max_khz = cppc_get_dmi_max_khz(); /* Convert the lowest and nominal freq from MHz to KHz */
cpu->perf_caps.lowest_freq *= 1000;
cpu->perf_caps.nominal_freq *= 1000;
/* /*
* Set min to lowest nonlinear perf to avoid any efficiency penalty (see * Set min to lowest nonlinear perf to avoid any efficiency penalty (see
* Section 8.4.7.1.1.5 of ACPI 6.1 spec) * Section 8.4.7.1.1.5 of ACPI 6.1 spec)
*/ */
policy->min = cpu->perf_caps.lowest_nonlinear_perf * cppc_dmi_max_khz / policy->min = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_nonlinear_perf);
cpu->perf_caps.highest_perf; policy->max = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf);
policy->max = cppc_dmi_max_khz;
/* /*
* Set cpuinfo.min_freq to Lowest to make the full range of performance * Set cpuinfo.min_freq to Lowest to make the full range of performance
* available if userspace wants to use any perf between lowest & lowest * available if userspace wants to use any perf between lowest & lowest
* nonlinear perf * nonlinear perf
*/ */
policy->cpuinfo.min_freq = cpu->perf_caps.lowest_perf * cppc_dmi_max_khz / policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_perf);
cpu->perf_caps.highest_perf; policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf);
policy->cpuinfo.max_freq = cppc_dmi_max_khz;
policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu_num); policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu_num);
policy->shared_type = cpu->shared_type; policy->shared_type = cpu->shared_type;
...@@ -229,7 +284,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -229,7 +284,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
cpu->cur_policy = policy; cpu->cur_policy = policy;
/* Set policy->cur to max now. The governors will adjust later. */ /* Set policy->cur to max now. The governors will adjust later. */
policy->cur = cppc_dmi_max_khz; policy->cur = cppc_cpufreq_perf_to_khz(cpu,
cpu->perf_caps.highest_perf);
cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf; cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);
......
...@@ -373,33 +373,24 @@ static const struct mbox_chan_ops pcc_chan_ops = { ...@@ -373,33 +373,24 @@ static const struct mbox_chan_ops pcc_chan_ops = {
}; };
/** /**
* parse_pcc_subspace - Parse the PCC table and verify PCC subspace * parse_pcc_subspaces -- Count PCC subspaces defined
* entries. There should be one entry per PCC client.
* @header: Pointer to the ACPI subtable header under the PCCT. * @header: Pointer to the ACPI subtable header under the PCCT.
* @end: End of subtable entry. * @end: End of subtable entry.
* *
* Return: 0 for Success, else errno. * Return: If we find a PCC subspace entry of a valid type, return 0.
* Otherwise, return -EINVAL.
* *
* This gets called for each entry in the PCC table. * This gets called for each entry in the PCC table.
*/ */
static int parse_pcc_subspace(struct acpi_subtable_header *header, static int parse_pcc_subspace(struct acpi_subtable_header *header,
const unsigned long end) const unsigned long end)
{ {
struct acpi_pcct_hw_reduced *pcct_ss; struct acpi_pcct_subspace *ss = (struct acpi_pcct_subspace *) header;
if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) {
pcct_ss = (struct acpi_pcct_hw_reduced *) header;
if ((pcct_ss->header.type != if (ss->header.type < ACPI_PCCT_TYPE_RESERVED)
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) return 0;
&& (pcct_ss->header.type !=
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2)) {
pr_err("Incorrect PCC Subspace type detected\n");
return -EINVAL;
}
}
return 0; return -EINVAL;
} }
/** /**
...@@ -449,8 +440,8 @@ static int __init acpi_pcc_probe(void) ...@@ -449,8 +440,8 @@ static int __init acpi_pcc_probe(void)
struct acpi_table_header *pcct_tbl; struct acpi_table_header *pcct_tbl;
struct acpi_subtable_header *pcct_entry; struct acpi_subtable_header *pcct_entry;
struct acpi_table_pcct *acpi_pcct_tbl; struct acpi_table_pcct *acpi_pcct_tbl;
struct acpi_subtable_proc proc[ACPI_PCCT_TYPE_RESERVED];
int count, i, rc; int count, i, rc;
int sum = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
/* Search for PCCT */ /* Search for PCCT */
...@@ -459,43 +450,41 @@ static int __init acpi_pcc_probe(void) ...@@ -459,43 +450,41 @@ static int __init acpi_pcc_probe(void)
if (ACPI_FAILURE(status) || !pcct_tbl) if (ACPI_FAILURE(status) || !pcct_tbl)
return -ENODEV; return -ENODEV;
count = acpi_table_parse_entries(ACPI_SIG_PCCT, /* Set up the subtable handlers */
sizeof(struct acpi_table_pcct), for (i = ACPI_PCCT_TYPE_GENERIC_SUBSPACE;
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE, i < ACPI_PCCT_TYPE_RESERVED; i++) {
parse_pcc_subspace, MAX_PCC_SUBSPACES); proc[i].id = i;
sum += (count > 0) ? count : 0; proc[i].count = 0;
proc[i].handler = parse_pcc_subspace;
count = acpi_table_parse_entries(ACPI_SIG_PCCT, }
sizeof(struct acpi_table_pcct),
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2,
parse_pcc_subspace, MAX_PCC_SUBSPACES);
sum += (count > 0) ? count : 0;
if (sum == 0 || sum >= MAX_PCC_SUBSPACES) { count = acpi_table_parse_entries_array(ACPI_SIG_PCCT,
pr_err("Error parsing PCC subspaces from PCCT\n"); sizeof(struct acpi_table_pcct), proc,
ACPI_PCCT_TYPE_RESERVED, MAX_PCC_SUBSPACES);
if (count == 0 || count > MAX_PCC_SUBSPACES) {
pr_warn("Invalid PCCT: %d PCC subspaces\n", count);
return -EINVAL; return -EINVAL;
} }
pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) * pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) * count, GFP_KERNEL);
sum, GFP_KERNEL);
if (!pcc_mbox_channels) { if (!pcc_mbox_channels) {
pr_err("Could not allocate space for PCC mbox channels\n"); pr_err("Could not allocate space for PCC mbox channels\n");
return -ENOMEM; return -ENOMEM;
} }
pcc_doorbell_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL); pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_vaddr) { if (!pcc_doorbell_vaddr) {
rc = -ENOMEM; rc = -ENOMEM;
goto err_free_mbox; goto err_free_mbox;
} }
pcc_doorbell_ack_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL); pcc_doorbell_ack_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_ack_vaddr) { if (!pcc_doorbell_ack_vaddr) {
rc = -ENOMEM; rc = -ENOMEM;
goto err_free_db_vaddr; goto err_free_db_vaddr;
} }
pcc_doorbell_irq = kcalloc(sum, sizeof(int), GFP_KERNEL); pcc_doorbell_irq = kcalloc(count, sizeof(int), GFP_KERNEL);
if (!pcc_doorbell_irq) { if (!pcc_doorbell_irq) {
rc = -ENOMEM; rc = -ENOMEM;
goto err_free_db_ack_vaddr; goto err_free_db_ack_vaddr;
...@@ -509,18 +498,24 @@ static int __init acpi_pcc_probe(void) ...@@ -509,18 +498,24 @@ static int __init acpi_pcc_probe(void)
if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL) if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL)
pcc_mbox_ctrl.txdone_irq = true; pcc_mbox_ctrl.txdone_irq = true;
for (i = 0; i < sum; i++) { for (i = 0; i < count; i++) {
struct acpi_generic_address *db_reg; struct acpi_generic_address *db_reg;
struct acpi_pcct_hw_reduced *pcct_ss; struct acpi_pcct_subspace *pcct_ss;
pcc_mbox_channels[i].con_priv = pcct_entry; pcc_mbox_channels[i].con_priv = pcct_entry;
pcct_ss = (struct acpi_pcct_hw_reduced *) pcct_entry; if (pcct_entry->type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE ||
pcct_entry->type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced *pcct_hrss;
pcct_hrss = (struct acpi_pcct_hw_reduced *) pcct_entry;
if (pcc_mbox_ctrl.txdone_irq) { if (pcc_mbox_ctrl.txdone_irq) {
rc = pcc_parse_subspace_irq(i, pcct_ss); rc = pcc_parse_subspace_irq(i, pcct_hrss);
if (rc < 0) if (rc < 0)
goto err; goto err;
}
} }
pcct_ss = (struct acpi_pcct_subspace *) pcct_entry;
/* If doorbell is in system memory cache the virt address */ /* If doorbell is in system memory cache the virt address */
db_reg = &pcct_ss->doorbell_register; db_reg = &pcct_ss->doorbell_register;
...@@ -531,7 +526,7 @@ static int __init acpi_pcc_probe(void) ...@@ -531,7 +526,7 @@ static int __init acpi_pcc_probe(void)
((unsigned long) pcct_entry + pcct_entry->length); ((unsigned long) pcct_entry + pcct_entry->length);
} }
pcc_mbox_ctrl.num_chans = sum; pcc_mbox_ctrl.num_chans = count;
pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans); pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans);
......
...@@ -49,11 +49,14 @@ ...@@ -49,11 +49,14 @@
/* Definitions of the predefined namespace names */ /* Definitions of the predefined namespace names */
#define ACPI_UNKNOWN_NAME (u32) 0x3F3F3F3F /* Unknown name is "????" */ #define ACPI_UNKNOWN_NAME (u32) 0x3F3F3F3F /* Unknown name is "????" */
#define ACPI_ROOT_NAME (u32) 0x5F5F5F5C /* Root name is "\___" */
#define ACPI_PREFIX_MIXED (u32) 0x69706341 /* "Acpi" */ #define ACPI_PREFIX_MIXED (u32) 0x69706341 /* "Acpi" */
#define ACPI_PREFIX_LOWER (u32) 0x69706361 /* "acpi" */ #define ACPI_PREFIX_LOWER (u32) 0x69706361 /* "acpi" */
/* Root name stuff */
#define ACPI_ROOT_NAME (u32) 0x5F5F5F5C /* Root name is "\___" */
#define ACPI_ROOT_PATHNAME "\\___"
#define ACPI_NAMESPACE_ROOT "Namespace Root"
#define ACPI_NS_ROOT_PATH "\\" #define ACPI_NS_ROOT_PATH "\\"
#endif /* __ACNAMES_H__ */ #endif /* __ACNAMES_H__ */
...@@ -97,6 +97,27 @@ acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock handle); ...@@ -97,6 +97,27 @@ acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock handle);
void acpi_os_release_lock(acpi_spinlock handle, acpi_cpu_flags flags); void acpi_os_release_lock(acpi_spinlock handle, acpi_cpu_flags flags);
#endif #endif
/*
* RAW spinlock primitives. If the OS does not provide them, fallback to
* spinlock primitives
*/
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_create_raw_lock
# define acpi_os_create_raw_lock(out_handle) acpi_os_create_lock(out_handle)
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_delete_raw_lock
# define acpi_os_delete_raw_lock(handle) acpi_os_delete_lock(handle)
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_acquire_raw_lock
# define acpi_os_acquire_raw_lock(handle) acpi_os_acquire_lock(handle)
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_release_raw_lock
# define acpi_os_release_raw_lock(handle, flags) \
acpi_os_release_lock(handle, flags)
#endif
/* /*
* Semaphore primitives * Semaphore primitives
*/ */
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
/* Current ACPICA subsystem version in YYYYMMDD format */ /* Current ACPICA subsystem version in YYYYMMDD format */
#define ACPI_CA_VERSION 0x20180313 #define ACPI_CA_VERSION 0x20180508
#include <acpi/acconfig.h> #include <acpi/acconfig.h>
#include <acpi/actypes.h> #include <acpi/actypes.h>
......
...@@ -245,6 +245,10 @@ typedef u64 acpi_physical_address; ...@@ -245,6 +245,10 @@ typedef u64 acpi_physical_address;
#define acpi_spinlock void * #define acpi_spinlock void *
#endif #endif
#ifndef acpi_raw_spinlock
#define acpi_raw_spinlock acpi_spinlock
#endif
#ifndef acpi_semaphore #ifndef acpi_semaphore
#define acpi_semaphore void * #define acpi_semaphore void *
#endif #endif
......
...@@ -20,14 +20,16 @@ ...@@ -20,14 +20,16 @@
#include <acpi/pcc.h> #include <acpi/pcc.h>
#include <acpi/processor.h> #include <acpi/processor.h>
/* Only support CPPCv2 for now. */ /* Support CPPCv2 and CPPCv3 */
#define CPPC_NUM_ENT 21 #define CPPC_V2_REV 2
#define CPPC_REV 2 #define CPPC_V3_REV 3
#define CPPC_V2_NUM_ENT 21
#define CPPC_V3_NUM_ENT 23
#define PCC_CMD_COMPLETE_MASK (1 << 0) #define PCC_CMD_COMPLETE_MASK (1 << 0)
#define PCC_ERROR_MASK (1 << 2) #define PCC_ERROR_MASK (1 << 2)
#define MAX_CPC_REG_ENT 19 #define MAX_CPC_REG_ENT 21
/* CPPC specific PCC commands. */ /* CPPC specific PCC commands. */
#define CMD_READ 0 #define CMD_READ 0
...@@ -91,6 +93,8 @@ enum cppc_regs { ...@@ -91,6 +93,8 @@ enum cppc_regs {
AUTO_ACT_WINDOW, AUTO_ACT_WINDOW,
ENERGY_PERF, ENERGY_PERF,
REFERENCE_PERF, REFERENCE_PERF,
LOWEST_FREQ,
NOMINAL_FREQ,
}; };
/* /*
...@@ -104,6 +108,8 @@ struct cppc_perf_caps { ...@@ -104,6 +108,8 @@ struct cppc_perf_caps {
u32 nominal_perf; u32 nominal_perf;
u32 lowest_perf; u32 lowest_perf;
u32 lowest_nonlinear_perf; u32 lowest_nonlinear_perf;
u32 lowest_freq;
u32 nominal_freq;
}; };
struct cppc_perf_ctrls { struct cppc_perf_ctrls {
......
...@@ -102,6 +102,7 @@ ...@@ -102,6 +102,7 @@
#define acpi_cache_t struct kmem_cache #define acpi_cache_t struct kmem_cache
#define acpi_spinlock spinlock_t * #define acpi_spinlock spinlock_t *
#define acpi_raw_spinlock raw_spinlock_t *
#define acpi_cpu_flags unsigned long #define acpi_cpu_flags unsigned long
/* Use native linux version of acpi_os_allocate_zeroed */ /* Use native linux version of acpi_os_allocate_zeroed */
...@@ -119,6 +120,10 @@ ...@@ -119,6 +120,10 @@
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_acquire_object #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_acquire_object
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_thread_id #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_thread_id
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_create_lock #define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_create_lock
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_create_raw_lock
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_delete_raw_lock
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_acquire_raw_lock
#define ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_release_raw_lock
/* /*
* OSL interfaces used by debugger/disassembler * OSL interfaces used by debugger/disassembler
......
...@@ -90,6 +90,36 @@ static inline acpi_thread_id acpi_os_get_thread_id(void) ...@@ -90,6 +90,36 @@ static inline acpi_thread_id acpi_os_get_thread_id(void)
lock ? AE_OK : AE_NO_MEMORY; \ lock ? AE_OK : AE_NO_MEMORY; \
}) })
#define acpi_os_create_raw_lock(__handle) \
({ \
raw_spinlock_t *lock = ACPI_ALLOCATE(sizeof(*lock)); \
if (lock) { \
*(__handle) = lock; \
raw_spin_lock_init(*(__handle)); \
} \
lock ? AE_OK : AE_NO_MEMORY; \
})
static inline acpi_cpu_flags acpi_os_acquire_raw_lock(acpi_raw_spinlock lockp)
{
acpi_cpu_flags flags;
raw_spin_lock_irqsave(lockp, flags);
return flags;
}
static inline void acpi_os_release_raw_lock(acpi_raw_spinlock lockp,
acpi_cpu_flags flags)
{
raw_spin_unlock_irqrestore(lockp, flags);
}
static inline void acpi_os_delete_raw_lock(acpi_raw_spinlock handle)
{
ACPI_FREE(handle);
}
static inline u8 acpi_os_readable(void *pointer, acpi_size length) static inline u8 acpi_os_readable(void *pointer, acpi_size length)
{ {
return TRUE; return TRUE;
......
...@@ -578,6 +578,7 @@ int acpi_match_platform_list(const struct acpi_platform_list *plat); ...@@ -578,6 +578,7 @@ int acpi_match_platform_list(const struct acpi_platform_list *plat);
extern void acpi_early_init(void); extern void acpi_early_init(void);
extern void acpi_subsystem_init(void); extern void acpi_subsystem_init(void);
extern void arch_post_acpi_subsys_init(void);
extern int acpi_nvs_register(__u64 start, __u64 size); extern int acpi_nvs_register(__u64 start, __u64 size);
......
/* SPDX-License-Identifier: MIT */
/*
* clock framework for AMD Stoney based clock
*
* Copyright 2018 Advanced Micro Devices, Inc.
*/
#ifndef __CLK_ST_H
#define __CLK_ST_H
#include <linux/compiler.h>
struct st_clk_data {
void __iomem *base;
};
#endif /* __CLK_ST_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment