summaryrefslogtreecommitdiffstats
path: root/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c
blob: 197b828fc6a0bab81928ae3edd0c477c33cdb608 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * AMD MP2 1.1 communication interfaces
 *
 * Copyright (c) 2022, Advanced Micro Devices, Inc.
 * All Rights Reserved.
 *
 * Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
 */
#include <linux/amd-pmf-io.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/iopoll.h>

#include "amd_sfh_interface.h"

static struct amd_mp2_dev *emp2;

static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id)
{
	struct sfh_cmd_response cmd_resp;

	/* Get response with status within a max of 10000 ms timeout */
	if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp,
				(cmd_resp.response.response == 0 &&
				cmd_resp.response.cmd_id == cmd_id && (sid == 0xff ||
				cmd_resp.response.sensor_id == sid)), 500, 10000000))
		return cmd_resp.response.response;

	return -1;
}

static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
{
	struct sfh_cmd_base cmd_base;

	cmd_base.ul = 0;
	cmd_base.cmd.cmd_id = ENABLE_SENSOR;
	cmd_base.cmd.intr_disable = 0;
	cmd_base.cmd.sub_cmd_value = 1;
	cmd_base.cmd.sensor_id = info.sensor_idx;

	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0));
}

static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
{
	struct sfh_cmd_base cmd_base;

	cmd_base.ul = 0;
	cmd_base.cmd.cmd_id = DISABLE_SENSOR;
	cmd_base.cmd.intr_disable = 0;
	cmd_base.cmd.sub_cmd_value = 1;
	cmd_base.cmd.sensor_id = sensor_idx;

	writeq(0x0, privdata->mmio + AMD_C2P_MSG(1));
	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0));
}

static void amd_stop_all_sensor(struct amd_mp2_dev *privdata)
{
	struct sfh_cmd_base cmd_base;

	cmd_base.ul = 0;
	cmd_base.cmd.cmd_id = DISABLE_SENSOR;
	cmd_base.cmd.intr_disable = 0;
	/* 0xf indicates all sensors */
	cmd_base.cmd.sensor_id = 0xf;

	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0));
}

static struct amd_mp2_ops amd_sfh_ops = {
	.start = amd_start_sensor,
	.stop = amd_stop_sensor,
	.stop_all = amd_stop_all_sensor,
	.response = amd_sfh_wait_response,
};

void sfh_deinit_emp2(void)
{
	emp2 = NULL;
}

void sfh_interface_init(struct amd_mp2_dev *mp2)
{
	mp2->mp2_ops = &amd_sfh_ops;
	emp2 = mp2;
}

static int amd_sfh_hpd_info(u8 *user_present)
{
	struct hpd_status hpdstatus;

	if (!user_present)
		return -EINVAL;

	if (!emp2 || !emp2->dev_en.is_hpd_present)
		return -ENODEV;

	hpdstatus.val = readl(emp2->mmio + AMD_C2P_MSG(4));
	*user_present = hpdstatus.shpd.presence;

	return 0;
}

int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op)
{
	if (sfh_info) {
		switch (op) {
		case MT_HPD:
			return amd_sfh_hpd_info(&sfh_info->user_present);
		}
	}
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(amd_get_sfh_info);