18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * IBM ASM Service Processor Device Driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation, 2004
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Max Asböck <amax@us.ibm.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/list.h>
158c2ecf20Sopenharmony_ci#include <linux/wait.h>
168c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/kref.h>
218c2ecf20Sopenharmony_ci#include <linux/device.h>
228c2ecf20Sopenharmony_ci#include <linux/input.h>
238c2ecf20Sopenharmony_ci#include <linux/time64.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Driver identification */
268c2ecf20Sopenharmony_ci#define DRIVER_NAME	"ibmasm"
278c2ecf20Sopenharmony_ci#define DRIVER_VERSION  "1.0"
288c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR   "Max Asbock <masbock@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
298c2ecf20Sopenharmony_ci#define DRIVER_DESC     "IBM ASM Service Processor Driver"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)
328c2ecf20Sopenharmony_ci#define info(msg) printk(KERN_INFO "%s: " msg "\n", DRIVER_NAME)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ciextern int ibmasm_debug;
358c2ecf20Sopenharmony_ci#define dbg(STR, ARGS...)					\
368c2ecf20Sopenharmony_ci	do {							\
378c2ecf20Sopenharmony_ci		if (ibmasm_debug)				\
388c2ecf20Sopenharmony_ci			printk(KERN_DEBUG STR , ##ARGS);	\
398c2ecf20Sopenharmony_ci	} while (0)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline char *get_timestamp(char *buf)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct timespec64 now;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	ktime_get_real_ts64(&now);
468c2ecf20Sopenharmony_ci	sprintf(buf, "%llu.%.08lu", (long long)now.tv_sec,
478c2ecf20Sopenharmony_ci				now.tv_nsec / NSEC_PER_USEC);
488c2ecf20Sopenharmony_ci	return buf;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define IBMASM_CMD_PENDING	0
528c2ecf20Sopenharmony_ci#define IBMASM_CMD_COMPLETE	1
538c2ecf20Sopenharmony_ci#define IBMASM_CMD_FAILED	2
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define IBMASM_CMD_TIMEOUT_NORMAL	45
568c2ecf20Sopenharmony_ci#define IBMASM_CMD_TIMEOUT_EXTRA	240
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define IBMASM_CMD_MAX_BUFFER_SIZE	0x8000
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define REVERSE_HEARTBEAT_TIMEOUT	120
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define HEARTBEAT_BUFFER_SIZE		0x400
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#ifdef IA64
658c2ecf20Sopenharmony_ci#define IBMASM_DRIVER_VPD "Lin64 6.08      "
668c2ecf20Sopenharmony_ci#else
678c2ecf20Sopenharmony_ci#define IBMASM_DRIVER_VPD "Lin32 6.08      "
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define SYSTEM_STATE_OS_UP      5
718c2ecf20Sopenharmony_ci#define SYSTEM_STATE_OS_DOWN    4
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define IBMASM_NAME_SIZE	16
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define IBMASM_NUM_EVENTS	10
768c2ecf20Sopenharmony_ci#define IBMASM_EVENT_MAX_SIZE	2048u
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistruct command {
808c2ecf20Sopenharmony_ci	struct list_head	queue_node;
818c2ecf20Sopenharmony_ci	wait_queue_head_t	wait;
828c2ecf20Sopenharmony_ci	unsigned char		*buffer;
838c2ecf20Sopenharmony_ci	size_t			buffer_size;
848c2ecf20Sopenharmony_ci	int			status;
858c2ecf20Sopenharmony_ci	struct kref		kref;
868c2ecf20Sopenharmony_ci	spinlock_t		*lock;
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci#define to_command(c) container_of(c, struct command, kref)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_civoid ibmasm_free_command(struct kref *kref);
918c2ecf20Sopenharmony_cistatic inline void command_put(struct command *cmd)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	unsigned long flags;
948c2ecf20Sopenharmony_ci	spinlock_t *lock = cmd->lock;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	spin_lock_irqsave(lock, flags);
978c2ecf20Sopenharmony_ci	kref_put(&cmd->kref, ibmasm_free_command);
988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(lock, flags);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic inline void command_get(struct command *cmd)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	kref_get(&cmd->kref);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistruct ibmasm_event {
1088c2ecf20Sopenharmony_ci	unsigned int	serial_number;
1098c2ecf20Sopenharmony_ci	unsigned int	data_size;
1108c2ecf20Sopenharmony_ci	unsigned char	data[IBMASM_EVENT_MAX_SIZE];
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistruct event_buffer {
1148c2ecf20Sopenharmony_ci	struct ibmasm_event	events[IBMASM_NUM_EVENTS];
1158c2ecf20Sopenharmony_ci	unsigned int		next_serial_number;
1168c2ecf20Sopenharmony_ci	unsigned int		next_index;
1178c2ecf20Sopenharmony_ci	struct list_head	readers;
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistruct event_reader {
1218c2ecf20Sopenharmony_ci	int			cancelled;
1228c2ecf20Sopenharmony_ci	unsigned int		next_serial_number;
1238c2ecf20Sopenharmony_ci	wait_queue_head_t	wait;
1248c2ecf20Sopenharmony_ci	struct list_head	node;
1258c2ecf20Sopenharmony_ci	unsigned int		data_size;
1268c2ecf20Sopenharmony_ci	unsigned char		data[IBMASM_EVENT_MAX_SIZE];
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistruct reverse_heartbeat {
1308c2ecf20Sopenharmony_ci	wait_queue_head_t	wait;
1318c2ecf20Sopenharmony_ci	unsigned int		stopped;
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistruct ibmasm_remote {
1358c2ecf20Sopenharmony_ci	struct input_dev *keybd_dev;
1368c2ecf20Sopenharmony_ci	struct input_dev *mouse_dev;
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistruct service_processor {
1408c2ecf20Sopenharmony_ci	struct list_head	node;
1418c2ecf20Sopenharmony_ci	spinlock_t		lock;
1428c2ecf20Sopenharmony_ci	void __iomem		*base_address;
1438c2ecf20Sopenharmony_ci	unsigned int		irq;
1448c2ecf20Sopenharmony_ci	struct command		*current_command;
1458c2ecf20Sopenharmony_ci	struct command		*heartbeat;
1468c2ecf20Sopenharmony_ci	struct list_head	command_queue;
1478c2ecf20Sopenharmony_ci	struct event_buffer	*event_buffer;
1488c2ecf20Sopenharmony_ci	char			dirname[IBMASM_NAME_SIZE];
1498c2ecf20Sopenharmony_ci	char			devname[IBMASM_NAME_SIZE];
1508c2ecf20Sopenharmony_ci	unsigned int		number;
1518c2ecf20Sopenharmony_ci	struct ibmasm_remote	remote;
1528c2ecf20Sopenharmony_ci	int			serial_line;
1538c2ecf20Sopenharmony_ci	struct device		*dev;
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/* command processing */
1578c2ecf20Sopenharmony_cistruct command *ibmasm_new_command(struct service_processor *sp, size_t buffer_size);
1588c2ecf20Sopenharmony_civoid ibmasm_exec_command(struct service_processor *sp, struct command *cmd);
1598c2ecf20Sopenharmony_civoid ibmasm_wait_for_response(struct command *cmd, int timeout);
1608c2ecf20Sopenharmony_civoid ibmasm_receive_command_response(struct service_processor *sp, void *response,  size_t size);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/* event processing */
1638c2ecf20Sopenharmony_ciint ibmasm_event_buffer_init(struct service_processor *sp);
1648c2ecf20Sopenharmony_civoid ibmasm_event_buffer_exit(struct service_processor *sp);
1658c2ecf20Sopenharmony_civoid ibmasm_receive_event(struct service_processor *sp, void *data,  unsigned int data_size);
1668c2ecf20Sopenharmony_civoid ibmasm_event_reader_register(struct service_processor *sp, struct event_reader *reader);
1678c2ecf20Sopenharmony_civoid ibmasm_event_reader_unregister(struct service_processor *sp, struct event_reader *reader);
1688c2ecf20Sopenharmony_ciint ibmasm_get_next_event(struct service_processor *sp, struct event_reader *reader);
1698c2ecf20Sopenharmony_civoid ibmasm_cancel_next_event(struct event_reader *reader);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/* heartbeat - from SP to OS */
1728c2ecf20Sopenharmony_civoid ibmasm_register_panic_notifier(void);
1738c2ecf20Sopenharmony_civoid ibmasm_unregister_panic_notifier(void);
1748c2ecf20Sopenharmony_ciint ibmasm_heartbeat_init(struct service_processor *sp);
1758c2ecf20Sopenharmony_civoid ibmasm_heartbeat_exit(struct service_processor *sp);
1768c2ecf20Sopenharmony_civoid ibmasm_receive_heartbeat(struct service_processor *sp,  void *message, size_t size);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* reverse heartbeat - from OS to SP */
1798c2ecf20Sopenharmony_civoid ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
1808c2ecf20Sopenharmony_ciint ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb);
1818c2ecf20Sopenharmony_civoid ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/* dot commands */
1848c2ecf20Sopenharmony_civoid ibmasm_receive_message(struct service_processor *sp, void *data, int data_size);
1858c2ecf20Sopenharmony_ciint ibmasm_send_driver_vpd(struct service_processor *sp);
1868c2ecf20Sopenharmony_ciint ibmasm_send_os_state(struct service_processor *sp, int os_state);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/* low level message processing */
1898c2ecf20Sopenharmony_ciint ibmasm_send_i2o_message(struct service_processor *sp);
1908c2ecf20Sopenharmony_ciirqreturn_t ibmasm_interrupt_handler(int irq, void * dev_id);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* remote console */
1938c2ecf20Sopenharmony_civoid ibmasm_handle_mouse_interrupt(struct service_processor *sp);
1948c2ecf20Sopenharmony_ciint ibmasm_init_remote_input_dev(struct service_processor *sp);
1958c2ecf20Sopenharmony_civoid ibmasm_free_remote_input_dev(struct service_processor *sp);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* file system */
1988c2ecf20Sopenharmony_ciint ibmasmfs_register(void);
1998c2ecf20Sopenharmony_civoid ibmasmfs_unregister(void);
2008c2ecf20Sopenharmony_civoid ibmasmfs_add_sp(struct service_processor *sp);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/* uart */
2038c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SERIAL_8250)
2048c2ecf20Sopenharmony_civoid ibmasm_register_uart(struct service_processor *sp);
2058c2ecf20Sopenharmony_civoid ibmasm_unregister_uart(struct service_processor *sp);
2068c2ecf20Sopenharmony_ci#else
2078c2ecf20Sopenharmony_ci#define ibmasm_register_uart(sp)	do { } while(0)
2088c2ecf20Sopenharmony_ci#define ibmasm_unregister_uart(sp)	do { } while(0)
2098c2ecf20Sopenharmony_ci#endif
210