diff options
Diffstat (limited to 'arch/powerpc/platforms/powernv/opal.c')
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 92 |
1 files changed, 81 insertions, 11 deletions
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 18fd4e71c9c1..2241565b0739 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/kobject.h> | 23 | #include <linux/kobject.h> |
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/memblock.h> | 25 | #include <linux/memblock.h> |
26 | #include <linux/kthread.h> | ||
27 | #include <linux/freezer.h> | ||
26 | 28 | ||
27 | #include <asm/machdep.h> | 29 | #include <asm/machdep.h> |
28 | #include <asm/opal.h> | 30 | #include <asm/opal.h> |
@@ -58,6 +60,7 @@ static struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX]; | |||
58 | static DEFINE_SPINLOCK(opal_notifier_lock); | 60 | static DEFINE_SPINLOCK(opal_notifier_lock); |
59 | static uint64_t last_notified_mask = 0x0ul; | 61 | static uint64_t last_notified_mask = 0x0ul; |
60 | static atomic_t opal_notifier_hold = ATOMIC_INIT(0); | 62 | static atomic_t opal_notifier_hold = ATOMIC_INIT(0); |
63 | static uint32_t opal_heartbeat; | ||
61 | 64 | ||
62 | static void opal_reinit_cores(void) | 65 | static void opal_reinit_cores(void) |
63 | { | 66 | { |
@@ -302,23 +305,26 @@ void opal_notifier_disable(void) | |||
302 | * Opal message notifier based on message type. Allow subscribers to get | 305 | * Opal message notifier based on message type. Allow subscribers to get |
303 | * notified for specific messgae type. | 306 | * notified for specific messgae type. |
304 | */ | 307 | */ |
305 | int opal_message_notifier_register(enum OpalMessageType msg_type, | 308 | int opal_message_notifier_register(enum opal_msg_type msg_type, |
306 | struct notifier_block *nb) | 309 | struct notifier_block *nb) |
307 | { | 310 | { |
308 | if (!nb) { | 311 | if (!nb || msg_type >= OPAL_MSG_TYPE_MAX) { |
309 | pr_warning("%s: Invalid argument (%p)\n", | 312 | pr_warning("%s: Invalid arguments, msg_type:%d\n", |
310 | __func__, nb); | ||
311 | return -EINVAL; | ||
312 | } | ||
313 | if (msg_type > OPAL_MSG_TYPE_MAX) { | ||
314 | pr_warning("%s: Invalid message type argument (%d)\n", | ||
315 | __func__, msg_type); | 313 | __func__, msg_type); |
316 | return -EINVAL; | 314 | return -EINVAL; |
317 | } | 315 | } |
316 | |||
318 | return atomic_notifier_chain_register( | 317 | return atomic_notifier_chain_register( |
319 | &opal_msg_notifier_head[msg_type], nb); | 318 | &opal_msg_notifier_head[msg_type], nb); |
320 | } | 319 | } |
321 | 320 | ||
321 | int opal_message_notifier_unregister(enum opal_msg_type msg_type, | ||
322 | struct notifier_block *nb) | ||
323 | { | ||
324 | return atomic_notifier_chain_unregister( | ||
325 | &opal_msg_notifier_head[msg_type], nb); | ||
326 | } | ||
327 | |||
322 | static void opal_message_do_notify(uint32_t msg_type, void *msg) | 328 | static void opal_message_do_notify(uint32_t msg_type, void *msg) |
323 | { | 329 | { |
324 | /* notify subscribers */ | 330 | /* notify subscribers */ |
@@ -351,7 +357,7 @@ static void opal_handle_message(void) | |||
351 | type = be32_to_cpu(msg.msg_type); | 357 | type = be32_to_cpu(msg.msg_type); |
352 | 358 | ||
353 | /* Sanity check */ | 359 | /* Sanity check */ |
354 | if (type > OPAL_MSG_TYPE_MAX) { | 360 | if (type >= OPAL_MSG_TYPE_MAX) { |
355 | pr_warning("%s: Unknown message type: %u\n", __func__, type); | 361 | pr_warning("%s: Unknown message type: %u\n", __func__, type); |
356 | return; | 362 | return; |
357 | } | 363 | } |
@@ -665,6 +671,9 @@ static void __init opal_dump_region_init(void) | |||
665 | uint64_t size; | 671 | uint64_t size; |
666 | int rc; | 672 | int rc; |
667 | 673 | ||
674 | if (!opal_check_token(OPAL_REGISTER_DUMP_REGION)) | ||
675 | return; | ||
676 | |||
668 | /* Register kernel log buffer */ | 677 | /* Register kernel log buffer */ |
669 | addr = log_buf_addr_get(); | 678 | addr = log_buf_addr_get(); |
670 | if (addr == NULL) | 679 | if (addr == NULL) |
@@ -684,6 +693,15 @@ static void __init opal_dump_region_init(void) | |||
684 | "rc = %d\n", rc); | 693 | "rc = %d\n", rc); |
685 | } | 694 | } |
686 | 695 | ||
696 | static void opal_flash_init(struct device_node *opal_node) | ||
697 | { | ||
698 | struct device_node *np; | ||
699 | |||
700 | for_each_child_of_node(opal_node, np) | ||
701 | if (of_device_is_compatible(np, "ibm,opal-flash")) | ||
702 | of_platform_device_create(np, NULL, NULL); | ||
703 | } | ||
704 | |||
687 | static void opal_ipmi_init(struct device_node *opal_node) | 705 | static void opal_ipmi_init(struct device_node *opal_node) |
688 | { | 706 | { |
689 | struct device_node *np; | 707 | struct device_node *np; |
@@ -741,6 +759,29 @@ static void __init opal_irq_init(struct device_node *dn) | |||
741 | } | 759 | } |
742 | } | 760 | } |
743 | 761 | ||
762 | static int kopald(void *unused) | ||
763 | { | ||
764 | set_freezable(); | ||
765 | do { | ||
766 | try_to_freeze(); | ||
767 | opal_poll_events(NULL); | ||
768 | msleep_interruptible(opal_heartbeat); | ||
769 | } while (!kthread_should_stop()); | ||
770 | |||
771 | return 0; | ||
772 | } | ||
773 | |||
774 | static void opal_init_heartbeat(void) | ||
775 | { | ||
776 | /* Old firwmware, we assume the HVC heartbeat is sufficient */ | ||
777 | if (of_property_read_u32(opal_node, "ibm,heartbeat-ms", | ||
778 | &opal_heartbeat) != 0) | ||
779 | opal_heartbeat = 0; | ||
780 | |||
781 | if (opal_heartbeat) | ||
782 | kthread_run(kopald, NULL, "kopald"); | ||
783 | } | ||
784 | |||
744 | static int __init opal_init(void) | 785 | static int __init opal_init(void) |
745 | { | 786 | { |
746 | struct device_node *np, *consoles; | 787 | struct device_node *np, *consoles; |
@@ -769,6 +810,9 @@ static int __init opal_init(void) | |||
769 | /* Create i2c platform devices */ | 810 | /* Create i2c platform devices */ |
770 | opal_i2c_create_devs(); | 811 | opal_i2c_create_devs(); |
771 | 812 | ||
813 | /* Setup a heatbeat thread if requested by OPAL */ | ||
814 | opal_init_heartbeat(); | ||
815 | |||
772 | /* Find all OPAL interrupts and request them */ | 816 | /* Find all OPAL interrupts and request them */ |
773 | opal_irq_init(opal_node); | 817 | opal_irq_init(opal_node); |
774 | 818 | ||
@@ -782,7 +826,7 @@ static int __init opal_init(void) | |||
782 | /* Setup error log interface */ | 826 | /* Setup error log interface */ |
783 | rc = opal_elog_init(); | 827 | rc = opal_elog_init(); |
784 | /* Setup code update interface */ | 828 | /* Setup code update interface */ |
785 | opal_flash_init(); | 829 | opal_flash_update_init(); |
786 | /* Setup platform dump extract interface */ | 830 | /* Setup platform dump extract interface */ |
787 | opal_platform_dump_init(); | 831 | opal_platform_dump_init(); |
788 | /* Setup system parameters interface */ | 832 | /* Setup system parameters interface */ |
@@ -791,8 +835,11 @@ static int __init opal_init(void) | |||
791 | opal_msglog_init(); | 835 | opal_msglog_init(); |
792 | } | 836 | } |
793 | 837 | ||
838 | /* Initialize OPAL IPMI backend */ | ||
794 | opal_ipmi_init(opal_node); | 839 | opal_ipmi_init(opal_node); |
795 | 840 | ||
841 | opal_flash_init(opal_node); | ||
842 | |||
796 | return 0; | 843 | return 0; |
797 | } | 844 | } |
798 | machine_subsys_initcall(powernv, opal_init); | 845 | machine_subsys_initcall(powernv, opal_init); |
@@ -823,13 +870,17 @@ void opal_shutdown(void) | |||
823 | } | 870 | } |
824 | 871 | ||
825 | /* Unregister memory dump region */ | 872 | /* Unregister memory dump region */ |
826 | opal_unregister_dump_region(OPAL_DUMP_REGION_LOG_BUF); | 873 | if (opal_check_token(OPAL_UNREGISTER_DUMP_REGION)) |
874 | opal_unregister_dump_region(OPAL_DUMP_REGION_LOG_BUF); | ||
827 | } | 875 | } |
828 | 876 | ||
829 | /* Export this so that test modules can use it */ | 877 | /* Export this so that test modules can use it */ |
830 | EXPORT_SYMBOL_GPL(opal_invalid_call); | 878 | EXPORT_SYMBOL_GPL(opal_invalid_call); |
831 | EXPORT_SYMBOL_GPL(opal_ipmi_send); | 879 | EXPORT_SYMBOL_GPL(opal_ipmi_send); |
832 | EXPORT_SYMBOL_GPL(opal_ipmi_recv); | 880 | EXPORT_SYMBOL_GPL(opal_ipmi_recv); |
881 | EXPORT_SYMBOL_GPL(opal_flash_read); | ||
882 | EXPORT_SYMBOL_GPL(opal_flash_write); | ||
883 | EXPORT_SYMBOL_GPL(opal_flash_erase); | ||
833 | 884 | ||
834 | /* Convert a region of vmalloc memory to an opal sg list */ | 885 | /* Convert a region of vmalloc memory to an opal sg list */ |
835 | struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr, | 886 | struct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr, |
@@ -894,6 +945,25 @@ void opal_free_sg_list(struct opal_sg_list *sg) | |||
894 | } | 945 | } |
895 | } | 946 | } |
896 | 947 | ||
948 | int opal_error_code(int rc) | ||
949 | { | ||
950 | switch (rc) { | ||
951 | case OPAL_SUCCESS: return 0; | ||
952 | |||
953 | case OPAL_PARAMETER: return -EINVAL; | ||
954 | case OPAL_ASYNC_COMPLETION: return -EINPROGRESS; | ||
955 | case OPAL_BUSY_EVENT: return -EBUSY; | ||
956 | case OPAL_NO_MEM: return -ENOMEM; | ||
957 | |||
958 | case OPAL_UNSUPPORTED: return -EIO; | ||
959 | case OPAL_HARDWARE: return -EIO; | ||
960 | case OPAL_INTERNAL_ERROR: return -EIO; | ||
961 | default: | ||
962 | pr_err("%s: unexpected OPAL error %d\n", __func__, rc); | ||
963 | return -EIO; | ||
964 | } | ||
965 | } | ||
966 | |||
897 | EXPORT_SYMBOL_GPL(opal_poll_events); | 967 | EXPORT_SYMBOL_GPL(opal_poll_events); |
898 | EXPORT_SYMBOL_GPL(opal_rtc_read); | 968 | EXPORT_SYMBOL_GPL(opal_rtc_read); |
899 | EXPORT_SYMBOL_GPL(opal_rtc_write); | 969 | EXPORT_SYMBOL_GPL(opal_rtc_write); |