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