aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorCorey Minyard <minyard@acm.org>2007-05-08 03:23:54 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-08 14:14:58 -0400
commitee6cd5f8f573ad11f270a07fb201822c2862474d (patch)
tree2daf7e039fc3ea304223addf2bc27678fc77487c /drivers/char
parentdba9b4f6a096f39dd58d67fbc643a7c1bf2973eb (diff)
ipmi: allow shared interrupts
The IPMI driver used enable_irq and disable_irq when it got into situations where it couldn't allocate memory; it did this to avoid having the interrupt just lock the machine when it couldn't get memory to perform the transaction to disable the interrupt. This patch modifies the driver to not use disable_irq and enable_irq. It instead sends the messages to the BMC to perform this operation. It also makes sure interrupts are cleanly disabled when the interface is shut down and cleans up some shutdown things that are no longer necessary. Signed-off-by: Corey Minyard <cminyard@mvista.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char')
-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);