diff options
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 124 |
1 files changed, 89 insertions, 35 deletions
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index e117049fb754..a323cc7a0265 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c | |||
@@ -82,6 +82,12 @@ | |||
82 | #define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a | 82 | #define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a |
83 | short timeout */ | 83 | short timeout */ |
84 | 84 | ||
85 | /* Bit for BMC global enables. */ | ||
86 | #define IPMI_BMC_RCV_MSG_INTR 0x01 | ||
87 | #define IPMI_BMC_EVT_MSG_INTR 0x02 | ||
88 | #define IPMI_BMC_EVT_MSG_BUFF 0x04 | ||
89 | #define IPMI_BMC_SYS_LOG 0x08 | ||
90 | |||
85 | enum si_intf_state { | 91 | enum si_intf_state { |
86 | SI_NORMAL, | 92 | SI_NORMAL, |
87 | SI_GETTING_FLAGS, | 93 | SI_GETTING_FLAGS, |
@@ -90,7 +96,9 @@ enum si_intf_state { | |||
90 | SI_CLEARING_FLAGS_THEN_SET_IRQ, | 96 | SI_CLEARING_FLAGS_THEN_SET_IRQ, |
91 | SI_GETTING_MESSAGES, | 97 | SI_GETTING_MESSAGES, |
92 | SI_ENABLE_INTERRUPTS1, | 98 | SI_ENABLE_INTERRUPTS1, |
93 | SI_ENABLE_INTERRUPTS2 | 99 | SI_ENABLE_INTERRUPTS2, |
100 | SI_DISABLE_INTERRUPTS1, | ||
101 | SI_DISABLE_INTERRUPTS2 | ||
94 | /* FIXME - add watchdog stuff. */ | 102 | /* FIXME - add watchdog stuff. */ |
95 | }; | 103 | }; |
96 | 104 | ||
@@ -339,6 +347,17 @@ static void start_enable_irq(struct smi_info *smi_info) | |||
339 | smi_info->si_state = SI_ENABLE_INTERRUPTS1; | 347 | smi_info->si_state = SI_ENABLE_INTERRUPTS1; |
340 | } | 348 | } |
341 | 349 | ||
350 | static void start_disable_irq(struct smi_info *smi_info) | ||
351 | { | ||
352 | unsigned char msg[2]; | ||
353 | |||
354 | msg[0] = (IPMI_NETFN_APP_REQUEST << 2); | ||
355 | msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; | ||
356 | |||
357 | smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); | ||
358 | smi_info->si_state = SI_DISABLE_INTERRUPTS1; | ||
359 | } | ||
360 | |||
342 | static void start_clear_flags(struct smi_info *smi_info) | 361 | static void start_clear_flags(struct smi_info *smi_info) |
343 | { | 362 | { |
344 | unsigned char msg[3]; | 363 | unsigned char msg[3]; |
@@ -359,7 +378,7 @@ static void start_clear_flags(struct smi_info *smi_info) | |||
359 | static inline void disable_si_irq(struct smi_info *smi_info) | 378 | static inline void disable_si_irq(struct smi_info *smi_info) |
360 | { | 379 | { |
361 | if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { | 380 | if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { |
362 | disable_irq_nosync(smi_info->irq); | 381 | start_disable_irq(smi_info); |
363 | smi_info->interrupt_disabled = 1; | 382 | smi_info->interrupt_disabled = 1; |
364 | } | 383 | } |
365 | } | 384 | } |
@@ -367,7 +386,7 @@ static inline void disable_si_irq(struct smi_info *smi_info) | |||
367 | static inline void enable_si_irq(struct smi_info *smi_info) | 386 | static inline void enable_si_irq(struct smi_info *smi_info) |
368 | { | 387 | { |
369 | if ((smi_info->irq) && (smi_info->interrupt_disabled)) { | 388 | if ((smi_info->irq) && (smi_info->interrupt_disabled)) { |
370 | enable_irq(smi_info->irq); | 389 | start_enable_irq(smi_info); |
371 | smi_info->interrupt_disabled = 0; | 390 | smi_info->interrupt_disabled = 0; |
372 | } | 391 | } |
373 | } | 392 | } |
@@ -589,7 +608,9 @@ static void handle_transaction_done(struct smi_info *smi_info) | |||
589 | } else { | 608 | } else { |
590 | msg[0] = (IPMI_NETFN_APP_REQUEST << 2); | 609 | msg[0] = (IPMI_NETFN_APP_REQUEST << 2); |
591 | msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; | 610 | msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; |
592 | msg[2] = msg[3] | 1; /* enable msg queue int */ | 611 | msg[2] = (msg[3] | |
612 | IPMI_BMC_RCV_MSG_INTR | | ||
613 | IPMI_BMC_EVT_MSG_INTR); | ||
593 | smi_info->handlers->start_transaction( | 614 | smi_info->handlers->start_transaction( |
594 | smi_info->si_sm, msg, 3); | 615 | smi_info->si_sm, msg, 3); |
595 | smi_info->si_state = SI_ENABLE_INTERRUPTS2; | 616 | smi_info->si_state = SI_ENABLE_INTERRUPTS2; |
@@ -611,6 +632,45 @@ static void handle_transaction_done(struct smi_info *smi_info) | |||
611 | smi_info->si_state = SI_NORMAL; | 632 | smi_info->si_state = SI_NORMAL; |
612 | break; | 633 | break; |
613 | } | 634 | } |
635 | |||
636 | case SI_DISABLE_INTERRUPTS1: | ||
637 | { | ||
638 | unsigned char msg[4]; | ||
639 | |||
640 | /* We got the flags from the SMI, now handle them. */ | ||
641 | smi_info->handlers->get_result(smi_info->si_sm, msg, 4); | ||
642 | if (msg[2] != 0) { | ||
643 | printk(KERN_WARNING | ||
644 | "ipmi_si: Could not disable interrupts" | ||
645 | ", failed get.\n"); | ||
646 | smi_info->si_state = SI_NORMAL; | ||
647 | } else { | ||
648 | msg[0] = (IPMI_NETFN_APP_REQUEST << 2); | ||
649 | msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; | ||
650 | msg[2] = (msg[3] & | ||
651 | ~(IPMI_BMC_RCV_MSG_INTR | | ||
652 | IPMI_BMC_EVT_MSG_INTR)); | ||
653 | smi_info->handlers->start_transaction( | ||
654 | smi_info->si_sm, msg, 3); | ||
655 | smi_info->si_state = SI_DISABLE_INTERRUPTS2; | ||
656 | } | ||
657 | break; | ||
658 | } | ||
659 | |||
660 | case SI_DISABLE_INTERRUPTS2: | ||
661 | { | ||
662 | unsigned char msg[4]; | ||
663 | |||
664 | /* We got the flags from the SMI, now handle them. */ | ||
665 | smi_info->handlers->get_result(smi_info->si_sm, msg, 4); | ||
666 | if (msg[2] != 0) { | ||
667 | printk(KERN_WARNING | ||
668 | "ipmi_si: Could not disable interrupts" | ||
669 | ", failed set.\n"); | ||
670 | } | ||
671 | smi_info->si_state = SI_NORMAL; | ||
672 | break; | ||
673 | } | ||
614 | } | 674 | } |
615 | } | 675 | } |
616 | 676 | ||
@@ -864,9 +924,6 @@ static void smi_timeout(unsigned long data) | |||
864 | struct timeval t; | 924 | struct timeval t; |
865 | #endif | 925 | #endif |
866 | 926 | ||
867 | if (atomic_read(&smi_info->stop_operation)) | ||
868 | return; | ||
869 | |||
870 | spin_lock_irqsave(&(smi_info->si_lock), flags); | 927 | spin_lock_irqsave(&(smi_info->si_lock), flags); |
871 | #ifdef DEBUG_TIMING | 928 | #ifdef DEBUG_TIMING |
872 | do_gettimeofday(&t); | 929 | do_gettimeofday(&t); |
@@ -922,15 +979,11 @@ static irqreturn_t si_irq_handler(int irq, void *data) | |||
922 | smi_info->interrupts++; | 979 | smi_info->interrupts++; |
923 | spin_unlock(&smi_info->count_lock); | 980 | spin_unlock(&smi_info->count_lock); |
924 | 981 | ||
925 | if (atomic_read(&smi_info->stop_operation)) | ||
926 | goto out; | ||
927 | |||
928 | #ifdef DEBUG_TIMING | 982 | #ifdef DEBUG_TIMING |
929 | do_gettimeofday(&t); | 983 | do_gettimeofday(&t); |
930 | printk("**Interrupt: %d.%9.9d\n", t.tv_sec, t.tv_usec); | 984 | printk("**Interrupt: %d.%9.9d\n", t.tv_sec, t.tv_usec); |
931 | #endif | 985 | #endif |
932 | smi_event_handler(smi_info, 0); | 986 | smi_event_handler(smi_info, 0); |
933 | out: | ||
934 | spin_unlock_irqrestore(&(smi_info->si_lock), flags); | 987 | spin_unlock_irqrestore(&(smi_info->si_lock), flags); |
935 | return IRQ_HANDLED; | 988 | return IRQ_HANDLED; |
936 | } | 989 | } |
@@ -1118,7 +1171,7 @@ static int std_irq_setup(struct smi_info *info) | |||
1118 | if (info->si_type == SI_BT) { | 1171 | if (info->si_type == SI_BT) { |
1119 | rv = request_irq(info->irq, | 1172 | rv = request_irq(info->irq, |
1120 | si_bt_irq_handler, | 1173 | si_bt_irq_handler, |
1121 | IRQF_DISABLED, | 1174 | IRQF_SHARED | IRQF_DISABLED, |
1122 | DEVICE_NAME, | 1175 | DEVICE_NAME, |
1123 | info); | 1176 | info); |
1124 | if (!rv) | 1177 | if (!rv) |
@@ -1128,7 +1181,7 @@ static int std_irq_setup(struct smi_info *info) | |||
1128 | } else | 1181 | } else |
1129 | rv = request_irq(info->irq, | 1182 | rv = request_irq(info->irq, |
1130 | si_irq_handler, | 1183 | si_irq_handler, |
1131 | IRQF_DISABLED, | 1184 | IRQF_SHARED | IRQF_DISABLED, |
1132 | DEVICE_NAME, | 1185 | DEVICE_NAME, |
1133 | info); | 1186 | info); |
1134 | if (rv) { | 1187 | if (rv) { |
@@ -1708,15 +1761,11 @@ static u32 ipmi_acpi_gpe(void *context) | |||
1708 | smi_info->interrupts++; | 1761 | smi_info->interrupts++; |
1709 | spin_unlock(&smi_info->count_lock); | 1762 | spin_unlock(&smi_info->count_lock); |
1710 | 1763 | ||
1711 | if (atomic_read(&smi_info->stop_operation)) | ||
1712 | goto out; | ||
1713 | |||
1714 | #ifdef DEBUG_TIMING | 1764 | #ifdef DEBUG_TIMING |
1715 | do_gettimeofday(&t); | 1765 | do_gettimeofday(&t); |
1716 | printk("**ACPI_GPE: %d.%9.9d\n", t.tv_sec, t.tv_usec); | 1766 | printk("**ACPI_GPE: %d.%9.9d\n", t.tv_sec, t.tv_usec); |
1717 | #endif | 1767 | #endif |
1718 | smi_event_handler(smi_info, 0); | 1768 | smi_event_handler(smi_info, 0); |
1719 | out: | ||
1720 | spin_unlock_irqrestore(&(smi_info->si_lock), flags); | 1769 | spin_unlock_irqrestore(&(smi_info->si_lock), flags); |
1721 | 1770 | ||
1722 | return ACPI_INTERRUPT_HANDLED; | 1771 | return ACPI_INTERRUPT_HANDLED; |
@@ -2942,28 +2991,33 @@ static void cleanup_one_si(struct smi_info *to_clean) | |||
2942 | 2991 | ||
2943 | list_del(&to_clean->link); | 2992 | list_del(&to_clean->link); |
2944 | 2993 | ||
2945 | /* Tell the timer and interrupt handlers that we are shutting | 2994 | /* Tell the driver that we are shutting down. */ |
2946 | down. */ | ||
2947 | spin_lock_irqsave(&(to_clean->si_lock), flags); | ||
2948 | spin_lock(&(to_clean->msg_lock)); | ||
2949 | |||
2950 | atomic_inc(&to_clean->stop_operation); | 2995 | atomic_inc(&to_clean->stop_operation); |
2951 | 2996 | ||
2952 | if (to_clean->irq_cleanup) | 2997 | /* Make sure the timer and thread are stopped and will not run |
2953 | to_clean->irq_cleanup(to_clean); | 2998 | again. */ |
2954 | |||
2955 | spin_unlock(&(to_clean->msg_lock)); | ||
2956 | spin_unlock_irqrestore(&(to_clean->si_lock), flags); | ||
2957 | |||
2958 | /* Wait until we know that we are out of any interrupt | ||
2959 | handlers might have been running before we freed the | ||
2960 | interrupt. */ | ||
2961 | synchronize_sched(); | ||
2962 | |||
2963 | wait_for_timer_and_thread(to_clean); | 2999 | wait_for_timer_and_thread(to_clean); |
2964 | 3000 | ||
2965 | /* Interrupts and timeouts are stopped, now make sure the | 3001 | /* Timeouts are stopped, now make sure the interrupts are off |
2966 | interface is in a clean state. */ | 3002 | for the device. A little tricky with locks to make sure |
3003 | there are no races. */ | ||
3004 | spin_lock_irqsave(&to_clean->si_lock, flags); | ||
3005 | while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { | ||
3006 | spin_unlock_irqrestore(&to_clean->si_lock, flags); | ||
3007 | poll(to_clean); | ||
3008 | schedule_timeout_uninterruptible(1); | ||
3009 | spin_lock_irqsave(&to_clean->si_lock, flags); | ||
3010 | } | ||
3011 | disable_si_irq(to_clean); | ||
3012 | spin_unlock_irqrestore(&to_clean->si_lock, flags); | ||
3013 | while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { | ||
3014 | poll(to_clean); | ||
3015 | schedule_timeout_uninterruptible(1); | ||
3016 | } | ||
3017 | |||
3018 | /* Clean up interrupts and make sure that everything is done. */ | ||
3019 | if (to_clean->irq_cleanup) | ||
3020 | to_clean->irq_cleanup(to_clean); | ||
2967 | while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { | 3021 | while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { |
2968 | poll(to_clean); | 3022 | poll(to_clean); |
2969 | schedule_timeout_uninterruptible(1); | 3023 | schedule_timeout_uninterruptible(1); |