aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Harkin <ryan.harkin@linaro.org>2017-07-21 12:49:32 +0100
committerRyan Harkin <ryan.harkin@linaro.org>2017-07-21 12:49:32 +0100
commitb50e0fc7e8354127bde191a0a769938df86149e3 (patch)
tree7a1e653f280a61ad7700dbe575054f04e1fbe238
parent4475a4101b94fd6f710dd45230f80faa755047c6 (diff)
parent7a7765ba87aa6025416f131b923c5f635d2691cb (diff)
Merge branch 'latest-armlt-juno' into latest-armlt
-rw-r--r--arch/arm/boot/dts/Makefile3
l---------arch/arm/boot/dts/juno-base.dtsi1
l---------arch/arm/boot/dts/juno-clocks.dtsi1
l---------arch/arm/boot/dts/juno-cs-r1r2.dtsi1
l---------arch/arm/boot/dts/juno-motherboard.dtsi1
l---------arch/arm/boot/dts/juno-r1.dts1
l---------arch/arm/boot/dts/juno-r2.dts1
l---------arch/arm/boot/dts/juno-sched-energy.dtsi1
l---------arch/arm/boot/dts/juno.dts1
l---------arch/arm/boot/dts/juno_r2-sched-energy.dtsi1
-rw-r--r--arch/arm64/boot/dts/arm/juno-base.dtsi2
-rw-r--r--arch/arm64/boot/dts/arm/juno-r1.dts2
-rw-r--r--arch/arm64/boot/dts/arm/juno-r2.dts2
-rw-r--r--arch/arm64/boot/dts/arm/juno.dts2
-rw-r--r--drivers/firmware/Kconfig4
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/arm_scpi.c56
-rw-r--r--drivers/firmware/arm_scpi_test.c568
-rw-r--r--drivers/net/ethernet/marvell/sky2.c14
-rw-r--r--linaro/configs/vexpress64.conf2
20 files changed, 647 insertions, 18 deletions
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 9c5e1d944d1c..7560c9166fa7 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -962,6 +962,9 @@ dtb-$(CONFIG_ARCH_VERSATILE) += \
versatile-ab.dtb \
versatile-pb.dtb
dtb-$(CONFIG_ARCH_VEXPRESS) += \
+ juno.dtb \
+ juno-r1.dtb \
+ juno-r2.dtb \
vexpress-v2p-ca5s.dtb \
vexpress-v2p-ca9.dtb \
vexpress-v2p-ca15-tc1.dtb \
diff --git a/arch/arm/boot/dts/juno-base.dtsi b/arch/arm/boot/dts/juno-base.dtsi
new file mode 120000
index 000000000000..5bf5772d7718
--- /dev/null
+++ b/arch/arm/boot/dts/juno-base.dtsi
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno-base.dtsi \ No newline at end of file
diff --git a/arch/arm/boot/dts/juno-clocks.dtsi b/arch/arm/boot/dts/juno-clocks.dtsi
new file mode 120000
index 000000000000..d26c206771d4
--- /dev/null
+++ b/arch/arm/boot/dts/juno-clocks.dtsi
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno-clocks.dtsi \ No newline at end of file
diff --git a/arch/arm/boot/dts/juno-cs-r1r2.dtsi b/arch/arm/boot/dts/juno-cs-r1r2.dtsi
new file mode 120000
index 000000000000..08c059e69464
--- /dev/null
+++ b/arch/arm/boot/dts/juno-cs-r1r2.dtsi
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno-cs-r1r2.dtsi \ No newline at end of file
diff --git a/arch/arm/boot/dts/juno-motherboard.dtsi b/arch/arm/boot/dts/juno-motherboard.dtsi
new file mode 120000
index 000000000000..a4e1f71b8533
--- /dev/null
+++ b/arch/arm/boot/dts/juno-motherboard.dtsi
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno-motherboard.dtsi \ No newline at end of file
diff --git a/arch/arm/boot/dts/juno-r1.dts b/arch/arm/boot/dts/juno-r1.dts
new file mode 120000
index 000000000000..f0bf74937285
--- /dev/null
+++ b/arch/arm/boot/dts/juno-r1.dts
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno-r1.dts \ No newline at end of file
diff --git a/arch/arm/boot/dts/juno-r2.dts b/arch/arm/boot/dts/juno-r2.dts
new file mode 120000
index 000000000000..aba7e1fac440
--- /dev/null
+++ b/arch/arm/boot/dts/juno-r2.dts
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno-r2.dts \ No newline at end of file
diff --git a/arch/arm/boot/dts/juno-sched-energy.dtsi b/arch/arm/boot/dts/juno-sched-energy.dtsi
new file mode 120000
index 000000000000..3508c8b8438f
--- /dev/null
+++ b/arch/arm/boot/dts/juno-sched-energy.dtsi
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno-sched-energy.dtsi \ No newline at end of file
diff --git a/arch/arm/boot/dts/juno.dts b/arch/arm/boot/dts/juno.dts
new file mode 120000
index 000000000000..186e53545ef2
--- /dev/null
+++ b/arch/arm/boot/dts/juno.dts
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno.dts \ No newline at end of file
diff --git a/arch/arm/boot/dts/juno_r2-sched-energy.dtsi b/arch/arm/boot/dts/juno_r2-sched-energy.dtsi
new file mode 120000
index 000000000000..0d000be54a83
--- /dev/null
+++ b/arch/arm/boot/dts/juno_r2-sched-energy.dtsi
@@ -0,0 +1 @@
+../../../arm64/boot/dts/arm/juno_r2-sched-energy.dtsi \ No newline at end of file
diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi
index bfe7d683a42e..01b7e96a61df 100644
--- a/arch/arm64/boot/dts/arm/juno-base.dtsi
+++ b/arch/arm64/boot/dts/arm/juno-base.dtsi
@@ -464,7 +464,7 @@
compatible = "arm,scpi-dvfs-clocks";
#clock-cells = <1>;
clock-indices = <0>, <1>, <2>;
- clock-output-names = "atlclk", "aplclk","gpuclk";
+ clock-output-names = "atlclk", "aplclk","clk_mali";
};
scpi_clk: scpi-clk {
compatible = "arm,scpi-variable-clocks";
diff --git a/arch/arm64/boot/dts/arm/juno-r1.dts b/arch/arm64/boot/dts/arm/juno-r1.dts
index 0e8943ab94d7..88c83d1d7076 100644
--- a/arch/arm64/boot/dts/arm/juno-r1.dts
+++ b/arch/arm64/boot/dts/arm/juno-r1.dts
@@ -14,7 +14,7 @@
/ {
model = "ARM Juno development board (r1)";
- compatible = "arm,juno-r1", "arm,juno", "arm,vexpress";
+ compatible = "arm,juno-r1", "arm,juno";
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
diff --git a/arch/arm64/boot/dts/arm/juno-r2.dts b/arch/arm64/boot/dts/arm/juno-r2.dts
index 405e2fba025b..5c194b4c830a 100644
--- a/arch/arm64/boot/dts/arm/juno-r2.dts
+++ b/arch/arm64/boot/dts/arm/juno-r2.dts
@@ -14,7 +14,7 @@
/ {
model = "ARM Juno development board (r2)";
- compatible = "arm,juno-r2", "arm,juno", "arm,vexpress";
+ compatible = "arm,juno-r2", "arm,juno";
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index 0220494c9b80..69aa396a8aad 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -13,7 +13,7 @@
/ {
model = "ARM Juno development board (r0)";
- compatible = "arm,juno", "arm,vexpress";
+ compatible = "arm,juno";
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6e4ed5a9c6fd..33e72ac4fa3b 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -48,6 +48,10 @@ config ARM_SCPI_POWER_DOMAIN
This enables support for the SCPI power domains which can be
enabled or disabled via the SCP firmware
+config ARM_SCPI_PROTOCOL_TEST
+ tristate "Test code for SCPI Message Protocol"
+ default ARM_SCPI_PROTOCOL
+
config EDD
tristate "BIOS Enhanced Disk Drive calls determine boot disk"
depends on X86
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index a37f12e8d137..f06daf68e68a 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_ARM_PSCI_FW) += psci.o
obj-$(CONFIG_ARM_PSCI_CHECKER) += psci_checker.o
obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o
obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o
+obj-$(CONFIG_ARM_SCPI_PROTOCOL_TEST) += arm_scpi_test.o
obj-$(CONFIG_DMI) += dmi_scan.o
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
obj-$(CONFIG_EDD) += edd.o
diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
index f6cfc31d34c7..8d39da71d6b9 100644
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -87,8 +87,6 @@
#define FW_REV_MINOR(x) (((x) & FW_REV_MINOR_MASK) >> FW_REV_MINOR_BITS)
#define FW_REV_PATCH(x) ((x) & FW_REV_PATCH_MASK)
-#define MAX_RX_TIMEOUT (msecs_to_jiffies(30))
-
enum scpi_error_codes {
SCPI_SUCCESS = 0, /* Success */
SCPI_ERR_PARAM = 1, /* Invalid parameter(s) */
@@ -480,6 +478,18 @@ static void scpi_tx_prepare(struct mbox_client *c, void *msg)
mem->command = cpu_to_le32(t->cmd);
}
+static void scpi_tx_done(struct mbox_client *c, void *msg, int result)
+{
+ struct scpi_xfer *t = msg;
+
+ if (!t->rx_buf)
+ complete(&t->done);
+ /*
+ * Messages with rx_buf are expecting a reply and will be on the
+ * rx_pending list, so leave them alone.
+ */
+}
+
static struct scpi_xfer *get_scpi_xfer(struct scpi_chan *ch)
{
struct scpi_xfer *t;
@@ -541,17 +551,24 @@ static int scpi_send_message(u8 idx, void *tx_buf, unsigned int tx_len,
reinit_completion(&msg->done);
ret = mbox_send_message(scpi_chan->chan, msg);
- if (ret < 0 || !rx_buf)
- goto out;
+ if (ret >= 0) {
+ /*
+ * Wait for message to be processed. If we end up having to wait
+ * for a very long time then there is a serious bug, probably in
+ * the firmware.
+ *
+ * IMPORTANT: We must not try and continue after the timeout
+ * because this driver and the mailbox framework still has data
+ * structures referring to the failed request and further
+ * serious bugs will result.
+ */
+ if (!wait_for_completion_timeout(&msg->done, msecs_to_jiffies(10000)))
+ BUG();
- if (!wait_for_completion_timeout(&msg->done, MAX_RX_TIMEOUT))
- ret = -ETIMEDOUT;
- else
/* first status word */
- ret = msg->status;
-out:
- if (ret < 0 && rx_buf) /* remove entry from the list if timed-out */
- scpi_process_cmd(scpi_chan, msg->cmd);
+ if (rx_buf)
+ ret = le32_to_cpu(msg->status);
+ }
put_scpi_xfer(msg, scpi_chan);
/* SCPI error codes > 0, translate them to Linux scale*/
@@ -642,6 +659,8 @@ static int opp_cmp_func(const void *opp1, const void *opp2)
return t1->freq - t2->freq;
}
+static bool juno_cpufreq_limit_hack = 0;
+
static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
{
struct scpi_dvfs_info *info;
@@ -680,6 +699,14 @@ static struct scpi_dvfs_info *scpi_dvfs_get_info(u8 domain)
sort(info->opps, info->count, sizeof(*opp), opp_cmp_func, NULL);
+ /*
+ * Juno silicon doesn't seem to be able to run the big cluster
+ * (domain == 0) at max frequency in AArch32 mode (it produces
+ * random and weird crashes) so drop the highest OPP in that case...
+ */
+ if (juno_cpufreq_limit_hack && domain == 0)
+ --info->count;
+
scpi_info->dvfs[domain] = info;
return info;
}
@@ -893,6 +920,10 @@ static int scpi_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
+ if (IS_ENABLED(CONFIG_ARM) && of_find_compatible_node(NULL,NULL,"arm,juno")) {
+ juno_cpufreq_limit_hack = true;
+ }
+
scpi_info = devm_kzalloc(dev, sizeof(*scpi_info), GFP_KERNEL);
if (!scpi_info)
return -ENOMEM;
@@ -935,8 +966,7 @@ static int scpi_probe(struct platform_device *pdev)
cl->dev = dev;
cl->rx_callback = scpi_handle_remote_msg;
cl->tx_prepare = scpi_tx_prepare;
- cl->tx_block = true;
- cl->tx_tout = 20;
+ cl->tx_done = scpi_tx_done;
cl->knows_txdone = false; /* controller can't ack */
INIT_LIST_HEAD(&pchan->rx_pending);
diff --git a/drivers/firmware/arm_scpi_test.c b/drivers/firmware/arm_scpi_test.c
new file mode 100644
index 000000000000..c21b35e6f4b8
--- /dev/null
+++ b/drivers/firmware/arm_scpi_test.c
@@ -0,0 +1,568 @@
+/*
+ * Test code for System Control and Power Interface (SCPI)
+ *
+ * Copyright (C) 2015 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/scpi_protocol.h>
+
+
+static int stress_time;
+module_param(stress_time, int, 0644);
+MODULE_PARM_DESC(stress_time, "Number of seconds to run each stress test for, overides each test's default.");
+
+static int run;
+static struct kernel_param_ops run_ops;
+module_param_cb(run, &run_ops, &run, 0644);
+MODULE_PARM_DESC(run, "The number of the test case to run, or -1 for all, 0 to stop tests.");
+
+
+static struct scpi_ops *scpi;
+
+static struct task_struct *main_thread;
+
+static DEFINE_MUTEX(main_thread_lock);
+
+
+#define MAX_TEST_THREADS 4
+
+static struct test_thread {
+ struct task_struct *task;
+ int thread_num;
+} test_threads[MAX_TEST_THREADS];
+
+static DEFINE_MUTEX(thread_lock);
+
+
+#define MAX_POWER_DOMAINS 8
+
+static u16 num_sensors;
+static u8 num_pd;
+static u8 num_opps[MAX_POWER_DOMAINS];
+static struct mutex pd_lock[MAX_POWER_DOMAINS];
+static u8 num_devices_with_power_states;
+
+
+#define FLAG_SERIAL_DVFS (1<<0)
+#define FLAG_SERIAL_PD (1<<1)
+
+static int test_flags;
+
+
+static int sensor_pmic = -1;
+
+
+static u32 random_seed;
+
+static u32 random(u32 range)
+{
+ random_seed = random_seed*69069+1;
+
+ return ((u64)random_seed * (u64)range) >> 32;
+}
+
+
+static atomic_t passes;
+static atomic_t failures;
+
+static bool fail_on(bool fail)
+{
+ if (fail)
+ atomic_inc(&failures);
+ else
+ atomic_inc(&passes);
+ return fail;
+}
+
+static void show_results(const char *title)
+{
+ int fail = atomic_xchg(&failures, 0);
+ int pass = atomic_xchg(&passes, 0);
+
+ if (fail)
+ pr_err("Results for '%s' is %d/%d (pass/fail)\n", title, pass, fail);
+ else
+ pr_info("Results for '%s' is %d/%d (pass/fail)\n", title, pass, fail);
+}
+
+
+static bool check_name(const char *name)
+{
+ char c;
+
+ if (!isalpha(*name++))
+ return false;
+
+ while ((c = *name++))
+ if (!isalnum(c) && c != '_')
+ return false;
+
+ return true;
+}
+
+static u64 get_sensor(u16 id)
+{
+ u64 val;
+ int ret;
+
+ ret = scpi->sensor_get_value(id, &val);
+ if (fail_on(ret < 0))
+ pr_err("FAILED sensor_get_value %d (%d)\n", id, ret);
+
+ return val;
+}
+
+static void init_sensors(void)
+{
+ u16 id;
+ int ret;
+
+ ret = scpi->sensor_get_capability(&num_sensors);
+ if (fail_on(ret))
+ pr_err("FAILED sensor_get_capability (%d)\n", ret);
+
+ pr_info("num_sensors: %d\n", num_sensors);
+
+ for (id = 0; id < num_sensors; id++) {
+
+ struct scpi_sensor_info info;
+ char name[sizeof(info.name) + 1];
+
+ ret = scpi->sensor_get_info(id, &info);
+ if (fail_on(ret)) {
+ pr_err("FAILED sensor_get_info (%d)\n", ret);
+ continue;
+ }
+
+ /* Get sensor name, guarding against missing NUL terminator */
+ memcpy(name, info.name, sizeof(info.name));
+ name[sizeof(info.name)] = 0;
+
+ pr_info("sensor[%d] id=%d class=%d trigger=%d name=%s\n", id,
+ info.sensor_id, info.class, info.trigger_type, name);
+
+ if (fail_on(id != info.sensor_id))
+ pr_err("FAILED bad sensor id\n");
+ if (fail_on(info.class > 4))
+ pr_err("FAILED bad sensor class\n");
+ if (fail_on(info.trigger_type > 3))
+ pr_err("FAILED bad sensor trigger type\n");
+ if (fail_on(strlen(name) >= sizeof(info.name) || !check_name(name)))
+ pr_err("FAILED bad name\n");
+
+ pr_info("sensor[%d] value is %llu\n", id, get_sensor(id));
+ if (strstr(name, "PMIC"))
+ sensor_pmic = id;
+ }
+}
+
+static int get_dvfs(u8 pd)
+{
+ int ret = scpi->dvfs_get_idx(pd);
+
+ if (fail_on(ret < 0))
+ pr_err("FAILED get_dvfs %d (%d)\n", pd, ret);
+ else if (fail_on(ret >= num_opps[pd]))
+ pr_err("FAILED get_dvfs %d returned out of range index (%d)\n", pd, ret);
+
+ return ret;
+}
+
+static int set_dvfs(u8 pd, u8 opp)
+{
+ int ret;
+
+ if (test_flags & FLAG_SERIAL_DVFS)
+ mutex_lock(&pd_lock[0]);
+ else if (test_flags & FLAG_SERIAL_PD)
+ mutex_lock(&pd_lock[pd]);
+
+ ret = scpi->dvfs_set_idx(pd, opp);
+
+ if (test_flags & FLAG_SERIAL_DVFS)
+ mutex_unlock(&pd_lock[0]);
+ else if (test_flags & FLAG_SERIAL_PD)
+ mutex_unlock(&pd_lock[pd]);
+
+ if (fail_on(ret < 0))
+ pr_err("FAILED set_dvfs %d %d (%d)\n", pd, opp, ret);
+
+ return ret;
+}
+
+static void init_dvfs(void)
+{
+ u8 pd;
+
+ for (pd = 0; pd < MAX_POWER_DOMAINS; ++pd) {
+ struct scpi_dvfs_info *info;
+ int opp;
+
+ info = scpi->dvfs_get_info(pd);
+ if (IS_ERR(info)) {
+ pr_info("dvfs_get_info %d failed with %d assume because no more power domains\n",
+ pd, (int)PTR_ERR(info));
+ break;
+ }
+
+ num_opps[pd] = info->count;
+ mutex_init(&pd_lock[pd]);
+ pr_info("pd[%d] count=%u latency=%u\n",
+ pd, info->count, info->latency);
+
+ opp = get_dvfs(pd);
+ pr_info("pd[%d] current opp=%d\n", pd, opp);
+
+ for (opp = 0; opp < info->count; ++opp) {
+ pr_info("pd[%d].opp[%d] freq=%u m_volt=%u\n", pd, opp,
+ info->opps[opp].freq, info->opps[opp].m_volt);
+ /*
+ * Try setting each opp. Note, failure is not necessarily
+ * an error because cpufreq may be setting values too.
+ */
+ set_dvfs(pd, opp);
+ if (get_dvfs(pd) == opp)
+ pr_info("pd[%d] set to opp %d OK\n", pd, opp);
+ else
+ pr_warn("pd[%d] failed to set opp to %d\n", pd, opp);
+ }
+ }
+
+ if (!pd) {
+ /* Assume device should have at least one DVFS power domain */
+ pr_err("FAILED no power domains\n");
+ fail_on(true);
+ }
+ num_pd = pd;
+}
+
+static int device_get_power_state(u16 dev_id)
+{
+ int ret;
+
+ ret = scpi->device_get_power_state(dev_id);
+ if (fail_on(ret < 0))
+ pr_err("FAILED device_get_power_state %d (%d)\n", dev_id, ret);
+
+ return ret;
+}
+
+static int device_set_power_state(u16 dev_id, u8 pstate)
+{
+ int ret;
+
+ ret = scpi->device_set_power_state(dev_id, pstate);
+ if (fail_on(ret < 0))
+ pr_err("FAILED device_get_power_state %d (%d)\n", dev_id, ret);
+
+ return ret;
+}
+
+static void init_device_power_states(void)
+{
+ u16 dev_id;
+
+ for (dev_id = 0; dev_id < 0xffff; ++dev_id) {
+ int state = scpi->device_get_power_state(dev_id);
+
+ if (state < 0) {
+ pr_info("device_get_power_state %d failed with %d assume because no more devices\n",
+ dev_id, state);
+ break;
+ }
+
+ pr_info("device[%d] current state=%d\n", dev_id, state);
+
+ device_set_power_state(dev_id, state);
+ if (device_get_power_state(dev_id) == state)
+ pr_info("device[%d] set state to %d OK\n", dev_id, state);
+ else
+ pr_warn("device[%d] failed set state to %d\n", dev_id, state);
+ }
+
+ if (!dev_id) {
+ /* Assume device should have at least one device power state */
+ pr_err("FAILED no devices with power states\n");
+ fail_on(true);
+ }
+ num_devices_with_power_states = dev_id;
+}
+
+static int stress_pmic(void *data)
+{
+ int sensor, pd, opp;
+
+ while (!kthread_should_stop()) {
+ sensor = sensor_pmic;
+ pd = random(num_pd);
+ opp = random(num_opps[pd]);
+
+ switch (random(3)) {
+ case 0:
+ if (sensor >= 0) {
+ get_sensor(sensor);
+ break;
+ }
+ /* If no sensor, do DFVS... */
+ case 1:
+ set_dvfs(pd, opp);
+ break;
+ default:
+ msleep(random(20));
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int stress_all(void *data)
+{
+ int sensor, pd, opp;
+
+ while (!kthread_should_stop()) {
+ sensor = random(num_sensors);
+ pd = random(num_pd);
+ opp = random(num_opps[pd]);
+
+ switch (random(4)) {
+ case 0:
+ set_dvfs(pd, opp);
+ break;
+ case 1:
+ opp = get_dvfs(pd);
+ break;
+ case 2:
+ get_sensor(sensor);
+ break;
+ default:
+ msleep(random(20));
+ break;
+ }
+ }
+
+ return 0;
+}
+
+struct test {
+ const char *title;
+ int (*thread_fn)(void *);
+ int flags;
+ int num_threads;
+ int duration;
+};
+
+static void stop_test_threads(void)
+{
+ int t, ret;
+
+ for (t = 0; t < MAX_TEST_THREADS; ++t) {
+ struct test_thread *thread = &test_threads[t];
+
+ mutex_lock(&thread_lock);
+ if (thread->task) {
+ ret = kthread_stop(thread->task);
+ thread->task = NULL;
+ if (ret)
+ pr_warn("Test thread %d exited with status %d\n", t, ret);
+ }
+ mutex_unlock(&thread_lock);
+ }
+}
+
+static void run_test(struct test *test)
+{
+ int num_threads = min(test->num_threads, MAX_TEST_THREADS);
+ int duration = stress_time;
+ int t;
+
+ if (test->duration <= 0)
+ duration = 0;
+ else if (duration <= 0)
+ duration = test->duration;
+
+ pr_info("Running test '%s' for %d seconds\n", test->title, duration);
+
+ test_flags = test->flags;
+
+ for (t = 0; t < num_threads; ++t) {
+ struct test_thread *thread = &test_threads[t];
+ struct task_struct *task;
+
+ mutex_lock(&thread_lock);
+ thread->thread_num = t;
+ task = kthread_run(test->thread_fn, thread, "scpi-test-%d", t);
+ if (IS_ERR(task))
+ pr_warn("Failed to create test thread %d\n", t);
+ else
+ thread->task = task;
+ mutex_unlock(&thread_lock);
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(duration * 1000));
+
+ stop_test_threads();
+
+ show_results(test->title);
+}
+
+static struct test tests[] = {
+ {"Stress All, concurrent DVFS",
+ stress_all, 0, MAX_TEST_THREADS, 60},
+ {"Stress All, concurrent DVFS on different PDs",
+ stress_all, FLAG_SERIAL_PD, MAX_TEST_THREADS, 60},
+ {"Stress All, no concurrent DVFS",
+ stress_all, FLAG_SERIAL_DVFS, MAX_TEST_THREADS, 60},
+ {"Stress PMIC, concurrent DVFS",
+ stress_pmic, 0, MAX_TEST_THREADS, 60},
+ {"Stress PMIC, concurrent DVFS on different PDs",
+ stress_pmic, FLAG_SERIAL_PD, MAX_TEST_THREADS, 60},
+ {"Stress PMIC, no concurrent DVFS",
+ stress_pmic, FLAG_SERIAL_DVFS, MAX_TEST_THREADS, 60},
+ {}
+};
+
+static int main_thread_fn(void *data)
+{
+ struct test *test = tests;
+ int i = 1;
+
+ for (; test->title && !kthread_should_stop(); ++test, ++i)
+ if (run < 0 || run == i)
+ run_test(test);
+
+ run = 0;
+ return 0;
+}
+
+static DEFINE_MUTEX(setup_lock);
+
+static int setup(void)
+{
+ int ret = 0;
+
+ mutex_lock(&setup_lock);
+
+ if (!scpi) {
+ int tries = 12;
+
+ pr_info("Initial setup\n");
+ while ((scpi = get_scpi_ops()) == 0 && --tries) {
+ pr_info("Waiting for get_scpi_ops\n");
+ msleep(5000);
+ }
+
+ if (scpi) {
+ init_sensors();
+ init_dvfs();
+ init_device_power_states();
+ show_results("Initial setup");
+ } else {
+ pr_err("Given up on get_scpi_ops\n");
+ ret = -ENODEV;
+ }
+ }
+
+ mutex_unlock(&setup_lock);
+
+ return ret;
+}
+
+static int start_tests(void)
+{
+ struct task_struct *task;
+ int ret;
+
+ ret = setup();
+ if (ret) {
+ run = 0;
+ return ret;
+ }
+
+ pr_info("Creating main thread\n");
+ mutex_lock(&main_thread_lock);
+ if (main_thread) {
+ ret = -EBUSY;
+ } else {
+ task = kthread_run(main_thread_fn, 0, "scpi-test-main");
+ if (IS_ERR(task))
+ ret = PTR_ERR(task);
+ else
+ main_thread = task;
+ }
+ mutex_unlock(&main_thread_lock);
+
+ if (ret) {
+ pr_err("Failed to create main thread (%d)\n", ret);
+ run = 0;
+ }
+
+ return ret;
+}
+
+static void stop_tests(void)
+{
+ pr_info("Stopping tests\n");
+ mutex_lock(&main_thread_lock);
+ if (main_thread)
+ kthread_stop(main_thread);
+ main_thread = NULL;
+ mutex_unlock(&main_thread_lock);
+}
+
+static int param_set_running(const char *val, const struct kernel_param *kp)
+{
+ int ret;
+
+ ret = param_set_int(val, kp);
+ if (!ret) {
+ if (run)
+ ret = start_tests();
+ else
+ stop_tests();
+ }
+
+ return ret;
+}
+
+static struct kernel_param_ops run_ops = {
+ .set = param_set_running,
+ .get = param_get_int,
+};
+
+
+static int scpi_test_init(void)
+{
+ return 0;
+}
+
+static void scpi_test_exit(void)
+{
+ stop_tests();
+}
+
+module_init(scpi_test_init);
+module_exit(scpi_test_exit);
+
+
+MODULE_AUTHOR("Jon Medhurst (Tixy) <tixy@linaro.org>");
+MODULE_DESCRIPTION("ARM SCPI driver tests");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 86816afead98..bf397e3c5195 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -101,6 +101,10 @@ static int legacy_pme = 0;
module_param(legacy_pme, int, 0);
MODULE_PARM_DESC(legacy_pme, "Legacy power management");
+/* Ugh! Let the firmware tell us the hardware address */
+static int mac_address[ETH_ALEN] = { 0, };
+module_param_array(mac_address, int, NULL, 0);
+
static const struct pci_device_id sky2_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */
@@ -4831,13 +4835,21 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port,
/* try to get mac address in the following order:
* 1) from device tree data
* 2) from internal registers set by bootloader
+ * 3) from the command line parameter
*/
iap = of_get_mac_address(hw->pdev->dev.of_node);
if (iap)
memcpy(dev->dev_addr, iap, ETH_ALEN);
- else
+ else {
memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8,
ETH_ALEN);
+ if (!is_valid_ether_addr(&dev->dev_addr[0])) {
+ int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = mac_address[i];
+ }
+ }
/* if the address is invalid, use a random value */
if (!is_valid_ether_addr(dev->dev_addr)) {
diff --git a/linaro/configs/vexpress64.conf b/linaro/configs/vexpress64.conf
index 00aa7f99ea4e..55137cb966ee 100644
--- a/linaro/configs/vexpress64.conf
+++ b/linaro/configs/vexpress64.conf
@@ -63,3 +63,5 @@ CONFIG_CONNECTOR=y
CONFIG_ATA=y
CONFIG_SATA_SIL24=y
CONFIG_SKY2=y
+CONFIG_ARM_TIMER_SP804=y
+CONFIG_ARM_CPUIDLE=y