diff options
author | Jiri Kosina <jkosina@suse.cz> | 2021-09-01 21:54:57 +0200 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2021-09-01 21:54:57 +0200 |
commit | 56e527b0f790dd8e7f077b9197eba11f2117a527 (patch) | |
tree | 2f173474be79fea9c8ed04b2a28670741fe63ab1 /drivers/hid | |
parent | bab94e97323baefe0afccad66e776f9c78b4f521 (diff) | |
parent | e665775591865f9f5c3ed6ff35d625a99795e4e0 (diff) | |
download | linux-stable-56e527b0f790dd8e7f077b9197eba11f2117a527.tar.gz linux-stable-56e527b0f790dd8e7f077b9197eba11f2117a527.tar.bz2 linux-stable-56e527b0f790dd8e7f077b9197eba11f2117a527.zip |
Merge branch 'for-5.15/amd-sfh' into for-linus
- Updates to the existing FW reporting mechanism, MP2 FW
status checks, adding proper power management support for amd-sfh
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/amd-sfh-hid/amd_sfh_client.c | 62 | ||||
-rw-r--r-- | drivers/hid/amd-sfh-hid/amd_sfh_pcie.c | 69 | ||||
-rw-r--r-- | drivers/hid/amd-sfh-hid/amd_sfh_pcie.h | 23 |
3 files changed, 139 insertions, 15 deletions
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c index efb849411d25..840fd075c56f 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c @@ -17,7 +17,6 @@ #include "amd_sfh_pcie.h" #include "amd_sfh_hid.h" -#define AMD_SFH_IDLE_LOOP 200 struct request_list { struct hid_device *hid; @@ -123,14 +122,24 @@ static void amd_sfh_work_buffer(struct work_struct *work) int i; for (i = 0; i < cli_data->num_hid_devices; i++) { - report_size = get_input_report(i, cli_data->sensor_idx[i], cli_data->report_id[i], - in_data); - hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT, - in_data->input_report[i], report_size, 0); + if (cli_data->sensor_sts[i] == SENSOR_ENABLED) { + report_size = get_input_report + (i, cli_data->sensor_idx[i], cli_data->report_id[i], in_data); + hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT, + in_data->input_report[i], report_size, 0); + } } schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); } +u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) +{ + if (mp2->mp2_ops->response) + sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts); + + return sensor_sts; +} + int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) { struct amd_input_data *in_data = &privdata->in_data; @@ -139,8 +148,8 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) struct device *dev; u32 feature_report_size; u32 input_report_size; + int rc, i, status; u8 cl_idx; - int rc, i; dev = &privdata->pdev->dev; @@ -155,7 +164,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) in_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8, &cl_data->sensor_dma_addr[i], GFP_KERNEL); - cl_data->sensor_sts[i] = 0; + cl_data->sensor_sts[i] = SENSOR_DISABLED; cl_data->sensor_requested_cnt[i] = 0; cl_data->cur_hid_dev = i; cl_idx = cl_data->sensor_idx[i]; @@ -184,7 +193,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) rc = -ENOMEM; goto cleanup; } - info.period = msecs_to_jiffies(AMD_SFH_IDLE_LOOP); + info.period = AMD_SFH_IDLE_LOOP; info.sensor_idx = cl_idx; info.dma_address = cl_data->sensor_dma_addr[i]; @@ -197,11 +206,25 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) rc = get_report_descriptor(cl_idx, cl_data->report_descr[i]); if (rc) return rc; - rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data); - if (rc) - return rc; privdata->mp2_ops->start(privdata, info); - cl_data->sensor_sts[i] = 1; + status = amd_sfh_wait_for_response + (privdata, cl_data->sensor_idx[i], SENSOR_ENABLED); + if (status == SENSOR_ENABLED) { + cl_data->sensor_sts[i] = SENSOR_ENABLED; + rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data); + if (rc) { + privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]); + status = amd_sfh_wait_for_response + (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED); + if (status != SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_DISABLED; + dev_dbg(dev, "sid 0x%x status 0x%x\n", + cl_data->sensor_idx[i], cl_data->sensor_sts[i]); + goto cleanup; + } + } + dev_dbg(dev, "sid 0x%x status 0x%x\n", + cl_data->sensor_idx[i], cl_data->sensor_sts[i]); } schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); return 0; @@ -224,10 +247,19 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) { struct amdtp_cl_data *cl_data = privdata->cl_data; struct amd_input_data *in_data = cl_data->in_data; - int i; + int i, status; - for (i = 0; i < cl_data->num_hid_devices; i++) - privdata->mp2_ops->stop(privdata, i); + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_sts[i] == SENSOR_ENABLED) { + privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]); + status = amd_sfh_wait_for_response + (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED); + if (status != SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_DISABLED; + dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x status 0x%x\n", + cl_data->sensor_idx[i], cl_data->sensor_sts[i]); + } + } cancel_delayed_work_sync(&cl_data->work); cancel_delayed_work_sync(&cl_data->work_buffer); diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index 8d68796aa905..79b138fd4261 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -13,6 +13,7 @@ #include <linux/dmi.h> #include <linux/interrupt.h> #include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/slab.h> @@ -31,6 +32,20 @@ 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 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 800 ms 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, 800000)) + return cmd_resp.response_v2.response; + + return SENSOR_DISABLED; +} + static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) { union sfh_cmd_base cmd_base; @@ -183,6 +198,7 @@ static const struct amd_mp2_ops amd_sfh_ops_v2 = { .start = amd_start_sensor_v2, .stop = amd_stop_sensor_v2, .stop_all = amd_stop_all_sensor_v2, + .response = amd_sfh_wait_response_v2, }; static const struct amd_mp2_ops amd_sfh_ops = { @@ -248,6 +264,58 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i return amd_sfh_hid_client_init(privdata); } +static int __maybe_unused amd_mp2_pci_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); + struct amdtp_cl_data *cl_data = mp2->cl_data; + struct amd_mp2_sensor_info info; + int i, status; + + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_sts[i] == SENSOR_DISABLED) { + info.period = AMD_SFH_IDLE_LOOP; + info.sensor_idx = cl_data->sensor_idx[i]; + info.dma_address = cl_data->sensor_dma_addr[i]; + mp2->mp2_ops->start(mp2, info); + status = amd_sfh_wait_for_response + (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED); + if (status == SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_ENABLED; + dev_dbg(dev, "resume sid 0x%x status 0x%x\n", + cl_data->sensor_idx[i], cl_data->sensor_sts[i]); + } + } + + return 0; +} + +static int __maybe_unused amd_mp2_pci_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev); + struct amdtp_cl_data *cl_data = mp2->cl_data; + int i, status; + + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_idx[i] != HPD_IDX && + cl_data->sensor_sts[i] == SENSOR_ENABLED) { + mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]); + status = amd_sfh_wait_for_response + (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED); + if (status != SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_DISABLED; + dev_dbg(dev, "suspend sid 0x%x status 0x%x\n", + cl_data->sensor_idx[i], cl_data->sensor_sts[i]); + } + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend, + amd_mp2_pci_resume); + static const struct pci_device_id amd_mp2_pci_tbl[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) }, { } @@ -258,6 +326,7 @@ static struct pci_driver amd_mp2_pci_driver = { .name = DRIVER_NAME, .id_table = amd_mp2_pci_tbl, .probe = amd_mp2_pci_probe, + .driver.pm = &amd_mp2_pm_ops, }; module_pci_driver(amd_mp2_pci_driver); diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h index 2d5c57e3782d..1ff6f83cb6fd 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h @@ -24,14 +24,20 @@ #define AMD_C2P_MSG2 0x10508 #define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4)) +#define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4)) /* MP2 P2C Message Registers */ #define AMD_P2C_MSG3 0x1068C /* Supported Sensors info */ #define V2_STATUS 0x2 +#define SENSOR_ENABLED 4 +#define SENSOR_DISABLED 5 + #define HPD_IDX 16 +#define AMD_SFH_IDLE_LOOP 200 + /* SFH Command register */ union sfh_cmd_base { u32 ul; @@ -51,6 +57,19 @@ union sfh_cmd_base { } cmd_v2; }; +union cmd_response { + u32 resp; + struct { + u32 status : 2; + u32 out_in_c2p : 1; + u32 rsvd1 : 1; + u32 response : 4; + u32 sub_cmd : 8; + u32 sensor_id : 6; + u32 rsvd2 : 10; + } response_v2; +}; + union sfh_cmd_param { u32 ul; struct { @@ -112,10 +131,14 @@ void amd_stop_all_sensors(struct amd_mp2_dev *privdata); int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id); int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata); int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata); +u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts); +void amd_mp2_suspend(struct amd_mp2_dev *mp2); +void amd_mp2_resume(struct amd_mp2_dev *mp2); struct amd_mp2_ops { void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info); void (*stop)(struct amd_mp2_dev *privdata, u16 sensor_idx); void (*stop_all)(struct amd_mp2_dev *privdata); + int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts); }; #endif |