xref: /linux/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c (revision 5b05bb3f6c5716fab6911e12d60dd1f43ad9806a)
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.h>
11 #include <linux/amd-pmf-io.h>
12 #include <linux/cleanup.h>
13 #include <linux/io-64-nonatomic-lo-hi.h>
14 #include <linux/iopoll.h>
15 
16 #include "amd_sfh_interface.h"
17 
18 static struct amd_mp2_dev *emp2;
19 static DEFINE_MUTEX(emp2_lock);
20 
amd_sfh_wait_response(struct amd_mp2_dev * mp2,u8 sid,u32 cmd_id)21 static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id)
22 {
23 	struct sfh_cmd_response cmd_resp;
24 
25 	/* Get response with status within a max of 10000 ms timeout */
26 	if (!readl_poll_timeout(mp2->mmio + amd_get_p2c_val(mp2, 0), cmd_resp.resp,
27 				(cmd_resp.response.response == 0 &&
28 				cmd_resp.response.cmd_id == cmd_id && (sid == 0xff ||
29 				cmd_resp.response.sensor_id == sid)), 500, 10000000))
30 		return cmd_resp.response.response;
31 
32 	return -1;
33 }
34 
amd_start_sensor(struct amd_mp2_dev * privdata,struct amd_mp2_sensor_info info)35 static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
36 {
37 	struct sfh_cmd_base cmd_base;
38 
39 	cmd_base.ul = 0;
40 	cmd_base.cmd.cmd_id = ENABLE_SENSOR;
41 	cmd_base.cmd.intr_disable = 0;
42 	cmd_base.cmd.sub_cmd_value = 1;
43 	cmd_base.cmd.sensor_id = info.sensor_idx;
44 
45 	writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
46 }
47 
amd_stop_sensor(struct amd_mp2_dev * privdata,u16 sensor_idx)48 static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
49 {
50 	struct sfh_cmd_base cmd_base;
51 
52 	cmd_base.ul = 0;
53 	cmd_base.cmd.cmd_id = DISABLE_SENSOR;
54 	cmd_base.cmd.intr_disable = 0;
55 	cmd_base.cmd.sub_cmd_value = 1;
56 	cmd_base.cmd.sensor_id = sensor_idx;
57 
58 	writeq(0x0, privdata->mmio + amd_get_c2p_val(privdata, 1));
59 	writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
60 }
61 
amd_stop_all_sensor(struct amd_mp2_dev * privdata)62 static void amd_stop_all_sensor(struct amd_mp2_dev *privdata)
63 {
64 	struct sfh_cmd_base cmd_base;
65 
66 	cmd_base.ul = 0;
67 	cmd_base.cmd.cmd_id = DISABLE_SENSOR;
68 	cmd_base.cmd.intr_disable = 0;
69 	/* 0xf indicates all sensors */
70 	cmd_base.cmd.sensor_id = 0xf;
71 
72 	writel(cmd_base.ul, privdata->mmio + amd_get_c2p_val(privdata, 0));
73 }
74 
75 static struct amd_mp2_ops amd_sfh_ops = {
76 	.start = amd_start_sensor,
77 	.stop = amd_stop_sensor,
78 	.stop_all = amd_stop_all_sensor,
79 	.response = amd_sfh_wait_response,
80 };
81 
sfh_deinit_emp2(void)82 void sfh_deinit_emp2(void)
83 {
84 	guard(mutex)(&emp2_lock);
85 	emp2 = NULL;
86 }
87 
sfh_set_emp2(struct amd_mp2_dev * mp2)88 void sfh_set_emp2(struct amd_mp2_dev *mp2)
89 {
90 	guard(mutex)(&emp2_lock);
91 	emp2 = mp2;
92 }
93 
sfh_interface_init(struct amd_mp2_dev * mp2)94 void sfh_interface_init(struct amd_mp2_dev *mp2)
95 {
96 	mp2->mp2_ops = &amd_sfh_ops;
97 	guard(mutex)(&emp2_lock);
98 	emp2 = mp2;
99 }
100 
amd_sfh_op_mode_info(u32 * op_mode)101 static int amd_sfh_op_mode_info(u32 *op_mode)
102 {
103 	struct sfh_op_mode mode;
104 	bool present;
105 
106 	if (!op_mode)
107 		return -EINVAL;
108 	if (!emp2)
109 		return -ENODEV;
110 
111 	present = emp2->sfh1_1_ops ? emp2->dev_en.is_sra_present
112 				   : (emp2->mp2_ver == MP2_VER_V2 &&
113 				      amd_sfh_op_idx_enabled(emp2));
114 	if (!present)
115 		return -ENODEV;
116 
117 	mode.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 3));
118 	dev_dbg(&emp2->pdev->dev, "op-mode: %s (mode=%u)\n",
119 		mode.op_mode.mode == SFH_MODE_TABLET ? "tablet" : "laptop",
120 		mode.op_mode.mode);
121 	*op_mode = mode.op_mode.mode;
122 
123 	return 0;
124 }
125 
amd_sfh_mode_info(u32 * platform_type,u32 * laptop_placement)126 static int amd_sfh_mode_info(u32 *platform_type, u32 *laptop_placement)
127 {
128 	struct sfh_op_mode mode;
129 
130 	if (!platform_type || !laptop_placement)
131 		return -EINVAL;
132 
133 	if (!emp2 || !emp2->dev_en.is_sra_present)
134 		return -ENODEV;
135 
136 	mode.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 3));
137 
138 	*platform_type = mode.op_mode.devicemode;
139 
140 	if (mode.op_mode.ontablestate == 1) {
141 		*laptop_placement = AMD_PMF_ON_TABLE;
142 	} else if (mode.op_mode.ontablestate == 2) {
143 		*laptop_placement = AMD_PMF_ON_LAP_MOTION;
144 	} else if (mode.op_mode.inbagstate == 1) {
145 		*laptop_placement = AMD_PMF_IN_BAG;
146 	} else if (mode.op_mode.outbagstate == 1) {
147 		*laptop_placement = AMD_PMF_OUT_OF_BAG;
148 	} else if (mode.op_mode.ontablestate == 0 || mode.op_mode.inbagstate == 0 ||
149 		 mode.op_mode.outbagstate == 0) {
150 		*laptop_placement = AMD_PMF_LP_UNKNOWN;
151 		pr_warn_once("Unknown laptop placement\n");
152 	} else if (mode.op_mode.ontablestate == 3 || mode.op_mode.inbagstate == 3 ||
153 		 mode.op_mode.outbagstate == 3) {
154 		*laptop_placement = AMD_PMF_LP_UNDEFINED;
155 		pr_warn_once("Undefined laptop placement\n");
156 	}
157 
158 	return 0;
159 }
160 
amd_sfh_hpd_info(u8 * user_present)161 static int amd_sfh_hpd_info(u8 *user_present)
162 {
163 	struct hpd_status hpdstatus;
164 
165 	if (!user_present)
166 		return -EINVAL;
167 
168 	if (!emp2 || !emp2->dev_en.is_hpd_present || !emp2->dev_en.is_hpd_enabled)
169 		return -ENODEV;
170 
171 	hpdstatus.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 4));
172 	*user_present = hpdstatus.shpd.presence;
173 
174 	return 0;
175 }
176 
amd_sfh_als_info(u32 * ambient_light)177 static int amd_sfh_als_info(u32 *ambient_light)
178 {
179 	struct sfh_als_data als_data;
180 	void __iomem *sensoraddr;
181 
182 	if (!ambient_light)
183 		return -EINVAL;
184 
185 	if (!emp2 || !emp2->dev_en.is_als_present)
186 		return -ENODEV;
187 
188 	sensoraddr = emp2->vsbase +
189 		(ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
190 		OFFSET_SENSOR_DATA_DEFAULT;
191 	memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
192 	*ambient_light = amd_sfh_float_to_int(als_data.lux);
193 
194 	return 0;
195 }
196 
amd_get_sfh_info(struct amd_sfh_info * sfh_info,enum sfh_message_type op)197 int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op)
198 {
199 	guard(mutex)(&emp2_lock);
200 
201 	if (sfh_info) {
202 		switch (op) {
203 		case MT_HPD:
204 			return amd_sfh_hpd_info(&sfh_info->user_present);
205 		case MT_ALS:
206 			return amd_sfh_als_info(&sfh_info->ambient_light);
207 		case MT_SRA:
208 			return amd_sfh_mode_info(&sfh_info->platform_type,
209 						 &sfh_info->laptop_placement);
210 		case MT_OP_MODE:
211 			return amd_sfh_op_mode_info(&sfh_info->op_mode);
212 		}
213 	}
214 	return -EINVAL;
215 }
216 EXPORT_SYMBOL_GPL(amd_get_sfh_info);
217