On Monday 16 November 2009 02:18:09 am yakui.zhao@xxxxxxxxx wrote: > From: Zhao Yakui <yakui.zhao@xxxxxxxxx> When you post a new version of a patch set, please add a "v2", "v3", etc., so we can tell that this is a new version of something we've seen before. When people have commented on previous versions of your patch, please CC: them when you post a new version. It's also nice if you mention what changed in your new version if you addressed any feedback. In fact, I gave you detailed feedback on previous versions of this patch, and you've ignored it all. This current patch is identical to the one you posted on October 26, except for a minor change in acpi_find_bmc(). As I've pointed out before, an ACPI driver must not use acpi_walk_namespace() to discover its devices. It must use acpi_bus_register_driver(). Bjorn > According to the IPMI 2.0 spec the IPMI system interface can be located with > ACPI. One is located in SPMI table(Service Processor Management Interface > table). Another is located in ACPI namespace. > This patch is to locate the IPMI system interface in ACPI namespace and > register it. > It includes the following two steps: > 1. enumerate the ACPI device tree to find the IPMI system interface > The IPMI device type is IPI0001. When the device is found, it > will continue to parse the corresponding resources. > For example: > interface type (KCS, BT, SMIC) (SSIF is not supported) > interrupt number and type (_GPE or GSI) > Memory or IO base address > 2. register the IPMI system interface. > > Signed-off-by: Zhao Yakui <yakui.zhao@xxxxxxxxx> > --- > drivers/char/ipmi/ipmi_si_intf.c | 401 +++++++++++++++++++++++++++++++++++++- > 1 files changed, 400 insertions(+), 1 deletions(-) > > diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c > index d2e6980..d6a2c60 100644 > --- a/drivers/char/ipmi/ipmi_si_intf.c > +++ b/drivers/char/ipmi/ipmi_si_intf.c > @@ -1813,6 +1813,35 @@ static __devinit void hardcode_find_bmc(void) > * are no more. > */ > static int acpi_failure; > +static LIST_HEAD(acpi_ipmi); > + > +struct acpi_device_ipmi { > + struct list_head link; > + u8 interfacetype; > + /* > + * 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; > + /* > + * If bit 1 of InterruptType is set, then this is the I/O > + * APIC/SAPIC interrupt. > + */ > + u32 global_interrupt; > + > + /* The actual register address. */ > + struct acpi_generic_address addr; > + struct acpi_generic_address sm_addr; > + > + u16 ipmi_revision; > + u8 resource_count; > + struct device *dev; > +}; > > /* For GPE-type interrupts. */ > static u32 ipmi_acpi_gpe(void *context) > @@ -2002,6 +2031,367 @@ static __devinit int try_init_acpi(struct SPMITable *spmi) > return 0; > } > > +static __devinit int try_init_acpi_device(struct acpi_device_ipmi *spmi) > +{ > + struct smi_info *info; > + u8 addr_space; > + > + if (spmi->addr.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->global_interrupt; > + info->irq_setup = std_irq_setup; > + } else { > + /* Use the default interrupt setting. */ > + info->irq = 0; > + info->irq_setup = NULL; > + } > + > + if (spmi->addr.bit_width) { > + /* A (hopefully) properly formed register bit width. */ > + info->io.regspacing = spmi->addr.bit_width / 8; > + } else { > + info->io.regspacing = DEFAULT_REGSPACING; > + } > + info->io.regsize = info->io.regspacing; > + info->io.regshift = spmi->addr.bit_offset; > + > + if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { > + info->io_setup = mem_setup; > + info->io.addr_type = IPMI_MEM_ADDR_SPACE; > + } else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) { > + info->io_setup = port_setup; > + info->io.addr_type = IPMI_IO_ADDR_SPACE; > + } else { > + kfree(info); > + printk(KERN_WARNING > + "ipmi_si: Unknown ACPI I/O Address type\n"); > + return -EIO; > + } > + info->io.addr_data = spmi->addr.address; > + info->dev = spmi->dev; > + > + try_smi_init(info); > + > + return 0; > +} > + > +static acpi_status > +acpi_parse_io_ports(struct acpi_resource *resource, void *context) > +{ > + struct acpi_device_ipmi *p_ipmi = context; > + > + if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ || > + resource->type == ACPI_RESOURCE_TYPE_IRQ) { > + unsigned int irq_number; > + if (p_ipmi->interrupttype) { > + /* > + * If it already support the interrupt through GPE, > + * it is unnecessary to get this interrupt again. > + */ > + printk(KERN_WARNING "Interrupt through GPE is already" > + " supported.\n"); > + return AE_OK; > + } > + if (resource->type == ACPI_RESOURCE_TYPE_IRQ) { > + struct acpi_resource_irq *irq; > + irq = &resource->data.irq; > + if (irq->interrupt_count != 1) { > + printk(KERN_WARNING "incorrect IRQ is " > + "defined in _CRS\n"); > + return AE_OK; > + } > + irq_number = irq->interrupts[0]; > + } else { > + struct acpi_resource_extended_irq *extended_irq; > + extended_irq = &resource->data.extended_irq; > + if (extended_irq->interrupt_count != 1) { > + printk(KERN_WARNING "incorrect IRQ is " > + "defined in _CRS\n"); > + return AE_OK; > + } > + irq_number = extended_irq->interrupts[0]; > + } > + p_ipmi->global_interrupt = irq_number; > + if (p_ipmi->global_interrupt) { > + /* GSI interrupt type */ > + p_ipmi->interrupttype |= 0x02; > + } > + return AE_OK; > + } > + if (resource->type == ACPI_RESOURCE_TYPE_IO || > + resource->type == ACPI_RESOURCE_TYPE_FIXED_IO) { > + u16 address; > + struct acpi_resource_io *io; > + struct acpi_resource_fixed_io *fixed_io; > + > + fixed_io = &resource->data.fixed_io; > + if (p_ipmi->resource_count) { > + /* > + * Multiply definitions of IO/memory address are > + * obtained. It is incorrect. We will continue > + * to use the first IO/memory definition. > + * If not correct, please fix me. > + */ > + return AE_OK; > + } > + if (resource->type == ACPI_RESOURCE_TYPE_IO) { > + io = &resource->data.io; > + if (!io->minimum) { > + /* when IO address is zero, return */ > + return AE_OK; > + } > + address = io->minimum; > + } else { > + fixed_io = &resource->data.fixed_io; > + if (!fixed_io->address) > + return AE_OK; > + address = fixed_io->address; > + } > + p_ipmi->resource_count++; > + p_ipmi->addr.space_id = ACPI_ADR_SPACE_SYSTEM_IO; > + p_ipmi->addr.address = address; > + return AE_OK; > + } > + > + if (resource->type == ACPI_RESOURCE_TYPE_MEMORY32 || > + resource->type == ACPI_RESOURCE_TYPE_MEMORY24 || > + resource->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { > + u32 address; > + if (p_ipmi->resource_count) { > + /* > + * Multiply definitions of IO/memory address are > + * obtained. It is incorrect. We will continue > + * to use the first IO/memory definition. > + * If not correct, please fix me. > + */ > + return AE_OK; > + } > + if (resource->type == ACPI_RESOURCE_TYPE_MEMORY32) { > + struct acpi_resource_memory32 *memory32; > + memory32 = &resource->data.memory32; > + address = memory32->minimum; > + } else if (resource->type == ACPI_RESOURCE_TYPE_MEMORY24) { > + struct acpi_resource_memory24 *memory24; > + memory24 = &resource->data.memory24; > + address = memory24->minimum; > + } else { > + struct acpi_resource_fixed_memory32 *fixed_memory32; > + fixed_memory32 = &resource->data.fixed_memory32; > + address = fixed_memory32->address; > + } > + p_ipmi->resource_count++; > + p_ipmi->addr.address = (u64) address; > + p_ipmi->addr.space_id = ACPI_ADR_SPACE_SYSTEM_MEMORY; > + return AE_OK; > + } > + if (resource->type == ACPI_RESOURCE_TYPE_ADDRESS16 || > + resource->type == ACPI_RESOURCE_TYPE_ADDRESS32 || > + resource->type == ACPI_RESOURCE_TYPE_ADDRESS64) { > + struct acpi_resource_address64 address64; > + acpi_resource_to_address64(resource, &address64); > + if (p_ipmi->resource_count) { > + /* > + * Multiply definitions of IO/memory address are > + * obtained. It is incorrect. We will continue > + * to use the first IO/memory definition. > + * If not correct, please fix me. > + */ > + return AE_OK; > + } > + if (address64.resource_type != ACPI_MEMORY_RANGE && > + address64.resource_type != ACPI_IO_RANGE) { > + /* ignore the incorrect resource type */ > + return AE_OK; > + } > + p_ipmi->addr.address = address64.minimum; > + p_ipmi->resource_count++; > + if (address64.resource_type == ACPI_MEMORY_RANGE) > + p_ipmi->addr.space_id = ACPI_ADR_SPACE_SYSTEM_MEMORY; > + else > + p_ipmi->addr.space_id = ACPI_ADR_SPACE_SYSTEM_IO; > + > + return AE_OK; > + } > + > + return AE_OK; > +} > + > +/* > + * acpi_parse_bmc_resource -- parse the BMC resources from ACPI > + * @p_ipmi: the memory to store the BCM resource > + * @handle: ACPI device handle > + */ > +static int acpi_parse_bmc_resource(struct acpi_device_ipmi *p_ipmi, > + acpi_handle handle) > +{ > + int parse_ok = false; > + unsigned long long temp_data; > + acpi_status status; > + > + /* According to IPMI spec there should exist the _IFT method > + * for the IPMI device. So when there is no _IFT, it is regarded > + * as the incorrect BMC device and won't parse the resource again. > + */ > + status = acpi_evaluate_integer(handle, "_IFT", NULL, &temp_data); > + if (ACPI_FAILURE(status)) > + return parse_ok; > + > + p_ipmi->interfacetype = temp_data; > + /* Figure out the interface type. If the interface type is not > + * KCS/SMIC/BT, it is regared as the incorrect IPMI device. > + * Of course the SSIF interface type is also defined, but we > + * can't handle it. So it is not supported */ > + switch (temp_data) { > + case 1: /* KCS */ > + case 2: /* SMIC */ > + case 3: /* BT */ > + break; > + default: > + printk(KERN_INFO "ipmi_si: Unknown ACPI/SPMI SI type %d\n", > + p_ipmi->interfacetype); > + return parse_ok; > + } > + /* check whether there exists the _GPE method. If it exists, it > + * means that interrupt through GPE is supported. > + */ > + temp_data = 0; > + status = acpi_evaluate_integer(handle, "_GPE", NULL, &temp_data); > + if (ACPI_SUCCESS(status)) { > + p_ipmi->gpe = temp_data; > + /* set the GPE interrupt type */ > + p_ipmi->interrupttype |= 0x01; > + } > + /* get the IPMI revision */ > + temp_data = 0; > + status = acpi_evaluate_integer(handle, "_SRV", NULL, &temp_data); > + if (ACPI_SUCCESS(status)) > + p_ipmi->ipmi_revision = temp_data; > + > + status = acpi_walk_resources(handle, METHOD_NAME__CRS, > + acpi_parse_io_ports, p_ipmi); > + if (ACPI_FAILURE(status)) { > + printk(KERN_WARNING "Can't parse the _CRS object \n"); > + return parse_ok; > + } > + if (!p_ipmi->resource_count) { > + /* The incorrect IO/Memory address is parsed */ > + printk(KERN_ERR "Incorrect IO/Memory address is parsed\n"); > + return parse_ok; > + } > + parse_ok = true; > + > + return parse_ok; > +} > + > +const struct acpi_device_id ipmi_ids[] = { > + {"IPI0001", 0}, > + {"", 0}, > +}; > + > +/* > + * acpi_check_bmc_device -- check whether @handle is a BMC device and then > + * get its corresponding resource. For example: IO/Mem > + * address, interface type > + * @handle: ACPI device handle > + * @level : depth in the ACPI namespace tree > + * @context: the number of bmc device. In theory there is not more than > + * one ACPI BMC device. > + * @rv: a return value to fill if desired (Not use) > + */ > +static acpi_status > +acpi_check_bmc_device(acpi_handle handle, u32 level, void *context, > + void **return_value) > +{ > + struct acpi_device *acpi_dev; > + struct acpi_device_ipmi *p_ipmi = NULL; > + int *count = (int *)context; > + > + acpi_dev = NULL; > + /* Get the acpi device for device handle */ > + if (acpi_bus_get_device(handle, &acpi_dev) || !acpi_dev) { > + /* If there is no ACPI device for handle, return */ > + return AE_OK; > + } > + > + if (acpi_match_device_ids(acpi_dev, ipmi_ids)) > + return AE_OK; > + > + p_ipmi = kzalloc(sizeof(*p_ipmi), GFP_KERNEL); > + if (!p_ipmi) { > + printk(KERN_ERR "Can't allocate memory for IPMI device\n"); > + return AE_OK; > + } > + p_ipmi->dev = &acpi_dev->dev; > + if (!acpi_parse_bmc_resource(p_ipmi, handle)) { > + kfree(p_ipmi); > + } else { > + list_add_tail(&p_ipmi->link, &acpi_ipmi); > + *count = *count + 1; > + } > + > + return AE_OK; > +} > + > +static __devinit void acpi_device_find_bmc(void) > +{ > + acpi_status status; > + int device_count = 0; > + struct acpi_device_ipmi *p_ipmi, *p_ipmi2; > + > + if (acpi_disabled) > + return; > + > + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, > + ACPI_UINT32_MAX, > + acpi_check_bmc_device, &device_count, NULL); > + if (!device_count) { > + /* when no IPMI device is found in ACPI namespace, return */ > + return; > + } > + list_for_each_entry_safe(p_ipmi, p_ipmi2, &acpi_ipmi, link) { > + try_init_acpi_device(p_ipmi); > + list_del(&p_ipmi->link); > + kfree(p_ipmi); > + } > + > + return; > +} > + > static __devinit void acpi_find_bmc(void) > { > acpi_status status; > @@ -2014,14 +2404,23 @@ static __devinit void acpi_find_bmc(void) > if (acpi_failure) > return; > > + /* locate the IPMI system interface in ACPI SPMI table */ > for (i = 0; ; i++) { > status = acpi_get_table(ACPI_SIG_SPMI, i+1, > (struct acpi_table_header **)&spmi); > + /* > + * When no SPMI table is found, we will exit the loop and > + * continue to try another ACPI mechanism to detect the IPMI > + * device. > + */ > if (status != AE_OK) > - return; > + break; > > try_init_acpi(spmi); > } > + > + /* locate the IPMI system interface in ACPI device */ > + acpi_device_find_bmc(); > } > #endif > -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html