xref: /linux/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * AMD MP2 1.1 communication interfaces
4  *
5  * Copyright (c) 2022, Advanced Micro Devices, Inc.
6  * All Rights Reserved.
7  *
8  * Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
9  */
10 #include <linux/amd-pmf-io.h>
11 #include <linux/io-64-nonatomic-lo-hi.h>
12 #include <linux/iopoll.h>
13 
14 #include "amd_sfh_interface.h"
15 
16 static struct amd_mp2_dev *emp2;
17 
18 static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id)
19 {
20 	struct sfh_cmd_response cmd_resp;
21 
22 	/* Get response with status within a max of 10000 ms timeout */
23 	if (!readl_poll_timeout(mp2->mmio + amd_get_p2c_val(mp2, 0), cmd_resp.resp,
24 				(cmd_resp.response.response == 0 &&
25 				cmd_resp.response.cmd_id == cmd_id && (sid == 0xff ||
26 				cmd_resp.response.sensor_id == sid)), 500, 10000000))
27 		return cmd_resp.response.response;
28 
29 	return -1;
30 }
31 
32 static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
33 {
34 	struct sfh_cmd_base cmd_base;
35 
36 	cmd_base.ul = 0;
37 	cmd_base.cmd.cmd_id = ENABLE_SENSOR;
38 	cmd_base.cmd.intr_disable = 0;
39 	cmd_base.cmd.sub_cmd_value = 1;
40 	cmd_base.cmd.sensor_id = info.sensor_idx;
41 
42 	writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
43 }
44 
45 static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
46 {
47 	struct sfh_cmd_base cmd_base;
48 
49 	cmd_base.ul = 0;
50 	cmd_base.cmd.cmd_id = DISABLE_SENSOR;
51 	cmd_base.cmd.intr_disable = 0;
52 	cmd_base.cmd.sub_cmd_value = 1;
53 	cmd_base.cmd.sensor_id = sensor_idx;
54 
55 	writeq(0x0, privdata->mmio + amd_get_c2p_val(privdata, 1));
56 	writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
57 }
58 
59 static void amd_stop_all_sensor(struct amd_mp2_dev *privdata)
60 {
61 	struct sfh_cmd_base cmd_base;
62 
63 	cmd_base.ul = 0;
64 	cmd_base.cmd.cmd_id = DISABLE_SENSOR;
65 	cmd_base.cmd.intr_disable = 0;
66 	/* 0xf indicates all sensors */
67 	cmd_base.cmd.sensor_id = 0xf;
68 
69 	writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
70 }
71 
72 static struct amd_mp2_ops amd_sfh_ops = {
73 	.start = amd_start_sensor,
74 	.stop = amd_stop_sensor,
75 	.stop_all = amd_stop_all_sensor,
76 	.response = amd_sfh_wait_response,
77 };
78 
79 void sfh_deinit_emp2(void)
80 {
81 	emp2 = NULL;
82 }
83 
84 void sfh_interface_init(struct amd_mp2_dev *mp2)
85 {
86 	mp2->mp2_ops = &amd_sfh_ops;
87 	emp2 = mp2;
88 }
89 
90 static int amd_sfh_hpd_info(u8 *user_present)
91 {
92 	struct hpd_status hpdstatus;
93 
94 	if (!user_present)
95 		return -EINVAL;
96 
97 	if (!emp2 || !emp2->dev_en.is_hpd_present)
98 		return -ENODEV;
99 
100 	hpdstatus.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 4));
101 	*user_present = hpdstatus.shpd.presence;
102 
103 	return 0;
104 }
105 
106 static int amd_sfh_als_info(u32 *ambient_light)
107 {
108 	struct sfh_als_data als_data;
109 	void __iomem *sensoraddr;
110 
111 	if (!ambient_light)
112 		return -EINVAL;
113 
114 	if (!emp2 || !emp2->dev_en.is_als_present)
115 		return -ENODEV;
116 
117 	sensoraddr = emp2->vsbase +
118 		(ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
119 		OFFSET_SENSOR_DATA_DEFAULT;
120 	memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
121 	*ambient_light = amd_sfh_float_to_int(als_data.lux);
122 
123 	return 0;
124 }
125 
126 int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op)
127 {
128 	if (sfh_info) {
129 		switch (op) {
130 		case MT_HPD:
131 			return amd_sfh_hpd_info(&sfh_info->user_present);
132 		case MT_ALS:
133 			return amd_sfh_als_info(&sfh_info->ambient_light);
134 		}
135 	}
136 	return -EINVAL;
137 }
138 EXPORT_SYMBOL_GPL(amd_get_sfh_info);
139