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_MSG1); 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