/* drivers/soc/qcom/smsm_debug.c * * Copyright (C) 2007 Google, Inc. * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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 #include #include #include #include #include #if defined(CONFIG_DEBUG_FS) static void debug_read_smsm_state(struct seq_file *s) { uint32_t *smsm; int n; smsm = smem_find(SMEM_SMSM_SHARED_STATE, SMSM_NUM_ENTRIES * sizeof(uint32_t), 0, SMEM_ANY_HOST_FLAG); if (smsm) for (n = 0; n < SMSM_NUM_ENTRIES; n++) seq_printf(s, "entry %d: 0x%08x\n", n, smsm[n]); } struct SMSM_CB_DATA { int cb_count; void *data; uint32_t old_state; uint32_t new_state; }; static struct SMSM_CB_DATA smsm_cb_data; static struct completion smsm_cb_completion; static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state) { smsm_cb_data.cb_count++; smsm_cb_data.old_state = old_state; smsm_cb_data.new_state = new_state; smsm_cb_data.data = data; complete_all(&smsm_cb_completion); } #define UT_EQ_INT(a, b) \ { \ if ((a) != (b)) { \ seq_printf(s, "%s:%d " #a "(%d) != " #b "(%d)\n", \ __func__, __LINE__, \ a, b); \ break; \ } \ } #define UT_GT_INT(a, b) \ { \ if ((a) <= (b)) { \ seq_printf(s, "%s:%d " #a "(%d) > " #b "(%d)\n", \ __func__, __LINE__, \ a, b); \ break; \ } \ } #define SMSM_CB_TEST_INIT() \ do { \ smsm_cb_data.cb_count = 0; \ smsm_cb_data.old_state = 0; \ smsm_cb_data.new_state = 0; \ smsm_cb_data.data = 0; \ } while (0) static void debug_test_smsm(struct seq_file *s) { int test_num = 0; int ret; /* Test case 1 - Register new callback for notification */ do { test_num++; SMSM_CB_TEST_INIT(); ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT, smsm_state_cb, (void *)0x1234); UT_EQ_INT(ret, 0); /* de-assert SMSM_SMD_INIT to trigger state update */ UT_EQ_INT(smsm_cb_data.cb_count, 0); reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 1); UT_EQ_INT(smsm_cb_data.old_state & SMSM_SMDINIT, SMSM_SMDINIT); UT_EQ_INT(smsm_cb_data.new_state & SMSM_SMDINIT, 0x0); UT_EQ_INT((int)(uintptr_t)smsm_cb_data.data, 0x1234); /* re-assert SMSM_SMD_INIT to trigger state update */ reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 2); UT_EQ_INT(smsm_cb_data.old_state & SMSM_SMDINIT, 0x0); UT_EQ_INT(smsm_cb_data.new_state & SMSM_SMDINIT, SMSM_SMDINIT); /* deregister callback */ ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_SMDINIT, smsm_state_cb, (void *)0x1234); UT_EQ_INT(ret, 2); /* make sure state change doesn't cause any more callbacks */ reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 2); seq_printf(s, "Test %d - PASS\n", test_num); } while (0); /* Test case 2 - Update already registered callback */ do { test_num++; SMSM_CB_TEST_INIT(); ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT, smsm_state_cb, (void *)0x1234); UT_EQ_INT(ret, 0); ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_INIT, smsm_state_cb, (void *)0x1234); UT_EQ_INT(ret, 1); /* verify both callback bits work */ reinit_completion(&smsm_cb_completion); UT_EQ_INT(smsm_cb_data.cb_count, 0); smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 1); reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 2); reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 3); reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 4); /* deregister 1st callback */ ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_SMDINIT, smsm_state_cb, (void *)0x1234); UT_EQ_INT(ret, 1); reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 4); reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 5); reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 6); /* deregister 2nd callback */ ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_INIT, smsm_state_cb, (void *)0x1234); UT_EQ_INT(ret, 2); /* make sure state change doesn't cause any more callbacks */ reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0); smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT); UT_EQ_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 6); seq_printf(s, "Test %d - PASS\n", test_num); } while (0); /* Test case 3 - Two callback registrations with different data */ do { test_num++; SMSM_CB_TEST_INIT(); ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_SMDINIT, smsm_state_cb, (void *)0x1234); UT_EQ_INT(ret, 0); ret = smsm_state_cb_register(SMSM_APPS_STATE, SMSM_INIT, smsm_state_cb, (void *)0x3456); UT_EQ_INT(ret, 0); /* verify both callbacks work */ reinit_completion(&smsm_cb_completion); UT_EQ_INT(smsm_cb_data.cb_count, 0); smsm_change_state(SMSM_APPS_STATE, SMSM_SMDINIT, 0x0); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 1); UT_EQ_INT((int)(uintptr_t)smsm_cb_data.data, 0x1234); reinit_completion(&smsm_cb_completion); smsm_change_state(SMSM_APPS_STATE, SMSM_INIT, 0x0); UT_GT_INT((int)wait_for_completion_timeout(&smsm_cb_completion, msecs_to_jiffies(20)), 0); UT_EQ_INT(smsm_cb_data.cb_count, 2); UT_EQ_INT((int)(uintptr_t)smsm_cb_data.data, 0x3456); /* cleanup and unregister * degregister in reverse to verify data field is * being used */ smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_SMDINIT); smsm_change_state(SMSM_APPS_STATE, 0x0, SMSM_INIT); ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_INIT, smsm_state_cb, (void *)0x3456); UT_EQ_INT(ret, 2); ret = smsm_state_cb_deregister(SMSM_APPS_STATE, SMSM_SMDINIT, smsm_state_cb, (void *)0x1234); UT_EQ_INT(ret, 2); seq_printf(s, "Test %d - PASS\n", test_num); } while (0); } static void debug_read_intr_mask(struct seq_file *s) { uint32_t *smsm; int m, n; smsm = smem_find(SMEM_SMSM_CPU_INTR_MASK, SMSM_NUM_ENTRIES * SMSM_NUM_HOSTS * sizeof(uint32_t), 0, SMEM_ANY_HOST_FLAG); if (smsm) for (m = 0; m < SMSM_NUM_ENTRIES; m++) { seq_printf(s, "entry %d:", m); for (n = 0; n < SMSM_NUM_HOSTS; n++) seq_printf(s, " host %d: 0x%08x", n, smsm[m * SMSM_NUM_HOSTS + n]); seq_puts(s, "\n"); } } static int debugfs_show(struct seq_file *s, void *data) { void (*show)(struct seq_file *) = s->private; show(s); return 0; } static int debug_open(struct inode *inode, struct file *file) { return single_open(file, debugfs_show, inode->i_private); } static const struct file_operations debug_ops = { .open = debug_open, .release = single_release, .read = seq_read, .llseek = seq_lseek, }; static void debug_create(const char *name, umode_t mode, struct dentry *dent, void (*show)(struct seq_file *)) { struct dentry *file; file = debugfs_create_file(name, mode, dent, show, &debug_ops); if (!file) pr_err("%s: unable to create file '%s'\n", __func__, name); } static int __init smsm_debugfs_init(void) { struct dentry *dent; dent = debugfs_create_dir("smsm", 0); if (IS_ERR(dent)) return PTR_ERR(dent); debug_create("state", 0444, dent, debug_read_smsm_state); debug_create("intr_mask", 0444, dent, debug_read_intr_mask); debug_create("smsm_test", 0444, dent, debug_test_smsm); init_completion(&smsm_cb_completion); return 0; } late_initcall(smsm_debugfs_init); #endif