📄 ipmi_si_intf.c
字号:
/* Once we get an ACPI failure, we don't try any more, because we go through the tables sequentially. Once we don't find a table, there are no more. */static int acpi_failure = 0;/* For GPE-type interrupts. */static u32 ipmi_acpi_gpe(void *context){ struct smi_info *smi_info = context; unsigned long flags;#ifdef DEBUG_TIMING struct timeval t;#endif spin_lock_irqsave(&(smi_info->si_lock), flags); spin_lock(&smi_info->count_lock); smi_info->interrupts++; spin_unlock(&smi_info->count_lock); if (atomic_read(&smi_info->stop_operation)) goto out;#ifdef DEBUG_TIMING do_gettimeofday(&t); printk("**ACPI_GPE: %d.%9.9d\n", t.tv_sec, t.tv_usec);#endif smi_event_handler(smi_info, 0); out: spin_unlock_irqrestore(&(smi_info->si_lock), flags); return ACPI_INTERRUPT_HANDLED;}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 { printk(" Using ACPI GPE %d\n", info->irq); return 0; }}static void acpi_gpe_irq_cleanup(struct smi_info *info){ if (! info->irq) return; acpi_remove_gpe_handler(NULL, info->irq, &ipmi_acpi_gpe);}/* * 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 int try_init_acpi(int intf_num, struct smi_info **new_info){ struct smi_info *info; acpi_status status; struct SPMITable *spmi; char *io_type; u8 addr_space; if (acpi_disabled) return -ENODEV; if (acpi_failure) return -ENODEV; status = acpi_get_firmware_table("SPMI", intf_num+1, ACPI_LOGICAL_ADDRESSING, (struct acpi_table_header **) &spmi); if (status != AE_OK) { acpi_failure = 1; return -ENODEV; } 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; if (! is_new_interface(-1, addr_space, spmi->addr.address)) return -ENODEV; if (! spmi->addr.register_bit_width) { acpi_failure = 1; return -ENODEV; } /* Figure out the interface type. */ switch (spmi->InterfaceType) { case 1: /* KCS */ si_type[intf_num] = "kcs"; break; case 2: /* SMIC */ si_type[intf_num] = "smic"; break; case 3: /* BT */ si_type[intf_num] = "bt"; break; default: printk(KERN_INFO "ipmi_si: Unknown ACPI/SPMI SI type %d\n", spmi->InterfaceType); return -EIO; } info = kmalloc(sizeof(*info), GFP_KERNEL); if (! info) { printk(KERN_ERR "ipmi_si: Could not allocate SI data (3)\n"); return -ENOMEM; } memset(info, 0, sizeof(*info)); if (spmi->InterruptType & 1) { /* We've got a GPE interrupt. */ info->irq = spmi->GPE; info->irq_setup = acpi_gpe_irq_setup; info->irq_cleanup = acpi_gpe_irq_cleanup; } else if (spmi->InterruptType & 2) { /* We've got an APIC/SAPIC interrupt. */ info->irq = spmi->GlobalSystemInterrupt; info->irq_setup = std_irq_setup; info->irq_cleanup = std_irq_cleanup; } 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. */ regspacings[intf_num] = spmi->addr.register_bit_width / 8; info->io.regspacing = spmi->addr.register_bit_width / 8; } else { /* Some broken systems get this wrong and set the value * to zero. Assume it is the default spacing. If that * is wrong, too bad, the vendor should fix the tables. */ regspacings[intf_num] = DEFAULT_REGSPACING; info->io.regspacing = DEFAULT_REGSPACING; } regsizes[intf_num] = regspacings[intf_num]; info->io.regsize = regsizes[intf_num]; regshifts[intf_num] = spmi->addr.register_bit_offset; info->io.regshift = regshifts[intf_num]; if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { io_type = "memory"; info->io_setup = mem_setup; addrs[intf_num] = spmi->addr.address; info->io.info = &(addrs[intf_num]); } else if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_IO) { io_type = "I/O"; info->io_setup = port_setup; ports[intf_num] = spmi->addr.address; info->io.info = &(ports[intf_num]); } else { kfree(info); printk("ipmi_si: Unknown ACPI I/O Address type\n"); return -EIO; } *new_info = info; printk("ipmi_si: ACPI/SPMI specifies \"%s\" %s SI @ 0x%lx\n", si_type[intf_num], io_type, (unsigned long) spmi->addr.address); return 0;}#endif#ifdef CONFIG_X86typedef struct dmi_ipmi_data{ u8 type; u8 addr_space; unsigned long base_addr; u8 irq; u8 offset; u8 slave_addr;} dmi_ipmi_data_t;static dmi_ipmi_data_t dmi_data[SI_MAX_DRIVERS];static int dmi_data_entries;static int __init decode_dmi(struct dmi_header *dm, int intf_num){ u8 *data = (u8 *)dm; unsigned long base_addr; u8 reg_spacing; u8 len = dm->length; dmi_ipmi_data_t *ipmi_data = dmi_data+intf_num; ipmi_data->type = data[4]; memcpy(&base_addr, data+8, sizeof(unsigned long)); if (len >= 0x11) { if (base_addr & 1) { /* I/O */ base_addr &= 0xFFFE; ipmi_data->addr_space = IPMI_IO_ADDR_SPACE; } else { /* Memory */ ipmi_data->addr_space = IPMI_MEM_ADDR_SPACE; } /* If bit 4 of byte 0x10 is set, then the lsb for the address is odd. */ ipmi_data->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); ipmi_data->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 */ ipmi_data->offset = 1; break; case 0x01: /* 32-bit boundaries */ ipmi_data->offset = 4; break; case 0x02: /* 16-byte boundaries */ ipmi_data->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. */ ipmi_data->base_addr = base_addr & 0xfffe; ipmi_data->addr_space = IPMI_IO_ADDR_SPACE; ipmi_data->offset = 1; } ipmi_data->slave_addr = data[6]; if (is_new_interface(-1, ipmi_data->addr_space,ipmi_data->base_addr)) { dmi_data_entries++; return 0; } memset(ipmi_data, 0, sizeof(dmi_ipmi_data_t)); return -1;}static void __init dmi_find_bmc(void){ struct dmi_device *dev = NULL; int intf_num = 0; while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { if (intf_num >= SI_MAX_DRIVERS) break; decode_dmi((struct dmi_header *) dev->device_data, intf_num++); }}static int try_init_smbios(int intf_num, struct smi_info **new_info){ struct smi_info *info; dmi_ipmi_data_t *ipmi_data = dmi_data+intf_num; char *io_type; if (intf_num >= dmi_data_entries) return -ENODEV; switch (ipmi_data->type) { case 0x01: /* KCS */ si_type[intf_num] = "kcs"; break; case 0x02: /* SMIC */ si_type[intf_num] = "smic"; break; case 0x03: /* BT */ si_type[intf_num] = "bt"; break; default: return -EIO; } info = kmalloc(sizeof(*info), GFP_KERNEL); if (! info) { printk(KERN_ERR "ipmi_si: Could not allocate SI data (4)\n"); return -ENOMEM; } memset(info, 0, sizeof(*info)); if (ipmi_data->addr_space == 1) { io_type = "memory"; info->io_setup = mem_setup; addrs[intf_num] = ipmi_data->base_addr; info->io.info = &(addrs[intf_num]); } else if (ipmi_data->addr_space == 2) { io_type = "I/O"; info->io_setup = port_setup; ports[intf_num] = ipmi_data->base_addr; info->io.info = &(ports[intf_num]); } else { kfree(info); printk("ipmi_si: Unknown SMBIOS I/O Address type.\n"); return -EIO; } regspacings[intf_num] = ipmi_data->offset; info->io.regspacing = regspacings[intf_num]; if (! info->io.regspacing) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; info->io.regshift = regshifts[intf_num]; info->slave_addr = ipmi_data->slave_addr; irqs[intf_num] = ipmi_data->irq; *new_info = info; printk("ipmi_si: Found SMBIOS-specified state machine at %s" " address 0x%lx, slave address 0x%x\n", io_type, (unsigned long)ipmi_data->base_addr, ipmi_data->slave_addr); return 0;}#endif /* CONFIG_X86 */#ifdef CONFIG_PCI#define PCI_ERMC_CLASSCODE 0x0C0700#define PCI_HP_VENDOR_ID 0x103C#define PCI_MMC_DEVICE_ID 0x121A#define PCI_MMC_ADDR_CW 0x10/* Avoid more than one attempt to probe pci smic. */static int pci_smic_checked = 0;static int find_pci_smic(int intf_num, struct smi_info **new_info){ struct smi_info *info; int error; struct pci_dev *pci_dev = NULL; u16 base_addr; int fe_rmc = 0; if (pci_smic_checked) return -ENODEV; pci_smic_checked = 1; pci_dev = pci_get_device(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID, NULL); if (! pci_dev) { pci_dev = pci_get_class(PCI_ERMC_CLASSCODE, NULL); if (pci_dev && (pci_dev->subsystem_vendor == PCI_HP_VENDOR_ID)) fe_rmc = 1; else return -ENODEV; } error = pci_read_config_word(pci_dev, PCI_MMC_ADDR_CW, &base_addr); if (error) { pci_dev_put(pci_dev); printk(KERN_ERR "ipmi_si: pci_read_config_word() failed (%d).\n", error); return -ENODEV; } /* Bit 0: 1 specifies programmed I/O, 0 specifies memory mapped I/O */ if (! (base_addr & 0x0001)) { pci_dev_put(pci_dev); printk(KERN_ERR "ipmi_si: memory mapped I/O not supported for PCI" " smic.\n"); return -ENODEV; } base_addr &= 0xFFFE; if (! fe_rmc) /* Data register starts at base address + 1 in eRMC */ ++base_addr; if (! is_new_interface(-1, IPMI_IO_ADDR_SPACE, base_addr)) { pci_dev_put(pci_dev); return -ENODEV; } info = kmalloc(sizeof(*info), GFP_KERNEL); if (! info) { pci_dev_put(pci_dev); printk(KERN_ERR "ipmi_si: Could not allocate SI data (5)\n"); return -ENOMEM; } memset(info, 0, sizeof(*info)); info->io_setup = port_setup; ports[intf_num] = base_addr; info->io.info = &(ports[intf_num]); info->io.regspacing = regspacings[intf_num]; if (! info->io.regspacing) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = DEFAULT_REGSPACING; info->io.regshift = regshifts[intf_num]; *new_info = info; irqs[intf_num] = pci_dev->irq; si_type[intf_num] = "smic";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -