1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Compaq Hot Plug Controller Driver 4 * 5 * Copyright (C) 1995,2001 Compaq Computer Corporation 6 * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) 7 * Copyright (C) 2001 IBM Corp. 8 * 9 * All rights reserved. 10 * 11 * Send feedback to <greg@kroah.com> 12 * 13 */ 14 15 #include <linux/module.h> 16 #include <linux/kernel.h> 17 #include <linux/slab.h> 18 #include <linux/types.h> 19 #include <linux/proc_fs.h> 20 #include <linux/workqueue.h> 21 #include <linux/pci.h> 22 #include <linux/pci_hotplug.h> 23 #include <linux/mutex.h> 24 #include <linux/debugfs.h> 25 #include "cpqphp.h" 26 27 static DEFINE_MUTEX(cpqphp_mutex); 28 static int show_ctrl(struct controller *ctrl, char *buf) 29 { 30 char *out = buf; 31 int index; 32 struct pci_resource *res; 33 34 out += sprintf(buf, "Free resources: memory\n"); 35 index = 11; 36 res = ctrl->mem_head; 37 while (res && index--) { 38 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 39 res = res->next; 40 } 41 out += sprintf(out, "Free resources: prefetchable memory\n"); 42 index = 11; 43 res = ctrl->p_mem_head; 44 while (res && index--) { 45 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 46 res = res->next; 47 } 48 out += sprintf(out, "Free resources: IO\n"); 49 index = 11; 50 res = ctrl->io_head; 51 while (res && index--) { 52 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 53 res = res->next; 54 } 55 out += sprintf(out, "Free resources: bus numbers\n"); 56 index = 11; 57 res = ctrl->bus_head; 58 while (res && index--) { 59 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 60 res = res->next; 61 } 62 63 return out - buf; 64 } 65 66 static int show_dev(struct controller *ctrl, char *buf) 67 { 68 char *out = buf; 69 int index; 70 struct pci_resource *res; 71 struct pci_func *new_slot; 72 struct slot *slot; 73 74 slot = ctrl->slot; 75 76 while (slot) { 77 new_slot = cpqhp_slot_find(slot->bus, slot->device, 0); 78 if (!new_slot) 79 break; 80 out += sprintf(out, "assigned resources: memory\n"); 81 index = 11; 82 res = new_slot->mem_head; 83 while (res && index--) { 84 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 85 res = res->next; 86 } 87 out += sprintf(out, "assigned resources: prefetchable memory\n"); 88 index = 11; 89 res = new_slot->p_mem_head; 90 while (res && index--) { 91 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 92 res = res->next; 93 } 94 out += sprintf(out, "assigned resources: IO\n"); 95 index = 11; 96 res = new_slot->io_head; 97 while (res && index--) { 98 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 99 res = res->next; 100 } 101 out += sprintf(out, "assigned resources: bus numbers\n"); 102 index = 11; 103 res = new_slot->bus_head; 104 while (res && index--) { 105 out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length); 106 res = res->next; 107 } 108 slot = slot->next; 109 } 110 111 return out - buf; 112 } 113 114 static int spew_debug_info(struct controller *ctrl, char *data, int size) 115 { 116 int used; 117 118 used = size - show_ctrl(ctrl, data); 119 used = (size - used) - show_dev(ctrl, &data[used]); 120 return used; 121 } 122 123 struct ctrl_dbg { 124 int size; 125 char *data; 126 struct controller *ctrl; 127 }; 128 129 #define MAX_OUTPUT (4*PAGE_SIZE) 130 131 static int open(struct inode *inode, struct file *file) 132 { 133 struct controller *ctrl = inode->i_private; 134 struct ctrl_dbg *dbg; 135 int retval = -ENOMEM; 136 137 mutex_lock(&cpqphp_mutex); 138 dbg = kmalloc(sizeof(*dbg), GFP_KERNEL); 139 if (!dbg) 140 goto exit; 141 dbg->data = kmalloc(MAX_OUTPUT, GFP_KERNEL); 142 if (!dbg->data) { 143 kfree(dbg); 144 goto exit; 145 } 146 dbg->size = spew_debug_info(ctrl, dbg->data, MAX_OUTPUT); 147 file->private_data = dbg; 148 retval = 0; 149 exit: 150 mutex_unlock(&cpqphp_mutex); 151 return retval; 152 } 153 154 static loff_t lseek(struct file *file, loff_t off, int whence) 155 { 156 struct ctrl_dbg *dbg = file->private_data; 157 return fixed_size_llseek(file, off, whence, dbg->size); 158 } 159 160 static ssize_t read(struct file *file, char __user *buf, 161 size_t nbytes, loff_t *ppos) 162 { 163 struct ctrl_dbg *dbg = file->private_data; 164 return simple_read_from_buffer(buf, nbytes, ppos, dbg->data, dbg->size); 165 } 166 167 static int release(struct inode *inode, struct file *file) 168 { 169 struct ctrl_dbg *dbg = file->private_data; 170 171 kfree(dbg->data); 172 kfree(dbg); 173 return 0; 174 } 175 176 static const struct file_operations debug_ops = { 177 .owner = THIS_MODULE, 178 .open = open, 179 .llseek = lseek, 180 .read = read, 181 .release = release, 182 }; 183 184 static struct dentry *root; 185 186 void cpqhp_initialize_debugfs(void) 187 { 188 if (!root) 189 root = debugfs_create_dir("cpqhp", NULL); 190 } 191 192 void cpqhp_shutdown_debugfs(void) 193 { 194 debugfs_remove(root); 195 } 196 197 void cpqhp_create_debugfs_files(struct controller *ctrl) 198 { 199 ctrl->dentry = debugfs_create_file(dev_name(&ctrl->pci_dev->dev), 200 S_IRUGO, root, ctrl, &debug_ops); 201 } 202 203 void cpqhp_remove_debugfs_files(struct controller *ctrl) 204 { 205 debugfs_remove(ctrl->dentry); 206 ctrl->dentry = NULL; 207 } 208 209