From ecbc06d1174b1d2bd08b01158a0b411454904e04 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 6 Oct 2015 11:06:45 +0100 Subject: arm64: dts: Fix scpi clock name for Mali on Juno Mali driver is hard coded to the name "clk_mali" and latest kernels use the clock-output-names in preference to any phandles when looking for clocks, so this name shold match expectations. --- arch/arm64/boot/dts/arm/juno-base.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"; -- cgit v1.2.3 From 87ee405246cceb8bfa0a3b0d369980891181376a Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 8 Jun 2015 17:21:34 +0100 Subject: configs: vexpress64: Enable SP804 We need this available to use as a broadcast timer on Juno r0 which has a broken memory mapped architected timer. Signed-off-by: Jon Medhurst --- linaro/configs/vexpress64.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/linaro/configs/vexpress64.conf b/linaro/configs/vexpress64.conf index 1eafbc7f0cce..e3668d63ef8b 100644 --- a/linaro/configs/vexpress64.conf +++ b/linaro/configs/vexpress64.conf @@ -74,3 +74,4 @@ CONFIG_CONNECTOR=y CONFIG_ATA=y CONFIG_SATA_SIL24=y CONFIG_SKY2=y +CONFIG_ARM_TIMER_SP804=y -- cgit v1.2.3 From d4527e45786aad4a4f2b092c642be189039cc99f Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 8 Jun 2015 17:26:10 +0100 Subject: configs: vexpress64: Enable cpuidle driver Signed-off-by: Jon Medhurst --- linaro/configs/vexpress64.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/linaro/configs/vexpress64.conf b/linaro/configs/vexpress64.conf index e3668d63ef8b..a1476baa6f49 100644 --- a/linaro/configs/vexpress64.conf +++ b/linaro/configs/vexpress64.conf @@ -75,3 +75,4 @@ CONFIG_ATA=y CONFIG_SATA_SIL24=y CONFIG_SKY2=y CONFIG_ARM_TIMER_SP804=y +CONFIG_ARM_CPUIDLE=y -- cgit v1.2.3 From af6d222b95252c393f29fbf3d7038a3ba56236d6 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Wed, 25 Feb 2015 18:44:02 +0000 Subject: sky2: Add module parameter for passing the MAC address For designs where EEPROMs are not connected to PCI Yukon2 chips we need to get the MAC address from the firmware. Add a module parameter called 'mac_address' for this. It will be used if no DT node can be found and the B2_MAC register holds an invalid value. Signed-off-by: Liviu Dudau Signed-off-by: Jon Medhurst --- drivers/net/ethernet/marvell/sky2.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 1145cde2274a..50a57976cf5d 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 */ @@ -4819,13 +4823,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)) { -- cgit v1.2.3 From 18203a14926e1013a02255a07f9a742de1a03297 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 27 Oct 2015 13:36:11 +0000 Subject: firmware: arm_scpi: Properly implement wait for messages to complete scpi_send_message is broken for a least two reasons. 1. The driver sets tx_block on the mailbox client and expects mbox_send_message to block until the message is sent, however this isn't the case. Whether by design or in error, mbox_send_message waits for tx_complete which will be signalled when any message is transmitted, not the one just queued. So in the case where two threads send messages concurrently, one thread may see the signal from the other's message completing, whilst it's message is still in the queue. 2. The second flaw is that even if the mailbox framework was waiting for the correct message signal the driver has set a timeout (mailbox client tx_tout) which means the wait for that signal may be aborted whilst the message is still in the queue for sending. Both the above mean that when mbox_send_message returns the message may still be pending transmission so we cannot safely do anything with its resources. The current code however goes on to free the message with put_scpi_xfer resulting in all sorts of bugs. To fix this, we add code to do a proper wait for the message to complete and if a timeout occurs BUG because we don't have any way to cancel messages in the queue. Signed-off-by: Jon Medhurst --- drivers/firmware/arm_scpi.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index f6cfc31d34c7..acf277d0a6eb 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*/ @@ -935,8 +952,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); -- cgit v1.2.3 From f6b3e1209960520d173eb63060e9f83328a4dde3 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 4 Dec 2015 11:11:45 +0000 Subject: firmware: arm_scpi_test: Stress tests for SCP and SCPI driver These can be run by passing kernel commandline parameters, e.g. arm_scpi_test.stress_time=60 arm_scpi_test.run=3 or from a shell after boot like: echo 60 > /sys/module/arm_scpi_test/parameters/stress_time echo 3 > /sys/module/arm_scpi_test/parameters/run The 'run' parameter gives the number of the test case to run, or -1 to indicate all tests. Setting run to zero will stop currently running tests. 'stress_time' is the number of seconds each stress test is run for and if omitted a default for each test will be used. Test progress is output with pr_info, pr_err etc, so will appear in the kernel log and on a console if the kernel log is also directed there. Signed-off-by: Jon Medhurst --- drivers/firmware/Kconfig | 4 + drivers/firmware/Makefile | 1 + drivers/firmware/arm_scpi_test.c | 478 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 drivers/firmware/arm_scpi_test.c 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_test.c b/drivers/firmware/arm_scpi_test.c new file mode 100644 index 000000000000..2348928a0e88 --- /dev/null +++ b/drivers/firmware/arm_scpi_test.c @@ -0,0 +1,478 @@ +/* + * 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 . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + + +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]; + + +#define FLAG_SERIAL_DVFS (1<<0) +#define FLAG_SERIAL_PD (1<<1) + +static int test_flags; + + + +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)); + } +} + +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 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}, + {} +}; + +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(); + 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) "); +MODULE_DESCRIPTION("ARM SCPI driver tests"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From fb848554542510e85e66fbcb544e3aafb37ba13f Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 22 Dec 2015 10:36:33 +0000 Subject: firmware: arm_scpi_test: Add tstress tests targeted at just the PMIC Signed-off-by: Jon Medhurst --- drivers/firmware/arm_scpi_test.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/firmware/arm_scpi_test.c b/drivers/firmware/arm_scpi_test.c index 2348928a0e88..80763e4d74a5 100644 --- a/drivers/firmware/arm_scpi_test.c +++ b/drivers/firmware/arm_scpi_test.c @@ -66,6 +66,8 @@ static struct mutex pd_lock[MAX_POWER_DOMAINS]; static int test_flags; +static int sensor_pmic = -1; + static u32 random_seed; @@ -166,6 +168,8 @@ static void init_sensors(void) 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; } } @@ -249,7 +253,33 @@ static void init_dvfs(void) num_pd = pd; } +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) { @@ -348,6 +378,12 @@ static struct test tests[] = { 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}, {} }; -- cgit v1.2.3 From 6d752a815875cae6496bcfca293467002853ad72 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 7 Jun 2016 16:31:16 +0100 Subject: firmware: arm_scpi_test: Query device power states Signed-off-by: Jon Medhurst --- drivers/firmware/arm_scpi_test.c | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/firmware/arm_scpi_test.c b/drivers/firmware/arm_scpi_test.c index 80763e4d74a5..c21b35e6f4b8 100644 --- a/drivers/firmware/arm_scpi_test.c +++ b/drivers/firmware/arm_scpi_test.c @@ -58,6 +58,7 @@ 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) @@ -253,6 +254,58 @@ static void init_dvfs(void) 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; @@ -420,6 +473,7 @@ static int setup(void) if (scpi) { init_sensors(); init_dvfs(); + init_device_power_states(); show_results("Initial setup"); } else { pr_err("Given up on get_scpi_ops\n"); -- cgit v1.2.3 From f479c9e2c2289420f6cb1400326c2af020aedf70 Mon Sep 17 00:00:00 2001 From: Ryan Harkin Date: Fri, 27 Jan 2017 17:01:47 +0000 Subject: arm: dts: juno: Add links to device-trees for Juno Juno can be run with CPUs set to 32-bit mode, so add links to the arm64 device-trees so be can easily build 32-bit kernels for that scenario. Signed-off-by: Ryan Harkin Signed-off-by: Jon Medhurst --- arch/arm/boot/dts/Makefile | 3 +++ arch/arm/boot/dts/juno-base.dtsi | 1 + arch/arm/boot/dts/juno-clocks.dtsi | 1 + arch/arm/boot/dts/juno-cs-r1r2.dtsi | 1 + arch/arm/boot/dts/juno-motherboard.dtsi | 1 + arch/arm/boot/dts/juno-r1.dts | 1 + arch/arm/boot/dts/juno-r2.dts | 1 + arch/arm/boot/dts/juno-sched-energy.dtsi | 1 + arch/arm/boot/dts/juno.dts | 1 + arch/arm/boot/dts/juno_r2-sched-energy.dtsi | 1 + 10 files changed, 12 insertions(+) create mode 120000 arch/arm/boot/dts/juno-base.dtsi create mode 120000 arch/arm/boot/dts/juno-clocks.dtsi create mode 120000 arch/arm/boot/dts/juno-cs-r1r2.dtsi create mode 120000 arch/arm/boot/dts/juno-motherboard.dtsi create mode 120000 arch/arm/boot/dts/juno-r1.dts create mode 120000 arch/arm/boot/dts/juno-r2.dts create mode 120000 arch/arm/boot/dts/juno-sched-energy.dtsi create mode 120000 arch/arm/boot/dts/juno.dts create mode 120000 arch/arm/boot/dts/juno_r2-sched-energy.dtsi 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 -- cgit v1.2.3 From b394776b7f698680019c65129f0111d60d949266 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 28 Feb 2017 11:46:46 +0000 Subject: arm64: dts: Remove "arm,vexpress" comatible string from Juno dts files Juno isn't really that compatible with Versatile Express and the only thing in Linux that tests for that string is the platform code which registers smp ops in 32-bit kernels. These won't work on Juno as it uses PSCI, so remove this compatible string and let 32-bit kernels work properly. Signed-off-by: Jon Medhurst --- arch/arm64/boot/dts/arm/juno-r1.dts | 2 +- arch/arm64/boot/dts/arm/juno-r2.dts | 2 +- arch/arm64/boot/dts/arm/juno.dts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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>; -- cgit v1.2.3 From 7a7765ba87aa6025416f131b923c5f635d2691cb Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 20 Apr 2017 11:48:19 +0100 Subject: HACK: juno: Limit CPU frequency when running AArch32 kernels The silicon doesn't seem to be able to cope with max frequency on the big core. Signed-off-by: Jon Medhurst --- drivers/firmware/arm_scpi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index acf277d0a6eb..8d39da71d6b9 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -659,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; @@ -697,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; } @@ -910,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; -- cgit v1.2.3