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 ALS_EN		BIT(19)
28 
29 static int sensor_mask_override = -1;
30 module_param_named(sensor_mask, sensor_mask_override, int, 0444);
31 MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
32 
33 void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
34 {
35 	union sfh_cmd_param cmd_param;
36 	union sfh_cmd_base cmd_base;
37 
38 	/* fill up command register */
39 	memset(&cmd_base, 0, sizeof(cmd_base));
40 	cmd_base.s.cmd_id = ENABLE_SENSOR;
41 	cmd_base.s.period = info.period;
42 	cmd_base.s.sensor_id = info.sensor_idx;
43 
44 	/* fill up command param register */
45 	memset(&cmd_param, 0, sizeof(cmd_param));
46 	cmd_param.s.buf_layout = 1;
47 	cmd_param.s.buf_length = 16;
48 
49 	writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG2);
50 	writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1);
51 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
52 }
53 
54 void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
55 {
56 	union sfh_cmd_base cmd_base;
57 
58 	/* fill up command register */
59 	memset(&cmd_base, 0, sizeof(cmd_base));
60 	cmd_base.s.cmd_id = DISABLE_SENSOR;
61 	cmd_base.s.period = 0;
62 	cmd_base.s.sensor_id = sensor_idx;
63 
64 	writeq(0x0, privdata->mmio + AMD_C2P_MSG2);
65 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
66 }
67 
68 void amd_stop_all_sensors(struct amd_mp2_dev *privdata)
69 {
70 	union sfh_cmd_base cmd_base;
71 
72 	/* fill up command register */
73 	memset(&cmd_base, 0, sizeof(cmd_base));
74 	cmd_base.s.cmd_id = STOP_ALL_SENSORS;
75 	cmd_base.s.period = 0;
76 	cmd_base.s.sensor_id = 0;
77 
78 	writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
79 }
80 
81 static const struct dmi_system_id dmi_sensor_mask_overrides[] = {
82 	{
83 		.matches = {
84 			DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 13-ag0xxx"),
85 		},
86 		.driver_data = (void *)(ACEL_EN | MAGNO_EN),
87 	},
88 	{
89 		.matches = {
90 			DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 15-cp0xxx"),
91 		},
92 		.driver_data = (void *)(ACEL_EN | MAGNO_EN),
93 	},
94 	{ }
95 };
96 
97 int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id)
98 {
99 	int activestatus, num_of_sensors = 0;
100 	const struct dmi_system_id *dmi_id;
101 	u32 activecontrolstatus;
102 
103 	if (sensor_mask_override == -1) {
104 		dmi_id = dmi_first_match(dmi_sensor_mask_overrides);
105 		if (dmi_id)
106 			sensor_mask_override = (long)dmi_id->driver_data;
107 	}
108 
109 	if (sensor_mask_override >= 0) {
110 		activestatus = sensor_mask_override;
111 	} else {
112 		activecontrolstatus = readl(privdata->mmio + AMD_P2C_MSG3);
113 		activestatus = activecontrolstatus >> 4;
114 	}
115 
116 	if (ACEL_EN  & activestatus)
117 		sensor_id[num_of_sensors++] = accel_idx;
118 
119 	if (GYRO_EN & activestatus)
120 		sensor_id[num_of_sensors++] = gyro_idx;
121 
122 	if (MAGNO_EN & activestatus)
123 		sensor_id[num_of_sensors++] = mag_idx;
124 
125 	if (ALS_EN & activestatus)
126 		sensor_id[num_of_sensors++] = als_idx;
127 
128 	return num_of_sensors;
129 }
130 
131 static void amd_mp2_pci_remove(void *privdata)
132 {
133 	amd_sfh_hid_client_deinit(privdata);
134 	amd_stop_all_sensors(privdata);
135 }
136 
137 static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
138 {
139 	struct amd_mp2_dev *privdata;
140 	int rc;
141 
142 	privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL);
143 	if (!privdata)
144 		return -ENOMEM;
145 
146 	privdata->pdev = pdev;
147 	pci_set_drvdata(pdev, privdata);
148 	rc = pcim_enable_device(pdev);
149 	if (rc)
150 		return rc;
151 
152 	rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME);
153 	if (rc)
154 		return rc;
155 
156 	privdata->mmio = pcim_iomap_table(pdev)[2];
157 	pci_set_master(pdev);
158 	rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
159 	if (rc) {
160 		rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
161 		return rc;
162 	}
163 	rc = devm_add_action_or_reset(&pdev->dev, amd_mp2_pci_remove, privdata);
164 	if (rc)
165 		return rc;
166 
167 	return amd_sfh_hid_client_init(privdata);
168 }
169 
170 static const struct pci_device_id amd_mp2_pci_tbl[] = {
171 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) },
172 	{ }
173 };
174 MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
175 
176 static struct pci_driver amd_mp2_pci_driver = {
177 	.name		= DRIVER_NAME,
178 	.id_table	= amd_mp2_pci_tbl,
179 	.probe		= amd_mp2_pci_probe,
180 };
181 module_pci_driver(amd_mp2_pci_driver);
182 
183 MODULE_DESCRIPTION(DRIVER_DESC);
184 MODULE_LICENSE("Dual BSD/GPL");
185 MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
186 MODULE_AUTHOR("Sandeep Singh <Sandeep.singh@amd.com>");
187