ipmi_si_intf.c
来自「linux 内核源代码」· C语言 代码 · 共 2,489 行 · 第 1/5 页
C
2,489 行
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: kfree(info); 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){ const struct dmi_device *dev = NULL; struct dmi_ipmi_data data; int rv; while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { memset(&data, 0, sizeof(data)); rv = decode_dmi((const 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; pci_set_drvdata(pdev, info); return try_smi_init(info);}static void __devexit ipmi_pci_remove(struct pci_dev *pdev){ struct smi_info *info = pci_get_drvdata(pdev); cleanup_one_si(info);}#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_MASK) }, { 0, }};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 */#ifdef CONFIG_PPC_OFstatic int __devinit ipmi_of_probe(struct of_device *dev, const struct of_device_id *match){ struct smi_info *info; struct resource resource; const int *regsize, *regspacing, *regshift; struct device_node *np = dev->node; int ret; int proplen; dev_info(&dev->dev, PFX "probing via device tree\n"); ret = of_address_to_resource(np, 0, &resource); if (ret) { dev_warn(&dev->dev, PFX "invalid address from OF\n"); return ret; } regsize = of_get_property(np, "reg-size", &proplen); if (regsize && proplen != 4) { dev_warn(&dev->dev, PFX "invalid regsize from OF\n"); return -EINVAL; } regspacing = of_get_property(np, "reg-spacing", &proplen); if (regspacing && proplen != 4) { dev_warn(&dev->dev, PFX "invalid regspacing from OF\n"); return -EINVAL; } regshift = of_get_property(np, "reg-shift", &proplen); if (regshift && proplen != 4) { dev_warn(&dev->dev, PFX "invalid regshift from OF\n"); return -EINVAL; } info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { dev_err(&dev->dev, PFX "could not allocate memory for OF probe\n"); return -ENOMEM; } info->si_type = (enum si_type) match->data; info->addr_source = "device-tree"; info->io_setup = mem_setup; info->irq_setup = std_irq_setup; info->io.addr_type = IPMI_MEM_ADDR_SPACE; info->io.addr_data = resource.start; info->io.regsize = regsize ? *regsize : DEFAULT_REGSIZE; info->io.regspacing = regspacing ? *regspacing : DEFAULT_REGSPACING; info->io.regshift = regshift ? *regshift : 0; info->irq = irq_of_parse_and_map(dev->node, 0); info->dev = &dev->dev; dev_dbg(&dev->dev, "addr 0x%lx regsize %d spacing %d irq %x\n", info->io.addr_data, info->io.regsize, info->io.regspacing, info->irq); dev->dev.driver_data = (void*) info; return try_smi_init(info);}static int __devexit ipmi_of_remove(struct of_device *dev){ cleanup_one_si(dev->dev.driver_data); return 0;}static struct of_device_id ipmi_match[] ={ { .type = "ipmi", .compatible = "ipmi-kcs", .data = (void *)(unsigned long) SI_KCS }, { .type = "ipmi", .compatible = "ipmi-smic", .data = (void *)(unsigned long) SI_SMIC }, { .type = "ipmi", .compatible = "ipmi-bt", .data = (void *)(unsigned long) SI_BT }, {},};static struct of_platform_driver ipmi_of_platform_driver ={ .name = "ipmi", .match_table = ipmi_match, .probe = ipmi_of_probe, .remove = __devexit_p(ipmi_of_remove),};#endif /* CONFIG_PPC_OF */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); smi_result = smi_info->handlers->event( smi_info->si_sm, 100); } else if (smi_result == SI_SM_CALL_WITHOUT_DELAY) { smi_result = smi_info->handlers->event( smi_info->si_sm, 0); } else break; } if (smi_result == SI_SM_HOSED) { /* We couldn't get the state machine to run, so whatever's at the port is probably not an IPMI SMI interface. */ rv = -ENODEV; goto out; } /* Otherwise, we got some data. */ resp_len = smi_info->handlers->get_result(smi_info->si_sm, resp, IPMI_MAX_MSG_LENGTH); /* Check and record info from the get device id, in case we need it. */ rv = ipmi_demangle_device_id(resp, resp_len, &smi_info->device_id); out: kfree(resp); return rv;}static int type_file_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ struct smi_info *smi = data; return sprintf(page, "%s\n", si_to_str[smi->si_type]);}static int stat_file_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ char *out = (char *) page; struct smi_info *smi = data; out += sprintf(out, "interrupts_enabled: %d\n", smi->irq && !smi->interrupt_disabled); out += sprintf(out, "short_timeouts: %ld\n", smi->short_timeouts); out += sprintf(out, "long_timeouts: %ld\n", smi->long_timeouts); out += sprintf(out, "timeout_restarts: %ld\n", smi->timeout_restarts); out += sprintf(out, "idles: %ld\n", smi->idles); out += sprintf(out, "interrupts: %ld\n", smi->interrupts); out += sprintf(out, "attentions: %ld\n", smi->attentions); out += sprintf(out, "flag_fetches: %ld\n", smi->flag_fetches); out += sprintf(out, "hosed_count: %ld\n", smi->hosed_count); out += sprintf(out, "complete_transactions: %ld\n", smi->complete_transactions); out += sprintf(out, "events: %ld\n", smi->events); out += sprintf(out, "watchdog_pretimeouts: %ld\n", smi->watchdog_pretimeouts); out += sprintf(out, "incoming_messages: %ld\n", smi->incoming_messages); return out - page;}static int param_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ struct smi_info *smi = data; return sprintf(page, "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", si_to_str[smi->si_type], addr_space_to_str[smi->io.addr_type], smi->io.addr_data, smi->io.regspacing, smi->io.regsize, smi->io.regshift, smi->irq, smi->slave_addr);}/* * oem_data_avail_to_receive_msg_avail * @info - smi_info structure with msg_flags set * * Converts flags from OEM_DATA_AVAIL to RECEIVE_MSG_AVAIL * Returns 1 indicating need to re-run handle_flags(). */static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info){ smi_info->msg_flags = ((smi_info->msg_flags & ~OEM_DATA_AVAIL) | RECEIVE_MSG_AVAIL); return 1;}/* * setup_dell_poweredge_oem_data_handler * @info - smi_info.device_id must be populated * * Systems that match, but have firmware version < 1.40 may assert * OEM0_DATA_AVAIL on their own, without being told via Set Flags that * it's safe to do so. Such systems will de-assert OEM1_DATA_AVAIL * upon receipt of IPMI_GET_MSG_CMD, so we should treat these flags * as RECEIVE_MSG_AVAIL instead. * * As Dell has no plans to release IPMI 1.5 firmware that *ever* * assert the OEM[012] bits, and if it did, the driver would have to * change to handle that properly, we don't actually check for the * firmware version. * Device ID = 0x20 BMC on PowerEdge 8G servers * Device Revision = 0x80 * Firmware Revision1 = 0x01 BMC version 1.40 * Firmware Revision2 = 0x40 BCD encoded * IPMI Version = 0x51 IPMI 1.5 * Manufacturer ID = A2 02 00 Dell IANA * * Additionally, PowerEdge systems with IPMI < 1.5 may also assert * OEM0_DATA_AVAIL and needs
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?