/*
* Functions to handle I2O controllers and I2O message handling
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* A lot of the I2O message side code from this is taken from the
* Red Creek RCPCI45 adapter driver by Red Creek Communications
*
* Fixes/additions:
* Philipp Rumpf
* Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
* Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
* Deepak Saxena <deepak@plexity.net>
* Boji T Kannanthanam <boji.t.kannanthanam@intel.com>
* Alan Cox <alan@lxorguk.ukuu.org.uk>:
* Ported to Linux 2.5.
* Markus Lidel <Markus.Lidel@shadowconnect.com>:
* Minor fixes for 2.6.
*/
#include <linux/module.h>
#include <linux/i2o.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include "core.h"
#define OSM_NAME "i2o"
#define OSM_VERSION "1.325"
#define OSM_DESCRIPTION "I2O subsystem"
/* global I2O controller list */
LIST_HEAD(i2o_controllers);
/*
* global I2O System Table. Contains information about all the IOPs in the
* system. Used to inform IOPs about each others existence.
*/
static struct i2o_dma i2o_systab;
static int i2o_hrt_get(struct i2o_controller *c);
/**
* i2o_msg_get_wait - obtain an I2O message from the IOP
* @c: I2O controller
* @wait: how long to wait until timeout
*
* This function waits up to wait seconds for a message slot to be
* available.
*
* On a success the message is returned and the pointer to the message is
* set in msg. The returned message is the physical page frame offset
* address from the read port (see the i2o spec). If no message is
* available returns I2O_QUEUE_EMPTY and msg is leaved untouched.
*/
struct i2o_message *i2o_msg_get_wait(struct i2o_controller *c, int wait)
{
unsigned long timeout = jiffies + wait * HZ;
struct i2o_message *msg;
while (IS_ERR(msg = i2o_msg_get(c))) {
if (time_after(jiffies, timeout)) {
osm_debug("%s: Timeout waiting for message frame.\n",
c->name);
return ERR_PTR(-ETIMEDOUT);
}
schedule_timeout_uninterruptible(1);
}
return msg;
};
#if BITS_PER_LONG == 64
/**
* i2o_cntxt_list_add - Append a pointer to context list and return a id
* @c: controller to which the context list belong
* @ptr: pointer to add to the context list
*
* Because the context field in I2O is only 32-bit large, on 64-bit the
* pointer is to large to fit in the context field. The i2o_cntxt_list
* functions therefore map pointers to context fields.
*
* Returns context id > 0 on success or 0 on failure.
*/
u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr)
{
struct i2o_context_list_element *entry;
unsigned long flags;
if (!ptr)
osm_err("%s: couldn't add NULL pointer to context list!\n",
c->name);
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry) {
osm_err("%s: Could not allocate memory for context list element"
"\n", c->name);
return 0;
}
entry->ptr = ptr;
entry->timestamp = jiffies;
INIT_LIST_HEAD(&entry->list);
spin_lock_irqsave(&c->context_list_lock, flags);
if (unlikely(atomic_inc_and_test(&c->context_list_counter)))
atomic_inc(&c->context_list_counter);
entry->context = atomic_read(&c->context_list_counter);
list_add(&entry->list, &c->context_list);
spin_unlock_irqrestore(&c->context_list_lock, flags);
osm_debug("%s: Add context to list %p -> %d\n", c->name, ptr, context);
return entry->context;
};
/**
* i2o_cntxt_list_remove - Remove a pointer from the context list
* @c: controller to which the context list belong
* @ptr: pointer which should be removed from the context list
*
* Removes a previously added pointer from the context list and returns
* the matching context id.
*
* Returns context id on success or 0 on failure.
*/
u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr)
{
struct i2o_context_list_element *entry;
u32 context = 0;
unsigned long flags;
spin_lock_irqsave(&c->context_list_lock, flags);
list_for_each_entry(entry, &c->context_list, list)
if (entry->ptr == ptr) {
list_del(&entry->list);
context = entry->context;
kfree(entry);
break;
}
spin_unlock_irqrestore(&c->context_list_lock, flags);
if (!context)
osm_warn("%s: Could not remove nonexistent ptr %p\n", c->name,
ptr);
osm_debug("%s: remove ptr from context list %d -> %p\n", c->name,
context, ptr);
return context;
};
/**
* i2o_cntxt_list_get - Get a pointer from the context list and remove it
* @c: controller to which the context list belong
* @context: context id to which the pointer belong
*
* Returns pointer to the matching context id on success or NULL on
* failure.
*/
void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
{
struct i2o_context_list_element *entry;
unsigned long flags;
void *ptr = NULL;
spin_lock_irqsave(&c->context_list_lock, flags);
list_for_each_entry(entry, &c->context_list, list)
if (entry->context == context) {
list_del(&entry->list);
ptr = entry->ptr;
kfree(entry);
break;
}
spin_unlock_irqrestore(&c->context_list_lock, flags);
if (!ptr)
osm_warn("%s: context id %d not found\n", c->name, context);
osm_debug("%s: get ptr from context list %d -> %p\n", c->name, context,
ptr);
return ptr;
};
/**
* i2o_cntxt_list_get_ptr - Get a context id from the context list
* @c: controller to which the context list belong
* @ptr: pointer to which the context id should be fetched
*
* Returns context id which matches to the pointer on success or 0 on
* failure.
*/
u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr)
{
struct i2o_context_list_element *entry;
u32 context = 0;
unsigned long flags;
spin_lock_irqsave(&c->context_list_lock, flags);
list_for_each_entry(entry, &c->context_list, list)
if (entry->ptr == ptr) {
context = entry->context;
break;
}
spin_unlock_irqrestore(&c->context_list_lock, flags);
if (!context)
osm_warn("%s: Could not find nonexistent ptr %p\n", c->name,
ptr);
osm_debug("%s: get context id from context list %p -> %d\n", c->name,
ptr, context);
return context;
};
#endif
/**
* i2o_iop_find - Find an I2O controller by id
* @unit: unit number of the I2O controller to search for
*
* Lookup the I2O controller on the controller list.
*
* Returns pointer to the I2O controller on success or NULL if not found.
*/
struct i2o_controller *i2o_find_iop(int unit)
{
struct i2o_controller *c;
list_for_each_entry(c, &i2o_controllers, list) {
if (c->unit == unit)
return c;
}
return NULL;
};
/**
* i2o_iop_find_device - Find a I2O device on an I2O controller
* @c: I2O controller where the I2O device hangs on
* @tid: TID of the I2O device to search for
*
* Searches the devices of the I2O controller for a device with TID tid and
* returns it.
*
* Returns a pointer to the I2O device if found, otherwise NULL.
*/
struct i2o_device *i2o_iop_find_device(struct i2o_controller *c, u16 tid)
{
struct i2o_device *dev;
list_for_each_entry(dev, &c->devices, list)
if (dev->lct_data.tid == tid)
return dev;
return NULL;
};
/**
* i2o_quiesce_controller - quiesce controller
* @c: controller
*
* Quiesce an IOP. Causes IOP to make external operation quiescent
* (i2o 'READY' state). Internal operation of the IOP continues normally.
*
* Returns 0 on success or negative error code on failure.
*/
static int i2o_iop_quiesce(struct i2o_controller *c)
{
struct i2o_message *msg;
i2o_status_block *sb = c->status_block.virt;
int rc;
i2o_status_get(c);
/* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */
if ((sb->iop_state != ADAPTER_STATE_READY) &&
(sb->iop_state != ADAPTER_STATE_OPERATIONAL))
return 0;
msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
if (IS_ERR(msg))
return PTR_ERR(msg);
msg->u.head[0] = cpu_to_le32(F