Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | |
2 | /* | |
3 | * IBM ASM Service Processor Device Driver | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | * | |
19 | * Copyright (C) IBM Corporation, 2004 | |
20 | * | |
d36b6910 | 21 | * Author: Max Asböck <amax@us.ibm.com> |
1da177e4 LT |
22 | * |
23 | */ | |
24 | ||
25 | #include <linux/kernel.h> | |
26 | #include <linux/types.h> | |
27 | #include <linux/errno.h> | |
28 | #include <linux/list.h> | |
29 | #include <linux/wait.h> | |
30 | #include <linux/spinlock.h> | |
31 | #include <linux/slab.h> | |
1da177e4 | 32 | #include <linux/module.h> |
1da177e4 | 33 | #include <linux/interrupt.h> |
a045171f | 34 | #include <linux/kref.h> |
1da177e4 | 35 | #include <linux/device.h> |
278d72ae | 36 | #include <linux/input.h> |
46d83a87 | 37 | #include <linux/time64.h> |
1da177e4 LT |
38 | |
39 | /* Driver identification */ | |
40 | #define DRIVER_NAME "ibmasm" | |
278d72ae MA |
41 | #define DRIVER_VERSION "1.0" |
42 | #define DRIVER_AUTHOR "Max Asbock <masbock@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>" | |
1da177e4 LT |
43 | #define DRIVER_DESC "IBM ASM Service Processor Driver" |
44 | ||
45 | #define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME) | |
46 | #define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME) | |
47 | ||
278d72ae MA |
48 | extern int ibmasm_debug; |
49 | #define dbg(STR, ARGS...) \ | |
50 | do { \ | |
51 | if (ibmasm_debug) \ | |
52 | printk(KERN_DEBUG STR , ##ARGS); \ | |
53 | } while (0) | |
54 | ||
55 | static inline char *get_timestamp(char *buf) | |
56 | { | |
46d83a87 AKC |
57 | struct timespec64 now; |
58 | ||
59 | ktime_get_real_ts64(&now); | |
60 | sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec, | |
61 | now.tv_nsec / NSEC_PER_USEC); | |
278d72ae MA |
62 | return buf; |
63 | } | |
1da177e4 | 64 | |
3110dc7a DT |
65 | #define IBMASM_CMD_PENDING 0 |
66 | #define IBMASM_CMD_COMPLETE 1 | |
1da177e4 LT |
67 | #define IBMASM_CMD_FAILED 2 |
68 | ||
69 | #define IBMASM_CMD_TIMEOUT_NORMAL 45 | |
70 | #define IBMASM_CMD_TIMEOUT_EXTRA 240 | |
71 | ||
f5ccc842 | 72 | #define IBMASM_CMD_MAX_BUFFER_SIZE 0x8000 |
1da177e4 LT |
73 | |
74 | #define REVERSE_HEARTBEAT_TIMEOUT 120 | |
75 | ||
76 | #define HEARTBEAT_BUFFER_SIZE 0x400 | |
77 | ||
78 | #ifdef IA64 | |
79 | #define IBMASM_DRIVER_VPD "Lin64 6.08 " | |
80 | #else | |
81 | #define IBMASM_DRIVER_VPD "Lin32 6.08 " | |
82 | #endif | |
83 | ||
84 | #define SYSTEM_STATE_OS_UP 5 | |
85 | #define SYSTEM_STATE_OS_DOWN 4 | |
86 | ||
87 | #define IBMASM_NAME_SIZE 16 | |
88 | ||
89 | #define IBMASM_NUM_EVENTS 10 | |
90 | #define IBMASM_EVENT_MAX_SIZE 2048u | |
91 | ||
92 | ||
93 | struct command { | |
94 | struct list_head queue_node; | |
95 | wait_queue_head_t wait; | |
96 | unsigned char *buffer; | |
97 | size_t buffer_size; | |
98 | int status; | |
a045171f | 99 | struct kref kref; |
88187605 | 100 | spinlock_t *lock; |
1da177e4 | 101 | }; |
a045171f | 102 | #define to_command(c) container_of(c, struct command, kref) |
1da177e4 | 103 | |
a045171f | 104 | void ibmasm_free_command(struct kref *kref); |
1da177e4 LT |
105 | static inline void command_put(struct command *cmd) |
106 | { | |
88187605 | 107 | unsigned long flags; |
6a88231f | 108 | spinlock_t *lock = cmd->lock; |
88187605 | 109 | |
6a88231f | 110 | spin_lock_irqsave(lock, flags); |
a045171f | 111 | kref_put(&cmd->kref, ibmasm_free_command); |
6a88231f | 112 | spin_unlock_irqrestore(lock, flags); |
1da177e4 LT |
113 | } |
114 | ||
115 | static inline void command_get(struct command *cmd) | |
116 | { | |
a045171f | 117 | kref_get(&cmd->kref); |
1da177e4 LT |
118 | } |
119 | ||
120 | ||
121 | struct ibmasm_event { | |
122 | unsigned int serial_number; | |
123 | unsigned int data_size; | |
124 | unsigned char data[IBMASM_EVENT_MAX_SIZE]; | |
125 | }; | |
126 | ||
127 | struct event_buffer { | |
128 | struct ibmasm_event events[IBMASM_NUM_EVENTS]; | |
129 | unsigned int next_serial_number; | |
130 | unsigned int next_index; | |
131 | struct list_head readers; | |
132 | }; | |
133 | ||
134 | struct event_reader { | |
b8acb808 | 135 | int cancelled; |
1da177e4 LT |
136 | unsigned int next_serial_number; |
137 | wait_queue_head_t wait; | |
138 | struct list_head node; | |
139 | unsigned int data_size; | |
140 | unsigned char data[IBMASM_EVENT_MAX_SIZE]; | |
141 | }; | |
142 | ||
143 | struct reverse_heartbeat { | |
144 | wait_queue_head_t wait; | |
145 | unsigned int stopped; | |
146 | }; | |
147 | ||
278d72ae | 148 | struct ibmasm_remote { |
736ce432 VM |
149 | struct input_dev *keybd_dev; |
150 | struct input_dev *mouse_dev; | |
1da177e4 LT |
151 | }; |
152 | ||
1da177e4 LT |
153 | struct service_processor { |
154 | struct list_head node; | |
155 | spinlock_t lock; | |
156 | void __iomem *base_address; | |
157 | unsigned int irq; | |
158 | struct command *current_command; | |
159 | struct command *heartbeat; | |
160 | struct list_head command_queue; | |
161 | struct event_buffer *event_buffer; | |
162 | char dirname[IBMASM_NAME_SIZE]; | |
163 | char devname[IBMASM_NAME_SIZE]; | |
164 | unsigned int number; | |
736ce432 | 165 | struct ibmasm_remote remote; |
1da177e4 LT |
166 | int serial_line; |
167 | struct device *dev; | |
168 | }; | |
169 | ||
170 | /* command processing */ | |
da6b9c92 DT |
171 | struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size); |
172 | void ibmasm_exec_command(struct service_processor *sp, struct command *cmd); | |
173 | void ibmasm_wait_for_response(struct command *cmd, int timeout); | |
174 | void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size); | |
1da177e4 LT |
175 | |
176 | /* event processing */ | |
da6b9c92 DT |
177 | int ibmasm_event_buffer_init(struct service_processor *sp); |
178 | void ibmasm_event_buffer_exit(struct service_processor *sp); | |
179 | void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size); | |
180 | void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader); | |
181 | void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader); | |
182 | int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader); | |
183 | void ibmasm_cancel_next_event(struct event_reader *reader); | |
1da177e4 LT |
184 | |
185 | /* heartbeat - from SP to OS */ | |
da6b9c92 DT |
186 | void ibmasm_register_panic_notifier(void); |
187 | void ibmasm_unregister_panic_notifier(void); | |
188 | int ibmasm_heartbeat_init(struct service_processor *sp); | |
189 | void ibmasm_heartbeat_exit(struct service_processor *sp); | |
190 | void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size); | |
1da177e4 LT |
191 | |
192 | /* reverse heartbeat - from OS to SP */ | |
da6b9c92 DT |
193 | void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); |
194 | int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb); | |
195 | void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb); | |
1da177e4 LT |
196 | |
197 | /* dot commands */ | |
da6b9c92 DT |
198 | void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size); |
199 | int ibmasm_send_driver_vpd(struct service_processor *sp); | |
200 | int ibmasm_send_os_state(struct service_processor *sp, int os_state); | |
1da177e4 LT |
201 | |
202 | /* low level message processing */ | |
da6b9c92 DT |
203 | int ibmasm_send_i2o_message(struct service_processor *sp); |
204 | irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id); | |
1da177e4 LT |
205 | |
206 | /* remote console */ | |
da6b9c92 DT |
207 | void ibmasm_handle_mouse_interrupt(struct service_processor *sp); |
208 | int ibmasm_init_remote_input_dev(struct service_processor *sp); | |
209 | void ibmasm_free_remote_input_dev(struct service_processor *sp); | |
1da177e4 LT |
210 | |
211 | /* file system */ | |
da6b9c92 DT |
212 | int ibmasmfs_register(void); |
213 | void ibmasmfs_unregister(void); | |
214 | void ibmasmfs_add_sp(struct service_processor *sp); | |
1da177e4 LT |
215 | |
216 | /* uart */ | |
cf4f2193 | 217 | #if IS_ENABLED(CONFIG_SERIAL_8250) |
da6b9c92 DT |
218 | void ibmasm_register_uart(struct service_processor *sp); |
219 | void ibmasm_unregister_uart(struct service_processor *sp); | |
1da177e4 LT |
220 | #else |
221 | #define ibmasm_register_uart(sp) do { } while(0) | |
222 | #define ibmasm_unregister_uart(sp) do { } while(0) | |
223 | #endif |