FILE
amd_sfh_pcie.c
File preview with syntax highlighting.
linux-6.19.14/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
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/devm-helpers.h> 14#include <linux/dma-mapping.h> 15#include <linux/dmi.h> 16#include <linux/interrupt.h> 17#include <linux/io-64-nonatomic-lo-hi.h> 18#include <linux/iopoll.h> 19#include <linux/module.h> 20#include <linux/slab.h> 21#include <linux/string_choices.h> 22 23#include "amd_sfh_pcie.h" 24#include "sfh1_1/amd_sfh_init.h" 25 26#define DRIVER_NAME "pcie_mp2_amd" 27#define DRIVER_DESC "AMD(R) PCIe MP2 Communication Driver" 28 29#define ACEL_EN BIT(0) 30#define GYRO_EN BIT(1) 31#define MAGNO_EN BIT(2) 32#define OP_EN BIT(15) 33#define HPD_EN BIT(16) 34#define ALS_EN BIT(19) 35#define ACS_EN BIT(22) 36 37static int sensor_mask_override = -1; 38module_param_named(sensor_mask, sensor_mask_override, int, 0444); 39MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask"); 40 41static bool intr_disable = true; 42 43static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) 44{ 45 union cmd_response cmd_resp; 46 47 /* Get response with status within a max of 10 seconds timeout */ 48 if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp, 49 (cmd_resp.response_v2.response == sensor_sts && 50 cmd_resp.response_v2.status == 0 && (sid == 0xff || 51 cmd_resp.response_v2.sensor_id == sid)), 500, 10000000)) 52 return cmd_resp.response_v2.response; 53 54 return SENSOR_DISABLED; 55} 56 57static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) 58{ 59 union sfh_cmd_base cmd_base; 60 61 cmd_base.ul = 0; 62 cmd_base.cmd_v2.cmd_id = ENABLE_SENSOR; 63 cmd_base.cmd_v2.intr_disable = intr_disable; 64 cmd_base.cmd_v2.period = info.period; 65 cmd_base.cmd_v2.sensor_id = info.sensor_idx; 66 cmd_base.cmd_v2.length = 16; 67 68 if (info.sensor_idx == als_idx) 69 cmd_base.cmd_v2.mem_type = USE_C2P_REG; 70 71 writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG1); 72 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 73} 74 75static void amd_stop_sensor_v2(struct amd_mp2_dev *privdata, u16 sensor_idx) 76{ 77 union sfh_cmd_base cmd_base; 78 79 cmd_base.ul = 0; 80 cmd_base.cmd_v2.cmd_id = DISABLE_SENSOR; 81 cmd_base.cmd_v2.intr_disable = intr_disable; 82 cmd_base.cmd_v2.period = 0; 83 cmd_base.cmd_v2.sensor_id = sensor_idx; 84 cmd_base.cmd_v2.length = 16; 85 86 writeq(0x0, privdata->mmio + AMD_C2P_MSG1); 87 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 88} 89 90static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata) 91{ 92 union sfh_cmd_base cmd_base; 93 94 cmd_base.cmd_v2.cmd_id = STOP_ALL_SENSORS; 95 cmd_base.cmd_v2.intr_disable = intr_disable; 96 cmd_base.cmd_v2.period = 0; 97 cmd_base.cmd_v2.sensor_id = 0; 98 99 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 100} 101 102void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata) 103{ 104 if (readl(privdata->mmio + amd_get_p2c_val(privdata, 4))) { 105 writel(0, privdata->mmio + amd_get_p2c_val(privdata, 4)); 106 writel(0xf, privdata->mmio + amd_get_p2c_val(privdata, 5)); 107 } 108} 109 110void amd_sfh_clear_intr(struct amd_mp2_dev *privdata) 111{ 112 if (privdata->mp2_ops->clear_intr) 113 privdata->mp2_ops->clear_intr(privdata); 114} 115 116static irqreturn_t amd_sfh_irq_handler(int irq, void *data) 117{ 118 amd_sfh_clear_intr(data); 119 120 return IRQ_HANDLED; 121} 122 123int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata) 124{ 125 int rc; 126 127 pcim_intx(privdata->pdev, true); 128 129 rc = devm_request_irq(&privdata->pdev->dev, privdata->pdev->irq, 130 amd_sfh_irq_handler, 0, DRIVER_NAME, privdata); 131 if (rc) { 132 dev_err(&privdata->pdev->dev, "failed to request irq %d err=%d\n", 133 privdata->pdev->irq, rc); 134 return rc; 135 } 136 137 return 0; 138} 139 140static int amd_sfh_dis_sts_v2(struct amd_mp2_dev *privdata) 141{ 142 return (readl(privdata->mmio + AMD_P2C_MSG(1)) & 143 SENSOR_DISCOVERY_STATUS_MASK) >> SENSOR_DISCOVERY_STATUS_SHIFT; 144} 145 146static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) 147{ 148 union sfh_cmd_param cmd_param; 149 union sfh_cmd_base cmd_base; 150 151 /* fill up command register */ 152 memset(&cmd_base, 0, sizeof(cmd_base)); 153 cmd_base.s.cmd_id = ENABLE_SENSOR; 154 cmd_base.s.period = info.period; 155 cmd_base.s.sensor_id = info.sensor_idx; 156 157 /* fill up command param register */ 158 memset(&cmd_param, 0, sizeof(cmd_param)); 159 cmd_param.s.buf_layout = 1; 160 cmd_param.s.buf_length = 16; 161 162 writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG2); 163 writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1); 164 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 165} 166 167static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) 168{ 169 union sfh_cmd_base cmd_base; 170 171 /* fill up command register */ 172 memset(&cmd_base, 0, sizeof(cmd_base)); 173 cmd_base.s.cmd_id = DISABLE_SENSOR; 174 cmd_base.s.period = 0; 175 cmd_base.s.sensor_id = sensor_idx; 176 177 writeq(0x0, privdata->mmio + AMD_C2P_MSG2); 178 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 179} 180 181static void amd_stop_all_sensors(struct amd_mp2_dev *privdata) 182{ 183 union sfh_cmd_base cmd_base; 184 185 /* fill up command register */ 186 memset(&cmd_base, 0, sizeof(cmd_base)); 187 cmd_base.s.cmd_id = STOP_ALL_SENSORS; 188 cmd_base.s.period = 0; 189 cmd_base.s.sensor_id = 0; 190 191 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 192} 193 194static const struct dmi_system_id dmi_sensor_mask_overrides[] = { 195 { 196 .matches = { 197 DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 13-ag0xxx"), 198 }, 199 .driver_data = (void *)(ACEL_EN | MAGNO_EN), 200 }, 201 { 202 .matches = { 203 DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 15-cp0xxx"), 204 }, 205 .driver_data = (void *)(ACEL_EN | MAGNO_EN), 206 }, 207 { } 208}; 209 210int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id) 211{ 212 int activestatus, num_of_sensors = 0; 213 const struct dmi_system_id *dmi_id; 214 215 if (sensor_mask_override == -1) { 216 dmi_id = dmi_first_match(dmi_sensor_mask_overrides); 217 if (dmi_id) 218 sensor_mask_override = (long)dmi_id->driver_data; 219 } 220 221 if (sensor_mask_override >= 0) { 222 activestatus = sensor_mask_override; 223 } else { 224 activestatus = privdata->mp2_acs >> 4; 225 } 226 227 if (ACEL_EN & activestatus) 228 sensor_id[num_of_sensors++] = accel_idx; 229 230 if (GYRO_EN & activestatus) 231 sensor_id[num_of_sensors++] = gyro_idx; 232 233 if (MAGNO_EN & activestatus) 234 sensor_id[num_of_sensors++] = mag_idx; 235 236 if (OP_EN & activestatus) 237 sensor_id[num_of_sensors++] = op_idx; 238 239 if (ALS_EN & activestatus) 240 sensor_id[num_of_sensors++] = als_idx; 241 242 if (HPD_EN & activestatus) 243 sensor_id[num_of_sensors++] = HPD_IDX; 244 245 if (ACS_EN & activestatus) 246 sensor_id[num_of_sensors++] = ACS_IDX; 247 248 return num_of_sensors; 249} 250 251static void amd_mp2_pci_remove(void *privdata) 252{ 253 struct amd_mp2_dev *mp2 = privdata; 254 amd_sfh_hid_client_deinit(privdata); 255 mp2->mp2_ops->stop_all(mp2); 256 pcim_intx(mp2->pdev, false); 257 amd_sfh_clear_intr(mp2); 258} 259 260static struct amd_mp2_ops amd_sfh_ops_v2 = { 261 .start = amd_start_sensor_v2, 262 .stop = amd_stop_sensor_v2, 263 .stop_all = amd_stop_all_sensor_v2, 264 .response = amd_sfh_wait_response_v2, 265 .clear_intr = amd_sfh_clear_intr_v2, 266 .init_intr = amd_sfh_irq_init_v2, 267 .discovery_status = amd_sfh_dis_sts_v2, 268 .remove = amd_mp2_pci_remove, 269}; 270 271static struct amd_mp2_ops amd_sfh_ops = { 272 .start = amd_start_sensor, 273 .stop = amd_stop_sensor, 274 .stop_all = amd_stop_all_sensors, 275 .remove = amd_mp2_pci_remove, 276}; 277 278static void mp2_select_ops(struct amd_mp2_dev *privdata) 279{ 280 u8 acs; 281 282 privdata->mp2_acs = readl(privdata->mmio + AMD_P2C_MSG3); 283 acs = privdata->mp2_acs & GENMASK(3, 0); 284 285 switch (acs) { 286 case V2_STATUS: 287 privdata->mp2_ops = &amd_sfh_ops_v2; 288 break; 289 default: 290 privdata->mp2_ops = &amd_sfh_ops; 291 break; 292 } 293} 294 295int amd_sfh_irq_init(struct amd_mp2_dev *privdata) 296{ 297 if (privdata->mp2_ops->init_intr) 298 return privdata->mp2_ops->init_intr(privdata); 299 300 return 0; 301} 302 303static int mp2_disable_intr(const struct dmi_system_id *id) 304{ 305 intr_disable = false; 306 return 0; 307} 308 309static const struct dmi_system_id dmi_sfh_table[] = { 310 { 311 /* 312 * https://bugzilla.kernel.org/show_bug.cgi?id=218104 313 */ 314 .callback = mp2_disable_intr, 315 .matches = { 316 DMI_MATCH(DMI_SYS_VENDOR, "HP"), 317 DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 435 G7"), 318 }, 319 }, 320 {} 321}; 322 323static const struct dmi_system_id dmi_nodevs[] = { 324 { 325 /* 326 * Google Chromebooks use Chrome OS Embedded Controller Sensor 327 * Hub instead of Sensor Hub Fusion and leaves MP2 328 * uninitialized, which disables all functionalities, even 329 * including the registers necessary for feature detections. 330 */ 331 .matches = { 332 DMI_MATCH(DMI_SYS_VENDOR, "Google"), 333 }, 334 }, 335 { } 336}; 337 338static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf) 339{ 340 struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); 341 342 return sysfs_emit(buf, "%s\n", str_enabled_disabled(mp2->dev_en.is_hpd_enabled)); 343} 344 345static ssize_t hpd_store(struct device *dev, 346 struct device_attribute *attr, 347 const char *buf, size_t count) 348{ 349 struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); 350 bool enabled; 351 int ret; 352 353 ret = kstrtobool(buf, &enabled); 354 if (ret) 355 return ret; 356 357 mp2->sfh1_1_ops->toggle_hpd(mp2, enabled); 358 359 return count; 360} 361static DEVICE_ATTR_RW(hpd); 362 363static umode_t sfh_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) 364{ 365 struct device *dev = kobj_to_dev(kobj); 366 struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); 367 368 if (!mp2->sfh1_1_ops || !mp2->dev_en.is_hpd_present) 369 return 0; 370 371 return attr->mode; 372} 373 374static struct attribute *sfh_attrs[] = { 375 &dev_attr_hpd.attr, 376 NULL, 377}; 378 379static struct attribute_group sfh_attr_group = { 380 .attrs = sfh_attrs, 381 .is_visible = sfh_attr_is_visible, 382}; 383 384static const struct attribute_group *amd_sfh_groups[] = { 385 &sfh_attr_group, 386 NULL, 387}; 388 389static void sfh1_1_init_work(struct work_struct *work) 390{ 391 struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work); 392 int rc; 393 394 rc = mp2->sfh1_1_ops->init(mp2); 395 if (rc) 396 return; 397 398 amd_sfh_clear_intr(mp2); 399 mp2->init_done = 1; 400 401 rc = sysfs_update_group(&mp2->pdev->dev.kobj, &sfh_attr_group); 402 if (rc) 403 dev_warn(&mp2->pdev->dev, "failed to update sysfs group\n"); 404 405} 406 407static void sfh_init_work(struct work_struct *work) 408{ 409 struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work); 410 struct pci_dev *pdev = mp2->pdev; 411 int rc; 412 413 rc = amd_sfh_hid_client_init(mp2); 414 if (rc) { 415 amd_sfh_clear_intr(mp2); 416 if (rc != -EOPNOTSUPP) 417 dev_err(&pdev->dev, "amd_sfh_hid_client_init failed err %d\n", rc); 418 return; 419 } 420 421 amd_sfh_clear_intr(mp2); 422 mp2->init_done = 1; 423} 424 425static void amd_sfh_remove(struct pci_dev *pdev) 426{ 427 struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); 428 429 flush_work(&mp2->work); 430 if (mp2->init_done) 431 mp2->mp2_ops->remove(mp2); 432} 433 434static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 435{ 436 struct amd_mp2_dev *privdata; 437 int rc; 438 439 if (dmi_first_match(dmi_nodevs)) 440 return -ENODEV; 441 442 dmi_check_system(dmi_sfh_table); 443 444 privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL); 445 if (!privdata) 446 return -ENOMEM; 447 448 privdata->pdev = pdev; 449 dev_set_drvdata(&pdev->dev, privdata); 450 rc = pcim_enable_device(pdev); 451 if (rc) 452 return rc; 453 454 rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME); 455 if (rc) 456 return rc; 457 458 privdata->mmio = pcim_iomap_table(pdev)[2]; 459 pci_set_master(pdev); 460 rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 461 if (rc) { 462 dev_err(&pdev->dev, "failed to set DMA mask\n"); 463 return rc; 464 } 465 466 privdata->cl_data = devm_kzalloc(&pdev->dev, sizeof(struct amdtp_cl_data), GFP_KERNEL); 467 if (!privdata->cl_data) 468 return -ENOMEM; 469 470 rc = devm_mutex_init(&pdev->dev, &privdata->lock); 471 if (rc) 472 return rc; 473 474 privdata->sfh1_1_ops = (const struct amd_sfh1_1_ops *)id->driver_data; 475 if (privdata->sfh1_1_ops) { 476 if (boot_cpu_data.x86 >= 0x1A) 477 privdata->rver = 1; 478 479 rc = devm_work_autocancel(&pdev->dev, &privdata->work, sfh1_1_init_work); 480 if (rc) 481 return rc; 482 483 schedule_work(&privdata->work); 484 return 0; 485 } 486 487 mp2_select_ops(privdata); 488 489 rc = amd_sfh_irq_init(privdata); 490 if (rc) { 491 dev_err(&pdev->dev, "amd_sfh_irq_init failed\n"); 492 return rc; 493 } 494 495 rc = devm_work_autocancel(&pdev->dev, &privdata->work, sfh_init_work); 496 if (rc) { 497 amd_sfh_clear_intr(privdata); 498 return rc; 499 } 500 501 schedule_work(&privdata->work); 502 return 0; 503} 504 505static void amd_sfh_shutdown(struct pci_dev *pdev) 506{ 507 struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); 508 509 if (mp2) { 510 flush_work(&mp2->work); 511 if (mp2->init_done) 512 mp2->mp2_ops->stop_all(mp2); 513 } 514} 515 516static int __maybe_unused amd_mp2_pci_resume(struct device *dev) 517{ 518 struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); 519 520 flush_work(&mp2->work); 521 if (mp2->init_done) 522 mp2->mp2_ops->resume(mp2); 523 524 return 0; 525} 526 527static int __maybe_unused amd_mp2_pci_suspend(struct device *dev) 528{ 529 struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); 530 531 flush_work(&mp2->work); 532 if (mp2->init_done) 533 mp2->mp2_ops->suspend(mp2); 534 535 return 0; 536} 537 538static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend, 539 amd_mp2_pci_resume); 540 541static const struct pci_device_id amd_mp2_pci_tbl[] = { 542 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) }, 543 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2_1_1), 544 .driver_data = (kernel_ulong_t)&sfh1_1_ops }, 545 { } 546}; 547MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl); 548 549static struct pci_driver amd_mp2_pci_driver = { 550 .name = DRIVER_NAME, 551 .id_table = amd_mp2_pci_tbl, 552 .probe = amd_mp2_pci_probe, 553 .driver.pm = &amd_mp2_pm_ops, 554 .shutdown = amd_sfh_shutdown, 555 .remove = amd_sfh_remove, 556 .dev_groups = amd_sfh_groups, 557}; 558module_pci_driver(amd_mp2_pci_driver); 559 560MODULE_DESCRIPTION(DRIVER_DESC); 561MODULE_LICENSE("Dual BSD/GPL"); 562MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>"); 563MODULE_AUTHOR("Sandeep Singh <Sandeep.singh@amd.com>"); 564MODULE_AUTHOR("Basavaraj Natikar <Basavaraj.Natikar@amd.com>");
~/
Read-only file preview.