aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c124
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
85enum si_intf_state { 91enum 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
350static 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
342static void start_clear_flags(struct smi_info *smi_info) 361static 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)
359static inline void disable_si_irq(struct smi_info *smi_info) 378static 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)
367static inline void enable_si_irq(struct smi_info *smi_info) 386static 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);