diff options
author | Ryan Harkin <ryan.harkin@linaro.org> | 2017-07-21 12:49:32 +0100 |
---|---|---|
committer | Ryan Harkin <ryan.harkin@linaro.org> | 2017-07-21 12:49:32 +0100 |
commit | b50e0fc7e8354127bde191a0a769938df86149e3 (patch) | |
tree | 7a1e653f280a61ad7700dbe575054f04e1fbe238 | |
parent | 4475a4101b94fd6f710dd45230f80faa755047c6 (diff) | |
parent | 7a7765ba87aa6025416f131b923c5f635d2691cb (diff) |
Merge branch 'latest-armlt-juno' into latest-armlt
-rw-r--r-- | arch/arm/boot/dts/Makefile | 3 | ||||
l--------- | arch/arm/boot/dts/juno-base.dtsi | 1 | ||||
l--------- | arch/arm/boot/dts/juno-clocks.dtsi | 1 | ||||
l--------- | arch/arm/boot/dts/juno-cs-r1r2.dtsi | 1 | ||||
l--------- | arch/arm/boot/dts/juno-motherboard.dtsi | 1 | ||||
l--------- | arch/arm/boot/dts/juno-r1.dts | 1 | ||||
l--------- | arch/arm/boot/dts/juno-r2.dts | 1 | ||||
l--------- | arch/arm/boot/dts/juno-sched-energy.dtsi | 1 | ||||
l--------- | arch/arm/boot/dts/juno.dts | 1 | ||||
l--------- | arch/arm/boot/dts/juno_r2-sched-energy.dtsi | 1 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/juno-base.dtsi | 2 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/juno-r1.dts | 2 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/juno-r2.dts | 2 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/juno.dts | 2 | ||||
-rw-r--r-- | drivers/firmware/Kconfig | 4 | ||||
-rw-r--r-- | drivers/firmware/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/arm_scpi.c | 56 | ||||
-rw-r--r-- | drivers/firmware/arm_scpi_test.c | 568 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/sky2.c | 14 | ||||
-rw-r--r-- | linaro/configs/vexpress64.conf | 2 |
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 |