diff options
Diffstat (limited to 'drivers/gpio/gpio-msm-smp2p-test.c')
-rw-r--r-- | drivers/gpio/gpio-msm-smp2p-test.c | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-msm-smp2p-test.c b/drivers/gpio/gpio-msm-smp2p-test.c new file mode 100644 index 000000000000..5907513b43c0 --- /dev/null +++ b/drivers/gpio/gpio-msm-smp2p-test.c @@ -0,0 +1,762 @@ +/* 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); |