diff options
Diffstat (limited to 'drivers/hid/amd-sfh-hid/amd_sfh_pcie.c')
| -rw-r--r-- | drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 192 |
1 files changed, 172 insertions, 20 deletions
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index 47774b9ab3de..1d9f955573aa 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/delay.h> +#include <linux/devm-helpers.h> #include <linux/dma-mapping.h> #include <linux/dmi.h> #include <linux/interrupt.h> @@ -17,6 +18,7 @@ #include <linux/iopoll.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include "amd_sfh_pcie.h" #include "sfh1_1/amd_sfh_init.h" @@ -27,22 +29,26 @@ #define ACEL_EN BIT(0) #define GYRO_EN BIT(1) #define MAGNO_EN BIT(2) +#define OP_EN BIT(15) #define HPD_EN BIT(16) #define ALS_EN BIT(19) +#define ACS_EN BIT(22) static int sensor_mask_override = -1; module_param_named(sensor_mask, sensor_mask_override, int, 0444); MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask"); +static bool intr_disable = true; + static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) { union cmd_response cmd_resp; - /* Get response with status within a max of 1600 ms timeout */ + /* Get response with status within a max of 10 seconds timeout */ if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp, (cmd_resp.response_v2.response == sensor_sts && cmd_resp.response_v2.status == 0 && (sid == 0xff || - cmd_resp.response_v2.sensor_id == sid)), 500, 1600000)) + cmd_resp.response_v2.sensor_id == sid)), 500, 10000000)) return cmd_resp.response_v2.response; return SENSOR_DISABLED; @@ -54,7 +60,7 @@ static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sen cmd_base.ul = 0; cmd_base.cmd_v2.cmd_id = ENABLE_SENSOR; - cmd_base.cmd_v2.intr_disable = 1; + cmd_base.cmd_v2.intr_disable = intr_disable; cmd_base.cmd_v2.period = info.period; cmd_base.cmd_v2.sensor_id = info.sensor_idx; cmd_base.cmd_v2.length = 16; @@ -72,7 +78,7 @@ static void amd_stop_sensor_v2(struct amd_mp2_dev *privdata, u16 sensor_idx) cmd_base.ul = 0; cmd_base.cmd_v2.cmd_id = DISABLE_SENSOR; - cmd_base.cmd_v2.intr_disable = 1; + cmd_base.cmd_v2.intr_disable = intr_disable; cmd_base.cmd_v2.period = 0; cmd_base.cmd_v2.sensor_id = sensor_idx; cmd_base.cmd_v2.length = 16; @@ -86,7 +92,7 @@ static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata) union sfh_cmd_base cmd_base; cmd_base.cmd_v2.cmd_id = STOP_ALL_SENSORS; - cmd_base.cmd_v2.intr_disable = 1; + cmd_base.cmd_v2.intr_disable = intr_disable; cmd_base.cmd_v2.period = 0; cmd_base.cmd_v2.sensor_id = 0; @@ -95,9 +101,9 @@ static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata) void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata) { - if (readl(privdata->mmio + AMD_P2C_MSG(4))) { - writel(0, privdata->mmio + AMD_P2C_MSG(4)); - writel(0xf, privdata->mmio + AMD_P2C_MSG(5)); + if (readl(privdata->mmio + amd_get_p2c_val(privdata, 4))) { + writel(0, privdata->mmio + amd_get_p2c_val(privdata, 4)); + writel(0xf, privdata->mmio + amd_get_p2c_val(privdata, 5)); } } @@ -118,7 +124,7 @@ int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata) { int rc; - pci_intx(privdata->pdev, true); + pcim_intx(privdata->pdev, true); rc = devm_request_irq(&privdata->pdev->dev, privdata->pdev->irq, amd_sfh_irq_handler, 0, DRIVER_NAME, privdata); @@ -227,12 +233,18 @@ int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id) if (MAGNO_EN & activestatus) sensor_id[num_of_sensors++] = mag_idx; + if (OP_EN & activestatus) + sensor_id[num_of_sensors++] = op_idx; + if (ALS_EN & activestatus) sensor_id[num_of_sensors++] = als_idx; if (HPD_EN & activestatus) sensor_id[num_of_sensors++] = HPD_IDX; + if (ACS_EN & activestatus) + sensor_id[num_of_sensors++] = ACS_IDX; + return num_of_sensors; } @@ -241,7 +253,7 @@ static void amd_mp2_pci_remove(void *privdata) struct amd_mp2_dev *mp2 = privdata; amd_sfh_hid_client_deinit(privdata); mp2->mp2_ops->stop_all(mp2); - pci_intx(mp2->pdev, false); + pcim_intx(mp2->pdev, false); amd_sfh_clear_intr(mp2); } @@ -288,6 +300,26 @@ int amd_sfh_irq_init(struct amd_mp2_dev *privdata) return 0; } +static int mp2_disable_intr(const struct dmi_system_id *id) +{ + intr_disable = false; + return 0; +} + +static const struct dmi_system_id dmi_sfh_table[] = { + { + /* + * https://bugzilla.kernel.org/show_bug.cgi?id=218104 + */ + .callback = mp2_disable_intr, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 435 G7"), + }, + }, + {} +}; + static const struct dmi_system_id dmi_nodevs[] = { { /* @@ -303,6 +335,101 @@ static const struct dmi_system_id dmi_nodevs[] = { { } }; +static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", str_enabled_disabled(mp2->dev_en.is_hpd_enabled)); +} + +static ssize_t hpd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); + bool enabled; + int ret; + + ret = kstrtobool(buf, &enabled); + if (ret) + return ret; + + mp2->sfh1_1_ops->toggle_hpd(mp2, enabled); + + return count; +} +static DEVICE_ATTR_RW(hpd); + +static umode_t sfh_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); + + if (!mp2->sfh1_1_ops || !mp2->dev_en.is_hpd_present) + return 0; + + return attr->mode; +} + +static struct attribute *sfh_attrs[] = { + &dev_attr_hpd.attr, + NULL, +}; + +static struct attribute_group sfh_attr_group = { + .attrs = sfh_attrs, + .is_visible = sfh_attr_is_visible, +}; + +static const struct attribute_group *amd_sfh_groups[] = { + &sfh_attr_group, + NULL, +}; + +static void sfh1_1_init_work(struct work_struct *work) +{ + struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work); + int rc; + + rc = mp2->sfh1_1_ops->init(mp2); + if (rc) + return; + + amd_sfh_clear_intr(mp2); + mp2->init_done = 1; + + rc = sysfs_update_group(&mp2->pdev->dev.kobj, &sfh_attr_group); + if (rc) + dev_warn(&mp2->pdev->dev, "failed to update sysfs group\n"); + +} + +static void sfh_init_work(struct work_struct *work) +{ + struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work); + struct pci_dev *pdev = mp2->pdev; + int rc; + + rc = amd_sfh_hid_client_init(mp2); + if (rc) { + amd_sfh_clear_intr(mp2); + dev_err(&pdev->dev, "amd_sfh_hid_client_init failed err %d\n", rc); + return; + } + + amd_sfh_clear_intr(mp2); + mp2->init_done = 1; +} + +static void amd_sfh_remove(struct pci_dev *pdev) +{ + struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); + + flush_work(&mp2->work); + if (mp2->init_done) + mp2->mp2_ops->remove(mp2); +} + static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct amd_mp2_dev *privdata; @@ -311,6 +438,8 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i if (dmi_first_match(dmi_nodevs)) return -ENODEV; + dmi_check_system(dmi_sfh_table); + privdata = devm_kzalloc(&pdev->dev, sizeof(*privdata), GFP_KERNEL); if (!privdata) return -ENOMEM; @@ -337,12 +466,21 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i if (!privdata->cl_data) return -ENOMEM; + rc = devm_mutex_init(&pdev->dev, &privdata->lock); + if (rc) + return rc; + privdata->sfh1_1_ops = (const struct amd_sfh1_1_ops *)id->driver_data; if (privdata->sfh1_1_ops) { - rc = privdata->sfh1_1_ops->init(privdata); + if (boot_cpu_data.x86 >= 0x1A) + privdata->rver = 1; + + rc = devm_work_autocancel(&pdev->dev, &privdata->work, sfh1_1_init_work); if (rc) return rc; - goto init_done; + + schedule_work(&privdata->work); + return 0; } mp2_select_ops(privdata); @@ -353,25 +491,34 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i return rc; } - rc = amd_sfh_hid_client_init(privdata); + rc = devm_work_autocancel(&pdev->dev, &privdata->work, sfh_init_work); if (rc) { amd_sfh_clear_intr(privdata); - if (rc != -EOPNOTSUPP) - dev_err(&pdev->dev, "amd_sfh_hid_client_init failed\n"); return rc; } -init_done: - amd_sfh_clear_intr(privdata); + schedule_work(&privdata->work); + return 0; +} + +static void amd_sfh_shutdown(struct pci_dev *pdev) +{ + struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); - return devm_add_action_or_reset(&pdev->dev, privdata->mp2_ops->remove, privdata); + if (mp2) { + flush_work(&mp2->work); + if (mp2->init_done) + mp2->mp2_ops->stop_all(mp2); + } } static int __maybe_unused amd_mp2_pci_resume(struct device *dev) { struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); - mp2->mp2_ops->resume(mp2); + flush_work(&mp2->work); + if (mp2->init_done) + mp2->mp2_ops->resume(mp2); return 0; } @@ -380,7 +527,9 @@ static int __maybe_unused amd_mp2_pci_suspend(struct device *dev) { struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); - mp2->mp2_ops->suspend(mp2); + flush_work(&mp2->work); + if (mp2->init_done) + mp2->mp2_ops->suspend(mp2); return 0; } @@ -401,6 +550,9 @@ static struct pci_driver amd_mp2_pci_driver = { .id_table = amd_mp2_pci_tbl, .probe = amd_mp2_pci_probe, .driver.pm = &amd_mp2_pm_ops, + .shutdown = amd_sfh_shutdown, + .remove = amd_sfh_remove, + .dev_groups = amd_sfh_groups, }; module_pci_driver(amd_mp2_pci_driver); |
