soc: qcom: Remove smp2p test support
Smp2p test code is used internally to test the functionality of drivers and has no real use case in end product. Change-Id: I7a50c077bb71068188b5411424c5782b3d0edbb7 Signed-off-by: Hardik Arya <harya@codeaurora.org>
This commit is contained in:
parent
294df2f14b
commit
d1adc60b72
6 changed files with 1 additions and 3125 deletions
|
@ -121,4 +121,3 @@ obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
|
|||
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
|
||||
obj-$(CONFIG_GPIO_ZX) += gpio-zx.o
|
||||
obj-$(CONFIG_MSM_SMP2P) += gpio-msm-smp2p.o
|
||||
obj-$(CONFIG_MSM_SMP2P_TEST) += gpio-msm-smp2p-test.o
|
||||
|
|
|
@ -1,762 +0,0 @@
|
|||
/* drivers/gpio/gpio-msm-smp2p-test.c
|
||||
*
|
||||
* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include "../soc/qcom/smp2p_private.h"
|
||||
#include "../soc/qcom/smp2p_test_common.h"
|
||||
|
||||
/* Interrupt callback data */
|
||||
struct gpio_info {
|
||||
int gpio_base_id;
|
||||
int irq_base_id;
|
||||
|
||||
bool initialized;
|
||||
struct completion cb_completion;
|
||||
int cb_count;
|
||||
DECLARE_BITMAP(triggered_irqs, SMP2P_BITS_PER_ENTRY);
|
||||
};
|
||||
|
||||
/* GPIO Inbound/Outbound callback info */
|
||||
struct gpio_inout {
|
||||
struct gpio_info in;
|
||||
struct gpio_info out;
|
||||
};
|
||||
|
||||
static struct gpio_inout gpio_info[SMP2P_NUM_PROCS];
|
||||
|
||||
/**
|
||||
* Init/reset the callback data.
|
||||
*
|
||||
* @info: Pointer to callback data
|
||||
*/
|
||||
static void cb_data_reset(struct gpio_info *info)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
if (!info->initialized) {
|
||||
init_completion(&info->cb_completion);
|
||||
info->initialized = true;
|
||||
}
|
||||
info->cb_count = 0;
|
||||
|
||||
for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n)
|
||||
clear_bit(n, info->triggered_irqs);
|
||||
|
||||
reinit_completion(&info->cb_completion);
|
||||
}
|
||||
|
||||
static int smp2p_gpio_test_probe(struct platform_device *pdev)
|
||||
{
|
||||
int id;
|
||||
int cnt;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct gpio_info *gpio_info_ptr = NULL;
|
||||
|
||||
/*
|
||||
* NOTE: This does a string-lookup of the GPIO pin name and doesn't
|
||||
* actually directly link to the SMP2P GPIO driver since all
|
||||
* GPIO/Interrupt access must be through standard
|
||||
* Linux GPIO / Interrupt APIs.
|
||||
*/
|
||||
if (strcmp("qcom,smp2pgpio_test_smp2p_1_in", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_MODEM_PROC].in;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_1_out", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_MODEM_PROC].out;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_2_in", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_AUDIO_PROC].in;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_2_out", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_AUDIO_PROC].out;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_3_in", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_SENSOR_PROC].in;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_3_out", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_SENSOR_PROC].out;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_4_in", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_WIRELESS_PROC].in;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_4_out", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_WIRELESS_PROC].out;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_5_in", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_CDSP_PROC].in;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_5_out", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_CDSP_PROC].out;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_7_in", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_TZ_PROC].in;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_7_out", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_TZ_PROC].out;
|
||||
} else if (strcmp("qcom,smp2pgpio_test_smp2p_15_in", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_REMOTE_MOCK_PROC].in;
|
||||
} else if (
|
||||
strcmp("qcom,smp2pgpio_test_smp2p_15_out", node->name) == 0) {
|
||||
gpio_info_ptr = &gpio_info[SMP2P_REMOTE_MOCK_PROC].out;
|
||||
} else {
|
||||
pr_err("%s: unable to match device type '%s'\n",
|
||||
__func__, node->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* retrieve the GPIO and interrupt ID's */
|
||||
cnt = of_gpio_count(node);
|
||||
if (cnt && gpio_info_ptr) {
|
||||
/*
|
||||
* Instead of looping through all 32-bits, we can just get the
|
||||
* first pin to get the base IDs. This saves on the verbosity
|
||||
* of the device tree nodes as well.
|
||||
*/
|
||||
id = of_get_gpio(node, 0);
|
||||
if (id == -EPROBE_DEFER)
|
||||
return id;
|
||||
gpio_info_ptr->gpio_base_id = id;
|
||||
gpio_info_ptr->irq_base_id = gpio_to_irq(id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: Instead of match table and device driver, you may be able to just
|
||||
* call of_find_compatible_node() in your init function.
|
||||
*/
|
||||
static struct of_device_id msm_smp2p_match_table[] = {
|
||||
/* modem */
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_1_out", },
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_1_in", },
|
||||
|
||||
/* audio (adsp) */
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_2_out", },
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_2_in", },
|
||||
|
||||
/* sensor */
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_3_out", },
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_3_in", },
|
||||
|
||||
/* wcnss */
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_4_out", },
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_4_in", },
|
||||
|
||||
/* CDSP */
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_5_out", },
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_5_in", },
|
||||
|
||||
/* TZ */
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_7_out", },
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_7_in", },
|
||||
|
||||
/* mock loopback */
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_15_out", },
|
||||
{.compatible = "qcom,smp2pgpio_test_smp2p_15_in", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver smp2p_gpio_driver = {
|
||||
.probe = smp2p_gpio_test_probe,
|
||||
.driver = {
|
||||
.name = "smp2pgpio_test",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = msm_smp2p_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* smp2p_ut_local_gpio_out - Verify outbound functionality.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
*/
|
||||
static void smp2p_ut_local_gpio_out(struct seq_file *s)
|
||||
{
|
||||
int failed = 0;
|
||||
struct gpio_info *cb_info = &gpio_info[SMP2P_REMOTE_MOCK_PROC].out;
|
||||
int ret;
|
||||
int id;
|
||||
struct msm_smp2p_remote_mock *mock;
|
||||
|
||||
seq_printf(s, "Running %s\n", __func__);
|
||||
do {
|
||||
/* initialize mock edge */
|
||||
ret = smp2p_reset_mock_edge();
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
mock = msm_smp2p_get_remote_mock();
|
||||
UT_ASSERT_PTR(mock, !=, NULL);
|
||||
|
||||
mock->rx_interrupt_count = 0;
|
||||
memset(&mock->remote_item, 0,
|
||||
sizeof(struct smp2p_smem_item));
|
||||
smp2p_init_header((struct smp2p_smem *)&mock->remote_item,
|
||||
SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
|
||||
0, 1);
|
||||
strlcpy(mock->remote_item.entries[0].name, "smp2p",
|
||||
SMP2P_MAX_ENTRY_NAME);
|
||||
SMP2P_SET_ENT_VALID(
|
||||
mock->remote_item.header.valid_total_ent, 1);
|
||||
msm_smp2p_set_remote_mock_exists(true);
|
||||
mock->tx_interrupt();
|
||||
|
||||
/* open GPIO entry */
|
||||
smp2p_gpio_open_test_entry("smp2p",
|
||||
SMP2P_REMOTE_MOCK_PROC, true);
|
||||
|
||||
/* verify set/get functions */
|
||||
UT_ASSERT_INT(0, <, cb_info->gpio_base_id);
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
int pin = cb_info->gpio_base_id + id;
|
||||
|
||||
mock->rx_interrupt_count = 0;
|
||||
gpio_set_value(pin, 1);
|
||||
UT_ASSERT_INT(1, ==, mock->rx_interrupt_count);
|
||||
UT_ASSERT_INT(1, ==, gpio_get_value(pin));
|
||||
|
||||
gpio_set_value(pin, 0);
|
||||
UT_ASSERT_INT(2, ==, mock->rx_interrupt_count);
|
||||
UT_ASSERT_INT(0, ==, gpio_get_value(pin));
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
seq_puts(s, "\tOK\n");
|
||||
} while (0);
|
||||
|
||||
if (failed) {
|
||||
pr_err("%s: Failed\n", __func__);
|
||||
seq_puts(s, "\tFailed\n");
|
||||
}
|
||||
|
||||
smp2p_gpio_open_test_entry("smp2p",
|
||||
SMP2P_REMOTE_MOCK_PROC, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_gpio_irq - Interrupt handler for inbound entries.
|
||||
*
|
||||
* @irq: Virtual IRQ being triggered
|
||||
* @data: Cookie data (struct gpio_info * in this case)
|
||||
* @returns: Number of bytes written
|
||||
*/
|
||||
static irqreturn_t smp2p_gpio_irq(int irq, void *data)
|
||||
{
|
||||
struct gpio_info *gpio_ptr = (struct gpio_info *)data;
|
||||
int offset;
|
||||
|
||||
if (!gpio_ptr) {
|
||||
pr_err("%s: gpio_ptr is NULL for irq %d\n", __func__, irq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
offset = irq - gpio_ptr->irq_base_id;
|
||||
if (offset >= 0 && offset < SMP2P_BITS_PER_ENTRY)
|
||||
set_bit(offset, gpio_ptr->triggered_irqs);
|
||||
else
|
||||
pr_err("%s: invalid irq offset base %d; irq %d\n",
|
||||
__func__, gpio_ptr->irq_base_id, irq);
|
||||
|
||||
++gpio_ptr->cb_count;
|
||||
complete(&gpio_ptr->cb_completion);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_local_gpio_in - Verify inbound functionality.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
*/
|
||||
static void smp2p_ut_local_gpio_in(struct seq_file *s)
|
||||
{
|
||||
int failed = 0;
|
||||
struct gpio_info *cb_info = &gpio_info[SMP2P_REMOTE_MOCK_PROC].in;
|
||||
int id;
|
||||
int ret;
|
||||
int virq;
|
||||
struct msm_smp2p_remote_mock *mock;
|
||||
|
||||
seq_printf(s, "Running %s\n", __func__);
|
||||
|
||||
cb_data_reset(cb_info);
|
||||
do {
|
||||
/* initialize mock edge */
|
||||
ret = smp2p_reset_mock_edge();
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
mock = msm_smp2p_get_remote_mock();
|
||||
UT_ASSERT_PTR(mock, !=, NULL);
|
||||
|
||||
mock->rx_interrupt_count = 0;
|
||||
memset(&mock->remote_item, 0,
|
||||
sizeof(struct smp2p_smem_item));
|
||||
smp2p_init_header((struct smp2p_smem *)&mock->remote_item,
|
||||
SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
|
||||
0, 1);
|
||||
strlcpy(mock->remote_item.entries[0].name, "smp2p",
|
||||
SMP2P_MAX_ENTRY_NAME);
|
||||
SMP2P_SET_ENT_VALID(
|
||||
mock->remote_item.header.valid_total_ent, 1);
|
||||
msm_smp2p_set_remote_mock_exists(true);
|
||||
mock->tx_interrupt();
|
||||
|
||||
smp2p_gpio_open_test_entry("smp2p",
|
||||
SMP2P_REMOTE_MOCK_PROC, true);
|
||||
|
||||
/* verify set/get functions locally */
|
||||
UT_ASSERT_INT(0, <, cb_info->gpio_base_id);
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
int pin;
|
||||
int current_value;
|
||||
|
||||
/* verify pin value cannot be set */
|
||||
pin = cb_info->gpio_base_id + id;
|
||||
current_value = gpio_get_value(pin);
|
||||
|
||||
gpio_set_value(pin, 0);
|
||||
UT_ASSERT_INT(current_value, ==, gpio_get_value(pin));
|
||||
gpio_set_value(pin, 1);
|
||||
UT_ASSERT_INT(current_value, ==, gpio_get_value(pin));
|
||||
|
||||
/* verify no interrupts */
|
||||
UT_ASSERT_INT(0, ==, cb_info->cb_count);
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
/* register for interrupts */
|
||||
UT_ASSERT_INT(0, <, cb_info->irq_base_id);
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
virq = cb_info->irq_base_id + id;
|
||||
UT_ASSERT_PTR(NULL, !=, irq_to_desc(virq));
|
||||
ret = request_irq(virq,
|
||||
smp2p_gpio_irq, IRQF_TRIGGER_RISING,
|
||||
"smp2p_test", cb_info);
|
||||
UT_ASSERT_INT(0, ==, ret);
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
/* verify both rising and falling edge interrupts */
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
virq = cb_info->irq_base_id + id;
|
||||
irq_set_irq_type(virq, IRQ_TYPE_EDGE_BOTH);
|
||||
cb_data_reset(cb_info);
|
||||
|
||||
/* verify rising-edge interrupt */
|
||||
mock->remote_item.entries[0].entry = 1 << id;
|
||||
mock->tx_interrupt();
|
||||
UT_ASSERT_INT(cb_info->cb_count, ==, 1);
|
||||
UT_ASSERT_INT(0, <,
|
||||
test_bit(id, cb_info->triggered_irqs));
|
||||
test_bit(id, cb_info->triggered_irqs);
|
||||
|
||||
/* verify falling-edge interrupt */
|
||||
mock->remote_item.entries[0].entry = 0;
|
||||
mock->tx_interrupt();
|
||||
UT_ASSERT_INT(cb_info->cb_count, ==, 2);
|
||||
UT_ASSERT_INT(0, <,
|
||||
test_bit(id, cb_info->triggered_irqs));
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
/* verify rising-edge interrupts */
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
virq = cb_info->irq_base_id + id;
|
||||
irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
|
||||
cb_data_reset(cb_info);
|
||||
|
||||
/* verify only rising-edge interrupt is triggered */
|
||||
mock->remote_item.entries[0].entry = 1 << id;
|
||||
mock->tx_interrupt();
|
||||
UT_ASSERT_INT(cb_info->cb_count, ==, 1);
|
||||
UT_ASSERT_INT(0, <,
|
||||
test_bit(id, cb_info->triggered_irqs));
|
||||
test_bit(id, cb_info->triggered_irqs);
|
||||
|
||||
mock->remote_item.entries[0].entry = 0;
|
||||
mock->tx_interrupt();
|
||||
UT_ASSERT_INT(cb_info->cb_count, ==, 1);
|
||||
UT_ASSERT_INT(0, <,
|
||||
test_bit(id, cb_info->triggered_irqs));
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
/* verify falling-edge interrupts */
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
virq = cb_info->irq_base_id + id;
|
||||
irq_set_irq_type(virq, IRQ_TYPE_EDGE_FALLING);
|
||||
cb_data_reset(cb_info);
|
||||
|
||||
/* verify only rising-edge interrupt is triggered */
|
||||
mock->remote_item.entries[0].entry = 1 << id;
|
||||
mock->tx_interrupt();
|
||||
UT_ASSERT_INT(cb_info->cb_count, ==, 0);
|
||||
UT_ASSERT_INT(0, ==,
|
||||
test_bit(id, cb_info->triggered_irqs));
|
||||
|
||||
mock->remote_item.entries[0].entry = 0;
|
||||
mock->tx_interrupt();
|
||||
UT_ASSERT_INT(cb_info->cb_count, ==, 1);
|
||||
UT_ASSERT_INT(0, <,
|
||||
test_bit(id, cb_info->triggered_irqs));
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
seq_puts(s, "\tOK\n");
|
||||
} while (0);
|
||||
|
||||
if (failed) {
|
||||
pr_err("%s: Failed\n", __func__);
|
||||
seq_puts(s, "\tFailed\n");
|
||||
}
|
||||
|
||||
/* unregister for interrupts */
|
||||
if (cb_info->irq_base_id) {
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY; ++id)
|
||||
free_irq(cb_info->irq_base_id + id, cb_info);
|
||||
}
|
||||
|
||||
smp2p_gpio_open_test_entry("smp2p",
|
||||
SMP2P_REMOTE_MOCK_PROC, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_local_gpio_in_update_open - Verify combined open/update.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
*
|
||||
* If the remote side updates the SMP2P bits and sends before negotiation is
|
||||
* complete, then the UPDATE event will have to be delayed until negotiation is
|
||||
* complete. This should result in both the OPEN and UPDATE events coming in
|
||||
* right after each other and the behavior should be transparent to the clients
|
||||
* of SMP2P GPIO.
|
||||
*/
|
||||
static void smp2p_ut_local_gpio_in_update_open(struct seq_file *s)
|
||||
{
|
||||
int failed = 0;
|
||||
struct gpio_info *cb_info = &gpio_info[SMP2P_REMOTE_MOCK_PROC].in;
|
||||
int id;
|
||||
int ret;
|
||||
int virq;
|
||||
struct msm_smp2p_remote_mock *mock;
|
||||
|
||||
seq_printf(s, "Running %s\n", __func__);
|
||||
|
||||
cb_data_reset(cb_info);
|
||||
do {
|
||||
/* initialize mock edge */
|
||||
ret = smp2p_reset_mock_edge();
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
mock = msm_smp2p_get_remote_mock();
|
||||
UT_ASSERT_PTR(mock, !=, NULL);
|
||||
|
||||
mock->rx_interrupt_count = 0;
|
||||
memset(&mock->remote_item, 0,
|
||||
sizeof(struct smp2p_smem_item));
|
||||
smp2p_init_header((struct smp2p_smem *)&mock->remote_item,
|
||||
SMP2P_REMOTE_MOCK_PROC, SMP2P_APPS_PROC,
|
||||
0, 1);
|
||||
strlcpy(mock->remote_item.entries[0].name, "smp2p",
|
||||
SMP2P_MAX_ENTRY_NAME);
|
||||
SMP2P_SET_ENT_VALID(
|
||||
mock->remote_item.header.valid_total_ent, 1);
|
||||
|
||||
/* register for interrupts */
|
||||
smp2p_gpio_open_test_entry("smp2p",
|
||||
SMP2P_REMOTE_MOCK_PROC, true);
|
||||
|
||||
UT_ASSERT_INT(0, <, cb_info->irq_base_id);
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
virq = cb_info->irq_base_id + id;
|
||||
UT_ASSERT_PTR(NULL, !=, irq_to_desc(virq));
|
||||
ret = request_irq(virq,
|
||||
smp2p_gpio_irq, IRQ_TYPE_EDGE_BOTH,
|
||||
"smp2p_test", cb_info);
|
||||
UT_ASSERT_INT(0, ==, ret);
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
/* update the state value and complete negotiation */
|
||||
mock->remote_item.entries[0].entry = 0xDEADDEAD;
|
||||
msm_smp2p_set_remote_mock_exists(true);
|
||||
mock->tx_interrupt();
|
||||
|
||||
/* verify delayed state updates were processed */
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
virq = cb_info->irq_base_id + id;
|
||||
|
||||
UT_ASSERT_INT(cb_info->cb_count, >, 0);
|
||||
if (0x1 & (0xDEADDEAD >> id)) {
|
||||
/* rising edge should have been triggered */
|
||||
if (!test_bit(id, cb_info->triggered_irqs)) {
|
||||
seq_printf(s, "%s:%d bit %d clear, ",
|
||||
__func__, __LINE__, id);
|
||||
seq_puts(s, "expected set\n");
|
||||
failed = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* edge should not have been triggered */
|
||||
if (test_bit(id, cb_info->triggered_irqs)) {
|
||||
seq_printf(s, "%s:%d bit %d set, ",
|
||||
__func__, __LINE__, id);
|
||||
seq_puts(s, "expected clear\n");
|
||||
failed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
seq_puts(s, "\tOK\n");
|
||||
} while (0);
|
||||
|
||||
if (failed) {
|
||||
pr_err("%s: Failed\n", __func__);
|
||||
seq_puts(s, "\tFailed\n");
|
||||
}
|
||||
|
||||
/* unregister for interrupts */
|
||||
if (cb_info->irq_base_id) {
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY; ++id)
|
||||
free_irq(cb_info->irq_base_id + id, cb_info);
|
||||
}
|
||||
|
||||
smp2p_gpio_open_test_entry("smp2p",
|
||||
SMP2P_REMOTE_MOCK_PROC, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_gpio_write_bits - writes value to each GPIO pin specified in mask.
|
||||
*
|
||||
* @gpio: gpio test structure
|
||||
* @mask: 1 = write gpio_value to this GPIO pin
|
||||
* @gpio_value: value to write to GPIO pin
|
||||
*/
|
||||
static void smp2p_gpio_write_bits(struct gpio_info *gpio, uint32_t mask,
|
||||
int gpio_value)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n) {
|
||||
if (mask & 0x1)
|
||||
gpio_set_value(gpio->gpio_base_id + n, gpio_value);
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void smp2p_gpio_set_bits(struct gpio_info *gpio, uint32_t mask)
|
||||
{
|
||||
smp2p_gpio_write_bits(gpio, mask, 1);
|
||||
}
|
||||
|
||||
static void smp2p_gpio_clr_bits(struct gpio_info *gpio, uint32_t mask)
|
||||
{
|
||||
smp2p_gpio_write_bits(gpio, mask, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_gpio_get_value - reads entire 32-bits of GPIO
|
||||
*
|
||||
* @gpio: gpio structure
|
||||
* @returns: 32 bit value of GPIO pins
|
||||
*/
|
||||
static uint32_t smp2p_gpio_get_value(struct gpio_info *gpio)
|
||||
{
|
||||
int n;
|
||||
uint32_t value = 0;
|
||||
|
||||
for (n = 0; n < SMP2P_BITS_PER_ENTRY; ++n) {
|
||||
if (gpio_get_value(gpio->gpio_base_id + n))
|
||||
value |= 1 << n;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_inout_core - Verify inbound/outbound functionality.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
* @remote_pid: Remote processor to test
|
||||
* @name: Name of the test for reporting
|
||||
*
|
||||
* This test verifies inbound/outbound functionality for the remote processor.
|
||||
*/
|
||||
static void smp2p_ut_remote_inout_core(struct seq_file *s, int remote_pid,
|
||||
const char *name)
|
||||
{
|
||||
int failed = 0;
|
||||
uint32_t request;
|
||||
uint32_t response;
|
||||
struct gpio_info *cb_in;
|
||||
struct gpio_info *cb_out;
|
||||
int id;
|
||||
int ret;
|
||||
|
||||
seq_printf(s, "Running %s for '%s' remote pid %d\n",
|
||||
__func__, smp2p_pid_to_name(remote_pid), remote_pid);
|
||||
|
||||
cb_in = &gpio_info[remote_pid].in;
|
||||
cb_out = &gpio_info[remote_pid].out;
|
||||
cb_data_reset(cb_in);
|
||||
cb_data_reset(cb_out);
|
||||
do {
|
||||
/* open test entries */
|
||||
msm_smp2p_deinit_rmt_lpb_proc(remote_pid);
|
||||
smp2p_gpio_open_test_entry("smp2p", remote_pid, true);
|
||||
|
||||
/* register for interrupts */
|
||||
UT_ASSERT_INT(0, <, cb_in->gpio_base_id);
|
||||
UT_ASSERT_INT(0, <, cb_in->irq_base_id);
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY && !failed; ++id) {
|
||||
int virq = cb_in->irq_base_id + id;
|
||||
UT_ASSERT_PTR(NULL, !=, irq_to_desc(virq));
|
||||
ret = request_irq(virq,
|
||||
smp2p_gpio_irq,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"smp2p_test", cb_in);
|
||||
UT_ASSERT_INT(0, ==, ret);
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
/* write echo of data value 0 */
|
||||
UT_ASSERT_INT(0, <, cb_out->gpio_base_id);
|
||||
request = 0x0;
|
||||
SMP2P_SET_RMT_CMD_TYPE(request, 1);
|
||||
SMP2P_SET_RMT_CMD(request, SMP2P_LB_CMD_ECHO);
|
||||
SMP2P_SET_RMT_DATA(request, 0x0);
|
||||
|
||||
smp2p_gpio_set_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
|
||||
smp2p_gpio_clr_bits(cb_out, ~SMP2P_RMT_IGNORE_MASK);
|
||||
smp2p_gpio_set_bits(cb_out, request);
|
||||
|
||||
UT_ASSERT_INT(cb_in->cb_count, ==, 0);
|
||||
smp2p_gpio_clr_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
|
||||
|
||||
/* verify response */
|
||||
do {
|
||||
/* wait for up to 32 changes */
|
||||
if (wait_for_completion_timeout(
|
||||
&cb_in->cb_completion, HZ / 2) == 0)
|
||||
break;
|
||||
reinit_completion(&cb_in->cb_completion);
|
||||
} while (cb_in->cb_count < 32);
|
||||
UT_ASSERT_INT(cb_in->cb_count, >, 0);
|
||||
response = smp2p_gpio_get_value(cb_in);
|
||||
SMP2P_SET_RMT_CMD_TYPE(request, 0);
|
||||
UT_ASSERT_HEX(request, ==, response);
|
||||
|
||||
/* write echo of data value of all 1's */
|
||||
request = 0x0;
|
||||
SMP2P_SET_RMT_CMD_TYPE(request, 1);
|
||||
SMP2P_SET_RMT_CMD(request, SMP2P_LB_CMD_ECHO);
|
||||
SMP2P_SET_RMT_DATA(request, ~0);
|
||||
|
||||
smp2p_gpio_set_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
|
||||
cb_data_reset(cb_in);
|
||||
smp2p_gpio_clr_bits(cb_out, ~SMP2P_RMT_IGNORE_MASK);
|
||||
smp2p_gpio_set_bits(cb_out, request);
|
||||
|
||||
UT_ASSERT_INT(cb_in->cb_count, ==, 0);
|
||||
smp2p_gpio_clr_bits(cb_out, SMP2P_RMT_IGNORE_MASK);
|
||||
|
||||
/* verify response including 24 interrupts */
|
||||
do {
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&cb_in->cb_completion, HZ / 2),
|
||||
>, 0);
|
||||
reinit_completion(&cb_in->cb_completion);
|
||||
} while (cb_in->cb_count < 24);
|
||||
response = smp2p_gpio_get_value(cb_in);
|
||||
SMP2P_SET_RMT_CMD_TYPE(request, 0);
|
||||
UT_ASSERT_HEX(request, ==, response);
|
||||
UT_ASSERT_INT(24, ==, cb_in->cb_count);
|
||||
|
||||
seq_puts(s, "\tOK\n");
|
||||
} while (0);
|
||||
|
||||
if (failed) {
|
||||
pr_err("%s: Failed\n", name);
|
||||
seq_puts(s, "\tFailed\n");
|
||||
}
|
||||
|
||||
/* unregister for interrupts */
|
||||
if (cb_in->irq_base_id) {
|
||||
for (id = 0; id < SMP2P_BITS_PER_ENTRY; ++id)
|
||||
free_irq(cb_in->irq_base_id + id, cb_in);
|
||||
}
|
||||
|
||||
smp2p_gpio_open_test_entry("smp2p", remote_pid, false);
|
||||
msm_smp2p_init_rmt_lpb_proc(remote_pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_inout - Verify inbound/outbound functionality for all.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
*
|
||||
* This test verifies inbound and outbound functionality for all
|
||||
* configured remote processor.
|
||||
*/
|
||||
static void smp2p_ut_remote_inout(struct seq_file *s)
|
||||
{
|
||||
struct smp2p_interrupt_config *int_cfg;
|
||||
int pid;
|
||||
|
||||
int_cfg = smp2p_get_interrupt_config();
|
||||
if (!int_cfg) {
|
||||
seq_puts(s, "Remote processor config unavailable\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
|
||||
if (!int_cfg[pid].is_configured)
|
||||
continue;
|
||||
|
||||
smp2p_ut_remote_inout_core(s, pid, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init smp2p_debugfs_init(void)
|
||||
{
|
||||
/* register GPIO pins */
|
||||
(void)platform_driver_register(&smp2p_gpio_driver);
|
||||
|
||||
/*
|
||||
* Add Unit Test entries.
|
||||
*
|
||||
* The idea with unit tests is that you can run all of them
|
||||
* from ADB shell by doing:
|
||||
* adb shell
|
||||
* cat ut*
|
||||
*
|
||||
* And if particular tests fail, you can then repeatedly run the
|
||||
* failing tests as you debug and resolve the failing test.
|
||||
*/
|
||||
smp2p_debug_create("ut_local_gpio_out", smp2p_ut_local_gpio_out);
|
||||
smp2p_debug_create("ut_local_gpio_in", smp2p_ut_local_gpio_in);
|
||||
smp2p_debug_create("ut_local_gpio_in_update_open",
|
||||
smp2p_ut_local_gpio_in_update_open);
|
||||
smp2p_debug_create("ut_remote_gpio_inout", smp2p_ut_remote_inout);
|
||||
return 0;
|
||||
}
|
||||
late_initcall(smp2p_debugfs_init);
|
|
@ -15,8 +15,7 @@ obj-$(CONFIG_MSM_GLINK_SPI_XPRT) += glink_spi_xprt.o
|
|||
obj-$(CONFIG_MSM_SMEM_LOGGING) += smem_log.o
|
||||
obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o sysmon-qmi.o
|
||||
obj-$(CONFIG_ARCH_MSM8996) += kryo-l2-accessors.o
|
||||
obj-$(CONFIG_MSM_SMP2P) += smp2p.o smp2p_debug.o smp2p_sleepstate.o
|
||||
obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_spinlock_test.o
|
||||
obj-$(CONFIG_MSM_SMP2P) += smp2p.o smp2p_loopback.o smp2p_debug.o smp2p_sleepstate.o
|
||||
obj-$(CONFIG_MSM_QMI_INTERFACE) += qmi_interface.o
|
||||
obj-$(CONFIG_MSM_RPM_SMD) += rpm-smd.o
|
||||
obj-$(CONFIG_MSM_HVC) += hvc.o
|
||||
|
|
|
@ -1,819 +0,0 @@
|
|||
/* drivers/soc/qcom/smp2p_spinlock_test.c
|
||||
*
|
||||
* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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.
|
||||
*/
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/remote_spinlock.h>
|
||||
#include <soc/qcom/smem.h>
|
||||
#include "smem_private.h"
|
||||
#include "smp2p_private.h"
|
||||
#include "smp2p_test_common.h"
|
||||
|
||||
#define RS_END_THIEF_PID_BIT 20
|
||||
#define RS_END_THIEF_MASK 0x00f00000
|
||||
|
||||
/* Spinlock commands used for testing Apps<->RPM spinlocks. */
|
||||
enum RPM_SPINLOCK_CMDS {
|
||||
RPM_CMD_INVALID,
|
||||
RPM_CMD_START,
|
||||
RPM_CMD_LOCKED,
|
||||
RPM_CMD_UNLOCKED,
|
||||
RPM_CMD_END,
|
||||
};
|
||||
|
||||
/* Shared structure for testing Apps<->RPM spinlocks. */
|
||||
struct rpm_spinlock_test {
|
||||
uint32_t apps_cmd;
|
||||
uint32_t apps_lock_count;
|
||||
uint32_t rpm_cmd;
|
||||
uint32_t rpm_lock_count;
|
||||
};
|
||||
|
||||
static uint32_t ut_remote_spinlock_run_time = 1;
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock_core - Verify remote spinlock.
|
||||
*
|
||||
* @s: Pointer to output file
|
||||
* @remote_pid: Remote processor to test
|
||||
* @use_trylock: Use trylock to prevent an Apps deadlock if the
|
||||
* remote spinlock fails.
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock_core(struct seq_file *s, int remote_pid,
|
||||
bool use_trylock)
|
||||
{
|
||||
int failed = 0;
|
||||
unsigned lock_count = 0;
|
||||
struct msm_smp2p_out *handle = NULL;
|
||||
int ret;
|
||||
uint32_t test_request;
|
||||
uint32_t test_response;
|
||||
struct mock_cb_data cb_out;
|
||||
struct mock_cb_data cb_in;
|
||||
unsigned long flags;
|
||||
unsigned n;
|
||||
bool have_lock;
|
||||
bool timeout;
|
||||
int failed_tmp;
|
||||
int spinlock_owner;
|
||||
remote_spinlock_t *smem_spinlock;
|
||||
unsigned long end;
|
||||
|
||||
seq_printf(s, "Running %s for '%s' remote pid %d\n",
|
||||
__func__, smp2p_pid_to_name(remote_pid), remote_pid);
|
||||
|
||||
cb_out.initialized = false;
|
||||
cb_in.initialized = false;
|
||||
mock_cb_data_init(&cb_out);
|
||||
mock_cb_data_init(&cb_in);
|
||||
do {
|
||||
smem_spinlock = smem_get_remote_spinlock();
|
||||
UT_ASSERT_PTR(smem_spinlock, !=, NULL);
|
||||
|
||||
/* Open output entry */
|
||||
ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
|
||||
&cb_out.nb, &handle);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&cb_out.cb_completion, HZ * 2),
|
||||
>, 0);
|
||||
UT_ASSERT_INT(cb_out.cb_count, ==, 1);
|
||||
UT_ASSERT_INT(cb_out.event_open, ==, 1);
|
||||
|
||||
/* Open inbound entry */
|
||||
ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
|
||||
&cb_in.nb);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&cb_in.cb_completion, HZ * 2),
|
||||
>, 0);
|
||||
UT_ASSERT_INT(cb_in.cb_count, ==, 1);
|
||||
UT_ASSERT_INT(cb_in.event_open, ==, 1);
|
||||
|
||||
/* Send start */
|
||||
mock_cb_data_reset(&cb_in);
|
||||
mock_cb_data_reset(&cb_out);
|
||||
test_request = 0x0;
|
||||
SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
|
||||
SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
|
||||
SMP2P_SET_RMT_DATA(test_request, 0x0);
|
||||
ret = msm_smp2p_out_write(handle, test_request);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&cb_in.cb_completion, HZ * 2),
|
||||
>, 0);
|
||||
UT_ASSERT_INT(cb_in.cb_count, ==, 1);
|
||||
UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
|
||||
ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
|
||||
&test_response);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
test_response = SMP2P_GET_RMT_CMD(test_response);
|
||||
if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
|
||||
test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
|
||||
/* invalid response from remote - abort test */
|
||||
test_request = 0x0;
|
||||
SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
|
||||
SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
|
||||
SMP2P_SET_RMT_DATA(test_request, 0x0);
|
||||
ret = msm_smp2p_out_write(handle, test_request);
|
||||
UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
|
||||
test_response);
|
||||
}
|
||||
|
||||
/* Run spinlock test */
|
||||
if (use_trylock)
|
||||
seq_puts(s, "\tUsing remote_spin_trylock\n");
|
||||
else
|
||||
seq_puts(s, "\tUsing remote_spin_lock\n");
|
||||
|
||||
flags = 0;
|
||||
have_lock = false;
|
||||
timeout = false;
|
||||
spinlock_owner = 0;
|
||||
test_request = 0x0;
|
||||
SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
|
||||
end = jiffies + (ut_remote_spinlock_run_time * HZ);
|
||||
if (ut_remote_spinlock_run_time < 300) {
|
||||
seq_printf(s, "\tRunning test for %u seconds; ",
|
||||
ut_remote_spinlock_run_time);
|
||||
seq_puts(s,
|
||||
"on physical hardware please run >= 300 seconds by doing 'echo 300 > ut_remote_spinlock_time'\n");
|
||||
}
|
||||
while (time_is_after_jiffies(end)) {
|
||||
/* try to acquire spinlock */
|
||||
if (use_trylock) {
|
||||
unsigned long j_start = jiffies;
|
||||
while (!remote_spin_trylock_irqsave(
|
||||
smem_spinlock, flags)) {
|
||||
if (jiffies_to_msecs(jiffies - j_start)
|
||||
> 1000) {
|
||||
seq_puts(s,
|
||||
"\tFail: Timeout trying to get the lock\n");
|
||||
timeout = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (timeout)
|
||||
break;
|
||||
} else {
|
||||
remote_spin_lock_irqsave(smem_spinlock, flags);
|
||||
}
|
||||
have_lock = true;
|
||||
++lock_count;
|
||||
|
||||
/* tell the remote side that we have the lock */
|
||||
SMP2P_SET_RMT_DATA(test_request, lock_count);
|
||||
SMP2P_SET_RMT_CMD(test_request,
|
||||
SMP2P_LB_CMD_RSPIN_LOCKED);
|
||||
ret = msm_smp2p_out_write(handle, test_request);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
/* verify the other side doesn't say it has the lock */
|
||||
for (n = 0; n < 1000; ++n) {
|
||||
spinlock_owner =
|
||||
remote_spin_owner(smem_spinlock);
|
||||
if (spinlock_owner != SMEM_APPS) {
|
||||
/* lock stolen by remote side */
|
||||
seq_puts(s, "\tFail: Remote side: ");
|
||||
seq_printf(s, "%d stole lock pid: %d\n",
|
||||
remote_pid, spinlock_owner);
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
spinlock_owner = 0;
|
||||
|
||||
ret = msm_smp2p_in_read(remote_pid,
|
||||
SMP2P_RLPB_ENTRY_NAME, &test_response);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
test_response =
|
||||
SMP2P_GET_RMT_CMD(test_response);
|
||||
UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_UNLOCKED, ==,
|
||||
test_response);
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
/* tell remote side we are unlocked and release lock */
|
||||
SMP2P_SET_RMT_CMD(test_request,
|
||||
SMP2P_LB_CMD_RSPIN_UNLOCKED);
|
||||
(void)msm_smp2p_out_write(handle, test_request);
|
||||
have_lock = false;
|
||||
remote_spin_unlock_irqrestore(smem_spinlock, flags);
|
||||
}
|
||||
if (have_lock)
|
||||
remote_spin_unlock_irqrestore(smem_spinlock, flags);
|
||||
|
||||
/* End test */
|
||||
mock_cb_data_reset(&cb_in);
|
||||
SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
|
||||
SMP2P_SET_RMT_DATA(test_request, lock_count |
|
||||
(spinlock_owner << RS_END_THIEF_PID_BIT));
|
||||
(void)msm_smp2p_out_write(handle, test_request);
|
||||
|
||||
failed_tmp = failed;
|
||||
failed = false;
|
||||
do {
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&cb_in.cb_completion, HZ * 2),
|
||||
>, 0);
|
||||
reinit_completion(&cb_in.cb_completion);
|
||||
ret = msm_smp2p_in_read(remote_pid,
|
||||
SMP2P_RLPB_ENTRY_NAME, &test_response);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
} while (!failed &&
|
||||
SMP2P_GET_RMT_CMD(test_response) !=
|
||||
SMP2P_LB_CMD_RSPIN_END);
|
||||
if (failed)
|
||||
break;
|
||||
failed = failed_tmp;
|
||||
|
||||
test_response = SMP2P_GET_RMT_DATA(test_response);
|
||||
seq_puts(s, "\tLocked spinlock ");
|
||||
seq_printf(s, "local %u times; remote %u times",
|
||||
lock_count,
|
||||
test_response & ((1 << RS_END_THIEF_PID_BIT) - 1)
|
||||
);
|
||||
if (test_response & RS_END_THIEF_MASK) {
|
||||
seq_puts(s, "Remote side reporting lock stolen by ");
|
||||
seq_printf(s, "pid %d.\n",
|
||||
SMP2P_GET_BITS(test_response,
|
||||
RS_END_THIEF_MASK,
|
||||
RS_END_THIEF_PID_BIT));
|
||||
failed = 1;
|
||||
}
|
||||
seq_puts(s, "\n");
|
||||
|
||||
/* Cleanup */
|
||||
ret = msm_smp2p_out_close(&handle);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
UT_ASSERT_PTR(handle, ==, NULL);
|
||||
ret = msm_smp2p_in_unregister(remote_pid,
|
||||
SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
if (!failed && !timeout)
|
||||
seq_puts(s, "\tOK\n");
|
||||
} while (0);
|
||||
|
||||
if (failed) {
|
||||
if (handle) {
|
||||
/* send end command */
|
||||
test_request = 0;
|
||||
SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
|
||||
SMP2P_SET_RMT_DATA(test_request, lock_count);
|
||||
(void)msm_smp2p_out_write(handle, test_request);
|
||||
(void)msm_smp2p_out_close(&handle);
|
||||
}
|
||||
(void)msm_smp2p_in_unregister(remote_pid,
|
||||
SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
|
||||
|
||||
pr_err("%s: Failed\n", __func__);
|
||||
seq_puts(s, "\tFailed\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock_pid - Verify remote spinlock for a processor.
|
||||
*
|
||||
* @s: Pointer to output file
|
||||
* @pid: Processor to test
|
||||
* @use_trylock: Use trylock to prevent an Apps deadlock if the
|
||||
* remote spinlock fails.
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock_pid(struct seq_file *s, int pid,
|
||||
bool use_trylock)
|
||||
{
|
||||
struct smp2p_interrupt_config *int_cfg;
|
||||
|
||||
int_cfg = smp2p_get_interrupt_config();
|
||||
if (!int_cfg) {
|
||||
seq_puts(s, "Remote processor config unavailable\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
|
||||
return;
|
||||
|
||||
msm_smp2p_deinit_rmt_lpb_proc(pid);
|
||||
smp2p_ut_remote_spinlock_core(s, pid, use_trylock);
|
||||
msm_smp2p_init_rmt_lpb_proc(pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock(struct seq_file *s)
|
||||
{
|
||||
int pid;
|
||||
|
||||
for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
|
||||
smp2p_ut_remote_spinlock_pid(s, pid, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spin_trylock - Verify remote trylock for all processors.
|
||||
*
|
||||
* @s: Pointer to output file
|
||||
*/
|
||||
static void smp2p_ut_remote_spin_trylock(struct seq_file *s)
|
||||
{
|
||||
int pid;
|
||||
|
||||
for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
|
||||
smp2p_ut_remote_spinlock_pid(s, pid, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
*
|
||||
* This test verifies inbound and outbound functionality for all
|
||||
* configured remote processor.
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock_modem(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_pid(s, SMP2P_MODEM_PROC, false);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_adsp(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_pid(s, SMP2P_AUDIO_PROC, false);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_dsps(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_pid(s, SMP2P_SENSOR_PROC, false);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_wcnss(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_pid(s, SMP2P_WIRELESS_PROC, false);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_cdsp(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_pid(s, SMP2P_CDSP_PROC, false);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_tz(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_pid(s, SMP2P_TZ_PROC, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock_rpm - Verify remote spinlock.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
* @remote_pid: Remote processor to test
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock_rpm(struct seq_file *s)
|
||||
{
|
||||
int failed = 0;
|
||||
unsigned long flags;
|
||||
unsigned n;
|
||||
unsigned test_num;
|
||||
struct rpm_spinlock_test *data_ptr;
|
||||
remote_spinlock_t *smem_spinlock;
|
||||
bool have_lock;
|
||||
|
||||
seq_printf(s, "Running %s for Apps<->RPM Test\n",
|
||||
__func__);
|
||||
do {
|
||||
smem_spinlock = smem_get_remote_spinlock();
|
||||
UT_ASSERT_PTR(smem_spinlock, !=, NULL);
|
||||
|
||||
data_ptr = smem_alloc(SMEM_ID_VENDOR0,
|
||||
sizeof(struct rpm_spinlock_test), 0,
|
||||
SMEM_ANY_HOST_FLAG);
|
||||
UT_ASSERT_PTR(0, !=, data_ptr);
|
||||
|
||||
/* Send start */
|
||||
writel_relaxed(0, &data_ptr->apps_lock_count);
|
||||
writel_relaxed(RPM_CMD_START, &data_ptr->apps_cmd);
|
||||
|
||||
seq_puts(s, "\tWaiting for RPM to start test\n");
|
||||
for (n = 0; n < 1000; ++n) {
|
||||
if (readl_relaxed(&data_ptr->rpm_cmd) !=
|
||||
RPM_CMD_INVALID)
|
||||
break;
|
||||
usleep_range(1000, 1200);
|
||||
}
|
||||
if (readl_relaxed(&data_ptr->rpm_cmd) == RPM_CMD_INVALID) {
|
||||
/* timeout waiting for RPM */
|
||||
writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
|
||||
UT_ASSERT_INT(RPM_CMD_LOCKED, !=, RPM_CMD_INVALID);
|
||||
}
|
||||
|
||||
/* Run spinlock test */
|
||||
flags = 0;
|
||||
have_lock = false;
|
||||
for (test_num = 0; !failed && test_num < 10000; ++test_num) {
|
||||
/* acquire spinlock */
|
||||
remote_spin_lock_irqsave(smem_spinlock, flags);
|
||||
have_lock = true;
|
||||
data_ptr->apps_lock_count++;
|
||||
writel_relaxed(data_ptr->apps_lock_count,
|
||||
&data_ptr->apps_lock_count);
|
||||
writel_relaxed(RPM_CMD_LOCKED, &data_ptr->apps_cmd);
|
||||
/*
|
||||
* Ensure that the remote side sees our lock has
|
||||
* been acquired before we start polling their status.
|
||||
*/
|
||||
wmb();
|
||||
|
||||
/* verify the other side doesn't say it has the lock */
|
||||
for (n = 0; n < 1000; ++n) {
|
||||
UT_ASSERT_HEX(RPM_CMD_UNLOCKED, ==,
|
||||
readl_relaxed(&data_ptr->rpm_cmd));
|
||||
}
|
||||
if (failed)
|
||||
break;
|
||||
|
||||
/* release spinlock */
|
||||
have_lock = false;
|
||||
writel_relaxed(RPM_CMD_UNLOCKED, &data_ptr->apps_cmd);
|
||||
/*
|
||||
* Ensure that our status-update write was committed
|
||||
* before we unlock the spinlock.
|
||||
*/
|
||||
wmb();
|
||||
remote_spin_unlock_irqrestore(smem_spinlock, flags);
|
||||
}
|
||||
if (have_lock)
|
||||
remote_spin_unlock_irqrestore(smem_spinlock, flags);
|
||||
|
||||
/* End test */
|
||||
writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
|
||||
seq_printf(s, "\tLocked spinlock local %u remote %u\n",
|
||||
readl_relaxed(&data_ptr->apps_lock_count),
|
||||
readl_relaxed(&data_ptr->rpm_lock_count));
|
||||
|
||||
if (!failed)
|
||||
seq_puts(s, "\tOK\n");
|
||||
} while (0);
|
||||
|
||||
if (failed) {
|
||||
pr_err("%s: Failed\n", __func__);
|
||||
seq_puts(s, "\tFailed\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct rmt_spinlock_work_item {
|
||||
struct work_struct work;
|
||||
struct completion try_lock;
|
||||
struct completion locked;
|
||||
bool has_locked;
|
||||
};
|
||||
|
||||
static void ut_remote_spinlock_ssr_worker(struct work_struct *work)
|
||||
{
|
||||
remote_spinlock_t *smem_spinlock;
|
||||
unsigned long flags;
|
||||
struct rmt_spinlock_work_item *work_item =
|
||||
container_of(work, struct rmt_spinlock_work_item, work);
|
||||
|
||||
work_item->has_locked = false;
|
||||
complete(&work_item->try_lock);
|
||||
smem_spinlock = smem_get_remote_spinlock();
|
||||
if (!smem_spinlock) {
|
||||
pr_err("%s Failed\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
remote_spin_lock_irqsave(smem_spinlock, flags);
|
||||
remote_spin_unlock_irqrestore(smem_spinlock, flags);
|
||||
work_item->has_locked = true;
|
||||
complete(&work_item->locked);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock_ssr - Verify remote spinlock.
|
||||
*
|
||||
* @s: pointer to output file
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock_ssr(struct seq_file *s)
|
||||
{
|
||||
int failed = 0;
|
||||
unsigned long flags;
|
||||
remote_spinlock_t *smem_spinlock;
|
||||
int spinlock_owner = 0;
|
||||
|
||||
struct workqueue_struct *ws = NULL;
|
||||
struct rmt_spinlock_work_item work_item = { .has_locked = false };
|
||||
|
||||
seq_printf(s, " Running %s Test\n",
|
||||
__func__);
|
||||
do {
|
||||
smem_spinlock = smem_get_remote_spinlock();
|
||||
UT_ASSERT_PTR(smem_spinlock, !=, NULL);
|
||||
|
||||
ws = create_singlethread_workqueue("ut_remote_spinlock_ssr");
|
||||
UT_ASSERT_PTR(ws, !=, NULL);
|
||||
INIT_WORK(&work_item.work, ut_remote_spinlock_ssr_worker);
|
||||
init_completion(&work_item.try_lock);
|
||||
init_completion(&work_item.locked);
|
||||
|
||||
remote_spin_lock_irqsave(smem_spinlock, flags);
|
||||
/* Unlock local spin lock and hold HW spinlock */
|
||||
spin_unlock_irqrestore(&((smem_spinlock)->local), flags);
|
||||
|
||||
queue_work(ws, &work_item.work);
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&work_item.try_lock, HZ * 2), >, 0);
|
||||
UT_ASSERT_INT((int)work_item.has_locked, ==, 0);
|
||||
spinlock_owner = remote_spin_owner(smem_spinlock);
|
||||
UT_ASSERT_INT(spinlock_owner, ==, SMEM_APPS);
|
||||
remote_spin_release_all(SMEM_APPS);
|
||||
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&work_item.locked, HZ * 2), >, 0);
|
||||
|
||||
if (!failed)
|
||||
seq_puts(s, "\tOK\n");
|
||||
} while (0);
|
||||
|
||||
if (failed) {
|
||||
pr_err("%s: Failed\n", __func__);
|
||||
seq_puts(s, "\tFailed\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock_track_core - Verify remote spinlock.
|
||||
*
|
||||
* @s: Pointer to output file
|
||||
* @remote_pid: Remote processor to test
|
||||
*
|
||||
* This test has the remote subsystem grab the lock, and then has the local
|
||||
* subsystem attempt to grab the lock using the trylock() API. It then verifies
|
||||
* that the ID in the hw_spinlocks array matches the owner of the lock.
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock_track_core(struct seq_file *s,
|
||||
int remote_pid)
|
||||
{
|
||||
int failed = 0;
|
||||
struct msm_smp2p_out *handle = NULL;
|
||||
int ret;
|
||||
uint32_t test_request;
|
||||
uint32_t test_response;
|
||||
struct mock_cb_data cb_out;
|
||||
struct mock_cb_data cb_in;
|
||||
unsigned long flags;
|
||||
int stored_value;
|
||||
remote_spinlock_t *smem_spinlock;
|
||||
|
||||
seq_printf(s, "Running %s for '%s' remote pid %d\n",
|
||||
__func__, smp2p_pid_to_name(remote_pid), remote_pid);
|
||||
|
||||
cb_out.initialized = false;
|
||||
cb_in.initialized = false;
|
||||
mock_cb_data_init(&cb_out);
|
||||
mock_cb_data_init(&cb_in);
|
||||
do {
|
||||
smem_spinlock = smem_get_remote_spinlock();
|
||||
UT_ASSERT_PTR(smem_spinlock, !=, NULL);
|
||||
|
||||
/* Open output entry */
|
||||
ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
|
||||
&cb_out.nb, &handle);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&cb_out.cb_completion, HZ * 2),
|
||||
>, 0);
|
||||
UT_ASSERT_INT(cb_out.cb_count, ==, 1);
|
||||
UT_ASSERT_INT(cb_out.event_open, ==, 1);
|
||||
|
||||
/* Open inbound entry */
|
||||
ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
|
||||
&cb_in.nb);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&cb_in.cb_completion, HZ * 2),
|
||||
>, 0);
|
||||
UT_ASSERT_INT(cb_in.cb_count, ==, 1);
|
||||
UT_ASSERT_INT(cb_in.event_open, ==, 1);
|
||||
|
||||
/* Send start */
|
||||
mock_cb_data_reset(&cb_in);
|
||||
mock_cb_data_reset(&cb_out);
|
||||
test_request = 0x0;
|
||||
SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
|
||||
SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
|
||||
SMP2P_SET_RMT_DATA(test_request, 0x0);
|
||||
ret = msm_smp2p_out_write(handle, test_request);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
UT_ASSERT_INT(
|
||||
(int)wait_for_completion_timeout(
|
||||
&cb_in.cb_completion, HZ * 2),
|
||||
>, 0);
|
||||
UT_ASSERT_INT(cb_in.cb_count, ==, 1);
|
||||
UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
|
||||
ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
|
||||
&test_response);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
test_response = SMP2P_GET_RMT_CMD(test_response);
|
||||
if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
|
||||
test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
|
||||
/* invalid response from remote - abort test */
|
||||
test_request = 0x0;
|
||||
SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
|
||||
SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
|
||||
SMP2P_SET_RMT_DATA(test_request, 0x0);
|
||||
ret = msm_smp2p_out_write(handle, test_request);
|
||||
UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
|
||||
test_response);
|
||||
}
|
||||
|
||||
/* Run spinlock test */
|
||||
flags = 0;
|
||||
test_request = 0x0;
|
||||
SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
|
||||
|
||||
/* try to acquire spinlock */
|
||||
remote_spin_trylock_irqsave(smem_spinlock, flags);
|
||||
/*
|
||||
* Need to check against the locking token (PID + 1)
|
||||
* because the remote_spin_owner() API only returns the
|
||||
* PID.
|
||||
*/
|
||||
stored_value = remote_spin_get_hw_spinlocks_element(
|
||||
smem_spinlock);
|
||||
UT_ASSERT_INT(stored_value, ==,
|
||||
remote_spin_owner(smem_spinlock) + 1);
|
||||
UT_ASSERT_INT(stored_value, ==, remote_pid + 1);
|
||||
|
||||
/* End test */
|
||||
test_request = 0x0;
|
||||
SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
|
||||
SMP2P_SET_RMT_DATA(test_request, 0x0);
|
||||
(void)msm_smp2p_out_write(handle, test_request);
|
||||
|
||||
/* Cleanup */
|
||||
ret = msm_smp2p_out_close(&handle);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
UT_ASSERT_PTR(handle, ==, NULL);
|
||||
ret = msm_smp2p_in_unregister(remote_pid,
|
||||
SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
|
||||
UT_ASSERT_INT(ret, ==, 0);
|
||||
|
||||
if (!failed)
|
||||
seq_puts(s, "\tOK\n");
|
||||
} while (0);
|
||||
|
||||
if (failed) {
|
||||
if (handle) {
|
||||
/* send end command */
|
||||
test_request = 0x0;
|
||||
SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
|
||||
SMP2P_SET_RMT_DATA(test_request, 0x0);
|
||||
(void)msm_smp2p_out_write(handle, test_request);
|
||||
(void)msm_smp2p_out_close(&handle);
|
||||
}
|
||||
(void)msm_smp2p_in_unregister(remote_pid,
|
||||
SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
|
||||
|
||||
pr_err("%s: Failed\n", __func__);
|
||||
seq_puts(s, "\tFailed\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock_track - Verify PID tracking for modem.
|
||||
*
|
||||
* @s: Pointer to output file
|
||||
* @pid: The processor to test
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock_track(struct seq_file *s, int pid)
|
||||
{
|
||||
struct smp2p_interrupt_config *int_cfg;
|
||||
|
||||
int_cfg = smp2p_get_interrupt_config();
|
||||
if (!int_cfg) {
|
||||
seq_puts(s, "Remote processor config unavailable\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
|
||||
return;
|
||||
|
||||
msm_smp2p_deinit_rmt_lpb_proc(pid);
|
||||
smp2p_ut_remote_spinlock_track_core(s, pid);
|
||||
msm_smp2p_init_rmt_lpb_proc(pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* smp2p_ut_remote_spinlock_track - Verify PID tracking for all processors.
|
||||
*
|
||||
* @s: Pointer to output file
|
||||
*
|
||||
* This test verifies PID tracking for all configured remote processors.
|
||||
*/
|
||||
static void smp2p_ut_remote_spinlock_track_modem(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_track(s, SMP2P_MODEM_PROC);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_track_adsp(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_track(s, SMP2P_AUDIO_PROC);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_track_dsps(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_track(s, SMP2P_SENSOR_PROC);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_track_wcnss(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_track(s, SMP2P_WIRELESS_PROC);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_track_cdsp(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_track(s, SMP2P_CDSP_PROC);
|
||||
}
|
||||
|
||||
static void smp2p_ut_remote_spinlock_track_tz(struct seq_file *s)
|
||||
{
|
||||
smp2p_ut_remote_spinlock_track(s, SMP2P_TZ_PROC);
|
||||
}
|
||||
|
||||
static int __init smp2p_debugfs_init(void)
|
||||
{
|
||||
/*
|
||||
* Add Unit Test entries.
|
||||
*
|
||||
* The idea with unit tests is that you can run all of them
|
||||
* from ADB shell by doing:
|
||||
* adb shell
|
||||
* cat ut*
|
||||
*
|
||||
* And if particular tests fail, you can then repeatedly run the
|
||||
* failing tests as you debug and resolve the failing test.
|
||||
*/
|
||||
smp2p_debug_create("ut_remote_spinlock",
|
||||
smp2p_ut_remote_spinlock);
|
||||
smp2p_debug_create("ut_remote_spin_trylock",
|
||||
smp2p_ut_remote_spin_trylock);
|
||||
smp2p_debug_create("ut_remote_spinlock_modem",
|
||||
smp2p_ut_remote_spinlock_modem);
|
||||
smp2p_debug_create("ut_remote_spinlock_adsp",
|
||||
smp2p_ut_remote_spinlock_adsp);
|
||||
smp2p_debug_create("ut_remote_spinlock_dsps",
|
||||
smp2p_ut_remote_spinlock_dsps);
|
||||
smp2p_debug_create("ut_remote_spinlock_wcnss",
|
||||
smp2p_ut_remote_spinlock_wcnss);
|
||||
smp2p_debug_create("ut_remote_spinlock_cdsp",
|
||||
smp2p_ut_remote_spinlock_cdsp);
|
||||
smp2p_debug_create("ut_remote_spinlock_tz",
|
||||
smp2p_ut_remote_spinlock_tz);
|
||||
smp2p_debug_create("ut_remote_spinlock_rpm",
|
||||
smp2p_ut_remote_spinlock_rpm);
|
||||
smp2p_debug_create_u32("ut_remote_spinlock_time",
|
||||
&ut_remote_spinlock_run_time);
|
||||
smp2p_debug_create("ut_remote_spinlock_ssr",
|
||||
&smp2p_ut_remote_spinlock_ssr);
|
||||
smp2p_debug_create("ut_remote_spinlock_track_modem",
|
||||
&smp2p_ut_remote_spinlock_track_modem);
|
||||
smp2p_debug_create("ut_remote_spinlock_track_adsp",
|
||||
&smp2p_ut_remote_spinlock_track_adsp);
|
||||
smp2p_debug_create("ut_remote_spinlock_track_dsps",
|
||||
&smp2p_ut_remote_spinlock_track_dsps);
|
||||
smp2p_debug_create("ut_remote_spinlock_track_wcnss",
|
||||
&smp2p_ut_remote_spinlock_track_wcnss);
|
||||
smp2p_debug_create("ut_remote_spinlock_track_cdsp",
|
||||
&smp2p_ut_remote_spinlock_track_cdsp);
|
||||
smp2p_debug_create("ut_remote_spinlock_track_tz",
|
||||
&smp2p_ut_remote_spinlock_track_tz);
|
||||
return 0;
|
||||
}
|
||||
module_init(smp2p_debugfs_init);
|
File diff suppressed because it is too large
Load diff
|
@ -1,214 +0,0 @@
|
|||
/* drivers/soc/qcom/smp2p_test_common.h
|
||||
*
|
||||
* Copyright (c) 2013-2014,2016 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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.
|
||||
*/
|
||||
#ifndef _SMP2P_TEST_COMMON_H_
|
||||
#define _SMP2P_TEST_COMMON_H_
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
/**
|
||||
* Unit test assertion for logging test cases.
|
||||
*
|
||||
* @a lval
|
||||
* @b rval
|
||||
* @cmp comparison operator
|
||||
*
|
||||
* Assertion fails if (@a cmp @b) is not true which then
|
||||
* logs the function and line number where the error occurred
|
||||
* along with the values of @a and @b.
|
||||
*
|
||||
* Assumes that the following local variables exist:
|
||||
* @s - sequential output file pointer
|
||||
* @failed - set to true if test fails
|
||||
*/
|
||||
#define UT_ASSERT_INT(a, cmp, b) \
|
||||
{ \
|
||||
int a_tmp = (a); \
|
||||
int b_tmp = (b); \
|
||||
if (!((a_tmp)cmp(b_tmp))) { \
|
||||
seq_printf(s, "%s:%d Fail: " #a "(%d) " #cmp " " #b "(%d)\n", \
|
||||
__func__, __LINE__, \
|
||||
a_tmp, b_tmp); \
|
||||
failed = 1; \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define UT_ASSERT_PTR(a, cmp, b) \
|
||||
{ \
|
||||
void *a_tmp = (a); \
|
||||
void *b_tmp = (b); \
|
||||
if (!((a_tmp)cmp(b_tmp))) { \
|
||||
seq_printf(s, "%s:%d Fail: " #a "(%pK) " #cmp \
|
||||
" " #b "(%pK)\n", \
|
||||
__func__, __LINE__, \
|
||||
a_tmp, b_tmp); \
|
||||
failed = 1; \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define UT_ASSERT_UINT(a, cmp, b) \
|
||||
{ \
|
||||
unsigned a_tmp = (a); \
|
||||
unsigned b_tmp = (b); \
|
||||
if (!((a_tmp)cmp(b_tmp))) { \
|
||||
seq_printf(s, "%s:%d Fail: " #a "(%u) " #cmp " " #b "(%u)\n", \
|
||||
__func__, __LINE__, \
|
||||
a_tmp, b_tmp); \
|
||||
failed = 1; \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define UT_ASSERT_HEX(a, cmp, b) \
|
||||
{ \
|
||||
unsigned a_tmp = (a); \
|
||||
unsigned b_tmp = (b); \
|
||||
if (!((a_tmp)cmp(b_tmp))) { \
|
||||
seq_printf(s, "%s:%d Fail: " #a "(%x) " #cmp " " #b "(%x)\n", \
|
||||
__func__, __LINE__, \
|
||||
a_tmp, b_tmp); \
|
||||
failed = 1; \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* In-range unit test assertion for test cases.
|
||||
*
|
||||
* @a lval
|
||||
* @minv Minimum value
|
||||
* @maxv Maximum value
|
||||
*
|
||||
* Assertion fails if @a is not on the exclusive range minv, maxv
|
||||
* ((@a < @minv) or (@a > @maxv)). In the failure case, the macro
|
||||
* logs the function and line number where the error occurred along
|
||||
* with the values of @a and @minv, @maxv.
|
||||
*
|
||||
* Assumes that the following local variables exist:
|
||||
* @s - sequential output file pointer
|
||||
* @failed - set to true if test fails
|
||||
*/
|
||||
#define UT_ASSERT_INT_IN_RANGE(a, minv, maxv) \
|
||||
{ \
|
||||
int a_tmp = (a); \
|
||||
int minv_tmp = (minv); \
|
||||
int maxv_tmp = (maxv); \
|
||||
if (((a_tmp) < (minv_tmp)) || ((a_tmp) > (maxv_tmp))) { \
|
||||
seq_printf(s, "%s:%d Fail: " #a "(%d) < " #minv "(%d) or " \
|
||||
#a "(%d) > " #maxv "(%d)\n", \
|
||||
__func__, __LINE__, \
|
||||
a_tmp, minv_tmp, a_tmp, maxv_tmp); \
|
||||
failed = 1; \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* Structure to track state changes for the notifier callback. */
|
||||
struct mock_cb_data {
|
||||
bool initialized;
|
||||
spinlock_t lock;
|
||||
struct notifier_block nb;
|
||||
|
||||
/* events */
|
||||
struct completion cb_completion;
|
||||
int cb_count;
|
||||
int event_open;
|
||||
int event_entry_update;
|
||||
struct msm_smp2p_update_notif entry_data;
|
||||
};
|
||||
|
||||
void smp2p_debug_create(const char *name, void (*show)(struct seq_file *));
|
||||
void smp2p_debug_create_u32(const char *name, uint32_t *value);
|
||||
static inline int smp2p_test_notify(struct notifier_block *self,
|
||||
unsigned long event, void *data);
|
||||
|
||||
/**
|
||||
* Reset mock callback data to default values.
|
||||
*
|
||||
* @cb: Mock callback data
|
||||
*/
|
||||
static inline void mock_cb_data_reset(struct mock_cb_data *cb)
|
||||
{
|
||||
reinit_completion(&cb->cb_completion);
|
||||
cb->cb_count = 0;
|
||||
cb->event_open = 0;
|
||||
cb->event_entry_update = 0;
|
||||
memset(&cb->entry_data, 0,
|
||||
sizeof(struct msm_smp2p_update_notif));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize mock callback data.
|
||||
*
|
||||
* @cb: Mock callback data
|
||||
*/
|
||||
static inline void mock_cb_data_init(struct mock_cb_data *cb)
|
||||
{
|
||||
if (!cb->initialized) {
|
||||
init_completion(&cb->cb_completion);
|
||||
spin_lock_init(&cb->lock);
|
||||
cb->initialized = true;
|
||||
cb->nb.notifier_call = smp2p_test_notify;
|
||||
memset(&cb->entry_data, 0,
|
||||
sizeof(struct msm_smp2p_update_notif));
|
||||
}
|
||||
mock_cb_data_reset(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifier function passed into SMP2P for testing.
|
||||
*
|
||||
* @self: Pointer to calling notifier block
|
||||
* @event: Event
|
||||
* @data: Event-specific data
|
||||
* @returns: 0
|
||||
*/
|
||||
static inline int smp2p_test_notify(struct notifier_block *self,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct mock_cb_data *cb_data_ptr;
|
||||
unsigned long flags;
|
||||
|
||||
cb_data_ptr = container_of(self, struct mock_cb_data, nb);
|
||||
|
||||
spin_lock_irqsave(&cb_data_ptr->lock, flags);
|
||||
|
||||
switch (event) {
|
||||
case SMP2P_OPEN:
|
||||
++cb_data_ptr->event_open;
|
||||
if (data) {
|
||||
cb_data_ptr->entry_data =
|
||||
*(struct msm_smp2p_update_notif *)(data);
|
||||
}
|
||||
break;
|
||||
case SMP2P_ENTRY_UPDATE:
|
||||
++cb_data_ptr->event_entry_update;
|
||||
if (data) {
|
||||
cb_data_ptr->entry_data =
|
||||
*(struct msm_smp2p_update_notif *)(data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_err("%s Unknown event\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
++cb_data_ptr->cb_count;
|
||||
complete(&cb_data_ptr->cb_completion);
|
||||
spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
#endif /* _SMP2P_TEST_COMMON_H_ */
|
Loading…
Add table
Reference in a new issue