164 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2 3/*4 * IBM ASM Service Processor Device Driver5 *6 * Copyright (C) IBM Corporation, 20047 *8 * Author: Max Asböck <amax@us.ibm.com>9 */10 11#include <linux/sched.h>12#include <linux/slab.h>13#include "ibmasm.h"14#include "lowlevel.h"15 16/*17 * ASM service processor event handling routines.18 *19 * Events are signalled to the device drivers through interrupts.20 * They have the format of dot commands, with the type field set to21 * sp_event.22 * The driver does not interpret the events, it simply stores them in a23 * circular buffer.24 */25 26static void wake_up_event_readers(struct service_processor *sp)27{28 struct event_reader *reader;29 30 list_for_each_entry(reader, &sp->event_buffer->readers, node)31 wake_up_interruptible(&reader->wait);32}33 34/*35 * receive_event36 * Called by the interrupt handler when a dot command of type sp_event is37 * received.38 * Store the event in the circular event buffer, wake up any sleeping39 * event readers.40 * There is no reader marker in the buffer, therefore readers are41 * responsible for keeping up with the writer, or they will lose events.42 */43void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size)44{45 struct event_buffer *buffer = sp->event_buffer;46 struct ibmasm_event *event;47 unsigned long flags;48 49 data_size = min(data_size, IBMASM_EVENT_MAX_SIZE);50 51 spin_lock_irqsave(&sp->lock, flags);52 /* copy the event into the next slot in the circular buffer */53 event = &buffer->events[buffer->next_index];54 memcpy_fromio(event->data, data, data_size);55 event->data_size = data_size;56 event->serial_number = buffer->next_serial_number;57 58 /* advance indices in the buffer */59 buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS;60 buffer->next_serial_number++;61 spin_unlock_irqrestore(&sp->lock, flags);62 63 wake_up_event_readers(sp);64}65 66static inline int event_available(struct event_buffer *b, struct event_reader *r)67{68 return (r->next_serial_number < b->next_serial_number);69}70 71/*72 * get_next_event73 * Called by event readers (initiated from user space through the file74 * system).75 * Sleeps until a new event is available.76 */77int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader)78{79 struct event_buffer *buffer = sp->event_buffer;80 struct ibmasm_event *event;81 unsigned int index;82 unsigned long flags;83 84 reader->cancelled = 0;85 86 if (wait_event_interruptible(reader->wait,87 event_available(buffer, reader) || reader->cancelled))88 return -ERESTARTSYS;89 90 if (!event_available(buffer, reader))91 return 0;92 93 spin_lock_irqsave(&sp->lock, flags);94 95 index = buffer->next_index;96 event = &buffer->events[index];97 while (event->serial_number < reader->next_serial_number) {98 index = (index + 1) % IBMASM_NUM_EVENTS;99 event = &buffer->events[index];100 }101 memcpy(reader->data, event->data, event->data_size);102 reader->data_size = event->data_size;103 reader->next_serial_number = event->serial_number + 1;104 105 spin_unlock_irqrestore(&sp->lock, flags);106 107 return event->data_size;108}109 110void ibmasm_cancel_next_event(struct event_reader *reader)111{112 reader->cancelled = 1;113 wake_up_interruptible(&reader->wait);114}115 116void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader)117{118 unsigned long flags;119 120 reader->next_serial_number = sp->event_buffer->next_serial_number;121 init_waitqueue_head(&reader->wait);122 spin_lock_irqsave(&sp->lock, flags);123 list_add(&reader->node, &sp->event_buffer->readers);124 spin_unlock_irqrestore(&sp->lock, flags);125}126 127void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader)128{129 unsigned long flags;130 131 spin_lock_irqsave(&sp->lock, flags);132 list_del(&reader->node);133 spin_unlock_irqrestore(&sp->lock, flags);134}135 136int ibmasm_event_buffer_init(struct service_processor *sp)137{138 struct event_buffer *buffer;139 struct ibmasm_event *event;140 int i;141 142 buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL);143 if (!buffer)144 return -ENOMEM;145 146 buffer->next_index = 0;147 buffer->next_serial_number = 1;148 149 event = buffer->events;150 for (i=0; i<IBMASM_NUM_EVENTS; i++, event++)151 event->serial_number = 0;152 153 INIT_LIST_HEAD(&buffer->readers);154 155 sp->event_buffer = buffer;156 157 return 0;158}159 160void ibmasm_event_buffer_exit(struct service_processor *sp)161{162 kfree(sp->event_buffer);163}164