1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * AMD MP2 PCIe communication driver 4 * Copyright 2020-2021 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 * Basavaraj Natikar <Basavaraj.Natikar@amd.com> 9 */ 10 11 #include <linux/bitops.h> 12 #include <linux/delay.h> 13 #include <linux/dma-mapping.h> 14 #include <linux/dmi.h> 15 #include <linux/interrupt.h> 16 #include <linux/io-64-nonatomic-lo-hi.h> 17 #include <linux/iopoll.h> 18 #include <linux/module.h> 19 #include <linux/slab.h> 20 21 #include "amd_sfh_pcie.h" 22 #include "sfh1_1/amd_sfh_init.h" 23 24 #define DRIVER_NAME "pcie_mp2_amd" 25 #define DRIVER_DESC "AMD(R) PCIe MP2 Communication Driver" 26 27 #define ACEL_EN BIT(0) 28 #define GYRO_EN BIT(1) 29 #define MAGNO_EN BIT(2) 30 #define HPD_EN BIT(16) 31 #define ALS_EN BIT(19) 32 #define ACS_EN BIT(22) 33 34 static int sensor_mask_override = -1; 35 module_param_named(sensor_mask, sensor_mask_override, int, 0444); 36 MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask"); 37 38 static bool intr_disable = true; 39 40 static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) 41 { 42 union cmd_response cmd_resp; 43 44 /* Get response with status within a max of 1600 ms timeout */ 45 if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp, 46 (cmd_resp.response_v2.response == sensor_sts && 47 cmd_resp.response_v2.status == 0 && (sid == 0xff || 48 cmd_resp.response_v2.sensor_id == sid)), 500, 1600000)) 49 return cmd_resp.response_v2.response; 50 51 return SENSOR_DISABLED; 52 } 53 54 static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) 55 { 56 union sfh_cmd_base cmd_base; 57 58 cmd_base.ul = 0; 59 cmd_base.cmd_v2.cmd_id = ENABLE_SENSOR; 60 cmd_base.cmd_v2.intr_disable = intr_disable; 61 cmd_base.cmd_v2.period = info.period; 62 cmd_base.cmd_v2.sensor_id = info.sensor_idx; 63 cmd_base.cmd_v2.length = 16; 64 65 if (info.sensor_idx == als_idx) 66 cmd_base.cmd_v2.mem_type = USE_C2P_REG; 67 68 writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG1); 69 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 70 } 71 72 static void amd_stop_sensor_v2(struct amd_mp2_dev *privdata, u16 sensor_idx) 73 { 74 union sfh_cmd_base cmd_base; 75 76 cmd_base.ul = 0; 77 cmd_base.cmd_v2.cmd_id = DISABLE_SENSOR; 78 cmd_base.cmd_v2.intr_disable = intr_disable; 79 cmd_base.cmd_v2.period = 0; 80 cmd_base.cmd_v2.sensor_id = sensor_idx; 81 cmd_base.cmd_v2.length = 16; 82 83 writeq(0x0, privdata->mmio + AMD_C2P_MSG1); 84 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 85 } 86 87 static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata) 88 { 89 union sfh_cmd_base cmd_base; 90 91 cmd_base.cmd_v2.cmd_id = STOP_ALL_SENSORS; 92 cmd_base.cmd_v2.intr_disable = intr_disable; 93 cmd_base.cmd_v2.period = 0; 94 cmd_base.cmd_v2.sensor_id = 0; 95 96 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 97 } 98 99 void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata) 100 { 101 if (readl(privdata->mmio + AMD_P2C_MSG(4))) { 102 writel(0, privdata->mmio + AMD_P2C_MSG(4)); 103 writel(0xf, privdata->mmio + AMD_P2C_MSG(5)); 104 } 105 } 106 107 void amd_sfh_clear_intr(struct amd_mp2_dev *privdata) 108 { 109 if (privdata->mp2_ops->clear_intr) 110 privdata->mp2_ops->clear_intr(privdata); 111 } 112 113 static irqreturn_t amd_sfh_irq_handler(int irq, void *data) 114 { 115 amd_sfh_clear_intr(data); 116 117 return IRQ_HANDLED; 118 } 119 120 int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata) 121 { 122 int rc; 123 124 pci_intx(privdata->pdev, true); 125 126 rc = devm_request_irq(&privdata->pdev->dev, privdata->pdev->irq, 127 amd_sfh_irq_handler, 0, DRIVER_NAME, privdata); 128 if (rc) { 129 dev_err(&privdata->pdev->dev, "failed to request irq %d err=%d\n", 130 privdata->pdev->irq, rc); 131 return rc; 132 } 133 134 return 0; 135 } 136 137 static int amd_sfh_dis_sts_v2(struct amd_mp2_dev *privdata) 138 { 139 return (readl(privdata->mmio + AMD_P2C_MSG(1)) & 140 SENSOR_DISCOVERY_STATUS_MASK) >> SENSOR_DISCOVERY_STATUS_SHIFT; 141 } 142 143 static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) 144 { 145 union sfh_cmd_param cmd_param; 146 union sfh_cmd_base cmd_base; 147 148 /* fill up command register */ 149 memset(&cmd_base, 0, sizeof(cmd_base)); 150 cmd_base.s.cmd_id = ENABLE_SENSOR; 151 cmd_base.s.period = info.period; 152 cmd_base.s.sensor_id = info.sensor_idx; 153 154 /* fill up command param register */ 155 memset(&cmd_param, 0, sizeof(cmd_param)); 156 cmd_param.s.buf_layout = 1; 157 cmd_param.s.buf_length = 16; 158 159 writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG2); 160 writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1); 161 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 162 } 163 164 static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) 165 { 166 union sfh_cmd_base cmd_base; 167 168 /* fill up command register */ 169 memset(&cmd_base, 0, sizeof(cmd_base)); 170 cmd_base.s.cmd_id = DISABLE_SENSOR; 171 cmd_base.s.period = 0; 172 cmd_base.s.sensor_id = sensor_idx; 173 174 writeq(0x0, privdata->mmio + AMD_C2P_MSG2); 175 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 176 } 177 178 static void amd_stop_all_sensors(struct amd_mp2_dev *privdata) 179 { 180 union sfh_cmd_base cmd_base; 181 182 /* fill up command register */ 183 memset(&cmd_base, 0, sizeof(cmd_base)); 184 cmd_base.s.cmd_id = STOP_ALL_SENSORS; 185 cmd_base.s.period = 0; 186 cmd_base.s.sensor_id = 0; 187 188 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 189 } 190 191 static const struct dmi_system_id dmi_sensor_mask_overrides[] = { 192 { 193 .matches = { 194 DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 13-ag0xxx"), 195 }, 196 .driver_data = (void *)(ACEL_EN | MAGNO_EN), 197 }, 198 { 199 .matches = { 200 DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 15-cp0xxx"), 201 }, 202 .driver_data = (void *)(ACEL_EN | MAGNO_EN), 203 }, 204 { } 205 }; 206 207 int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id) 208 { 209 int activestatus, num_of_sensors = 0; 210 const struct dmi_system_id *dmi_id; 211 212 if (sensor_mask_override == -1) { 213 dmi_id = dmi_first_match(dmi_sensor_mask_overrides); 214 if (dmi_id) 215 sensor_mask_override = (long)dmi_id->driver_data; 216 } 217 218 if (sensor_mask_override >= 0) { 219 activestatus = sensor_mask_override; 220 } else { 221 activestatus = privdata->mp2_acs >> 4; 222 } 223 224 if (ACEL_EN & activestatus) 225 sensor_id[num_of_sensors++] = accel_idx; 226 227 if (GYRO_EN & activestatus) 228 sensor_id[num_of_sensors++] = gyro_idx; 229 230 if (MAGNO_EN & activestatus) 231 sensor_id[num_of_sensors++] = mag_idx; 232 233 if (ALS_EN & activestatus) 234 sensor_id[num_of_sensors++] = als_idx; 235 236 if (HPD_EN & activestatus) 237 sensor_id[num_of_sensors++] = HPD_IDX; 238 239 if (ACS_EN & activestatus) 240 sensor_id[num_of_sensors++] = ACS_IDX; 241 242 return num_of_sensors; 243 } 244 245 static void amd_mp2_pci_remove(void *privdata) 246 { 247 struct amd_mp2_dev *mp2 = privdata; 248 amd_sfh_hid_client_deinit(privdata); 249 mp2->mp2_ops->stop_all(mp2); 250 pci_intx(mp2->pdev, false); 251 amd_sfh_clear_intr(mp2); 252 } 253 254 static struct amd_mp2_ops amd_sfh_ops_v2 = { 255 .start = amd_start_sensor_v2, 256 .stop = amd_stop_sensor_v2, 257 .stop_all = amd_stop_all_sensor_v2, 258 .response = amd_sfh_wait_response_v2, 259 .clear_intr = amd_sfh_clear_intr_v2, 260 .init_intr = amd_sfh_irq_init_v2, 261 .discovery_status = amd_sfh_dis_sts_v2, 262 .remove = amd_mp2_pci_remove, 263 }; 264 265 static struct amd_mp2_ops amd_sfh_ops = { 266 .start = amd_start_sensor, 267 .stop = amd_stop_sensor, 268 .stop_all = amd_stop_all_sensors, 269 .remove = amd_mp2_pci_remove, 270 }; 271 272 static void mp2_select_ops(struct amd_mp2_dev *privdata) 273 { 274 u8 acs; 275 276 privdata->mp2_acs = readl(privdata->mmio + AMD_P2C_MSG3); 277 acs = privdata->mp2_acs & GENMASK(3, 0); 278 279 switch (acs) { 280 case V2_STATUS: 281 privdata->mp2_ops = &amd_sfh_ops_v2; 282 break; 283 default: 284 privdata->mp2_ops = &amd_sfh_ops; 285 break; 286 } 287 } 288 289 int amd_sfh_irq_init(struct amd_mp2_dev *privdata) 290 { 291 if (privdata->mp2_ops->init_intr) 292 return privdata->mp2_ops->init_intr(privdata); 293 294 return 0; 295 } 296 297 static int mp2_disable_intr(const struct dmi_system_id *id) 298 { 299 intr_disable = false; 300 return 0; 301 } 302 303 static const struct dmi_system_id dmi_sfh_table[] = { 304 { 305 /* 306 * https://bugzilla.kernel.org/show_bug.cgi?id=218104 307 */ 308 .callback = mp2_disable_intr, 309 .matches = { 310 DMI_MATCH(DMI_SYS_VENDOR, "HP"), 311 DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 435 G7"), 312 }, 313 }, 314 {} 315 }; 316 317 static const struct dmi_system_id dmi_nodevs[] = { 318 { 319 /* 320 * Google Chromebooks use Chrome OS Embedded Controller Sensor 321 * Hub instead of Sensor Hub Fusion and leaves MP2 322 * uninitialized, which disables all functionalities, even 323 * including the registers necessary for feature detections. 324 */ 325 .matches = { 326 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 327 }, 328 }, 329 { } 330 }; 331 332 static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 333 { 334 struct amd_mp2_dev *privdata; 335 int rc; 336 337 if (dmi_first_match(dmi_nodevs)) 338 return -ENODEV; 339 340 dmi_check_system(dmi_sfh_table); 341 342 privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL); 343 if (!privdata) 344 return -ENOMEM; 345 346 privdata->pdev = pdev; 347 dev_set_drvdata(&pdev->dev, privdata); 348 rc = pcim_enable_device(pdev); 349 if (rc) 350 return rc; 351 352 rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME); 353 if (rc) 354 return rc; 355 356 privdata->mmio = pcim_iomap_table(pdev)[2]; 357 pci_set_master(pdev); 358 rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 359 if (rc) { 360 dev_err(&pdev->dev, "failed to set DMA mask\n"); 361 return rc; 362 } 363 364 privdata->cl_data = devm_kzalloc(&pdev->dev, sizeof(struct amdtp_cl_data), GFP_KERNEL); 365 if (!privdata->cl_data) 366 return -ENOMEM; 367 368 privdata->sfh1_1_ops = (const struct amd_sfh1_1_ops *)id->driver_data; 369 if (privdata->sfh1_1_ops) { 370 rc = privdata->sfh1_1_ops->init(privdata); 371 if (rc) 372 return rc; 373 goto init_done; 374 } 375 376 mp2_select_ops(privdata); 377 378 rc = amd_sfh_irq_init(privdata); 379 if (rc) { 380 dev_err(&pdev->dev, "amd_sfh_irq_init failed\n"); 381 return rc; 382 } 383 384 rc = amd_sfh_hid_client_init(privdata); 385 if (rc) { 386 amd_sfh_clear_intr(privdata); 387 if (rc != -EOPNOTSUPP) 388 dev_err(&pdev->dev, "amd_sfh_hid_client_init failed\n"); 389 return rc; 390 } 391 392 init_done: 393 amd_sfh_clear_intr(privdata); 394 395 return devm_add_action_or_reset(&pdev->dev, privdata->mp2_ops->remove, privdata); 396 } 397 398 static void amd_sfh_shutdown(struct pci_dev *pdev) 399 { 400 struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); 401 402 if (mp2 && mp2->mp2_ops) 403 mp2->mp2_ops->stop_all(mp2); 404 } 405 406 static int __maybe_unused amd_mp2_pci_resume(struct device *dev) 407 { 408 struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); 409 410 mp2->mp2_ops->resume(mp2); 411 412 return 0; 413 } 414 415 static int __maybe_unused amd_mp2_pci_suspend(struct device *dev) 416 { 417 struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); 418 419 mp2->mp2_ops->suspend(mp2); 420 421 return 0; 422 } 423 424 static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend, 425 amd_mp2_pci_resume); 426 427 static const struct pci_device_id amd_mp2_pci_tbl[] = { 428 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) }, 429 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2_1_1), 430 .driver_data = (kernel_ulong_t)&sfh1_1_ops }, 431 { } 432 }; 433 MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl); 434 435 static struct pci_driver amd_mp2_pci_driver = { 436 .name = DRIVER_NAME, 437 .id_table = amd_mp2_pci_tbl, 438 .probe = amd_mp2_pci_probe, 439 .driver.pm = &amd_mp2_pm_ops, 440 .shutdown = amd_sfh_shutdown, 441 }; 442 module_pci_driver(amd_mp2_pci_driver); 443 444 MODULE_DESCRIPTION(DRIVER_DESC); 445 MODULE_LICENSE("Dual BSD/GPL"); 446 MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>"); 447 MODULE_AUTHOR("Sandeep Singh <Sandeep.singh@amd.com>"); 448 MODULE_AUTHOR("Basavaraj Natikar <Basavaraj.Natikar@amd.com>"); 449