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 ALS_EN BIT(19) 28 29 static int sensor_mask_override = -1; 30 module_param_named(sensor_mask, sensor_mask_override, int, 0444); 31 MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask"); 32 33 void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) 34 { 35 union sfh_cmd_param cmd_param; 36 union sfh_cmd_base cmd_base; 37 38 /* fill up command register */ 39 memset(&cmd_base, 0, sizeof(cmd_base)); 40 cmd_base.s.cmd_id = ENABLE_SENSOR; 41 cmd_base.s.period = info.period; 42 cmd_base.s.sensor_id = info.sensor_idx; 43 44 /* fill up command param register */ 45 memset(&cmd_param, 0, sizeof(cmd_param)); 46 cmd_param.s.buf_layout = 1; 47 cmd_param.s.buf_length = 16; 48 49 writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG2); 50 writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1); 51 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 52 } 53 54 void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) 55 { 56 union sfh_cmd_base cmd_base; 57 58 /* fill up command register */ 59 memset(&cmd_base, 0, sizeof(cmd_base)); 60 cmd_base.s.cmd_id = DISABLE_SENSOR; 61 cmd_base.s.period = 0; 62 cmd_base.s.sensor_id = sensor_idx; 63 64 writeq(0x0, privdata->mmio + AMD_C2P_MSG2); 65 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 66 } 67 68 void amd_stop_all_sensors(struct amd_mp2_dev *privdata) 69 { 70 union sfh_cmd_base cmd_base; 71 72 /* fill up command register */ 73 memset(&cmd_base, 0, sizeof(cmd_base)); 74 cmd_base.s.cmd_id = STOP_ALL_SENSORS; 75 cmd_base.s.period = 0; 76 cmd_base.s.sensor_id = 0; 77 78 writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); 79 } 80 81 static const struct dmi_system_id dmi_sensor_mask_overrides[] = { 82 { 83 .matches = { 84 DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 13-ag0xxx"), 85 }, 86 .driver_data = (void *)(ACEL_EN | MAGNO_EN), 87 }, 88 { 89 .matches = { 90 DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 15-cp0xxx"), 91 }, 92 .driver_data = (void *)(ACEL_EN | MAGNO_EN), 93 }, 94 { } 95 }; 96 97 int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id) 98 { 99 int activestatus, num_of_sensors = 0; 100 const struct dmi_system_id *dmi_id; 101 u32 activecontrolstatus; 102 103 if (sensor_mask_override == -1) { 104 dmi_id = dmi_first_match(dmi_sensor_mask_overrides); 105 if (dmi_id) 106 sensor_mask_override = (long)dmi_id->driver_data; 107 } 108 109 if (sensor_mask_override >= 0) { 110 activestatus = sensor_mask_override; 111 } else { 112 activecontrolstatus = readl(privdata->mmio + AMD_P2C_MSG3); 113 activestatus = activecontrolstatus >> 4; 114 } 115 116 if (ACEL_EN & activestatus) 117 sensor_id[num_of_sensors++] = accel_idx; 118 119 if (GYRO_EN & activestatus) 120 sensor_id[num_of_sensors++] = gyro_idx; 121 122 if (MAGNO_EN & activestatus) 123 sensor_id[num_of_sensors++] = mag_idx; 124 125 if (ALS_EN & activestatus) 126 sensor_id[num_of_sensors++] = als_idx; 127 128 return num_of_sensors; 129 } 130 131 static void amd_mp2_pci_remove(void *privdata) 132 { 133 amd_sfh_hid_client_deinit(privdata); 134 amd_stop_all_sensors(privdata); 135 } 136 137 static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 138 { 139 struct amd_mp2_dev *privdata; 140 int rc; 141 142 privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL); 143 if (!privdata) 144 return -ENOMEM; 145 146 privdata->pdev = pdev; 147 pci_set_drvdata(pdev, privdata); 148 rc = pcim_enable_device(pdev); 149 if (rc) 150 return rc; 151 152 rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME); 153 if (rc) 154 return rc; 155 156 privdata->mmio = pcim_iomap_table(pdev)[2]; 157 pci_set_master(pdev); 158 rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); 159 if (rc) { 160 rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 161 return rc; 162 } 163 rc = devm_add_action_or_reset(&pdev->dev, amd_mp2_pci_remove, privdata); 164 if (rc) 165 return rc; 166 167 return amd_sfh_hid_client_init(privdata); 168 } 169 170 static const struct pci_device_id amd_mp2_pci_tbl[] = { 171 { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) }, 172 { } 173 }; 174 MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl); 175 176 static struct pci_driver amd_mp2_pci_driver = { 177 .name = DRIVER_NAME, 178 .id_table = amd_mp2_pci_tbl, 179 .probe = amd_mp2_pci_probe, 180 }; 181 module_pci_driver(amd_mp2_pci_driver); 182 183 MODULE_DESCRIPTION(DRIVER_DESC); 184 MODULE_LICENSE("Dual BSD/GPL"); 185 MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>"); 186 MODULE_AUTHOR("Sandeep Singh <Sandeep.singh@amd.com>"); 187