1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0-or-later */
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun /*
4*4882a593Smuzhiyun * IBM ASM Service Processor Device Driver
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) IBM Corporation, 2004
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Author: Max Asböck <amax@us.ibm.com>
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/types.h>
13*4882a593Smuzhiyun #include <linux/errno.h>
14*4882a593Smuzhiyun #include <linux/list.h>
15*4882a593Smuzhiyun #include <linux/wait.h>
16*4882a593Smuzhiyun #include <linux/spinlock.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/interrupt.h>
20*4882a593Smuzhiyun #include <linux/kref.h>
21*4882a593Smuzhiyun #include <linux/device.h>
22*4882a593Smuzhiyun #include <linux/input.h>
23*4882a593Smuzhiyun #include <linux/time64.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /* Driver identification */
26*4882a593Smuzhiyun #define DRIVER_NAME "ibmasm"
27*4882a593Smuzhiyun #define DRIVER_VERSION "1.0"
28*4882a593Smuzhiyun #define DRIVER_AUTHOR "Max Asbock <masbock@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
29*4882a593Smuzhiyun #define DRIVER_DESC "IBM ASM Service Processor Driver"
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun #define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
32*4882a593Smuzhiyun #define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME)
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun extern int ibmasm_debug;
35*4882a593Smuzhiyun #define dbg(STR, ARGS...) \
36*4882a593Smuzhiyun do { \
37*4882a593Smuzhiyun if (ibmasm_debug) \
38*4882a593Smuzhiyun printk(KERN_DEBUG STR , ##ARGS); \
39*4882a593Smuzhiyun } while (0)
40*4882a593Smuzhiyun
get_timestamp(char * buf)41*4882a593Smuzhiyun static inline char *get_timestamp(char *buf)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun struct timespec64 now;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun ktime_get_real_ts64(&now);
46*4882a593Smuzhiyun sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec,
47*4882a593Smuzhiyun now.tv_nsec / NSEC_PER_USEC);
48*4882a593Smuzhiyun return buf;
49*4882a593Smuzhiyun }
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun #define IBMASM_CMD_PENDING 0
52*4882a593Smuzhiyun #define IBMASM_CMD_COMPLETE 1
53*4882a593Smuzhiyun #define IBMASM_CMD_FAILED 2
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun #define IBMASM_CMD_TIMEOUT_NORMAL 45
56*4882a593Smuzhiyun #define IBMASM_CMD_TIMEOUT_EXTRA 240
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun #define IBMASM_CMD_MAX_BUFFER_SIZE 0x8000
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun #define REVERSE_HEARTBEAT_TIMEOUT 120
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun #define HEARTBEAT_BUFFER_SIZE 0x400
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun #ifdef IA64
65*4882a593Smuzhiyun #define IBMASM_DRIVER_VPD "Lin64 6.08 "
66*4882a593Smuzhiyun #else
67*4882a593Smuzhiyun #define IBMASM_DRIVER_VPD "Lin32 6.08 "
68*4882a593Smuzhiyun #endif
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun #define SYSTEM_STATE_OS_UP 5
71*4882a593Smuzhiyun #define SYSTEM_STATE_OS_DOWN 4
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun #define IBMASM_NAME_SIZE 16
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun #define IBMASM_NUM_EVENTS 10
76*4882a593Smuzhiyun #define IBMASM_EVENT_MAX_SIZE 2048u
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun struct command {
80*4882a593Smuzhiyun struct list_head queue_node;
81*4882a593Smuzhiyun wait_queue_head_t wait;
82*4882a593Smuzhiyun unsigned char *buffer;
83*4882a593Smuzhiyun size_t buffer_size;
84*4882a593Smuzhiyun int status;
85*4882a593Smuzhiyun struct kref kref;
86*4882a593Smuzhiyun spinlock_t *lock;
87*4882a593Smuzhiyun };
88*4882a593Smuzhiyun #define to_command(c) container_of(c, struct command, kref)
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun void ibmasm_free_command(struct kref *kref);
command_put(struct command * cmd)91*4882a593Smuzhiyun static inline void command_put(struct command *cmd)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun unsigned long flags;
94*4882a593Smuzhiyun spinlock_t *lock = cmd->lock;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun spin_lock_irqsave(lock, flags);
97*4882a593Smuzhiyun kref_put(&cmd->kref, ibmasm_free_command);
98*4882a593Smuzhiyun spin_unlock_irqrestore(lock, flags);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun
command_get(struct command * cmd)101*4882a593Smuzhiyun static inline void command_get(struct command *cmd)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun kref_get(&cmd->kref);
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun struct ibmasm_event {
108*4882a593Smuzhiyun unsigned int serial_number;
109*4882a593Smuzhiyun unsigned int data_size;
110*4882a593Smuzhiyun unsigned char data[IBMASM_EVENT_MAX_SIZE];
111*4882a593Smuzhiyun };
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun struct event_buffer {
114*4882a593Smuzhiyun struct ibmasm_event events[IBMASM_NUM_EVENTS];
115*4882a593Smuzhiyun unsigned int next_serial_number;
116*4882a593Smuzhiyun unsigned int next_index;
117*4882a593Smuzhiyun struct list_head readers;
118*4882a593Smuzhiyun };
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun struct event_reader {
121*4882a593Smuzhiyun int cancelled;
122*4882a593Smuzhiyun unsigned int next_serial_number;
123*4882a593Smuzhiyun wait_queue_head_t wait;
124*4882a593Smuzhiyun struct list_head node;
125*4882a593Smuzhiyun unsigned int data_size;
126*4882a593Smuzhiyun unsigned char data[IBMASM_EVENT_MAX_SIZE];
127*4882a593Smuzhiyun };
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun struct reverse_heartbeat {
130*4882a593Smuzhiyun wait_queue_head_t wait;
131*4882a593Smuzhiyun unsigned int stopped;
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun struct ibmasm_remote {
135*4882a593Smuzhiyun struct input_dev *keybd_dev;
136*4882a593Smuzhiyun struct input_dev *mouse_dev;
137*4882a593Smuzhiyun };
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun struct service_processor {
140*4882a593Smuzhiyun struct list_head node;
141*4882a593Smuzhiyun spinlock_t lock;
142*4882a593Smuzhiyun void __iomem *base_address;
143*4882a593Smuzhiyun unsigned int irq;
144*4882a593Smuzhiyun struct command *current_command;
145*4882a593Smuzhiyun struct command *heartbeat;
146*4882a593Smuzhiyun struct list_head command_queue;
147*4882a593Smuzhiyun struct event_buffer *event_buffer;
148*4882a593Smuzhiyun char dirname[IBMASM_NAME_SIZE];
149*4882a593Smuzhiyun char devname[IBMASM_NAME_SIZE];
150*4882a593Smuzhiyun unsigned int number;
151*4882a593Smuzhiyun struct ibmasm_remote remote;
152*4882a593Smuzhiyun int serial_line;
153*4882a593Smuzhiyun struct device *dev;
154*4882a593Smuzhiyun };
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun /* command processing */
157*4882a593Smuzhiyun struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size);
158*4882a593Smuzhiyun void ibmasm_exec_command(struct service_processor *sp, struct command *cmd);
159*4882a593Smuzhiyun void ibmasm_wait_for_response(struct command *cmd, int timeout);
160*4882a593Smuzhiyun void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size);
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun /* event processing */
163*4882a593Smuzhiyun int ibmasm_event_buffer_init(struct service_processor *sp);
164*4882a593Smuzhiyun void ibmasm_event_buffer_exit(struct service_processor *sp);
165*4882a593Smuzhiyun void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size);
166*4882a593Smuzhiyun void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader);
167*4882a593Smuzhiyun void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader);
168*4882a593Smuzhiyun int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader);
169*4882a593Smuzhiyun void ibmasm_cancel_next_event(struct event_reader *reader);
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun /* heartbeat - from SP to OS */
172*4882a593Smuzhiyun void ibmasm_register_panic_notifier(void);
173*4882a593Smuzhiyun void ibmasm_unregister_panic_notifier(void);
174*4882a593Smuzhiyun int ibmasm_heartbeat_init(struct service_processor *sp);
175*4882a593Smuzhiyun void ibmasm_heartbeat_exit(struct service_processor *sp);
176*4882a593Smuzhiyun void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun /* reverse heartbeat - from OS to SP */
179*4882a593Smuzhiyun void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
180*4882a593Smuzhiyun int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
181*4882a593Smuzhiyun void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb);
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun /* dot commands */
184*4882a593Smuzhiyun void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size);
185*4882a593Smuzhiyun int ibmasm_send_driver_vpd(struct service_processor *sp);
186*4882a593Smuzhiyun int ibmasm_send_os_state(struct service_processor *sp, int os_state);
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun /* low level message processing */
189*4882a593Smuzhiyun int ibmasm_send_i2o_message(struct service_processor *sp);
190*4882a593Smuzhiyun irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id);
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun /* remote console */
193*4882a593Smuzhiyun void ibmasm_handle_mouse_interrupt(struct service_processor *sp);
194*4882a593Smuzhiyun int ibmasm_init_remote_input_dev(struct service_processor *sp);
195*4882a593Smuzhiyun void ibmasm_free_remote_input_dev(struct service_processor *sp);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun /* file system */
198*4882a593Smuzhiyun int ibmasmfs_register(void);
199*4882a593Smuzhiyun void ibmasmfs_unregister(void);
200*4882a593Smuzhiyun void ibmasmfs_add_sp(struct service_processor *sp);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun /* uart */
203*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_SERIAL_8250)
204*4882a593Smuzhiyun void ibmasm_register_uart(struct service_processor *sp);
205*4882a593Smuzhiyun void ibmasm_unregister_uart(struct service_processor *sp);
206*4882a593Smuzhiyun #else
207*4882a593Smuzhiyun #define ibmasm_register_uart(sp) do { } while(0)
208*4882a593Smuzhiyun #define ibmasm_unregister_uart(sp) do { } while(0)
209*4882a593Smuzhiyun #endif
210