aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r--arch/powerpc/platforms/powernv/opal-lpc.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/powernv/opal-lpc.c b/arch/powerpc/platforms/powernv/opal-lpc.c
index 79d83cad3d67..70eff22aef73 100644
--- a/arch/powerpc/platforms/powernv/opal-lpc.c
+++ b/arch/powerpc/platforms/powernv/opal-lpc.c
@@ -12,12 +12,16 @@
12#include <linux/kernel.h> 12#include <linux/kernel.h>
13#include <linux/of.h> 13#include <linux/of.h>
14#include <linux/bug.h> 14#include <linux/bug.h>
15#include <linux/debugfs.h>
16#include <linux/io.h>
17#include <linux/slab.h>
15 18
16#include <asm/machdep.h> 19#include <asm/machdep.h>
17#include <asm/firmware.h> 20#include <asm/firmware.h>
18#include <asm/xics.h> 21#include <asm/xics.h>
19#include <asm/opal.h> 22#include <asm/opal.h>
20#include <asm/prom.h> 23#include <asm/prom.h>
24#include <asm/uaccess.h>
21 25
22static int opal_lpc_chip_id = -1; 26static int opal_lpc_chip_id = -1;
23 27
@@ -176,6 +180,152 @@ static const struct ppc_pci_io opal_lpc_io = {
176 .outsl = opal_lpc_outsl, 180 .outsl = opal_lpc_outsl,
177}; 181};
178 182
183#ifdef CONFIG_DEBUG_FS
184struct lpc_debugfs_entry {
185 enum OpalLPCAddressType lpc_type;
186};
187
188static ssize_t lpc_debug_read(struct file *filp, char __user *ubuf,
189 size_t count, loff_t *ppos)
190{
191 struct lpc_debugfs_entry *lpc = filp->private_data;
192 u32 data, pos, len, todo;
193 int rc;
194
195 if (!access_ok(VERIFY_WRITE, ubuf, count))
196 return -EFAULT;
197
198 todo = count;
199 while (todo) {
200 pos = *ppos;
201
202 /*
203 * Select access size based on count and alignment and
204 * access type. IO and MEM only support byte acceses,
205 * FW supports all 3.
206 */
207 len = 1;
208 if (lpc->lpc_type == OPAL_LPC_FW) {
209 if (todo > 3 && (pos & 3) == 0)
210 len = 4;
211 else if (todo > 1 && (pos & 1) == 0)
212 len = 2;
213 }
214 rc = opal_lpc_read(opal_lpc_chip_id, lpc->lpc_type, pos,
215 &data, len);
216 if (rc)
217 return -ENXIO;
218 switch(len) {
219 case 4:
220 rc = __put_user((u32)data, (u32 __user *)ubuf);
221 break;
222 case 2:
223 rc = __put_user((u16)data, (u16 __user *)ubuf);
224 break;
225 default:
226 rc = __put_user((u8)data, (u8 __user *)ubuf);
227 break;
228 }
229 if (rc)
230 return -EFAULT;
231 *ppos += len;
232 ubuf += len;
233 todo -= len;
234 }
235
236 return count;
237}
238
239static ssize_t lpc_debug_write(struct file *filp, const char __user *ubuf,
240 size_t count, loff_t *ppos)
241{
242 struct lpc_debugfs_entry *lpc = filp->private_data;
243 u32 data, pos, len, todo;
244 int rc;
245
246 if (!access_ok(VERIFY_READ, ubuf, count))
247 return -EFAULT;
248
249 todo = count;
250 while (todo) {
251 pos = *ppos;
252
253 /*
254 * Select access size based on count and alignment and
255 * access type. IO and MEM only support byte acceses,
256 * FW supports all 3.
257 */
258 len = 1;
259 if (lpc->lpc_type == OPAL_LPC_FW) {
260 if (todo > 3 && (pos & 3) == 0)
261 len = 4;
262 else if (todo > 1 && (pos & 1) == 0)
263 len = 2;
264 }
265 switch(len) {
266 case 4:
267 rc = __get_user(data, (u32 __user *)ubuf);
268 break;
269 case 2:
270 rc = __get_user(data, (u16 __user *)ubuf);
271 break;
272 default:
273 rc = __get_user(data, (u8 __user *)ubuf);
274 break;
275 }
276 if (rc)
277 return -EFAULT;
278
279 rc = opal_lpc_write(opal_lpc_chip_id, lpc->lpc_type, pos,
280 data, len);
281 if (rc)
282 return -ENXIO;
283 *ppos += len;
284 ubuf += len;
285 todo -= len;
286 }
287
288 return count;
289}
290
291static const struct file_operations lpc_fops = {
292 .read = lpc_debug_read,
293 .write = lpc_debug_write,
294 .open = simple_open,
295 .llseek = default_llseek,
296};
297
298static int opal_lpc_debugfs_create_type(struct dentry *folder,
299 const char *fname,
300 enum OpalLPCAddressType type)
301{
302 struct lpc_debugfs_entry *entry;
303 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
304 if (!entry)
305 return -ENOMEM;
306 entry->lpc_type = type;
307 debugfs_create_file(fname, 0600, folder, entry, &lpc_fops);
308 return 0;
309}
310
311static int opal_lpc_init_debugfs(void)
312{
313 struct dentry *root;
314 int rc = 0;
315
316 if (opal_lpc_chip_id < 0)
317 return -ENODEV;
318
319 root = debugfs_create_dir("lpc", powerpc_debugfs_root);
320
321 rc |= opal_lpc_debugfs_create_type(root, "io", OPAL_LPC_IO);
322 rc |= opal_lpc_debugfs_create_type(root, "mem", OPAL_LPC_MEM);
323 rc |= opal_lpc_debugfs_create_type(root, "fw", OPAL_LPC_FW);
324 return rc;
325}
326device_initcall(opal_lpc_init_debugfs);
327#endif /* CONFIG_DEBUG_FS */
328
179void opal_lpc_init(void) 329void opal_lpc_init(void)
180{ 330{
181 struct device_node *np; 331 struct device_node *np;