summaryrefslogtreecommitdiffstats
path: root/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c
blob: ae36312bc23650e0b77171e46d1b972e463c1899 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// 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;
}

static int amd_sfh_als_info(u32 *ambient_light)
{
	struct sfh_als_data als_data;
	void __iomem *sensoraddr;

	if (!ambient_light)
		return -EINVAL;

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

	sensoraddr = emp2->vsbase +
		(ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
		OFFSET_SENSOR_DATA_DEFAULT;
	memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
	*ambient_light = amd_sfh_float_to_int(als_data.lux);

	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);
		case MT_ALS:
			return amd_sfh_als_info(&sfh_info->ambient_light);
		}
	}
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(amd_get_sfh_info);