1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) ST-Ericsson SA 2010 |
4 | * |
5 | * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson |
6 | * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson |
7 | * |
8 | * UX500 common part of Power domain regulators |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/err.h> |
13 | #include <linux/regulator/driver.h> |
14 | #include <linux/debugfs.h> |
15 | #include <linux/seq_file.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/module.h> |
18 | |
19 | #include "dbx500-prcmu.h" |
20 | |
21 | /* |
22 | * power state reference count |
23 | */ |
24 | static int power_state_active_cnt; /* will initialize to zero */ |
25 | static DEFINE_SPINLOCK(power_state_active_lock); |
26 | |
27 | void power_state_active_enable(void) |
28 | { |
29 | unsigned long flags; |
30 | |
31 | spin_lock_irqsave(&power_state_active_lock, flags); |
32 | power_state_active_cnt++; |
33 | spin_unlock_irqrestore(lock: &power_state_active_lock, flags); |
34 | } |
35 | |
36 | int power_state_active_disable(void) |
37 | { |
38 | int ret = 0; |
39 | unsigned long flags; |
40 | |
41 | spin_lock_irqsave(&power_state_active_lock, flags); |
42 | if (power_state_active_cnt <= 0) { |
43 | pr_err("power state: unbalanced enable/disable calls\n" ); |
44 | ret = -EINVAL; |
45 | goto out; |
46 | } |
47 | |
48 | power_state_active_cnt--; |
49 | out: |
50 | spin_unlock_irqrestore(lock: &power_state_active_lock, flags); |
51 | return ret; |
52 | } |
53 | |
54 | #ifdef CONFIG_REGULATOR_DEBUG |
55 | |
56 | static int power_state_active_get(void) |
57 | { |
58 | unsigned long flags; |
59 | int cnt; |
60 | |
61 | spin_lock_irqsave(&power_state_active_lock, flags); |
62 | cnt = power_state_active_cnt; |
63 | spin_unlock_irqrestore(lock: &power_state_active_lock, flags); |
64 | |
65 | return cnt; |
66 | } |
67 | |
68 | static struct ux500_regulator_debug { |
69 | struct dentry *dir; |
70 | struct dbx500_regulator_info *regulator_array; |
71 | int num_regulators; |
72 | u8 *state_before_suspend; |
73 | u8 *state_after_suspend; |
74 | } rdebug; |
75 | |
76 | static int ux500_regulator_power_state_cnt_show(struct seq_file *s, void *p) |
77 | { |
78 | /* print power state count */ |
79 | seq_printf(m: s, fmt: "ux500-regulator power state count: %i\n" , |
80 | power_state_active_get()); |
81 | |
82 | return 0; |
83 | } |
84 | DEFINE_SHOW_ATTRIBUTE(ux500_regulator_power_state_cnt); |
85 | |
86 | static int ux500_regulator_status_show(struct seq_file *s, void *p) |
87 | { |
88 | int i; |
89 | |
90 | /* print dump header */ |
91 | seq_puts(m: s, s: "ux500-regulator status:\n" ); |
92 | seq_printf(m: s, fmt: "%31s : %8s : %8s\n" , "current" , "before" , "after" ); |
93 | |
94 | for (i = 0; i < rdebug.num_regulators; i++) { |
95 | struct dbx500_regulator_info *info; |
96 | /* Access per-regulator data */ |
97 | info = &rdebug.regulator_array[i]; |
98 | |
99 | /* print status */ |
100 | seq_printf(m: s, fmt: "%20s : %8s : %8s : %8s\n" , |
101 | info->desc.name, |
102 | info->is_enabled ? "enabled" : "disabled" , |
103 | rdebug.state_before_suspend[i] ? "enabled" : "disabled" , |
104 | rdebug.state_after_suspend[i] ? "enabled" : "disabled" ); |
105 | } |
106 | |
107 | return 0; |
108 | } |
109 | DEFINE_SHOW_ATTRIBUTE(ux500_regulator_status); |
110 | |
111 | int |
112 | ux500_regulator_debug_init(struct platform_device *pdev, |
113 | struct dbx500_regulator_info *regulator_info, |
114 | int num_regulators) |
115 | { |
116 | /* create directory */ |
117 | rdebug.dir = debugfs_create_dir(name: "ux500-regulator" , NULL); |
118 | |
119 | /* create "status" file */ |
120 | debugfs_create_file(name: "status" , mode: 0444, parent: rdebug.dir, data: &pdev->dev, |
121 | fops: &ux500_regulator_status_fops); |
122 | |
123 | /* create "power-state-count" file */ |
124 | debugfs_create_file(name: "power-state-count" , mode: 0444, parent: rdebug.dir, |
125 | data: &pdev->dev, fops: &ux500_regulator_power_state_cnt_fops); |
126 | |
127 | rdebug.regulator_array = regulator_info; |
128 | rdebug.num_regulators = num_regulators; |
129 | |
130 | rdebug.state_before_suspend = kzalloc(size: num_regulators, GFP_KERNEL); |
131 | if (!rdebug.state_before_suspend) |
132 | goto exit_destroy_power_state; |
133 | |
134 | rdebug.state_after_suspend = kzalloc(size: num_regulators, GFP_KERNEL); |
135 | if (!rdebug.state_after_suspend) |
136 | goto exit_free; |
137 | |
138 | return 0; |
139 | |
140 | exit_free: |
141 | kfree(objp: rdebug.state_before_suspend); |
142 | exit_destroy_power_state: |
143 | debugfs_remove_recursive(dentry: rdebug.dir); |
144 | return -ENOMEM; |
145 | } |
146 | |
147 | int ux500_regulator_debug_exit(void) |
148 | { |
149 | debugfs_remove_recursive(dentry: rdebug.dir); |
150 | kfree(objp: rdebug.state_after_suspend); |
151 | kfree(objp: rdebug.state_before_suspend); |
152 | |
153 | return 0; |
154 | } |
155 | #endif |
156 | |