162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2004
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Max Asböck <amax@us.ibm.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/sched/signal.h>
1162306a36Sopenharmony_ci#include "ibmasm.h"
1262306a36Sopenharmony_ci#include "dot_command.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * Reverse Heartbeat, i.e. heartbeats sent from the driver to the
1662306a36Sopenharmony_ci * service processor.
1762306a36Sopenharmony_ci * These heartbeats are initiated by user level programs.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* the reverse heartbeat dot command */
2162306a36Sopenharmony_ci#pragma pack(1)
2262306a36Sopenharmony_cistatic struct {
2362306a36Sopenharmony_ci	struct dot_command_header	header;
2462306a36Sopenharmony_ci	unsigned char			command[3];
2562306a36Sopenharmony_ci} rhb_dot_cmd = {
2662306a36Sopenharmony_ci	.header = {
2762306a36Sopenharmony_ci		.type =		sp_read,
2862306a36Sopenharmony_ci		.command_size = 3,
2962306a36Sopenharmony_ci		.data_size =	0,
3062306a36Sopenharmony_ci		.status =	0
3162306a36Sopenharmony_ci	},
3262306a36Sopenharmony_ci	.command = { 4, 3, 6 }
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci#pragma pack()
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_civoid ibmasm_init_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	init_waitqueue_head(&rhb->wait);
3962306a36Sopenharmony_ci	rhb->stopped = 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * start_reverse_heartbeat
4462306a36Sopenharmony_ci * Loop forever, sending a reverse heartbeat dot command to the service
4562306a36Sopenharmony_ci * processor, then sleeping. The loop comes to an end if the service
4662306a36Sopenharmony_ci * processor fails to respond 3 times or we were interrupted.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ciint ibmasm_start_reverse_heartbeat(struct service_processor *sp, struct reverse_heartbeat *rhb)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct command *cmd;
5162306a36Sopenharmony_ci	int times_failed = 0;
5262306a36Sopenharmony_ci	int result = 1;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	cmd = ibmasm_new_command(sp, sizeof rhb_dot_cmd);
5562306a36Sopenharmony_ci	if (!cmd)
5662306a36Sopenharmony_ci		return -ENOMEM;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	while (times_failed < 3) {
5962306a36Sopenharmony_ci		memcpy(cmd->buffer, (void *)&rhb_dot_cmd, sizeof rhb_dot_cmd);
6062306a36Sopenharmony_ci		cmd->status = IBMASM_CMD_PENDING;
6162306a36Sopenharmony_ci		ibmasm_exec_command(sp, cmd);
6262306a36Sopenharmony_ci		ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		if (cmd->status != IBMASM_CMD_COMPLETE)
6562306a36Sopenharmony_ci			times_failed++;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		wait_event_interruptible_timeout(rhb->wait,
6862306a36Sopenharmony_ci			rhb->stopped,
6962306a36Sopenharmony_ci			REVERSE_HEARTBEAT_TIMEOUT * HZ);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		if (signal_pending(current) || rhb->stopped) {
7262306a36Sopenharmony_ci			result = -EINTR;
7362306a36Sopenharmony_ci			break;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci	command_put(cmd);
7762306a36Sopenharmony_ci	rhb->stopped = 0;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return result;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_civoid ibmasm_stop_reverse_heartbeat(struct reverse_heartbeat *rhb)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	rhb->stopped = 1;
8562306a36Sopenharmony_ci	wake_up_interruptible(&rhb->wait);
8662306a36Sopenharmony_ci}
87