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