linux/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
<<
>>
Prefs
   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/iopoll.h>
  17#include <linux/module.h>
  18#include <linux/slab.h>
  19
  20#include "amd_sfh_pcie.h"
  21
  22#define DRIVER_NAME     "pcie_mp2_amd"
  23#define DRIVER_DESC     "AMD(R) PCIe MP2 Communication Driver"
  24
  25#define ACEL_EN         BIT(0)
  26#define GYRO_EN         BIT(1)
  27#define MAGNO_EN        BIT(2)
  28#define HPD_EN          BIT(16)
  29#define ALS_EN          BIT(19)
  30
  31static int sensor_mask_override = -1;
  32module_param_named(sensor_mask, sensor_mask_override, int, 0444);
  33MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
  34
  35static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
  36{
  37        union cmd_response cmd_resp;
  38
  39        /* Get response with status within a max of 800 ms timeout */
  40        if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp,
  41                                (cmd_resp.response_v2.response == sensor_sts &&
  42                                cmd_resp.response_v2.status == 0 && (sid == 0xff ||
  43                                cmd_resp.response_v2.sensor_id == sid)), 500, 800000))
  44                return cmd_resp.response_v2.response;
  45
  46        return SENSOR_DISABLED;
  47}
  48
  49static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
  50{
  51        union sfh_cmd_base cmd_base;
  52
  53        cmd_base.ul = 0;
  54        cmd_base.cmd_v2.cmd_id = ENABLE_SENSOR;
  55        cmd_base.cmd_v2.period = info.period;
  56        cmd_base.cmd_v2.sensor_id = info.sensor_idx;
  57        cmd_base.cmd_v2.length = 16;
  58
  59        if (info.sensor_idx == als_idx)
  60                cmd_base.cmd_v2.mem_type = USE_C2P_REG;
  61
  62        writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG1);
  63        writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
  64}
  65
  66static void amd_stop_sensor_v2(struct amd_mp2_dev *privdata, u16 sensor_idx)
  67{
  68        union sfh_cmd_base cmd_base;
  69
  70        cmd_base.ul = 0;
  71        cmd_base.cmd_v2.cmd_id = DISABLE_SENSOR;
  72        cmd_base.cmd_v2.period = 0;
  73        cmd_base.cmd_v2.sensor_id = sensor_idx;
  74        cmd_base.cmd_v2.length  = 16;
  75
  76        writeq(0x0, privdata->mmio + AMD_C2P_MSG1);
  77        writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
  78}
  79
  80static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata)
  81{
  82        union sfh_cmd_base cmd_base;
  83
  84        cmd_base.cmd_v2.cmd_id = STOP_ALL_SENSORS;
  85        cmd_base.cmd_v2.period = 0;
  86        cmd_base.cmd_v2.sensor_id = 0;
  87
  88        writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
  89}
  90
  91void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
  92{
  93        union sfh_cmd_param cmd_param;
  94        union sfh_cmd_base cmd_base;
  95
  96        /* fill up command register */
  97        memset(&cmd_base, 0, sizeof(cmd_base));
  98        cmd_base.s.cmd_id = ENABLE_SENSOR;
  99        cmd_base.s.period = info.period;
 100        cmd_base.s.sensor_id = info.sensor_idx;
 101
 102        /* fill up command param register */
 103        memset(&cmd_param, 0, sizeof(cmd_param));
 104        cmd_param.s.buf_layout = 1;
 105        cmd_param.s.buf_length = 16;
 106
 107        writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG2);
 108        writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1);
 109        writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
 110}
 111
 112void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx)
 113{
 114        union sfh_cmd_base cmd_base;
 115
 116        /* fill up command register */
 117        memset(&cmd_base, 0, sizeof(cmd_base));
 118        cmd_base.s.cmd_id = DISABLE_SENSOR;
 119        cmd_base.s.period = 0;
 120        cmd_base.s.sensor_id = sensor_idx;
 121
 122        writeq(0x0, privdata->mmio + AMD_C2P_MSG2);
 123        writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
 124}
 125
 126void amd_stop_all_sensors(struct amd_mp2_dev *privdata)
 127{
 128        union sfh_cmd_base cmd_base;
 129
 130        /* fill up command register */
 131        memset(&cmd_base, 0, sizeof(cmd_base));
 132        cmd_base.s.cmd_id = STOP_ALL_SENSORS;
 133        cmd_base.s.period = 0;
 134        cmd_base.s.sensor_id = 0;
 135
 136        writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
 137}
 138
 139static const struct dmi_system_id dmi_sensor_mask_overrides[] = {
 140        {
 141                .matches = {
 142                        DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 13-ag0xxx"),
 143                },
 144                .driver_data = (void *)(ACEL_EN | MAGNO_EN),
 145        },
 146        {
 147                .matches = {
 148                        DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY x360 Convertible 15-cp0xxx"),
 149                },
 150                .driver_data = (void *)(ACEL_EN | MAGNO_EN),
 151        },
 152        { }
 153};
 154
 155int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id)
 156{
 157        int activestatus, num_of_sensors = 0;
 158        const struct dmi_system_id *dmi_id;
 159
 160        if (sensor_mask_override == -1) {
 161                dmi_id = dmi_first_match(dmi_sensor_mask_overrides);
 162                if (dmi_id)
 163                        sensor_mask_override = (long)dmi_id->driver_data;
 164        }
 165
 166        if (sensor_mask_override >= 0) {
 167                activestatus = sensor_mask_override;
 168        } else {
 169                activestatus = privdata->mp2_acs >> 4;
 170        }
 171
 172        if (ACEL_EN  & activestatus)
 173                sensor_id[num_of_sensors++] = accel_idx;
 174
 175        if (GYRO_EN & activestatus)
 176                sensor_id[num_of_sensors++] = gyro_idx;
 177
 178        if (MAGNO_EN & activestatus)
 179                sensor_id[num_of_sensors++] = mag_idx;
 180
 181        if (ALS_EN & activestatus)
 182                sensor_id[num_of_sensors++] = als_idx;
 183
 184        if (HPD_EN & activestatus)
 185                sensor_id[num_of_sensors++] = HPD_IDX;
 186
 187        return num_of_sensors;
 188}
 189
 190static void amd_mp2_pci_remove(void *privdata)
 191{
 192        struct amd_mp2_dev *mp2 = privdata;
 193        amd_sfh_hid_client_deinit(privdata);
 194        mp2->mp2_ops->stop_all(mp2);
 195}
 196
 197static const struct amd_mp2_ops amd_sfh_ops_v2 = {
 198        .start = amd_start_sensor_v2,
 199        .stop = amd_stop_sensor_v2,
 200        .stop_all = amd_stop_all_sensor_v2,
 201        .response = amd_sfh_wait_response_v2,
 202};
 203
 204static const struct amd_mp2_ops amd_sfh_ops = {
 205        .start = amd_start_sensor,
 206        .stop = amd_stop_sensor,
 207        .stop_all = amd_stop_all_sensors,
 208};
 209
 210static void mp2_select_ops(struct amd_mp2_dev *privdata)
 211{
 212        u8 acs;
 213
 214        privdata->mp2_acs = readl(privdata->mmio + AMD_P2C_MSG3);
 215        acs = privdata->mp2_acs & GENMASK(3, 0);
 216
 217        switch (acs) {
 218        case V2_STATUS:
 219                privdata->mp2_ops = &amd_sfh_ops_v2;
 220                break;
 221        default:
 222                privdata->mp2_ops = &amd_sfh_ops;
 223                break;
 224        }
 225}
 226
 227static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 228{
 229        struct amd_mp2_dev *privdata;
 230        int rc;
 231
 232        privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL);
 233        if (!privdata)
 234                return -ENOMEM;
 235
 236        privdata->pdev = pdev;
 237        pci_set_drvdata(pdev, privdata);
 238        rc = pcim_enable_device(pdev);
 239        if (rc)
 240                return rc;
 241
 242        rc = pcim_iomap_regions(pdev, BIT(2), DRIVER_NAME);
 243        if (rc)
 244                return rc;
 245
 246        privdata->mmio = pcim_iomap_table(pdev)[2];
 247        pci_set_master(pdev);
 248        rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
 249        if (rc) {
 250                rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
 251                return rc;
 252        }
 253
 254        privdata->cl_data = devm_kzalloc(&pdev->dev, sizeof(struct amdtp_cl_data), GFP_KERNEL);
 255        if (!privdata->cl_data)
 256                return -ENOMEM;
 257
 258        mp2_select_ops(privdata);
 259
 260        rc = amd_sfh_hid_client_init(privdata);
 261        if (rc)
 262                return rc;
 263
 264        return devm_add_action_or_reset(&pdev->dev, amd_mp2_pci_remove, privdata);
 265}
 266
 267static int __maybe_unused amd_mp2_pci_resume(struct device *dev)
 268{
 269        struct pci_dev *pdev = to_pci_dev(dev);
 270        struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
 271        struct amdtp_cl_data *cl_data = mp2->cl_data;
 272        struct amd_mp2_sensor_info info;
 273        int i, status;
 274
 275        for (i = 0; i < cl_data->num_hid_devices; i++) {
 276                if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {
 277                        info.period = AMD_SFH_IDLE_LOOP;
 278                        info.sensor_idx = cl_data->sensor_idx[i];
 279                        info.dma_address = cl_data->sensor_dma_addr[i];
 280                        mp2->mp2_ops->start(mp2, info);
 281                        status = amd_sfh_wait_for_response
 282                                        (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);
 283                        if (status == SENSOR_ENABLED)
 284                                cl_data->sensor_sts[i] = SENSOR_ENABLED;
 285                        dev_dbg(dev, "resume sid 0x%x status 0x%x\n",
 286                                cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
 287                }
 288        }
 289
 290        return 0;
 291}
 292
 293static int __maybe_unused amd_mp2_pci_suspend(struct device *dev)
 294{
 295        struct pci_dev *pdev = to_pci_dev(dev);
 296        struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
 297        struct amdtp_cl_data *cl_data = mp2->cl_data;
 298        int i, status;
 299
 300        for (i = 0; i < cl_data->num_hid_devices; i++) {
 301                if (cl_data->sensor_idx[i] != HPD_IDX &&
 302                    cl_data->sensor_sts[i] == SENSOR_ENABLED) {
 303                        mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
 304                        status = amd_sfh_wait_for_response
 305                                        (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);
 306                        if (status != SENSOR_ENABLED)
 307                                cl_data->sensor_sts[i] = SENSOR_DISABLED;
 308                        dev_dbg(dev, "suspend sid 0x%x status 0x%x\n",
 309                                cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
 310                }
 311        }
 312
 313        return 0;
 314}
 315
 316static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend,
 317                amd_mp2_pci_resume);
 318
 319static const struct pci_device_id amd_mp2_pci_tbl[] = {
 320        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) },
 321        { }
 322};
 323MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl);
 324
 325static struct pci_driver amd_mp2_pci_driver = {
 326        .name           = DRIVER_NAME,
 327        .id_table       = amd_mp2_pci_tbl,
 328        .probe          = amd_mp2_pci_probe,
 329        .driver.pm      = &amd_mp2_pm_ops,
 330};
 331module_pci_driver(amd_mp2_pci_driver);
 332
 333MODULE_DESCRIPTION(DRIVER_DESC);
 334MODULE_LICENSE("Dual BSD/GPL");
 335MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
 336MODULE_AUTHOR("Sandeep Singh <Sandeep.singh@amd.com>");
 337