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 + -
显示快捷键?