diff options
-rw-r--r-- | drivers/firmware/dmi-sysfs.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index d20961011039..a5afd805e66f 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c | |||
@@ -312,6 +312,140 @@ static struct kobj_type dmi_system_event_log_ktype = { | |||
312 | .default_attrs = dmi_sysfs_sel_attrs, | 312 | .default_attrs = dmi_sysfs_sel_attrs, |
313 | }; | 313 | }; |
314 | 314 | ||
315 | typedef u8 (*sel_io_reader)(const struct dmi_system_event_log *sel, | ||
316 | loff_t offset); | ||
317 | |||
318 | static DEFINE_MUTEX(io_port_lock); | ||
319 | |||
320 | static u8 read_sel_8bit_indexed_io(const struct dmi_system_event_log *sel, | ||
321 | loff_t offset) | ||
322 | { | ||
323 | u8 ret; | ||
324 | |||
325 | mutex_lock(&io_port_lock); | ||
326 | outb((u8)offset, sel->io.index_addr); | ||
327 | ret = inb(sel->io.data_addr); | ||
328 | mutex_unlock(&io_port_lock); | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | static u8 read_sel_2x8bit_indexed_io(const struct dmi_system_event_log *sel, | ||
333 | loff_t offset) | ||
334 | { | ||
335 | u8 ret; | ||
336 | |||
337 | mutex_lock(&io_port_lock); | ||
338 | outb((u8)offset, sel->io.index_addr); | ||
339 | outb((u8)(offset >> 8), sel->io.index_addr + 1); | ||
340 | ret = inb(sel->io.data_addr); | ||
341 | mutex_unlock(&io_port_lock); | ||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | static u8 read_sel_16bit_indexed_io(const struct dmi_system_event_log *sel, | ||
346 | loff_t offset) | ||
347 | { | ||
348 | u8 ret; | ||
349 | |||
350 | mutex_lock(&io_port_lock); | ||
351 | outw((u16)offset, sel->io.index_addr); | ||
352 | ret = inb(sel->io.data_addr); | ||
353 | mutex_unlock(&io_port_lock); | ||
354 | return ret; | ||
355 | } | ||
356 | |||
357 | static sel_io_reader sel_io_readers[] = { | ||
358 | [DMI_SEL_ACCESS_METHOD_IO8] = read_sel_8bit_indexed_io, | ||
359 | [DMI_SEL_ACCESS_METHOD_IO2x8] = read_sel_2x8bit_indexed_io, | ||
360 | [DMI_SEL_ACCESS_METHOD_IO16] = read_sel_16bit_indexed_io, | ||
361 | }; | ||
362 | |||
363 | static ssize_t dmi_sel_raw_read_io(struct dmi_sysfs_entry *entry, | ||
364 | const struct dmi_system_event_log *sel, | ||
365 | char *buf, loff_t pos, size_t count) | ||
366 | { | ||
367 | ssize_t wrote = 0; | ||
368 | |||
369 | sel_io_reader io_reader = sel_io_readers[sel->access_method]; | ||
370 | |||
371 | while (count && pos < sel->area_length) { | ||
372 | count--; | ||
373 | *(buf++) = io_reader(sel, pos++); | ||
374 | wrote++; | ||
375 | } | ||
376 | |||
377 | return wrote; | ||
378 | } | ||
379 | |||
380 | static ssize_t dmi_sel_raw_read_phys32(struct dmi_sysfs_entry *entry, | ||
381 | const struct dmi_system_event_log *sel, | ||
382 | char *buf, loff_t pos, size_t count) | ||
383 | { | ||
384 | u8 __iomem *mapped; | ||
385 | ssize_t wrote = 0; | ||
386 | |||
387 | mapped = ioremap(sel->access_method_address, sel->area_length); | ||
388 | if (!mapped) | ||
389 | return -EIO; | ||
390 | |||
391 | while (count && pos < sel->area_length) { | ||
392 | count--; | ||
393 | *(buf++) = readb(mapped + pos++); | ||
394 | wrote++; | ||
395 | } | ||
396 | |||
397 | iounmap(mapped); | ||
398 | return wrote; | ||
399 | } | ||
400 | |||
401 | static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry, | ||
402 | const struct dmi_header *dh, | ||
403 | void *_state) | ||
404 | { | ||
405 | struct dmi_read_state *state = _state; | ||
406 | const struct dmi_system_event_log *sel = to_sel(dh); | ||
407 | |||
408 | if (sizeof(*sel) > dmi_entry_length(dh)) | ||
409 | return -EIO; | ||
410 | |||
411 | switch (sel->access_method) { | ||
412 | case DMI_SEL_ACCESS_METHOD_IO8: | ||
413 | case DMI_SEL_ACCESS_METHOD_IO2x8: | ||
414 | case DMI_SEL_ACCESS_METHOD_IO16: | ||
415 | return dmi_sel_raw_read_io(entry, sel, state->buf, | ||
416 | state->pos, state->count); | ||
417 | case DMI_SEL_ACCESS_METHOD_PHYS32: | ||
418 | return dmi_sel_raw_read_phys32(entry, sel, state->buf, | ||
419 | state->pos, state->count); | ||
420 | case DMI_SEL_ACCESS_METHOD_GPNV: | ||
421 | pr_info("dmi-sysfs: GPNV support missing.\n"); | ||
422 | return -EIO; | ||
423 | default: | ||
424 | pr_info("dmi-sysfs: Unknown access method %02x\n", | ||
425 | sel->access_method); | ||
426 | return -EIO; | ||
427 | } | ||
428 | } | ||
429 | |||
430 | static ssize_t dmi_sel_raw_read(struct file *filp, struct kobject *kobj, | ||
431 | struct bin_attribute *bin_attr, | ||
432 | char *buf, loff_t pos, size_t count) | ||
433 | { | ||
434 | struct dmi_sysfs_entry *entry = to_entry(kobj->parent); | ||
435 | struct dmi_read_state state = { | ||
436 | .buf = buf, | ||
437 | .pos = pos, | ||
438 | .count = count, | ||
439 | }; | ||
440 | |||
441 | return find_dmi_entry(entry, dmi_sel_raw_read_helper, &state); | ||
442 | } | ||
443 | |||
444 | static struct bin_attribute dmi_sel_raw_attr = { | ||
445 | .attr = {.name = "raw_event_log", .mode = 0400}, | ||
446 | .read = dmi_sel_raw_read, | ||
447 | }; | ||
448 | |||
315 | static int dmi_system_event_log(struct dmi_sysfs_entry *entry) | 449 | static int dmi_system_event_log(struct dmi_sysfs_entry *entry) |
316 | { | 450 | { |
317 | int ret; | 451 | int ret; |
@@ -325,6 +459,15 @@ static int dmi_system_event_log(struct dmi_sysfs_entry *entry) | |||
325 | "system_event_log"); | 459 | "system_event_log"); |
326 | if (ret) | 460 | if (ret) |
327 | goto out_free; | 461 | goto out_free; |
462 | |||
463 | ret = sysfs_create_bin_file(entry->child, &dmi_sel_raw_attr); | ||
464 | if (ret) | ||
465 | goto out_del; | ||
466 | |||
467 | return 0; | ||
468 | |||
469 | out_del: | ||
470 | kobject_del(entry->child); | ||
328 | out_free: | 471 | out_free: |
329 | kfree(entry->child); | 472 | kfree(entry->child); |
330 | return ret; | 473 | return ret; |