18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IBM ASM Service Processor Device Driver
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 "ibmasm.h"
118c2ecf20Sopenharmony_ci#include "dot_command.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * Dispatch an incoming message to the specific handler for the message.
158c2ecf20Sopenharmony_ci * Called from interrupt context.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_civoid ibmasm_receive_message(struct service_processor *sp, void *message, int message_size)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	u32 size;
208c2ecf20Sopenharmony_ci	struct dot_command_header *header = (struct dot_command_header *)message;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	if (message_size == 0)
238c2ecf20Sopenharmony_ci		return;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	size = get_dot_command_size(message);
268c2ecf20Sopenharmony_ci	if (size == 0)
278c2ecf20Sopenharmony_ci		return;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	if (size > message_size)
308c2ecf20Sopenharmony_ci		size = message_size;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	switch (header->type) {
338c2ecf20Sopenharmony_ci	case sp_event:
348c2ecf20Sopenharmony_ci		ibmasm_receive_event(sp, message, size);
358c2ecf20Sopenharmony_ci		break;
368c2ecf20Sopenharmony_ci	case sp_command_response:
378c2ecf20Sopenharmony_ci		ibmasm_receive_command_response(sp, message, size);
388c2ecf20Sopenharmony_ci		break;
398c2ecf20Sopenharmony_ci	case sp_heartbeat:
408c2ecf20Sopenharmony_ci		ibmasm_receive_heartbeat(sp, message, size);
418c2ecf20Sopenharmony_ci		break;
428c2ecf20Sopenharmony_ci	default:
438c2ecf20Sopenharmony_ci		dev_err(sp->dev, "Received unknown message from service processor\n");
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define INIT_BUFFER_SIZE 32
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * send the 4.3.5.10 dot command (driver VPD) to the service processor
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_ciint ibmasm_send_driver_vpd(struct service_processor *sp)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct command *command;
578c2ecf20Sopenharmony_ci	struct dot_command_header *header;
588c2ecf20Sopenharmony_ci	u8 *vpd_command;
598c2ecf20Sopenharmony_ci	u8 *vpd_data;
608c2ecf20Sopenharmony_ci	int result = 0;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	command = ibmasm_new_command(sp, INIT_BUFFER_SIZE);
638c2ecf20Sopenharmony_ci	if (command == NULL)
648c2ecf20Sopenharmony_ci		return -ENOMEM;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	header = (struct dot_command_header *)command->buffer;
678c2ecf20Sopenharmony_ci	header->type                = sp_write;
688c2ecf20Sopenharmony_ci	header->command_size        = 4;
698c2ecf20Sopenharmony_ci	header->data_size           = 16;
708c2ecf20Sopenharmony_ci	header->status              = 0;
718c2ecf20Sopenharmony_ci	header->reserved            = 0;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	vpd_command = command->buffer + sizeof(struct dot_command_header);
748c2ecf20Sopenharmony_ci	vpd_command[0] = 0x4;
758c2ecf20Sopenharmony_ci	vpd_command[1] = 0x3;
768c2ecf20Sopenharmony_ci	vpd_command[2] = 0x5;
778c2ecf20Sopenharmony_ci	vpd_command[3] = 0xa;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	vpd_data = vpd_command + header->command_size;
808c2ecf20Sopenharmony_ci	vpd_data[0] = 0;
818c2ecf20Sopenharmony_ci	strcat(vpd_data, IBMASM_DRIVER_VPD);
828c2ecf20Sopenharmony_ci	vpd_data[10] = 0;
838c2ecf20Sopenharmony_ci	vpd_data[15] = 0;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	ibmasm_exec_command(sp, command);
868c2ecf20Sopenharmony_ci	ibmasm_wait_for_response(command, IBMASM_CMD_TIMEOUT_NORMAL);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (command->status != IBMASM_CMD_COMPLETE)
898c2ecf20Sopenharmony_ci		result = -ENODEV;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	command_put(command);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return result;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct os_state_command {
978c2ecf20Sopenharmony_ci	struct dot_command_header	header;
988c2ecf20Sopenharmony_ci	unsigned char			command[3];
998c2ecf20Sopenharmony_ci	unsigned char			data;
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*
1038c2ecf20Sopenharmony_ci * send the 4.3.6 dot command (os state) to the service processor
1048c2ecf20Sopenharmony_ci * During driver init this function is called with os state "up".
1058c2ecf20Sopenharmony_ci * This causes the service processor to start sending heartbeats the
1068c2ecf20Sopenharmony_ci * driver.
1078c2ecf20Sopenharmony_ci * During driver exit the function is called with os state "down",
1088c2ecf20Sopenharmony_ci * causing the service processor to stop the heartbeats.
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_ciint ibmasm_send_os_state(struct service_processor *sp, int os_state)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct command *cmd;
1138c2ecf20Sopenharmony_ci	struct os_state_command *os_state_cmd;
1148c2ecf20Sopenharmony_ci	int result = 0;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	cmd = ibmasm_new_command(sp, sizeof(struct os_state_command));
1178c2ecf20Sopenharmony_ci	if (cmd == NULL)
1188c2ecf20Sopenharmony_ci		return -ENOMEM;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	os_state_cmd = (struct os_state_command *)cmd->buffer;
1218c2ecf20Sopenharmony_ci	os_state_cmd->header.type		= sp_write;
1228c2ecf20Sopenharmony_ci	os_state_cmd->header.command_size	= 3;
1238c2ecf20Sopenharmony_ci	os_state_cmd->header.data_size		= 1;
1248c2ecf20Sopenharmony_ci	os_state_cmd->header.status		= 0;
1258c2ecf20Sopenharmony_ci	os_state_cmd->command[0]		= 4;
1268c2ecf20Sopenharmony_ci	os_state_cmd->command[1]		= 3;
1278c2ecf20Sopenharmony_ci	os_state_cmd->command[2]		= 6;
1288c2ecf20Sopenharmony_ci	os_state_cmd->data			= os_state;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	ibmasm_exec_command(sp, cmd);
1318c2ecf20Sopenharmony_ci	ibmasm_wait_for_response(cmd, IBMASM_CMD_TIMEOUT_NORMAL);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (cmd->status != IBMASM_CMD_COMPLETE)
1348c2ecf20Sopenharmony_ci		result = -ENODEV;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	command_put(cmd);
1378c2ecf20Sopenharmony_ci	return result;
1388c2ecf20Sopenharmony_ci}
139