1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright 2017 Thomas Gleixner <tglx@linutronix.de> |
3 | |
4 | #include <linux/irqdomain.h> |
5 | #include <linux/irq.h> |
6 | #include <linux/uaccess.h> |
7 | |
8 | #include "internals.h" |
9 | |
10 | static struct dentry *irq_dir; |
11 | |
12 | struct irq_bit_descr { |
13 | unsigned int mask; |
14 | char *name; |
15 | }; |
16 | #define BIT_MASK_DESCR(m) { .mask = m, .name = #m } |
17 | |
18 | static void irq_debug_show_bits(struct seq_file *m, int ind, unsigned int state, |
19 | const struct irq_bit_descr *sd, int size) |
20 | { |
21 | int i; |
22 | |
23 | for (i = 0; i < size; i++, sd++) { |
24 | if (state & sd->mask) |
25 | seq_printf(m, fmt: "%*s%s\n" , ind + 12, "" , sd->name); |
26 | } |
27 | } |
28 | |
29 | #ifdef CONFIG_SMP |
30 | static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc) |
31 | { |
32 | struct irq_data *data = irq_desc_get_irq_data(desc); |
33 | const struct cpumask *msk; |
34 | |
35 | msk = irq_data_get_affinity_mask(d: data); |
36 | seq_printf(m, fmt: "affinity: %*pbl\n" , cpumask_pr_args(msk)); |
37 | #ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK |
38 | msk = irq_data_get_effective_affinity_mask(d: data); |
39 | seq_printf(m, fmt: "effectiv: %*pbl\n" , cpumask_pr_args(msk)); |
40 | #endif |
41 | #ifdef CONFIG_GENERIC_PENDING_IRQ |
42 | msk = desc->pending_mask; |
43 | seq_printf(m, fmt: "pending: %*pbl\n" , cpumask_pr_args(msk)); |
44 | #endif |
45 | } |
46 | #else |
47 | static void irq_debug_show_masks(struct seq_file *m, struct irq_desc *desc) { } |
48 | #endif |
49 | |
50 | static const struct irq_bit_descr irqchip_flags[] = { |
51 | BIT_MASK_DESCR(IRQCHIP_SET_TYPE_MASKED), |
52 | BIT_MASK_DESCR(IRQCHIP_EOI_IF_HANDLED), |
53 | BIT_MASK_DESCR(IRQCHIP_MASK_ON_SUSPEND), |
54 | BIT_MASK_DESCR(IRQCHIP_ONOFFLINE_ENABLED), |
55 | BIT_MASK_DESCR(IRQCHIP_SKIP_SET_WAKE), |
56 | BIT_MASK_DESCR(IRQCHIP_ONESHOT_SAFE), |
57 | BIT_MASK_DESCR(IRQCHIP_EOI_THREADED), |
58 | BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI), |
59 | BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI), |
60 | BIT_MASK_DESCR(IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND), |
61 | BIT_MASK_DESCR(IRQCHIP_IMMUTABLE), |
62 | }; |
63 | |
64 | static void |
65 | irq_debug_show_chip(struct seq_file *m, struct irq_data *data, int ind) |
66 | { |
67 | struct irq_chip *chip = data->chip; |
68 | |
69 | if (!chip) { |
70 | seq_printf(m, fmt: "chip: None\n" ); |
71 | return; |
72 | } |
73 | seq_printf(m, fmt: "%*schip: " , ind, "" ); |
74 | if (chip->irq_print_chip) |
75 | chip->irq_print_chip(data, m); |
76 | else |
77 | seq_printf(m, fmt: "%s" , chip->name); |
78 | seq_printf(m, fmt: "\n%*sflags: 0x%lx\n" , ind + 1, "" , chip->flags); |
79 | irq_debug_show_bits(m, ind, state: chip->flags, sd: irqchip_flags, |
80 | ARRAY_SIZE(irqchip_flags)); |
81 | } |
82 | |
83 | static void |
84 | irq_debug_show_data(struct seq_file *m, struct irq_data *data, int ind) |
85 | { |
86 | seq_printf(m, fmt: "%*sdomain: %s\n" , ind, "" , |
87 | data->domain ? data->domain->name : "" ); |
88 | seq_printf(m, fmt: "%*shwirq: 0x%lx\n" , ind + 1, "" , data->hwirq); |
89 | irq_debug_show_chip(m, data, ind: ind + 1); |
90 | if (data->domain && data->domain->ops && data->domain->ops->debug_show) |
91 | data->domain->ops->debug_show(m, NULL, data, ind + 1); |
92 | #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY |
93 | if (!data->parent_data) |
94 | return; |
95 | seq_printf(m, fmt: "%*sparent:\n" , ind + 1, "" ); |
96 | irq_debug_show_data(m, data: data->parent_data, ind: ind + 4); |
97 | #endif |
98 | } |
99 | |
100 | static const struct irq_bit_descr irqdata_states[] = { |
101 | BIT_MASK_DESCR(IRQ_TYPE_EDGE_RISING), |
102 | BIT_MASK_DESCR(IRQ_TYPE_EDGE_FALLING), |
103 | BIT_MASK_DESCR(IRQ_TYPE_LEVEL_HIGH), |
104 | BIT_MASK_DESCR(IRQ_TYPE_LEVEL_LOW), |
105 | BIT_MASK_DESCR(IRQD_LEVEL), |
106 | |
107 | BIT_MASK_DESCR(IRQD_ACTIVATED), |
108 | BIT_MASK_DESCR(IRQD_IRQ_STARTED), |
109 | BIT_MASK_DESCR(IRQD_IRQ_DISABLED), |
110 | BIT_MASK_DESCR(IRQD_IRQ_MASKED), |
111 | BIT_MASK_DESCR(IRQD_IRQ_INPROGRESS), |
112 | |
113 | BIT_MASK_DESCR(IRQD_PER_CPU), |
114 | BIT_MASK_DESCR(IRQD_NO_BALANCING), |
115 | |
116 | BIT_MASK_DESCR(IRQD_SINGLE_TARGET), |
117 | BIT_MASK_DESCR(IRQD_MOVE_PCNTXT), |
118 | BIT_MASK_DESCR(IRQD_AFFINITY_SET), |
119 | BIT_MASK_DESCR(IRQD_SETAFFINITY_PENDING), |
120 | BIT_MASK_DESCR(IRQD_AFFINITY_MANAGED), |
121 | BIT_MASK_DESCR(IRQD_AFFINITY_ON_ACTIVATE), |
122 | BIT_MASK_DESCR(IRQD_MANAGED_SHUTDOWN), |
123 | BIT_MASK_DESCR(IRQD_CAN_RESERVE), |
124 | |
125 | BIT_MASK_DESCR(IRQD_FORWARDED_TO_VCPU), |
126 | |
127 | BIT_MASK_DESCR(IRQD_WAKEUP_STATE), |
128 | BIT_MASK_DESCR(IRQD_WAKEUP_ARMED), |
129 | |
130 | BIT_MASK_DESCR(IRQD_DEFAULT_TRIGGER_SET), |
131 | |
132 | BIT_MASK_DESCR(IRQD_HANDLE_ENFORCE_IRQCTX), |
133 | |
134 | BIT_MASK_DESCR(IRQD_IRQ_ENABLED_ON_SUSPEND), |
135 | |
136 | BIT_MASK_DESCR(IRQD_RESEND_WHEN_IN_PROGRESS), |
137 | }; |
138 | |
139 | static const struct irq_bit_descr irqdesc_states[] = { |
140 | BIT_MASK_DESCR(_IRQ_NOPROBE), |
141 | BIT_MASK_DESCR(_IRQ_NOREQUEST), |
142 | BIT_MASK_DESCR(_IRQ_NOTHREAD), |
143 | BIT_MASK_DESCR(_IRQ_NOAUTOEN), |
144 | BIT_MASK_DESCR(_IRQ_NESTED_THREAD), |
145 | BIT_MASK_DESCR(_IRQ_PER_CPU_DEVID), |
146 | BIT_MASK_DESCR(_IRQ_IS_POLLED), |
147 | BIT_MASK_DESCR(_IRQ_DISABLE_UNLAZY), |
148 | BIT_MASK_DESCR(_IRQ_HIDDEN), |
149 | }; |
150 | |
151 | static const struct irq_bit_descr irqdesc_istates[] = { |
152 | BIT_MASK_DESCR(IRQS_AUTODETECT), |
153 | BIT_MASK_DESCR(IRQS_SPURIOUS_DISABLED), |
154 | BIT_MASK_DESCR(IRQS_POLL_INPROGRESS), |
155 | BIT_MASK_DESCR(IRQS_ONESHOT), |
156 | BIT_MASK_DESCR(IRQS_REPLAY), |
157 | BIT_MASK_DESCR(IRQS_WAITING), |
158 | BIT_MASK_DESCR(IRQS_PENDING), |
159 | BIT_MASK_DESCR(IRQS_SUSPENDED), |
160 | BIT_MASK_DESCR(IRQS_NMI), |
161 | }; |
162 | |
163 | |
164 | static int irq_debug_show(struct seq_file *m, void *p) |
165 | { |
166 | struct irq_desc *desc = m->private; |
167 | struct irq_data *data; |
168 | |
169 | raw_spin_lock_irq(&desc->lock); |
170 | data = irq_desc_get_irq_data(desc); |
171 | seq_printf(m, fmt: "handler: %ps\n" , desc->handle_irq); |
172 | seq_printf(m, fmt: "device: %s\n" , desc->dev_name); |
173 | seq_printf(m, fmt: "status: 0x%08x\n" , desc->status_use_accessors); |
174 | irq_debug_show_bits(m, ind: 0, state: desc->status_use_accessors, sd: irqdesc_states, |
175 | ARRAY_SIZE(irqdesc_states)); |
176 | seq_printf(m, fmt: "istate: 0x%08x\n" , desc->istate); |
177 | irq_debug_show_bits(m, ind: 0, state: desc->istate, sd: irqdesc_istates, |
178 | ARRAY_SIZE(irqdesc_istates)); |
179 | seq_printf(m, fmt: "ddepth: %u\n" , desc->depth); |
180 | seq_printf(m, fmt: "wdepth: %u\n" , desc->wake_depth); |
181 | seq_printf(m, fmt: "dstate: 0x%08x\n" , irqd_get(d: data)); |
182 | irq_debug_show_bits(m, ind: 0, state: irqd_get(d: data), sd: irqdata_states, |
183 | ARRAY_SIZE(irqdata_states)); |
184 | seq_printf(m, fmt: "node: %d\n" , irq_data_get_node(d: data)); |
185 | irq_debug_show_masks(m, desc); |
186 | irq_debug_show_data(m, data, ind: 0); |
187 | raw_spin_unlock_irq(&desc->lock); |
188 | return 0; |
189 | } |
190 | |
191 | static int irq_debug_open(struct inode *inode, struct file *file) |
192 | { |
193 | return single_open(file, irq_debug_show, inode->i_private); |
194 | } |
195 | |
196 | static ssize_t irq_debug_write(struct file *file, const char __user *user_buf, |
197 | size_t count, loff_t *ppos) |
198 | { |
199 | struct irq_desc *desc = file_inode(f: file)->i_private; |
200 | char buf[8] = { 0, }; |
201 | size_t size; |
202 | |
203 | size = min(sizeof(buf) - 1, count); |
204 | if (copy_from_user(to: buf, from: user_buf, n: size)) |
205 | return -EFAULT; |
206 | |
207 | if (!strncmp(buf, "trigger" , size)) { |
208 | int err = irq_inject_interrupt(irq: irq_desc_get_irq(desc)); |
209 | |
210 | return err ? err : count; |
211 | } |
212 | |
213 | return count; |
214 | } |
215 | |
216 | static const struct file_operations dfs_irq_ops = { |
217 | .open = irq_debug_open, |
218 | .write = irq_debug_write, |
219 | .read = seq_read, |
220 | .llseek = seq_lseek, |
221 | .release = single_release, |
222 | }; |
223 | |
224 | void irq_debugfs_copy_devname(int irq, struct device *dev) |
225 | { |
226 | struct irq_desc *desc = irq_to_desc(irq); |
227 | const char *name = dev_name(dev); |
228 | |
229 | if (name) |
230 | desc->dev_name = kstrdup(s: name, GFP_KERNEL); |
231 | } |
232 | |
233 | void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc) |
234 | { |
235 | char name [10]; |
236 | |
237 | if (!irq_dir || !desc || desc->debugfs_file) |
238 | return; |
239 | |
240 | sprintf(buf: name, fmt: "%d" , irq); |
241 | desc->debugfs_file = debugfs_create_file(name, mode: 0644, parent: irq_dir, data: desc, |
242 | fops: &dfs_irq_ops); |
243 | } |
244 | |
245 | static int __init irq_debugfs_init(void) |
246 | { |
247 | struct dentry *root_dir; |
248 | int irq; |
249 | |
250 | root_dir = debugfs_create_dir(name: "irq" , NULL); |
251 | |
252 | irq_domain_debugfs_init(root: root_dir); |
253 | |
254 | irq_dir = debugfs_create_dir(name: "irqs" , parent: root_dir); |
255 | |
256 | irq_lock_sparse(); |
257 | for_each_active_irq(irq) |
258 | irq_add_debugfs_entry(irq, desc: irq_to_desc(irq)); |
259 | irq_unlock_sparse(); |
260 | |
261 | return 0; |
262 | } |
263 | __initcall(irq_debugfs_init); |
264 | |