diff options
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/platforms/powernv/opal-lpc.c | 150 |
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 | ||
22 | static int opal_lpc_chip_id = -1; | 26 | static 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 | ||
184 | struct lpc_debugfs_entry { | ||
185 | enum OpalLPCAddressType lpc_type; | ||
186 | }; | ||
187 | |||
188 | static 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 | |||
239 | static 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 | |||
291 | static 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 | |||
298 | static 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 | |||
311 | static 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 | } | ||
326 | device_initcall(opal_lpc_init_debugfs); | ||
327 | #endif /* CONFIG_DEBUG_FS */ | ||
328 | |||
179 | void opal_lpc_init(void) | 329 | void opal_lpc_init(void) |
180 | { | 330 | { |
181 | struct device_node *np; | 331 | struct device_node *np; |