1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * AMD MP2 PCIe communication driver
4  * Copyright 2020 Advanced Micro Devices, Inc.
5  *
6  * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
7  *	    Sandeep Singh <Sandeep.singh@amd.com>
8  */
9 
10 #include <linux/bitops.h>
11 #include <linux/delay.h>
12 #include <linux/dma-mapping.h>
13 #include <linux/dmi.h>
14 #include <linux/interrupt.h>
15 #include <linux/io-64-nonatomic-lo-hi.h>
16 #include <linux/module.h>
17 #include <linux/slab.h>
18 
19 #include "amd_sfh_pcie.h"
20 
21 #define DRIVER_NAME	"pcie_mp2_amd"
22 #define DRIVER_DESC	"AMD(R) PCIe MP2 Communication Driver"
23 
24 #define ACEL_EN		BIT(0)
25 #define GYRO_EN		BIT(1)
26 #define MAGNO_EN	BIT(2)
27 #define HPD_EN		BIT(16)
28 #define ALS_EN		BIT(19)
29 
30 static int sensor_mask_override = -1;
31 module_param_named(sensor_mask, sensor_mask_override, int, 0444);
32 MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
33 
34 static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
35 {
36 	union sfh_cmd_base cmd_base;
37 
38 	cmd_base.ul = 0;
39 	cmd_base.cmd_v2.cmd_id = ENABLE_SENSOR;
40 	cmd_base.cmd_v2.period = info.period;
41 	cmd_base.cmd_v2.sensor_id = info.sensor_idx;
42 	cmd_base.cmd_v2.length = 16;
43 
44 	if (info.sensor_idx == als_idx)
45 		cmd_base.cmd_v2.mem_type = USE_C2P_REG;
46 
47 	writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG1);
48 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
49 }
50 
51 static void amd_stop_sensor_v2(struct amd_mp2_dev *privdata, u16 sensor_idx)
52 {
53 	union sfh_cmd_base cmd_base;
54 
55 	cmd_base.ul = 0;
56 	cmd_base.cmd_v2.cmd_id = DISABLE_SENSOR;
57 	cmd_base.cmd_v2.period = 0;
58 	cmd_base.cmd_v2.sensor_id = sensor_idx;
59 	cmd_base.cmd_v2.length  = 16;
60 
61 	writeq(0x0, privdata->mmio + AMD_C2P_MSG2);
62 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
63 }
64 
65 static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata)
66 {
67 	union sfh_cmd_base cmd_base;
68 
69 	cmd_base.cmd_v2.cmd_id = STOP_ALL_SENSORS;
70 	cmd_base.cmd_v2.period = 0;
71 	cmd_base.cmd_v2.sensor_id = 0;
72 
73 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
74 }
75 
76 void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
77 {
78 	union sfh_cmd_param cmd_param;
79 	union sfh_cmd_base cmd_base;
80 
81 	/* fill up command register */
82 	memset(&cmd_base, 0, sizeof(cmd_base));
83 	cmd_base.s.cmd_id = ENABLE_SENSOR;
84 	cmd_base.s.period = info.period;
85 	cmd_base.s.sensor_id = info.sensor_idx;
86 
87 	/* fill up command param register */
88 	memset(&cmd_param, 0, sizeof(cmd_param));
89 	cmd_param.s.buf_layout = 1;
90 	cmd_param.s.buf_length = 16;
91 
92 	writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG2);
93 	writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1);
94 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
95 }
96 
97 void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
98 {
99 	union sfh_cmd_base cmd_base;
100 
101 	/* fill up command register */
102 	memset(&cmd_base, 0, sizeof(cmd_base));
103 	cmd_base.s.cmd_id = DISABLE_SENSOR;
104 	cmd_base.s.period = 0;
105 	cmd_base.s.sensor_id = sensor_idx;
106 
107 	writeq(0x0, privdata->mmio + AMD_C2P_MSG2);
108 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
109 }
110 
111 void amd_stop_all_sensors(struct amd_mp2_dev *privdata)
112 {
113 	union sfh_cmd_base cmd_base;
114 
115 	/* fill up command register */
116 	memset(&cmd_base, 0, sizeof(cmd_base));
117 	cmd_base.s.cmd_id = STOP_ALL_SENSORS;
118 	cmd_base.s.period = 0;
119 	cmd_base.s.sensor_id = 0;
120 
121 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
122 }
123 
124 static const struct dmi_system_id dmi_sensor_mask_overrides[] = {
125 	{
126 		.matches = {
127 			DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 13-ag0xxx"),
128 		},
129 		.driver_data = (void *)(ACEL_EN | MAGNO_EN),
130 	},
131 	{
132 		.matches = {
133 			DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 15-cp0xxx"),
134 		},
135 		.driver_data = (void *)(ACEL_EN | MAGNO_EN),
136 	},
137 	{ }
138 };
139 
140 int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id)
141 {
142 	int activestatus, num_of_sensors = 0;
143 	const struct dmi_system_id *dmi_id;
144 
145 	if (sensor_mask_override == -1) {
146 		dmi_id = dmi_first_match(dmi_sensor_mask_overrides);
147 		if (dmi_id)
148 			sensor_mask_override = (long)dmi_id->driver_data;
149 	}
150 
151 	if (sensor_mask_override >= 0) {
152 		activestatus = sensor_mask_override;
153 	} else {
154 		activestatus = privdata->mp2_acs >> 4;
155 	}
156 
157 	if (ACEL_EN  & activestatus)
158 		sensor_id[num_of_sensors++] = accel_idx;
159 
160 	if (GYRO_EN & activestatus)
161 		sensor_id[num_of_sensors++] = gyro_idx;
162 
163 	if (MAGNO_EN & activestatus)
164 		sensor_id[num_of_sensors++] = mag_idx;
165 
166 	if (ALS_EN & activestatus)
167 		sensor_id[num_of_sensors++] = als_idx;
168 
169 	if (HPD_EN & activestatus)
170 		sensor_id[num_of_sensors++] = HPD_IDX;
171 
172 	return num_of_sensors;
173 }
174 
175 static void amd_mp2_pci_remove(void *privdata)
176 {
177 	struct amd_mp2_dev *mp2 = privdata;
178 	amd_sfh_hid_client_deinit(privdata);
179 	mp2->mp2_ops->stop_all(mp2);
180 }
181 
182 static const struct amd_mp2_ops amd_sfh_ops_v2 = {
183 	.start = amd_start_sensor_v2,
184 	.stop = amd_stop_sensor_v2,
185 	.stop_all = amd_stop_all_sensor_v2,
186 };
187 
188 static const struct amd_mp2_ops amd_sfh_ops = {
189 	.start = amd_start_sensor,
190 	.stop = amd_stop_sensor,
191 	.stop_all = amd_stop_all_sensors,
192 };
193 
194 static void mp2_select_ops(struct amd_mp2_dev *privdata)
195 {
196 	u8 acs;
197 
198 	privdata->mp2_acs = readl(privdata->mmio + AMD_P2C_MSG3);
199 	acs = privdata->mp2_acs & GENMASK(3, 0);
200 
201 	switch (acs) {
202 	case V2_STATUS:
203 		privdata->mp2_ops = &amd_sfh_ops_v2;
204 		break;
205 	default:
206 		privdata->mp2_ops = &amd_sfh_ops;
207 		break;
208 	}
209 }
210 
211 static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
212 {
213 	struct amd_mp2_dev *privdata;
214 	int rc;
215 
216 	privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL);
217 	if (!privdata)
218 		return -ENOMEM;
219 
220 	privdata->pdev = pdev;
221 	pci_set_drvdata(pdev, privdata);
222 	rc = pcim_enable_device(pdev);
223 	if (rc)
224 		return rc;
225 
226 	rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME);
227 	if (rc)
228 		return rc;
229 
230 	privdata->mmio = pcim_iomap_table(pdev)[2];
231 	pci_set_master(pdev);
232 	rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
233 	if (rc) {
234 		rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
235 		return rc;
236 	}
237 
238 	privdata->cl_data = devm_kzalloc(&pdev->dev, sizeof(struct amdtp_cl_data), GFP_KERNEL);
239 	if (!privdata->cl_data)
240 		return -ENOMEM;
241 
242 	rc = devm_add_action_or_reset(&pdev->dev, amd_mp2_pci_remove, privdata);
243 	if (rc)
244 		return rc;
245 
246 	mp2_select_ops(privdata);
247 
248 	return amd_sfh_hid_client_init(privdata);
249 }
250 
251 static const struct pci_device_id amd_mp2_pci_tbl[] = {
252 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) },
253 	{ }
254 };
255 MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
256 
257 static struct pci_driver amd_mp2_pci_driver = {
258 	.name		= DRIVER_NAME,
259 	.id_table	= amd_mp2_pci_tbl,
260 	.probe		= amd_mp2_pci_probe,
261 };
262 module_pci_driver(amd_mp2_pci_driver);
263 
264 MODULE_DESCRIPTION(DRIVER_DESC);
265 MODULE_LICENSE("Dual BSD/GPL");
266 MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
267 MODULE_AUTHOR("Sandeep Singh <Sandeep.singh@amd.com>");
268