blob: 19714670e88421eb4774d06a80bdcd21df234e79 [file] [log] [blame]
/* Copyright 2013-2014 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define pr_fmt(fmt) "opalmsg: " fmt
#include <skiboot.h>
#include <opal-msg.h>
#include <opal-api.h>
#include <lock.h>
#define OPAL_MAX_MSGS (OPAL_MSG_TYPE_MAX + OPAL_MAX_ASYNC_COMP - 1)
struct opal_msg_entry {
struct list_node link;
void (*consumed)(void *data);
void *data;
struct opal_msg msg;
};
static LIST_HEAD(msg_free_list);
static LIST_HEAD(msg_pending_list);
static struct lock opal_msg_lock = LOCK_UNLOCKED;
int _opal_queue_msg(enum opal_msg_type msg_type, void *data,
void (*consumed)(void *data), size_t num_params,
const u64 *params)
{
struct opal_msg_entry *entry;
lock(&opal_msg_lock);
entry = list_pop(&msg_free_list, struct opal_msg_entry, link);
if (!entry) {
prerror("No available node in the free list, allocating\n");
entry = zalloc(sizeof(struct opal_msg_entry));
if (!entry) {
prerror("Allocation failed\n");
unlock(&opal_msg_lock);
return OPAL_RESOURCE;
}
}
entry->consumed = consumed;
entry->data = data;
entry->msg.msg_type = cpu_to_be32(msg_type);
if (num_params > ARRAY_SIZE(entry->msg.params)) {
prerror("Discarding extra parameters\n");
num_params = ARRAY_SIZE(entry->msg.params);
}
memcpy(entry->msg.params, params, num_params*sizeof(u64));
list_add_tail(&msg_pending_list, &entry->link);
opal_update_pending_evt(OPAL_EVENT_MSG_PENDING,
OPAL_EVENT_MSG_PENDING);
unlock(&opal_msg_lock);
return 0;
}
static int64_t opal_get_msg(uint64_t *buffer, uint64_t size)
{
struct opal_msg_entry *entry;
void (*callback)(void *data);
void *data;
if (size < sizeof(struct opal_msg) || !buffer)
return OPAL_PARAMETER;
if (!opal_addr_valid(buffer))
return OPAL_PARAMETER;
lock(&opal_msg_lock);
entry = list_pop(&msg_pending_list, struct opal_msg_entry, link);
if (!entry) {
unlock(&opal_msg_lock);
return OPAL_RESOURCE;
}
memcpy(buffer, &entry->msg, sizeof(entry->msg));
callback = entry->consumed;
data = entry->data;
list_add(&msg_free_list, &entry->link);
if (list_empty(&msg_pending_list))
opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, 0);
unlock(&opal_msg_lock);
if (callback)
callback(data);
return OPAL_SUCCESS;
}
opal_call(OPAL_GET_MSG, opal_get_msg, 2);
static int64_t opal_check_completion(uint64_t *buffer, uint64_t size,
uint64_t token)
{
struct opal_msg_entry *entry, *next_entry;
void (*callback)(void *data) = NULL;
int rc = OPAL_BUSY;
void *data = NULL;
if (!opal_addr_valid(buffer))
return OPAL_PARAMETER;
lock(&opal_msg_lock);
list_for_each_safe(&msg_pending_list, entry, next_entry, link) {
if (entry->msg.msg_type == OPAL_MSG_ASYNC_COMP &&
be64_to_cpu(entry->msg.params[0]) == token) {
list_del(&entry->link);
callback = entry->consumed;
data = entry->data;
list_add(&msg_free_list, &entry->link);
if (list_empty(&msg_pending_list))
opal_update_pending_evt(OPAL_EVENT_MSG_PENDING,
0);
rc = OPAL_SUCCESS;
break;
}
}
if (rc == OPAL_SUCCESS && size >= sizeof(struct opal_msg))
memcpy(buffer, &entry->msg, sizeof(entry->msg));
unlock(&opal_msg_lock);
if (callback)
callback(data);
return rc;
}
opal_call(OPAL_CHECK_ASYNC_COMPLETION, opal_check_completion, 3);
void opal_init_msg(void)
{
struct opal_msg_entry *entry;
int i;
for (i = 0; i < OPAL_MAX_MSGS; i++, entry++) {
entry = zalloc(sizeof(*entry));
if (!entry)
goto err;
list_add_tail(&msg_free_list, &entry->link);
}
return;
err:
for (; i > 0; i--) {
entry = list_pop(&msg_free_list, struct opal_msg_entry, link);
if (entry)
free(entry);
}
}