18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * IBM ASM Service Processor Device Driver 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation, 2004 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Max Asböck <amax@us.ibm.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/sched.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include "ibmasm.h" 148c2ecf20Sopenharmony_ci#include "lowlevel.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* 178c2ecf20Sopenharmony_ci * ASM service processor event handling routines. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Events are signalled to the device drivers through interrupts. 208c2ecf20Sopenharmony_ci * They have the format of dot commands, with the type field set to 218c2ecf20Sopenharmony_ci * sp_event. 228c2ecf20Sopenharmony_ci * The driver does not interpret the events, it simply stores them in a 238c2ecf20Sopenharmony_ci * circular buffer. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void wake_up_event_readers(struct service_processor *sp) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct event_reader *reader; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci list_for_each_entry(reader, &sp->event_buffer->readers, node) 318c2ecf20Sopenharmony_ci wake_up_interruptible(&reader->wait); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * receive_event 368c2ecf20Sopenharmony_ci * Called by the interrupt handler when a dot command of type sp_event is 378c2ecf20Sopenharmony_ci * received. 388c2ecf20Sopenharmony_ci * Store the event in the circular event buffer, wake up any sleeping 398c2ecf20Sopenharmony_ci * event readers. 408c2ecf20Sopenharmony_ci * There is no reader marker in the buffer, therefore readers are 418c2ecf20Sopenharmony_ci * responsible for keeping up with the writer, or they will lose events. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_civoid ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct event_buffer *buffer = sp->event_buffer; 468c2ecf20Sopenharmony_ci struct ibmasm_event *event; 478c2ecf20Sopenharmony_ci unsigned long flags; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci data_size = min(data_size, IBMASM_EVENT_MAX_SIZE); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 528c2ecf20Sopenharmony_ci /* copy the event into the next slot in the circular buffer */ 538c2ecf20Sopenharmony_ci event = &buffer->events[buffer->next_index]; 548c2ecf20Sopenharmony_ci memcpy_fromio(event->data, data, data_size); 558c2ecf20Sopenharmony_ci event->data_size = data_size; 568c2ecf20Sopenharmony_ci event->serial_number = buffer->next_serial_number; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* advance indices in the buffer */ 598c2ecf20Sopenharmony_ci buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS; 608c2ecf20Sopenharmony_ci buffer->next_serial_number++; 618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci wake_up_event_readers(sp); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic inline int event_available(struct event_buffer *b, struct event_reader *r) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci return (r->next_serial_number < b->next_serial_number); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * get_next_event 738c2ecf20Sopenharmony_ci * Called by event readers (initiated from user space through the file 748c2ecf20Sopenharmony_ci * system). 758c2ecf20Sopenharmony_ci * Sleeps until a new event is available. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ciint ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct event_buffer *buffer = sp->event_buffer; 808c2ecf20Sopenharmony_ci struct ibmasm_event *event; 818c2ecf20Sopenharmony_ci unsigned int index; 828c2ecf20Sopenharmony_ci unsigned long flags; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci reader->cancelled = 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (wait_event_interruptible(reader->wait, 878c2ecf20Sopenharmony_ci event_available(buffer, reader) || reader->cancelled)) 888c2ecf20Sopenharmony_ci return -ERESTARTSYS; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (!event_available(buffer, reader)) 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci index = buffer->next_index; 968c2ecf20Sopenharmony_ci event = &buffer->events[index]; 978c2ecf20Sopenharmony_ci while (event->serial_number < reader->next_serial_number) { 988c2ecf20Sopenharmony_ci index = (index + 1) % IBMASM_NUM_EVENTS; 998c2ecf20Sopenharmony_ci event = &buffer->events[index]; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci memcpy(reader->data, event->data, event->data_size); 1028c2ecf20Sopenharmony_ci reader->data_size = event->data_size; 1038c2ecf20Sopenharmony_ci reader->next_serial_number = event->serial_number + 1; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return event->data_size; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_civoid ibmasm_cancel_next_event(struct event_reader *reader) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci reader->cancelled = 1; 1138c2ecf20Sopenharmony_ci wake_up_interruptible(&reader->wait); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_civoid ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci unsigned long flags; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci reader->next_serial_number = sp->event_buffer->next_serial_number; 1218c2ecf20Sopenharmony_ci init_waitqueue_head(&reader->wait); 1228c2ecf20Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 1238c2ecf20Sopenharmony_ci list_add(&reader->node, &sp->event_buffer->readers); 1248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_civoid ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci unsigned long flags; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci spin_lock_irqsave(&sp->lock, flags); 1328c2ecf20Sopenharmony_ci list_del(&reader->node); 1338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sp->lock, flags); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciint ibmasm_event_buffer_init(struct service_processor *sp) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct event_buffer *buffer; 1398c2ecf20Sopenharmony_ci struct ibmasm_event *event; 1408c2ecf20Sopenharmony_ci int i; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL); 1438c2ecf20Sopenharmony_ci if (!buffer) 1448c2ecf20Sopenharmony_ci return -ENOMEM; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci buffer->next_index = 0; 1478c2ecf20Sopenharmony_ci buffer->next_serial_number = 1; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci event = buffer->events; 1508c2ecf20Sopenharmony_ci for (i=0; i<IBMASM_NUM_EVENTS; i++, event++) 1518c2ecf20Sopenharmony_ci event->serial_number = 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&buffer->readers); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci sp->event_buffer = buffer; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_civoid ibmasm_event_buffer_exit(struct service_processor *sp) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci kfree(sp->event_buffer); 1638c2ecf20Sopenharmony_ci} 164