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