⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ipmi_si_intf.c

📁 h内核
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * ipmi_si.c * * The interface to the IPMI driver for the system interfaces (KCS, SMIC, * BT). * * Author: MontaVista Software, Inc. *         Corey Minyard <minyard@mvista.com> *         source@mvista.com * * Copyright 2002 MontaVista Software Inc. * *  This program is free software; you can redistribute it and/or modify it *  under the terms of the GNU General Public License as published by the *  Free Software Foundation; either version 2 of the License, or (at your *  option) any later version. * * *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *  You should have received a copy of the GNU General Public License along *  with this program; if not, write to the Free Software Foundation, Inc., *  675 Mass Ave, Cambridge, MA 02139, USA. *//* * This file holds the "policy" for the interface to the SMI state * machine.  It does the configuration, handles timers and interrupts, * and drives the real SMI state machine. */#include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <asm/system.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/list.h>#include <linux/pci.h>#include <linux/ioport.h>#include <asm/irq.h>#ifdef CONFIG_HIGH_RES_TIMERS#include <linux/hrtime.h># if defined(schedule_next_int)/* Old high-res timer code, do translations. */#  define get_arch_cycles(a) quick_update_jiffies_sub(a)#  define arch_cycles_per_jiffy cycles_per_jiffies# endifstatic inline void add_usec_to_timer(struct timer_list *t, long v){	t->sub_expires += nsec_to_arch_cycle(v * 1000);	while (t->sub_expires >= arch_cycles_per_jiffy)	{		t->expires++;		t->sub_expires -= arch_cycles_per_jiffy;	}}#endif#include <linux/interrupt.h>#include <linux/rcupdate.h>#include <linux/ipmi_smi.h>#include <asm/io.h>#include "ipmi_si_sm.h"#include <linux/init.h>#define IPMI_SI_VERSION "v33"/* Measure times between events in the driver. */#undef DEBUG_TIMING/* Call every 10 ms. */#define SI_TIMEOUT_TIME_USEC	10000#define SI_USEC_PER_JIFFY	(1000000/HZ)#define SI_TIMEOUT_JIFFIES	(SI_TIMEOUT_TIME_USEC/SI_USEC_PER_JIFFY)#define SI_SHORT_TIMEOUT_USEC  250 /* .25ms when the SM request a                                       short timeout */enum si_intf_state {	SI_NORMAL,	SI_GETTING_FLAGS,	SI_GETTING_EVENTS,	SI_CLEARING_FLAGS,	SI_CLEARING_FLAGS_THEN_SET_IRQ,	SI_GETTING_MESSAGES,	SI_ENABLE_INTERRUPTS1,	SI_ENABLE_INTERRUPTS2	/* FIXME - add watchdog stuff. */};enum si_type {    SI_KCS, SI_SMIC, SI_BT};struct smi_info{	ipmi_smi_t             intf;	struct si_sm_data      *si_sm;	struct si_sm_handlers  *handlers;	enum si_type           si_type;	spinlock_t             si_lock;	spinlock_t             msg_lock;	struct list_head       xmit_msgs;	struct list_head       hp_xmit_msgs;	struct ipmi_smi_msg    *curr_msg;	enum si_intf_state     si_state;	/* Used to handle the various types of I/O that can occur with           IPMI */	struct si_sm_io io;	int (*io_setup)(struct smi_info *info);	void (*io_cleanup)(struct smi_info *info);	int (*irq_setup)(struct smi_info *info);	void (*irq_cleanup)(struct smi_info *info);	unsigned int io_size;	/* Flags from the last GET_MSG_FLAGS command, used when an ATTN	   is set to hold the flags until we are done handling everything	   from the flags. */#define RECEIVE_MSG_AVAIL	0x01#define EVENT_MSG_BUFFER_FULL	0x02#define WDT_PRE_TIMEOUT_INT	0x08	unsigned char       msg_flags;	/* If set to true, this will request events the next time the	   state machine is idle. */	atomic_t            req_events;	/* If true, run the state machine to completion on every send	   call.  Generally used after a panic to make sure stuff goes	   out. */	int                 run_to_completion;	/* The I/O port of an SI interface. */	int                 port;	/* The space between start addresses of the two ports.  For	   instance, if the first port is 0xca2 and the spacing is 4, then	   the second port is 0xca6. */	unsigned int        spacing;	/* zero if no irq; */	int                 irq;	/* The timer for this si. */	struct timer_list   si_timer;	/* The time (in jiffies) the last timeout occurred at. */	unsigned long       last_timeout_jiffies;	/* Used to gracefully stop the timer without race conditions. */	volatile int        stop_operation;	volatile int        timer_stopped;	/* The driver will disable interrupts when it gets into a	   situation where it cannot handle messages due to lack of	   memory.  Once that situation clears up, it will re-enable	   interrupts. */	int interrupt_disabled;	unsigned char ipmi_si_dev_rev;	unsigned char ipmi_si_fw_rev_major;	unsigned char ipmi_si_fw_rev_minor;	unsigned char ipmi_version_major;	unsigned char ipmi_version_minor;	/* Counters and things for the proc filesystem. */	spinlock_t count_lock;	unsigned long short_timeouts;	unsigned long long_timeouts;	unsigned long timeout_restarts;	unsigned long idles;	unsigned long interrupts;	unsigned long attentions;	unsigned long flag_fetches;	unsigned long hosed_count;	unsigned long complete_transactions;	unsigned long events;	unsigned long watchdog_pretimeouts;	unsigned long incoming_messages;};static void si_restart_short_timer(struct smi_info *smi_info);static void deliver_recv_msg(struct smi_info *smi_info,			     struct ipmi_smi_msg *msg){	/* Deliver the message to the upper layer with the lock           released. */	spin_unlock(&(smi_info->si_lock));	ipmi_smi_msg_received(smi_info->intf, msg);	spin_lock(&(smi_info->si_lock));}static void return_hosed_msg(struct smi_info *smi_info){	struct ipmi_smi_msg *msg = smi_info->curr_msg;	/* Make it a reponse */	msg->rsp[0] = msg->data[0] | 4;	msg->rsp[1] = msg->data[1];	msg->rsp[2] = 0xFF; /* Unknown error. */	msg->rsp_size = 3;	smi_info->curr_msg = NULL;	deliver_recv_msg(smi_info, msg);}static enum si_sm_result start_next_msg(struct smi_info *smi_info){	int              rv;	struct list_head *entry = NULL;#ifdef DEBUG_TIMING	struct timeval t;#endif	/* No need to save flags, we aleady have interrupts off and we	   already hold the SMI lock. */	spin_lock(&(smi_info->msg_lock));	/* Pick the high priority queue first. */	if (! list_empty(&(smi_info->hp_xmit_msgs))) {		entry = smi_info->hp_xmit_msgs.next;	} else if (! list_empty(&(smi_info->xmit_msgs))) {		entry = smi_info->xmit_msgs.next;	}	if (!entry) {		smi_info->curr_msg = NULL;		rv = SI_SM_IDLE;	} else {		int err;		list_del(entry);		smi_info->curr_msg = list_entry(entry,						struct ipmi_smi_msg,						link);#ifdef DEBUG_TIMING		do_gettimeofday(&t);		printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec);#endif		err = smi_info->handlers->start_transaction(			smi_info->si_sm,			smi_info->curr_msg->data,			smi_info->curr_msg->data_size);		if (err) {			return_hosed_msg(smi_info);		}		rv = SI_SM_CALL_WITHOUT_DELAY;	}	spin_unlock(&(smi_info->msg_lock));	return rv;}static void start_enable_irq(struct smi_info *smi_info){	unsigned char msg[2];	/* If we are enabling interrupts, we have to tell the	   BMC to use them. */	msg[0] = (IPMI_NETFN_APP_REQUEST << 2);	msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);	smi_info->si_state = SI_ENABLE_INTERRUPTS1;}static void start_clear_flags(struct smi_info *smi_info){	unsigned char msg[3];	/* Make sure the watchdog pre-timeout flag is not set at startup. */	msg[0] = (IPMI_NETFN_APP_REQUEST << 2);	msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD;	msg[2] = WDT_PRE_TIMEOUT_INT;	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3);	smi_info->si_state = SI_CLEARING_FLAGS;}/* When we have a situtaion where we run out of memory and cannot   allocate messages, we just leave them in the BMC and run the system   polled until we can allocate some memory.  Once we have some   memory, we will re-enable the interrupt. */static inline void disable_si_irq(struct smi_info *smi_info){	if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {		disable_irq_nosync(smi_info->irq);		smi_info->interrupt_disabled = 1;	}}static inline void enable_si_irq(struct smi_info *smi_info){	if ((smi_info->irq) && (smi_info->interrupt_disabled)) {		enable_irq(smi_info->irq);		smi_info->interrupt_disabled = 0;	}}static void handle_flags(struct smi_info *smi_info){	if (smi_info->msg_flags & WDT_PRE_TIMEOUT_INT) {		/* Watchdog pre-timeout */		spin_lock(&smi_info->count_lock);		smi_info->watchdog_pretimeouts++;		spin_unlock(&smi_info->count_lock);		start_clear_flags(smi_info);		smi_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT;		spin_unlock(&(smi_info->si_lock));		ipmi_smi_watchdog_pretimeout(smi_info->intf);		spin_lock(&(smi_info->si_lock));	} else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) {		/* Messages available. */		smi_info->curr_msg = ipmi_alloc_smi_msg();		if (!smi_info->curr_msg) {			disable_si_irq(smi_info);			smi_info->si_state = SI_NORMAL;			return;		}		enable_si_irq(smi_info);		smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);		smi_info->curr_msg->data[1] = IPMI_GET_MSG_CMD;		smi_info->curr_msg->data_size = 2;		smi_info->handlers->start_transaction(			smi_info->si_sm,			smi_info->curr_msg->data,			smi_info->curr_msg->data_size);		smi_info->si_state = SI_GETTING_MESSAGES;	} else if (smi_info->msg_flags & EVENT_MSG_BUFFER_FULL) {		/* Events available. */		smi_info->curr_msg = ipmi_alloc_smi_msg();		if (!smi_info->curr_msg) {			disable_si_irq(smi_info);			smi_info->si_state = SI_NORMAL;			return;		}		enable_si_irq(smi_info);		smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);		smi_info->curr_msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD;		smi_info->curr_msg->data_size = 2;		smi_info->handlers->start_transaction(			smi_info->si_sm,			smi_info->curr_msg->data,			smi_info->curr_msg->data_size);		smi_info->si_state = SI_GETTING_EVENTS;	} else {		smi_info->si_state = SI_NORMAL;	}}static void handle_transaction_done(struct smi_info *smi_info){	struct ipmi_smi_msg *msg;#ifdef DEBUG_TIMING	struct timeval t;	do_gettimeofday(&t);	printk("**Done: %d.%9.9d\n", t.tv_sec, t.tv_usec);#endif	switch (smi_info->si_state) {	case SI_NORMAL:		if (!smi_info->curr_msg)			break;		smi_info->curr_msg->rsp_size			= smi_info->handlers->get_result(				smi_info->si_sm,				smi_info->curr_msg->rsp,				IPMI_MAX_MSG_LENGTH);		/* Do this here becase deliver_recv_msg() releases the		   lock, and a new message can be put in during the		   time the lock is released. */		msg = smi_info->curr_msg;		smi_info->curr_msg = NULL;		deliver_recv_msg(smi_info, msg);		break;	case SI_GETTING_FLAGS:	{		unsigned char msg[4];		unsigned int  len;		/* We got the flags from the SMI, now handle them. */		len = smi_info->handlers->get_result(smi_info->si_sm, msg, 4);		if (msg[2] != 0) {			/* Error fetching flags, just give up for			   now. */			smi_info->si_state = SI_NORMAL;		} else if (len < 3) {			/* Hmm, no flags.  That's technically illegal, but			   don't use uninitialized data. */			smi_info->si_state = SI_NORMAL;		} else {			smi_info->msg_flags = msg[3];			handle_flags(smi_info);		}		break;	}	case SI_CLEARING_FLAGS:	case SI_CLEARING_FLAGS_THEN_SET_IRQ:	{		unsigned char msg[3];		/* We cleared the flags. */		smi_info->handlers->get_result(smi_info->si_sm, msg, 3);		if (msg[2] != 0) {			/* Error clearing flags */			printk(KERN_WARNING			       "ipmi_si: Error clearing flags: %2.2x\n",			       msg[2]);		}		if (smi_info->si_state == SI_CLEARING_FLAGS_THEN_SET_IRQ)			start_enable_irq(smi_info);		else			smi_info->si_state = SI_NORMAL;		break;	}	case SI_GETTING_EVENTS:	{		smi_info->curr_msg->rsp_size			= smi_info->handlers->get_result(				smi_info->si_sm,				smi_info->curr_msg->rsp,				IPMI_MAX_MSG_LENGTH);		/* Do this here becase deliver_recv_msg() releases the		   lock, and a new message can be put in during the		   time the lock is released. */		msg = smi_info->curr_msg;		smi_info->curr_msg = NULL;		if (msg->rsp[2] != 0) {			/* Error getting event, probably done. */			msg->done(msg);			/* Take off the event flag. */			smi_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;			handle_flags(smi_info);		} else {			spin_lock(&smi_info->count_lock);			smi_info->events++;			spin_unlock(&smi_info->count_lock);			/* Do this before we deliver the message			   because delivering the message releases the			   lock and something else can mess with the			   state. */			handle_flags(smi_info);			deliver_recv_msg(smi_info, msg);		}		break;	}	case SI_GETTING_MESSAGES:	{		smi_info->curr_msg->rsp_size			= smi_info->handlers->get_result(				smi_info->si_sm,				smi_info->curr_msg->rsp,				IPMI_MAX_MSG_LENGTH);		/* Do this here becase deliver_recv_msg() releases the		   lock, and a new message can be put in during the		   time the lock is released. */		msg = smi_info->curr_msg;		smi_info->curr_msg = NULL;		if (msg->rsp[2] != 0) {			/* Error getting event, probably done. */			msg->done(msg);			/* Take off the msg flag. */			smi_info->msg_flags &= ~RECEIVE_MSG_AVAIL;			handle_flags(smi_info);		} else {			spin_lock(&smi_info->count_lock);			smi_info->incoming_messages++;			spin_unlock(&smi_info->count_lock);			/* Do this before we deliver the message			   because delivering the message releases the			   lock and something else can mess with the			   state. */			handle_flags(smi_info);			deliver_recv_msg(smi_info, msg);		}		break;	}	case SI_ENABLE_INTERRUPTS1:	{		unsigned char msg[4];		/* We got the flags from the SMI, now handle them. */		smi_info->handlers->get_result(smi_info->si_sm, msg, 4);		if (msg[2] != 0) {			printk(KERN_WARNING			       "ipmi_si: Could not enable interrupts"			       ", failed get, using polled mode.\n");			smi_info->si_state = SI_NORMAL;		} else {			msg[0] = (IPMI_NETFN_APP_REQUEST << 2);			msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;			msg[2] = msg[3] | 1; /* enable msg queue int */			smi_info->handlers->start_transaction(				smi_info->si_sm, msg, 3);			smi_info->si_state = SI_ENABLE_INTERRUPTS2;		}		break;	}	case SI_ENABLE_INTERRUPTS2:	{		unsigned char msg[4];		/* We got the flags from the SMI, now handle them. */		smi_info->handlers->get_result(smi_info->si_sm, msg, 4);		if (msg[2] != 0) {			printk(KERN_WARNING			       "ipmi_si: Could not enable interrupts"			       ", failed set, using polled mode.\n");		}		smi_info->si_state = SI_NORMAL;		break;	}	}}/* Called on timeouts and events.  Timeouts should pass the elapsed   time, interrupts should pass in zero. */static enum si_sm_result smi_event_handler(struct smi_info *smi_info,					   int time){	enum si_sm_result si_sm_result; restart:	/* There used to be a loop here that waited a little while	   (around 25us) before giving up.  That turned out to be	   pointless, the minimum delays I was seeing were in the 300us	   range, which is far too long to wait in an interrupt.  So	   we just run until the state machine tells us something	   happened or it needs a delay. */	si_sm_result = smi_info->handlers->event(smi_info->si_sm, time);	time = 0;	while (si_sm_result == SI_SM_CALL_WITHOUT_DELAY)	{		si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0);	}	if (si_sm_result == SI_SM_TRANSACTION_COMPLETE)	{		spin_lock(&smi_info->count_lock);		smi_info->complete_transactions++;		spin_unlock(&smi_info->count_lock);		handle_transaction_done(smi_info);		si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0);	}	else if (si_sm_result == SI_SM_HOSED)	{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -