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 <[email protected]>
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 
amd_sfh_wait_response(struct amd_mp2_dev * mp2,u8 sid,u32 cmd_id)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 
amd_start_sensor(struct amd_mp2_dev * privdata,struct amd_mp2_sensor_info info)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 
amd_stop_sensor(struct amd_mp2_dev * privdata,u16 sensor_idx)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 
amd_stop_all_sensor(struct amd_mp2_dev * privdata)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 
sfh_deinit_emp2(void)79 void sfh_deinit_emp2(void)
80 {
81 	emp2 = NULL;
82 }
83 
sfh_interface_init(struct amd_mp2_dev * mp2)84 void sfh_interface_init(struct amd_mp2_dev *mp2)
85 {
86 	mp2->mp2_ops = &amd_sfh_ops;
87 	emp2 = mp2;
88 }
89 
amd_sfh_mode_info(u32 * platform_type,u32 * laptop_placement)90 static int amd_sfh_mode_info(u32 *platform_type, u32 *laptop_placement)
91 {
92 	struct sfh_op_mode mode;
93 
94 	if (!platform_type || !laptop_placement)
95 		return -EINVAL;
96 
97 	if (!emp2 || !emp2->dev_en.is_sra_present)
98 		return -ENODEV;
99 
100 	mode.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 3));
101 
102 	*platform_type = mode.op_mode.devicemode;
103 
104 	if (mode.op_mode.ontablestate == 1) {
105 		*laptop_placement = ON_TABLE;
106 	} else if (mode.op_mode.ontablestate == 2) {
107 		*laptop_placement = ON_LAP_MOTION;
108 	} else if (mode.op_mode.inbagstate == 1) {
109 		*laptop_placement = IN_BAG;
110 	} else if (mode.op_mode.outbagstate == 1) {
111 		*laptop_placement = OUT_OF_BAG;
112 	} else if (mode.op_mode.ontablestate == 0 || mode.op_mode.inbagstate == 0 ||
113 		 mode.op_mode.outbagstate == 0) {
114 		*laptop_placement = LP_UNKNOWN;
115 		pr_warn_once("Unknown laptop placement\n");
116 	} else if (mode.op_mode.ontablestate == 3 || mode.op_mode.inbagstate == 3 ||
117 		 mode.op_mode.outbagstate == 3) {
118 		*laptop_placement = LP_UNDEFINED;
119 		pr_warn_once("Undefined laptop placement\n");
120 	}
121 
122 	return 0;
123 }
124 
amd_sfh_hpd_info(u8 * user_present)125 static int amd_sfh_hpd_info(u8 *user_present)
126 {
127 	struct hpd_status hpdstatus;
128 
129 	if (!user_present)
130 		return -EINVAL;
131 
132 	if (!emp2 || !emp2->dev_en.is_hpd_present)
133 		return -ENODEV;
134 
135 	hpdstatus.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 4));
136 	*user_present = hpdstatus.shpd.presence;
137 
138 	return 0;
139 }
140 
amd_sfh_als_info(u32 * ambient_light)141 static int amd_sfh_als_info(u32 *ambient_light)
142 {
143 	struct sfh_als_data als_data;
144 	void __iomem *sensoraddr;
145 
146 	if (!ambient_light)
147 		return -EINVAL;
148 
149 	if (!emp2 || !emp2->dev_en.is_als_present)
150 		return -ENODEV;
151 
152 	sensoraddr = emp2->vsbase +
153 		(ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
154 		OFFSET_SENSOR_DATA_DEFAULT;
155 	memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
156 	*ambient_light = amd_sfh_float_to_int(als_data.lux);
157 
158 	return 0;
159 }
160 
amd_get_sfh_info(struct amd_sfh_info * sfh_info,enum sfh_message_type op)161 int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op)
162 {
163 	if (sfh_info) {
164 		switch (op) {
165 		case MT_HPD:
166 			return amd_sfh_hpd_info(&sfh_info->user_present);
167 		case MT_ALS:
168 			return amd_sfh_als_info(&sfh_info->ambient_light);
169 		case MT_SRA:
170 			return amd_sfh_mode_info(&sfh_info->platform_type,
171 						 &sfh_info->laptop_placement);
172 		}
173 	}
174 	return -EINVAL;
175 }
176 EXPORT_SYMBOL_GPL(amd_get_sfh_info);
177