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
amd_start_sensor(struct amd_mp2_dev * privdata,struct amd_mp2_sensor_info info)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
amd_stop_sensor(struct amd_mp2_dev * privdata,u16 sensor_idx)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
amd_stop_all_sensors(struct amd_mp2_dev * privdata)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
amd_mp2_get_sensor_num(struct amd_mp2_dev * privdata,u8 * sensor_id)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
amd_mp2_pci_remove(void * privdata)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
amd_mp2_pci_probe(struct pci_dev * pdev,const struct pci_device_id * id)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