18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation, 2004
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Max Asböck <amax@us.ibm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
118c2ecf20Sopenharmony_ci#include "ibmasm.h"
128c2ecf20Sopenharmony_ci#include "dot_command.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * Reverse Heartbeat, i.e. heartbeats sent from the driver to the
168c2ecf20Sopenharmony_ci * service processor.
178c2ecf20Sopenharmony_ci * These heartbeats are initiated by user level programs.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* the reverse heartbeat dot command */
218c2ecf20Sopenharmony_ci#pragma pack(1)
228c2ecf20Sopenharmony_cistatic struct {
238c2ecf20Sopenharmony_ci	struct dot_command_header	header;
248c2ecf20Sopenharmony_ci	unsigned char			command[3];
258c2ecf20Sopenharmony_ci} rhb_dot_cmd = {
268c2ecf20Sopenharmony_ci	.header = {
278c2ecf20Sopenharmony_ci		.type =		sp_read,
288c2ecf20Sopenharmony_ci		.command_size = 3,
298c2ecf20Sopenharmony_ci		.data_size =	0,
308c2ecf20Sopenharmony_ci		.status =	0
318c2ecf20Sopenharmony_ci	},
328c2ecf20Sopenharmony_ci	.command = { 4, 3, 6 }
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci#pragma pack()
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_civoid ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	init_waitqueue_head(&rhb->wait);
398c2ecf20Sopenharmony_ci	rhb->stopped = 0;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * start_reverse_heartbeat
448c2ecf20Sopenharmony_ci * Loop forever, sending a reverse heartbeat dot command to the service
458c2ecf20Sopenharmony_ci * processor, then sleeping. The loop comes to an end if the service
468c2ecf20Sopenharmony_ci * processor fails to respond 3 times or we were interrupted.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ciint ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct command *cmd;
518c2ecf20Sopenharmony_ci	int times_failed = 0;
528c2ecf20Sopenharmony_ci	int result = 1;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	cmd = ibmasm_new_command(sp, sizeof rhb_dot_cmd);
558c2ecf20Sopenharmony_ci	if (!cmd)
568c2ecf20Sopenharmony_ci		return -ENOMEM;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	while (times_failed < 3) {
598c2ecf20Sopenharmony_ci		memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd);
608c2ecf20Sopenharmony_ci		cmd->status = IBMASM_CMD_PENDING;
618c2ecf20Sopenharmony_ci		ibmasm_exec_command(sp, cmd);
628c2ecf20Sopenharmony_ci		ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		if (cmd->status != IBMASM_CMD_COMPLETE)
658c2ecf20Sopenharmony_ci			times_failed++;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		wait_event_interruptible_timeout(rhb->wait,
688c2ecf20Sopenharmony_ci			rhb->stopped,
698c2ecf20Sopenharmony_ci			REVERSE_HEARTBEAT_TIMEOUT * HZ);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		if (signal_pending(current) || rhb->stopped) {
728c2ecf20Sopenharmony_ci			result = -EINTR;
738c2ecf20Sopenharmony_ci			break;
748c2ecf20Sopenharmony_ci		}
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci	command_put(cmd);
778c2ecf20Sopenharmony_ci	rhb->stopped = 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return result;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_civoid ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	rhb->stopped = 1;
858c2ecf20Sopenharmony_ci	wake_up_interruptible(&rhb->wait);
868c2ecf20Sopenharmony_ci}
87