aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-06-03 06:07:39 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-06-04 23:20:37 -0400
commitfa2dbe2e0fcf2cda8fc56845e475b617385b1ec6 (patch)
treeb732b892b56b9a1eb141b09827f8f104091c5e92 /arch/powerpc/platforms
parentc4cad90f9e9dcb85afc5e75a02ae3522ed077296 (diff)
powerpc/powernv: Provide debugfs access to the LPC bus via OPAL
This provides debugfs files to access the LPC bus on Power8 non-virtualized using the appropriate OPAL firmware calls. The usage is simple: one file per space (IO, MEM and FW), lseek to the address and read/write the data. IO and MEM always generate series of byte accesses. FW can generate word and dword accesses if aligned properly. Based on an original patch from Rob Lippert and reworked. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
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;