ipmi_si_intf.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,443 行 · 第 1/5 页

C
2,443
字号
	return ACPI_INTERRUPT_HANDLED;}static void acpi_gpe_irq_cleanup(struct smi_info *info){	if (!info->irq)		return;	acpi_remove_gpe_handler(NULL, info->irq, &ipmi_acpi_gpe);}static int acpi_gpe_irq_setup(struct smi_info *info){	acpi_status status;	if (!info->irq)		return 0;	/* FIXME - is level triggered right? */	status = acpi_install_gpe_handler(NULL,					  info->irq,					  ACPI_GPE_LEVEL_TRIGGERED,					  &ipmi_acpi_gpe,					  info);	if (status != AE_OK) {		printk(KERN_WARNING		       "ipmi_si: %s unable to claim ACPI GPE %d,"		       " running polled\n",		       DEVICE_NAME, info->irq);		info->irq = 0;		return -EINVAL;	} else {		info->irq_cleanup = acpi_gpe_irq_cleanup;		printk("  Using ACPI GPE %d\n", info->irq);		return 0;	}}/* * Defined at * http://h21007.www2.hp.com/dspp/files/unprotected/devresource/Docs/TechPapers/IA64/hpspmi.pdf */struct SPMITable {	s8	Signature[4];	u32	Length;	u8	Revision;	u8	Checksum;	s8	OEMID[6];	s8	OEMTableID[8];	s8	OEMRevision[4];	s8	CreatorID[4];	s8	CreatorRevision[4];	u8	InterfaceType;	u8	IPMIlegacy;	s16	SpecificationRevision;	/*	 * Bit 0 - SCI interrupt supported	 * Bit 1 - I/O APIC/SAPIC	 */	u8	InterruptType;	/* If bit 0 of InterruptType is set, then this is the SCI           interrupt in the GPEx_STS register. */	u8	GPE;	s16	Reserved;	/* If bit 1 of InterruptType is set, then this is the I/O           APIC/SAPIC interrupt. */	u32	GlobalSystemInterrupt;	/* The actual register address. */	struct acpi_generic_address addr;	u8	UID[4];	s8      spmi_id[1]; /* A '\0' terminated array starts here. */};static __devinit int try_init_acpi(struct SPMITable *spmi){	struct smi_info  *info;	char             *io_type;	u8 		 addr_space;	if (spmi->IPMIlegacy != 1) {	    printk(KERN_INFO "IPMI: Bad SPMI legacy %d\n", spmi->IPMIlegacy);  	    return -ENODEV;	}	if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)		addr_space = IPMI_MEM_ADDR_SPACE;	else		addr_space = IPMI_IO_ADDR_SPACE;	info = kzalloc(sizeof(*info), GFP_KERNEL);	if (!info) {		printk(KERN_ERR "ipmi_si: Could not allocate SI data (3)\n");		return -ENOMEM;	}	info->addr_source = "ACPI";	/* Figure out the interface type. */	switch (spmi->InterfaceType)	{	case 1:	/* KCS */		info->si_type = SI_KCS;		break;	case 2:	/* SMIC */		info->si_type = SI_SMIC;		break;	case 3:	/* BT */		info->si_type = SI_BT;		break;	default:		printk(KERN_INFO "ipmi_si: Unknown ACPI/SPMI SI type %d\n",			spmi->InterfaceType);		kfree(info);		return -EIO;	}	if (spmi->InterruptType & 1) {		/* We've got a GPE interrupt. */		info->irq = spmi->GPE;		info->irq_setup = acpi_gpe_irq_setup;	} else if (spmi->InterruptType & 2) {		/* We've got an APIC/SAPIC interrupt. */		info->irq = spmi->GlobalSystemInterrupt;		info->irq_setup = std_irq_setup;	} else {		/* Use the default interrupt setting. */		info->irq = 0;		info->irq_setup = NULL;	}	if (spmi->addr.register_bit_width) {		/* A (hopefully) properly formed register bit width. */		info->io.regspacing = spmi->addr.register_bit_width / 8;	} else {		info->io.regspacing = DEFAULT_REGSPACING;	}	info->io.regsize = info->io.regspacing;	info->io.regshift = spmi->addr.register_bit_offset;	if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {		io_type = "memory";		info->io_setup = mem_setup;		info->io.addr_type = IPMI_IO_ADDR_SPACE;	} else if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_IO) {		io_type = "I/O";		info->io_setup = port_setup;		info->io.addr_type = IPMI_MEM_ADDR_SPACE;	} else {		kfree(info);		printk("ipmi_si: Unknown ACPI I/O Address type\n");		return -EIO;	}	info->io.addr_data = spmi->addr.address;	try_smi_init(info);	return 0;}static __devinit void acpi_find_bmc(void){	acpi_status      status;	struct SPMITable *spmi;	int              i;	if (acpi_disabled)		return;	if (acpi_failure)		return;	for (i = 0; ; i++) {		status = acpi_get_firmware_table("SPMI", i+1,						 ACPI_LOGICAL_ADDRESSING,						 (struct acpi_table_header **)						 &spmi);		if (status != AE_OK)			return;		try_init_acpi(spmi);	}}#endif#ifdef CONFIG_DMIstruct dmi_ipmi_data{	u8   		type;	u8   		addr_space;	unsigned long	base_addr;	u8   		irq;	u8              offset;	u8              slave_addr;};static int __devinit decode_dmi(struct dmi_header *dm,				struct dmi_ipmi_data *dmi){	u8              *data = (u8 *)dm;	unsigned long  	base_addr;	u8		reg_spacing;	u8              len = dm->length;	dmi->type = data[4];	memcpy(&base_addr, data+8, sizeof(unsigned long));	if (len >= 0x11) {		if (base_addr & 1) {			/* I/O */			base_addr &= 0xFFFE;			dmi->addr_space = IPMI_IO_ADDR_SPACE;		}		else {			/* Memory */			dmi->addr_space = IPMI_MEM_ADDR_SPACE;		}		/* If bit 4 of byte 0x10 is set, then the lsb for the address		   is odd. */		dmi->base_addr = base_addr | ((data[0x10] & 0x10) >> 4);		dmi->irq = data[0x11];		/* The top two bits of byte 0x10 hold the register spacing. */		reg_spacing = (data[0x10] & 0xC0) >> 6;		switch(reg_spacing){		case 0x00: /* Byte boundaries */		    dmi->offset = 1;		    break;		case 0x01: /* 32-bit boundaries */		    dmi->offset = 4;		    break;		case 0x02: /* 16-byte boundaries */		    dmi->offset = 16;		    break;		default:		    /* Some other interface, just ignore it. */		    return -EIO;		}	} else {		/* Old DMI spec. */		/* Note that technically, the lower bit of the base		 * address should be 1 if the address is I/O and 0 if		 * the address is in memory.  So many systems get that		 * wrong (and all that I have seen are I/O) so we just		 * ignore that bit and assume I/O.  Systems that use		 * memory should use the newer spec, anyway. */		dmi->base_addr = base_addr & 0xfffe;		dmi->addr_space = IPMI_IO_ADDR_SPACE;		dmi->offset = 1;	}	dmi->slave_addr = data[6];	return 0;}static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data){	struct smi_info *info;	info = kzalloc(sizeof(*info), GFP_KERNEL);	if (!info) {		printk(KERN_ERR		       "ipmi_si: Could not allocate SI data\n");		return;	}	info->addr_source = "SMBIOS";	switch (ipmi_data->type) {	case 0x01: /* KCS */		info->si_type = SI_KCS;		break;	case 0x02: /* SMIC */		info->si_type = SI_SMIC;		break;	case 0x03: /* BT */		info->si_type = SI_BT;		break;	default:		return;	}	switch (ipmi_data->addr_space) {	case IPMI_MEM_ADDR_SPACE:		info->io_setup = mem_setup;		info->io.addr_type = IPMI_MEM_ADDR_SPACE;		break;	case IPMI_IO_ADDR_SPACE:		info->io_setup = port_setup;		info->io.addr_type = IPMI_IO_ADDR_SPACE;		break;	default:		kfree(info);		printk(KERN_WARNING		       "ipmi_si: Unknown SMBIOS I/O Address type: %d.\n",		       ipmi_data->addr_space);		return;	}	info->io.addr_data = ipmi_data->base_addr;	info->io.regspacing = ipmi_data->offset;	if (!info->io.regspacing)		info->io.regspacing = DEFAULT_REGSPACING;	info->io.regsize = DEFAULT_REGSPACING;	info->io.regshift = 0;	info->slave_addr = ipmi_data->slave_addr;	info->irq = ipmi_data->irq;	if (info->irq)		info->irq_setup = std_irq_setup;	try_smi_init(info);}static void __devinit dmi_find_bmc(void){	struct dmi_device    *dev = NULL;	struct dmi_ipmi_data data;	int                  rv;	while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) {		rv = decode_dmi((struct dmi_header *) dev->device_data, &data);		if (!rv)			try_init_dmi(&data);	}}#endif /* CONFIG_DMI */#ifdef CONFIG_PCI#define PCI_ERMC_CLASSCODE		0x0C0700#define PCI_ERMC_CLASSCODE_MASK		0xffffff00#define PCI_ERMC_CLASSCODE_TYPE_MASK	0xff#define PCI_ERMC_CLASSCODE_TYPE_SMIC	0x00#define PCI_ERMC_CLASSCODE_TYPE_KCS	0x01#define PCI_ERMC_CLASSCODE_TYPE_BT	0x02#define PCI_HP_VENDOR_ID    0x103C#define PCI_MMC_DEVICE_ID   0x121A#define PCI_MMC_ADDR_CW     0x10static void ipmi_pci_cleanup(struct smi_info *info){	struct pci_dev *pdev = info->addr_source_data;	pci_disable_device(pdev);}static int __devinit ipmi_pci_probe(struct pci_dev *pdev,				    const struct pci_device_id *ent){	int rv;	int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;	struct smi_info *info;	int first_reg_offset = 0;	info = kzalloc(sizeof(*info), GFP_KERNEL);	if (!info)		return ENOMEM;	info->addr_source = "PCI";	switch (class_type) {	case PCI_ERMC_CLASSCODE_TYPE_SMIC:		info->si_type = SI_SMIC;		break;	case PCI_ERMC_CLASSCODE_TYPE_KCS:		info->si_type = SI_KCS;		break;	case PCI_ERMC_CLASSCODE_TYPE_BT:		info->si_type = SI_BT;		break;	default:		kfree(info);		printk(KERN_INFO "ipmi_si: %s: Unknown IPMI type: %d\n",		       pci_name(pdev), class_type);		return ENOMEM;	}	rv = pci_enable_device(pdev);	if (rv) {		printk(KERN_ERR "ipmi_si: %s: couldn't enable PCI device\n",		       pci_name(pdev));		kfree(info);		return rv;	}	info->addr_source_cleanup = ipmi_pci_cleanup;	info->addr_source_data = pdev;	if (pdev->subsystem_vendor == PCI_HP_VENDOR_ID)		first_reg_offset = 1;	if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {		info->io_setup = port_setup;		info->io.addr_type = IPMI_IO_ADDR_SPACE;	} else {		info->io_setup = mem_setup;		info->io.addr_type = IPMI_MEM_ADDR_SPACE;	}	info->io.addr_data = pci_resource_start(pdev, 0);	info->io.regspacing = DEFAULT_REGSPACING;	info->io.regsize = DEFAULT_REGSPACING;	info->io.regshift = 0;	info->irq = pdev->irq;	if (info->irq)		info->irq_setup = std_irq_setup;	info->dev = &pdev->dev;	return try_smi_init(info);}static void __devexit ipmi_pci_remove(struct pci_dev *pdev){}#ifdef CONFIG_PMstatic int ipmi_pci_suspend(struct pci_dev *pdev, pm_message_t state){	return 0;}static int ipmi_pci_resume(struct pci_dev *pdev){	return 0;}#endifstatic struct pci_device_id ipmi_pci_devices[] = {	{ PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) },	{ PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE) }};MODULE_DEVICE_TABLE(pci, ipmi_pci_devices);static struct pci_driver ipmi_pci_driver = {        .name =         DEVICE_NAME,        .id_table =     ipmi_pci_devices,        .probe =        ipmi_pci_probe,        .remove =       __devexit_p(ipmi_pci_remove),#ifdef CONFIG_PM        .suspend =      ipmi_pci_suspend,        .resume =       ipmi_pci_resume,#endif};#endif /* CONFIG_PCI */static int try_get_dev_id(struct smi_info *smi_info){	unsigned char         msg[2];	unsigned char         *resp;	unsigned long         resp_len;	enum si_sm_result     smi_result;	int                   rv = 0;	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);	if (!resp)		return -ENOMEM;	/* Do a Get Device ID command, since it comes back with some	   useful info. */	msg[0] = IPMI_NETFN_APP_REQUEST << 2;	msg[1] = IPMI_GET_DEVICE_ID_CMD;	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);	smi_result = smi_info->handlers->event(smi_info->si_sm, 0);	for (;;)	{		if (smi_result == SI_SM_CALL_WITH_DELAY ||		    smi_result == SI_SM_CALL_WITH_TICK_DELAY) {			schedule_timeout_uninterruptible(1);

⌨️ 快捷键说明

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