1
0
Fork 0

IPMI updates for 4.3

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iEYEABECAAYFAlXoqLgACgkQIXnXXONXEReNCwCghq2EtqGYTvLhupB4bJFCdtA5
 ZJMAoIJ+1WIbvGJmBZ/RxehiCl/FjxTl
 =+N3y
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-4.3' of git://git.code.sf.net/p/openipmi/linux-ipmi

Pull IPMI updates from Corey Minyard:
 "Most of these have been sitting in linux-next for more than a release,
  particularly commit 0fbcf4af7c ("ipmi: Convert the IPMI SI ACPI
  handling to a platform device") which is probably the most complex
  patch.

  That is also the one that changes drivers/acpi/acpi_pnp.c.  The change
  in that file is only removing IPMI from a "special platform devices"
  list, since I convert it to the standard PNP interface.  I posted this
  one to the ACPI list twice and got no response, and it seems to work
  well in my testing, so I'm hoping it's good.

  Hidehiro Kawai posted a set of changes that improves the panic time
  handling in the IPMI driver.

  The rest of the changes are minor bug fixes or cleanups and some
  documentation"

* tag 'for-linus-4.3' of git://git.code.sf.net/p/openipmi/linux-ipmi:
  ipmi:ssif: Add a module parm to specify that SMBus alerts don't work
  ipmi: add of_device_id in MODULE_DEVICE_TABLE
  ipmi: Compensate for BMCs that wont set the irq enable bit
  ipmi: Don't call receive handler in the panic context
  ipmi: Avoid touching possible corrupted lists in the panic context
  ipmi: Don't flush messages in sender() in run-to-completion mode
  ipmi: Factor out message flushing procedure
  ipmi: Remove unneeded set_run_to_completion call
  ipmi: Make some data const that was only read
  ipmi: constify SSIF ACPI device ids
  ipmi: Delete an unnecessary check before the function call "cleanup_one_si"
  char:ipmi - Change 1 to true for bool type variables during initialization.
  impi:Remove unneeded setting of module owner to THIS_MODULE in the platform structure, powernv_ipmi_driver
  ipmi: Add a comment in how messages are delivered from the lower layer
  ipmi/powernv: Fix potential invalid pointer dereference
  ipmi: Convert the IPMI SI ACPI handling to a platform device
  ipmi: Add device tree bindings information
hifive-unleashed-5.1
Linus Torvalds 2015-09-08 18:19:17 -07:00
commit a794b4f329
11 changed files with 438 additions and 281 deletions

View File

@ -0,0 +1,25 @@
IPMI device
Required properties:
- compatible: should be one of ipmi-kcs, ipmi-smic, or ipmi-bt
- device_type: should be ipmi
- reg: Address and length of the register set for the device
Optional properties:
- interrupts: The interrupt for the device. Without this the interface
is polled.
- reg-size - The size of the register. Defaults to 1
- reg-spacing - The number of bytes between register starts. Defaults to 1
- reg-shift - The amount to shift the registers to the right to get the data
into bit zero.
Example:
smic@fff3a000 {
compatible = "ipmi-smic";
device_type = "ipmi";
reg = <0xfff3a000 0x1000>;
interrupts = <0 24 4>;
reg-size = <4>;
reg-spacing = <4>;
};

View File

@ -19,8 +19,6 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = {
{"PNP0600"}, /* Generic ESDI/IDE/ATA compatible hard disk controller */
/* floppy */
{"PNP0700"},
/* ipmi_si */
{"IPI0001"},
/* tpm_inf_pnp */
{"IFX0101"}, /* Infineon TPMs */
{"IFX0102"}, /* Infineon TPMs */

View File

@ -694,7 +694,7 @@ static int bt_size(void)
return sizeof(struct si_sm_data);
}
struct si_sm_handlers bt_smi_handlers = {
const struct si_sm_handlers bt_smi_handlers = {
.init_data = bt_init_data,
.start_transaction = bt_start_transaction,
.get_result = bt_get_result,

View File

@ -540,7 +540,7 @@ static void kcs_cleanup(struct si_sm_data *kcs)
{
}
struct si_sm_handlers kcs_smi_handlers = {
const struct si_sm_handlers kcs_smi_handlers = {
.init_data = init_kcs_data,
.start_transaction = start_kcs_transaction,
.get_result = get_kcs_result,

View File

@ -342,7 +342,7 @@ struct ipmi_smi {
* an umpreemptible region to use this. You must fetch the
* value into a local variable and make sure it is not NULL.
*/
struct ipmi_smi_handlers *handlers;
const struct ipmi_smi_handlers *handlers;
void *send_info;
#ifdef CONFIG_PROC_FS
@ -744,7 +744,13 @@ static void deliver_response(struct ipmi_recv_msg *msg)
ipmi_inc_stat(intf, unhandled_local_responses);
}
ipmi_free_recv_msg(msg);
} else {
} else if (!oops_in_progress) {
/*
* If we are running in the panic context, calling the
* receive handler doesn't much meaning and has a deadlock
* risk. At this moment, simply skip it in that case.
*/
ipmi_user_t user = msg->user;
user->handler->ipmi_recv_hndl(msg, user->handler_data);
}
@ -1015,7 +1021,7 @@ int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data)
{
int rv = 0;
ipmi_smi_t intf;
struct ipmi_smi_handlers *handlers;
const struct ipmi_smi_handlers *handlers;
mutex_lock(&ipmi_interfaces_mutex);
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
@ -1501,7 +1507,7 @@ static struct ipmi_smi_msg *smi_add_send_msg(ipmi_smi_t intf,
}
static void smi_send(ipmi_smi_t intf, struct ipmi_smi_handlers *handlers,
static void smi_send(ipmi_smi_t intf, const struct ipmi_smi_handlers *handlers,
struct ipmi_smi_msg *smi_msg, int priority)
{
int run_to_completion = intf->run_to_completion;
@ -2747,7 +2753,7 @@ void ipmi_poll_interface(ipmi_user_t user)
}
EXPORT_SYMBOL(ipmi_poll_interface);
int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
void *send_info,
struct ipmi_device_id *device_id,
struct device *si_dev,
@ -3959,6 +3965,10 @@ free_msg:
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
/*
* We can get an asynchronous event or receive message in addition
* to commands we send.
*/
if (msg == intf->curr_msg)
intf->curr_msg = NULL;
if (!run_to_completion)
@ -4015,7 +4025,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
unsigned int *waiting_msgs)
{
struct ipmi_recv_msg *msg;
struct ipmi_smi_handlers *handlers;
const struct ipmi_smi_handlers *handlers;
if (intf->in_shutdown)
return;
@ -4082,7 +4092,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
ipmi_inc_stat(intf,
retransmitted_ipmb_commands);
smi_send(intf, intf->handlers, smi_msg, 0);
smi_send(intf, handlers, smi_msg, 0);
} else
ipmi_free_smi_msg(smi_msg);
@ -4291,6 +4301,9 @@ static void ipmi_panic_request_and_wait(ipmi_smi_t intf,
0, 1); /* Don't retry, and don't wait. */
if (rv)
atomic_sub(2, &panic_done_count);
else if (intf->handlers->flush_messages)
intf->handlers->flush_messages(intf->send_info);
while (atomic_read(&panic_done_count) != 0)
ipmi_poll(intf);
}
@ -4364,9 +4377,7 @@ static void send_panic_events(char *str)
/* Interface is not ready. */
continue;
intf->run_to_completion = 1;
/* Send the event announcing the panic. */
intf->handlers->set_run_to_completion(intf->send_info, 1);
ipmi_panic_request_and_wait(intf, &addr, &msg);
}
@ -4506,6 +4517,23 @@ static int panic_event(struct notifier_block *this,
/* Interface is not ready. */
continue;
/*
* If we were interrupted while locking xmit_msgs_lock or
* waiting_rcv_msgs_lock, the corresponding list may be
* corrupted. In this case, drop items on the list for
* the safety.
*/
if (!spin_trylock(&intf->xmit_msgs_lock)) {
INIT_LIST_HEAD(&intf->xmit_msgs);
INIT_LIST_HEAD(&intf->hp_xmit_msgs);
} else
spin_unlock(&intf->xmit_msgs_lock);
if (!spin_trylock(&intf->waiting_rcv_msgs_lock))
INIT_LIST_HEAD(&intf->waiting_rcv_msgs);
else
spin_unlock(&intf->waiting_rcv_msgs_lock);
intf->run_to_completion = 1;
intf->handlers->set_run_to_completion(intf->send_info, 1);
}

View File

@ -143,8 +143,15 @@ static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi)
pr_devel("%s: -> %d (size %lld)\n", __func__,
rc, rc == 0 ? size : 0);
if (rc) {
/* If came via the poll, and response was not yet ready */
if (rc == OPAL_EMPTY) {
spin_unlock_irqrestore(&smi->msg_lock, flags);
return 0;
}
smi->cur_msg = NULL;
spin_unlock_irqrestore(&smi->msg_lock, flags);
ipmi_free_smi_msg(msg);
send_error_reply(smi, msg, IPMI_ERR_UNSPECIFIED);
return 0;
}
@ -300,7 +307,6 @@ static const struct of_device_id ipmi_powernv_match[] = {
static struct platform_driver powernv_ipmi_driver = {
.driver = {
.name = "ipmi-powernv",
.owner = THIS_MODULE,
.of_match_table = ipmi_powernv_match,
},
.probe = ipmi_powernv_probe,

View File

@ -64,7 +64,6 @@
#include <linux/dmi.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/pnp.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
@ -164,7 +163,7 @@ struct smi_info {
int intf_num;
ipmi_smi_t intf;
struct si_sm_data *si_sm;
struct si_sm_handlers *handlers;
const struct si_sm_handlers *handlers;
enum si_type si_type;
spinlock_t si_lock;
struct ipmi_smi_msg *waiting_msg;
@ -263,9 +262,21 @@ struct smi_info {
bool supports_event_msg_buff;
/*
* Can we clear the global enables receive irq bit?
* Can we disable interrupts the global enables receive irq
* bit? There are currently two forms of brokenness, some
* systems cannot disable the bit (which is technically within
* the spec but a bad idea) and some systems have the bit
* forced to zero even though interrupts work (which is
* clearly outside the spec). The next bool tells which form
* of brokenness is present.
*/
bool cannot_clear_recv_irq_bit;
bool cannot_disable_irq;
/*
* Some systems are broken and cannot set the irq enable
* bit, even if they support interrupts.
*/
bool irq_enable_broken;
/*
* Did we get an attention that we did not handle?
@ -309,9 +320,6 @@ static int num_force_kipmid;
#ifdef CONFIG_PCI
static bool pci_registered;
#endif
#ifdef CONFIG_ACPI
static bool pnp_registered;
#endif
#ifdef CONFIG_PARISC
static bool parisc_registered;
#endif
@ -558,13 +566,14 @@ 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) ||
smi_info->cannot_clear_recv_irq_bit)
if (((smi_info->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->irq && !smi_info->interrupt_disabled &&
!smi_info->irq_enable_broken)
enables |= IPMI_BMC_EVT_MSG_INTR;
*irq_on = enables & (IPMI_BMC_EVT_MSG_INTR | IPMI_BMC_RCV_MSG_INTR);
@ -928,33 +937,36 @@ static void check_start_timer_thread(struct smi_info *smi_info)
}
}
static void flush_messages(void *send_info)
{
struct smi_info *smi_info = send_info;
enum si_sm_result result;
/*
* Currently, this function is called only in run-to-completion
* mode. This means we are single-threaded, no need for locks.
*/
result = smi_event_handler(smi_info, 0);
while (result != SI_SM_IDLE) {
udelay(SI_SHORT_TIMEOUT_USEC);
result = smi_event_handler(smi_info, SI_SHORT_TIMEOUT_USEC);
}
}
static void sender(void *send_info,
struct ipmi_smi_msg *msg)
{
struct smi_info *smi_info = send_info;
enum si_sm_result result;
unsigned long flags;
debug_timestamp("Enqueue");
if (smi_info->run_to_completion) {
/*
* If we are running to completion, start it and run
* transactions until everything is clear.
* If we are running to completion, start it. Upper
* layer will call flush_messages to clear it out.
*/
smi_info->waiting_msg = msg;
/*
* Run to completion means we are single-threaded, no
* need for locks.
*/
result = smi_event_handler(smi_info, 0);
while (result != SI_SM_IDLE) {
udelay(SI_SHORT_TIMEOUT_USEC);
result = smi_event_handler(smi_info,
SI_SHORT_TIMEOUT_USEC);
}
return;
}
@ -975,17 +987,10 @@ static void sender(void *send_info,
static void set_run_to_completion(void *send_info, bool i_run_to_completion)
{
struct smi_info *smi_info = send_info;
enum si_sm_result result;
smi_info->run_to_completion = i_run_to_completion;
if (i_run_to_completion) {
result = smi_event_handler(smi_info, 0);
while (result != SI_SM_IDLE) {
udelay(SI_SHORT_TIMEOUT_USEC);
result = smi_event_handler(smi_info,
SI_SHORT_TIMEOUT_USEC);
}
}
if (i_run_to_completion)
flush_messages(smi_info);
}
/*
@ -1258,7 +1263,7 @@ static void set_maintenance_mode(void *send_info, bool enable)
atomic_set(&smi_info->req_events, 0);
}
static struct ipmi_smi_handlers handlers = {
static const struct ipmi_smi_handlers handlers = {
.owner = THIS_MODULE,
.start_processing = smi_start_processing,
.get_smi_info = get_smi_info,
@ -1267,6 +1272,7 @@ static struct ipmi_smi_handlers handlers = {
.set_need_watch = set_need_watch,
.set_maintenance_mode = set_maintenance_mode,
.set_run_to_completion = set_run_to_completion,
.flush_messages = flush_messages,
.poll = poll,
};
@ -1283,14 +1289,14 @@ static int smi_num; /* Used to sequence the SMIs */
#define DEFAULT_REGSIZE 1
#ifdef CONFIG_ACPI
static bool si_tryacpi = 1;
static bool si_tryacpi = true;
#endif
#ifdef CONFIG_DMI
static bool si_trydmi = 1;
static bool si_trydmi = true;
#endif
static bool si_tryplatform = 1;
static bool si_tryplatform = true;
#ifdef CONFIG_PCI
static bool si_trypci = 1;
static bool si_trypci = true;
#endif
static bool si_trydefaults = IS_ENABLED(CONFIG_IPMI_SI_PROBE_DEFAULTS);
static char *si_type[SI_MAX_PARMS];
@ -1446,14 +1452,14 @@ static int std_irq_setup(struct smi_info *info)
return rv;
}
static unsigned char port_inb(struct si_sm_io *io, unsigned int offset)
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(struct si_sm_io *io, unsigned int offset,
static void port_outb(const struct si_sm_io *io, unsigned int offset,
unsigned char b)
{
unsigned int addr = io->addr_data;
@ -1461,14 +1467,14 @@ static void port_outb(struct si_sm_io *io, unsigned int offset,
outb(b, addr + (offset * io->regspacing));
}
static unsigned char port_inw(struct si_sm_io *io, unsigned int offset)
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(struct si_sm_io *io, unsigned int offset,
static void port_outw(const struct si_sm_io *io, unsigned int offset,
unsigned char b)
{
unsigned int addr = io->addr_data;
@ -1476,14 +1482,14 @@ static void port_outw(struct si_sm_io *io, unsigned int offset,
outw(b << io->regshift, addr + (offset * io->regspacing));
}
static unsigned char port_inl(struct si_sm_io *io, unsigned int offset)
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(struct si_sm_io *io, unsigned int offset,
static void port_outl(const struct si_sm_io *io, unsigned int offset,
unsigned char b)
{
unsigned int addr = io->addr_data;
@ -1556,49 +1562,52 @@ static int port_setup(struct smi_info *info)
return 0;
}
static unsigned char intf_mem_inb(struct si_sm_io *io, unsigned int offset)
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(struct si_sm_io *io, unsigned int offset,
unsigned char b)
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(struct si_sm_io *io, unsigned int offset)
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(struct si_sm_io *io, unsigned int offset,
unsigned char b)
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(struct si_sm_io *io, unsigned int offset)
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(struct si_sm_io *io, unsigned int offset,
unsigned char b)
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(struct si_sm_io *io, unsigned int offset)
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(struct si_sm_io *io, unsigned int offset,
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));
@ -2233,134 +2242,6 @@ static void spmi_find_bmc(void)
try_init_spmi(spmi);
}
}
static int ipmi_pnp_probe(struct pnp_dev *dev,
const struct pnp_device_id *dev_id)
{
struct acpi_device *acpi_dev;
struct smi_info *info;
struct resource *res, *res_second;
acpi_handle handle;
acpi_status status;
unsigned long long tmp;
int rv = -EINVAL;
acpi_dev = pnp_acpi_device(dev);
if (!acpi_dev)
return -ENODEV;
info = smi_info_alloc();
if (!info)
return -ENOMEM;
info->addr_source = SI_ACPI;
printk(KERN_INFO PFX "probing via ACPI\n");
handle = acpi_dev->handle;
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 = pnp_get_resource(dev, IORESOURCE_IO, 0);
if (res) {
info->io_setup = port_setup;
info->io.addr_type = IPMI_IO_ADDR_SPACE;
} else {
res = pnp_get_resource(dev, IORESOURCE_MEM, 0);
if (res) {
info->io_setup = mem_setup;
info->io.addr_type = IPMI_MEM_ADDR_SPACE;
}
}
if (!res) {
dev_err(&dev->dev, "no I/O or memory address\n");
goto err_free;
}
info->io.addr_data = res->start;
info->io.regspacing = DEFAULT_REGSPACING;
res_second = pnp_get_resource(dev,
(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_REGSPACING;
info->io.regshift = 0;
/* 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 if (pnp_irq_valid(dev, 0)) {
info->irq = pnp_irq(dev, 0);
info->irq_setup = std_irq_setup;
}
info->dev = &dev->dev;
pnp_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 void ipmi_pnp_remove(struct pnp_dev *dev)
{
struct smi_info *info = pnp_get_drvdata(dev);
cleanup_one_si(info);
}
static const struct pnp_device_id pnp_dev_table[] = {
{"IPI0001", 0},
{"", 0},
};
static struct pnp_driver ipmi_pnp_driver = {
.name = DEVICE_NAME,
.probe = ipmi_pnp_probe,
.remove = ipmi_pnp_remove,
.id_table = pnp_dev_table,
};
MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
#endif
#ifdef CONFIG_DMI
@ -2654,7 +2535,7 @@ static void ipmi_pci_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
}
static struct pci_device_id ipmi_pci_devices[] = {
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, }
@ -2669,10 +2550,19 @@ static struct pci_driver ipmi_pci_driver = {
};
#endif /* CONFIG_PCI */
static const struct of_device_id ipmi_match[];
static int ipmi_probe(struct platform_device *dev)
{
#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 },
{},
};
static int of_ipmi_probe(struct platform_device *dev)
{
const struct of_device_id *match;
struct smi_info *info;
struct resource resource;
@ -2683,9 +2573,9 @@ static int ipmi_probe(struct platform_device *dev)
dev_info(&dev->dev, "probing via device tree\n");
match = of_match_device(ipmi_match, &dev->dev);
match = of_match_device(of_ipmi_match, &dev->dev);
if (!match)
return -EINVAL;
return -ENODEV;
if (!of_device_is_available(np))
return -EINVAL;
@ -2754,33 +2644,160 @@ static int ipmi_probe(struct platform_device *dev)
kfree(info);
return ret;
}
#endif
return 0;
}
MODULE_DEVICE_TABLE(of, of_ipmi_match);
#else
#define of_ipmi_match NULL
static int of_ipmi_probe(struct platform_device *dev)
{
return -ENODEV;
}
#endif
#ifdef CONFIG_ACPI
static int acpi_ipmi_probe(struct platform_device *dev)
{
struct smi_info *info;
struct resource *res, *res_second;
acpi_handle handle;
acpi_status status;
unsigned long long tmp;
int rv = -EINVAL;
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 = platform_get_resource(dev, IORESOURCE_IO, 0);
if (res) {
info->io_setup = port_setup;
info->io.addr_type = IPMI_IO_ADDR_SPACE;
} else {
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (res) {
info->io_setup = mem_setup;
info->io.addr_type = IPMI_MEM_ADDR_SPACE;
}
}
if (!res) {
dev_err(&dev->dev, "no I/O or memory address\n");
goto err_free;
}
info->io.addr_data = res->start;
info->io.regspacing = DEFAULT_REGSPACING;
res_second = platform_get_resource(dev,
(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_REGSPACING;
info->io.regshift = 0;
/* 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->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;
return acpi_ipmi_probe(dev);
}
static int ipmi_remove(struct platform_device *dev)
{
#ifdef CONFIG_OF
cleanup_one_si(dev_get_drvdata(&dev->dev));
#endif
struct smi_info *info = dev_get_drvdata(&dev->dev);
cleanup_one_si(info);
return 0;
}
static const struct of_device_id ipmi_match[] =
{
{ .type = "ipmi", .compatible = "ipmi-kcs",
.data = (void *)(unsigned long) SI_KCS },
{ .type = "ipmi", .compatible = "ipmi-smic",
.data = (void *)(unsigned long) SI_SMIC },
{ .type = "ipmi", .compatible = "ipmi-bt",
.data = (void *)(unsigned long) SI_BT },
{},
};
static struct platform_driver ipmi_driver = {
.driver = {
.name = DEVICE_NAME,
.of_match_table = ipmi_match,
.of_match_table = of_ipmi_match,
.acpi_match_table = ACPI_PTR(acpi_ipmi_match),
},
.probe = ipmi_probe,
.remove = ipmi_remove,
@ -2905,12 +2922,7 @@ static int try_get_dev_id(struct smi_info *smi_info)
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)
static int get_global_enables(struct smi_info *smi_info, u8 *enables)
{
unsigned char msg[3];
unsigned char *resp;
@ -2918,12 +2930,8 @@ static void check_clr_rcv_irq(struct smi_info *smi_info)
int rv;
resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
if (!resp) {
printk(KERN_WARNING PFX "Out of memory allocating response for"
" global enables command, cannot check recv irq bit"
" handling.\n");
return;
}
if (!resp)
return -ENOMEM;
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
@ -2931,9 +2939,9 @@ static void check_clr_rcv_irq(struct smi_info *smi_info)
rv = wait_for_msg_done(smi_info);
if (rv) {
printk(KERN_WARNING PFX "Error getting response from get"
" global enables command, cannot check recv irq bit"
" handling.\n");
dev_warn(smi_info->dev,
"Error getting response from get global enables command: %d\n",
rv);
goto out;
}
@ -2944,27 +2952,44 @@ static void check_clr_rcv_irq(struct smi_info *smi_info)
resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD ||
resp[2] != 0) {
printk(KERN_WARNING PFX "Invalid return from get global"
" enables command, cannot check recv irq bit"
" handling.\n");
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];
}
if ((resp[3] & IPMI_BMC_RCV_MSG_INTR) == 0)
/* Already clear, should work ok. */
goto out;
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] = resp[3] & ~IPMI_BMC_RCV_MSG_INTR;
msg[2] = enables;
smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3);
rv = wait_for_msg_done(smi_info);
if (rv) {
printk(KERN_WARNING PFX "Error getting response from set"
" global enables command, cannot check recv irq bit"
" handling.\n");
dev_warn(smi_info->dev,
"Error getting response from set global enables command: %d\n",
rv);
goto out;
}
@ -2974,25 +2999,93 @@ static void check_clr_rcv_irq(struct smi_info *smi_info)
if (resp_len < 3 ||
resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) {
printk(KERN_WARNING PFX "Invalid return from get global"
" enables command, cannot check recv irq bit"
" handling.\n");
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) {
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.
*/
printk(KERN_WARNING PFX "The BMC does not support clearing"
" the recv irq bit, compensating, but the BMC needs to"
" be fixed.\n");
smi_info->cannot_clear_recv_irq_bit = true;
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;
}
out:
kfree(resp);
}
static int try_enable_event_buffer(struct smi_info *smi_info)
@ -3313,6 +3406,12 @@ static void setup_xaction_handlers(struct smi_info *smi_info)
setup_dell_poweredge_bt_xaction_handler(smi_info);
}
static void check_for_broken_irqs(struct smi_info *smi_info)
{
check_clr_rcv_irq(smi_info);
check_set_rcv_irq(smi_info);
}
static inline void wait_for_timer_and_thread(struct smi_info *smi_info)
{
if (smi_info->thread != NULL)
@ -3321,7 +3420,7 @@ static inline void wait_for_timer_and_thread(struct smi_info *smi_info)
del_timer_sync(&smi_info->si_timer);
}
static struct ipmi_default_vals
static const struct ipmi_default_vals
{
int type;
int port;
@ -3490,10 +3589,9 @@ static int try_smi_init(struct smi_info *new_smi)
goto out_err;
}
check_clr_rcv_irq(new_smi);
setup_oem_data_handler(new_smi);
setup_xaction_handlers(new_smi);
check_for_broken_irqs(new_smi);
new_smi->waiting_msg = NULL;
new_smi->curr_msg = NULL;
@ -3692,13 +3790,6 @@ static int init_ipmi_si(void)
}
#endif
#ifdef CONFIG_ACPI
if (si_tryacpi) {
pnp_register_driver(&ipmi_pnp_driver);
pnp_registered = true;
}
#endif
#ifdef CONFIG_DMI
if (si_trydmi)
dmi_find_bmc();
@ -3850,10 +3941,6 @@ static void cleanup_ipmi_si(void)
if (pci_registered)
pci_unregister_driver(&ipmi_pci_driver);
#endif
#ifdef CONFIG_ACPI
if (pnp_registered)
pnp_unregister_driver(&ipmi_pnp_driver);
#endif
#ifdef CONFIG_PARISC
if (parisc_registered)
unregister_parisc_driver(&ipmi_parisc_driver);

View File

@ -46,8 +46,8 @@ struct si_sm_data;
* this interface.
*/
struct si_sm_io {
unsigned char (*inputb)(struct si_sm_io *io, unsigned int offset);
void (*outputb)(struct si_sm_io *io,
unsigned char (*inputb)(const struct si_sm_io *io, unsigned int offset);
void (*outputb)(const struct si_sm_io *io,
unsigned int offset,
unsigned char b);
@ -135,7 +135,7 @@ struct si_sm_handlers {
};
/* Current state machines that we can use. */
extern struct si_sm_handlers kcs_smi_handlers;
extern struct si_sm_handlers smic_smi_handlers;
extern struct si_sm_handlers bt_smi_handlers;
extern const struct si_sm_handlers kcs_smi_handlers;
extern const struct si_sm_handlers smic_smi_handlers;
extern const struct si_sm_handlers bt_smi_handlers;

View File

@ -589,7 +589,7 @@ static int smic_size(void)
return sizeof(struct si_sm_data);
}
struct si_sm_handlers smic_smi_handlers = {
const struct si_sm_handlers smic_smi_handlers = {
.init_data = init_smic_data,
.start_transaction = start_smic_transaction,
.get_result = smic_get_result,

View File

@ -1136,6 +1136,10 @@ module_param_array(slave_addrs, int, &num_slave_addrs, 0);
MODULE_PARM_DESC(slave_addrs,
"The default IPMB slave address for the controller.");
static bool alerts_broken;
module_param(alerts_broken, bool, 0);
MODULE_PARM_DESC(alerts_broken, "Don't enable alerts for the controller.");
/*
* Bit 0 enables message debugging, bit 1 enables state debugging, and
* bit 2 enables timing debugging. This is an array indexed by
@ -1154,11 +1158,11 @@ static int use_thread;
module_param(use_thread, int, 0);
MODULE_PARM_DESC(use_thread, "Use the thread interface.");
static bool ssif_tryacpi = 1;
static bool ssif_tryacpi = true;
module_param_named(tryacpi, ssif_tryacpi, bool, 0);
MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the default scan of the interfaces identified via ACPI");
static bool ssif_trydmi = 1;
static bool ssif_trydmi = true;
module_param_named(trydmi, ssif_trydmi, bool, 0);
MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the default scan of the interfaces identified via DMI (SMBIOS)");
@ -1582,6 +1586,10 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ssif_info->global_enables |= IPMI_BMC_EVT_MSG_BUFF;
}
/* Some systems don't behave well if you enable alerts. */
if (alerts_broken)
goto found;
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR;
@ -1787,7 +1795,7 @@ skip_addr:
}
#ifdef CONFIG_ACPI
static struct acpi_device_id ssif_acpi_match[] = {
static const struct acpi_device_id ssif_acpi_match[] = {
{ "IPI0001", 0 },
{ },
};

View File

@ -115,6 +115,11 @@ struct ipmi_smi_handlers {
implement it. */
void (*set_need_watch)(void *send_info, bool enable);
/*
* Called when flushing all pending messages.
*/
void (*flush_messages)(void *send_info);
/* Called when the interface should go into "run to
completion" mode. If this call sets the value to true, the
interface should make sure that all messages are flushed
@ -207,7 +212,7 @@ static inline int ipmi_demangle_device_id(const unsigned char *data,
upper layer until the start_processing() function in the handlers
is called, and the lower layer must get the interface from that
call. */
int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
void *send_info,
struct ipmi_device_id *device_id,
struct device *dev,