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_ci/* 1762306a36Sopenharmony_ci * ASM service processor event handling routines. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Events are signalled to the device drivers through interrupts. 2062306a36Sopenharmony_ci * They have the format of dot commands, with the type field set to 2162306a36Sopenharmony_ci * sp_event. 2262306a36Sopenharmony_ci * The driver does not interpret the events, it simply stores them in a 2362306a36Sopenharmony_ci * circular buffer. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void wake_up_event_readers(struct service_processor *sp) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct event_reader *reader; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci list_for_each_entry(reader, &sp->event_buffer->readers, node) 3162306a36Sopenharmony_ci wake_up_interruptible(&reader->wait); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * receive_event 3662306a36Sopenharmony_ci * Called by the interrupt handler when a dot command of type sp_event is 3762306a36Sopenharmony_ci * received. 3862306a36Sopenharmony_ci * Store the event in the circular event buffer, wake up any sleeping 3962306a36Sopenharmony_ci * event readers. 4062306a36Sopenharmony_ci * There is no reader marker in the buffer, therefore readers are 4162306a36Sopenharmony_ci * responsible for keeping up with the writer, or they will lose events. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_civoid ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct event_buffer *buffer = sp->event_buffer; 4662306a36Sopenharmony_ci struct ibmasm_event *event; 4762306a36Sopenharmony_ci unsigned long flags; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci data_size = min(data_size, IBMASM_EVENT_MAX_SIZE); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 5262306a36Sopenharmony_ci /* copy the event into the next slot in the circular buffer */ 5362306a36Sopenharmony_ci event = &buffer->events[buffer->next_index]; 5462306a36Sopenharmony_ci memcpy_fromio(event->data, data, data_size); 5562306a36Sopenharmony_ci event->data_size = data_size; 5662306a36Sopenharmony_ci event->serial_number = buffer->next_serial_number; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* advance indices in the buffer */ 5962306a36Sopenharmony_ci buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS; 6062306a36Sopenharmony_ci buffer->next_serial_number++; 6162306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci wake_up_event_readers(sp); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic inline int event_available(struct event_buffer *b, struct event_reader *r) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci return (r->next_serial_number < b->next_serial_number); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * get_next_event 7362306a36Sopenharmony_ci * Called by event readers (initiated from user space through the file 7462306a36Sopenharmony_ci * system). 7562306a36Sopenharmony_ci * Sleeps until a new event is available. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ciint ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct event_buffer *buffer = sp->event_buffer; 8062306a36Sopenharmony_ci struct ibmasm_event *event; 8162306a36Sopenharmony_ci unsigned int index; 8262306a36Sopenharmony_ci unsigned long flags; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci reader->cancelled = 0; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (wait_event_interruptible(reader->wait, 8762306a36Sopenharmony_ci event_available(buffer, reader) || reader->cancelled)) 8862306a36Sopenharmony_ci return -ERESTARTSYS; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (!event_available(buffer, reader)) 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci index = buffer->next_index; 9662306a36Sopenharmony_ci event = &buffer->events[index]; 9762306a36Sopenharmony_ci while (event->serial_number < reader->next_serial_number) { 9862306a36Sopenharmony_ci index = (index + 1) % IBMASM_NUM_EVENTS; 9962306a36Sopenharmony_ci event = &buffer->events[index]; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci memcpy(reader->data, event->data, event->data_size); 10262306a36Sopenharmony_ci reader->data_size = event->data_size; 10362306a36Sopenharmony_ci reader->next_serial_number = event->serial_number + 1; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return event->data_size; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_civoid ibmasm_cancel_next_event(struct event_reader *reader) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci reader->cancelled = 1; 11362306a36Sopenharmony_ci wake_up_interruptible(&reader->wait); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_civoid ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned long flags; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci reader->next_serial_number = sp->event_buffer->next_serial_number; 12162306a36Sopenharmony_ci init_waitqueue_head(&reader->wait); 12262306a36Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 12362306a36Sopenharmony_ci list_add(&reader->node, &sp->event_buffer->readers); 12462306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_civoid ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci unsigned long flags; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 13262306a36Sopenharmony_ci list_del(&reader->node); 13362306a36Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciint ibmasm_event_buffer_init(struct service_processor *sp) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct event_buffer *buffer; 13962306a36Sopenharmony_ci struct ibmasm_event *event; 14062306a36Sopenharmony_ci int i; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL); 14362306a36Sopenharmony_ci if (!buffer) 14462306a36Sopenharmony_ci return -ENOMEM; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci buffer->next_index = 0; 14762306a36Sopenharmony_ci buffer->next_serial_number = 1; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci event = buffer->events; 15062306a36Sopenharmony_ci for (i=0; i<IBMASM_NUM_EVENTS; i++, event++) 15162306a36Sopenharmony_ci event->serial_number = 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci INIT_LIST_HEAD(&buffer->readers); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci sp->event_buffer = buffer; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_civoid ibmasm_event_buffer_exit(struct service_processor *sp) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci kfree(sp->event_buffer); 16362306a36Sopenharmony_ci} 164