1/* 2 * Copyright (C) 2007 Atmel Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8#include <linux/init.h> 9#include <linux/sched.h> 10#include <linux/spinlock.h> 11 12#include <asm/ocd.h> 13 14static long ocd_count; 15static spinlock_t ocd_lock; 16 17/** 18 * ocd_enable - enable on-chip debugging 19 * @child: task to be debugged 20 * 21 * If @child is non-NULL, ocd_enable() first checks if debugging has 22 * already been enabled for @child, and if it has, does nothing. 23 * 24 * If @child is NULL (e.g. when debugging the kernel), or debugging 25 * has not already been enabled for it, ocd_enable() increments the 26 * reference count and enables the debugging hardware. 27 */ 28void ocd_enable(struct task_struct *child) 29{ 30 u32 dc; 31 32 if (child) 33 pr_debug("ocd_enable: child=%s [%u]\n", 34 child->comm, child->pid); 35 else 36 pr_debug("ocd_enable (no child)\n"); 37 38 if (!child || !test_and_set_tsk_thread_flag(child, TIF_DEBUG)) { 39 spin_lock(&ocd_lock); 40 ocd_count++; 41 dc = ocd_read(DC); 42 dc |= (1 << OCD_DC_MM_BIT) | (1 << OCD_DC_DBE_BIT); 43 ocd_write(DC, dc); 44 spin_unlock(&ocd_lock); 45 } 46} 47 48/** 49 * ocd_disable - disable on-chip debugging 50 * @child: task that was being debugged, but isn't anymore 51 * 52 * If @child is non-NULL, ocd_disable() checks if debugging is enabled 53 * for @child, and if it isn't, does nothing. 54 * 55 * If @child is NULL (e.g. when debugging the kernel), or debugging is 56 * enabled, ocd_disable() decrements the reference count, and if it 57 * reaches zero, disables the debugging hardware. 58 */ 59void ocd_disable(struct task_struct *child) 60{ 61 u32 dc; 62 63 if (!child) 64 pr_debug("ocd_disable (no child)\n"); 65 else if (test_tsk_thread_flag(child, TIF_DEBUG)) 66 pr_debug("ocd_disable: child=%s [%u]\n", 67 child->comm, child->pid); 68 69 if (!child || test_and_clear_tsk_thread_flag(child, TIF_DEBUG)) { 70 spin_lock(&ocd_lock); 71 ocd_count--; 72 73 WARN_ON(ocd_count < 0); 74 75 if (ocd_count <= 0) { 76 dc = ocd_read(DC); 77 dc &= ~((1 << OCD_DC_MM_BIT) | (1 << OCD_DC_DBE_BIT)); 78 ocd_write(DC, dc); 79 } 80 spin_unlock(&ocd_lock); 81 } 82} 83 84#ifdef CONFIG_DEBUG_FS 85#include <linux/debugfs.h> 86#include <linux/module.h> 87 88static struct dentry *ocd_debugfs_root; 89static struct dentry *ocd_debugfs_DC; 90static struct dentry *ocd_debugfs_DS; 91static struct dentry *ocd_debugfs_count; 92 93static int ocd_DC_get(void *data, u64 *val) 94{ 95 *val = ocd_read(DC); 96 return 0; 97} 98static int ocd_DC_set(void *data, u64 val) 99{ 100 ocd_write(DC, val); 101 return 0; 102} 103DEFINE_SIMPLE_ATTRIBUTE(fops_DC, ocd_DC_get, ocd_DC_set, "0x%08llx\n"); 104 105static int ocd_DS_get(void *data, u64 *val) 106{ 107 *val = ocd_read(DS); 108 return 0; 109} 110DEFINE_SIMPLE_ATTRIBUTE(fops_DS, ocd_DS_get, NULL, "0x%08llx\n"); 111 112static int ocd_count_get(void *data, u64 *val) 113{ 114 *val = ocd_count; 115 return 0; 116} 117DEFINE_SIMPLE_ATTRIBUTE(fops_count, ocd_count_get, NULL, "%lld\n"); 118 119static void ocd_debugfs_init(void) 120{ 121 struct dentry *root; 122 123 root = debugfs_create_dir("ocd", NULL); 124 if (IS_ERR(root) || !root) 125 goto err_root; 126 ocd_debugfs_root = root; 127 128 ocd_debugfs_DC = debugfs_create_file("DC", S_IRUSR | S_IWUSR, 129 root, NULL, &fops_DC); 130 if (!ocd_debugfs_DC) 131 goto err_DC; 132 133 ocd_debugfs_DS = debugfs_create_file("DS", S_IRUSR, root, 134 NULL, &fops_DS); 135 if (!ocd_debugfs_DS) 136 goto err_DS; 137 138 ocd_debugfs_count = debugfs_create_file("count", S_IRUSR, root, 139 NULL, &fops_count); 140 if (!ocd_debugfs_count) 141 goto err_count; 142 143 return; 144 145err_count: 146 debugfs_remove(ocd_debugfs_DS); 147err_DS: 148 debugfs_remove(ocd_debugfs_DC); 149err_DC: 150 debugfs_remove(ocd_debugfs_root); 151err_root: 152 printk(KERN_WARNING "OCD: Failed to create debugfs entries\n"); 153} 154#else 155static inline void ocd_debugfs_init(void) 156{ 157 158} 159#endif 160 161static int __init ocd_init(void) 162{ 163 spin_lock_init(&ocd_lock); 164 ocd_debugfs_init(); 165 return 0; 166} 167arch_initcall(ocd_init); 168