1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | /* |
4 | * IBM ASM Service Processor Device Driver |
5 | * |
6 | * Copyright (C) IBM Corporation, 2004 |
7 | * |
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 to |
21 | * sp_event. |
22 | * The driver does not interpret the events, it simply stores them in a |
23 | * circular buffer. |
24 | */ |
25 | |
26 | static 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_event |
36 | * Called by the interrupt handler when a dot command of type sp_event is |
37 | * received. |
38 | * Store the event in the circular event buffer, wake up any sleeping |
39 | * event readers. |
40 | * There is no reader marker in the buffer, therefore readers are |
41 | * responsible for keeping up with the writer, or they will lose events. |
42 | */ |
43 | void 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(lock: &sp->lock, flags); |
62 | |
63 | wake_up_event_readers(sp); |
64 | } |
65 | |
66 | static 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_event |
73 | * Called by event readers (initiated from user space through the file |
74 | * system). |
75 | * Sleeps until a new event is available. |
76 | */ |
77 | int 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(b: buffer, r: 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(lock: &sp->lock, flags); |
106 | |
107 | return event->data_size; |
108 | } |
109 | |
110 | void ibmasm_cancel_next_event(struct event_reader *reader) |
111 | { |
112 | reader->cancelled = 1; |
113 | wake_up_interruptible(&reader->wait); |
114 | } |
115 | |
116 | void 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(new: &reader->node, head: &sp->event_buffer->readers); |
124 | spin_unlock_irqrestore(lock: &sp->lock, flags); |
125 | } |
126 | |
127 | void 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(entry: &reader->node); |
133 | spin_unlock_irqrestore(lock: &sp->lock, flags); |
134 | } |
135 | |
136 | int 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(size: 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(list: &buffer->readers); |
154 | |
155 | sp->event_buffer = buffer; |
156 | |
157 | return 0; |
158 | } |
159 | |
160 | void ibmasm_event_buffer_exit(struct service_processor *sp) |
161 | { |
162 | kfree(objp: sp->event_buffer); |
163 | } |
164 | |