xref: /openbmc/linux/drivers/misc/ibmasm/event.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds 
2*1da177e4SLinus Torvalds /*
3*1da177e4SLinus Torvalds  * IBM ASM Service Processor Device Driver
4*1da177e4SLinus Torvalds  *
5*1da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
6*1da177e4SLinus Torvalds  * it under the terms of the GNU General Public License as published by
7*1da177e4SLinus Torvalds  * the Free Software Foundation; either version 2 of the License, or
8*1da177e4SLinus Torvalds  * (at your option) any later version.
9*1da177e4SLinus Torvalds  *
10*1da177e4SLinus Torvalds  * This program is distributed in the hope that it will be useful,
11*1da177e4SLinus Torvalds  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*1da177e4SLinus Torvalds  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*1da177e4SLinus Torvalds  * GNU General Public License for more details.
14*1da177e4SLinus Torvalds  *
15*1da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
16*1da177e4SLinus Torvalds  * along with this program; if not, write to the Free Software
17*1da177e4SLinus Torvalds  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18*1da177e4SLinus Torvalds  *
19*1da177e4SLinus Torvalds  * Copyright (C) IBM Corporation, 2004
20*1da177e4SLinus Torvalds  *
21*1da177e4SLinus Torvalds  * Author: Max Asb�ck <amax@us.ibm.com>
22*1da177e4SLinus Torvalds  *
23*1da177e4SLinus Torvalds  */
24*1da177e4SLinus Torvalds 
25*1da177e4SLinus Torvalds #include "ibmasm.h"
26*1da177e4SLinus Torvalds 
27*1da177e4SLinus Torvalds /*
28*1da177e4SLinus Torvalds  * ASM service processor event handling routines.
29*1da177e4SLinus Torvalds  *
30*1da177e4SLinus Torvalds  * Events are signalled to the device drivers through interrupts.
31*1da177e4SLinus Torvalds  * They have the format of dot commands, with the type field set to
32*1da177e4SLinus Torvalds  * sp_event.
33*1da177e4SLinus Torvalds  * The driver does not interpret the events, it simply stores them in a
34*1da177e4SLinus Torvalds  * circular buffer.
35*1da177e4SLinus Torvalds  */
36*1da177e4SLinus Torvalds 
37*1da177e4SLinus Torvalds 
38*1da177e4SLinus Torvalds static void wake_up_event_readers(struct service_processor *sp)
39*1da177e4SLinus Torvalds {
40*1da177e4SLinus Torvalds 	struct event_reader *reader;
41*1da177e4SLinus Torvalds 
42*1da177e4SLinus Torvalds 	list_for_each_entry(reader, &sp->event_buffer->readers, node)
43*1da177e4SLinus Torvalds                 wake_up_interruptible(&reader->wait);
44*1da177e4SLinus Torvalds }
45*1da177e4SLinus Torvalds 
46*1da177e4SLinus Torvalds /**
47*1da177e4SLinus Torvalds  * receive_event
48*1da177e4SLinus Torvalds  * Called by the interrupt handler when a dot command of type sp_event is
49*1da177e4SLinus Torvalds  * received.
50*1da177e4SLinus Torvalds  * Store the event in the circular event buffer, wake up any sleeping
51*1da177e4SLinus Torvalds  * event readers.
52*1da177e4SLinus Torvalds  * There is no reader marker in the buffer, therefore readers are
53*1da177e4SLinus Torvalds  * responsible for keeping up with the writer, or they will loose events.
54*1da177e4SLinus Torvalds  */
55*1da177e4SLinus Torvalds void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size)
56*1da177e4SLinus Torvalds {
57*1da177e4SLinus Torvalds 	struct event_buffer *buffer = sp->event_buffer;
58*1da177e4SLinus Torvalds 	struct ibmasm_event *event;
59*1da177e4SLinus Torvalds 	unsigned long flags;
60*1da177e4SLinus Torvalds 
61*1da177e4SLinus Torvalds 	data_size = min(data_size, IBMASM_EVENT_MAX_SIZE);
62*1da177e4SLinus Torvalds 
63*1da177e4SLinus Torvalds 	spin_lock_irqsave(&sp->lock, flags);
64*1da177e4SLinus Torvalds 	/* copy the event into the next slot in the circular buffer */
65*1da177e4SLinus Torvalds 	event = &buffer->events[buffer->next_index];
66*1da177e4SLinus Torvalds 	memcpy(event->data, data, data_size);
67*1da177e4SLinus Torvalds 	event->data_size = data_size;
68*1da177e4SLinus Torvalds 	event->serial_number = buffer->next_serial_number;
69*1da177e4SLinus Torvalds 
70*1da177e4SLinus Torvalds 	/* advance indices in the buffer */
71*1da177e4SLinus Torvalds 	buffer->next_index = (buffer->next_index + 1) % IBMASM_NUM_EVENTS;
72*1da177e4SLinus Torvalds 	buffer->next_serial_number++;
73*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sp->lock, flags);
74*1da177e4SLinus Torvalds 
75*1da177e4SLinus Torvalds 	wake_up_event_readers(sp);
76*1da177e4SLinus Torvalds }
77*1da177e4SLinus Torvalds 
78*1da177e4SLinus Torvalds static inline int event_available(struct event_buffer *b, struct event_reader *r)
79*1da177e4SLinus Torvalds {
80*1da177e4SLinus Torvalds 	return 	(r->next_serial_number < b->next_serial_number);
81*1da177e4SLinus Torvalds }
82*1da177e4SLinus Torvalds 
83*1da177e4SLinus Torvalds /**
84*1da177e4SLinus Torvalds  * get_next_event
85*1da177e4SLinus Torvalds  * Called by event readers (initiated from user space through the file
86*1da177e4SLinus Torvalds  * system).
87*1da177e4SLinus Torvalds  * Sleeps until a new event is available.
88*1da177e4SLinus Torvalds  */
89*1da177e4SLinus Torvalds int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader)
90*1da177e4SLinus Torvalds {
91*1da177e4SLinus Torvalds 	struct event_buffer *buffer = sp->event_buffer;
92*1da177e4SLinus Torvalds 	struct ibmasm_event *event;
93*1da177e4SLinus Torvalds 	unsigned int index;
94*1da177e4SLinus Torvalds 	unsigned long flags;
95*1da177e4SLinus Torvalds 
96*1da177e4SLinus Torvalds 	if (wait_event_interruptible(reader->wait, event_available(buffer, reader)))
97*1da177e4SLinus Torvalds 		return -ERESTARTSYS;
98*1da177e4SLinus Torvalds 
99*1da177e4SLinus Torvalds 	if (!event_available(buffer, reader))
100*1da177e4SLinus Torvalds 		return 0;
101*1da177e4SLinus Torvalds 
102*1da177e4SLinus Torvalds 	spin_lock_irqsave(&sp->lock, flags);
103*1da177e4SLinus Torvalds 
104*1da177e4SLinus Torvalds 	index = buffer->next_index;
105*1da177e4SLinus Torvalds 	event = &buffer->events[index];
106*1da177e4SLinus Torvalds 	while (event->serial_number < reader->next_serial_number) {
107*1da177e4SLinus Torvalds 		index = (index + 1) % IBMASM_NUM_EVENTS;
108*1da177e4SLinus Torvalds 		event = &buffer->events[index];
109*1da177e4SLinus Torvalds 	}
110*1da177e4SLinus Torvalds 	memcpy(reader->data, event->data, event->data_size);
111*1da177e4SLinus Torvalds 	reader->data_size = event->data_size;
112*1da177e4SLinus Torvalds 	reader->next_serial_number = event->serial_number + 1;
113*1da177e4SLinus Torvalds 
114*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sp->lock, flags);
115*1da177e4SLinus Torvalds 
116*1da177e4SLinus Torvalds 	return event->data_size;
117*1da177e4SLinus Torvalds }
118*1da177e4SLinus Torvalds 
119*1da177e4SLinus Torvalds void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader)
120*1da177e4SLinus Torvalds {
121*1da177e4SLinus Torvalds 	unsigned long flags;
122*1da177e4SLinus Torvalds 
123*1da177e4SLinus Torvalds 	reader->next_serial_number = sp->event_buffer->next_serial_number;
124*1da177e4SLinus Torvalds 	init_waitqueue_head(&reader->wait);
125*1da177e4SLinus Torvalds 	spin_lock_irqsave(&sp->lock, flags);
126*1da177e4SLinus Torvalds 	list_add(&reader->node, &sp->event_buffer->readers);
127*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sp->lock, flags);
128*1da177e4SLinus Torvalds }
129*1da177e4SLinus Torvalds 
130*1da177e4SLinus Torvalds void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader)
131*1da177e4SLinus Torvalds {
132*1da177e4SLinus Torvalds 	unsigned long flags;
133*1da177e4SLinus Torvalds 
134*1da177e4SLinus Torvalds 	wake_up_interruptible(&reader->wait);
135*1da177e4SLinus Torvalds 
136*1da177e4SLinus Torvalds 	spin_lock_irqsave(&sp->lock, flags);
137*1da177e4SLinus Torvalds 	list_del(&reader->node);
138*1da177e4SLinus Torvalds 	spin_unlock_irqrestore(&sp->lock, flags);
139*1da177e4SLinus Torvalds }
140*1da177e4SLinus Torvalds 
141*1da177e4SLinus Torvalds int ibmasm_event_buffer_init(struct service_processor *sp)
142*1da177e4SLinus Torvalds {
143*1da177e4SLinus Torvalds 	struct event_buffer *buffer;
144*1da177e4SLinus Torvalds 	struct ibmasm_event *event;
145*1da177e4SLinus Torvalds 	int i;
146*1da177e4SLinus Torvalds 
147*1da177e4SLinus Torvalds 	buffer = kmalloc(sizeof(struct event_buffer), GFP_KERNEL);
148*1da177e4SLinus Torvalds 	if (!buffer)
149*1da177e4SLinus Torvalds 		return 1;
150*1da177e4SLinus Torvalds 
151*1da177e4SLinus Torvalds 	buffer->next_index = 0;
152*1da177e4SLinus Torvalds 	buffer->next_serial_number = 1;
153*1da177e4SLinus Torvalds 
154*1da177e4SLinus Torvalds 	event = buffer->events;
155*1da177e4SLinus Torvalds 	for (i=0; i<IBMASM_NUM_EVENTS; i++, event++)
156*1da177e4SLinus Torvalds 		event->serial_number = 0;
157*1da177e4SLinus Torvalds 
158*1da177e4SLinus Torvalds 	INIT_LIST_HEAD(&buffer->readers);
159*1da177e4SLinus Torvalds 
160*1da177e4SLinus Torvalds 	sp->event_buffer = buffer;
161*1da177e4SLinus Torvalds 
162*1da177e4SLinus Torvalds 	return 0;
163*1da177e4SLinus Torvalds }
164*1da177e4SLinus Torvalds 
165*1da177e4SLinus Torvalds void ibmasm_event_buffer_exit(struct service_processor *sp)
166*1da177e4SLinus Torvalds {
167*1da177e4SLinus Torvalds 	wake_up_event_readers(sp);
168*1da177e4SLinus Torvalds 	kfree(sp->event_buffer);
169*1da177e4SLinus Torvalds }
170