162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * IBM ASM Service Processor Device Driver 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2004 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Max Asböck <amax@us.ibm.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/sched.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include "ibmasm.h" 1462306a36Sopenharmony_ci#include "lowlevel.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic void exec_next_command(struct service_processor *sp); 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic atomic_t command_count = ATOMIC_INIT(0); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct command *cmd; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci if (buffer_size > IBMASM_CMD_MAX_BUFFER_SIZE) 2562306a36Sopenharmony_ci return NULL; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci cmd = kzalloc(sizeof(struct command), GFP_KERNEL); 2862306a36Sopenharmony_ci if (cmd == NULL) 2962306a36Sopenharmony_ci return NULL; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci cmd->buffer = kzalloc(buffer_size, GFP_KERNEL); 3362306a36Sopenharmony_ci if (cmd->buffer == NULL) { 3462306a36Sopenharmony_ci kfree(cmd); 3562306a36Sopenharmony_ci return NULL; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci cmd->buffer_size = buffer_size; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci kref_init(&cmd->kref); 4062306a36Sopenharmony_ci cmd->lock = &sp->lock; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci cmd->status = IBMASM_CMD_PENDING; 4362306a36Sopenharmony_ci init_waitqueue_head(&cmd->wait); 4462306a36Sopenharmony_ci INIT_LIST_HEAD(&cmd->queue_node); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci atomic_inc(&command_count); 4762306a36Sopenharmony_ci dbg("command count: %d\n", atomic_read(&command_count)); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return cmd; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_civoid ibmasm_free_command(struct kref *kref) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct command *cmd = to_command(kref); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci list_del(&cmd->queue_node); 5762306a36Sopenharmony_ci atomic_dec(&command_count); 5862306a36Sopenharmony_ci dbg("command count: %d\n", atomic_read(&command_count)); 5962306a36Sopenharmony_ci kfree(cmd->buffer); 6062306a36Sopenharmony_ci kfree(cmd); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void enqueue_command(struct service_processor *sp, struct command *cmd) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci list_add_tail(&cmd->queue_node, &sp->command_queue); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct command *dequeue_command(struct service_processor *sp) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct command *cmd; 7162306a36Sopenharmony_ci struct list_head *next; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (list_empty(&sp->command_queue)) 7462306a36Sopenharmony_ci return NULL; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci next = sp->command_queue.next; 7762306a36Sopenharmony_ci list_del_init(next); 7862306a36Sopenharmony_ci cmd = list_entry(next, struct command, queue_node); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return cmd; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic inline void do_exec_command(struct service_processor *sp) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci char tsbuf[32]; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (ibmasm_send_i2o_message(sp)) { 9062306a36Sopenharmony_ci sp->current_command->status = IBMASM_CMD_FAILED; 9162306a36Sopenharmony_ci wake_up(&sp->current_command->wait); 9262306a36Sopenharmony_ci command_put(sp->current_command); 9362306a36Sopenharmony_ci exec_next_command(sp); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * exec_command 9962306a36Sopenharmony_ci * send a command to a service processor 10062306a36Sopenharmony_ci * Commands are executed sequentially. One command (sp->current_command) 10162306a36Sopenharmony_ci * is sent to the service processor. Once the interrupt handler gets a 10262306a36Sopenharmony_ci * message of type command_response, the message is copied into 10362306a36Sopenharmony_ci * the current commands buffer, 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_civoid ibmasm_exec_command(struct service_processor *sp, struct command *cmd) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci unsigned long flags; 10862306a36Sopenharmony_ci char tsbuf[32]; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (!sp->current_command) { 11562306a36Sopenharmony_ci sp->current_command = cmd; 11662306a36Sopenharmony_ci command_get(sp->current_command); 11762306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 11862306a36Sopenharmony_ci do_exec_command(sp); 11962306a36Sopenharmony_ci } else { 12062306a36Sopenharmony_ci enqueue_command(sp, cmd); 12162306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void exec_next_command(struct service_processor *sp) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci unsigned long flags; 12862306a36Sopenharmony_ci char tsbuf[32]; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci dbg("%s:%d at %s\n", __func__, __LINE__, get_timestamp(tsbuf)); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 13362306a36Sopenharmony_ci sp->current_command = dequeue_command(sp); 13462306a36Sopenharmony_ci if (sp->current_command) { 13562306a36Sopenharmony_ci command_get(sp->current_command); 13662306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 13762306a36Sopenharmony_ci do_exec_command(sp); 13862306a36Sopenharmony_ci } else { 13962306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * Sleep until a command has failed or a response has been received 14562306a36Sopenharmony_ci * and the command status been updated by the interrupt handler. 14662306a36Sopenharmony_ci * (see receive_response). 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_civoid ibmasm_wait_for_response(struct command *cmd, int timeout) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci wait_event_interruptible_timeout(cmd->wait, 15162306a36Sopenharmony_ci cmd->status == IBMASM_CMD_COMPLETE || 15262306a36Sopenharmony_ci cmd->status == IBMASM_CMD_FAILED, 15362306a36Sopenharmony_ci timeout * HZ); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* 15762306a36Sopenharmony_ci * receive_command_response 15862306a36Sopenharmony_ci * called by the interrupt handler when a dot command of type command_response 15962306a36Sopenharmony_ci * was received. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_civoid ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct command *cmd = sp->current_command; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!sp->current_command) 16662306a36Sopenharmony_ci return; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci memcpy_fromio(cmd->buffer, response, min(size, cmd->buffer_size)); 16962306a36Sopenharmony_ci cmd->status = IBMASM_CMD_COMPLETE; 17062306a36Sopenharmony_ci wake_up(&sp->current_command->wait); 17162306a36Sopenharmony_ci command_put(sp->current_command); 17262306a36Sopenharmony_ci exec_next_command(sp); 17362306a36Sopenharmony_ci} 174