1*1a59d1b8SThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-or-later */
21da177e4SLinus Torvalds
31da177e4SLinus Torvalds /*
41da177e4SLinus Torvalds * IBM ASM Service Processor Device Driver
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * Copyright (C) IBM Corporation, 2004
71da177e4SLinus Torvalds *
8d36b6910SAl Viro * Author: Max Asböck <amax@us.ibm.com>
91da177e4SLinus Torvalds */
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds #include <linux/kernel.h>
121da177e4SLinus Torvalds #include <linux/types.h>
131da177e4SLinus Torvalds #include <linux/errno.h>
141da177e4SLinus Torvalds #include <linux/list.h>
151da177e4SLinus Torvalds #include <linux/wait.h>
161da177e4SLinus Torvalds #include <linux/spinlock.h>
171da177e4SLinus Torvalds #include <linux/slab.h>
181da177e4SLinus Torvalds #include <linux/module.h>
191da177e4SLinus Torvalds #include <linux/interrupt.h>
20a045171fSGreg Kroah-Hartman #include <linux/kref.h>
211da177e4SLinus Torvalds #include <linux/device.h>
22278d72aeSMax Asbock #include <linux/input.h>
2346d83a87SAmitoj Kaur Chawla #include <linux/time64.h>
241da177e4SLinus Torvalds
251da177e4SLinus Torvalds /* Driver identification */
261da177e4SLinus Torvalds #define DRIVER_NAME "ibmasm"
27278d72aeSMax Asbock #define DRIVER_VERSION "1.0"
28278d72aeSMax Asbock #define DRIVER_AUTHOR "Max Asbock <masbock@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
291da177e4SLinus Torvalds #define DRIVER_DESC "IBM ASM Service Processor Driver"
301da177e4SLinus Torvalds
311da177e4SLinus Torvalds #define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
321da177e4SLinus Torvalds #define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME)
331da177e4SLinus Torvalds
34278d72aeSMax Asbock extern int ibmasm_debug;
35278d72aeSMax Asbock #define dbg(STR, ARGS...) \
36278d72aeSMax Asbock do { \
37278d72aeSMax Asbock if (ibmasm_debug) \
38278d72aeSMax Asbock printk(KERN_DEBUG STR , ##ARGS); \
39278d72aeSMax Asbock } while (0)
40278d72aeSMax Asbock
get_timestamp(char * buf)41278d72aeSMax Asbock static inline char *get_timestamp(char *buf)
42278d72aeSMax Asbock {
4346d83a87SAmitoj Kaur Chawla struct timespec64 now;
4446d83a87SAmitoj Kaur Chawla
4546d83a87SAmitoj Kaur Chawla ktime_get_real_ts64(&now);
4646d83a87SAmitoj Kaur Chawla sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec,
4746d83a87SAmitoj Kaur Chawla now.tv_nsec / NSEC_PER_USEC);
48278d72aeSMax Asbock return buf;
49278d72aeSMax Asbock }
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds #define IBMASM_CMD_PENDING 0
521da177e4SLinus Torvalds #define IBMASM_CMD_COMPLETE 1
531da177e4SLinus Torvalds #define IBMASM_CMD_FAILED 2
541da177e4SLinus Torvalds
551da177e4SLinus Torvalds #define IBMASM_CMD_TIMEOUT_NORMAL 45
561da177e4SLinus Torvalds #define IBMASM_CMD_TIMEOUT_EXTRA 240
571da177e4SLinus Torvalds
58f5ccc842SMax Asbock #define IBMASM_CMD_MAX_BUFFER_SIZE 0x8000
591da177e4SLinus Torvalds
601da177e4SLinus Torvalds #define REVERSE_HEARTBEAT_TIMEOUT 120
611da177e4SLinus Torvalds
621da177e4SLinus Torvalds #define HEARTBEAT_BUFFER_SIZE 0x400
631da177e4SLinus Torvalds
641da177e4SLinus Torvalds #ifdef IA64
651da177e4SLinus Torvalds #define IBMASM_DRIVER_VPD "Lin64 6.08 "
661da177e4SLinus Torvalds #else
671da177e4SLinus Torvalds #define IBMASM_DRIVER_VPD "Lin32 6.08 "
681da177e4SLinus Torvalds #endif
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds #define SYSTEM_STATE_OS_UP 5
711da177e4SLinus Torvalds #define SYSTEM_STATE_OS_DOWN 4
721da177e4SLinus Torvalds
731da177e4SLinus Torvalds #define IBMASM_NAME_SIZE 16
741da177e4SLinus Torvalds
751da177e4SLinus Torvalds #define IBMASM_NUM_EVENTS 10
761da177e4SLinus Torvalds #define IBMASM_EVENT_MAX_SIZE 2048u
771da177e4SLinus Torvalds
781da177e4SLinus Torvalds
791da177e4SLinus Torvalds struct command {
801da177e4SLinus Torvalds struct list_head queue_node;
811da177e4SLinus Torvalds wait_queue_head_t wait;
821da177e4SLinus Torvalds unsigned char *buffer;
831da177e4SLinus Torvalds size_t buffer_size;
841da177e4SLinus Torvalds int status;
85a045171fSGreg Kroah-Hartman struct kref kref;
8688187605SMax Asbock spinlock_t *lock;
871da177e4SLinus Torvalds };
88a045171fSGreg Kroah-Hartman #define to_command(c) container_of(c, struct command, kref)
891da177e4SLinus Torvalds
90a045171fSGreg Kroah-Hartman void ibmasm_free_command(struct kref *kref);
command_put(struct command * cmd)911da177e4SLinus Torvalds static inline void command_put(struct command *cmd)
921da177e4SLinus Torvalds {
9388187605SMax Asbock unsigned long flags;
946a88231fSMax Asbock spinlock_t *lock = cmd->lock;
9588187605SMax Asbock
966a88231fSMax Asbock spin_lock_irqsave(lock, flags);
97a045171fSGreg Kroah-Hartman kref_put(&cmd->kref, ibmasm_free_command);
986a88231fSMax Asbock spin_unlock_irqrestore(lock, flags);
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds
command_get(struct command * cmd)1011da177e4SLinus Torvalds static inline void command_get(struct command *cmd)
1021da177e4SLinus Torvalds {
103a045171fSGreg Kroah-Hartman kref_get(&cmd->kref);
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds
1061da177e4SLinus Torvalds
1071da177e4SLinus Torvalds struct ibmasm_event {
1081da177e4SLinus Torvalds unsigned int serial_number;
1091da177e4SLinus Torvalds unsigned int data_size;
1101da177e4SLinus Torvalds unsigned char data[IBMASM_EVENT_MAX_SIZE];
1111da177e4SLinus Torvalds };
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds struct event_buffer {
1141da177e4SLinus Torvalds struct ibmasm_event events[IBMASM_NUM_EVENTS];
1151da177e4SLinus Torvalds unsigned int next_serial_number;
1161da177e4SLinus Torvalds unsigned int next_index;
1171da177e4SLinus Torvalds struct list_head readers;
1181da177e4SLinus Torvalds };
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds struct event_reader {
121b8acb808SMax Asbock int cancelled;
1221da177e4SLinus Torvalds unsigned int next_serial_number;
1231da177e4SLinus Torvalds wait_queue_head_t wait;
1241da177e4SLinus Torvalds struct list_head node;
1251da177e4SLinus Torvalds unsigned int data_size;
1261da177e4SLinus Torvalds unsigned char data[IBMASM_EVENT_MAX_SIZE];
1271da177e4SLinus Torvalds };
1281da177e4SLinus Torvalds
1291da177e4SLinus Torvalds struct reverse_heartbeat {
1301da177e4SLinus Torvalds wait_queue_head_t wait;
1311da177e4SLinus Torvalds unsigned int stopped;
1321da177e4SLinus Torvalds };
1331da177e4SLinus Torvalds
134278d72aeSMax Asbock struct ibmasm_remote {
135736ce432SVernon Mauery struct input_dev *keybd_dev;
136736ce432SVernon Mauery struct input_dev *mouse_dev;
1371da177e4SLinus Torvalds };
1381da177e4SLinus Torvalds
1391da177e4SLinus Torvalds struct service_processor {
1401da177e4SLinus Torvalds struct list_head node;
1411da177e4SLinus Torvalds spinlock_t lock;
1421da177e4SLinus Torvalds void __iomem *base_address;
1431da177e4SLinus Torvalds unsigned int irq;
1441da177e4SLinus Torvalds struct command *current_command;
1451da177e4SLinus Torvalds struct command *heartbeat;
1461da177e4SLinus Torvalds struct list_head command_queue;
1471da177e4SLinus Torvalds struct event_buffer *event_buffer;
1481da177e4SLinus Torvalds char dirname[IBMASM_NAME_SIZE];
1491da177e4SLinus Torvalds char devname[IBMASM_NAME_SIZE];
1501da177e4SLinus Torvalds unsigned int number;
151736ce432SVernon Mauery struct ibmasm_remote remote;
1521da177e4SLinus Torvalds int serial_line;
1531da177e4SLinus Torvalds struct device *dev;
1541da177e4SLinus Torvalds };
1551da177e4SLinus Torvalds
1561da177e4SLinus Torvalds /* command processing */
157da6b9c92SDmitry Torokhov struct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size);
158da6b9c92SDmitry Torokhov void ibmasm_exec_command(struct service_processor *sp, struct command *cmd);
159da6b9c92SDmitry Torokhov void ibmasm_wait_for_response(struct command *cmd, int timeout);
160da6b9c92SDmitry Torokhov void ibmasm_receive_command_response(struct service_processor *sp, void *response, size_t size);
1611da177e4SLinus Torvalds
1621da177e4SLinus Torvalds /* event processing */
163da6b9c92SDmitry Torokhov int ibmasm_event_buffer_init(struct service_processor *sp);
164da6b9c92SDmitry Torokhov void ibmasm_event_buffer_exit(struct service_processor *sp);
165da6b9c92SDmitry Torokhov void ibmasm_receive_event(struct service_processor *sp, void *data, unsigned int data_size);
166da6b9c92SDmitry Torokhov void ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader);
167da6b9c92SDmitry Torokhov void ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader);
168da6b9c92SDmitry Torokhov int ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader);
169da6b9c92SDmitry Torokhov void ibmasm_cancel_next_event(struct event_reader *reader);
1701da177e4SLinus Torvalds
1711da177e4SLinus Torvalds /* heartbeat - from SP to OS */
172da6b9c92SDmitry Torokhov void ibmasm_register_panic_notifier(void);
173da6b9c92SDmitry Torokhov void ibmasm_unregister_panic_notifier(void);
174da6b9c92SDmitry Torokhov int ibmasm_heartbeat_init(struct service_processor *sp);
175da6b9c92SDmitry Torokhov void ibmasm_heartbeat_exit(struct service_processor *sp);
176da6b9c92SDmitry Torokhov void ibmasm_receive_heartbeat(struct service_processor *sp, void *message, size_t size);
1771da177e4SLinus Torvalds
1781da177e4SLinus Torvalds /* reverse heartbeat - from OS to SP */
179da6b9c92SDmitry Torokhov void ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
180da6b9c92SDmitry Torokhov int ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
181da6b9c92SDmitry Torokhov void ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb);
1821da177e4SLinus Torvalds
1831da177e4SLinus Torvalds /* dot commands */
184da6b9c92SDmitry Torokhov void ibmasm_receive_message(struct service_processor *sp, void *data, int data_size);
185da6b9c92SDmitry Torokhov int ibmasm_send_driver_vpd(struct service_processor *sp);
186da6b9c92SDmitry Torokhov int ibmasm_send_os_state(struct service_processor *sp, int os_state);
1871da177e4SLinus Torvalds
1881da177e4SLinus Torvalds /* low level message processing */
189da6b9c92SDmitry Torokhov int ibmasm_send_i2o_message(struct service_processor *sp);
190da6b9c92SDmitry Torokhov irqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id);
1911da177e4SLinus Torvalds
1921da177e4SLinus Torvalds /* remote console */
193da6b9c92SDmitry Torokhov void ibmasm_handle_mouse_interrupt(struct service_processor *sp);
194da6b9c92SDmitry Torokhov int ibmasm_init_remote_input_dev(struct service_processor *sp);
195da6b9c92SDmitry Torokhov void ibmasm_free_remote_input_dev(struct service_processor *sp);
1961da177e4SLinus Torvalds
1971da177e4SLinus Torvalds /* file system */
198da6b9c92SDmitry Torokhov int ibmasmfs_register(void);
199da6b9c92SDmitry Torokhov void ibmasmfs_unregister(void);
200da6b9c92SDmitry Torokhov void ibmasmfs_add_sp(struct service_processor *sp);
2011da177e4SLinus Torvalds
2021da177e4SLinus Torvalds /* uart */
203cf4f2193SMichal Marek #if IS_ENABLED(CONFIG_SERIAL_8250)
204da6b9c92SDmitry Torokhov void ibmasm_register_uart(struct service_processor *sp);
205da6b9c92SDmitry Torokhov void ibmasm_unregister_uart(struct service_processor *sp);
2061da177e4SLinus Torvalds #else
2071da177e4SLinus Torvalds #define ibmasm_register_uart(sp) do { } while(0)
2081da177e4SLinus Torvalds #define ibmasm_unregister_uart(sp) do { } while(0)
2091da177e4SLinus Torvalds #endif
210