@@ -21,6 +21,7 @@
#include <asm/runlatch.h>
#include <asm/idle.h>
#include <asm/plpar_wrappers.h>
+#include <asm/rtas.h>
static struct cpuidle_driver pseries_idle_driver = {
.name = "pseries_idle",
@@ -86,9 +87,120 @@ static void check_and_cede_processor(void)
}
}
-#define NR_CEDE_STATES 1 /* CEDE with latency-hint 0 */
+struct xcede_latency_records {
+ u8 latency_hint;
+ u64 wakeup_latency_tb_ticks;
+ u8 responsive_to_irqs;
+};
+
+/*
+ * XCEDE : Extended CEDE states discovered through the
+ * "ibm,get-systems-parameter" rtas-call with the token
+ * CEDE_LATENCY_TOKEN
+ */
+#define MAX_XCEDE_STATES 4
+#define XCEDE_LATENCY_RECORD_SIZE 10
+#define XCEDE_LATENCY_PARAM_MAX_LENGTH (2 + 2 + \
+ (MAX_XCEDE_STATES * XCEDE_LATENCY_RECORD_SIZE))
+
+#define CEDE_LATENCY_TOKEN 45
+
+#define NR_CEDE_STATES (MAX_XCEDE_STATES + 1) /* CEDE with latency-hint 0 */
#define NR_DEDICATED_STATES (NR_CEDE_STATES + 1) /* Includes snooze */
+struct xcede_latency_records xcede_records[MAX_XCEDE_STATES];
+unsigned int nr_xcede_records;
+char xcede_parameters[XCEDE_LATENCY_PARAM_MAX_LENGTH];
+
+static int parse_cede_parameters(void)
+{
+ int ret = -1, i;
+ u16 payload_length;
+ u8 xcede_record_size;
+ u32 total_xcede_records_size;
+ char *payload;
+
+ memset(xcede_parameters, 0, XCEDE_LATENCY_PARAM_MAX_LENGTH);
+
+ ret = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1,
+ NULL, CEDE_LATENCY_TOKEN, __pa(xcede_parameters),
+ XCEDE_LATENCY_PARAM_MAX_LENGTH);
+
+ if (ret) {
+ pr_err("xcede: Error parsing CEDE_LATENCY_TOKEN\n");
+ return ret;
+ }
+
+ payload_length = be16_to_cpu(*(__be16 *)(&xcede_parameters[0]));
+ payload = &xcede_parameters[2];
+
+ /*
+ * If the platform supports the cede latency settings
+ * information system parameter it must provide the following
+ * information in the NULL terminated parameter string:
+ *
+ * a. The first byte is the length āNā of each cede
+ * latency setting record minus one (zero indicates a length
+ * of 1 byte).
+ *
+ * b. For each supported cede latency setting a cede latency
+ * setting record consisting of the first āNā bytes as per
+ * the following table.
+ *
+ * -----------------------------
+ * | Field | Field |
+ * | Name | Length |
+ * -----------------------------
+ * | Cede Latency | 1 Byte |
+ * | Specifier Value | |
+ * -----------------------------
+ * | Maximum wakeup | |
+ * | latency in | 8 Bytes|
+ * | tb-ticks | |
+ * -----------------------------
+ * | Responsive to | |
+ * | external | 1 Byte |
+ * | interrupts | |
+ * -----------------------------
+ *
+ * This version has cede latency record size = 10.
+ */
+ xcede_record_size = (u8)payload[0] + 1;
+
+ if (xcede_record_size != XCEDE_LATENCY_RECORD_SIZE) {
+ pr_err("xcede : Expected record-size %d. Observed size %d.\n",
+ XCEDE_LATENCY_RECORD_SIZE, xcede_record_size);
+ return -EINVAL;
+ }
+
+ pr_info("xcede : xcede_record_size = %d\n", xcede_record_size);
+
+ /*
+ * Since the payload_length includes the last NULL byte and
+ * the xcede_record_size, the remaining bytes correspond to
+ * array of all cede_latency settings.
+ */
+ total_xcede_records_size = payload_length - 2;
+ nr_xcede_records = total_xcede_records_size / xcede_record_size;
+
+ payload++;
+ for (i = 0; i < nr_xcede_records; i++) {
+ struct xcede_latency_records *record = &xcede_records[i];
+
+ record->latency_hint = (u8)payload[0];
+ record->wakeup_latency_tb_ticks =
+ be64_to_cpu(*(__be64 *)(&payload[1]));
+ record->responsive_to_irqs = (u8)payload[9];
+ payload += xcede_record_size;
+ pr_info("xcede : Record %d : hint = %u, latency =0x%llx tb-ticks, Wake-on-irq = %u\n",
+ i, record->latency_hint,
+ record->wakeup_latency_tb_ticks,
+ record->responsive_to_irqs);
+ }
+
+ return 0;
+}
+
u8 cede_latency_hint[NR_DEDICATED_STATES];
static int dedicated_cede_loop(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
@@ -219,6 +331,19 @@ static int pseries_cpuidle_driver_init(void)
return 0;
}
+static int add_pseries_idle_states(void)
+{
+ int nr_states = 2; /* By default we have snooze, CEDE */
+
+ if (parse_cede_parameters())
+ return nr_states;
+
+ pr_info("cpuidle : Skipping the %d Extended CEDE idle states\n",
+ nr_xcede_records);
+
+ return nr_states;
+}
+
/*
* pseries_idle_probe()
* Choose state table for shared versus dedicated partition
@@ -241,7 +366,7 @@ static int pseries_idle_probe(void)
max_idle_state = ARRAY_SIZE(shared_states);
} else {
cpuidle_state_table = dedicated_states;
- max_idle_state = ARRAY_SIZE(dedicated_states);
+ max_idle_state = add_pseries_idle_states();
}
} else
return -ENODEV;