📄 ipmi_si_intf.c
字号:
static void mem_outb(struct si_sm_io *io, unsigned int offset, unsigned char b){ writeb(b, (io->addr)+(offset * io->regspacing));}static unsigned char mem_inw(struct si_sm_io *io, unsigned int offset){ return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift) && 0xff;}static void mem_outw(struct si_sm_io *io, unsigned int offset, unsigned char b){ writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));}static unsigned char mem_inl(struct si_sm_io *io, unsigned int offset){ return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift) && 0xff;}static void mem_outl(struct si_sm_io *io, unsigned int offset, unsigned char b){ writel(b << io->regshift, (io->addr)+(offset * io->regspacing));}#ifdef readqstatic unsigned char mem_inq(struct si_sm_io *io, unsigned int offset){ return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift) && 0xff;}static void mem_outq(struct si_sm_io *io, unsigned int offset, unsigned char b){ writeq(b << io->regshift, (io->addr)+(offset * io->regspacing));}#endifstatic void mem_cleanup(struct smi_info *info){ unsigned long *addr = info->io.info; int mapsize; if (info->io.addr) { iounmap(info->io.addr); mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); release_mem_region(*addr, mapsize); } kfree(info);}static int mem_setup(struct smi_info *info){ unsigned long *addr = info->io.info; int mapsize; if (!addr || (!*addr)) return -ENODEV; info->io_cleanup = mem_cleanup; /* Figure out the actual readb/readw/readl/etc routine to use based upon the register size. */ switch (info->io.regsize) { case 1: info->io.inputb = mem_inb; info->io.outputb = mem_outb; break; case 2: info->io.inputb = mem_inw; info->io.outputb = mem_outw; break; case 4: info->io.inputb = mem_inl; info->io.outputb = mem_outl; break;#ifdef readq case 8: info->io.inputb = mem_inq; info->io.outputb = mem_outq; break;#endif default: printk("ipmi_si: Invalid register size: %d\n", info->io.regsize); return -EINVAL; } /* Calculate the total amount of memory to claim. This is an * unusual looking calculation, but it avoids claiming any * more memory than it has to. It will claim everything * between the first address to the end of the last full * register. */ mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); if (request_mem_region(*addr, mapsize, DEVICE_NAME) == NULL) return -EIO; info->io.addr = ioremap(*addr, mapsize); if (info->io.addr == NULL) { release_mem_region(*addr, mapsize); return -EIO; } return 0;}static int try_init_mem(int intf_num, struct smi_info **new_info){ struct smi_info *info; if (!addrs[intf_num]) return -ENODEV; if (!is_new_interface(intf_num, IPMI_MEM_ADDR_SPACE, addrs[intf_num])) return -ENODEV; info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) { printk(KERN_ERR "ipmi_si: Could not allocate SI data (2)\n"); return -ENOMEM; } memset(info, 0, sizeof(*info)); info->io_setup = mem_setup; info->io.info = &addrs[intf_num]; info->io.addr = NULL; info->io.regspacing = regspacings[intf_num]; if (!info->io.regspacing) info->io.regspacing = DEFAULT_REGSPACING; info->io.regsize = regsizes[intf_num]; if (!info->io.regsize) info->io.regsize = DEFAULT_REGSPACING; info->io.regshift = regshifts[intf_num]; info->irq = 0; info->irq_setup = NULL; *new_info = info; if (si_type[intf_num] == NULL) si_type[intf_num] = "kcs"; printk("ipmi_si: Trying \"%s\" at memory address 0x%lx\n", si_type[intf_num], addrs[intf_num]); return 0;}#ifdef CONFIG_ACPI_INTERPRETER#include <linux/acpi.h>/* 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 (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_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; /* 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; } regspacings[intf_num] = spmi->addr.register_bit_width / 8; info->io.regspacing = spmi->addr.register_bit_width / 8; 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;}dmi_ipmi_data_t;typedef struct dmi_header{ u8 type; u8 length; u16 handle;}dmi_header_t;static int decode_dmi(dmi_header_t *dm, dmi_ipmi_data_t *ipmi_data){ u8 *data = (u8 *)dm; unsigned long base_addr; u8 reg_spacing; u8 len = dm->length; 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. */ ipmi_data->base_addr = base_addr; ipmi_data->addr_space = IPMI_IO_ADDR_SPACE; ipmi_data->offset = 1; } if (is_new_interface(-1, ipmi_data->addr_space,ipmi_data->base_addr)) return 0; memset(ipmi_data, 0, sizeof(dmi_ipmi_data_t)); return -1;}static int dmi_table(u32 base, int len, int num, dmi_ipmi_data_t *ipmi_data){ u8 *buf; struct dmi_header *dm; u8 *data; int i=1; int status=-1; buf = ioremap(base, len); if(buf==NULL) return -1; data = buf; while(i<num && (data - buf) < len) { dm=(dmi_header_t *)data; if((data-buf+dm->length) >= len) break; if (dm->type == 38) { if (decode_dmi(dm, ipmi_data) == 0) { status = 0; break; } } data+=dm->length; while((data-buf) < len && (*data || data[1])) data++; data+=2; i++; } iounmap(buf); return status;}inline static int dmi_checksum(u8 *buf){ u8 sum=0; int a; for(a=0; a<15; a++) sum+=buf[a]; return (sum==0);}static int dmi_iterator(dmi_ipmi_data_t *ipmi_data){ u8 buf[15]; u32 fp=0xF0000;#ifdef CONFIG_SIMNOW return -1;#endif while(fp < 0xFFFFF) { isa_memcpy_fromio(buf, fp, 15); if(memcmp(buf, "_DMI_", 5)==0 && dmi_checksum(buf)) { u16 num=buf[13]<<8|buf[12]; u16 len=buf[7]<<8|buf[6]; u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]; if(dmi_table(base, len, num, ipmi_data) == 0) return 0; } fp+=16; } return -1;}static int try_init_smbios(int intf_num, struct smi_info **new_info){ struct smi_info *info; dmi_ipmi_data_t ipmi_data; char *io_type; int status; status = dmi_iterator(&ipmi_data); if (status < 0) 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: printk("ipmi_si: Unknown SMBIOS SI type.\n"); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -