diff --git a/Documentation/IPMI.txt b/Documentation/IPMI.txt index aa77a25a09400d91bbc45cf5f20152965c5b309a..5ef1047e2e6635b4cf060fa2f927820d917f5dc4 100644 --- a/Documentation/IPMI.txt +++ b/Documentation/IPMI.txt @@ -81,7 +81,9 @@ If you want the driver to put an event into the event log on a panic, enable the 'Generate a panic event to all BMCs on a panic' option. If you want the whole panic string put into the event log using OEM events, enable the 'Generate OEM events containing the panic string' -option. +option. You can also enable these dynamically by setting the module +parameter named "panic_op" in the ipmi_msghandler module to "event" +or "string". Setting that parameter to "none" disables this function. Basic Design ------------ diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index f6fa056a52fcfac0f846e5c6d2efb0df26452502..3544abc0f9f90d171511062b6bee077dc042109b 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -22,24 +22,39 @@ config IPMI_DMI_DECODE if IPMI_HANDLER +config IPMI_PROC_INTERFACE + bool 'Provide an interface for IPMI stats in /proc (deprecated)' + depends on PROC_FS + default y + help + Do not use this any more, use sysfs for this info. It will be + removed in future kernel versions. + config IPMI_PANIC_EVENT bool 'Generate a panic event to all BMCs on a panic' help - When a panic occurs, this will cause the IPMI message handler to - generate an IPMI event describing the panic to each interface - registered with the message handler. + When a panic occurs, this will cause the IPMI message handler to, + by default, generate an IPMI event describing the panic to each + interface registered with the message handler. This is always + available, the module parameter for ipmi_msghandler named + panic_op can be set to "event" to chose this value, this config + simply causes the default value to be set to "event". config IPMI_PANIC_STRING bool 'Generate OEM events containing the panic string' depends on IPMI_PANIC_EVENT help - When a panic occurs, this will cause the IPMI message handler to - generate IPMI OEM type f0 events holding the IPMB address of the - panic generator (byte 4 of the event), a sequence number for the - string (byte 5 of the event) and part of the string (the rest of the - event). Bytes 1, 2, and 3 are the normal usage for an OEM event. - You can fetch these events and use the sequence numbers to piece the - string together. + When a panic occurs, this will cause the IPMI message handler to, + by default, generate IPMI OEM type f0 events holding the IPMB + address of the panic generator (byte 4 of the event), a sequence + number for the string (byte 5 of the event) and part of the + string (the rest of the event). Bytes 1, 2, and 3 are the normal + usage for an OEM event. You can fetch these events and use the + sequence numbers to piece the string together. This config + parameter sets the default value to generate these events, + the module parameter for ipmi_msghandler named panic_op can + be set to "string" to chose this value, this config simply + causes the default value to be set to "string". config IPMI_DEVICE_INTERFACE tristate 'Device interface for IPMI' diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 43b7d86cc5f29f967e577a4f0d78513c0e592ac6..33b899fcf14a3bdd37de7e5cbe369e1e191fdeb6 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -3,7 +3,15 @@ # Makefile for the ipmi drivers. # -ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o +ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o \ + ipmi_si_hotmod.o ipmi_si_hardcode.o ipmi_si_platform.o \ + ipmi_si_port_io.o ipmi_si_mem_io.o +ifdef CONFIG_PCI +ipmi_si-y += ipmi_si_pci.o +endif +ifdef CONFIG_PARISC +ipmi_si-y += ipmi_si_parisc.o +endif obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c index 2059f79d669a07f2f734ad88a907538a6085cfe8..ab78b3be7e33590b46befdfee90a905f2eef3402 100644 --- a/drivers/char/ipmi/ipmi_dmi.c +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -9,10 +9,16 @@ #include #include #include +#include "ipmi_si_sm.h" #include "ipmi_dmi.h" +#define IPMI_DMI_TYPE_KCS 0x01 +#define IPMI_DMI_TYPE_SMIC 0x02 +#define IPMI_DMI_TYPE_BT 0x03 +#define IPMI_DMI_TYPE_SSIF 0x04 + struct ipmi_dmi_info { - int type; + enum si_type si_type; u32 flags; unsigned long addr; u8 slave_addr; @@ -23,6 +29,15 @@ static struct ipmi_dmi_info *ipmi_dmi_infos; static int ipmi_dmi_nr __initdata; +#define set_prop_entry(_p_, _name_, type, val) \ +do { \ + struct property_entry *_p = &_p_; \ + _p->name = _name_; \ + _p->length = sizeof(type); \ + _p->is_string = false; \ + _p->value.type##_data = val; \ +} while(0) + static void __init dmi_add_platform_ipmi(unsigned long base_addr, u32 flags, u8 slave_addr, @@ -33,27 +48,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, struct platform_device *pdev; struct resource r[4]; unsigned int num_r = 1, size; - struct property_entry p[4] = { - PROPERTY_ENTRY_U8("slave-addr", slave_addr), - PROPERTY_ENTRY_U8("ipmi-type", type), - PROPERTY_ENTRY_U16("i2c-addr", base_addr), - { } - }; + struct property_entry p[5]; + unsigned int pidx = 0; char *name, *override; int rv; + enum si_type si_type; struct ipmi_dmi_info *info; - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - pr_warn("ipmi:dmi: Could not allocate dmi info\n"); - } else { - info->type = type; - info->flags = flags; - info->addr = base_addr; - info->slave_addr = slave_addr; - info->next = ipmi_dmi_infos; - ipmi_dmi_infos = info; - } + memset(p, 0, sizeof(p)); name = "dmi-ipmi-si"; override = "ipmi_si"; @@ -63,28 +65,53 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, override = "ipmi_ssif"; offset = 1; size = 1; + si_type = SI_TYPE_INVALID; break; case IPMI_DMI_TYPE_BT: size = 3; + si_type = SI_BT; break; case IPMI_DMI_TYPE_KCS: + size = 2; + si_type = SI_KCS; + break; case IPMI_DMI_TYPE_SMIC: size = 2; + si_type = SI_SMIC; break; default: - pr_err("ipmi:dmi: Invalid IPMI type: %d", type); + pr_err("ipmi:dmi: Invalid IPMI type: %d\n", type); return; } + if (si_type != SI_TYPE_INVALID) + set_prop_entry(p[pidx++], "ipmi-type", u8, si_type); + set_prop_entry(p[pidx++], "slave-addr", u8, slave_addr); + set_prop_entry(p[pidx++], "addr-source", u8, SI_SMBIOS); + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + pr_warn("ipmi:dmi: Could not allocate dmi info\n"); + } else { + info->si_type = si_type; + info->flags = flags; + info->addr = base_addr; + info->slave_addr = slave_addr; + info->next = ipmi_dmi_infos; + ipmi_dmi_infos = info; + } + pdev = platform_device_alloc(name, ipmi_dmi_nr); if (!pdev) { - pr_err("ipmi:dmi: Error allocation IPMI platform device"); + pr_err("ipmi:dmi: Error allocation IPMI platform device\n"); return; } pdev->driver_override = override; - if (type == IPMI_DMI_TYPE_SSIF) + if (type == IPMI_DMI_TYPE_SSIF) { + set_prop_entry(p[pidx++], "i2c-addr", u16, base_addr); goto add_properties; + } memset(r, 0, sizeof(r)); @@ -152,12 +179,13 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, * This function allows an ACPI-specified IPMI device to look up the * slave address from the DMI table. */ -int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr) +int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, + unsigned long base_addr) { struct ipmi_dmi_info *info = ipmi_dmi_infos; while (info) { - if (info->type == type && + if (info->si_type == si_type && info->flags == flags && info->addr == base_addr) return info->slave_addr; @@ -240,7 +268,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm) offset = 16; break; default: - pr_err("ipmi:dmi: Invalid offset: 0"); + pr_err("ipmi:dmi: Invalid offset: 0\n"); return; } } diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h index ea990a8e3b09b698847681dd68d1a1155caa64ff..6c21018e366860dab8f2390a95075b1e218f57ce 100644 --- a/drivers/char/ipmi/ipmi_dmi.h +++ b/drivers/char/ipmi/ipmi_dmi.h @@ -3,11 +3,7 @@ * DMI defines for use by IPMI */ -#define IPMI_DMI_TYPE_KCS 0x01 -#define IPMI_DMI_TYPE_SMIC 0x02 -#define IPMI_DMI_TYPE_BT 0x03 -#define IPMI_DMI_TYPE_SSIF 0x04 - #ifdef CONFIG_IPMI_DMI_DECODE -int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr); +int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, + unsigned long base_addr); #endif diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 810b138f5897b85374b0cc19e7b175b86757d232..9de189db2cc3c6486884decb5073d4c1e3830707 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -46,6 +46,9 @@ #include #include #include +#include +#include +#include #define PFX "IPMI message handler: " @@ -61,9 +64,77 @@ static int handle_one_recv_msg(ipmi_smi_t intf, static int initialized; -#ifdef CONFIG_PROC_FS +enum ipmi_panic_event_op { + IPMI_SEND_PANIC_EVENT_NONE, + IPMI_SEND_PANIC_EVENT, + IPMI_SEND_PANIC_EVENT_STRING +}; +#ifdef CONFIG_IPMI_PANIC_STRING +#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_STRING +#elif defined(CONFIG_IPMI_PANIC_EVENT) +#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT +#else +#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_NONE +#endif +static enum ipmi_panic_event_op ipmi_send_panic_event = IPMI_PANIC_DEFAULT; + +static int panic_op_write_handler(const char *val, + const struct kernel_param *kp) +{ + char valcp[16]; + char *s; + + strncpy(valcp, val, 16); + valcp[15] = '\0'; + + s = strstrip(valcp); + + if (strcmp(s, "none") == 0) + ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_NONE; + else if (strcmp(s, "event") == 0) + ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT; + else if (strcmp(s, "string") == 0) + ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_STRING; + else + return -EINVAL; + + return 0; +} + +static int panic_op_read_handler(char *buffer, const struct kernel_param *kp) +{ + switch (ipmi_send_panic_event) { + case IPMI_SEND_PANIC_EVENT_NONE: + strcpy(buffer, "none"); + break; + + case IPMI_SEND_PANIC_EVENT: + strcpy(buffer, "event"); + break; + + case IPMI_SEND_PANIC_EVENT_STRING: + strcpy(buffer, "string"); + break; + + default: + strcpy(buffer, "???"); + break; + } + + return strlen(buffer); +} + +static const struct kernel_param_ops panic_op_ops = { + .set = panic_op_write_handler, + .get = panic_op_read_handler +}; +module_param_cb(panic_op, &panic_op_ops, NULL, 0600); +MODULE_PARM_DESC(panic_op, "Sets if the IPMI driver will attempt to store panic information in the event log in the event of a panic. Set to 'none' for no, 'event' for a single event, or 'string' for a generic event and the panic string in IPMI OEM events."); + + +#ifdef CONFIG_IPMI_PROC_INTERFACE static struct proc_dir_entry *proc_ipmi_root; -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_IPMI_PROC_INTERFACE */ /* Remain in auto-maintenance mode for this amount of time (in ms). */ #define IPMI_MAINTENANCE_MODE_TIMEOUT 30000 @@ -90,6 +161,9 @@ static struct proc_dir_entry *proc_ipmi_root; */ #define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) +/* How long should we cache dynamic device IDs? */ +#define IPMI_DYN_DEV_ID_EXPIRY (10 * HZ) + /* * The main "user" data structure. */ @@ -169,10 +243,17 @@ struct seq_table { #define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff) +#define IPMI_MAX_CHANNELS 16 struct ipmi_channel { unsigned char medium; unsigned char protocol; +}; +struct ipmi_channel_set { + struct ipmi_channel c[IPMI_MAX_CHANNELS]; +}; + +struct ipmi_my_addrinfo { /* * My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, * but may be changed by the user. @@ -186,23 +267,38 @@ struct ipmi_channel { unsigned char lun; }; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE struct ipmi_proc_entry { char *name; struct ipmi_proc_entry *next; }; #endif +/* + * Note that the product id, manufacturer id, guid, and device id are + * immutable in this structure, so dyn_mutex is not required for + * accessing those. If those change on a BMC, a new BMC is allocated. + */ struct bmc_device { struct platform_device pdev; + struct list_head intfs; /* Interfaces on this BMC. */ struct ipmi_device_id id; - unsigned char guid[16]; - int guid_set; - char name[16]; + struct ipmi_device_id fetch_id; + int dyn_id_set; + unsigned long dyn_id_expiry; + struct mutex dyn_mutex; /* Protects id, intfs, & dyn* */ + guid_t guid; + guid_t fetch_guid; + int dyn_guid_set; struct kref usecount; + struct work_struct remove_work; }; #define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev) +static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, + struct ipmi_device_id *id, + bool *guid_set, guid_t *guid); + /* * Various statistics for IPMI, these index stats[] in the ipmi_smi * structure. @@ -308,7 +404,6 @@ enum ipmi_stat_indexes { #define IPMI_IPMB_NUM_SEQ 64 -#define IPMI_MAX_CHANNELS 16 struct ipmi_smi { /* What interface number are we? */ int intf_num; @@ -327,15 +422,23 @@ struct ipmi_smi { */ struct list_head users; - /* Information to supply to users. */ - unsigned char ipmi_version_major; - unsigned char ipmi_version_minor; - /* Used for wake ups at startup. */ wait_queue_head_t waitq; + /* + * Prevents the interface from being unregistered when the + * interface is used by being looked up through the BMC + * structure. + */ + struct mutex bmc_reg_mutex; + + struct bmc_device tmp_bmc; struct bmc_device *bmc; + bool bmc_registered; + struct list_head bmc_link; char *my_dev_name; + bool in_bmc_register; /* Handle recursive situations. Yuck. */ + struct work_struct bmc_reg_work; /* * This is the lower-layer's sender routine. Note that you @@ -346,10 +449,13 @@ struct ipmi_smi { const struct ipmi_smi_handlers *handlers; void *send_info; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE /* A list of proc entries for this interface. */ struct mutex proc_entry_lock; struct ipmi_proc_entry *proc_entries; + + struct proc_dir_entry *proc_dir; + char proc_dir_name[10]; #endif /* Driver-model device for the system interface. */ @@ -421,6 +527,8 @@ struct ipmi_smi { * interface comes in with a NULL user, call this routine with * it. Note that the message will still be freed by the * caller. This only works on the system interface. + * + * Protected by bmc_reg_mutex. */ void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg); @@ -431,11 +539,11 @@ struct ipmi_smi { int curr_channel; /* Channel information */ - struct ipmi_channel channels[IPMI_MAX_CHANNELS]; - - /* Proc FS stuff. */ - struct proc_dir_entry *proc_dir; - char proc_dir_name[10]; + struct ipmi_channel_set *channel_list; + unsigned int curr_working_cset; /* First index into the following. */ + struct ipmi_channel_set wchannels[2]; + struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS]; + bool channels_ready; atomic_t stats[IPMI_NUM_STATS]; @@ -448,6 +556,14 @@ struct ipmi_smi { }; #define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) +static void __get_guid(ipmi_smi_t intf); +static void __ipmi_bmc_unregister(ipmi_smi_t intf); +static int __ipmi_bmc_register(ipmi_smi_t intf, + struct ipmi_device_id *id, + bool guid_set, guid_t *guid, int intf_num); +static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id); + + /** * The driver model view of the IPMI messaging driver. */ @@ -457,6 +573,9 @@ static struct platform_driver ipmidriver = { .bus = &platform_bus_type } }; +/* + * This mutex keeps us from adding the same BMC twice. + */ static DEFINE_MUTEX(ipmidriver_mutex); static LIST_HEAD(ipmi_interfaces); @@ -475,7 +594,7 @@ static DEFINE_MUTEX(smi_watchers_mutex); static const char * const addr_src_to_str[] = { "invalid", "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI", - "device-tree" + "device-tree", "platform" }; const char *ipmi_addr_src_to_str(enum ipmi_addr_src src) @@ -1119,12 +1238,21 @@ int ipmi_destroy_user(ipmi_user_t user) } EXPORT_SYMBOL(ipmi_destroy_user); -void ipmi_get_version(ipmi_user_t user, - unsigned char *major, - unsigned char *minor) +int ipmi_get_version(ipmi_user_t user, + unsigned char *major, + unsigned char *minor) { - *major = user->intf->ipmi_version_major; - *minor = user->intf->ipmi_version_minor; + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL); + if (rv) + return rv; + + *major = ipmi_version_major(&id); + *minor = ipmi_version_minor(&id); + + return 0; } EXPORT_SYMBOL(ipmi_get_version); @@ -1134,7 +1262,7 @@ int ipmi_set_my_address(ipmi_user_t user, { if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; - user->intf->channels[channel].address = address; + user->intf->addrinfo[channel].address = address; return 0; } EXPORT_SYMBOL(ipmi_set_my_address); @@ -1145,7 +1273,7 @@ int ipmi_get_my_address(ipmi_user_t user, { if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; - *address = user->intf->channels[channel].address; + *address = user->intf->addrinfo[channel].address; return 0; } EXPORT_SYMBOL(ipmi_get_my_address); @@ -1156,7 +1284,7 @@ int ipmi_set_my_LUN(ipmi_user_t user, { if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; - user->intf->channels[channel].lun = LUN & 0x3; + user->intf->addrinfo[channel].lun = LUN & 0x3; return 0; } EXPORT_SYMBOL(ipmi_set_my_LUN); @@ -1167,7 +1295,7 @@ int ipmi_get_my_LUN(ipmi_user_t user, { if (channel >= IPMI_MAX_CHANNELS) return -EINVAL; - *address = user->intf->channels[channel].lun; + *address = user->intf->addrinfo[channel].lun; return 0; } EXPORT_SYMBOL(ipmi_get_my_LUN); @@ -1264,8 +1392,8 @@ int ipmi_set_gets_events(ipmi_user_t user, bool val) list_move_tail(&msg->link, &msgs); intf->waiting_events_count = 0; if (intf->event_msg_printed) { - printk(KERN_WARNING PFX "Event queue no longer" - " full\n"); + dev_warn(intf->si_dev, + PFX "Event queue no longer full\n"); intf->event_msg_printed = 0; } @@ -1655,6 +1783,7 @@ static int i_ipmi_request(ipmi_user_t user, unsigned char ipmb_seq; long seqid; int broadcast = 0; + struct ipmi_channel *chans; if (addr->channel >= IPMI_MAX_CHANNELS) { ipmi_inc_stat(intf, sent_invalid_commands); @@ -1662,8 +1791,9 @@ static int i_ipmi_request(ipmi_user_t user, goto out_err; } - if (intf->channels[addr->channel].medium - != IPMI_CHANNEL_MEDIUM_IPMB) { + chans = READ_ONCE(intf->channel_list)->c; + + if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; @@ -1785,6 +1915,7 @@ static int i_ipmi_request(ipmi_user_t user, struct ipmi_lan_addr *lan_addr; unsigned char ipmb_seq; long seqid; + struct ipmi_channel *chans; if (addr->channel >= IPMI_MAX_CHANNELS) { ipmi_inc_stat(intf, sent_invalid_commands); @@ -1792,9 +1923,11 @@ static int i_ipmi_request(ipmi_user_t user, goto out_err; } - if ((intf->channels[addr->channel].medium + chans = READ_ONCE(intf->channel_list)->c; + + if ((chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_8023LAN) - && (intf->channels[addr->channel].medium + && (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_ASYNC)) { ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; @@ -1928,8 +2061,8 @@ static int check_addr(ipmi_smi_t intf, { if (addr->channel >= IPMI_MAX_CHANNELS) return -EINVAL; - *lun = intf->channels[addr->channel].lun; - *saddr = intf->channels[addr->channel].address; + *lun = intf->addrinfo[addr->channel].lun; + *saddr = intf->addrinfo[addr->channel].address; return 0; } @@ -1997,15 +2130,249 @@ int ipmi_request_supply_msgs(ipmi_user_t user, } EXPORT_SYMBOL(ipmi_request_supply_msgs); -#ifdef CONFIG_PROC_FS +static void bmc_device_id_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +{ + int rv; + + if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) + || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) + || (msg->msg.cmd != IPMI_GET_DEVICE_ID_CMD)) { + dev_warn(intf->si_dev, + PFX "invalid device_id msg: addr_type=%d netfn=%x cmd=%x\n", + msg->addr.addr_type, msg->msg.netfn, msg->msg.cmd); + return; + } + + rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd, + msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id); + if (rv) { + dev_warn(intf->si_dev, + PFX "device id demangle failed: %d\n", rv); + intf->bmc->dyn_id_set = 0; + } else { + /* + * Make sure the id data is available before setting + * dyn_id_set. + */ + smp_wmb(); + intf->bmc->dyn_id_set = 1; + } + + wake_up(&intf->waitq); +} + +static int +send_get_device_id_cmd(ipmi_smi_t intf) +{ + struct ipmi_system_interface_addr si; + struct kernel_ipmi_msg msg; + + si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + si.channel = IPMI_BMC_CHANNEL; + si.lun = 0; + + msg.netfn = IPMI_NETFN_APP_REQUEST; + msg.cmd = IPMI_GET_DEVICE_ID_CMD; + msg.data = NULL; + msg.data_len = 0; + + return i_ipmi_request(NULL, + intf, + (struct ipmi_addr *) &si, + 0, + &msg, + intf, + NULL, + NULL, + 0, + intf->addrinfo[0].address, + intf->addrinfo[0].lun, + -1, 0); +} + +static int __get_device_id(ipmi_smi_t intf, struct bmc_device *bmc) +{ + int rv; + + bmc->dyn_id_set = 2; + + intf->null_user_handler = bmc_device_id_handler; + + rv = send_get_device_id_cmd(intf); + if (rv) + return rv; + + wait_event(intf->waitq, bmc->dyn_id_set != 2); + + if (!bmc->dyn_id_set) + rv = -EIO; /* Something went wrong in the fetch. */ + + /* dyn_id_set makes the id data available. */ + smp_rmb(); + + intf->null_user_handler = NULL; + + return rv; +} + +/* + * Fetch the device id for the bmc/interface. You must pass in either + * bmc or intf, this code will get the other one. If the data has + * been recently fetched, this will just use the cached data. Otherwise + * it will run a new fetch. + * + * Except for the first time this is called (in ipmi_register_smi()), + * this will always return good data; + */ +static int __bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, + struct ipmi_device_id *id, + bool *guid_set, guid_t *guid, int intf_num) +{ + int rv = 0; + int prev_dyn_id_set, prev_guid_set; + bool intf_set = intf != NULL; + + if (!intf) { + mutex_lock(&bmc->dyn_mutex); +retry_bmc_lock: + if (list_empty(&bmc->intfs)) { + mutex_unlock(&bmc->dyn_mutex); + return -ENOENT; + } + intf = list_first_entry(&bmc->intfs, struct ipmi_smi, + bmc_link); + kref_get(&intf->refcount); + mutex_unlock(&bmc->dyn_mutex); + mutex_lock(&intf->bmc_reg_mutex); + mutex_lock(&bmc->dyn_mutex); + if (intf != list_first_entry(&bmc->intfs, struct ipmi_smi, + bmc_link)) { + mutex_unlock(&intf->bmc_reg_mutex); + kref_put(&intf->refcount, intf_free); + goto retry_bmc_lock; + } + } else { + mutex_lock(&intf->bmc_reg_mutex); + bmc = intf->bmc; + mutex_lock(&bmc->dyn_mutex); + kref_get(&intf->refcount); + } + + /* If we have a valid and current ID, just return that. */ + if (intf->in_bmc_register || + (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry))) + goto out_noprocessing; + + prev_guid_set = bmc->dyn_guid_set; + __get_guid(intf); + + prev_dyn_id_set = bmc->dyn_id_set; + rv = __get_device_id(intf, bmc); + if (rv) + goto out; + + /* + * The guid, device id, manufacturer id, and product id should + * not change on a BMC. If it does we have to do some dancing. + */ + if (!intf->bmc_registered + || (!prev_guid_set && bmc->dyn_guid_set) + || (!prev_dyn_id_set && bmc->dyn_id_set) + || (prev_guid_set && bmc->dyn_guid_set + && !guid_equal(&bmc->guid, &bmc->fetch_guid)) + || bmc->id.device_id != bmc->fetch_id.device_id + || bmc->id.manufacturer_id != bmc->fetch_id.manufacturer_id + || bmc->id.product_id != bmc->fetch_id.product_id) { + struct ipmi_device_id id = bmc->fetch_id; + int guid_set = bmc->dyn_guid_set; + guid_t guid; + + guid = bmc->fetch_guid; + mutex_unlock(&bmc->dyn_mutex); + + __ipmi_bmc_unregister(intf); + /* Fill in the temporary BMC for good measure. */ + intf->bmc->id = id; + intf->bmc->dyn_guid_set = guid_set; + intf->bmc->guid = guid; + if (__ipmi_bmc_register(intf, &id, guid_set, &guid, intf_num)) + need_waiter(intf); /* Retry later on an error. */ + else + __scan_channels(intf, &id); + + + if (!intf_set) { + /* + * We weren't given the interface on the + * command line, so restart the operation on + * the next interface for the BMC. + */ + mutex_unlock(&intf->bmc_reg_mutex); + mutex_lock(&bmc->dyn_mutex); + goto retry_bmc_lock; + } + + /* We have a new BMC, set it up. */ + bmc = intf->bmc; + mutex_lock(&bmc->dyn_mutex); + goto out_noprocessing; + } else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id))) + /* Version info changes, scan the channels again. */ + __scan_channels(intf, &bmc->fetch_id); + + bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY; + +out: + if (rv && prev_dyn_id_set) { + rv = 0; /* Ignore failures if we have previous data. */ + bmc->dyn_id_set = prev_dyn_id_set; + } + if (!rv) { + bmc->id = bmc->fetch_id; + if (bmc->dyn_guid_set) + bmc->guid = bmc->fetch_guid; + else if (prev_guid_set) + /* + * The guid used to be valid and it failed to fetch, + * just use the cached value. + */ + bmc->dyn_guid_set = prev_guid_set; + } +out_noprocessing: + if (!rv) { + if (id) + *id = bmc->id; + + if (guid_set) + *guid_set = bmc->dyn_guid_set; + + if (guid && bmc->dyn_guid_set) + *guid = bmc->guid; + } + + mutex_unlock(&bmc->dyn_mutex); + mutex_unlock(&intf->bmc_reg_mutex); + + kref_put(&intf->refcount, intf_free); + return rv; +} + +static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, + struct ipmi_device_id *id, + bool *guid_set, guid_t *guid) +{ + return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1); +} + +#ifdef CONFIG_IPMI_PROC_INTERFACE static int smi_ipmb_proc_show(struct seq_file *m, void *v) { ipmi_smi_t intf = m->private; int i; - seq_printf(m, "%x", intf->channels[0].address); + seq_printf(m, "%x", intf->addrinfo[0].address); for (i = 1; i < IPMI_MAX_CHANNELS; i++) - seq_printf(m, " %x", intf->channels[i].address); + seq_printf(m, " %x", intf->addrinfo[i].address); seq_putc(m, '\n'); return 0; @@ -2026,10 +2393,16 @@ static const struct file_operations smi_ipmb_proc_ops = { static int smi_version_proc_show(struct seq_file *m, void *v) { ipmi_smi_t intf = m->private; + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(intf, NULL, &id, NULL, NULL); + if (rv) + return rv; seq_printf(m, "%u.%u\n", - ipmi_version_major(&intf->bmc->id), - ipmi_version_minor(&intf->bmc->id)); + ipmi_version_major(&id), + ipmi_version_minor(&id)); return 0; } @@ -2120,14 +2493,12 @@ static const struct file_operations smi_stats_proc_ops = { .llseek = seq_lseek, .release = single_release, }; -#endif /* CONFIG_PROC_FS */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, const struct file_operations *proc_ops, void *data) { int rv = 0; -#ifdef CONFIG_PROC_FS struct proc_dir_entry *file; struct ipmi_proc_entry *entry; @@ -2153,7 +2524,6 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, smi->proc_entries = entry; mutex_unlock(&smi->proc_entry_lock); } -#endif /* CONFIG_PROC_FS */ return rv; } @@ -2163,7 +2533,6 @@ static int add_proc_entries(ipmi_smi_t smi, int num) { int rv = 0; -#ifdef CONFIG_PROC_FS sprintf(smi->proc_dir_name, "%d", num); smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) @@ -2183,14 +2552,12 @@ static int add_proc_entries(ipmi_smi_t smi, int num) rv = ipmi_smi_add_proc_entry(smi, "version", &smi_version_proc_ops, smi); -#endif /* CONFIG_PROC_FS */ return rv; } static void remove_proc_entries(ipmi_smi_t smi) { -#ifdef CONFIG_PROC_FS struct ipmi_proc_entry *entry; mutex_lock(&smi->proc_entry_lock); @@ -2204,66 +2571,22 @@ static void remove_proc_entries(ipmi_smi_t smi) } mutex_unlock(&smi->proc_entry_lock); remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); -#endif /* CONFIG_PROC_FS */ -} - -static int __find_bmc_guid(struct device *dev, void *data) -{ - unsigned char *id = data; - struct bmc_device *bmc = to_bmc_device(dev); - return memcmp(bmc->guid, id, 16) == 0; -} - -static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv, - unsigned char *guid) -{ - struct device *dev; - - dev = driver_find_device(drv, NULL, guid, __find_bmc_guid); - if (dev) - return to_bmc_device(dev); - else - return NULL; -} - -struct prod_dev_id { - unsigned int product_id; - unsigned char device_id; -}; - -static int __find_bmc_prod_dev_id(struct device *dev, void *data) -{ - struct prod_dev_id *id = data; - struct bmc_device *bmc = to_bmc_device(dev); - - return (bmc->id.product_id == id->product_id - && bmc->id.device_id == id->device_id); -} - -static struct bmc_device *ipmi_find_bmc_prod_dev_id( - struct device_driver *drv, - unsigned int product_id, unsigned char device_id) -{ - struct prod_dev_id id = { - .product_id = product_id, - .device_id = device_id, - }; - struct device *dev; - - dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id); - if (dev) - return to_bmc_device(dev); - else - return NULL; } +#endif /* CONFIG_IPMI_PROC_INTERFACE */ static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; - return snprintf(buf, 10, "%u\n", bmc->id.device_id); + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; + + return snprintf(buf, 10, "%u\n", id.device_id); } static DEVICE_ATTR(device_id, S_IRUGO, device_id_show, NULL); @@ -2272,9 +2595,14 @@ static ssize_t provides_device_sdrs_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; - return snprintf(buf, 10, "%u\n", - (bmc->id.device_revision & 0x80) >> 7); + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; + + return snprintf(buf, 10, "%u\n", (id.device_revision & 0x80) >> 7); } static DEVICE_ATTR(provides_device_sdrs, S_IRUGO, provides_device_sdrs_show, NULL); @@ -2283,9 +2611,14 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; - return snprintf(buf, 20, "%u\n", - bmc->id.device_revision & 0x0F); + return snprintf(buf, 20, "%u\n", id.device_revision & 0x0F); } static DEVICE_ATTR(revision, S_IRUGO, revision_show, NULL); @@ -2294,9 +2627,15 @@ static ssize_t firmware_revision_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; - return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1, - bmc->id.firmware_revision_2); + return snprintf(buf, 20, "%u.%x\n", id.firmware_revision_1, + id.firmware_revision_2); } static DEVICE_ATTR(firmware_revision, S_IRUGO, firmware_revision_show, NULL); @@ -2305,10 +2644,16 @@ static ssize_t ipmi_version_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; return snprintf(buf, 20, "%u.%u\n", - ipmi_version_major(&bmc->id), - ipmi_version_minor(&bmc->id)); + ipmi_version_major(&id), + ipmi_version_minor(&id)); } static DEVICE_ATTR(ipmi_version, S_IRUGO, ipmi_version_show, NULL); @@ -2317,9 +2662,14 @@ static ssize_t add_dev_support_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; - return snprintf(buf, 10, "0x%02x\n", - bmc->id.additional_device_support); + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; + + return snprintf(buf, 10, "0x%02x\n", id.additional_device_support); } static DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show, NULL); @@ -2329,8 +2679,14 @@ static ssize_t manufacturer_id_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; - return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id); + return snprintf(buf, 20, "0x%6.6x\n", id.manufacturer_id); } static DEVICE_ATTR(manufacturer_id, S_IRUGO, manufacturer_id_show, NULL); @@ -2339,8 +2695,14 @@ static ssize_t product_id_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; - return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id); + return snprintf(buf, 10, "0x%4.4x\n", id.product_id); } static DEVICE_ATTR(product_id, S_IRUGO, product_id_show, NULL); @@ -2349,12 +2711,18 @@ static ssize_t aux_firmware_rev_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + if (rv) + return rv; return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n", - bmc->id.aux_firmware_revision[3], - bmc->id.aux_firmware_revision[2], - bmc->id.aux_firmware_revision[1], - bmc->id.aux_firmware_revision[0]); + id.aux_firmware_revision[3], + id.aux_firmware_revision[2], + id.aux_firmware_revision[1], + id.aux_firmware_revision[0]); } static DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL); @@ -2362,10 +2730,17 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + bool guid_set; + guid_t guid; + int rv; - return snprintf(buf, 100, "%Lx%Lx\n", - (long long) bmc->guid[0], - (long long) bmc->guid[8]); + rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, &guid); + if (rv) + return rv; + if (!guid_set) + return -ENOENT; + + return snprintf(buf, 38, "%pUl\n", guid.b); } static DEVICE_ATTR(guid, S_IRUGO, guid_show, NULL); @@ -2389,11 +2764,20 @@ static umode_t bmc_dev_attr_is_visible(struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct bmc_device *bmc = to_bmc_device(dev); umode_t mode = attr->mode; + int rv; - if (attr == &dev_attr_aux_firmware_revision.attr) - return bmc->id.aux_firmware_revision_set ? mode : 0; - if (attr == &dev_attr_guid.attr) - return bmc->guid_set ? mode : 0; + if (attr == &dev_attr_aux_firmware_revision.attr) { + struct ipmi_device_id id; + + rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL); + return (!rv && id.aux_firmware_revision_set) ? mode : 0; + } + if (attr == &dev_attr_guid.attr) { + bool guid_set; + + rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, NULL); + return (!rv && guid_set) ? mode : 0; + } return mode; } @@ -2411,127 +2795,239 @@ static const struct device_type bmc_device_type = { .groups = bmc_dev_attr_groups, }; +static int __find_bmc_guid(struct device *dev, void *data) +{ + guid_t *guid = data; + struct bmc_device *bmc; + int rv; + + if (dev->type != &bmc_device_type) + return 0; + + bmc = to_bmc_device(dev); + rv = bmc->dyn_guid_set && guid_equal(&bmc->guid, guid); + if (rv) + rv = kref_get_unless_zero(&bmc->usecount); + return rv; +} + +/* + * Returns with the bmc's usecount incremented, if it is non-NULL. + */ +static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv, + guid_t *guid) +{ + struct device *dev; + struct bmc_device *bmc = NULL; + + dev = driver_find_device(drv, NULL, guid, __find_bmc_guid); + if (dev) { + bmc = to_bmc_device(dev); + put_device(dev); + } + return bmc; +} + +struct prod_dev_id { + unsigned int product_id; + unsigned char device_id; +}; + +static int __find_bmc_prod_dev_id(struct device *dev, void *data) +{ + struct prod_dev_id *cid = data; + struct bmc_device *bmc; + int rv; + + if (dev->type != &bmc_device_type) + return 0; + + bmc = to_bmc_device(dev); + rv = (bmc->id.product_id == cid->product_id + && bmc->id.device_id == cid->device_id); + if (rv) + rv = kref_get_unless_zero(&bmc->usecount); + return rv; +} + +/* + * Returns with the bmc's usecount incremented, if it is non-NULL. + */ +static struct bmc_device *ipmi_find_bmc_prod_dev_id( + struct device_driver *drv, + unsigned int product_id, unsigned char device_id) +{ + struct prod_dev_id id = { + .product_id = product_id, + .device_id = device_id, + }; + struct device *dev; + struct bmc_device *bmc = NULL; + + dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id); + if (dev) { + bmc = to_bmc_device(dev); + put_device(dev); + } + return bmc; +} + +static DEFINE_IDA(ipmi_bmc_ida); + static void release_bmc_device(struct device *dev) { kfree(to_bmc_device(dev)); } +static void cleanup_bmc_work(struct work_struct *work) +{ + struct bmc_device *bmc = container_of(work, struct bmc_device, + remove_work); + int id = bmc->pdev.id; /* Unregister overwrites id */ + + platform_device_unregister(&bmc->pdev); + ida_simple_remove(&ipmi_bmc_ida, id); +} + static void cleanup_bmc_device(struct kref *ref) { struct bmc_device *bmc = container_of(ref, struct bmc_device, usecount); - platform_device_unregister(&bmc->pdev); + /* + * Remove the platform device in a work queue to avoid issues + * with removing the device attributes while reading a device + * attribute. + */ + schedule_work(&bmc->remove_work); } -static void ipmi_bmc_unregister(ipmi_smi_t intf) +/* + * Must be called with intf->bmc_reg_mutex held. + */ +static void __ipmi_bmc_unregister(ipmi_smi_t intf) { struct bmc_device *bmc = intf->bmc; - sysfs_remove_link(&intf->si_dev->kobj, "bmc"); - if (intf->my_dev_name) { - sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name); - kfree(intf->my_dev_name); - intf->my_dev_name = NULL; - } + if (!intf->bmc_registered) + return; - mutex_lock(&ipmidriver_mutex); + sysfs_remove_link(&intf->si_dev->kobj, "bmc"); + sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name); + kfree(intf->my_dev_name); + intf->my_dev_name = NULL; + + mutex_lock(&bmc->dyn_mutex); + list_del(&intf->bmc_link); + mutex_unlock(&bmc->dyn_mutex); + intf->bmc = &intf->tmp_bmc; kref_put(&bmc->usecount, cleanup_bmc_device); - intf->bmc = NULL; - mutex_unlock(&ipmidriver_mutex); + intf->bmc_registered = false; } -static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum) +static void ipmi_bmc_unregister(ipmi_smi_t intf) +{ + mutex_lock(&intf->bmc_reg_mutex); + __ipmi_bmc_unregister(intf); + mutex_unlock(&intf->bmc_reg_mutex); +} + +/* + * Must be called with intf->bmc_reg_mutex held. + */ +static int __ipmi_bmc_register(ipmi_smi_t intf, + struct ipmi_device_id *id, + bool guid_set, guid_t *guid, int intf_num) { int rv; - struct bmc_device *bmc = intf->bmc; + struct bmc_device *bmc; struct bmc_device *old_bmc; - mutex_lock(&ipmidriver_mutex); + /* + * platform_device_register() can cause bmc_reg_mutex to + * be claimed because of the is_visible functions of + * the attributes. Eliminate possible recursion and + * release the lock. + */ + intf->in_bmc_register = true; + mutex_unlock(&intf->bmc_reg_mutex); /* * Try to find if there is an bmc_device struct * representing the interfaced BMC already */ - if (bmc->guid_set) - old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, bmc->guid); + mutex_lock(&ipmidriver_mutex); + if (guid_set) + old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, guid); else old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, - bmc->id.product_id, - bmc->id.device_id); + id->product_id, + id->device_id); /* * If there is already an bmc_device, free the new one, * otherwise register the new BMC device */ if (old_bmc) { - kfree(bmc); - intf->bmc = old_bmc; bmc = old_bmc; + /* + * Note: old_bmc already has usecount incremented by + * the BMC find functions. + */ + intf->bmc = old_bmc; + mutex_lock(&bmc->dyn_mutex); + list_add_tail(&intf->bmc_link, &bmc->intfs); + mutex_unlock(&bmc->dyn_mutex); - kref_get(&bmc->usecount); - mutex_unlock(&ipmidriver_mutex); - - printk(KERN_INFO - "ipmi: interfacing existing BMC (man_id: 0x%6.6x," - " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", - bmc->id.manufacturer_id, - bmc->id.product_id, - bmc->id.device_id); + dev_info(intf->si_dev, + "ipmi: interfacing existing BMC (man_id: 0x%6.6x," + " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); } else { - unsigned char orig_dev_id = bmc->id.device_id; - int warn_printed = 0; - - snprintf(bmc->name, sizeof(bmc->name), - "ipmi_bmc.%4.4x", bmc->id.product_id); - bmc->pdev.name = bmc->name; - - while (ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, - bmc->id.product_id, - bmc->id.device_id)) { - if (!warn_printed) { - printk(KERN_WARNING PFX - "This machine has two different BMCs" - " with the same product id and device" - " id. This is an error in the" - " firmware, but incrementing the" - " device id to work around the problem." - " Prod ID = 0x%x, Dev ID = 0x%x\n", - bmc->id.product_id, bmc->id.device_id); - warn_printed = 1; - } - bmc->id.device_id++; /* Wraps at 255 */ - if (bmc->id.device_id == orig_dev_id) { - printk(KERN_ERR PFX - "Out of device ids!\n"); - break; - } + bmc = kzalloc(sizeof(*bmc), GFP_KERNEL); + if (!bmc) { + rv = -ENOMEM; + goto out; } + INIT_LIST_HEAD(&bmc->intfs); + mutex_init(&bmc->dyn_mutex); + INIT_WORK(&bmc->remove_work, cleanup_bmc_work); + + bmc->id = *id; + bmc->dyn_id_set = 1; + bmc->dyn_guid_set = guid_set; + bmc->guid = *guid; + bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY; + + bmc->pdev.name = "ipmi_bmc"; + rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL); + if (rv < 0) + goto out; bmc->pdev.dev.driver = &ipmidriver.driver; - bmc->pdev.id = bmc->id.device_id; + bmc->pdev.id = rv; bmc->pdev.dev.release = release_bmc_device; bmc->pdev.dev.type = &bmc_device_type; kref_init(&bmc->usecount); + intf->bmc = bmc; + mutex_lock(&bmc->dyn_mutex); + list_add_tail(&intf->bmc_link, &bmc->intfs); + mutex_unlock(&bmc->dyn_mutex); + rv = platform_device_register(&bmc->pdev); - mutex_unlock(&ipmidriver_mutex); if (rv) { - put_device(&bmc->pdev.dev); - printk(KERN_ERR - "ipmi_msghandler:" - " Unable to register bmc device: %d\n", - rv); - /* - * Don't go to out_err, you can only do that if - * the device is registered already. - */ - return rv; + dev_err(intf->si_dev, + PFX " Unable to register bmc device: %d\n", + rv); + goto out_list_del; } - dev_info(intf->si_dev, "Found new BMC (man_id: 0x%6.6x, " - "prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + dev_info(intf->si_dev, + "Found new BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", bmc->id.manufacturer_id, bmc->id.product_id, bmc->id.device_id); @@ -2543,19 +3039,19 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum) */ rv = sysfs_create_link(&intf->si_dev->kobj, &bmc->pdev.dev.kobj, "bmc"); if (rv) { - printk(KERN_ERR - "ipmi_msghandler: Unable to create bmc symlink: %d\n", - rv); - goto out_err; + dev_err(intf->si_dev, + PFX "Unable to create bmc symlink: %d\n", rv); + goto out_put_bmc; } - intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", ifnum); + if (intf_num == -1) + intf_num = intf->intf_num; + intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", intf_num); if (!intf->my_dev_name) { rv = -ENOMEM; - printk(KERN_ERR - "ipmi_msghandler: allocate link from BMC: %d\n", - rv); - goto out_err; + dev_err(intf->si_dev, + PFX "Unable to allocate link from BMC: %d\n", rv); + goto out_unlink1; } rv = sysfs_create_link(&bmc->pdev.dev.kobj, &intf->si_dev->kobj, @@ -2563,18 +3059,42 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum) if (rv) { kfree(intf->my_dev_name); intf->my_dev_name = NULL; - printk(KERN_ERR - "ipmi_msghandler:" - " Unable to create symlink to bmc: %d\n", - rv); - goto out_err; + dev_err(intf->si_dev, + PFX "Unable to create symlink to bmc: %d\n", rv); + goto out_free_my_dev_name; } - return 0; + intf->bmc_registered = true; -out_err: - ipmi_bmc_unregister(intf); +out: + mutex_unlock(&ipmidriver_mutex); + mutex_lock(&intf->bmc_reg_mutex); + intf->in_bmc_register = false; return rv; + + +out_free_my_dev_name: + kfree(intf->my_dev_name); + intf->my_dev_name = NULL; + +out_unlink1: + sysfs_remove_link(&intf->si_dev->kobj, "bmc"); + +out_put_bmc: + mutex_lock(&bmc->dyn_mutex); + list_del(&intf->bmc_link); + mutex_unlock(&bmc->dyn_mutex); + intf->bmc = &intf->tmp_bmc; + kref_put(&bmc->usecount, cleanup_bmc_device); + goto out; + +out_list_del: + mutex_lock(&bmc->dyn_mutex); + list_del(&intf->bmc_link); + mutex_unlock(&bmc->dyn_mutex); + intf->bmc = &intf->tmp_bmc; + put_device(&bmc->pdev.dev); + goto out; } static int @@ -2600,14 +3120,15 @@ send_guid_cmd(ipmi_smi_t intf, int chan) NULL, NULL, 0, - intf->channels[0].address, - intf->channels[0].lun, + intf->addrinfo[0].address, + intf->addrinfo[0].lun, -1, 0); } -static void -guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) +static void guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) { + struct bmc_device *bmc = intf->bmc; + if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE) || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE) || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD)) @@ -2616,38 +3137,46 @@ guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) if (msg->msg.data[0] != 0) { /* Error from getting the GUID, the BMC doesn't have one. */ - intf->bmc->guid_set = 0; + bmc->dyn_guid_set = 0; goto out; } if (msg->msg.data_len < 17) { - intf->bmc->guid_set = 0; - printk(KERN_WARNING PFX - "guid_handler: The GUID response from the BMC was too" - " short, it was %d but should have been 17. Assuming" - " GUID is not available.\n", - msg->msg.data_len); + bmc->dyn_guid_set = 0; + dev_warn(intf->si_dev, + PFX "The GUID response from the BMC was too short, it was %d but should have been 17. Assuming GUID is not available.\n", + msg->msg.data_len); goto out; } - memcpy(intf->bmc->guid, msg->msg.data, 16); - intf->bmc->guid_set = 1; + memcpy(bmc->fetch_guid.b, msg->msg.data + 1, 16); + /* + * Make sure the guid data is available before setting + * dyn_guid_set. + */ + smp_wmb(); + bmc->dyn_guid_set = 1; out: wake_up(&intf->waitq); } -static void -get_guid(ipmi_smi_t intf) +static void __get_guid(ipmi_smi_t intf) { int rv; + struct bmc_device *bmc = intf->bmc; - intf->bmc->guid_set = 0x2; + bmc->dyn_guid_set = 2; intf->null_user_handler = guid_handler; rv = send_guid_cmd(intf, 0); if (rv) /* Send failed, no GUID available. */ - intf->bmc->guid_set = 0; - wait_event(intf->waitq, intf->bmc->guid_set != 2); + bmc->dyn_guid_set = 0; + + wait_event(intf->waitq, bmc->dyn_guid_set != 2); + + /* dyn_guid_set makes the guid data available. */ + smp_rmb(); + intf->null_user_handler = NULL; } @@ -2676,8 +3205,8 @@ send_channel_info_cmd(ipmi_smi_t intf, int chan) NULL, NULL, 0, - intf->channels[0].address, - intf->channels[0].lun, + intf->addrinfo[0].address, + intf->addrinfo[0].lun, -1, 0); } @@ -2685,7 +3214,9 @@ static void channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) { int rv = 0; - int chan; + int ch; + unsigned int set = intf->curr_working_cset; + struct ipmi_channel *chans; if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) @@ -2701,12 +3232,13 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) * assume it has one IPMB at channel * zero. */ - intf->channels[0].medium + intf->wchannels[set].c[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; - intf->channels[0].protocol + intf->wchannels[set].c[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; - intf->curr_channel = IPMI_MAX_CHANNELS; + intf->channel_list = intf->wchannels + set; + intf->channels_ready = true; wake_up(&intf->waitq); goto out; } @@ -2716,24 +3248,31 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) /* Message not big enough, just go on. */ goto next_channel; } - chan = intf->curr_channel; - intf->channels[chan].medium = msg->msg.data[2] & 0x7f; - intf->channels[chan].protocol = msg->msg.data[3] & 0x1f; + ch = intf->curr_channel; + chans = intf->wchannels[set].c; + chans[ch].medium = msg->msg.data[2] & 0x7f; + chans[ch].protocol = msg->msg.data[3] & 0x1f; next_channel: intf->curr_channel++; - if (intf->curr_channel >= IPMI_MAX_CHANNELS) + if (intf->curr_channel >= IPMI_MAX_CHANNELS) { + intf->channel_list = intf->wchannels + set; + intf->channels_ready = true; wake_up(&intf->waitq); - else + } else { + intf->channel_list = intf->wchannels + set; + intf->channels_ready = true; rv = send_channel_info_cmd(intf, intf->curr_channel); + } if (rv) { /* Got an error somehow, just give up. */ - printk(KERN_WARNING PFX - "Error sending channel information for channel" - " %d: %d\n", intf->curr_channel, rv); + dev_warn(intf->si_dev, + PFX "Error sending channel information for channel %d: %d\n", + intf->curr_channel, rv); - intf->curr_channel = IPMI_MAX_CHANNELS; + intf->channel_list = intf->wchannels + set; + intf->channels_ready = true; wake_up(&intf->waitq); } } @@ -2741,6 +3280,53 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) return; } +/* + * Must be holding intf->bmc_reg_mutex to call this. + */ +static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id) +{ + int rv; + + if (ipmi_version_major(id) > 1 + || (ipmi_version_major(id) == 1 + && ipmi_version_minor(id) >= 5)) { + unsigned int set; + + /* + * Start scanning the channels to see what is + * available. + */ + set = !intf->curr_working_cset; + intf->curr_working_cset = set; + memset(&intf->wchannels[set], 0, + sizeof(struct ipmi_channel_set)); + + intf->null_user_handler = channel_handler; + intf->curr_channel = 0; + rv = send_channel_info_cmd(intf, 0); + if (rv) { + dev_warn(intf->si_dev, + "Error sending channel information for channel 0, %d\n", + rv); + return -EIO; + } + + /* Wait for the channel info to be read. */ + wait_event(intf->waitq, intf->channels_ready); + intf->null_user_handler = NULL; + } else { + unsigned int set = intf->curr_working_cset; + + /* Assume a single IPMB channel at zero. */ + intf->wchannels[set].c[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; + intf->wchannels[set].c[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; + intf->channel_list = intf->wchannels + set; + intf->channels_ready = true; + } + + return 0; +} + static void ipmi_poll(ipmi_smi_t intf) { if (intf->handlers->poll) @@ -2755,9 +3341,18 @@ void ipmi_poll_interface(ipmi_user_t user) } EXPORT_SYMBOL(ipmi_poll_interface); +static void redo_bmc_reg(struct work_struct *work) +{ + ipmi_smi_t intf = container_of(work, struct ipmi_smi, bmc_reg_work); + + if (!intf->in_shutdown) + bmc_get_device_id(intf, NULL, NULL, NULL, NULL); + + kref_put(&intf->refcount, intf_free); +} + int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, void *send_info, - struct ipmi_device_id *device_id, struct device *si_dev, unsigned char slave_addr) { @@ -2766,6 +3361,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, ipmi_smi_t intf; ipmi_smi_t tintf; struct list_head *link; + struct ipmi_device_id id; /* * Make sure the driver is actually initialized, this handles @@ -2787,24 +3383,21 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, if (!intf) return -ENOMEM; - intf->ipmi_version_major = ipmi_version_major(device_id); - intf->ipmi_version_minor = ipmi_version_minor(device_id); - - intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL); - if (!intf->bmc) { - kfree(intf); - return -ENOMEM; - } + intf->bmc = &intf->tmp_bmc; + INIT_LIST_HEAD(&intf->bmc->intfs); + mutex_init(&intf->bmc->dyn_mutex); + INIT_LIST_HEAD(&intf->bmc_link); + mutex_init(&intf->bmc_reg_mutex); intf->intf_num = -1; /* Mark it invalid for now. */ kref_init(&intf->refcount); - intf->bmc->id = *device_id; + INIT_WORK(&intf->bmc_reg_work, redo_bmc_reg); intf->si_dev = si_dev; for (j = 0; j < IPMI_MAX_CHANNELS; j++) { - intf->channels[j].address = IPMI_BMC_SLAVE_ADDR; - intf->channels[j].lun = 2; + intf->addrinfo[j].address = IPMI_BMC_SLAVE_ADDR; + intf->addrinfo[j].lun = 2; } if (slave_addr != 0) - intf->channels[0].address = slave_addr; + intf->addrinfo[0].address = slave_addr; INIT_LIST_HEAD(&intf->users); intf->handlers = handlers; intf->send_info = send_info; @@ -2814,7 +3407,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, intf->seq_table[j].seqid = 0; } intf->curr_seq = 0; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE mutex_init(&intf->proc_entry_lock); #endif spin_lock_init(&intf->waiting_rcv_msgs_lock); @@ -2838,7 +3431,9 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, for (i = 0; i < IPMI_NUM_STATS; i++) atomic_set(&intf->stats[i], 0); +#ifdef CONFIG_IPMI_PROC_INTERFACE intf->proc_dir = NULL; +#endif mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); @@ -2862,45 +3457,29 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, if (rv) goto out; - get_guid(intf); - - if ((intf->ipmi_version_major > 1) - || ((intf->ipmi_version_major == 1) - && (intf->ipmi_version_minor >= 5))) { - /* - * Start scanning the channels to see what is - * available. - */ - intf->null_user_handler = channel_handler; - intf->curr_channel = 0; - rv = send_channel_info_cmd(intf, 0); - if (rv) { - printk(KERN_WARNING PFX - "Error sending channel information for channel" - " 0, %d\n", rv); - goto out; - } - - /* Wait for the channel info to be read. */ - wait_event(intf->waitq, - intf->curr_channel >= IPMI_MAX_CHANNELS); - intf->null_user_handler = NULL; - } else { - /* Assume a single IPMB channel at zero. */ - intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; - intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; - intf->curr_channel = IPMI_MAX_CHANNELS; + rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i); + if (rv) { + dev_err(si_dev, "Unable to get the device id: %d\n", rv); + goto out; } - rv = ipmi_bmc_register(intf, i); + mutex_lock(&intf->bmc_reg_mutex); + rv = __scan_channels(intf, &id); + mutex_unlock(&intf->bmc_reg_mutex); + if (rv) + goto out; - if (rv == 0) - rv = add_proc_entries(intf, i); +#ifdef CONFIG_IPMI_PROC_INTERFACE + rv = add_proc_entries(intf, i); +#endif out: if (rv) { + ipmi_bmc_unregister(intf); +#ifdef CONFIG_IPMI_PROC_INTERFACE if (intf->proc_dir) remove_proc_entries(intf); +#endif intf->handlers = NULL; list_del_rcu(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); @@ -3005,7 +3584,9 @@ int ipmi_unregister_smi(ipmi_smi_t intf) intf->handlers = NULL; mutex_unlock(&ipmi_interfaces_mutex); +#ifdef CONFIG_IPMI_PROC_INTERFACE remove_proc_entries(intf); +#endif ipmi_bmc_unregister(intf); /* @@ -3130,7 +3711,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, msg->data[3] = msg->rsp[6]; msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); msg->data[5] = ipmb_checksum(&(msg->data[3]), 2); - msg->data[6] = intf->channels[msg->rsp[3] & 0xf].address; + msg->data[6] = intf->addrinfo[msg->rsp[3] & 0xf].address; /* rqseq/lun */ msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); msg->data[8] = msg->rsp[8]; /* cmd */ @@ -3584,8 +4165,8 @@ static int handle_read_event_rsp(ipmi_smi_t intf, * There's too many things in the queue, discard this * message. */ - printk(KERN_WARNING PFX "Event queue full, discarding" - " incoming events\n"); + dev_warn(intf->si_dev, + PFX "Event queue full, discarding incoming events\n"); intf->event_msg_printed = 1; } @@ -3603,11 +4184,8 @@ static int handle_bmc_rsp(ipmi_smi_t intf, recv_msg = (struct ipmi_recv_msg *) msg->user_data; if (recv_msg == NULL) { - printk(KERN_WARNING - "IPMI message received with no owner. This\n" - "could be because of a malformed message, or\n" - "because of a hardware error. Contact your\n" - "hardware vender for assistance\n"); + dev_warn(intf->si_dev, + "IPMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vender for assistance\n"); return 0; } @@ -3661,9 +4239,9 @@ static int handle_one_recv_msg(ipmi_smi_t intf, #endif if (msg->rsp_size < 2) { /* Message is too small to be correct. */ - printk(KERN_WARNING PFX "BMC returned to small a message" - " for netfn %x cmd %x, got %d bytes\n", - (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size); + dev_warn(intf->si_dev, + PFX "BMC returned to small a message for netfn %x cmd %x, got %d bytes\n", + (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size); /* Generate an error response for the message. */ msg->rsp[0] = msg->data[0] | (1 << 2); @@ -3676,10 +4254,10 @@ static int handle_one_recv_msg(ipmi_smi_t intf, * The NetFN and Command in the response is not even * marginally correct. */ - printk(KERN_WARNING PFX "BMC returned incorrect response," - " expected netfn %x cmd %x, got netfn %x cmd %x\n", - (msg->data[0] >> 2) | 1, msg->data[1], - msg->rsp[0] >> 2, msg->rsp[1]); + dev_warn(intf->si_dev, + PFX "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n", + (msg->data[0] >> 2) | 1, msg->data[1], + msg->rsp[0] >> 2, msg->rsp[1]); /* Generate an error response for the message. */ msg->rsp[0] = msg->data[0] | (1 << 2); @@ -3721,6 +4299,8 @@ static int handle_one_recv_msg(ipmi_smi_t intf, deliver_response(recv_msg); } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { + struct ipmi_channel *chans; + /* It's from the receive queue. */ chan = msg->rsp[3] & 0xf; if (chan >= IPMI_MAX_CHANNELS) { @@ -3735,12 +4315,14 @@ static int handle_one_recv_msg(ipmi_smi_t intf, * equal to or greater than IPMI_MAX_CHANNELS when all the * channels for this interface have been initialized. */ - if (intf->curr_channel < IPMI_MAX_CHANNELS) { + if (!intf->channels_ready) { requeue = 0; /* Throw the message away */ goto out; } - switch (intf->channels[chan].medium) { + chans = READ_ONCE(intf->channel_list)->c; + + switch (chans[chan].medium) { case IPMI_CHANNEL_MEDIUM_IPMB: if (msg->rsp[4] & 0x04) { /* @@ -3777,9 +4359,8 @@ static int handle_one_recv_msg(ipmi_smi_t intf, default: /* Check for OEM Channels. Clients had better register for these commands. */ - if ((intf->channels[chan].medium - >= IPMI_CHANNEL_MEDIUM_OEM_MIN) - && (intf->channels[chan].medium + if ((chans[chan].medium >= IPMI_CHANNEL_MEDIUM_OEM_MIN) + && (chans[chan].medium <= IPMI_CHANNEL_MEDIUM_OEM_MAX)) { requeue = handle_oem_get_msg_cmd(intf, msg); } else { @@ -3941,15 +4522,14 @@ void ipmi_smi_msg_received(ipmi_smi_t intf, && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR) && (msg->rsp[2] != IPMI_BUS_ERR) && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) { - int chan = msg->rsp[3] & 0xf; + int ch = msg->rsp[3] & 0xf; + struct ipmi_channel *chans; /* Got an error sending the message, handle it. */ - if (chan >= IPMI_MAX_CHANNELS) - ; /* This shouldn't happen */ - else if ((intf->channels[chan].medium - == IPMI_CHANNEL_MEDIUM_8023LAN) - || (intf->channels[chan].medium - == IPMI_CHANNEL_MEDIUM_ASYNC)) + + chans = READ_ONCE(intf->channel_list)->c; + if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN) + || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC)) ipmi_inc_stat(intf, sent_lan_command_errs); else ipmi_inc_stat(intf, sent_ipmb_command_errs); @@ -4030,7 +4610,8 @@ smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, } static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, - struct list_head *timeouts, long timeout_period, + struct list_head *timeouts, + unsigned long timeout_period, int slot, unsigned long *flags, unsigned int *waiting_msgs) { @@ -4043,8 +4624,8 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, if (!ent->inuse) return; - ent->timeout -= timeout_period; - if (ent->timeout > 0) { + if (timeout_period < ent->timeout) { + ent->timeout -= timeout_period; (*waiting_msgs)++; return; } @@ -4110,7 +4691,8 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, } } -static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period) +static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, + unsigned long timeout_period) { struct list_head timeouts; struct ipmi_recv_msg *msg, *msg2; @@ -4118,6 +4700,14 @@ static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period) int i; unsigned int waiting_msgs = 0; + if (!intf->bmc_registered) { + kref_get(&intf->refcount); + if (!schedule_work(&intf->bmc_reg_work)) { + kref_put(&intf->refcount, intf_free); + waiting_msgs++; + } + } + /* * Go through the seq table and find any messages that * have timed out, putting them in the timeouts @@ -4269,8 +4859,6 @@ void ipmi_free_recv_msg(struct ipmi_recv_msg *msg) } EXPORT_SYMBOL(ipmi_free_recv_msg); -#ifdef CONFIG_IPMI_PANIC_EVENT - static atomic_t panic_done_count = ATOMIC_INIT(0); static void dummy_smi_done_handler(struct ipmi_smi_msg *msg) @@ -4306,8 +4894,8 @@ static void ipmi_panic_request_and_wait(ipmi_smi_t intf, &smi_msg, &recv_msg, 0, - intf->channels[0].address, - intf->channels[0].lun, + intf->addrinfo[0].address, + intf->addrinfo[0].lun, 0, 1); /* Don't retry, and don't wait. */ if (rv) atomic_sub(2, &panic_done_count); @@ -4318,7 +4906,6 @@ static void ipmi_panic_request_and_wait(ipmi_smi_t intf, ipmi_poll(intf); } -#ifdef CONFIG_IPMI_PANIC_STRING static void event_receiver_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) { if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) @@ -4345,7 +4932,6 @@ static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) intf->local_event_generator = (msg->msg.data[6] >> 5) & 1; } } -#endif static void send_panic_events(char *str) { @@ -4355,6 +4941,9 @@ static void send_panic_events(char *str) struct ipmi_system_interface_addr *si; struct ipmi_addr addr; + if (ipmi_send_panic_event == IPMI_SEND_PANIC_EVENT_NONE) + return; + si = (struct ipmi_system_interface_addr *) &addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si->channel = IPMI_BMC_CHANNEL; @@ -4383,20 +4972,19 @@ static void send_panic_events(char *str) /* For every registered interface, send the event. */ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - if (!intf->handlers) - /* Interface is not ready. */ + if (!intf->handlers || !intf->handlers->poll) + /* Interface is not ready or can't run at panic time. */ continue; /* Send the event announcing the panic. */ ipmi_panic_request_and_wait(intf, &addr, &msg); } -#ifdef CONFIG_IPMI_PANIC_STRING /* * On every interface, dump a bunch of OEM event holding the * string. */ - if (!str) + if (ipmi_send_panic_event != IPMI_SEND_PANIC_EVENT_STRING || !str) return; /* For every registered interface, send the event. */ @@ -4456,7 +5044,7 @@ static void send_panic_events(char *str) */ if (((intf->event_receiver & 1) == 0) && (intf->event_receiver != 0) - && (intf->event_receiver != intf->channels[0].address)) { + && (intf->event_receiver != intf->addrinfo[0].address)) { /* * The event receiver is valid, send an IPMB * message. @@ -4493,7 +5081,7 @@ static void send_panic_events(char *str) data[0] = 0; data[1] = 0; data[2] = 0xf0; /* OEM event without timestamp. */ - data[3] = intf->channels[0].address; + data[3] = intf->addrinfo[0].address; data[4] = j++; /* sequence # */ /* * Always give 11 bytes, so strncpy will fill @@ -4505,9 +5093,7 @@ static void send_panic_events(char *str) ipmi_panic_request_and_wait(intf, &addr, &msg); } } -#endif /* CONFIG_IPMI_PANIC_STRING */ } -#endif /* CONFIG_IPMI_PANIC_EVENT */ static int has_panicked; @@ -4545,12 +5131,12 @@ static int panic_event(struct notifier_block *this, spin_unlock(&intf->waiting_rcv_msgs_lock); intf->run_to_completion = 1; - intf->handlers->set_run_to_completion(intf->send_info, 1); + if (intf->handlers->set_run_to_completion) + intf->handlers->set_run_to_completion(intf->send_info, + 1); } -#ifdef CONFIG_IPMI_PANIC_EVENT send_panic_events(ptr); -#endif return NOTIFY_DONE; } @@ -4570,22 +5156,21 @@ static int ipmi_init_msghandler(void) rv = driver_register(&ipmidriver.driver); if (rv) { - printk(KERN_ERR PFX "Could not register IPMI driver\n"); + pr_err(PFX "Could not register IPMI driver\n"); return rv; } - printk(KERN_INFO "ipmi message handler version " - IPMI_DRIVER_VERSION "\n"); + pr_info("ipmi message handler version " IPMI_DRIVER_VERSION "\n"); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE proc_ipmi_root = proc_mkdir("ipmi", NULL); if (!proc_ipmi_root) { - printk(KERN_ERR PFX "Unable to create IPMI proc dir"); + pr_err(PFX "Unable to create IPMI proc dir"); driver_unregister(&ipmidriver.driver); return -ENOMEM; } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_IPMI_PROC_INTERFACE */ setup_timer(&ipmi_timer, ipmi_timeout, 0); mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); @@ -4625,9 +5210,9 @@ static void __exit cleanup_ipmi(void) atomic_inc(&stop_operation); del_timer_sync(&ipmi_timer); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE proc_remove(proc_ipmi_root); -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_IPMI_PROC_INTERFACE */ driver_unregister(&ipmidriver.driver); @@ -4636,12 +5221,10 @@ static void __exit cleanup_ipmi(void) /* Check for buffer leaks. */ count = atomic_read(&smi_msg_inuse_count); if (count != 0) - printk(KERN_WARNING PFX "SMI message count %d at exit\n", - count); + pr_warn(PFX "SMI message count %d at exit\n", count); count = atomic_read(&recv_msg_inuse_count); if (count != 0) - printk(KERN_WARNING PFX "recv message count %d at exit\n", - count); + pr_warn(PFX "recv message count %d at exit\n", count); } module_exit(cleanup_ipmi); diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index b338a4becbf8c72baa8a8e9b6b0396c1e6ee3c60..07fddbefefe482f4ce0c32fe1a92e7858d68c4d5 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -23,7 +23,6 @@ struct ipmi_smi_powernv { u64 interface_id; - struct ipmi_device_id ipmi_id; ipmi_smi_t intf; unsigned int irq; @@ -266,8 +265,7 @@ static int ipmi_powernv_probe(struct platform_device *pdev) } /* todo: query actual ipmi_device_id */ - rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi, - &ipmi->ipmi_id, dev, 0); + rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi, dev, 0); if (rc) { dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc); goto err_free_msg; diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 676c910e990fb753218f91aebb699d6fff577438..38e6af1c8e381b4c8c80bdc341a26cf8bd70e25b 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -133,7 +133,7 @@ static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) complete(comp); } -static struct ipmi_user_hndl ipmi_poweroff_handler = { +static const struct ipmi_user_hndl ipmi_poweroff_handler = { .ipmi_recv_hndl = receive_handler }; diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h new file mode 100644 index 0000000000000000000000000000000000000000..17ce5f7b89abccd740e8f5366095d931781e4d3f --- /dev/null +++ b/drivers/char/ipmi/ipmi_si.h @@ -0,0 +1,49 @@ +/* + * ipmi_si.h + * + * Interface from the device-specific interfaces (OF, DMI, ACPI, PCI, + * etc) to the base ipmi system interface code. + */ + +#include +#include "ipmi_si_sm.h" + +#define IPMI_IO_ADDR_SPACE 0 +#define IPMI_MEM_ADDR_SPACE 1 + +#define DEFAULT_REGSPACING 1 +#define DEFAULT_REGSIZE 1 + +#define DEVICE_NAME "ipmi_si" + +int ipmi_si_add_smi(struct si_sm_io *io); +irqreturn_t ipmi_si_irq_handler(int irq, void *data); +void ipmi_irq_start_cleanup(struct si_sm_io *io); +int ipmi_std_irq_setup(struct si_sm_io *io); +void ipmi_irq_finish_setup(struct si_sm_io *io); +int ipmi_si_remove_by_dev(struct device *dev); +void ipmi_si_remove_by_data(int addr_space, enum si_type si_type, + unsigned long addr); +int ipmi_si_hardcode_find_bmc(void); +void ipmi_si_platform_init(void); +void ipmi_si_platform_shutdown(void); + +extern struct platform_driver ipmi_platform_driver; + +#ifdef CONFIG_PCI +void ipmi_si_pci_init(void); +void ipmi_si_pci_shutdown(void); +#else +static inline void ipmi_si_pci_init(void) { } +static inline void ipmi_si_pci_shutdown(void) { } +#endif +#ifdef CONFIG_PARISC +void ipmi_si_parisc_init(void); +void ipmi_si_parisc_shutdown(void); +#else +static inline void ipmi_si_parisc_init(void) { } +static inline void ipmi_si_parisc_shutdown(void) { } +#endif + +int ipmi_si_port_setup(struct si_sm_io *io); +int ipmi_si_mem_setup(struct si_sm_io *io); diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c new file mode 100644 index 0000000000000000000000000000000000000000..fa9a4780de365d671535a40b17f2ce9b5edc0504 --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_hardcode.c @@ -0,0 +1,146 @@ + +#include +#include "ipmi_si.h" + +#define PFX "ipmi_hardcode: " +/* + * There can be 4 IO ports passed in (with or without IRQs), 4 addresses, + * a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS. + */ + +#define SI_MAX_PARMS 4 + +static char *si_type[SI_MAX_PARMS]; +#define MAX_SI_TYPE_STR 30 +static char si_type_str[MAX_SI_TYPE_STR]; +static unsigned long addrs[SI_MAX_PARMS]; +static unsigned int num_addrs; +static unsigned int ports[SI_MAX_PARMS]; +static unsigned int num_ports; +static int irqs[SI_MAX_PARMS]; +static unsigned int num_irqs; +static int regspacings[SI_MAX_PARMS]; +static unsigned int num_regspacings; +static int regsizes[SI_MAX_PARMS]; +static unsigned int num_regsizes; +static int regshifts[SI_MAX_PARMS]; +static unsigned int num_regshifts; +static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */ +static unsigned int num_slave_addrs; + +module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0); +MODULE_PARM_DESC(type, "Defines the type of each interface, each" + " interface separated by commas. The types are 'kcs'," + " 'smic', and 'bt'. For example si_type=kcs,bt will set" + " the first interface to kcs and the second to bt"); +module_param_hw_array(addrs, ulong, iomem, &num_addrs, 0); +MODULE_PARM_DESC(addrs, "Sets the memory address of each interface, the" + " addresses separated by commas. Only use if an interface" + " is in memory. Otherwise, set it to zero or leave" + " it blank."); +module_param_hw_array(ports, uint, ioport, &num_ports, 0); +MODULE_PARM_DESC(ports, "Sets the port address of each interface, the" + " addresses separated by commas. Only use if an interface" + " is a port. Otherwise, set it to zero or leave" + " it blank."); +module_param_hw_array(irqs, int, irq, &num_irqs, 0); +MODULE_PARM_DESC(irqs, "Sets the interrupt of each interface, the" + " addresses separated by commas. Only use if an interface" + " has an interrupt. Otherwise, set it to zero or leave" + " it blank."); +module_param_hw_array(regspacings, int, other, &num_regspacings, 0); +MODULE_PARM_DESC(regspacings, "The number of bytes between the start address" + " and each successive register used by the interface. For" + " instance, if the start address is 0xca2 and the spacing" + " is 2, then the second address is at 0xca4. Defaults" + " to 1."); +module_param_hw_array(regsizes, int, other, &num_regsizes, 0); +MODULE_PARM_DESC(regsizes, "The size of the specific IPMI register in bytes." + " This should generally be 1, 2, 4, or 8 for an 8-bit," + " 16-bit, 32-bit, or 64-bit register. Use this if you" + " the 8-bit IPMI register has to be read from a larger" + " register."); +module_param_hw_array(regshifts, int, other, &num_regshifts, 0); +MODULE_PARM_DESC(regshifts, "The amount to shift the data read from the." + " IPMI register, in bits. For instance, if the data" + " is read from a 32-bit word and the IPMI data is in" + " bit 8-15, then the shift would be 8"); +module_param_hw_array(slave_addrs, int, other, &num_slave_addrs, 0); +MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for" + " the controller. Normally this is 0x20, but can be" + " overridden by this parm. This is an array indexed" + " by interface number."); + +int ipmi_si_hardcode_find_bmc(void) +{ + int ret = -ENODEV; + int i; + struct si_sm_io io; + char *str; + + /* Parse out the si_type string into its components. */ + str = si_type_str; + if (*str != '\0') { + for (i = 0; (i < SI_MAX_PARMS) && (*str != '\0'); i++) { + si_type[i] = str; + str = strchr(str, ','); + if (str) { + *str = '\0'; + str++; + } else { + break; + } + } + } + + memset(&io, 0, sizeof(io)); + for (i = 0; i < SI_MAX_PARMS; i++) { + if (!ports[i] && !addrs[i]) + continue; + + io.addr_source = SI_HARDCODED; + pr_info(PFX "probing via hardcoded address\n"); + + if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) { + io.si_type = SI_KCS; + } else if (strcmp(si_type[i], "smic") == 0) { + io.si_type = SI_SMIC; + } else if (strcmp(si_type[i], "bt") == 0) { + io.si_type = SI_BT; + } else { + pr_warn(PFX "Interface type specified for interface %d, was invalid: %s\n", + i, si_type[i]); + continue; + } + + if (ports[i]) { + /* An I/O port */ + io.addr_data = ports[i]; + io.addr_type = IPMI_IO_ADDR_SPACE; + } else if (addrs[i]) { + /* A memory port */ + io.addr_data = addrs[i]; + io.addr_type = IPMI_MEM_ADDR_SPACE; + } else { + pr_warn(PFX "Interface type specified for interface %d, but port and address were not set or set to zero.\n", + i); + continue; + } + + io.addr = NULL; + io.regspacing = regspacings[i]; + if (!io.regspacing) + io.regspacing = DEFAULT_REGSPACING; + io.regsize = regsizes[i]; + if (!io.regsize) + io.regsize = DEFAULT_REGSIZE; + io.regshift = regshifts[i]; + io.irq = irqs[i]; + if (io.irq) + io.irq_setup = ipmi_std_irq_setup; + io.slave_addr = slave_addrs[i]; + + ret = ipmi_si_add_smi(&io); + } + return ret; +} diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c new file mode 100644 index 0000000000000000000000000000000000000000..fc03b9be2f3d3fccb2ae1efef0278b8fc67f2738 --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_hotmod.c @@ -0,0 +1,242 @@ +/* + * ipmi_si_hotmod.c + * + * Handling for dynamically adding/removing IPMI devices through + * a module parameter (and thus sysfs). + */ +#include +#include +#include "ipmi_si.h" + +#define PFX "ipmi_hotmod: " + +static int hotmod_handler(const char *val, const struct kernel_param *kp); + +module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); +MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" + " Documentation/IPMI.txt in the kernel sources for the" + " gory details."); + +/* + * Parms come in as [:op2[:op3...]]. ops are: + * add|remove,kcs|bt|smic,mem|i/o,
[,[,[,...]]] + * Options are: + * rsp= + * rsi= + * rsh= + * irq= + * ipmb= + */ +enum hotmod_op { HM_ADD, HM_REMOVE }; +struct hotmod_vals { + const char *name; + const int val; +}; + +static const struct hotmod_vals hotmod_ops[] = { + { "add", HM_ADD }, + { "remove", HM_REMOVE }, + { NULL } +}; + +static const struct hotmod_vals hotmod_si[] = { + { "kcs", SI_KCS }, + { "smic", SI_SMIC }, + { "bt", SI_BT }, + { NULL } +}; + +static const struct hotmod_vals hotmod_as[] = { + { "mem", IPMI_MEM_ADDR_SPACE }, + { "i/o", IPMI_IO_ADDR_SPACE }, + { NULL } +}; + +static int parse_str(const struct hotmod_vals *v, int *val, char *name, + char **curr) +{ + char *s; + int i; + + s = strchr(*curr, ','); + if (!s) { + pr_warn(PFX "No hotmod %s given.\n", name); + return -EINVAL; + } + *s = '\0'; + s++; + for (i = 0; v[i].name; i++) { + if (strcmp(*curr, v[i].name) == 0) { + *val = v[i].val; + *curr = s; + return 0; + } + } + + pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr); + return -EINVAL; +} + +static int check_hotmod_int_op(const char *curr, const char *option, + const char *name, int *val) +{ + char *n; + + if (strcmp(curr, name) == 0) { + if (!option) { + pr_warn(PFX "No option given for '%s'\n", curr); + return -EINVAL; + } + *val = simple_strtoul(option, &n, 0); + if ((*n != '\0') || (*option == '\0')) { + pr_warn(PFX "Bad option given for '%s'\n", curr); + return -EINVAL; + } + return 1; + } + return 0; +} + +static int hotmod_handler(const char *val, const struct kernel_param *kp) +{ + char *str = kstrdup(val, GFP_KERNEL); + int rv; + char *next, *curr, *s, *n, *o; + enum hotmod_op op; + enum si_type si_type; + int addr_space; + unsigned long addr; + int regspacing; + int regsize; + int regshift; + int irq; + int ipmb; + int ival; + int len; + + if (!str) + return -ENOMEM; + + /* Kill any trailing spaces, as we can get a "\n" from echo. */ + len = strlen(str); + ival = len - 1; + while ((ival >= 0) && isspace(str[ival])) { + str[ival] = '\0'; + ival--; + } + + for (curr = str; curr; curr = next) { + regspacing = 1; + regsize = 1; + regshift = 0; + irq = 0; + ipmb = 0; /* Choose the default if not specified */ + + next = strchr(curr, ':'); + if (next) { + *next = '\0'; + next++; + } + + rv = parse_str(hotmod_ops, &ival, "operation", &curr); + if (rv) + break; + op = ival; + + rv = parse_str(hotmod_si, &ival, "interface type", &curr); + if (rv) + break; + si_type = ival; + + rv = parse_str(hotmod_as, &addr_space, "address space", &curr); + if (rv) + break; + + s = strchr(curr, ','); + if (s) { + *s = '\0'; + s++; + } + addr = simple_strtoul(curr, &n, 0); + if ((*n != '\0') || (*curr == '\0')) { + pr_warn(PFX "Invalid hotmod address '%s'\n", curr); + break; + } + + while (s) { + curr = s; + s = strchr(curr, ','); + if (s) { + *s = '\0'; + s++; + } + o = strchr(curr, '='); + if (o) { + *o = '\0'; + o++; + } + rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "rsi", ®size); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "rsh", ®shift); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "irq", &irq); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); + if (rv < 0) + goto out; + else if (rv) + continue; + + rv = -EINVAL; + pr_warn(PFX "Invalid hotmod option '%s'\n", curr); + goto out; + } + + if (op == HM_ADD) { + struct si_sm_io io; + + memset(&io, 0, sizeof(io)); + io.addr_source = SI_HOTMOD; + io.si_type = si_type; + io.addr_data = addr; + io.addr_type = addr_space; + + io.addr = NULL; + io.regspacing = regspacing; + if (!io.regspacing) + io.regspacing = DEFAULT_REGSPACING; + io.regsize = regsize; + if (!io.regsize) + io.regsize = DEFAULT_REGSIZE; + io.regshift = regshift; + io.irq = irq; + if (io.irq) + io.irq_setup = ipmi_std_irq_setup; + io.slave_addr = ipmb; + + rv = ipmi_si_add_smi(&io); + if (rv) + goto out; + } else { + ipmi_si_remove_by_data(addr_space, si_type, addr); + } + } + rv = len; +out: + kfree(str); + return rv; +} diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index d2ac66fe0645ae5972abcba99ec0925727e58423..71d33a1807e46fabd27984e4bcafe993e9ef0f13 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -49,8 +49,6 @@ #include #include #include -#include -#include #include #include #include @@ -59,22 +57,9 @@ #include #include #include -#include -#include "ipmi_si_sm.h" -#include "ipmi_dmi.h" -#include +#include "ipmi_si.h" #include #include -#include -#include -#include -#include -#include - -#ifdef CONFIG_PARISC -#include /* for register_parisc_driver() stuff */ -#include -#endif #define PFX "ipmi_si: " @@ -104,15 +89,9 @@ enum si_intf_state { #define IPMI_BT_INTMASK_CLEAR_IRQ_BIT 2 #define IPMI_BT_INTMASK_ENABLE_IRQ_BIT 1 -enum si_type { - SI_KCS, SI_SMIC, SI_BT -}; - -static const char * const si_to_str[] = { "kcs", "smic", "bt" }; - -#define DEVICE_NAME "ipmi_si" +static const char * const si_to_str[] = { "invalid", "kcs", "smic", "bt" }; -static struct platform_driver ipmi_driver; +static int initialized; /* * Indexes into stats[] in smi_info below. @@ -167,7 +146,6 @@ struct smi_info { ipmi_smi_t intf; struct si_sm_data *si_sm; const struct si_sm_handlers *handlers; - enum si_type si_type; spinlock_t si_lock; struct ipmi_smi_msg *waiting_msg; struct ipmi_smi_msg *curr_msg; @@ -178,14 +156,6 @@ struct smi_info { * IPMI */ struct si_sm_io io; - int (*io_setup)(struct smi_info *info); - void (*io_cleanup)(struct smi_info *info); - int (*irq_setup)(struct smi_info *info); - void (*irq_cleanup)(struct smi_info *info); - unsigned int io_size; - enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ - void (*addr_source_cleanup)(struct smi_info *info); - void *addr_source_data; /* * Per-OEM handler, called from handle_flags(). Returns 1 @@ -226,19 +196,6 @@ struct smi_info { */ bool run_to_completion; - /* The I/O port of an SI interface. */ - int port; - - /* - * The space between start addresses of the two ports. For - * instance, if the first port is 0xca2 and the spacing is 4, then - * the second port is 0xca6. - */ - unsigned int spacing; - - /* zero if no irq; */ - int irq; - /* The timer for this si. */ struct timer_list si_timer; @@ -289,26 +246,15 @@ struct smi_info { /* From the get device id response... */ struct ipmi_device_id device_id; - /* Driver model stuff. */ - struct device *dev; + /* Default driver model device. */ struct platform_device *pdev; - /* - * True if we allocated the device, false if it came from - * someplace else (like PCI). - */ - bool dev_registered; - - /* Slave address, could be reported from DMI. */ - unsigned char slave_addr; - /* Counters and things for the proc filesystem. */ atomic_t stats[SI_NUM_STATS]; struct task_struct *thread; struct list_head link; - union ipmi_smi_info_union addr_info; }; #define smi_inc_stat(smi, stat) \ @@ -316,23 +262,15 @@ struct smi_info { #define smi_get_stat(smi, stat) \ ((unsigned int) atomic_read(&(smi)->stats[SI_STAT_ ## stat])) -#define SI_MAX_PARMS 4 - -static int force_kipmid[SI_MAX_PARMS]; +#define IPMI_MAX_INTFS 4 +static int force_kipmid[IPMI_MAX_INTFS]; static int num_force_kipmid; -#ifdef CONFIG_PCI -static bool pci_registered; -#endif -#ifdef CONFIG_PARISC -static bool parisc_registered; -#endif -static unsigned int kipmid_max_busy_us[SI_MAX_PARMS]; +static unsigned int kipmid_max_busy_us[IPMI_MAX_INTFS]; static int num_max_busy_us; static bool unload_when_empty = true; -static int add_smi(struct smi_info *smi); static int try_smi_init(struct smi_info *smi); static void cleanup_one_si(struct smi_info *to_clean); static void cleanup_ipmi_si(void); @@ -499,7 +437,7 @@ static void start_getting_events(struct smi_info *smi_info) */ static inline bool disable_si_irq(struct smi_info *smi_info, bool start_timer) { - if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { + if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) { smi_info->interrupt_disabled = true; start_check_enables(smi_info, start_timer); return true; @@ -509,7 +447,7 @@ static inline bool disable_si_irq(struct smi_info *smi_info, bool start_timer) static inline bool enable_si_irq(struct smi_info *smi_info) { - if ((smi_info->irq) && (smi_info->interrupt_disabled)) { + if ((smi_info->io.irq) && (smi_info->interrupt_disabled)) { smi_info->interrupt_disabled = false; start_check_enables(smi_info, true); return true; @@ -585,13 +523,13 @@ static u8 current_global_enables(struct smi_info *smi_info, u8 base, if (smi_info->supports_event_msg_buff) enables |= IPMI_BMC_EVT_MSG_BUFF; - if (((smi_info->irq && !smi_info->interrupt_disabled) || + if (((smi_info->io.irq && !smi_info->interrupt_disabled) || smi_info->cannot_disable_irq) && !smi_info->irq_enable_broken) enables |= IPMI_BMC_RCV_MSG_INTR; if (smi_info->supports_event_msg_buff && - smi_info->irq && !smi_info->interrupt_disabled && + smi_info->io.irq && !smi_info->interrupt_disabled && !smi_info->irq_enable_broken) enables |= IPMI_BMC_EVT_MSG_INTR; @@ -673,7 +611,7 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->handlers->get_result(smi_info->si_sm, msg, 3); if (msg[2] != 0) { /* Error clearing flags */ - dev_warn(smi_info->dev, + dev_warn(smi_info->io.dev, "Error clearing flags: %2.2x\n", msg[2]); } smi_info->si_state = SI_NORMAL; @@ -765,15 +703,15 @@ static void handle_transaction_done(struct smi_info *smi_info) /* We got the flags from the SMI, now handle them. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { - dev_warn(smi_info->dev, + dev_warn(smi_info->io.dev, "Couldn't get irq info: %x.\n", msg[2]); - dev_warn(smi_info->dev, + dev_warn(smi_info->io.dev, "Maybe ok, but ipmi might run very slowly.\n"); smi_info->si_state = SI_NORMAL; break; } enables = current_global_enables(smi_info, 0, &irq_on); - if (smi_info->si_type == SI_BT) + if (smi_info->io.si_type == SI_BT) /* BT has its own interrupt enable bit. */ check_bt_irq(smi_info, irq_on); if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) { @@ -803,7 +741,7 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) - dev_warn(smi_info->dev, + dev_warn(smi_info->io.dev, "Could not set the global enables: 0x%x.\n", msg[2]); @@ -927,7 +865,7 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info, * asynchronously reset, and may thus get interrupts * disable and messages disabled. */ - if (smi_info->supports_event_msg_buff || smi_info->irq) { + if (smi_info->supports_event_msg_buff || smi_info->io.irq) { start_check_enables(smi_info, true); } else { smi_info->curr_msg = alloc_msg_handle_irq(smi_info); @@ -1153,8 +1091,6 @@ static void set_need_watch(void *send_info, bool enable) spin_unlock_irqrestore(&smi_info->si_lock, flags); } -static int initialized; - static void smi_timeout(unsigned long data) { struct smi_info *smi_info = (struct smi_info *) data; @@ -1172,7 +1108,7 @@ static void smi_timeout(unsigned long data) * SI_USEC_PER_JIFFY); smi_result = smi_event_handler(smi_info, time_diff); - if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { + if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) { /* Running with interrupts, only do long timeouts. */ timeout = jiffies + SI_TIMEOUT_JIFFIES; smi_inc_stat(smi_info, long_timeouts); @@ -1199,11 +1135,17 @@ static void smi_timeout(unsigned long data) spin_unlock_irqrestore(&(smi_info->si_lock), flags); } -static irqreturn_t si_irq_handler(int irq, void *data) +irqreturn_t ipmi_si_irq_handler(int irq, void *data) { struct smi_info *smi_info = data; unsigned long flags; + if (smi_info->io.si_type == SI_BT) + /* We need to clear the IRQ flag for the BT interface. */ + smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG, + IPMI_BT_INTMASK_CLEAR_IRQ_BIT + | IPMI_BT_INTMASK_ENABLE_IRQ_BIT); + spin_lock_irqsave(&(smi_info->si_lock), flags); smi_inc_stat(smi_info, interrupts); @@ -1215,16 +1157,6 @@ static irqreturn_t si_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static irqreturn_t si_bt_irq_handler(int irq, void *data) -{ - struct smi_info *smi_info = data; - /* We need to clear the IRQ flag for the BT interface. */ - smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG, - IPMI_BT_INTMASK_CLEAR_IRQ_BIT - | IPMI_BT_INTMASK_ENABLE_IRQ_BIT); - return si_irq_handler(irq, data); -} - static int smi_start_processing(void *send_info, ipmi_smi_t intf) { @@ -1238,8 +1170,10 @@ static int smi_start_processing(void *send_info, smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES); /* Try to claim any interrupts. */ - if (new_smi->irq_setup) - new_smi->irq_setup(new_smi); + if (new_smi->io.irq_setup) { + new_smi->io.irq_handler_data = new_smi; + new_smi->io.irq_setup(&new_smi->io); + } /* * Check if the user forcefully enabled the daemon. @@ -1250,14 +1184,14 @@ static int smi_start_processing(void *send_info, * The BT interface is efficient enough to not need a thread, * and there is no need for a thread if we have interrupts. */ - else if ((new_smi->si_type != SI_BT) && (!new_smi->irq)) + else if ((new_smi->io.si_type != SI_BT) && (!new_smi->io.irq)) enable = 1; if (enable) { new_smi->thread = kthread_run(ipmi_thread, new_smi, "kipmi%d", new_smi->intf_num); if (IS_ERR(new_smi->thread)) { - dev_notice(new_smi->dev, "Could not start" + dev_notice(new_smi->io.dev, "Could not start" " kernel thread due to error %ld, only using" " timers to drive the interface\n", PTR_ERR(new_smi->thread)); @@ -1272,10 +1206,10 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data) { struct smi_info *smi = send_info; - data->addr_src = smi->addr_source; - data->dev = smi->dev; - data->addr_info = smi->addr_info; - get_device(smi->dev); + data->addr_src = smi->io.addr_source; + data->dev = smi->io.dev; + data->addr_info = smi->io.addr_info; + get_device(smi->io.dev); return 0; } @@ -1301,118 +1235,12 @@ static const struct ipmi_smi_handlers handlers = { .poll = poll, }; -/* - * There can be 4 IO ports passed in (with or without IRQs), 4 addresses, - * a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS. - */ - static LIST_HEAD(smi_infos); static DEFINE_MUTEX(smi_infos_lock); static int smi_num; /* Used to sequence the SMIs */ -#define DEFAULT_REGSPACING 1 -#define DEFAULT_REGSIZE 1 - -#ifdef CONFIG_ACPI -static bool si_tryacpi = true; -#endif -#ifdef CONFIG_DMI -static bool si_trydmi = true; -#endif -static bool si_tryplatform = true; -#ifdef CONFIG_PCI -static bool si_trypci = true; -#endif -static char *si_type[SI_MAX_PARMS]; -#define MAX_SI_TYPE_STR 30 -static char si_type_str[MAX_SI_TYPE_STR]; -static unsigned long addrs[SI_MAX_PARMS]; -static unsigned int num_addrs; -static unsigned int ports[SI_MAX_PARMS]; -static unsigned int num_ports; -static int irqs[SI_MAX_PARMS]; -static unsigned int num_irqs; -static int regspacings[SI_MAX_PARMS]; -static unsigned int num_regspacings; -static int regsizes[SI_MAX_PARMS]; -static unsigned int num_regsizes; -static int regshifts[SI_MAX_PARMS]; -static unsigned int num_regshifts; -static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */ -static unsigned int num_slave_addrs; - -#define IPMI_IO_ADDR_SPACE 0 -#define IPMI_MEM_ADDR_SPACE 1 static const char * const addr_space_to_str[] = { "i/o", "mem" }; -static int hotmod_handler(const char *val, const struct kernel_param *kp); - -module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); -MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" - " Documentation/IPMI.txt in the kernel sources for the" - " gory details."); - -#ifdef CONFIG_ACPI -module_param_named(tryacpi, si_tryacpi, bool, 0); -MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" - " default scan of the interfaces identified via ACPI"); -#endif -#ifdef CONFIG_DMI -module_param_named(trydmi, si_trydmi, bool, 0); -MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the" - " default scan of the interfaces identified via DMI"); -#endif -module_param_named(tryplatform, si_tryplatform, bool, 0); -MODULE_PARM_DESC(tryplatform, "Setting this to zero will disable the" - " default scan of the interfaces identified via platform" - " interfaces like openfirmware"); -#ifdef CONFIG_PCI -module_param_named(trypci, si_trypci, bool, 0); -MODULE_PARM_DESC(trypci, "Setting this to zero will disable the" - " default scan of the interfaces identified via pci"); -#endif -module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0); -MODULE_PARM_DESC(type, "Defines the type of each interface, each" - " interface separated by commas. The types are 'kcs'," - " 'smic', and 'bt'. For example si_type=kcs,bt will set" - " the first interface to kcs and the second to bt"); -module_param_hw_array(addrs, ulong, iomem, &num_addrs, 0); -MODULE_PARM_DESC(addrs, "Sets the memory address of each interface, the" - " addresses separated by commas. Only use if an interface" - " is in memory. Otherwise, set it to zero or leave" - " it blank."); -module_param_hw_array(ports, uint, ioport, &num_ports, 0); -MODULE_PARM_DESC(ports, "Sets the port address of each interface, the" - " addresses separated by commas. Only use if an interface" - " is a port. Otherwise, set it to zero or leave" - " it blank."); -module_param_hw_array(irqs, int, irq, &num_irqs, 0); -MODULE_PARM_DESC(irqs, "Sets the interrupt of each interface, the" - " addresses separated by commas. Only use if an interface" - " has an interrupt. Otherwise, set it to zero or leave" - " it blank."); -module_param_hw_array(regspacings, int, other, &num_regspacings, 0); -MODULE_PARM_DESC(regspacings, "The number of bytes between the start address" - " and each successive register used by the interface. For" - " instance, if the start address is 0xca2 and the spacing" - " is 2, then the second address is at 0xca4. Defaults" - " to 1."); -module_param_hw_array(regsizes, int, other, &num_regsizes, 0); -MODULE_PARM_DESC(regsizes, "The size of the specific IPMI register in bytes." - " This should generally be 1, 2, 4, or 8 for an 8-bit," - " 16-bit, 32-bit, or 64-bit register. Use this if you" - " the 8-bit IPMI register has to be read from a larger" - " register."); -module_param_hw_array(regshifts, int, other, &num_regshifts, 0); -MODULE_PARM_DESC(regshifts, "The amount to shift the data read from the." - " IPMI register, in bits. For instance, if the data" - " is read from a 32-bit word and the IPMI data is in" - " bit 8-15, then the shift would be 8"); -module_param_hw_array(slave_addrs, int, other, &num_slave_addrs, 0); -MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for" - " the controller. Normally this is 0x20, but can be" - " overridden by this parm. This is an array indexed" - " by interface number."); module_param_array(force_kipmid, int, &num_force_kipmid, 0); MODULE_PARM_DESC(force_kipmid, "Force the kipmi daemon to be enabled (1) or" " disabled(0). Normally the IPMI driver auto-detects" @@ -1427,1683 +1255,287 @@ MODULE_PARM_DESC(kipmid_max_busy_us, " sleeping. 0 (default) means to wait forever. Set to 100-500" " if kipmid is using up a lot of CPU time."); +void ipmi_irq_finish_setup(struct si_sm_io *io) +{ + if (io->si_type == SI_BT) + /* Enable the interrupt in the BT interface. */ + io->outputb(io, IPMI_BT_INTMASK_REG, + IPMI_BT_INTMASK_ENABLE_IRQ_BIT); +} -static void std_irq_cleanup(struct smi_info *info) +void ipmi_irq_start_cleanup(struct si_sm_io *io) { - if (info->si_type == SI_BT) + if (io->si_type == SI_BT) /* Disable the interrupt in the BT interface. */ - info->io.outputb(&info->io, IPMI_BT_INTMASK_REG, 0); - free_irq(info->irq, info); + io->outputb(io, IPMI_BT_INTMASK_REG, 0); } -static int std_irq_setup(struct smi_info *info) +static void std_irq_cleanup(struct si_sm_io *io) +{ + ipmi_irq_start_cleanup(io); + free_irq(io->irq, io->irq_handler_data); +} + +int ipmi_std_irq_setup(struct si_sm_io *io) { int rv; - if (!info->irq) + if (!io->irq) return 0; - if (info->si_type == SI_BT) { - rv = request_irq(info->irq, - si_bt_irq_handler, - IRQF_SHARED, - DEVICE_NAME, - info); - if (!rv) - /* Enable the interrupt in the BT interface. */ - info->io.outputb(&info->io, IPMI_BT_INTMASK_REG, - IPMI_BT_INTMASK_ENABLE_IRQ_BIT); - } else - rv = request_irq(info->irq, - si_irq_handler, - IRQF_SHARED, - DEVICE_NAME, - info); + rv = request_irq(io->irq, + ipmi_si_irq_handler, + IRQF_SHARED, + DEVICE_NAME, + io->irq_handler_data); if (rv) { - dev_warn(info->dev, "%s unable to claim interrupt %d," + dev_warn(io->dev, "%s unable to claim interrupt %d," " running polled\n", - DEVICE_NAME, info->irq); - info->irq = 0; + DEVICE_NAME, io->irq); + io->irq = 0; } else { - info->irq_cleanup = std_irq_cleanup; - dev_info(info->dev, "Using irq %d\n", info->irq); + io->irq_cleanup = std_irq_cleanup; + ipmi_irq_finish_setup(io); + dev_info(io->dev, "Using irq %d\n", io->irq); } return rv; } -static unsigned char port_inb(const struct si_sm_io *io, unsigned int offset) +static int wait_for_msg_done(struct smi_info *smi_info) { - unsigned int addr = io->addr_data; + enum si_sm_result smi_result; + + 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, jiffies_to_usecs(1)); + } 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. + */ + return -ENODEV; - return inb(addr + (offset * io->regspacing)); + return 0; } -static void port_outb(const struct si_sm_io *io, unsigned int offset, - unsigned char b) +static int try_get_dev_id(struct smi_info *smi_info) { - unsigned int addr = io->addr_data; + unsigned char msg[2]; + unsigned char *resp; + unsigned long resp_len; + int rv = 0; - outb(b, addr + (offset * io->regspacing)); -} + resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); + if (!resp) + return -ENOMEM; -static unsigned char port_inw(const struct si_sm_io *io, unsigned int offset) -{ - unsigned int addr = io->addr_data; + /* + * 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); - return (inw(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; -} + rv = wait_for_msg_done(smi_info); + if (rv) + goto out; -static void port_outw(const struct si_sm_io *io, unsigned int offset, - unsigned char b) -{ - unsigned int addr = io->addr_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[0] >> 2, resp[1], + resp + 2, resp_len - 2, &smi_info->device_id); - outw(b << io->regshift, addr + (offset * io->regspacing)); +out: + kfree(resp); + return rv; } -static unsigned char port_inl(const struct si_sm_io *io, unsigned int offset) +static int get_global_enables(struct smi_info *smi_info, u8 *enables) { - unsigned int addr = io->addr_data; + unsigned char msg[3]; + unsigned char *resp; + unsigned long resp_len; + int rv; - return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; -} + resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); + if (!resp) + return -ENOMEM; -static void port_outl(const struct si_sm_io *io, unsigned int offset, - unsigned char b) -{ - unsigned int addr = io->addr_data; + msg[0] = IPMI_NETFN_APP_REQUEST << 2; + msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); - outl(b << io->regshift, addr+(offset * io->regspacing)); -} + rv = wait_for_msg_done(smi_info); + if (rv) { + dev_warn(smi_info->io.dev, + "Error getting response from get global enables command: %d\n", + rv); + goto out; + } -static void port_cleanup(struct smi_info *info) -{ - unsigned int addr = info->io.addr_data; - int idx; + resp_len = smi_info->handlers->get_result(smi_info->si_sm, + resp, IPMI_MAX_MSG_LENGTH); - if (addr) { - for (idx = 0; idx < info->io_size; idx++) - release_region(addr + idx * info->io.regspacing, - info->io.regsize); + if (resp_len < 4 || + resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || + resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD || + resp[2] != 0) { + dev_warn(smi_info->io.dev, + "Invalid return from get global enables command: %ld %x %x %x\n", + resp_len, resp[0], resp[1], resp[2]); + rv = -EINVAL; + goto out; + } else { + *enables = resp[3]; } + +out: + kfree(resp); + return rv; } -static int port_setup(struct smi_info *info) +/* + * Returns 1 if it gets an error from the command. + */ +static int set_global_enables(struct smi_info *smi_info, u8 enables) { - unsigned int addr = info->io.addr_data; - int idx; + unsigned char msg[3]; + unsigned char *resp; + unsigned long resp_len; + int rv; - if (!addr) - return -ENODEV; + resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); + if (!resp) + return -ENOMEM; - info->io_cleanup = port_cleanup; + msg[0] = IPMI_NETFN_APP_REQUEST << 2; + msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; + msg[2] = enables; + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3); - /* - * Figure out the actual inb/inw/inl/etc routine to use based - * upon the register size. - */ - switch (info->io.regsize) { - case 1: - info->io.inputb = port_inb; - info->io.outputb = port_outb; - break; - case 2: - info->io.inputb = port_inw; - info->io.outputb = port_outw; - break; - case 4: - info->io.inputb = port_inl; - info->io.outputb = port_outl; - break; - default: - dev_warn(info->dev, "Invalid register size: %d\n", - info->io.regsize); - return -EINVAL; + rv = wait_for_msg_done(smi_info); + if (rv) { + dev_warn(smi_info->io.dev, + "Error getting response from set global enables command: %d\n", + rv); + goto out; } - /* - * Some BIOSes reserve disjoint I/O regions in their ACPI - * tables. This causes problems when trying to register the - * entire I/O region. Therefore we must register each I/O - * port separately. - */ - for (idx = 0; idx < info->io_size; idx++) { - if (request_region(addr + idx * info->io.regspacing, - info->io.regsize, DEVICE_NAME) == NULL) { - /* Undo allocations */ - while (idx--) - release_region(addr + idx * info->io.regspacing, - info->io.regsize); - return -EIO; - } + resp_len = smi_info->handlers->get_result(smi_info->si_sm, + resp, IPMI_MAX_MSG_LENGTH); + + if (resp_len < 3 || + resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || + resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) { + dev_warn(smi_info->io.dev, + "Invalid return from set global enables command: %ld %x %x\n", + resp_len, resp[0], resp[1]); + rv = -EINVAL; + goto out; } - return 0; -} -static unsigned char intf_mem_inb(const struct si_sm_io *io, - unsigned int offset) -{ - return readb((io->addr)+(offset * io->regspacing)); -} + if (resp[2] != 0) + rv = 1; -static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset, - unsigned char b) -{ - writeb(b, (io->addr)+(offset * io->regspacing)); +out: + kfree(resp); + return rv; } -static unsigned char intf_mem_inw(const struct si_sm_io *io, - unsigned int offset) +/* + * Some BMCs do not support clearing the receive irq bit in the global + * enables (even if they don't support interrupts on the BMC). Check + * for this and handle it properly. + */ +static void check_clr_rcv_irq(struct smi_info *smi_info) { - return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift) - & 0xff; -} + u8 enables = 0; + int rv; -static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset, - unsigned char b) -{ - writeb(b << io->regshift, (io->addr)+(offset * io->regspacing)); -} + rv = get_global_enables(smi_info, &enables); + if (!rv) { + if ((enables & IPMI_BMC_RCV_MSG_INTR) == 0) + /* Already clear, should work ok. */ + return; -static unsigned char intf_mem_inl(const struct si_sm_io *io, - unsigned int offset) -{ - return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift) - & 0xff; -} + enables &= ~IPMI_BMC_RCV_MSG_INTR; + rv = set_global_enables(smi_info, enables); + } -static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset, - unsigned char b) -{ - writel(b << io->regshift, (io->addr)+(offset * io->regspacing)); -} + if (rv < 0) { + dev_err(smi_info->io.dev, + "Cannot check clearing the rcv irq: %d\n", rv); + return; + } -#ifdef readq -static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset) -{ - return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift) - & 0xff; + if (rv) { + /* + * An error when setting the event buffer bit means + * clearing the bit is not supported. + */ + dev_warn(smi_info->io.dev, + "The BMC does not support clearing the recv irq bit, compensating, but the BMC needs to be fixed.\n"); + smi_info->cannot_disable_irq = true; + } } -static void mem_outq(const struct si_sm_io *io, unsigned int offset, - unsigned char b) +/* + * Some BMCs do not support setting the interrupt bits in the global + * enables even if they support interrupts. Clearly bad, but we can + * compensate. + */ +static void check_set_rcv_irq(struct smi_info *smi_info) { - writeq(b << io->regshift, (io->addr)+(offset * io->regspacing)); -} -#endif + u8 enables = 0; + int rv; -static void mem_region_cleanup(struct smi_info *info, int num) -{ - unsigned long addr = info->io.addr_data; - int idx; + if (!smi_info->io.irq) + return; - for (idx = 0; idx < num; idx++) - release_mem_region(addr + idx * info->io.regspacing, - info->io.regsize); -} + rv = get_global_enables(smi_info, &enables); + if (!rv) { + enables |= IPMI_BMC_RCV_MSG_INTR; + rv = set_global_enables(smi_info, enables); + } -static void mem_cleanup(struct smi_info *info) -{ - if (info->io.addr) { - iounmap(info->io.addr); - mem_region_cleanup(info, info->io_size); + if (rv < 0) { + dev_err(smi_info->io.dev, + "Cannot check setting the rcv irq: %d\n", rv); + return; + } + + if (rv) { + /* + * An error when setting the event buffer bit means + * setting the bit is not supported. + */ + dev_warn(smi_info->io.dev, + "The BMC does not support setting the recv irq bit, compensating, but the BMC needs to be fixed.\n"); + smi_info->cannot_disable_irq = true; + smi_info->irq_enable_broken = true; } } -static int mem_setup(struct smi_info *info) +static int try_enable_event_buffer(struct smi_info *smi_info) { - unsigned long addr = info->io.addr_data; - int mapsize, idx; - - if (!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 = intf_mem_inb; - info->io.outputb = intf_mem_outb; - break; - case 2: - info->io.inputb = intf_mem_inw; - info->io.outputb = intf_mem_outw; - break; - case 4: - info->io.inputb = intf_mem_inl; - info->io.outputb = intf_mem_outl; - break; -#ifdef readq - case 8: - info->io.inputb = mem_inq; - info->io.outputb = mem_outq; - break; -#endif - default: - dev_warn(info->dev, "Invalid register size: %d\n", - info->io.regsize); - return -EINVAL; - } - - /* - * Some BIOSes reserve disjoint memory regions in their ACPI - * tables. This causes problems when trying to request the - * entire region. Therefore we must request each register - * separately. - */ - for (idx = 0; idx < info->io_size; idx++) { - if (request_mem_region(addr + idx * info->io.regspacing, - info->io.regsize, DEVICE_NAME) == NULL) { - /* Undo allocations */ - mem_region_cleanup(info, idx); - return -EIO; - } - } - - /* - * 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)); - info->io.addr = ioremap(addr, mapsize); - if (info->io.addr == NULL) { - mem_region_cleanup(info, info->io_size); - return -EIO; - } - return 0; -} - -/* - * Parms come in as [:op2[:op3...]]. ops are: - * add|remove,kcs|bt|smic,mem|i/o,
[,[,[,...]]] - * Options are: - * rsp= - * rsi= - * rsh= - * irq= - * ipmb= - */ -enum hotmod_op { HM_ADD, HM_REMOVE }; -struct hotmod_vals { - const char *name; - const int val; -}; - -static const struct hotmod_vals hotmod_ops[] = { - { "add", HM_ADD }, - { "remove", HM_REMOVE }, - { NULL } -}; - -static const struct hotmod_vals hotmod_si[] = { - { "kcs", SI_KCS }, - { "smic", SI_SMIC }, - { "bt", SI_BT }, - { NULL } -}; - -static const struct hotmod_vals hotmod_as[] = { - { "mem", IPMI_MEM_ADDR_SPACE }, - { "i/o", IPMI_IO_ADDR_SPACE }, - { NULL } -}; - -static int parse_str(const struct hotmod_vals *v, int *val, char *name, - char **curr) -{ - char *s; - int i; - - s = strchr(*curr, ','); - if (!s) { - pr_warn(PFX "No hotmod %s given.\n", name); - return -EINVAL; - } - *s = '\0'; - s++; - for (i = 0; v[i].name; i++) { - if (strcmp(*curr, v[i].name) == 0) { - *val = v[i].val; - *curr = s; - return 0; - } - } - - pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr); - return -EINVAL; -} - -static int check_hotmod_int_op(const char *curr, const char *option, - const char *name, int *val) -{ - char *n; - - if (strcmp(curr, name) == 0) { - if (!option) { - pr_warn(PFX "No option given for '%s'\n", curr); - return -EINVAL; - } - *val = simple_strtoul(option, &n, 0); - if ((*n != '\0') || (*option == '\0')) { - pr_warn(PFX "Bad option given for '%s'\n", curr); - return -EINVAL; - } - return 1; - } - return 0; -} - -static struct smi_info *smi_info_alloc(void) -{ - struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL); - - if (info) - spin_lock_init(&info->si_lock); - return info; -} - -static int hotmod_handler(const char *val, const struct kernel_param *kp) -{ - char *str = kstrdup(val, GFP_KERNEL); - int rv; - char *next, *curr, *s, *n, *o; - enum hotmod_op op; - enum si_type si_type; - int addr_space; - unsigned long addr; - int regspacing; - int regsize; - int regshift; - int irq; - int ipmb; - int ival; - int len; - struct smi_info *info; - - if (!str) - return -ENOMEM; - - /* Kill any trailing spaces, as we can get a "\n" from echo. */ - len = strlen(str); - ival = len - 1; - while ((ival >= 0) && isspace(str[ival])) { - str[ival] = '\0'; - ival--; - } - - for (curr = str; curr; curr = next) { - regspacing = 1; - regsize = 1; - regshift = 0; - irq = 0; - ipmb = 0; /* Choose the default if not specified */ - - next = strchr(curr, ':'); - if (next) { - *next = '\0'; - next++; - } - - rv = parse_str(hotmod_ops, &ival, "operation", &curr); - if (rv) - break; - op = ival; - - rv = parse_str(hotmod_si, &ival, "interface type", &curr); - if (rv) - break; - si_type = ival; - - rv = parse_str(hotmod_as, &addr_space, "address space", &curr); - if (rv) - break; - - s = strchr(curr, ','); - if (s) { - *s = '\0'; - s++; - } - addr = simple_strtoul(curr, &n, 0); - if ((*n != '\0') || (*curr == '\0')) { - pr_warn(PFX "Invalid hotmod address '%s'\n", curr); - break; - } - - while (s) { - curr = s; - s = strchr(curr, ','); - if (s) { - *s = '\0'; - s++; - } - o = strchr(curr, '='); - if (o) { - *o = '\0'; - o++; - } - rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "rsi", ®size); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "rsh", ®shift); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "irq", &irq); - if (rv < 0) - goto out; - else if (rv) - continue; - rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); - if (rv < 0) - goto out; - else if (rv) - continue; - - rv = -EINVAL; - pr_warn(PFX "Invalid hotmod option '%s'\n", curr); - goto out; - } - - if (op == HM_ADD) { - info = smi_info_alloc(); - if (!info) { - rv = -ENOMEM; - goto out; - } - - info->addr_source = SI_HOTMOD; - info->si_type = si_type; - info->io.addr_data = addr; - info->io.addr_type = addr_space; - if (addr_space == IPMI_MEM_ADDR_SPACE) - info->io_setup = mem_setup; - else - info->io_setup = port_setup; - - info->io.addr = NULL; - info->io.regspacing = regspacing; - if (!info->io.regspacing) - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = regsize; - if (!info->io.regsize) - info->io.regsize = DEFAULT_REGSIZE; - info->io.regshift = regshift; - info->irq = irq; - if (info->irq) - info->irq_setup = std_irq_setup; - info->slave_addr = ipmb; - - rv = add_smi(info); - if (rv) { - kfree(info); - goto out; - } - mutex_lock(&smi_infos_lock); - rv = try_smi_init(info); - mutex_unlock(&smi_infos_lock); - if (rv) { - cleanup_one_si(info); - goto out; - } - } else { - /* remove */ - struct smi_info *e, *tmp_e; - - mutex_lock(&smi_infos_lock); - list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { - if (e->io.addr_type != addr_space) - continue; - if (e->si_type != si_type) - continue; - if (e->io.addr_data == addr) - cleanup_one_si(e); - } - mutex_unlock(&smi_infos_lock); - } - } - rv = len; -out: - kfree(str); - return rv; -} - -static int hardcode_find_bmc(void) -{ - int ret = -ENODEV; - int i; - struct smi_info *info; - - for (i = 0; i < SI_MAX_PARMS; i++) { - if (!ports[i] && !addrs[i]) - continue; - - info = smi_info_alloc(); - if (!info) - return -ENOMEM; - - info->addr_source = SI_HARDCODED; - pr_info(PFX "probing via hardcoded address\n"); - - if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) { - info->si_type = SI_KCS; - } else if (strcmp(si_type[i], "smic") == 0) { - info->si_type = SI_SMIC; - } else if (strcmp(si_type[i], "bt") == 0) { - info->si_type = SI_BT; - } else { - pr_warn(PFX "Interface type specified for interface %d, was invalid: %s\n", - i, si_type[i]); - kfree(info); - continue; - } - - if (ports[i]) { - /* An I/O port */ - info->io_setup = port_setup; - info->io.addr_data = ports[i]; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - } else if (addrs[i]) { - /* A memory port */ - info->io_setup = mem_setup; - info->io.addr_data = addrs[i]; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - } else { - pr_warn(PFX "Interface type specified for interface %d, but port and address were not set or set to zero.\n", - i); - kfree(info); - continue; - } - - info->io.addr = NULL; - info->io.regspacing = regspacings[i]; - if (!info->io.regspacing) - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = regsizes[i]; - if (!info->io.regsize) - info->io.regsize = DEFAULT_REGSIZE; - info->io.regshift = regshifts[i]; - info->irq = irqs[i]; - if (info->irq) - info->irq_setup = std_irq_setup; - info->slave_addr = slave_addrs[i]; - - if (!add_smi(info)) { - mutex_lock(&smi_infos_lock); - if (try_smi_init(info)) - cleanup_one_si(info); - mutex_unlock(&smi_infos_lock); - ret = 0; - } else { - kfree(info); - } - } - return ret; -} - -#ifdef CONFIG_ACPI - -/* - * 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; - -/* For GPE-type interrupts. */ -static u32 ipmi_acpi_gpe(acpi_handle gpe_device, - u32 gpe_number, void *context) -{ - struct smi_info *smi_info = context; - unsigned long flags; - - spin_lock_irqsave(&(smi_info->si_lock), flags); - - smi_inc_stat(smi_info, interrupts); - - debug_timestamp("ACPI_GPE"); - - smi_event_handler(smi_info, 0); - spin_unlock_irqrestore(&(smi_info->si_lock), flags); - - return ACPI_INTERRUPT_HANDLED; -} - -static void acpi_gpe_irq_cleanup(struct smi_info *info) -{ - if (!info->irq) - return; - - acpi_remove_gpe_handler(NULL, info->irq, &ipmi_acpi_gpe); -} - -static int acpi_gpe_irq_setup(struct smi_info *info) -{ - acpi_status status; - - if (!info->irq) - return 0; - - status = acpi_install_gpe_handler(NULL, - info->irq, - ACPI_GPE_LEVEL_TRIGGERED, - &ipmi_acpi_gpe, - info); - if (status != AE_OK) { - dev_warn(info->dev, "%s unable to claim ACPI GPE %d," - " running polled\n", DEVICE_NAME, info->irq); - info->irq = 0; - return -EINVAL; - } else { - info->irq_cleanup = acpi_gpe_irq_cleanup; - dev_info(info->dev, "Using ACPI GPE %d\n", info->irq); - return 0; - } -} - -/* - * Defined at - * http://h21007.www2.hp.com/portal/download/files/unprot/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_spmi(struct SPMITable *spmi) -{ - struct smi_info *info; - int rv; - - if (spmi->IPMIlegacy != 1) { - pr_info(PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy); - return -ENODEV; - } - - info = smi_info_alloc(); - if (!info) { - pr_err(PFX "Could not allocate SI data (3)\n"); - return -ENOMEM; - } - - info->addr_source = SI_SPMI; - pr_info(PFX "probing via SPMI\n"); - - /* 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; - case 4: /* SSIF, just ignore */ - kfree(info); - return -EIO; - default: - pr_info(PFX "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->GlobalSystemInterrupt; - 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); - pr_warn(PFX "Unknown ACPI I/O Address type\n"); - return -EIO; - } - info->io.addr_data = spmi->addr.address; - - pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n", - (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", - info->io.addr_data, info->io.regsize, info->io.regspacing, - info->irq); - - rv = add_smi(info); - if (rv) - kfree(info); - - return rv; -} - -static void spmi_find_bmc(void) -{ - acpi_status status; - struct SPMITable *spmi; - int i; - - if (acpi_disabled) - return; - - if (acpi_failure) - return; - - for (i = 0; ; i++) { - status = acpi_get_table(ACPI_SIG_SPMI, i+1, - (struct acpi_table_header **)&spmi); - if (status != AE_OK) - return; - - try_init_spmi(spmi); - } -} -#endif - -#if defined(CONFIG_DMI) || defined(CONFIG_ACPI) -struct resource *ipmi_get_info_from_resources(struct platform_device *pdev, - struct smi_info *info) -{ - struct resource *res, *res_second; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (res) { - info->io_setup = port_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - } else { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) { - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - } - } - if (!res) { - dev_err(&pdev->dev, "no I/O or memory address\n"); - return NULL; - } - info->io.addr_data = res->start; - - info->io.regspacing = DEFAULT_REGSPACING; - res_second = platform_get_resource(pdev, - (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? - IORESOURCE_IO : IORESOURCE_MEM, - 1); - if (res_second) { - if (res_second->start > info->io.addr_data) - info->io.regspacing = - res_second->start - info->io.addr_data; - } - info->io.regsize = DEFAULT_REGSIZE; - info->io.regshift = 0; - - return res; -} - -#endif - -#ifdef CONFIG_DMI -static int dmi_ipmi_probe(struct platform_device *pdev) -{ - struct smi_info *info; - u8 type, slave_addr; - int rv; - - if (!si_trydmi) - return -ENODEV; - - rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); - if (rv) - return -ENODEV; - - info = smi_info_alloc(); - if (!info) { - pr_err(PFX "Could not allocate SI data\n"); - return -ENOMEM; - } - - info->addr_source = SI_SMBIOS; - pr_info(PFX "probing via SMBIOS\n"); - - switch (type) { - case IPMI_DMI_TYPE_KCS: - info->si_type = SI_KCS; - break; - case IPMI_DMI_TYPE_SMIC: - info->si_type = SI_SMIC; - break; - case IPMI_DMI_TYPE_BT: - info->si_type = SI_BT; - break; - default: - kfree(info); - return -EINVAL; - } - - if (!ipmi_get_info_from_resources(pdev, info)) { - rv = -EINVAL; - goto err_free; - } - - rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); - if (rv) { - dev_warn(&pdev->dev, "device has no slave-addr property"); - info->slave_addr = 0x20; - } else { - info->slave_addr = slave_addr; - } - - info->irq = platform_get_irq(pdev, 0); - if (info->irq > 0) - info->irq_setup = std_irq_setup; - else - info->irq = 0; - - info->dev = &pdev->dev; - - pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n", - (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", - info->io.addr_data, info->io.regsize, info->io.regspacing, - info->irq); - - if (add_smi(info)) - kfree(info); - - return 0; - -err_free: - kfree(info); - return rv; -} -#else -static int dmi_ipmi_probe(struct platform_device *pdev) -{ - return -ENODEV; -} -#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 0x10 - -static void ipmi_pci_cleanup(struct smi_info *info) -{ - struct pci_dev *pdev = info->addr_source_data; - - pci_disable_device(pdev); -} - -static int ipmi_pci_probe_regspacing(struct smi_info *info) -{ - if (info->si_type == SI_KCS) { - unsigned char status; - int regspacing; - - info->io.regsize = DEFAULT_REGSIZE; - info->io.regshift = 0; - info->io_size = 2; - info->handlers = &kcs_smi_handlers; - - /* detect 1, 4, 16byte spacing */ - for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) { - info->io.regspacing = regspacing; - if (info->io_setup(info)) { - dev_err(info->dev, - "Could not setup I/O space\n"); - return DEFAULT_REGSPACING; - } - /* write invalid cmd */ - info->io.outputb(&info->io, 1, 0x10); - /* read status back */ - status = info->io.inputb(&info->io, 1); - info->io_cleanup(info); - if (status) - return regspacing; - regspacing *= 4; - } - } - return DEFAULT_REGSPACING; -} - -static int 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; - - info = smi_info_alloc(); - if (!info) - return -ENOMEM; - - info->addr_source = SI_PCI; - dev_info(&pdev->dev, "probing via 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); - dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type); - return -ENOMEM; - } - - rv = pci_enable_device(pdev); - if (rv) { - dev_err(&pdev->dev, "couldn't enable PCI device\n"); - kfree(info); - return rv; - } - - info->addr_source_cleanup = ipmi_pci_cleanup; - info->addr_source_data = pdev; - - 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 = ipmi_pci_probe_regspacing(info); - info->io.regsize = DEFAULT_REGSIZE; - 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); - - dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n", - &pdev->resource[0], info->io.regsize, info->io.regspacing, - info->irq); - - rv = add_smi(info); - if (rv) { - kfree(info); - pci_disable_device(pdev); - } - - return rv; -} - -static void ipmi_pci_remove(struct pci_dev *pdev) -{ - struct smi_info *info = pci_get_drvdata(pdev); - cleanup_one_si(info); -} - -static const 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 = ipmi_pci_remove, -}; -#endif /* CONFIG_PCI */ - -#ifdef CONFIG_OF -static const struct of_device_id of_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 }, - {}, -}; -MODULE_DEVICE_TABLE(of, of_ipmi_match); - -static int of_ipmi_probe(struct platform_device *dev) -{ - const struct of_device_id *match; - struct smi_info *info; - struct resource resource; - const __be32 *regsize, *regspacing, *regshift; - struct device_node *np = dev->dev.of_node; - int ret; - int proplen; - - dev_info(&dev->dev, "probing via device tree\n"); - - match = of_match_device(of_ipmi_match, &dev->dev); - if (!match) - return -ENODEV; - - if (!of_device_is_available(np)) - return -EINVAL; - - 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 = smi_info_alloc(); - - if (!info) { - dev_err(&dev->dev, - "could not allocate memory for OF probe\n"); - return -ENOMEM; - } - - info->si_type = (enum si_type) match->data; - info->addr_source = SI_DEVICETREE; - info->irq_setup = std_irq_setup; - - if (resource.flags & 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 = resource.start; - - info->io.regsize = regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE; - info->io.regspacing = regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING; - info->io.regshift = regshift ? be32_to_cpup(regshift) : 0; - - info->irq = irq_of_parse_and_map(dev->dev.of_node, 0); - info->dev = &dev->dev; - - dev_dbg(&dev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n", - info->io.addr_data, info->io.regsize, info->io.regspacing, - info->irq); - - dev_set_drvdata(&dev->dev, info); - - ret = add_smi(info); - if (ret) { - kfree(info); - return ret; - } - return 0; -} -#else -#define of_ipmi_match NULL -static int of_ipmi_probe(struct platform_device *dev) -{ - return -ENODEV; -} -#endif - -#ifdef CONFIG_ACPI -static int find_slave_address(struct smi_info *info, int slave_addr) -{ -#ifdef CONFIG_IPMI_DMI_DECODE - if (!slave_addr) { - int type = -1; - u32 flags = IORESOURCE_IO; - - switch (info->si_type) { - case SI_KCS: - type = IPMI_DMI_TYPE_KCS; - break; - case SI_BT: - type = IPMI_DMI_TYPE_BT; - break; - case SI_SMIC: - type = IPMI_DMI_TYPE_SMIC; - break; - } - - if (info->io.addr_type == IPMI_MEM_ADDR_SPACE) - flags = IORESOURCE_MEM; - - slave_addr = ipmi_dmi_get_slave_addr(type, flags, - info->io.addr_data); - } -#endif - - return slave_addr; -} - -static int acpi_ipmi_probe(struct platform_device *dev) -{ - struct smi_info *info; - acpi_handle handle; - acpi_status status; - unsigned long long tmp; - struct resource *res; - int rv = -EINVAL; - - if (!si_tryacpi) - return -ENODEV; - - handle = ACPI_HANDLE(&dev->dev); - if (!handle) - return -ENODEV; - - info = smi_info_alloc(); - if (!info) - return -ENOMEM; - - info->addr_source = SI_ACPI; - dev_info(&dev->dev, PFX "probing via ACPI\n"); - - info->addr_info.acpi_info.acpi_handle = handle; - - /* _IFT tells us the interface type: KCS, BT, etc */ - status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp); - if (ACPI_FAILURE(status)) { - dev_err(&dev->dev, "Could not find ACPI IPMI interface type\n"); - goto err_free; - } - - switch (tmp) { - case 1: - info->si_type = SI_KCS; - break; - case 2: - info->si_type = SI_SMIC; - break; - case 3: - info->si_type = SI_BT; - break; - case 4: /* SSIF, just ignore */ - rv = -ENODEV; - goto err_free; - default: - dev_info(&dev->dev, "unknown IPMI type %lld\n", tmp); - goto err_free; - } - - res = ipmi_get_info_from_resources(dev, info); - if (!res) { - rv = -EINVAL; - goto err_free; - } - - /* If _GPE exists, use it; otherwise use standard interrupts */ - status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); - if (ACPI_SUCCESS(status)) { - info->irq = tmp; - info->irq_setup = acpi_gpe_irq_setup; - } else { - int irq = platform_get_irq(dev, 0); - - if (irq > 0) { - info->irq = irq; - info->irq_setup = std_irq_setup; - } - } - - info->slave_addr = find_slave_address(info, info->slave_addr); - - info->dev = &dev->dev; - platform_set_drvdata(dev, info); - - dev_info(info->dev, "%pR regsize %d spacing %d irq %d\n", - res, info->io.regsize, info->io.regspacing, - info->irq); - - rv = add_smi(info); - if (rv) - kfree(info); - - return rv; - -err_free: - kfree(info); - return rv; -} - -static const struct acpi_device_id acpi_ipmi_match[] = { - { "IPI0001", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, acpi_ipmi_match); -#else -static int acpi_ipmi_probe(struct platform_device *dev) -{ - return -ENODEV; -} -#endif - -static int ipmi_probe(struct platform_device *dev) -{ - if (of_ipmi_probe(dev) == 0) - return 0; - - if (acpi_ipmi_probe(dev) == 0) - return 0; - - return dmi_ipmi_probe(dev); -} - -static int ipmi_remove(struct platform_device *dev) -{ - struct smi_info *info = dev_get_drvdata(&dev->dev); - - cleanup_one_si(info); - return 0; -} - -static struct platform_driver ipmi_driver = { - .driver = { - .name = DEVICE_NAME, - .of_match_table = of_ipmi_match, - .acpi_match_table = ACPI_PTR(acpi_ipmi_match), - }, - .probe = ipmi_probe, - .remove = ipmi_remove, -}; - -#ifdef CONFIG_PARISC -static int __init ipmi_parisc_probe(struct parisc_device *dev) -{ - struct smi_info *info; - int rv; - - info = smi_info_alloc(); - - if (!info) { - dev_err(&dev->dev, - "could not allocate memory for PARISC probe\n"); - return -ENOMEM; - } - - info->si_type = SI_KCS; - info->addr_source = SI_DEVICETREE; - info->io_setup = mem_setup; - info->io.addr_type = IPMI_MEM_ADDR_SPACE; - info->io.addr_data = dev->hpa.start; - info->io.regsize = 1; - info->io.regspacing = 1; - info->io.regshift = 0; - info->irq = 0; /* no interrupt */ - info->irq_setup = NULL; - info->dev = &dev->dev; - - dev_dbg(&dev->dev, "addr 0x%lx\n", info->io.addr_data); - - dev_set_drvdata(&dev->dev, info); - - rv = add_smi(info); - if (rv) { - kfree(info); - return rv; - } - - return 0; -} - -static int __exit ipmi_parisc_remove(struct parisc_device *dev) -{ - cleanup_one_si(dev_get_drvdata(&dev->dev)); - return 0; -} - -static const struct parisc_device_id ipmi_parisc_tbl[] __initconst = { - { HPHW_MC, HVERSION_REV_ANY_ID, 0x004, 0xC0 }, - { 0, } -}; - -MODULE_DEVICE_TABLE(parisc, ipmi_parisc_tbl); - -static struct parisc_driver ipmi_parisc_driver __refdata = { - .name = "ipmi", - .id_table = ipmi_parisc_tbl, - .probe = ipmi_parisc_probe, - .remove = __exit_p(ipmi_parisc_remove), -}; -#endif /* CONFIG_PARISC */ - -static int wait_for_msg_done(struct smi_info *smi_info) -{ - enum si_sm_result smi_result; - - 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, jiffies_to_usecs(1)); - } 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. - */ - return -ENODEV; - - return 0; -} - -static int try_get_dev_id(struct smi_info *smi_info) -{ - unsigned char msg[2]; - unsigned char *resp; - unsigned long resp_len; - 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); - - rv = wait_for_msg_done(smi_info); - if (rv) - goto out; - - 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 get_global_enables(struct smi_info *smi_info, u8 *enables) -{ - unsigned char msg[3]; - unsigned char *resp; - unsigned long resp_len; - int rv; - - resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); - if (!resp) - return -ENOMEM; - - msg[0] = IPMI_NETFN_APP_REQUEST << 2; - msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; - smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); - - rv = wait_for_msg_done(smi_info); - if (rv) { - dev_warn(smi_info->dev, - "Error getting response from get global enables command: %d\n", - rv); - goto out; - } - - resp_len = smi_info->handlers->get_result(smi_info->si_sm, - resp, IPMI_MAX_MSG_LENGTH); - - if (resp_len < 4 || - resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || - resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD || - resp[2] != 0) { - dev_warn(smi_info->dev, - "Invalid return from get global enables command: %ld %x %x %x\n", - resp_len, resp[0], resp[1], resp[2]); - rv = -EINVAL; - goto out; - } else { - *enables = resp[3]; - } - -out: - kfree(resp); - return rv; -} - -/* - * Returns 1 if it gets an error from the command. - */ -static int set_global_enables(struct smi_info *smi_info, u8 enables) -{ - unsigned char msg[3]; - unsigned char *resp; - unsigned long resp_len; - int rv; - - resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); - if (!resp) - return -ENOMEM; - - msg[0] = IPMI_NETFN_APP_REQUEST << 2; - msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; - msg[2] = enables; - smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3); - - rv = wait_for_msg_done(smi_info); - if (rv) { - dev_warn(smi_info->dev, - "Error getting response from set global enables command: %d\n", - rv); - goto out; - } - - resp_len = smi_info->handlers->get_result(smi_info->si_sm, - resp, IPMI_MAX_MSG_LENGTH); - - if (resp_len < 3 || - resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || - resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) { - dev_warn(smi_info->dev, - "Invalid return from set global enables command: %ld %x %x\n", - resp_len, resp[0], resp[1]); - rv = -EINVAL; - goto out; - } - - if (resp[2] != 0) - rv = 1; - -out: - kfree(resp); - return rv; -} - -/* - * Some BMCs do not support clearing the receive irq bit in the global - * enables (even if they don't support interrupts on the BMC). Check - * for this and handle it properly. - */ -static void check_clr_rcv_irq(struct smi_info *smi_info) -{ - u8 enables = 0; - int rv; - - rv = get_global_enables(smi_info, &enables); - if (!rv) { - if ((enables & IPMI_BMC_RCV_MSG_INTR) == 0) - /* Already clear, should work ok. */ - return; - - enables &= ~IPMI_BMC_RCV_MSG_INTR; - rv = set_global_enables(smi_info, enables); - } - - if (rv < 0) { - dev_err(smi_info->dev, - "Cannot check clearing the rcv irq: %d\n", rv); - return; - } - - if (rv) { - /* - * An error when setting the event buffer bit means - * clearing the bit is not supported. - */ - dev_warn(smi_info->dev, - "The BMC does not support clearing the recv irq bit, compensating, but the BMC needs to be fixed.\n"); - smi_info->cannot_disable_irq = true; - } -} - -/* - * Some BMCs do not support setting the interrupt bits in the global - * enables even if they support interrupts. Clearly bad, but we can - * compensate. - */ -static void check_set_rcv_irq(struct smi_info *smi_info) -{ - u8 enables = 0; - int rv; - - if (!smi_info->irq) - return; - - rv = get_global_enables(smi_info, &enables); - if (!rv) { - enables |= IPMI_BMC_RCV_MSG_INTR; - rv = set_global_enables(smi_info, enables); - } - - if (rv < 0) { - dev_err(smi_info->dev, - "Cannot check setting the rcv irq: %d\n", rv); - return; - } - - if (rv) { - /* - * An error when setting the event buffer bit means - * setting the bit is not supported. - */ - dev_warn(smi_info->dev, - "The BMC does not support setting the recv irq bit, compensating, but the BMC needs to be fixed.\n"); - smi_info->cannot_disable_irq = true; - smi_info->irq_enable_broken = true; - } -} - -static int try_enable_event_buffer(struct smi_info *smi_info) -{ - unsigned char msg[3]; - unsigned char *resp; - unsigned long resp_len; - int rv = 0; + unsigned char msg[3]; + unsigned char *resp; + unsigned long resp_len; + int rv = 0; resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); if (!resp) @@ -3173,11 +1605,12 @@ static int try_enable_event_buffer(struct smi_info *smi_info) return rv; } +#ifdef CONFIG_IPMI_PROC_INTERFACE static int smi_type_proc_show(struct seq_file *m, void *v) { struct smi_info *smi = m->private; - seq_printf(m, "%s\n", si_to_str[smi->si_type]); + seq_printf(m, "%s\n", si_to_str[smi->io.si_type]); return 0; } @@ -3199,7 +1632,7 @@ static int smi_si_stats_proc_show(struct seq_file *m, void *v) struct smi_info *smi = m->private; seq_printf(m, "interrupts_enabled: %d\n", - smi->irq && !smi->interrupt_disabled); + smi->io.irq && !smi->interrupt_disabled); seq_printf(m, "short_timeouts: %u\n", smi_get_stat(smi, short_timeouts)); seq_printf(m, "long_timeouts: %u\n", @@ -3243,14 +1676,14 @@ static int smi_params_proc_show(struct seq_file *m, void *v) seq_printf(m, "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", - si_to_str[smi->si_type], + si_to_str[smi->io.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); + smi->io.irq, + smi->io.slave_addr); return 0; } @@ -3266,6 +1699,93 @@ static const struct file_operations smi_params_proc_ops = { .llseek = seq_lseek, .release = single_release, }; +#endif + +#define IPMI_SI_ATTR(name) \ +static ssize_t ipmi_##name##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct smi_info *smi_info = dev_get_drvdata(dev); \ + \ + return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name)); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL) + +static ssize_t ipmi_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct smi_info *smi_info = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]); +} +static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL); + +static ssize_t ipmi_interrupts_enabled_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct smi_info *smi_info = dev_get_drvdata(dev); + int enabled = smi_info->io.irq && !smi_info->interrupt_disabled; + + return snprintf(buf, 10, "%d\n", enabled); +} +static DEVICE_ATTR(interrupts_enabled, S_IRUGO, + ipmi_interrupts_enabled_show, NULL); + +IPMI_SI_ATTR(short_timeouts); +IPMI_SI_ATTR(long_timeouts); +IPMI_SI_ATTR(idles); +IPMI_SI_ATTR(interrupts); +IPMI_SI_ATTR(attentions); +IPMI_SI_ATTR(flag_fetches); +IPMI_SI_ATTR(hosed_count); +IPMI_SI_ATTR(complete_transactions); +IPMI_SI_ATTR(events); +IPMI_SI_ATTR(watchdog_pretimeouts); +IPMI_SI_ATTR(incoming_messages); + +static ssize_t ipmi_params_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct smi_info *smi_info = dev_get_drvdata(dev); + + return snprintf(buf, 200, + "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", + si_to_str[smi_info->io.si_type], + addr_space_to_str[smi_info->io.addr_type], + smi_info->io.addr_data, + smi_info->io.regspacing, + smi_info->io.regsize, + smi_info->io.regshift, + smi_info->io.irq, + smi_info->io.slave_addr); +} +static DEVICE_ATTR(params, S_IRUGO, ipmi_params_show, NULL); + +static struct attribute *ipmi_si_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_interrupts_enabled.attr, + &dev_attr_short_timeouts.attr, + &dev_attr_long_timeouts.attr, + &dev_attr_idles.attr, + &dev_attr_interrupts.attr, + &dev_attr_attentions.attr, + &dev_attr_flag_fetches.attr, + &dev_attr_hosed_count.attr, + &dev_attr_complete_transactions.attr, + &dev_attr_events.attr, + &dev_attr_watchdog_pretimeouts.attr, + &dev_attr_incoming_messages.attr, + &dev_attr_params.attr, + NULL +}; + +static const struct attribute_group ipmi_si_dev_attr_group = { + .attrs = ipmi_si_dev_attrs, +}; /* * oem_data_avail_to_receive_msg_avail @@ -3388,7 +1908,7 @@ setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info) { struct ipmi_device_id *id = &smi_info->device_id; if (id->manufacturer_id == DELL_IANA_MFR_ID && - smi_info->si_type == SI_BT) + smi_info->io.si_type == SI_BT) register_xaction_notifier(&dell_poweredge_bt_xaction_notifier); } @@ -3424,7 +1944,7 @@ static inline void wait_for_timer_and_thread(struct smi_info *smi_info) del_timer_sync(&smi_info->si_timer); } -static int is_new_interface(struct smi_info *info) +static struct smi_info *find_dup_si(struct smi_info *info) { struct smi_info *e; @@ -3437,31 +1957,61 @@ static int is_new_interface(struct smi_info *info) * slave address but SMBIOS does. Pick it up from * any source that has it available. */ - if (info->slave_addr && !e->slave_addr) - e->slave_addr = info->slave_addr; - return 0; + if (info->io.slave_addr && !e->io.slave_addr) + e->io.slave_addr = info->io.slave_addr; + return e; } } - return 1; + return NULL; } -static int add_smi(struct smi_info *new_smi) +int ipmi_si_add_smi(struct si_sm_io *io) { int rv = 0; + struct smi_info *new_smi, *dup; + + if (!io->io_setup) { + if (io->addr_type == IPMI_IO_ADDR_SPACE) { + io->io_setup = ipmi_si_port_setup; + } else if (io->addr_type == IPMI_MEM_ADDR_SPACE) { + io->io_setup = ipmi_si_mem_setup; + } else { + return -EINVAL; + } + } + + new_smi = kzalloc(sizeof(*new_smi), GFP_KERNEL); + if (!new_smi) + return -ENOMEM; + spin_lock_init(&new_smi->si_lock); + + new_smi->io = *io; mutex_lock(&smi_infos_lock); - if (!is_new_interface(new_smi)) { - pr_info(PFX "%s-specified %s state machine: duplicate\n", - ipmi_addr_src_to_str(new_smi->addr_source), - si_to_str[new_smi->si_type]); - rv = -EBUSY; - goto out_err; + dup = find_dup_si(new_smi); + if (dup) { + if (new_smi->io.addr_source == SI_ACPI && + dup->io.addr_source == SI_SMBIOS) { + /* We prefer ACPI over SMBIOS. */ + dev_info(dup->io.dev, + "Removing SMBIOS-specified %s state machine in favor of ACPI\n", + si_to_str[new_smi->io.si_type]); + cleanup_one_si(dup); + } else { + dev_info(new_smi->io.dev, + "%s-specified %s state machine: duplicate\n", + ipmi_addr_src_to_str(new_smi->io.addr_source), + si_to_str[new_smi->io.si_type]); + rv = -EBUSY; + kfree(new_smi); + goto out_err; + } } pr_info(PFX "Adding %s-specified %s state machine\n", - ipmi_addr_src_to_str(new_smi->addr_source), - si_to_str[new_smi->si_type]); + ipmi_addr_src_to_str(new_smi->io.addr_source), + si_to_str[new_smi->io.si_type]); /* So we know not to free it unless we have allocated one. */ new_smi->intf = NULL; @@ -3470,6 +2020,14 @@ static int add_smi(struct smi_info *new_smi) list_add_tail(&new_smi->link, &smi_infos); + if (initialized) { + rv = try_smi_init(new_smi); + if (rv) { + mutex_unlock(&smi_infos_lock); + cleanup_one_si(new_smi); + return rv; + } + } out_err: mutex_unlock(&smi_infos_lock); return rv; @@ -3487,13 +2045,13 @@ static int try_smi_init(struct smi_info *new_smi) char *init_name = NULL; pr_info(PFX "Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n", - ipmi_addr_src_to_str(new_smi->addr_source), - si_to_str[new_smi->si_type], + ipmi_addr_src_to_str(new_smi->io.addr_source), + si_to_str[new_smi->io.si_type], addr_space_to_str[new_smi->io.addr_type], new_smi->io.addr_data, - new_smi->slave_addr, new_smi->irq); + new_smi->io.slave_addr, new_smi->io.irq); - switch (new_smi->si_type) { + switch (new_smi->io.si_type) { case SI_KCS: new_smi->handlers = &kcs_smi_handlers; break; @@ -3515,7 +2073,7 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->intf_num = smi_num; /* Do this early so it's available for logs. */ - if (!new_smi->dev) { + if (!new_smi->io.dev) { init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d", new_smi->intf_num); @@ -3529,33 +2087,33 @@ static int try_smi_init(struct smi_info *new_smi) pr_err(PFX "Unable to allocate platform device\n"); goto out_err; } - new_smi->dev = &new_smi->pdev->dev; - new_smi->dev->driver = &ipmi_driver.driver; + new_smi->io.dev = &new_smi->pdev->dev; + new_smi->io.dev->driver = &ipmi_platform_driver.driver; /* Nulled by device_add() */ - new_smi->dev->init_name = init_name; + new_smi->io.dev->init_name = init_name; } /* Allocate the state machine's data and initialize it. */ new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL); if (!new_smi->si_sm) { - pr_err(PFX "Could not allocate state machine memory\n"); rv = -ENOMEM; goto out_err; } - new_smi->io_size = new_smi->handlers->init_data(new_smi->si_sm, - &new_smi->io); + new_smi->io.io_size = new_smi->handlers->init_data(new_smi->si_sm, + &new_smi->io); /* Now that we know the I/O size, we can set up the I/O. */ - rv = new_smi->io_setup(new_smi); + rv = new_smi->io.io_setup(&new_smi->io); if (rv) { - dev_err(new_smi->dev, "Could not set up I/O space\n"); + dev_err(new_smi->io.dev, "Could not set up I/O space\n"); goto out_err; } /* Do low-level detection first. */ if (new_smi->handlers->detect(new_smi->si_sm)) { - if (new_smi->addr_source) - dev_err(new_smi->dev, "Interface detection failed\n"); + if (new_smi->io.addr_source) + dev_err(new_smi->io.dev, + "Interface detection failed\n"); rv = -ENODEV; goto out_err; } @@ -3566,8 +2124,9 @@ static int try_smi_init(struct smi_info *new_smi) */ rv = try_get_dev_id(new_smi); if (rv) { - if (new_smi->addr_source) - dev_err(new_smi->dev, "There appears to be no BMC at this location\n"); + if (new_smi->io.addr_source) + dev_err(new_smi->io.dev, + "There appears to be no BMC at this location\n"); goto out_err; } @@ -3599,7 +2158,7 @@ static int try_smi_init(struct smi_info *new_smi) * IRQ is defined to be set when non-zero. req_events will * cause a global flags check that will enable interrupts. */ - if (new_smi->irq) { + if (new_smi->io.irq) { new_smi->interrupt_disabled = false; atomic_set(&new_smi->req_events, 1); } @@ -3607,30 +2166,40 @@ static int try_smi_init(struct smi_info *new_smi) if (new_smi->pdev) { rv = platform_device_add(new_smi->pdev); if (rv) { - dev_err(new_smi->dev, + dev_err(new_smi->io.dev, "Unable to register system interface device: %d\n", rv); goto out_err; } - new_smi->dev_registered = true; + } + + dev_set_drvdata(new_smi->io.dev, new_smi); + rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group); + if (rv) { + dev_err(new_smi->io.dev, + "Unable to add device attributes: error %d\n", + rv); + goto out_err_stop_timer; } rv = ipmi_register_smi(&handlers, new_smi, - &new_smi->device_id, - new_smi->dev, - new_smi->slave_addr); + new_smi->io.dev, + new_smi->io.slave_addr); if (rv) { - dev_err(new_smi->dev, "Unable to register device: error %d\n", + dev_err(new_smi->io.dev, + "Unable to register device: error %d\n", rv); - goto out_err_stop_timer; + goto out_err_remove_attrs; } +#ifdef CONFIG_IPMI_PROC_INTERFACE rv = ipmi_smi_add_proc_entry(new_smi->intf, "type", &smi_type_proc_ops, new_smi); if (rv) { - dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); + dev_err(new_smi->io.dev, + "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } @@ -3638,7 +2207,8 @@ static int try_smi_init(struct smi_info *new_smi) &smi_si_stats_proc_ops, new_smi); if (rv) { - dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); + dev_err(new_smi->io.dev, + "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } @@ -3646,21 +2216,27 @@ static int try_smi_init(struct smi_info *new_smi) &smi_params_proc_ops, new_smi); if (rv) { - dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); + dev_err(new_smi->io.dev, + "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } +#endif /* Don't increment till we know we have succeeded. */ smi_num++; - dev_info(new_smi->dev, "IPMI %s interface initialized\n", - si_to_str[new_smi->si_type]); + dev_info(new_smi->io.dev, "IPMI %s interface initialized\n", + si_to_str[new_smi->io.si_type]); - WARN_ON(new_smi->dev->init_name != NULL); + WARN_ON(new_smi->io.dev->init_name != NULL); kfree(init_name); return 0; +out_err_remove_attrs: + device_remove_group(new_smi->io.dev, &ipmi_si_dev_attr_group); + dev_set_drvdata(new_smi->io.dev, NULL); + out_err_stop_timer: wait_for_timer_and_thread(new_smi); @@ -3673,9 +2249,9 @@ static int try_smi_init(struct smi_info *new_smi) ipmi_unregister_smi(intf); } - if (new_smi->irq_cleanup) { - new_smi->irq_cleanup(new_smi); - new_smi->irq_cleanup = NULL; + if (new_smi->io.irq_cleanup) { + new_smi->io.irq_cleanup(&new_smi->io); + new_smi->io.irq_cleanup = NULL; } /* @@ -3691,22 +2267,20 @@ static int try_smi_init(struct smi_info *new_smi) kfree(new_smi->si_sm); new_smi->si_sm = NULL; } - if (new_smi->addr_source_cleanup) { - new_smi->addr_source_cleanup(new_smi); - new_smi->addr_source_cleanup = NULL; + if (new_smi->io.addr_source_cleanup) { + new_smi->io.addr_source_cleanup(&new_smi->io); + new_smi->io.addr_source_cleanup = NULL; } - if (new_smi->io_cleanup) { - new_smi->io_cleanup(new_smi); - new_smi->io_cleanup = NULL; + if (new_smi->io.io_cleanup) { + new_smi->io.io_cleanup(&new_smi->io); + new_smi->io.io_cleanup = NULL; } - if (new_smi->dev_registered) { + if (new_smi->pdev) { platform_device_unregister(new_smi->pdev); - new_smi->dev_registered = false; new_smi->pdev = NULL; } else if (new_smi->pdev) { platform_device_put(new_smi->pdev); - new_smi->pdev = NULL; } kfree(init_name); @@ -3716,97 +2290,57 @@ static int try_smi_init(struct smi_info *new_smi) static int init_ipmi_si(void) { - int i; - char *str; - int rv; struct smi_info *e; enum ipmi_addr_src type = SI_INVALID; if (initialized) return 0; - initialized = 1; - - if (si_tryplatform) { - rv = platform_driver_register(&ipmi_driver); - if (rv) { - pr_err(PFX "Unable to register driver: %d\n", rv); - return rv; - } - } - - /* Parse out the si_type string into its components. */ - str = si_type_str; - if (*str != '\0') { - for (i = 0; (i < SI_MAX_PARMS) && (*str != '\0'); i++) { - si_type[i] = str; - str = strchr(str, ','); - if (str) { - *str = '\0'; - str++; - } else { - break; - } - } - } pr_info("IPMI System Interface driver.\n"); /* If the user gave us a device, they presumably want us to use it */ - if (!hardcode_find_bmc()) - return 0; + if (!ipmi_si_hardcode_find_bmc()) + goto do_scan; -#ifdef CONFIG_PCI - if (si_trypci) { - rv = pci_register_driver(&ipmi_pci_driver); - if (rv) - pr_err(PFX "Unable to register PCI driver: %d\n", rv); - else - pci_registered = true; - } -#endif + ipmi_si_platform_init(); -#ifdef CONFIG_ACPI - if (si_tryacpi) - spmi_find_bmc(); -#endif + ipmi_si_pci_init(); -#ifdef CONFIG_PARISC - register_parisc_driver(&ipmi_parisc_driver); - parisc_registered = true; -#endif + ipmi_si_parisc_init(); /* We prefer devices with interrupts, but in the case of a machine with multiple BMCs we assume that there will be several instances of a given type so if we succeed in registering a type then also try to register everything else of the same type */ - +do_scan: mutex_lock(&smi_infos_lock); list_for_each_entry(e, &smi_infos, link) { /* Try to register a device if it has an IRQ and we either haven't successfully registered a device yet or this device has the same type as one we successfully registered */ - if (e->irq && (!type || e->addr_source == type)) { + if (e->io.irq && (!type || e->io.addr_source == type)) { if (!try_smi_init(e)) { - type = e->addr_source; + type = e->io.addr_source; } } } /* type will only have been set if we successfully registered an si */ - if (type) { - mutex_unlock(&smi_infos_lock); - return 0; - } + if (type) + goto skip_fallback_noirq; /* Fall back to the preferred device */ list_for_each_entry(e, &smi_infos, link) { - if (!e->irq && (!type || e->addr_source == type)) { + if (!e->io.irq && (!type || e->io.addr_source == type)) { if (!try_smi_init(e)) { - type = e->addr_source; + type = e->io.addr_source; } } } + +skip_fallback_noirq: + initialized = 1; mutex_unlock(&smi_infos_lock); if (type) @@ -3843,8 +2377,8 @@ static void cleanup_one_si(struct smi_info *to_clean) } } - if (to_clean->dev) - dev_set_drvdata(to_clean->dev, NULL); + device_remove_group(to_clean->io.dev, &ipmi_si_dev_attr_group); + dev_set_drvdata(to_clean->io.dev, NULL); list_del(&to_clean->link); @@ -3852,8 +2386,8 @@ static void cleanup_one_si(struct smi_info *to_clean) * Make sure that interrupts, the timer and the thread are * stopped and will not run again. */ - if (to_clean->irq_cleanup) - to_clean->irq_cleanup(to_clean); + if (to_clean->io.irq_cleanup) + to_clean->io.irq_cleanup(&to_clean->io); wait_for_timer_and_thread(to_clean); /* @@ -3865,7 +2399,8 @@ static void cleanup_one_si(struct smi_info *to_clean) poll(to_clean); schedule_timeout_uninterruptible(1); } - disable_si_irq(to_clean, false); + if (to_clean->handlers) + disable_si_irq(to_clean, false); while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { poll(to_clean); schedule_timeout_uninterruptible(1); @@ -3876,17 +2411,53 @@ static void cleanup_one_si(struct smi_info *to_clean) kfree(to_clean->si_sm); - if (to_clean->addr_source_cleanup) - to_clean->addr_source_cleanup(to_clean); - if (to_clean->io_cleanup) - to_clean->io_cleanup(to_clean); + if (to_clean->io.addr_source_cleanup) + to_clean->io.addr_source_cleanup(&to_clean->io); + if (to_clean->io.io_cleanup) + to_clean->io.io_cleanup(&to_clean->io); - if (to_clean->dev_registered) + if (to_clean->pdev) platform_device_unregister(to_clean->pdev); kfree(to_clean); } +int ipmi_si_remove_by_dev(struct device *dev) +{ + struct smi_info *e; + int rv = -ENOENT; + + mutex_lock(&smi_infos_lock); + list_for_each_entry(e, &smi_infos, link) { + if (e->io.dev == dev) { + cleanup_one_si(e); + rv = 0; + break; + } + } + mutex_unlock(&smi_infos_lock); + + return rv; +} + +void ipmi_si_remove_by_data(int addr_space, enum si_type si_type, + unsigned long addr) +{ + /* remove */ + struct smi_info *e, *tmp_e; + + mutex_lock(&smi_infos_lock); + list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { + if (e->io.addr_type != addr_space) + continue; + if (e->io.si_type != si_type) + continue; + if (e->io.addr_data == addr) + cleanup_one_si(e); + } + mutex_unlock(&smi_infos_lock); +} + static void cleanup_ipmi_si(void) { struct smi_info *e, *tmp_e; @@ -3894,16 +2465,11 @@ static void cleanup_ipmi_si(void) if (!initialized) return; -#ifdef CONFIG_PCI - if (pci_registered) - pci_unregister_driver(&ipmi_pci_driver); -#endif -#ifdef CONFIG_PARISC - if (parisc_registered) - unregister_parisc_driver(&ipmi_parisc_driver); -#endif + ipmi_si_pci_shutdown(); + + ipmi_si_parisc_shutdown(); - platform_driver_unregister(&ipmi_driver); + ipmi_si_platform_shutdown(); mutex_lock(&smi_infos_lock); list_for_each_entry_safe(e, tmp_e, &smi_infos, link) diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c new file mode 100644 index 0000000000000000000000000000000000000000..8796396ecd0faedc698558612ddda42d9cf58582 --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_mem_io.c @@ -0,0 +1,144 @@ + +#include +#include "ipmi_si.h" + +static unsigned char intf_mem_inb(const struct si_sm_io *io, + unsigned int offset) +{ + return readb((io->addr)+(offset * io->regspacing)); +} + +static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writeb(b, (io->addr)+(offset * io->regspacing)); +} + +static unsigned char intf_mem_inw(const struct si_sm_io *io, + unsigned int offset) +{ + return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift) + & 0xff; +} + +static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writeb(b << io->regshift, (io->addr)+(offset * io->regspacing)); +} + +static unsigned char intf_mem_inl(const struct si_sm_io *io, + unsigned int offset) +{ + return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift) + & 0xff; +} + +static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writel(b << io->regshift, (io->addr)+(offset * io->regspacing)); +} + +#ifdef readq +static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset) +{ + return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift) + & 0xff; +} + +static void mem_outq(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + writeq(b << io->regshift, (io->addr)+(offset * io->regspacing)); +} +#endif + +static void mem_region_cleanup(struct si_sm_io *io, int num) +{ + unsigned long addr = io->addr_data; + int idx; + + for (idx = 0; idx < num; idx++) + release_mem_region(addr + idx * io->regspacing, + io->regsize); +} + +static void mem_cleanup(struct si_sm_io *io) +{ + if (io->addr) { + iounmap(io->addr); + mem_region_cleanup(io, io->io_size); + } +} + +int ipmi_si_mem_setup(struct si_sm_io *io) +{ + unsigned long addr = io->addr_data; + int mapsize, idx; + + if (!addr) + return -ENODEV; + + io->io_cleanup = mem_cleanup; + + /* + * Figure out the actual readb/readw/readl/etc routine to use based + * upon the register size. + */ + switch (io->regsize) { + case 1: + io->inputb = intf_mem_inb; + io->outputb = intf_mem_outb; + break; + case 2: + io->inputb = intf_mem_inw; + io->outputb = intf_mem_outw; + break; + case 4: + io->inputb = intf_mem_inl; + io->outputb = intf_mem_outl; + break; +#ifdef readq + case 8: + io->inputb = mem_inq; + io->outputb = mem_outq; + break; +#endif + default: + dev_warn(io->dev, "Invalid register size: %d\n", + io->regsize); + return -EINVAL; + } + + /* + * Some BIOSes reserve disjoint memory regions in their ACPI + * tables. This causes problems when trying to request the + * entire region. Therefore we must request each register + * separately. + */ + for (idx = 0; idx < io->io_size; idx++) { + if (request_mem_region(addr + idx * io->regspacing, + io->regsize, DEVICE_NAME) == NULL) { + /* Undo allocations */ + mem_region_cleanup(io, idx); + return -EIO; + } + } + + /* + * 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 = ((io->io_size * io->regspacing) + - (io->regspacing - io->regsize)); + io->addr = ioremap(addr, mapsize); + if (io->addr == NULL) { + mem_region_cleanup(io, io->io_size); + return -EIO; + } + return 0; +} diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c new file mode 100644 index 0000000000000000000000000000000000000000..090b073ab441961f5c530dd9f453f1f1290e5a08 --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_parisc.c @@ -0,0 +1,58 @@ + +#include +#include /* for register_parisc_driver() stuff */ +#include +#include "ipmi_si.h" + +static bool parisc_registered; + +static int __init ipmi_parisc_probe(struct parisc_device *dev) +{ + struct si_sm_io io; + + io.si_type = SI_KCS; + io.addr_source = SI_DEVICETREE; + io.addr_type = IPMI_MEM_ADDR_SPACE; + io.addr_data = dev->hpa.start; + io.regsize = 1; + io.regspacing = 1; + io.regshift = 0; + io.irq = 0; /* no interrupt */ + io.irq_setup = NULL; + io.dev = &dev->dev; + + dev_dbg(&dev->dev, "addr 0x%lx\n", io.addr_data); + + return ipmi_si_add_smi(&io); +} + +static int __exit ipmi_parisc_remove(struct parisc_device *dev) +{ + return ipmi_si_remove_by_dev(&dev->dev); +} + +static const struct parisc_device_id ipmi_parisc_tbl[] __initconst = { + { HPHW_MC, HVERSION_REV_ANY_ID, 0x004, 0xC0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(parisc, ipmi_parisc_tbl); + +static struct parisc_driver ipmi_parisc_driver __refdata = { + .name = "ipmi", + .id_table = ipmi_parisc_tbl, + .probe = ipmi_parisc_probe, + .remove = __exit_p(ipmi_parisc_remove), +}; + +void ipmi_si_parisc_init(void) +{ + register_parisc_driver(&ipmi_parisc_driver); + parisc_registered = true; +} + +void ipmi_si_parisc_shutdown(void) +{ + if (parisc_registered) + unregister_parisc_driver(&ipmi_parisc_driver); +} diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c new file mode 100644 index 0000000000000000000000000000000000000000..99771f5cad07a7b25285f33cf0073739ca27e4ae --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_pci.c @@ -0,0 +1,166 @@ +/* + * ipmi_si_pci.c + * + * Handling for IPMI devices on the PCI bus. + */ +#include +#include +#include "ipmi_si.h" + +#define PFX "ipmi_pci: " + +static bool pci_registered; + +static bool si_trypci = true; + +module_param_named(trypci, si_trypci, bool, 0); +MODULE_PARM_DESC(trypci, "Setting this to zero will disable the" + " default scan of the interfaces identified via 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 0x10 + +static void ipmi_pci_cleanup(struct si_sm_io *io) +{ + struct pci_dev *pdev = io->addr_source_data; + + pci_disable_device(pdev); +} + +static int ipmi_pci_probe_regspacing(struct si_sm_io *io) +{ + if (io->si_type == SI_KCS) { + unsigned char status; + int regspacing; + + io->regsize = DEFAULT_REGSIZE; + io->regshift = 0; + + /* detect 1, 4, 16byte spacing */ + for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) { + io->regspacing = regspacing; + if (io->io_setup(io)) { + dev_err(io->dev, + "Could not setup I/O space\n"); + return DEFAULT_REGSPACING; + } + /* write invalid cmd */ + io->outputb(io, 1, 0x10); + /* read status back */ + status = io->inputb(io, 1); + io->io_cleanup(io); + if (status) + return regspacing; + regspacing *= 4; + } + } + return DEFAULT_REGSPACING; +} + +static int 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 si_sm_io io; + + memset(&io, 0, sizeof(io)); + io.addr_source = SI_PCI; + dev_info(&pdev->dev, "probing via PCI"); + + switch (class_type) { + case PCI_ERMC_CLASSCODE_TYPE_SMIC: + io.si_type = SI_SMIC; + break; + + case PCI_ERMC_CLASSCODE_TYPE_KCS: + io.si_type = SI_KCS; + break; + + case PCI_ERMC_CLASSCODE_TYPE_BT: + io.si_type = SI_BT; + break; + + default: + dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type); + return -ENOMEM; + } + + rv = pci_enable_device(pdev); + if (rv) { + dev_err(&pdev->dev, "couldn't enable PCI device\n"); + return rv; + } + + io.addr_source_cleanup = ipmi_pci_cleanup; + io.addr_source_data = pdev; + + if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) + io.addr_type = IPMI_IO_ADDR_SPACE; + else + io.addr_type = IPMI_MEM_ADDR_SPACE; + io.addr_data = pci_resource_start(pdev, 0); + + io.regspacing = ipmi_pci_probe_regspacing(&io); + io.regsize = DEFAULT_REGSIZE; + io.regshift = 0; + + io.irq = pdev->irq; + if (io.irq) + io.irq_setup = ipmi_std_irq_setup; + + io.dev = &pdev->dev; + + dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n", + &pdev->resource[0], io.regsize, io.regspacing, io.irq); + + rv = ipmi_si_add_smi(&io); + if (rv) + pci_disable_device(pdev); + + return rv; +} + +static void ipmi_pci_remove(struct pci_dev *pdev) +{ + ipmi_si_remove_by_dev(&pdev->dev); +} + +static const 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 = ipmi_pci_remove, +}; + +void ipmi_si_pci_init(void) +{ + if (si_trypci) { + int rv = pci_register_driver(&ipmi_pci_driver); + if (rv) + pr_err(PFX "Unable to register PCI driver: %d\n", rv); + else + pci_registered = true; + } +} + +void ipmi_si_pci_shutdown(void) +{ + if (pci_registered) + pci_unregister_driver(&ipmi_pci_driver); +} diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c new file mode 100644 index 0000000000000000000000000000000000000000..9573f1116450a89b6561bbeb02601760d65cbdc6 --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -0,0 +1,593 @@ +/* + * ipmi_si_platform.c + * + * Handling for platform devices in IPMI (ACPI, OF, and things + * coming from the platform. + */ +#include +#include +#include +#include +#include +#include +#include +#include "ipmi_si.h" +#include "ipmi_dmi.h" + +#define PFX "ipmi_platform: " + +static bool si_tryplatform = true; +#ifdef CONFIG_ACPI +static bool si_tryacpi = true; +#endif +#ifdef CONFIG_OF +static bool si_tryopenfirmware = true; +#endif +#ifdef CONFIG_DMI +static bool si_trydmi = true; +#else +static bool si_trydmi = false; +#endif + +module_param_named(tryplatform, si_tryplatform, bool, 0); +MODULE_PARM_DESC(tryplatform, "Setting this to zero will disable the" + " default scan of the interfaces identified via platform" + " interfaces besides ACPI, OpenFirmware, and DMI"); +#ifdef CONFIG_ACPI +module_param_named(tryacpi, si_tryacpi, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via ACPI"); +#endif +#ifdef CONFIG_OF +module_param_named(tryopenfirmware, si_tryopenfirmware, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via OpenFirmware"); +#endif +#ifdef CONFIG_DMI +module_param_named(trydmi, si_trydmi, bool, 0); +MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the" + " default scan of the interfaces identified via DMI"); +#endif + +#ifdef CONFIG_ACPI + +/* + * 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; + +/* For GPE-type interrupts. */ +static u32 ipmi_acpi_gpe(acpi_handle gpe_device, + u32 gpe_number, void *context) +{ + struct si_sm_io *io = context; + + ipmi_si_irq_handler(io->irq, io->irq_handler_data); + return ACPI_INTERRUPT_HANDLED; +} + +static void acpi_gpe_irq_cleanup(struct si_sm_io *io) +{ + if (!io->irq) + return; + + ipmi_irq_start_cleanup(io); + acpi_remove_gpe_handler(NULL, io->irq, &ipmi_acpi_gpe); +} + +static int acpi_gpe_irq_setup(struct si_sm_io *io) +{ + acpi_status status; + + if (!io->irq) + return 0; + + status = acpi_install_gpe_handler(NULL, + io->irq, + ACPI_GPE_LEVEL_TRIGGERED, + &ipmi_acpi_gpe, + io); + if (status != AE_OK) { + dev_warn(io->dev, + "Unable to claim ACPI GPE %d, running polled\n", + io->irq); + io->irq = 0; + return -EINVAL; + } else { + io->irq_cleanup = acpi_gpe_irq_cleanup; + ipmi_irq_finish_setup(io); + dev_info(io->dev, "Using ACPI GPE %d\n", io->irq); + return 0; + } +} + +/* + * Defined at + * http://h21007.www2.hp.com/portal/download/files/unprot/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_spmi(struct SPMITable *spmi) +{ + struct si_sm_io io; + + if (spmi->IPMIlegacy != 1) { + pr_info(PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy); + return -ENODEV; + } + + memset(&io, 0, sizeof(io)); + io.addr_source = SI_SPMI; + pr_info(PFX "probing via SPMI\n"); + + /* Figure out the interface type. */ + switch (spmi->InterfaceType) { + case 1: /* KCS */ + io.si_type = SI_KCS; + break; + case 2: /* SMIC */ + io.si_type = SI_SMIC; + break; + case 3: /* BT */ + io.si_type = SI_BT; + break; + case 4: /* SSIF, just ignore */ + return -EIO; + default: + pr_info(PFX "Unknown ACPI/SPMI SI type %d\n", + spmi->InterfaceType); + return -EIO; + } + + if (spmi->InterruptType & 1) { + /* We've got a GPE interrupt. */ + io.irq = spmi->GPE; + io.irq_setup = acpi_gpe_irq_setup; + } else if (spmi->InterruptType & 2) { + /* We've got an APIC/SAPIC interrupt. */ + io.irq = spmi->GlobalSystemInterrupt; + io.irq_setup = ipmi_std_irq_setup; + } else { + /* Use the default interrupt setting. */ + io.irq = 0; + io.irq_setup = NULL; + } + + if (spmi->addr.bit_width) { + /* A (hopefully) properly formed register bit width. */ + io.regspacing = spmi->addr.bit_width / 8; + } else { + io.regspacing = DEFAULT_REGSPACING; + } + io.regsize = io.regspacing; + io.regshift = spmi->addr.bit_offset; + + if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + io.addr_type = IPMI_MEM_ADDR_SPACE; + } else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) { + io.addr_type = IPMI_IO_ADDR_SPACE; + } else { + pr_warn(PFX "Unknown ACPI I/O Address type\n"); + return -EIO; + } + io.addr_data = spmi->addr.address; + + pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n", + (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", + io.addr_data, io.regsize, io.regspacing, io.irq); + + return ipmi_si_add_smi(&io); +} + +static void spmi_find_bmc(void) +{ + acpi_status status; + struct SPMITable *spmi; + int i; + + if (acpi_disabled) + return; + + if (acpi_failure) + return; + + for (i = 0; ; i++) { + status = acpi_get_table(ACPI_SIG_SPMI, i+1, + (struct acpi_table_header **)&spmi); + if (status != AE_OK) + return; + + try_init_spmi(spmi); + } +} +#endif + +static struct resource * +ipmi_get_info_from_resources(struct platform_device *pdev, + struct si_sm_io *io) +{ + struct resource *res, *res_second; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res) { + io->addr_type = IPMI_IO_ADDR_SPACE; + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + io->addr_type = IPMI_MEM_ADDR_SPACE; + } + if (!res) { + dev_err(&pdev->dev, "no I/O or memory address\n"); + return NULL; + } + io->addr_data = res->start; + + io->regspacing = DEFAULT_REGSPACING; + res_second = platform_get_resource(pdev, + (io->addr_type == IPMI_IO_ADDR_SPACE) ? + IORESOURCE_IO : IORESOURCE_MEM, + 1); + if (res_second) { + if (res_second->start > io->addr_data) + io->regspacing = res_second->start - io->addr_data; + } + io->regsize = DEFAULT_REGSIZE; + io->regshift = 0; + + return res; +} + +static int platform_ipmi_probe(struct platform_device *pdev) +{ + struct si_sm_io io; + u8 type, slave_addr, addr_source; + int rv; + + rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source); + if (rv) + addr_source = SI_PLATFORM; + if (addr_source >= SI_LAST) + return -EINVAL; + + if (addr_source == SI_SMBIOS) { + if (!si_trydmi) + return -ENODEV; + } else { + if (!si_tryplatform) + return -ENODEV; + } + + rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); + if (rv) + return -ENODEV; + + memset(&io, 0, sizeof(io)); + io.addr_source = addr_source; + dev_info(&pdev->dev, PFX "probing via %s\n", + ipmi_addr_src_to_str(addr_source)); + + switch (type) { + case SI_KCS: + case SI_SMIC: + case SI_BT: + io.si_type = type; + break; + default: + dev_err(&pdev->dev, "ipmi-type property is invalid\n"); + return -EINVAL; + } + + if (!ipmi_get_info_from_resources(pdev, &io)) + return -EINVAL; + + rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); + if (rv) { + dev_warn(&pdev->dev, "device has no slave-addr property\n"); + io.slave_addr = 0x20; + } else { + io.slave_addr = slave_addr; + } + + io.irq = platform_get_irq(pdev, 0); + if (io.irq > 0) + io.irq_setup = ipmi_std_irq_setup; + else + io.irq = 0; + + io.dev = &pdev->dev; + + pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n", + (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", + io.addr_data, io.regsize, io.regspacing, io.irq); + + ipmi_si_add_smi(&io); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id of_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 }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_ipmi_match); + +static int of_ipmi_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct si_sm_io io; + struct resource resource; + const __be32 *regsize, *regspacing, *regshift; + struct device_node *np = pdev->dev.of_node; + int ret; + int proplen; + + if (!si_tryopenfirmware) + return -ENODEV; + + dev_info(&pdev->dev, "probing via device tree\n"); + + match = of_match_device(of_ipmi_match, &pdev->dev); + if (!match) + return -ENODEV; + + if (!of_device_is_available(np)) + return -EINVAL; + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_warn(&pdev->dev, PFX "invalid address from OF\n"); + return ret; + } + + regsize = of_get_property(np, "reg-size", &proplen); + if (regsize && proplen != 4) { + dev_warn(&pdev->dev, PFX "invalid regsize from OF\n"); + return -EINVAL; + } + + regspacing = of_get_property(np, "reg-spacing", &proplen); + if (regspacing && proplen != 4) { + dev_warn(&pdev->dev, PFX "invalid regspacing from OF\n"); + return -EINVAL; + } + + regshift = of_get_property(np, "reg-shift", &proplen); + if (regshift && proplen != 4) { + dev_warn(&pdev->dev, PFX "invalid regshift from OF\n"); + return -EINVAL; + } + + memset(&io, 0, sizeof(io)); + io.si_type = (enum si_type) match->data; + io.addr_source = SI_DEVICETREE; + io.irq_setup = ipmi_std_irq_setup; + + if (resource.flags & IORESOURCE_IO) + io.addr_type = IPMI_IO_ADDR_SPACE; + else + io.addr_type = IPMI_MEM_ADDR_SPACE; + + io.addr_data = resource.start; + + io.regsize = regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE; + io.regspacing = regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING; + io.regshift = regshift ? be32_to_cpup(regshift) : 0; + + io.irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + io.dev = &pdev->dev; + + dev_dbg(&pdev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n", + io.addr_data, io.regsize, io.regspacing, io.irq); + + return ipmi_si_add_smi(&io); +} +#else +#define of_ipmi_match NULL +static int of_ipmi_probe(struct platform_device *dev) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_ACPI +static int find_slave_address(struct si_sm_io *io, int slave_addr) +{ +#ifdef CONFIG_IPMI_DMI_DECODE + if (!slave_addr) { + u32 flags = IORESOURCE_IO; + + if (io->addr_type == IPMI_MEM_ADDR_SPACE) + flags = IORESOURCE_MEM; + + slave_addr = ipmi_dmi_get_slave_addr(io->si_type, flags, + io->addr_data); + } +#endif + + return slave_addr; +} + +static int acpi_ipmi_probe(struct platform_device *pdev) +{ + struct si_sm_io io; + acpi_handle handle; + acpi_status status; + unsigned long long tmp; + struct resource *res; + int rv = -EINVAL; + + if (!si_tryacpi) + return -ENODEV; + + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + + memset(&io, 0, sizeof(io)); + io.addr_source = SI_ACPI; + dev_info(&pdev->dev, PFX "probing via ACPI\n"); + + io.addr_info.acpi_info.acpi_handle = handle; + + /* _IFT tells us the interface type: KCS, BT, etc */ + status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp); + if (ACPI_FAILURE(status)) { + dev_err(&pdev->dev, + "Could not find ACPI IPMI interface type\n"); + goto err_free; + } + + switch (tmp) { + case 1: + io.si_type = SI_KCS; + break; + case 2: + io.si_type = SI_SMIC; + break; + case 3: + io.si_type = SI_BT; + break; + case 4: /* SSIF, just ignore */ + rv = -ENODEV; + goto err_free; + default: + dev_info(&pdev->dev, "unknown IPMI type %lld\n", tmp); + goto err_free; + } + + res = ipmi_get_info_from_resources(pdev, &io); + if (!res) { + rv = -EINVAL; + goto err_free; + } + + /* If _GPE exists, use it; otherwise use standard interrupts */ + status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); + if (ACPI_SUCCESS(status)) { + io.irq = tmp; + io.irq_setup = acpi_gpe_irq_setup; + } else { + int irq = platform_get_irq(pdev, 0); + + if (irq > 0) { + io.irq = irq; + io.irq_setup = ipmi_std_irq_setup; + } + } + + io.slave_addr = find_slave_address(&io, io.slave_addr); + + io.dev = &pdev->dev; + + dev_info(io.dev, "%pR regsize %d spacing %d irq %d\n", + res, io.regsize, io.regspacing, io.irq); + + return ipmi_si_add_smi(&io); + +err_free: + return rv; +} + +static const struct acpi_device_id acpi_ipmi_match[] = { + { "IPI0001", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, acpi_ipmi_match); +#else +static int acpi_ipmi_probe(struct platform_device *dev) +{ + return -ENODEV; +} +#endif + +static int ipmi_probe(struct platform_device *pdev) +{ + if (pdev->dev.of_node && of_ipmi_probe(pdev) == 0) + return 0; + + if (acpi_ipmi_probe(pdev) == 0) + return 0; + + return platform_ipmi_probe(pdev); +} + +static int ipmi_remove(struct platform_device *pdev) +{ + return ipmi_si_remove_by_dev(&pdev->dev); +} + +struct platform_driver ipmi_platform_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = of_ipmi_match, + .acpi_match_table = ACPI_PTR(acpi_ipmi_match), + }, + .probe = ipmi_probe, + .remove = ipmi_remove, +}; + +void ipmi_si_platform_init(void) +{ + int rv = platform_driver_register(&ipmi_platform_driver); + if (rv) + pr_err(PFX "Unable to register driver: %d\n", rv); + +#ifdef CONFIG_ACPI + if (si_tryacpi) + spmi_find_bmc(); +#endif + +} + +void ipmi_si_platform_shutdown(void) +{ + platform_driver_unregister(&ipmi_platform_driver); +} diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c new file mode 100644 index 0000000000000000000000000000000000000000..e5ce174fbeeba675422ff60065af9bb17c3c8319 --- /dev/null +++ b/drivers/char/ipmi/ipmi_si_port_io.c @@ -0,0 +1,112 @@ + +#include +#include "ipmi_si.h" + +static unsigned char port_inb(const struct si_sm_io *io, unsigned int offset) +{ + unsigned int addr = io->addr_data; + + return inb(addr + (offset * io->regspacing)); +} + +static void port_outb(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + unsigned int addr = io->addr_data; + + outb(b, addr + (offset * io->regspacing)); +} + +static unsigned char port_inw(const struct si_sm_io *io, unsigned int offset) +{ + unsigned int addr = io->addr_data; + + return (inw(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; +} + +static void port_outw(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + unsigned int addr = io->addr_data; + + outw(b << io->regshift, addr + (offset * io->regspacing)); +} + +static unsigned char port_inl(const struct si_sm_io *io, unsigned int offset) +{ + unsigned int addr = io->addr_data; + + return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff; +} + +static void port_outl(const struct si_sm_io *io, unsigned int offset, + unsigned char b) +{ + unsigned int addr = io->addr_data; + + outl(b << io->regshift, addr+(offset * io->regspacing)); +} + +static void port_cleanup(struct si_sm_io *io) +{ + unsigned int addr = io->addr_data; + int idx; + + if (addr) { + for (idx = 0; idx < io->io_size; idx++) + release_region(addr + idx * io->regspacing, + io->regsize); + } +} + +int ipmi_si_port_setup(struct si_sm_io *io) +{ + unsigned int addr = io->addr_data; + int idx; + + if (!addr) + return -ENODEV; + + io->io_cleanup = port_cleanup; + + /* + * Figure out the actual inb/inw/inl/etc routine to use based + * upon the register size. + */ + switch (io->regsize) { + case 1: + io->inputb = port_inb; + io->outputb = port_outb; + break; + case 2: + io->inputb = port_inw; + io->outputb = port_outw; + break; + case 4: + io->inputb = port_inl; + io->outputb = port_outl; + break; + default: + dev_warn(io->dev, "Invalid register size: %d\n", + io->regsize); + return -EINVAL; + } + + /* + * Some BIOSes reserve disjoint I/O regions in their ACPI + * tables. This causes problems when trying to register the + * entire I/O region. Therefore we must register each I/O + * port separately. + */ + for (idx = 0; idx < io->io_size; idx++) { + if (request_region(addr + idx * io->regspacing, + io->regsize, DEVICE_NAME) == NULL) { + /* Undo allocations */ + while (idx--) + release_region(addr + idx * io->regspacing, + io->regsize); + return -EIO; + } + } + return 0; +} diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h index a705027c0493f2241a9b917f6ac9f06849eeb9db..aa8d88ab4433e9e6c7d6eacba821794457d9bc04 100644 --- a/drivers/char/ipmi/ipmi_si_sm.h +++ b/drivers/char/ipmi/ipmi_si_sm.h @@ -34,12 +34,18 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + /* * This is defined by the state machines themselves, it is an opaque * data type for them to use. */ struct si_sm_data; +enum si_type { + SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT +}; + /* * The structure for doing I/O in the state machine. The state * machine doesn't have the actual I/O routines, they are done through @@ -61,6 +67,23 @@ struct si_sm_io { int regshift; int addr_type; long addr_data; + enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ + void (*addr_source_cleanup)(struct si_sm_io *io); + void *addr_source_data; + union ipmi_smi_info_union addr_info; + + int (*io_setup)(struct si_sm_io *info); + void (*io_cleanup)(struct si_sm_io *info); + unsigned int io_size; + + int irq; + int (*irq_setup)(struct si_sm_io *io); + void *irq_handler_data; + void (*irq_cleanup)(struct si_sm_io *io); + + u8 slave_addr; + enum si_type si_type; + struct device *dev; }; /* Results of SMI events. */ diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 0aea3bcb615848f65d9eef06806cbacf2ae92ed3..466b3a1c0adfd3fe419ee57338d279d18436eb7e 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -53,6 +53,7 @@ #include #include #include +#include "ipmi_si_sm.h" #include "ipmi_dmi.h" #define PFX "ipmi_ssif: " @@ -267,9 +268,6 @@ struct ssif_info { unsigned char *i2c_data; unsigned int i2c_size; - /* From the device id response. */ - struct ipmi_device_id device_id; - struct timer_list retry_timer; int retries_left; @@ -1176,6 +1174,61 @@ MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the default scan of static DEFINE_MUTEX(ssif_infos_mutex); static LIST_HEAD(ssif_infos); +#define IPMI_SSIF_ATTR(name) \ +static ssize_t ipmi_##name##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct ssif_info *ssif_info = dev_get_drvdata(dev); \ + \ + return snprintf(buf, 10, "%u\n", ssif_get_stat(ssif_info, name));\ +} \ +static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL) + +static ssize_t ipmi_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, 10, "ssif\n"); +} +static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL); + +IPMI_SSIF_ATTR(sent_messages); +IPMI_SSIF_ATTR(sent_messages_parts); +IPMI_SSIF_ATTR(send_retries); +IPMI_SSIF_ATTR(send_errors); +IPMI_SSIF_ATTR(received_messages); +IPMI_SSIF_ATTR(received_message_parts); +IPMI_SSIF_ATTR(receive_retries); +IPMI_SSIF_ATTR(receive_errors); +IPMI_SSIF_ATTR(flag_fetches); +IPMI_SSIF_ATTR(hosed); +IPMI_SSIF_ATTR(events); +IPMI_SSIF_ATTR(watchdog_pretimeouts); +IPMI_SSIF_ATTR(alerts); + +static struct attribute *ipmi_ssif_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_sent_messages.attr, + &dev_attr_sent_messages_parts.attr, + &dev_attr_send_retries.attr, + &dev_attr_send_errors.attr, + &dev_attr_received_messages.attr, + &dev_attr_received_message_parts.attr, + &dev_attr_receive_retries.attr, + &dev_attr_receive_errors.attr, + &dev_attr_flag_fetches.attr, + &dev_attr_hosed.attr, + &dev_attr_events.attr, + &dev_attr_watchdog_pretimeouts.attr, + &dev_attr_alerts.attr, + NULL +}; + +static const struct attribute_group ipmi_ssif_dev_attr_group = { + .attrs = ipmi_ssif_dev_attrs, +}; + static int ssif_remove(struct i2c_client *client) { struct ssif_info *ssif_info = i2c_get_clientdata(client); @@ -1196,6 +1249,9 @@ static int ssif_remove(struct i2c_client *client) } ssif_info->intf = NULL; + device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); + dev_set_drvdata(&ssif_info->client->dev, NULL); + /* make sure the driver is not looking for flags any more. */ while (ssif_info->ssif_state != SSIF_NORMAL) schedule_timeout(1); @@ -1289,6 +1345,7 @@ static int ssif_detect(struct i2c_client *client, struct i2c_board_info *info) return rv; } +#ifdef CONFIG_IPMI_PROC_INTERFACE static int smi_type_proc_show(struct seq_file *m, void *v) { seq_puts(m, "ssif\n"); @@ -1352,6 +1409,7 @@ static const struct file_operations smi_stats_proc_ops = { .llseek = seq_lseek, .release = single_release, }; +#endif static int strcmp_nospace(char *s1, char *s2) { @@ -1425,7 +1483,7 @@ static int find_slave_address(struct i2c_client *client, int slave_addr) #ifdef CONFIG_IPMI_DMI_DECODE if (!slave_addr) slave_addr = ipmi_dmi_get_slave_addr( - IPMI_DMI_TYPE_SSIF, + SI_TYPE_INVALID, i2c_adapter_id(client->adapter), client->addr); #endif @@ -1481,20 +1539,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ipmi_addr_src_to_str(ssif_info->addr_source), client->addr, client->adapter->name, slave_addr); - /* - * 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; - rv = do_cmd(client, 2, msg, &len, resp); - if (rv) - goto out; - - rv = ipmi_demangle_device_id(resp, len, &ssif_info->device_id); - if (rv) - goto out; - ssif_info->client = client; i2c_set_clientdata(client, ssif_info); @@ -1682,16 +1726,26 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) } } + dev_set_drvdata(&ssif_info->client->dev, ssif_info); + rv = device_add_group(&ssif_info->client->dev, + &ipmi_ssif_dev_attr_group); + if (rv) { + dev_err(&ssif_info->client->dev, + "Unable to add device attributes: error %d\n", + rv); + goto out; + } + rv = ipmi_register_smi(&ssif_info->handlers, ssif_info, - &ssif_info->device_id, &ssif_info->client->dev, slave_addr); if (rv) { pr_err(PFX "Unable to register device: error %d\n", rv); - goto out; + goto out_remove_attr; } +#ifdef CONFIG_IPMI_PROC_INTERFACE rv = ipmi_smi_add_proc_entry(ssif_info->intf, "type", &smi_type_proc_ops, ssif_info); @@ -1707,6 +1761,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) pr_err(PFX "Unable to create proc entry: %d\n", rv); goto out_err_unreg; } +#endif out: if (rv) { @@ -1725,8 +1780,14 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) kfree(resp); return rv; - out_err_unreg: +#ifdef CONFIG_IPMI_PROC_INTERFACE +out_err_unreg: ipmi_unregister_smi(ssif_info->intf); +#endif + +out_remove_attr: + device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); + dev_set_drvdata(&ssif_info->client->dev, NULL); goto out; } @@ -1953,20 +2014,13 @@ static void spmi_find_bmc(void) { } #ifdef CONFIG_DMI static int dmi_ipmi_probe(struct platform_device *pdev) { - u8 type, slave_addr = 0; + u8 slave_addr = 0; u16 i2c_addr; int rv; if (!ssif_trydmi) return -ENODEV; - rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); - if (rv) - return -ENODEV; - - if (type != IPMI_DMI_TYPE_SSIF) - return -ENODEV; - rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr); if (rv) { dev_warn(&pdev->dev, PFX "No i2c-addr property\n"); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 3d832d0362a4fc6150f8f03886ffce552104a3af..76b270678b50f477cc3cb9899938c823abe50401 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1009,9 +1009,14 @@ static void ipmi_register_watchdog(int ipmi_intf) goto out; } - ipmi_get_version(watchdog_user, - &ipmi_version_major, - &ipmi_version_minor); + rv = ipmi_get_version(watchdog_user, + &ipmi_version_major, + &ipmi_version_minor); + if (rv) { + pr_warn(PFX "Unable to get IPMI version, assuming 1.0\n"); + ipmi_version_major = 1; + ipmi_version_minor = 0; + } rv = misc_register(&ipmi_wdog_miscdev); if (rv < 0) { diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index f1045b2c6a00a0543da1892eebff3f120c32a679..f4ffacf4fe9d0d97314fed76f896a1fdc394fe74 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -113,9 +113,9 @@ int ipmi_create_user(unsigned int if_num, int ipmi_destroy_user(ipmi_user_t user); /* Get the IPMI version of the BMC we are talking to. */ -void ipmi_get_version(ipmi_user_t user, - unsigned char *major, - unsigned char *minor); +int ipmi_get_version(ipmi_user_t user, + unsigned char *major, + unsigned char *minor); /* Set and get the slave address and LUN that we will use for our source messages. Note that this affects the interface, not just @@ -277,7 +277,7 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len); */ enum ipmi_addr_src { SI_INVALID = 0, SI_HOTMOD, SI_HARDCODED, SI_SPMI, SI_ACPI, SI_SMBIOS, - SI_PCI, SI_DEVICETREE, SI_LAST + SI_PCI, SI_DEVICETREE, SI_PLATFORM, SI_LAST }; const char *ipmi_addr_src_to_str(enum ipmi_addr_src src); diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index f8cea14485ddde6f5d73bea2817b9bfa8913dc10..5be51281e14d21b4a88d4cd0947c13ca33015cd8 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -162,27 +162,27 @@ struct ipmi_device_id { #define ipmi_version_major(v) ((v)->ipmi_version & 0xf) #define ipmi_version_minor(v) ((v)->ipmi_version >> 4) -/* Take a pointer to a raw data buffer and a length and extract device - id information from it. The first byte of data must point to the - netfn << 2, the data should be of the format: - netfn << 2, cmd, completion code, data - as normally comes from a device interface. */ -static inline int ipmi_demangle_device_id(const unsigned char *data, +/* Take a pointer to an IPMI response and extract device id information from + * it. @netfn is in the IPMI_NETFN_ format, so may need to be shifted from + * a SI response. + */ +static inline int ipmi_demangle_device_id(uint8_t netfn, uint8_t cmd, + const unsigned char *data, unsigned int data_len, struct ipmi_device_id *id) { - if (data_len < 9) + if (data_len < 7) return -EINVAL; - if (data[0] != IPMI_NETFN_APP_RESPONSE << 2 || - data[1] != IPMI_GET_DEVICE_ID_CMD) + if (netfn != IPMI_NETFN_APP_RESPONSE || cmd != IPMI_GET_DEVICE_ID_CMD) /* Strange, didn't get the response we expected. */ return -EINVAL; - if (data[2] != 0) + if (data[0] != 0) /* That's odd, it shouldn't be able to fail. */ return -EINVAL; - data += 3; - data_len -= 3; + data++; + data_len--; + id->device_id = data[0]; id->device_revision = data[1]; id->firmware_revision_1 = data[2]; @@ -214,7 +214,6 @@ static inline int ipmi_demangle_device_id(const unsigned char *data, call. */ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, void *send_info, - struct ipmi_device_id *device_id, struct device *dev, unsigned char slave_addr); @@ -242,11 +241,13 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg) msg->done(msg); } +#ifdef CONFIG_IPMI_PROC_INTERFACE /* Allow the lower layer to add things to the proc filesystem directory for this interface. Note that the entry will automatically be dstroyed when the interface is destroyed. */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, const struct file_operations *proc_ops, void *data); +#endif #endif /* __LINUX_IPMI_SMI_H */