From 45b9e04d5ba0b043783dfe2b19bb728e712cb32e Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Thu, 8 Oct 2020 16:37:22 +0200 Subject: firmware: arm_scmi: Fix ARCH_COLD_RESET The defination for ARCH_COLD_RESET is wrong. Let us fix it according to the SCMI specification. Link: https://lore.kernel.org/r/20201008143722.21888-5-etienne.carriere@linaro.org Fixes: 95a15d80aa0d ("firmware: arm_scmi: Add RESET protocol in SCMI v2.0") Signed-off-by: Etienne Carriere Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/reset.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index f063cfe17e02..a981a22cfe89 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -36,9 +36,7 @@ struct scmi_msg_reset_domain_reset { #define EXPLICIT_RESET_ASSERT BIT(1) #define ASYNCHRONOUS_RESET BIT(2) __le32 reset_state; -#define ARCH_RESET_TYPE BIT(31) -#define COLD_RESET_STATE BIT(0) -#define ARCH_COLD_RESET (ARCH_RESET_TYPE | COLD_RESET_STATE) +#define ARCH_COLD_RESET 0 }; struct scmi_msg_reset_notify { -- cgit v1.2.3 From 7adb2c8aaaa6a387af7140e57004beba2c04a4c6 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Thu, 8 Oct 2020 16:37:21 +0200 Subject: firmware: arm_scmi: Expand SMC/HVC message pool to more than one SMC/HVC can transmit only one message at the time as the shared memory needs to be protected and the calls are synchronous. However, in order to allow multiple threads to send SCMI messages simultaneously, we need a larger poll of memory. Let us just use value of 20 to keep it in sync mailbox transport implementation. Any other value must work perfectly. Link: https://lore.kernel.org/r/20201008143722.21888-4-etienne.carriere@linaro.org Fixes: 1dc6558062da ("firmware: arm_scmi: Add smc/hvc transport") Cc: Peng Fan Signed-off-by: Etienne Carriere [sudeep.holla: reworded the commit message to indicate the practicality] Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/smc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 1a03c3ec0230..82a82a5dc86a 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -149,6 +149,6 @@ static const struct scmi_transport_ops scmi_smc_ops = { const struct scmi_desc scmi_smc_desc = { .ops = &scmi_smc_ops, .max_rx_timeout_ms = 30, - .max_msg = 1, + .max_msg = 20, .max_msg_size = 128, }; -- cgit v1.2.3 From 722939528a37aa0cb22d441e2045c0cf53e78fb0 Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 17 Sep 2020 19:10:22 +0530 Subject: tee: client UUID: Skip REE kernel login method as well Since the addition of session's client UUID generation via commit [1], login via REE kernel method was disallowed. So fix that via passing nill UUID in case of TEE_IOCTL_LOGIN_REE_KERNEL method as well. Fixes: e33bcbab16d1 ("tee: add support for session's client UUID generation") [1] Signed-off-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 64637e09a095..2f6199ebf769 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -200,7 +200,8 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, int name_len; int rc; - if (connection_method == TEE_IOCTL_LOGIN_PUBLIC) { + if (connection_method == TEE_IOCTL_LOGIN_PUBLIC || + connection_method == TEE_IOCTL_LOGIN_REE_KERNEL) { /* Nil UUID to be passed to TEE environment */ uuid_copy(uuid, &uuid_null); return 0; -- cgit v1.2.3 From 9724722fde8f9bbd2b87340f00b9300c9284001e Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 12 Oct 2020 14:26:24 +0100 Subject: firmware: arm_scmi: Add missing Rx size re-initialisation Few commands provide the list of description partially and require to be called consecutively until all the descriptors are fetched completely. In such cases, we don't release the buffers and reuse them for consecutive transmits. However, currently we don't reset the Rx size which will be set as per the response for the last transmit. This may result in incorrect response size being interpretted as the firmware may repond with size greater than the one set but we read only upto the size set by previous response. Let us reset the receive buffer size to max possible in such cases as we don't know the exact size of the response. Link: https://lore.kernel.org/r/20201012141746.32575-1-sudeep.holla@arm.com Fixes: b6f20ff8bd94 ("firmware: arm_scmi: add common infrastructure and support for base protocol") Reported-by: Etienne Carriere Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 2 ++ drivers/firmware/arm_scmi/clock.c | 2 ++ drivers/firmware/arm_scmi/common.h | 2 ++ drivers/firmware/arm_scmi/driver.c | 8 ++++++++ drivers/firmware/arm_scmi/perf.c | 2 ++ drivers/firmware/arm_scmi/sensors.c | 2 ++ 6 files changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index 9853bd3c4d45..017e5d8bd869 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -197,6 +197,8 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle, protocols_imp[tot_num_ret + loop] = *(list + loop); tot_num_ret += loop_num_ret; + + scmi_reset_rx_to_maxsz(handle, t); } while (loop_num_ret); scmi_xfer_put(handle, t); diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index c1cfe3ee3d55..4645677d86f1 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -192,6 +192,8 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id, } tot_rate_cnt += num_returned; + + scmi_reset_rx_to_maxsz(handle, t); /* * check for both returned and remaining to avoid infinite * loop due to buggy firmware diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 37fb583f1bf5..65063fa948d4 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -147,6 +147,8 @@ int scmi_do_xfer_with_response(const struct scmi_handle *h, struct scmi_xfer *xfer); int scmi_xfer_get_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id, size_t tx_size, size_t rx_size, struct scmi_xfer **p); +void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle, + struct scmi_xfer *xfer); int scmi_handle_put(const struct scmi_handle *handle); struct scmi_handle *scmi_handle_get(struct device *dev); void scmi_set_handle(struct scmi_device *scmi_dev); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index c5dea87edf8f..3dfd8b6a0ebf 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -402,6 +402,14 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) return ret; } +void scmi_reset_rx_to_maxsz(const struct scmi_handle *handle, + struct scmi_xfer *xfer) +{ + struct scmi_info *info = handle_to_scmi_info(handle); + + xfer->rx.len = info->desc->max_msg_size; +} + #define SCMI_MAX_RESPONSE_TIMEOUT (2 * MSEC_PER_SEC) /** diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index ed475b40bd08..82fb3babff72 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -304,6 +304,8 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain, } tot_opp_cnt += num_returned; + + scmi_reset_rx_to_maxsz(handle, t); /* * check for both returned and remaining to avoid infinite * loop due to buggy firmware diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 9703cf6356a0..b4232d611033 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -166,6 +166,8 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, } desc_index += num_returned; + + scmi_reset_rx_to_maxsz(handle, t); /* * check for both returned and remaining to avoid infinite * loop due to buggy firmware -- cgit v1.2.3 From 3cb73bc3fa2a3cb80b88aa63b48409939e0d996b Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Wed, 14 Oct 2020 17:24:29 +0800 Subject: hyperv_fb: Update screen_info after removing old framebuffer On gen2 HyperV VM, hyperv_fb will remove the old framebuffer, and the new allocated framebuffer address could be at a differnt location, and it might be no longer a VGA framebuffer. Update screen_info so that after kexec the kernel won't try to reuse the old invalid/stale framebuffer address as VGA, corrupting memory. [ mingo: Tidied up the changelog. ] Signed-off-by: Kairui Song Signed-off-by: Ingo Molnar Cc: Dexuan Cui Cc: Jake Oshins Cc: Wei Hu Cc: "K. Y. Srinivasan" Cc: Haiyang Zhang Cc: Stephen Hemminger Link: https://lore.kernel.org/r/20201014092429.1415040-3-kasong@redhat.com --- drivers/video/fbdev/hyperv_fb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index 02411d89cb46..e36fb1a0ecdb 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -1114,8 +1114,15 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) getmem_done: remove_conflicting_framebuffers(info->apertures, KBUILD_MODNAME, false); - if (!gen2vm) + + if (gen2vm) { + /* framebuffer is reallocated, clear screen_info to avoid misuse from kexec */ + screen_info.lfb_size = 0; + screen_info.lfb_base = 0; + screen_info.orig_video_isVGA = 0; + } else { pci_dev_put(pdev); + } kfree(info->apertures); return 0; -- cgit v1.2.3 From c7821c2d9c0dda0adf2bcf88e79b02a19a430be4 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Tue, 13 Oct 2020 14:31:09 +0100 Subject: firmware: arm_scmi: Fix locking in notifications When a protocol registers its events, the notification core takes care to rescan the hashtable of pending event handlers and activate all the possibly existent handlers referring to any of the events that are just registered by the new protocol. When a pending handler becomes active the core requests and enables the corresponding events in the SCMI firmware. If, for whatever reason, the enable fails, such invalid event handler must be finally removed and freed. Let us ensure to use the scmi_put_active_handler() helper which handles properly the needed additional locking. Failing to properly acquire all the needed mutexes exposes a race that leads to the following splat being observed: WARNING: CPU: 0 PID: 388 at lib/refcount.c:28 refcount_warn_saturate+0xf8/0x148 Hardware name: ARM LTD ARM Juno Development Platform/ARM Juno Development Platform, BIOS EDK II Jun 30 2020 pstate: 40000005 (nZcv daif -PAN -UAO BTYPE=--) pc : refcount_warn_saturate+0xf8/0x148 lr : refcount_warn_saturate+0xf8/0x148 Call trace: refcount_warn_saturate+0xf8/0x148 scmi_put_handler_unlocked.isra.10+0x204/0x208 scmi_put_handler+0x50/0xa0 scmi_unregister_notifier+0x1bc/0x240 scmi_notify_tester_remove+0x4c/0x68 [dummy_scmi_consumer] scmi_dev_remove+0x54/0x68 device_release_driver_internal+0x114/0x1e8 driver_detach+0x58/0xe8 bus_remove_driver+0x88/0xe0 driver_unregister+0x38/0x68 scmi_driver_unregister+0x1c/0x28 scmi_drv_exit+0x1c/0xae0 [dummy_scmi_consumer] __arm64_sys_delete_module+0x1a4/0x268 el0_svc_common.constprop.3+0x94/0x178 do_el0_svc+0x2c/0x98 el0_sync_handler+0x148/0x1a8 el0_sync+0x158/0x180 Link: https://lore.kernel.org/r/20201013133109.49821-1-cristian.marussi@arm.com Fixes: e7c215f358a35 ("firmware: arm_scmi: Add notification callbacks-registration") Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/notify.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 2754f9d01636..c24e427dce0d 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -1403,15 +1403,21 @@ static void scmi_protocols_late_init(struct work_struct *work) "finalized PENDING handler - key:%X\n", hndl->key); ret = scmi_event_handler_enable_events(hndl); + if (ret) { + dev_dbg(ni->handle->dev, + "purging INVALID handler - key:%X\n", + hndl->key); + scmi_put_active_handler(ni, hndl); + } } else { ret = scmi_valid_pending_handler(ni, hndl); - } - if (ret) { - dev_dbg(ni->handle->dev, - "purging PENDING handler - key:%X\n", - hndl->key); - /* this hndl can be only a pending one */ - scmi_put_handler_unlocked(ni, hndl); + if (ret) { + dev_dbg(ni->handle->dev, + "purging PENDING handler - key:%X\n", + hndl->key); + /* this hndl can be only a pending one */ + scmi_put_handler_unlocked(ni, hndl); + } } } mutex_unlock(&ni->pending_mtx); -- cgit v1.2.3 From b9ceca6be43233845be70792be9b5ab315d2e010 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 13 Oct 2020 19:17:37 -0700 Subject: firmware: arm_scmi: Fix duplicate workqueue name When more than a single SCMI device are present in the system, the creation of the notification workqueue with the WQ_SYSFS flag will lead to the following sysfs duplicate node warning: sysfs: cannot create duplicate filename '/devices/virtual/workqueue/scmi_notify' CPU: 0 PID: 20 Comm: kworker/0:1 Not tainted 5.9.0-gdf4dd84a3f7d #29 Hardware name: Broadcom STB (Flattened Device Tree) Workqueue: events deferred_probe_work_func Backtrace: show_stack + 0x20/0x24 dump_stack + 0xbc/0xe0 sysfs_warn_dup + 0x70/0x80 sysfs_create_dir_ns + 0x15c/0x1a4 kobject_add_internal + 0x140/0x4d0 kobject_add + 0xc8/0x138 device_add + 0x1dc/0xc20 device_register + 0x24/0x28 workqueue_sysfs_register + 0xe4/0x1f0 alloc_workqueue + 0x448/0x6ac scmi_notification_init + 0x78/0x1dc scmi_probe + 0x268/0x4fc platform_drv_probe + 0x70/0xc8 really_probe + 0x184/0x728 driver_probe_device + 0xa4/0x278 __device_attach_driver + 0xe8/0x148 bus_for_each_drv + 0x108/0x158 __device_attach + 0x190/0x234 device_initial_probe + 0x1c/0x20 bus_probe_device + 0xdc/0xec deferred_probe_work_func + 0xd4/0x11c process_one_work + 0x420/0x8f0 worker_thread + 0x4fc/0x91c kthread + 0x21c/0x22c ret_from_fork + 0x14/0x20 kobject_add_internal failed for scmi_notify with -EEXIST, don't try to register things with the same name in the same directory. arm-scmi brcm_scmi@1: SCMI Notifications - Initialization Failed. arm-scmi brcm_scmi@1: SCMI Notifications NOT available. arm-scmi brcm_scmi@1: SCMI Protocol v1.0 'brcm-scmi:' Firmware version 0x1 Fix this by using dev_name(handle->dev) which guarantees that the name is unique and this also helps correlate which notification workqueue corresponds to which SCMI device instance. Link: https://lore.kernel.org/r/20201014021737.287340-1-f.fainelli@gmail.com Fixes: bd31b249692e ("firmware: arm_scmi: Add notification dispatch and delivery") Signed-off-by: Florian Fainelli [sudeep.holla: trimmed backtrace to remove all unwanted hexcodes and timestamps] Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/notify.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index c24e427dce0d..ce336899d636 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -1474,7 +1474,7 @@ int scmi_notification_init(struct scmi_handle *handle) ni->gid = gid; ni->handle = handle; - ni->notify_wq = alloc_workqueue("scmi_notify", + ni->notify_wq = alloc_workqueue(dev_name(handle->dev), WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS, 0); if (!ni->notify_wq) -- cgit v1.2.3 From 5be1805dc3961ce0465bcb0beab85fe8580af08d Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Mon, 12 Oct 2020 22:40:34 +0300 Subject: interconnect: qcom: sdm845: Enable keepalive for the MM1 BCM After enabling interconnect scaling for display on the db845c board, in certain configurations the board hangs, while the following errors are observed on the console: Error sending AMC RPMH requests (-110) qcom_rpmh TCS Busy, retrying RPMH message send: addr=0x50000 qcom_rpmh TCS Busy, retrying RPMH message send: addr=0x50000 qcom_rpmh TCS Busy, retrying RPMH message send: addr=0x50000 ... In this specific case, the above is related to one of the sequencers being stuck, while client drivers are returning from probe and trying to disable the currently unused clock and interconnect resources. Generally we want to keep the multimedia NoC enabled like the rest of the NoCs, so let's set the keepalive flag on it too. Fixes: aae57773fbe0 ("interconnect: qcom: sdm845: Split qnodes into their respective NoCs") Reported-by: Amit Pundir Reviewed-by: Mike Tipton Tested-by: John Stultz Link: https://lore.kernel.org/r/20201012194034.26944-1-georgi.djakov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sdm845.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index d79e3163e2c3..67e8fc3396f8 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -151,7 +151,7 @@ DEFINE_QBCM(bcm_mc0, "MC0", true, &ebi); DEFINE_QBCM(bcm_sh0, "SH0", true, &qns_llcc); DEFINE_QBCM(bcm_mm0, "MM0", false, &qns_mem_noc_hf); DEFINE_QBCM(bcm_sh1, "SH1", false, &qns_apps_io); -DEFINE_QBCM(bcm_mm1, "MM1", false, &qxm_camnoc_hf0_uncomp, &qxm_camnoc_hf1_uncomp, &qxm_camnoc_sf_uncomp, &qxm_camnoc_hf0, &qxm_camnoc_hf1, &qxm_mdp0, &qxm_mdp1); +DEFINE_QBCM(bcm_mm1, "MM1", true, &qxm_camnoc_hf0_uncomp, &qxm_camnoc_hf1_uncomp, &qxm_camnoc_sf_uncomp, &qxm_camnoc_hf0, &qxm_camnoc_hf1, &qxm_mdp0, &qxm_mdp1); DEFINE_QBCM(bcm_sh2, "SH2", false, &qns_memnoc_snoc); DEFINE_QBCM(bcm_mm2, "MM2", false, &qns2_mem_noc); DEFINE_QBCM(bcm_sh3, "SH3", false, &acm_tcu); -- cgit v1.2.3 From d3703b3e255f56d543aac183f8aafdbfd7096559 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Tue, 13 Oct 2020 16:59:11 +0300 Subject: interconnect: Aggregate before setting initial bandwidth When setting the initial bandwidth, make sure to call the aggregate() function (if such is implemented for the current provider), to handle cases when data needs to be aggregated first. Fixes: b1d681d8d324 ("interconnect: Add sync state support") Acked-by: Saravana Kannan Link: https://lore.kernel.org/r/20201013135913.29059-1-georgi.djakov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index eea47b4c84aa..974a66725d09 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -971,6 +971,9 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider) } node->avg_bw = node->init_avg; node->peak_bw = node->init_peak; + if (provider->aggregate) + provider->aggregate(node, 0, node->init_avg, node->init_peak, + &node->avg_bw, &node->peak_bw); provider->set(node, node); node->avg_bw = 0; node->peak_bw = 0; -- cgit v1.2.3 From 0f221a729049ab727c87b0fe47e309b952d879ce Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Tue, 13 Oct 2020 16:59:12 +0300 Subject: interconnect: qcom: sdm845: Init BCMs before creating the nodes Currently if we use sync_state, by default the bandwidth is maxed out, but in order to set this in hardware, the BCMs (Bus Clock Managers) need to be initialized first. Move the BCM initialization before creating the nodes to fix this. Fixes: 7d3b0b0d8184 ("interconnect: qcom: Use icc_sync_state") Acked-by: Saravana Kannan Link: https://lore.kernel.org/r/20201013135913.29059-2-georgi.djakov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sdm845.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index 67e8fc3396f8..5304aea3b058 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -489,6 +489,9 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + for (i = 0; i < num_nodes; i++) { size_t j; @@ -512,9 +515,6 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - platform_set_drvdata(pdev, qp); return 0; -- cgit v1.2.3 From 599809540f173f572c32a35d712accdc14d6357c Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Tue, 13 Oct 2020 16:59:13 +0300 Subject: interconnect: qcom: sc7180: Init BCMs before creating the nodes Currently if we use sync_state, by default the bandwidth is maxed out, but in order to set this in hardware, the BCMs (Bus Clock Managers) need to be initialized first. Move the BCM initialization before creating the nodes to fix this. Fixes: 7d3b0b0d8184 ("interconnect: qcom: Use icc_sync_state") Acked-by: Saravana Kannan Link: https://lore.kernel.org/r/20201013135913.29059-3-georgi.djakov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sc7180.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/interconnect/qcom/sc7180.c b/drivers/interconnect/qcom/sc7180.c index bf11b82ed55c..8d9044ed18ab 100644 --- a/drivers/interconnect/qcom/sc7180.c +++ b/drivers/interconnect/qcom/sc7180.c @@ -553,6 +553,9 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + for (i = 0; i < num_nodes; i++) { size_t j; @@ -576,9 +579,6 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - platform_set_drvdata(pdev, qp); return 0; -- cgit v1.2.3 From b3e1ea16fb39fb6e1a1cf1dbdd6738531de3dc7d Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 15 Oct 2020 17:41:15 +0800 Subject: mmc: sdhci: Use Auto CMD Auto Select only when v4_mode is true sdhci-of-dwcmshc meets an eMMC read performance regression with below command after commit 427b6514d095 ("mmc: sdhci: Add Auto CMD Auto Select support"): dd if=/dev/mmcblk0 of=/dev/null bs=8192 count=100000 Before the commit, the above command gives 120MB/s After the commit, the above command gives 51.3 MB/s So it looks like sdhci-of-dwcmshc expects Version 4 Mode for Auto CMD Auto Select. Fix the performance degradation by ensuring v4_mode is true to use Auto CMD Auto Select. Fixes: 427b6514d095 ("mmc: sdhci: Add Auto CMD Auto Select support") Signed-off-by: Jisheng Zhang Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20201015174115.4cf2c19a@xhacker.debian Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 592a55a34b58..3561ae8a481a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1384,9 +1384,11 @@ static inline void sdhci_auto_cmd_select(struct sdhci_host *host, /* * In case of Version 4.10 or later, use of 'Auto CMD Auto * Select' is recommended rather than use of 'Auto CMD12 - * Enable' or 'Auto CMD23 Enable'. + * Enable' or 'Auto CMD23 Enable'. We require Version 4 Mode + * here because some controllers (e.g sdhci-of-dwmshc) expect it. */ - if (host->version >= SDHCI_SPEC_410 && (use_cmd12 || use_cmd23)) { + if (host->version >= SDHCI_SPEC_410 && host->v4_mode && + (use_cmd12 || use_cmd23)) { *mode |= SDHCI_TRNS_AUTO_SEL; ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); -- cgit v1.2.3 From 266cd33b59138501579c64648f54b93eab2e5adf Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Thu, 22 Oct 2020 13:26:26 +0300 Subject: interconnect: qcom: Ensure that the floor bandwidth value is enforced Take into account the initial bandwidth from the framework and update the internal sum and max values before committing if needed. This will ensure that the floor bandwidth values are enforced until the providers get into sync state. Fixes: 7d3b0b0d8184 ("interconnect: qcom: Use icc_sync_state") Tested-by: Akash Asthana Link: https://lore.kernel.org/r/20201021155938.9223-1-georgi.djakov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpmh.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index cf10a4b9611b..bf01d09dba6c 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -79,6 +79,7 @@ EXPORT_SYMBOL_GPL(qcom_icc_aggregate); int qcom_icc_set(struct icc_node *src, struct icc_node *dst) { struct qcom_icc_provider *qp; + struct qcom_icc_node *qn; struct icc_node *node; if (!src) @@ -87,6 +88,12 @@ int qcom_icc_set(struct icc_node *src, struct icc_node *dst) node = src; qp = to_qcom_provider(node->provider); + qn = node->data; + + qn->sum_avg[QCOM_ICC_BUCKET_AMC] = max_t(u64, qn->sum_avg[QCOM_ICC_BUCKET_AMC], + node->avg_bw); + qn->max_peak[QCOM_ICC_BUCKET_AMC] = max_t(u64, qn->max_peak[QCOM_ICC_BUCKET_AMC], + node->peak_bw); qcom_icc_bcm_voter_commit(qp->voter); -- cgit v1.2.3 From 938f324e7df25e89226c6fe137028af73cd6160b Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Tue, 13 Oct 2020 12:32:46 +0200 Subject: drm/panel: mantix: Don't dereference NULL mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't dereference mode which was just NULL checked. Fixes: 72967d5616d3 ("drm/panel: Add panel driver for the Mantix MLAF057WE51-X DSI panel") Signed-off-by: Guido Günther Reported-by: Dan Carpenter Signed-off-by: Sam Ravnborg Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/659158549f3c6cc1c71ceed0943e760e861c1206.1602584953.git.agx@sigxcpu.org --- drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 3482e28e30fc..4a7fbf64bb7a 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -204,7 +204,7 @@ static int mantix_get_modes(struct drm_panel *panel, if (!mode) { dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n", default_mode.hdisplay, default_mode.vdisplay, - drm_mode_vrefresh(mode)); + drm_mode_vrefresh(&default_mode)); return -ENOMEM; } -- cgit v1.2.3 From 6af672523fe4bd71f5e70c50258fd0fc09663fae Mon Sep 17 00:00:00 2001 From: Guido Günther Date: Tue, 13 Oct 2020 12:32:47 +0200 Subject: drm/panel: mantix: Fix panel reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mantix panel needs two reset lines (RESX and TP_RSTN) deasserted to output an image. Only deasserting RESX is not enough and the display will stay blank. Deassert in prepare() and assert in unprepare() to keep device held in reset when off. Fixes: 72967d5616d3 ("drm/panel: Add panel driver for the Mantix MLAF057WE51-X DSI panel") Signed-off-by: Guido Günther Signed-off-by: Sam Ravnborg Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/ba71a8ab010d263a8058dd4f711e3bcd95877bf2.1602584953.git.agx@sigxcpu.org --- drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 4a7fbf64bb7a..0c5f22e95c2d 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -26,7 +26,9 @@ struct mantix { struct device *dev; struct drm_panel panel; + struct gpio_desc *reset_gpio; + struct gpio_desc *tp_rstn_gpio; struct regulator *avdd; struct regulator *avee; @@ -124,6 +126,10 @@ static int mantix_unprepare(struct drm_panel *panel) { struct mantix *ctx = panel_to_mantix(panel); + gpiod_set_value_cansleep(ctx->tp_rstn_gpio, 1); + usleep_range(5000, 6000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_disable(ctx->avee); regulator_disable(ctx->avdd); /* T11 */ @@ -165,13 +171,10 @@ static int mantix_prepare(struct drm_panel *panel) return ret; } - /* T3+T5 */ - usleep_range(10000, 12000); - - gpiod_set_value_cansleep(ctx->reset_gpio, 1); - usleep_range(5150, 7000); - + /* T3 + T4 + time for voltage to become stable: */ + usleep_range(6000, 7000); gpiod_set_value_cansleep(ctx->reset_gpio, 0); + gpiod_set_value_cansleep(ctx->tp_rstn_gpio, 0); /* T6 */ msleep(50); @@ -236,12 +239,18 @@ static int mantix_probe(struct mipi_dsi_device *dsi) if (!ctx) return -ENOMEM; - ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->reset_gpio)) { dev_err(dev, "cannot get reset gpio\n"); return PTR_ERR(ctx->reset_gpio); } + ctx->tp_rstn_gpio = devm_gpiod_get(dev, "mantix,tp-rstn", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->tp_rstn_gpio)) { + dev_err(dev, "cannot get tp-rstn gpio\n"); + return PTR_ERR(ctx->tp_rstn_gpio); + } + mipi_dsi_set_drvdata(dsi, ctx); ctx->dev = dev; -- cgit v1.2.3 From 0add6e9b88d0632a25323aaf4987dbacb0e4ae64 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 23 Oct 2020 00:23:37 +0200 Subject: mmc: sdhci-of-esdhc: set timeout to max before tuning On rare occations there is the following error: mmc0: Tuning timeout, falling back to fixed sampling clock There are SD cards which takes a significant longer time to reply to the first CMD19 command. The eSDHC takes the data timeout value into account during the tuning period. The SDHCI core doesn't explicitly set this timeout for the tuning procedure. Thus on the slow cards, there might be a spurious "Buffer Read Ready" interrupt, which in turn triggers a wrong sequence of events. In the end this will lead to an unsuccessful tuning procedure and to the above error. To workaround this, set the timeout to the maximum value (which is the best we can do) and the SDHCI core will take care of the proper timeout handling. Fixes: ba49cbd0936e ("mmc: sdhci-of-esdhc: add tuning support") Signed-off-by: Michael Walle Acked-by: Adrian Hunter Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20201022222337.19857-1-michael@walle.cc Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0b45eff6fed4..baf7801a1804 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -1052,6 +1052,17 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) esdhc_tuning_block_enable(host, true); + /* + * The eSDHC controller takes the data timeout value into account + * during tuning. If the SD card is too slow sending the response, the + * timer will expire and a "Buffer Read Ready" interrupt without data + * is triggered. This leads to tuning errors. + * + * Just set the timeout to the maximum value because the core will + * already take care of it in sdhci_send_tuning(). + */ + sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); + hs400_tuning = host->flags & SDHCI_HS400_TUNING; do { -- cgit v1.2.3 From 1b48dc03e575a872404f33b04cd237953c5d7498 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 23 Oct 2020 17:00:42 +0800 Subject: vhost: vdpa: report iova range This patch introduces a new ioctl for vhost-vdpa device that can report the iova range by the device. For device that implements get_iova_range() method, we fetch it from the vDPA device. If device doesn't implement get_iova_range() but depends on platform IOMMU, we will query via DOMAIN_ATTR_GEOMETRY, otherwise [0, ULLONG_MAX] is assumed. For safety, this patch also rules out the map request which is not in the valid range. Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20201023090043.14430-3-jasowang@redhat.com Signed-off-by: Michael S. Tsirkin --- drivers/vhost/vdpa.c | 41 ++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/vhost.h | 4 ++++ include/uapi/linux/vhost_types.h | 9 +++++++++ 3 files changed, 54 insertions(+) (limited to 'drivers') diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index a2dbc85e0b0d..846de69d9c01 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -47,6 +47,7 @@ struct vhost_vdpa { int minor; struct eventfd_ctx *config_ctx; int in_batch; + struct vdpa_iova_range range; }; static DEFINE_IDA(vhost_vdpa_ida); @@ -337,6 +338,16 @@ static long vhost_vdpa_set_config_call(struct vhost_vdpa *v, u32 __user *argp) return 0; } +static long vhost_vdpa_get_iova_range(struct vhost_vdpa *v, u32 __user *argp) +{ + struct vhost_vdpa_iova_range range = { + .first = v->range.first, + .last = v->range.last, + }; + + return copy_to_user(argp, &range, sizeof(range)); +} + static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd, void __user *argp) { @@ -471,6 +482,9 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, features = VHOST_VDPA_BACKEND_FEATURES; r = copy_to_user(featurep, &features, sizeof(features)); break; + case VHOST_VDPA_GET_IOVA_RANGE: + r = vhost_vdpa_get_iova_range(v, argp); + break; default: r = vhost_dev_ioctl(&v->vdev, cmd, argp); if (r == -ENOIOCTLCMD) @@ -597,6 +611,10 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, long pinned; int ret = 0; + if (msg->iova < v->range.first || + msg->iova + msg->size - 1 > v->range.last) + return -EINVAL; + if (vhost_iotlb_itree_first(iotlb, msg->iova, msg->iova + msg->size - 1)) return -EEXIST; @@ -783,6 +801,27 @@ static void vhost_vdpa_free_domain(struct vhost_vdpa *v) v->domain = NULL; } +static void vhost_vdpa_set_iova_range(struct vhost_vdpa *v) +{ + struct vdpa_iova_range *range = &v->range; + struct iommu_domain_geometry geo; + struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; + + if (ops->get_iova_range) { + *range = ops->get_iova_range(vdpa); + } else if (v->domain && + !iommu_domain_get_attr(v->domain, + DOMAIN_ATTR_GEOMETRY, &geo) && + geo.force_aperture) { + range->first = geo.aperture_start; + range->last = geo.aperture_end; + } else { + range->first = 0; + range->last = ULLONG_MAX; + } +} + static int vhost_vdpa_open(struct inode *inode, struct file *filep) { struct vhost_vdpa *v; @@ -823,6 +862,8 @@ static int vhost_vdpa_open(struct inode *inode, struct file *filep) if (r) goto err_init_iotlb; + vhost_vdpa_set_iova_range(v); + filep->private_data = v; return 0; diff --git a/include/uapi/linux/vhost.h b/include/uapi/linux/vhost.h index 75232185324a..c998860d7bbc 100644 --- a/include/uapi/linux/vhost.h +++ b/include/uapi/linux/vhost.h @@ -146,4 +146,8 @@ /* Set event fd for config interrupt*/ #define VHOST_VDPA_SET_CONFIG_CALL _IOW(VHOST_VIRTIO, 0x77, int) + +/* Get the valid iova range */ +#define VHOST_VDPA_GET_IOVA_RANGE _IOR(VHOST_VIRTIO, 0x78, \ + struct vhost_vdpa_iova_range) #endif diff --git a/include/uapi/linux/vhost_types.h b/include/uapi/linux/vhost_types.h index 9a269a88a6ff..f7f6a3a28977 100644 --- a/include/uapi/linux/vhost_types.h +++ b/include/uapi/linux/vhost_types.h @@ -138,6 +138,15 @@ struct vhost_vdpa_config { __u8 buf[0]; }; +/* vhost vdpa IOVA range + * @first: First address that can be mapped by vhost-vDPA + * @last: Last address that can be mapped by vhost-vDPA + */ +struct vhost_vdpa_iova_range { + __u64 first; + __u64 last; +}; + /* Feature bits */ /* Log all write descriptors. Can be changed while device is active. */ #define VHOST_F_LOG_ALL 26 -- cgit v1.2.3 From 70a62fce262854c4aabaa47e46e82fce9f3f3938 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 23 Oct 2020 17:00:43 +0800 Subject: vdpa_sim: implement get_iova_range() This implements a sample get_iova_range() for the simulator which advertise [0, ULLONG_MAX] as the valid range. Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20201023090043.14430-4-jasowang@redhat.com Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/vdpa_sim/vdpa_sim.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index 62d640327145..ff6c9fd8d879 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -574,6 +574,16 @@ static u32 vdpasim_get_generation(struct vdpa_device *vdpa) return vdpasim->generation; } +static struct vdpa_iova_range vdpasim_get_iova_range(struct vdpa_device *vdpa) +{ + struct vdpa_iova_range range = { + .first = 0ULL, + .last = ULLONG_MAX, + }; + + return range; +} + static int vdpasim_set_map(struct vdpa_device *vdpa, struct vhost_iotlb *iotlb) { @@ -657,6 +667,7 @@ static const struct vdpa_config_ops vdpasim_net_config_ops = { .get_config = vdpasim_get_config, .set_config = vdpasim_set_config, .get_generation = vdpasim_get_generation, + .get_iova_range = vdpasim_get_iova_range, .dma_map = vdpasim_dma_map, .dma_unmap = vdpasim_dma_unmap, .free = vdpasim_free, @@ -683,6 +694,7 @@ static const struct vdpa_config_ops vdpasim_net_batch_config_ops = { .get_config = vdpasim_get_config, .set_config = vdpasim_set_config, .get_generation = vdpasim_get_generation, + .get_iova_range = vdpasim_get_iova_range, .set_map = vdpasim_set_map, .free = vdpasim_free, }; -- cgit v1.2.3 From df833050cced27e1b343cc8bc41f90191b289334 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 21 Oct 2020 20:00:29 -0500 Subject: net: ipa: command payloads already mapped IPA transactions describe actions to be performed by the IPA hardware. Three cases use IPA transactions: transmitting a socket buffer; providing a page to receive packet data; and issuing an IPA immediate command. An IPA transaction contains a scatter/gather list (SGL) to hold the set of actions to be performed. We map buffers in the SGL for DMA at the time they are added to the transaction. For skb TX transactions, we fill the SGL with a call to skb_to_sgvec(). Page RX transactions involve a single page pointer, and that is recorded in the SGL with sg_set_page(). In both of these cases we then map the SGL for DMA with a call to dma_map_sg(). Immediate commands are different. The payload for an immediate command comes from a region of coherent DMA memory, which must *not* be mapped for DMA. For that reason, gsi_trans_cmd_add() sort of hand-crafts each SGL entry added to a command transaction. This patch fixes a problem with the code that crafts the SGL entry for an immediate command. Previously a portion of the SGL entry was updated using sg_set_buf(). However this is not valid because it includes a call to virt_to_page() on the buffer, but the command buffer pointer is not a linear address. Since we never actually map the SGL for command transactions, there are very few fields in the SGL we need to fill. Specifically, we only need to record the DMA address and the length, so they can be used by __gsi_trans_commit() to fill a TRE. We additionally need to preserve the SGL flags so for_each_sg() still works. For that we can simply assign a null page pointer for command SGL entries. Fixes: 9dd441e4ed575 ("soc: qcom: ipa: GSI transactions") Reported-by: Stephen Boyd Tested-by: Stephen Boyd Signed-off-by: Alex Elder Link: https://lore.kernel.org/r/20201022010029.11877-1-elder@linaro.org Signed-off-by: Jakub Kicinski --- drivers/net/ipa/gsi_trans.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ipa/gsi_trans.c b/drivers/net/ipa/gsi_trans.c index 43f5f5d93cb0..92642030e735 100644 --- a/drivers/net/ipa/gsi_trans.c +++ b/drivers/net/ipa/gsi_trans.c @@ -397,15 +397,24 @@ void gsi_trans_cmd_add(struct gsi_trans *trans, void *buf, u32 size, /* assert(which < trans->tre_count); */ - /* Set the page information for the buffer. We also need to fill in - * the DMA address and length for the buffer (something dma_map_sg() - * normally does). + /* Commands are quite different from data transfer requests. + * Their payloads come from a pool whose memory is allocated + * using dma_alloc_coherent(). We therefore do *not* map them + * for DMA (unlike what we do for pages and skbs). + * + * When a transaction completes, the SGL is normally unmapped. + * A command transaction has direction DMA_NONE, which tells + * gsi_trans_complete() to skip the unmapping step. + * + * The only things we use directly in a command scatter/gather + * entry are the DMA address and length. We still need the SG + * table flags to be maintained though, so assign a NULL page + * pointer for that purpose. */ sg = &trans->sgl[which]; - - sg_set_buf(sg, buf, size); + sg_assign_page(sg, NULL); sg_dma_address(sg) = addr; - sg_dma_len(sg) = sg->length; + sg_dma_len(sg) = size; info = &trans->info[which]; info->opcode = opcode; -- cgit v1.2.3 From 4f3391ce8f5a69e7e6d66d0a3fc654eb6dbdc919 Mon Sep 17 00:00:00 2001 From: Vinay Kumar Yadav Date: Fri, 23 Oct 2020 00:35:57 +0530 Subject: chelsio/chtls: fix tls record info to user chtls_pt_recvmsg() receives a skb with tls header and subsequent skb with data, need to finalize the data copy whenever next skb with tls header is available. but here current tls header is overwritten by next available tls header, ends up corrupting user buffer data. fixing it by finalizing current record whenever next skb contains tls header. v1->v2: - Improved commit message. Fixes: 17a7d24aa89d ("crypto: chtls - generic handling of data and hdr") Signed-off-by: Vinay Kumar Yadav Link: https://lore.kernel.org/r/20201022190556.21308-1-vinay.yadav@chelsio.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c index 9fb5ca6682ea..188d871f6b8c 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c @@ -1585,6 +1585,7 @@ skip_copy: tp->urg_data = 0; if ((avail + offset) >= skb->len) { + struct sk_buff *next_skb; if (ULP_SKB_CB(skb)->flags & ULPCB_FLAG_TLS_HDR) { tp->copied_seq += skb->len; hws->rcvpld = skb->hdr_len; @@ -1595,8 +1596,10 @@ skip_copy: chtls_free_skb(sk, skb); buffers_freed++; hws->copied_seq = 0; - if (copied >= target && - !skb_peek(&sk->sk_receive_queue)) + next_skb = skb_peek(&sk->sk_receive_queue); + if (copied >= target && !next_skb) + break; + if (ULP_SKB_CB(next_skb)->flags & ULPCB_FLAG_TLS_HDR) break; } } while (len > 0); -- cgit v1.2.3 From d701ec326a31945d1533b438a6feab753829b738 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 22 Oct 2020 16:55:29 -0700 Subject: ionic: clean up sparse complaints The sparse complaints around the static_asserts were obscuring more useful complaints. So, don't check the static_asserts, and fix the remaining sparse complaints. Signed-off-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/pensando/ionic/ionic_dev.c | 4 ++-- drivers/net/ethernet/pensando/ionic/ionic_dev.h | 2 ++ drivers/net/ethernet/pensando/ionic/ionic_fw.c | 6 ++--- drivers/net/ethernet/pensando/ionic/ionic_lif.c | 28 +++++++++++------------ drivers/net/ethernet/pensando/ionic/ionic_main.c | 4 ++-- drivers/net/ethernet/pensando/ionic/ionic_stats.h | 2 +- drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 10 ++++---- 7 files changed, 29 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c index 545c99b15df8..dc5fbc2704f3 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c @@ -333,7 +333,7 @@ int ionic_set_vf_config(struct ionic *ionic, int vf, u8 attr, u8 *data) union ionic_dev_cmd cmd = { .vf_setattr.opcode = IONIC_CMD_VF_SETATTR, .vf_setattr.attr = attr, - .vf_setattr.vf_index = vf, + .vf_setattr.vf_index = cpu_to_le16(vf), }; int err; @@ -391,7 +391,7 @@ void ionic_dev_cmd_queue_identify(struct ionic_dev *idev, { union ionic_dev_cmd cmd = { .q_identify.opcode = IONIC_CMD_Q_IDENTIFY, - .q_identify.lif_type = lif_type, + .q_identify.lif_type = cpu_to_le16(lif_type), .q_identify.type = qtype, .q_identify.ver = qver, }; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index c109cd5a0471..6c243b17312c 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -29,6 +29,7 @@ struct ionic_dev_bar { int res_index; }; +#ifndef __CHECKER__ /* Registers */ static_assert(sizeof(struct ionic_intr) == 32); @@ -119,6 +120,7 @@ static_assert(sizeof(struct ionic_vf_setattr_cmd) == 64); static_assert(sizeof(struct ionic_vf_setattr_comp) == 16); static_assert(sizeof(struct ionic_vf_getattr_cmd) == 64); static_assert(sizeof(struct ionic_vf_getattr_comp) == 16); +#endif /* __CHECKER__ */ struct ionic_devinfo { u8 asic_type; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_fw.c b/drivers/net/ethernet/pensando/ionic/ionic_fw.c index f492ae406a60..d7bbf336c6f6 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_fw.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_fw.c @@ -27,9 +27,9 @@ static void ionic_dev_cmd_firmware_download(struct ionic_dev *idev, u64 addr, { union ionic_dev_cmd cmd = { .fw_download.opcode = IONIC_CMD_FW_DOWNLOAD, - .fw_download.offset = offset, - .fw_download.addr = addr, - .fw_download.length = length + .fw_download.offset = cpu_to_le32(offset), + .fw_download.addr = cpu_to_le64(addr), + .fw_download.length = cpu_to_le32(length), }; ionic_dev_cmd_go(idev, &cmd); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index d655a7ae3058..591c644b8e69 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -1915,11 +1915,11 @@ static int ionic_get_vf_config(struct net_device *netdev, ret = -EINVAL; } else { ivf->vf = vf; - ivf->vlan = ionic->vfs[vf].vlanid; + ivf->vlan = le16_to_cpu(ionic->vfs[vf].vlanid); ivf->qos = 0; ivf->spoofchk = ionic->vfs[vf].spoofchk; ivf->linkstate = ionic->vfs[vf].linkstate; - ivf->max_tx_rate = ionic->vfs[vf].maxrate; + ivf->max_tx_rate = le32_to_cpu(ionic->vfs[vf].maxrate); ivf->trusted = ionic->vfs[vf].trusted; ether_addr_copy(ivf->mac, ionic->vfs[vf].macaddr); } @@ -2019,7 +2019,7 @@ static int ionic_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, ret = ionic_set_vf_config(ionic, vf, IONIC_VF_ATTR_VLAN, (u8 *)&vlan); if (!ret) - ionic->vfs[vf].vlanid = vlan; + ionic->vfs[vf].vlanid = cpu_to_le16(vlan); } up_write(&ionic->vf_op_lock); @@ -2048,7 +2048,7 @@ static int ionic_set_vf_rate(struct net_device *netdev, int vf, ret = ionic_set_vf_config(ionic, vf, IONIC_VF_ATTR_RATE, (u8 *)&tx_max); if (!ret) - lif->ionic->vfs[vf].maxrate = tx_max; + lif->ionic->vfs[vf].maxrate = cpu_to_le32(tx_max); } up_write(&ionic->vf_op_lock); @@ -2981,14 +2981,14 @@ void ionic_lif_unregister(struct ionic_lif *lif) static void ionic_lif_queue_identify(struct ionic_lif *lif) { + union ionic_q_identity __iomem *q_ident; struct ionic *ionic = lif->ionic; - union ionic_q_identity *q_ident; struct ionic_dev *idev; int qtype; int err; idev = &lif->ionic->idev; - q_ident = (union ionic_q_identity *)&idev->dev_cmd_regs->data; + q_ident = (union ionic_q_identity __iomem *)&idev->dev_cmd_regs->data; for (qtype = 0; qtype < ARRAY_SIZE(ionic_qtype_versions); qtype++) { struct ionic_qtype_info *qti = &lif->qtype_info[qtype]; @@ -3011,14 +3011,14 @@ static void ionic_lif_queue_identify(struct ionic_lif *lif) ionic_qtype_versions[qtype]); err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); if (!err) { - qti->version = q_ident->version; - qti->supported = q_ident->supported; - qti->features = le64_to_cpu(q_ident->features); - qti->desc_sz = le16_to_cpu(q_ident->desc_sz); - qti->comp_sz = le16_to_cpu(q_ident->comp_sz); - qti->sg_desc_sz = le16_to_cpu(q_ident->sg_desc_sz); - qti->max_sg_elems = le16_to_cpu(q_ident->max_sg_elems); - qti->sg_desc_stride = le16_to_cpu(q_ident->sg_desc_stride); + qti->version = readb(&q_ident->version); + qti->supported = readb(&q_ident->supported); + qti->features = readq(&q_ident->features); + qti->desc_sz = readw(&q_ident->desc_sz); + qti->comp_sz = readw(&q_ident->comp_sz); + qti->sg_desc_sz = readw(&q_ident->sg_desc_sz); + qti->max_sg_elems = readw(&q_ident->max_sg_elems); + qti->sg_desc_stride = readw(&q_ident->sg_desc_stride); } mutex_unlock(&ionic->dev_cmd_lock); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c index ee0740881af3..d355676f6c16 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_main.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c @@ -311,7 +311,7 @@ int ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) static void ionic_dev_cmd_clean(struct ionic *ionic) { - union ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs; + union __iomem ionic_dev_cmd_regs *regs = ionic->idev.dev_cmd_regs; iowrite32(0, ®s->doorbell); memset_io(®s->cmd, 0, sizeof(regs->cmd)); @@ -333,7 +333,7 @@ int ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) */ max_wait = jiffies + (max_seconds * HZ); try_again: - opcode = idev->dev_cmd_regs->cmd.cmd.opcode; + opcode = readb(&idev->dev_cmd_regs->cmd.cmd.opcode); start_time = jiffies; do { done = ionic_dev_cmd_done(idev); diff --git a/drivers/net/ethernet/pensando/ionic/ionic_stats.h b/drivers/net/ethernet/pensando/ionic/ionic_stats.h index 3f543512616e..2a725834f792 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_stats.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_stats.h @@ -49,7 +49,7 @@ extern const int ionic_num_stats_grps; (*((u64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset))) #define IONIC_READ_STAT_LE64(base_ptr, desc_ptr) \ - __le64_to_cpu(*((u64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset))) + __le64_to_cpu(*((__le64 *)(((u8 *)(base_ptr)) + (desc_ptr)->offset))) struct ionic_stat_desc { char name[ETH_GSTRING_LEN]; diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 169ac4f54640..8f6fc7142bc5 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -200,7 +200,7 @@ static void ionic_rx_clean(struct ionic_queue *q, if (likely(netdev->features & NETIF_F_RXCSUM)) { if (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_CALC) { skb->ip_summed = CHECKSUM_COMPLETE; - skb->csum = (__wsum)le16_to_cpu(comp->csum); + skb->csum = (__force __wsum)le16_to_cpu(comp->csum); stats->csum_complete++; } } else { @@ -812,6 +812,7 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) skb_frag_t *frag; bool start, done; bool outer_csum; + dma_addr_t addr; bool has_vlan; u16 desc_len; u8 desc_nsge; @@ -893,11 +894,10 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) if (frag_left > 0) { len = min(frag_left, left); frag_left -= len; - elem->addr = - cpu_to_le64(ionic_tx_map_frag(q, frag, - offset, len)); - if (dma_mapping_error(dev, elem->addr)) + addr = ionic_tx_map_frag(q, frag, offset, len); + if (dma_mapping_error(dev, addr)) goto err_out_abort; + elem->addr = cpu_to_le64(addr); elem->len = cpu_to_le16(len); elem++; desc_nsge++; -- cgit v1.2.3 From 43ecf7b46f2688fd37909801aee264f288b3917b Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 22 Oct 2020 16:55:30 -0700 Subject: ionic: no rx flush in deinit Kmemleak pointed out to us that ionic_rx_flush() is sending skbs into napi_gro_XXX with a disabled napi context, and these end up getting lost and leaked. We can safely remove the flush. Fixes: 0f3154e6bcb3 ("ionic: Add Tx and Rx handling") Signed-off-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/pensando/ionic/ionic_lif.c | 1 - drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 13 ------------- drivers/net/ethernet/pensando/ionic/ionic_txrx.h | 1 - 3 files changed, 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 591c644b8e69..a12df3946a07 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -1656,7 +1656,6 @@ static void ionic_txrx_deinit(struct ionic_lif *lif) if (lif->rxqcqs) { for (i = 0; i < lif->nxqs && lif->rxqcqs[i]; i++) { ionic_lif_qcq_deinit(lif, lif->rxqcqs[i]); - ionic_rx_flush(&lif->rxqcqs[i]->cq); ionic_rx_empty(&lif->rxqcqs[i]->q); } } diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 8f6fc7142bc5..35acb4d66e31 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -253,19 +253,6 @@ static bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) return true; } -void ionic_rx_flush(struct ionic_cq *cq) -{ - struct ionic_dev *idev = &cq->lif->ionic->idev; - u32 work_done; - - work_done = ionic_cq_service(cq, cq->num_descs, - ionic_rx_service, NULL, NULL); - - if (work_done) - ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index, - work_done, IONIC_INTR_CRED_RESET_COALESCE); -} - static int ionic_rx_page_alloc(struct ionic_queue *q, struct ionic_page_info *page_info) { diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h index a5883be0413f..7667b72232b8 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.h @@ -4,7 +4,6 @@ #ifndef _IONIC_TXRX_H_ #define _IONIC_TXRX_H_ -void ionic_rx_flush(struct ionic_cq *cq); void ionic_tx_flush(struct ionic_cq *cq); void ionic_rx_fill(struct ionic_queue *q); -- cgit v1.2.3 From 0c32a28e247f51b0b67b5abb6e9368542e30c136 Mon Sep 17 00:00:00 2001 From: Shannon Nelson Date: Thu, 22 Oct 2020 16:55:31 -0700 Subject: ionic: fix mem leak in rx_empty The sentinel descriptor entry was getting missed in the traverse of the ring from head to tail, so change to a loop of 0 to the end. Fixes: f1d2e894f1b7 ("ionic: use index not pointer for queue tracking") Signed-off-by: Shannon Nelson Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/pensando/ionic/ionic_txrx.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 35acb4d66e31..b3d2250c77d0 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -400,22 +400,20 @@ static void ionic_rx_fill_cb(void *arg) void ionic_rx_empty(struct ionic_queue *q) { struct ionic_desc_info *desc_info; - struct ionic_rxq_desc *desc; - unsigned int i; - u16 idx; - - idx = q->tail_idx; - while (idx != q->head_idx) { - desc_info = &q->info[idx]; - desc = desc_info->desc; - desc->addr = 0; - desc->len = 0; + struct ionic_page_info *page_info; + unsigned int i, j; - for (i = 0; i < desc_info->npages; i++) - ionic_rx_page_free(q, &desc_info->pages[i]); + for (i = 0; i < q->num_descs; i++) { + desc_info = &q->info[i]; + for (j = 0; j < IONIC_RX_MAX_SG_ELEMS + 1; j++) { + page_info = &desc_info->pages[j]; + if (page_info->page) + ionic_rx_page_free(q, page_info); + } + desc_info->npages = 0; + desc_info->cb = NULL; desc_info->cb_arg = NULL; - idx = (idx + 1) & (q->num_descs - 1); } } -- cgit v1.2.3 From dab234227cbdc6a088c6f9bce38b5dcfefe90832 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 23 Oct 2020 14:32:36 +1100 Subject: net: ucc_geth: Drop extraneous parentheses in comparison Clang warns about the extra parentheses in this comparison: drivers/net/ethernet/freescale/ucc_geth.c:1361:28: warning: equality comparison with extraneous parentheses if ((ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII)) ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~ It seems clear the intent here is to do a comparison not an assignment, so drop the extra parentheses to avoid any confusion. Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20201023033236.3296988-1-mpe@ellerman.id.au Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/freescale/ucc_geth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 714b501be7d0..ba8869c3d891 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -1358,7 +1358,7 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UCC_GETH_UPSMR_TBIM; } - if ((ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII)) + if (ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII) upsmr |= UCC_GETH_UPSMR_SGMM; out_be32(&uf_regs->upsmr, upsmr); -- cgit v1.2.3 From ca05f33316559a04867295dd49f85aeedbfd6bfd Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Wed, 21 Oct 2020 11:53:59 +0200 Subject: of: Fix reserved-memory overlap detection The reserved-memory overlap detection code fails to detect overlaps if either of the regions starts at address 0x0. The code explicitly checks for and ignores such regions, apparently in order to ignore dynamically allocated regions which have an address of 0x0 at this point. These dynamically allocated regions also have a size of 0x0 at this point, so fix this by removing the check and sorting the dynamically allocated regions ahead of any static regions at address 0x0. For example, there are two overlaps in this case but they are not currently reported: foo@0 { reg = <0x0 0x2000>; }; bar@0 { reg = <0x0 0x1000>; }; baz@1000 { reg = <0x1000 0x1000>; }; quux { size = <0x1000>; }; but they are after this patch: OF: reserved mem: OVERLAP DETECTED! bar@0 (0x00000000--0x00001000) overlaps with foo@0 (0x00000000--0x00002000) OF: reserved mem: OVERLAP DETECTED! foo@0 (0x00000000--0x00002000) overlaps with baz@1000 (0x00001000--0x00002000) Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/ded6fd6b47b58741aabdcc6967f73eca6a3f311e.1603273666.git-series.vincent.whitchurch@axis.com Signed-off-by: Rob Herring --- drivers/of/of_reserved_mem.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index bcd154485972..a7fbc5e37e19 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -200,6 +200,16 @@ static int __init __rmem_cmp(const void *a, const void *b) if (ra->base > rb->base) return 1; + /* + * Put the dynamic allocations (address == 0, size == 0) before static + * allocations at address 0x0 so that overlap detection works + * correctly. + */ + if (ra->size < rb->size) + return -1; + if (ra->size > rb->size) + return 1; + return 0; } @@ -217,8 +227,7 @@ static void __init __rmem_check_for_overlap(void) this = &reserved_mem[i]; next = &reserved_mem[i + 1]; - if (!(this->base && next->base)) - continue; + if (this->base + this->size > next->base) { phys_addr_t this_end, next_end; -- cgit v1.2.3 From 84c971b356379c621df595bd00c3114579dfa59f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 15 Oct 2020 11:36:40 +0200 Subject: drm/sun4i: frontend: Rework a bit the phase data The scaler filter phase setup in the allwinner kernel has two different cases for setting up the scaler filter, the first one using different phase parameters for the two channels, and the second one reusing the first channel parameters on the second channel. The allwinner kernel has a third option where the horizontal phase of the second channel will be set to a different value than the vertical one (and seems like it's the same value than one used on the first channel). However, that code path seems to never be taken, so we can ignore it for now, and it's essentially what we're doing so far as well. Since we will have always the same values across each components of the filter setup for a given channel, we can simplify a bit our frontend structure by only storing the phase value we want to apply to a given channel. Signed-off-by: Maxime Ripard Acked-by: Jernej Skrabec Link: https://patchwork.freedesktop.org/patch/msgid/20201015093642.261440-1-maxime@cerno.tech --- drivers/gpu/drm/sun4i/sun4i_frontend.c | 34 ++++++++-------------------------- drivers/gpu/drm/sun4i/sun4i_frontend.h | 6 +----- 2 files changed, 9 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c index b51cc685c13a..b9eaaef41876 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.c +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c @@ -443,17 +443,17 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, * related to the scaler FIR filter phase parameters. */ regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, - frontend->data->ch_phase[0].horzphase); + frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, - frontend->data->ch_phase[1].horzphase); + frontend->data->ch_phase[1]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, - frontend->data->ch_phase[0].vertphase[0]); + frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, - frontend->data->ch_phase[1].vertphase[0]); + frontend->data->ch_phase[1]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, - frontend->data->ch_phase[0].vertphase[1]); + frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, - frontend->data->ch_phase[1].vertphase[1]); + frontend->data->ch_phase[1]); /* * Checking the input format is sufficient since we currently only @@ -687,30 +687,12 @@ static const struct dev_pm_ops sun4i_frontend_pm_ops = { }; static const struct sun4i_frontend_data sun4i_a10_frontend = { - .ch_phase = { - { - .horzphase = 0, - .vertphase = { 0, 0 }, - }, - { - .horzphase = 0xfc000, - .vertphase = { 0xfc000, 0xfc000 }, - }, - }, + .ch_phase = { 0x000, 0xfc000 }, .has_coef_rdy = true, }; static const struct sun4i_frontend_data sun8i_a33_frontend = { - .ch_phase = { - { - .horzphase = 0x400, - .vertphase = { 0x400, 0x400 }, - }, - { - .horzphase = 0x400, - .vertphase = { 0x400, 0x400 }, - }, - }, + .ch_phase = { 0x400, 0x400 }, .has_coef_access_ctrl = true, }; diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.h b/drivers/gpu/drm/sun4i/sun4i_frontend.h index 0c382c1ddb0f..2e7b76e50c2b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.h +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.h @@ -115,11 +115,7 @@ struct reset_control; struct sun4i_frontend_data { bool has_coef_access_ctrl; bool has_coef_rdy; - - struct { - u32 horzphase; - u32 vertphase[2]; - } ch_phase[2]; + u32 ch_phase[2]; }; struct sun4i_frontend { -- cgit v1.2.3 From 2db9ef9d9e6ea89a9feb5338f58d1f8f83875577 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 15 Oct 2020 11:36:41 +0200 Subject: drm/sun4i: frontend: Reuse the ch0 phase for RGB formats When using the scaler on the A10-like frontend with single-planar formats, the current code will setup the channel 0 filter (used for the R or Y component) with a different phase parameter than the channel 1 filter (used for the G/B or U/V components). This creates a bleed out that keeps repeating on of the last line of the RGB plane across the rest of the display. The Allwinner BSP either applies the same phase parameter over both channels or use a separate one, the condition being whether the input format is YUV420 or not. Since YUV420 is both subsampled and multi-planar, and since YUYV is subsampled but single-planar, we can rule out the subsampling and assume that the condition is actually whether the format is single or multi-planar. And it looks like applying the same phase parameter over both channels for single-planar formats fixes our issue, while we keep the multi-planar formats working properly. Reported-by: Taras Galchenko Signed-off-by: Maxime Ripard Acked-by: Jernej Skrabec Link: https://patchwork.freedesktop.org/patch/msgid/20201015093642.261440-2-maxime@cerno.tech --- drivers/gpu/drm/sun4i/sun4i_frontend.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c index b9eaaef41876..80f0c4392fef 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.c +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c @@ -407,6 +407,7 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, struct drm_framebuffer *fb = state->fb; const struct drm_format_info *format = fb->format; uint64_t modifier = fb->modifier; + unsigned int ch1_phase_idx; u32 out_fmt_val; u32 in_fmt_val, in_mod_val, in_ps_val; unsigned int i; @@ -442,18 +443,19 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, * I have no idea what this does exactly, but it seems to be * related to the scaler FIR filter phase parameters. */ + ch1_phase_idx = (format->num_planes > 1) ? 1 : 0; regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, - frontend->data->ch_phase[1]); + frontend->data->ch_phase[ch1_phase_idx]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, - frontend->data->ch_phase[1]); + frontend->data->ch_phase[ch1_phase_idx]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, frontend->data->ch_phase[0]); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, - frontend->data->ch_phase[1]); + frontend->data->ch_phase[ch1_phase_idx]); /* * Checking the input format is sufficient since we currently only -- cgit v1.2.3 From e3190b5e9462067714d267c40d8c8c1d0463dda3 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Thu, 15 Oct 2020 11:36:42 +0200 Subject: drm/sun4i: frontend: Fix the scaler phase on A33 The A33 has a different phase parameter in the Allwinner BSP on the channel1 than the one currently applied. Fix this. Signed-off-by: Maxime Ripard Acked-by: Jernej Skrabec Link: https://patchwork.freedesktop.org/patch/msgid/20201015093642.261440-3-maxime@cerno.tech --- drivers/gpu/drm/sun4i/sun4i_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c index 80f0c4392fef..edb60ae0a9b7 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.c +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c @@ -694,7 +694,7 @@ static const struct sun4i_frontend_data sun4i_a10_frontend = { }; static const struct sun4i_frontend_data sun8i_a33_frontend = { - .ch_phase = { 0x400, 0x400 }, + .ch_phase = { 0x400, 0xfc400 }, .has_coef_access_ctrl = true, }; -- cgit v1.2.3 From 897dbea6b716c0f2c5bcd4ba1eb4d809caba290c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 26 Oct 2020 12:49:05 +0300 Subject: drm/v3d: Fix double free in v3d_submit_cl_ioctl() Originally this error path used to leak "bin" but then we accidentally applied two separate commits to fix it and ended up with a double free. Signed-off-by: Dan Carpenter Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20201026094905.GA1634423@mwanda --- drivers/gpu/drm/v3d/v3d_gem.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 915f8bfdb58c..182c586525eb 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -568,7 +568,6 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, ret = v3d_job_init(v3d, file_priv, &bin->base, v3d_job_free, args->in_sync_bcl); if (ret) { - kfree(bin); v3d_job_put(&render->base); kfree(bin); return ret; -- cgit v1.2.3 From 831e3405c2a344018a18fcc2665acc5a38c3a707 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 10 Oct 2020 11:25:39 +0800 Subject: scsi: core: Don't start concurrent async scan on same host The current scanning mechanism is supposed to fall back to a synchronous host scan if an asynchronous scan is in progress. However, this rule isn't strictly respected, scsi_prep_async_scan() doesn't hold scan_mutex when checking shost->async_scan. When scsi_scan_host() is called concurrently, two async scans on same host can be started and a hang in do_scan_async() is observed. Fixes this issue by checking & setting shost->async_scan atomically with shost->scan_mutex. Link: https://lore.kernel.org/r/20201010032539.426615-1-ming.lei@redhat.com Cc: Christoph Hellwig Cc: Ewan D. Milne Cc: Hannes Reinecke Cc: Bart Van Assche Reviewed-by: Lee Duncan Reviewed-by: Bart Van Assche Signed-off-by: Ming Lei Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_scan.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index f2437a7570ce..9af50e6f94c4 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -1714,15 +1714,16 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost) */ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) { - struct async_scan_data *data; + struct async_scan_data *data = NULL; unsigned long flags; if (strncmp(scsi_scan_type, "sync", 4) == 0) return NULL; + mutex_lock(&shost->scan_mutex); if (shost->async_scan) { shost_printk(KERN_DEBUG, shost, "%s called twice\n", __func__); - return NULL; + goto err; } data = kmalloc(sizeof(*data), GFP_KERNEL); @@ -1733,7 +1734,6 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) goto err; init_completion(&data->prev_finished); - mutex_lock(&shost->scan_mutex); spin_lock_irqsave(shost->host_lock, flags); shost->async_scan = 1; spin_unlock_irqrestore(shost->host_lock, flags); @@ -1748,6 +1748,7 @@ static struct async_scan_data *scsi_prep_async_scan(struct Scsi_Host *shost) return data; err: + mutex_unlock(&shost->scan_mutex); kfree(data); return NULL; } -- cgit v1.2.3 From 98f9d33c5d0d779e6c96a4a8ac995085b11924f0 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Wed, 14 Oct 2020 09:30:48 +0200 Subject: scsi: qla2xxx: Return EBUSY on fcport deletion When the fcport is about to be deleted we should return EBUSY instead of ENODEV. Only for EBUSY will the request be requeued in a multipath setup. Also return EBUSY when the firmware has not yet started to avoid dropping the request. Link: https://lore.kernel.org/r/20201014073048.36219-1-dwagner@suse.de Reviewed-by: Arun Easi Signed-off-by: Daniel Wagner Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_nvme.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 1f9005125313..b7a1dc24db38 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -554,10 +554,12 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport, fcport = qla_rport->fcport; - if (!qpair || !fcport || (qpair && !qpair->fw_started) || - (fcport && fcport->deleted)) + if (!qpair || !fcport) return -ENODEV; + if (!qpair->fw_started || fcport->deleted) + return -EBUSY; + vha = fcport->vha; if (!(fcport->nvme_flag & NVME_FLAG_REGISTERED)) -- cgit v1.2.3 From bf3a28cf42412c0a85631da94f198048bb37a8e5 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 23 Oct 2020 16:19:22 +0300 Subject: regulator: fixed: support using power domain for enable/disable Adds possibility to choose the compatible "fixed-regulator-domain" for regulators which use power domain for enabling/disabling corresponding regulator. Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20201023131925.334864-3-dmitry.baryshkov@linaro.org Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 63 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 3de7709bdcd4..02ad83153e19 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -34,11 +36,13 @@ struct fixed_voltage_data { struct regulator_dev *dev; struct clk *enable_clock; - unsigned int clk_enable_counter; + unsigned int enable_counter; + int performance_state; }; struct fixed_dev_type { bool has_enable_clock; + bool has_performance_state; }; static int reg_clock_enable(struct regulator_dev *rdev) @@ -50,7 +54,7 @@ static int reg_clock_enable(struct regulator_dev *rdev) if (ret) return ret; - priv->clk_enable_counter++; + priv->enable_counter++; return ret; } @@ -60,16 +64,41 @@ static int reg_clock_disable(struct regulator_dev *rdev) struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); clk_disable_unprepare(priv->enable_clock); - priv->clk_enable_counter--; + priv->enable_counter--; return 0; } -static int reg_clock_is_enabled(struct regulator_dev *rdev) +static int reg_domain_enable(struct regulator_dev *rdev) { struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + struct device *dev = rdev->dev.parent; + int ret; + + ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state); + if (ret) + return ret; - return priv->clk_enable_counter > 0; + priv->enable_counter++; + + return ret; +} + +static int reg_domain_disable(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + struct device *dev = rdev->dev.parent; + + priv->enable_counter--; + + return dev_pm_genpd_set_performance_state(dev, 0); +} + +static int reg_is_enabled(struct regulator_dev *rdev) +{ + struct fixed_voltage_data *priv = rdev_get_drvdata(rdev); + + return priv->enable_counter > 0; } @@ -129,7 +158,13 @@ static const struct regulator_ops fixed_voltage_ops = { static const struct regulator_ops fixed_voltage_clkenabled_ops = { .enable = reg_clock_enable, .disable = reg_clock_disable, - .is_enabled = reg_clock_is_enabled, + .is_enabled = reg_is_enabled, +}; + +static const struct regulator_ops fixed_voltage_domain_ops = { + .enable = reg_domain_enable, + .disable = reg_domain_disable, + .is_enabled = reg_is_enabled, }; static int reg_fixed_voltage_probe(struct platform_device *pdev) @@ -177,6 +212,14 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) dev_err(dev, "Can't get enable-clock from devicetree\n"); return -ENOENT; } + } else if (drvtype && drvtype->has_performance_state) { + drvdata->desc.ops = &fixed_voltage_domain_ops; + + drvdata->performance_state = of_get_required_opp_performance_state(dev->of_node, 0); + if (drvdata->performance_state < 0) { + dev_err(dev, "Can't get performance state from devicetree\n"); + return drvdata->performance_state; + } } else { drvdata->desc.ops = &fixed_voltage_ops; } @@ -260,6 +303,10 @@ static const struct fixed_dev_type fixed_clkenable_data = { .has_enable_clock = true, }; +static const struct fixed_dev_type fixed_domain_data = { + .has_performance_state = true, +}; + static const struct of_device_id fixed_of_match[] = { { .compatible = "regulator-fixed", @@ -269,6 +316,10 @@ static const struct of_device_id fixed_of_match[] = { .compatible = "regulator-fixed-clock", .data = &fixed_clkenable_data, }, + { + .compatible = "regulator-fixed-domain", + .data = &fixed_domain_data, + }, { }, }; -- cgit v1.2.3 From 390d828f56a602c9201601bff1170d9d2bf5801c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Oct 2020 23:22:35 +0100 Subject: regulator: lp872x: make a const array static, makes object smaller Don't populate const array lp872x_num_regulators on the stack but instead make it static. Makes the object code smaller by 29 bytes. Before: text data bss dec hex filename 18441 4624 64 23129 5a59 drivers/regulator/lp872x.o After: text data bss dec hex filename 18316 4720 64 23100 5a3c drivers/regulator/lp872x.o (gcc version 10.2.0) Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20201016222235.686981-1-colin.king@canonical.com Signed-off-by: Mark Brown --- drivers/regulator/lp872x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index 303d7e2dc838..e84be29533f4 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -892,7 +892,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) struct lp872x *lp; struct lp872x_platform_data *pdata; int ret; - const int lp872x_num_regulators[] = { + static const int lp872x_num_regulators[] = { [LP8720] = LP8720_NUM_REGULATORS, [LP8725] = LP8725_NUM_REGULATORS, }; -- cgit v1.2.3 From 2f4843b172c2c0360ee7792ad98025fae7baefde Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 22 Oct 2020 11:00:05 +0200 Subject: scsi: mptfusion: Fix null pointer dereferences in mptscsih_remove() The mptscsih_remove() function triggers a kernel oops if the Scsi_Host pointer (ioc->sh) is NULL, as can be seen in this syslog: ioc0: LSI53C1030 B2: Capabilities={Initiator,Target} Begin: Waiting for root file system ... scsi host2: error handler thread failed to spawn, error = -4 mptspi: ioc0: WARNING - Unable to register controller with SCSI subsystem Backtrace: [<000000001045b7cc>] mptspi_probe+0x248/0x3d0 [mptspi] [<0000000040946470>] pci_device_probe+0x1ac/0x2d8 [<0000000040add668>] really_probe+0x1bc/0x988 [<0000000040ade704>] driver_probe_device+0x160/0x218 [<0000000040adee24>] device_driver_attach+0x160/0x188 [<0000000040adef90>] __driver_attach+0x144/0x320 [<0000000040ad7c78>] bus_for_each_dev+0xd4/0x158 [<0000000040adc138>] driver_attach+0x4c/0x80 [<0000000040adb3ec>] bus_add_driver+0x3e0/0x498 [<0000000040ae0130>] driver_register+0xf4/0x298 [<00000000409450c4>] __pci_register_driver+0x78/0xa8 [<000000000007d248>] mptspi_init+0x18c/0x1c4 [mptspi] This patch adds the necessary NULL-pointer checks. Successfully tested on a HP C8000 parisc workstation with buggy SCSI drives. Link: https://lore.kernel.org/r/20201022090005.GA9000@ls3530.fritz.box Cc: Signed-off-by: Helge Deller Signed-off-by: Martin K. Petersen --- drivers/message/fusion/mptscsih.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index a5ef9faf71c7..e7f0d4ae0f96 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -1176,8 +1176,10 @@ mptscsih_remove(struct pci_dev *pdev) MPT_SCSI_HOST *hd; int sz1; - if((hd = shost_priv(host)) == NULL) - return; + if (host == NULL) + hd = NULL; + else + hd = shost_priv(host); mptscsih_shutdown(pdev); @@ -1193,14 +1195,15 @@ mptscsih_remove(struct pci_dev *pdev) "Free'd ScsiLookup (%d) memory\n", ioc->name, sz1)); - kfree(hd->info_kbuf); + if (hd) + kfree(hd->info_kbuf); /* NULL the Scsi_Host pointer */ ioc->sh = NULL; - scsi_host_put(host); - + if (host) + scsi_host_put(host); mpt_detach(pdev); } -- cgit v1.2.3 From a305e7dc5fa86ff9cf6cd2da30215a92d43c9285 Mon Sep 17 00:00:00 2001 From: "Tianci.Yin" Date: Thu, 22 Oct 2020 11:40:26 +0800 Subject: drm/amdgpu: disable DCN and VCN for navi10 blockchain SKU(v3) The blockchain SKU has no display and video support, remove them. Reviewed-by: Alex Deucher Signed-off-by: Tianci.Yin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/nv.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/nv.c b/drivers/gpu/drm/amd/amdgpu/nv.c index 1ce741a0c6a7..03462c857498 100644 --- a/drivers/gpu/drm/amd/amdgpu/nv.c +++ b/drivers/gpu/drm/amd/amdgpu/nv.c @@ -455,6 +455,14 @@ void nv_set_virt_ops(struct amdgpu_device *adev) adev->virt.ops = &xgpu_nv_virt_ops; } +static bool nv_is_blockchain_sku(struct pci_dev *pdev) +{ + if (pdev->device == 0x731E && + (pdev->revision == 0xC6 || pdev->revision == 0xC7)) + return true; + return false; +} + int nv_set_ip_blocks(struct amdgpu_device *adev) { int r; @@ -483,7 +491,8 @@ int nv_set_ip_blocks(struct amdgpu_device *adev) if (adev->enable_virtual_display || amdgpu_sriov_vf(adev)) amdgpu_device_ip_block_add(adev, &dce_virtual_ip_block); #if defined(CONFIG_DRM_AMD_DC) - else if (amdgpu_device_has_dc_support(adev)) + else if (amdgpu_device_has_dc_support(adev) && + !nv_is_blockchain_sku(adev->pdev)) amdgpu_device_ip_block_add(adev, &dm_ip_block); #endif amdgpu_device_ip_block_add(adev, &gfx_v10_0_ip_block); @@ -491,7 +500,8 @@ int nv_set_ip_blocks(struct amdgpu_device *adev) if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT && !amdgpu_sriov_vf(adev)) amdgpu_device_ip_block_add(adev, &smu_v11_0_ip_block); - amdgpu_device_ip_block_add(adev, &vcn_v2_0_ip_block); + if (!nv_is_blockchain_sku(adev->pdev)) + amdgpu_device_ip_block_add(adev, &vcn_v2_0_ip_block); amdgpu_device_ip_block_add(adev, &jpeg_v2_0_ip_block); if (adev->enable_mes) amdgpu_device_ip_block_add(adev, &mes_v10_1_ip_block); -- cgit v1.2.3 From 8942881144a7365143f196f5eafed24783a424a3 Mon Sep 17 00:00:00 2001 From: "Tianci.Yin" Date: Wed, 14 Oct 2020 17:05:50 +0800 Subject: drm/amdgpu: add DID for navi10 blockchain SKU Reviewed-by: Alex Deucher Reviewed-by: Guchun Chen Signed-off-by: Tianci.Yin Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index c241317edee7..42d9748921f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -1066,6 +1066,7 @@ static const struct pci_device_id pciidlist[] = { {0x1002, 0x7319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, {0x1002, 0x731A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, {0x1002, 0x731B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, + {0x1002, 0x731E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, {0x1002, 0x731F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, /* Navi14 */ {0x1002, 0x7340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI14}, -- cgit v1.2.3 From c4aa8dff6091cc9536aeb255e544b0b4ba29faf4 Mon Sep 17 00:00:00 2001 From: Madhav Chauhan Date: Fri, 16 Oct 2020 18:03:07 +0530 Subject: drm/amdgpu: don't map BO in reserved region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2MB area is reserved at top inside VM. Suggested-by: Christian König Signed-off-by: Madhav Chauhan Reviewed-by: Christian König Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index aa7f230c71bf..7e8265da9f25 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -596,6 +596,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct ww_acquire_ctx ticket; struct list_head list, duplicates; uint64_t va_flags; + uint64_t vm_size; int r = 0; if (args->va_address < AMDGPU_VA_RESERVED_SIZE) { @@ -616,6 +617,15 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, args->va_address &= AMDGPU_GMC_HOLE_MASK; + vm_size = adev->vm_manager.max_pfn * AMDGPU_GPU_PAGE_SIZE; + vm_size -= AMDGPU_VA_RESERVED_SIZE; + if (args->va_address + args->map_size > vm_size) { + dev_dbg(&dev->pdev->dev, + "va_address 0x%llx is in top reserved area 0x%llx\n", + args->va_address + args->map_size, vm_size); + return -EINVAL; + } + if ((args->flags & ~valid_flags) && (args->flags & ~prt_flags)) { dev_dbg(&dev->pdev->dev, "invalid flags combination 0x%08X\n", args->flags); -- cgit v1.2.3 From 866e09f0110c6e86071954033e3067975946592a Mon Sep 17 00:00:00 2001 From: David Galiffi Date: Wed, 29 Apr 2020 13:31:12 -0400 Subject: drm/amd/display: Fixed panic during seamless boot. [why] get_pixel_clk_frequency_100hz is undefined in clock_source_funcs. [how] set function pointer: ".get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz" Signed-off-by: David Galiffi Reviewed-by: Bhawanpreet Lakha Acked-by: Aurabindo Pillai Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index 9cc65dc1970f..49ae5ff12da6 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -1149,7 +1149,8 @@ static uint32_t dcn3_get_pix_clk_dividers( static const struct clock_source_funcs dcn3_clk_src_funcs = { .cs_power_down = dce110_clock_source_power_down, .program_pix_clk = dcn3_program_pix_clk, - .get_pix_clk_dividers = dcn3_get_pix_clk_dividers + .get_pix_clk_dividers = dcn3_get_pix_clk_dividers, + .get_pixel_clk_frequency_100hz = get_pixel_clk_frequency_100hz }; #endif /*****************************************/ -- cgit v1.2.3 From 55bb919be4e4973cd037a04f527ecc6686800437 Mon Sep 17 00:00:00 2001 From: Christian König Date: Mon, 12 Oct 2020 13:09:36 +0200 Subject: drm/amdgpu: increase the reserved VM size to 2MB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ideally this should be a multiple of the VM block size. 2MB should at least fit for Vega/Navi. Signed-off-by: Christian König Reviewed-by: Madhav Chauhan Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index c6abb16e8018..58c83a7ad0fd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -112,8 +112,8 @@ struct amdgpu_bo_list_entry; #define AMDGPU_MMHUB_0 1 #define AMDGPU_MMHUB_1 2 -/* hardcode that limit for now */ -#define AMDGPU_VA_RESERVED_SIZE (1ULL << 20) +/* Reserve 2MB at top/bottom of address space for kernel use */ +#define AMDGPU_VA_RESERVED_SIZE (2ULL << 20) /* max vmids dedicated for process */ #define AMDGPU_VM_MAX_RESERVED_VMID 1 -- cgit v1.2.3 From 140b2ef1c28d3f5a5fc767368eaa8b45afc5bf1f Mon Sep 17 00:00:00 2001 From: Dmytro Laktyushkin Date: Thu, 15 Oct 2020 14:49:56 -0400 Subject: drm/amd/display: prevent null pointer access Prevent null pointer access when checking odm tree. Signed-off-by: Dmytro Laktyushkin Acked-by: Aurabindo Pillai Signed-off-by: Alex Deucher Cc: --- drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c index 24fb39a11e5d..2455d210ccf6 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c @@ -2105,12 +2105,12 @@ static bool dcn30_internal_validate_bw( if (split[i]) { if (odm) { - if (split[i] == 4 && old_pipe->next_odm_pipe->next_odm_pipe) + if (split[i] == 4 && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe) old_index = old_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; else if (old_pipe->next_odm_pipe) old_index = old_pipe->next_odm_pipe->pipe_idx; } else { - if (split[i] == 4 && old_pipe->bottom_pipe->bottom_pipe && + if (split[i] == 4 && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) old_index = old_pipe->bottom_pipe->bottom_pipe->pipe_idx; else if (old_pipe->bottom_pipe && @@ -2150,10 +2150,12 @@ static bool dcn30_internal_validate_bw( goto validate_fail; newly_split[pipe_4to1->pipe_idx] = true; - if (odm && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe) + if (odm && old_pipe->next_odm_pipe && old_pipe->next_odm_pipe->next_odm_pipe + && old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe) old_index = old_pipe->next_odm_pipe->next_odm_pipe->next_odm_pipe->pipe_idx; - else if (!odm && old_pipe->bottom_pipe->bottom_pipe->bottom_pipe && - old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) + else if (!odm && old_pipe->bottom_pipe && old_pipe->bottom_pipe->bottom_pipe && + old_pipe->bottom_pipe->bottom_pipe->bottom_pipe && + old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->plane_state == old_pipe->plane_state) old_index = old_pipe->bottom_pipe->bottom_pipe->bottom_pipe->pipe_idx; else old_index = -1; -- cgit v1.2.3 From 665e0224a3d76f36da40bd9012270fa629aa42ed Mon Sep 17 00:00:00 2001 From: Tyrel Datwyler Date: Sat, 24 Oct 2020 19:13:55 -0500 Subject: scsi: ibmvscsi: Fix potential race after loss of transport After a loss of transport due to an adapter migration or crash/disconnect from the host partner there is a tiny window where we can race adjusting the request_limit of the adapter. The request limit is atomically increased/decreased to track the number of inflight requests against the allowed limit of our VIOS partner. After a transport loss we set the request_limit to zero to reflect this state. However, there is a window where the adapter may attempt to queue a command because the transport loss event hasn't been fully processed yet and request_limit is still greater than zero. The hypercall to send the event will fail and the error path will increment the request_limit as a result. If the adapter processes the transport event prior to this increment the request_limit becomes out of sync with the adapter state and can result in SCSI commands being submitted on the now reset connection prior to an SRP Login resulting in a protocol violation. Fix this race by protecting request_limit with the host lock when changing the value via atomic_set() to indicate no transport. Link: https://lore.kernel.org/r/20201025001355.4527-1-tyreld@linux.ibm.com Signed-off-by: Tyrel Datwyler Signed-off-by: Martin K. Petersen --- drivers/scsi/ibmvscsi/ibmvscsi.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index b1f3017b6547..29fcc44be2d5 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -806,6 +806,22 @@ static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code) spin_unlock_irqrestore(hostdata->host->host_lock, flags); } +/** + * ibmvscsi_set_request_limit - Set the adapter request_limit in response to + * an adapter failure, reset, or SRP Login. Done under host lock to prevent + * race with SCSI command submission. + * @hostdata: adapter to adjust + * @limit: new request limit + */ +static void ibmvscsi_set_request_limit(struct ibmvscsi_host_data *hostdata, int limit) +{ + unsigned long flags; + + spin_lock_irqsave(hostdata->host->host_lock, flags); + atomic_set(&hostdata->request_limit, limit); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); +} + /** * ibmvscsi_reset_host - Reset the connection to the server * @hostdata: struct ibmvscsi_host_data to reset @@ -813,7 +829,7 @@ static void purge_requests(struct ibmvscsi_host_data *hostdata, int error_code) static void ibmvscsi_reset_host(struct ibmvscsi_host_data *hostdata) { scsi_block_requests(hostdata->host); - atomic_set(&hostdata->request_limit, 0); + ibmvscsi_set_request_limit(hostdata, 0); purge_requests(hostdata, DID_ERROR); hostdata->action = IBMVSCSI_HOST_ACTION_RESET; @@ -1146,13 +1162,13 @@ static void login_rsp(struct srp_event_struct *evt_struct) dev_info(hostdata->dev, "SRP_LOGIN_REJ reason %u\n", evt_struct->xfer_iu->srp.login_rej.reason); /* Login failed. */ - atomic_set(&hostdata->request_limit, -1); + ibmvscsi_set_request_limit(hostdata, -1); return; default: dev_err(hostdata->dev, "Invalid login response typecode 0x%02x!\n", evt_struct->xfer_iu->srp.login_rsp.opcode); /* Login failed. */ - atomic_set(&hostdata->request_limit, -1); + ibmvscsi_set_request_limit(hostdata, -1); return; } @@ -1163,7 +1179,7 @@ static void login_rsp(struct srp_event_struct *evt_struct) * This value is set rather than added to request_limit because * request_limit could have been set to -1 by this client. */ - atomic_set(&hostdata->request_limit, + ibmvscsi_set_request_limit(hostdata, be32_to_cpu(evt_struct->xfer_iu->srp.login_rsp.req_lim_delta)); /* If we had any pending I/Os, kick them */ @@ -1195,13 +1211,13 @@ static int send_srp_login(struct ibmvscsi_host_data *hostdata) login->req_buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); - spin_lock_irqsave(hostdata->host->host_lock, flags); /* Start out with a request limit of 0, since this is negotiated in * the login request we are just sending and login requests always * get sent by the driver regardless of request_limit. */ - atomic_set(&hostdata->request_limit, 0); + ibmvscsi_set_request_limit(hostdata, 0); + spin_lock_irqsave(hostdata->host->host_lock, flags); rc = ibmvscsi_send_srp_event(evt_struct, hostdata, login_timeout * 2); spin_unlock_irqrestore(hostdata->host->host_lock, flags); dev_info(hostdata->dev, "sent SRP login\n"); @@ -1781,7 +1797,7 @@ static void ibmvscsi_handle_crq(struct viosrp_crq *crq, return; case VIOSRP_CRQ_XPORT_EVENT: /* Hypervisor telling us the connection is closed */ scsi_block_requests(hostdata->host); - atomic_set(&hostdata->request_limit, 0); + ibmvscsi_set_request_limit(hostdata, 0); if (crq->format == 0x06) { /* We need to re-setup the interpartition connection */ dev_info(hostdata->dev, "Re-enabling adapter!\n"); @@ -2137,12 +2153,12 @@ static void ibmvscsi_do_work(struct ibmvscsi_host_data *hostdata) } hostdata->action = IBMVSCSI_HOST_ACTION_NONE; + spin_unlock_irqrestore(hostdata->host->host_lock, flags); if (rc) { - atomic_set(&hostdata->request_limit, -1); + ibmvscsi_set_request_limit(hostdata, -1); dev_err(hostdata->dev, "error after %s\n", action); } - spin_unlock_irqrestore(hostdata->host->host_lock, flags); scsi_unblock_requests(hostdata->host); } @@ -2226,7 +2242,7 @@ static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) init_waitqueue_head(&hostdata->work_wait_q); hostdata->host = host; hostdata->dev = dev; - atomic_set(&hostdata->request_limit, -1); + ibmvscsi_set_request_limit(hostdata, -1); hostdata->host->max_sectors = IBMVSCSI_MAX_SECTORS_DEFAULT; if (map_persist_bufs(hostdata)) { -- cgit v1.2.3 From a1d2afc5dde29a943d32bf92eb0408c9f19541fc Mon Sep 17 00:00:00 2001 From: Martin Leung Date: Wed, 7 Oct 2020 12:17:22 -0400 Subject: drm/amd/display: adding ddc_gpio_vga_reg_list to ddc reg def'ns why: oem-related ddc read/write fails without these regs how: copy from hw_factory_dcn20.c Signed-off-by: Martin Leung Acked-by: Aurabindo Pillai Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c index 3be2c90b0c61..21583699f992 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_factory_dcn30.c @@ -117,6 +117,12 @@ static const struct ddc_registers ddc_data_regs_dcn[] = { ddc_data_regs_dcn2(4), ddc_data_regs_dcn2(5), ddc_data_regs_dcn2(6), + { + DDC_GPIO_VGA_REG_LIST(DATA), + .ddc_setup = 0, + .phy_aux_cntl = 0, + .dc_gpio_aux_ctrl_5 = 0 + } }; static const struct ddc_registers ddc_clk_regs_dcn[] = { @@ -126,6 +132,12 @@ static const struct ddc_registers ddc_clk_regs_dcn[] = { ddc_clk_regs_dcn2(4), ddc_clk_regs_dcn2(5), ddc_clk_regs_dcn2(6), + { + DDC_GPIO_VGA_REG_LIST(CLK), + .ddc_setup = 0, + .phy_aux_cntl = 0, + .dc_gpio_aux_ctrl_5 = 0 + } }; static const struct ddc_sh_mask ddc_shift[] = { -- cgit v1.2.3 From 0689dcf3e4d6b89cc2087139561dc12b60461dca Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 26 Oct 2020 10:25:36 -0400 Subject: drm/amdgpu/display: use kvzalloc again in dc_create_state It looks this was accidently lost in a follow up patch. dc context is large and we don't need contiguous pages. Fixes: e4863f118a7d ("drm/amd/display: Multi display cause system lag on mode change") Reviewed-by: Nicholas Kazlauskas Signed-off-by: Alex Deucher Cc: Aric Cyr Cc: Alex Xu Reported-by: Alex Xu (Hello71) Tested-by: Alex Xu (Hello71) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/core/dc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 1eb29c362122..45ad05f6e03b 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1571,8 +1571,8 @@ static void init_state(struct dc *dc, struct dc_state *context) struct dc_state *dc_create_state(struct dc *dc) { - struct dc_state *context = kzalloc(sizeof(struct dc_state), - GFP_KERNEL); + struct dc_state *context = kvzalloc(sizeof(struct dc_state), + GFP_KERNEL); if (!context) return NULL; -- cgit v1.2.3 From 920bb38c518408fa2600eaefa0af9e82cf48f166 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 23 Oct 2020 09:46:54 +0200 Subject: drm/amd/display: Fix kernel panic by dal_gpio_open() error Currently both error code paths handled in dal_gpio_open_ex() issues ASSERT_CRITICAL(), and this leads to a kernel panic unnecessarily if CONFIG_KGDB is enabled. Since basically both are non-critical errors and can be recovered, drop those assert calls and use a safer one, BREAK_TO_DEBUGGER(), for allowing the debugging, instead. BugLink: https://bugzilla.opensuse.org/show_bug.cgi?id=1177973 Cc: Acked-by: Alex Deucher Reviewed-by: Nicholas Kazlauskas Signed-off-by: Takashi Iwai Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c b/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c index f67c18375bfd..dac427b68fd7 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/gpio_base.c @@ -63,13 +63,13 @@ enum gpio_result dal_gpio_open_ex( enum gpio_mode mode) { if (gpio->pin) { - ASSERT_CRITICAL(false); + BREAK_TO_DEBUGGER(); return GPIO_RESULT_ALREADY_OPENED; } // No action if allocation failed during gpio construct if (!gpio->hw_container.ddc) { - ASSERT_CRITICAL(false); + BREAK_TO_DEBUGGER(); return GPIO_RESULT_NON_SPECIFIC_ERROR; } gpio->mode = mode; -- cgit v1.2.3 From 8b7dc1fe1a5c1093551f6cd7dfbb941bd9081c2e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 23 Oct 2020 09:46:55 +0200 Subject: drm/amd/display: Don't invoke kgdb_breakpoint() unconditionally ASSERT_CRITICAL() invokes kgdb_breakpoint() whenever either CONFIG_KGDB or CONFIG_HAVE_KGDB is set. This, however, may lead to a kernel panic when no kdb stuff is attached, since the kgdb_breakpoint() call issues INT3. It's nothing but a surprise for normal end-users. For avoiding the pitfall, make the kgdb_breakpoint() call only when CONFIG_DEBUG_KERNEL_DC is set. https://bugzilla.opensuse.org/show_bug.cgi?id=1177973 Cc: Acked-by: Alex Deucher Reviewed-by: Nicholas Kazlauskas Signed-off-by: Takashi Iwai Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/os_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/dc/os_types.h b/drivers/gpu/drm/amd/display/dc/os_types.h index 330acaaed79a..32758b245754 100644 --- a/drivers/gpu/drm/amd/display/dc/os_types.h +++ b/drivers/gpu/drm/amd/display/dc/os_types.h @@ -94,7 +94,7 @@ * general debug capabilities * */ -#if defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB) +#if defined(CONFIG_DEBUG_KERNEL_DC) && (defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB)) #define ASSERT_CRITICAL(expr) do { \ if (WARN_ON(!(expr))) { \ kgdb_breakpoint(); \ -- cgit v1.2.3 From bcc3775dcf920b25d7123c141ed61f7f9f9bb6c3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 23 Oct 2020 09:46:56 +0200 Subject: drm/amd/display: Clean up debug macros This patch simplifies the ASSERT*() and BREAK_TO_DEBUGGER() macros: - Move the dependency check of CONFIG_KGDB into Kconfig - Unify the kgdb_breakpoint() call - Drop the non-existing CONFIG_HAVE_KGDB Also align the behavior of ASSERT() macro in both cases with and without CONFIG_DEBUG_KERNEL_DC. Acked-by: Alex Deucher Reviewed-by: Nicholas Kazlauskas Signed-off-by: Takashi Iwai Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/Kconfig | 1 + drivers/gpu/drm/amd/display/dc/os_types.h | 33 +++++++++++-------------------- 2 files changed, 13 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/display/Kconfig b/drivers/gpu/drm/amd/display/Kconfig index f24abf428534..60dfdd432aba 100644 --- a/drivers/gpu/drm/amd/display/Kconfig +++ b/drivers/gpu/drm/amd/display/Kconfig @@ -42,6 +42,7 @@ config DRM_AMD_DC_SI config DEBUG_KERNEL_DC bool "Enable kgdb break in DC" depends on DRM_AMD_DC + depends on KGDB help Choose this option if you want to hit kdgb_break in assert. diff --git a/drivers/gpu/drm/amd/display/dc/os_types.h b/drivers/gpu/drm/amd/display/dc/os_types.h index 32758b245754..95cb56929e79 100644 --- a/drivers/gpu/drm/amd/display/dc/os_types.h +++ b/drivers/gpu/drm/amd/display/dc/os_types.h @@ -94,36 +94,27 @@ * general debug capabilities * */ -#if defined(CONFIG_DEBUG_KERNEL_DC) && (defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB)) -#define ASSERT_CRITICAL(expr) do { \ - if (WARN_ON(!(expr))) { \ - kgdb_breakpoint(); \ - } \ -} while (0) +#ifdef CONFIG_DEBUG_KERNEL_DC +#define dc_breakpoint() kgdb_breakpoint() #else -#define ASSERT_CRITICAL(expr) do { \ - if (WARN_ON(!(expr))) { \ - ; \ - } \ -} while (0) +#define dc_breakpoint() do {} while (0) #endif -#if defined(CONFIG_DEBUG_KERNEL_DC) -#define ASSERT(expr) ASSERT_CRITICAL(expr) +#define ASSERT_CRITICAL(expr) do { \ + if (WARN_ON(!(expr))) \ + dc_breakpoint(); \ + } while (0) -#else -#define ASSERT(expr) WARN_ON_ONCE(!(expr)) -#endif +#define ASSERT(expr) do { \ + if (WARN_ON_ONCE(!(expr))) \ + dc_breakpoint(); \ + } while (0) -#if defined(CONFIG_DEBUG_KERNEL_DC) && (defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB)) #define BREAK_TO_DEBUGGER() \ do { \ DRM_DEBUG_DRIVER("%s():%d\n", __func__, __LINE__); \ - kgdb_breakpoint(); \ + dc_breakpoint(); \ } while (0) -#else -#define BREAK_TO_DEBUGGER() DRM_DEBUG_DRIVER("%s():%d\n", __func__, __LINE__) -#endif #define DC_ERR(...) do { \ dm_error(__VA_ARGS__); \ -- cgit v1.2.3 From fab09aaee80389a37d8ab49396afbb77fa86583a Mon Sep 17 00:00:00 2001 From: John Garry Date: Thu, 15 Oct 2020 16:27:05 +0800 Subject: scsi: hisi_sas: Stop using queue #0 always for v2 hw In commit 8d98416a55eb ("scsi: hisi_sas: Switch v3 hw to MQ"), the dispatch function was changed to choose the delivery queue based on the request tag HW queue index. This heavily degrades performance for v2 hw, since the HW queues are not exposed there, and, as such, HW queue #0 is used for every command. Revert to previous behaviour for when nr_hw_queues is not set, that being to choose the HW queue based on target device index. Link: https://lore.kernel.org/r/1602750425-240341-1-git-send-email-john.garry@huawei.com Fixes: 8d98416a55eb ("scsi: hisi_sas: Switch v3 hw to MQ") Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 128583dfccf2..c8dd8588f800 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -445,7 +445,7 @@ static int hisi_sas_task_prep(struct sas_task *task, } } - if (scmd) { + if (scmd && hisi_hba->shost->nr_hw_queues) { unsigned int dq_index; u32 blk_tag; -- cgit v1.2.3 From edebc8407b8891ec0ea9ca4089f3d3343a5e50dd Mon Sep 17 00:00:00 2001 From: Bob Pearson Date: Fri, 16 Oct 2020 16:13:44 -0500 Subject: RDMA/rxe: Fix small problem in network_type patch The patch referenced below has a typo that results in using the wrong L2 header size for outbound traffic. (V4 <-> V6). It also breaks kernel-side RC traffic because they use AVs that use RDMA_NETWORK_XXX enums instead of RXE_NETWORK_TYPE_XXX enums. Fix this by transcoding between these enum types. Fixes: e0d696d201dd ("RDMA/rxe: Move the definitions for rxe_av.network_type to uAPI") Link: https://lore.kernel.org/r/20201016211343.22906-1-rpearson@hpe.com Signed-off-by: Bob Pearson Signed-off-by: Jason Gunthorpe --- drivers/infiniband/sw/rxe/rxe_av.c | 35 +++++++++++++++++++++++++++++++---- drivers/infiniband/sw/rxe/rxe_net.c | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/sw/rxe/rxe_av.c b/drivers/infiniband/sw/rxe/rxe_av.c index 38021e2c8688..df0d173d6acb 100644 --- a/drivers/infiniband/sw/rxe/rxe_av.c +++ b/drivers/infiniband/sw/rxe/rxe_av.c @@ -16,15 +16,24 @@ void rxe_init_av(struct rdma_ah_attr *attr, struct rxe_av *av) int rxe_av_chk_attr(struct rxe_dev *rxe, struct rdma_ah_attr *attr) { + const struct ib_global_route *grh = rdma_ah_read_grh(attr); struct rxe_port *port; + int type; port = &rxe->port; if (rdma_ah_get_ah_flags(attr) & IB_AH_GRH) { - u8 sgid_index = rdma_ah_read_grh(attr)->sgid_index; + if (grh->sgid_index > port->attr.gid_tbl_len) { + pr_warn("invalid sgid index = %d\n", + grh->sgid_index); + return -EINVAL; + } - if (sgid_index > port->attr.gid_tbl_len) { - pr_warn("invalid sgid index = %d\n", sgid_index); + type = rdma_gid_attr_network_type(grh->sgid_attr); + if (type < RDMA_NETWORK_IPV4 || + type > RDMA_NETWORK_IPV6) { + pr_warn("invalid network type for rdma_rxe = %d\n", + type); return -EINVAL; } } @@ -65,11 +74,29 @@ void rxe_av_to_attr(struct rxe_av *av, struct rdma_ah_attr *attr) void rxe_av_fill_ip_info(struct rxe_av *av, struct rdma_ah_attr *attr) { const struct ib_gid_attr *sgid_attr = attr->grh.sgid_attr; + int ibtype; + int type; rdma_gid2ip((struct sockaddr *)&av->sgid_addr, &sgid_attr->gid); rdma_gid2ip((struct sockaddr *)&av->dgid_addr, &rdma_ah_read_grh(attr)->dgid); - av->network_type = rdma_gid_attr_network_type(sgid_attr); + + ibtype = rdma_gid_attr_network_type(sgid_attr); + + switch (ibtype) { + case RDMA_NETWORK_IPV4: + type = RXE_NETWORK_TYPE_IPV4; + break; + case RDMA_NETWORK_IPV6: + type = RXE_NETWORK_TYPE_IPV4; + break; + default: + /* not reached - checked in rxe_av_chk_attr */ + type = 0; + break; + } + + av->network_type = type; } struct rxe_av *rxe_get_av(struct rxe_pkt_info *pkt) diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index 575e1a4ec821..34bef7d8e6b4 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -442,7 +442,7 @@ struct sk_buff *rxe_init_packet(struct rxe_dev *rxe, struct rxe_av *av, if (IS_ERR(attr)) return NULL; - if (av->network_type == RXE_NETWORK_TYPE_IPV6) + if (av->network_type == RXE_NETWORK_TYPE_IPV4) hdr_len = ETH_HLEN + sizeof(struct udphdr) + sizeof(struct iphdr); else -- cgit v1.2.3 From fbdd0049d98d44914fc57d4b91f867f4996c787b Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Mon, 26 Oct 2020 15:43:59 +0200 Subject: RDMA/mlx5: Fix devlink deadlock on net namespace deletion When a mlx5 core devlink instance is reloaded in different net namespace, its associated IB device is deleted and recreated. Example sequence is: $ ip netns add foo $ devlink dev reload pci/0000:00:08.0 netns foo $ ip netns del foo mlx5 IB device needs to attach and detach the netdevice to it through the netdev notifier chain during load and unload sequence. A below call graph of the unload flow. cleanup_net() down_read(&pernet_ops_rwsem); <- first sem acquired ops_pre_exit_list() pre_exit() devlink_pernet_pre_exit() devlink_reload() mlx5_devlink_reload_down() mlx5_unload_one() [...] mlx5_ib_remove() mlx5_ib_unbind_slave_port() mlx5_remove_netdev_notifier() unregister_netdevice_notifier() down_write(&pernet_ops_rwsem);<- recurrsive lock Hence, when net namespace is deleted, mlx5 reload results in deadlock. When deadlock occurs, devlink mutex is also held. This not only deadlocks the mlx5 device under reload, but all the processes which attempt to access unrelated devlink devices are deadlocked. Hence, fix this by mlx5 ib driver to register for per net netdev notifier instead of global one, which operats on the net namespace without holding the pernet_ops_rwsem. Fixes: 4383cfcc65e7 ("net/mlx5: Add devlink reload") Link: https://lore.kernel.org/r/20201026134359.23150-1-parav@nvidia.com Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/main.c | 6 ++++-- drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h | 5 ----- include/linux/mlx5/driver.h | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 89e04ca62ae0..246e3cbe0b2c 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -3305,7 +3305,8 @@ static int mlx5_add_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num) int err; dev->port[port_num].roce.nb.notifier_call = mlx5_netdev_event; - err = register_netdevice_notifier(&dev->port[port_num].roce.nb); + err = register_netdevice_notifier_net(mlx5_core_net(dev->mdev), + &dev->port[port_num].roce.nb); if (err) { dev->port[port_num].roce.nb.notifier_call = NULL; return err; @@ -3317,7 +3318,8 @@ static int mlx5_add_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num) static void mlx5_remove_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num) { if (dev->port[port_num].roce.nb.notifier_call) { - unregister_netdevice_notifier(&dev->port[port_num].roce.nb); + unregister_netdevice_notifier_net(mlx5_core_net(dev->mdev), + &dev->port[port_num].roce.nb); dev->port[port_num].roce.nb.notifier_call = NULL; } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h index d046db7bb047..3a9fa629503f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h @@ -90,9 +90,4 @@ int mlx5_create_encryption_key(struct mlx5_core_dev *mdev, u32 key_type, u32 *p_key_id); void mlx5_destroy_encryption_key(struct mlx5_core_dev *mdev, u32 key_id); -static inline struct net *mlx5_core_net(struct mlx5_core_dev *dev) -{ - return devlink_net(priv_to_devlink(dev)); -} - #endif diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index add85094f9a5..0f23e1ed5e71 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -1213,4 +1213,22 @@ static inline bool mlx5_is_roce_enabled(struct mlx5_core_dev *dev) return val.vbool; } +/** + * mlx5_core_net - Provide net namespace of the mlx5_core_dev + * @dev: mlx5 core device + * + * mlx5_core_net() returns the net namespace of mlx5 core device. + * This can be called only in below described limited context. + * (a) When a devlink instance for mlx5_core is registered and + * when devlink reload operation is disabled. + * or + * (b) during devlink reload reload_down() and reload_up callbacks + * where it is ensured that devlink instance's net namespace is + * stable. + */ +static inline struct net *mlx5_core_net(struct mlx5_core_dev *dev) +{ + return devlink_net(priv_to_devlink(dev)); +} + #endif /* MLX5_DRIVER_H */ -- cgit v1.2.3 From 7d66a71488d7c14506ab81d6455c095992efca04 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Mon, 26 Oct 2020 10:26:21 +0200 Subject: RDMA/uverbs: Fix false error in query gid IOCTL Some drivers (such as EFA) have a GID table, but aren't IB/RoCE devices. Remove the unnecessary rdma_ib_or_roce() check. This fixes rdma-core failures for EFA when it uses the new ioctl interface for querying the GID table. Fixes: 9f85cbe50aa0 ("RDMA/uverbs: Expose the new GID query API to user space") Link: https://lore.kernel.org/r/20201026082621.32463-1-galpress@amazon.com Signed-off-by: Gal Pressman Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/uverbs_std_types_device.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/core/uverbs_std_types_device.c b/drivers/infiniband/core/uverbs_std_types_device.c index f367d523a46b..302f898c5833 100644 --- a/drivers/infiniband/core/uverbs_std_types_device.c +++ b/drivers/infiniband/core/uverbs_std_types_device.c @@ -401,9 +401,6 @@ static int UVERBS_HANDLER(UVERBS_METHOD_QUERY_GID_ENTRY)( if (!rdma_is_port_valid(ib_dev, port_num)) return -EINVAL; - if (!rdma_ib_or_roce(ib_dev, port_num)) - return -EOPNOTSUPP; - gid_attr = rdma_get_gid_attr(ib_dev, port_num, gid_index); if (IS_ERR(gid_attr)) return PTR_ERR(gid_attr); -- cgit v1.2.3 From 4525c8781ec0701ce824e8bd379ae1b129e26568 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 26 Oct 2020 15:45:22 -0700 Subject: scsi: qla2xxx: remove incorrect sparse #ifdef The code to try to shut up sparse warnings about questionable locking didn't shut up sparse: it made the result not parse as valid C at all, since the end result now has a label with no statement. The proper fix is to just always lock the hardware, the same way Bart did in commit 8ae178760b23 ("scsi: qla2xxx: Simplify the functions for dumping firmware"). That avoids the whole problem with having locking that is not statically obvious. But in the meantime, just remove the incorrect attempt at trying to avoid a sparse warning that just made things worse. This was exposed by commit 3e6efab865ac ("scsi: qla2xxx: Fix reset of MPI firmware"), very similarly to how commit cbb01c2f2f63 ("scsi: qla2xxx: Fix MPI failure AEN (8200) handling") exposed the same problem in another place, and caused that commit 8ae178760b23. Please don't add code to just shut up sparse without actually fixing what sparse complains about. Reported-by: Luc Van Oostenryck Cc: Bart Van Assche Cc: Arun Easi Signed-off-by: Linus Torvalds --- drivers/scsi/qla2xxx/qla_tmpl.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c index 84f4416d366f..bd8623ee156a 100644 --- a/drivers/scsi/qla2xxx/qla_tmpl.c +++ b/drivers/scsi/qla2xxx/qla_tmpl.c @@ -1001,10 +1001,8 @@ qla27xx_mpi_fwdump(scsi_qla_host_t *vha, int hardware_locked) { ulong flags = 0; -#ifndef __CHECKER__ if (!hardware_locked) spin_lock_irqsave(&vha->hw->hardware_lock, flags); -#endif if (!vha->hw->mpi_fw_dump) { ql_log(ql_log_warn, vha, 0x02f3, "-> mpi_fwdump no buffer\n"); } else { @@ -1050,10 +1048,8 @@ qla27xx_mpi_fwdump(scsi_qla_host_t *vha, int hardware_locked) } bailout: -#ifndef __CHECKER__ if (!hardware_locked) spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); -#endif } void -- cgit v1.2.3 From ee7a376421dd7bc65b610d42d42c084a0d16d6fa Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Oct 2020 14:22:12 +0300 Subject: net: hns3: clean up a return in hclge_tm_bp_setup() Smatch complains that "ret" might be uninitialized if we don't enter the loop. We do always enter the loop so it's a false positive, but it's cleaner to just return a literal zero and that silences the warning as well. Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20201023112212.GA282278@mwanda Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c index 15f69fa86323..e8495f58a1a8 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -1373,7 +1373,7 @@ static int hclge_tm_bp_setup(struct hclge_dev *hdev) return ret; } - return ret; + return 0; } int hclge_pause_setup_hw(struct hclge_dev *hdev, bool init) -- cgit v1.2.3 From 937d8420588421eaa5c7aa5c79b26b42abb288ef Mon Sep 17 00:00:00 2001 From: Raju Rangoju Date: Fri, 23 Oct 2020 17:28:52 +0530 Subject: cxgb4: set up filter action after rewrites The current code sets up the filter action field before rewrites are set up. When the action 'switch' is used with rewrites, this may result in initial few packets that get switched out don't have rewrites applied on them. So, make sure filter action is set up along with rewrites or only after everything else is set up for rewrites. Fixes: 12b276fbf6e0 ("cxgb4: add support to create hash filters") Signed-off-by: Raju Rangoju Link: https://lore.kernel.org/r/20201023115852.18262-1-rajur@chelsio.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c | 56 +++++++++++------------ drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h | 4 ++ 2 files changed, 31 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c index 6ec5f2f26f05..4e55f7081644 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_filter.c @@ -145,13 +145,13 @@ static int configure_filter_smac(struct adapter *adap, struct filter_entry *f) int err; /* do a set-tcb for smac-sel and CWR bit.. */ - err = set_tcb_tflag(adap, f, f->tid, TF_CCTRL_CWR_S, 1, 1); - if (err) - goto smac_err; - err = set_tcb_field(adap, f, f->tid, TCB_SMAC_SEL_W, TCB_SMAC_SEL_V(TCB_SMAC_SEL_M), TCB_SMAC_SEL_V(f->smt->idx), 1); + if (err) + goto smac_err; + + err = set_tcb_tflag(adap, f, f->tid, TF_CCTRL_CWR_S, 1, 1); if (!err) return 0; @@ -862,6 +862,7 @@ int set_filter_wr(struct adapter *adapter, int fidx) FW_FILTER_WR_DIRSTEERHASH_V(f->fs.dirsteerhash) | FW_FILTER_WR_LPBK_V(f->fs.action == FILTER_SWITCH) | FW_FILTER_WR_DMAC_V(f->fs.newdmac) | + FW_FILTER_WR_SMAC_V(f->fs.newsmac) | FW_FILTER_WR_INSVLAN_V(f->fs.newvlan == VLAN_INSERT || f->fs.newvlan == VLAN_REWRITE) | FW_FILTER_WR_RMVLAN_V(f->fs.newvlan == VLAN_REMOVE || @@ -879,7 +880,7 @@ int set_filter_wr(struct adapter *adapter, int fidx) FW_FILTER_WR_OVLAN_VLD_V(f->fs.val.ovlan_vld) | FW_FILTER_WR_IVLAN_VLDM_V(f->fs.mask.ivlan_vld) | FW_FILTER_WR_OVLAN_VLDM_V(f->fs.mask.ovlan_vld)); - fwr->smac_sel = 0; + fwr->smac_sel = f->smt->idx; fwr->rx_chan_rx_rpl_iq = htons(FW_FILTER_WR_RX_CHAN_V(0) | FW_FILTER_WR_RX_RPL_IQ_V(adapter->sge.fw_evtq.abs_id)); @@ -1323,11 +1324,8 @@ static void mk_act_open_req6(struct filter_entry *f, struct sk_buff *skb, TX_QUEUE_V(f->fs.nat_mode) | T5_OPT_2_VALID_F | RX_CHANNEL_V(cxgb4_port_e2cchan(f->dev)) | - CONG_CNTRL_V((f->fs.action == FILTER_DROP) | - (f->fs.dirsteer << 1)) | PACE_V((f->fs.maskhash) | - ((f->fs.dirsteerhash) << 1)) | - CCTRL_ECN_V(f->fs.action == FILTER_SWITCH)); + ((f->fs.dirsteerhash) << 1))); } static void mk_act_open_req(struct filter_entry *f, struct sk_buff *skb, @@ -1363,11 +1361,8 @@ static void mk_act_open_req(struct filter_entry *f, struct sk_buff *skb, TX_QUEUE_V(f->fs.nat_mode) | T5_OPT_2_VALID_F | RX_CHANNEL_V(cxgb4_port_e2cchan(f->dev)) | - CONG_CNTRL_V((f->fs.action == FILTER_DROP) | - (f->fs.dirsteer << 1)) | PACE_V((f->fs.maskhash) | - ((f->fs.dirsteerhash) << 1)) | - CCTRL_ECN_V(f->fs.action == FILTER_SWITCH)); + ((f->fs.dirsteerhash) << 1))); } static int cxgb4_set_hash_filter(struct net_device *dev, @@ -2039,6 +2034,20 @@ void hash_filter_rpl(struct adapter *adap, const struct cpl_act_open_rpl *rpl) } return; } + switch (f->fs.action) { + case FILTER_PASS: + if (f->fs.dirsteer) + set_tcb_tflag(adap, f, tid, + TF_DIRECT_STEER_S, 1, 1); + break; + case FILTER_DROP: + set_tcb_tflag(adap, f, tid, TF_DROP_S, 1, 1); + break; + case FILTER_SWITCH: + set_tcb_tflag(adap, f, tid, TF_LPBK_S, 1, 1); + break; + } + break; default: @@ -2106,22 +2115,11 @@ void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) if (ctx) ctx->result = 0; } else if (ret == FW_FILTER_WR_FLT_ADDED) { - int err = 0; - - if (f->fs.newsmac) - err = configure_filter_smac(adap, f); - - if (!err) { - f->pending = 0; /* async setup completed */ - f->valid = 1; - if (ctx) { - ctx->result = 0; - ctx->tid = idx; - } - } else { - clear_filter(adap, f); - if (ctx) - ctx->result = err; + f->pending = 0; /* async setup completed */ + f->valid = 1; + if (ctx) { + ctx->result = 0; + ctx->tid = idx; } } else { /* Something went wrong. Issue a warning about the diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h b/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h index 50232e063f49..92473dda55d9 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h @@ -50,6 +50,10 @@ #define TCB_T_FLAGS_M 0xffffffffffffffffULL #define TCB_T_FLAGS_V(x) ((__u64)(x) << TCB_T_FLAGS_S) +#define TF_DROP_S 22 +#define TF_DIRECT_STEER_S 23 +#define TF_LPBK_S 59 + #define TF_CCTRL_ECE_S 60 #define TF_CCTRL_CWR_S 61 #define TF_CCTRL_RFR_S 62 -- cgit v1.2.3 From 1dc0d1cf6f3d910ce3fffa83c5ae40c564e12373 Mon Sep 17 00:00:00 2001 From: Karsten Graul Date: Fri, 23 Oct 2020 20:48:30 +0200 Subject: s390/ism: fix incorrect system EID The system EID that is defined by the ISM driver is not correct. Using an incorrect system EID allows to communicate with remote Linux systems that use the same incorrect system EID, but when it comes to interoperability with other operating systems then the system EIDs do never match which prevents SMC-Dv2 communication. Using the correct system EID fixes this problem. Fixes: 201091ebb2a1 ("net/smc: introduce System Enterprise ID (SEID)") Signed-off-by: Karsten Graul Signed-off-by: Jakub Kicinski --- drivers/s390/net/ism_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index fe96ca3c88a5..26cc943d2034 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -390,7 +390,7 @@ static int ism_move(struct smcd_dev *smcd, u64 dmb_tok, unsigned int idx, } static struct ism_systemeid SYSTEM_EID = { - .seid_string = "IBM-SYSZ-IBMSEID00000000", + .seid_string = "IBM-SYSZ-ISMSEID00000000", .serial_number = "0000", .type = "0000", }; -- cgit v1.2.3 From 1601559be3e4213148b4cb4a1abe672b00bf4f67 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Sat, 24 Oct 2020 16:37:31 +0300 Subject: mlxsw: Only advertise link modes supported by both driver and device During port creation the driver instructs the device to advertise all the supported link modes queried from the device. Since cited commit not all the link modes supported by the device are supported by the driver. This can result in the device negotiating a link mode that is not recognized by the driver causing ethtool to show an unsupported speed: $ ethtool swp1 ... Speed: Unknown! This is especially problematic when the netdev is enslaved to a bond, as the bond driver uses unknown speed as an indication that the link is down: [13048.900895] net_ratelimit: 86 callbacks suppressed [13048.900902] t_bond0: (slave swp52): failed to get link speed/duplex [13048.912160] t_bond0: (slave swp49): failed to get link speed/duplex Fix this by making sure that only link modes that are supported by both the device and the driver are advertised. Fixes: b97cd891268d ("mlxsw: Remove 56G speed support") Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 9 +++++-- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 1 + .../net/ethernet/mellanox/mlxsw/spectrum_ethtool.c | 30 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 16b47fce540b..b08853f71b2b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1174,11 +1174,14 @@ mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port) u32 eth_proto_cap, eth_proto_admin, eth_proto_oper; const struct mlxsw_sp_port_type_speed_ops *ops; char ptys_pl[MLXSW_REG_PTYS_LEN]; + u32 eth_proto_cap_masked; int err; ops = mlxsw_sp->port_type_speed_ops; - /* Set advertised speeds to supported speeds. */ + /* Set advertised speeds to speeds supported by both the driver + * and the device. + */ ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port, 0, false); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); @@ -1187,8 +1190,10 @@ mlxsw_sp_port_speed_by_width_set(struct mlxsw_sp_port *mlxsw_sp_port) ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, ð_proto_cap, ð_proto_admin, ð_proto_oper); + eth_proto_cap_masked = ops->ptys_proto_cap_masked_get(eth_proto_cap); ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl, mlxsw_sp_port->local_port, - eth_proto_cap, mlxsw_sp_port->link.autoneg); + eth_proto_cap_masked, + mlxsw_sp_port->link.autoneg); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 3e26eb6cb140..74b3959b36d4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -342,6 +342,7 @@ struct mlxsw_sp_port_type_speed_ops { u32 *p_eth_proto_cap, u32 *p_eth_proto_admin, u32 *p_eth_proto_oper); + u32 (*ptys_proto_cap_masked_get)(u32 eth_proto_cap); }; static inline struct net_device * diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c index 2096b6478958..540616469e28 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c @@ -1303,6 +1303,20 @@ mlxsw_sp1_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload, p_eth_proto_oper); } +static u32 mlxsw_sp1_ptys_proto_cap_masked_get(u32 eth_proto_cap) +{ + u32 ptys_proto_cap_masked = 0; + int i; + + for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { + if (mlxsw_sp1_port_link_mode[i].mask & eth_proto_cap) + ptys_proto_cap_masked |= + mlxsw_sp1_port_link_mode[i].mask; + } + + return ptys_proto_cap_masked; +} + const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = { .from_ptys_supported_port = mlxsw_sp1_from_ptys_supported_port, .from_ptys_link = mlxsw_sp1_from_ptys_link, @@ -1313,6 +1327,7 @@ const struct mlxsw_sp_port_type_speed_ops mlxsw_sp1_port_type_speed_ops = { .to_ptys_speed = mlxsw_sp1_to_ptys_speed, .reg_ptys_eth_pack = mlxsw_sp1_reg_ptys_eth_pack, .reg_ptys_eth_unpack = mlxsw_sp1_reg_ptys_eth_unpack, + .ptys_proto_cap_masked_get = mlxsw_sp1_ptys_proto_cap_masked_get, }; static const enum ethtool_link_mode_bit_indices @@ -1731,6 +1746,20 @@ mlxsw_sp2_reg_ptys_eth_unpack(struct mlxsw_sp *mlxsw_sp, char *payload, p_eth_proto_admin, p_eth_proto_oper); } +static u32 mlxsw_sp2_ptys_proto_cap_masked_get(u32 eth_proto_cap) +{ + u32 ptys_proto_cap_masked = 0; + int i; + + for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { + if (mlxsw_sp2_port_link_mode[i].mask & eth_proto_cap) + ptys_proto_cap_masked |= + mlxsw_sp2_port_link_mode[i].mask; + } + + return ptys_proto_cap_masked; +} + const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = { .from_ptys_supported_port = mlxsw_sp2_from_ptys_supported_port, .from_ptys_link = mlxsw_sp2_from_ptys_link, @@ -1741,4 +1770,5 @@ const struct mlxsw_sp_port_type_speed_ops mlxsw_sp2_port_type_speed_ops = { .to_ptys_speed = mlxsw_sp2_to_ptys_speed, .reg_ptys_eth_pack = mlxsw_sp2_reg_ptys_eth_pack, .reg_ptys_eth_unpack = mlxsw_sp2_reg_ptys_eth_unpack, + .ptys_proto_cap_masked_get = mlxsw_sp2_ptys_proto_cap_masked_get, }; -- cgit v1.2.3 From adc80b6cfedff6dad8b93d46a5ea2775fd5af9ec Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sat, 24 Oct 2020 16:37:32 +0300 Subject: mlxsw: core: Fix memory leak on module removal Free the devlink instance during the teardown sequence in the non-reload case to avoid the following memory leak. unreferenced object 0xffff888232895000 (size 2048): comm "modprobe", pid 1073, jiffies 4295568857 (age 164.871s) hex dump (first 32 bytes): 00 01 00 00 00 00 ad de 22 01 00 00 00 00 ad de ........"....... 10 50 89 32 82 88 ff ff 10 50 89 32 82 88 ff ff .P.2.....P.2.... backtrace: [<00000000c704e9a6>] __kmalloc+0x13a/0x2a0 [<00000000ee30129d>] devlink_alloc+0xff/0x760 [<0000000092ab3e5d>] 0xffffffffa042e5b0 [<000000004f3f8a31>] 0xffffffffa042f6ad [<0000000092800b4b>] 0xffffffffa0491df3 [<00000000c4843903>] local_pci_probe+0xcb/0x170 [<000000006993ded7>] pci_device_probe+0x2c2/0x4e0 [<00000000a8e0de75>] really_probe+0x2c5/0xf90 [<00000000d42ba75d>] driver_probe_device+0x1eb/0x340 [<00000000bcc95e05>] device_driver_attach+0x294/0x300 [<000000000e2bc177>] __driver_attach+0x167/0x2f0 [<000000007d44cd6e>] bus_for_each_dev+0x148/0x1f0 [<000000003cd5a91e>] driver_attach+0x45/0x60 [<000000000041ce51>] bus_add_driver+0x3b8/0x720 [<00000000f5215476>] driver_register+0x230/0x4e0 [<00000000d79356f5>] __pci_register_driver+0x190/0x200 Fixes: a22712a96291 ("mlxsw: core: Fix devlink unregister flow") Signed-off-by: Ido Schimmel Reported-by: Vadim Pasternak Tested-by: Oleksandr Shamray Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index 7f77c2a71d1c..a2efbb1e3cf4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -2064,6 +2064,8 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core, if (!reload) devlink_resources_unregister(devlink, NULL); mlxsw_core->bus->fini(mlxsw_core->bus_priv); + if (!reload) + devlink_free(devlink); return; -- cgit v1.2.3 From 0daf2bf5a2dcf33d446b76360908f109816e2e21 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Sat, 24 Oct 2020 16:37:33 +0300 Subject: mlxsw: core: Fix use-after-free in mlxsw_emad_trans_finish() Each EMAD transaction stores the skb used to issue the EMAD request ('trans->tx_skb') so that the request could be retried in case of a timeout. The skb can be freed when a corresponding response is received or as part of the retry logic (e.g., failed retransmit, exceeded maximum number of retries). The two tasks (i.e., response processing and retransmits) are synchronized by the atomic 'trans->active' field which ensures that responses to inactive transactions are ignored. In case of a failed retransmit the transaction is finished and all of its resources are freed. However, the current code does not mark it as inactive. Syzkaller was able to hit a race condition in which a concurrent response is processed while the transaction's resources are being freed, resulting in a use-after-free [1]. Fix the issue by making sure to mark the transaction as inactive after a failed retransmit and free its resources only if a concurrent task did not already do that. [1] BUG: KASAN: use-after-free in consume_skb+0x30/0x370 net/core/skbuff.c:833 Read of size 4 at addr ffff88804f570494 by task syz-executor.0/1004 CPU: 0 PID: 1004 Comm: syz-executor.0 Not tainted 5.8.0-rc7+ #68 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xf6/0x16e lib/dump_stack.c:118 print_address_description.constprop.0+0x1c/0x250 mm/kasan/report.c:383 __kasan_report mm/kasan/report.c:513 [inline] kasan_report.cold+0x1f/0x37 mm/kasan/report.c:530 check_memory_region_inline mm/kasan/generic.c:186 [inline] check_memory_region+0x14e/0x1b0 mm/kasan/generic.c:192 instrument_atomic_read include/linux/instrumented.h:56 [inline] atomic_read include/asm-generic/atomic-instrumented.h:27 [inline] refcount_read include/linux/refcount.h:147 [inline] skb_unref include/linux/skbuff.h:1044 [inline] consume_skb+0x30/0x370 net/core/skbuff.c:833 mlxsw_emad_trans_finish+0x64/0x1c0 drivers/net/ethernet/mellanox/mlxsw/core.c:592 mlxsw_emad_process_response drivers/net/ethernet/mellanox/mlxsw/core.c:651 [inline] mlxsw_emad_rx_listener_func+0x5c9/0xac0 drivers/net/ethernet/mellanox/mlxsw/core.c:672 mlxsw_core_skb_receive+0x4df/0x770 drivers/net/ethernet/mellanox/mlxsw/core.c:2063 mlxsw_pci_cqe_rdq_handle drivers/net/ethernet/mellanox/mlxsw/pci.c:595 [inline] mlxsw_pci_cq_tasklet+0x12a6/0x2520 drivers/net/ethernet/mellanox/mlxsw/pci.c:651 tasklet_action_common.isra.0+0x13f/0x3e0 kernel/softirq.c:550 __do_softirq+0x223/0x964 kernel/softirq.c:292 asm_call_on_stack+0x12/0x20 arch/x86/entry/entry_64.S:711 Allocated by task 1006: save_stack+0x1b/0x40 mm/kasan/common.c:48 set_track mm/kasan/common.c:56 [inline] __kasan_kmalloc mm/kasan/common.c:494 [inline] __kasan_kmalloc.constprop.0+0xc2/0xd0 mm/kasan/common.c:467 slab_post_alloc_hook mm/slab.h:586 [inline] slab_alloc_node mm/slub.c:2824 [inline] slab_alloc mm/slub.c:2832 [inline] kmem_cache_alloc+0xcd/0x2e0 mm/slub.c:2837 __build_skb+0x21/0x60 net/core/skbuff.c:311 __netdev_alloc_skb+0x1e2/0x360 net/core/skbuff.c:464 netdev_alloc_skb include/linux/skbuff.h:2810 [inline] mlxsw_emad_alloc drivers/net/ethernet/mellanox/mlxsw/core.c:756 [inline] mlxsw_emad_reg_access drivers/net/ethernet/mellanox/mlxsw/core.c:787 [inline] mlxsw_core_reg_access_emad+0x1ab/0x1420 drivers/net/ethernet/mellanox/mlxsw/core.c:1817 mlxsw_reg_trans_query+0x39/0x50 drivers/net/ethernet/mellanox/mlxsw/core.c:1831 mlxsw_sp_sb_pm_occ_clear drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c:260 [inline] mlxsw_sp_sb_occ_max_clear+0xbff/0x10a0 drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c:1365 mlxsw_devlink_sb_occ_max_clear+0x76/0xb0 drivers/net/ethernet/mellanox/mlxsw/core.c:1037 devlink_nl_cmd_sb_occ_max_clear_doit+0x1ec/0x280 net/core/devlink.c:1765 genl_family_rcv_msg_doit net/netlink/genetlink.c:669 [inline] genl_family_rcv_msg net/netlink/genetlink.c:714 [inline] genl_rcv_msg+0x617/0x980 net/netlink/genetlink.c:731 netlink_rcv_skb+0x152/0x440 net/netlink/af_netlink.c:2470 genl_rcv+0x24/0x40 net/netlink/genetlink.c:742 netlink_unicast_kernel net/netlink/af_netlink.c:1304 [inline] netlink_unicast+0x53a/0x750 net/netlink/af_netlink.c:1330 netlink_sendmsg+0x850/0xd90 net/netlink/af_netlink.c:1919 sock_sendmsg_nosec net/socket.c:651 [inline] sock_sendmsg+0x150/0x190 net/socket.c:671 ____sys_sendmsg+0x6d8/0x840 net/socket.c:2359 ___sys_sendmsg+0xff/0x170 net/socket.c:2413 __sys_sendmsg+0xe5/0x1b0 net/socket.c:2446 do_syscall_64+0x56/0xa0 arch/x86/entry/common.c:384 entry_SYSCALL_64_after_hwframe+0x44/0xa9 Freed by task 73: save_stack+0x1b/0x40 mm/kasan/common.c:48 set_track mm/kasan/common.c:56 [inline] kasan_set_free_info mm/kasan/common.c:316 [inline] __kasan_slab_free+0x12c/0x170 mm/kasan/common.c:455 slab_free_hook mm/slub.c:1474 [inline] slab_free_freelist_hook mm/slub.c:1507 [inline] slab_free mm/slub.c:3072 [inline] kmem_cache_free+0xbe/0x380 mm/slub.c:3088 kfree_skbmem net/core/skbuff.c:622 [inline] kfree_skbmem+0xef/0x1b0 net/core/skbuff.c:616 __kfree_skb net/core/skbuff.c:679 [inline] consume_skb net/core/skbuff.c:837 [inline] consume_skb+0xe1/0x370 net/core/skbuff.c:831 mlxsw_emad_trans_finish+0x64/0x1c0 drivers/net/ethernet/mellanox/mlxsw/core.c:592 mlxsw_emad_transmit_retry.isra.0+0x9d/0xc0 drivers/net/ethernet/mellanox/mlxsw/core.c:613 mlxsw_emad_trans_timeout_work+0x43/0x50 drivers/net/ethernet/mellanox/mlxsw/core.c:625 process_one_work+0xa3e/0x17a0 kernel/workqueue.c:2269 worker_thread+0x9e/0x1050 kernel/workqueue.c:2415 kthread+0x355/0x470 kernel/kthread.c:291 ret_from_fork+0x22/0x30 arch/x86/entry/entry_64.S:293 The buggy address belongs to the object at ffff88804f5703c0 which belongs to the cache skbuff_head_cache of size 224 The buggy address is located 212 bytes inside of 224-byte region [ffff88804f5703c0, ffff88804f5704a0) The buggy address belongs to the page: page:ffffea00013d5c00 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 flags: 0x100000000000200(slab) raw: 0100000000000200 dead000000000100 dead000000000122 ffff88806c625400 raw: 0000000000000000 00000000000c000c 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff88804f570380: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb ffff88804f570400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb >ffff88804f570480: fb fb fb fb fc fc fc fc fc fc fc fc fc fc fc fc ^ ffff88804f570500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff88804f570580: 00 00 00 00 00 00 00 00 00 00 00 00 fc fc fc fc Fixes: caf7297e7ab5f ("mlxsw: core: Introduce support for asynchronous EMAD register access") Signed-off-by: Amit Cohen Reviewed-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlxsw/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index a2efbb1e3cf4..937b8e46f8c7 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -620,6 +620,9 @@ static void mlxsw_emad_transmit_retry(struct mlxsw_core *mlxsw_core, err = mlxsw_emad_transmit(trans->core, trans); if (err == 0) return; + + if (!atomic_dec_and_test(&trans->active)) + return; } else { err = -EIO; } -- cgit v1.2.3 From 21d6a11e2cadfb8446265a3efff0e2aad206e15e Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Mon, 26 Oct 2020 00:18:17 -0400 Subject: bnxt_en: Fix regression in workqueue cleanup logic in bnxt_remove_one(). A recent patch has moved the workqueue cleanup logic before calling unregister_netdev() in bnxt_remove_one(). This caused a regression because the workqueue can be restarted if the device is still open. Workqueue cleanup must be done after unregister_netdev(). The workqueue will not restart itself after the device is closed. Call bnxt_cancel_sp_work() after unregister_netdev() and call bnxt_dl_fw_reporters_destroy() after that. This fixes the regession and the original NULL ptr dereference issue. Fixes: b16939b59cc0 ("bnxt_en: Fix NULL ptr dereference crash in bnxt_fw_reset_task()") Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index fa147865e33f..e4e5ea080391 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -12108,15 +12108,16 @@ static void bnxt_remove_one(struct pci_dev *pdev) if (BNXT_PF(bp)) bnxt_sriov_disable(bp); + if (BNXT_PF(bp)) + devlink_port_type_clear(&bp->dl_port); + pci_disable_pcie_error_reporting(pdev); + unregister_netdev(dev); clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state); + /* Flush any pending tasks */ bnxt_cancel_sp_work(bp); bp->sp_event = 0; bnxt_dl_fw_reporters_destroy(bp, true); - if (BNXT_PF(bp)) - devlink_port_type_clear(&bp->dl_port); - pci_disable_pcie_error_reporting(pdev); - unregister_netdev(dev); bnxt_dl_unregister(bp); bnxt_shutdown_tc(bp); -- cgit v1.2.3 From 631ce27a3006fc0b732bfd589c6df505f62eadd9 Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Mon, 26 Oct 2020 00:18:18 -0400 Subject: bnxt_en: Invoke cancel_delayed_work_sync() for PFs also. As part of the commit b148bb238c02 ("bnxt_en: Fix possible crash in bnxt_fw_reset_task()."), cancel_delayed_work_sync() is called only for VFs to fix a possible crash by cancelling any pending delayed work items. It was assumed by mistake that the flush_workqueue() call on the PF would flush delayed work items as well. As flush_workqueue() does not cancel the delayed workqueue, extend the fix for PFs. This fix will avoid the system crash, if there are any pending delayed work items in fw_reset_task() during driver's .remove() call. Unify the workqueue cleanup logic for both PF and VF by calling cancel_work_sync() and cancel_delayed_work_sync() directly in bnxt_remove_one(). Fixes: b148bb238c02 ("bnxt_en: Fix possible crash in bnxt_fw_reset_task().") Reviewed-by: Pavan Chebbi Reviewed-by: Andy Gospodarek Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index e4e5ea080391..7be232018015 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1160,16 +1160,6 @@ static void bnxt_queue_sp_work(struct bnxt *bp) schedule_work(&bp->sp_task); } -static void bnxt_cancel_sp_work(struct bnxt *bp) -{ - if (BNXT_PF(bp)) { - flush_workqueue(bnxt_pf_wq); - } else { - cancel_work_sync(&bp->sp_task); - cancel_delayed_work_sync(&bp->fw_reset_task); - } -} - static void bnxt_sched_reset(struct bnxt *bp, struct bnxt_rx_ring_info *rxr) { if (!rxr->bnapi->in_reset) { @@ -12114,7 +12104,8 @@ static void bnxt_remove_one(struct pci_dev *pdev) unregister_netdev(dev); clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state); /* Flush any pending tasks */ - bnxt_cancel_sp_work(bp); + cancel_work_sync(&bp->sp_task); + cancel_delayed_work_sync(&bp->fw_reset_task); bp->sp_event = 0; bnxt_dl_fw_reporters_destroy(bp, true); -- cgit v1.2.3 From f75d9a0aa96721d20011cd5f8c7a24eb32728589 Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Mon, 26 Oct 2020 00:18:19 -0400 Subject: bnxt_en: Re-write PCI BARs after PCI fatal error. When a PCIe fatal error occurs, the internal latched BAR addresses in the chip get reset even though the BAR register values in config space are retained. pci_restore_state() will not rewrite the BAR addresses if the BAR address values are valid, causing the chip's internal BAR addresses to stay invalid. So we need to zero the BAR registers during PCIe fatal error to force pci_restore_state() to restore the BAR addresses. These write cycles to the BAR registers will cause the proper BAR addresses to latch internally. Fixes: 6316ea6db93d ("bnxt_en: Enable AER support.") Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 19 ++++++++++++++++++- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7be232018015..8012386b4a0f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -12852,6 +12852,9 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev, return PCI_ERS_RESULT_DISCONNECT; } + if (state == pci_channel_io_frozen) + set_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, &bp->state); + if (netif_running(netdev)) bnxt_close(netdev); @@ -12878,7 +12881,7 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct bnxt *bp = netdev_priv(netdev); - int err = 0; + int err = 0, off; pci_ers_result_t result = PCI_ERS_RESULT_DISCONNECT; netdev_info(bp->dev, "PCI Slot Reset\n"); @@ -12890,6 +12893,20 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) "Cannot re-enable PCI device after reset.\n"); } else { pci_set_master(pdev); + /* Upon fatal error, our device internal logic that latches to + * BAR value is getting reset and will restore only upon + * rewritting the BARs. + * + * As pci_restore_state() does not re-write the BARs if the + * value is same as saved value earlier, driver needs to + * write the BARs to 0 to force restore, in case of fatal error. + */ + if (test_and_clear_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, + &bp->state)) { + for (off = PCI_BASE_ADDRESS_0; + off <= PCI_BASE_ADDRESS_5; off += 4) + pci_write_config_dword(bp->pdev, off, 0); + } pci_restore_state(pdev); pci_save_state(pdev); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 21ef1c21f602..47b3c3127879 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1781,6 +1781,7 @@ struct bnxt { #define BNXT_STATE_ABORT_ERR 5 #define BNXT_STATE_FW_FATAL_COND 6 #define BNXT_STATE_DRV_REGISTERED 7 +#define BNXT_STATE_PCI_CHANNEL_IO_FROZEN 8 #define BNXT_NO_FW_ACCESS(bp) \ (test_bit(BNXT_STATE_FW_FATAL_COND, &(bp)->state) || \ -- cgit v1.2.3 From a1301f08c5acf992d9c1fafddc84c3a822844b04 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Mon, 26 Oct 2020 00:18:20 -0400 Subject: bnxt_en: Check abort error state in bnxt_open_nic(). bnxt_open_nic() is called during configuration changes that require the NIC to be closed and then opened. This call is protected by rtnl_lock. Firmware reset can be happening at the same time. Only critical portions of the entire firmware reset sequence are protected by the rtnl_lock. It is possible that bnxt_open_nic() can be called when the firmware reset sequence is aborting. In that case, bnxt_open_nic() needs to check if the ABORT_ERR flag is set and abort if it is. The configuration change that resulted in the bnxt_open_nic() call will fail but the NIC will be brought to a consistent IF_DOWN state. Without this patch, if bnxt_open_nic() were to continue in this error state, it may crash like this: [ 1648.659736] BUG: unable to handle kernel NULL pointer dereference at (null) [ 1648.659768] IP: [] bnxt_alloc_mem+0x50a/0x1140 [bnxt_en] [ 1648.659796] PGD 101e1b3067 PUD 101e1b2067 PMD 0 [ 1648.659813] Oops: 0000 [#1] SMP [ 1648.659825] Modules linked in: xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack ipt_REJECT nf_reject_ipv4 tun bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter sunrpc dell_smbios dell_wmi_descriptor dcdbas amd64_edac_mod edac_mce_amd kvm_amd kvm irqbypass crc32_pclmul ghash_clmulni_intel aesni_intel lrw gf128mul glue_helper ablk_helper vfat cryptd fat pcspkr ipmi_ssif sg k10temp i2c_piix4 wmi ipmi_si ipmi_devintf ipmi_msghandler tpm_crb acpi_power_meter sch_fq_codel ip_tables xfs libcrc32c sd_mod crc_t10dif crct10dif_generic mgag200 i2c_algo_bit drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ttm ahci drm libahci megaraid_sas crct10dif_pclmul crct10dif_common [ 1648.660063] tg3 libata crc32c_intel bnxt_en(OE) drm_panel_orientation_quirks devlink ptp pps_core dm_mirror dm_region_hash dm_log dm_mod fuse [ 1648.660105] CPU: 13 PID: 3867 Comm: ethtool Kdump: loaded Tainted: G OE ------------ 3.10.0-1152.el7.x86_64 #1 [ 1648.660911] Hardware name: Dell Inc. PowerEdge R7515/0R4CNN, BIOS 1.2.14 01/28/2020 [ 1648.661662] task: ffff94e64cbc9080 ti: ffff94f55df1c000 task.ti: ffff94f55df1c000 [ 1648.662409] RIP: 0010:[] [] bnxt_alloc_mem+0x50a/0x1140 [bnxt_en] [ 1648.663171] RSP: 0018:ffff94f55df1fba8 EFLAGS: 00010202 [ 1648.663927] RAX: 0000000000000000 RBX: ffff94e6827e0000 RCX: 0000000000000000 [ 1648.664684] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff94e6827e08c0 [ 1648.665433] RBP: ffff94f55df1fc20 R08: 00000000000001ff R09: 0000000000000008 [ 1648.666184] R10: 0000000000000d53 R11: ffff94f55df1f7ce R12: ffff94e6827e08c0 [ 1648.666940] R13: ffff94e6827e08c0 R14: ffff94e6827e08c0 R15: ffffffffb9115e40 [ 1648.667695] FS: 00007f8aadba5740(0000) GS:ffff94f57eb40000(0000) knlGS:0000000000000000 [ 1648.668447] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1648.669202] CR2: 0000000000000000 CR3: 0000001022772000 CR4: 0000000000340fe0 [ 1648.669966] Call Trace: [ 1648.670730] [] ? bnxt_need_reserve_rings+0x9d/0x170 [bnxt_en] [ 1648.671496] [] __bnxt_open_nic+0x8a/0x9a0 [bnxt_en] [ 1648.672263] [] ? bnxt_close_nic+0x59/0x1b0 [bnxt_en] [ 1648.673031] [] bnxt_open_nic+0x1b/0x50 [bnxt_en] [ 1648.673793] [] bnxt_set_ringparam+0x6c/0xa0 [bnxt_en] [ 1648.674550] [] dev_ethtool+0x1334/0x21a0 [ 1648.675306] [] dev_ioctl+0x1ef/0x5f0 [ 1648.676061] [] sock_do_ioctl+0x4d/0x60 [ 1648.676810] [] sock_ioctl+0x1eb/0x2d0 [ 1648.677548] [] do_vfs_ioctl+0x3a0/0x5b0 [ 1648.678282] [] ? __do_page_fault+0x238/0x500 [ 1648.679016] [] SyS_ioctl+0xa1/0xc0 [ 1648.679745] [] system_call_fastpath+0x25/0x2a [ 1648.680461] Code: 9e 60 01 00 00 0f 1f 40 00 45 8b 8e 48 01 00 00 31 c9 45 85 c9 0f 8e 73 01 00 00 66 0f 1f 44 00 00 49 8b 86 a8 00 00 00 48 63 d1 <48> 8b 14 d0 48 85 d2 0f 84 46 01 00 00 41 8b 86 44 01 00 00 c7 [ 1648.681986] RIP [] bnxt_alloc_mem+0x50a/0x1140 [bnxt_en] [ 1648.682724] RSP [ 1648.683451] CR2: 0000000000000000 Fixes: ec5d31e3c15d ("bnxt_en: Handle firmware reset status during IF_UP.") Reviewed-by: Vasundhara Volam Reviewed-by: Pavan Chebbi Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 8012386b4a0f..0165f70dba74 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -9779,7 +9779,10 @@ int bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) { int rc = 0; - rc = __bnxt_open_nic(bp, irq_re_init, link_re_init); + if (test_bit(BNXT_STATE_ABORT_ERR, &bp->state)) + rc = -EIO; + if (!rc) + rc = __bnxt_open_nic(bp, irq_re_init, link_re_init); if (rc) { netdev_err(bp->dev, "nic open fail (rc: %x)\n", rc); dev_close(bp->dev); -- cgit v1.2.3 From 825741b071722f1c8ad692cead562c4b5f5eaa93 Mon Sep 17 00:00:00 2001 From: Vasundhara Volam Date: Mon, 26 Oct 2020 00:18:21 -0400 Subject: bnxt_en: Send HWRM_FUNC_RESET fw command unconditionally. In the AER or firmware reset flow, if we are in fatal error state or if pci_channel_offline() is true, we don't send any commands to the firmware because the commands will likely not reach the firmware and most commands don't matter much because the firmware is likely to be reset imminently. However, the HWRM_FUNC_RESET command is different and we should always attempt to send it. In the AER flow for example, the .slot_reset() call will trigger this fw command and we need to try to send it to effect the proper reset. Fixes: b340dc680ed4 ("bnxt_en: Avoid sending firmware messages when AER error is detected.") Reviewed-by: Edwin Peer Signed-off-by: Vasundhara Volam Signed-off-by: Michael Chan Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 0165f70dba74..7975f59735d6 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4352,7 +4352,8 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len, u32 bar_offset = BNXT_GRCPF_REG_CHIMP_COMM; u16 dst = BNXT_HWRM_CHNL_CHIMP; - if (BNXT_NO_FW_ACCESS(bp)) + if (BNXT_NO_FW_ACCESS(bp) && + le16_to_cpu(req->req_type) != HWRM_FUNC_RESET) return -EBUSY; if (msg_len > BNXT_HWRM_MAX_REQ_LEN) { -- cgit v1.2.3 From e3364c5ff3ff975b943a7bf47e21a2a4bf20f3fe Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Fri, 23 Oct 2020 13:15:50 +0800 Subject: net: hns3: Clear the CMDQ registers before unmapping BAR region When unbinding the hns3 driver with the HNS3 VF, I got the following kernel panic: [ 265.709989] Unable to handle kernel paging request at virtual address ffff800054627000 [ 265.717928] Mem abort info: [ 265.720740] ESR = 0x96000047 [ 265.723810] EC = 0x25: DABT (current EL), IL = 32 bits [ 265.729126] SET = 0, FnV = 0 [ 265.732195] EA = 0, S1PTW = 0 [ 265.735351] Data abort info: [ 265.738227] ISV = 0, ISS = 0x00000047 [ 265.742071] CM = 0, WnR = 1 [ 265.745055] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000009b54000 [ 265.751753] [ffff800054627000] pgd=0000202ffffff003, p4d=0000202ffffff003, pud=00002020020eb003, pmd=00000020a0dfc003, pte=0000000000000000 [ 265.764314] Internal error: Oops: 96000047 [#1] SMP [ 265.830357] CPU: 61 PID: 20319 Comm: bash Not tainted 5.9.0+ #206 [ 265.836423] Hardware name: Huawei TaiShan 2280 V2/BC82AMDDA, BIOS 1.05 09/18/2019 [ 265.843873] pstate: 80400009 (Nzcv daif +PAN -UAO -TCO BTYPE=--) [ 265.843890] pc : hclgevf_cmd_uninit+0xbc/0x300 [ 265.861988] lr : hclgevf_cmd_uninit+0xb0/0x300 [ 265.861992] sp : ffff80004c983b50 [ 265.881411] pmr_save: 000000e0 [ 265.884453] x29: ffff80004c983b50 x28: ffff20280bbce500 [ 265.889744] x27: 0000000000000000 x26: 0000000000000000 [ 265.895034] x25: ffff800011a1f000 x24: ffff800011a1fe90 [ 265.900325] x23: ffff0020ce9b00d8 x22: ffff0020ce9b0150 [ 265.905616] x21: ffff800010d70e90 x20: ffff800010d70e90 [ 265.910906] x19: ffff0020ce9b0080 x18: 0000000000000004 [ 265.916198] x17: 0000000000000000 x16: ffff800011ae32e8 [ 265.916201] x15: 0000000000000028 x14: 0000000000000002 [ 265.916204] x13: ffff800011ae32e8 x12: 0000000000012ad8 [ 265.946619] x11: ffff80004c983b50 x10: 0000000000000000 [ 265.951911] x9 : ffff8000115d0888 x8 : 0000000000000000 [ 265.951914] x7 : ffff800011890b20 x6 : c0000000ffff7fff [ 265.951917] x5 : ffff80004c983930 x4 : 0000000000000001 [ 265.951919] x3 : ffffa027eec1b000 x2 : 2b78ccbbff369100 [ 265.964487] x1 : 0000000000000000 x0 : ffff800054627000 [ 265.964491] Call trace: [ 265.964494] hclgevf_cmd_uninit+0xbc/0x300 [ 265.964496] hclgevf_uninit_ae_dev+0x9c/0xe8 [ 265.964501] hnae3_unregister_ae_dev+0xb0/0x130 [ 265.964516] hns3_remove+0x34/0x88 [hns3] [ 266.009683] pci_device_remove+0x48/0xf0 [ 266.009692] device_release_driver_internal+0x114/0x1e8 [ 266.030058] device_driver_detach+0x28/0x38 [ 266.034224] unbind_store+0xd4/0x108 [ 266.037784] drv_attr_store+0x40/0x58 [ 266.041435] sysfs_kf_write+0x54/0x80 [ 266.045081] kernfs_fop_write+0x12c/0x250 [ 266.049076] vfs_write+0xc4/0x248 [ 266.052378] ksys_write+0x74/0xf8 [ 266.055677] __arm64_sys_write+0x24/0x30 [ 266.059584] el0_svc_common.constprop.3+0x84/0x270 [ 266.064354] do_el0_svc+0x34/0xa0 [ 266.067658] el0_svc+0x38/0x40 [ 266.070700] el0_sync_handler+0x8c/0xb0 [ 266.074519] el0_sync+0x140/0x180 It looks like the BAR memory region had already been unmapped before we start clearing CMDQ registers in it, which is pretty bad and the kernel happily kills itself because of a Current EL Data Abort (on arm64). Moving the CMDQ uninitialization a bit early fixes the issue for me. Fixes: 862d969a3a4d ("net: hns3: do VF's pci re-initialization while PF doing FLR") Signed-off-by: Zenghui Yu Link: https://lore.kernel.org/r/20201023051550.793-1-yuzenghui@huawei.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 50c84c5e65d2..c8e3fdd5999c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -3262,8 +3262,8 @@ static void hclgevf_uninit_hdev(struct hclgevf_dev *hdev) hclgevf_uninit_msi(hdev); } - hclgevf_pci_uninit(hdev); hclgevf_cmd_uninit(hdev); + hclgevf_pci_uninit(hdev); hclgevf_uninit_mac_list(hdev); } -- cgit v1.2.3 From 8685699c28d7452ff06d62b4692df985eb7301f0 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Fri, 23 Oct 2020 12:16:28 -0700 Subject: nvme: ignore zone validate errors on subsequent scans Revalidating nvme zoned namespaces requires IO commands, and there are controller states that prevent IO. For example, a sanitize in progress is required to fail all IO, but we don't want to remove a namespace we've previously added just because the controller is in such a state. Suppress the error in this case. Reported-by: Michael Nguyen Signed-off-by: Keith Busch Reviewed-by: Chaitanya Kulkarni Signed-off-by: Christoph Hellwig --- drivers/nvme/host/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 95ef4943d8bd..376096bfc54a 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2125,7 +2125,7 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_id_ns *id) if (blk_queue_is_zoned(ns->queue)) { ret = nvme_revalidate_zones(ns); - if (ret) + if (ret && !nvme_first_scan(ns->disk)) return ret; } -- cgit v1.2.3 From 25c1ca6ecaba3b751d3f7ff92d5cddff3b05f8d0 Mon Sep 17 00:00:00 2001 From: zhenwei pi Date: Sun, 25 Oct 2020 19:51:24 +0800 Subject: nvme-rdma: handle unexpected nvme completion data length Receiving a zero length message leads to the following warnings because the CQE is processed twice: refcount_t: underflow; use-after-free. WARNING: CPU: 0 PID: 0 at lib/refcount.c:28 RIP: 0010:refcount_warn_saturate+0xd9/0xe0 Call Trace: nvme_rdma_recv_done+0xf3/0x280 [nvme_rdma] __ib_process_cq+0x76/0x150 [ib_core] ... Sanity check the received data length, to avoids this. Thanks to Chao Leng & Sagi for suggestions. Signed-off-by: zhenwei pi Reviewed-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- drivers/nvme/host/rdma.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index aad829a2b50d..40a0a3b6476c 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -1768,6 +1768,14 @@ static void nvme_rdma_recv_done(struct ib_cq *cq, struct ib_wc *wc) return; } + /* sanity checking for received data length */ + if (unlikely(wc->byte_len < len)) { + dev_err(queue->ctrl->ctrl.device, + "Unexpected nvme completion length(%d)\n", wc->byte_len); + nvme_rdma_error_recovery(queue->ctrl); + return; + } + ib_dma_sync_single_for_cpu(ibdev, qe->dma, len, DMA_FROM_DEVICE); /* * AEN requests are special as they don't time out and can -- cgit v1.2.3 From caf1cbe3677576ef860f7bbba9074d0e6a0bcabb Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 23 Oct 2020 15:26:04 -0700 Subject: nvme-fc: track error_recovery while connecting Whenever there are errors during CONNECTING, the driver recovers by aborting all outstanding ios and counts on the io completion to fail them and thus the connection/association they are on. However, the connection failure depends on a failure state from the core routines. Not all commands that are issued by the core routine are guaranteed to cause a failure of the core routine. They may be treated as a failure status and the status is then ignored. As such, whenever the transport enters error_recovery while CONNECTING, it will set a new flag indicating an association failed. The create_association routine which creates and initializes the controller, will monitor the state of the flag as well as the core routine error status and ensure the association fails if there was an error. Signed-off-by: James Smart Signed-off-by: Christoph Hellwig --- drivers/nvme/host/fc.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 3c002bdcace3..cfb6ef74c742 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -146,7 +146,8 @@ struct nvme_fc_rport { /* fc_ctrl flags values - specified as bit positions */ #define ASSOC_ACTIVE 0 -#define FCCTRL_TERMIO 1 +#define ASSOC_FAILED 1 +#define FCCTRL_TERMIO 2 struct nvme_fc_ctrl { spinlock_t lock; @@ -2988,6 +2989,8 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) ctrl->cnum, ctrl->lport->localport.port_name, ctrl->rport->remoteport.port_name, ctrl->ctrl.opts->subsysnqn); + clear_bit(ASSOC_FAILED, &ctrl->flags); + /* * Create the admin queue */ @@ -3016,7 +3019,7 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) */ ret = nvme_enable_ctrl(&ctrl->ctrl); - if (ret) + if (ret || test_bit(ASSOC_FAILED, &ctrl->flags)) goto out_disconnect_admin_queue; ctrl->ctrl.max_segments = ctrl->lport->ops->max_sgl_segments; @@ -3026,7 +3029,7 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) blk_mq_unquiesce_queue(ctrl->ctrl.admin_q); ret = nvme_init_identify(&ctrl->ctrl); - if (ret) + if (ret || test_bit(ASSOC_FAILED, &ctrl->flags)) goto out_disconnect_admin_queue; /* sanity checks */ @@ -3071,9 +3074,9 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) ret = nvme_fc_create_io_queues(ctrl); else ret = nvme_fc_recreate_io_queues(ctrl); - if (ret) - goto out_term_aen_ops; } + if (ret || test_bit(ASSOC_FAILED, &ctrl->flags)) + goto out_term_aen_ops; changed = nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_LIVE); @@ -3301,6 +3304,7 @@ __nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl) */ if (ctrl->ctrl.state == NVME_CTRL_CONNECTING) { __nvme_fc_abort_outstanding_ios(ctrl, true); + set_bit(ASSOC_FAILED, &ctrl->flags); return; } -- cgit v1.2.3 From 9c2bb2577d81b1a09f7e342e947986e55cad18e3 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 23 Oct 2020 15:27:50 -0700 Subject: nvme-fc: remove err_work work item err_work was created to handle errors (mainly I/O timeouts) while in CONNECTING state. The flag for err_work_active is also unneeded. Remove err_work_active and err_work. The actions to abort I/Os are moved inline to nvme_error_recovery(). Signed-off-by: James Smart Signed-off-by: Christoph Hellwig --- drivers/nvme/host/fc.c | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index cfb6ef74c742..9cdb116b545b 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -158,7 +158,6 @@ struct nvme_fc_ctrl { u32 cnum; bool ioq_live; - atomic_t err_work_active; u64 association_id; struct nvmefc_ls_rcv_op *rcv_disconn; @@ -168,7 +167,6 @@ struct nvme_fc_ctrl { struct blk_mq_tag_set tag_set; struct delayed_work connect_work; - struct work_struct err_work; struct kref ref; unsigned long flags; @@ -2415,11 +2413,11 @@ nvme_fc_nvme_ctrl_freed(struct nvme_ctrl *nctrl) nvme_fc_ctrl_put(ctrl); } +static void __nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl); + static void nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg) { - int active; - /* * if an error (io timeout, etc) while (re)connecting, * it's an error on creating the new association. @@ -2428,11 +2426,14 @@ nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg) * ios hitting this path before things are cleaned up. */ if (ctrl->ctrl.state == NVME_CTRL_CONNECTING) { - active = atomic_xchg(&ctrl->err_work_active, 1); - if (!active && !queue_work(nvme_fc_wq, &ctrl->err_work)) { - atomic_set(&ctrl->err_work_active, 0); - WARN_ON(1); - } + __nvme_fc_terminate_io(ctrl); + + /* + * Rescheduling the connection after recovering + * from the io error is left to the reconnect work + * item, which is what should have stalled waiting on + * the io that had the error that scheduled this work. + */ return; } @@ -3240,7 +3241,6 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl) { struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl); - cancel_work_sync(&ctrl->err_work); cancel_delayed_work_sync(&ctrl->connect_work); /* * kill the association on the link side. this will block @@ -3351,23 +3351,6 @@ nvme_fc_reset_ctrl_work(struct work_struct *work) ctrl->cnum); } -static void -nvme_fc_connect_err_work(struct work_struct *work) -{ - struct nvme_fc_ctrl *ctrl = - container_of(work, struct nvme_fc_ctrl, err_work); - - __nvme_fc_terminate_io(ctrl); - - atomic_set(&ctrl->err_work_active, 0); - - /* - * Rescheduling the connection after recovering - * from the io error is left to the reconnect work - * item, which is what should have stalled waiting on - * the io that had the error that scheduled this work. - */ -} static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = { .name = "fc", @@ -3495,7 +3478,6 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, ctrl->dev = lport->dev; ctrl->cnum = idx; ctrl->ioq_live = false; - atomic_set(&ctrl->err_work_active, 0); init_waitqueue_head(&ctrl->ioabort_wait); get_device(ctrl->dev); @@ -3503,7 +3485,6 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, INIT_WORK(&ctrl->ctrl.reset_work, nvme_fc_reset_ctrl_work); INIT_DELAYED_WORK(&ctrl->connect_work, nvme_fc_connect_ctrl_work); - INIT_WORK(&ctrl->err_work, nvme_fc_connect_err_work); spin_lock_init(&ctrl->lock); /* io queue count */ @@ -3596,7 +3577,6 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, fail_ctrl: nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING); cancel_work_sync(&ctrl->ctrl.reset_work); - cancel_work_sync(&ctrl->err_work); cancel_delayed_work_sync(&ctrl->connect_work); ctrl->ctrl.opts = NULL; -- cgit v1.2.3 From 95ced8a2c72dd48167adc4981c3c465c491f2f02 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 23 Oct 2020 15:27:51 -0700 Subject: nvme-fc: eliminate terminate_io use by nvme_fc_error_recovery nvme_fc_error_recovery() special cases handling when in CONNECTING state and calls __nvme_fc_terminate_io(). __nvme_fc_terminate_io() itself special cases CONNECTING state and calls the routine to abort outstanding ios. Simplify the sequence by putting the call to abort outstanding I/Os directly in nvme_fc_error_recovery. Move the location of __nvme_fc_abort_outstanding_ios(), and nvme_fc_terminate_exchange() which is called by it, to avoid adding function prototypes for nvme_fc_error_recovery(). Signed-off-by: James Smart Signed-off-by: Christoph Hellwig --- drivers/nvme/host/fc.c | 187 ++++++++++++++++++++++--------------------------- 1 file changed, 84 insertions(+), 103 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 9cdb116b545b..ffbfb0533cac 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2413,27 +2413,97 @@ nvme_fc_nvme_ctrl_freed(struct nvme_ctrl *nctrl) nvme_fc_ctrl_put(ctrl); } -static void __nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl); +/* + * This routine is used by the transport when it needs to find active + * io on a queue that is to be terminated. The transport uses + * blk_mq_tagset_busy_itr() to find the busy requests, which then invoke + * this routine to kill them on a 1 by 1 basis. + * + * As FC allocates FC exchange for each io, the transport must contact + * the LLDD to terminate the exchange, thus releasing the FC exchange. + * After terminating the exchange the LLDD will call the transport's + * normal io done path for the request, but it will have an aborted + * status. The done path will return the io request back to the block + * layer with an error status. + */ +static bool +nvme_fc_terminate_exchange(struct request *req, void *data, bool reserved) +{ + struct nvme_ctrl *nctrl = data; + struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl); + struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(req); + + __nvme_fc_abort_op(ctrl, op); + return true; +} + +/* + * This routine runs through all outstanding commands on the association + * and aborts them. This routine is typically be called by the + * delete_association routine. It is also called due to an error during + * reconnect. In that scenario, it is most likely a command that initializes + * the controller, including fabric Connect commands on io queues, that + * may have timed out or failed thus the io must be killed for the connect + * thread to see the error. + */ +static void +__nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues) +{ + /* + * If io queues are present, stop them and terminate all outstanding + * ios on them. As FC allocates FC exchange for each io, the + * transport must contact the LLDD to terminate the exchange, + * thus releasing the FC exchange. We use blk_mq_tagset_busy_itr() + * to tell us what io's are busy and invoke a transport routine + * to kill them with the LLDD. After terminating the exchange + * the LLDD will call the transport's normal io done path, but it + * will have an aborted status. The done path will return the + * io requests back to the block layer as part of normal completions + * (but with error status). + */ + if (ctrl->ctrl.queue_count > 1) { + nvme_stop_queues(&ctrl->ctrl); + blk_mq_tagset_busy_iter(&ctrl->tag_set, + nvme_fc_terminate_exchange, &ctrl->ctrl); + blk_mq_tagset_wait_completed_request(&ctrl->tag_set); + if (start_queues) + nvme_start_queues(&ctrl->ctrl); + } + + /* + * Other transports, which don't have link-level contexts bound + * to sqe's, would try to gracefully shutdown the controller by + * writing the registers for shutdown and polling (call + * nvme_shutdown_ctrl()). Given a bunch of i/o was potentially + * just aborted and we will wait on those contexts, and given + * there was no indication of how live the controlelr is on the + * link, don't send more io to create more contexts for the + * shutdown. Let the controller fail via keepalive failure if + * its still present. + */ + + /* + * clean up the admin queue. Same thing as above. + */ + blk_mq_quiesce_queue(ctrl->ctrl.admin_q); + blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, + nvme_fc_terminate_exchange, &ctrl->ctrl); + blk_mq_tagset_wait_completed_request(&ctrl->admin_tag_set); +} static void nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg) { /* - * if an error (io timeout, etc) while (re)connecting, - * it's an error on creating the new association. - * Start the error recovery thread if it hasn't already - * been started. It is expected there could be multiple - * ios hitting this path before things are cleaned up. + * if an error (io timeout, etc) while (re)connecting, the remote + * port requested terminating of the association (disconnect_ls) + * or an error (timeout or abort) occurred on an io while creating + * the controller. Abort any ios on the association and let the + * create_association error path resolve things. */ if (ctrl->ctrl.state == NVME_CTRL_CONNECTING) { - __nvme_fc_terminate_io(ctrl); - - /* - * Rescheduling the connection after recovering - * from the io error is left to the reconnect work - * item, which is what should have stalled waiting on - * the io that had the error that scheduled this work. - */ + __nvme_fc_abort_outstanding_ios(ctrl, true); + set_bit(ASSOC_FAILED, &ctrl->flags); return; } @@ -2747,30 +2817,6 @@ nvme_fc_complete_rq(struct request *rq) nvme_fc_ctrl_put(ctrl); } -/* - * This routine is used by the transport when it needs to find active - * io on a queue that is to be terminated. The transport uses - * blk_mq_tagset_busy_itr() to find the busy requests, which then invoke - * this routine to kill them on a 1 by 1 basis. - * - * As FC allocates FC exchange for each io, the transport must contact - * the LLDD to terminate the exchange, thus releasing the FC exchange. - * After terminating the exchange the LLDD will call the transport's - * normal io done path for the request, but it will have an aborted - * status. The done path will return the io request back to the block - * layer with an error status. - */ -static bool -nvme_fc_terminate_exchange(struct request *req, void *data, bool reserved) -{ - struct nvme_ctrl *nctrl = data; - struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl); - struct nvme_fc_fcp_op *op = blk_mq_rq_to_pdu(req); - - __nvme_fc_abort_op(ctrl, op); - return true; -} - static const struct blk_mq_ops nvme_fc_mq_ops = { .queue_rq = nvme_fc_queue_rq, @@ -3111,60 +3157,6 @@ out_free_queue: } -/* - * This routine runs through all outstanding commands on the association - * and aborts them. This routine is typically be called by the - * delete_association routine. It is also called due to an error during - * reconnect. In that scenario, it is most likely a command that initializes - * the controller, including fabric Connect commands on io queues, that - * may have timed out or failed thus the io must be killed for the connect - * thread to see the error. - */ -static void -__nvme_fc_abort_outstanding_ios(struct nvme_fc_ctrl *ctrl, bool start_queues) -{ - /* - * If io queues are present, stop them and terminate all outstanding - * ios on them. As FC allocates FC exchange for each io, the - * transport must contact the LLDD to terminate the exchange, - * thus releasing the FC exchange. We use blk_mq_tagset_busy_itr() - * to tell us what io's are busy and invoke a transport routine - * to kill them with the LLDD. After terminating the exchange - * the LLDD will call the transport's normal io done path, but it - * will have an aborted status. The done path will return the - * io requests back to the block layer as part of normal completions - * (but with error status). - */ - if (ctrl->ctrl.queue_count > 1) { - nvme_stop_queues(&ctrl->ctrl); - blk_mq_tagset_busy_iter(&ctrl->tag_set, - nvme_fc_terminate_exchange, &ctrl->ctrl); - blk_mq_tagset_wait_completed_request(&ctrl->tag_set); - if (start_queues) - nvme_start_queues(&ctrl->ctrl); - } - - /* - * Other transports, which don't have link-level contexts bound - * to sqe's, would try to gracefully shutdown the controller by - * writing the registers for shutdown and polling (call - * nvme_shutdown_ctrl()). Given a bunch of i/o was potentially - * just aborted and we will wait on those contexts, and given - * there was no indication of how live the controlelr is on the - * link, don't send more io to create more contexts for the - * shutdown. Let the controller fail via keepalive failure if - * its still present. - */ - - /* - * clean up the admin queue. Same thing as above. - */ - blk_mq_quiesce_queue(ctrl->ctrl.admin_q); - blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, - nvme_fc_terminate_exchange, &ctrl->ctrl); - blk_mq_tagset_wait_completed_request(&ctrl->admin_tag_set); -} - /* * This routine stops operation of the controller on the host side. * On the host os stack side: Admin and IO queues are stopped, @@ -3297,17 +3289,6 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status) static void __nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl) { - /* - * if state is CONNECTING - the error occurred as part of a - * reconnect attempt. Abort any ios on the association and - * let the create_association error paths resolve things. - */ - if (ctrl->ctrl.state == NVME_CTRL_CONNECTING) { - __nvme_fc_abort_outstanding_ios(ctrl, true); - set_bit(ASSOC_FAILED, &ctrl->flags); - return; - } - /* * For any other state, kill the association. As this routine * is a common io abort routine for resetting and such, after -- cgit v1.2.3 From ac9b820e713bdc8f340e7d65ca6a19b10b790f2a Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 23 Oct 2020 15:27:52 -0700 Subject: nvme-fc: remove nvme_fc_terminate_io() __nvme_fc_terminate_io() is now called by only 1 place, in reset_work. Consoldate and move the functionality of terminate_io into reset_work. In reset_work, rather than calling the create_association directly, schedule the connect work element to do its thing. After scheduling, flush the connect work element to continue with semantic of not returning until connect has been attempted at least once. Signed-off-by: James Smart Signed-off-by: Christoph Hellwig --- drivers/nvme/host/fc.c | 49 ++++++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index ffbfb0533cac..f4c246462658 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -3287,49 +3287,32 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status) } static void -__nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl) +nvme_fc_reset_ctrl_work(struct work_struct *work) { - /* - * For any other state, kill the association. As this routine - * is a common io abort routine for resetting and such, after - * the association is terminated, ensure that the state is set - * to CONNECTING. - */ + struct nvme_fc_ctrl *ctrl = + container_of(work, struct nvme_fc_ctrl, ctrl.reset_work); - nvme_stop_keep_alive(&ctrl->ctrl); + nvme_stop_ctrl(&ctrl->ctrl); /* will block will waiting for io to terminate */ nvme_fc_delete_association(ctrl); - if (ctrl->ctrl.state != NVME_CTRL_CONNECTING && - !nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) + if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) dev_err(ctrl->ctrl.device, "NVME-FC{%d}: error_recovery: Couldn't change state " "to CONNECTING\n", ctrl->cnum); -} - -static void -nvme_fc_reset_ctrl_work(struct work_struct *work) -{ - struct nvme_fc_ctrl *ctrl = - container_of(work, struct nvme_fc_ctrl, ctrl.reset_work); - int ret; - - __nvme_fc_terminate_io(ctrl); - nvme_stop_ctrl(&ctrl->ctrl); - - if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE) - ret = nvme_fc_create_association(ctrl); - else - ret = -ENOTCONN; - - if (ret) - nvme_fc_reconnect_or_delete(ctrl, ret); - else - dev_info(ctrl->ctrl.device, - "NVME-FC{%d}: controller reset complete\n", - ctrl->cnum); + if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE) { + if (!queue_delayed_work(nvme_wq, &ctrl->connect_work, 0)) { + dev_err(ctrl->ctrl.device, + "NVME-FC{%d}: failed to schedule connect " + "after reset\n", ctrl->cnum); + } else { + flush_delayed_work(&ctrl->connect_work); + } + } else { + nvme_fc_reconnect_or_delete(ctrl, -ENOTCONN); + } } -- cgit v1.2.3 From 3c3751f2daf6675f6b5bee83b792354c272f5bd2 Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Thu, 22 Oct 2020 16:58:21 -0700 Subject: nvmet: fix a NULL pointer dereference when tracing the flush command When target side trace in turned on and flush command is issued from the host it results in the following Oops. [ 856.789724] BUG: kernel NULL pointer dereference, address: 0000000000000068 [ 856.790686] #PF: supervisor read access in kernel mode [ 856.791262] #PF: error_code(0x0000) - not-present page [ 856.791863] PGD 6d7110067 P4D 6d7110067 PUD 66f0ad067 PMD 0 [ 856.792527] Oops: 0000 [#1] SMP NOPTI [ 856.792950] CPU: 15 PID: 7034 Comm: nvme Tainted: G OE 5.9.0nvme-5.9+ #71 [ 856.793790] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba5276e3214 [ 856.794956] RIP: 0010:trace_event_raw_event_nvmet_req_init+0x13e/0x170 [nvmet] [ 856.795734] Code: 41 5c 41 5d c3 31 d2 31 f6 e8 4e 9b b8 e0 e9 0e ff ff ff 49 8b 55 00 48 8b 38 8b 0 [ 856.797740] RSP: 0018:ffffc90001be3a60 EFLAGS: 00010246 [ 856.798375] RAX: 0000000000000000 RBX: ffff8887e7d2c01c RCX: 0000000000000000 [ 856.799234] RDX: 0000000000000020 RSI: 0000000057e70ea2 RDI: ffff8887e7d2c034 [ 856.800088] RBP: ffff88869f710578 R08: ffff888807500d40 R09: 00000000fffffffe [ 856.800951] R10: 0000000064c66670 R11: 00000000ef955201 R12: ffff8887e7d2c034 [ 856.801807] R13: ffff88869f7105c8 R14: 0000000000000040 R15: ffff88869f710440 [ 856.802667] FS: 00007f6a22bd8780(0000) GS:ffff888813a00000(0000) knlGS:0000000000000000 [ 856.803635] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 856.804367] CR2: 0000000000000068 CR3: 00000006d73e0000 CR4: 00000000003506e0 [ 856.805283] Call Trace: [ 856.805613] nvmet_req_init+0x27c/0x480 [nvmet] [ 856.806200] nvme_loop_queue_rq+0xcb/0x1d0 [nvme_loop] [ 856.806862] blk_mq_dispatch_rq_list+0x123/0x7b0 [ 856.807459] ? kvm_sched_clock_read+0x14/0x30 [ 856.808025] __blk_mq_sched_dispatch_requests+0xc7/0x170 [ 856.808708] blk_mq_sched_dispatch_requests+0x30/0x60 [ 856.809372] __blk_mq_run_hw_queue+0x70/0x100 [ 856.809935] __blk_mq_delay_run_hw_queue+0x156/0x170 [ 856.810574] blk_mq_run_hw_queue+0x86/0xe0 [ 856.811104] blk_mq_sched_insert_request+0xef/0x160 [ 856.811733] blk_execute_rq+0x69/0xc0 [ 856.812212] ? blk_mq_rq_ctx_init+0xd0/0x230 [ 856.812784] nvme_execute_passthru_rq+0x57/0x130 [nvme_core] [ 856.813461] nvme_submit_user_cmd+0xeb/0x300 [nvme_core] [ 856.814099] nvme_user_cmd.isra.82+0x11e/0x1a0 [nvme_core] [ 856.814752] blkdev_ioctl+0x1dc/0x2c0 [ 856.815197] block_ioctl+0x3f/0x50 [ 856.815606] __x64_sys_ioctl+0x84/0xc0 [ 856.816074] do_syscall_64+0x33/0x40 [ 856.816533] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 856.817168] RIP: 0033:0x7f6a222ed107 [ 856.817617] Code: 44 00 00 48 8b 05 81 cd 2c 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 8 [ 856.819901] RSP: 002b:00007ffca848f058 EFLAGS: 00000202 ORIG_RAX: 0000000000000010 [ 856.820846] RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 00007f6a222ed107 [ 856.821726] RDX: 00007ffca848f060 RSI: 00000000c0484e43 RDI: 0000000000000003 [ 856.822603] RBP: 0000000000000003 R08: 000000000000003f R09: 0000000000000005 [ 856.823478] R10: 00007ffca848ece0 R11: 0000000000000202 R12: 00007ffca84912d3 [ 856.824359] R13: 00007ffca848f4d0 R14: 0000000000000002 R15: 000000000067e900 [ 856.825236] Modules linked in: nvme_loop(OE) nvmet(OE) nvme_fabrics(OE) null_blk nvme(OE) nvme_corel Move the nvmet_req_init() tracepoint after we parse the command in nvmet_req_init() so that we can get rid of the duplicate nvmet_find_namespace() call. Rename __assign_disk_name() -> __assign_req_name(). Now that we call tracepoint after parsing the command simplify the newly added __assign_req_name() which fixes this bug. Signed-off-by: Chaitanya Kulkarni Signed-off-by: Christoph Hellwig --- drivers/nvme/target/core.c | 4 ++-- drivers/nvme/target/trace.h | 21 +++++++-------------- 2 files changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index aafcbc424b7a..957b39a82431 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -907,8 +907,6 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq, req->error_loc = NVMET_NO_ERROR_LOC; req->error_slba = 0; - trace_nvmet_req_init(req, req->cmd); - /* no support for fused commands yet */ if (unlikely(flags & (NVME_CMD_FUSE_FIRST | NVME_CMD_FUSE_SECOND))) { req->error_loc = offsetof(struct nvme_common_command, flags); @@ -938,6 +936,8 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq, if (status) goto fail; + trace_nvmet_req_init(req, req->cmd); + if (unlikely(!percpu_ref_tryget_live(&sq->ref))) { status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; goto fail; diff --git a/drivers/nvme/target/trace.h b/drivers/nvme/target/trace.h index 0458046d6501..c14e3249a14d 100644 --- a/drivers/nvme/target/trace.h +++ b/drivers/nvme/target/trace.h @@ -46,19 +46,12 @@ static inline struct nvmet_ctrl *nvmet_req_to_ctrl(struct nvmet_req *req) return req->sq->ctrl; } -static inline void __assign_disk_name(char *name, struct nvmet_req *req, - bool init) +static inline void __assign_req_name(char *name, struct nvmet_req *req) { - struct nvmet_ctrl *ctrl = nvmet_req_to_ctrl(req); - struct nvmet_ns *ns; - - if ((init && req->sq->qid) || (!init && req->cq->qid)) { - ns = nvmet_find_namespace(ctrl, req->cmd->rw.nsid); - strncpy(name, ns->device_path, DISK_NAME_LEN); - return; - } - - memset(name, 0, DISK_NAME_LEN); + if (req->ns) + strncpy(name, req->ns->device_path, DISK_NAME_LEN); + else + memset(name, 0, DISK_NAME_LEN); } #endif @@ -81,7 +74,7 @@ TRACE_EVENT(nvmet_req_init, TP_fast_assign( __entry->cmd = cmd; __entry->ctrl = nvmet_req_to_ctrl(req); - __assign_disk_name(__entry->disk, req, true); + __assign_req_name(__entry->disk, req); __entry->qid = req->sq->qid; __entry->cid = cmd->common.command_id; __entry->opcode = cmd->common.opcode; @@ -121,7 +114,7 @@ TRACE_EVENT(nvmet_req_complete, __entry->cid = req->cqe->command_id; __entry->result = le64_to_cpu(req->cqe->result.u64); __entry->status = le16_to_cpu(req->cqe->status) >> 1; - __assign_disk_name(__entry->disk, req, false); + __assign_req_name(__entry->disk, req); ), TP_printk("nvmet%s: %sqid=%d, cmdid=%u, res=%#llx, status=%#x", __print_ctrl_name(__entry->ctrl), -- cgit v1.2.3 From 444d03badc72e246bbcbca4b9c922d70ef046934 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 27 Oct 2020 10:51:14 +0100 Subject: drm: kernel-doc: document drm_dp_set_subconnector_property() params Changeset e5b92773287c ("drm: report dp downstream port type as a subconnector property") added a new function to the kAPI, but didn't add any documentation for the parameters for drm_dp_set_subconnector_property(). Fixes: e5b92773287c ("drm: report dp downstream port type as a subconnector property") Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/0870be85a77bea4ba5cf1715010834289a4e10b1.1603791716.git.mchehab+huawei@kernel.org --- drivers/gpu/drm/drm_dp_helper.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 90807a6b415c..b1c71af88579 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -1028,7 +1028,8 @@ EXPORT_SYMBOL(drm_dp_downstream_debug); /** * drm_dp_subconnector_type() - get DP branch device type - * + * @dpcd: DisplayPort configuration data + * @port_cap: port capabilities */ enum drm_mode_subconnector drm_dp_subconnector_type(const u8 dpcd[DP_RECEIVER_CAP_SIZE], @@ -1079,6 +1080,10 @@ EXPORT_SYMBOL(drm_dp_subconnector_type); /** * drm_mode_set_dp_subconnector_property - set subconnector for DP connector + * @connector: connector to set property on + * @status: connector status + * @dpcd: DisplayPort configuration data + * @port_cap: port capabilities * * Called by a driver on every detect event. */ -- cgit v1.2.3 From 21a53bbd469b6df21c13f3a5aee1b935f032c03c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 27 Oct 2020 10:51:15 +0100 Subject: drm/dp: fix kernel-doc warnings at drm_dp_helper.c As warned by kernel-doc: ./drivers/gpu/drm/drm_dp_helper.c:385: warning: Function parameter or member 'type' not described in 'drm_dp_downstream_is_type' ./drivers/gpu/drm/drm_dp_helper.c:886: warning: Function parameter or member 'dev' not described in 'drm_dp_downstream_mode' Some function parameters weren't documented. Fixes: 38784f6f8805 ("drm/dp: Add helpers to identify downstream facing port types") Reviewed-by: Lyude Paul Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/03c9c8ba3f492aca76e2b4836803219cd9c971cf.1603791716.git.mchehab+huawei@kernel.org --- drivers/gpu/drm/drm_dp_helper.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index b1c71af88579..deeed73f4ed6 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -374,6 +374,10 @@ static bool is_edid_digital_input_dp(const struct edid *edid) * drm_dp_downstream_is_type() - is the downstream facing port of certain type? * @dpcd: DisplayPort configuration data * @port_cap: port capabilities + * @type: port type to be checked. Can be: + * %DP_DS_PORT_TYPE_DP, %DP_DS_PORT_TYPE_VGA, %DP_DS_PORT_TYPE_DVI, + * %DP_DS_PORT_TYPE_HDMI, %DP_DS_PORT_TYPE_NON_EDID, + * %DP_DS_PORT_TYPE_DP_DUALMODE or %DP_DS_PORT_TYPE_WIRELESS. * * Caveat: Only works with DPCD 1.1+ port caps. * @@ -870,6 +874,7 @@ EXPORT_SYMBOL(drm_dp_downstream_444_to_420_conversion); /** * drm_dp_downstream_mode() - return a mode for downstream facing port + * @dev: DRM device * @dpcd: DisplayPort configuration data * @port_cap: port capabilities * -- cgit v1.2.3 From 8d7d8c0afbeae253a2bc0a304ebbd21fa980467f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 27 Oct 2020 10:51:16 +0100 Subject: drm/dp: fix a kernel-doc issue at drm_edid.c The name of the argument is different, causing those warnings: ./drivers/gpu/drm/drm_edid.c:3754: warning: Function parameter or member 'video_code' not described in 'drm_display_mode_from_cea_vic' ./drivers/gpu/drm/drm_edid.c:3754: warning: Excess function parameter 'vic' description in 'drm_display_mode_from_cea_vic' Fixes: 7af655bce275 ("drm/dp: Add drm_dp_downstream_mode()") Reviewed-by: Lyude Paul Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/7f4d6c3ff6df63ebd006eb90a5108006c23e2168.1603791716.git.mchehab+huawei@kernel.org --- drivers/gpu/drm/drm_edid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a82f37d44258..631125b46e04 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3741,7 +3741,7 @@ drm_add_cmdb_modes(struct drm_connector *connector, u8 svd) /** * drm_display_mode_from_cea_vic() - return a mode for CEA VIC * @dev: DRM device - * @vic: CEA VIC of the mode + * @video_code: CEA VIC of the mode * * Creates a new mode matching the specified CEA VIC. * -- cgit v1.2.3 From 7811a339da2e3b741dc19de50bad5a7728b6ef22 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 27 Oct 2020 10:51:29 +0100 Subject: drm: kernel-doc: add description for a new function parameter As reported by "make htmldocs": ./drivers/gpu/drm/drm_prime.c:808: warning: Function parameter or member 'dev' not described in 'drm_prime_pages_to_sg' Add a description for the new parameter. Fixes: 707d561f77b5 ("drm: allow limiting the scatter list size.") Signed-off-by: Mauro Carvalho Chehab Acked-by: Gerd Hoffmann Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/9366f48e6e9c3ec2f31a3e68452a2b23a1089fce.1603791716.git.mchehab+huawei@kernel.org --- drivers/gpu/drm/drm_prime.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index d6808f678db5..9f955f2010c2 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -794,6 +794,7 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { /** * drm_prime_pages_to_sg - converts a page array into an sg list + * @dev: DRM device * @pages: pointer to the array of page pointers to convert * @nr_pages: length of the page vector * -- cgit v1.2.3 From 179f5dc36b0a1aa31538d7d8823deb65c39847b3 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 16 Oct 2020 12:18:57 +0200 Subject: staging: octeon: repair "fixed-link" support The PHYs must be registered once in device probe function, not in device open callback because it's only possible to register them once. Fixes: a25e278020bf ("staging: octeon: support fixed-link phys") Signed-off-by: Alexander Sverdlin Cc: stable Link: https://lore.kernel.org/r/20201016101858.11374-1-alexander.sverdlin@nokia.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/ethernet-mdio.c | 6 ------ drivers/staging/octeon/ethernet.c | 9 +++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-mdio.c b/drivers/staging/octeon/ethernet-mdio.c index cfb673a52b25..0bf545849b11 100644 --- a/drivers/staging/octeon/ethernet-mdio.c +++ b/drivers/staging/octeon/ethernet-mdio.c @@ -147,12 +147,6 @@ int cvm_oct_phy_setup_device(struct net_device *dev) phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0); if (!phy_node && of_phy_is_fixed_link(priv->of_node)) { - int rc; - - rc = of_phy_register_fixed_link(priv->of_node); - if (rc) - return rc; - phy_node = of_node_get(priv->of_node); } if (!phy_node) diff --git a/drivers/staging/octeon/ethernet.c b/drivers/staging/octeon/ethernet.c index 204f0b1e2739..5dea6e96ec90 100644 --- a/drivers/staging/octeon/ethernet.c +++ b/drivers/staging/octeon/ethernet.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -892,6 +893,14 @@ static int cvm_oct_probe(struct platform_device *pdev) break; } + if (priv->of_node && of_phy_is_fixed_link(priv->of_node)) { + if (of_phy_register_fixed_link(priv->of_node)) { + netdev_err(dev, "Failed to register fixed link for interface %d, port %d\n", + interface, priv->port); + dev->netdev_ops = NULL; + } + } + if (!dev->netdev_ops) { free_netdev(dev); } else if (register_netdev(dev) < 0) { -- cgit v1.2.3 From 49d28ebdf1e30d806410eefc7de0a7a1ca5d747c Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 16 Oct 2020 16:56:30 +0200 Subject: staging: octeon: Drop on uncorrectable alignment or FCS error Currently in case of alignment or FCS error if the packet cannot be corrected it's still not dropped. Report the error properly and drop the packet while making the code around a little bit more readable. Fixes: 80ff0fd3ab64 ("Staging: Add octeon-ethernet driver files.") Signed-off-by: Alexander Sverdlin Cc: stable Link: https://lore.kernel.org/r/20201016145630.41852-1-alexander.sverdlin@nokia.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/octeon/ethernet-rx.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index 2c16230f993c..9ebd665e5d42 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -69,15 +69,17 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) else port = work->word1.cn38xx.ipprt; - if ((work->word2.snoip.err_code == 10) && (work->word1.len <= 64)) { + if ((work->word2.snoip.err_code == 10) && (work->word1.len <= 64)) /* * Ignore length errors on min size packets. Some * equipment incorrectly pads packets to 64+4FCS * instead of 60+4FCS. Note these packets still get * counted as frame errors. */ - } else if (work->word2.snoip.err_code == 5 || - work->word2.snoip.err_code == 7) { + return 0; + + if (work->word2.snoip.err_code == 5 || + work->word2.snoip.err_code == 7) { /* * We received a packet with either an alignment error * or a FCS error. This may be signalling that we are @@ -108,7 +110,10 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) /* Port received 0xd5 preamble */ work->packet_ptr.s.addr += i + 1; work->word1.len -= i + 5; - } else if ((*ptr & 0xf) == 0xd) { + return 0; + } + + if ((*ptr & 0xf) == 0xd) { /* Port received 0xd preamble */ work->packet_ptr.s.addr += i; work->word1.len -= i + 4; @@ -118,21 +123,20 @@ static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) ((*(ptr + 1) & 0xf) << 4); ptr++; } - } else { - printk_ratelimited("Port %d unknown preamble, packet dropped\n", - port); - cvm_oct_free_work(work); - return 1; + return 0; } + + printk_ratelimited("Port %d unknown preamble, packet dropped\n", + port); + cvm_oct_free_work(work); + return 1; } - } else { - printk_ratelimited("Port %d receive error code %d, packet dropped\n", - port, work->word2.snoip.err_code); - cvm_oct_free_work(work); - return 1; } - return 0; + printk_ratelimited("Port %d receive error code %d, packet dropped\n", + port, work->word2.snoip.err_code); + cvm_oct_free_work(work); + return 1; } static void copy_segments_to_skb(struct cvmx_wqe *work, struct sk_buff *skb) -- cgit v1.2.3 From 647a6002cb41d358d9ac5de101a8a6dc74748a59 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 21 Oct 2020 13:21:42 +0100 Subject: staging: comedi: cb_pcidas: Allow 2-channel commands for AO subdevice The "cb_pcidas" driver supports asynchronous commands on the analog output (AO) subdevice for those boards that have an AO FIFO. The code (in `cb_pcidas_ao_check_chanlist()` and `cb_pcidas_ao_cmd()`) to validate and set up the command supports output to a single channel or to two channels simultaneously (the boards have two AO channels). However, the code in `cb_pcidas_auto_attach()` that initializes the subdevices neglects to initialize the AO subdevice's `len_chanlist` member, leaving it set to 0, but the Comedi core will "correct" it to 1 if the driver neglected to set it. This limits commands to use a single channel (either channel 0 or 1), but the limit should be two channels. Set the AO subdevice's `len_chanlist` member to be the same value as the `n_chan` member, which will be 2. Cc: Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20201021122142.81628-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/cb_pcidas.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/staging/comedi/drivers/cb_pcidas.c b/drivers/staging/comedi/drivers/cb_pcidas.c index 48ec2ee953dc..d740c4782775 100644 --- a/drivers/staging/comedi/drivers/cb_pcidas.c +++ b/drivers/staging/comedi/drivers/cb_pcidas.c @@ -1342,6 +1342,7 @@ static int cb_pcidas_auto_attach(struct comedi_device *dev, if (dev->irq && board->has_ao_fifo) { dev->write_subdev = s; s->subdev_flags |= SDF_CMD_WRITE; + s->len_chanlist = s->n_chan; s->do_cmdtest = cb_pcidas_ao_cmdtest; s->do_cmd = cb_pcidas_ao_cmd; s->cancel = cb_pcidas_ao_cancel; -- cgit v1.2.3 From b6ae84d648954fae096d94faea1ddb6518b27841 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Mon, 26 Oct 2020 18:55:50 +0900 Subject: staging: mmal-vchiq: Fix memory leak for vchiq_instance The vchiq_instance is allocated with vchiq_initialise() but never freed properly. Fix memory leak for the vchiq_instance. Reported-by: Jaehoon Chung Signed-off-by: Seung-Woo Kim Link: https://lore.kernel.org/r/1603706150-10806-1-git-send-email-sw0312.kim@samsung.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 3a4202551cfc..9097bcbd67d8 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -179,6 +179,9 @@ struct vchiq_mmal_instance { /* ordered workqueue to process all bulk operations */ struct workqueue_struct *bulk_wq; + + /* handle for a vchiq instance */ + struct vchiq_instance *vchiq_instance; }; static struct mmal_msg_context * @@ -1840,6 +1843,7 @@ int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) mutex_unlock(&instance->vchiq_mutex); + vchiq_shutdown(instance->vchiq_instance); flush_workqueue(instance->bulk_wq); destroy_workqueue(instance->bulk_wq); @@ -1856,6 +1860,7 @@ EXPORT_SYMBOL_GPL(vchiq_mmal_finalise); int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) { int status; + int err = -ENODEV; struct vchiq_mmal_instance *instance; static struct vchiq_instance *vchiq_instance; struct vchiq_service_params_kernel params = { @@ -1890,17 +1895,21 @@ int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) status = vchiq_connect(vchiq_instance); if (status) { pr_err("Failed to connect VCHI instance (status=%d)\n", status); - return -EIO; + err = -EIO; + goto err_shutdown_vchiq; } instance = kzalloc(sizeof(*instance), GFP_KERNEL); - if (!instance) - return -ENOMEM; + if (!instance) { + err = -ENOMEM; + goto err_shutdown_vchiq; + } mutex_init(&instance->vchiq_mutex); instance->bulk_scratch = vmalloc(PAGE_SIZE); + instance->vchiq_instance = vchiq_instance; mutex_init(&instance->context_map_lock); idr_init_base(&instance->context_map, 1); @@ -1932,7 +1941,9 @@ err_close_services: err_free: vfree(instance->bulk_scratch); kfree(instance); - return -ENODEV; +err_shutdown_vchiq: + vchiq_shutdown(vchiq_instance); + return err; } EXPORT_SYMBOL_GPL(vchiq_mmal_init); -- cgit v1.2.3 From d3a85737521a8570adf3a8171b4b3e42216686ec Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Mon, 19 Oct 2020 18:06:03 +0200 Subject: staging: wfx: fix use of uninitialized pointer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With -Wuninitialized, the compiler complains: drivers/staging/wfx/data_tx.c:34:19: warning: variable 'band' is uninitialized when used here [-Wuninitialized] if (rate->idx >= band->n_bitrates) { ^~~~ Reported-by: kernel test robot Reported-by: Nathan Chancellor Fixes: 868fd970e187 ("staging: wfx: improve robustness of wfx_get_hw_rate()") Signed-off-by: Jérôme Pouiller Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20201019160604.1609180-1-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/data_tx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/wfx/data_tx.c b/drivers/staging/wfx/data_tx.c index 41f6a604a697..36b36ef39d05 100644 --- a/drivers/staging/wfx/data_tx.c +++ b/drivers/staging/wfx/data_tx.c @@ -31,13 +31,13 @@ static int wfx_get_hw_rate(struct wfx_dev *wdev, } return rate->idx + 14; } + // WFx only support 2GHz, else band information should be retrieved + // from ieee80211_tx_info + band = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]; if (rate->idx >= band->n_bitrates) { WARN(1, "wrong rate->idx value: %d", rate->idx); return -1; } - // WFx only support 2GHz, else band information should be retrieved - // from ieee80211_tx_info - band = wdev->hw->wiphy->bands[NL80211_BAND_2GHZ]; return band->bitrates[rate->idx].hw_value; } -- cgit v1.2.3 From df5fbca677c916d1959f814b41b841b65e450027 Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Mon, 19 Oct 2020 18:06:04 +0200 Subject: staging: wfx: fix test on return value of gpiod_get_value() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit 8522d62e6bca ("staging: wfx: gpiod_get_value() can return an error") has changed the way the driver test the value returned by gpiod_get_value(). The new code was wrong. Fixes: 8522d62e6bca ("staging: wfx: gpiod_get_value() can return an error") Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20201019160604.1609180-2-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wfx/bh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c index 2ffa587aefaa..ed53d0b45592 100644 --- a/drivers/staging/wfx/bh.c +++ b/drivers/staging/wfx/bh.c @@ -21,7 +21,7 @@ static void device_wakeup(struct wfx_dev *wdev) if (!wdev->pdata.gpio_wakeup) return; - if (gpiod_get_value_cansleep(wdev->pdata.gpio_wakeup) >= 0) + if (gpiod_get_value_cansleep(wdev->pdata.gpio_wakeup) > 0) return; if (wfx_api_older_than(wdev, 1, 4)) { -- cgit v1.2.3 From 7e97e4cbf30026b49b0145c3bfe06087958382c5 Mon Sep 17 00:00:00 2001 From: Jing Xiangfeng Date: Mon, 12 Oct 2020 21:24:04 +0800 Subject: staging: fieldbus: anybuss: jump to correct label in an error path In current code, controller_probe() misses to call ida_simple_remove() in an error path. Jump to correct label to fix it. Fixes: 17614978ed34 ("staging: fieldbus: anybus-s: support the Arcx anybus controller") Reviewed-by: Sven Van Asbroeck Signed-off-by: Jing Xiangfeng Cc: stable Link: https://lore.kernel.org/r/20201012132404.113031-1-jingxiangfeng@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/staging/fieldbus/anybuss/arcx-anybus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/fieldbus/anybuss/arcx-anybus.c b/drivers/staging/fieldbus/anybuss/arcx-anybus.c index 5b8d0bae9ff3..b5fded15e8a6 100644 --- a/drivers/staging/fieldbus/anybuss/arcx-anybus.c +++ b/drivers/staging/fieldbus/anybuss/arcx-anybus.c @@ -293,7 +293,7 @@ static int controller_probe(struct platform_device *pdev) regulator = devm_regulator_register(dev, &can_power_desc, &config); if (IS_ERR(regulator)) { err = PTR_ERR(regulator); - goto out_reset; + goto out_ida; } /* make controller info visible to userspace */ cd->class_dev = kzalloc(sizeof(*cd->class_dev), GFP_KERNEL); -- cgit v1.2.3 From fce52ad3483281930dc9215a68d69837f219f889 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 27 Oct 2020 16:34:18 +0300 Subject: interconnect: qcom: use icc_sync state for sm8[12]50 In addition to the rest of Qcom interconnect drivers use icc_sync_state for SM8150/SM8250 interconnect drivers to notify the interconnect framework when all consumers are probed and there is no need to keep the bandwidth set to maximum anymore. Also move the BCM initialization before creating the nodes to set the max bandwidth in hardware for the initialization/probing stage. Signed-off-by: Dmitry Baryshkov Fixes: 7d3b0b0d8184 ("interconnect: qcom: Use icc_sync_state") Link: https://lore.kernel.org/r/20201027133418.976687-1-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sm8150.c | 7 ++++--- drivers/interconnect/qcom/sm8250.c | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/interconnect/qcom/sm8150.c b/drivers/interconnect/qcom/sm8150.c index 9218efed04a0..c76b2c7f9b10 100644 --- a/drivers/interconnect/qcom/sm8150.c +++ b/drivers/interconnect/qcom/sm8150.c @@ -551,6 +551,9 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + for (i = 0; i < num_nodes; i++) { size_t j; @@ -574,9 +577,6 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - platform_set_drvdata(pdev, qp); return 0; @@ -627,6 +627,7 @@ static struct platform_driver qnoc_driver = { .driver = { .name = "qnoc-sm8150", .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, }, }; module_platform_driver(qnoc_driver); diff --git a/drivers/interconnect/qcom/sm8250.c b/drivers/interconnect/qcom/sm8250.c index 9b58946f7898..cc558fec74e3 100644 --- a/drivers/interconnect/qcom/sm8250.c +++ b/drivers/interconnect/qcom/sm8250.c @@ -567,6 +567,9 @@ static int qnoc_probe(struct platform_device *pdev) return ret; } + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + for (i = 0; i < num_nodes; i++) { size_t j; @@ -590,9 +593,6 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - platform_set_drvdata(pdev, qp); return 0; @@ -643,6 +643,7 @@ static struct platform_driver qnoc_driver = { .driver = { .name = "qnoc-sm8250", .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, }, }; module_platform_driver(qnoc_driver); -- cgit v1.2.3 From 9ce0af3e9573fb84c4c807183d13ea2a68271e4b Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Tue, 27 Oct 2020 13:14:42 +0900 Subject: drm/vc4: drv: Add error handding for bind There is a problem that if vc4_drm bind fails, a memory leak occurs on the drm_property_create side. Add error handding for drm_mode_config. Signed-off-by: Hoegeun Kwon Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20201027041442.30352-2-hoegeun.kwon@samsung.com --- drivers/gpu/drm/vc4/vc4_drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index f1a5fd5dab6f..a17aa1db11b6 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -314,6 +314,7 @@ unbind_all: component_unbind_all(dev, drm); gem_destroy: vc4_gem_destroy(drm); + drm_mode_config_cleanup(drm); vc4_bo_cache_destroy(drm); dev_put: drm_dev_put(drm); -- cgit v1.2.3 From 6e9643a864aa4d532b0d467bacc18a15adf5ca82 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Fri, 23 Oct 2020 17:58:39 +0800 Subject: cpufreq: e_powersaver: remove unreachable break A 'break' following a 'return' statement is pointless, so remove it. Signed-off-by: Zhang Qilong Acked-by: Viresh Kumar [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/e_powersaver.c | 1 - drivers/cpufreq/longhaul.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index 776a58bab0ff..ab93bce8ae77 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -223,7 +223,6 @@ static int eps_cpu_init(struct cpufreq_policy *policy) case EPS_BRAND_C3: pr_cont("C3\n"); return -ENODEV; - break; } /* Enable Enhanced PowerSaver */ rdmsrl(MSR_IA32_MISC_ENABLE, val); diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index 123fb006810d..182a4dbca095 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -593,7 +593,6 @@ static void longhaul_setup_voltagescaling(void) break; default: return; - break; } if (min_vid_speed >= highest_speed) return; -- cgit v1.2.3 From db865272d9c4687520dc29f77e701a1b2669872f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 23 Oct 2020 17:15:56 +0200 Subject: cpufreq: Avoid configuring old governors as default with intel_pstate Commit 33aa46f252c7 ("cpufreq: intel_pstate: Use passive mode by default without HWP") was meant to cause intel_pstate to be used in the passive mode with the schedutil governor on top of it, but it missed the case in which either "ondemand" or "conservative" was selected as the default governor in the existing kernel config, in which case the previous old governor configuration would be used, causing the default legacy governor to be used on top of intel_pstate instead of schedutil. Address this by preventing "ondemand" and "conservative" from being configured as the default cpufreq governor in the case when schedutil is the default choice for the default governor setting. [Note that the default cpufreq governor can still be set via the kernel command line if need be and that choice is not limited, so if anyone really wants to use one of the legacy governors by default, it can be achieved this way.] Fixes: 33aa46f252c7 ("cpufreq: intel_pstate: Use passive mode by default without HWP") Reported-by: Julia Lawall Cc: 5.8+ # 5.8+ Signed-off-by: Rafael J. Wysocki Acked-by: Viresh Kumar --- drivers/cpufreq/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 2c7171e0b001..85de313ddec2 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -71,6 +71,7 @@ config CPU_FREQ_DEFAULT_GOV_USERSPACE config CPU_FREQ_DEFAULT_GOV_ONDEMAND bool "ondemand" + depends on !(X86_INTEL_PSTATE && SMP) select CPU_FREQ_GOV_ONDEMAND select CPU_FREQ_GOV_PERFORMANCE help @@ -83,6 +84,7 @@ config CPU_FREQ_DEFAULT_GOV_ONDEMAND config CPU_FREQ_DEFAULT_GOV_CONSERVATIVE bool "conservative" + depends on !(X86_INTEL_PSTATE && SMP) select CPU_FREQ_GOV_CONSERVATIVE select CPU_FREQ_GOV_PERFORMANCE help -- cgit v1.2.3 From 1c534352f47fd83eb08075ac2474f707e74bf7f7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 23 Oct 2020 17:35:19 +0200 Subject: cpufreq: Introduce CPUFREQ_NEED_UPDATE_LIMITS driver flag Generally, a cpufreq driver may need to update some internal upper and lower frequency boundaries on policy max and min changes, respectively, but currently this does not work if the target frequency does not change along with the policy limit. Namely, if the target frequency does not change along with the policy min or max, the "target_freq == policy->cur" check in __cpufreq_driver_target() prevents driver callbacks from being invoked and they do not even have a chance to update the corresponding internal boundary. This particularly affects the "powersave" and "performance" governors that always set the target frequency to one of the policy limits and it never changes when the other limit is updated. To allow cpufreq the drivers needing to update internal frequency boundaries on policy limits changes to avoid this issue, introduce a new driver flag, CPUFREQ_NEED_UPDATE_LIMITS, that (when set) will neutralize the check mentioned above. Signed-off-by: Rafael J. Wysocki Acked-by: Viresh Kumar --- drivers/cpufreq/cpufreq.c | 3 ++- include/linux/cpufreq.h | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index f4b60663efe6..ea58337fb65f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2187,7 +2187,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, * exactly same freq is called again and so we can save on few function * calls. */ - if (target_freq == policy->cur) + if (target_freq == policy->cur && + !(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS)) return 0; /* Save last value to restore later on errors */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index fa37b1c66443..038ed83aab41 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -298,7 +298,7 @@ __ATTR(_name, 0644, show_##_name, store_##_name) struct cpufreq_driver { char name[CPUFREQ_NAME_LEN]; - u8 flags; + u16 flags; void *driver_data; /* needed by all drivers */ @@ -422,6 +422,14 @@ struct cpufreq_driver { */ #define CPUFREQ_IS_COOLING_DEV BIT(7) +/* + * Set by drivers that need to update internale upper and lower boundaries along + * with the target frequency and so the core and governors should also invoke + * the diver if the target frequency does not change, but the policy min or max + * may have changed. + */ +#define CPUFREQ_NEED_UPDATE_LIMITS BIT(8) + int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); -- cgit v1.2.3 From e0be38ed4ab413ddd492118cf146369b86ee0ab5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 23 Oct 2020 17:35:32 +0200 Subject: cpufreq: intel_pstate: Avoid missing HWP max updates in passive mode If the cpufreq policy max limit is changed when intel_pstate operates in the passive mode with HWP enabled and the "powersave" governor is used on top of it, the HWP max limit is not updated as appropriate. Namely, in the "powersave" governor case, the target P-state is always equal to the policy min limit, so if the latter does not change, intel_cpufreq_adjust_hwp() is not invoked to update the HWP Request MSR due to the "target_pstate != old_pstate" check in intel_cpufreq_update_pstate(), so the HWP max limit is not updated as a result. Also, if the CPUFREQ_NEED_UPDATE_LIMITS flag is not set for the driver and the target frequency does not change along with the policy max limit, the "target_freq == policy->cur" check in __cpufreq_driver_target() prevents the driver's ->target() callback from being invoked at all, so the HWP max limit is not updated. To prevent that occurring, set the CPUFREQ_NEED_UPDATE_LIMITS flag in the intel_cpufreq driver structure if HWP is enabled and modify intel_cpufreq_update_pstate() to do the "target_pstate != old_pstate" check only in the non-HWP case and let intel_cpufreq_adjust_hwp() always run in the HWP case (it will update HWP Request only if the cached value of the register is different from the new one including the limits, so if neither the target P-state value nor the max limit changes, the register write will still be avoided). Fixes: f6ebbcf08f37 ("cpufreq: intel_pstate: Implement passive mode with HWP enabled") Reported-by: Zhang Rui Cc: 5.9+ # 5.9+: 1c534352f47f cpufreq: Introduce CPUFREQ_NEED_UPDATE_LIMITS ... Signed-off-by: Rafael J. Wysocki Acked-by: Viresh Kumar Tested-by: Zhang Rui --- drivers/cpufreq/intel_pstate.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 3c1455518738..b7a9779250aa 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2568,14 +2568,12 @@ static int intel_cpufreq_update_pstate(struct cpudata *cpu, int target_pstate, int old_pstate = cpu->pstate.current_pstate; target_pstate = intel_pstate_prepare_request(cpu, target_pstate); - if (target_pstate != old_pstate) { + if (hwp_active) { + intel_cpufreq_adjust_hwp(cpu, target_pstate, fast_switch); + cpu->pstate.current_pstate = target_pstate; + } else if (target_pstate != old_pstate) { + intel_cpufreq_adjust_perf_ctl(cpu, target_pstate, fast_switch); cpu->pstate.current_pstate = target_pstate; - if (hwp_active) - intel_cpufreq_adjust_hwp(cpu, target_pstate, - fast_switch); - else - intel_cpufreq_adjust_perf_ctl(cpu, target_pstate, - fast_switch); } intel_cpufreq_trace(cpu, fast_switch ? INTEL_PSTATE_TRACE_FAST_SWITCH : @@ -3032,6 +3030,7 @@ static int __init intel_pstate_init(void) hwp_mode_bdw = id->driver_data; intel_pstate.attr = hwp_cpufreq_attrs; intel_cpufreq.attr = hwp_cpufreq_attrs; + intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS; if (!default_driver) default_driver = &intel_pstate; -- cgit v1.2.3 From 4e0ba5577dba686f96c1c10ef4166380667fdec7 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Sun, 25 Oct 2020 00:29:53 +0800 Subject: intel_idle: Fix max_cstate for processor models without C-state tables Currently intel_idle driver gets the c-state information from ACPI _CST if the processor model is not recognized by it. However the c-state in _CST starts with index 1 which is different from the index in intel_idle driver's internal c-state table. While intel_idle_max_cstate_reached() was previously introduced to deal with intel_idle driver's internal c-state table, re-using this function directly on _CST is incorrect. Fix this by subtracting 1 from the index when checking max_cstate in the _CST case. For example, append intel_idle.max_cstate=1 in boot command line, Before the patch: grep . /sys/devices/system/cpu/cpu0/cpuidle/state*/name POLL After the patch: grep . /sys/devices/system/cpu/cpu0/cpuidle/state*/name /sys/devices/system/cpu/cpu0/cpuidle/state0/name:POLL /sys/devices/system/cpu/cpu0/cpuidle/state1/name:C1_ACPI Fixes: 18734958e9bf ("intel_idle: Use ACPI _CST for processor models without C-state tables") Reported-by: Pengfei Xu Cc: 5.6+ # 5.6+ Signed-off-by: Chen Yu [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 56f5b8077cba..01bace49a962 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1239,7 +1239,7 @@ static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) struct acpi_processor_cx *cx; struct cpuidle_state *state; - if (intel_idle_max_cstate_reached(cstate)) + if (intel_idle_max_cstate_reached(cstate - 1)) break; cx = &acpi_state_table.states[cstate]; -- cgit v1.2.3 From 10105d0c9763f058f6a9a09f78397d5bf94dc94c Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 26 Oct 2020 17:30:28 -0400 Subject: drm/amdgpu/swsmu: drop smu i2c bus on navi1x Stop registering the SMU i2c bus on navi1x. This leads to instability issues when userspace processes mess with the bus and also seems to cause display stability issues in some cases. Bug: https://gitlab.freedesktop.org/drm/amd/-/issues/1314 Bug: https://gitlab.freedesktop.org/drm/amd/-/issues/1341 Reviewed-by: Evan Quan Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index 8d8081c6bd38..9cf97744b67e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -2534,29 +2534,6 @@ static const struct i2c_algorithm navi10_i2c_algo = { .functionality = navi10_i2c_func, }; -static int navi10_i2c_control_init(struct smu_context *smu, struct i2c_adapter *control) -{ - struct amdgpu_device *adev = to_amdgpu_device(control); - int res; - - control->owner = THIS_MODULE; - control->class = I2C_CLASS_SPD; - control->dev.parent = &adev->pdev->dev; - control->algo = &navi10_i2c_algo; - snprintf(control->name, sizeof(control->name), "AMDGPU SMU"); - - res = i2c_add_adapter(control); - if (res) - DRM_ERROR("Failed to register hw i2c, err: %d\n", res); - - return res; -} - -static void navi10_i2c_control_fini(struct smu_context *smu, struct i2c_adapter *control) -{ - i2c_del_adapter(control); -} - static ssize_t navi10_get_gpu_metrics(struct smu_context *smu, void **table) { @@ -2687,8 +2664,6 @@ static const struct pptable_funcs navi10_ppt_funcs = { .set_default_dpm_table = navi10_set_default_dpm_table, .dpm_set_vcn_enable = navi10_dpm_set_vcn_enable, .dpm_set_jpeg_enable = navi10_dpm_set_jpeg_enable, - .i2c_init = navi10_i2c_control_init, - .i2c_fini = navi10_i2c_control_fini, .print_clk_levels = navi10_print_clk_levels, .force_clk_levels = navi10_force_clk_levels, .populate_umd_state_clk = navi10_populate_umd_state_clk, -- cgit v1.2.3 From e72d98445d089aa2d623a70b27ec9011be2c5166 Mon Sep 17 00:00:00 2001 From: Kenneth Feng Date: Wed, 21 Oct 2020 14:03:08 +0800 Subject: drm/amd/pm: fix the wrong fan speed in fan1_input fix the wrong fan speed in fan1_input when the fan control mode is manual. the fan speed value is not correct when we set manual mode to fan1_enalbe - 1. since the fan speed in the metrics table always reflects the real fan speed,we can fetch the fan speed for both auto and manual mode. Signed-off-by: Kenneth Feng Reviewed-by: Likun Gao Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index 685a8a3b25d4..895d89bea7fa 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -1177,14 +1177,9 @@ static int sienna_cichlid_get_fan_speed_rpm(struct smu_context *smu, if (!speed) return -EINVAL; - switch (smu_v11_0_get_fan_control_mode(smu)) { - case AMD_FAN_CTRL_AUTO: - return sienna_cichlid_get_smu_metrics_data(smu, - METRICS_CURR_FANSPEED, - speed); - default: - return smu_v11_0_get_fan_speed_rpm(smu, speed); - } + return sienna_cichlid_get_smu_metrics_data(smu, + METRICS_CURR_FANSPEED, + speed); } static int sienna_cichlid_get_fan_parameters(struct smu_context *smu) -- cgit v1.2.3 From 65d437b83b2b8eb9ae47b6145399fc7683b028f7 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 26 Oct 2020 17:40:42 -0400 Subject: drm/amdgpu/pm: fix the fan speed in fan1_input in manual mode for navi1x It has been confirmed that the SMU metrics table should always reflect the current fan speed even in manual mode. Fixes: 3033e9f1c2de ("drm/amdgpu/swsmu: handle manual fan readback on SMU11") Reviewed-by: Evan Quan Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index 9cf97744b67e..ef1a62e86a0e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -1361,14 +1361,9 @@ static int navi10_get_fan_speed_rpm(struct smu_context *smu, if (!speed) return -EINVAL; - switch (smu_v11_0_get_fan_control_mode(smu)) { - case AMD_FAN_CTRL_AUTO: - return navi10_get_smu_metrics_data(smu, - METRICS_CURR_FANSPEED, - speed); - default: - return smu_v11_0_get_fan_speed_rpm(smu, speed); - } + return navi10_get_smu_metrics_data(smu, + METRICS_CURR_FANSPEED, + speed); } static int navi10_get_fan_parameters(struct smu_context *smu) -- cgit v1.2.3 From d5dcce0c414fcbfe4c2037b66ac69ea5f9b3f75c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Oct 2020 21:40:59 +0300 Subject: device property: Keep secondary firmware node secondary by type Behind primary and secondary we understand the type of the nodes which might define their ordering. However, if primary node gone, we can't maintain the ordering by definition of the linked list. Thus, by ordering secondary node becomes first in the list. But in this case the meaning of it is still secondary (or auxiliary). The type of the node is maintained by the secondary pointer in it: secondary pointer Meaning NULL or valid primary node ERR_PTR(-ENODEV) secondary node So, if by some reason we do the following sequence of calls set_primary_fwnode(dev, NULL); set_primary_fwnode(dev, primary); we should preserve secondary node. This concept is supported by the description of set_primary_fwnode() along with implementation of set_secondary_fwnode(). Hence, fix the commit c15e1bdda436 to follow this as well. Fixes: c15e1bdda436 ("device property: Fix the secondary firmware node handling in set_primary_fwnode()") Cc: Ferry Toth Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Tested-by: Ferry Toth Cc: 5.9+ # 5.9+ Signed-off-by: Rafael J. Wysocki --- drivers/base/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index c852f16c111b..41feab679fa1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4278,7 +4278,7 @@ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) } else { if (fwnode_is_primary(fn)) { dev->fwnode = fn->secondary; - fn->secondary = NULL; + fn->secondary = ERR_PTR(-ENODEV); } else { dev->fwnode = NULL; } -- cgit v1.2.3 From 99aed9227073fb34ce2880cbc7063e04185a65e1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Oct 2020 21:41:00 +0300 Subject: device property: Don't clear secondary pointer for shared primary firmware node It appears that firmware nodes can be shared between devices. In such case when a (child) device is about to be deleted, its firmware node may be shared and ACPI_COMPANION_SET(..., NULL) call for it breaks the secondary link of the shared primary firmware node. In order to prevent that, check, if the device has a parent and parent's firmware node is shared with its child, and avoid crashing the link. Fixes: c15e1bdda436 ("device property: Fix the secondary firmware node handling in set_primary_fwnode()") Reported-by: Ferry Toth Signed-off-by: Andy Shevchenko Reviewed-by: Heikki Krogerus Tested-by: Ferry Toth Cc: 5.9+ # 5.9+ Signed-off-by: Rafael J. Wysocki --- drivers/base/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 41feab679fa1..78114ddac755 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4264,6 +4264,7 @@ static inline bool fwnode_is_primary(struct fwnode_handle *fwnode) */ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) { + struct device *parent = dev->parent; struct fwnode_handle *fn = dev->fwnode; if (fwnode) { @@ -4278,7 +4279,8 @@ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode) } else { if (fwnode_is_primary(fn)) { dev->fwnode = fn->secondary; - fn->secondary = ERR_PTR(-ENODEV); + if (!(parent && fn == parent->fwnode)) + fn->secondary = ERR_PTR(-ENODEV); } else { dev->fwnode = NULL; } -- cgit v1.2.3 From e510785f8aca4a7346497edd4d5aceefe5370960 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 23 Oct 2020 18:33:11 +0200 Subject: PNP: fix kernel-doc markups It sounds that there were function renames. Update the kernel-doc markups accordingly. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Rafael J. Wysocki --- drivers/pnp/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index 3bf18d718975..a50ab002e9e4 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -51,7 +51,7 @@ static void pnp_remove_protocol(struct pnp_protocol *protocol) } /** - * pnp_protocol_register - adds a pnp protocol to the pnp layer + * pnp_register_protocol - adds a pnp protocol to the pnp layer * @protocol: pointer to the corresponding pnp_protocol structure * * Ex protocols: ISAPNP, PNPBIOS, etc @@ -91,7 +91,7 @@ int pnp_register_protocol(struct pnp_protocol *protocol) } /** - * pnp_protocol_unregister - removes a pnp protocol from the pnp layer + * pnp_unregister_protocol - removes a pnp protocol from the pnp layer * @protocol: pointer to the corresponding pnp_protocol structure */ void pnp_unregister_protocol(struct pnp_protocol *protocol) -- cgit v1.2.3 From be0e97526455fefe3b7668b7fdf6cd247bf13318 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 26 Oct 2020 22:48:34 +0100 Subject: ACPI: dock: fix enum-conversion warning gcc points out a type mismatch: drivers/acpi/dock.c: In function 'hot_remove_dock_devices': drivers/acpi/dock.c:234:53: warning: implicit conversion from 'enum ' to 'enum dock_callback_type' [-Wenum-conversion] 234 | dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); This is harmless because 'false' still has the correct numeric value, but passing DOCK_CALL_HANDLER documents better what is going on and avoids the warning. Fixes: 37f908778f20 ("ACPI / dock: Walk list in reverse order during removal of devices") Fixes: f09ce741a03a ("ACPI / dock / PCI: Drop ACPI dock notifier chain") Signed-off-by: Arnd Bergmann Reviewed-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 45d4b7b69de8..24e076f44d23 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -231,7 +231,8 @@ static void hot_remove_dock_devices(struct dock_station *ds) * between them). */ list_for_each_entry_reverse(dd, &ds->dependent_devices, list) - dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false); + dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, + DOCK_CALL_HANDLER); list_for_each_entry_reverse(dd, &ds->dependent_devices, list) acpi_bus_trim(dd->adev); -- cgit v1.2.3 From 85f971b65a692b68181438e099b946cc06ed499b Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Tue, 27 Oct 2020 21:49:01 +0800 Subject: ACPI: NFIT: Fix comparison to '-ENXIO' Initial value of rc is '-ENXIO', and we should use the initial value to check it. Signed-off-by: Zhang Qilong Reviewed-by: Pankaj Gupta Reviewed-by: Vishal Verma [ rjw: Subject edit ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/nfit/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 756227837b3b..3a3c209ed3d3 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1564,7 +1564,7 @@ static ssize_t format1_show(struct device *dev, le16_to_cpu(nfit_dcr->dcr->code)); break; } - if (rc != ENXIO) + if (rc != -ENXIO) break; } mutex_unlock(&acpi_desc->init_mutex); -- cgit v1.2.3 From 58d043690deb7c145f8a0bd07370dcaefa97a733 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 27 Oct 2020 11:15:58 +0100 Subject: drm/vc4: hdmi: Avoid sleeping in atomic context When running the trigger hook, ALSA by default will take a spinlock, and thus will run the trigger hook in atomic context. However, our HDMI driver will send the infoframes as part of the trigger hook, and part of that process is to wait for a bit to be cleared for up to 100ms. To be nicer to the system, that wait has some usleep_range that interact poorly with the atomic context. There's several ways we can fix this, but the more obvious one is to make ALSA take a mutex instead by setting the nonatomic flag on the DAI link. That doesn't work though, since now the cyclic callback installed by the dmaengine helpers in ALSA will take a mutex, while that callback is run by dmaengine's virt-chan code in a tasklet where sleeping is not allowed either. Given the delay we need to poll the bit for, changing the usleep_range for a udelay and keep running it from a context where interrupts are disabled is not really a good option either. However, we can move the infoframe setup code in the hw_params hook, like is usually done in other HDMI controllers, that isn't protected by a spinlock and thus where we can sleep. Infoframes will be sent on a regular basis anyway, and since hw_params is where the audio parameters that end up in the infoframes are setup, this also makes a bit more sense. Fixes: bb7d78568814 ("drm/vc4: Add HDMI audio support") Suggested-by: Mark Brown Signed-off-by: Maxime Ripard Reviewed-by: Mark Brown Reviewed-by: Takashi Iwai Link: https://patchwork.freedesktop.org/patch/msgid/20201027101558.427256-1-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index e8f99e290655..73ac52714e54 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -922,6 +922,7 @@ static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); + struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; struct device *dev = &vc4_hdmi->pdev->dev; u32 audio_packet_config, channel_mask; u32 channel_map; @@ -981,6 +982,8 @@ static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream, HDMI_WRITE(HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); vc4_hdmi_set_n_cts(vc4_hdmi); + vc4_hdmi_set_audio_infoframe(encoder); + return 0; } @@ -988,11 +991,9 @@ static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct vc4_hdmi *vc4_hdmi = dai_to_hdmi(dai); - struct drm_encoder *encoder = &vc4_hdmi->encoder.base.base; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - vc4_hdmi_set_audio_infoframe(encoder); vc4_hdmi->audio.streaming = true; if (vc4_hdmi->variant->phy_rng_enable) -- cgit v1.2.3 From 28e9dcd9172028263c8225c15c4e329e08475e89 Mon Sep 17 00:00:00 2001 From: Vinay Kumar Yadav Date: Mon, 26 Oct 2020 01:05:39 +0530 Subject: chelsio/chtls: fix deadlock issue In chtls_pass_establish() we hold child socket lock using bh_lock_sock and we are again trying bh_lock_sock in add_to_reap_list, causing deadlock. Remove bh_lock_sock in add_to_reap_list() as lock is already held. Fixes: cc35c88ae4db ("crypto : chtls - CPL handler definition") Signed-off-by: Vinay Kumar Yadav Link: https://lore.kernel.org/r/20201025193538.31112-1-vinay.yadav@chelsio.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c index ec4f79049a06..2fdcac0bcb14 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c @@ -1514,7 +1514,6 @@ static void add_to_reap_list(struct sock *sk) struct chtls_sock *csk = sk->sk_user_data; local_bh_disable(); - bh_lock_sock(sk); release_tcp_port(sk); /* release the port immediately */ spin_lock(&reap_list_lock); @@ -1523,7 +1522,6 @@ static void add_to_reap_list(struct sock *sk) if (!csk->passive_reap_next) schedule_work(&reap_task); spin_unlock(&reap_list_lock); - bh_unlock_sock(sk); local_bh_enable(); } -- cgit v1.2.3 From 6daa1da4e262b0cd52ef0acc1989ff22b5540264 Mon Sep 17 00:00:00 2001 From: Vinay Kumar Yadav Date: Mon, 26 Oct 2020 01:12:29 +0530 Subject: chelsio/chtls: fix memory leaks in CPL handlers CPL handler functions chtls_pass_open_rpl() and chtls_close_listsrv_rpl() should return CPL_RET_BUF_DONE so that caller function will do skb free to avoid leak. Fixes: cc35c88ae4db ("crypto : chtls - CPL handler definition") Signed-off-by: Vinay Kumar Yadav Link: https://lore.kernel.org/r/20201025194228.31271-1-vinay.yadav@chelsio.com Signed-off-by: Jakub Kicinski --- .../chelsio/inline_crypto/chtls/chtls_cm.c | 27 ++++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c index 2fdcac0bcb14..d581c4e623f8 100644 --- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c +++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c @@ -772,14 +772,13 @@ static int chtls_pass_open_rpl(struct chtls_dev *cdev, struct sk_buff *skb) if (rpl->status != CPL_ERR_NONE) { pr_info("Unexpected PASS_OPEN_RPL status %u for STID %u\n", rpl->status, stid); - return CPL_RET_BUF_DONE; + } else { + cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); + sock_put(listen_ctx->lsk); + kfree(listen_ctx); + module_put(THIS_MODULE); } - cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); - sock_put(listen_ctx->lsk); - kfree(listen_ctx); - module_put(THIS_MODULE); - - return 0; + return CPL_RET_BUF_DONE; } static int chtls_close_listsrv_rpl(struct chtls_dev *cdev, struct sk_buff *skb) @@ -796,15 +795,13 @@ static int chtls_close_listsrv_rpl(struct chtls_dev *cdev, struct sk_buff *skb) if (rpl->status != CPL_ERR_NONE) { pr_info("Unexpected CLOSE_LISTSRV_RPL status %u for STID %u\n", rpl->status, stid); - return CPL_RET_BUF_DONE; + } else { + cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); + sock_put(listen_ctx->lsk); + kfree(listen_ctx); + module_put(THIS_MODULE); } - - cxgb4_free_stid(cdev->tids, stid, listen_ctx->lsk->sk_family); - sock_put(listen_ctx->lsk); - kfree(listen_ctx); - module_put(THIS_MODULE); - - return 0; + return CPL_RET_BUF_DONE; } static void chtls_purge_wr_queue(struct sock *sk) -- cgit v1.2.3 From 68b9f0865b1ef545da180c57d54b82c94cb464a4 Mon Sep 17 00:00:00 2001 From: Andrew Gabbasov Date: Mon, 26 Oct 2020 05:21:30 -0500 Subject: ravb: Fix bit fields checking in ravb_hwtstamp_get() In the function ravb_hwtstamp_get() in ravb_main.c with the existing values for RAVB_RXTSTAMP_TYPE_V2_L2_EVENT (0x2) and RAVB_RXTSTAMP_TYPE_ALL (0x6) if (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE_V2_L2_EVENT) config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; else if (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE_ALL) config.rx_filter = HWTSTAMP_FILTER_ALL; if the test on RAVB_RXTSTAMP_TYPE_ALL should be true, it will never be reached. This issue can be verified with 'hwtstamp_config' testing program (tools/testing/selftests/net/hwtstamp_config.c). Setting filter type to ALL and subsequent retrieving it gives incorrect value: $ hwtstamp_config eth0 OFF ALL flags = 0 tx_type = OFF rx_filter = ALL $ hwtstamp_config eth0 flags = 0 tx_type = OFF rx_filter = PTP_V2_L2_EVENT Correct this by converting if-else's to switch. Fixes: c156633f1353 ("Renesas Ethernet AVB driver proper") Reported-by: Julia Lawall Signed-off-by: Andrew Gabbasov Reviewed-by: Sergei Shtylyov Link: https://lore.kernel.org/r/20201026102130.29368-1-andrew_gabbasov@mentor.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/renesas/ravb_main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 9c4df4ede011..bd30505fbc57 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1744,12 +1744,16 @@ static int ravb_hwtstamp_get(struct net_device *ndev, struct ifreq *req) config.flags = 0; config.tx_type = priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; - if (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE_V2_L2_EVENT) + switch (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE) { + case RAVB_RXTSTAMP_TYPE_V2_L2_EVENT: config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT; - else if (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE_ALL) + break; + case RAVB_RXTSTAMP_TYPE_ALL: config.rx_filter = HWTSTAMP_FILTER_ALL; - else + break; + default: config.rx_filter = HWTSTAMP_FILTER_NONE; + } return copy_to_user(req->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; -- cgit v1.2.3 From 2ac8af0967aaa2b67cb382727e784900d2f4d0da Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Mon, 26 Oct 2020 11:42:21 +0100 Subject: ibmveth: Fix use of ibmveth in a bridge. The check for src mac address in ibmveth_is_packet_unsupported is wrong. Commit 6f2275433a2f wanted to shut down messages for loopback packets, but now suppresses bridged frames, which are accepted by the hypervisor otherwise bridging won't work at all. Fixes: 6f2275433a2f ("ibmveth: Detect unsupported packets before sending to the hypervisor") Signed-off-by: Michal Suchanek Signed-off-by: Thomas Bogendoerfer Link: https://lore.kernel.org/r/20201026104221.26570-1-msuchanek@suse.de Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ibm/ibmveth.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 7ef3369953b6..c3ec9ceed833 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1031,12 +1031,6 @@ static int ibmveth_is_packet_unsupported(struct sk_buff *skb, ret = -EOPNOTSUPP; } - if (!ether_addr_equal(ether_header->h_source, netdev->dev_addr)) { - netdev_dbg(netdev, "source packet MAC address does not match veth device's, dropping packet.\n"); - netdev->stats.tx_dropped++; - ret = -EOPNOTSUPP; - } - return ret; } -- cgit v1.2.3 From 35bc10b2eafbb701064b94f283b77c54d3304842 Mon Sep 17 00:00:00 2001 From: Kanchan Joshi Date: Mon, 28 Sep 2020 15:25:49 +0530 Subject: null_blk: synchronization fix for zoned device Parallel write,read,zone-mgmt operations accessing/altering zone state and write-pointer may get into race. Avoid the situation by using a new spinlock for zoned device. Concurrent zone-appends (on a zone) returning same write-pointer issue is also avoided using this lock. Cc: stable@vger.kernel.org Fixes: e0489ed5daeb ("null_blk: Support REQ_OP_ZONE_APPEND") Signed-off-by: Kanchan Joshi Reviewed-by: Damien Le Moal Signed-off-by: Jens Axboe --- drivers/block/null_blk.h | 1 + drivers/block/null_blk_zoned.c | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/null_blk.h b/drivers/block/null_blk.h index d2e7db43a52a..3176b269b822 100644 --- a/drivers/block/null_blk.h +++ b/drivers/block/null_blk.h @@ -47,6 +47,7 @@ struct nullb_device { unsigned int nr_zones_closed; struct blk_zone *zones; sector_t zone_size_sects; + spinlock_t zone_lock; unsigned long size; /* device size in MB */ unsigned long completion_nsec; /* time in ns to complete a request */ diff --git a/drivers/block/null_blk_zoned.c b/drivers/block/null_blk_zoned.c index 7d94f2d47a6a..98056c88926b 100644 --- a/drivers/block/null_blk_zoned.c +++ b/drivers/block/null_blk_zoned.c @@ -45,6 +45,7 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q) if (!dev->zones) return -ENOMEM; + spin_lock_init(&dev->zone_lock); if (dev->zone_nr_conv >= dev->nr_zones) { dev->zone_nr_conv = dev->nr_zones - 1; pr_info("changed the number of conventional zones to %u", @@ -149,8 +150,11 @@ int null_report_zones(struct gendisk *disk, sector_t sector, * So use a local copy to avoid corruption of the device zone * array. */ + spin_lock_irq(&dev->zone_lock); memcpy(&zone, &dev->zones[first_zone + i], sizeof(struct blk_zone)); + spin_unlock_irq(&dev->zone_lock); + error = cb(&zone, i, data); if (error) return error; @@ -499,18 +503,28 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_opf op, sector_t sector, sector_t nr_sectors) { + blk_status_t sts; + struct nullb_device *dev = cmd->nq->dev; + + spin_lock_irq(&dev->zone_lock); switch (op) { case REQ_OP_WRITE: - return null_zone_write(cmd, sector, nr_sectors, false); + sts = null_zone_write(cmd, sector, nr_sectors, false); + break; case REQ_OP_ZONE_APPEND: - return null_zone_write(cmd, sector, nr_sectors, true); + sts = null_zone_write(cmd, sector, nr_sectors, true); + break; case REQ_OP_ZONE_RESET: case REQ_OP_ZONE_RESET_ALL: case REQ_OP_ZONE_OPEN: case REQ_OP_ZONE_CLOSE: case REQ_OP_ZONE_FINISH: - return null_zone_mgmt(cmd, op, sector); + sts = null_zone_mgmt(cmd, op, sector); + break; default: - return null_process_cmd(cmd, op, sector, nr_sectors); + sts = null_process_cmd(cmd, op, sector, nr_sectors); } + spin_unlock_irq(&dev->zone_lock); + + return sts; } -- cgit v1.2.3 From defe40af1a7143a0538d7c3e87224459eea0a877 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Mon, 12 Oct 2020 08:45:48 +0200 Subject: usb: cdns3: Variable 'length' set but not used Patch removes not used variable 'length' from cdns3_wa2_descmiss_copy_data function. Fixes: 141e70fef4ee ("usb: cdns3: gadget: need to handle sg case for workaround 2 case") Acked-by: Roger Quadros Signed-off-by: Pawel Laszczak Signed-off-by: Peter Chen --- drivers/usb/cdns3/gadget.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 6e7b70a2e352..692acf7b9b14 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -506,7 +506,6 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, while (!list_empty(&priv_ep->wa2_descmiss_req_list)) { int chunk_end; - int length; descmiss_priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list); @@ -517,7 +516,6 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep, break; chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH; - length = request->actual + descmiss_req->actual; request->status = descmiss_req->status; __cdns3_descmiss_copy_data(request, descmiss_req); list_del_init(&descmiss_priv_req->list); -- cgit v1.2.3 From 011fde48394b7dc8dfd6660d1013b26a00157b80 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Tue, 20 Oct 2020 16:11:16 +0800 Subject: mmc: sdhci-of-esdhc: make sure delay chain locked for HS400 For eMMC HS400 mode initialization, the DLL reset is a required step if DLL is enabled to use previously, like in bootloader. This step has not been documented in reference manual, but the RM will be fixed sooner or later. This patch is to add the step of DLL reset, and make sure delay chain locked for HS400. Signed-off-by: Yangbo Lu Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20201020081116.20918-1-yangbo.lu@nxp.com Fixes: 54e08d9a95ca ("mmc: sdhci-of-esdhc: add hs400 mode support") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc.h | 2 ++ drivers/mmc/host/sdhci-of-esdhc.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a30796e79b1c..6de02f09c322 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -5,6 +5,7 @@ * Copyright (c) 2007 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. * Copyright (c) 2010 Pengutronix e.K. + * Copyright 2020 NXP * Author: Wolfram Sang */ @@ -88,6 +89,7 @@ /* DLL Config 0 Register */ #define ESDHC_DLLCFG0 0x160 #define ESDHC_DLL_ENABLE 0x80000000 +#define ESDHC_DLL_RESET 0x40000000 #define ESDHC_DLL_FREQ_SEL 0x08000000 /* DLL Config 1 Register */ diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index baf7801a1804..bb094459196a 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -4,6 +4,7 @@ * * Copyright (c) 2007, 2010, 2012 Freescale Semiconductor, Inc. * Copyright (c) 2009 MontaVista Software, Inc. + * Copyright 2020 NXP * * Authors: Xiaobo Xie * Anton Vorontsov @@ -19,6 +20,7 @@ #include #include #include +#include #include #include #include "sdhci-pltfm.h" @@ -743,6 +745,21 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) if (host->mmc->actual_clock == MMC_HS200_MAX_DTR) temp |= ESDHC_DLL_FREQ_SEL; sdhci_writel(host, temp, ESDHC_DLLCFG0); + + temp |= ESDHC_DLL_RESET; + sdhci_writel(host, temp, ESDHC_DLLCFG0); + udelay(1); + temp &= ~ESDHC_DLL_RESET; + sdhci_writel(host, temp, ESDHC_DLLCFG0); + + /* Wait max 20 ms */ + if (read_poll_timeout(sdhci_readl, temp, + temp & ESDHC_DLL_STS_SLV_LOCK, + 10, 20000, false, + host, ESDHC_DLLSTAT0)) + pr_err("%s: timeout for delay chain lock.\n", + mmc_hostname(host->mmc)); + temp = sdhci_readl(host, ESDHC_TBCTL); sdhci_writel(host, temp | ESDHC_HS400_WNDW_ADJUST, ESDHC_TBCTL); -- cgit v1.2.3 From f49a51bfdc8ea717c97ccd4cc98b7e6daaa5553a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 27 Oct 2020 22:49:22 +0100 Subject: drm/shme-helpers: Fix dma_buf_mmap forwarding bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we forward an mmap to the dma_buf exporter, they get to own everything. Unfortunately drm_gem_mmap_obj() overwrote vma->vm_private_data after the driver callback, wreaking the exporter complete. This was noticed because vb2_common_vm_close blew up on mali gpu with panfrost after commit 26d3ac3cb04d ("drm/shmem-helpers: Redirect mmap for imported dma-buf"). Unfortunately drm_gem_mmap_obj also acquires a surplus reference that we need to drop in shmem helpers, which is a bit of a mislayer situation. Maybe the entire dma_buf_mmap forwarding should be pulled into core gem code. Note that the only two other drivers which forward mmap in their own code (etnaviv and exynos) get this somewhat right by overwriting the gem mmap code. But they seem to still have the leak. This might be a good excuse to move these drivers over to shmem helpers completely. Reviewed-by: Boris Brezillon Acked-by: Christian König Cc: Christian König Cc: Sumit Semwal Cc: Lucas Stach Cc: Russell King Cc: Christian Gmeiner Cc: Inki Dae Cc: Joonyoung Shim Cc: Seung-Woo Kim Cc: Kyungmin Park Fixes: 26d3ac3cb04d ("drm/shmem-helpers: Redirect mmap for imported dma-buf") Cc: Boris Brezillon Cc: Thomas Zimmermann Cc: Gerd Hoffmann Cc: Rob Herring Cc: dri-devel@lists.freedesktop.org Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Cc: # v5.9+ Reported-and-tested-by: piotr.oniszczuk@gmail.com Cc: piotr.oniszczuk@gmail.com Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20201027214922.3566743-1-daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_gem.c | 4 ++-- drivers/gpu/drm/drm_gem_shmem_helper.c | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 19d73868490e..69c2c079d803 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1085,6 +1085,8 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, */ drm_gem_object_get(obj); + vma->vm_private_data = obj; + if (obj->funcs && obj->funcs->mmap) { ret = obj->funcs->mmap(obj, vma); if (ret) { @@ -1107,8 +1109,6 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); } - vma->vm_private_data = obj; - return 0; } EXPORT_SYMBOL(drm_gem_mmap_obj); diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index d77c9f8ff26c..e00616d94f26 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -593,8 +593,13 @@ int drm_gem_shmem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) /* Remove the fake offset */ vma->vm_pgoff -= drm_vma_node_start(&obj->vma_node); - if (obj->import_attach) + if (obj->import_attach) { + /* Drop the reference drm_gem_mmap_obj() acquired.*/ + drm_gem_object_put(obj); + vma->vm_private_data = NULL; + return dma_buf_mmap(obj->dma_buf, vma, 0); + } shmem = to_drm_gem_shmem_obj(obj); -- cgit v1.2.3 From 063a3d67693ecde7f24ca00d4a131883edab1e73 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Oct 2020 14:24:12 +0300 Subject: usb: typec: stusb160x: fix some signedness bugs These variables are enums but in this situation GCC will treat them as unsigned so the conditions are never true. Fixes: da0cb6310094 ("usb: typec: add support for STUSB160x Type-C controller family") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20201023112412.GD282278@mwanda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/stusb160x.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index ce0bd7b3ad88..7143852ce94d 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -545,7 +545,7 @@ static int stusb160x_get_fw_caps(struct stusb160x *chip, ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); if (!ret) { chip->port_type = typec_find_port_power_role(cap_str); - if (chip->port_type < 0) { + if ((int)chip->port_type < 0) { ret = chip->port_type; return ret; } @@ -567,9 +567,10 @@ static int stusb160x_get_fw_caps(struct stusb160x *chip, if (!ret) { chip->pwr_opmode = typec_find_pwr_opmode(cap_str); /* Power delivery not yet supported */ - if (chip->pwr_opmode < 0 || + if ((int)chip->pwr_opmode < 0 || chip->pwr_opmode == TYPEC_PWR_MODE_PD) { - ret = chip->pwr_opmode < 0 ? chip->pwr_opmode : -EINVAL; + ret = (int)chip->pwr_opmode < 0 ? chip->pwr_opmode : + -EINVAL; dev_err(chip->dev, "bad power operation mode: %d\n", chip->pwr_opmode); return ret; -- cgit v1.2.3 From 071ba4cc559de47160761b9500b72e8fa09d923d Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 26 Oct 2020 11:25:49 -0300 Subject: RDMA: Add rdma_connect_locked() There are two flows for handling RDMA_CM_EVENT_ROUTE_RESOLVED, either the handler triggers a completion and another thread does rdma_connect() or the handler directly calls rdma_connect(). In all cases rdma_connect() needs to hold the handler_mutex, but when handler's are invoked this is already held by the core code. This causes ULPs using the 2nd method to deadlock. Provide a rdma_connect_locked() and have all ULPs call it from their handlers. Link: https://lore.kernel.org/r/0-v2-53c22d5c1405+33-rdma_connect_locking_jgg@nvidia.com Reported-and-tested-by: Guoqing Jiang Fixes: 2a7cec538169 ("RDMA/cma: Fix locking for the RDMA_CM_CONNECT state") Acked-by: Santosh Shilimkar Acked-by: Jack Wang Reviewed-by: Christoph Hellwig Reviewed-by: Max Gurtovoy Reviewed-by: Sagi Grimberg Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cma.c | 48 +++++++++++++++++++++++++------- drivers/infiniband/ulp/iser/iser_verbs.c | 2 +- drivers/infiniband/ulp/rtrs/rtrs-clt.c | 4 +-- drivers/nvme/host/rdma.c | 4 +-- include/rdma/rdma_cm.h | 14 ++-------- net/rds/ib_cm.c | 5 ++-- 6 files changed, 48 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 7c2ab1f2fbea..a77750b8954d 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -405,10 +405,10 @@ static int cma_comp_exch(struct rdma_id_private *id_priv, /* * The FSM uses a funny double locking where state is protected by both * the handler_mutex and the spinlock. State is not allowed to change - * away from a handler_mutex protected value without also holding + * to/from a handler_mutex protected value without also holding * handler_mutex. */ - if (comp == RDMA_CM_CONNECT) + if (comp == RDMA_CM_CONNECT || exch == RDMA_CM_CONNECT) lockdep_assert_held(&id_priv->handler_mutex); spin_lock_irqsave(&id_priv->lock, flags); @@ -4038,17 +4038,23 @@ out: return ret; } -int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) +/** + * rdma_connect_locked - Initiate an active connection request. + * @id: Connection identifier to connect. + * @conn_param: Connection information used for connected QPs. + * + * Same as rdma_connect() but can only be called from the + * RDMA_CM_EVENT_ROUTE_RESOLVED handler callback. + */ +int rdma_connect_locked(struct rdma_cm_id *id, + struct rdma_conn_param *conn_param) { struct rdma_id_private *id_priv = container_of(id, struct rdma_id_private, id); int ret; - mutex_lock(&id_priv->handler_mutex); - if (!cma_comp_exch(id_priv, RDMA_CM_ROUTE_RESOLVED, RDMA_CM_CONNECT)) { - ret = -EINVAL; - goto err_unlock; - } + if (!cma_comp_exch(id_priv, RDMA_CM_ROUTE_RESOLVED, RDMA_CM_CONNECT)) + return -EINVAL; if (!id->qp) { id_priv->qp_num = conn_param->qp_num; @@ -4066,11 +4072,33 @@ int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) ret = -ENOSYS; if (ret) goto err_state; - mutex_unlock(&id_priv->handler_mutex); return 0; err_state: cma_comp_exch(id_priv, RDMA_CM_CONNECT, RDMA_CM_ROUTE_RESOLVED); -err_unlock: + return ret; +} +EXPORT_SYMBOL(rdma_connect_locked); + +/** + * rdma_connect - Initiate an active connection request. + * @id: Connection identifier to connect. + * @conn_param: Connection information used for connected QPs. + * + * Users must have resolved a route for the rdma_cm_id to connect with by having + * called rdma_resolve_route before calling this routine. + * + * This call will either connect to a remote QP or obtain remote QP information + * for unconnected rdma_cm_id's. The actual operation is based on the + * rdma_cm_id's port space. + */ +int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) +{ + struct rdma_id_private *id_priv = + container_of(id, struct rdma_id_private, id); + int ret; + + mutex_lock(&id_priv->handler_mutex); + ret = rdma_connect_locked(id, conn_param); mutex_unlock(&id_priv->handler_mutex); return ret; } diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 2f3ebc0a75d9..2bd18b006893 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -620,7 +620,7 @@ static void iser_route_handler(struct rdma_cm_id *cma_id) conn_param.private_data = (void *)&req_hdr; conn_param.private_data_len = sizeof(struct iser_cm_hdr); - ret = rdma_connect(cma_id, &conn_param); + ret = rdma_connect_locked(cma_id, &conn_param); if (ret) { iser_err("failure connecting: %d\n", ret); goto failure; diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index 776e89231c52..f298adc02acb 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -1674,9 +1674,9 @@ static int rtrs_rdma_route_resolved(struct rtrs_clt_con *con) uuid_copy(&msg.sess_uuid, &sess->s.uuid); uuid_copy(&msg.paths_uuid, &clt->paths_uuid); - err = rdma_connect(con->c.cm_id, ¶m); + err = rdma_connect_locked(con->c.cm_id, ¶m); if (err) - rtrs_err(clt, "rdma_connect(): %d\n", err); + rtrs_err(clt, "rdma_connect_locked(): %d\n", err); return err; } diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index aad829a2b50d..8bbc48cc45dc 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -1890,10 +1890,10 @@ static int nvme_rdma_route_resolved(struct nvme_rdma_queue *queue) priv.hsqsize = cpu_to_le16(queue->ctrl->ctrl.sqsize); } - ret = rdma_connect(queue->cm_id, ¶m); + ret = rdma_connect_locked(queue->cm_id, ¶m); if (ret) { dev_err(ctrl->ctrl.device, - "rdma_connect failed (%d).\n", ret); + "rdma_connect_locked failed (%d).\n", ret); goto out_destroy_queue_ib; } diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h index c672ae1da26b..32a67af18415 100644 --- a/include/rdma/rdma_cm.h +++ b/include/rdma/rdma_cm.h @@ -227,19 +227,9 @@ void rdma_destroy_qp(struct rdma_cm_id *id); int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, int *qp_attr_mask); -/** - * rdma_connect - Initiate an active connection request. - * @id: Connection identifier to connect. - * @conn_param: Connection information used for connected QPs. - * - * Users must have resolved a route for the rdma_cm_id to connect with - * by having called rdma_resolve_route before calling this routine. - * - * This call will either connect to a remote QP or obtain remote QP - * information for unconnected rdma_cm_id's. The actual operation is - * based on the rdma_cm_id's port space. - */ int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param); +int rdma_connect_locked(struct rdma_cm_id *id, + struct rdma_conn_param *conn_param); int rdma_connect_ece(struct rdma_cm_id *id, struct rdma_conn_param *conn_param, struct rdma_ucm_ece *ece); diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 06603dd1c8aa..b36b60668b1d 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -956,9 +956,10 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6) rds_ib_cm_fill_conn_param(conn, &conn_param, &dp, conn->c_proposed_version, UINT_MAX, UINT_MAX, isv6); - ret = rdma_connect(cm_id, &conn_param); + ret = rdma_connect_locked(cm_id, &conn_param); if (ret) - rds_ib_conn_error(conn, "rdma_connect failed (%d)\n", ret); + rds_ib_conn_error(conn, "rdma_connect_locked failed (%d)\n", + ret); out: /* Beware - returning non-zero tells the rdma_cm to destroy -- cgit v1.2.3 From cbdc0f54560f94c2205ddbebb5464d65868af0d8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 23 Oct 2020 18:33:18 +0200 Subject: usb: fix kernel-doc markups There is a common comment marked, instead, with kernel-doc notation. Also, some identifiers have different names between their prototypes and the kernel-doc markup. Signed-off-by: Mauro Carvalho Chehab Acked-by: Felipe Balbi Link: https://lore.kernel.org/r/0b964be3884def04fcd20ea5c12cb90d0014871c.1603469755.git.mchehab+huawei@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 2 +- drivers/usb/dwc3/core.h | 2 +- drivers/usb/gadget/composite.c | 2 +- drivers/usb/typec/mux.c | 2 +- include/linux/usb/composite.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index bdf0925da6b6..841daec70b6e 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/** +/* * core.c - DesignWare USB3 DRD Controller Core file * * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 74323b10a64a..2f95f08ca511 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1277,7 +1277,7 @@ struct dwc3_event_type { #define DWC3_DEPEVT_EPCMDCMPLT 0x07 /** - * struct dwc3_event_depvt - Device Endpoint Events + * struct dwc3_event_depevt - Device Endpoint Events * @one_bit: indicates this is an endpoint event (not used) * @endpoint_number: number of the endpoint * @endpoint_event: The event we have: diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 05b176c82cc5..c6d455f2bb92 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1245,7 +1245,7 @@ int usb_string_id(struct usb_composite_dev *cdev) EXPORT_SYMBOL_GPL(usb_string_id); /** - * usb_string_ids() - allocate unused string IDs in batch + * usb_string_ids_tab() - allocate unused string IDs in batch * @cdev: the device whose string descriptor IDs are being allocated * @str: an array of usb_string objects to assign numbers to * Context: single threaded during gadget setup diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index b069a5122aaa..cf720e944aaa 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -71,7 +71,7 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode) EXPORT_SYMBOL_GPL(fwnode_typec_switch_get); /** - * typec_put_switch - Release USB Type-C orientation switch + * typec_switch_put - Release USB Type-C orientation switch * @sw: USB Type-C orientation switch * * Decrement reference count for @sw. diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 2040696d75b6..a2d229ab63ba 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -437,7 +437,7 @@ static inline struct usb_composite_driver *to_cdriver( #define OS_STRING_IDX 0xEE /** - * struct usb_composite_device - represents one composite usb gadget + * struct usb_composite_dev - represents one composite usb gadget * @gadget: read-only, abstracts the gadget's usb peripheral controller * @req: used for control responses; buffer is pre-allocated * @os_desc_req: used for OS descriptors responses; buffer is pre-allocated -- cgit v1.2.3 From 3cd54a618834430a26a648d880dd83d740f2ae30 Mon Sep 17 00:00:00 2001 From: Ran Wang Date: Sat, 10 Oct 2020 14:03:08 +0800 Subject: usb: host: fsl-mph-dr-of: check return of dma_set_mask() fsl_usb2_device_register() should stop init if dma_set_mask() return error. Fixes: cae058610465 ("drivers/usb/host: fsl: Set DMA_MASK of usb platform device") Reviewed-by: Peter Chen Signed-off-by: Ran Wang Link: https://lore.kernel.org/r/20201010060308.33693-1-ran.wang_1@nxp.com Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/fsl-mph-dr-of.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index ae8f60f6e6a5..44a7e58a26e3 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -94,10 +94,13 @@ static struct platform_device *fsl_usb2_device_register( pdev->dev.coherent_dma_mask = ofdev->dev.coherent_dma_mask; - if (!pdev->dev.dma_mask) + if (!pdev->dev.dma_mask) { pdev->dev.dma_mask = &ofdev->dev.coherent_dma_mask; - else - dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + } else { + retval = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (retval) + goto error; + } retval = platform_device_add_data(pdev, pdata, sizeof(*pdata)); if (retval) -- cgit v1.2.3 From 38203b8385bf6283537162bde7d499f830964711 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 19 Oct 2020 19:07:02 +0200 Subject: usb: cdc-acm: fix cooldown mechanism Commit a4e7279cd1d1 ("cdc-acm: introduce a cool down") is causing regression if there is some USB error, such as -EPROTO. This has been reported on some samples of the Odroid-N2 using the Combee II Zibgee USB dongle. > struct acm *acm = container_of(work, struct acm, work) is incorrect in case of a delayed work and causes warnings, usually from the workqueue: > WARNING: CPU: 0 PID: 0 at kernel/workqueue.c:1474 __queue_work+0x480/0x528. When this happens, USB eventually stops working completely after a while. Also the ACM_ERROR_DELAY bit is never set, so the cooldown mechanism previously introduced cannot be triggered and acm_submit_read_urb() is never called. This changes makes the cdc-acm driver use a single delayed work, fixing the pointer arithmetic in acm_softint() and set the ACM_ERROR_DELAY when the cooldown mechanism appear to be needed. Fixes: a4e7279cd1d1 ("cdc-acm: introduce a cool down") Cc: Oliver Neukum Reported-by: Pascal Vizeli Acked-by: Oliver Neukum Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20201019170702.150534-1-jbrunet@baylibre.com Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 12 +++++------- drivers/usb/class/cdc-acm.h | 3 +-- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 30ef946a8e1a..1e7568867910 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -508,6 +508,7 @@ static void acm_read_bulk_callback(struct urb *urb) "%s - cooling babbling device\n", __func__); usb_mark_last_busy(acm->dev); set_bit(rb->index, &acm->urbs_in_error_delay); + set_bit(ACM_ERROR_DELAY, &acm->flags); cooldown = true; break; default: @@ -533,7 +534,7 @@ static void acm_read_bulk_callback(struct urb *urb) if (stopped || stalled || cooldown) { if (stalled) - schedule_work(&acm->work); + schedule_delayed_work(&acm->dwork, 0); else if (cooldown) schedule_delayed_work(&acm->dwork, HZ / 2); return; @@ -563,13 +564,13 @@ static void acm_write_bulk(struct urb *urb) acm_write_done(acm, wb); spin_unlock_irqrestore(&acm->write_lock, flags); set_bit(EVENT_TTY_WAKEUP, &acm->flags); - schedule_work(&acm->work); + schedule_delayed_work(&acm->dwork, 0); } static void acm_softint(struct work_struct *work) { int i; - struct acm *acm = container_of(work, struct acm, work); + struct acm *acm = container_of(work, struct acm, dwork.work); if (test_bit(EVENT_RX_STALL, &acm->flags)) { smp_mb(); /* against acm_suspend() */ @@ -585,7 +586,7 @@ static void acm_softint(struct work_struct *work) if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) { for (i = 0; i < acm->rx_buflimit; i++) if (test_and_clear_bit(i, &acm->urbs_in_error_delay)) - acm_submit_read_urb(acm, i, GFP_NOIO); + acm_submit_read_urb(acm, i, GFP_KERNEL); } if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags)) @@ -1351,7 +1352,6 @@ made_compressed_probe: acm->ctrlsize = ctrlsize; acm->readsize = readsize; acm->rx_buflimit = num_rx_buf; - INIT_WORK(&acm->work, acm_softint); INIT_DELAYED_WORK(&acm->dwork, acm_softint); init_waitqueue_head(&acm->wioctl); spin_lock_init(&acm->write_lock); @@ -1561,7 +1561,6 @@ static void acm_disconnect(struct usb_interface *intf) } acm_kill_urbs(acm); - cancel_work_sync(&acm->work); cancel_delayed_work_sync(&acm->dwork); tty_unregister_device(acm_tty_driver, acm->minor); @@ -1604,7 +1603,6 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message) return 0; acm_kill_urbs(acm); - cancel_work_sync(&acm->work); cancel_delayed_work_sync(&acm->dwork); acm->urbs_in_error_delay = 0; diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 9dce179d031b..8aef5eb769a0 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -112,8 +112,7 @@ struct acm { # define ACM_ERROR_DELAY 3 unsigned long urbs_in_error_delay; /* these need to be restarted after a delay */ struct usb_cdc_line_coding line; /* bits, stop, parity */ - struct work_struct work; /* work queue entry for various purposes*/ - struct delayed_work dwork; /* for cool downs needed in error recovery */ + struct delayed_work dwork; /* work queue entry for various purposes */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ struct async_icount iocount; /* counters for control line changes */ -- cgit v1.2.3 From 2d9c6442a9c81f4f8dee678d0b3c183173ab1e2d Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 12 Oct 2020 19:03:12 +0800 Subject: usb: typec: tcpm: reset hard_reset_count for any disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current tcpm_detach() only reset hard_reset_count if port->attached is true, this may cause this counter clear is missed if the CC disconnect event is generated after tcpm_port_reset() is done by other events, e.g. VBUS off comes first before CC disconect for a power sink, in that case the first tcpm_detach() will only clear port->attached flag but leave hard_reset_count there because tcpm_port_is_disconnected() is still false, then later tcpm_detach() by CC disconnect will directly return due to port->attached is cleared, finally this will result tcpm will not try hard reset or error recovery for later attach. ChiYuan reported this issue on his platform with below tcpm trace: After power sink session setup after hard reset 2 times, detach from the power source and then attach: [ 4848.046358] VBUS off [ 4848.046384] state change SNK_READY -> SNK_UNATTACHED [ 4848.050908] Setting voltage/current limit 0 mV 0 mA [ 4848.050936] polarity 0 [ 4848.052593] Requesting mux state 0, usb-role 0, orientation 0 [ 4848.053222] Start toggling [ 4848.086500] state change SNK_UNATTACHED -> TOGGLING [ 4848.089983] CC1: 0 -> 0, CC2: 3 -> 3 [state TOGGLING, polarity 0, connected] [ 4848.089993] state change TOGGLING -> SNK_ATTACH_WAIT [ 4848.090031] pending state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED @200 ms [ 4848.141162] CC1: 0 -> 0, CC2: 3 -> 0 [state SNK_ATTACH_WAIT, polarity 0, disconnected] [ 4848.141170] state change SNK_ATTACH_WAIT -> SNK_ATTACH_WAIT [ 4848.141184] pending state change SNK_ATTACH_WAIT -> SNK_UNATTACHED @20 ms [ 4848.163156] state change SNK_ATTACH_WAIT -> SNK_UNATTACHED [delayed 20 ms] [ 4848.163162] Start toggling [ 4848.216918] CC1: 0 -> 0, CC2: 0 -> 3 [state TOGGLING, polarity 0, connected] [ 4848.216954] state change TOGGLING -> SNK_ATTACH_WAIT [ 4848.217080] pending state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED @200 ms [ 4848.231771] CC1: 0 -> 0, CC2: 3 -> 0 [state SNK_ATTACH_WAIT, polarity 0, disconnected] [ 4848.231800] state change SNK_ATTACH_WAIT -> SNK_ATTACH_WAIT [ 4848.231857] pending state change SNK_ATTACH_WAIT -> SNK_UNATTACHED @20 ms [ 4848.256022] state change SNK_ATTACH_WAIT -> SNK_UNATTACHED [delayed20 ms] [ 4848.256049] Start toggling [ 4848.871148] VBUS on [ 4848.885324] CC1: 0 -> 0, CC2: 0 -> 3 [state TOGGLING, polarity 0, connected] [ 4848.885372] state change TOGGLING -> SNK_ATTACH_WAIT [ 4848.885548] pending state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED @200 ms [ 4849.088240] state change SNK_ATTACH_WAIT -> SNK_DEBOUNCED [delayed200 ms] [ 4849.088284] state change SNK_DEBOUNCED -> SNK_ATTACHED [ 4849.088291] polarity 1 [ 4849.088769] Requesting mux state 1, usb-role 2, orientation 2 [ 4849.088895] state change SNK_ATTACHED -> SNK_STARTUP [ 4849.088907] state change SNK_STARTUP -> SNK_DISCOVERY [ 4849.088915] Setting voltage/current limit 5000 mV 0 mA [ 4849.088927] vbus=0 charge:=1 [ 4849.090505] state change SNK_DISCOVERY -> SNK_WAIT_CAPABILITIES [ 4849.090828] pending state change SNK_WAIT_CAPABILITIES -> SNK_READY @240 ms [ 4849.335878] state change SNK_WAIT_CAPABILITIES -> SNK_READY [delayed240 ms] this patch fix this issue by clear hard_reset_count at any cases of cc disconnect, í.e. don't check port->attached flag. Fixes: 4b4e02c83167 ("typec: tcpm: Move out of staging") Cc: stable@vger.kernel.org Reported-and-tested-by: ChiYuan Huang Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Signed-off-by: Li Jun Link: https://lore.kernel.org/r/1602500592-3817-1-git-send-email-jun.li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 55535c4f66bf..a6fae1f86505 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -2890,6 +2890,9 @@ static void tcpm_reset_port(struct tcpm_port *port) static void tcpm_detach(struct tcpm_port *port) { + if (tcpm_port_is_disconnected(port)) + port->hard_reset_count = 0; + if (!port->attached) return; @@ -2898,9 +2901,6 @@ static void tcpm_detach(struct tcpm_port *port) port->tcpc->set_bist_data(port->tcpc, false); } - if (tcpm_port_is_disconnected(port)) - port->hard_reset_count = 0; - tcpm_reset_port(port); } -- cgit v1.2.3 From b9c6f782150c9992308f4474f920586eaaca4cf1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Oct 2020 14:40:17 +0300 Subject: usb: typec: stusb160x: fix an IS_ERR() vs NULL check in probe The typec_register_port() function doesn't return NULL, it returns error pointers. Fixes: da0cb6310094 ("usb: typec: add support for STUSB160x Type-C controller family") Signed-off-by: Dan Carpenter Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20201023114017.GE18329@kadam Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/stusb160x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index 7143852ce94d..da7f1957bcb3 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -730,8 +730,8 @@ static int stusb160x_probe(struct i2c_client *client) } chip->port = typec_register_port(chip->dev, &chip->capability); - if (!chip->port) { - ret = -ENODEV; + if (IS_ERR(chip->port)) { + ret = PTR_ERR(chip->port); goto all_reg_disable; } -- cgit v1.2.3 From 32d174d2d5eb318c34ff36771adefabdf227c186 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Mon, 26 Oct 2020 17:06:57 +0800 Subject: usb: host: ehci-tegra: Fix error handling in tegra_ehci_probe() If the function platform_get_irq() failed, the negative value returned will not be detected here. So fix error handling in tegra_ehci_probe(). Fixes: 79ad3b5add4a ("usb: host: Add EHCI driver for NVIDIA Tegra SoCs") Acked-by: Alan Stern Acked-by: Thierry Reding Signed-off-by: Tang Bin Link: https://lore.kernel.org/r/20201026090657.49988-1-tangbin@cmss.chinamobile.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index e077b2ca53c5..869d9c4de5fc 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -479,8 +479,8 @@ static int tegra_ehci_probe(struct platform_device *pdev) u_phy->otg->host = hcd_to_bus(hcd); irq = platform_get_irq(pdev, 0); - if (!irq) { - err = -ENODEV; + if (irq < 0) { + err = irq; goto cleanup_phy; } -- cgit v1.2.3 From 0942d59b0af46511d59dbf5bd69ec4a64d1a854c Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Thu, 22 Oct 2020 09:55:20 -0400 Subject: usbcore: Check both id_table and match() when both available From: Bastien Nocera When a USB device driver has both an id_table and a match() function, make sure to check both to find a match, first matching the id_table, then checking the match() function. This makes it possible to have module autoloading done through the id_table when devices are plugged in, before checking for further device eligibility in the match() function. Cc: # 5.8 Cc: Greg Kroah-Hartman Cc: Alan Stern Co-developed-by: M. Vefa Bicakci Tested-by: Bastien Nocera Signed-off-by: Bastien Nocera Signed-off-by: M. Vefa Bicakci Tested-by: Pan (Pany) YUAN Link: https://lore.kernel.org/r/20201022135521.375211-2-m.v.b@runbox.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 30 +++++++++++++++++++++--------- drivers/usb/core/generic.c | 4 +--- drivers/usb/core/usb.h | 2 ++ 3 files changed, 24 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 98b7449c11f3..4dfa44d6cc3c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -839,6 +839,22 @@ const struct usb_device_id *usb_device_match_id(struct usb_device *udev, return NULL; } +bool usb_driver_applicable(struct usb_device *udev, + struct usb_device_driver *udrv) +{ + if (udrv->id_table && udrv->match) + return usb_device_match_id(udev, udrv->id_table) != NULL && + udrv->match(udev); + + if (udrv->id_table) + return usb_device_match_id(udev, udrv->id_table) != NULL; + + if (udrv->match) + return udrv->match(udev); + + return false; +} + static int usb_device_match(struct device *dev, struct device_driver *drv) { /* devices and interfaces are handled separately */ @@ -853,17 +869,14 @@ static int usb_device_match(struct device *dev, struct device_driver *drv) udev = to_usb_device(dev); udrv = to_usb_device_driver(drv); - if (udrv->id_table) - return usb_device_match_id(udev, udrv->id_table) != NULL; - - if (udrv->match) - return udrv->match(udev); - /* If the device driver under consideration does not have a * id_table or a match function, then let the driver's probe * function decide. */ - return 1; + if (!udrv->id_table && !udrv->match) + return 1; + + return usb_driver_applicable(udev, udrv); } else if (is_usb_interface(dev)) { struct usb_interface *intf; @@ -941,8 +954,7 @@ static int __usb_bus_reprobe_drivers(struct device *dev, void *data) return 0; udev = to_usb_device(dev); - if (usb_device_match_id(udev, new_udriver->id_table) == NULL && - (!new_udriver->match || new_udriver->match(udev) == 0)) + if (!usb_driver_applicable(udev, new_udriver)) return 0; ret = device_reprobe(dev); diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 22c887f5c497..26f9fb9f67ca 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -205,9 +205,7 @@ static int __check_for_non_generic_match(struct device_driver *drv, void *data) udrv = to_usb_device_driver(drv); if (udrv == &usb_generic_driver) return 0; - if (usb_device_match_id(udev, udrv->id_table) != NULL) - return 1; - return (udrv->match && udrv->match(udev)); + return usb_driver_applicable(udev, udrv); } static bool usb_generic_driver_match(struct usb_device *udev) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index c893f54a3420..82538daac8b8 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -74,6 +74,8 @@ extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); extern const struct usb_device_id *usb_device_match_id(struct usb_device *udev, const struct usb_device_id *id); +extern bool usb_driver_applicable(struct usb_device *udev, + struct usb_device_driver *udrv); extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev); -- cgit v1.2.3 From 0cb686692fd200db12dcfb8231e793c1c98aec41 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Thu, 22 Oct 2020 09:55:21 -0400 Subject: USB: apple-mfi-fastcharge: don't probe unhandled devices From: Bastien Nocera Contrary to the comment above the id table, we didn't implement a match function. This meant that every single Apple device that was already plugged in to the computer would have its device driver reprobed when the apple-mfi-fastcharge driver was loaded, eg. the SD card reader could be reprobed when the apple-mfi-fastcharge after pivoting root during boot up and the module became available. Make sure that the driver probe isn't being run for unsupported devices by adding a match function that checks the product ID, in addition to the id_table checking the vendor ID. Link: https://bugzilla.redhat.com/show_bug.cgi?id=1878347 Link: https://lore.kernel.org/linux-usb/CAE3RAxt0WhBEz8zkHrVO5RiyEOasayy1QUAjsv-pB0fAbY1GSw@mail.gmail.com/ Fixes: 249fa8217b84 ("USB: Add driver to control USB fast charge for iOS devices") Cc: # 5.8 Cc: Greg Kroah-Hartman Cc: Alan Stern [m.v.b: Add Link and Reported-by tags to the commit message] Reported-by: Pany Tested-by: Pan (Pany) YUAN Tested-by: Bastien Nocera Signed-off-by: Bastien Nocera Signed-off-by: M. Vefa Bicakci Link: https://lore.kernel.org/r/20201022135521.375211-3-m.v.b@runbox.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/apple-mfi-fastcharge.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index b403094a6b3a..579d8c84de42 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -163,17 +163,23 @@ static const struct power_supply_desc apple_mfi_fc_desc = { .property_is_writeable = apple_mfi_fc_property_is_writeable }; +static bool mfi_fc_match(struct usb_device *udev) +{ + int idProduct; + + idProduct = le16_to_cpu(udev->descriptor.idProduct); + /* See comment above mfi_fc_id_table[] */ + return (idProduct >= 0x1200 && idProduct <= 0x12ff); +} + static int mfi_fc_probe(struct usb_device *udev) { struct power_supply_config battery_cfg = {}; struct mfi_device *mfi = NULL; - int err, idProduct; + int err; - idProduct = le16_to_cpu(udev->descriptor.idProduct); - /* See comment above mfi_fc_id_table[] */ - if (idProduct < 0x1200 || idProduct > 0x12ff) { + if (!mfi_fc_match(udev)) return -ENODEV; - } mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); if (!mfi) { @@ -220,6 +226,7 @@ static struct usb_device_driver mfi_fc_driver = { .probe = mfi_fc_probe, .disconnect = mfi_fc_disconnect, .id_table = mfi_fc_id_table, + .match = mfi_fc_match, .generic_subclass = 1, }; -- cgit v1.2.3 From 82776f6c75a90e1d2103e689b84a689de8f1aa02 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 18 Oct 2020 09:42:04 +0100 Subject: tty: serial: 21285: fix lockup on open Commit 293f89959483 ("tty: serial: 21285: stop using the unused[] variable from struct uart_port") introduced a bug which stops the transmit interrupt being disabled when there are no characters to transmit - disabling the transmit interrupt at the interrupt controller is the only way to stop an interrupt storm. If this interrupt is not disabled when there are no transmit characters, we end up with an interrupt storm which prevents the machine making forward progress. Fixes: 293f89959483 ("tty: serial: 21285: stop using the unused[] variable from struct uart_port") Signed-off-by: Russell King Cc: stable Link: https://lore.kernel.org/r/E1kU4GS-0006lE-OO@rmk-PC.armlinux.org.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/21285.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c index 718e010fcb04..09baef4ccc39 100644 --- a/drivers/tty/serial/21285.c +++ b/drivers/tty/serial/21285.c @@ -50,25 +50,25 @@ static const char serial21285_name[] = "Footbridge UART"; static bool is_enabled(struct uart_port *port, int bit) { - unsigned long private_data = (unsigned long)port->private_data; + unsigned long *private_data = (unsigned long *)&port->private_data; - if (test_bit(bit, &private_data)) + if (test_bit(bit, private_data)) return true; return false; } static void enable(struct uart_port *port, int bit) { - unsigned long private_data = (unsigned long)port->private_data; + unsigned long *private_data = (unsigned long *)&port->private_data; - set_bit(bit, &private_data); + set_bit(bit, private_data); } static void disable(struct uart_port *port, int bit) { - unsigned long private_data = (unsigned long)port->private_data; + unsigned long *private_data = (unsigned long *)&port->private_data; - clear_bit(bit, &private_data); + clear_bit(bit, private_data); } #define is_tx_enabled(port) is_enabled(port, tx_enabled_bit) -- cgit v1.2.3 From c97f2a6fb3dfbfbbc88edc8ea62ef2b944e18849 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 23 Oct 2020 04:34:29 +0300 Subject: tty: serial: fsl_lpuart: LS1021A has a FIFO size of 16 words, like LS1028A MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior to the commit that this one fixes, the FIFO size was derived from the read-only register LPUARTx_FIFO[TXFIFOSIZE] using the following formula: TX FIFO size = 2 ^ (LPUARTx_FIFO[TXFIFOSIZE] - 1) The documentation for LS1021A is a mess. Under chapter 26.1.3 LS1021A LPUART module special consideration, it mentions TXFIFO_SZ and RXFIFO_SZ being equal to 4, and in the register description for LPUARTx_FIFO, it shows the out-of-reset value of TXFIFOSIZE and RXFIFOSIZE fields as "011", even though these registers read as "101" in reality. And when LPUART on LS1021A was working, the "101" value did correspond to "16 datawords", by applying the formula above, even though the documentation is wrong again (!!!!) and says that "101" means 64 datawords (hint: it doesn't). So the "new" formula created by commit f77ebb241ce0 has all the premises of being wrong for LS1021A, because it relied only on false data and no actual experimentation. Interestingly, in commit c2f448cff22a ("tty: serial: fsl_lpuart: add LS1028A support"), Michael Walle applied a workaround to this by manually setting the FIFO widths for LS1028A. It looks like the same values are used by LS1021A as well, in fact. When the driver thinks that it has a deeper FIFO than it really has, getty (user space) output gets truncated. Many thanks to Michael for pointing out where to look. Fixes: f77ebb241ce0 ("tty: serial: fsl_lpuart: correct the FIFO depth size") Suggested-by: Michael Walle Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20201023013429.3551026-1-vladimir.oltean@nxp.com Reviewed-by:Fugang Duan Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index ff4b88c637d0..bd047e1f9bea 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -314,9 +314,10 @@ MODULE_DEVICE_TABLE(of, lpuart_dt_ids); /* Forward declare this for the dma callbacks*/ static void lpuart_dma_tx_complete(void *arg); -static inline bool is_ls1028a_lpuart(struct lpuart_port *sport) +static inline bool is_layerscape_lpuart(struct lpuart_port *sport) { - return sport->devtype == LS1028A_LPUART; + return (sport->devtype == LS1021A_LPUART || + sport->devtype == LS1028A_LPUART); } static inline bool is_imx8qxp_lpuart(struct lpuart_port *sport) @@ -1701,11 +1702,11 @@ static int lpuart32_startup(struct uart_port *port) UARTFIFO_FIFOSIZE_MASK); /* - * The LS1028A has a fixed length of 16 words. Although it supports the - * RX/TXSIZE fields their encoding is different. Eg the reference manual - * states 0b101 is 16 words. + * The LS1021A and LS1028A have a fixed FIFO depth of 16 words. + * Although they support the RX/TXSIZE fields, their encoding is + * different. Eg the reference manual states 0b101 is 16 words. */ - if (is_ls1028a_lpuart(sport)) { + if (is_layerscape_lpuart(sport)) { sport->rxfifo_size = 16; sport->txfifo_size = 16; sport->port.fifosize = sport->txfifo_size; -- cgit v1.2.3 From 6ca03f90527e499dd5e32d6522909e2ad390896b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 19 Oct 2020 10:55:16 +0200 Subject: vt: keyboard, simplify vt_kdgkbsent Use 'strlen' of the string, add one for NUL terminator and simply do 'copy_to_user' instead of the explicit 'for' loop. This makes the KDGKBSENT case more compact. The only thing we need to take care about is NULL 'func_table[i]'. Use an empty string in that case. The original check for overflow could never trigger as the func_buf strings are always shorter or equal to 'struct kbsentry's. Cc: Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20201019085517.10176-1-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 0db53b5b3acf..7bfa95c3252c 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1995,9 +1995,7 @@ out: int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) { struct kbsentry *kbs; - char *p; u_char *q; - u_char __user *up; int sz, fnw_sz; int delta; char *first_free, *fj, *fnw; @@ -2023,23 +2021,15 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) i = array_index_nospec(kbs->kb_func, MAX_NR_FUNC); switch (cmd) { - case KDGKBSENT: - sz = sizeof(kbs->kb_string) - 1; /* sz should have been - a struct member */ - up = user_kdgkb->kb_string; - p = func_table[i]; - if(p) - for ( ; *p && sz; p++, sz--) - if (put_user(*p, up++)) { - ret = -EFAULT; - goto reterr; - } - if (put_user('\0', up)) { - ret = -EFAULT; - goto reterr; - } - kfree(kbs); - return ((p && *p) ? -EOVERFLOW : 0); + case KDGKBSENT: { + /* size should have been a struct member */ + unsigned char *from = func_table[i] ? : ""; + + ret = copy_to_user(user_kdgkb->kb_string, from, + strlen(from) + 1) ? -EFAULT : 0; + + goto reterr; + } case KDSKBSENT: if (!perm) { ret = -EPERM; -- cgit v1.2.3 From 82e61c3909db51d91b9d3e2071557b6435018b80 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 19 Oct 2020 10:55:17 +0200 Subject: vt: keyboard, extend func_buf_lock to readers Both read-side users of func_table/func_buf need locking. Without that, one can easily confuse the code by repeatedly setting altering strings like: while (1) for (a = 0; a < 2; a++) { struct kbsentry kbs = {}; strcpy((char *)kbs.kb_string, a ? ".\n" : "88888\n"); ioctl(fd, KDSKBSENT, &kbs); } When that program runs, one can get unexpected output by holding F1 (note the unxpected period on the last line): . 88888 .8888 So protect all accesses to 'func_table' (and func_buf) by preexisting 'func_buf_lock'. It is easy in 'k_fn' handler as 'puts_queue' is expected not to sleep. On the other hand, KDGKBSENT needs a local (atomic) copy of the string because copy_to_user can sleep. Use already allocated, but unused 'kbs->kb_string' for that purpose. Note that the program above needs at least CAP_SYS_TTY_CONFIG. This depends on the previous patch and on the func_buf_lock lock added in commit 46ca3f735f34 (tty/vt: fix write/write race in ioctl(KDSKBSENT) handler) in 5.2. Likely fixes CVE-2020-25656. Cc: Reported-by: Minh Yuan Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20201019085517.10176-2-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 7bfa95c3252c..78acc270e39a 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -743,8 +743,13 @@ static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) return; if ((unsigned)value < ARRAY_SIZE(func_table)) { + unsigned long flags; + + spin_lock_irqsave(&func_buf_lock, flags); if (func_table[value]) puts_queue(vc, func_table[value]); + spin_unlock_irqrestore(&func_buf_lock, flags); + } else pr_err("k_fn called with value=%d\n", value); } @@ -1991,7 +1996,7 @@ out: #undef s #undef v -/* FIXME: This one needs untangling and locking */ +/* FIXME: This one needs untangling */ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) { struct kbsentry *kbs; @@ -2023,10 +2028,14 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) switch (cmd) { case KDGKBSENT: { /* size should have been a struct member */ - unsigned char *from = func_table[i] ? : ""; + ssize_t len = sizeof(user_kdgkb->kb_string); + + spin_lock_irqsave(&func_buf_lock, flags); + len = strlcpy(kbs->kb_string, func_table[i] ? : "", len); + spin_unlock_irqrestore(&func_buf_lock, flags); - ret = copy_to_user(user_kdgkb->kb_string, from, - strlen(from) + 1) ? -EFAULT : 0; + ret = copy_to_user(user_kdgkb->kb_string, kbs->kb_string, + len + 1) ? -EFAULT : 0; goto reterr; } -- cgit v1.2.3 From d54654790302ccaa72589380dce060d376ef8716 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 26 Oct 2020 06:54:19 +0100 Subject: vt_ioctl: fix GIO_UNIMAP regression In commit 5ba127878722, we shuffled with the check of 'perm'. But my brain somehow inverted the condition in 'do_unimap_ioctl' (I thought it is ||, not &&), so GIO_UNIMAP stopped working completely. Move the 'perm' checks back to do_unimap_ioctl and do them right again. In fact, this reverts this part of code to the pre-5ba127878722 state. Except 'perm' is now a bool. Fixes: 5ba127878722 ("vt_ioctl: move perm checks level up") Cc: stable@vger.kernel.org Reported-by: Fabian Vogt Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20201026055419.30518-1-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt_ioctl.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 0a33b8ababe3..2321775ef098 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -549,7 +549,7 @@ static int vt_io_fontreset(struct console_font_op *op) } static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, - struct vc_data *vc) + bool perm, struct vc_data *vc) { struct unimapdesc tmp; @@ -557,9 +557,11 @@ static inline int do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, return -EFAULT; switch (cmd) { case PIO_UNIMAP: + if (!perm) + return -EPERM; return con_set_unimap(vc, tmp.entry_ct, tmp.entries); case GIO_UNIMAP: - if (fg_console != vc->vc_num) + if (!perm && fg_console != vc->vc_num) return -EPERM; return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp.entries); @@ -639,10 +641,7 @@ static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up, case PIO_UNIMAP: case GIO_UNIMAP: - if (!perm) - return -EPERM; - - return do_unimap_ioctl(cmd, up, vc); + return do_unimap_ioctl(cmd, up, perm, vc); default: return -ENOIOCTLCMD; -- cgit v1.2.3 From a2267f8a52eea9096861affd463f691be0f0e8c9 Mon Sep 17 00:00:00 2001 From: Alok Prasad Date: Wed, 21 Oct 2020 11:50:08 +0000 Subject: RDMA/qedr: Fix memory leak in iWARP CM Fixes memory leak in iWARP CM Fixes: e411e0587e0d ("RDMA/qedr: Add iWARP connection management functions") Link: https://lore.kernel.org/r/20201021115008.28138-1-palok@marvell.com Signed-off-by: Michal Kalderon Signed-off-by: Igor Russkikh Signed-off-by: Alok Prasad Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/qedr/qedr_iw_cm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/infiniband/hw/qedr/qedr_iw_cm.c b/drivers/infiniband/hw/qedr/qedr_iw_cm.c index c7169d2c69e5..c4bc58736e48 100644 --- a/drivers/infiniband/hw/qedr/qedr_iw_cm.c +++ b/drivers/infiniband/hw/qedr/qedr_iw_cm.c @@ -727,6 +727,7 @@ int qedr_iw_destroy_listen(struct iw_cm_id *cm_id) listener->qed_handle); cm_id->rem_ref(cm_id); + kfree(listener); return rc; } -- cgit v1.2.3 From 136a4dfe87cd34db2153330aa84dbbc210eb36b6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 26 Oct 2020 22:28:40 +0100 Subject: ACPI: button: Drop no longer necessary Acer SW5-012 lid_init_state quirk Commit 78a5b53e9fb4 ("Input: soc_button_array - work around DSDTs which modify the irqflags") adds a workaround for DSDTs with a _LID method which play tricks with the irqflags, assuming that the OS is using an irq-type of IRQ_TYPE_LEVEL_LOW. Now that this workaround is in place, we no longer need to disable the lid functionality on the Acer SW5-012. Fixes: 78a5b53e9fb4 ("Input: soc_button_array - work around DSDTs which modify the irqflags") Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki --- drivers/acpi/button.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index da4b125ab4c3..0761529cac05 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -73,19 +73,6 @@ MODULE_DEVICE_TABLE(acpi, button_device_ids); /* Please keep this list sorted alphabetically by vendor and model */ static const struct dmi_system_id dmi_lid_quirks[] = { - { - /* - * Acer Switch 10 SW5-012. _LID method messes with home and - * power button GPIO IRQ settings causing an interrupt storm on - * both GPIOs. This is unfixable without a DSDT override, so we - * have to disable the lid-switch functionality altogether :| - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), - }, - .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, - }, { /* GP-electronic T701, _LID method points to a floating GPIO */ .matches = { -- cgit v1.2.3 From 8e4c309f9f33b76c09daa02b796ef87918eee494 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 8 May 2020 07:28:19 +0200 Subject: ata: sata_nv: Fix retrieving of active qcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ata_qc_complete_multiple() has to be called with the tags physically active, that is the hw tag is at bit 0. ap->qc_active has the same tag at bit ATA_TAG_INTERNAL instead, so call ata_qc_get_active() to fix that up. This is done in the vein of 8385d756e114 ("libata: Fix retrieving of active qcs"). Fixes: 28361c403683 ("libata: add extra internal command") Tested-by: Pali Rohár Signed-off-by: Sascha Hauer Signed-off-by: Jens Axboe --- drivers/ata/sata_nv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index eb9dc14e5147..20190f66ced9 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -2100,7 +2100,7 @@ static int nv_swncq_sdbfis(struct ata_port *ap) pp->dhfis_bits &= ~done_mask; pp->dmafis_bits &= ~done_mask; pp->sdbfis_bits |= done_mask; - ata_qc_complete_multiple(ap, ap->qc_active ^ done_mask); + ata_qc_complete_multiple(ap, ata_qc_get_active(ap) ^ done_mask); if (!ap->qc_active) { DPRINTK("over\n"); -- cgit v1.2.3 From d73e873bcfff86b0de7dae2610131b50a2970f88 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 23 Oct 2020 18:33:13 +0200 Subject: regulator: fix a kernel-doc markup It seems that the function was renamed. kernel-doc markup should follow it. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/dffad16d4d6427d7d0fc89797e4126fe7c69d5de.1603469755.git.mchehab+huawei@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a4ffd71696da..79d00e7039d9 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5529,7 +5529,7 @@ void regulator_set_drvdata(struct regulator *regulator, void *data) EXPORT_SYMBOL_GPL(regulator_set_drvdata); /** - * regulator_get_id - get regulator ID + * rdev_get_id - get regulator ID * @rdev: regulator */ int rdev_get_id(struct regulator_dev *rdev) -- cgit v1.2.3 From 00d4394792418f8fe968f0cb22557053c6310010 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Tue, 27 Oct 2020 11:59:34 -0700 Subject: cpufreq: speedstep: remove unneeded semicolon A semicolon is not needed after a switch statement. Signed-off-by: Tom Rix Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/speedstep-lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/speedstep-lib.c b/drivers/cpufreq/speedstep-lib.c index a13a2d1e444e..0b66df4ed513 100644 --- a/drivers/cpufreq/speedstep-lib.c +++ b/drivers/cpufreq/speedstep-lib.c @@ -240,7 +240,7 @@ unsigned int speedstep_get_frequency(enum speedstep_processor processor) return pentium3_get_frequency(processor); default: return 0; - }; + } return 0; } EXPORT_SYMBOL_GPL(speedstep_get_frequency); -- cgit v1.2.3 From 80ade22c06ca115b81dd168e99479c8e09843513 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Tue, 27 Oct 2020 20:14:15 -0700 Subject: misc: mic: remove the MIC drivers This patch removes the MIC drivers from the kernel tree since the corresponding devices have been discontinued. Removing the dma and char-misc changes in one patch and merging via the char-misc tree is best to avoid any potential build breakage. Cc: Nikhil Rao Reviewed-by: Ashutosh Dixit Signed-off-by: Sudeep Dutt Acked-By: Vinod Koul Reviewed-by: Sherry Sun Link: https://lore.kernel.org/r/8c1443136563de34699d2c084df478181c205db4.1603854416.git.sudeep.dutt@intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/misc-devices/mic/index.rst | 16 - Documentation/misc-devices/mic/mic_overview.rst | 85 - Documentation/misc-devices/mic/scif_overview.rst | 108 -- MAINTAINERS | 16 - drivers/dma/Kconfig | 18 - drivers/dma/Makefile | 1 - drivers/dma/mic_x100_dma.c | 770 --------- drivers/dma/mic_x100_dma.h | 275 --- drivers/misc/Kconfig | 1 - drivers/misc/Makefile | 1 - drivers/misc/mic/Kconfig | 141 -- drivers/misc/mic/Makefile | 12 - drivers/misc/mic/bus/Makefile | 9 - drivers/misc/mic/bus/cosm_bus.c | 130 -- drivers/misc/mic/bus/cosm_bus.h | 125 -- drivers/misc/mic/bus/mic_bus.c | 194 --- drivers/misc/mic/bus/scif_bus.c | 201 --- drivers/misc/mic/bus/scif_bus.h | 125 -- drivers/misc/mic/bus/vop_bus.c | 194 --- drivers/misc/mic/bus/vop_bus.h | 129 -- drivers/misc/mic/card/Makefile | 11 - drivers/misc/mic/card/mic_debugfs.c | 85 - drivers/misc/mic/card/mic_device.c | 417 ----- drivers/misc/mic/card/mic_device.h | 137 -- drivers/misc/mic/card/mic_x100.c | 347 ---- drivers/misc/mic/card/mic_x100.h | 37 - drivers/misc/mic/common/mic_dev.h | 55 - drivers/misc/mic/cosm/Makefile | 11 - drivers/misc/mic/cosm/cosm_debugfs.c | 116 -- drivers/misc/mic/cosm/cosm_main.c | 382 ----- drivers/misc/mic/cosm/cosm_main.h | 61 - drivers/misc/mic/cosm/cosm_scif_server.c | 399 ----- drivers/misc/mic/cosm/cosm_sysfs.c | 449 ----- drivers/misc/mic/cosm_client/Makefile | 8 - drivers/misc/mic/cosm_client/cosm_scif_client.c | 269 --- drivers/misc/mic/host/Makefile | 12 - drivers/misc/mic/host/mic_boot.c | 588 ------- drivers/misc/mic/host/mic_debugfs.c | 149 -- drivers/misc/mic/host/mic_device.h | 157 -- drivers/misc/mic/host/mic_intr.c | 635 ------- drivers/misc/mic/host/mic_intr.h | 137 -- drivers/misc/mic/host/mic_main.c | 335 ---- drivers/misc/mic/host/mic_smpt.c | 427 ----- drivers/misc/mic/host/mic_smpt.h | 87 - drivers/misc/mic/host/mic_x100.c | 585 ------- drivers/misc/mic/host/mic_x100.h | 77 - drivers/misc/mic/scif/Makefile | 21 - drivers/misc/mic/scif/scif_api.c | 1485 ----------------- drivers/misc/mic/scif/scif_debugfs.c | 116 -- drivers/misc/mic/scif/scif_dma.c | 1940 ---------------------- drivers/misc/mic/scif/scif_epd.c | 357 ---- drivers/misc/mic/scif/scif_epd.h | 200 --- drivers/misc/mic/scif/scif_fd.c | 462 ------ drivers/misc/mic/scif/scif_fence.c | 783 --------- drivers/misc/mic/scif/scif_main.c | 351 ---- drivers/misc/mic/scif/scif_main.h | 274 --- drivers/misc/mic/scif/scif_map.h | 127 -- drivers/misc/mic/scif/scif_mmap.c | 690 -------- drivers/misc/mic/scif/scif_nm.c | 229 --- drivers/misc/mic/scif/scif_nodeqp.c | 1349 --------------- drivers/misc/mic/scif/scif_nodeqp.h | 221 --- drivers/misc/mic/scif/scif_peer_bus.c | 175 -- drivers/misc/mic/scif/scif_peer_bus.h | 23 - drivers/misc/mic/scif/scif_ports.c | 116 -- drivers/misc/mic/scif/scif_rb.c | 240 --- drivers/misc/mic/scif/scif_rb.h | 100 -- drivers/misc/mic/scif/scif_rma.c | 1760 -------------------- drivers/misc/mic/scif/scif_rma.h | 477 ------ drivers/misc/mic/scif/scif_rma_list.c | 282 ---- drivers/misc/mic/scif/scif_rma_list.h | 48 - drivers/misc/mic/vop/Makefile | 10 - drivers/misc/mic/vop/vop_debugfs.c | 184 -- drivers/misc/mic/vop/vop_main.c | 784 --------- drivers/misc/mic/vop/vop_main.h | 158 -- drivers/misc/mic/vop/vop_vringh.c | 1166 ------------- include/linux/mic_bus.h | 100 -- include/linux/scif.h | 1339 --------------- include/uapi/linux/mic_common.h | 235 --- include/uapi/linux/mic_ioctl.h | 77 - samples/mic/mpssd/.gitignore | 2 - samples/mic/mpssd/Makefile | 28 - samples/mic/mpssd/micctrl | 162 -- samples/mic/mpssd/mpss | 189 --- samples/mic/mpssd/mpssd.c | 1815 -------------------- samples/mic/mpssd/mpssd.h | 89 - samples/mic/mpssd/sysfs.c | 91 - 86 files changed, 26779 deletions(-) delete mode 100644 Documentation/misc-devices/mic/index.rst delete mode 100644 Documentation/misc-devices/mic/mic_overview.rst delete mode 100644 Documentation/misc-devices/mic/scif_overview.rst delete mode 100644 drivers/dma/mic_x100_dma.c delete mode 100644 drivers/dma/mic_x100_dma.h delete mode 100644 drivers/misc/mic/Kconfig delete mode 100644 drivers/misc/mic/Makefile delete mode 100644 drivers/misc/mic/bus/Makefile delete mode 100644 drivers/misc/mic/bus/cosm_bus.c delete mode 100644 drivers/misc/mic/bus/cosm_bus.h delete mode 100644 drivers/misc/mic/bus/mic_bus.c delete mode 100644 drivers/misc/mic/bus/scif_bus.c delete mode 100644 drivers/misc/mic/bus/scif_bus.h delete mode 100644 drivers/misc/mic/bus/vop_bus.c delete mode 100644 drivers/misc/mic/bus/vop_bus.h delete mode 100644 drivers/misc/mic/card/Makefile delete mode 100644 drivers/misc/mic/card/mic_debugfs.c delete mode 100644 drivers/misc/mic/card/mic_device.c delete mode 100644 drivers/misc/mic/card/mic_device.h delete mode 100644 drivers/misc/mic/card/mic_x100.c delete mode 100644 drivers/misc/mic/card/mic_x100.h delete mode 100644 drivers/misc/mic/common/mic_dev.h delete mode 100644 drivers/misc/mic/cosm/Makefile delete mode 100644 drivers/misc/mic/cosm/cosm_debugfs.c delete mode 100644 drivers/misc/mic/cosm/cosm_main.c delete mode 100644 drivers/misc/mic/cosm/cosm_main.h delete mode 100644 drivers/misc/mic/cosm/cosm_scif_server.c delete mode 100644 drivers/misc/mic/cosm/cosm_sysfs.c delete mode 100644 drivers/misc/mic/cosm_client/Makefile delete mode 100644 drivers/misc/mic/cosm_client/cosm_scif_client.c delete mode 100644 drivers/misc/mic/host/Makefile delete mode 100644 drivers/misc/mic/host/mic_boot.c delete mode 100644 drivers/misc/mic/host/mic_debugfs.c delete mode 100644 drivers/misc/mic/host/mic_device.h delete mode 100644 drivers/misc/mic/host/mic_intr.c delete mode 100644 drivers/misc/mic/host/mic_intr.h delete mode 100644 drivers/misc/mic/host/mic_main.c delete mode 100644 drivers/misc/mic/host/mic_smpt.c delete mode 100644 drivers/misc/mic/host/mic_smpt.h delete mode 100644 drivers/misc/mic/host/mic_x100.c delete mode 100644 drivers/misc/mic/host/mic_x100.h delete mode 100644 drivers/misc/mic/scif/Makefile delete mode 100644 drivers/misc/mic/scif/scif_api.c delete mode 100644 drivers/misc/mic/scif/scif_debugfs.c delete mode 100644 drivers/misc/mic/scif/scif_dma.c delete mode 100644 drivers/misc/mic/scif/scif_epd.c delete mode 100644 drivers/misc/mic/scif/scif_epd.h delete mode 100644 drivers/misc/mic/scif/scif_fd.c delete mode 100644 drivers/misc/mic/scif/scif_fence.c delete mode 100644 drivers/misc/mic/scif/scif_main.c delete mode 100644 drivers/misc/mic/scif/scif_main.h delete mode 100644 drivers/misc/mic/scif/scif_map.h delete mode 100644 drivers/misc/mic/scif/scif_mmap.c delete mode 100644 drivers/misc/mic/scif/scif_nm.c delete mode 100644 drivers/misc/mic/scif/scif_nodeqp.c delete mode 100644 drivers/misc/mic/scif/scif_nodeqp.h delete mode 100644 drivers/misc/mic/scif/scif_peer_bus.c delete mode 100644 drivers/misc/mic/scif/scif_peer_bus.h delete mode 100644 drivers/misc/mic/scif/scif_ports.c delete mode 100644 drivers/misc/mic/scif/scif_rb.c delete mode 100644 drivers/misc/mic/scif/scif_rb.h delete mode 100644 drivers/misc/mic/scif/scif_rma.c delete mode 100644 drivers/misc/mic/scif/scif_rma.h delete mode 100644 drivers/misc/mic/scif/scif_rma_list.c delete mode 100644 drivers/misc/mic/scif/scif_rma_list.h delete mode 100644 drivers/misc/mic/vop/Makefile delete mode 100644 drivers/misc/mic/vop/vop_debugfs.c delete mode 100644 drivers/misc/mic/vop/vop_main.c delete mode 100644 drivers/misc/mic/vop/vop_main.h delete mode 100644 drivers/misc/mic/vop/vop_vringh.c delete mode 100644 include/linux/mic_bus.h delete mode 100644 include/linux/scif.h delete mode 100644 include/uapi/linux/mic_common.h delete mode 100644 include/uapi/linux/mic_ioctl.h delete mode 100644 samples/mic/mpssd/.gitignore delete mode 100644 samples/mic/mpssd/Makefile delete mode 100755 samples/mic/mpssd/micctrl delete mode 100755 samples/mic/mpssd/mpss delete mode 100644 samples/mic/mpssd/mpssd.c delete mode 100644 samples/mic/mpssd/mpssd.h delete mode 100644 samples/mic/mpssd/sysfs.c (limited to 'drivers') diff --git a/Documentation/misc-devices/mic/index.rst b/Documentation/misc-devices/mic/index.rst deleted file mode 100644 index 3a8d06367ef1..000000000000 --- a/Documentation/misc-devices/mic/index.rst +++ /dev/null @@ -1,16 +0,0 @@ -============================================= -Intel Many Integrated Core (MIC) architecture -============================================= - -.. toctree:: - :maxdepth: 1 - - mic_overview - scif_overview - -.. only:: subproject and html - - Indices - ======= - - * :ref:`genindex` diff --git a/Documentation/misc-devices/mic/mic_overview.rst b/Documentation/misc-devices/mic/mic_overview.rst deleted file mode 100644 index 17d956bdaf7c..000000000000 --- a/Documentation/misc-devices/mic/mic_overview.rst +++ /dev/null @@ -1,85 +0,0 @@ -====================================================== -Intel Many Integrated Core (MIC) architecture overview -====================================================== - -An Intel MIC X100 device is a PCIe form factor add-in coprocessor -card based on the Intel Many Integrated Core (MIC) architecture -that runs a Linux OS. It is a PCIe endpoint in a platform and therefore -implements the three required standard address spaces i.e. configuration, -memory and I/O. The host OS loads a device driver as is typical for -PCIe devices. The card itself runs a bootstrap after reset that -transfers control to the card OS downloaded from the host driver. The -host driver supports OSPM suspend and resume operations. It shuts down -the card during suspend and reboots the card OS during resume. -The card OS as shipped by Intel is a Linux kernel with modifications -for the X100 devices. - -Since it is a PCIe card, it does not have the ability to host hardware -devices for networking, storage and console. We provide these devices -on X100 coprocessors thus enabling a self-bootable equivalent -environment for applications. A key benefit of our solution is that it -leverages the standard virtio framework for network, disk and console -devices, though in our case the virtio framework is used across a PCIe -bus. A Virtio Over PCIe (VOP) driver allows creating user space -backends or devices on the host which are used to probe virtio drivers -for these devices on the MIC card. The existing VRINGH infrastructure -in the kernel is used to access virtio rings from the host. The card -VOP driver allows card virtio drivers to communicate with their user -space backends on the host via a device page. Ring 3 apps on the host -can add, remove and configure virtio devices. A thin MIC specific -virtio_config_ops is implemented which is borrowed heavily from -previous similar implementations in lguest and s390. - -MIC PCIe card has a dma controller with 8 channels. These channels are -shared between the host s/w and the card s/w. 0 to 3 are used by host -and 4 to 7 by card. As the dma device doesn't show up as PCIe device, -a virtual bus called mic bus is created and virtual dma devices are -created on it by the host/card drivers. On host the channels are private -and used only by the host driver to transfer data for the virtio devices. - -The Symmetric Communication Interface (SCIF (pronounced as skiff)) is a -low level communications API across PCIe currently implemented for MIC. -More details are available at scif_overview.txt. - -The Coprocessor State Management (COSM) driver on the host allows for -boot, shutdown and reset of Intel MIC devices. It communicates with a COSM -"client" driver on the MIC cards over SCIF to perform these functions. - -Here is a block diagram of the various components described above. The -virtio backends are situated on the host rather than the card given better -single threaded performance for the host compared to MIC, the ability of -the host to initiate DMA's to/from the card using the MIC DMA engine and -the fact that the virtio block storage backend can only be on the host:: - - +----------+ | +----------+ - | Card OS | | | Host OS | - +----------+ | +----------+ - | - +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ - | Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio | - | Net | |Console | |Block | | |Net | |Console | |Block | - | Driver| |Driver | |Driver| | |backend | |backend | |backend | - +---+---+ +---+----+ +--+---+ | +---------+ +----+---+ +--------+ - | | | | | | | - | | | |User | | | - | | | |------|------------|--+------|------- - +---------+---------+ |Kernel | - | | | - +---------+ +---+----+ +------+ | +------+ +------+ +--+---+ +-------+ - |MIC DMA | | VOP | | SCIF | | | SCIF | | COSM | | VOP | |MIC DMA| - +---+-----+ +---+----+ +--+---+ | +--+---+ +--+---+ +------+ +----+--+ - | | | | | | | - +---+-----+ +---+----+ +--+---+ | +--+---+ +--+---+ +------+ +----+--+ - |MIC | | VOP | |SCIF | | |SCIF | | COSM | | VOP | | MIC | - |HW Bus | | HW Bus| |HW Bus| | |HW Bus| | Bus | |HW Bus| |HW Bus | - +---------+ +--------+ +--+---+ | +--+---+ +------+ +------+ +-------+ - | | | | | | | - | +-----------+--+ | | | +---------------+ | - | |Intel MIC | | | | |Intel MIC | | - | |Card Driver | | | | |Host Driver | | - +---+--------------+------+ | +----+---------------+-----+ - | | | - +-------------------------------------------------------------+ - | | - | PCIe Bus | - +-------------------------------------------------------------+ diff --git a/Documentation/misc-devices/mic/scif_overview.rst b/Documentation/misc-devices/mic/scif_overview.rst deleted file mode 100644 index 4c8ad9e43706..000000000000 --- a/Documentation/misc-devices/mic/scif_overview.rst +++ /dev/null @@ -1,108 +0,0 @@ -======================================== -Symmetric Communication Interface (SCIF) -======================================== - -The Symmetric Communication Interface (SCIF (pronounced as skiff)) is a low -level communications API across PCIe currently implemented for MIC. Currently -SCIF provides inter-node communication within a single host platform, where a -node is a MIC Coprocessor or Xeon based host. SCIF abstracts the details of -communicating over the PCIe bus while providing an API that is symmetric -across all the nodes in the PCIe network. An important design objective for SCIF -is to deliver the maximum possible performance given the communication -abilities of the hardware. SCIF has been used to implement an offload compiler -runtime and OFED support for MPI implementations for MIC coprocessors. - -SCIF API Components -=================== - -The SCIF API has the following parts: - -1. Connection establishment using a client server model -2. Byte stream messaging intended for short messages -3. Node enumeration to determine online nodes -4. Poll semantics for detection of incoming connections and messages -5. Memory registration to pin down pages -6. Remote memory mapping for low latency CPU accesses via mmap -7. Remote DMA (RDMA) for high bandwidth DMA transfers -8. Fence APIs for RDMA synchronization - -SCIF exposes the notion of a connection which can be used by peer processes on -nodes in a SCIF PCIe "network" to share memory "windows" and to communicate. A -process in a SCIF node initiates a SCIF connection to a peer process on a -different node via a SCIF "endpoint". SCIF endpoints support messaging APIs -which are similar to connection oriented socket APIs. Connected SCIF endpoints -can also register local memory which is followed by data transfer using either -DMA, CPU copies or remote memory mapping via mmap. SCIF supports both user and -kernel mode clients which are functionally equivalent. - -SCIF Performance for MIC -======================== - -DMA bandwidth comparison between the TCP (over ethernet over PCIe) stack versus -SCIF shows the performance advantages of SCIF for HPC applications and -runtimes:: - - Comparison of TCP and SCIF based BW - - Throughput (GB/sec) - 8 + PCIe Bandwidth ****** - + TCP ###### - 7 + ************************************** SCIF %%%%%% - | %%%%%%%%%%%%%%%%%%% - 6 + %%%% - | %% - | %%% - 5 + %% - | %% - 4 + %% - | %% - 3 + %% - | % - 2 + %% - | %% - | % - 1 + - + ###################################### - 0 +++---+++--+--+-+--+--+-++-+--+-++-+--+-++-+- - 1 10 100 1000 10000 100000 - Transfer Size (KBytes) - -SCIF allows memory sharing via mmap(..) between processes on different PCIe -nodes and thus provides bare-metal PCIe latency. The round trip SCIF mmap -latency from the host to an x100 MIC for an 8 byte message is 0.44 usecs. - -SCIF has a user space library which is a thin IOCTL wrapper providing a user -space API similar to the kernel API in scif.h. The SCIF user space library -is distributed @ https://software.intel.com/en-us/mic-developer - -Here is some pseudo code for an example of how two applications on two PCIe -nodes would typically use the SCIF API:: - - Process A (on node A) Process B (on node B) - - /* get online node information */ - scif_get_node_ids(..) scif_get_node_ids(..) - scif_open(..) scif_open(..) - scif_bind(..) scif_bind(..) - scif_listen(..) - scif_accept(..) scif_connect(..) - /* SCIF connection established */ - - /* Send and receive short messages */ - scif_send(..)/scif_recv(..) scif_send(..)/scif_recv(..) - - /* Register memory */ - scif_register(..) scif_register(..) - - /* RDMA */ - scif_readfrom(..)/scif_writeto(..) scif_readfrom(..)/scif_writeto(..) - - /* Fence DMAs */ - scif_fence_signal(..) scif_fence_signal(..) - - mmap(..) mmap(..) - - /* Access remote registered memory */ - - /* Close the endpoints */ - scif_close(..) scif_close(..) diff --git a/MAINTAINERS b/MAINTAINERS index e73636b75f29..9289a9b43a51 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8976,22 +8976,6 @@ S: Supported W: https://01.org/linux-acpi F: drivers/platform/x86/intel_menlow.c -INTEL MIC DRIVERS (mic) -M: Sudeep Dutt -M: Ashutosh Dixit -S: Supported -W: https://github.com/sudeepdutt/mic -W: http://software.intel.com/en-us/mic-developer -F: Documentation/misc-devices/mic/ -F: drivers/dma/mic_x100_dma.c -F: drivers/dma/mic_x100_dma.h -F: drivers/misc/mic/ -F: include/linux/mic_bus.h -F: include/linux/scif.h -F: include/uapi/linux/mic_common.h -F: include/uapi/linux/mic_ioctl.h -F: include/uapi/linux/scif_ioctl.h - INTEL P-Unit IPC DRIVER M: Zha Qipeng L: platform-driver-x86@vger.kernel.org diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 518a1437862a..90284ffda58a 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -318,24 +318,6 @@ config INTEL_IOP_ADMA help Enable support for the Intel(R) IOP Series RAID engines. -config INTEL_MIC_X100_DMA - tristate "Intel MIC X100 DMA Driver" - depends on 64BIT && X86 && INTEL_MIC_BUS - select DMA_ENGINE - help - This enables DMA support for the Intel Many Integrated Core - (MIC) family of PCIe form factor coprocessor X100 devices that - run a 64 bit Linux OS. This driver will be used by both MIC - host and card drivers. - - If you are building host kernel with a MIC device or a card - kernel for a MIC device, then say M (recommended) or Y, else - say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - . - config K3_DMA tristate "Hisilicon K3 DMA support" depends on ARCH_HI3xxx || ARCH_HISI || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index e60f81331d4c..948a8da05f8b 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -44,7 +44,6 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IDXD) += idxd/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o -obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o diff --git a/drivers/dma/mic_x100_dma.c b/drivers/dma/mic_x100_dma.c deleted file mode 100644 index fea8608a7810..000000000000 --- a/drivers/dma/mic_x100_dma.c +++ /dev/null @@ -1,770 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel MIC X100 DMA Driver. - * - * Adapted from IOAT dma driver. - */ -#include -#include -#include -#include - -#include "mic_x100_dma.h" - -#define MIC_DMA_MAX_XFER_SIZE_CARD (1 * 1024 * 1024 -\ - MIC_DMA_ALIGN_BYTES) -#define MIC_DMA_MAX_XFER_SIZE_HOST (1 * 1024 * 1024 >> 1) -#define MIC_DMA_DESC_TYPE_SHIFT 60 -#define MIC_DMA_MEMCPY_LEN_SHIFT 46 -#define MIC_DMA_STAT_INTR_SHIFT 59 - -/* high-water mark for pushing dma descriptors */ -static int mic_dma_pending_level = 4; - -/* Status descriptor is used to write a 64 bit value to a memory location */ -enum mic_dma_desc_format_type { - MIC_DMA_MEMCPY = 1, - MIC_DMA_STATUS, -}; - -static inline u32 mic_dma_hw_ring_inc(u32 val) -{ - return (val + 1) % MIC_DMA_DESC_RX_SIZE; -} - -static inline u32 mic_dma_hw_ring_dec(u32 val) -{ - return val ? val - 1 : MIC_DMA_DESC_RX_SIZE - 1; -} - -static inline void mic_dma_hw_ring_inc_head(struct mic_dma_chan *ch) -{ - ch->head = mic_dma_hw_ring_inc(ch->head); -} - -/* Prepare a memcpy desc */ -static inline void mic_dma_memcpy_desc(struct mic_dma_desc *desc, - dma_addr_t src_phys, dma_addr_t dst_phys, u64 size) -{ - u64 qw0, qw1; - - qw0 = src_phys; - qw0 |= (size >> MIC_DMA_ALIGN_SHIFT) << MIC_DMA_MEMCPY_LEN_SHIFT; - qw1 = MIC_DMA_MEMCPY; - qw1 <<= MIC_DMA_DESC_TYPE_SHIFT; - qw1 |= dst_phys; - desc->qw0 = qw0; - desc->qw1 = qw1; -} - -/* Prepare a status desc. with @data to be written at @dst_phys */ -static inline void mic_dma_prep_status_desc(struct mic_dma_desc *desc, u64 data, - dma_addr_t dst_phys, bool generate_intr) -{ - u64 qw0, qw1; - - qw0 = data; - qw1 = (u64) MIC_DMA_STATUS << MIC_DMA_DESC_TYPE_SHIFT | dst_phys; - if (generate_intr) - qw1 |= (1ULL << MIC_DMA_STAT_INTR_SHIFT); - desc->qw0 = qw0; - desc->qw1 = qw1; -} - -static void mic_dma_cleanup(struct mic_dma_chan *ch) -{ - struct dma_async_tx_descriptor *tx; - u32 tail; - u32 last_tail; - - spin_lock(&ch->cleanup_lock); - tail = mic_dma_read_cmp_cnt(ch); - /* - * This is the barrier pair for smp_wmb() in fn. - * mic_dma_tx_submit_unlock. It's required so that we read the - * updated cookie value from tx->cookie. - */ - smp_rmb(); - for (last_tail = ch->last_tail; tail != last_tail;) { - tx = &ch->tx_array[last_tail]; - if (tx->cookie) { - dma_cookie_complete(tx); - dmaengine_desc_get_callback_invoke(tx, NULL); - tx->callback = NULL; - } - last_tail = mic_dma_hw_ring_inc(last_tail); - } - /* finish all completion callbacks before incrementing tail */ - smp_mb(); - ch->last_tail = last_tail; - spin_unlock(&ch->cleanup_lock); -} - -static u32 mic_dma_ring_count(u32 head, u32 tail) -{ - u32 count; - - if (head >= tail) - count = (tail - 0) + (MIC_DMA_DESC_RX_SIZE - head); - else - count = tail - head; - return count - 1; -} - -/* Returns the num. of free descriptors on success, -ENOMEM on failure */ -static int mic_dma_avail_desc_ring_space(struct mic_dma_chan *ch, int required) -{ - struct device *dev = mic_dma_ch_to_device(ch); - u32 count; - - count = mic_dma_ring_count(ch->head, ch->last_tail); - if (count < required) { - mic_dma_cleanup(ch); - count = mic_dma_ring_count(ch->head, ch->last_tail); - } - - if (count < required) { - dev_dbg(dev, "Not enough desc space"); - dev_dbg(dev, "%s %d required=%u, avail=%u\n", - __func__, __LINE__, required, count); - return -ENOMEM; - } else { - return count; - } -} - -/* Program memcpy descriptors into the descriptor ring and update s/w head ptr*/ -static int mic_dma_prog_memcpy_desc(struct mic_dma_chan *ch, dma_addr_t src, - dma_addr_t dst, size_t len) -{ - size_t current_transfer_len; - size_t max_xfer_size = to_mic_dma_dev(ch)->max_xfer_size; - /* 3 is added to make sure we have enough space for status desc */ - int num_desc = len / max_xfer_size + 3; - int ret; - - if (len % max_xfer_size) - num_desc++; - - ret = mic_dma_avail_desc_ring_space(ch, num_desc); - if (ret < 0) - return ret; - do { - current_transfer_len = min(len, max_xfer_size); - mic_dma_memcpy_desc(&ch->desc_ring[ch->head], - src, dst, current_transfer_len); - mic_dma_hw_ring_inc_head(ch); - len -= current_transfer_len; - dst = dst + current_transfer_len; - src = src + current_transfer_len; - } while (len > 0); - return 0; -} - -/* It's a h/w quirk and h/w needs 2 status descriptors for every status desc */ -static void mic_dma_prog_intr(struct mic_dma_chan *ch) -{ - mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, - ch->status_dest_micpa, false); - mic_dma_hw_ring_inc_head(ch); - mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, - ch->status_dest_micpa, true); - mic_dma_hw_ring_inc_head(ch); -} - -/* Wrapper function to program memcpy descriptors/status descriptors */ -static int mic_dma_do_dma(struct mic_dma_chan *ch, int flags, dma_addr_t src, - dma_addr_t dst, size_t len) -{ - if (len && -ENOMEM == mic_dma_prog_memcpy_desc(ch, src, dst, len)) { - return -ENOMEM; - } else { - /* 3 is the maximum number of status descriptors */ - int ret = mic_dma_avail_desc_ring_space(ch, 3); - - if (ret < 0) - return ret; - } - - /* Above mic_dma_prog_memcpy_desc() makes sure we have enough space */ - if (flags & DMA_PREP_FENCE) { - mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, - ch->status_dest_micpa, false); - mic_dma_hw_ring_inc_head(ch); - } - - if (flags & DMA_PREP_INTERRUPT) - mic_dma_prog_intr(ch); - - return 0; -} - -static inline void mic_dma_issue_pending(struct dma_chan *ch) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - - spin_lock(&mic_ch->issue_lock); - /* - * Write to head triggers h/w to act on the descriptors. - * On MIC, writing the same head value twice causes - * a h/w error. On second write, h/w assumes we filled - * the entire ring & overwrote some of the descriptors. - */ - if (mic_ch->issued == mic_ch->submitted) - goto out; - mic_ch->issued = mic_ch->submitted; - /* - * make descriptor updates visible before advancing head, - * this is purposefully not smp_wmb() since we are also - * publishing the descriptor updates to a dma device - */ - wmb(); - mic_dma_write_reg(mic_ch, MIC_DMA_REG_DHPR, mic_ch->issued); -out: - spin_unlock(&mic_ch->issue_lock); -} - -static inline void mic_dma_update_pending(struct mic_dma_chan *ch) -{ - if (mic_dma_ring_count(ch->issued, ch->submitted) - > mic_dma_pending_level) - mic_dma_issue_pending(&ch->api_ch); -} - -static dma_cookie_t mic_dma_tx_submit_unlock(struct dma_async_tx_descriptor *tx) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(tx->chan); - dma_cookie_t cookie; - - dma_cookie_assign(tx); - cookie = tx->cookie; - /* - * We need an smp write barrier here because another CPU might see - * an update to submitted and update h/w head even before we - * assigned a cookie to this tx. - */ - smp_wmb(); - mic_ch->submitted = mic_ch->head; - spin_unlock(&mic_ch->prep_lock); - mic_dma_update_pending(mic_ch); - return cookie; -} - -static inline struct dma_async_tx_descriptor * -allocate_tx(struct mic_dma_chan *ch) -{ - u32 idx = mic_dma_hw_ring_dec(ch->head); - struct dma_async_tx_descriptor *tx = &ch->tx_array[idx]; - - dma_async_tx_descriptor_init(tx, &ch->api_ch); - tx->tx_submit = mic_dma_tx_submit_unlock; - return tx; -} - -/* Program a status descriptor with dst as address and value to be written */ -static struct dma_async_tx_descriptor * -mic_dma_prep_status_lock(struct dma_chan *ch, dma_addr_t dst, u64 src_val, - unsigned long flags) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - int result; - - spin_lock(&mic_ch->prep_lock); - result = mic_dma_avail_desc_ring_space(mic_ch, 4); - if (result < 0) - goto error; - mic_dma_prep_status_desc(&mic_ch->desc_ring[mic_ch->head], src_val, dst, - false); - mic_dma_hw_ring_inc_head(mic_ch); - result = mic_dma_do_dma(mic_ch, flags, 0, 0, 0); - if (result < 0) - goto error; - - return allocate_tx(mic_ch); -error: - dev_err(mic_dma_ch_to_device(mic_ch), - "Error enqueueing dma status descriptor, error=%d\n", result); - spin_unlock(&mic_ch->prep_lock); - return NULL; -} - -/* - * Prepare a memcpy descriptor to be added to the ring. - * Note that the temporary descriptor adds an extra overhead of copying the - * descriptor to ring. So, we copy directly to the descriptor ring - */ -static struct dma_async_tx_descriptor * -mic_dma_prep_memcpy_lock(struct dma_chan *ch, dma_addr_t dma_dest, - dma_addr_t dma_src, size_t len, unsigned long flags) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - struct device *dev = mic_dma_ch_to_device(mic_ch); - int result; - - if (!len && !flags) - return NULL; - - spin_lock(&mic_ch->prep_lock); - result = mic_dma_do_dma(mic_ch, flags, dma_src, dma_dest, len); - if (result >= 0) - return allocate_tx(mic_ch); - dev_err(dev, "Error enqueueing dma, error=%d\n", result); - spin_unlock(&mic_ch->prep_lock); - return NULL; -} - -static struct dma_async_tx_descriptor * -mic_dma_prep_interrupt_lock(struct dma_chan *ch, unsigned long flags) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - int ret; - - spin_lock(&mic_ch->prep_lock); - ret = mic_dma_do_dma(mic_ch, flags, 0, 0, 0); - if (!ret) - return allocate_tx(mic_ch); - spin_unlock(&mic_ch->prep_lock); - return NULL; -} - -/* Return the status of the transaction */ -static enum dma_status -mic_dma_tx_status(struct dma_chan *ch, dma_cookie_t cookie, - struct dma_tx_state *txstate) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - - if (DMA_COMPLETE != dma_cookie_status(ch, cookie, txstate)) - mic_dma_cleanup(mic_ch); - - return dma_cookie_status(ch, cookie, txstate); -} - -static irqreturn_t mic_dma_thread_fn(int irq, void *data) -{ - mic_dma_cleanup((struct mic_dma_chan *)data); - return IRQ_HANDLED; -} - -static irqreturn_t mic_dma_intr_handler(int irq, void *data) -{ - struct mic_dma_chan *ch = ((struct mic_dma_chan *)data); - - mic_dma_ack_interrupt(ch); - return IRQ_WAKE_THREAD; -} - -static int mic_dma_alloc_desc_ring(struct mic_dma_chan *ch) -{ - u64 desc_ring_size = MIC_DMA_DESC_RX_SIZE * sizeof(*ch->desc_ring); - struct device *dev = &to_mbus_device(ch)->dev; - - desc_ring_size = ALIGN(desc_ring_size, MIC_DMA_ALIGN_BYTES); - ch->desc_ring = kzalloc(desc_ring_size, GFP_KERNEL); - - if (!ch->desc_ring) - return -ENOMEM; - - ch->desc_ring_micpa = dma_map_single(dev, ch->desc_ring, - desc_ring_size, DMA_BIDIRECTIONAL); - if (dma_mapping_error(dev, ch->desc_ring_micpa)) - goto map_error; - - ch->tx_array = vzalloc(array_size(MIC_DMA_DESC_RX_SIZE, - sizeof(*ch->tx_array))); - if (!ch->tx_array) - goto tx_error; - return 0; -tx_error: - dma_unmap_single(dev, ch->desc_ring_micpa, desc_ring_size, - DMA_BIDIRECTIONAL); -map_error: - kfree(ch->desc_ring); - return -ENOMEM; -} - -static void mic_dma_free_desc_ring(struct mic_dma_chan *ch) -{ - u64 desc_ring_size = MIC_DMA_DESC_RX_SIZE * sizeof(*ch->desc_ring); - - vfree(ch->tx_array); - desc_ring_size = ALIGN(desc_ring_size, MIC_DMA_ALIGN_BYTES); - dma_unmap_single(&to_mbus_device(ch)->dev, ch->desc_ring_micpa, - desc_ring_size, DMA_BIDIRECTIONAL); - kfree(ch->desc_ring); - ch->desc_ring = NULL; -} - -static void mic_dma_free_status_dest(struct mic_dma_chan *ch) -{ - dma_unmap_single(&to_mbus_device(ch)->dev, ch->status_dest_micpa, - L1_CACHE_BYTES, DMA_BIDIRECTIONAL); - kfree(ch->status_dest); -} - -static int mic_dma_alloc_status_dest(struct mic_dma_chan *ch) -{ - struct device *dev = &to_mbus_device(ch)->dev; - - ch->status_dest = kzalloc(L1_CACHE_BYTES, GFP_KERNEL); - if (!ch->status_dest) - return -ENOMEM; - ch->status_dest_micpa = dma_map_single(dev, ch->status_dest, - L1_CACHE_BYTES, DMA_BIDIRECTIONAL); - if (dma_mapping_error(dev, ch->status_dest_micpa)) { - kfree(ch->status_dest); - ch->status_dest = NULL; - return -ENOMEM; - } - return 0; -} - -static int mic_dma_check_chan(struct mic_dma_chan *ch) -{ - if (mic_dma_read_reg(ch, MIC_DMA_REG_DCHERR) || - mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT) & MIC_DMA_CHAN_QUIESCE) { - mic_dma_disable_chan(ch); - mic_dma_chan_mask_intr(ch); - dev_err(mic_dma_ch_to_device(ch), - "%s %d error setting up mic dma chan %d\n", - __func__, __LINE__, ch->ch_num); - return -EBUSY; - } - return 0; -} - -static int mic_dma_chan_setup(struct mic_dma_chan *ch) -{ - if (MIC_DMA_CHAN_MIC == ch->owner) - mic_dma_chan_set_owner(ch); - mic_dma_disable_chan(ch); - mic_dma_chan_mask_intr(ch); - mic_dma_write_reg(ch, MIC_DMA_REG_DCHERRMSK, 0); - mic_dma_chan_set_desc_ring(ch); - ch->last_tail = mic_dma_read_reg(ch, MIC_DMA_REG_DTPR); - ch->head = ch->last_tail; - ch->issued = 0; - mic_dma_chan_unmask_intr(ch); - mic_dma_enable_chan(ch); - return mic_dma_check_chan(ch); -} - -static void mic_dma_chan_destroy(struct mic_dma_chan *ch) -{ - mic_dma_disable_chan(ch); - mic_dma_chan_mask_intr(ch); -} - -static int mic_dma_setup_irq(struct mic_dma_chan *ch) -{ - ch->cookie = - to_mbus_hw_ops(ch)->request_threaded_irq(to_mbus_device(ch), - mic_dma_intr_handler, mic_dma_thread_fn, - "mic dma_channel", ch, ch->ch_num); - return PTR_ERR_OR_ZERO(ch->cookie); -} - -static inline void mic_dma_free_irq(struct mic_dma_chan *ch) -{ - to_mbus_hw_ops(ch)->free_irq(to_mbus_device(ch), ch->cookie, ch); -} - -static int mic_dma_chan_init(struct mic_dma_chan *ch) -{ - int ret = mic_dma_alloc_desc_ring(ch); - - if (ret) - goto ring_error; - ret = mic_dma_alloc_status_dest(ch); - if (ret) - goto status_error; - ret = mic_dma_chan_setup(ch); - if (ret) - goto chan_error; - return ret; -chan_error: - mic_dma_free_status_dest(ch); -status_error: - mic_dma_free_desc_ring(ch); -ring_error: - return ret; -} - -static int mic_dma_drain_chan(struct mic_dma_chan *ch) -{ - struct dma_async_tx_descriptor *tx; - int err = 0; - dma_cookie_t cookie; - - tx = mic_dma_prep_memcpy_lock(&ch->api_ch, 0, 0, 0, DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - goto error; - } - - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) - err = -ENOMEM; - else - err = dma_sync_wait(&ch->api_ch, cookie); - if (err) { - dev_err(mic_dma_ch_to_device(ch), "%s %d TO chan 0x%x\n", - __func__, __LINE__, ch->ch_num); - err = -EIO; - } -error: - mic_dma_cleanup(ch); - return err; -} - -static inline void mic_dma_chan_uninit(struct mic_dma_chan *ch) -{ - mic_dma_chan_destroy(ch); - mic_dma_cleanup(ch); - mic_dma_free_status_dest(ch); - mic_dma_free_desc_ring(ch); -} - -static int mic_dma_init(struct mic_dma_device *mic_dma_dev, - enum mic_dma_chan_owner owner) -{ - int i, first_chan = mic_dma_dev->start_ch; - struct mic_dma_chan *ch; - int ret; - - for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { - ch = &mic_dma_dev->mic_ch[i]; - ch->ch_num = i; - ch->owner = owner; - spin_lock_init(&ch->cleanup_lock); - spin_lock_init(&ch->prep_lock); - spin_lock_init(&ch->issue_lock); - ret = mic_dma_setup_irq(ch); - if (ret) - goto error; - } - return 0; -error: - for (i = i - 1; i >= first_chan; i--) - mic_dma_free_irq(ch); - return ret; -} - -static void mic_dma_uninit(struct mic_dma_device *mic_dma_dev) -{ - int i, first_chan = mic_dma_dev->start_ch; - struct mic_dma_chan *ch; - - for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { - ch = &mic_dma_dev->mic_ch[i]; - mic_dma_free_irq(ch); - } -} - -static int mic_dma_alloc_chan_resources(struct dma_chan *ch) -{ - int ret = mic_dma_chan_init(to_mic_dma_chan(ch)); - if (ret) - return ret; - return MIC_DMA_DESC_RX_SIZE; -} - -static void mic_dma_free_chan_resources(struct dma_chan *ch) -{ - struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); - mic_dma_drain_chan(mic_ch); - mic_dma_chan_uninit(mic_ch); -} - -/* Set the fn. handlers and register the dma device with dma api */ -static int mic_dma_register_dma_device(struct mic_dma_device *mic_dma_dev, - enum mic_dma_chan_owner owner) -{ - int i, first_chan = mic_dma_dev->start_ch; - - dma_cap_zero(mic_dma_dev->dma_dev.cap_mask); - /* - * This dma engine is not capable of host memory to host memory - * transfers - */ - dma_cap_set(DMA_MEMCPY, mic_dma_dev->dma_dev.cap_mask); - - if (MIC_DMA_CHAN_HOST == owner) - dma_cap_set(DMA_PRIVATE, mic_dma_dev->dma_dev.cap_mask); - mic_dma_dev->dma_dev.device_alloc_chan_resources = - mic_dma_alloc_chan_resources; - mic_dma_dev->dma_dev.device_free_chan_resources = - mic_dma_free_chan_resources; - mic_dma_dev->dma_dev.device_tx_status = mic_dma_tx_status; - mic_dma_dev->dma_dev.device_prep_dma_memcpy = mic_dma_prep_memcpy_lock; - mic_dma_dev->dma_dev.device_prep_dma_imm_data = - mic_dma_prep_status_lock; - mic_dma_dev->dma_dev.device_prep_dma_interrupt = - mic_dma_prep_interrupt_lock; - mic_dma_dev->dma_dev.device_issue_pending = mic_dma_issue_pending; - mic_dma_dev->dma_dev.copy_align = MIC_DMA_ALIGN_SHIFT; - INIT_LIST_HEAD(&mic_dma_dev->dma_dev.channels); - for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { - mic_dma_dev->mic_ch[i].api_ch.device = &mic_dma_dev->dma_dev; - dma_cookie_init(&mic_dma_dev->mic_ch[i].api_ch); - list_add_tail(&mic_dma_dev->mic_ch[i].api_ch.device_node, - &mic_dma_dev->dma_dev.channels); - } - return dmaenginem_async_device_register(&mic_dma_dev->dma_dev); -} - -/* - * Initializes dma channels and registers the dma device with the - * dma engine api. - */ -static struct mic_dma_device *mic_dma_dev_reg(struct mbus_device *mbdev, - enum mic_dma_chan_owner owner) -{ - struct mic_dma_device *mic_dma_dev; - int ret; - struct device *dev = &mbdev->dev; - - mic_dma_dev = devm_kzalloc(dev, sizeof(*mic_dma_dev), GFP_KERNEL); - if (!mic_dma_dev) { - ret = -ENOMEM; - goto alloc_error; - } - mic_dma_dev->mbdev = mbdev; - mic_dma_dev->dma_dev.dev = dev; - mic_dma_dev->mmio = mbdev->mmio_va; - if (MIC_DMA_CHAN_HOST == owner) { - mic_dma_dev->start_ch = 0; - mic_dma_dev->max_xfer_size = MIC_DMA_MAX_XFER_SIZE_HOST; - } else { - mic_dma_dev->start_ch = 4; - mic_dma_dev->max_xfer_size = MIC_DMA_MAX_XFER_SIZE_CARD; - } - ret = mic_dma_init(mic_dma_dev, owner); - if (ret) - goto init_error; - ret = mic_dma_register_dma_device(mic_dma_dev, owner); - if (ret) - goto reg_error; - return mic_dma_dev; -reg_error: - mic_dma_uninit(mic_dma_dev); -init_error: - mic_dma_dev = NULL; -alloc_error: - dev_err(dev, "Error at %s %d ret=%d\n", __func__, __LINE__, ret); - return mic_dma_dev; -} - -static void mic_dma_dev_unreg(struct mic_dma_device *mic_dma_dev) -{ - mic_dma_uninit(mic_dma_dev); -} - -/* DEBUGFS CODE */ -static int mic_dma_reg_show(struct seq_file *s, void *pos) -{ - struct mic_dma_device *mic_dma_dev = s->private; - int i, chan_num, first_chan = mic_dma_dev->start_ch; - struct mic_dma_chan *ch; - - seq_printf(s, "SBOX_DCR: %#x\n", - mic_dma_mmio_read(&mic_dma_dev->mic_ch[first_chan], - MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR)); - seq_puts(s, "DMA Channel Registers\n"); - seq_printf(s, "%-10s| %-10s %-10s %-10s %-10s %-10s", - "Channel", "DCAR", "DTPR", "DHPR", "DRAR_HI", "DRAR_LO"); - seq_printf(s, " %-11s %-14s %-10s\n", "DCHERR", "DCHERRMSK", "DSTAT"); - for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { - ch = &mic_dma_dev->mic_ch[i]; - chan_num = ch->ch_num; - seq_printf(s, "%-10i| %-#10x %-#10x %-#10x %-#10x", - chan_num, - mic_dma_read_reg(ch, MIC_DMA_REG_DCAR), - mic_dma_read_reg(ch, MIC_DMA_REG_DTPR), - mic_dma_read_reg(ch, MIC_DMA_REG_DHPR), - mic_dma_read_reg(ch, MIC_DMA_REG_DRAR_HI)); - seq_printf(s, " %-#10x %-#10x %-#14x %-#10x\n", - mic_dma_read_reg(ch, MIC_DMA_REG_DRAR_LO), - mic_dma_read_reg(ch, MIC_DMA_REG_DCHERR), - mic_dma_read_reg(ch, MIC_DMA_REG_DCHERRMSK), - mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT)); - } - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_dma_reg); - -/* Debugfs parent dir */ -static struct dentry *mic_dma_dbg; - -static int mic_dma_driver_probe(struct mbus_device *mbdev) -{ - struct mic_dma_device *mic_dma_dev; - enum mic_dma_chan_owner owner; - - if (MBUS_DEV_DMA_MIC == mbdev->id.device) - owner = MIC_DMA_CHAN_MIC; - else - owner = MIC_DMA_CHAN_HOST; - - mic_dma_dev = mic_dma_dev_reg(mbdev, owner); - dev_set_drvdata(&mbdev->dev, mic_dma_dev); - - if (mic_dma_dbg) { - mic_dma_dev->dbg_dir = debugfs_create_dir(dev_name(&mbdev->dev), - mic_dma_dbg); - debugfs_create_file("mic_dma_reg", 0444, mic_dma_dev->dbg_dir, - mic_dma_dev, &mic_dma_reg_fops); - } - return 0; -} - -static void mic_dma_driver_remove(struct mbus_device *mbdev) -{ - struct mic_dma_device *mic_dma_dev; - - mic_dma_dev = dev_get_drvdata(&mbdev->dev); - debugfs_remove_recursive(mic_dma_dev->dbg_dir); - mic_dma_dev_unreg(mic_dma_dev); -} - -static struct mbus_device_id id_table[] = { - {MBUS_DEV_DMA_MIC, MBUS_DEV_ANY_ID}, - {MBUS_DEV_DMA_HOST, MBUS_DEV_ANY_ID}, - {0}, -}; - -static struct mbus_driver mic_dma_driver = { - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = mic_dma_driver_probe, - .remove = mic_dma_driver_remove, -}; - -static int __init mic_x100_dma_init(void) -{ - int rc = mbus_register_driver(&mic_dma_driver); - if (rc) - return rc; - mic_dma_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); - return 0; -} - -static void __exit mic_x100_dma_exit(void) -{ - debugfs_remove_recursive(mic_dma_dbg); - mbus_unregister_driver(&mic_dma_driver); -} - -module_init(mic_x100_dma_init); -module_exit(mic_x100_dma_exit); - -MODULE_DEVICE_TABLE(mbus, id_table); -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC X100 DMA Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/mic_x100_dma.h b/drivers/dma/mic_x100_dma.h deleted file mode 100644 index 68ef43a91714..000000000000 --- a/drivers/dma/mic_x100_dma.h +++ /dev/null @@ -1,275 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel MIC X100 DMA Driver. - * - * Adapted from IOAT dma driver. - */ -#ifndef _MIC_X100_DMA_H_ -#define _MIC_X100_DMA_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "dmaengine.h" - -/* - * MIC has a total of 8 dma channels. - * Four channels are assigned for host SW use & the remaining for MIC SW. - * MIC DMA transfer size & addresses need to be 64 byte aligned. - */ -#define MIC_DMA_MAX_NUM_CHAN 8 -#define MIC_DMA_NUM_CHAN 4 -#define MIC_DMA_ALIGN_SHIFT DMAENGINE_ALIGN_64_BYTES -#define MIC_DMA_ALIGN_BYTES (1 << MIC_DMA_ALIGN_SHIFT) -#define MIC_DMA_DESC_RX_SIZE (128 * 1024 - 4) - -/* - * Register descriptions - * All the registers are 32 bit registers. - * DCR is a global register and all others are per-channel. - * DCR - bits 0, 2, 4, 6, 8, 10, 12, 14 - enable bits for channels 0 to 7 - * bits 1, 3, 5, 7, 9, 11, 13, 15 - owner bits for channels 0 to 7 - * DCAR - bit 24 & 25 interrupt masks for mic owned & host owned channels - * DHPR - head of the descriptor ring updated by s/w - * DTPR - tail of the descriptor ring updated by h/w - * DRAR_LO - lower 32 bits of descriptor ring's mic address - * DRAR_HI - 3:0 - remaining 4 bits of descriptor ring's mic address - * 20:4 descriptor ring size - * 25:21 mic smpt entry number - * DSTAT - 16:0 h/w completion count; 31:28 dma engine status - * DCHERR - this register is non-zero on error - * DCHERRMSK - interrupt mask register - */ -#define MIC_DMA_HW_CMP_CNT_MASK 0x1ffff -#define MIC_DMA_CHAN_QUIESCE 0x20000000 -#define MIC_DMA_SBOX_BASE 0x00010000 -#define MIC_DMA_SBOX_DCR 0x0000A280 -#define MIC_DMA_SBOX_CH_BASE 0x0001A000 -#define MIC_DMA_SBOX_CHAN_OFF 0x40 -#define MIC_DMA_SBOX_DCAR_IM0 (0x1 << 24) -#define MIC_DMA_SBOX_DCAR_IM1 (0x1 << 25) -#define MIC_DMA_SBOX_DRARHI_SYS_MASK (0x1 << 26) -#define MIC_DMA_REG_DCAR 0 -#define MIC_DMA_REG_DHPR 4 -#define MIC_DMA_REG_DTPR 8 -#define MIC_DMA_REG_DRAR_LO 20 -#define MIC_DMA_REG_DRAR_HI 24 -#define MIC_DMA_REG_DSTAT 32 -#define MIC_DMA_REG_DCHERR 44 -#define MIC_DMA_REG_DCHERRMSK 48 - -/* HW dma desc */ -struct mic_dma_desc { - u64 qw0; - u64 qw1; -}; - -enum mic_dma_chan_owner { - MIC_DMA_CHAN_MIC = 0, - MIC_DMA_CHAN_HOST -}; - -/* - * mic_dma_chan - channel specific information - * @ch_num: channel number - * @owner: owner of this channel - * @last_tail: cached value of descriptor ring tail - * @head: index of next descriptor in desc_ring - * @issued: hardware notification point - * @submitted: index that will be used to submit descriptors to h/w - * @api_ch: dma engine api channel - * @desc_ring: dma descriptor ring - * @desc_ring_micpa: mic physical address of desc_ring - * @status_dest: destination for status (fence) descriptor - * @status_dest_micpa: mic address for status_dest, - * DMA controller uses this address - * @tx_array: array of async_tx - * @cleanup_lock: lock held when processing completed tx - * @prep_lock: lock held in prep_memcpy & released in tx_submit - * @issue_lock: lock used to synchronize writes to head - * @cookie: mic_irq cookie used with mic irq request - */ -struct mic_dma_chan { - int ch_num; - enum mic_dma_chan_owner owner; - u32 last_tail; - u32 head; - u32 issued; - u32 submitted; - struct dma_chan api_ch; - struct mic_dma_desc *desc_ring; - dma_addr_t desc_ring_micpa; - u64 *status_dest; - dma_addr_t status_dest_micpa; - struct dma_async_tx_descriptor *tx_array; - spinlock_t cleanup_lock; - spinlock_t prep_lock; - spinlock_t issue_lock; - struct mic_irq *cookie; -}; - -/* - * struct mic_dma_device - per mic device - * @mic_ch: dma channels - * @dma_dev: underlying dma device - * @mbdev: mic bus dma device - * @mmio: virtual address of the mmio space - * @dbg_dir: debugfs directory - * @start_ch: first channel number that can be used - * @max_xfer_size: maximum transfer size per dma descriptor - */ -struct mic_dma_device { - struct mic_dma_chan mic_ch[MIC_DMA_MAX_NUM_CHAN]; - struct dma_device dma_dev; - struct mbus_device *mbdev; - void __iomem *mmio; - struct dentry *dbg_dir; - int start_ch; - size_t max_xfer_size; -}; - -static inline struct mic_dma_chan *to_mic_dma_chan(struct dma_chan *ch) -{ - return container_of(ch, struct mic_dma_chan, api_ch); -} - -static inline struct mic_dma_device *to_mic_dma_dev(struct mic_dma_chan *ch) -{ - return - container_of((const typeof(((struct mic_dma_device *)0)->mic_ch)*) - (ch - ch->ch_num), struct mic_dma_device, mic_ch); -} - -static inline struct mbus_device *to_mbus_device(struct mic_dma_chan *ch) -{ - return to_mic_dma_dev(ch)->mbdev; -} - -static inline struct mbus_hw_ops *to_mbus_hw_ops(struct mic_dma_chan *ch) -{ - return to_mbus_device(ch)->hw_ops; -} - -static inline struct device *mic_dma_ch_to_device(struct mic_dma_chan *ch) -{ - return to_mic_dma_dev(ch)->dma_dev.dev; -} - -static inline void __iomem *mic_dma_chan_to_mmio(struct mic_dma_chan *ch) -{ - return to_mic_dma_dev(ch)->mmio; -} - -static inline u32 mic_dma_read_reg(struct mic_dma_chan *ch, u32 reg) -{ - return ioread32(mic_dma_chan_to_mmio(ch) + MIC_DMA_SBOX_CH_BASE + - ch->ch_num * MIC_DMA_SBOX_CHAN_OFF + reg); -} - -static inline void mic_dma_write_reg(struct mic_dma_chan *ch, u32 reg, u32 val) -{ - iowrite32(val, mic_dma_chan_to_mmio(ch) + MIC_DMA_SBOX_CH_BASE + - ch->ch_num * MIC_DMA_SBOX_CHAN_OFF + reg); -} - -static inline u32 mic_dma_mmio_read(struct mic_dma_chan *ch, u32 offset) -{ - return ioread32(mic_dma_chan_to_mmio(ch) + offset); -} - -static inline void mic_dma_mmio_write(struct mic_dma_chan *ch, u32 val, - u32 offset) -{ - iowrite32(val, mic_dma_chan_to_mmio(ch) + offset); -} - -static inline u32 mic_dma_read_cmp_cnt(struct mic_dma_chan *ch) -{ - return mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT) & - MIC_DMA_HW_CMP_CNT_MASK; -} - -static inline void mic_dma_chan_set_owner(struct mic_dma_chan *ch) -{ - u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); - u32 chan_num = ch->ch_num; - - dcr = (dcr & ~(0x1 << (chan_num * 2))) | (ch->owner << (chan_num * 2)); - mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); -} - -static inline void mic_dma_enable_chan(struct mic_dma_chan *ch) -{ - u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); - - dcr |= 2 << (ch->ch_num << 1); - mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); -} - -static inline void mic_dma_disable_chan(struct mic_dma_chan *ch) -{ - u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); - - dcr &= ~(2 << (ch->ch_num << 1)); - mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); -} - -static void mic_dma_chan_set_desc_ring(struct mic_dma_chan *ch) -{ - u32 drar_hi; - dma_addr_t desc_ring_micpa = ch->desc_ring_micpa; - - drar_hi = (MIC_DMA_DESC_RX_SIZE & 0x1ffff) << 4; - if (MIC_DMA_CHAN_MIC == ch->owner) { - drar_hi |= (desc_ring_micpa >> 32) & 0xf; - } else { - drar_hi |= MIC_DMA_SBOX_DRARHI_SYS_MASK; - drar_hi |= ((desc_ring_micpa >> 34) - & 0x1f) << 21; - drar_hi |= (desc_ring_micpa >> 32) & 0x3; - } - mic_dma_write_reg(ch, MIC_DMA_REG_DRAR_LO, (u32) desc_ring_micpa); - mic_dma_write_reg(ch, MIC_DMA_REG_DRAR_HI, drar_hi); -} - -static inline void mic_dma_chan_mask_intr(struct mic_dma_chan *ch) -{ - u32 dcar = mic_dma_read_reg(ch, MIC_DMA_REG_DCAR); - - if (MIC_DMA_CHAN_MIC == ch->owner) - dcar |= MIC_DMA_SBOX_DCAR_IM0; - else - dcar |= MIC_DMA_SBOX_DCAR_IM1; - mic_dma_write_reg(ch, MIC_DMA_REG_DCAR, dcar); -} - -static inline void mic_dma_chan_unmask_intr(struct mic_dma_chan *ch) -{ - u32 dcar = mic_dma_read_reg(ch, MIC_DMA_REG_DCAR); - - if (MIC_DMA_CHAN_MIC == ch->owner) - dcar &= ~MIC_DMA_SBOX_DCAR_IM0; - else - dcar &= ~MIC_DMA_SBOX_DCAR_IM1; - mic_dma_write_reg(ch, MIC_DMA_REG_DCAR, dcar); -} - -static void mic_dma_ack_interrupt(struct mic_dma_chan *ch) -{ - if (MIC_DMA_CHAN_MIC == ch->owner) { - /* HW errata */ - mic_dma_chan_mask_intr(ch); - mic_dma_chan_unmask_intr(ch); - } - to_mbus_hw_ops(ch)->ack_interrupt(to_mbus_device(ch), ch->ch_num); -} -#endif diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index d5ce8082b0a0..fafa8b0d8099 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -474,7 +474,6 @@ source "drivers/misc/lis3lv02d/Kconfig" source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" source "drivers/misc/vmw_vmci/Kconfig" -source "drivers/misc/mic/Kconfig" source "drivers/misc/genwqe/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 2521359e8ef7..d23231e73330 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -46,7 +46,6 @@ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o obj-$(CONFIG_SRAM) += sram.o obj-$(CONFIG_SRAM_EXEC) += sram-exec.o -obj-y += mic/ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_CXL_BASE) += cxl/ diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig deleted file mode 100644 index 8a7c2c5711d5..000000000000 --- a/drivers/misc/mic/Kconfig +++ /dev/null @@ -1,141 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -menu "Intel MIC & related support" - -config INTEL_MIC_BUS - tristate "Intel MIC Bus Driver" - depends on 64BIT && PCI && X86 - select DMA_OPS - help - This option is selected by any driver which registers a - device or driver on the MIC Bus, such as CONFIG_INTEL_MIC_HOST, - CONFIG_INTEL_MIC_CARD, CONFIG_INTEL_MIC_X100_DMA etc. - - If you are building a host/card kernel with an Intel MIC device - then say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - . - -config SCIF_BUS - tristate "SCIF Bus Driver" - depends on 64BIT && PCI && X86 - select DMA_OPS - help - This option is selected by any driver which registers a - device or driver on the SCIF Bus, such as CONFIG_INTEL_MIC_HOST - and CONFIG_INTEL_MIC_CARD. - - If you are building a host/card kernel with an Intel MIC device - then say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - . - -config VOP_BUS - tristate "VOP Bus Driver" - depends on HAS_DMA - select DMA_OPS - help - This option is selected by any driver which registers a - device or driver on the VOP Bus, such as CONFIG_INTEL_MIC_HOST - and CONFIG_INTEL_MIC_CARD. - - If you are building a host/card kernel with an Intel MIC device - then say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - . - -config INTEL_MIC_HOST - tristate "Intel MIC Host Driver" - depends on 64BIT && PCI && X86 - depends on INTEL_MIC_BUS && SCIF_BUS && MIC_COSM && VOP_BUS - select DMA_OPS - help - This enables Host Driver support for the Intel Many Integrated - Core (MIC) family of PCIe form factor coprocessor devices that - run a 64 bit Linux OS. The driver manages card OS state and - enables communication between host and card. Intel MIC X100 - devices are currently supported. - - If you are building a host kernel with an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - . - -config INTEL_MIC_CARD - tristate "Intel MIC Card Driver" - depends on 64BIT && X86 - depends on INTEL_MIC_BUS && SCIF_BUS && MIC_COSM && VOP_BUS - select VIRTIO - help - This enables card driver support for the Intel Many Integrated - Core (MIC) device family. The card driver communicates shutdown/ - crash events to the host and allows registration/configuration of - virtio devices. Intel MIC X100 devices are currently supported. - - If you are building a card kernel for an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - For more information see - . - -config SCIF - tristate "SCIF Driver" - depends on 64BIT && PCI && X86 && SCIF_BUS && IOMMU_SUPPORT - select IOMMU_IOVA - help - This enables SCIF Driver support for the Intel Many Integrated - Core (MIC) family of PCIe form factor coprocessor devices that - run a 64 bit Linux OS. The Symmetric Communication Interface - (SCIF (pronounced as skiff)) is a low level communications API - across PCIe currently implemented for MIC. - - If you are building a host kernel with an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - . - -config MIC_COSM - tristate "Intel MIC Coprocessor State Management (COSM) Drivers" - depends on 64BIT && PCI && X86 && SCIF - help - This enables COSM driver support for the Intel Many - Integrated Core (MIC) family of PCIe form factor coprocessor - devices. COSM drivers implement functions such as boot, - shutdown, reset and reboot of MIC devices. - - If you are building a host kernel with an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - . - -config VOP - tristate "VOP Driver" - depends on VOP_BUS - select VHOST_RING - select VIRTIO - help - This enables VOP (Virtio over PCIe) Driver support for the Intel - Many Integrated Core (MIC) family of PCIe form factor coprocessor - devices. The VOP driver allows virtio drivers, e.g. net, console - and block drivers, on the card connect to user space virtio - devices on the host. - - If you are building a host kernel with an Intel MIC device then - say M (recommended) or Y, else say N. If unsure say N. - - More information about the Intel MIC family as well as the Linux - OS and tools for MIC to use with this driver are available from - . - -endmenu diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile deleted file mode 100644 index 1a43622b183f..000000000000 --- a/drivers/misc/mic/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2013, Intel Corporation. -# -obj-$(CONFIG_INTEL_MIC_HOST) += host/ -obj-$(CONFIG_INTEL_MIC_CARD) += card/ -obj-y += bus/ -obj-$(CONFIG_SCIF) += scif/ -obj-$(CONFIG_MIC_COSM) += cosm/ -obj-$(CONFIG_MIC_COSM) += cosm_client/ -obj-$(CONFIG_VOP) += vop/ diff --git a/drivers/misc/mic/bus/Makefile b/drivers/misc/mic/bus/Makefile deleted file mode 100644 index 0a6aa21b2f67..000000000000 --- a/drivers/misc/mic/bus/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2014, Intel Corporation. -# -obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o -obj-$(CONFIG_SCIF_BUS) += scif_bus.o -obj-$(CONFIG_MIC_COSM) += cosm_bus.o -obj-$(CONFIG_VOP_BUS) += vop_bus.o diff --git a/drivers/misc/mic/bus/cosm_bus.c b/drivers/misc/mic/bus/cosm_bus.c deleted file mode 100644 index 5f2141c71738..000000000000 --- a/drivers/misc/mic/bus/cosm_bus.c +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC COSM Bus Driver - */ -#include -#include -#include -#include "cosm_bus.h" - -/* Unique numbering for cosm devices. */ -static DEFINE_IDA(cosm_index_ida); - -static int cosm_dev_probe(struct device *d) -{ - struct cosm_device *dev = dev_to_cosm(d); - struct cosm_driver *drv = drv_to_cosm(dev->dev.driver); - - return drv->probe(dev); -} - -static int cosm_dev_remove(struct device *d) -{ - struct cosm_device *dev = dev_to_cosm(d); - struct cosm_driver *drv = drv_to_cosm(dev->dev.driver); - - drv->remove(dev); - return 0; -} - -static struct bus_type cosm_bus = { - .name = "cosm_bus", - .probe = cosm_dev_probe, - .remove = cosm_dev_remove, -}; - -int cosm_register_driver(struct cosm_driver *driver) -{ - driver->driver.bus = &cosm_bus; - return driver_register(&driver->driver); -} -EXPORT_SYMBOL_GPL(cosm_register_driver); - -void cosm_unregister_driver(struct cosm_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(cosm_unregister_driver); - -static inline void cosm_release_dev(struct device *d) -{ - struct cosm_device *cdev = dev_to_cosm(d); - - kfree(cdev); -} - -struct cosm_device * -cosm_register_device(struct device *pdev, struct cosm_hw_ops *hw_ops) -{ - struct cosm_device *cdev; - int ret; - - cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); - if (!cdev) - return ERR_PTR(-ENOMEM); - - cdev->dev.parent = pdev; - cdev->dev.release = cosm_release_dev; - cdev->hw_ops = hw_ops; - dev_set_drvdata(&cdev->dev, cdev); - cdev->dev.bus = &cosm_bus; - - /* Assign a unique device index and hence name */ - ret = ida_simple_get(&cosm_index_ida, 0, 0, GFP_KERNEL); - if (ret < 0) - goto free_cdev; - - cdev->index = ret; - cdev->dev.id = ret; - dev_set_name(&cdev->dev, "cosm-dev%u", cdev->index); - - ret = device_register(&cdev->dev); - if (ret) - goto ida_remove; - return cdev; -ida_remove: - ida_simple_remove(&cosm_index_ida, cdev->index); -free_cdev: - put_device(&cdev->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(cosm_register_device); - -void cosm_unregister_device(struct cosm_device *dev) -{ - int index = dev->index; /* save for after device release */ - - device_unregister(&dev->dev); - ida_simple_remove(&cosm_index_ida, index); -} -EXPORT_SYMBOL_GPL(cosm_unregister_device); - -struct cosm_device *cosm_find_cdev_by_id(int id) -{ - struct device *dev = subsys_find_device_by_id(&cosm_bus, id, NULL); - - return dev ? container_of(dev, struct cosm_device, dev) : NULL; -} -EXPORT_SYMBOL_GPL(cosm_find_cdev_by_id); - -static int __init cosm_init(void) -{ - return bus_register(&cosm_bus); -} - -static void __exit cosm_exit(void) -{ - bus_unregister(&cosm_bus); - ida_destroy(&cosm_index_ida); -} - -core_initcall(cosm_init); -module_exit(cosm_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC card OS state management bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/cosm_bus.h b/drivers/misc/mic/bus/cosm_bus.h deleted file mode 100644 index d50d7aea168d..000000000000 --- a/drivers/misc/mic/bus/cosm_bus.h +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC COSM Bus Driver - */ -#ifndef _COSM_BUS_H_ -#define _COSM_BUS_H_ - -#include -#include -#include "../common/mic_dev.h" - -/** - * cosm_device - representation of a cosm device - * - * @attr_group: Pointer to list of sysfs attribute groups. - * @sdev: Device for sysfs entries. - * @state: MIC state. - * @prev_state: MIC state previous to MIC_RESETTING - * @shutdown_status: MIC status reported by card for shutdown/crashes. - * @shutdown_status_int: Internal shutdown status maintained by the driver - * @cosm_mutex: Mutex for synchronizing access to data structures. - * @reset_trigger_work: Work for triggering reset requests. - * @scif_work: Work for handling per device SCIF connections - * @cmdline: Kernel command line. - * @firmware: Firmware file name. - * @ramdisk: Ramdisk file name. - * @bootmode: Boot mode i.e. "linux" or "elf" for flash updates. - * @log_buf_addr: Log buffer address for MIC. - * @log_buf_len: Log buffer length address for MIC. - * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes. - * @hw_ops: the hardware bus ops for this device. - * @dev: underlying device. - * @index: unique position on the cosm bus - * @dbg_dir: debug fs directory - * @newepd: new endpoint from scif accept to be assigned to this cdev - * @epd: SCIF endpoint for this cdev - * @heartbeat_watchdog_enable: if heartbeat watchdog is enabled for this cdev - * @sysfs_heartbeat_enable: sysfs setting for disabling heartbeat notification - */ -struct cosm_device { - const struct attribute_group **attr_group; - struct device *sdev; - u8 state; - u8 prev_state; - u8 shutdown_status; - u8 shutdown_status_int; - struct mutex cosm_mutex; - struct work_struct reset_trigger_work; - struct work_struct scif_work; - char *cmdline; - char *firmware; - char *ramdisk; - char *bootmode; - void *log_buf_addr; - int *log_buf_len; - struct kernfs_node *state_sysfs; - struct cosm_hw_ops *hw_ops; - struct device dev; - int index; - struct dentry *dbg_dir; - scif_epd_t newepd; - scif_epd_t epd; - bool heartbeat_watchdog_enable; - bool sysfs_heartbeat_enable; -}; - -/** - * cosm_driver - operations for a cosm driver - * - * @driver: underlying device driver (populate name and owner). - * @probe: the function to call when a device is found. Returns 0 or -errno. - * @remove: the function to call when a device is removed. - */ -struct cosm_driver { - struct device_driver driver; - int (*probe)(struct cosm_device *dev); - void (*remove)(struct cosm_device *dev); -}; - -/** - * cosm_hw_ops - cosm bus ops - * - * @reset: trigger MIC reset - * @force_reset: force MIC reset - * @post_reset: inform MIC reset is complete - * @ready: is MIC ready for OS download - * @start: boot MIC - * @stop: prepare MIC for reset - * @family: return MIC HW family string - * @stepping: return MIC HW stepping string - * @aper: return MIC PCIe aperture - */ -struct cosm_hw_ops { - void (*reset)(struct cosm_device *cdev); - void (*force_reset)(struct cosm_device *cdev); - void (*post_reset)(struct cosm_device *cdev, enum mic_states state); - bool (*ready)(struct cosm_device *cdev); - int (*start)(struct cosm_device *cdev, int id); - void (*stop)(struct cosm_device *cdev, bool force); - ssize_t (*family)(struct cosm_device *cdev, char *buf); - ssize_t (*stepping)(struct cosm_device *cdev, char *buf); - struct mic_mw *(*aper)(struct cosm_device *cdev); -}; - -struct cosm_device * -cosm_register_device(struct device *pdev, struct cosm_hw_ops *hw_ops); -void cosm_unregister_device(struct cosm_device *dev); -int cosm_register_driver(struct cosm_driver *drv); -void cosm_unregister_driver(struct cosm_driver *drv); -struct cosm_device *cosm_find_cdev_by_id(int id); - -static inline struct cosm_device *dev_to_cosm(struct device *dev) -{ - return container_of(dev, struct cosm_device, dev); -} - -static inline struct cosm_driver *drv_to_cosm(struct device_driver *drv) -{ - return container_of(drv, struct cosm_driver, driver); -} -#endif /* _COSM_BUS_H */ diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c deleted file mode 100644 index a08cb29692a8..000000000000 --- a/drivers/misc/mic/bus/mic_bus.c +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel MIC Bus driver. - * - * This implementation is very similar to the the virtio bus driver - * implementation @ drivers/virtio/virtio.c - */ -#include -#include -#include -#include -#include - -static ssize_t device_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct mbus_device *dev = dev_to_mbus(d); - return sprintf(buf, "0x%04x\n", dev->id.device); -} -static DEVICE_ATTR_RO(device); - -static ssize_t vendor_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct mbus_device *dev = dev_to_mbus(d); - return sprintf(buf, "0x%04x\n", dev->id.vendor); -} -static DEVICE_ATTR_RO(vendor); - -static ssize_t modalias_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct mbus_device *dev = dev_to_mbus(d); - return sprintf(buf, "mbus:d%08Xv%08X\n", - dev->id.device, dev->id.vendor); -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *mbus_dev_attrs[] = { - &dev_attr_device.attr, - &dev_attr_vendor.attr, - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(mbus_dev); - -static inline int mbus_id_match(const struct mbus_device *dev, - const struct mbus_device_id *id) -{ - if (id->device != dev->id.device && id->device != MBUS_DEV_ANY_ID) - return 0; - - return id->vendor == MBUS_DEV_ANY_ID || id->vendor == dev->id.vendor; -} - -/* - * This looks through all the IDs a driver claims to support. If any of them - * match, we return 1 and the kernel will call mbus_dev_probe(). - */ -static int mbus_dev_match(struct device *dv, struct device_driver *dr) -{ - unsigned int i; - struct mbus_device *dev = dev_to_mbus(dv); - const struct mbus_device_id *ids; - - ids = drv_to_mbus(dr)->id_table; - for (i = 0; ids[i].device; i++) - if (mbus_id_match(dev, &ids[i])) - return 1; - return 0; -} - -static int mbus_uevent(struct device *dv, struct kobj_uevent_env *env) -{ - struct mbus_device *dev = dev_to_mbus(dv); - - return add_uevent_var(env, "MODALIAS=mbus:d%08Xv%08X", - dev->id.device, dev->id.vendor); -} - -static int mbus_dev_probe(struct device *d) -{ - int err; - struct mbus_device *dev = dev_to_mbus(d); - struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); - - err = drv->probe(dev); - if (!err) - if (drv->scan) - drv->scan(dev); - return err; -} - -static int mbus_dev_remove(struct device *d) -{ - struct mbus_device *dev = dev_to_mbus(d); - struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); - - drv->remove(dev); - return 0; -} - -static struct bus_type mic_bus = { - .name = "mic_bus", - .match = mbus_dev_match, - .dev_groups = mbus_dev_groups, - .uevent = mbus_uevent, - .probe = mbus_dev_probe, - .remove = mbus_dev_remove, -}; - -int mbus_register_driver(struct mbus_driver *driver) -{ - driver->driver.bus = &mic_bus; - return driver_register(&driver->driver); -} -EXPORT_SYMBOL_GPL(mbus_register_driver); - -void mbus_unregister_driver(struct mbus_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(mbus_unregister_driver); - -static void mbus_release_dev(struct device *d) -{ - struct mbus_device *mbdev = dev_to_mbus(d); - kfree(mbdev); -} - -struct mbus_device * -mbus_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, - struct mbus_hw_ops *hw_ops, int index, - void __iomem *mmio_va) -{ - int ret; - struct mbus_device *mbdev; - - mbdev = kzalloc(sizeof(*mbdev), GFP_KERNEL); - if (!mbdev) - return ERR_PTR(-ENOMEM); - - mbdev->mmio_va = mmio_va; - mbdev->dev.parent = pdev; - mbdev->id.device = id; - mbdev->id.vendor = MBUS_DEV_ANY_ID; - mbdev->dev.dma_ops = dma_ops; - mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask; - dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64)); - mbdev->dev.release = mbus_release_dev; - mbdev->hw_ops = hw_ops; - mbdev->dev.bus = &mic_bus; - mbdev->index = index; - dev_set_name(&mbdev->dev, "mbus-dev%u", mbdev->index); - /* - * device_register() causes the bus infrastructure to look for a - * matching driver. - */ - ret = device_register(&mbdev->dev); - if (ret) - goto free_mbdev; - return mbdev; -free_mbdev: - put_device(&mbdev->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(mbus_register_device); - -void mbus_unregister_device(struct mbus_device *mbdev) -{ - device_unregister(&mbdev->dev); -} -EXPORT_SYMBOL_GPL(mbus_unregister_device); - -static int __init mbus_init(void) -{ - return bus_register(&mic_bus); -} - -static void __exit mbus_exit(void) -{ - bus_unregister(&mic_bus); -} - -core_initcall(mbus_init); -module_exit(mbus_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC Bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/scif_bus.c b/drivers/misc/mic/bus/scif_bus.c deleted file mode 100644 index ad7c3604f151..000000000000 --- a/drivers/misc/mic/bus/scif_bus.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel Symmetric Communications Interface Bus driver. - */ -#include -#include -#include -#include - -#include "scif_bus.h" - -static ssize_t device_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - - return sprintf(buf, "0x%04x\n", dev->id.device); -} -static DEVICE_ATTR_RO(device); - -static ssize_t vendor_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - - return sprintf(buf, "0x%04x\n", dev->id.vendor); -} -static DEVICE_ATTR_RO(vendor); - -static ssize_t modalias_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - - return sprintf(buf, "scif:d%08Xv%08X\n", - dev->id.device, dev->id.vendor); -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *scif_dev_attrs[] = { - &dev_attr_device.attr, - &dev_attr_vendor.attr, - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(scif_dev); - -static inline int scif_id_match(const struct scif_hw_dev *dev, - const struct scif_hw_dev_id *id) -{ - if (id->device != dev->id.device && id->device != SCIF_DEV_ANY_ID) - return 0; - - return id->vendor == SCIF_DEV_ANY_ID || id->vendor == dev->id.vendor; -} - -/* - * This looks through all the IDs a driver claims to support. If any of them - * match, we return 1 and the kernel will call scif_dev_probe(). - */ -static int scif_dev_match(struct device *dv, struct device_driver *dr) -{ - unsigned int i; - struct scif_hw_dev *dev = dev_to_scif(dv); - const struct scif_hw_dev_id *ids; - - ids = drv_to_scif(dr)->id_table; - for (i = 0; ids[i].device; i++) - if (scif_id_match(dev, &ids[i])) - return 1; - return 0; -} - -static int scif_uevent(struct device *dv, struct kobj_uevent_env *env) -{ - struct scif_hw_dev *dev = dev_to_scif(dv); - - return add_uevent_var(env, "MODALIAS=scif:d%08Xv%08X", - dev->id.device, dev->id.vendor); -} - -static int scif_dev_probe(struct device *d) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - struct scif_driver *drv = drv_to_scif(dev->dev.driver); - - return drv->probe(dev); -} - -static int scif_dev_remove(struct device *d) -{ - struct scif_hw_dev *dev = dev_to_scif(d); - struct scif_driver *drv = drv_to_scif(dev->dev.driver); - - drv->remove(dev); - return 0; -} - -static struct bus_type scif_bus = { - .name = "scif_bus", - .match = scif_dev_match, - .dev_groups = scif_dev_groups, - .uevent = scif_uevent, - .probe = scif_dev_probe, - .remove = scif_dev_remove, -}; - -int scif_register_driver(struct scif_driver *driver) -{ - driver->driver.bus = &scif_bus; - return driver_register(&driver->driver); -} -EXPORT_SYMBOL_GPL(scif_register_driver); - -void scif_unregister_driver(struct scif_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(scif_unregister_driver); - -static void scif_release_dev(struct device *d) -{ - struct scif_hw_dev *sdev = dev_to_scif(d); - - kfree(sdev); -} - -struct scif_hw_dev * -scif_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, - struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, - struct mic_mw *mmio, struct mic_mw *aper, void *dp, - void __iomem *rdp, struct dma_chan **chan, int num_chan, - bool card_rel_da) -{ - int ret; - struct scif_hw_dev *sdev; - - sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); - if (!sdev) - return ERR_PTR(-ENOMEM); - - sdev->dev.parent = pdev; - sdev->id.device = id; - sdev->id.vendor = SCIF_DEV_ANY_ID; - sdev->dev.dma_ops = dma_ops; - sdev->dev.release = scif_release_dev; - sdev->hw_ops = hw_ops; - sdev->dnode = dnode; - sdev->snode = snode; - dev_set_drvdata(&sdev->dev, sdev); - sdev->dev.bus = &scif_bus; - sdev->mmio = mmio; - sdev->aper = aper; - sdev->dp = dp; - sdev->rdp = rdp; - sdev->dev.dma_mask = &sdev->dev.coherent_dma_mask; - dma_set_mask(&sdev->dev, DMA_BIT_MASK(64)); - sdev->dma_ch = chan; - sdev->num_dma_ch = num_chan; - sdev->card_rel_da = card_rel_da; - dev_set_name(&sdev->dev, "scif-dev%u", sdev->dnode); - /* - * device_register() causes the bus infrastructure to look for a - * matching driver. - */ - ret = device_register(&sdev->dev); - if (ret) - goto free_sdev; - return sdev; -free_sdev: - put_device(&sdev->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(scif_register_device); - -void scif_unregister_device(struct scif_hw_dev *sdev) -{ - device_unregister(&sdev->dev); -} -EXPORT_SYMBOL_GPL(scif_unregister_device); - -static int __init scif_init(void) -{ - return bus_register(&scif_bus); -} - -static void __exit scif_exit(void) -{ - bus_unregister(&scif_bus); -} - -core_initcall(scif_init); -module_exit(scif_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) SCIF Bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/scif_bus.h b/drivers/misc/mic/bus/scif_bus.h deleted file mode 100644 index 4981eb56f879..000000000000 --- a/drivers/misc/mic/bus/scif_bus.h +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel Symmetric Communications Interface Bus driver. - */ -#ifndef _SCIF_BUS_H_ -#define _SCIF_BUS_H_ -/* - * Everything a scif driver needs to work with any particular scif - * hardware abstraction layer. - */ -#include - -#include -#include "../common/mic_dev.h" - -struct scif_hw_dev_id { - u32 device; - u32 vendor; -}; - -#define MIC_SCIF_DEV 1 -#define SCIF_DEV_ANY_ID 0xffffffff - -/** - * scif_hw_dev - representation of a hardware device abstracted for scif - * @hw_ops: the hardware ops supported by this device - * @id: the device type identification (used to match it with a driver) - * @mmio: MMIO memory window - * @aper: Aperture memory window - * @dev: underlying device - * @dnode - The destination node which this device will communicate with. - * @snode - The source node for this device. - * @dp - Self device page - * @rdp - Remote device page - * @dma_ch - Array of DMA channels - * @num_dma_ch - Number of DMA channels available - * @card_rel_da - Set to true if DMA addresses programmed in the DMA engine - * are relative to the card point of view - */ -struct scif_hw_dev { - struct scif_hw_ops *hw_ops; - struct scif_hw_dev_id id; - struct mic_mw *mmio; - struct mic_mw *aper; - struct device dev; - u8 dnode; - u8 snode; - void *dp; - void __iomem *rdp; - struct dma_chan **dma_ch; - int num_dma_ch; - bool card_rel_da; -}; - -/** - * scif_driver - operations for a scif I/O driver - * @driver: underlying device driver (populate name and owner). - * @id_table: the ids serviced by this driver. - * @probe: the function to call when a device is found. Returns 0 or -errno. - * @remove: the function to call when a device is removed. - */ -struct scif_driver { - struct device_driver driver; - const struct scif_hw_dev_id *id_table; - int (*probe)(struct scif_hw_dev *dev); - void (*remove)(struct scif_hw_dev *dev); -}; - -/** - * scif_hw_ops - Hardware operations for accessing a SCIF device on the SCIF bus. - * - * @next_db: Obtain the next available doorbell. - * @request_irq: Request an interrupt on a particular doorbell. - * @free_irq: Free an interrupt requested previously. - * @ack_interrupt: acknowledge an interrupt in the ISR. - * @send_intr: Send an interrupt to the remote node on a specified doorbell. - * @send_p2p_intr: Send an interrupt to the peer node on a specified doorbell - * which is specifically targeted for a peer to peer node. - * @remap: Map a buffer with the specified physical address and length. - * @unmap: Unmap a buffer previously mapped. - */ -struct scif_hw_ops { - int (*next_db)(struct scif_hw_dev *sdev); - struct mic_irq * (*request_irq)(struct scif_hw_dev *sdev, - irqreturn_t (*func)(int irq, - void *data), - const char *name, void *data, - int db); - void (*free_irq)(struct scif_hw_dev *sdev, - struct mic_irq *cookie, void *data); - void (*ack_interrupt)(struct scif_hw_dev *sdev, int num); - void (*send_intr)(struct scif_hw_dev *sdev, int db); - void (*send_p2p_intr)(struct scif_hw_dev *sdev, int db, - struct mic_mw *mw); - void __iomem * (*remap)(struct scif_hw_dev *sdev, - phys_addr_t pa, size_t len); - void (*unmap)(struct scif_hw_dev *sdev, void __iomem *va); -}; - -int scif_register_driver(struct scif_driver *driver); -void scif_unregister_driver(struct scif_driver *driver); -struct scif_hw_dev * -scif_register_device(struct device *pdev, int id, - const struct dma_map_ops *dma_ops, - struct scif_hw_ops *hw_ops, u8 dnode, u8 snode, - struct mic_mw *mmio, struct mic_mw *aper, - void *dp, void __iomem *rdp, - struct dma_chan **chan, int num_chan, - bool card_rel_da); -void scif_unregister_device(struct scif_hw_dev *sdev); - -static inline struct scif_hw_dev *dev_to_scif(struct device *dev) -{ - return container_of(dev, struct scif_hw_dev, dev); -} - -static inline struct scif_driver *drv_to_scif(struct device_driver *drv) -{ - return container_of(drv, struct scif_driver, driver); -} -#endif /* _SCIF_BUS_H */ diff --git a/drivers/misc/mic/bus/vop_bus.c b/drivers/misc/mic/bus/vop_bus.c deleted file mode 100644 index 6935ddca1bd5..000000000000 --- a/drivers/misc/mic/bus/vop_bus.c +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio Over PCIe (VOP) Bus driver. - */ -#include -#include -#include -#include - -#include "vop_bus.h" - -static ssize_t device_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct vop_device *dev = dev_to_vop(d); - - return sprintf(buf, "0x%04x\n", dev->id.device); -} -static DEVICE_ATTR_RO(device); - -static ssize_t vendor_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct vop_device *dev = dev_to_vop(d); - - return sprintf(buf, "0x%04x\n", dev->id.vendor); -} -static DEVICE_ATTR_RO(vendor); - -static ssize_t modalias_show(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct vop_device *dev = dev_to_vop(d); - - return sprintf(buf, "vop:d%08Xv%08X\n", - dev->id.device, dev->id.vendor); -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *vop_dev_attrs[] = { - &dev_attr_device.attr, - &dev_attr_vendor.attr, - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(vop_dev); - -static inline int vop_id_match(const struct vop_device *dev, - const struct vop_device_id *id) -{ - if (id->device != dev->id.device && id->device != VOP_DEV_ANY_ID) - return 0; - - return id->vendor == VOP_DEV_ANY_ID || id->vendor == dev->id.vendor; -} - -/* - * This looks through all the IDs a driver claims to support. If any of them - * match, we return 1 and the kernel will call vop_dev_probe(). - */ -static int vop_dev_match(struct device *dv, struct device_driver *dr) -{ - unsigned int i; - struct vop_device *dev = dev_to_vop(dv); - const struct vop_device_id *ids; - - ids = drv_to_vop(dr)->id_table; - for (i = 0; ids[i].device; i++) - if (vop_id_match(dev, &ids[i])) - return 1; - return 0; -} - -static int vop_uevent(struct device *dv, struct kobj_uevent_env *env) -{ - struct vop_device *dev = dev_to_vop(dv); - - return add_uevent_var(env, "MODALIAS=vop:d%08Xv%08X", - dev->id.device, dev->id.vendor); -} - -static int vop_dev_probe(struct device *d) -{ - struct vop_device *dev = dev_to_vop(d); - struct vop_driver *drv = drv_to_vop(dev->dev.driver); - - return drv->probe(dev); -} - -static int vop_dev_remove(struct device *d) -{ - struct vop_device *dev = dev_to_vop(d); - struct vop_driver *drv = drv_to_vop(dev->dev.driver); - - drv->remove(dev); - return 0; -} - -static struct bus_type vop_bus = { - .name = "vop_bus", - .match = vop_dev_match, - .dev_groups = vop_dev_groups, - .uevent = vop_uevent, - .probe = vop_dev_probe, - .remove = vop_dev_remove, -}; - -int vop_register_driver(struct vop_driver *driver) -{ - driver->driver.bus = &vop_bus; - return driver_register(&driver->driver); -} -EXPORT_SYMBOL_GPL(vop_register_driver); - -void vop_unregister_driver(struct vop_driver *driver) -{ - driver_unregister(&driver->driver); -} -EXPORT_SYMBOL_GPL(vop_unregister_driver); - -static void vop_release_dev(struct device *d) -{ - struct vop_device *dev = dev_to_vop(d); - - kfree(dev); -} - -struct vop_device * -vop_register_device(struct device *pdev, int id, - const struct dma_map_ops *dma_ops, - struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper, - struct dma_chan *chan) -{ - int ret; - struct vop_device *vdev; - - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) - return ERR_PTR(-ENOMEM); - - vdev->dev.parent = pdev; - vdev->id.device = id; - vdev->id.vendor = VOP_DEV_ANY_ID; - vdev->dev.dma_ops = dma_ops; - vdev->dev.dma_mask = &vdev->dev.coherent_dma_mask; - dma_set_mask(&vdev->dev, DMA_BIT_MASK(64)); - vdev->dev.release = vop_release_dev; - vdev->hw_ops = hw_ops; - vdev->dev.bus = &vop_bus; - vdev->dnode = dnode; - vdev->aper = aper; - vdev->dma_ch = chan; - vdev->index = dnode - 1; - dev_set_name(&vdev->dev, "vop-dev%u", vdev->index); - /* - * device_register() causes the bus infrastructure to look for a - * matching driver. - */ - ret = device_register(&vdev->dev); - if (ret) - goto free_vdev; - return vdev; -free_vdev: - put_device(&vdev->dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(vop_register_device); - -void vop_unregister_device(struct vop_device *dev) -{ - device_unregister(&dev->dev); -} -EXPORT_SYMBOL_GPL(vop_unregister_device); - -static int __init vop_init(void) -{ - return bus_register(&vop_bus); -} - -static void __exit vop_exit(void) -{ - bus_unregister(&vop_bus); -} - -core_initcall(vop_init); -module_exit(vop_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) VOP Bus driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/bus/vop_bus.h b/drivers/misc/mic/bus/vop_bus.h deleted file mode 100644 index 4fa02808c1e2..000000000000 --- a/drivers/misc/mic/bus/vop_bus.h +++ /dev/null @@ -1,129 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio over PCIe Bus driver. - */ -#ifndef _VOP_BUS_H_ -#define _VOP_BUS_H_ -/* - * Everything a vop driver needs to work with any particular vop - * implementation. - */ -#include -#include - -#include "../common/mic_dev.h" - -struct vop_device_id { - u32 device; - u32 vendor; -}; - -#define VOP_DEV_TRNSP 1 -#define VOP_DEV_ANY_ID 0xffffffff -/* - * Size of the internal buffer used during DMA's as an intermediate buffer - * for copy to/from user. Must be an integral number of pages. - */ -#define VOP_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) - -/** - * vop_device - representation of a device using vop - * @hw_ops: the hardware ops supported by this device. - * @id: the device type identification (used to match it with a driver). - * @dev: underlying device. - * @dnode - The destination node which this device will communicate with. - * @aper: Aperture memory window - * @dma_ch - DMA channel - * @index: unique position on the vop bus - */ -struct vop_device { - struct vop_hw_ops *hw_ops; - struct vop_device_id id; - struct device dev; - u8 dnode; - struct mic_mw *aper; - struct dma_chan *dma_ch; - int index; -}; - -/** - * vop_driver - operations for a vop I/O driver - * @driver: underlying device driver (populate name and owner). - * @id_table: the ids serviced by this driver. - * @probe: the function to call when a device is found. Returns 0 or -errno. - * @remove: the function to call when a device is removed. - */ -struct vop_driver { - struct device_driver driver; - const struct vop_device_id *id_table; - int (*probe)(struct vop_device *dev); - void (*remove)(struct vop_device *dev); -}; - -/** - * vop_hw_ops - Hardware operations for accessing a VOP device on the VOP bus. - * - * @next_db: Obtain the next available doorbell. - * @request_irq: Request an interrupt on a particular doorbell. - * @free_irq: Free an interrupt requested previously. - * @ack_interrupt: acknowledge an interrupt in the ISR. - * @get_remote_dp: Get access to the virtio device page used by the remote - * node to add/remove/configure virtio devices. - * @get_dp: Get access to the virtio device page used by the self - * node to add/remove/configure virtio devices. - * @send_intr: Send an interrupt to the peer node on a specified doorbell. - * @remap: Map a buffer with the specified DMA address and length. - * @unmap: Unmap a buffer previously mapped. - * @dma_filter: The DMA filter function to use for obtaining access to - * a DMA channel on the peer node. - */ -struct vop_hw_ops { - int (*next_db)(struct vop_device *vpdev); - struct mic_irq *(*request_irq)(struct vop_device *vpdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, - int intr_src); - void (*free_irq)(struct vop_device *vpdev, - struct mic_irq *cookie, void *data); - void (*ack_interrupt)(struct vop_device *vpdev, int num); - void __iomem * (*get_remote_dp)(struct vop_device *vpdev); - void * (*get_dp)(struct vop_device *vpdev); - void (*send_intr)(struct vop_device *vpdev, int db); - void __iomem * (*remap)(struct vop_device *vpdev, - dma_addr_t pa, size_t len); - void (*unmap)(struct vop_device *vpdev, void __iomem *va); -}; - -struct vop_device * -vop_register_device(struct device *pdev, int id, - const struct dma_map_ops *dma_ops, - struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper, - struct dma_chan *chan); -void vop_unregister_device(struct vop_device *dev); -int vop_register_driver(struct vop_driver *drv); -void vop_unregister_driver(struct vop_driver *drv); - -/* - * module_vop_driver() - Helper macro for drivers that don't do - * anything special in module init/exit. This eliminates a lot of - * boilerplate. Each module may only use this macro once, and - * calling it replaces module_init() and module_exit() - */ -#define module_vop_driver(__vop_driver) \ - module_driver(__vop_driver, vop_register_driver, \ - vop_unregister_driver) - -static inline struct vop_device *dev_to_vop(struct device *dev) -{ - return container_of(dev, struct vop_device, dev); -} - -static inline struct vop_driver *drv_to_vop(struct device_driver *drv) -{ - return container_of(drv, struct vop_driver, driver); -} -#endif /* _VOP_BUS_H */ diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile deleted file mode 100644 index 921a7e7e0fbd..000000000000 --- a/drivers/misc/mic/card/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2013, Intel Corporation. -# -ccflags-y += -DINTEL_MIC_CARD - -obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o -mic_card-y += mic_x100.o -mic_card-y += mic_device.o -mic_card-y += mic_debugfs.o diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c deleted file mode 100644 index 4c326e8f4d99..000000000000 --- a/drivers/misc/mic/card/mic_debugfs.c +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#include -#include -#include -#include -#include - -#include "../common/mic_dev.h" -#include "mic_device.h" - -/* Debugfs parent dir */ -static struct dentry *mic_dbg; - -/* - * mic_intr_show - Send interrupts to host. - */ -static int mic_intr_show(struct seq_file *s, void *unused) -{ - struct mic_driver *mdrv = s->private; - struct mic_device *mdev = &mdrv->mdev; - - mic_send_intr(mdev, 0); - msleep(1000); - mic_send_intr(mdev, 1); - msleep(1000); - mic_send_intr(mdev, 2); - msleep(1000); - mic_send_intr(mdev, 3); - msleep(1000); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_intr); - -/* - * mic_create_card_debug_dir - Initialize MIC debugfs entries. - */ -void __init mic_create_card_debug_dir(struct mic_driver *mdrv) -{ - if (!mic_dbg) - return; - - mdrv->dbg_dir = debugfs_create_dir(mdrv->name, mic_dbg); - - debugfs_create_file("intr_test", 0444, mdrv->dbg_dir, mdrv, - &mic_intr_fops); -} - -/* - * mic_delete_card_debug_dir - Uninitialize MIC debugfs entries. - */ -void mic_delete_card_debug_dir(struct mic_driver *mdrv) -{ - debugfs_remove_recursive(mdrv->dbg_dir); -} - -/* - * mic_init_card_debugfs - Initialize global debugfs entry. - */ -void __init mic_init_card_debugfs(void) -{ - mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); -} - -/* - * mic_exit_card_debugfs - Uninitialize global debugfs entry - */ -void mic_exit_card_debugfs(void) -{ - debugfs_remove(mic_dbg); -} diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c deleted file mode 100644 index a15606259bdc..000000000000 --- a/drivers/misc/mic/card/mic_device.c +++ /dev/null @@ -1,417 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#include -#include -#include -#include -#include -#include - -#include -#include "../common/mic_dev.h" -#include "mic_device.h" - -static struct mic_driver *g_drv; - -static int __init mic_dp_init(void) -{ - struct mic_driver *mdrv = g_drv; - struct mic_device *mdev = &mdrv->mdev; - struct mic_bootparam __iomem *bootparam; - u64 lo, hi, dp_dma_addr; - u32 magic; - - lo = mic_read_spad(&mdrv->mdev, MIC_DPLO_SPAD); - hi = mic_read_spad(&mdrv->mdev, MIC_DPHI_SPAD); - - dp_dma_addr = lo | (hi << 32); - mdrv->dp = mic_card_map(mdev, dp_dma_addr, MIC_DP_SIZE); - if (!mdrv->dp) { - dev_err(mdrv->dev, "Cannot remap Aperture BAR\n"); - return -ENOMEM; - } - bootparam = mdrv->dp; - magic = ioread32(&bootparam->magic); - if (MIC_MAGIC != magic) { - dev_err(mdrv->dev, "bootparam magic mismatch 0x%x\n", magic); - return -EIO; - } - return 0; -} - -/* Uninitialize the device page */ -static void mic_dp_uninit(void) -{ - mic_card_unmap(&g_drv->mdev, g_drv->dp); -} - -/** - * mic_request_card_irq - request an irq. - * - * @handler: interrupt handler passed to request_threaded_irq. - * @thread_fn: thread fn. passed to request_threaded_irq. - * @name: The ASCII name of the callee requesting the irq. - * @data: private data that is returned back when calling the - * function handler. - * @index: The doorbell index of the requester. - * - * returns: The cookie that is transparent to the caller. Passed - * back when calling mic_free_irq. An appropriate error code - * is returned on failure. Caller needs to use IS_ERR(return_val) - * to check for failure and PTR_ERR(return_val) to obtained the - * error code. - * - */ -struct mic_irq * -mic_request_card_irq(irq_handler_t handler, - irq_handler_t thread_fn, const char *name, - void *data, int index) -{ - int rc = 0; - unsigned long cookie; - struct mic_driver *mdrv = g_drv; - - rc = request_threaded_irq(mic_db_to_irq(mdrv, index), handler, - thread_fn, 0, name, data); - if (rc) { - dev_err(mdrv->dev, "request_threaded_irq failed rc = %d\n", rc); - goto err; - } - mdrv->irq_info.irq_usage_count[index]++; - cookie = index; - return (struct mic_irq *)cookie; -err: - return ERR_PTR(rc); -} - -/** - * mic_free_card_irq - free irq. - * - * @cookie: cookie obtained during a successful call to mic_request_threaded_irq - * @data: private data specified by the calling function during the - * mic_request_threaded_irq - * - * returns: none. - */ -void mic_free_card_irq(struct mic_irq *cookie, void *data) -{ - int index; - struct mic_driver *mdrv = g_drv; - - index = (unsigned long)cookie & 0xFFFFU; - free_irq(mic_db_to_irq(mdrv, index), data); - mdrv->irq_info.irq_usage_count[index]--; -} - -/** - * mic_next_card_db - Get the doorbell with minimum usage count. - * - * Returns the irq index. - */ -int mic_next_card_db(void) -{ - int i; - int index = 0; - struct mic_driver *mdrv = g_drv; - - for (i = 0; i < mdrv->intr_info.num_intr; i++) { - if (mdrv->irq_info.irq_usage_count[i] < - mdrv->irq_info.irq_usage_count[index]) - index = i; - } - - return index; -} - -/** - * mic_init_irq - Initialize irq information. - * - * Returns 0 in success. Appropriate error code on failure. - */ -static int mic_init_irq(void) -{ - struct mic_driver *mdrv = g_drv; - - mdrv->irq_info.irq_usage_count = kzalloc((sizeof(u32) * - mdrv->intr_info.num_intr), - GFP_KERNEL); - if (!mdrv->irq_info.irq_usage_count) - return -ENOMEM; - return 0; -} - -/** - * mic_uninit_irq - Uninitialize irq information. - * - * None. - */ -static void mic_uninit_irq(void) -{ - struct mic_driver *mdrv = g_drv; - - kfree(mdrv->irq_info.irq_usage_count); -} - -static inline struct mic_driver *scdev_to_mdrv(struct scif_hw_dev *scdev) -{ - return dev_get_drvdata(scdev->dev.parent); -} - -static struct mic_irq * -___mic_request_irq(struct scif_hw_dev *scdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, - int db) -{ - return mic_request_card_irq(func, NULL, name, data, db); -} - -static void -___mic_free_irq(struct scif_hw_dev *scdev, - struct mic_irq *cookie, void *data) -{ - return mic_free_card_irq(cookie, data); -} - -static void ___mic_ack_interrupt(struct scif_hw_dev *scdev, int num) -{ - struct mic_driver *mdrv = scdev_to_mdrv(scdev); - - mic_ack_interrupt(&mdrv->mdev); -} - -static int ___mic_next_db(struct scif_hw_dev *scdev) -{ - return mic_next_card_db(); -} - -static void ___mic_send_intr(struct scif_hw_dev *scdev, int db) -{ - struct mic_driver *mdrv = scdev_to_mdrv(scdev); - - mic_send_intr(&mdrv->mdev, db); -} - -static void ___mic_send_p2p_intr(struct scif_hw_dev *scdev, int db, - struct mic_mw *mw) -{ - mic_send_p2p_intr(db, mw); -} - -static void __iomem * -___mic_ioremap(struct scif_hw_dev *scdev, - phys_addr_t pa, size_t len) -{ - struct mic_driver *mdrv = scdev_to_mdrv(scdev); - - return mic_card_map(&mdrv->mdev, pa, len); -} - -static void ___mic_iounmap(struct scif_hw_dev *scdev, void __iomem *va) -{ - struct mic_driver *mdrv = scdev_to_mdrv(scdev); - - mic_card_unmap(&mdrv->mdev, va); -} - -static struct scif_hw_ops scif_hw_ops = { - .request_irq = ___mic_request_irq, - .free_irq = ___mic_free_irq, - .ack_interrupt = ___mic_ack_interrupt, - .next_db = ___mic_next_db, - .send_intr = ___mic_send_intr, - .send_p2p_intr = ___mic_send_p2p_intr, - .remap = ___mic_ioremap, - .unmap = ___mic_iounmap, -}; - -static inline struct mic_driver *vpdev_to_mdrv(struct vop_device *vpdev) -{ - return dev_get_drvdata(vpdev->dev.parent); -} - -static struct mic_irq * -__mic_request_irq(struct vop_device *vpdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, int intr_src) -{ - return mic_request_card_irq(func, NULL, name, data, intr_src); -} - -static void __mic_free_irq(struct vop_device *vpdev, - struct mic_irq *cookie, void *data) -{ - return mic_free_card_irq(cookie, data); -} - -static void __mic_ack_interrupt(struct vop_device *vpdev, int num) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - mic_ack_interrupt(&mdrv->mdev); -} - -static int __mic_next_db(struct vop_device *vpdev) -{ - return mic_next_card_db(); -} - -static void __iomem *__mic_get_remote_dp(struct vop_device *vpdev) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - return mdrv->dp; -} - -static void __mic_send_intr(struct vop_device *vpdev, int db) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - mic_send_intr(&mdrv->mdev, db); -} - -static void __iomem *__mic_ioremap(struct vop_device *vpdev, - dma_addr_t pa, size_t len) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - return mic_card_map(&mdrv->mdev, pa, len); -} - -static void __mic_iounmap(struct vop_device *vpdev, void __iomem *va) -{ - struct mic_driver *mdrv = vpdev_to_mdrv(vpdev); - - mic_card_unmap(&mdrv->mdev, va); -} - -static struct vop_hw_ops vop_hw_ops = { - .request_irq = __mic_request_irq, - .free_irq = __mic_free_irq, - .ack_interrupt = __mic_ack_interrupt, - .next_db = __mic_next_db, - .get_remote_dp = __mic_get_remote_dp, - .send_intr = __mic_send_intr, - .remap = __mic_ioremap, - .unmap = __mic_iounmap, -}; - -static int mic_request_dma_chans(struct mic_driver *mdrv) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - - dma_cap_zero(mask); - dma_cap_set(DMA_MEMCPY, mask); - - do { - chan = dma_request_channel(mask, NULL, NULL); - if (chan) { - mdrv->dma_ch[mdrv->num_dma_ch++] = chan; - if (mdrv->num_dma_ch >= MIC_MAX_DMA_CHAN) - break; - } - } while (chan); - dev_info(mdrv->dev, "DMA channels # %d\n", mdrv->num_dma_ch); - return mdrv->num_dma_ch; -} - -static void mic_free_dma_chans(struct mic_driver *mdrv) -{ - int i = 0; - - for (i = 0; i < mdrv->num_dma_ch; i++) { - dma_release_channel(mdrv->dma_ch[i]); - mdrv->dma_ch[i] = NULL; - } - mdrv->num_dma_ch = 0; -} - -/* - * mic_driver_init - MIC driver initialization tasks. - * - * Returns 0 in success. Appropriate error code on failure. - */ -int __init mic_driver_init(struct mic_driver *mdrv) -{ - int rc; - struct mic_bootparam __iomem *bootparam; - u8 node_id; - - g_drv = mdrv; - /* Unloading the card module is not supported. */ - if (!try_module_get(mdrv->dev->driver->owner)) { - rc = -ENODEV; - goto done; - } - rc = mic_dp_init(); - if (rc) - goto put; - rc = mic_init_irq(); - if (rc) - goto dp_uninit; - if (!mic_request_dma_chans(mdrv)) { - rc = -ENODEV; - goto irq_uninit; - } - mdrv->vpdev = vop_register_device(mdrv->dev, VOP_DEV_TRNSP, - NULL, &vop_hw_ops, 0, - NULL, mdrv->dma_ch[0]); - if (IS_ERR(mdrv->vpdev)) { - rc = PTR_ERR(mdrv->vpdev); - goto dma_free; - } - bootparam = mdrv->dp; - node_id = ioread8(&bootparam->node_id); - mdrv->scdev = scif_register_device(mdrv->dev, MIC_SCIF_DEV, - NULL, &scif_hw_ops, - 0, node_id, &mdrv->mdev.mmio, NULL, - NULL, mdrv->dp, mdrv->dma_ch, - mdrv->num_dma_ch, true); - if (IS_ERR(mdrv->scdev)) { - rc = PTR_ERR(mdrv->scdev); - goto vop_remove; - } - mic_create_card_debug_dir(mdrv); -done: - return rc; -vop_remove: - vop_unregister_device(mdrv->vpdev); -dma_free: - mic_free_dma_chans(mdrv); -irq_uninit: - mic_uninit_irq(); -dp_uninit: - mic_dp_uninit(); -put: - module_put(mdrv->dev->driver->owner); - return rc; -} - -/* - * mic_driver_uninit - MIC driver uninitialization tasks. - * - * Returns None - */ -void mic_driver_uninit(struct mic_driver *mdrv) -{ - mic_delete_card_debug_dir(mdrv); - scif_unregister_device(mdrv->scdev); - vop_unregister_device(mdrv->vpdev); - mic_free_dma_chans(mdrv); - mic_uninit_irq(); - mic_dp_uninit(); - module_put(mdrv->dev->driver->owner); -} diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h deleted file mode 100644 index d6cc69a235a3..000000000000 --- a/drivers/misc/mic/card/mic_device.h +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#ifndef _MIC_CARD_DEVICE_H_ -#define _MIC_CARD_DEVICE_H_ - -#include -#include -#include -#include -#include "../bus/scif_bus.h" -#include "../bus/vop_bus.h" - -/** - * struct mic_intr_info - Contains h/w specific interrupt sources info - * - * @num_intr: The number of irqs available - */ -struct mic_intr_info { - u32 num_intr; -}; - -/** - * struct mic_irq_info - OS specific irq information - * - * @irq_usage_count: usage count array tracking the number of sources - * assigned for each irq. - */ -struct mic_irq_info { - int *irq_usage_count; -}; - -/** - * struct mic_device - MIC device information. - * - * @mmio: MMIO bar information. - */ -struct mic_device { - struct mic_mw mmio; -}; - -/** - * struct mic_driver - MIC card driver information. - * - * @name: Name for MIC driver. - * @dbg_dir: debugfs directory of this MIC device. - * @dev: The device backing this MIC. - * @dp: The pointer to the virtio device page. - * @mdev: MIC device information for the host. - * @hotplug_work: Hot plug work for adding/removing virtio devices. - * @irq_info: The OS specific irq information - * @intr_info: H/W specific interrupt information. - * @dma_mbdev: dma device on the MIC virtual bus. - * @dma_ch - Array of DMA channels - * @num_dma_ch - Number of DMA channels available - * @scdev: SCIF device on the SCIF virtual bus. - * @vpdev: Virtio over PCIe device on the VOP virtual bus. - */ -struct mic_driver { - char name[20]; - struct dentry *dbg_dir; - struct device *dev; - void __iomem *dp; - struct mic_device mdev; - struct work_struct hotplug_work; - struct mic_irq_info irq_info; - struct mic_intr_info intr_info; - struct mbus_device *dma_mbdev; - struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; - int num_dma_ch; - struct scif_hw_dev *scdev; - struct vop_device *vpdev; -}; - -/** - * struct mic_irq - opaque pointer used as cookie - */ -struct mic_irq; - -/** - * mic_mmio_read - read from an MMIO register. - * @mw: MMIO register base virtual address. - * @offset: register offset. - * - * RETURNS: register value. - */ -static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) -{ - return ioread32(mw->va + offset); -} - -/** - * mic_mmio_write - write to an MMIO register. - * @mw: MMIO register base virtual address. - * @val: the data value to put into the register - * @offset: register offset. - * - * RETURNS: none. - */ -static inline void -mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) -{ - iowrite32(val, mw->va + offset); -} - -int mic_driver_init(struct mic_driver *mdrv); -void mic_driver_uninit(struct mic_driver *mdrv); -int mic_next_card_db(void); -struct mic_irq * -mic_request_card_irq(irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int db); -void mic_free_card_irq(struct mic_irq *cookie, void *data); -u32 mic_read_spad(struct mic_device *mdev, unsigned int idx); -void mic_send_intr(struct mic_device *mdev, int doorbell); -void mic_send_p2p_intr(int doorbell, struct mic_mw *mw); -int mic_db_to_irq(struct mic_driver *mdrv, int db); -u32 mic_ack_interrupt(struct mic_device *mdev); -void mic_hw_intr_init(struct mic_driver *mdrv); -void __iomem * -mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size); -void mic_card_unmap(struct mic_device *mdev, void __iomem *addr); -void __init mic_create_card_debug_dir(struct mic_driver *mdrv); -void mic_delete_card_debug_dir(struct mic_driver *mdrv); -void __init mic_init_card_debugfs(void); -void mic_exit_card_debugfs(void); -#endif diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c deleted file mode 100644 index c8bff2916d3d..000000000000 --- a/drivers/misc/mic/card/mic_x100.c +++ /dev/null @@ -1,347 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#include -#include -#include - -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_x100.h" - -static const char mic_driver_name[] = "mic"; - -static struct mic_driver g_drv; - -/** - * mic_read_spad - read from the scratchpad register - * @mdev: pointer to mic_device instance - * @idx: index to scratchpad register, 0 based - * - * This function allows reading of the 32bit scratchpad register. - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -u32 mic_read_spad(struct mic_device *mdev, unsigned int idx) -{ - return mic_mmio_read(&mdev->mmio, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SPAD0 + idx * 4); -} - -/** - * __mic_send_intr - Send interrupt to Host. - * @mdev: pointer to mic_device instance - * @doorbell: Doorbell number. - */ -void mic_send_intr(struct mic_device *mdev, int doorbell) -{ - struct mic_mw *mw = &mdev->mmio; - - if (doorbell > MIC_X100_MAX_DOORBELL_IDX) - return; - /* Ensure that the interrupt is ordered w.r.t previous stores. */ - wmb(); - mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT, - MIC_X100_SBOX_BASE_ADDRESS + - (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); -} - -/* - * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. - */ -static void mic_x100_send_sbox_intr(struct mic_mw *mw, int doorbell) -{ - u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; - u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + - apic_icr_offset); - - /* for MIC we need to make sure we "hit" the send_icr bit (13) */ - apicicr_low = (apicicr_low | (1 << 13)); - /* - * Ensure that the interrupt is ordered w.r.t. previous stores - * to main memory. Fence instructions are not implemented in X100 - * since execution is in order but a compiler barrier is still - * required. - */ - wmb(); - mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); -} - -static void mic_x100_send_rdmasr_intr(struct mic_mw *mw, int doorbell) -{ - int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); - /* - * Ensure that the interrupt is ordered w.r.t. previous stores - * to main memory. Fence instructions are not implemented in X100 - * since execution is in order but a compiler barrier is still - * required. - */ - wmb(); - mic_mmio_write(mw, 0, MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); -} - -/** - * mic_ack_interrupt - Device specific interrupt handling. - * @mdev: pointer to mic_device instance - * - * Returns: bitmask of doorbell events triggered. - */ -u32 mic_ack_interrupt(struct mic_device *mdev) -{ - return 0; -} - -static inline int mic_get_sbox_irq(int db) -{ - return MIC_X100_IRQ_BASE + db; -} - -static inline int mic_get_rdmasr_irq(int index) -{ - return MIC_X100_RDMASR_IRQ_BASE + index; -} - -void mic_send_p2p_intr(int db, struct mic_mw *mw) -{ - int rdmasr_index; - - if (db < MIC_X100_NUM_SBOX_IRQ) { - mic_x100_send_sbox_intr(mw, db); - } else { - rdmasr_index = db - MIC_X100_NUM_SBOX_IRQ; - mic_x100_send_rdmasr_intr(mw, rdmasr_index); - } -} - -/** - * mic_hw_intr_init - Initialize h/w specific interrupt - * information. - * @mdrv: pointer to mic_driver - */ -void mic_hw_intr_init(struct mic_driver *mdrv) -{ - mdrv->intr_info.num_intr = MIC_X100_NUM_SBOX_IRQ + - MIC_X100_NUM_RDMASR_IRQ; -} - -/** - * mic_db_to_irq - Retrieve irq number corresponding to a doorbell. - * @mdrv: pointer to mic_driver - * @db: The doorbell obtained for which the irq is needed. Doorbell - * may correspond to an sbox doorbell or an rdmasr index. - * - * Returns the irq corresponding to the doorbell. - */ -int mic_db_to_irq(struct mic_driver *mdrv, int db) -{ - int rdmasr_index; - - /* - * The total number of doorbell interrupts on the card are 16. Indices - * 0-8 falls in the SBOX category and 8-15 fall in the RDMASR category. - */ - if (db < MIC_X100_NUM_SBOX_IRQ) { - return mic_get_sbox_irq(db); - } else { - rdmasr_index = db - MIC_X100_NUM_SBOX_IRQ; - return mic_get_rdmasr_irq(rdmasr_index); - } -} - -/* - * mic_card_map - Allocate virtual address for a remote memory region. - * @mdev: pointer to mic_device instance. - * @addr: Remote DMA address. - * @size: Size of the region. - * - * Returns: Virtual address backing the remote memory region. - */ -void __iomem * -mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size) -{ - return ioremap(addr, size); -} - -/* - * mic_card_unmap - Unmap the virtual address for a remote memory region. - * @mdev: pointer to mic_device instance. - * @addr: Virtual address for remote memory region. - * - * Returns: None. - */ -void mic_card_unmap(struct mic_device *mdev, void __iomem *addr) -{ - iounmap(addr); -} - -static inline struct mic_driver *mbdev_to_mdrv(struct mbus_device *mbdev) -{ - return dev_get_drvdata(mbdev->dev.parent); -} - -static struct mic_irq * -_mic_request_threaded_irq(struct mbus_device *mbdev, - irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int intr_src) -{ - int rc = 0; - unsigned int irq = intr_src; - unsigned long cookie = irq; - - rc = request_threaded_irq(irq, handler, thread_fn, 0, name, data); - if (rc) { - dev_err(mbdev_to_mdrv(mbdev)->dev, - "request_threaded_irq failed rc = %d\n", rc); - return ERR_PTR(rc); - } - return (struct mic_irq *)cookie; -} - -static void _mic_free_irq(struct mbus_device *mbdev, - struct mic_irq *cookie, void *data) -{ - unsigned long irq = (unsigned long)cookie; - free_irq(irq, data); -} - -static void _mic_ack_interrupt(struct mbus_device *mbdev, int num) -{ - mic_ack_interrupt(&mbdev_to_mdrv(mbdev)->mdev); -} - -static struct mbus_hw_ops mbus_hw_ops = { - .request_threaded_irq = _mic_request_threaded_irq, - .free_irq = _mic_free_irq, - .ack_interrupt = _mic_ack_interrupt, -}; - -static int __init mic_probe(struct platform_device *pdev) -{ - struct mic_driver *mdrv = &g_drv; - struct mic_device *mdev = &mdrv->mdev; - int rc = 0; - - mdrv->dev = &pdev->dev; - snprintf(mdrv->name, sizeof(mic_driver_name), mic_driver_name); - - /* FIXME: use dma_set_mask_and_coherent() and check result */ - dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); - - mdev->mmio.pa = MIC_X100_MMIO_BASE; - mdev->mmio.len = MIC_X100_MMIO_LEN; - mdev->mmio.va = devm_ioremap(&pdev->dev, MIC_X100_MMIO_BASE, - MIC_X100_MMIO_LEN); - if (!mdev->mmio.va) { - dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); - rc = -EIO; - goto done; - } - mic_hw_intr_init(mdrv); - platform_set_drvdata(pdev, mdrv); - mdrv->dma_mbdev = mbus_register_device(mdrv->dev, MBUS_DEV_DMA_MIC, - NULL, &mbus_hw_ops, 0, - mdrv->mdev.mmio.va); - if (IS_ERR(mdrv->dma_mbdev)) { - rc = PTR_ERR(mdrv->dma_mbdev); - dev_err(&pdev->dev, "mbus_add_device failed rc %d\n", rc); - goto done; - } - rc = mic_driver_init(mdrv); - if (rc) { - dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc); - goto remove_dma; - } -done: - return rc; -remove_dma: - mbus_unregister_device(mdrv->dma_mbdev); - return rc; -} - -static int mic_remove(struct platform_device *pdev) -{ - struct mic_driver *mdrv = &g_drv; - - mic_driver_uninit(mdrv); - mbus_unregister_device(mdrv->dma_mbdev); - return 0; -} - -static void mic_platform_shutdown(struct platform_device *pdev) -{ - mic_remove(pdev); -} - -static struct platform_driver __refdata mic_platform_driver = { - .probe = mic_probe, - .remove = mic_remove, - .shutdown = mic_platform_shutdown, - .driver = { - .name = mic_driver_name, - }, -}; - -static struct platform_device *mic_platform_dev; - -static int __init mic_init(void) -{ - int ret; - struct cpuinfo_x86 *c = &cpu_data(0); - - if (!(c->x86 == 11 && c->x86_model == 1)) { - ret = -ENODEV; - pr_err("%s not running on X100 ret %d\n", __func__, ret); - goto done; - } - - request_module("mic_x100_dma"); - mic_init_card_debugfs(); - - mic_platform_dev = platform_device_register_simple(mic_driver_name, - 0, NULL, 0); - ret = PTR_ERR_OR_ZERO(mic_platform_dev); - if (ret) { - pr_err("platform_device_register_full ret %d\n", ret); - goto cleanup_debugfs; - } - ret = platform_driver_register(&mic_platform_driver); - if (ret) { - pr_err("platform_driver_register ret %d\n", ret); - goto device_unregister; - } - return ret; - -device_unregister: - platform_device_unregister(mic_platform_dev); -cleanup_debugfs: - mic_exit_card_debugfs(); -done: - return ret; -} - -static void __exit mic_exit(void) -{ - platform_driver_unregister(&mic_platform_driver); - platform_device_unregister(mic_platform_dev); - mic_exit_card_debugfs(); -} - -module_init(mic_init); -module_exit(mic_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC X100 Card driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/card/mic_x100.h b/drivers/misc/mic/card/mic_x100.h deleted file mode 100644 index 46644dde0c07..000000000000 --- a/drivers/misc/mic/card/mic_x100.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Disclaimer: The codes contained in these modules may be specific to - * the Intel Software Development Platform codenamed: Knights Ferry, and - * the Intel product codenamed: Knights Corner, and are not backward - * compatible with other Intel products. Additionally, Intel will NOT - * support the codes or instruction set in future products. - * - * Intel MIC Card driver. - */ -#ifndef _MIC_X100_CARD_H_ -#define _MIC_X100_CARD_H_ - -#define MIC_X100_MMIO_BASE 0x08007C0000ULL -#define MIC_X100_MMIO_LEN 0x00020000ULL -#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000ULL - -#define MIC_X100_SBOX_SPAD0 0x0000AB20 -#define MIC_X100_SBOX_SDBIC0 0x0000CC90 -#define MIC_X100_SBOX_SDBIC0_DBREQ_BIT 0x80000000 -#define MIC_X100_SBOX_RDMASR0 0x0000B180 -#define MIC_X100_SBOX_APICICR0 0x0000A9D0 - -#define MIC_X100_MAX_DOORBELL_IDX 8 - -#define MIC_X100_NUM_SBOX_IRQ 8 -#define MIC_X100_NUM_RDMASR_IRQ 8 -#define MIC_X100_SBOX_IRQ_BASE 0 -#define MIC_X100_RDMASR_IRQ_BASE 17 - -#define MIC_X100_IRQ_BASE 26 - -#endif diff --git a/drivers/misc/mic/common/mic_dev.h b/drivers/misc/mic/common/mic_dev.h deleted file mode 100644 index f94f08df0260..000000000000 --- a/drivers/misc/mic/common/mic_dev.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC driver. - */ -#ifndef __MIC_DEV_H__ -#define __MIC_DEV_H__ - -/* The maximum number of MIC devices supported in a single host system. */ -#define MIC_MAX_NUM_DEVS 128 - -/** - * enum mic_hw_family - The hardware family to which a device belongs. - */ -enum mic_hw_family { - MIC_FAMILY_X100 = 0, - MIC_FAMILY_X200, - MIC_FAMILY_UNKNOWN, - MIC_FAMILY_LAST -}; - -/** - * struct mic_mw - MIC memory window - * - * @pa: Base physical address. - * @va: Base ioremap'd virtual address. - * @len: Size of the memory window. - */ -struct mic_mw { - phys_addr_t pa; - void __iomem *va; - resource_size_t len; -}; - -/* - * Scratch pad register offsets used by the host to communicate - * device page DMA address to the card. - */ -#define MIC_DPLO_SPAD 14 -#define MIC_DPHI_SPAD 15 - -/* - * These values are supposed to be in the config_change field of the - * device page when the host sends a config change interrupt to the card. - */ -#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 -#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 - -/* Maximum number of DMA channels */ -#define MIC_MAX_DMA_CHAN 4 - -#endif diff --git a/drivers/misc/mic/cosm/Makefile b/drivers/misc/mic/cosm/Makefile deleted file mode 100644 index 97d74cb12030..000000000000 --- a/drivers/misc/mic/cosm/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - Intel MIC Coprocessor State Management (COSM) Driver -# Copyright(c) 2015, Intel Corporation. -# -obj-$(CONFIG_MIC_COSM) += mic_cosm.o - -mic_cosm-objs := cosm_main.o -mic_cosm-objs += cosm_debugfs.o -mic_cosm-objs += cosm_sysfs.o -mic_cosm-objs += cosm_scif_server.o diff --git a/drivers/misc/mic/cosm/cosm_debugfs.c b/drivers/misc/mic/cosm/cosm_debugfs.c deleted file mode 100644 index cb55653cf1f9..000000000000 --- a/drivers/misc/mic/cosm/cosm_debugfs.c +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ - -#include -#include -#include -#include "cosm_main.h" - -/* Debugfs parent dir */ -static struct dentry *cosm_dbg; - -/* - * log_buf_show - Display MIC kernel log buffer - * - * log_buf addr/len is read from System.map by user space - * and populated in sysfs entries. - */ -static int log_buf_show(struct seq_file *s, void *unused) -{ - void __iomem *log_buf_va; - int __iomem *log_buf_len_va; - struct cosm_device *cdev = s->private; - void *kva; - int size; - u64 aper_offset; - - if (!cdev || !cdev->log_buf_addr || !cdev->log_buf_len) - goto done; - - mutex_lock(&cdev->cosm_mutex); - switch (cdev->state) { - case MIC_BOOTING: - case MIC_ONLINE: - case MIC_SHUTTING_DOWN: - break; - default: - goto unlock; - } - - /* - * Card kernel will never be relocated and any kernel text/data mapping - * can be translated to phys address by subtracting __START_KERNEL_map. - */ - aper_offset = (u64)cdev->log_buf_len - __START_KERNEL_map; - log_buf_len_va = cdev->hw_ops->aper(cdev)->va + aper_offset; - aper_offset = (u64)cdev->log_buf_addr - __START_KERNEL_map; - log_buf_va = cdev->hw_ops->aper(cdev)->va + aper_offset; - - size = ioread32(log_buf_len_va); - kva = kmalloc(size, GFP_KERNEL); - if (!kva) - goto unlock; - - memcpy_fromio(kva, log_buf_va, size); - seq_write(s, kva, size); - kfree(kva); -unlock: - mutex_unlock(&cdev->cosm_mutex); -done: - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(log_buf); - -/* - * force_reset_show - Force MIC reset - * - * Invokes the force_reset COSM bus op instead of the standard reset - * op in case a force reset of the MIC device is required - */ -static int force_reset_show(struct seq_file *s, void *pos) -{ - struct cosm_device *cdev = s->private; - - cosm_stop(cdev, true); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(force_reset); - -void cosm_create_debug_dir(struct cosm_device *cdev) -{ - char name[16]; - - if (!cosm_dbg) - return; - - scnprintf(name, sizeof(name), "mic%d", cdev->index); - cdev->dbg_dir = debugfs_create_dir(name, cosm_dbg); - - debugfs_create_file("log_buf", 0444, cdev->dbg_dir, cdev, - &log_buf_fops); - debugfs_create_file("force_reset", 0444, cdev->dbg_dir, cdev, - &force_reset_fops); -} - -void cosm_delete_debug_dir(struct cosm_device *cdev) -{ - debugfs_remove_recursive(cdev->dbg_dir); -} - -void cosm_init_debugfs(void) -{ - cosm_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); -} - -void cosm_exit_debugfs(void) -{ - debugfs_remove(cosm_dbg); -} diff --git a/drivers/misc/mic/cosm/cosm_main.c b/drivers/misc/mic/cosm/cosm_main.c deleted file mode 100644 index ebb0eac43754..000000000000 --- a/drivers/misc/mic/cosm/cosm_main.c +++ /dev/null @@ -1,382 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ - -#include -#include -#include -#include -#include -#include "cosm_main.h" - -static const char cosm_driver_name[] = "mic"; - -/* COSM ID allocator */ -static struct ida g_cosm_ida; -/* Class of MIC devices for sysfs accessibility. */ -static struct class *g_cosm_class; -/* Number of MIC devices */ -static atomic_t g_num_dev; - -/** - * cosm_hw_reset - Issue a HW reset for the MIC device - * @cdev: pointer to cosm_device instance - * @force: force a MIC to reset even if it is already reset and ready - */ -static void cosm_hw_reset(struct cosm_device *cdev, bool force) -{ - int i; - -#define MIC_RESET_TO (45) - if (force && cdev->hw_ops->force_reset) - cdev->hw_ops->force_reset(cdev); - else - cdev->hw_ops->reset(cdev); - - for (i = 0; i < MIC_RESET_TO; i++) { - if (cdev->hw_ops->ready(cdev)) { - cosm_set_state(cdev, MIC_READY); - return; - } - /* - * Resets typically take 10s of seconds to complete. - * Since an MMIO read is required to check if the - * firmware is ready or not, a 1 second delay works nicely. - */ - msleep(1000); - } - cosm_set_state(cdev, MIC_RESET_FAILED); -} - -/** - * cosm_start - Start the MIC - * @cdev: pointer to cosm_device instance - * - * This function prepares an MIC for boot and initiates boot. - * RETURNS: An appropriate -ERRNO error value on error, or 0 for success. - */ -int cosm_start(struct cosm_device *cdev) -{ - const struct cred *orig_cred; - struct cred *override_cred; - int rc; - - mutex_lock(&cdev->cosm_mutex); - if (!cdev->bootmode) { - dev_err(&cdev->dev, "%s %d bootmode not set\n", - __func__, __LINE__); - rc = -EINVAL; - goto unlock_ret; - } -retry: - if (cdev->state != MIC_READY) { - dev_err(&cdev->dev, "%s %d MIC state not READY\n", - __func__, __LINE__); - rc = -EINVAL; - goto unlock_ret; - } - if (!cdev->hw_ops->ready(cdev)) { - cosm_hw_reset(cdev, false); - /* - * The state will either be MIC_READY if the reset succeeded - * or MIC_RESET_FAILED if the firmware reset failed. - */ - goto retry; - } - - /* - * Set credentials to root to allow non-root user to download initramsfs - * with 600 permissions - */ - override_cred = prepare_creds(); - if (!override_cred) { - dev_err(&cdev->dev, "%s %d prepare_creds failed\n", - __func__, __LINE__); - rc = -ENOMEM; - goto unlock_ret; - } - override_cred->fsuid = GLOBAL_ROOT_UID; - orig_cred = override_creds(override_cred); - - rc = cdev->hw_ops->start(cdev, cdev->index); - - revert_creds(orig_cred); - put_cred(override_cred); - if (rc) - goto unlock_ret; - - /* - * If linux is being booted, card is treated 'online' only - * when the scif interface in the card is up. If anything else - * is booted, we set card to 'online' immediately. - */ - if (!strcmp(cdev->bootmode, "linux")) - cosm_set_state(cdev, MIC_BOOTING); - else - cosm_set_state(cdev, MIC_ONLINE); -unlock_ret: - mutex_unlock(&cdev->cosm_mutex); - if (rc) - dev_err(&cdev->dev, "cosm_start failed rc %d\n", rc); - return rc; -} - -/** - * cosm_stop - Prepare the MIC for reset and trigger reset - * @cdev: pointer to cosm_device instance - * @force: force a MIC to reset even if it is already reset and ready. - * - * RETURNS: None - */ -void cosm_stop(struct cosm_device *cdev, bool force) -{ - mutex_lock(&cdev->cosm_mutex); - if (cdev->state != MIC_READY || force) { - /* - * Don't call hw_ops if they have been called previously. - * stop(..) calls device_unregister and will crash the system if - * called multiple times. - */ - u8 state = cdev->state == MIC_RESETTING ? - cdev->prev_state : cdev->state; - bool call_hw_ops = state != MIC_RESET_FAILED && - state != MIC_READY; - - if (cdev->state != MIC_RESETTING) - cosm_set_state(cdev, MIC_RESETTING); - cdev->heartbeat_watchdog_enable = false; - if (call_hw_ops) - cdev->hw_ops->stop(cdev, force); - cosm_hw_reset(cdev, force); - cosm_set_shutdown_status(cdev, MIC_NOP); - if (call_hw_ops && cdev->hw_ops->post_reset) - cdev->hw_ops->post_reset(cdev, cdev->state); - } - mutex_unlock(&cdev->cosm_mutex); - flush_work(&cdev->scif_work); -} - -/** - * cosm_reset_trigger_work - Trigger MIC reset - * @work: The work structure - * - * This work is scheduled whenever the host wants to reset the MIC. - */ -static void cosm_reset_trigger_work(struct work_struct *work) -{ - struct cosm_device *cdev = container_of(work, struct cosm_device, - reset_trigger_work); - cosm_stop(cdev, false); -} - -/** - * cosm_reset - Schedule MIC reset - * @cdev: pointer to cosm_device instance - * - * RETURNS: An -EINVAL if the card is already READY or 0 for success. - */ -int cosm_reset(struct cosm_device *cdev) -{ - int rc = 0; - - mutex_lock(&cdev->cosm_mutex); - if (cdev->state != MIC_READY) { - if (cdev->state != MIC_RESETTING) { - cdev->prev_state = cdev->state; - cosm_set_state(cdev, MIC_RESETTING); - schedule_work(&cdev->reset_trigger_work); - } - } else { - dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__); - rc = -EINVAL; - } - mutex_unlock(&cdev->cosm_mutex); - return rc; -} - -/** - * cosm_shutdown - Initiate MIC shutdown. - * @cdev: pointer to cosm_device instance - * - * RETURNS: None - */ -int cosm_shutdown(struct cosm_device *cdev) -{ - struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN }; - int rc = 0; - - mutex_lock(&cdev->cosm_mutex); - if (cdev->state != MIC_ONLINE) { - rc = -EINVAL; - dev_err(&cdev->dev, "%s %d skipping shutdown in state: %s\n", - __func__, __LINE__, cosm_state_string[cdev->state]); - goto err; - } - - if (!cdev->epd) { - rc = -ENOTCONN; - dev_err(&cdev->dev, "%s %d scif endpoint not connected rc %d\n", - __func__, __LINE__, rc); - goto err; - } - - rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); - if (rc < 0) { - dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n", - __func__, __LINE__, rc); - goto err; - } - cdev->heartbeat_watchdog_enable = false; - cosm_set_state(cdev, MIC_SHUTTING_DOWN); - rc = 0; -err: - mutex_unlock(&cdev->cosm_mutex); - return rc; -} - -static int cosm_driver_probe(struct cosm_device *cdev) -{ - int rc; - - /* Initialize SCIF server at first probe */ - if (atomic_add_return(1, &g_num_dev) == 1) { - rc = cosm_scif_init(); - if (rc) - goto scif_exit; - } - mutex_init(&cdev->cosm_mutex); - INIT_WORK(&cdev->reset_trigger_work, cosm_reset_trigger_work); - INIT_WORK(&cdev->scif_work, cosm_scif_work); - cdev->sysfs_heartbeat_enable = true; - cosm_sysfs_init(cdev); - cdev->sdev = device_create_with_groups(g_cosm_class, cdev->dev.parent, - MKDEV(0, cdev->index), cdev, cdev->attr_group, - "mic%d", cdev->index); - if (IS_ERR(cdev->sdev)) { - rc = PTR_ERR(cdev->sdev); - dev_err(&cdev->dev, "device_create_with_groups failed rc %d\n", - rc); - goto scif_exit; - } - - cdev->state_sysfs = sysfs_get_dirent(cdev->sdev->kobj.sd, - "state"); - if (!cdev->state_sysfs) { - rc = -ENODEV; - dev_err(&cdev->dev, "sysfs_get_dirent failed rc %d\n", rc); - goto destroy_device; - } - cosm_create_debug_dir(cdev); - return 0; -destroy_device: - device_destroy(g_cosm_class, MKDEV(0, cdev->index)); -scif_exit: - if (atomic_dec_and_test(&g_num_dev)) - cosm_scif_exit(); - return rc; -} - -static void cosm_driver_remove(struct cosm_device *cdev) -{ - cosm_delete_debug_dir(cdev); - sysfs_put(cdev->state_sysfs); - device_destroy(g_cosm_class, MKDEV(0, cdev->index)); - flush_work(&cdev->reset_trigger_work); - cosm_stop(cdev, false); - if (atomic_dec_and_test(&g_num_dev)) - cosm_scif_exit(); - - /* These sysfs entries might have allocated */ - kfree(cdev->cmdline); - kfree(cdev->firmware); - kfree(cdev->ramdisk); - kfree(cdev->bootmode); -} - -static int cosm_suspend(struct device *dev) -{ - struct cosm_device *cdev = dev_to_cosm(dev); - - mutex_lock(&cdev->cosm_mutex); - switch (cdev->state) { - /** - * Suspend/freeze hooks in userspace have already shutdown the card. - * Card should be 'ready' in most cases. It is however possible that - * some userspace application initiated a boot. In those cases, we - * simply reset the card. - */ - case MIC_ONLINE: - case MIC_BOOTING: - case MIC_SHUTTING_DOWN: - mutex_unlock(&cdev->cosm_mutex); - cosm_stop(cdev, false); - break; - default: - mutex_unlock(&cdev->cosm_mutex); - break; - } - return 0; -} - -static const struct dev_pm_ops cosm_pm_ops = { - .suspend = cosm_suspend, - .freeze = cosm_suspend -}; - -static struct cosm_driver cosm_driver = { - .driver = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE, - .pm = &cosm_pm_ops, - }, - .probe = cosm_driver_probe, - .remove = cosm_driver_remove -}; - -static int __init cosm_init(void) -{ - int ret; - - cosm_init_debugfs(); - - g_cosm_class = class_create(THIS_MODULE, cosm_driver_name); - if (IS_ERR(g_cosm_class)) { - ret = PTR_ERR(g_cosm_class); - pr_err("class_create failed ret %d\n", ret); - goto cleanup_debugfs; - } - - ida_init(&g_cosm_ida); - ret = cosm_register_driver(&cosm_driver); - if (ret) { - pr_err("cosm_register_driver failed ret %d\n", ret); - goto ida_destroy; - } - return 0; -ida_destroy: - ida_destroy(&g_cosm_ida); - class_destroy(g_cosm_class); -cleanup_debugfs: - cosm_exit_debugfs(); - return ret; -} - -static void __exit cosm_exit(void) -{ - cosm_unregister_driver(&cosm_driver); - ida_destroy(&g_cosm_ida); - class_destroy(g_cosm_class); - cosm_exit_debugfs(); -} - -module_init(cosm_init); -module_exit(cosm_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/cosm/cosm_main.h b/drivers/misc/mic/cosm/cosm_main.h deleted file mode 100644 index 5188ad245814..000000000000 --- a/drivers/misc/mic/cosm/cosm_main.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ -#ifndef _COSM_COSM_H_ -#define _COSM_COSM_H_ - -#include -#include "../bus/cosm_bus.h" - -#define COSM_HEARTBEAT_SEND_SEC 30 -#define SCIF_COSM_LISTEN_PORT 201 - -/** - * enum COSM msg id's - * @COSM_MSG_SHUTDOWN: host->card trigger shutdown - * @COSM_MSG_SYNC_TIME: host->card send host time to card to sync time - * @COSM_MSG_HEARTBEAT: card->host heartbeat - * @COSM_MSG_SHUTDOWN_STATUS: card->host with shutdown status as payload - */ -enum cosm_msg_id { - COSM_MSG_SHUTDOWN, - COSM_MSG_SYNC_TIME, - COSM_MSG_HEARTBEAT, - COSM_MSG_SHUTDOWN_STATUS, -}; - -struct cosm_msg { - u64 id; - union { - u64 shutdown_status; - struct { - u64 tv_sec; - u64 tv_nsec; - } timespec; - }; -}; - -extern const char * const cosm_state_string[]; -extern const char * const cosm_shutdown_status_string[]; - -void cosm_sysfs_init(struct cosm_device *cdev); -int cosm_start(struct cosm_device *cdev); -void cosm_stop(struct cosm_device *cdev, bool force); -int cosm_reset(struct cosm_device *cdev); -int cosm_shutdown(struct cosm_device *cdev); -void cosm_set_state(struct cosm_device *cdev, u8 state); -void cosm_set_shutdown_status(struct cosm_device *cdev, u8 status); -void cosm_init_debugfs(void); -void cosm_exit_debugfs(void); -void cosm_create_debug_dir(struct cosm_device *cdev); -void cosm_delete_debug_dir(struct cosm_device *cdev); -int cosm_scif_init(void); -void cosm_scif_exit(void); -void cosm_scif_work(struct work_struct *work); - -#endif diff --git a/drivers/misc/mic/cosm/cosm_scif_server.c b/drivers/misc/mic/cosm/cosm_scif_server.c deleted file mode 100644 index 7baec9fd8756..000000000000 --- a/drivers/misc/mic/cosm/cosm_scif_server.c +++ /dev/null @@ -1,399 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ -#include -#include - -#include "cosm_main.h" - -/* - * The COSM driver uses SCIF to communicate between the management node and the - * MIC cards. SCIF is used to (a) Send a shutdown command to the card (b) - * receive a shutdown status back from the card upon completion of shutdown and - * (c) receive periodic heartbeat messages from the card used to deduce if the - * card has crashed. - * - * A COSM server consisting of a SCIF listening endpoint waits for incoming - * connections from the card. Upon acceptance of the connection, a separate - * work-item is scheduled to handle SCIF message processing for that card. The - * life-time of this work-item is therefore the time from which the connection - * from a card is accepted to the time at which the connection is closed. A new - * work-item starts each time the card boots and is alive till the card (a) - * shuts down (b) is reset (c) crashes (d) cosm_client driver on the card is - * unloaded. - * - * From the point of view of COSM interactions with SCIF during card - * shutdown, reset and crash are as follows: - * - * Card shutdown - * ------------- - * 1. COSM client on the card invokes orderly_poweroff() in response to SHUTDOWN - * message from the host. - * 2. Card driver shutdown callback invokes scif_unregister_device(..) resulting - * in scif_remove(..) getting called on the card - * 3. scif_remove -> scif_stop -> scif_handle_remove_node -> - * scif_peer_unregister_device -> device_unregister for the host peer device - * 4. During device_unregister remove(..) method of cosm_client is invoked which - * closes the COSM SCIF endpoint on the card. This results in a SCIF_DISCNCT - * message being sent to host SCIF. SCIF_DISCNCT message processing on the - * host SCIF sets the host COSM SCIF endpoint state to DISCONNECTED and wakes - * up the host COSM thread blocked in scif_poll(..) resulting in - * scif_poll(..) returning EPOLLHUP. - * 5. On the card, scif_peer_release_dev is next called which results in an - * SCIF_EXIT message being sent to the host and after receiving the - * SCIF_EXIT_ACK from the host the peer device teardown on the card is - * complete. - * 6. As part of the SCIF_EXIT message processing on the host, host sends a - * SCIF_REMOVE_NODE to itself corresponding to the card being removed. This - * starts a similar SCIF peer device teardown sequence on the host - * corresponding to the card being shut down. - * - * Card reset - * ---------- - * The case of interest here is when the card has not been previously shut down - * since most of the steps below are skipped in that case: - - * 1. cosm_stop(..) invokes hw_ops->stop(..) method of the base PCIe driver - * which unregisters the SCIF HW device resulting in scif_remove(..) being - * called on the host. - * 2. scif_remove(..) calls scif_disconnect_node(..) which results in a - * SCIF_EXIT message being sent to the card. - * 3. The card executes scif_stop() as part of SCIF_EXIT message - * processing. This results in the COSM endpoint on the card being closed and - * the SCIF host peer device on the card getting unregistered similar to - * steps 3, 4 and 5 for the card shutdown case above. scif_poll(..) on the - * host returns EPOLLHUP as a result. - * 4. On the host, card peer device unregister and SCIF HW remove(..) also - * subsequently complete. - * - * Card crash - * ---------- - * If a reset is issued after the card has crashed, there is no SCIF_DISCNT - * message from the card which would result in scif_poll(..) returning - * EPOLLHUP. In this case when the host SCIF driver sends a SCIF_REMOVE_NODE - * message to itself resulting in the card SCIF peer device being unregistered, - * this results in a scif_peer_release_dev -> scif_cleanup_scifdev-> - * scif_invalidate_ep call sequence which sets the endpoint state to - * DISCONNECTED and results in scif_poll(..) returning EPOLLHUP. - */ - -#define COSM_SCIF_BACKLOG 16 -#define COSM_HEARTBEAT_CHECK_DELTA_SEC 10 -#define COSM_HEARTBEAT_TIMEOUT_SEC \ - (COSM_HEARTBEAT_SEND_SEC + COSM_HEARTBEAT_CHECK_DELTA_SEC) -#define COSM_HEARTBEAT_TIMEOUT_MSEC (COSM_HEARTBEAT_TIMEOUT_SEC * MSEC_PER_SEC) - -static struct task_struct *server_thread; -static scif_epd_t listen_epd; - -/* Publish MIC card's shutdown status to user space MIC daemon */ -static void cosm_update_mic_status(struct cosm_device *cdev) -{ - if (cdev->shutdown_status_int != MIC_NOP) { - cosm_set_shutdown_status(cdev, cdev->shutdown_status_int); - cdev->shutdown_status_int = MIC_NOP; - } -} - -/* Store MIC card's shutdown status internally when it is received */ -static void cosm_shutdown_status_int(struct cosm_device *cdev, - enum mic_status shutdown_status) -{ - switch (shutdown_status) { - case MIC_HALTED: - case MIC_POWER_OFF: - case MIC_RESTART: - case MIC_CRASHED: - break; - default: - dev_err(&cdev->dev, "%s %d Unexpected shutdown_status %d\n", - __func__, __LINE__, shutdown_status); - return; - }; - cdev->shutdown_status_int = shutdown_status; - cdev->heartbeat_watchdog_enable = false; - - if (cdev->state != MIC_SHUTTING_DOWN) - cosm_set_state(cdev, MIC_SHUTTING_DOWN); -} - -/* Non-blocking recv. Read and process all available messages */ -static void cosm_scif_recv(struct cosm_device *cdev) -{ - struct cosm_msg msg; - int rc; - - while (1) { - rc = scif_recv(cdev->epd, &msg, sizeof(msg), 0); - if (!rc) { - break; - } else if (rc < 0) { - dev_dbg(&cdev->dev, "%s: %d rc %d\n", - __func__, __LINE__, rc); - break; - } - dev_dbg(&cdev->dev, "%s: %d rc %d id 0x%llx\n", - __func__, __LINE__, rc, msg.id); - - switch (msg.id) { - case COSM_MSG_SHUTDOWN_STATUS: - cosm_shutdown_status_int(cdev, msg.shutdown_status); - break; - case COSM_MSG_HEARTBEAT: - /* Nothing to do, heartbeat only unblocks scif_poll */ - break; - default: - dev_err(&cdev->dev, "%s: %d unknown msg.id %lld\n", - __func__, __LINE__, msg.id); - break; - } - } -} - -/* Publish crashed status for this MIC card */ -static void cosm_set_crashed(struct cosm_device *cdev) -{ - dev_err(&cdev->dev, "node alive timeout\n"); - cosm_shutdown_status_int(cdev, MIC_CRASHED); - cosm_update_mic_status(cdev); -} - -/* Send host time to the MIC card to sync system time between host and MIC */ -static void cosm_send_time(struct cosm_device *cdev) -{ - struct cosm_msg msg = { .id = COSM_MSG_SYNC_TIME }; - struct timespec64 ts; - int rc; - - ktime_get_real_ts64(&ts); - msg.timespec.tv_sec = ts.tv_sec; - msg.timespec.tv_nsec = ts.tv_nsec; - - rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); - if (rc < 0) - dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n", - __func__, __LINE__, rc); -} - -/* - * Close this cosm_device's endpoint after its peer endpoint on the card has - * been closed. In all cases except MIC card crash EPOLLHUP on the host is - * triggered by the client's endpoint being closed. - */ -static void cosm_scif_close(struct cosm_device *cdev) -{ - /* - * Because SHUTDOWN_STATUS message is sent by the MIC cards in the - * reboot notifier when shutdown is still not complete, we notify mpssd - * to reset the card when SCIF endpoint is closed. - */ - cosm_update_mic_status(cdev); - scif_close(cdev->epd); - cdev->epd = NULL; - dev_dbg(&cdev->dev, "%s %d\n", __func__, __LINE__); -} - -/* - * Set card state to ONLINE when a new SCIF connection from a MIC card is - * received. Normally the state is BOOTING when the connection comes in, but can - * be ONLINE if cosm_client driver on the card was unloaded and then reloaded. - */ -static int cosm_set_online(struct cosm_device *cdev) -{ - int rc = 0; - - if (MIC_BOOTING == cdev->state || MIC_ONLINE == cdev->state) { - cdev->heartbeat_watchdog_enable = cdev->sysfs_heartbeat_enable; - cdev->epd = cdev->newepd; - if (cdev->state == MIC_BOOTING) - cosm_set_state(cdev, MIC_ONLINE); - cosm_send_time(cdev); - dev_dbg(&cdev->dev, "%s %d\n", __func__, __LINE__); - } else { - dev_warn(&cdev->dev, "%s %d not going online in state: %s\n", - __func__, __LINE__, cosm_state_string[cdev->state]); - rc = -EINVAL; - } - /* Drop reference acquired by bus_find_device in the server thread */ - put_device(&cdev->dev); - return rc; -} - -/* - * Work function for handling work for a SCIF connection from a particular MIC - * card. It first sets the card state to ONLINE and then calls scif_poll to - * block on activity such as incoming messages on the SCIF endpoint. When the - * endpoint is closed, the work function exits, completing its life cycle, from - * MIC card boot to card shutdown/reset/crash. - */ -void cosm_scif_work(struct work_struct *work) -{ - struct cosm_device *cdev = container_of(work, struct cosm_device, - scif_work); - struct scif_pollepd pollepd; - int rc; - - mutex_lock(&cdev->cosm_mutex); - if (cosm_set_online(cdev)) - goto exit; - - while (1) { - pollepd.epd = cdev->epd; - pollepd.events = EPOLLIN; - - /* Drop the mutex before blocking in scif_poll(..) */ - mutex_unlock(&cdev->cosm_mutex); - /* poll(..) with timeout on our endpoint */ - rc = scif_poll(&pollepd, 1, COSM_HEARTBEAT_TIMEOUT_MSEC); - mutex_lock(&cdev->cosm_mutex); - if (rc < 0) { - dev_err(&cdev->dev, "%s %d scif_poll rc %d\n", - __func__, __LINE__, rc); - continue; - } - - /* There is a message from the card */ - if (pollepd.revents & EPOLLIN) - cosm_scif_recv(cdev); - - /* The peer endpoint is closed or this endpoint disconnected */ - if (pollepd.revents & EPOLLHUP) { - cosm_scif_close(cdev); - break; - } - - /* Did we timeout from poll? */ - if (!rc && cdev->heartbeat_watchdog_enable) - cosm_set_crashed(cdev); - } -exit: - dev_dbg(&cdev->dev, "%s %d exiting\n", __func__, __LINE__); - mutex_unlock(&cdev->cosm_mutex); -} - -/* - * COSM SCIF server thread function. Accepts incoming SCIF connections from MIC - * cards, finds the correct cosm_device to associate that connection with and - * schedules individual work items for each MIC card. - */ -static int cosm_scif_server(void *unused) -{ - struct cosm_device *cdev; - scif_epd_t newepd; - struct scif_port_id port_id; - int rc; - - allow_signal(SIGKILL); - - while (!kthread_should_stop()) { - rc = scif_accept(listen_epd, &port_id, &newepd, - SCIF_ACCEPT_SYNC); - if (rc < 0) { - if (-ERESTARTSYS != rc) - pr_err("%s %d rc %d\n", __func__, __LINE__, rc); - continue; - } - - /* - * Associate the incoming connection with a particular - * cosm_device, COSM device ID == SCIF node ID - 1 - */ - cdev = cosm_find_cdev_by_id(port_id.node - 1); - if (!cdev) - continue; - cdev->newepd = newepd; - schedule_work(&cdev->scif_work); - } - - pr_debug("%s %d Server thread stopped\n", __func__, __LINE__); - return 0; -} - -static int cosm_scif_listen(void) -{ - int rc; - - listen_epd = scif_open(); - if (!listen_epd) { - pr_err("%s %d scif_open failed\n", __func__, __LINE__); - return -ENOMEM; - } - - rc = scif_bind(listen_epd, SCIF_COSM_LISTEN_PORT); - if (rc < 0) { - pr_err("%s %d scif_bind failed rc %d\n", - __func__, __LINE__, rc); - goto err; - } - - rc = scif_listen(listen_epd, COSM_SCIF_BACKLOG); - if (rc < 0) { - pr_err("%s %d scif_listen rc %d\n", __func__, __LINE__, rc); - goto err; - } - pr_debug("%s %d listen_epd set up\n", __func__, __LINE__); - return 0; -err: - scif_close(listen_epd); - listen_epd = NULL; - return rc; -} - -static void cosm_scif_listen_exit(void) -{ - pr_debug("%s %d closing listen_epd\n", __func__, __LINE__); - if (listen_epd) { - scif_close(listen_epd); - listen_epd = NULL; - } -} - -/* - * Create a listening SCIF endpoint and a server kthread which accepts incoming - * SCIF connections from MIC cards - */ -int cosm_scif_init(void) -{ - int rc = cosm_scif_listen(); - - if (rc) { - pr_err("%s %d cosm_scif_listen rc %d\n", - __func__, __LINE__, rc); - goto err; - } - - server_thread = kthread_run(cosm_scif_server, NULL, "cosm_server"); - if (IS_ERR(server_thread)) { - rc = PTR_ERR(server_thread); - pr_err("%s %d kthread_run rc %d\n", __func__, __LINE__, rc); - goto listen_exit; - } - return 0; -listen_exit: - cosm_scif_listen_exit(); -err: - return rc; -} - -/* Stop the running server thread and close the listening SCIF endpoint */ -void cosm_scif_exit(void) -{ - int rc; - - if (!IS_ERR_OR_NULL(server_thread)) { - rc = send_sig(SIGKILL, server_thread, 0); - if (rc) { - pr_err("%s %d send_sig rc %d\n", - __func__, __LINE__, rc); - return; - } - kthread_stop(server_thread); - } - - cosm_scif_listen_exit(); -} diff --git a/drivers/misc/mic/cosm/cosm_sysfs.c b/drivers/misc/mic/cosm/cosm_sysfs.c deleted file mode 100644 index e6dac967c1af..000000000000 --- a/drivers/misc/mic/cosm/cosm_sysfs.c +++ /dev/null @@ -1,449 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC Coprocessor State Management (COSM) Driver - */ -#include -#include "cosm_main.h" - -/* - * A state-to-string lookup table, for exposing a human readable state - * via sysfs. Always keep in sync with enum cosm_states - */ -const char * const cosm_state_string[] = { - [MIC_READY] = "ready", - [MIC_BOOTING] = "booting", - [MIC_ONLINE] = "online", - [MIC_SHUTTING_DOWN] = "shutting_down", - [MIC_RESETTING] = "resetting", - [MIC_RESET_FAILED] = "reset_failed", -}; - -/* - * A shutdown-status-to-string lookup table, for exposing a human - * readable state via sysfs. Always keep in sync with enum cosm_shutdown_status - */ -const char * const cosm_shutdown_status_string[] = { - [MIC_NOP] = "nop", - [MIC_CRASHED] = "crashed", - [MIC_HALTED] = "halted", - [MIC_POWER_OFF] = "poweroff", - [MIC_RESTART] = "restart", -}; - -void cosm_set_shutdown_status(struct cosm_device *cdev, u8 shutdown_status) -{ - dev_dbg(&cdev->dev, "Shutdown Status %s -> %s\n", - cosm_shutdown_status_string[cdev->shutdown_status], - cosm_shutdown_status_string[shutdown_status]); - cdev->shutdown_status = shutdown_status; -} - -void cosm_set_state(struct cosm_device *cdev, u8 state) -{ - dev_dbg(&cdev->dev, "State %s -> %s\n", - cosm_state_string[cdev->state], - cosm_state_string[state]); - cdev->state = state; - sysfs_notify_dirent(cdev->state_sysfs); -} - -static ssize_t -family_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return cdev->hw_ops->family(cdev, buf); -} -static DEVICE_ATTR_RO(family); - -static ssize_t -stepping_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return cdev->hw_ops->stepping(cdev, buf); -} -static DEVICE_ATTR_RO(stepping); - -static ssize_t -state_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev || cdev->state >= MIC_LAST) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%s\n", - cosm_state_string[cdev->state]); -} - -static ssize_t -state_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - int rc; - - if (!cdev) - return -EINVAL; - - if (sysfs_streq(buf, "boot")) { - rc = cosm_start(cdev); - goto done; - } - if (sysfs_streq(buf, "reset")) { - rc = cosm_reset(cdev); - goto done; - } - - if (sysfs_streq(buf, "shutdown")) { - rc = cosm_shutdown(cdev); - goto done; - } - rc = -EINVAL; -done: - if (rc) - count = rc; - return count; -} -static DEVICE_ATTR_RW(state); - -static ssize_t shutdown_status_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev || cdev->shutdown_status >= MIC_STATUS_LAST) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%s\n", - cosm_shutdown_status_string[cdev->shutdown_status]); -} -static DEVICE_ATTR_RO(shutdown_status); - -static ssize_t -heartbeat_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%d\n", cdev->sysfs_heartbeat_enable); -} - -static ssize_t -heartbeat_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - int enable; - int ret; - - if (!cdev) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - ret = kstrtoint(buf, 10, &enable); - if (ret) - goto unlock; - - cdev->sysfs_heartbeat_enable = enable; - /* if state is not online, cdev->heartbeat_watchdog_enable is 0 */ - if (cdev->state == MIC_ONLINE) - cdev->heartbeat_watchdog_enable = enable; - ret = count; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return ret; -} -static DEVICE_ATTR_RW(heartbeat_enable); - -static ssize_t -cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - char *cmdline; - - if (!cdev) - return -EINVAL; - - cmdline = cdev->cmdline; - - if (cmdline) - return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline); - return 0; -} - -static ssize_t -cmdline_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - kfree(cdev->cmdline); - - cdev->cmdline = kmalloc(count + 1, GFP_KERNEL); - if (!cdev->cmdline) { - count = -ENOMEM; - goto unlock; - } - - strncpy(cdev->cmdline, buf, count); - - if (cdev->cmdline[count - 1] == '\n') - cdev->cmdline[count - 1] = '\0'; - else - cdev->cmdline[count] = '\0'; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return count; -} -static DEVICE_ATTR_RW(cmdline); - -static ssize_t -firmware_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - char *firmware; - - if (!cdev) - return -EINVAL; - - firmware = cdev->firmware; - - if (firmware) - return scnprintf(buf, PAGE_SIZE, "%s\n", firmware); - return 0; -} - -static ssize_t -firmware_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - kfree(cdev->firmware); - - cdev->firmware = kmalloc(count + 1, GFP_KERNEL); - if (!cdev->firmware) { - count = -ENOMEM; - goto unlock; - } - strncpy(cdev->firmware, buf, count); - - if (cdev->firmware[count - 1] == '\n') - cdev->firmware[count - 1] = '\0'; - else - cdev->firmware[count] = '\0'; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return count; -} -static DEVICE_ATTR_RW(firmware); - -static ssize_t -ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - char *ramdisk; - - if (!cdev) - return -EINVAL; - - ramdisk = cdev->ramdisk; - - if (ramdisk) - return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk); - return 0; -} - -static ssize_t -ramdisk_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - kfree(cdev->ramdisk); - - cdev->ramdisk = kmalloc(count + 1, GFP_KERNEL); - if (!cdev->ramdisk) { - count = -ENOMEM; - goto unlock; - } - - strncpy(cdev->ramdisk, buf, count); - - if (cdev->ramdisk[count - 1] == '\n') - cdev->ramdisk[count - 1] = '\0'; - else - cdev->ramdisk[count] = '\0'; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return count; -} -static DEVICE_ATTR_RW(ramdisk); - -static ssize_t -bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - char *bootmode; - - if (!cdev) - return -EINVAL; - - bootmode = cdev->bootmode; - - if (bootmode) - return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode); - return 0; -} - -static ssize_t -bootmode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "flash")) - return -EINVAL; - - mutex_lock(&cdev->cosm_mutex); - kfree(cdev->bootmode); - - cdev->bootmode = kmalloc(count + 1, GFP_KERNEL); - if (!cdev->bootmode) { - count = -ENOMEM; - goto unlock; - } - - strncpy(cdev->bootmode, buf, count); - - if (cdev->bootmode[count - 1] == '\n') - cdev->bootmode[count - 1] = '\0'; - else - cdev->bootmode[count] = '\0'; -unlock: - mutex_unlock(&cdev->cosm_mutex); - return count; -} -static DEVICE_ATTR_RW(bootmode); - -static ssize_t -log_buf_addr_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_addr); -} - -static ssize_t -log_buf_addr_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - int ret; - unsigned long addr; - - if (!cdev) - return -EINVAL; - - ret = kstrtoul(buf, 16, &addr); - if (ret) - goto exit; - - cdev->log_buf_addr = (void *)addr; - ret = count; -exit: - return ret; -} -static DEVICE_ATTR_RW(log_buf_addr); - -static ssize_t -log_buf_len_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - - if (!cdev) - return -EINVAL; - - return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_len); -} - -static ssize_t -log_buf_len_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cosm_device *cdev = dev_get_drvdata(dev); - int ret; - unsigned long addr; - - if (!cdev) - return -EINVAL; - - ret = kstrtoul(buf, 16, &addr); - if (ret) - goto exit; - - cdev->log_buf_len = (int *)addr; - ret = count; -exit: - return ret; -} -static DEVICE_ATTR_RW(log_buf_len); - -static struct attribute *cosm_default_attrs[] = { - &dev_attr_family.attr, - &dev_attr_stepping.attr, - &dev_attr_state.attr, - &dev_attr_shutdown_status.attr, - &dev_attr_heartbeat_enable.attr, - &dev_attr_cmdline.attr, - &dev_attr_firmware.attr, - &dev_attr_ramdisk.attr, - &dev_attr_bootmode.attr, - &dev_attr_log_buf_addr.attr, - &dev_attr_log_buf_len.attr, - - NULL -}; - -ATTRIBUTE_GROUPS(cosm_default); - -void cosm_sysfs_init(struct cosm_device *cdev) -{ - cdev->attr_group = cosm_default_groups; -} diff --git a/drivers/misc/mic/cosm_client/Makefile b/drivers/misc/mic/cosm_client/Makefile deleted file mode 100644 index 5b62270bc2ab..000000000000 --- a/drivers/misc/mic/cosm_client/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile - Intel MIC COSM Client Driver -# Copyright(c) 2015, Intel Corporation. -# -obj-$(CONFIG_MIC_COSM) += cosm_client.o - -cosm_client-objs += cosm_scif_client.o diff --git a/drivers/misc/mic/cosm_client/cosm_scif_client.c b/drivers/misc/mic/cosm_client/cosm_scif_client.c deleted file mode 100644 index a03213dd9319..000000000000 --- a/drivers/misc/mic/cosm_client/cosm_scif_client.c +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel MIC COSM Client Driver - */ -#include -#include -#include -#include -#include - -#include "../cosm/cosm_main.h" - -#define COSM_SCIF_MAX_RETRIES 10 -#define COSM_HEARTBEAT_SEND_MSEC (COSM_HEARTBEAT_SEND_SEC * MSEC_PER_SEC) - -static struct task_struct *client_thread; -static scif_epd_t client_epd; -static struct scif_peer_dev *client_spdev; - -/* - * Reboot notifier: receives shutdown status from the OS and communicates it - * back to the COSM process on the host - */ -static int cosm_reboot_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN_STATUS }; - int rc; - - event = (event == SYS_RESTART) ? SYSTEM_RESTART : event; - dev_info(&client_spdev->dev, "%s %d received event %ld\n", - __func__, __LINE__, event); - - msg.shutdown_status = event; - rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); - if (rc < 0) - dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n", - __func__, __LINE__, rc); - - return NOTIFY_DONE; -} - -static struct notifier_block cosm_reboot = { - .notifier_call = cosm_reboot_event, -}; - -/* Set system time from timespec value received from the host */ -static void cosm_set_time(struct cosm_msg *msg) -{ - struct timespec64 ts = { - .tv_sec = msg->timespec.tv_sec, - .tv_nsec = msg->timespec.tv_nsec, - }; - int rc = do_settimeofday64(&ts); - - if (rc) - dev_err(&client_spdev->dev, "%s: %d settimeofday rc %d\n", - __func__, __LINE__, rc); -} - -/* COSM client receive message processing */ -static void cosm_client_recv(void) -{ - struct cosm_msg msg; - int rc; - - while (1) { - rc = scif_recv(client_epd, &msg, sizeof(msg), 0); - if (!rc) { - return; - } else if (rc < 0) { - dev_err(&client_spdev->dev, "%s: %d rc %d\n", - __func__, __LINE__, rc); - return; - } - - dev_dbg(&client_spdev->dev, "%s: %d rc %d id 0x%llx\n", - __func__, __LINE__, rc, msg.id); - - switch (msg.id) { - case COSM_MSG_SYNC_TIME: - cosm_set_time(&msg); - break; - case COSM_MSG_SHUTDOWN: - orderly_poweroff(true); - break; - default: - dev_err(&client_spdev->dev, "%s: %d unknown id %lld\n", - __func__, __LINE__, msg.id); - break; - } - } -} - -/* Initiate connection to the COSM server on the host */ -static int cosm_scif_connect(void) -{ - struct scif_port_id port_id; - int i, rc; - - client_epd = scif_open(); - if (!client_epd) { - dev_err(&client_spdev->dev, "%s %d scif_open failed\n", - __func__, __LINE__); - return -ENOMEM; - } - - port_id.node = 0; - port_id.port = SCIF_COSM_LISTEN_PORT; - - for (i = 0; i < COSM_SCIF_MAX_RETRIES; i++) { - rc = scif_connect(client_epd, &port_id); - if (rc < 0) - msleep(1000); - else - break; - } - - if (rc < 0) { - dev_err(&client_spdev->dev, "%s %d scif_connect rc %d\n", - __func__, __LINE__, rc); - scif_close(client_epd); - client_epd = NULL; - } - return rc < 0 ? rc : 0; -} - -/* Close host SCIF connection */ -static void cosm_scif_connect_exit(void) -{ - if (client_epd) { - scif_close(client_epd); - client_epd = NULL; - } -} - -/* - * COSM SCIF client thread function: waits for messages from the host and sends - * a heartbeat to the host - */ -static int cosm_scif_client(void *unused) -{ - struct cosm_msg msg = { .id = COSM_MSG_HEARTBEAT }; - struct scif_pollepd pollepd; - int rc; - - allow_signal(SIGKILL); - - while (!kthread_should_stop()) { - pollepd.epd = client_epd; - pollepd.events = EPOLLIN; - - rc = scif_poll(&pollepd, 1, COSM_HEARTBEAT_SEND_MSEC); - if (rc < 0) { - if (-EINTR != rc) - dev_err(&client_spdev->dev, - "%s %d scif_poll rc %d\n", - __func__, __LINE__, rc); - continue; - } - - if (pollepd.revents & EPOLLIN) - cosm_client_recv(); - - msg.id = COSM_MSG_HEARTBEAT; - rc = scif_send(client_epd, &msg, sizeof(msg), SCIF_SEND_BLOCK); - if (rc < 0) - dev_err(&client_spdev->dev, "%s %d scif_send rc %d\n", - __func__, __LINE__, rc); - } - - dev_dbg(&client_spdev->dev, "%s %d Client thread stopped\n", - __func__, __LINE__); - return 0; -} - -static void cosm_scif_probe(struct scif_peer_dev *spdev) -{ - int rc; - - dev_dbg(&spdev->dev, "%s %d: dnode %d\n", - __func__, __LINE__, spdev->dnode); - - /* We are only interested in the host with spdev->dnode == 0 */ - if (spdev->dnode) - return; - - client_spdev = spdev; - rc = cosm_scif_connect(); - if (rc) - goto exit; - - rc = register_reboot_notifier(&cosm_reboot); - if (rc) { - dev_err(&spdev->dev, - "reboot notifier registration failed rc %d\n", rc); - goto connect_exit; - } - - client_thread = kthread_run(cosm_scif_client, NULL, "cosm_client"); - if (IS_ERR(client_thread)) { - rc = PTR_ERR(client_thread); - dev_err(&spdev->dev, "%s %d kthread_run rc %d\n", - __func__, __LINE__, rc); - goto unreg_reboot; - } - return; -unreg_reboot: - unregister_reboot_notifier(&cosm_reboot); -connect_exit: - cosm_scif_connect_exit(); -exit: - client_spdev = NULL; -} - -static void cosm_scif_remove(struct scif_peer_dev *spdev) -{ - int rc; - - dev_dbg(&spdev->dev, "%s %d: dnode %d\n", - __func__, __LINE__, spdev->dnode); - - if (spdev->dnode) - return; - - if (!IS_ERR_OR_NULL(client_thread)) { - rc = send_sig(SIGKILL, client_thread, 0); - if (rc) { - pr_err("%s %d send_sig rc %d\n", - __func__, __LINE__, rc); - return; - } - kthread_stop(client_thread); - } - unregister_reboot_notifier(&cosm_reboot); - cosm_scif_connect_exit(); - client_spdev = NULL; -} - -static struct scif_client scif_client_cosm = { - .name = KBUILD_MODNAME, - .probe = cosm_scif_probe, - .remove = cosm_scif_remove, -}; - -static int __init cosm_client_init(void) -{ - int rc = scif_client_register(&scif_client_cosm); - - if (rc) - pr_err("scif_client_register failed rc %d\n", rc); - return rc; -} - -static void __exit cosm_client_exit(void) -{ - scif_client_unregister(&scif_client_cosm); -} - -module_init(cosm_client_init); -module_exit(cosm_client_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC card OS state management client driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile deleted file mode 100644 index 25f153367980..000000000000 --- a/drivers/misc/mic/host/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2013, Intel Corporation. -# -obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o -mic_host-objs := mic_main.o -mic_host-objs += mic_x100.o -mic_host-objs += mic_smpt.o -mic_host-objs += mic_intr.o -mic_host-objs += mic_boot.o -mic_host-objs += mic_debugfs.o diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c deleted file mode 100644 index 8cb85b8b3e19..000000000000 --- a/drivers/misc/mic/host/mic_boot.c +++ /dev/null @@ -1,588 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include -#include -#include -#include -#include -#include -#include -#include "../bus/scif_bus.h" -#include "../bus/vop_bus.h" -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_smpt.h" - -static inline struct mic_device *vpdev_to_mdev(struct device *dev) -{ - return dev_get_drvdata(dev->parent); -} - -static dma_addr_t -_mic_dma_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, - enum dma_data_direction dir, unsigned long attrs) -{ - void *va = phys_to_virt(page_to_phys(page)) + offset; - struct mic_device *mdev = vpdev_to_mdev(dev); - - return mic_map_single(mdev, va, size); -} - -static void _mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - struct mic_device *mdev = vpdev_to_mdev(dev); - - mic_unmap_single(mdev, dma_addr, size); -} - -static const struct dma_map_ops _mic_dma_ops = { - .map_page = _mic_dma_map_page, - .unmap_page = _mic_dma_unmap_page, -}; - -static struct mic_irq * -__mic_request_irq(struct vop_device *vpdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, int intr_src) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - return mic_request_threaded_irq(mdev, func, NULL, name, data, - intr_src, MIC_INTR_DB); -} - -static void __mic_free_irq(struct vop_device *vpdev, - struct mic_irq *cookie, void *data) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - mic_free_irq(mdev, cookie, data); -} - -static void __mic_ack_interrupt(struct vop_device *vpdev, int num) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - mdev->ops->intr_workarounds(mdev); -} - -static int __mic_next_db(struct vop_device *vpdev) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - return mic_next_db(mdev); -} - -static void *__mic_get_dp(struct vop_device *vpdev) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - return mdev->dp; -} - -static void __iomem *__mic_get_remote_dp(struct vop_device *vpdev) -{ - return NULL; -} - -static void __mic_send_intr(struct vop_device *vpdev, int db) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - mdev->ops->send_intr(mdev, db); -} - -static void __iomem *__mic_ioremap(struct vop_device *vpdev, - dma_addr_t pa, size_t len) -{ - struct mic_device *mdev = vpdev_to_mdev(&vpdev->dev); - - return mdev->aper.va + pa; -} - -static void __mic_iounmap(struct vop_device *vpdev, void __iomem *va) -{ - /* nothing to do */ -} - -static struct vop_hw_ops vop_hw_ops = { - .request_irq = __mic_request_irq, - .free_irq = __mic_free_irq, - .ack_interrupt = __mic_ack_interrupt, - .next_db = __mic_next_db, - .get_dp = __mic_get_dp, - .get_remote_dp = __mic_get_remote_dp, - .send_intr = __mic_send_intr, - .remap = __mic_ioremap, - .unmap = __mic_iounmap, -}; - -static inline struct mic_device *scdev_to_mdev(struct scif_hw_dev *scdev) -{ - return dev_get_drvdata(scdev->dev.parent); -} - -static void *__mic_dma_alloc(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp, - unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - dma_addr_t tmp; - void *va = kzalloc(size, gfp); - - if (va) { - tmp = mic_map_single(mdev, va, size); - if (dma_mapping_error(dev, tmp)) { - kfree(va); - va = NULL; - } else { - *dma_handle = tmp; - } - } - return va; -} - -static void __mic_dma_free(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle, unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - - mic_unmap_single(mdev, dma_handle, size); - kfree(vaddr); -} - -static dma_addr_t -__mic_dma_map_page(struct device *dev, struct page *page, unsigned long offset, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - void *va = phys_to_virt(page_to_phys(page)) + offset; - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - - return mic_map_single(mdev, va, size); -} - -static void -__mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - - mic_unmap_single(mdev, dma_addr, size); -} - -static int __mic_dma_map_sg(struct device *dev, struct scatterlist *sg, - int nents, enum dma_data_direction dir, - unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - struct scatterlist *s; - int i, j, ret; - dma_addr_t da; - - ret = dma_map_sg(&mdev->pdev->dev, sg, nents, dir); - if (ret <= 0) - return 0; - - for_each_sg(sg, s, nents, i) { - da = mic_map(mdev, sg_dma_address(s) + s->offset, s->length); - if (!da) - goto err; - sg_dma_address(s) = da; - } - return nents; -err: - for_each_sg(sg, s, i, j) { - mic_unmap(mdev, sg_dma_address(s), s->length); - sg_dma_address(s) = mic_to_dma_addr(mdev, sg_dma_address(s)); - } - dma_unmap_sg(&mdev->pdev->dev, sg, nents, dir); - return 0; -} - -static void __mic_dma_unmap_sg(struct device *dev, - struct scatterlist *sg, int nents, - enum dma_data_direction dir, - unsigned long attrs) -{ - struct scif_hw_dev *scdev = dev_get_drvdata(dev); - struct mic_device *mdev = scdev_to_mdev(scdev); - struct scatterlist *s; - dma_addr_t da; - int i; - - for_each_sg(sg, s, nents, i) { - da = mic_to_dma_addr(mdev, sg_dma_address(s)); - mic_unmap(mdev, sg_dma_address(s), s->length); - sg_dma_address(s) = da; - } - dma_unmap_sg(&mdev->pdev->dev, sg, nents, dir); -} - -static const struct dma_map_ops __mic_dma_ops = { - .alloc = __mic_dma_alloc, - .free = __mic_dma_free, - .map_page = __mic_dma_map_page, - .unmap_page = __mic_dma_unmap_page, - .map_sg = __mic_dma_map_sg, - .unmap_sg = __mic_dma_unmap_sg, -}; - -static struct mic_irq * -___mic_request_irq(struct scif_hw_dev *scdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, - void *data, int db) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - return mic_request_threaded_irq(mdev, func, NULL, name, data, - db, MIC_INTR_DB); -} - -static void -___mic_free_irq(struct scif_hw_dev *scdev, - struct mic_irq *cookie, void *data) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - mic_free_irq(mdev, cookie, data); -} - -static void ___mic_ack_interrupt(struct scif_hw_dev *scdev, int num) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - mdev->ops->intr_workarounds(mdev); -} - -static int ___mic_next_db(struct scif_hw_dev *scdev) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - return mic_next_db(mdev); -} - -static void ___mic_send_intr(struct scif_hw_dev *scdev, int db) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - mdev->ops->send_intr(mdev, db); -} - -static void __iomem *___mic_ioremap(struct scif_hw_dev *scdev, - phys_addr_t pa, size_t len) -{ - struct mic_device *mdev = scdev_to_mdev(scdev); - - return mdev->aper.va + pa; -} - -static void ___mic_iounmap(struct scif_hw_dev *scdev, void __iomem *va) -{ - /* nothing to do */ -} - -static struct scif_hw_ops scif_hw_ops = { - .request_irq = ___mic_request_irq, - .free_irq = ___mic_free_irq, - .ack_interrupt = ___mic_ack_interrupt, - .next_db = ___mic_next_db, - .send_intr = ___mic_send_intr, - .remap = ___mic_ioremap, - .unmap = ___mic_iounmap, -}; - -static inline struct mic_device *mbdev_to_mdev(struct mbus_device *mbdev) -{ - return dev_get_drvdata(mbdev->dev.parent); -} - -static dma_addr_t -mic_dma_map_page(struct device *dev, struct page *page, - unsigned long offset, size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - void *va = phys_to_virt(page_to_phys(page)) + offset; - struct mic_device *mdev = dev_get_drvdata(dev->parent); - - return mic_map_single(mdev, va, size); -} - -static void -mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, - size_t size, enum dma_data_direction dir, - unsigned long attrs) -{ - struct mic_device *mdev = dev_get_drvdata(dev->parent); - mic_unmap_single(mdev, dma_addr, size); -} - -static const struct dma_map_ops mic_dma_ops = { - .map_page = mic_dma_map_page, - .unmap_page = mic_dma_unmap_page, -}; - -static struct mic_irq * -_mic_request_threaded_irq(struct mbus_device *mbdev, - irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int intr_src) -{ - return mic_request_threaded_irq(mbdev_to_mdev(mbdev), handler, - thread_fn, name, data, - intr_src, MIC_INTR_DMA); -} - -static void _mic_free_irq(struct mbus_device *mbdev, - struct mic_irq *cookie, void *data) -{ - mic_free_irq(mbdev_to_mdev(mbdev), cookie, data); -} - -static void _mic_ack_interrupt(struct mbus_device *mbdev, int num) -{ - struct mic_device *mdev = mbdev_to_mdev(mbdev); - mdev->ops->intr_workarounds(mdev); -} - -static struct mbus_hw_ops mbus_hw_ops = { - .request_threaded_irq = _mic_request_threaded_irq, - .free_irq = _mic_free_irq, - .ack_interrupt = _mic_ack_interrupt, -}; - -/* Initialize the MIC bootparams */ -void mic_bootparam_init(struct mic_device *mdev) -{ - struct mic_bootparam *bootparam = mdev->dp; - - bootparam->magic = cpu_to_le32(MIC_MAGIC); - bootparam->h2c_config_db = -1; - bootparam->node_id = mdev->id + 1; - bootparam->scif_host_dma_addr = 0x0; - bootparam->scif_card_dma_addr = 0x0; - bootparam->c2h_scif_db = -1; - bootparam->h2c_scif_db = -1; -} - -static inline struct mic_device *cosmdev_to_mdev(struct cosm_device *cdev) -{ - return dev_get_drvdata(cdev->dev.parent); -} - -static void _mic_reset(struct cosm_device *cdev) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - - mdev->ops->reset_fw_ready(mdev); - mdev->ops->reset(mdev); -} - -static bool _mic_ready(struct cosm_device *cdev) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - - return mdev->ops->is_fw_ready(mdev); -} - -/** - * mic_request_dma_chans - Request DMA channels - * @mdev: pointer to mic_device instance - * - * returns number of DMA channels acquired - */ -static int mic_request_dma_chans(struct mic_device *mdev) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - - dma_cap_zero(mask); - dma_cap_set(DMA_MEMCPY, mask); - - do { - chan = dma_request_channel(mask, mdev->ops->dma_filter, - &mdev->pdev->dev); - if (chan) { - mdev->dma_ch[mdev->num_dma_ch++] = chan; - if (mdev->num_dma_ch >= MIC_MAX_DMA_CHAN) - break; - } - } while (chan); - dev_info(&mdev->pdev->dev, "DMA channels # %d\n", mdev->num_dma_ch); - return mdev->num_dma_ch; -} - -/** - * mic_free_dma_chans - release DMA channels - * @mdev: pointer to mic_device instance - * - * returns none - */ -static void mic_free_dma_chans(struct mic_device *mdev) -{ - int i = 0; - - for (i = 0; i < mdev->num_dma_ch; i++) { - dma_release_channel(mdev->dma_ch[i]); - mdev->dma_ch[i] = NULL; - } - mdev->num_dma_ch = 0; -} - -/** - * _mic_start - Start the MIC. - * @cdev: pointer to cosm_device instance - * @id: MIC device id/index provided by COSM used in other drivers like SCIF - * - * This function prepares an MIC for boot and initiates boot. - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - * - * For all cosm_hw_ops the caller holds a mutex to ensure serialization. - */ -static int _mic_start(struct cosm_device *cdev, int id) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - int rc; - - mic_bootparam_init(mdev); - mdev->dma_mbdev = mbus_register_device(&mdev->pdev->dev, - MBUS_DEV_DMA_HOST, &mic_dma_ops, - &mbus_hw_ops, id, mdev->mmio.va); - if (IS_ERR(mdev->dma_mbdev)) { - rc = PTR_ERR(mdev->dma_mbdev); - goto unlock_ret; - } - if (!mic_request_dma_chans(mdev)) { - rc = -ENODEV; - goto dma_remove; - } - mdev->scdev = scif_register_device(&mdev->pdev->dev, MIC_SCIF_DEV, - &__mic_dma_ops, &scif_hw_ops, - id + 1, 0, &mdev->mmio, - &mdev->aper, mdev->dp, NULL, - mdev->dma_ch, mdev->num_dma_ch, - true); - if (IS_ERR(mdev->scdev)) { - rc = PTR_ERR(mdev->scdev); - goto dma_free; - } - - mdev->vpdev = vop_register_device(&mdev->pdev->dev, - VOP_DEV_TRNSP, &_mic_dma_ops, - &vop_hw_ops, id + 1, &mdev->aper, - mdev->dma_ch[0]); - if (IS_ERR(mdev->vpdev)) { - rc = PTR_ERR(mdev->vpdev); - goto scif_remove; - } - - rc = mdev->ops->load_mic_fw(mdev, NULL); - if (rc) - goto vop_remove; - mic_smpt_restore(mdev); - mic_intr_restore(mdev); - mdev->intr_ops->enable_interrupts(mdev); - mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); - mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); - mdev->ops->send_firmware_intr(mdev); - goto unlock_ret; -vop_remove: - vop_unregister_device(mdev->vpdev); -scif_remove: - scif_unregister_device(mdev->scdev); -dma_free: - mic_free_dma_chans(mdev); -dma_remove: - mbus_unregister_device(mdev->dma_mbdev); -unlock_ret: - return rc; -} - -/** - * _mic_stop - Prepare the MIC for reset and trigger reset. - * @cdev: pointer to cosm_device instance - * @force: force a MIC to reset even if it is already offline. - * - * RETURNS: None. - */ -static void _mic_stop(struct cosm_device *cdev, bool force) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - - /* - * Since SCIF handles card shutdown and reset (using COSM), it will - * will be the first to be registered and the last to be - * unregistered. - */ - vop_unregister_device(mdev->vpdev); - scif_unregister_device(mdev->scdev); - mic_free_dma_chans(mdev); - mbus_unregister_device(mdev->dma_mbdev); - mic_bootparam_init(mdev); -} - -static ssize_t _mic_family(struct cosm_device *cdev, char *buf) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - static const char *family[MIC_FAMILY_LAST] = { "x100", "Unknown" }; - - return scnprintf(buf, PAGE_SIZE, "%s\n", family[mdev->family]); -} - -static ssize_t _mic_stepping(struct cosm_device *cdev, char *buf) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - const char *string = "??"; - - switch (mdev->stepping) { - case MIC_A0_STEP: - string = "A0"; - break; - case MIC_B0_STEP: - string = "B0"; - break; - case MIC_B1_STEP: - string = "B1"; - break; - case MIC_C0_STEP: - string = "C0"; - break; - default: - break; - } - return scnprintf(buf, PAGE_SIZE, "%s\n", string); -} - -static struct mic_mw *_mic_aper(struct cosm_device *cdev) -{ - struct mic_device *mdev = cosmdev_to_mdev(cdev); - - return &mdev->aper; -} - -struct cosm_hw_ops cosm_hw_ops = { - .reset = _mic_reset, - .force_reset = _mic_reset, - .post_reset = NULL, - .ready = _mic_ready, - .start = _mic_start, - .stop = _mic_stop, - .family = _mic_family, - .stepping = _mic_stepping, - .aper = _mic_aper, -}; diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c deleted file mode 100644 index ffda740e20d5..000000000000 --- a/drivers/misc/mic/host/mic_debugfs.c +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include -#include -#include - -#include -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_smpt.h" - -/* Debugfs parent dir */ -static struct dentry *mic_dbg; - -static int mic_smpt_show(struct seq_file *s, void *pos) -{ - int i; - struct mic_device *mdev = s->private; - unsigned long flags; - - seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n", - mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); - seq_puts(s, "====================================================\n"); - - if (mdev->smpt) { - struct mic_smpt_info *smpt_info = mdev->smpt; - spin_lock_irqsave(&smpt_info->smpt_lock, flags); - for (i = 0; i < smpt_info->info.num_reg; i++) { - seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n", - " ", i, smpt_info->entry[i].dma_addr, - smpt_info->entry[i].ref_count); - } - spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); - } - seq_puts(s, "====================================================\n"); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_smpt); - -static int mic_post_code_show(struct seq_file *s, void *pos) -{ - struct mic_device *mdev = s->private; - u32 reg = mdev->ops->get_postcode(mdev); - - seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_post_code); - -static int mic_msi_irq_info_show(struct seq_file *s, void *pos) -{ - struct mic_device *mdev = s->private; - int reg; - int i, j; - u16 entry; - u16 vector; - struct pci_dev *pdev = mdev->pdev; - - if (pci_dev_msi_enabled(pdev)) { - for (i = 0; i < mdev->irq_info.num_vectors; i++) { - if (pdev->msix_enabled) { - entry = mdev->irq_info.msix_entries[i].entry; - vector = mdev->irq_info.msix_entries[i].vector; - } else { - entry = 0; - vector = pdev->irq; - } - - reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry); - - seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n", - "IRQ:", vector, "Entry:", entry, i, reg); - - seq_printf(s, "%-10s", "offset:"); - for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) - seq_printf(s, "%4d ", j); - seq_puts(s, "\n"); - - - seq_printf(s, "%-10s", "count:"); - for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) - seq_printf(s, "%4d ", - (mdev->irq_info.mic_msi_map[i] & - BIT(j)) ? 1 : 0); - seq_puts(s, "\n\n"); - } - } else { - seq_puts(s, "MSI/MSIx interrupts not enabled\n"); - } - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(mic_msi_irq_info); - -/* - * mic_create_debug_dir - Initialize MIC debugfs entries. - */ -void mic_create_debug_dir(struct mic_device *mdev) -{ - char name[16]; - - if (!mic_dbg) - return; - - scnprintf(name, sizeof(name), "mic%d", mdev->id); - mdev->dbg_dir = debugfs_create_dir(name, mic_dbg); - - debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, - &mic_smpt_fops); - - debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev, - &mic_post_code_fops); - - debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, - &mic_msi_irq_info_fops); -} - -/* - * mic_delete_debug_dir - Uninitialize MIC debugfs entries. - */ -void mic_delete_debug_dir(struct mic_device *mdev) -{ - debugfs_remove_recursive(mdev->dbg_dir); -} - -/* - * mic_init_debugfs - Initialize global debugfs entry. - */ -void __init mic_init_debugfs(void) -{ - mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); -} - -/* - * mic_exit_debugfs - Uninitialize global debugfs entry - */ -void mic_exit_debugfs(void) -{ - debugfs_remove(mic_dbg); -} diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h deleted file mode 100644 index 41bcd308ae59..000000000000 --- a/drivers/misc/mic/host/mic_device.h +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#ifndef _MIC_DEVICE_H_ -#define _MIC_DEVICE_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "../bus/scif_bus.h" -#include "../bus/vop_bus.h" -#include "../bus/cosm_bus.h" -#include "mic_intr.h" - -/** - * enum mic_stepping - MIC stepping ids. - */ -enum mic_stepping { - MIC_A0_STEP = 0x0, - MIC_B0_STEP = 0x10, - MIC_B1_STEP = 0x11, - MIC_C0_STEP = 0x20, -}; - -extern struct cosm_hw_ops cosm_hw_ops; - -/** - * struct mic_device - MIC device information for each card. - * - * @mmio: MMIO bar information. - * @aper: Aperture bar information. - * @family: The MIC family to which this device belongs. - * @ops: MIC HW specific operations. - * @id: The unique device id for this MIC device. - * @stepping: Stepping ID. - * @pdev: Underlying PCI device. - * @mic_mutex: Mutex for synchronizing access to mic_device. - * @intr_ops: HW specific interrupt operations. - * @smpt_ops: Hardware specific SMPT operations. - * @smpt: MIC SMPT information. - * @intr_info: H/W specific interrupt information. - * @irq_info: The OS specific irq information - * @dbg_dir: debugfs directory of this MIC device. - * @bootaddr: MIC boot address. - * @dp: virtio device page - * @dp_dma_addr: virtio device page DMA address. - * @dma_mbdev: MIC BUS DMA device. - * @dma_ch - Array of DMA channels - * @num_dma_ch - Number of DMA channels available - * @scdev: SCIF device on the SCIF virtual bus. - * @vpdev: Virtio over PCIe device on the VOP virtual bus. - * @cosm_dev: COSM device - */ -struct mic_device { - struct mic_mw mmio; - struct mic_mw aper; - enum mic_hw_family family; - struct mic_hw_ops *ops; - int id; - enum mic_stepping stepping; - struct pci_dev *pdev; - struct mutex mic_mutex; - struct mic_hw_intr_ops *intr_ops; - struct mic_smpt_ops *smpt_ops; - struct mic_smpt_info *smpt; - struct mic_intr_info *intr_info; - struct mic_irq_info irq_info; - struct dentry *dbg_dir; - u32 bootaddr; - void *dp; - dma_addr_t dp_dma_addr; - struct mbus_device *dma_mbdev; - struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN]; - int num_dma_ch; - struct scif_hw_dev *scdev; - struct vop_device *vpdev; - struct cosm_device *cosm_dev; -}; - -/** - * struct mic_hw_ops - MIC HW specific operations. - * @aper_bar: Aperture bar resource number. - * @mmio_bar: MMIO bar resource number. - * @read_spad: Read from scratch pad register. - * @write_spad: Write to scratch pad register. - * @send_intr: Send an interrupt for a particular doorbell on the card. - * @ack_interrupt: Hardware specific operations to ack the h/w on - * receipt of an interrupt. - * @intr_workarounds: Hardware specific workarounds needed after - * handling an interrupt. - * @reset: Reset the remote processor. - * @reset_fw_ready: Reset firmware ready field. - * @is_fw_ready: Check if firmware is ready for OS download. - * @send_firmware_intr: Send an interrupt to the card firmware. - * @load_mic_fw: Load firmware segments required to boot the card - * into card memory. This includes the kernel, command line, ramdisk etc. - * @get_postcode: Get post code status from firmware. - * @dma_filter: DMA filter function to be used. - */ -struct mic_hw_ops { - u8 aper_bar; - u8 mmio_bar; - u32 (*read_spad)(struct mic_device *mdev, unsigned int idx); - void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); - void (*send_intr)(struct mic_device *mdev, int doorbell); - u32 (*ack_interrupt)(struct mic_device *mdev); - void (*intr_workarounds)(struct mic_device *mdev); - void (*reset)(struct mic_device *mdev); - void (*reset_fw_ready)(struct mic_device *mdev); - bool (*is_fw_ready)(struct mic_device *mdev); - void (*send_firmware_intr)(struct mic_device *mdev); - int (*load_mic_fw)(struct mic_device *mdev, const char *buf); - u32 (*get_postcode)(struct mic_device *mdev); - bool (*dma_filter)(struct dma_chan *chan, void *param); -}; - -/** - * mic_mmio_read - read from an MMIO register. - * @mw: MMIO register base virtual address. - * @offset: register offset. - * - * RETURNS: register value. - */ -static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) -{ - return ioread32(mw->va + offset); -} - -/** - * mic_mmio_write - write to an MMIO register. - * @mw: MMIO register base virtual address. - * @val: the data value to put into the register - * @offset: register offset. - * - * RETURNS: none. - */ -static inline void -mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) -{ - iowrite32(val, mw->va + offset); -} - -void mic_bootparam_init(struct mic_device *mdev); -void mic_create_debug_dir(struct mic_device *dev); -void mic_delete_debug_dir(struct mic_device *dev); -void __init mic_init_debugfs(void); -void mic_exit_debugfs(void); -#endif diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c deleted file mode 100644 index 85b3221b5d40..000000000000 --- a/drivers/misc/mic/host/mic_intr.c +++ /dev/null @@ -1,635 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include -#include - -#include "../common/mic_dev.h" -#include "mic_device.h" - -static irqreturn_t mic_thread_fn(int irq, void *dev) -{ - struct mic_device *mdev = dev; - struct mic_intr_info *intr_info = mdev->intr_info; - struct mic_irq_info *irq_info = &mdev->irq_info; - struct mic_intr_cb *intr_cb; - struct pci_dev *pdev = mdev->pdev; - int i; - - spin_lock(&irq_info->mic_thread_lock); - for (i = intr_info->intr_start_idx[MIC_INTR_DB]; - i < intr_info->intr_len[MIC_INTR_DB]; i++) - if (test_and_clear_bit(i, &irq_info->mask)) { - list_for_each_entry(intr_cb, &irq_info->cb_list[i], - list) - if (intr_cb->thread_fn) - intr_cb->thread_fn(pdev->irq, - intr_cb->data); - } - spin_unlock(&irq_info->mic_thread_lock); - return IRQ_HANDLED; -} -/** - * mic_interrupt - Generic interrupt handler for - * MSI and INTx based interrupts. - * @irq: interrupt to handle (unused) - * @dev: pointer to the mic_device instance - */ -static irqreturn_t mic_interrupt(int irq, void *dev) -{ - struct mic_device *mdev = dev; - struct mic_intr_info *intr_info = mdev->intr_info; - struct mic_irq_info *irq_info = &mdev->irq_info; - struct mic_intr_cb *intr_cb; - struct pci_dev *pdev = mdev->pdev; - u32 mask; - int i; - - mask = mdev->ops->ack_interrupt(mdev); - if (!mask) - return IRQ_NONE; - - spin_lock(&irq_info->mic_intr_lock); - for (i = intr_info->intr_start_idx[MIC_INTR_DB]; - i < intr_info->intr_len[MIC_INTR_DB]; i++) - if (mask & BIT(i)) { - list_for_each_entry(intr_cb, &irq_info->cb_list[i], - list) - if (intr_cb->handler) - intr_cb->handler(pdev->irq, - intr_cb->data); - set_bit(i, &irq_info->mask); - } - spin_unlock(&irq_info->mic_intr_lock); - return IRQ_WAKE_THREAD; -} - -/* Return the interrupt offset from the index. Index is 0 based. */ -static u16 mic_map_src_to_offset(struct mic_device *mdev, - int intr_src, enum mic_intr_type type) -{ - if (type >= MIC_NUM_INTR_TYPES) - return MIC_NUM_OFFSETS; - if (intr_src >= mdev->intr_info->intr_len[type]) - return MIC_NUM_OFFSETS; - - return mdev->intr_info->intr_start_idx[type] + intr_src; -} - -/* Return next available msix_entry. */ -static struct msix_entry *mic_get_available_vector(struct mic_device *mdev) -{ - int i; - struct mic_irq_info *info = &mdev->irq_info; - - for (i = 0; i < info->num_vectors; i++) - if (!info->mic_msi_map[i]) - return &info->msix_entries[i]; - return NULL; -} - -/** - * mic_register_intr_callback - Register a callback handler for the - * given source id. - * - * @mdev: pointer to the mic_device instance - * @idx: The source id to be registered. - * @handler: The function to be called when the source id receives - * the interrupt. - * @thread_fn: thread fn. corresponding to the handler - * @data: Private data of the requester. - * Return the callback structure that was registered or an - * appropriate error on failure. - */ -static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, - u8 idx, irq_handler_t handler, irq_handler_t thread_fn, - void *data) -{ - struct mic_intr_cb *intr_cb; - unsigned long flags; - int rc; - intr_cb = kmalloc(sizeof(*intr_cb), GFP_KERNEL); - - if (!intr_cb) - return ERR_PTR(-ENOMEM); - - intr_cb->handler = handler; - intr_cb->thread_fn = thread_fn; - intr_cb->data = data; - intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, - 0, 0, GFP_KERNEL); - if (intr_cb->cb_id < 0) { - rc = intr_cb->cb_id; - goto ida_fail; - } - - spin_lock(&mdev->irq_info.mic_thread_lock); - spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); - list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); - spin_unlock(&mdev->irq_info.mic_thread_lock); - - return intr_cb; -ida_fail: - kfree(intr_cb); - return ERR_PTR(rc); -} - -/** - * mic_unregister_intr_callback - Unregister the callback handler - * identified by its callback id. - * - * @mdev: pointer to the mic_device instance - * @idx: The callback structure id to be unregistered. - * Return the source id that was unregistered or MIC_NUM_OFFSETS if no - * such callback handler was found. - */ -static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) -{ - struct list_head *pos, *tmp; - struct mic_intr_cb *intr_cb; - unsigned long flags; - int i; - - spin_lock(&mdev->irq_info.mic_thread_lock); - spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); - for (i = 0; i < MIC_NUM_OFFSETS; i++) { - list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { - intr_cb = list_entry(pos, struct mic_intr_cb, list); - if (intr_cb->cb_id == idx) { - list_del(pos); - ida_simple_remove(&mdev->irq_info.cb_ida, - intr_cb->cb_id); - kfree(intr_cb); - spin_unlock_irqrestore( - &mdev->irq_info.mic_intr_lock, flags); - spin_unlock(&mdev->irq_info.mic_thread_lock); - return i; - } - } - } - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); - spin_unlock(&mdev->irq_info.mic_thread_lock); - return MIC_NUM_OFFSETS; -} - -/** - * mic_setup_msix - Initializes MSIx interrupts. - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) -{ - int rc, i; - int entry_size = sizeof(*mdev->irq_info.msix_entries); - - mdev->irq_info.msix_entries = kmalloc_array(MIC_MIN_MSIX, - entry_size, GFP_KERNEL); - if (!mdev->irq_info.msix_entries) { - rc = -ENOMEM; - goto err_nomem1; - } - - for (i = 0; i < MIC_MIN_MSIX; i++) - mdev->irq_info.msix_entries[i].entry = i; - - rc = pci_enable_msix_exact(pdev, mdev->irq_info.msix_entries, - MIC_MIN_MSIX); - if (rc) { - dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc); - goto err_enable_msix; - } - - mdev->irq_info.num_vectors = MIC_MIN_MSIX; - mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * - mdev->irq_info.num_vectors), GFP_KERNEL); - - if (!mdev->irq_info.mic_msi_map) { - rc = -ENOMEM; - goto err_nomem2; - } - - dev_dbg(&mdev->pdev->dev, - "%d MSIx irqs setup\n", mdev->irq_info.num_vectors); - return 0; -err_nomem2: - pci_disable_msix(pdev); -err_enable_msix: - kfree(mdev->irq_info.msix_entries); -err_nomem1: - mdev->irq_info.num_vectors = 0; - return rc; -} - -/** - * mic_setup_callbacks - Initialize data structures needed - * to handle callbacks. - * - * @mdev: pointer to mic_device instance - */ -static int mic_setup_callbacks(struct mic_device *mdev) -{ - int i; - - mdev->irq_info.cb_list = kmalloc_array(MIC_NUM_OFFSETS, - sizeof(*mdev->irq_info.cb_list), - GFP_KERNEL); - if (!mdev->irq_info.cb_list) - return -ENOMEM; - - for (i = 0; i < MIC_NUM_OFFSETS; i++) - INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); - ida_init(&mdev->irq_info.cb_ida); - spin_lock_init(&mdev->irq_info.mic_intr_lock); - spin_lock_init(&mdev->irq_info.mic_thread_lock); - return 0; -} - -/** - * mic_release_callbacks - Uninitialize data structures needed - * to handle callbacks. - * - * @mdev: pointer to mic_device instance - */ -static void mic_release_callbacks(struct mic_device *mdev) -{ - unsigned long flags; - struct list_head *pos, *tmp; - struct mic_intr_cb *intr_cb; - int i; - - spin_lock(&mdev->irq_info.mic_thread_lock); - spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); - for (i = 0; i < MIC_NUM_OFFSETS; i++) { - if (list_empty(&mdev->irq_info.cb_list[i])) - break; - - list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { - intr_cb = list_entry(pos, struct mic_intr_cb, list); - list_del(pos); - ida_simple_remove(&mdev->irq_info.cb_ida, - intr_cb->cb_id); - kfree(intr_cb); - } - } - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); - spin_unlock(&mdev->irq_info.mic_thread_lock); - ida_destroy(&mdev->irq_info.cb_ida); - kfree(mdev->irq_info.cb_list); -} - -/** - * mic_setup_msi - Initializes MSI interrupts. - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev) -{ - int rc; - - rc = pci_enable_msi(pdev); - if (rc) { - dev_dbg(&pdev->dev, "Error enabling MSI. rc = %d\n", rc); - return rc; - } - - mdev->irq_info.num_vectors = 1; - mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * - mdev->irq_info.num_vectors), GFP_KERNEL); - - if (!mdev->irq_info.mic_msi_map) { - rc = -ENOMEM; - goto err_nomem1; - } - - rc = mic_setup_callbacks(mdev); - if (rc) { - dev_err(&pdev->dev, "Error setting up callbacks\n"); - goto err_nomem2; - } - - rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, - 0, "mic-msi", mdev); - if (rc) { - dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); - goto err_irq_req_fail; - } - - dev_dbg(&pdev->dev, "%d MSI irqs setup\n", mdev->irq_info.num_vectors); - return 0; -err_irq_req_fail: - mic_release_callbacks(mdev); -err_nomem2: - kfree(mdev->irq_info.mic_msi_map); -err_nomem1: - pci_disable_msi(pdev); - mdev->irq_info.num_vectors = 0; - return rc; -} - -/** - * mic_setup_intx - Initializes legacy interrupts. - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) -{ - int rc; - - /* Enable intx */ - pci_intx(pdev, 1); - rc = mic_setup_callbacks(mdev); - if (rc) { - dev_err(&pdev->dev, "Error setting up callbacks\n"); - goto err_nomem; - } - - rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, - IRQF_SHARED, "mic-intx", mdev); - if (rc) - goto err; - - dev_dbg(&pdev->dev, "intx irq setup\n"); - return 0; -err: - mic_release_callbacks(mdev); -err_nomem: - return rc; -} - -/** - * mic_next_db - Retrieve the next doorbell interrupt source id. - * The id is picked sequentially from the available pool of - * doorlbell ids. - * - * @mdev: pointer to the mic_device instance. - * - * Returns the next doorbell interrupt source. - */ -int mic_next_db(struct mic_device *mdev) -{ - int next_db; - - next_db = mdev->irq_info.next_avail_src % - mdev->intr_info->intr_len[MIC_INTR_DB]; - mdev->irq_info.next_avail_src++; - return next_db; -} - -#define COOKIE_ID_SHIFT 16 -#define GET_ENTRY(cookie) ((cookie) & 0xFFFF) -#define GET_OFFSET(cookie) ((cookie) >> COOKIE_ID_SHIFT) -#define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) - -/** - * mic_request_threaded_irq - request an irq. mic_mutex needs - * to be held before calling this function. - * - * @mdev: pointer to mic_device instance - * @handler: The callback function that handles the interrupt. - * The function needs to call ack_interrupts - * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. - * @thread_fn: thread fn required by request_threaded_irq. - * @name: The ASCII name of the callee requesting the irq. - * @data: private data that is returned back when calling the - * function handler. - * @intr_src: The source id of the requester. Its the doorbell id - * for Doorbell interrupts and DMA channel id for DMA interrupts. - * @type: The type of interrupt. Values defined in mic_intr_type - * - * returns: The cookie that is transparent to the caller. Passed - * back when calling mic_free_irq. An appropriate error code - * is returned on failure. Caller needs to use IS_ERR(return_val) - * to check for failure and PTR_ERR(return_val) to obtained the - * error code. - * - */ -struct mic_irq * -mic_request_threaded_irq(struct mic_device *mdev, - irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int intr_src, - enum mic_intr_type type) -{ - u16 offset; - int rc = 0; - struct msix_entry *msix = NULL; - unsigned long cookie = 0; - u16 entry; - struct mic_intr_cb *intr_cb; - struct pci_dev *pdev = mdev->pdev; - - offset = mic_map_src_to_offset(mdev, intr_src, type); - if (offset >= MIC_NUM_OFFSETS) { - dev_err(&mdev->pdev->dev, - "Error mapping index %d to a valid source id.\n", - intr_src); - rc = -EINVAL; - goto err; - } - - if (mdev->irq_info.num_vectors > 1) { - msix = mic_get_available_vector(mdev); - if (!msix) { - dev_err(&mdev->pdev->dev, - "No MSIx vectors available for use.\n"); - rc = -ENOSPC; - goto err; - } - - rc = request_threaded_irq(msix->vector, handler, thread_fn, - 0, name, data); - if (rc) { - dev_dbg(&mdev->pdev->dev, - "request irq failed rc = %d\n", rc); - goto err; - } - entry = msix->entry; - mdev->irq_info.mic_msi_map[entry] |= BIT(offset); - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, offset, true); - cookie = MK_COOKIE(entry, offset); - dev_dbg(&mdev->pdev->dev, "irq: %d assigned for src: %d\n", - msix->vector, intr_src); - } else { - intr_cb = mic_register_intr_callback(mdev, offset, handler, - thread_fn, data); - if (IS_ERR(intr_cb)) { - dev_err(&mdev->pdev->dev, - "No available callback entries for use\n"); - rc = PTR_ERR(intr_cb); - goto err; - } - - entry = 0; - if (pci_dev_msi_enabled(pdev)) { - mdev->irq_info.mic_msi_map[entry] |= (1 << offset); - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, offset, true); - } - cookie = MK_COOKIE(entry, intr_cb->cb_id); - dev_dbg(&mdev->pdev->dev, "callback %d registered for src: %d\n", - intr_cb->cb_id, intr_src); - } - return (struct mic_irq *)cookie; -err: - return ERR_PTR(rc); -} - -/** - * mic_free_irq - free irq. mic_mutex - * needs to be held before calling this function. - * - * @mdev: pointer to mic_device instance - * @cookie: cookie obtained during a successful call to mic_request_threaded_irq - * @data: private data specified by the calling function during the - * mic_request_threaded_irq - * - * returns: none. - */ -void mic_free_irq(struct mic_device *mdev, - struct mic_irq *cookie, void *data) -{ - u32 offset; - u32 entry; - u8 src_id; - unsigned int irq; - struct pci_dev *pdev = mdev->pdev; - - entry = GET_ENTRY((unsigned long)cookie); - offset = GET_OFFSET((unsigned long)cookie); - if (mdev->irq_info.num_vectors > 1) { - if (entry >= mdev->irq_info.num_vectors) { - dev_warn(&mdev->pdev->dev, - "entry %d should be < num_irq %d\n", - entry, mdev->irq_info.num_vectors); - return; - } - irq = mdev->irq_info.msix_entries[entry].vector; - free_irq(irq, data); - mdev->irq_info.mic_msi_map[entry] &= ~(BIT(offset)); - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, offset, false); - - dev_dbg(&mdev->pdev->dev, "irq: %d freed\n", irq); - } else { - irq = pdev->irq; - src_id = mic_unregister_intr_callback(mdev, offset); - if (src_id >= MIC_NUM_OFFSETS) { - dev_warn(&mdev->pdev->dev, "Error unregistering callback\n"); - return; - } - if (pci_dev_msi_enabled(pdev)) { - mdev->irq_info.mic_msi_map[entry] &= ~(BIT(src_id)); - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, src_id, false); - } - dev_dbg(&mdev->pdev->dev, "callback %d unregistered for src: %d\n", - offset, src_id); - } -} - -/** - * mic_setup_interrupts - Initializes interrupts. - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev) -{ - int rc; - - rc = mic_setup_msix(mdev, pdev); - if (!rc) - goto done; - - rc = mic_setup_msi(mdev, pdev); - if (!rc) - goto done; - - rc = mic_setup_intx(mdev, pdev); - if (rc) { - dev_err(&mdev->pdev->dev, "no usable interrupts\n"); - return rc; - } -done: - mdev->intr_ops->enable_interrupts(mdev); - return 0; -} - -/** - * mic_free_interrupts - Frees interrupts setup by mic_setup_interrupts - * - * @mdev: pointer to mic_device instance - * @pdev: PCI device structure - * - * returns none. - */ -void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev) -{ - int i; - - mdev->intr_ops->disable_interrupts(mdev); - if (mdev->irq_info.num_vectors > 1) { - for (i = 0; i < mdev->irq_info.num_vectors; i++) { - if (mdev->irq_info.mic_msi_map[i]) - dev_warn(&pdev->dev, "irq %d may still be in use.\n", - mdev->irq_info.msix_entries[i].vector); - } - kfree(mdev->irq_info.mic_msi_map); - kfree(mdev->irq_info.msix_entries); - pci_disable_msix(pdev); - } else { - if (pci_dev_msi_enabled(pdev)) { - free_irq(pdev->irq, mdev); - kfree(mdev->irq_info.mic_msi_map); - pci_disable_msi(pdev); - } else { - free_irq(pdev->irq, mdev); - } - mic_release_callbacks(mdev); - } -} - -/** - * mic_intr_restore - Restore MIC interrupt registers. - * - * @mdev: pointer to mic_device instance. - * - * Restore the interrupt registers to values previously - * stored in the SW data structures. mic_mutex needs to - * be held before calling this function. - * - * returns None. - */ -void mic_intr_restore(struct mic_device *mdev) -{ - int entry, offset; - struct pci_dev *pdev = mdev->pdev; - - if (!pci_dev_msi_enabled(pdev)) - return; - - for (entry = 0; entry < mdev->irq_info.num_vectors; entry++) { - for (offset = 0; offset < MIC_NUM_OFFSETS; offset++) { - if (mdev->irq_info.mic_msi_map[entry] & BIT(offset)) - mdev->intr_ops->program_msi_to_src_map(mdev, - entry, offset, true); - } - } -} diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h deleted file mode 100644 index b14ba818006f..000000000000 --- a/drivers/misc/mic/host/mic_intr.h +++ /dev/null @@ -1,137 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#ifndef _MIC_INTR_H_ -#define _MIC_INTR_H_ - -#include -#include -/* - * The minimum number of msix vectors required for normal operation. - * 3 for virtio network, console and block devices. - * 1 for card shutdown notifications. - * 4 for host owned DMA channels. - * 1 for SCIF - */ -#define MIC_MIN_MSIX 9 -#define MIC_NUM_OFFSETS 32 - -/** - * mic_intr_source - The type of source that will generate - * the interrupt.The number of types needs to be in sync with - * MIC_NUM_INTR_TYPES - * - * MIC_INTR_DB: The source is a doorbell - * MIC_INTR_DMA: The source is a DMA channel - * MIC_INTR_ERR: The source is an error interrupt e.g. SBOX ERR - * MIC_NUM_INTR_TYPES: Total number of interrupt sources. - */ -enum mic_intr_type { - MIC_INTR_DB = 0, - MIC_INTR_DMA, - MIC_INTR_ERR, - MIC_NUM_INTR_TYPES -}; - -/** - * struct mic_intr_info - Contains h/w specific interrupt sources - * information. - * - * @intr_start_idx: Contains the starting indexes of the - * interrupt types. - * @intr_len: Contains the length of the interrupt types. - */ -struct mic_intr_info { - u16 intr_start_idx[MIC_NUM_INTR_TYPES]; - u16 intr_len[MIC_NUM_INTR_TYPES]; -}; - -/** - * struct mic_irq_info - OS specific irq information - * - * @next_avail_src: next available doorbell that can be assigned. - * @msix_entries: msix entries allocated while setting up MSI-x - * @mic_msi_map: The MSI/MSI-x mapping information. - * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. - * @cb_ida: callback ID allocator to track the callbacks registered. - * @mic_intr_lock: spinlock to protect the interrupt callback list. - * @mic_thread_lock: spinlock to protect the thread callback list. - * This lock is used to protect against thread_fn while - * mic_intr_lock is used to protect against interrupt handler. - * @cb_list: Array of callback lists one for each source. - * @mask: Mask used by the main thread fn to call the underlying thread fns. - */ -struct mic_irq_info { - int next_avail_src; - struct msix_entry *msix_entries; - u32 *mic_msi_map; - u16 num_vectors; - struct ida cb_ida; - spinlock_t mic_intr_lock; - spinlock_t mic_thread_lock; - struct list_head *cb_list; - unsigned long mask; -}; - -/** - * struct mic_intr_cb - Interrupt callback structure. - * - * @handler: The callback function - * @thread_fn: The thread_fn. - * @data: Private data of the requester. - * @cb_id: The callback id. Identifies this callback. - * @list: list head pointing to the next callback structure. - */ -struct mic_intr_cb { - irq_handler_t handler; - irq_handler_t thread_fn; - void *data; - int cb_id; - struct list_head list; -}; - -/** - * struct mic_irq - opaque pointer used as cookie - */ -struct mic_irq; - -/* Forward declaration */ -struct mic_device; - -/** - * struct mic_hw_intr_ops: MIC HW specific interrupt operations - * @intr_init: Initialize H/W specific interrupt information. - * @enable_interrupts: Enable interrupts from the hardware. - * @disable_interrupts: Disable interrupts from the hardware. - * @program_msi_to_src_map: Update MSI mapping registers with - * irq information. - * @read_msi_to_src_map: Read MSI mapping registers containing - * irq information. - */ -struct mic_hw_intr_ops { - void (*intr_init)(struct mic_device *mdev); - void (*enable_interrupts)(struct mic_device *mdev); - void (*disable_interrupts)(struct mic_device *mdev); - void (*program_msi_to_src_map) (struct mic_device *mdev, - int idx, int intr_src, bool set); - u32 (*read_msi_to_src_map) (struct mic_device *mdev, - int idx); -}; - -int mic_next_db(struct mic_device *mdev); -struct mic_irq * -mic_request_threaded_irq(struct mic_device *mdev, - irq_handler_t handler, irq_handler_t thread_fn, - const char *name, void *data, int intr_src, - enum mic_intr_type type); -void mic_free_irq(struct mic_device *mdev, - struct mic_irq *cookie, void *data); -int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); -void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev); -void mic_intr_restore(struct mic_device *mdev); -#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c deleted file mode 100644 index ea4608527ea0..000000000000 --- a/drivers/misc/mic/host/mic_main.c +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include -#include -#include -#include - -#include -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_x100.h" -#include "mic_smpt.h" - -static const char mic_driver_name[] = "mic"; - -static const struct pci_device_id mic_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2253)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2254)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2255)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2256)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2257)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2258)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2259)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225a)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225b)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225c)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225d)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225e)}, - - /* required last entry */ - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, mic_pci_tbl); - -/* ID allocator for MIC devices */ -static struct ida g_mic_ida; - -/* Initialize the device page */ -static int mic_dp_init(struct mic_device *mdev) -{ - mdev->dp = kzalloc(MIC_DP_SIZE, GFP_KERNEL); - if (!mdev->dp) - return -ENOMEM; - - mdev->dp_dma_addr = mic_map_single(mdev, - mdev->dp, MIC_DP_SIZE); - if (mic_map_error(mdev->dp_dma_addr)) { - kfree(mdev->dp); - dev_err(&mdev->pdev->dev, "%s %d err %d\n", - __func__, __LINE__, -ENOMEM); - return -ENOMEM; - } - mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); - mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); - return 0; -} - -/* Uninitialize the device page */ -static void mic_dp_uninit(struct mic_device *mdev) -{ - mic_unmap_single(mdev, mdev->dp_dma_addr, MIC_DP_SIZE); - kfree(mdev->dp); -} - -/** - * mic_ops_init: Initialize HW specific operation tables. - * - * @mdev: pointer to mic_device instance - * - * returns none. - */ -static void mic_ops_init(struct mic_device *mdev) -{ - switch (mdev->family) { - case MIC_FAMILY_X100: - mdev->ops = &mic_x100_ops; - mdev->intr_ops = &mic_x100_intr_ops; - mdev->smpt_ops = &mic_x100_smpt_ops; - break; - default: - break; - } -} - -/** - * mic_get_family - Determine hardware family to which this MIC belongs. - * - * @pdev: The pci device structure - * - * returns family. - */ -static enum mic_hw_family mic_get_family(struct pci_dev *pdev) -{ - enum mic_hw_family family; - - switch (pdev->device) { - case MIC_X100_PCI_DEVICE_2250: - case MIC_X100_PCI_DEVICE_2251: - case MIC_X100_PCI_DEVICE_2252: - case MIC_X100_PCI_DEVICE_2253: - case MIC_X100_PCI_DEVICE_2254: - case MIC_X100_PCI_DEVICE_2255: - case MIC_X100_PCI_DEVICE_2256: - case MIC_X100_PCI_DEVICE_2257: - case MIC_X100_PCI_DEVICE_2258: - case MIC_X100_PCI_DEVICE_2259: - case MIC_X100_PCI_DEVICE_225a: - case MIC_X100_PCI_DEVICE_225b: - case MIC_X100_PCI_DEVICE_225c: - case MIC_X100_PCI_DEVICE_225d: - case MIC_X100_PCI_DEVICE_225e: - family = MIC_FAMILY_X100; - break; - default: - family = MIC_FAMILY_UNKNOWN; - break; - } - return family; -} - -/** - * mic_device_init - Allocates and initializes the MIC device structure - * - * @mdev: pointer to mic_device instance - * @pdev: The pci device structure - * - * returns none. - */ -static void -mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) -{ - mdev->pdev = pdev; - mdev->family = mic_get_family(pdev); - mdev->stepping = pdev->revision; - mic_ops_init(mdev); - mutex_init(&mdev->mic_mutex); - mdev->irq_info.next_avail_src = 0; -} - -/** - * mic_probe - Device Initialization Routine - * - * @pdev: PCI device structure - * @ent: entry in mic_pci_tbl - * - * returns 0 on success, < 0 on failure. - */ -static int mic_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int rc; - struct mic_device *mdev; - - mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); - if (!mdev) { - rc = -ENOMEM; - goto mdev_alloc_fail; - } - mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL); - if (mdev->id < 0) { - rc = mdev->id; - dev_err(&pdev->dev, "ida_simple_get failed rc %d\n", rc); - goto ida_fail; - } - - mic_device_init(mdev, pdev); - - rc = pci_enable_device(pdev); - if (rc) { - dev_err(&pdev->dev, "failed to enable pci device.\n"); - goto ida_remove; - } - - pci_set_master(pdev); - - rc = pci_request_regions(pdev, mic_driver_name); - if (rc) { - dev_err(&pdev->dev, "failed to get pci regions.\n"); - goto disable_device; - } - - rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); - if (rc) { - dev_err(&pdev->dev, "Cannot set DMA mask\n"); - goto release_regions; - } - - mdev->mmio.pa = pci_resource_start(pdev, mdev->ops->mmio_bar); - mdev->mmio.len = pci_resource_len(pdev, mdev->ops->mmio_bar); - mdev->mmio.va = pci_ioremap_bar(pdev, mdev->ops->mmio_bar); - if (!mdev->mmio.va) { - dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); - rc = -EIO; - goto release_regions; - } - - mdev->aper.pa = pci_resource_start(pdev, mdev->ops->aper_bar); - mdev->aper.len = pci_resource_len(pdev, mdev->ops->aper_bar); - mdev->aper.va = ioremap_wc(mdev->aper.pa, mdev->aper.len); - if (!mdev->aper.va) { - dev_err(&pdev->dev, "Cannot remap Aperture BAR\n"); - rc = -EIO; - goto unmap_mmio; - } - - mdev->intr_ops->intr_init(mdev); - rc = mic_setup_interrupts(mdev, pdev); - if (rc) { - dev_err(&pdev->dev, "mic_setup_interrupts failed %d\n", rc); - goto unmap_aper; - } - rc = mic_smpt_init(mdev); - if (rc) { - dev_err(&pdev->dev, "smpt_init failed %d\n", rc); - goto free_interrupts; - } - - pci_set_drvdata(pdev, mdev); - - rc = mic_dp_init(mdev); - if (rc) { - dev_err(&pdev->dev, "mic_dp_init failed rc %d\n", rc); - goto smpt_uninit; - } - mic_bootparam_init(mdev); - mic_create_debug_dir(mdev); - - mdev->cosm_dev = cosm_register_device(&mdev->pdev->dev, &cosm_hw_ops); - if (IS_ERR(mdev->cosm_dev)) { - rc = PTR_ERR(mdev->cosm_dev); - dev_err(&pdev->dev, "cosm_add_device failed rc %d\n", rc); - goto cleanup_debug_dir; - } - return 0; -cleanup_debug_dir: - mic_delete_debug_dir(mdev); - mic_dp_uninit(mdev); -smpt_uninit: - mic_smpt_uninit(mdev); -free_interrupts: - mic_free_interrupts(mdev, pdev); -unmap_aper: - iounmap(mdev->aper.va); -unmap_mmio: - iounmap(mdev->mmio.va); -release_regions: - pci_release_regions(pdev); -disable_device: - pci_disable_device(pdev); -ida_remove: - ida_simple_remove(&g_mic_ida, mdev->id); -ida_fail: - kfree(mdev); -mdev_alloc_fail: - dev_err(&pdev->dev, "Probe failed rc %d\n", rc); - return rc; -} - -/** - * mic_remove - Device Removal Routine - * mic_remove is called by the PCI subsystem to alert the driver - * that it should release a PCI device. - * - * @pdev: PCI device structure - */ -static void mic_remove(struct pci_dev *pdev) -{ - struct mic_device *mdev; - - mdev = pci_get_drvdata(pdev); - if (!mdev) - return; - - cosm_unregister_device(mdev->cosm_dev); - mic_delete_debug_dir(mdev); - mic_dp_uninit(mdev); - mic_smpt_uninit(mdev); - mic_free_interrupts(mdev, pdev); - iounmap(mdev->aper.va); - iounmap(mdev->mmio.va); - pci_release_regions(pdev); - pci_disable_device(pdev); - ida_simple_remove(&g_mic_ida, mdev->id); - kfree(mdev); -} - -static struct pci_driver mic_driver = { - .name = mic_driver_name, - .id_table = mic_pci_tbl, - .probe = mic_probe, - .remove = mic_remove -}; - -static int __init mic_init(void) -{ - int ret; - - request_module("mic_x100_dma"); - mic_init_debugfs(); - ida_init(&g_mic_ida); - ret = pci_register_driver(&mic_driver); - if (ret) { - pr_err("pci_register_driver failed ret %d\n", ret); - goto cleanup_debugfs; - } - return 0; -cleanup_debugfs: - ida_destroy(&g_mic_ida); - mic_exit_debugfs(); - return ret; -} - -static void __exit mic_exit(void) -{ - pci_unregister_driver(&mic_driver); - ida_destroy(&g_mic_ida); - mic_exit_debugfs(); -} - -module_init(mic_init); -module_exit(mic_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) MIC X100 Host driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c deleted file mode 100644 index 50d1bebecd54..000000000000 --- a/drivers/misc/mic/host/mic_smpt.c +++ /dev/null @@ -1,427 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include - -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_smpt.h" - -static inline u64 mic_system_page_mask(struct mic_device *mdev) -{ - return (1ULL << mdev->smpt->info.page_shift) - 1ULL; -} - -static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa) -{ - return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift; -} - -static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index) -{ - return mdev->smpt->info.base + (index * mdev->smpt->info.page_size); -} - -static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa) -{ - return pa & mic_system_page_mask(mdev); -} - -static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa) -{ - return ALIGN(pa - mic_system_page_mask(mdev), - mdev->smpt->info.page_size); -} - -static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa) -{ - return ALIGN(pa, mdev->smpt->info.page_size); -} - -/* Total Cumulative system memory accessible by MIC across all SMPT entries */ -static inline u64 mic_max_system_memory(struct mic_device *mdev) -{ - return mdev->smpt->info.num_reg * mdev->smpt->info.page_size; -} - -/* Maximum system memory address accessible by MIC */ -static inline u64 mic_max_system_addr(struct mic_device *mdev) -{ - return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL; -} - -/* Check if the DMA address is a MIC system memory address */ -static inline bool -mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa) -{ - return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev); -} - -/* Populate an SMPT entry and update the reference counts. */ -static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr, - int entries, struct mic_device *mdev) -{ - struct mic_smpt_info *smpt_info = mdev->smpt; - int i; - - for (i = spt; i < spt + entries; i++, - addr += smpt_info->info.page_size) { - if (!smpt_info->entry[i].ref_count && - (smpt_info->entry[i].dma_addr != addr)) { - mdev->smpt_ops->set(mdev, addr, i); - smpt_info->entry[i].dma_addr = addr; - } - smpt_info->entry[i].ref_count += ref[i - spt]; - } -} - -/* - * Find an available MIC address in MIC SMPT address space - * for a given DMA address and size. - */ -static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr, - int entries, s64 *ref, size_t size) -{ - int spt; - int ae = 0; - int i; - unsigned long flags; - dma_addr_t mic_addr = 0; - dma_addr_t addr = dma_addr; - struct mic_smpt_info *smpt_info = mdev->smpt; - - spin_lock_irqsave(&smpt_info->smpt_lock, flags); - - /* find existing entries */ - for (i = 0; i < smpt_info->info.num_reg; i++) { - if (smpt_info->entry[i].dma_addr == addr) { - ae++; - addr += smpt_info->info.page_size; - } else if (ae) /* cannot find contiguous entries */ - goto not_found; - - if (ae == entries) - goto found; - } - - /* find free entry */ - for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) { - ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0; - if (ae == entries) - goto found; - } - -not_found: - spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); - return mic_addr; - -found: - spt = i - entries + 1; - mic_addr = mic_smpt_to_pa(mdev, spt); - mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev); - smpt_info->map_count++; - smpt_info->ref_count += (s64)size; - spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); - return mic_addr; -} - -/* - * Returns number of smpt entries needed for dma_addr to dma_addr + size - * also returns the reference count array for each of those entries - * and the starting smpt address - */ -static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr, - size_t size, s64 *ref, u64 *smpt_start) -{ - u64 start = dma_addr; - u64 end = dma_addr + size; - int i = 0; - - while (start < end) { - ref[i++] = min(mic_smpt_align_high(mdev, start + 1), - end) - start; - start = mic_smpt_align_high(mdev, start + 1); - } - - if (smpt_start) - *smpt_start = mic_smpt_align_low(mdev, dma_addr); - - return i; -} - -/* - * mic_to_dma_addr - Converts a MIC address to a DMA address. - * - * @mdev: pointer to mic_device instance. - * @mic_addr: MIC address. - * - * returns a DMA address. - */ -dma_addr_t mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr) -{ - struct mic_smpt_info *smpt_info = mdev->smpt; - int spt; - dma_addr_t dma_addr; - - if (!mic_is_system_addr(mdev, mic_addr)) { - dev_err(&mdev->pdev->dev, - "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); - return -EINVAL; - } - spt = mic_sys_addr_to_smpt(mdev, mic_addr); - dma_addr = smpt_info->entry[spt].dma_addr + - mic_smpt_offset(mdev, mic_addr); - return dma_addr; -} - -/** - * mic_map - Maps a DMA address to a MIC physical address. - * - * @mdev: pointer to mic_device instance. - * @dma_addr: DMA address. - * @size: Size of the region to be mapped. - * - * This API converts the DMA address provided to a DMA address understood - * by MIC. Caller should check for errors by calling mic_map_error(..). - * - * returns DMA address as required by MIC. - */ -dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size) -{ - dma_addr_t mic_addr = 0; - int num_entries; - s64 *ref; - u64 smpt_start; - - if (!size || size > mic_max_system_memory(mdev)) - return mic_addr; - - ref = kmalloc_array(mdev->smpt->info.num_reg, sizeof(s64), GFP_ATOMIC); - if (!ref) - return mic_addr; - - num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size, - ref, &smpt_start); - - /* Set the smpt table appropriately and get 16G aligned mic address */ - mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size); - - kfree(ref); - - /* - * If mic_addr is zero then its an error case - * since mic_addr can never be zero. - * else generate mic_addr by adding the 16G offset in dma_addr - */ - if (!mic_addr && MIC_FAMILY_X100 == mdev->family) { - dev_err(&mdev->pdev->dev, - "mic_map failed dma_addr 0x%llx size 0x%lx\n", - dma_addr, size); - return mic_addr; - } else { - return mic_addr + mic_smpt_offset(mdev, dma_addr); - } -} - -/** - * mic_unmap - Unmaps a MIC physical address. - * - * @mdev: pointer to mic_device instance. - * @mic_addr: MIC physical address. - * @size: Size of the region to be unmapped. - * - * This API unmaps the mappings created by mic_map(..). - * - * returns None. - */ -void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) -{ - struct mic_smpt_info *smpt_info = mdev->smpt; - s64 *ref; - int num_smpt; - int spt; - int i; - unsigned long flags; - - if (!size) - return; - - if (!mic_is_system_addr(mdev, mic_addr)) { - dev_err(&mdev->pdev->dev, - "invalid address: 0x%llx\n", mic_addr); - return; - } - - spt = mic_sys_addr_to_smpt(mdev, mic_addr); - ref = kmalloc_array(mdev->smpt->info.num_reg, sizeof(s64), GFP_ATOMIC); - if (!ref) - return; - - /* Get number of smpt entries to be mapped, ref count array */ - num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL); - - spin_lock_irqsave(&smpt_info->smpt_lock, flags); - smpt_info->unmap_count++; - smpt_info->ref_count -= (s64)size; - - for (i = spt; i < spt + num_smpt; i++) { - smpt_info->entry[i].ref_count -= ref[i - spt]; - if (smpt_info->entry[i].ref_count < 0) - dev_warn(&mdev->pdev->dev, - "ref count for entry %d is negative\n", i); - } - spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); - kfree(ref); -} - -/** - * mic_map_single - Maps a virtual address to a MIC physical address. - * - * @mdev: pointer to mic_device instance. - * @va: Kernel direct mapped virtual address. - * @size: Size of the region to be mapped. - * - * This API calls pci_map_single(..) for the direct mapped virtual address - * and then converts the DMA address provided to a DMA address understood - * by MIC. Caller should check for errors by calling mic_map_error(..). - * - * returns DMA address as required by MIC. - */ -dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size) -{ - dma_addr_t mic_addr = 0; - struct pci_dev *pdev = mdev->pdev; - dma_addr_t dma_addr = - pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL); - - if (!pci_dma_mapping_error(pdev, dma_addr)) { - mic_addr = mic_map(mdev, dma_addr, size); - if (!mic_addr) { - dev_err(&mdev->pdev->dev, - "mic_map failed dma_addr 0x%llx size 0x%lx\n", - dma_addr, size); - pci_unmap_single(pdev, dma_addr, - size, PCI_DMA_BIDIRECTIONAL); - } - } - return mic_addr; -} - -/** - * mic_unmap_single - Unmaps a MIC physical address. - * - * @mdev: pointer to mic_device instance. - * @mic_addr: MIC physical address. - * @size: Size of the region to be unmapped. - * - * This API unmaps the mappings created by mic_map_single(..). - * - * returns None. - */ -void -mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) -{ - struct pci_dev *pdev = mdev->pdev; - dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr); - mic_unmap(mdev, mic_addr, size); - pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); -} - -/** - * mic_smpt_init - Initialize MIC System Memory Page Tables. - * - * @mdev: pointer to mic_device instance. - * - * returns 0 for success and -errno for error. - */ -int mic_smpt_init(struct mic_device *mdev) -{ - int i, err = 0; - dma_addr_t dma_addr; - struct mic_smpt_info *smpt_info; - - mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL); - if (!mdev->smpt) - return -ENOMEM; - - smpt_info = mdev->smpt; - mdev->smpt_ops->init(mdev); - smpt_info->entry = kmalloc_array(smpt_info->info.num_reg, - sizeof(*smpt_info->entry), GFP_KERNEL); - if (!smpt_info->entry) { - err = -ENOMEM; - goto free_smpt; - } - spin_lock_init(&smpt_info->smpt_lock); - for (i = 0; i < smpt_info->info.num_reg; i++) { - dma_addr = i * smpt_info->info.page_size; - smpt_info->entry[i].dma_addr = dma_addr; - smpt_info->entry[i].ref_count = 0; - mdev->smpt_ops->set(mdev, dma_addr, i); - } - smpt_info->ref_count = 0; - smpt_info->map_count = 0; - smpt_info->unmap_count = 0; - return 0; -free_smpt: - kfree(smpt_info); - return err; -} - -/** - * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables. - * - * @mdev: pointer to mic_device instance. - * - * returns None. - */ -void mic_smpt_uninit(struct mic_device *mdev) -{ - struct mic_smpt_info *smpt_info = mdev->smpt; - int i; - - dev_dbg(&mdev->pdev->dev, - "nodeid %d SMPT ref count %lld map %lld unmap %lld\n", - mdev->id, smpt_info->ref_count, - smpt_info->map_count, smpt_info->unmap_count); - - for (i = 0; i < smpt_info->info.num_reg; i++) { - dev_dbg(&mdev->pdev->dev, - "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n", - i, smpt_info->entry[i].dma_addr, - smpt_info->entry[i].ref_count); - if (smpt_info->entry[i].ref_count) - dev_warn(&mdev->pdev->dev, - "ref count for entry %d is not zero\n", i); - } - kfree(smpt_info->entry); - kfree(smpt_info); -} - -/** - * mic_smpt_restore - Restore MIC System Memory Page Tables. - * - * @mdev: pointer to mic_device instance. - * - * Restore the SMPT registers to values previously stored in the - * SW data structures. Some MIC steppings lose register state - * across resets and this API should be called for performing - * a restore operation if required. - * - * returns None. - */ -void mic_smpt_restore(struct mic_device *mdev) -{ - int i; - dma_addr_t dma_addr; - - for (i = 0; i < mdev->smpt->info.num_reg; i++) { - dma_addr = mdev->smpt->entry[i].dma_addr; - mdev->smpt_ops->set(mdev, dma_addr, i); - } -} diff --git a/drivers/misc/mic/host/mic_smpt.h b/drivers/misc/mic/host/mic_smpt.h deleted file mode 100644 index 3b1ec14a9d81..000000000000 --- a/drivers/misc/mic/host/mic_smpt.h +++ /dev/null @@ -1,87 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#ifndef MIC_SMPT_H -#define MIC_SMPT_H -/** - * struct mic_smpt_ops - MIC HW specific SMPT operations. - * @init: Initialize hardware specific SMPT information in mic_smpt_hw_info. - * @set: Set the value for a particular SMPT entry. - */ -struct mic_smpt_ops { - void (*init)(struct mic_device *mdev); - void (*set)(struct mic_device *mdev, dma_addr_t dma_addr, u8 index); -}; - -/** - * struct mic_smpt - MIC SMPT entry information. - * @dma_addr: Base DMA address for this SMPT entry. - * @ref_count: Number of active mappings for this SMPT entry in bytes. - */ -struct mic_smpt { - dma_addr_t dma_addr; - s64 ref_count; -}; - -/** - * struct mic_smpt_hw_info - MIC SMPT hardware specific information. - * @num_reg: Number of SMPT registers. - * @page_shift: System memory page shift. - * @page_size: System memory page size. - * @base: System address base. - */ -struct mic_smpt_hw_info { - u8 num_reg; - u8 page_shift; - u64 page_size; - u64 base; -}; - -/** - * struct mic_smpt_info - MIC SMPT information. - * @entry: Array of SMPT entries. - * @smpt_lock: Spin lock protecting access to SMPT data structures. - * @info: Hardware specific SMPT information. - * @ref_count: Number of active SMPT mappings (for debug). - * @map_count: Number of SMPT mappings created (for debug). - * @unmap_count: Number of SMPT mappings destroyed (for debug). - */ -struct mic_smpt_info { - struct mic_smpt *entry; - spinlock_t smpt_lock; - struct mic_smpt_hw_info info; - s64 ref_count; - s64 map_count; - s64 unmap_count; -}; - -dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size); -void mic_unmap_single(struct mic_device *mdev, - dma_addr_t mic_addr, size_t size); -dma_addr_t mic_map(struct mic_device *mdev, - dma_addr_t dma_addr, size_t size); -void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size); -dma_addr_t mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr); - -/** - * mic_map_error - Check a MIC address for errors. - * - * @mdev: pointer to mic_device instance. - * - * returns Whether there was an error during mic_map..(..) APIs. - */ -static inline bool mic_map_error(dma_addr_t mic_addr) -{ - return !mic_addr; -} - -int mic_smpt_init(struct mic_device *mdev); -void mic_smpt_uninit(struct mic_device *mdev); -void mic_smpt_restore(struct mic_device *mdev); - -#endif diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c deleted file mode 100644 index f5536c1ad607..000000000000 --- a/drivers/misc/mic/host/mic_x100.c +++ /dev/null @@ -1,585 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#include -#include -#include -#include -#include - -#include "../common/mic_dev.h" -#include "mic_device.h" -#include "mic_x100.h" -#include "mic_smpt.h" - -static const u16 mic_x100_intr_init[] = { - MIC_X100_DOORBELL_IDX_START, - MIC_X100_DMA_IDX_START, - MIC_X100_ERR_IDX_START, - MIC_X100_NUM_DOORBELL, - MIC_X100_NUM_DMA, - MIC_X100_NUM_ERR, -}; - -/** - * mic_x100_write_spad - write to the scratchpad register - * @mdev: pointer to mic_device instance - * @idx: index to the scratchpad register, 0 based - * @val: the data value to put into the register - * - * This function allows writing of a 32bit value to the indexed scratchpad - * register. - * - * RETURNS: none. - */ -static void -mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) -{ - dev_dbg(&mdev->pdev->dev, "Writing 0x%x to scratch pad index %d\n", - val, idx); - mic_mmio_write(&mdev->mmio, val, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SPAD0 + idx * 4); -} - -/** - * mic_x100_read_spad - read from the scratchpad register - * @mdev: pointer to mic_device instance - * @idx: index to scratchpad register, 0 based - * - * This function allows reading of the 32bit scratchpad register. - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static u32 -mic_x100_read_spad(struct mic_device *mdev, unsigned int idx) -{ - u32 val = mic_mmio_read(&mdev->mmio, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SPAD0 + idx * 4); - - dev_dbg(&mdev->pdev->dev, - "Reading 0x%x from scratch pad index %d\n", val, idx); - return val; -} - -/** - * mic_x100_enable_interrupts - Enable interrupts. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_enable_interrupts(struct mic_device *mdev) -{ - u32 reg; - struct mic_mw *mw = &mdev->mmio; - u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; - u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; - - reg = mic_mmio_read(mw, sice0); - reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff); - mic_mmio_write(mw, reg, sice0); - - /* - * Enable auto-clear when enabling interrupts. Applicable only for - * MSI-x. Legacy and MSI mode cannot have auto-clear enabled. - */ - if (mdev->irq_info.num_vectors > 1) { - reg = mic_mmio_read(mw, siac0); - reg |= MIC_X100_SBOX_DBR_BITS(0xf) | - MIC_X100_SBOX_DMA_BITS(0xff); - mic_mmio_write(mw, reg, siac0); - } -} - -/** - * mic_x100_disable_interrupts - Disable interrupts. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_disable_interrupts(struct mic_device *mdev) -{ - u32 reg; - struct mic_mw *mw = &mdev->mmio; - u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; - u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; - u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0; - - reg = mic_mmio_read(mw, sice0); - mic_mmio_write(mw, reg, sicc0); - - if (mdev->irq_info.num_vectors > 1) { - reg = mic_mmio_read(mw, siac0); - reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) | - MIC_X100_SBOX_DMA_BITS(0xff)); - mic_mmio_write(mw, reg, siac0); - } -} - -/** - * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. - * @mdev: pointer to mic_device instance - * @doorbell: doorbell number - */ -static void mic_x100_send_sbox_intr(struct mic_device *mdev, - int doorbell) -{ - struct mic_mw *mw = &mdev->mmio; - u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; - u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + - apic_icr_offset); - - /* for MIC we need to make sure we "hit" the send_icr bit (13) */ - apicicr_low = (apicicr_low | (1 << 13)); - - /* Ensure that the interrupt is ordered w.r.t. previous stores. */ - wmb(); - mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); -} - -/** - * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC. - * @mdev: pointer to mic_device instance - * @doorbell: doorbell number - */ -static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, - int doorbell) -{ - int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); - /* Ensure that the interrupt is ordered w.r.t. previous stores. */ - wmb(); - mic_mmio_write(&mdev->mmio, 0, - MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); -} - -/** - * __mic_x100_send_intr - Send interrupt to MIC. - * @mdev: pointer to mic_device instance - * @doorbell: doorbell number. - */ -static void mic_x100_send_intr(struct mic_device *mdev, int doorbell) -{ - int rdmasr_db; - if (doorbell < MIC_X100_NUM_SBOX_IRQ) { - mic_x100_send_sbox_intr(mdev, doorbell); - } else { - rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ; - mic_x100_send_rdmasr_intr(mdev, rdmasr_db); - } -} - -/** - * mic_x100_ack_interrupt - Read the interrupt sources register and - * clear it. This function will be called in the MSI/INTx case. - * @mdev: Pointer to mic_device instance. - * - * Returns: bitmask of interrupt sources triggered. - */ -static u32 mic_x100_ack_interrupt(struct mic_device *mdev) -{ - u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; - u32 reg = mic_mmio_read(&mdev->mmio, sicr0); - mic_mmio_write(&mdev->mmio, reg, sicr0); - return reg; -} - -/** - * mic_x100_intr_workarounds - These hardware specific workarounds are - * to be invoked everytime an interrupt is handled. - * @mdev: Pointer to mic_device instance. - * - * Returns: none - */ -static void mic_x100_intr_workarounds(struct mic_device *mdev) -{ - struct mic_mw *mw = &mdev->mmio; - - /* Clear pending bit array. */ - if (MIC_A0_STEP == mdev->stepping) - mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_MSIXPBACR); - - if (mdev->stepping >= MIC_B0_STEP) - mdev->intr_ops->enable_interrupts(mdev); -} - -/** - * mic_x100_hw_intr_init - Initialize h/w specific interrupt - * information. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_hw_intr_init(struct mic_device *mdev) -{ - mdev->intr_info = (struct mic_intr_info *)mic_x100_intr_init; -} - -/** - * mic_x100_read_msi_to_src_map - read from the MSI mapping registers - * @mdev: pointer to mic_device instance - * @idx: index to the mapping register, 0 based - * - * This function allows reading of the 32bit MSI mapping register. - * - * RETURNS: The value in the register. - */ -static u32 -mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx) -{ - return mic_mmio_read(&mdev->mmio, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_MXAR0 + idx * 4); -} - -/** - * mic_x100_program_msi_to_src_map - program the MSI mapping registers - * @mdev: pointer to mic_device instance - * @idx: index to the mapping register, 0 based - * @offset: The bit offset in the register that needs to be updated. - * @set: boolean specifying if the bit in the specified offset needs - * to be set or cleared. - * - * RETURNS: None. - */ -static void -mic_x100_program_msi_to_src_map(struct mic_device *mdev, - int idx, int offset, bool set) -{ - unsigned long reg; - struct mic_mw *mw = &mdev->mmio; - u32 mxar = MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_MXAR0 + idx * 4; - - reg = mic_mmio_read(mw, mxar); - if (set) - __set_bit(offset, ®); - else - __clear_bit(offset, ®); - mic_mmio_write(mw, reg, mxar); -} - -/* - * mic_x100_reset_fw_ready - Reset Firmware ready status field. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_reset_fw_ready(struct mic_device *mdev) -{ - mdev->ops->write_spad(mdev, MIC_X100_DOWNLOAD_INFO, 0); -} - -/* - * mic_x100_is_fw_ready - Check if firmware is ready. - * @mdev: pointer to mic_device instance - */ -static bool mic_x100_is_fw_ready(struct mic_device *mdev) -{ - u32 scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); - return MIC_X100_SPAD2_DOWNLOAD_STATUS(scratch2) ? true : false; -} - -/** - * mic_x100_get_apic_id - Get bootstrap APIC ID. - * @mdev: pointer to mic_device instance - */ -static u32 mic_x100_get_apic_id(struct mic_device *mdev) -{ - u32 scratch2 = 0; - - scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); - return MIC_X100_SPAD2_APIC_ID(scratch2); -} - -/** - * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_send_firmware_intr(struct mic_device *mdev) -{ - u32 apicicr_low; - u64 apic_icr_offset = MIC_X100_SBOX_APICICR7; - int vector = MIC_X100_BSP_INTERRUPT_VECTOR; - struct mic_mw *mw = &mdev->mmio; - - /* - * For MIC we need to make sure we "hit" - * the send_icr bit (13). - */ - apicicr_low = (vector | (1 << 13)); - - mic_mmio_write(mw, mic_x100_get_apic_id(mdev), - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); - - /* Ensure that the interrupt is ordered w.r.t. previous stores. */ - wmb(); - mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); -} - -/** - * mic_x100_hw_reset - Reset the MIC device. - * @mdev: pointer to mic_device instance - */ -static void mic_x100_hw_reset(struct mic_device *mdev) -{ - u32 reset_reg; - u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR; - struct mic_mw *mw = &mdev->mmio; - - /* Ensure that the reset is ordered w.r.t. previous loads and stores */ - mb(); - /* Trigger reset */ - reset_reg = mic_mmio_read(mw, rgcr); - reset_reg |= 0x1; - mic_mmio_write(mw, reset_reg, rgcr); - /* - * It seems we really want to delay at least 1 second - * after touching reset to prevent a lot of problems. - */ - msleep(1000); -} - -/** - * mic_x100_load_command_line - Load command line to MIC. - * @mdev: pointer to mic_device instance - * @fw: the firmware image - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int -mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw) -{ - u32 len = 0; - u32 boot_mem; - char *buf; - void __iomem *cmd_line_va = mdev->aper.va + mdev->bootaddr + fw->size; -#define CMDLINE_SIZE 2048 - - boot_mem = mdev->aper.len >> 20; - buf = kzalloc(CMDLINE_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - len += scnprintf(buf, CMDLINE_SIZE - len, - " mem=%dM", boot_mem); - if (mdev->cosm_dev->cmdline) - scnprintf(buf + len, CMDLINE_SIZE - len, " %s", - mdev->cosm_dev->cmdline); - memcpy_toio(cmd_line_va, buf, strlen(buf) + 1); - kfree(buf); - return 0; -} - -/** - * mic_x100_load_ramdisk - Load ramdisk to MIC. - * @mdev: pointer to mic_device instance - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int -mic_x100_load_ramdisk(struct mic_device *mdev) -{ - const struct firmware *fw; - int rc; - struct boot_params __iomem *bp = mdev->aper.va + mdev->bootaddr; - - rc = request_firmware(&fw, mdev->cosm_dev->ramdisk, &mdev->pdev->dev); - if (rc < 0) { - dev_err(&mdev->pdev->dev, - "ramdisk request_firmware failed: %d %s\n", - rc, mdev->cosm_dev->ramdisk); - goto error; - } - /* - * Typically the bootaddr for card OS is 64M - * so copy over the ramdisk @ 128M. - */ - memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), fw->data, fw->size); - iowrite32(mdev->bootaddr << 1, &bp->hdr.ramdisk_image); - iowrite32(fw->size, &bp->hdr.ramdisk_size); - release_firmware(fw); -error: - return rc; -} - -/** - * mic_x100_get_boot_addr - Get MIC boot address. - * @mdev: pointer to mic_device instance - * - * This function is called during firmware load to determine - * the address at which the OS should be downloaded in card - * memory i.e. GDDR. - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int -mic_x100_get_boot_addr(struct mic_device *mdev) -{ - u32 scratch2, boot_addr; - int rc = 0; - - scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); - boot_addr = MIC_X100_SPAD2_DOWNLOAD_ADDR(scratch2); - dev_dbg(&mdev->pdev->dev, "%s %d boot_addr 0x%x\n", - __func__, __LINE__, boot_addr); - if (boot_addr > (1 << 31)) { - dev_err(&mdev->pdev->dev, - "incorrect bootaddr 0x%x\n", - boot_addr); - rc = -EINVAL; - goto error; - } - mdev->bootaddr = boot_addr; -error: - return rc; -} - -/** - * mic_x100_load_firmware - Load firmware to MIC. - * @mdev: pointer to mic_device instance - * @buf: buffer containing boot string including firmware/ramdisk path. - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -static int -mic_x100_load_firmware(struct mic_device *mdev, const char *buf) -{ - int rc; - const struct firmware *fw; - - rc = mic_x100_get_boot_addr(mdev); - if (rc) - return rc; - /* load OS */ - rc = request_firmware(&fw, mdev->cosm_dev->firmware, &mdev->pdev->dev); - if (rc < 0) { - dev_err(&mdev->pdev->dev, - "ramdisk request_firmware failed: %d %s\n", - rc, mdev->cosm_dev->firmware); - return rc; - } - if (mdev->bootaddr > mdev->aper.len - fw->size) { - rc = -EINVAL; - dev_err(&mdev->pdev->dev, "%s %d rc %d bootaddr 0x%x\n", - __func__, __LINE__, rc, mdev->bootaddr); - goto error; - } - memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); - mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); - if (!strcmp(mdev->cosm_dev->bootmode, "flash")) { - rc = -EINVAL; - dev_err(&mdev->pdev->dev, "%s %d rc %d\n", - __func__, __LINE__, rc); - goto error; - } - /* load command line */ - rc = mic_x100_load_command_line(mdev, fw); - if (rc) { - dev_err(&mdev->pdev->dev, "%s %d rc %d\n", - __func__, __LINE__, rc); - goto error; - } - release_firmware(fw); - /* load ramdisk */ - if (mdev->cosm_dev->ramdisk) - rc = mic_x100_load_ramdisk(mdev); - - return rc; - -error: - release_firmware(fw); - return rc; -} - -/** - * mic_x100_get_postcode - Get postcode status from firmware. - * @mdev: pointer to mic_device instance - * - * RETURNS: postcode. - */ -static u32 mic_x100_get_postcode(struct mic_device *mdev) -{ - return mic_mmio_read(&mdev->mmio, MIC_X100_POSTCODE); -} - -/** - * mic_x100_smpt_set - Update an SMPT entry with a DMA address. - * @mdev: pointer to mic_device instance - * @dma_addr: DMA address to use - * @index: entry to write to - * - * RETURNS: none. - */ -static void -mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index) -{ -#define SNOOP_ON (0 << 0) -#define SNOOP_OFF (1 << 0) -/* - * Sbox Smpt Reg Bits: - * Bits 31:2 Host address - * Bits 1 RSVD - * Bits 0 No snoop - */ -#define BUILD_SMPT(NO_SNOOP, HOST_ADDR) \ - (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01)) - - uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON, - dma_addr >> mdev->smpt->info.page_shift); - mic_mmio_write(&mdev->mmio, smpt_reg_val, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SMPT00 + (4 * index)); -} - -/** - * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields. - * @mdev: pointer to mic_device instance - * - * RETURNS: none. - */ -static void mic_x100_smpt_hw_init(struct mic_device *mdev) -{ - struct mic_smpt_hw_info *info = &mdev->smpt->info; - - info->num_reg = 32; - info->page_shift = 34; - info->page_size = (1ULL << info->page_shift); - info->base = 0x8000000000ULL; -} - -struct mic_smpt_ops mic_x100_smpt_ops = { - .init = mic_x100_smpt_hw_init, - .set = mic_x100_smpt_set, -}; - -static bool mic_x100_dma_filter(struct dma_chan *chan, void *param) -{ - if (chan->device->dev->parent == (struct device *)param) - return true; - return false; -} - -struct mic_hw_ops mic_x100_ops = { - .aper_bar = MIC_X100_APER_BAR, - .mmio_bar = MIC_X100_MMIO_BAR, - .read_spad = mic_x100_read_spad, - .write_spad = mic_x100_write_spad, - .send_intr = mic_x100_send_intr, - .ack_interrupt = mic_x100_ack_interrupt, - .intr_workarounds = mic_x100_intr_workarounds, - .reset = mic_x100_hw_reset, - .reset_fw_ready = mic_x100_reset_fw_ready, - .is_fw_ready = mic_x100_is_fw_ready, - .send_firmware_intr = mic_x100_send_firmware_intr, - .load_mic_fw = mic_x100_load_firmware, - .get_postcode = mic_x100_get_postcode, - .dma_filter = mic_x100_dma_filter, -}; - -struct mic_hw_intr_ops mic_x100_intr_ops = { - .intr_init = mic_x100_hw_intr_init, - .enable_interrupts = mic_x100_enable_interrupts, - .disable_interrupts = mic_x100_disable_interrupts, - .program_msi_to_src_map = mic_x100_program_msi_to_src_map, - .read_msi_to_src_map = mic_x100_read_msi_to_src_map, -}; diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h deleted file mode 100644 index aebcaed6fa72..000000000000 --- a/drivers/misc/mic/host/mic_x100.h +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC Host driver. - */ -#ifndef _MIC_X100_HW_H_ -#define _MIC_X100_HW_H_ - -#define MIC_X100_PCI_DEVICE_2250 0x2250 -#define MIC_X100_PCI_DEVICE_2251 0x2251 -#define MIC_X100_PCI_DEVICE_2252 0x2252 -#define MIC_X100_PCI_DEVICE_2253 0x2253 -#define MIC_X100_PCI_DEVICE_2254 0x2254 -#define MIC_X100_PCI_DEVICE_2255 0x2255 -#define MIC_X100_PCI_DEVICE_2256 0x2256 -#define MIC_X100_PCI_DEVICE_2257 0x2257 -#define MIC_X100_PCI_DEVICE_2258 0x2258 -#define MIC_X100_PCI_DEVICE_2259 0x2259 -#define MIC_X100_PCI_DEVICE_225a 0x225a -#define MIC_X100_PCI_DEVICE_225b 0x225b -#define MIC_X100_PCI_DEVICE_225c 0x225c -#define MIC_X100_PCI_DEVICE_225d 0x225d -#define MIC_X100_PCI_DEVICE_225e 0x225e - -#define MIC_X100_APER_BAR 0 -#define MIC_X100_MMIO_BAR 4 - -#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000 -#define MIC_X100_SBOX_SPAD0 0x0000AB20 -#define MIC_X100_SBOX_SICR0_DBR(x) ((x) & 0xf) -#define MIC_X100_SBOX_SICR0_DMA(x) (((x) >> 8) & 0xff) -#define MIC_X100_SBOX_SICE0_DBR(x) ((x) & 0xf) -#define MIC_X100_SBOX_DBR_BITS(x) ((x) & 0xf) -#define MIC_X100_SBOX_SICE0_DMA(x) (((x) >> 8) & 0xff) -#define MIC_X100_SBOX_DMA_BITS(x) (((x) & 0xff) << 8) - -#define MIC_X100_SBOX_APICICR0 0x0000A9D0 -#define MIC_X100_SBOX_SICR0 0x00009004 -#define MIC_X100_SBOX_SICE0 0x0000900C -#define MIC_X100_SBOX_SICC0 0x00009010 -#define MIC_X100_SBOX_SIAC0 0x00009014 -#define MIC_X100_SBOX_MSIXPBACR 0x00009084 -#define MIC_X100_SBOX_MXAR0 0x00009044 -#define MIC_X100_SBOX_SMPT00 0x00003100 -#define MIC_X100_SBOX_RDMASR0 0x0000B180 - -#define MIC_X100_DOORBELL_IDX_START 0 -#define MIC_X100_NUM_DOORBELL 4 -#define MIC_X100_DMA_IDX_START 8 -#define MIC_X100_NUM_DMA 8 -#define MIC_X100_ERR_IDX_START 30 -#define MIC_X100_NUM_ERR 1 - -#define MIC_X100_NUM_SBOX_IRQ 8 -#define MIC_X100_NUM_RDMASR_IRQ 8 -#define MIC_X100_RDMASR_IRQ_BASE 17 -#define MIC_X100_SPAD2_DOWNLOAD_STATUS(x) ((x) & 0x1) -#define MIC_X100_SPAD2_APIC_ID(x) (((x) >> 1) & 0x1ff) -#define MIC_X100_SPAD2_DOWNLOAD_ADDR(x) ((x) & 0xfffff000) -#define MIC_X100_SBOX_APICICR7 0x0000AA08 -#define MIC_X100_SBOX_RGCR 0x00004010 -#define MIC_X100_SBOX_SDBIC0 0x0000CC90 -#define MIC_X100_DOWNLOAD_INFO 2 -#define MIC_X100_FW_SIZE 5 -#define MIC_X100_POSTCODE 0x242c - -/* Host->Card(bootstrap) Interrupt Vector */ -#define MIC_X100_BSP_INTERRUPT_VECTOR 229 - -extern struct mic_hw_ops mic_x100_ops; -extern struct mic_smpt_ops mic_x100_smpt_ops; -extern struct mic_hw_intr_ops mic_x100_intr_ops; - -#endif diff --git a/drivers/misc/mic/scif/Makefile b/drivers/misc/mic/scif/Makefile deleted file mode 100644 index ff372555d118..000000000000 --- a/drivers/misc/mic/scif/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile - SCIF driver. -# Copyright(c) 2014, Intel Corporation. -# -obj-$(CONFIG_SCIF) += scif.o -scif-objs := scif_main.o -scif-objs += scif_peer_bus.o -scif-objs += scif_ports.o -scif-objs += scif_debugfs.o -scif-objs += scif_fd.o -scif-objs += scif_api.o -scif-objs += scif_epd.o -scif-objs += scif_rb.o -scif-objs += scif_nodeqp.o -scif-objs += scif_nm.o -scif-objs += scif_dma.o -scif-objs += scif_fence.o -scif-objs += scif_mmap.o -scif-objs += scif_rma.o -scif-objs += scif_rma_list.o diff --git a/drivers/misc/mic/scif/scif_api.c b/drivers/misc/mic/scif/scif_api.c deleted file mode 100644 index 304d6c833712..000000000000 --- a/drivers/misc/mic/scif/scif_api.c +++ /dev/null @@ -1,1485 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include -#include "scif_main.h" -#include "scif_map.h" - -static const char * const scif_ep_states[] = { - "Unbound", - "Bound", - "Listening", - "Connected", - "Connecting", - "Mapping", - "Closing", - "Close Listening", - "Disconnected", - "Zombie"}; - -enum conn_async_state { - ASYNC_CONN_IDLE = 1, /* ep setup for async connect */ - ASYNC_CONN_INPROGRESS, /* async connect in progress */ - ASYNC_CONN_FLUSH_WORK /* async work flush in progress */ -}; - -/* - * File operations for anonymous inode file associated with a SCIF endpoint, - * used in kernel mode SCIF poll. Kernel mode SCIF poll calls portions of the - * poll API in the kernel and these take in a struct file *. Since a struct - * file is not available to kernel mode SCIF, it uses an anonymous file for - * this purpose. - */ -const struct file_operations scif_anon_fops = { - .owner = THIS_MODULE, -}; - -scif_epd_t scif_open(void) -{ - struct scif_endpt *ep; - int err; - - might_sleep(); - ep = kzalloc(sizeof(*ep), GFP_KERNEL); - if (!ep) - goto err_ep_alloc; - - ep->qp_info.qp = kzalloc(sizeof(*ep->qp_info.qp), GFP_KERNEL); - if (!ep->qp_info.qp) - goto err_qp_alloc; - - err = scif_anon_inode_getfile(ep); - if (err) - goto err_anon_inode; - - spin_lock_init(&ep->lock); - mutex_init(&ep->sendlock); - mutex_init(&ep->recvlock); - - scif_rma_ep_init(ep); - ep->state = SCIFEP_UNBOUND; - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI open: ep %p success\n", ep); - return ep; - -err_anon_inode: - kfree(ep->qp_info.qp); -err_qp_alloc: - kfree(ep); -err_ep_alloc: - return NULL; -} -EXPORT_SYMBOL_GPL(scif_open); - -/* - * scif_disconnect_ep - Disconnects the endpoint if found - * @epd: The end point returned from scif_open() - */ -static struct scif_endpt *scif_disconnect_ep(struct scif_endpt *ep) -{ - struct scifmsg msg; - struct scif_endpt *fep = NULL; - struct scif_endpt *tmpep; - struct list_head *pos, *tmpq; - int err; - - /* - * Wake up any threads blocked in send()/recv() before closing - * out the connection. Grabbing and releasing the send/recv lock - * will ensure that any blocked senders/receivers have exited for - * Ring 0 endpoints. It is a Ring 0 bug to call send/recv after - * close. Ring 3 endpoints are not affected since close will not - * be called while there are IOCTLs executing. - */ - wake_up_interruptible(&ep->sendwq); - wake_up_interruptible(&ep->recvwq); - mutex_lock(&ep->sendlock); - mutex_unlock(&ep->sendlock); - mutex_lock(&ep->recvlock); - mutex_unlock(&ep->recvlock); - - /* Remove from the connected list */ - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.connected) { - tmpep = list_entry(pos, struct scif_endpt, list); - if (tmpep == ep) { - list_del(pos); - fep = tmpep; - spin_lock(&ep->lock); - break; - } - } - - if (!fep) { - /* - * The other side has completed the disconnect before - * the end point can be removed from the list. Therefore - * the ep lock is not locked, traverse the disconnected - * list to find the endpoint and release the conn lock. - */ - list_for_each_safe(pos, tmpq, &scif_info.disconnected) { - tmpep = list_entry(pos, struct scif_endpt, list); - if (tmpep == ep) { - list_del(pos); - break; - } - } - mutex_unlock(&scif_info.connlock); - return NULL; - } - - init_completion(&ep->discon); - msg.uop = SCIF_DISCNCT; - msg.src = ep->port; - msg.dst = ep->peer; - msg.payload[0] = (u64)ep; - msg.payload[1] = ep->remote_ep; - - err = scif_nodeqp_send(ep->remote_dev, &msg); - spin_unlock(&ep->lock); - mutex_unlock(&scif_info.connlock); - - if (!err) - /* Wait for the remote node to respond with SCIF_DISCNT_ACK */ - wait_for_completion_timeout(&ep->discon, - SCIF_NODE_ALIVE_TIMEOUT); - return ep; -} - -int scif_close(scif_epd_t epd) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_endpt *tmpep; - struct list_head *pos, *tmpq; - enum scif_epd_state oldstate; - bool flush_conn; - - dev_dbg(scif_info.mdev.this_device, "SCIFAPI close: ep %p %s\n", - ep, scif_ep_states[ep->state]); - might_sleep(); - spin_lock(&ep->lock); - flush_conn = (ep->conn_async_state == ASYNC_CONN_INPROGRESS); - spin_unlock(&ep->lock); - - if (flush_conn) - flush_work(&scif_info.conn_work); - - spin_lock(&ep->lock); - oldstate = ep->state; - - ep->state = SCIFEP_CLOSING; - - switch (oldstate) { - case SCIFEP_ZOMBIE: - dev_err(scif_info.mdev.this_device, - "SCIFAPI close: zombie state unexpected\n"); - fallthrough; - case SCIFEP_DISCONNECTED: - spin_unlock(&ep->lock); - scif_unregister_all_windows(epd); - /* Remove from the disconnected list */ - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.disconnected) { - tmpep = list_entry(pos, struct scif_endpt, list); - if (tmpep == ep) { - list_del(pos); - break; - } - } - mutex_unlock(&scif_info.connlock); - break; - case SCIFEP_UNBOUND: - case SCIFEP_BOUND: - case SCIFEP_CONNECTING: - spin_unlock(&ep->lock); - break; - case SCIFEP_MAPPING: - case SCIFEP_CONNECTED: - case SCIFEP_CLOSING: - { - spin_unlock(&ep->lock); - scif_unregister_all_windows(epd); - scif_disconnect_ep(ep); - break; - } - case SCIFEP_LISTENING: - case SCIFEP_CLLISTEN: - { - struct scif_conreq *conreq; - struct scifmsg msg; - struct scif_endpt *aep; - - spin_unlock(&ep->lock); - mutex_lock(&scif_info.eplock); - - /* remove from listen list */ - list_for_each_safe(pos, tmpq, &scif_info.listen) { - tmpep = list_entry(pos, struct scif_endpt, list); - if (tmpep == ep) - list_del(pos); - } - /* Remove any dangling accepts */ - while (ep->acceptcnt) { - aep = list_first_entry(&ep->li_accept, - struct scif_endpt, liacceptlist); - list_del(&aep->liacceptlist); - scif_put_port(aep->port.port); - list_for_each_safe(pos, tmpq, &scif_info.uaccept) { - tmpep = list_entry(pos, struct scif_endpt, - miacceptlist); - if (tmpep == aep) { - list_del(pos); - break; - } - } - mutex_unlock(&scif_info.eplock); - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.connected) { - tmpep = list_entry(pos, - struct scif_endpt, list); - if (tmpep == aep) { - list_del(pos); - break; - } - } - list_for_each_safe(pos, tmpq, &scif_info.disconnected) { - tmpep = list_entry(pos, - struct scif_endpt, list); - if (tmpep == aep) { - list_del(pos); - break; - } - } - mutex_unlock(&scif_info.connlock); - scif_teardown_ep(aep); - mutex_lock(&scif_info.eplock); - scif_add_epd_to_zombie_list(aep, SCIF_EPLOCK_HELD); - ep->acceptcnt--; - } - - spin_lock(&ep->lock); - mutex_unlock(&scif_info.eplock); - - /* Remove and reject any pending connection requests. */ - while (ep->conreqcnt) { - conreq = list_first_entry(&ep->conlist, - struct scif_conreq, list); - list_del(&conreq->list); - - msg.uop = SCIF_CNCT_REJ; - msg.dst.node = conreq->msg.src.node; - msg.dst.port = conreq->msg.src.port; - msg.payload[0] = conreq->msg.payload[0]; - msg.payload[1] = conreq->msg.payload[1]; - /* - * No Error Handling on purpose for scif_nodeqp_send(). - * If the remote node is lost we still want free the - * connection requests on the self node. - */ - scif_nodeqp_send(&scif_dev[conreq->msg.src.node], - &msg); - ep->conreqcnt--; - kfree(conreq); - } - - spin_unlock(&ep->lock); - /* If a kSCIF accept is waiting wake it up */ - wake_up_interruptible(&ep->conwq); - break; - } - } - scif_put_port(ep->port.port); - scif_anon_inode_fput(ep); - scif_teardown_ep(ep); - scif_add_epd_to_zombie_list(ep, !SCIF_EPLOCK_HELD); - return 0; -} -EXPORT_SYMBOL_GPL(scif_close); - -/** - * scif_flush() - Wakes up any blocking accepts. The endpoint will no longer - * accept new connections. - * @epd: The end point returned from scif_open() - */ -int __scif_flush(scif_epd_t epd) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - - switch (ep->state) { - case SCIFEP_LISTENING: - { - ep->state = SCIFEP_CLLISTEN; - - /* If an accept is waiting wake it up */ - wake_up_interruptible(&ep->conwq); - break; - } - default: - break; - } - return 0; -} - -int scif_bind(scif_epd_t epd, u16 pn) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int ret = 0; - int tmp; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI bind: ep %p %s requested port number %d\n", - ep, scif_ep_states[ep->state], pn); - if (pn) { - /* - * Similar to IETF RFC 1700, SCIF ports below - * SCIF_ADMIN_PORT_END can only be bound by system (or root) - * processes or by processes executed by privileged users. - */ - if (pn < SCIF_ADMIN_PORT_END && !capable(CAP_SYS_ADMIN)) { - ret = -EACCES; - goto scif_bind_admin_exit; - } - } - - spin_lock(&ep->lock); - if (ep->state == SCIFEP_BOUND) { - ret = -EINVAL; - goto scif_bind_exit; - } else if (ep->state != SCIFEP_UNBOUND) { - ret = -EISCONN; - goto scif_bind_exit; - } - - if (pn) { - tmp = scif_rsrv_port(pn); - if (tmp != pn) { - ret = -EINVAL; - goto scif_bind_exit; - } - } else { - ret = scif_get_new_port(); - if (ret < 0) - goto scif_bind_exit; - pn = ret; - } - - ep->state = SCIFEP_BOUND; - ep->port.node = scif_info.nodeid; - ep->port.port = pn; - ep->conn_async_state = ASYNC_CONN_IDLE; - ret = pn; - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI bind: bound to port number %d\n", pn); -scif_bind_exit: - spin_unlock(&ep->lock); -scif_bind_admin_exit: - return ret; -} -EXPORT_SYMBOL_GPL(scif_bind); - -int scif_listen(scif_epd_t epd, int backlog) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI listen: ep %p %s\n", ep, scif_ep_states[ep->state]); - spin_lock(&ep->lock); - switch (ep->state) { - case SCIFEP_ZOMBIE: - case SCIFEP_CLOSING: - case SCIFEP_CLLISTEN: - case SCIFEP_UNBOUND: - case SCIFEP_DISCONNECTED: - spin_unlock(&ep->lock); - return -EINVAL; - case SCIFEP_LISTENING: - case SCIFEP_CONNECTED: - case SCIFEP_CONNECTING: - case SCIFEP_MAPPING: - spin_unlock(&ep->lock); - return -EISCONN; - case SCIFEP_BOUND: - break; - } - - ep->state = SCIFEP_LISTENING; - ep->backlog = backlog; - - ep->conreqcnt = 0; - ep->acceptcnt = 0; - INIT_LIST_HEAD(&ep->conlist); - init_waitqueue_head(&ep->conwq); - INIT_LIST_HEAD(&ep->li_accept); - spin_unlock(&ep->lock); - - /* - * Listen status is complete so delete the qp information not needed - * on a listen before placing on the list of listening ep's - */ - scif_teardown_ep(ep); - ep->qp_info.qp = NULL; - - mutex_lock(&scif_info.eplock); - list_add_tail(&ep->list, &scif_info.listen); - mutex_unlock(&scif_info.eplock); - return 0; -} -EXPORT_SYMBOL_GPL(scif_listen); - -/* - ************************************************************************ - * SCIF connection flow: - * - * 1) A SCIF listening endpoint can call scif_accept(..) to wait for SCIF - * connections via a SCIF_CNCT_REQ message - * 2) A SCIF endpoint can initiate a SCIF connection by calling - * scif_connect(..) which calls scif_setup_qp_connect(..) which - * allocates the local qp for the endpoint ring buffer and then sends - * a SCIF_CNCT_REQ to the remote node and waits for a SCIF_CNCT_GNT or - * a SCIF_CNCT_REJ message - * 3) The peer node handles a SCIF_CNCT_REQ via scif_cnctreq_resp(..) which - * wakes up any threads blocked in step 1 or sends a SCIF_CNCT_REJ - * message otherwise - * 4) A thread blocked waiting for incoming connections allocates its local - * endpoint QP and ring buffer following which it sends a SCIF_CNCT_GNT - * and waits for a SCIF_CNCT_GNT(N)ACK. If the allocation fails then - * the node sends a SCIF_CNCT_REJ message - * 5) Upon receipt of a SCIF_CNCT_GNT or a SCIF_CNCT_REJ message the - * connecting endpoint is woken up as part of handling - * scif_cnctgnt_resp(..) following which it maps the remote endpoints' - * QP, updates its outbound QP and sends a SCIF_CNCT_GNTACK message on - * success or a SCIF_CNCT_GNTNACK message on failure and completes - * the scif_connect(..) API - * 6) Upon receipt of a SCIF_CNCT_GNT(N)ACK the accepting endpoint blocked - * in step 4 is woken up and completes the scif_accept(..) API - * 7) The SCIF connection is now established between the two SCIF endpoints. - */ -static int scif_conn_func(struct scif_endpt *ep) -{ - int err = 0; - struct scifmsg msg; - struct device *spdev; - - err = scif_reserve_dma_chan(ep); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - ep->state = SCIFEP_BOUND; - goto connect_error_simple; - } - /* Initiate the first part of the endpoint QP setup */ - err = scif_setup_qp_connect(ep->qp_info.qp, &ep->qp_info.qp_offset, - SCIF_ENDPT_QP_SIZE, ep->remote_dev); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s err %d qp_offset 0x%llx\n", - __func__, err, ep->qp_info.qp_offset); - ep->state = SCIFEP_BOUND; - goto connect_error_simple; - } - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - goto cleanup_qp; - } - /* Format connect message and send it */ - msg.src = ep->port; - msg.dst = ep->conn_port; - msg.uop = SCIF_CNCT_REQ; - msg.payload[0] = (u64)ep; - msg.payload[1] = ep->qp_info.qp_offset; - err = _scif_nodeqp_send(ep->remote_dev, &msg); - if (err) - goto connect_error_dec; - scif_put_peer_dev(spdev); - /* - * Wait for the remote node to respond with SCIF_CNCT_GNT or - * SCIF_CNCT_REJ message. - */ - err = wait_event_timeout(ep->conwq, ep->state != SCIFEP_CONNECTING, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d timeout\n", __func__, __LINE__); - ep->state = SCIFEP_BOUND; - } - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - goto cleanup_qp; - } - if (ep->state == SCIFEP_MAPPING) { - err = scif_setup_qp_connect_response(ep->remote_dev, - ep->qp_info.qp, - ep->qp_info.gnt_pld); - /* - * If the resource to map the queue are not available then - * we need to tell the other side to terminate the accept - */ - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - msg.uop = SCIF_CNCT_GNTNACK; - msg.payload[0] = ep->remote_ep; - _scif_nodeqp_send(ep->remote_dev, &msg); - ep->state = SCIFEP_BOUND; - goto connect_error_dec; - } - - msg.uop = SCIF_CNCT_GNTACK; - msg.payload[0] = ep->remote_ep; - err = _scif_nodeqp_send(ep->remote_dev, &msg); - if (err) { - ep->state = SCIFEP_BOUND; - goto connect_error_dec; - } - ep->state = SCIFEP_CONNECTED; - mutex_lock(&scif_info.connlock); - list_add_tail(&ep->list, &scif_info.connected); - mutex_unlock(&scif_info.connlock); - dev_dbg(&ep->remote_dev->sdev->dev, - "SCIFAPI connect: ep %p connected\n", ep); - } else if (ep->state == SCIFEP_BOUND) { - dev_dbg(&ep->remote_dev->sdev->dev, - "SCIFAPI connect: ep %p connection refused\n", ep); - err = -ECONNREFUSED; - goto connect_error_dec; - } - scif_put_peer_dev(spdev); - return err; -connect_error_dec: - scif_put_peer_dev(spdev); -cleanup_qp: - scif_cleanup_ep_qp(ep); -connect_error_simple: - return err; -} - -/* - * scif_conn_handler: - * - * Workqueue handler for servicing non-blocking SCIF connect - * - */ -void scif_conn_handler(struct work_struct *work) -{ - struct scif_endpt *ep; - - do { - ep = NULL; - spin_lock(&scif_info.nb_connect_lock); - if (!list_empty(&scif_info.nb_connect_list)) { - ep = list_first_entry(&scif_info.nb_connect_list, - struct scif_endpt, conn_list); - list_del(&ep->conn_list); - } - spin_unlock(&scif_info.nb_connect_lock); - if (ep) { - ep->conn_err = scif_conn_func(ep); - wake_up_interruptible(&ep->conn_pend_wq); - } - } while (ep); -} - -int __scif_connect(scif_epd_t epd, struct scif_port_id *dst, bool non_block) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - struct scif_dev *remote_dev; - struct device *spdev; - - dev_dbg(scif_info.mdev.this_device, "SCIFAPI connect: ep %p %s\n", ep, - scif_ep_states[ep->state]); - - if (!scif_dev || dst->node > scif_info.maxid) - return -ENODEV; - - might_sleep(); - - remote_dev = &scif_dev[dst->node]; - spdev = scif_get_peer_dev(remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - return err; - } - - spin_lock(&ep->lock); - switch (ep->state) { - case SCIFEP_ZOMBIE: - case SCIFEP_CLOSING: - err = -EINVAL; - break; - case SCIFEP_DISCONNECTED: - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) - ep->conn_async_state = ASYNC_CONN_FLUSH_WORK; - else - err = -EINVAL; - break; - case SCIFEP_LISTENING: - case SCIFEP_CLLISTEN: - err = -EOPNOTSUPP; - break; - case SCIFEP_CONNECTING: - case SCIFEP_MAPPING: - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) - err = -EINPROGRESS; - else - err = -EISCONN; - break; - case SCIFEP_CONNECTED: - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) - ep->conn_async_state = ASYNC_CONN_FLUSH_WORK; - else - err = -EISCONN; - break; - case SCIFEP_UNBOUND: - err = scif_get_new_port(); - if (err < 0) - break; - ep->port.port = err; - ep->port.node = scif_info.nodeid; - ep->conn_async_state = ASYNC_CONN_IDLE; - fallthrough; - case SCIFEP_BOUND: - /* - * If a non-blocking connect has been already initiated - * (conn_async_state is either ASYNC_CONN_INPROGRESS or - * ASYNC_CONN_FLUSH_WORK), the end point could end up in - * SCIF_BOUND due an error in the connection process - * (e.g., connection refused) If conn_async_state is - * ASYNC_CONN_INPROGRESS - transition to ASYNC_CONN_FLUSH_WORK - * so that the error status can be collected. If the state is - * already ASYNC_CONN_FLUSH_WORK - then set the error to - * EINPROGRESS since some other thread is waiting to collect - * error status. - */ - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { - ep->conn_async_state = ASYNC_CONN_FLUSH_WORK; - } else if (ep->conn_async_state == ASYNC_CONN_FLUSH_WORK) { - err = -EINPROGRESS; - } else { - ep->conn_port = *dst; - init_waitqueue_head(&ep->sendwq); - init_waitqueue_head(&ep->recvwq); - init_waitqueue_head(&ep->conwq); - ep->conn_async_state = 0; - - if (unlikely(non_block)) - ep->conn_async_state = ASYNC_CONN_INPROGRESS; - } - break; - } - - if (err || ep->conn_async_state == ASYNC_CONN_FLUSH_WORK) - goto connect_simple_unlock1; - - ep->state = SCIFEP_CONNECTING; - ep->remote_dev = &scif_dev[dst->node]; - ep->qp_info.qp->magic = SCIFEP_MAGIC; - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { - init_waitqueue_head(&ep->conn_pend_wq); - spin_lock(&scif_info.nb_connect_lock); - list_add_tail(&ep->conn_list, &scif_info.nb_connect_list); - spin_unlock(&scif_info.nb_connect_lock); - err = -EINPROGRESS; - schedule_work(&scif_info.conn_work); - } -connect_simple_unlock1: - spin_unlock(&ep->lock); - scif_put_peer_dev(spdev); - if (err) { - return err; - } else if (ep->conn_async_state == ASYNC_CONN_FLUSH_WORK) { - flush_work(&scif_info.conn_work); - err = ep->conn_err; - spin_lock(&ep->lock); - ep->conn_async_state = ASYNC_CONN_IDLE; - spin_unlock(&ep->lock); - } else { - err = scif_conn_func(ep); - } - return err; -} - -int scif_connect(scif_epd_t epd, struct scif_port_id *dst) -{ - return __scif_connect(epd, dst, false); -} -EXPORT_SYMBOL_GPL(scif_connect); - -/* - * scif_accept() - Accept a connection request from the remote node - * - * The function accepts a connection request from the remote node. Successful - * complete is indicate by a new end point being created and passed back - * to the caller for future reference. - * - * Upon successful complete a zero will be returned and the peer information - * will be filled in. - * - * If the end point is not in the listening state -EINVAL will be returned. - * - * If during the connection sequence resource allocation fails the -ENOMEM - * will be returned. - * - * If the function is called with the ASYNC flag set and no connection requests - * are pending it will return -EAGAIN. - * - * If the remote side is not sending any connection requests the caller may - * terminate this function with a signal. If so a -EINTR will be returned. - */ -int scif_accept(scif_epd_t epd, struct scif_port_id *peer, - scif_epd_t *newepd, int flags) -{ - struct scif_endpt *lep = (struct scif_endpt *)epd; - struct scif_endpt *cep; - struct scif_conreq *conreq; - struct scifmsg msg; - int err; - struct device *spdev; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI accept: ep %p %s\n", lep, scif_ep_states[lep->state]); - - if (flags & ~SCIF_ACCEPT_SYNC) - return -EINVAL; - - if (!peer || !newepd) - return -EINVAL; - - might_sleep(); - spin_lock(&lep->lock); - if (lep->state != SCIFEP_LISTENING) { - spin_unlock(&lep->lock); - return -EINVAL; - } - - if (!lep->conreqcnt && !(flags & SCIF_ACCEPT_SYNC)) { - /* No connection request present and we do not want to wait */ - spin_unlock(&lep->lock); - return -EAGAIN; - } - - lep->files = current->files; -retry_connection: - spin_unlock(&lep->lock); - /* Wait for the remote node to send us a SCIF_CNCT_REQ */ - err = wait_event_interruptible(lep->conwq, - (lep->conreqcnt || - (lep->state != SCIFEP_LISTENING))); - if (err) - return err; - - if (lep->state != SCIFEP_LISTENING) - return -EINTR; - - spin_lock(&lep->lock); - - if (!lep->conreqcnt) - goto retry_connection; - - /* Get the first connect request off the list */ - conreq = list_first_entry(&lep->conlist, struct scif_conreq, list); - list_del(&conreq->list); - lep->conreqcnt--; - spin_unlock(&lep->lock); - - /* Fill in the peer information */ - peer->node = conreq->msg.src.node; - peer->port = conreq->msg.src.port; - - cep = kzalloc(sizeof(*cep), GFP_KERNEL); - if (!cep) { - err = -ENOMEM; - goto scif_accept_error_epalloc; - } - spin_lock_init(&cep->lock); - mutex_init(&cep->sendlock); - mutex_init(&cep->recvlock); - cep->state = SCIFEP_CONNECTING; - cep->remote_dev = &scif_dev[peer->node]; - cep->remote_ep = conreq->msg.payload[0]; - - scif_rma_ep_init(cep); - - err = scif_reserve_dma_chan(cep); - if (err) { - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - goto scif_accept_error_qpalloc; - } - - cep->qp_info.qp = kzalloc(sizeof(*cep->qp_info.qp), GFP_KERNEL); - if (!cep->qp_info.qp) { - err = -ENOMEM; - goto scif_accept_error_qpalloc; - } - - err = scif_anon_inode_getfile(cep); - if (err) - goto scif_accept_error_anon_inode; - - cep->qp_info.qp->magic = SCIFEP_MAGIC; - spdev = scif_get_peer_dev(cep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - goto scif_accept_error_map; - } - err = scif_setup_qp_accept(cep->qp_info.qp, &cep->qp_info.qp_offset, - conreq->msg.payload[1], SCIF_ENDPT_QP_SIZE, - cep->remote_dev); - if (err) { - dev_dbg(&cep->remote_dev->sdev->dev, - "SCIFAPI accept: ep %p new %p scif_setup_qp_accept %d qp_offset 0x%llx\n", - lep, cep, err, cep->qp_info.qp_offset); - scif_put_peer_dev(spdev); - goto scif_accept_error_map; - } - - cep->port.node = lep->port.node; - cep->port.port = lep->port.port; - cep->peer.node = peer->node; - cep->peer.port = peer->port; - init_waitqueue_head(&cep->sendwq); - init_waitqueue_head(&cep->recvwq); - init_waitqueue_head(&cep->conwq); - - msg.uop = SCIF_CNCT_GNT; - msg.src = cep->port; - msg.payload[0] = cep->remote_ep; - msg.payload[1] = cep->qp_info.qp_offset; - msg.payload[2] = (u64)cep; - - err = _scif_nodeqp_send(cep->remote_dev, &msg); - scif_put_peer_dev(spdev); - if (err) - goto scif_accept_error_map; -retry: - /* Wait for the remote node to respond with SCIF_CNCT_GNT(N)ACK */ - err = wait_event_timeout(cep->conwq, cep->state != SCIFEP_CONNECTING, - SCIF_NODE_ACCEPT_TIMEOUT); - if (!err && scifdev_alive(cep)) - goto retry; - err = !err ? -ENODEV : 0; - if (err) - goto scif_accept_error_map; - kfree(conreq); - - spin_lock(&cep->lock); - - if (cep->state == SCIFEP_CLOSING) { - /* - * Remote failed to allocate resources and NAKed the grant. - * There is at this point nothing referencing the new end point. - */ - spin_unlock(&cep->lock); - scif_teardown_ep(cep); - kfree(cep); - - /* If call with sync flag then go back and wait. */ - if (flags & SCIF_ACCEPT_SYNC) { - spin_lock(&lep->lock); - goto retry_connection; - } - return -EAGAIN; - } - - scif_get_port(cep->port.port); - *newepd = (scif_epd_t)cep; - spin_unlock(&cep->lock); - return 0; -scif_accept_error_map: - scif_anon_inode_fput(cep); -scif_accept_error_anon_inode: - scif_teardown_ep(cep); -scif_accept_error_qpalloc: - kfree(cep); -scif_accept_error_epalloc: - msg.uop = SCIF_CNCT_REJ; - msg.dst.node = conreq->msg.src.node; - msg.dst.port = conreq->msg.src.port; - msg.payload[0] = conreq->msg.payload[0]; - msg.payload[1] = conreq->msg.payload[1]; - scif_nodeqp_send(&scif_dev[conreq->msg.src.node], &msg); - kfree(conreq); - return err; -} -EXPORT_SYMBOL_GPL(scif_accept); - -/* - * scif_msg_param_check: - * @epd: The end point returned from scif_open() - * @len: Length to receive - * @flags: blocking or non blocking - * - * Validate parameters for messaging APIs scif_send(..)/scif_recv(..). - */ -static inline int scif_msg_param_check(scif_epd_t epd, int len, int flags) -{ - int ret = -EINVAL; - - if (len < 0) - goto err_ret; - if (flags && (!(flags & SCIF_RECV_BLOCK))) - goto err_ret; - ret = 0; -err_ret: - return ret; -} - -static int _scif_send(scif_epd_t epd, void *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scifmsg notif_msg; - int curr_xfer_len = 0, sent_len = 0, write_count; - int ret = 0; - struct scif_qp *qp = ep->qp_info.qp; - - if (flags & SCIF_SEND_BLOCK) - might_sleep(); - - spin_lock(&ep->lock); - while (sent_len != len && SCIFEP_CONNECTED == ep->state) { - write_count = scif_rb_space(&qp->outbound_q); - if (write_count) { - /* Best effort to send as much data as possible */ - curr_xfer_len = min(len - sent_len, write_count); - ret = scif_rb_write(&qp->outbound_q, msg, - curr_xfer_len); - if (ret < 0) - break; - /* Success. Update write pointer */ - scif_rb_commit(&qp->outbound_q); - /* - * Send a notification to the peer about the - * produced data message. - */ - notif_msg.src = ep->port; - notif_msg.uop = SCIF_CLIENT_SENT; - notif_msg.payload[0] = ep->remote_ep; - ret = _scif_nodeqp_send(ep->remote_dev, ¬if_msg); - if (ret) - break; - sent_len += curr_xfer_len; - msg = msg + curr_xfer_len; - continue; - } - curr_xfer_len = min(len - sent_len, SCIF_ENDPT_QP_SIZE - 1); - /* Not enough RB space. return for the Non Blocking case */ - if (!(flags & SCIF_SEND_BLOCK)) - break; - - spin_unlock(&ep->lock); - /* Wait for a SCIF_CLIENT_RCVD message in the Blocking case */ - ret = - wait_event_interruptible(ep->sendwq, - (SCIFEP_CONNECTED != ep->state) || - (scif_rb_space(&qp->outbound_q) >= - curr_xfer_len)); - spin_lock(&ep->lock); - if (ret) - break; - } - if (sent_len) - ret = sent_len; - else if (!ret && SCIFEP_CONNECTED != ep->state) - ret = SCIFEP_DISCONNECTED == ep->state ? - -ECONNRESET : -ENOTCONN; - spin_unlock(&ep->lock); - return ret; -} - -static int _scif_recv(scif_epd_t epd, void *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scifmsg notif_msg; - int curr_recv_len = 0, remaining_len = len, read_count; - int ret = 0; - struct scif_qp *qp = ep->qp_info.qp; - - if (flags & SCIF_RECV_BLOCK) - might_sleep(); - spin_lock(&ep->lock); - while (remaining_len && (SCIFEP_CONNECTED == ep->state || - SCIFEP_DISCONNECTED == ep->state)) { - read_count = scif_rb_count(&qp->inbound_q, remaining_len); - if (read_count) { - /* - * Best effort to recv as much data as there - * are bytes to read in the RB particularly - * important for the Non Blocking case. - */ - curr_recv_len = min(remaining_len, read_count); - scif_rb_get_next(&qp->inbound_q, msg, curr_recv_len); - if (ep->state == SCIFEP_CONNECTED) { - /* - * Update the read pointer only if the endpoint - * is still connected else the read pointer - * might no longer exist since the peer has - * freed resources! - */ - scif_rb_update_read_ptr(&qp->inbound_q); - /* - * Send a notification to the peer about the - * consumed data message only if the EP is in - * SCIFEP_CONNECTED state. - */ - notif_msg.src = ep->port; - notif_msg.uop = SCIF_CLIENT_RCVD; - notif_msg.payload[0] = ep->remote_ep; - ret = _scif_nodeqp_send(ep->remote_dev, - ¬if_msg); - if (ret) - break; - } - remaining_len -= curr_recv_len; - msg = msg + curr_recv_len; - continue; - } - /* - * Bail out now if the EP is in SCIFEP_DISCONNECTED state else - * we will keep looping forever. - */ - if (ep->state == SCIFEP_DISCONNECTED) - break; - /* - * Return in the Non Blocking case if there is no data - * to read in this iteration. - */ - if (!(flags & SCIF_RECV_BLOCK)) - break; - curr_recv_len = min(remaining_len, SCIF_ENDPT_QP_SIZE - 1); - spin_unlock(&ep->lock); - /* - * Wait for a SCIF_CLIENT_SEND message in the blocking case - * or until other side disconnects. - */ - ret = - wait_event_interruptible(ep->recvwq, - SCIFEP_CONNECTED != ep->state || - scif_rb_count(&qp->inbound_q, - curr_recv_len) - >= curr_recv_len); - spin_lock(&ep->lock); - if (ret) - break; - } - if (len - remaining_len) - ret = len - remaining_len; - else if (!ret && ep->state != SCIFEP_CONNECTED) - ret = ep->state == SCIFEP_DISCONNECTED ? - -ECONNRESET : -ENOTCONN; - spin_unlock(&ep->lock); - return ret; -} - -/** - * scif_user_send() - Send data to connection queue - * @epd: The end point returned from scif_open() - * @msg: Address to place data - * @len: Length to receive - * @flags: blocking or non blocking - * - * This function is called from the driver IOCTL entry point - * only and is a wrapper for _scif_send(). - */ -int scif_user_send(scif_epd_t epd, void __user *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - int sent_len = 0; - char *tmp; - int loop_len; - int chunk_len = min(len, (1 << (MAX_ORDER + PAGE_SHIFT - 1))); - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI send (U): ep %p %s\n", ep, scif_ep_states[ep->state]); - if (!len) - return 0; - - err = scif_msg_param_check(epd, len, flags); - if (err) - goto send_err; - - tmp = kmalloc(chunk_len, GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto send_err; - } - /* - * Grabbing the lock before breaking up the transfer in - * multiple chunks is required to ensure that messages do - * not get fragmented and reordered. - */ - mutex_lock(&ep->sendlock); - while (sent_len != len) { - loop_len = len - sent_len; - loop_len = min(chunk_len, loop_len); - if (copy_from_user(tmp, msg, loop_len)) { - err = -EFAULT; - goto send_free_err; - } - err = _scif_send(epd, tmp, loop_len, flags); - if (err < 0) - goto send_free_err; - sent_len += err; - msg += err; - if (err != loop_len) - goto send_free_err; - } -send_free_err: - mutex_unlock(&ep->sendlock); - kfree(tmp); -send_err: - return err < 0 ? err : sent_len; -} - -/** - * scif_user_recv() - Receive data from connection queue - * @epd: The end point returned from scif_open() - * @msg: Address to place data - * @len: Length to receive - * @flags: blocking or non blocking - * - * This function is called from the driver IOCTL entry point - * only and is a wrapper for _scif_recv(). - */ -int scif_user_recv(scif_epd_t epd, void __user *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - int recv_len = 0; - char *tmp; - int loop_len; - int chunk_len = min(len, (1 << (MAX_ORDER + PAGE_SHIFT - 1))); - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI recv (U): ep %p %s\n", ep, scif_ep_states[ep->state]); - if (!len) - return 0; - - err = scif_msg_param_check(epd, len, flags); - if (err) - goto recv_err; - - tmp = kmalloc(chunk_len, GFP_KERNEL); - if (!tmp) { - err = -ENOMEM; - goto recv_err; - } - /* - * Grabbing the lock before breaking up the transfer in - * multiple chunks is required to ensure that messages do - * not get fragmented and reordered. - */ - mutex_lock(&ep->recvlock); - while (recv_len != len) { - loop_len = len - recv_len; - loop_len = min(chunk_len, loop_len); - err = _scif_recv(epd, tmp, loop_len, flags); - if (err < 0) - goto recv_free_err; - if (copy_to_user(msg, tmp, err)) { - err = -EFAULT; - goto recv_free_err; - } - recv_len += err; - msg += err; - if (err != loop_len) - goto recv_free_err; - } -recv_free_err: - mutex_unlock(&ep->recvlock); - kfree(tmp); -recv_err: - return err < 0 ? err : recv_len; -} - -/** - * scif_send() - Send data to connection queue - * @epd: The end point returned from scif_open() - * @msg: Address to place data - * @len: Length to receive - * @flags: blocking or non blocking - * - * This function is called from the kernel mode only and is - * a wrapper for _scif_send(). - */ -int scif_send(scif_epd_t epd, void *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int ret; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI send (K): ep %p %s\n", ep, scif_ep_states[ep->state]); - if (!len) - return 0; - - ret = scif_msg_param_check(epd, len, flags); - if (ret) - return ret; - if (!ep->remote_dev) - return -ENOTCONN; - /* - * Grab the mutex lock in the blocking case only - * to ensure messages do not get fragmented/reordered. - * The non blocking mode is protected using spin locks - * in _scif_send(). - */ - if (flags & SCIF_SEND_BLOCK) - mutex_lock(&ep->sendlock); - - ret = _scif_send(epd, msg, len, flags); - - if (flags & SCIF_SEND_BLOCK) - mutex_unlock(&ep->sendlock); - return ret; -} -EXPORT_SYMBOL_GPL(scif_send); - -/** - * scif_recv() - Receive data from connection queue - * @epd: The end point returned from scif_open() - * @msg: Address to place data - * @len: Length to receive - * @flags: blocking or non blocking - * - * This function is called from the kernel mode only and is - * a wrapper for _scif_recv(). - */ -int scif_recv(scif_epd_t epd, void *msg, int len, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int ret; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI recv (K): ep %p %s\n", ep, scif_ep_states[ep->state]); - if (!len) - return 0; - - ret = scif_msg_param_check(epd, len, flags); - if (ret) - return ret; - /* - * Grab the mutex lock in the blocking case only - * to ensure messages do not get fragmented/reordered. - * The non blocking mode is protected using spin locks - * in _scif_send(). - */ - if (flags & SCIF_RECV_BLOCK) - mutex_lock(&ep->recvlock); - - ret = _scif_recv(epd, msg, len, flags); - - if (flags & SCIF_RECV_BLOCK) - mutex_unlock(&ep->recvlock); - - return ret; -} -EXPORT_SYMBOL_GPL(scif_recv); - -static inline void _scif_poll_wait(struct file *f, wait_queue_head_t *wq, - poll_table *p, struct scif_endpt *ep) -{ - /* - * Because poll_wait makes a GFP_KERNEL allocation, give up the lock - * and regrab it afterwards. Because the endpoint state might have - * changed while the lock was given up, the state must be checked - * again after re-acquiring the lock. The code in __scif_pollfd(..) - * does this. - */ - spin_unlock(&ep->lock); - poll_wait(f, wq, p); - spin_lock(&ep->lock); -} - -__poll_t -__scif_pollfd(struct file *f, poll_table *wait, struct scif_endpt *ep) -{ - __poll_t mask = 0; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI pollfd: ep %p %s\n", ep, scif_ep_states[ep->state]); - - spin_lock(&ep->lock); - - /* Endpoint is waiting for a non-blocking connect to complete */ - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { - _scif_poll_wait(f, &ep->conn_pend_wq, wait, ep); - if (ep->conn_async_state == ASYNC_CONN_INPROGRESS) { - if (ep->state == SCIFEP_CONNECTED || - ep->state == SCIFEP_DISCONNECTED || - ep->conn_err) - mask |= EPOLLOUT; - goto exit; - } - } - - /* Endpoint is listening for incoming connection requests */ - if (ep->state == SCIFEP_LISTENING) { - _scif_poll_wait(f, &ep->conwq, wait, ep); - if (ep->state == SCIFEP_LISTENING) { - if (ep->conreqcnt) - mask |= EPOLLIN; - goto exit; - } - } - - /* Endpoint is connected or disconnected */ - if (ep->state == SCIFEP_CONNECTED || ep->state == SCIFEP_DISCONNECTED) { - if (poll_requested_events(wait) & EPOLLIN) - _scif_poll_wait(f, &ep->recvwq, wait, ep); - if (poll_requested_events(wait) & EPOLLOUT) - _scif_poll_wait(f, &ep->sendwq, wait, ep); - if (ep->state == SCIFEP_CONNECTED || - ep->state == SCIFEP_DISCONNECTED) { - /* Data can be read without blocking */ - if (scif_rb_count(&ep->qp_info.qp->inbound_q, 1)) - mask |= EPOLLIN; - /* Data can be written without blocking */ - if (scif_rb_space(&ep->qp_info.qp->outbound_q)) - mask |= EPOLLOUT; - /* Return EPOLLHUP if endpoint is disconnected */ - if (ep->state == SCIFEP_DISCONNECTED) - mask |= EPOLLHUP; - goto exit; - } - } - - /* Return EPOLLERR if the endpoint is in none of the above states */ - mask |= EPOLLERR; -exit: - spin_unlock(&ep->lock); - return mask; -} - -/** - * scif_poll() - Kernel mode SCIF poll - * @ufds: Array of scif_pollepd structures containing the end points - * and events to poll on - * @nfds: Size of the ufds array - * @timeout_msecs: Timeout in msecs, -ve implies infinite timeout - * - * The code flow in this function is based on do_poll(..) in select.c - * - * Returns the number of endpoints which have pending events or 0 in - * the event of a timeout. If a signal is used for wake up, -EINTR is - * returned. - */ -int -scif_poll(struct scif_pollepd *ufds, unsigned int nfds, long timeout_msecs) -{ - struct poll_wqueues table; - poll_table *pt; - int i, count = 0, timed_out = timeout_msecs == 0; - __poll_t mask; - u64 timeout = timeout_msecs < 0 ? MAX_SCHEDULE_TIMEOUT - : msecs_to_jiffies(timeout_msecs); - - poll_initwait(&table); - pt = &table.pt; - while (1) { - for (i = 0; i < nfds; i++) { - pt->_key = ufds[i].events | EPOLLERR | EPOLLHUP; - mask = __scif_pollfd(ufds[i].epd->anon, - pt, ufds[i].epd); - mask &= ufds[i].events | EPOLLERR | EPOLLHUP; - if (mask) { - count++; - pt->_qproc = NULL; - } - ufds[i].revents = mask; - } - pt->_qproc = NULL; - if (!count) { - count = table.error; - if (signal_pending(current)) - count = -EINTR; - } - if (count || timed_out) - break; - - if (!schedule_timeout_interruptible(timeout)) - timed_out = 1; - } - poll_freewait(&table); - return count; -} -EXPORT_SYMBOL_GPL(scif_poll); - -int scif_get_node_ids(u16 *nodes, int len, u16 *self) -{ - int online = 0; - int offset = 0; - int node; - - if (!scif_is_mgmt_node()) - scif_get_node_info(); - - *self = scif_info.nodeid; - mutex_lock(&scif_info.conflock); - len = min_t(int, len, scif_info.total); - for (node = 0; node <= scif_info.maxid; node++) { - if (_scifdev_alive(&scif_dev[node])) { - online++; - if (offset < len) - nodes[offset++] = node; - } - } - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI get_node_ids total %d online %d filled in %d nodes\n", - scif_info.total, online, offset); - mutex_unlock(&scif_info.conflock); - - return online; -} -EXPORT_SYMBOL_GPL(scif_get_node_ids); - -static int scif_add_client_dev(struct device *dev, struct subsys_interface *si) -{ - struct scif_client *client = - container_of(si, struct scif_client, si); - struct scif_peer_dev *spdev = - container_of(dev, struct scif_peer_dev, dev); - - if (client->probe) - client->probe(spdev); - return 0; -} - -static void scif_remove_client_dev(struct device *dev, - struct subsys_interface *si) -{ - struct scif_client *client = - container_of(si, struct scif_client, si); - struct scif_peer_dev *spdev = - container_of(dev, struct scif_peer_dev, dev); - - if (client->remove) - client->remove(spdev); -} - -void scif_client_unregister(struct scif_client *client) -{ - subsys_interface_unregister(&client->si); -} -EXPORT_SYMBOL_GPL(scif_client_unregister); - -int scif_client_register(struct scif_client *client) -{ - struct subsys_interface *si = &client->si; - - si->name = client->name; - si->subsys = &scif_peer_bus; - si->add_dev = scif_add_client_dev; - si->remove_dev = scif_remove_client_dev; - - return subsys_interface_register(&client->si); -} -EXPORT_SYMBOL_GPL(scif_client_register); diff --git a/drivers/misc/mic/scif/scif_debugfs.c b/drivers/misc/mic/scif/scif_debugfs.c deleted file mode 100644 index 8fe38e7ca6e6..000000000000 --- a/drivers/misc/mic/scif/scif_debugfs.c +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include -#include - -#include "../common/mic_dev.h" -#include "scif_main.h" - -/* Debugfs parent dir */ -static struct dentry *scif_dbg; - -static int scif_dev_show(struct seq_file *s, void *unused) -{ - int node; - - seq_printf(s, "Total Nodes %d Self Node Id %d Maxid %d\n", - scif_info.total, scif_info.nodeid, - scif_info.maxid); - - if (!scif_dev) - return 0; - - seq_printf(s, "%-16s\t%-16s\n", "node_id", "state"); - - for (node = 0; node <= scif_info.maxid; node++) - seq_printf(s, "%-16d\t%-16s\n", scif_dev[node].node, - _scifdev_alive(&scif_dev[node]) ? - "Running" : "Offline"); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(scif_dev); - -static void scif_display_window(struct scif_window *window, struct seq_file *s) -{ - int j; - struct scatterlist *sg; - scif_pinned_pages_t pin = window->pinned_pages; - - seq_printf(s, "window %p type %d temp %d offset 0x%llx ", - window, window->type, window->temp, window->offset); - seq_printf(s, "nr_pages 0x%llx nr_contig_chunks 0x%x prot %d ", - window->nr_pages, window->nr_contig_chunks, window->prot); - seq_printf(s, "ref_count %d magic 0x%llx peer_window 0x%llx ", - window->ref_count, window->magic, window->peer_window); - seq_printf(s, "unreg_state 0x%x va_for_temp 0x%lx\n", - window->unreg_state, window->va_for_temp); - - for (j = 0; j < window->nr_contig_chunks; j++) - seq_printf(s, "page[%d] dma_addr 0x%llx num_pages 0x%llx\n", j, - window->dma_addr[j], window->num_pages[j]); - - if (window->type == SCIF_WINDOW_SELF && pin) - for (j = 0; j < window->nr_pages; j++) - seq_printf(s, "page[%d] = pinned_pages %p address %p\n", - j, pin->pages[j], - page_address(pin->pages[j])); - - if (window->st) - for_each_sg(window->st->sgl, sg, window->st->nents, j) - seq_printf(s, "sg[%d] dma addr 0x%llx length 0x%x\n", - j, sg_dma_address(sg), sg_dma_len(sg)); -} - -static void scif_display_all_windows(struct list_head *head, struct seq_file *s) -{ - struct list_head *item; - struct scif_window *window; - - list_for_each(item, head) { - window = list_entry(item, struct scif_window, list); - scif_display_window(window, s); - } -} - -static int scif_rma_show(struct seq_file *s, void *unused) -{ - struct scif_endpt *ep; - struct list_head *pos; - - mutex_lock(&scif_info.connlock); - list_for_each(pos, &scif_info.connected) { - ep = list_entry(pos, struct scif_endpt, list); - seq_printf(s, "ep %p self windows\n", ep); - mutex_lock(&ep->rma_info.rma_lock); - scif_display_all_windows(&ep->rma_info.reg_list, s); - seq_printf(s, "ep %p remote windows\n", ep); - scif_display_all_windows(&ep->rma_info.remote_reg_list, s); - mutex_unlock(&ep->rma_info.rma_lock); - } - mutex_unlock(&scif_info.connlock); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(scif_rma); - -void __init scif_init_debugfs(void) -{ - scif_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); - - debugfs_create_file("scif_dev", 0444, scif_dbg, NULL, &scif_dev_fops); - debugfs_create_file("scif_rma", 0444, scif_dbg, NULL, &scif_rma_fops); - debugfs_create_u8("en_msg_log", 0666, scif_dbg, &scif_info.en_msg_log); - debugfs_create_u8("p2p_enable", 0666, scif_dbg, &scif_info.p2p_enable); -} - -void scif_exit_debugfs(void) -{ - debugfs_remove_recursive(scif_dbg); -} diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c deleted file mode 100644 index 401b98e5ad79..000000000000 --- a/drivers/misc/mic/scif/scif_dma.c +++ /dev/null @@ -1,1940 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" -#include "scif_map.h" - -/* - * struct scif_dma_comp_cb - SCIF DMA completion callback - * - * @dma_completion_func: DMA completion callback - * @cb_cookie: DMA completion callback cookie - * @temp_buf: Temporary buffer - * @temp_buf_to_free: Temporary buffer to be freed - * @is_cache: Is a kmem_cache allocated buffer - * @dst_offset: Destination registration offset - * @dst_window: Destination registration window - * @len: Length of the temp buffer - * @temp_phys: DMA address of the temp buffer - * @sdev: The SCIF device - * @header_padding: padding for cache line alignment - */ -struct scif_dma_comp_cb { - void (*dma_completion_func)(void *cookie); - void *cb_cookie; - u8 *temp_buf; - u8 *temp_buf_to_free; - bool is_cache; - s64 dst_offset; - struct scif_window *dst_window; - size_t len; - dma_addr_t temp_phys; - struct scif_dev *sdev; - int header_padding; -}; - -/** - * struct scif_copy_work - Work for DMA copy - * - * @src_offset: Starting source offset - * @dst_offset: Starting destination offset - * @src_window: Starting src registered window - * @dst_window: Starting dst registered window - * @loopback: true if this is a loopback DMA transfer - * @len: Length of the transfer - * @comp_cb: DMA copy completion callback - * @remote_dev: The remote SCIF peer device - * @fence_type: polling or interrupt based - * @ordered: is this a tail byte ordered DMA transfer - */ -struct scif_copy_work { - s64 src_offset; - s64 dst_offset; - struct scif_window *src_window; - struct scif_window *dst_window; - int loopback; - size_t len; - struct scif_dma_comp_cb *comp_cb; - struct scif_dev *remote_dev; - int fence_type; - bool ordered; -}; - -/** - * scif_reserve_dma_chan: - * @ep: Endpoint Descriptor. - * - * This routine reserves a DMA channel for a particular - * endpoint. All DMA transfers for an endpoint are always - * programmed on the same DMA channel. - */ -int scif_reserve_dma_chan(struct scif_endpt *ep) -{ - int err = 0; - struct scif_dev *scifdev; - struct scif_hw_dev *sdev; - struct dma_chan *chan; - - /* Loopback DMAs are not supported on the management node */ - if (!scif_info.nodeid && scifdev_self(ep->remote_dev)) - return 0; - if (scif_info.nodeid) - scifdev = &scif_dev[0]; - else - scifdev = ep->remote_dev; - sdev = scifdev->sdev; - if (!sdev->num_dma_ch) - return -ENODEV; - chan = sdev->dma_ch[scifdev->dma_ch_idx]; - scifdev->dma_ch_idx = (scifdev->dma_ch_idx + 1) % sdev->num_dma_ch; - mutex_lock(&ep->rma_info.rma_lock); - ep->rma_info.dma_chan = chan; - mutex_unlock(&ep->rma_info.rma_lock); - return err; -} - -#ifdef CONFIG_MMU_NOTIFIER -/* - * scif_rma_destroy_tcw: - * - * This routine destroys temporary cached windows - */ -static -void __scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, - u64 start, u64 len) -{ - struct list_head *item, *tmp; - struct scif_window *window; - u64 start_va, end_va; - u64 end = start + len; - - if (end <= start) - return; - - list_for_each_safe(item, tmp, &mmn->tc_reg_list) { - window = list_entry(item, struct scif_window, list); - if (!len) - break; - start_va = window->va_for_temp; - end_va = start_va + (window->nr_pages << PAGE_SHIFT); - if (start < start_va && end <= start_va) - break; - if (start >= end_va) - continue; - __scif_rma_destroy_tcw_helper(window); - } -} - -static void scif_rma_destroy_tcw(struct scif_mmu_notif *mmn, u64 start, u64 len) -{ - struct scif_endpt *ep = mmn->ep; - - spin_lock(&ep->rma_info.tc_lock); - __scif_rma_destroy_tcw(mmn, start, len); - spin_unlock(&ep->rma_info.tc_lock); -} - -static void scif_rma_destroy_tcw_ep(struct scif_endpt *ep) -{ - struct list_head *item, *tmp; - struct scif_mmu_notif *mmn; - - list_for_each_safe(item, tmp, &ep->rma_info.mmn_list) { - mmn = list_entry(item, struct scif_mmu_notif, list); - scif_rma_destroy_tcw(mmn, 0, ULONG_MAX); - } -} - -static void __scif_rma_destroy_tcw_ep(struct scif_endpt *ep) -{ - struct list_head *item, *tmp; - struct scif_mmu_notif *mmn; - - spin_lock(&ep->rma_info.tc_lock); - list_for_each_safe(item, tmp, &ep->rma_info.mmn_list) { - mmn = list_entry(item, struct scif_mmu_notif, list); - __scif_rma_destroy_tcw(mmn, 0, ULONG_MAX); - } - spin_unlock(&ep->rma_info.tc_lock); -} - -static bool scif_rma_tc_can_cache(struct scif_endpt *ep, size_t cur_bytes) -{ - if ((cur_bytes >> PAGE_SHIFT) > scif_info.rma_tc_limit) - return false; - if ((atomic_read(&ep->rma_info.tcw_total_pages) - + (cur_bytes >> PAGE_SHIFT)) > - scif_info.rma_tc_limit) { - dev_info(scif_info.mdev.this_device, - "%s %d total=%d, current=%zu reached max\n", - __func__, __LINE__, - atomic_read(&ep->rma_info.tcw_total_pages), - (1 + (cur_bytes >> PAGE_SHIFT))); - scif_rma_destroy_tcw_invalid(); - __scif_rma_destroy_tcw_ep(ep); - } - return true; -} - -static void scif_mmu_notifier_release(struct mmu_notifier *mn, - struct mm_struct *mm) -{ - struct scif_mmu_notif *mmn; - - mmn = container_of(mn, struct scif_mmu_notif, ep_mmu_notifier); - scif_rma_destroy_tcw(mmn, 0, ULONG_MAX); - schedule_work(&scif_info.misc_work); -} - -static int scif_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct scif_mmu_notif *mmn; - - mmn = container_of(mn, struct scif_mmu_notif, ep_mmu_notifier); - scif_rma_destroy_tcw(mmn, range->start, range->end - range->start); - - return 0; -} - -static void scif_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - /* - * Nothing to do here, everything needed was done in - * invalidate_range_start. - */ -} - -static const struct mmu_notifier_ops scif_mmu_notifier_ops = { - .release = scif_mmu_notifier_release, - .clear_flush_young = NULL, - .invalidate_range_start = scif_mmu_notifier_invalidate_range_start, - .invalidate_range_end = scif_mmu_notifier_invalidate_range_end}; - -static void scif_ep_unregister_mmu_notifier(struct scif_endpt *ep) -{ - struct scif_endpt_rma_info *rma = &ep->rma_info; - struct scif_mmu_notif *mmn = NULL; - struct list_head *item, *tmp; - - mutex_lock(&ep->rma_info.mmn_lock); - list_for_each_safe(item, tmp, &rma->mmn_list) { - mmn = list_entry(item, struct scif_mmu_notif, list); - mmu_notifier_unregister(&mmn->ep_mmu_notifier, mmn->mm); - list_del(item); - kfree(mmn); - } - mutex_unlock(&ep->rma_info.mmn_lock); -} - -static void scif_init_mmu_notifier(struct scif_mmu_notif *mmn, - struct mm_struct *mm, struct scif_endpt *ep) -{ - mmn->ep = ep; - mmn->mm = mm; - mmn->ep_mmu_notifier.ops = &scif_mmu_notifier_ops; - INIT_LIST_HEAD(&mmn->list); - INIT_LIST_HEAD(&mmn->tc_reg_list); -} - -static struct scif_mmu_notif * -scif_find_mmu_notifier(struct mm_struct *mm, struct scif_endpt_rma_info *rma) -{ - struct scif_mmu_notif *mmn; - - list_for_each_entry(mmn, &rma->mmn_list, list) - if (mmn->mm == mm) - return mmn; - return NULL; -} - -static struct scif_mmu_notif * -scif_add_mmu_notifier(struct mm_struct *mm, struct scif_endpt *ep) -{ - struct scif_mmu_notif *mmn - = kzalloc(sizeof(*mmn), GFP_KERNEL); - - if (!mmn) - return ERR_PTR(-ENOMEM); - - scif_init_mmu_notifier(mmn, current->mm, ep); - if (mmu_notifier_register(&mmn->ep_mmu_notifier, current->mm)) { - kfree(mmn); - return ERR_PTR(-EBUSY); - } - list_add(&mmn->list, &ep->rma_info.mmn_list); - return mmn; -} - -/* - * Called from the misc thread to destroy temporary cached windows and - * unregister the MMU notifier for the SCIF endpoint. - */ -void scif_mmu_notif_handler(struct work_struct *work) -{ - struct list_head *pos, *tmpq; - struct scif_endpt *ep; -restart: - scif_rma_destroy_tcw_invalid(); - spin_lock(&scif_info.rmalock); - list_for_each_safe(pos, tmpq, &scif_info.mmu_notif_cleanup) { - ep = list_entry(pos, struct scif_endpt, mmu_list); - list_del(&ep->mmu_list); - spin_unlock(&scif_info.rmalock); - scif_rma_destroy_tcw_ep(ep); - scif_ep_unregister_mmu_notifier(ep); - goto restart; - } - spin_unlock(&scif_info.rmalock); -} - -static bool scif_is_set_reg_cache(int flags) -{ - return !!(flags & SCIF_RMA_USECACHE); -} -#else -static struct scif_mmu_notif * -scif_find_mmu_notifier(struct mm_struct *mm, - struct scif_endpt_rma_info *rma) -{ - return NULL; -} - -static struct scif_mmu_notif * -scif_add_mmu_notifier(struct mm_struct *mm, struct scif_endpt *ep) -{ - return NULL; -} - -void scif_mmu_notif_handler(struct work_struct *work) -{ -} - -static bool scif_is_set_reg_cache(int flags) -{ - return false; -} - -static bool scif_rma_tc_can_cache(struct scif_endpt *ep, size_t cur_bytes) -{ - return false; -} -#endif - -/** - * scif_register_temp: - * @epd: End Point Descriptor. - * @addr: virtual address to/from which to copy - * @len: length of range to copy - * @prot: read/write protection - * @out_offset: computed offset returned by reference. - * @out_window: allocated registered window returned by reference. - * - * Create a temporary registered window. The peer will not know about this - * window. This API is used for scif_vreadfrom()/scif_vwriteto() API's. - */ -static int -scif_register_temp(scif_epd_t epd, unsigned long addr, size_t len, int prot, - off_t *out_offset, struct scif_window **out_window) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err; - scif_pinned_pages_t pinned_pages; - size_t aligned_len; - - aligned_len = ALIGN(len, PAGE_SIZE); - - err = __scif_pin_pages((void *)(addr & PAGE_MASK), - aligned_len, &prot, 0, &pinned_pages); - if (err) - return err; - - pinned_pages->prot = prot; - - /* Compute the offset for this registration */ - err = scif_get_window_offset(ep, 0, 0, - aligned_len >> PAGE_SHIFT, - (s64 *)out_offset); - if (err) - goto error_unpin; - - /* Allocate and prepare self registration window */ - *out_window = scif_create_window(ep, aligned_len >> PAGE_SHIFT, - *out_offset, true); - if (!*out_window) { - scif_free_window_offset(ep, NULL, *out_offset); - err = -ENOMEM; - goto error_unpin; - } - - (*out_window)->pinned_pages = pinned_pages; - (*out_window)->nr_pages = pinned_pages->nr_pages; - (*out_window)->prot = pinned_pages->prot; - - (*out_window)->va_for_temp = addr & PAGE_MASK; - err = scif_map_window(ep->remote_dev, *out_window); - if (err) { - /* Something went wrong! Rollback */ - scif_destroy_window(ep, *out_window); - *out_window = NULL; - } else { - *out_offset |= (addr - (*out_window)->va_for_temp); - } - return err; -error_unpin: - if (err) - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - scif_unpin_pages(pinned_pages); - return err; -} - -#define SCIF_DMA_TO (3 * HZ) - -/* - * scif_sync_dma - Program a DMA without an interrupt descriptor - * - * @dev - The address of the pointer to the device instance used - * for DMA registration. - * @chan - DMA channel to be used. - * @sync_wait: Wait for DMA to complete? - * - * Return 0 on success and -errno on error. - */ -static int scif_sync_dma(struct scif_hw_dev *sdev, struct dma_chan *chan, - bool sync_wait) -{ - int err = 0; - struct dma_async_tx_descriptor *tx = NULL; - enum dma_ctrl_flags flags = DMA_PREP_FENCE; - dma_cookie_t cookie; - struct dma_device *ddev; - - if (!chan) { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - ddev = chan->device; - - tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, flags); - if (!tx) { - err = -ENOMEM; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - cookie = tx->tx_submit(tx); - - if (dma_submit_error(cookie)) { - err = -ENOMEM; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - if (!sync_wait) { - dma_async_issue_pending(chan); - } else { - if (dma_sync_wait(chan, cookie) == DMA_COMPLETE) { - err = 0; - } else { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - } - } -release: - return err; -} - -static void scif_dma_callback(void *arg) -{ - struct completion *done = (struct completion *)arg; - - complete(done); -} - -#define SCIF_DMA_SYNC_WAIT true -#define SCIF_DMA_POLL BIT(0) -#define SCIF_DMA_INTR BIT(1) - -/* - * scif_async_dma - Program a DMA with an interrupt descriptor - * - * @dev - The address of the pointer to the device instance used - * for DMA registration. - * @chan - DMA channel to be used. - * Return 0 on success and -errno on error. - */ -static int scif_async_dma(struct scif_hw_dev *sdev, struct dma_chan *chan) -{ - int err = 0; - struct dma_device *ddev; - struct dma_async_tx_descriptor *tx = NULL; - enum dma_ctrl_flags flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE; - DECLARE_COMPLETION_ONSTACK(done_wait); - dma_cookie_t cookie; - enum dma_status status; - - if (!chan) { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - ddev = chan->device; - - tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, flags); - if (!tx) { - err = -ENOMEM; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - reinit_completion(&done_wait); - tx->callback = scif_dma_callback; - tx->callback_param = &done_wait; - cookie = tx->tx_submit(tx); - - if (dma_submit_error(cookie)) { - err = -ENOMEM; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - dma_async_issue_pending(chan); - - err = wait_for_completion_timeout(&done_wait, SCIF_DMA_TO); - if (!err) { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } - err = 0; - status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); - if (status != DMA_COMPLETE) { - err = -EIO; - dev_err(&sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto release; - } -release: - return err; -} - -/* - * scif_drain_dma_poll - Drain all outstanding DMA operations for a particular - * DMA channel via polling. - * - * @sdev - The SCIF device - * @chan - DMA channel - * Return 0 on success and -errno on error. - */ -static int scif_drain_dma_poll(struct scif_hw_dev *sdev, struct dma_chan *chan) -{ - if (!chan) - return -EINVAL; - return scif_sync_dma(sdev, chan, SCIF_DMA_SYNC_WAIT); -} - -/* - * scif_drain_dma_intr - Drain all outstanding DMA operations for a particular - * DMA channel via interrupt based blocking wait. - * - * @sdev - The SCIF device - * @chan - DMA channel - * Return 0 on success and -errno on error. - */ -int scif_drain_dma_intr(struct scif_hw_dev *sdev, struct dma_chan *chan) -{ - if (!chan) - return -EINVAL; - return scif_async_dma(sdev, chan); -} - -/** - * scif_rma_destroy_windows: - * - * This routine destroys all windows queued for cleanup - */ -void scif_rma_destroy_windows(void) -{ - struct list_head *item, *tmp; - struct scif_window *window; - struct scif_endpt *ep; - struct dma_chan *chan; - - might_sleep(); -restart: - spin_lock(&scif_info.rmalock); - list_for_each_safe(item, tmp, &scif_info.rma) { - window = list_entry(item, struct scif_window, - list); - ep = (struct scif_endpt *)window->ep; - chan = ep->rma_info.dma_chan; - - list_del_init(&window->list); - spin_unlock(&scif_info.rmalock); - if (!chan || !scifdev_alive(ep) || - !scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan)) - /* Remove window from global list */ - window->unreg_state = OP_COMPLETED; - else - dev_warn(&ep->remote_dev->sdev->dev, - "DMA engine hung?\n"); - if (window->unreg_state == OP_COMPLETED) { - if (window->type == SCIF_WINDOW_SELF) - scif_destroy_window(ep, window); - else - scif_destroy_remote_window(window); - atomic_dec(&ep->rma_info.tw_refcount); - } - goto restart; - } - spin_unlock(&scif_info.rmalock); -} - -/** - * scif_rma_destroy_tcw: - * - * This routine destroys temporary cached registered windows - * which have been queued for cleanup. - */ -void scif_rma_destroy_tcw_invalid(void) -{ - struct list_head *item, *tmp; - struct scif_window *window; - struct scif_endpt *ep; - struct dma_chan *chan; - - might_sleep(); -restart: - spin_lock(&scif_info.rmalock); - list_for_each_safe(item, tmp, &scif_info.rma_tc) { - window = list_entry(item, struct scif_window, list); - ep = (struct scif_endpt *)window->ep; - chan = ep->rma_info.dma_chan; - list_del_init(&window->list); - spin_unlock(&scif_info.rmalock); - mutex_lock(&ep->rma_info.rma_lock); - if (!chan || !scifdev_alive(ep) || - !scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan)) { - atomic_sub(window->nr_pages, - &ep->rma_info.tcw_total_pages); - scif_destroy_window(ep, window); - atomic_dec(&ep->rma_info.tcw_refcount); - } else { - dev_warn(&ep->remote_dev->sdev->dev, - "DMA engine hung?\n"); - } - mutex_unlock(&ep->rma_info.rma_lock); - goto restart; - } - spin_unlock(&scif_info.rmalock); -} - -static inline -void *_get_local_va(off_t off, struct scif_window *window, size_t len) -{ - int page_nr = (off - window->offset) >> PAGE_SHIFT; - off_t page_off = off & ~PAGE_MASK; - void *va = NULL; - - if (window->type == SCIF_WINDOW_SELF) { - struct page **pages = window->pinned_pages->pages; - - va = page_address(pages[page_nr]) + page_off; - } - return va; -} - -static inline -void *ioremap_remote(off_t off, struct scif_window *window, - size_t len, struct scif_dev *dev, - struct scif_window_iter *iter) -{ - dma_addr_t phys = scif_off_to_dma_addr(window, off, NULL, iter); - - /* - * If the DMA address is not card relative then we need the DMA - * addresses to be an offset into the bar. The aperture base was already - * added so subtract it here since scif_ioremap is going to add it again - */ - if (!scifdev_self(dev) && window->type == SCIF_WINDOW_PEER && - dev->sdev->aper && !dev->sdev->card_rel_da) - phys = phys - dev->sdev->aper->pa; - return scif_ioremap(phys, len, dev); -} - -static inline void -iounmap_remote(void *virt, size_t size, struct scif_copy_work *work) -{ - scif_iounmap(virt, size, work->remote_dev); -} - -/* - * Takes care of ordering issue caused by - * 1. Hardware: Only in the case of cpu copy from mgmt node to card - * because of WC memory. - * 2. Software: If memcpy reorders copy instructions for optimization. - * This could happen at both mgmt node and card. - */ -static inline void -scif_ordered_memcpy_toio(char *dst, const char *src, size_t count) -{ - if (!count) - return; - - memcpy_toio((void __iomem __force *)dst, src, --count); - /* Order the last byte with the previous stores */ - wmb(); - *(dst + count) = *(src + count); -} - -static inline void scif_unaligned_cpy_toio(char *dst, const char *src, - size_t count, bool ordered) -{ - if (ordered) - scif_ordered_memcpy_toio(dst, src, count); - else - memcpy_toio((void __iomem __force *)dst, src, count); -} - -static inline -void scif_ordered_memcpy_fromio(char *dst, const char *src, size_t count) -{ - if (!count) - return; - - memcpy_fromio(dst, (void __iomem __force *)src, --count); - /* Order the last byte with the previous loads */ - rmb(); - *(dst + count) = *(src + count); -} - -static inline void scif_unaligned_cpy_fromio(char *dst, const char *src, - size_t count, bool ordered) -{ - if (ordered) - scif_ordered_memcpy_fromio(dst, src, count); - else - memcpy_fromio(dst, (void __iomem __force *)src, count); -} - -#define SCIF_RMA_ERROR_CODE (~(dma_addr_t)0x0) - -/* - * scif_off_to_dma_addr: - * Obtain the dma_addr given the window and the offset. - * @window: Registered window. - * @off: Window offset. - * @nr_bytes: Return the number of contiguous bytes till next DMA addr index. - * @index: Return the index of the dma_addr array found. - * @start_off: start offset of index of the dma addr array found. - * The nr_bytes provides the callee an estimate of the maximum possible - * DMA xfer possible while the index/start_off provide faster lookups - * for the next iteration. - */ -dma_addr_t scif_off_to_dma_addr(struct scif_window *window, s64 off, - size_t *nr_bytes, struct scif_window_iter *iter) -{ - int i, page_nr; - s64 start, end; - off_t page_off; - - if (window->nr_pages == window->nr_contig_chunks) { - page_nr = (off - window->offset) >> PAGE_SHIFT; - page_off = off & ~PAGE_MASK; - - if (nr_bytes) - *nr_bytes = PAGE_SIZE - page_off; - return window->dma_addr[page_nr] | page_off; - } - if (iter) { - i = iter->index; - start = iter->offset; - } else { - i = 0; - start = window->offset; - } - for (; i < window->nr_contig_chunks; i++) { - end = start + (window->num_pages[i] << PAGE_SHIFT); - if (off >= start && off < end) { - if (iter) { - iter->index = i; - iter->offset = start; - } - if (nr_bytes) - *nr_bytes = end - off; - return (window->dma_addr[i] + (off - start)); - } - start += (window->num_pages[i] << PAGE_SHIFT); - } - dev_err(scif_info.mdev.this_device, - "%s %d BUG. Addr not found? window %p off 0x%llx\n", - __func__, __LINE__, window, off); - return SCIF_RMA_ERROR_CODE; -} - -/* - * Copy between rma window and temporary buffer - */ -static void scif_rma_local_cpu_copy(s64 offset, struct scif_window *window, - u8 *temp, size_t rem_len, bool to_temp) -{ - void *window_virt; - size_t loop_len; - int offset_in_page; - s64 end_offset; - - offset_in_page = offset & ~PAGE_MASK; - loop_len = PAGE_SIZE - offset_in_page; - - if (rem_len < loop_len) - loop_len = rem_len; - - window_virt = _get_local_va(offset, window, loop_len); - if (!window_virt) - return; - if (to_temp) - memcpy(temp, window_virt, loop_len); - else - memcpy(window_virt, temp, loop_len); - - offset += loop_len; - temp += loop_len; - rem_len -= loop_len; - - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - while (rem_len) { - if (offset == end_offset) { - window = list_next_entry(window, list); - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - } - loop_len = min(PAGE_SIZE, rem_len); - window_virt = _get_local_va(offset, window, loop_len); - if (!window_virt) - return; - if (to_temp) - memcpy(temp, window_virt, loop_len); - else - memcpy(window_virt, temp, loop_len); - offset += loop_len; - temp += loop_len; - rem_len -= loop_len; - } -} - -/** - * scif_rma_completion_cb: - * @data: RMA cookie - * - * RMA interrupt completion callback. - */ -static void scif_rma_completion_cb(void *data) -{ - struct scif_dma_comp_cb *comp_cb = data; - - /* Free DMA Completion CB. */ - if (comp_cb->dst_window) - scif_rma_local_cpu_copy(comp_cb->dst_offset, - comp_cb->dst_window, - comp_cb->temp_buf + - comp_cb->header_padding, - comp_cb->len, false); - scif_unmap_single(comp_cb->temp_phys, comp_cb->sdev, - SCIF_KMEM_UNALIGNED_BUF_SIZE); - if (comp_cb->is_cache) - kmem_cache_free(unaligned_cache, - comp_cb->temp_buf_to_free); - else - kfree(comp_cb->temp_buf_to_free); -} - -/* Copies between temporary buffer and offsets provided in work */ -static int -scif_rma_list_dma_copy_unaligned(struct scif_copy_work *work, - u8 *temp, struct dma_chan *chan, - bool src_local) -{ - struct scif_dma_comp_cb *comp_cb = work->comp_cb; - dma_addr_t window_dma_addr, temp_dma_addr; - dma_addr_t temp_phys = comp_cb->temp_phys; - size_t loop_len, nr_contig_bytes = 0, remaining_len = work->len; - int offset_in_ca, ret = 0; - s64 end_offset, offset; - struct scif_window *window; - void *window_virt_addr; - size_t tail_len; - struct dma_async_tx_descriptor *tx; - struct dma_device *dev = chan->device; - dma_cookie_t cookie; - - if (src_local) { - offset = work->dst_offset; - window = work->dst_window; - } else { - offset = work->src_offset; - window = work->src_window; - } - - offset_in_ca = offset & (L1_CACHE_BYTES - 1); - if (offset_in_ca) { - loop_len = L1_CACHE_BYTES - offset_in_ca; - loop_len = min(loop_len, remaining_len); - window_virt_addr = ioremap_remote(offset, window, - loop_len, - work->remote_dev, - NULL); - if (!window_virt_addr) - return -ENOMEM; - if (src_local) - scif_unaligned_cpy_toio(window_virt_addr, temp, - loop_len, - work->ordered && - !(remaining_len - loop_len)); - else - scif_unaligned_cpy_fromio(temp, window_virt_addr, - loop_len, work->ordered && - !(remaining_len - loop_len)); - iounmap_remote(window_virt_addr, loop_len, work); - - offset += loop_len; - temp += loop_len; - temp_phys += loop_len; - remaining_len -= loop_len; - } - - offset_in_ca = offset & ~PAGE_MASK; - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - - tail_len = remaining_len & (L1_CACHE_BYTES - 1); - remaining_len -= tail_len; - while (remaining_len) { - if (offset == end_offset) { - window = list_next_entry(window, list); - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - } - if (scif_is_mgmt_node()) - temp_dma_addr = temp_phys; - else - /* Fix if we ever enable IOMMU on the card */ - temp_dma_addr = (dma_addr_t)virt_to_phys(temp); - window_dma_addr = scif_off_to_dma_addr(window, offset, - &nr_contig_bytes, - NULL); - loop_len = min(nr_contig_bytes, remaining_len); - if (src_local) { - if (work->ordered && !tail_len && - !(remaining_len - loop_len) && - loop_len != L1_CACHE_BYTES) { - /* - * Break up the last chunk of the transfer into - * two steps. if there is no tail to guarantee - * DMA ordering. SCIF_DMA_POLLING inserts - * a status update descriptor in step 1 which - * acts as a double sided synchronization fence - * for the DMA engine to ensure that the last - * cache line in step 2 is updated last. - */ - /* Step 1) DMA: Body Length - L1_CACHE_BYTES. */ - tx = - dev->device_prep_dma_memcpy(chan, - window_dma_addr, - temp_dma_addr, - loop_len - - L1_CACHE_BYTES, - DMA_PREP_FENCE); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - offset += (loop_len - L1_CACHE_BYTES); - temp_dma_addr += (loop_len - L1_CACHE_BYTES); - window_dma_addr += (loop_len - L1_CACHE_BYTES); - remaining_len -= (loop_len - L1_CACHE_BYTES); - loop_len = remaining_len; - - /* Step 2) DMA: L1_CACHE_BYTES */ - tx = - dev->device_prep_dma_memcpy(chan, - window_dma_addr, - temp_dma_addr, - loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } else { - tx = - dev->device_prep_dma_memcpy(chan, - window_dma_addr, - temp_dma_addr, - loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } - } else { - tx = dev->device_prep_dma_memcpy(chan, temp_dma_addr, - window_dma_addr, loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } - offset += loop_len; - temp += loop_len; - temp_phys += loop_len; - remaining_len -= loop_len; - offset_in_ca = 0; - } - if (tail_len) { - if (offset == end_offset) { - window = list_next_entry(window, list); - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - } - window_virt_addr = ioremap_remote(offset, window, tail_len, - work->remote_dev, - NULL); - if (!window_virt_addr) - return -ENOMEM; - /* - * The CPU copy for the tail bytes must be initiated only once - * previous DMA transfers for this endpoint have completed - * to guarantee ordering. - */ - if (work->ordered) { - struct scif_dev *rdev = work->remote_dev; - - ret = scif_drain_dma_intr(rdev->sdev, chan); - if (ret) - return ret; - } - if (src_local) - scif_unaligned_cpy_toio(window_virt_addr, temp, - tail_len, work->ordered); - else - scif_unaligned_cpy_fromio(temp, window_virt_addr, - tail_len, work->ordered); - iounmap_remote(window_virt_addr, tail_len, work); - } - tx = dev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_INTERRUPT); - if (!tx) { - ret = -ENOMEM; - return ret; - } - tx->callback = &scif_rma_completion_cb; - tx->callback_param = comp_cb; - cookie = tx->tx_submit(tx); - - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - return ret; - } - dma_async_issue_pending(chan); - return 0; -err: - dev_err(scif_info.mdev.this_device, - "%s %d Desc Prog Failed ret %d\n", - __func__, __LINE__, ret); - return ret; -} - -/* - * _scif_rma_list_dma_copy_aligned: - * - * Traverse all the windows and perform DMA copy. - */ -static int _scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, - struct dma_chan *chan) -{ - dma_addr_t src_dma_addr, dst_dma_addr; - size_t loop_len, remaining_len, src_contig_bytes = 0; - size_t dst_contig_bytes = 0; - struct scif_window_iter src_win_iter; - struct scif_window_iter dst_win_iter; - s64 end_src_offset, end_dst_offset; - struct scif_window *src_window = work->src_window; - struct scif_window *dst_window = work->dst_window; - s64 src_offset = work->src_offset, dst_offset = work->dst_offset; - int ret = 0; - struct dma_async_tx_descriptor *tx; - struct dma_device *dev = chan->device; - dma_cookie_t cookie; - - remaining_len = work->len; - - scif_init_window_iter(src_window, &src_win_iter); - scif_init_window_iter(dst_window, &dst_win_iter); - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - while (remaining_len) { - if (src_offset == end_src_offset) { - src_window = list_next_entry(src_window, list); - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - scif_init_window_iter(src_window, &src_win_iter); - } - if (dst_offset == end_dst_offset) { - dst_window = list_next_entry(dst_window, list); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - scif_init_window_iter(dst_window, &dst_win_iter); - } - - /* compute dma addresses for transfer */ - src_dma_addr = scif_off_to_dma_addr(src_window, src_offset, - &src_contig_bytes, - &src_win_iter); - dst_dma_addr = scif_off_to_dma_addr(dst_window, dst_offset, - &dst_contig_bytes, - &dst_win_iter); - loop_len = min(src_contig_bytes, dst_contig_bytes); - loop_len = min(loop_len, remaining_len); - if (work->ordered && !(remaining_len - loop_len)) { - /* - * Break up the last chunk of the transfer into two - * steps to ensure that the last byte in step 2 is - * updated last. - */ - /* Step 1) DMA: Body Length - 1 */ - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, - loop_len - 1, - DMA_PREP_FENCE); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - src_offset += (loop_len - 1); - dst_offset += (loop_len - 1); - src_dma_addr += (loop_len - 1); - dst_dma_addr += (loop_len - 1); - remaining_len -= (loop_len - 1); - loop_len = remaining_len; - - /* Step 2) DMA: 1 BYTES */ - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } else { - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - } - src_offset += loop_len; - dst_offset += loop_len; - remaining_len -= loop_len; - } - return ret; -err: - dev_err(scif_info.mdev.this_device, - "%s %d Desc Prog Failed ret %d\n", - __func__, __LINE__, ret); - return ret; -} - -/* - * scif_rma_list_dma_copy_aligned: - * - * Traverse all the windows and perform DMA copy. - */ -static int scif_rma_list_dma_copy_aligned(struct scif_copy_work *work, - struct dma_chan *chan) -{ - dma_addr_t src_dma_addr, dst_dma_addr; - size_t loop_len, remaining_len, tail_len, src_contig_bytes = 0; - size_t dst_contig_bytes = 0; - int src_cache_off; - s64 end_src_offset, end_dst_offset; - struct scif_window_iter src_win_iter; - struct scif_window_iter dst_win_iter; - void *src_virt, *dst_virt; - struct scif_window *src_window = work->src_window; - struct scif_window *dst_window = work->dst_window; - s64 src_offset = work->src_offset, dst_offset = work->dst_offset; - int ret = 0; - struct dma_async_tx_descriptor *tx; - struct dma_device *dev = chan->device; - dma_cookie_t cookie; - - remaining_len = work->len; - scif_init_window_iter(src_window, &src_win_iter); - scif_init_window_iter(dst_window, &dst_win_iter); - - src_cache_off = src_offset & (L1_CACHE_BYTES - 1); - if (src_cache_off != 0) { - /* Head */ - loop_len = L1_CACHE_BYTES - src_cache_off; - loop_len = min(loop_len, remaining_len); - src_dma_addr = __scif_off_to_dma_addr(src_window, src_offset); - dst_dma_addr = __scif_off_to_dma_addr(dst_window, dst_offset); - if (src_window->type == SCIF_WINDOW_SELF) - src_virt = _get_local_va(src_offset, src_window, - loop_len); - else - src_virt = ioremap_remote(src_offset, src_window, - loop_len, - work->remote_dev, NULL); - if (!src_virt) - return -ENOMEM; - if (dst_window->type == SCIF_WINDOW_SELF) - dst_virt = _get_local_va(dst_offset, dst_window, - loop_len); - else - dst_virt = ioremap_remote(dst_offset, dst_window, - loop_len, - work->remote_dev, NULL); - if (!dst_virt) { - if (src_window->type != SCIF_WINDOW_SELF) - iounmap_remote(src_virt, loop_len, work); - return -ENOMEM; - } - if (src_window->type == SCIF_WINDOW_SELF) - scif_unaligned_cpy_toio(dst_virt, src_virt, loop_len, - remaining_len == loop_len ? - work->ordered : false); - else - scif_unaligned_cpy_fromio(dst_virt, src_virt, loop_len, - remaining_len == loop_len ? - work->ordered : false); - if (src_window->type != SCIF_WINDOW_SELF) - iounmap_remote(src_virt, loop_len, work); - if (dst_window->type != SCIF_WINDOW_SELF) - iounmap_remote(dst_virt, loop_len, work); - src_offset += loop_len; - dst_offset += loop_len; - remaining_len -= loop_len; - } - - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - tail_len = remaining_len & (L1_CACHE_BYTES - 1); - remaining_len -= tail_len; - while (remaining_len) { - if (src_offset == end_src_offset) { - src_window = list_next_entry(src_window, list); - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - scif_init_window_iter(src_window, &src_win_iter); - } - if (dst_offset == end_dst_offset) { - dst_window = list_next_entry(dst_window, list); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - scif_init_window_iter(dst_window, &dst_win_iter); - } - - /* compute dma addresses for transfer */ - src_dma_addr = scif_off_to_dma_addr(src_window, src_offset, - &src_contig_bytes, - &src_win_iter); - dst_dma_addr = scif_off_to_dma_addr(dst_window, dst_offset, - &dst_contig_bytes, - &dst_win_iter); - loop_len = min(src_contig_bytes, dst_contig_bytes); - loop_len = min(loop_len, remaining_len); - if (work->ordered && !tail_len && - !(remaining_len - loop_len)) { - /* - * Break up the last chunk of the transfer into two - * steps. if there is no tail to gurantee DMA ordering. - * Passing SCIF_DMA_POLLING inserts a status update - * descriptor in step 1 which acts as a double sided - * synchronization fence for the DMA engine to ensure - * that the last cache line in step 2 is updated last. - */ - /* Step 1) DMA: Body Length - L1_CACHE_BYTES. */ - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, - loop_len - - L1_CACHE_BYTES, - DMA_PREP_FENCE); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - src_offset += (loop_len - L1_CACHE_BYTES); - dst_offset += (loop_len - L1_CACHE_BYTES); - src_dma_addr += (loop_len - L1_CACHE_BYTES); - dst_dma_addr += (loop_len - L1_CACHE_BYTES); - remaining_len -= (loop_len - L1_CACHE_BYTES); - loop_len = remaining_len; - - /* Step 2) DMA: L1_CACHE_BYTES */ - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, - loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } else { - tx = dev->device_prep_dma_memcpy(chan, dst_dma_addr, - src_dma_addr, - loop_len, 0); - if (!tx) { - ret = -ENOMEM; - goto err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - ret = -ENOMEM; - goto err; - } - dma_async_issue_pending(chan); - } - src_offset += loop_len; - dst_offset += loop_len; - remaining_len -= loop_len; - } - remaining_len = tail_len; - if (remaining_len) { - loop_len = remaining_len; - if (src_offset == end_src_offset) - src_window = list_next_entry(src_window, list); - if (dst_offset == end_dst_offset) - dst_window = list_next_entry(dst_window, list); - - src_dma_addr = __scif_off_to_dma_addr(src_window, src_offset); - dst_dma_addr = __scif_off_to_dma_addr(dst_window, dst_offset); - /* - * The CPU copy for the tail bytes must be initiated only once - * previous DMA transfers for this endpoint have completed to - * guarantee ordering. - */ - if (work->ordered) { - struct scif_dev *rdev = work->remote_dev; - - ret = scif_drain_dma_poll(rdev->sdev, chan); - if (ret) - return ret; - } - if (src_window->type == SCIF_WINDOW_SELF) - src_virt = _get_local_va(src_offset, src_window, - loop_len); - else - src_virt = ioremap_remote(src_offset, src_window, - loop_len, - work->remote_dev, NULL); - if (!src_virt) - return -ENOMEM; - - if (dst_window->type == SCIF_WINDOW_SELF) - dst_virt = _get_local_va(dst_offset, dst_window, - loop_len); - else - dst_virt = ioremap_remote(dst_offset, dst_window, - loop_len, - work->remote_dev, NULL); - if (!dst_virt) { - if (src_window->type != SCIF_WINDOW_SELF) - iounmap_remote(src_virt, loop_len, work); - return -ENOMEM; - } - - if (src_window->type == SCIF_WINDOW_SELF) - scif_unaligned_cpy_toio(dst_virt, src_virt, loop_len, - work->ordered); - else - scif_unaligned_cpy_fromio(dst_virt, src_virt, - loop_len, work->ordered); - if (src_window->type != SCIF_WINDOW_SELF) - iounmap_remote(src_virt, loop_len, work); - - if (dst_window->type != SCIF_WINDOW_SELF) - iounmap_remote(dst_virt, loop_len, work); - remaining_len -= loop_len; - } - return ret; -err: - dev_err(scif_info.mdev.this_device, - "%s %d Desc Prog Failed ret %d\n", - __func__, __LINE__, ret); - return ret; -} - -/* - * scif_rma_list_cpu_copy: - * - * Traverse all the windows and perform CPU copy. - */ -static int scif_rma_list_cpu_copy(struct scif_copy_work *work) -{ - void *src_virt, *dst_virt; - size_t loop_len, remaining_len; - int src_page_off, dst_page_off; - s64 src_offset = work->src_offset, dst_offset = work->dst_offset; - struct scif_window *src_window = work->src_window; - struct scif_window *dst_window = work->dst_window; - s64 end_src_offset, end_dst_offset; - int ret = 0; - struct scif_window_iter src_win_iter; - struct scif_window_iter dst_win_iter; - - remaining_len = work->len; - - scif_init_window_iter(src_window, &src_win_iter); - scif_init_window_iter(dst_window, &dst_win_iter); - while (remaining_len) { - src_page_off = src_offset & ~PAGE_MASK; - dst_page_off = dst_offset & ~PAGE_MASK; - loop_len = min(PAGE_SIZE - - max(src_page_off, dst_page_off), - remaining_len); - - if (src_window->type == SCIF_WINDOW_SELF) - src_virt = _get_local_va(src_offset, src_window, - loop_len); - else - src_virt = ioremap_remote(src_offset, src_window, - loop_len, - work->remote_dev, - &src_win_iter); - if (!src_virt) { - ret = -ENOMEM; - goto error; - } - - if (dst_window->type == SCIF_WINDOW_SELF) - dst_virt = _get_local_va(dst_offset, dst_window, - loop_len); - else - dst_virt = ioremap_remote(dst_offset, dst_window, - loop_len, - work->remote_dev, - &dst_win_iter); - if (!dst_virt) { - if (src_window->type == SCIF_WINDOW_PEER) - iounmap_remote(src_virt, loop_len, work); - ret = -ENOMEM; - goto error; - } - - if (work->loopback) { - memcpy(dst_virt, src_virt, loop_len); - } else { - if (src_window->type == SCIF_WINDOW_SELF) - memcpy_toio((void __iomem __force *)dst_virt, - src_virt, loop_len); - else - memcpy_fromio(dst_virt, - (void __iomem __force *)src_virt, - loop_len); - } - if (src_window->type == SCIF_WINDOW_PEER) - iounmap_remote(src_virt, loop_len, work); - - if (dst_window->type == SCIF_WINDOW_PEER) - iounmap_remote(dst_virt, loop_len, work); - - src_offset += loop_len; - dst_offset += loop_len; - remaining_len -= loop_len; - if (remaining_len) { - end_src_offset = src_window->offset + - (src_window->nr_pages << PAGE_SHIFT); - end_dst_offset = dst_window->offset + - (dst_window->nr_pages << PAGE_SHIFT); - if (src_offset == end_src_offset) { - src_window = list_next_entry(src_window, list); - scif_init_window_iter(src_window, - &src_win_iter); - } - if (dst_offset == end_dst_offset) { - dst_window = list_next_entry(dst_window, list); - scif_init_window_iter(dst_window, - &dst_win_iter); - } - } - } -error: - return ret; -} - -static int scif_rma_list_dma_copy_wrapper(struct scif_endpt *epd, - struct scif_copy_work *work, - struct dma_chan *chan, off_t loffset) -{ - int src_cache_off, dst_cache_off; - s64 src_offset = work->src_offset, dst_offset = work->dst_offset; - u8 *temp = NULL; - bool src_local = true; - struct scif_dma_comp_cb *comp_cb; - int err; - - if (is_dma_copy_aligned(chan->device, 1, 1, 1)) - return _scif_rma_list_dma_copy_aligned(work, chan); - - src_cache_off = src_offset & (L1_CACHE_BYTES - 1); - dst_cache_off = dst_offset & (L1_CACHE_BYTES - 1); - - if (dst_cache_off == src_cache_off) - return scif_rma_list_dma_copy_aligned(work, chan); - - if (work->loopback) - return scif_rma_list_cpu_copy(work); - src_local = work->src_window->type == SCIF_WINDOW_SELF; - - /* Allocate dma_completion cb */ - comp_cb = kzalloc(sizeof(*comp_cb), GFP_KERNEL); - if (!comp_cb) - goto error; - - work->comp_cb = comp_cb; - comp_cb->cb_cookie = comp_cb; - comp_cb->dma_completion_func = &scif_rma_completion_cb; - - if (work->len + (L1_CACHE_BYTES << 1) < SCIF_KMEM_UNALIGNED_BUF_SIZE) { - comp_cb->is_cache = false; - /* Allocate padding bytes to align to a cache line */ - temp = kmalloc(work->len + (L1_CACHE_BYTES << 1), - GFP_KERNEL); - if (!temp) - goto free_comp_cb; - comp_cb->temp_buf_to_free = temp; - /* kmalloc(..) does not guarantee cache line alignment */ - if (!IS_ALIGNED((u64)temp, L1_CACHE_BYTES)) - temp = PTR_ALIGN(temp, L1_CACHE_BYTES); - } else { - comp_cb->is_cache = true; - temp = kmem_cache_alloc(unaligned_cache, GFP_KERNEL); - if (!temp) - goto free_comp_cb; - comp_cb->temp_buf_to_free = temp; - } - - if (src_local) { - temp += dst_cache_off; - scif_rma_local_cpu_copy(work->src_offset, work->src_window, - temp, work->len, true); - } else { - comp_cb->dst_window = work->dst_window; - comp_cb->dst_offset = work->dst_offset; - work->src_offset = work->src_offset - src_cache_off; - comp_cb->len = work->len; - work->len = ALIGN(work->len + src_cache_off, L1_CACHE_BYTES); - comp_cb->header_padding = src_cache_off; - } - comp_cb->temp_buf = temp; - - err = scif_map_single(&comp_cb->temp_phys, temp, - work->remote_dev, SCIF_KMEM_UNALIGNED_BUF_SIZE); - if (err) - goto free_temp_buf; - comp_cb->sdev = work->remote_dev; - if (scif_rma_list_dma_copy_unaligned(work, temp, chan, src_local) < 0) - goto free_temp_buf; - if (!src_local) - work->fence_type = SCIF_DMA_INTR; - return 0; -free_temp_buf: - if (comp_cb->is_cache) - kmem_cache_free(unaligned_cache, comp_cb->temp_buf_to_free); - else - kfree(comp_cb->temp_buf_to_free); -free_comp_cb: - kfree(comp_cb); -error: - return -ENOMEM; -} - -/** - * scif_rma_copy: - * @epd: end point descriptor. - * @loffset: offset in local registered address space to/from which to copy - * @addr: user virtual address to/from which to copy - * @len: length of range to copy - * @roffset: offset in remote registered address space to/from which to copy - * @flags: flags - * @dir: LOCAL->REMOTE or vice versa. - * @last_chunk: true if this is the last chunk of a larger transfer - * - * Validate parameters, check if src/dst registered ranges requested for copy - * are valid and initiate either CPU or DMA copy. - */ -static int scif_rma_copy(scif_epd_t epd, off_t loffset, unsigned long addr, - size_t len, off_t roffset, int flags, - enum scif_rma_dir dir, bool last_chunk) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_rma_req remote_req; - struct scif_rma_req req; - struct scif_window *local_window = NULL; - struct scif_window *remote_window = NULL; - struct scif_copy_work copy_work; - bool loopback; - int err = 0; - struct dma_chan *chan; - struct scif_mmu_notif *mmn = NULL; - bool cache = false; - struct device *spdev; - - err = scif_verify_epd(ep); - if (err) - return err; - - if (flags && !(flags & (SCIF_RMA_USECPU | SCIF_RMA_USECACHE | - SCIF_RMA_SYNC | SCIF_RMA_ORDERED))) - return -EINVAL; - - loopback = scifdev_self(ep->remote_dev) ? true : false; - copy_work.fence_type = ((flags & SCIF_RMA_SYNC) && last_chunk) ? - SCIF_DMA_POLL : 0; - copy_work.ordered = !!((flags & SCIF_RMA_ORDERED) && last_chunk); - - /* Use CPU for Mgmt node <-> Mgmt node copies */ - if (loopback && scif_is_mgmt_node()) { - flags |= SCIF_RMA_USECPU; - copy_work.fence_type = 0x0; - } - - cache = scif_is_set_reg_cache(flags); - - remote_req.out_window = &remote_window; - remote_req.offset = roffset; - remote_req.nr_bytes = len; - /* - * If transfer is from local to remote then the remote window - * must be writeable and vice versa. - */ - remote_req.prot = dir == SCIF_LOCAL_TO_REMOTE ? VM_WRITE : VM_READ; - remote_req.type = SCIF_WINDOW_PARTIAL; - remote_req.head = &ep->rma_info.remote_reg_list; - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - return err; - } - - if (addr && cache) { - mutex_lock(&ep->rma_info.mmn_lock); - mmn = scif_find_mmu_notifier(current->mm, &ep->rma_info); - if (!mmn) - mmn = scif_add_mmu_notifier(current->mm, ep); - mutex_unlock(&ep->rma_info.mmn_lock); - if (IS_ERR(mmn)) { - scif_put_peer_dev(spdev); - return PTR_ERR(mmn); - } - cache = cache && !scif_rma_tc_can_cache(ep, len); - } - mutex_lock(&ep->rma_info.rma_lock); - if (addr) { - req.out_window = &local_window; - req.nr_bytes = ALIGN(len + (addr & ~PAGE_MASK), - PAGE_SIZE); - req.va_for_temp = addr & PAGE_MASK; - req.prot = (dir == SCIF_LOCAL_TO_REMOTE ? - VM_READ : VM_WRITE | VM_READ); - /* Does a valid local window exist? */ - if (mmn) { - spin_lock(&ep->rma_info.tc_lock); - req.head = &mmn->tc_reg_list; - err = scif_query_tcw(ep, &req); - spin_unlock(&ep->rma_info.tc_lock); - } - if (!mmn || err) { - err = scif_register_temp(epd, req.va_for_temp, - req.nr_bytes, req.prot, - &loffset, &local_window); - if (err) { - mutex_unlock(&ep->rma_info.rma_lock); - goto error; - } - if (!cache) - goto skip_cache; - atomic_inc(&ep->rma_info.tcw_refcount); - atomic_add_return(local_window->nr_pages, - &ep->rma_info.tcw_total_pages); - if (mmn) { - spin_lock(&ep->rma_info.tc_lock); - scif_insert_tcw(local_window, - &mmn->tc_reg_list); - spin_unlock(&ep->rma_info.tc_lock); - } - } -skip_cache: - loffset = local_window->offset + - (addr - local_window->va_for_temp); - } else { - req.out_window = &local_window; - req.offset = loffset; - /* - * If transfer is from local to remote then the self window - * must be readable and vice versa. - */ - req.prot = dir == SCIF_LOCAL_TO_REMOTE ? VM_READ : VM_WRITE; - req.nr_bytes = len; - req.type = SCIF_WINDOW_PARTIAL; - req.head = &ep->rma_info.reg_list; - /* Does a valid local window exist? */ - err = scif_query_window(&req); - if (err) { - mutex_unlock(&ep->rma_info.rma_lock); - goto error; - } - } - - /* Does a valid remote window exist? */ - err = scif_query_window(&remote_req); - if (err) { - mutex_unlock(&ep->rma_info.rma_lock); - goto error; - } - - /* - * Prepare copy_work for submitting work to the DMA kernel thread - * or CPU copy routine. - */ - copy_work.len = len; - copy_work.loopback = loopback; - copy_work.remote_dev = ep->remote_dev; - if (dir == SCIF_LOCAL_TO_REMOTE) { - copy_work.src_offset = loffset; - copy_work.src_window = local_window; - copy_work.dst_offset = roffset; - copy_work.dst_window = remote_window; - } else { - copy_work.src_offset = roffset; - copy_work.src_window = remote_window; - copy_work.dst_offset = loffset; - copy_work.dst_window = local_window; - } - - if (flags & SCIF_RMA_USECPU) { - scif_rma_list_cpu_copy(©_work); - } else { - chan = ep->rma_info.dma_chan; - err = scif_rma_list_dma_copy_wrapper(epd, ©_work, - chan, loffset); - } - if (addr && !cache) - atomic_inc(&ep->rma_info.tw_refcount); - - mutex_unlock(&ep->rma_info.rma_lock); - - if (last_chunk) { - struct scif_dev *rdev = ep->remote_dev; - - if (copy_work.fence_type == SCIF_DMA_POLL) - err = scif_drain_dma_poll(rdev->sdev, - ep->rma_info.dma_chan); - else if (copy_work.fence_type == SCIF_DMA_INTR) - err = scif_drain_dma_intr(rdev->sdev, - ep->rma_info.dma_chan); - } - - if (addr && !cache) - scif_queue_for_cleanup(local_window, &scif_info.rma); - scif_put_peer_dev(spdev); - return err; -error: - if (err) { - if (addr && local_window && !cache) - scif_destroy_window(ep, local_window); - dev_err(scif_info.mdev.this_device, - "%s %d err %d len 0x%lx\n", - __func__, __LINE__, err, len); - } - scif_put_peer_dev(spdev); - return err; -} - -int scif_readfrom(scif_epd_t epd, off_t loffset, size_t len, - off_t roffset, int flags) -{ - int err; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI readfrom: ep %p loffset 0x%lx len 0x%lx offset 0x%lx flags 0x%x\n", - epd, loffset, len, roffset, flags); - if (scif_unaligned(loffset, roffset)) { - while (len > SCIF_MAX_UNALIGNED_BUF_SIZE) { - err = scif_rma_copy(epd, loffset, 0x0, - SCIF_MAX_UNALIGNED_BUF_SIZE, - roffset, flags, - SCIF_REMOTE_TO_LOCAL, false); - if (err) - goto readfrom_err; - loffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - roffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - len -= SCIF_MAX_UNALIGNED_BUF_SIZE; - } - } - err = scif_rma_copy(epd, loffset, 0x0, len, - roffset, flags, SCIF_REMOTE_TO_LOCAL, true); -readfrom_err: - return err; -} -EXPORT_SYMBOL_GPL(scif_readfrom); - -int scif_writeto(scif_epd_t epd, off_t loffset, size_t len, - off_t roffset, int flags) -{ - int err; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI writeto: ep %p loffset 0x%lx len 0x%lx roffset 0x%lx flags 0x%x\n", - epd, loffset, len, roffset, flags); - if (scif_unaligned(loffset, roffset)) { - while (len > SCIF_MAX_UNALIGNED_BUF_SIZE) { - err = scif_rma_copy(epd, loffset, 0x0, - SCIF_MAX_UNALIGNED_BUF_SIZE, - roffset, flags, - SCIF_LOCAL_TO_REMOTE, false); - if (err) - goto writeto_err; - loffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - roffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - len -= SCIF_MAX_UNALIGNED_BUF_SIZE; - } - } - err = scif_rma_copy(epd, loffset, 0x0, len, - roffset, flags, SCIF_LOCAL_TO_REMOTE, true); -writeto_err: - return err; -} -EXPORT_SYMBOL_GPL(scif_writeto); - -int scif_vreadfrom(scif_epd_t epd, void *addr, size_t len, - off_t roffset, int flags) -{ - int err; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI vreadfrom: ep %p addr %p len 0x%lx roffset 0x%lx flags 0x%x\n", - epd, addr, len, roffset, flags); - if (scif_unaligned((off_t __force)addr, roffset)) { - if (len > SCIF_MAX_UNALIGNED_BUF_SIZE) - flags &= ~SCIF_RMA_USECACHE; - - while (len > SCIF_MAX_UNALIGNED_BUF_SIZE) { - err = scif_rma_copy(epd, 0, (u64)addr, - SCIF_MAX_UNALIGNED_BUF_SIZE, - roffset, flags, - SCIF_REMOTE_TO_LOCAL, false); - if (err) - goto vreadfrom_err; - addr += SCIF_MAX_UNALIGNED_BUF_SIZE; - roffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - len -= SCIF_MAX_UNALIGNED_BUF_SIZE; - } - } - err = scif_rma_copy(epd, 0, (u64)addr, len, - roffset, flags, SCIF_REMOTE_TO_LOCAL, true); -vreadfrom_err: - return err; -} -EXPORT_SYMBOL_GPL(scif_vreadfrom); - -int scif_vwriteto(scif_epd_t epd, void *addr, size_t len, - off_t roffset, int flags) -{ - int err; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI vwriteto: ep %p addr %p len 0x%lx roffset 0x%lx flags 0x%x\n", - epd, addr, len, roffset, flags); - if (scif_unaligned((off_t __force)addr, roffset)) { - if (len > SCIF_MAX_UNALIGNED_BUF_SIZE) - flags &= ~SCIF_RMA_USECACHE; - - while (len > SCIF_MAX_UNALIGNED_BUF_SIZE) { - err = scif_rma_copy(epd, 0, (u64)addr, - SCIF_MAX_UNALIGNED_BUF_SIZE, - roffset, flags, - SCIF_LOCAL_TO_REMOTE, false); - if (err) - goto vwriteto_err; - addr += SCIF_MAX_UNALIGNED_BUF_SIZE; - roffset += SCIF_MAX_UNALIGNED_BUF_SIZE; - len -= SCIF_MAX_UNALIGNED_BUF_SIZE; - } - } - err = scif_rma_copy(epd, 0, (u64)addr, len, - roffset, flags, SCIF_LOCAL_TO_REMOTE, true); -vwriteto_err: - return err; -} -EXPORT_SYMBOL_GPL(scif_vwriteto); diff --git a/drivers/misc/mic/scif/scif_epd.c b/drivers/misc/mic/scif/scif_epd.c deleted file mode 100644 index 426687f6696b..000000000000 --- a/drivers/misc/mic/scif/scif_epd.c +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" -#include "scif_map.h" - -void scif_cleanup_ep_qp(struct scif_endpt *ep) -{ - struct scif_qp *qp = ep->qp_info.qp; - - if (qp->outbound_q.rb_base) { - scif_iounmap((void *)qp->outbound_q.rb_base, - qp->outbound_q.size, ep->remote_dev); - qp->outbound_q.rb_base = NULL; - } - if (qp->remote_qp) { - scif_iounmap((void *)qp->remote_qp, - sizeof(struct scif_qp), ep->remote_dev); - qp->remote_qp = NULL; - } - if (qp->local_qp) { - scif_unmap_single(qp->local_qp, ep->remote_dev, - sizeof(struct scif_qp)); - qp->local_qp = 0x0; - } - if (qp->local_buf) { - scif_unmap_single(qp->local_buf, ep->remote_dev, - SCIF_ENDPT_QP_SIZE); - qp->local_buf = 0; - } -} - -void scif_teardown_ep(void *endpt) -{ - struct scif_endpt *ep = endpt; - struct scif_qp *qp = ep->qp_info.qp; - - if (qp) { - spin_lock(&ep->lock); - scif_cleanup_ep_qp(ep); - spin_unlock(&ep->lock); - kfree(qp->inbound_q.rb_base); - kfree(qp); - } -} - -/* - * Enqueue the endpoint to the zombie list for cleanup. - * The endpoint should not be accessed once this API returns. - */ -void scif_add_epd_to_zombie_list(struct scif_endpt *ep, bool eplock_held) -{ - if (!eplock_held) - mutex_lock(&scif_info.eplock); - spin_lock(&ep->lock); - ep->state = SCIFEP_ZOMBIE; - spin_unlock(&ep->lock); - list_add_tail(&ep->list, &scif_info.zombie); - scif_info.nr_zombies++; - if (!eplock_held) - mutex_unlock(&scif_info.eplock); - schedule_work(&scif_info.misc_work); -} - -static struct scif_endpt *scif_find_listen_ep(u16 port) -{ - struct scif_endpt *ep = NULL; - struct list_head *pos, *tmpq; - - mutex_lock(&scif_info.eplock); - list_for_each_safe(pos, tmpq, &scif_info.listen) { - ep = list_entry(pos, struct scif_endpt, list); - if (ep->port.port == port) { - mutex_unlock(&scif_info.eplock); - return ep; - } - } - mutex_unlock(&scif_info.eplock); - return NULL; -} - -void scif_cleanup_zombie_epd(void) -{ - struct list_head *pos, *tmpq; - struct scif_endpt *ep; - - mutex_lock(&scif_info.eplock); - list_for_each_safe(pos, tmpq, &scif_info.zombie) { - ep = list_entry(pos, struct scif_endpt, list); - if (scif_rma_ep_can_uninit(ep)) { - list_del(pos); - scif_info.nr_zombies--; - put_iova_domain(&ep->rma_info.iovad); - kfree(ep); - } - } - mutex_unlock(&scif_info.eplock); -} - -/** - * scif_cnctreq() - Respond to SCIF_CNCT_REQ interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * This message is initiated by the remote node to request a connection - * to the local node. This function looks for an end point in the - * listen state on the requested port id. - * - * If it finds a listening port it places the connect request on the - * listening end points queue and wakes up any pending accept calls. - * - * If it does not find a listening end point it sends a connection - * reject message to the remote node. - */ -void scif_cnctreq(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = NULL; - struct scif_conreq *conreq; - - conreq = kmalloc(sizeof(*conreq), GFP_KERNEL); - if (!conreq) - /* Lack of resources so reject the request. */ - goto conreq_sendrej; - - ep = scif_find_listen_ep(msg->dst.port); - if (!ep) - /* Send reject due to no listening ports */ - goto conreq_sendrej_free; - else - spin_lock(&ep->lock); - - if (ep->backlog <= ep->conreqcnt) { - /* Send reject due to too many pending requests */ - spin_unlock(&ep->lock); - goto conreq_sendrej_free; - } - - conreq->msg = *msg; - list_add_tail(&conreq->list, &ep->conlist); - ep->conreqcnt++; - wake_up_interruptible(&ep->conwq); - spin_unlock(&ep->lock); - return; - -conreq_sendrej_free: - kfree(conreq); -conreq_sendrej: - msg->uop = SCIF_CNCT_REJ; - scif_nodeqp_send(&scif_dev[msg->src.node], msg); -} - -/** - * scif_cnctgnt() - Respond to SCIF_CNCT_GNT interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * An accept() on the remote node has occurred and sent this message - * to indicate success. Place the end point in the MAPPING state and - * save the remote nodes memory information. Then wake up the connect - * request so it can finish. - */ -void scif_cnctgnt(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - if (SCIFEP_CONNECTING == ep->state) { - ep->peer.node = msg->src.node; - ep->peer.port = msg->src.port; - ep->qp_info.gnt_pld = msg->payload[1]; - ep->remote_ep = msg->payload[2]; - ep->state = SCIFEP_MAPPING; - - wake_up(&ep->conwq); - } - spin_unlock(&ep->lock); -} - -/** - * scif_cnctgnt_ack() - Respond to SCIF_CNCT_GNTACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The remote connection request has finished mapping the local memory. - * Place the connection in the connected state and wake up the pending - * accept() call. - */ -void scif_cnctgnt_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - mutex_lock(&scif_info.connlock); - spin_lock(&ep->lock); - /* New ep is now connected with all resources set. */ - ep->state = SCIFEP_CONNECTED; - list_add_tail(&ep->list, &scif_info.connected); - wake_up(&ep->conwq); - spin_unlock(&ep->lock); - mutex_unlock(&scif_info.connlock); -} - -/** - * scif_cnctgnt_nack() - Respond to SCIF_CNCT_GNTNACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The remote connection request failed to map the local memory it was sent. - * Place the end point in the CLOSING state to indicate it and wake up - * the pending accept(); - */ -void scif_cnctgnt_nack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - ep->state = SCIFEP_CLOSING; - wake_up(&ep->conwq); - spin_unlock(&ep->lock); -} - -/** - * scif_cnctrej() - Respond to SCIF_CNCT_REJ interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The remote end has rejected the connection request. Set the end - * point back to the bound state and wake up the pending connect(). - */ -void scif_cnctrej(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - if (SCIFEP_CONNECTING == ep->state) { - ep->state = SCIFEP_BOUND; - wake_up(&ep->conwq); - } - spin_unlock(&ep->lock); -} - -/** - * scif_discnct() - Respond to SCIF_DISCNCT interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The remote node has indicated close() has been called on its end - * point. Remove the local end point from the connected list, set its - * state to disconnected and ensure accesses to the remote node are - * shutdown. - * - * When all accesses to the remote end have completed then send a - * DISCNT_ACK to indicate it can remove its resources and complete - * the close routine. - */ -void scif_discnct(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = NULL; - struct scif_endpt *tmpep; - struct list_head *pos, *tmpq; - - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.connected) { - tmpep = list_entry(pos, struct scif_endpt, list); - /* - * The local ep may have sent a disconnect and and been closed - * due to a message response time out. It may have been - * allocated again and formed a new connection so we want to - * check if the remote ep matches - */ - if (((u64)tmpep == msg->payload[1]) && - ((u64)tmpep->remote_ep == msg->payload[0])) { - list_del(pos); - ep = tmpep; - spin_lock(&ep->lock); - break; - } - } - - /* - * If the terminated end is not found then this side started closing - * before the other side sent the disconnect. If so the ep will no - * longer be on the connected list. Regardless the other side - * needs to be acked to let it know close is complete. - */ - if (!ep) { - mutex_unlock(&scif_info.connlock); - goto discnct_ack; - } - - ep->state = SCIFEP_DISCONNECTED; - list_add_tail(&ep->list, &scif_info.disconnected); - - wake_up_interruptible(&ep->sendwq); - wake_up_interruptible(&ep->recvwq); - spin_unlock(&ep->lock); - mutex_unlock(&scif_info.connlock); - -discnct_ack: - msg->uop = SCIF_DISCNT_ACK; - scif_nodeqp_send(&scif_dev[msg->src.node], msg); -} - -/** - * scif_discnct_ack() - Respond to SCIF_DISCNT_ACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side has indicated it has not more references to local resources - */ -void scif_discnt_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - ep->state = SCIFEP_DISCONNECTED; - spin_unlock(&ep->lock); - complete(&ep->discon); -} - -/** - * scif_clientsend() - Respond to SCIF_CLIENT_SEND interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side is confirming send or receive interrupt handling is complete. - */ -void scif_clientsend(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - if (SCIFEP_CONNECTED == ep->state) - wake_up_interruptible(&ep->recvwq); - spin_unlock(&ep->lock); -} - -/** - * scif_clientrcvd() - Respond to SCIF_CLIENT_RCVD interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side is confirming send or receive interrupt handling is complete. - */ -void scif_clientrcvd(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - - spin_lock(&ep->lock); - if (SCIFEP_CONNECTED == ep->state) - wake_up_interruptible(&ep->sendwq); - spin_unlock(&ep->lock); -} diff --git a/drivers/misc/mic/scif/scif_epd.h b/drivers/misc/mic/scif/scif_epd.h deleted file mode 100644 index 0b9dfe1cc06c..000000000000 --- a/drivers/misc/mic/scif/scif_epd.h +++ /dev/null @@ -1,200 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef SCIF_EPD_H -#define SCIF_EPD_H - -#include -#include -#include - -#define SCIF_EPLOCK_HELD true - -enum scif_epd_state { - SCIFEP_UNBOUND, - SCIFEP_BOUND, - SCIFEP_LISTENING, - SCIFEP_CONNECTED, - SCIFEP_CONNECTING, - SCIFEP_MAPPING, - SCIFEP_CLOSING, - SCIFEP_CLLISTEN, - SCIFEP_DISCONNECTED, - SCIFEP_ZOMBIE -}; - -/* - * struct scif_conreq - Data structure added to the connection list. - * - * @msg: connection request message received - * @list: link to list of connection requests - */ -struct scif_conreq { - struct scifmsg msg; - struct list_head list; -}; - -/* Size of the RB for the Endpoint QP */ -#define SCIF_ENDPT_QP_SIZE 0x1000 - -/* - * scif_endpt_qp_info - SCIF endpoint queue pair - * - * @qp - Qpair for this endpoint - * @qp_offset - DMA address of the QP - * @gnt_pld - Payload in a SCIF_CNCT_GNT message containing the - * physical address of the remote_qp. - */ -struct scif_endpt_qp_info { - struct scif_qp *qp; - dma_addr_t qp_offset; - dma_addr_t gnt_pld; -}; - -/* - * struct scif_endpt - The SCIF endpoint data structure - * - * @state: end point state - * @lock: lock synchronizing access to endpoint fields like state etc - * @port: self port information - * @peer: peer port information - * @backlog: maximum pending connection requests - * @qp_info: Endpoint QP information for SCIF messaging - * @remote_dev: scifdev used by this endpt to communicate with remote node. - * @remote_ep: remote endpoint - * @conreqcnt: Keep track of number of connection requests. - * @files: Open file information used to match the id passed in with - * the flush routine. - * @conlist: list of connection requests - * @conwq: waitqueue for connection processing - * @discon: completion used during disconnection - * @sendwq: waitqueue used during sending messages - * @recvwq: waitqueue used during message receipt - * @sendlock: Synchronize ordering of messages sent - * @recvlock: Synchronize ordering of messages received - * @list: link to list of various endpoints like connected, listening etc - * @li_accept: pending ACCEPTREG - * @acceptcnt: pending ACCEPTREG cnt - * @liacceptlist: link to listen accept - * @miacceptlist: link to uaccept - * @listenep: associated listen ep - * @conn_work: Non blocking connect work - * @conn_port: Connection port - * @conn_err: Errors during connection - * @conn_async_state: Async connection - * @conn_pend_wq: Used by poll while waiting for incoming connections - * @conn_list: List of async connection requests - * @rma_info: Information for triggering SCIF RMA and DMA operations - * @mmu_list: link to list of MMU notifier cleanup work - * @anon: anonymous file for use in kernel mode scif poll - */ -struct scif_endpt { - enum scif_epd_state state; - spinlock_t lock; - struct scif_port_id port; - struct scif_port_id peer; - int backlog; - struct scif_endpt_qp_info qp_info; - struct scif_dev *remote_dev; - u64 remote_ep; - int conreqcnt; - struct files_struct *files; - struct list_head conlist; - wait_queue_head_t conwq; - struct completion discon; - wait_queue_head_t sendwq; - wait_queue_head_t recvwq; - struct mutex sendlock; - struct mutex recvlock; - struct list_head list; - struct list_head li_accept; - int acceptcnt; - struct list_head liacceptlist; - struct list_head miacceptlist; - struct scif_endpt *listenep; - struct scif_port_id conn_port; - int conn_err; - int conn_async_state; - wait_queue_head_t conn_pend_wq; - struct list_head conn_list; - struct scif_endpt_rma_info rma_info; - struct list_head mmu_list; - struct file *anon; -}; - -static inline int scifdev_alive(struct scif_endpt *ep) -{ - return _scifdev_alive(ep->remote_dev); -} - -/* - * scif_verify_epd: - * ep: SCIF endpoint - * - * Checks several generic error conditions and returns the - * appropriate error. - */ -static inline int scif_verify_epd(struct scif_endpt *ep) -{ - if (ep->state == SCIFEP_DISCONNECTED) - return -ECONNRESET; - - if (ep->state != SCIFEP_CONNECTED) - return -ENOTCONN; - - if (!scifdev_alive(ep)) - return -ENODEV; - - return 0; -} - -static inline int scif_anon_inode_getfile(scif_epd_t epd) -{ - epd->anon = anon_inode_getfile("scif", &scif_anon_fops, NULL, 0); - - return PTR_ERR_OR_ZERO(epd->anon); -} - -static inline void scif_anon_inode_fput(scif_epd_t epd) -{ - if (epd->anon) { - fput(epd->anon); - epd->anon = NULL; - } -} - -void scif_cleanup_zombie_epd(void); -void scif_teardown_ep(void *endpt); -void scif_cleanup_ep_qp(struct scif_endpt *ep); -void scif_add_epd_to_zombie_list(struct scif_endpt *ep, bool eplock_held); -void scif_get_node_info(void); -void scif_send_acks(struct scif_dev *dev); -void scif_conn_handler(struct work_struct *work); -int scif_rsrv_port(u16 port); -void scif_get_port(u16 port); -int scif_get_new_port(void); -void scif_put_port(u16 port); -int scif_user_send(scif_epd_t epd, void __user *msg, int len, int flags); -int scif_user_recv(scif_epd_t epd, void __user *msg, int len, int flags); -void scif_cnctreq(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_cnctgnt(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_cnctgnt_ack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_cnctgnt_nack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_cnctrej(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_discnct(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_discnt_ack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_clientsend(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_clientrcvd(struct scif_dev *scifdev, struct scifmsg *msg); -int __scif_connect(scif_epd_t epd, struct scif_port_id *dst, bool non_block); -int __scif_flush(scif_epd_t epd); -int scif_mmap(struct vm_area_struct *vma, scif_epd_t epd); -__poll_t __scif_pollfd(struct file *f, poll_table *wait, - struct scif_endpt *ep); -int __scif_pin_pages(void *addr, size_t len, int *out_prot, - int map_flags, scif_pinned_pages_t *pages); -#endif /* SCIF_EPD_H */ diff --git a/drivers/misc/mic/scif/scif_fd.c b/drivers/misc/mic/scif/scif_fd.c deleted file mode 100644 index 3f08646cd78a..000000000000 --- a/drivers/misc/mic/scif/scif_fd.c +++ /dev/null @@ -1,462 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" - -static int scif_fdopen(struct inode *inode, struct file *f) -{ - struct scif_endpt *priv = scif_open(); - - if (!priv) - return -ENOMEM; - f->private_data = priv; - return 0; -} - -static int scif_fdclose(struct inode *inode, struct file *f) -{ - struct scif_endpt *priv = f->private_data; - - return scif_close(priv); -} - -static int scif_fdmmap(struct file *f, struct vm_area_struct *vma) -{ - struct scif_endpt *priv = f->private_data; - - return scif_mmap(vma, priv); -} - -static __poll_t scif_fdpoll(struct file *f, poll_table *wait) -{ - struct scif_endpt *priv = f->private_data; - - return __scif_pollfd(f, wait, priv); -} - -static int scif_fdflush(struct file *f, fl_owner_t id) -{ - struct scif_endpt *ep = f->private_data; - - spin_lock(&ep->lock); - /* - * The listening endpoint stashes the open file information before - * waiting for incoming connections. The release callback would never be - * called if the application closed the endpoint, while waiting for - * incoming connections from a separate thread since the file descriptor - * reference count is bumped up in the accept IOCTL. Call the flush - * routine if the id matches the endpoint open file information so that - * the listening endpoint can be woken up and the fd released. - */ - if (ep->files == id) - __scif_flush(ep); - spin_unlock(&ep->lock); - return 0; -} - -static __always_inline void scif_err_debug(int err, const char *str) -{ - /* - * ENOTCONN is a common uninteresting error which is - * flooding debug messages to the console unnecessarily. - */ - if (err < 0 && err != -ENOTCONN) - dev_dbg(scif_info.mdev.this_device, "%s err %d\n", str, err); -} - -static long scif_fdioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ - struct scif_endpt *priv = f->private_data; - void __user *argp = (void __user *)arg; - int err = 0; - struct scifioctl_msg request; - bool non_block = false; - - non_block = !!(f->f_flags & O_NONBLOCK); - - switch (cmd) { - case SCIF_BIND: - { - int pn; - - if (copy_from_user(&pn, argp, sizeof(pn))) - return -EFAULT; - - pn = scif_bind(priv, pn); - if (pn < 0) - return pn; - - if (copy_to_user(argp, &pn, sizeof(pn))) - return -EFAULT; - - return 0; - } - case SCIF_LISTEN: - return scif_listen(priv, arg); - case SCIF_CONNECT: - { - struct scifioctl_connect req; - struct scif_endpt *ep = (struct scif_endpt *)priv; - - if (copy_from_user(&req, argp, sizeof(req))) - return -EFAULT; - - err = __scif_connect(priv, &req.peer, non_block); - if (err < 0) - return err; - - req.self.node = ep->port.node; - req.self.port = ep->port.port; - - if (copy_to_user(argp, &req, sizeof(req))) - return -EFAULT; - - return 0; - } - /* - * Accept is done in two halves. The request ioctl does the basic - * functionality of accepting the request and returning the information - * about it including the internal ID of the end point. The register - * is done with the internal ID on a new file descriptor opened by the - * requesting process. - */ - case SCIF_ACCEPTREQ: - { - struct scifioctl_accept request; - scif_epd_t *ep = (scif_epd_t *)&request.endpt; - - if (copy_from_user(&request, argp, sizeof(request))) - return -EFAULT; - - err = scif_accept(priv, &request.peer, ep, request.flags); - if (err < 0) - return err; - - if (copy_to_user(argp, &request, sizeof(request))) { - scif_close(*ep); - return -EFAULT; - } - /* - * Add to the list of user mode eps where the second half - * of the accept is not yet completed. - */ - mutex_lock(&scif_info.eplock); - list_add_tail(&((*ep)->miacceptlist), &scif_info.uaccept); - list_add_tail(&((*ep)->liacceptlist), &priv->li_accept); - (*ep)->listenep = priv; - priv->acceptcnt++; - mutex_unlock(&scif_info.eplock); - - return 0; - } - case SCIF_ACCEPTREG: - { - struct scif_endpt *priv = f->private_data; - struct scif_endpt *newep; - struct scif_endpt *lisep; - struct scif_endpt *fep = NULL; - struct scif_endpt *tmpep; - struct list_head *pos, *tmpq; - - /* Finally replace the pointer to the accepted endpoint */ - if (copy_from_user(&newep, argp, sizeof(void *))) - return -EFAULT; - - /* Remove form the user accept queue */ - mutex_lock(&scif_info.eplock); - list_for_each_safe(pos, tmpq, &scif_info.uaccept) { - tmpep = list_entry(pos, - struct scif_endpt, miacceptlist); - if (tmpep == newep) { - list_del(pos); - fep = tmpep; - break; - } - } - - if (!fep) { - mutex_unlock(&scif_info.eplock); - return -ENOENT; - } - - lisep = newep->listenep; - list_for_each_safe(pos, tmpq, &lisep->li_accept) { - tmpep = list_entry(pos, - struct scif_endpt, liacceptlist); - if (tmpep == newep) { - list_del(pos); - lisep->acceptcnt--; - break; - } - } - - mutex_unlock(&scif_info.eplock); - - /* Free the resources automatically created from the open. */ - scif_anon_inode_fput(priv); - scif_teardown_ep(priv); - scif_add_epd_to_zombie_list(priv, !SCIF_EPLOCK_HELD); - f->private_data = newep; - return 0; - } - case SCIF_SEND: - { - struct scif_endpt *priv = f->private_data; - - if (copy_from_user(&request, argp, - sizeof(struct scifioctl_msg))) { - err = -EFAULT; - goto send_err; - } - err = scif_user_send(priv, (void __user *)request.msg, - request.len, request.flags); - if (err < 0) - goto send_err; - if (copy_to_user(& - ((struct scifioctl_msg __user *)argp)->out_len, - &err, sizeof(err))) { - err = -EFAULT; - goto send_err; - } - err = 0; -send_err: - scif_err_debug(err, "scif_send"); - return err; - } - case SCIF_RECV: - { - struct scif_endpt *priv = f->private_data; - - if (copy_from_user(&request, argp, - sizeof(struct scifioctl_msg))) { - err = -EFAULT; - goto recv_err; - } - - err = scif_user_recv(priv, (void __user *)request.msg, - request.len, request.flags); - if (err < 0) - goto recv_err; - - if (copy_to_user(& - ((struct scifioctl_msg __user *)argp)->out_len, - &err, sizeof(err))) { - err = -EFAULT; - goto recv_err; - } - err = 0; -recv_err: - scif_err_debug(err, "scif_recv"); - return err; - } - case SCIF_GET_NODEIDS: - { - struct scifioctl_node_ids node_ids; - int entries; - u16 *nodes; - void __user *unodes, *uself; - u16 self; - - if (copy_from_user(&node_ids, argp, sizeof(node_ids))) { - err = -EFAULT; - goto getnodes_err2; - } - - entries = min_t(int, scif_info.maxid, node_ids.len); - nodes = kmalloc_array(entries, sizeof(u16), GFP_KERNEL); - if (entries && !nodes) { - err = -ENOMEM; - goto getnodes_err2; - } - node_ids.len = scif_get_node_ids(nodes, entries, &self); - - unodes = (void __user *)node_ids.nodes; - if (copy_to_user(unodes, nodes, sizeof(u16) * entries)) { - err = -EFAULT; - goto getnodes_err1; - } - - uself = (void __user *)node_ids.self; - if (copy_to_user(uself, &self, sizeof(u16))) { - err = -EFAULT; - goto getnodes_err1; - } - - if (copy_to_user(argp, &node_ids, sizeof(node_ids))) { - err = -EFAULT; - goto getnodes_err1; - } -getnodes_err1: - kfree(nodes); -getnodes_err2: - return err; - } - case SCIF_REG: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_reg reg; - off_t ret; - - if (copy_from_user(®, argp, sizeof(reg))) { - err = -EFAULT; - goto reg_err; - } - if (reg.flags & SCIF_MAP_KERNEL) { - err = -EINVAL; - goto reg_err; - } - ret = scif_register(priv, (void *)reg.addr, reg.len, - reg.offset, reg.prot, reg.flags); - if (ret < 0) { - err = (int)ret; - goto reg_err; - } - - if (copy_to_user(&((struct scifioctl_reg __user *)argp) - ->out_offset, &ret, sizeof(reg.out_offset))) { - err = -EFAULT; - goto reg_err; - } - err = 0; -reg_err: - scif_err_debug(err, "scif_register"); - return err; - } - case SCIF_UNREG: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_unreg unreg; - - if (copy_from_user(&unreg, argp, sizeof(unreg))) { - err = -EFAULT; - goto unreg_err; - } - err = scif_unregister(priv, unreg.offset, unreg.len); -unreg_err: - scif_err_debug(err, "scif_unregister"); - return err; - } - case SCIF_READFROM: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_copy copy; - - if (copy_from_user(©, argp, sizeof(copy))) { - err = -EFAULT; - goto readfrom_err; - } - err = scif_readfrom(priv, copy.loffset, copy.len, copy.roffset, - copy.flags); -readfrom_err: - scif_err_debug(err, "scif_readfrom"); - return err; - } - case SCIF_WRITETO: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_copy copy; - - if (copy_from_user(©, argp, sizeof(copy))) { - err = -EFAULT; - goto writeto_err; - } - err = scif_writeto(priv, copy.loffset, copy.len, copy.roffset, - copy.flags); -writeto_err: - scif_err_debug(err, "scif_writeto"); - return err; - } - case SCIF_VREADFROM: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_copy copy; - - if (copy_from_user(©, argp, sizeof(copy))) { - err = -EFAULT; - goto vreadfrom_err; - } - err = scif_vreadfrom(priv, (void __force *)copy.addr, copy.len, - copy.roffset, copy.flags); -vreadfrom_err: - scif_err_debug(err, "scif_vreadfrom"); - return err; - } - case SCIF_VWRITETO: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_copy copy; - - if (copy_from_user(©, argp, sizeof(copy))) { - err = -EFAULT; - goto vwriteto_err; - } - err = scif_vwriteto(priv, (void __force *)copy.addr, copy.len, - copy.roffset, copy.flags); -vwriteto_err: - scif_err_debug(err, "scif_vwriteto"); - return err; - } - case SCIF_FENCE_MARK: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_fence_mark mark; - int tmp_mark = 0; - - if (copy_from_user(&mark, argp, sizeof(mark))) { - err = -EFAULT; - goto fence_mark_err; - } - err = scif_fence_mark(priv, mark.flags, &tmp_mark); - if (err) - goto fence_mark_err; - if (copy_to_user((void __user *)mark.mark, &tmp_mark, - sizeof(tmp_mark))) { - err = -EFAULT; - goto fence_mark_err; - } -fence_mark_err: - scif_err_debug(err, "scif_fence_mark"); - return err; - } - case SCIF_FENCE_WAIT: - { - struct scif_endpt *priv = f->private_data; - - err = scif_fence_wait(priv, arg); - scif_err_debug(err, "scif_fence_wait"); - return err; - } - case SCIF_FENCE_SIGNAL: - { - struct scif_endpt *priv = f->private_data; - struct scifioctl_fence_signal signal; - - if (copy_from_user(&signal, argp, sizeof(signal))) { - err = -EFAULT; - goto fence_signal_err; - } - - err = scif_fence_signal(priv, signal.loff, signal.lval, - signal.roff, signal.rval, signal.flags); -fence_signal_err: - scif_err_debug(err, "scif_fence_signal"); - return err; - } - } - return -EINVAL; -} - -const struct file_operations scif_fops = { - .open = scif_fdopen, - .release = scif_fdclose, - .unlocked_ioctl = scif_fdioctl, - .mmap = scif_fdmmap, - .poll = scif_fdpoll, - .flush = scif_fdflush, - .owner = THIS_MODULE, -}; diff --git a/drivers/misc/mic/scif/scif_fence.c b/drivers/misc/mic/scif/scif_fence.c deleted file mode 100644 index 4fedf6183951..000000000000 --- a/drivers/misc/mic/scif/scif_fence.c +++ /dev/null @@ -1,783 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ - -#include "scif_main.h" - -/** - * scif_recv_mark: Handle SCIF_MARK request - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has requested a mark. - */ -void scif_recv_mark(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - int mark = 0; - int err; - - err = _scif_fence_mark(ep, &mark); - if (err) - msg->uop = SCIF_MARK_NACK; - else - msg->uop = SCIF_MARK_ACK; - msg->payload[0] = ep->remote_ep; - msg->payload[2] = mark; - scif_nodeqp_send(ep->remote_dev, msg); -} - -/** - * scif_recv_mark_resp: Handle SCIF_MARK_(N)ACK messages. - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has responded to a SCIF_MARK message. - */ -void scif_recv_mark_resp(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_fence_info *fence_req = - (struct scif_fence_info *)msg->payload[1]; - - mutex_lock(&ep->rma_info.rma_lock); - if (msg->uop == SCIF_MARK_ACK) { - fence_req->state = OP_COMPLETED; - fence_req->dma_mark = (int)msg->payload[2]; - } else { - fence_req->state = OP_FAILED; - } - mutex_unlock(&ep->rma_info.rma_lock); - complete(&fence_req->comp); -} - -/** - * scif_recv_wait: Handle SCIF_WAIT request - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has requested waiting on a fence. - */ -void scif_recv_wait(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_remote_fence_info *fence; - - /* - * Allocate structure for remote fence information and - * send a NACK if the allocation failed. The peer will - * return ENOMEM upon receiving a NACK. - */ - fence = kmalloc(sizeof(*fence), GFP_KERNEL); - if (!fence) { - msg->payload[0] = ep->remote_ep; - msg->uop = SCIF_WAIT_NACK; - scif_nodeqp_send(ep->remote_dev, msg); - return; - } - - /* Prepare the fence request */ - memcpy(&fence->msg, msg, sizeof(struct scifmsg)); - INIT_LIST_HEAD(&fence->list); - - /* Insert to the global remote fence request list */ - mutex_lock(&scif_info.fencelock); - atomic_inc(&ep->rma_info.fence_refcount); - list_add_tail(&fence->list, &scif_info.fence); - mutex_unlock(&scif_info.fencelock); - - schedule_work(&scif_info.misc_work); -} - -/** - * scif_recv_wait_resp: Handle SCIF_WAIT_(N)ACK messages. - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has responded to a SCIF_WAIT message. - */ -void scif_recv_wait_resp(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_fence_info *fence_req = - (struct scif_fence_info *)msg->payload[1]; - - mutex_lock(&ep->rma_info.rma_lock); - if (msg->uop == SCIF_WAIT_ACK) - fence_req->state = OP_COMPLETED; - else - fence_req->state = OP_FAILED; - mutex_unlock(&ep->rma_info.rma_lock); - complete(&fence_req->comp); -} - -/** - * scif_recv_sig_local: Handle SCIF_SIG_LOCAL request - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has requested a signal on a local offset. - */ -void scif_recv_sig_local(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - int err; - - err = scif_prog_signal(ep, msg->payload[1], msg->payload[2], - SCIF_WINDOW_SELF); - if (err) - msg->uop = SCIF_SIG_NACK; - else - msg->uop = SCIF_SIG_ACK; - msg->payload[0] = ep->remote_ep; - scif_nodeqp_send(ep->remote_dev, msg); -} - -/** - * scif_recv_sig_remote: Handle SCIF_SIGNAL_REMOTE request - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has requested a signal on a remote offset. - */ -void scif_recv_sig_remote(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - int err; - - err = scif_prog_signal(ep, msg->payload[1], msg->payload[2], - SCIF_WINDOW_PEER); - if (err) - msg->uop = SCIF_SIG_NACK; - else - msg->uop = SCIF_SIG_ACK; - msg->payload[0] = ep->remote_ep; - scif_nodeqp_send(ep->remote_dev, msg); -} - -/** - * scif_recv_sig_resp: Handle SCIF_SIG_(N)ACK messages. - * @scifdev: SCIF device - * @msg: Interrupt message - * - * The peer has responded to a signal request. - */ -void scif_recv_sig_resp(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_fence_info *fence_req = - (struct scif_fence_info *)msg->payload[3]; - - mutex_lock(&ep->rma_info.rma_lock); - if (msg->uop == SCIF_SIG_ACK) - fence_req->state = OP_COMPLETED; - else - fence_req->state = OP_FAILED; - mutex_unlock(&ep->rma_info.rma_lock); - complete(&fence_req->comp); -} - -static inline void *scif_get_local_va(off_t off, struct scif_window *window) -{ - struct page **pages = window->pinned_pages->pages; - int page_nr = (off - window->offset) >> PAGE_SHIFT; - off_t page_off = off & ~PAGE_MASK; - - return page_address(pages[page_nr]) + page_off; -} - -static void scif_prog_signal_cb(void *arg) -{ - struct scif_cb_arg *cb_arg = arg; - - dma_pool_free(cb_arg->ep->remote_dev->signal_pool, cb_arg->status, - cb_arg->src_dma_addr); - kfree(cb_arg); -} - -static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct dma_chan *chan = ep->rma_info.dma_chan; - struct dma_device *ddev = chan->device; - bool x100 = !is_dma_copy_aligned(chan->device, 1, 1, 1); - struct dma_async_tx_descriptor *tx; - struct scif_status *status = NULL; - struct scif_cb_arg *cb_arg = NULL; - dma_addr_t src; - dma_cookie_t cookie; - int err; - - tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto alloc_fail; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = (int)cookie; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto alloc_fail; - } - dma_async_issue_pending(chan); - if (x100) { - /* - * For X100 use the status descriptor to write the value to - * the destination. - */ - tx = ddev->device_prep_dma_imm_data(chan, dst, val, 0); - } else { - status = dma_pool_alloc(ep->remote_dev->signal_pool, GFP_KERNEL, - &src); - if (!status) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto alloc_fail; - } - status->val = val; - status->src_dma_addr = src; - status->ep = ep; - src += offsetof(struct scif_status, val); - tx = ddev->device_prep_dma_memcpy(chan, dst, src, sizeof(val), - DMA_PREP_INTERRUPT); - } - if (!tx) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto dma_fail; - } - if (!x100) { - cb_arg = kmalloc(sizeof(*cb_arg), GFP_KERNEL); - if (!cb_arg) { - err = -ENOMEM; - goto dma_fail; - } - cb_arg->src_dma_addr = src; - cb_arg->status = status; - cb_arg->ep = ep; - tx->callback = scif_prog_signal_cb; - tx->callback_param = cb_arg; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = -EIO; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - goto dma_fail; - } - dma_async_issue_pending(chan); - return 0; -dma_fail: - if (!x100) { - dma_pool_free(ep->remote_dev->signal_pool, status, - src - offsetof(struct scif_status, val)); - kfree(cb_arg); - } -alloc_fail: - return err; -} - -/** - * scif_prog_signal: - * @epd: Endpoint Descriptor - * @offset: registered address to write @val to - * @val: Value to be written at @offset - * @type: Type of the window. - * - * Arrange to write a value to the registered offset after ensuring that the - * offset provided is indeed valid. - */ -int scif_prog_signal(scif_epd_t epd, off_t offset, u64 val, - enum scif_window_type type) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_window *window = NULL; - struct scif_rma_req req; - dma_addr_t dst_dma_addr; - int err; - - mutex_lock(&ep->rma_info.rma_lock); - req.out_window = &window; - req.offset = offset; - req.nr_bytes = sizeof(u64); - req.prot = SCIF_PROT_WRITE; - req.type = SCIF_WINDOW_SINGLE; - if (type == SCIF_WINDOW_SELF) - req.head = &ep->rma_info.reg_list; - else - req.head = &ep->rma_info.remote_reg_list; - /* Does a valid window exist? */ - err = scif_query_window(&req); - if (err) { - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - goto unlock_ret; - } - - if (scif_is_mgmt_node() && scifdev_self(ep->remote_dev)) { - u64 *dst_virt; - - if (type == SCIF_WINDOW_SELF) - dst_virt = scif_get_local_va(offset, window); - else - dst_virt = - scif_get_local_va(offset, (struct scif_window *) - window->peer_window); - *dst_virt = val; - } else { - dst_dma_addr = __scif_off_to_dma_addr(window, offset); - err = _scif_prog_signal(epd, dst_dma_addr, val); - } -unlock_ret: - mutex_unlock(&ep->rma_info.rma_lock); - return err; -} - -static int _scif_fence_wait(scif_epd_t epd, int mark) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - dma_cookie_t cookie = mark & ~SCIF_REMOTE_FENCE; - int err; - - /* Wait for DMA callback in scif_fence_mark_cb(..) */ - err = wait_event_interruptible_timeout(ep->rma_info.markwq, - dma_async_is_tx_complete( - ep->rma_info.dma_chan, - cookie, NULL, NULL) == - DMA_COMPLETE, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err) - err = -ETIMEDOUT; - else if (err > 0) - err = 0; - return err; -} - -/** - * scif_rma_handle_remote_fences: - * - * This routine services remote fence requests. - */ -void scif_rma_handle_remote_fences(void) -{ - struct list_head *item, *tmp; - struct scif_remote_fence_info *fence; - struct scif_endpt *ep; - int mark, err; - - might_sleep(); - mutex_lock(&scif_info.fencelock); - list_for_each_safe(item, tmp, &scif_info.fence) { - fence = list_entry(item, struct scif_remote_fence_info, - list); - /* Remove fence from global list */ - list_del(&fence->list); - - /* Initiate the fence operation */ - ep = (struct scif_endpt *)fence->msg.payload[0]; - mark = fence->msg.payload[2]; - err = _scif_fence_wait(ep, mark); - if (err) - fence->msg.uop = SCIF_WAIT_NACK; - else - fence->msg.uop = SCIF_WAIT_ACK; - fence->msg.payload[0] = ep->remote_ep; - scif_nodeqp_send(ep->remote_dev, &fence->msg); - kfree(fence); - if (!atomic_sub_return(1, &ep->rma_info.fence_refcount)) - schedule_work(&scif_info.misc_work); - } - mutex_unlock(&scif_info.fencelock); -} - -static int _scif_send_fence(scif_epd_t epd, int uop, int mark, int *out_mark) -{ - int err; - struct scifmsg msg; - struct scif_fence_info *fence_req; - struct scif_endpt *ep = (struct scif_endpt *)epd; - - fence_req = kmalloc(sizeof(*fence_req), GFP_KERNEL); - if (!fence_req) { - err = -ENOMEM; - goto error; - } - - fence_req->state = OP_IN_PROGRESS; - init_completion(&fence_req->comp); - - msg.src = ep->port; - msg.uop = uop; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = (u64)fence_req; - if (uop == SCIF_WAIT) - msg.payload[2] = mark; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) - err = scif_nodeqp_send(ep->remote_dev, &msg); - else - err = -ENOTCONN; - spin_unlock(&ep->lock); - if (err) - goto error_free; -retry: - /* Wait for a SCIF_WAIT_(N)ACK message */ - err = wait_for_completion_timeout(&fence_req->comp, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - if (!err) - err = -ENODEV; - if (err > 0) - err = 0; - mutex_lock(&ep->rma_info.rma_lock); - if (err < 0) { - if (fence_req->state == OP_IN_PROGRESS) - fence_req->state = OP_FAILED; - } - if (fence_req->state == OP_FAILED && !err) - err = -ENOMEM; - if (uop == SCIF_MARK && fence_req->state == OP_COMPLETED) - *out_mark = SCIF_REMOTE_FENCE | fence_req->dma_mark; - mutex_unlock(&ep->rma_info.rma_lock); -error_free: - kfree(fence_req); -error: - return err; -} - -/** - * scif_send_fence_mark: - * @epd: end point descriptor. - * @out_mark: Output DMA mark reported by peer. - * - * Send a remote fence mark request. - */ -static int scif_send_fence_mark(scif_epd_t epd, int *out_mark) -{ - return _scif_send_fence(epd, SCIF_MARK, 0, out_mark); -} - -/** - * scif_send_fence_wait: - * @epd: end point descriptor. - * @mark: DMA mark to wait for. - * - * Send a remote fence wait request. - */ -static int scif_send_fence_wait(scif_epd_t epd, int mark) -{ - return _scif_send_fence(epd, SCIF_WAIT, mark, NULL); -} - -static int _scif_send_fence_signal_wait(struct scif_endpt *ep, - struct scif_fence_info *fence_req) -{ - int err; - -retry: - /* Wait for a SCIF_SIG_(N)ACK message */ - err = wait_for_completion_timeout(&fence_req->comp, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - if (!err) - err = -ENODEV; - if (err > 0) - err = 0; - if (err < 0) { - mutex_lock(&ep->rma_info.rma_lock); - if (fence_req->state == OP_IN_PROGRESS) - fence_req->state = OP_FAILED; - mutex_unlock(&ep->rma_info.rma_lock); - } - if (fence_req->state == OP_FAILED && !err) - err = -ENXIO; - return err; -} - -/** - * scif_send_fence_signal: - * @epd: endpoint descriptor - * @loff: local offset - * @lval: local value to write to loffset - * @roff: remote offset - * @rval: remote value to write to roffset - * @flags: flags - * - * Sends a remote fence signal request - */ -static int scif_send_fence_signal(scif_epd_t epd, off_t roff, u64 rval, - off_t loff, u64 lval, int flags) -{ - int err = 0; - struct scifmsg msg; - struct scif_fence_info *fence_req; - struct scif_endpt *ep = (struct scif_endpt *)epd; - - fence_req = kmalloc(sizeof(*fence_req), GFP_KERNEL); - if (!fence_req) { - err = -ENOMEM; - goto error; - } - - fence_req->state = OP_IN_PROGRESS; - init_completion(&fence_req->comp); - msg.src = ep->port; - if (flags & SCIF_SIGNAL_LOCAL) { - msg.uop = SCIF_SIG_LOCAL; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = roff; - msg.payload[2] = rval; - msg.payload[3] = (u64)fence_req; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) - err = scif_nodeqp_send(ep->remote_dev, &msg); - else - err = -ENOTCONN; - spin_unlock(&ep->lock); - if (err) - goto error_free; - err = _scif_send_fence_signal_wait(ep, fence_req); - if (err) - goto error_free; - } - fence_req->state = OP_IN_PROGRESS; - - if (flags & SCIF_SIGNAL_REMOTE) { - msg.uop = SCIF_SIG_REMOTE; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = loff; - msg.payload[2] = lval; - msg.payload[3] = (u64)fence_req; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) - err = scif_nodeqp_send(ep->remote_dev, &msg); - else - err = -ENOTCONN; - spin_unlock(&ep->lock); - if (err) - goto error_free; - err = _scif_send_fence_signal_wait(ep, fence_req); - } -error_free: - kfree(fence_req); -error: - return err; -} - -static void scif_fence_mark_cb(void *arg) -{ - struct scif_endpt *ep = (struct scif_endpt *)arg; - - wake_up_interruptible(&ep->rma_info.markwq); - atomic_dec(&ep->rma_info.fence_refcount); -} - -/** - * _scif_fence_mark: - * @epd: endpoint descriptor - * @mark: DMA mark to set-up - * - * Set up a mark for this endpoint and return the value of the mark. - */ -int _scif_fence_mark(scif_epd_t epd, int *mark) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct dma_chan *chan = ep->rma_info.dma_chan; - struct dma_device *ddev = chan->device; - struct dma_async_tx_descriptor *tx; - dma_cookie_t cookie; - int err; - - tx = ddev->device_prep_dma_memcpy(chan, 0, 0, 0, DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = (int)cookie; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - dma_async_issue_pending(chan); - tx = ddev->device_prep_dma_interrupt(chan, DMA_PREP_INTERRUPT); - if (!tx) { - err = -ENOMEM; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - tx->callback = scif_fence_mark_cb; - tx->callback_param = ep; - *mark = cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = (int)cookie; - dev_err(&ep->remote_dev->sdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; - } - atomic_inc(&ep->rma_info.fence_refcount); - dma_async_issue_pending(chan); - return 0; -} - -#define SCIF_LOOPB_MAGIC_MARK 0xdead - -int scif_fence_mark(scif_epd_t epd, int flags, int *mark) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI fence_mark: ep %p flags 0x%x mark 0x%x\n", - ep, flags, *mark); - err = scif_verify_epd(ep); - if (err) - return err; - - /* Invalid flags? */ - if (flags & ~(SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER)) - return -EINVAL; - - /* At least one of init self or peer RMA should be set */ - if (!(flags & (SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER))) - return -EINVAL; - - /* Exactly one of init self or peer RMA should be set but not both */ - if ((flags & SCIF_FENCE_INIT_SELF) && (flags & SCIF_FENCE_INIT_PEER)) - return -EINVAL; - - /* - * Management node loopback does not need to use DMA. - * Return a valid mark to be symmetric. - */ - if (scifdev_self(ep->remote_dev) && scif_is_mgmt_node()) { - *mark = SCIF_LOOPB_MAGIC_MARK; - return 0; - } - - if (flags & SCIF_FENCE_INIT_SELF) - err = _scif_fence_mark(epd, mark); - else - err = scif_send_fence_mark(ep, mark); - - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI fence_mark: ep %p flags 0x%x mark 0x%x err %d\n", - ep, flags, *mark, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_fence_mark); - -int scif_fence_wait(scif_epd_t epd, int mark) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI fence_wait: ep %p mark 0x%x\n", - ep, mark); - err = scif_verify_epd(ep); - if (err) - return err; - /* - * Management node loopback does not need to use DMA. - * The only valid mark provided is 0 so simply - * return success if the mark is valid. - */ - if (scifdev_self(ep->remote_dev) && scif_is_mgmt_node()) { - if (mark == SCIF_LOOPB_MAGIC_MARK) - return 0; - else - return -EINVAL; - } - if (mark & SCIF_REMOTE_FENCE) - err = scif_send_fence_wait(epd, mark); - else - err = _scif_fence_wait(epd, mark); - if (err < 0) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_fence_wait); - -int scif_fence_signal(scif_epd_t epd, off_t loff, u64 lval, - off_t roff, u64 rval, int flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - int err = 0; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI fence_signal: ep %p loff 0x%lx lval 0x%llx roff 0x%lx rval 0x%llx flags 0x%x\n", - ep, loff, lval, roff, rval, flags); - err = scif_verify_epd(ep); - if (err) - return err; - - /* Invalid flags? */ - if (flags & ~(SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER | - SCIF_SIGNAL_LOCAL | SCIF_SIGNAL_REMOTE)) - return -EINVAL; - - /* At least one of init self or peer RMA should be set */ - if (!(flags & (SCIF_FENCE_INIT_SELF | SCIF_FENCE_INIT_PEER))) - return -EINVAL; - - /* Exactly one of init self or peer RMA should be set but not both */ - if ((flags & SCIF_FENCE_INIT_SELF) && (flags & SCIF_FENCE_INIT_PEER)) - return -EINVAL; - - /* At least one of SCIF_SIGNAL_LOCAL or SCIF_SIGNAL_REMOTE required */ - if (!(flags & (SCIF_SIGNAL_LOCAL | SCIF_SIGNAL_REMOTE))) - return -EINVAL; - - /* Only Dword offsets allowed */ - if ((flags & SCIF_SIGNAL_LOCAL) && (loff & (sizeof(u32) - 1))) - return -EINVAL; - - /* Only Dword aligned offsets allowed */ - if ((flags & SCIF_SIGNAL_REMOTE) && (roff & (sizeof(u32) - 1))) - return -EINVAL; - - if (flags & SCIF_FENCE_INIT_PEER) { - err = scif_send_fence_signal(epd, roff, rval, loff, - lval, flags); - } else { - /* Local Signal in Local RAS */ - if (flags & SCIF_SIGNAL_LOCAL) { - err = scif_prog_signal(epd, loff, lval, - SCIF_WINDOW_SELF); - if (err) - goto error_ret; - } - - /* Signal in Remote RAS */ - if (flags & SCIF_SIGNAL_REMOTE) - err = scif_prog_signal(epd, roff, - rval, SCIF_WINDOW_PEER); - } -error_ret: - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_fence_signal); diff --git a/drivers/misc/mic/scif/scif_main.c b/drivers/misc/mic/scif/scif_main.c deleted file mode 100644 index e2278bf9f11d..000000000000 --- a/drivers/misc/mic/scif/scif_main.c +++ /dev/null @@ -1,351 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include -#include - -#include -#include "../common/mic_dev.h" -#include "../bus/scif_bus.h" -#include "scif_peer_bus.h" -#include "scif_main.h" -#include "scif_map.h" - -struct scif_info scif_info = { - .mdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "scif", - .fops = &scif_fops, - } -}; - -struct scif_dev *scif_dev; -struct kmem_cache *unaligned_cache; -static atomic_t g_loopb_cnt; - -/* Runs in the context of intr_wq */ -static void scif_intr_bh_handler(struct work_struct *work) -{ - struct scif_dev *scifdev = - container_of(work, struct scif_dev, intr_bh); - - if (scifdev_self(scifdev)) - scif_loopb_msg_handler(scifdev, scifdev->qpairs); - else - scif_nodeqp_intrhandler(scifdev, scifdev->qpairs); -} - -int scif_setup_intr_wq(struct scif_dev *scifdev) -{ - if (!scifdev->intr_wq) { - snprintf(scifdev->intr_wqname, sizeof(scifdev->intr_wqname), - "SCIF INTR %d", scifdev->node); - scifdev->intr_wq = - alloc_ordered_workqueue(scifdev->intr_wqname, 0); - if (!scifdev->intr_wq) - return -ENOMEM; - INIT_WORK(&scifdev->intr_bh, scif_intr_bh_handler); - } - return 0; -} - -void scif_destroy_intr_wq(struct scif_dev *scifdev) -{ - if (scifdev->intr_wq) { - destroy_workqueue(scifdev->intr_wq); - scifdev->intr_wq = NULL; - } -} - -irqreturn_t scif_intr_handler(int irq, void *data) -{ - struct scif_dev *scifdev = data; - struct scif_hw_dev *sdev = scifdev->sdev; - - sdev->hw_ops->ack_interrupt(sdev, scifdev->db); - queue_work(scifdev->intr_wq, &scifdev->intr_bh); - return IRQ_HANDLED; -} - -static void scif_qp_setup_handler(struct work_struct *work) -{ - struct scif_dev *scifdev = container_of(work, struct scif_dev, - qp_dwork.work); - struct scif_hw_dev *sdev = scifdev->sdev; - dma_addr_t da = 0; - int err; - - if (scif_is_mgmt_node()) { - struct mic_bootparam *bp = sdev->dp; - - da = bp->scif_card_dma_addr; - scifdev->rdb = bp->h2c_scif_db; - } else { - struct mic_bootparam __iomem *bp = sdev->rdp; - - da = readq(&bp->scif_host_dma_addr); - scifdev->rdb = ioread8(&bp->c2h_scif_db); - } - if (da) { - err = scif_qp_response(da, scifdev); - if (err) - dev_err(&scifdev->sdev->dev, - "scif_qp_response err %d\n", err); - } else { - schedule_delayed_work(&scifdev->qp_dwork, - msecs_to_jiffies(1000)); - } -} - -static int scif_setup_scifdev(void) -{ - /* We support a maximum of 129 SCIF nodes including the mgmt node */ -#define MAX_SCIF_NODES 129 - int i; - u8 num_nodes = MAX_SCIF_NODES; - - scif_dev = kcalloc(num_nodes, sizeof(*scif_dev), GFP_KERNEL); - if (!scif_dev) - return -ENOMEM; - for (i = 0; i < num_nodes; i++) { - struct scif_dev *scifdev = &scif_dev[i]; - - scifdev->node = i; - scifdev->exit = OP_IDLE; - init_waitqueue_head(&scifdev->disconn_wq); - mutex_init(&scifdev->lock); - INIT_WORK(&scifdev->peer_add_work, scif_add_peer_device); - INIT_DELAYED_WORK(&scifdev->p2p_dwork, - scif_poll_qp_state); - INIT_DELAYED_WORK(&scifdev->qp_dwork, - scif_qp_setup_handler); - INIT_LIST_HEAD(&scifdev->p2p); - RCU_INIT_POINTER(scifdev->spdev, NULL); - } - return 0; -} - -static void scif_destroy_scifdev(void) -{ - kfree(scif_dev); - scif_dev = NULL; -} - -static int scif_probe(struct scif_hw_dev *sdev) -{ - struct scif_dev *scifdev = &scif_dev[sdev->dnode]; - int rc; - - dev_set_drvdata(&sdev->dev, sdev); - scifdev->sdev = sdev; - - if (1 == atomic_add_return(1, &g_loopb_cnt)) { - struct scif_dev *loopb_dev = &scif_dev[sdev->snode]; - - loopb_dev->sdev = sdev; - rc = scif_setup_loopback_qp(loopb_dev); - if (rc) - goto exit; - } - - rc = scif_setup_intr_wq(scifdev); - if (rc) - goto destroy_loopb; - rc = scif_setup_qp(scifdev); - if (rc) - goto destroy_intr; - scifdev->db = sdev->hw_ops->next_db(sdev); - scifdev->cookie = sdev->hw_ops->request_irq(sdev, scif_intr_handler, - "SCIF_INTR", scifdev, - scifdev->db); - if (IS_ERR(scifdev->cookie)) { - rc = PTR_ERR(scifdev->cookie); - goto free_qp; - } - if (scif_is_mgmt_node()) { - struct mic_bootparam *bp = sdev->dp; - - bp->c2h_scif_db = scifdev->db; - bp->scif_host_dma_addr = scifdev->qp_dma_addr; - } else { - struct mic_bootparam __iomem *bp = sdev->rdp; - - iowrite8(scifdev->db, &bp->h2c_scif_db); - writeq(scifdev->qp_dma_addr, &bp->scif_card_dma_addr); - } - schedule_delayed_work(&scifdev->qp_dwork, - msecs_to_jiffies(1000)); - return rc; -free_qp: - scif_free_qp(scifdev); -destroy_intr: - scif_destroy_intr_wq(scifdev); -destroy_loopb: - if (atomic_dec_and_test(&g_loopb_cnt)) - scif_destroy_loopback_qp(&scif_dev[sdev->snode]); -exit: - return rc; -} - -void scif_stop(struct scif_dev *scifdev) -{ - struct scif_dev *dev; - int i; - - for (i = scif_info.maxid; i >= 0; i--) { - dev = &scif_dev[i]; - if (scifdev_self(dev)) - continue; - scif_handle_remove_node(i); - } -} - -static void scif_remove(struct scif_hw_dev *sdev) -{ - struct scif_dev *scifdev = &scif_dev[sdev->dnode]; - - if (scif_is_mgmt_node()) { - struct mic_bootparam *bp = sdev->dp; - - bp->c2h_scif_db = -1; - bp->scif_host_dma_addr = 0x0; - } else { - struct mic_bootparam __iomem *bp = sdev->rdp; - - iowrite8(-1, &bp->h2c_scif_db); - writeq(0x0, &bp->scif_card_dma_addr); - } - if (scif_is_mgmt_node()) { - scif_disconnect_node(scifdev->node, true); - } else { - scif_info.card_initiated_exit = true; - scif_stop(scifdev); - } - if (atomic_dec_and_test(&g_loopb_cnt)) - scif_destroy_loopback_qp(&scif_dev[sdev->snode]); - if (scifdev->cookie) { - sdev->hw_ops->free_irq(sdev, scifdev->cookie, scifdev); - scifdev->cookie = NULL; - } - scif_destroy_intr_wq(scifdev); - cancel_delayed_work(&scifdev->qp_dwork); - scif_free_qp(scifdev); - scifdev->rdb = -1; - scifdev->sdev = NULL; -} - -static struct scif_hw_dev_id id_table[] = { - { MIC_SCIF_DEV, SCIF_DEV_ANY_ID }, - { 0 }, -}; - -static struct scif_driver scif_driver = { - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = scif_probe, - .remove = scif_remove, -}; - -static int _scif_init(void) -{ - int rc; - - mutex_init(&scif_info.eplock); - spin_lock_init(&scif_info.rmalock); - spin_lock_init(&scif_info.nb_connect_lock); - spin_lock_init(&scif_info.port_lock); - mutex_init(&scif_info.conflock); - mutex_init(&scif_info.connlock); - mutex_init(&scif_info.fencelock); - INIT_LIST_HEAD(&scif_info.uaccept); - INIT_LIST_HEAD(&scif_info.listen); - INIT_LIST_HEAD(&scif_info.zombie); - INIT_LIST_HEAD(&scif_info.connected); - INIT_LIST_HEAD(&scif_info.disconnected); - INIT_LIST_HEAD(&scif_info.rma); - INIT_LIST_HEAD(&scif_info.rma_tc); - INIT_LIST_HEAD(&scif_info.mmu_notif_cleanup); - INIT_LIST_HEAD(&scif_info.fence); - INIT_LIST_HEAD(&scif_info.nb_connect_list); - init_waitqueue_head(&scif_info.exitwq); - scif_info.rma_tc_limit = SCIF_RMA_TEMP_CACHE_LIMIT; - scif_info.en_msg_log = 0; - scif_info.p2p_enable = 1; - rc = scif_setup_scifdev(); - if (rc) - goto error; - unaligned_cache = kmem_cache_create("Unaligned_DMA", - SCIF_KMEM_UNALIGNED_BUF_SIZE, - 0, SLAB_HWCACHE_ALIGN, NULL); - if (!unaligned_cache) { - rc = -ENOMEM; - goto free_sdev; - } - INIT_WORK(&scif_info.misc_work, scif_misc_handler); - INIT_WORK(&scif_info.mmu_notif_work, scif_mmu_notif_handler); - INIT_WORK(&scif_info.conn_work, scif_conn_handler); - idr_init(&scif_ports); - return 0; -free_sdev: - scif_destroy_scifdev(); -error: - return rc; -} - -static void _scif_exit(void) -{ - idr_destroy(&scif_ports); - kmem_cache_destroy(unaligned_cache); - scif_destroy_scifdev(); -} - -static int __init scif_init(void) -{ - struct miscdevice *mdev = &scif_info.mdev; - int rc; - - _scif_init(); - iova_cache_get(); - rc = scif_peer_bus_init(); - if (rc) - goto exit; - rc = scif_register_driver(&scif_driver); - if (rc) - goto peer_bus_exit; - rc = misc_register(mdev); - if (rc) - goto unreg_scif; - scif_init_debugfs(); - return 0; -unreg_scif: - scif_unregister_driver(&scif_driver); -peer_bus_exit: - scif_peer_bus_exit(); -exit: - _scif_exit(); - return rc; -} - -static void __exit scif_exit(void) -{ - scif_exit_debugfs(); - misc_deregister(&scif_info.mdev); - scif_unregister_driver(&scif_driver); - scif_peer_bus_exit(); - iova_cache_put(); - _scif_exit(); -} - -module_init(scif_init); -module_exit(scif_exit); - -MODULE_DEVICE_TABLE(scif, id_table); -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) SCIF driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/scif/scif_main.h b/drivers/misc/mic/scif/scif_main.h deleted file mode 100644 index bb3ab97d5b35..000000000000 --- a/drivers/misc/mic/scif/scif_main.h +++ /dev/null @@ -1,274 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef SCIF_MAIN_H -#define SCIF_MAIN_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../common/mic_dev.h" - -#define SCIF_MGMT_NODE 0 -#define SCIF_DEFAULT_WATCHDOG_TO 30 -#define SCIF_NODE_ACCEPT_TIMEOUT (3 * HZ) -#define SCIF_NODE_ALIVE_TIMEOUT (SCIF_DEFAULT_WATCHDOG_TO * HZ) -#define SCIF_RMA_TEMP_CACHE_LIMIT 0x20000 - -/* - * Generic state used for certain node QP message exchanges - * like Unregister, Alloc etc. - */ -enum scif_msg_state { - OP_IDLE = 1, - OP_IN_PROGRESS, - OP_COMPLETED, - OP_FAILED -}; - -/* - * struct scif_info - Global SCIF information - * - * @nodeid: Node ID this node is to others - * @maxid: Max known node ID - * @total: Total number of SCIF nodes - * @nr_zombies: number of zombie endpoints - * @eplock: Lock to synchronize listening, zombie endpoint lists - * @connlock: Lock to synchronize connected and disconnected lists - * @nb_connect_lock: Synchronize non blocking connect operations - * @port_lock: Synchronize access to SCIF ports - * @uaccept: List of user acceptreq waiting for acceptreg - * @listen: List of listening end points - * @zombie: List of zombie end points with pending RMA's - * @connected: List of end points in connected state - * @disconnected: List of end points in disconnected state - * @nb_connect_list: List for non blocking connections - * @misc_work: miscellaneous SCIF tasks - * @conflock: Lock to synchronize SCIF node configuration changes - * @en_msg_log: Enable debug message logging - * @p2p_enable: Enable P2P SCIF network - * @mdev: The MISC device - * @conn_work: Work for workqueue handling all connections - * @exitwq: Wait queue for waiting for an EXIT node QP message response - * @loopb_dev: Dummy SCIF device used for loopback - * @loopb_wq: Workqueue used for handling loopback messages - * @loopb_wqname[16]: Name of loopback workqueue - * @loopb_work: Used for submitting work to loopb_wq - * @loopb_recv_q: List of messages received on the loopb_wq - * @card_initiated_exit: set when the card has initiated the exit - * @rmalock: Synchronize access to RMA operations - * @fencelock: Synchronize access to list of remote fences requested. - * @rma: List of temporary registered windows to be destroyed. - * @rma_tc: List of temporary registered & cached Windows to be destroyed - * @fence: List of remote fence requests - * @mmu_notif_work: Work for registration caching MMU notifier workqueue - * @mmu_notif_cleanup: List of temporary cached windows for reg cache - * @rma_tc_limit: RMA temporary cache limit - */ -struct scif_info { - u8 nodeid; - u8 maxid; - u8 total; - u32 nr_zombies; - struct mutex eplock; - struct mutex connlock; - spinlock_t nb_connect_lock; - spinlock_t port_lock; - struct list_head uaccept; - struct list_head listen; - struct list_head zombie; - struct list_head connected; - struct list_head disconnected; - struct list_head nb_connect_list; - struct work_struct misc_work; - struct mutex conflock; - u8 en_msg_log; - u8 p2p_enable; - struct miscdevice mdev; - struct work_struct conn_work; - wait_queue_head_t exitwq; - struct scif_dev *loopb_dev; - struct workqueue_struct *loopb_wq; - char loopb_wqname[16]; - struct work_struct loopb_work; - struct list_head loopb_recv_q; - bool card_initiated_exit; - spinlock_t rmalock; - struct mutex fencelock; - struct list_head rma; - struct list_head rma_tc; - struct list_head fence; - struct work_struct mmu_notif_work; - struct list_head mmu_notif_cleanup; - unsigned long rma_tc_limit; -}; - -/* - * struct scif_p2p_info - SCIF mapping information used for P2P - * - * @ppi_peer_id - SCIF peer node id - * @ppi_sg - Scatter list for bar information (One for mmio and one for aper) - * @sg_nentries - Number of entries in the scatterlist - * @ppi_da: DMA address for MMIO and APER bars - * @ppi_len: Length of MMIO and APER bars - * @ppi_list: Link in list of mapping information - */ -struct scif_p2p_info { - u8 ppi_peer_id; - struct scatterlist *ppi_sg[2]; - u64 sg_nentries[2]; - dma_addr_t ppi_da[2]; - u64 ppi_len[2]; -#define SCIF_PPI_MMIO 0 -#define SCIF_PPI_APER 1 - struct list_head ppi_list; -}; - -/* - * struct scif_dev - SCIF remote device specific fields - * - * @node: Node id - * @p2p: List of P2P mapping information - * @qpairs: The node queue pair for exchanging control messages - * @intr_wq: Workqueue for handling Node QP messages - * @intr_wqname: Name of node QP workqueue for handling interrupts - * @intr_bh: Used for submitting work to intr_wq - * @lock: Lock used for synchronizing access to the scif device - * @sdev: SCIF hardware device on the SCIF hardware bus - * @db: doorbell the peer will trigger to generate an interrupt on self - * @rdb: Doorbell to trigger on the peer to generate an interrupt on the peer - * @cookie: Cookie received while registering the interrupt handler - * @peer_add_work: Work for handling device_add for peer devices - * @p2p_dwork: Delayed work to enable polling for P2P state - * @qp_dwork: Delayed work for enabling polling for remote QP information - * @p2p_retry: Number of times to retry polling of P2P state - * @base_addr: P2P aperture bar base address - * @mic_mw mmio: The peer MMIO information used for P2P - * @spdev: SCIF peer device on the SCIF peer bus - * @node_remove_ack_pending: True if a node_remove_ack is pending - * @exit_ack_pending: true if an exit_ack is pending - * @disconn_wq: Used while waiting for a node remove response - * @disconn_rescnt: Keeps track of number of node remove requests sent - * @exit: Status of exit message - * @qp_dma_addr: Queue pair DMA address passed to the peer - * @dma_ch_idx: Round robin index for DMA channels - * @signal_pool: DMA pool used for scheduling scif_fence_signal DMA's -*/ -struct scif_dev { - u8 node; - struct list_head p2p; - struct scif_qp *qpairs; - struct workqueue_struct *intr_wq; - char intr_wqname[16]; - struct work_struct intr_bh; - struct mutex lock; - struct scif_hw_dev *sdev; - int db; - int rdb; - struct mic_irq *cookie; - struct work_struct peer_add_work; - struct delayed_work p2p_dwork; - struct delayed_work qp_dwork; - int p2p_retry; - dma_addr_t base_addr; - struct mic_mw mmio; - struct scif_peer_dev __rcu *spdev; - bool node_remove_ack_pending; - bool exit_ack_pending; - wait_queue_head_t disconn_wq; - atomic_t disconn_rescnt; - enum scif_msg_state exit; - dma_addr_t qp_dma_addr; - int dma_ch_idx; - struct dma_pool *signal_pool; -}; - -extern bool scif_reg_cache_enable; -extern bool scif_ulimit_check; -extern struct scif_info scif_info; -extern struct idr scif_ports; -extern struct bus_type scif_peer_bus; -extern struct scif_dev *scif_dev; -extern const struct file_operations scif_fops; -extern const struct file_operations scif_anon_fops; - -/* Size of the RB for the Node QP */ -#define SCIF_NODE_QP_SIZE 0x10000 - -#include "scif_nodeqp.h" -#include "scif_rma.h" -#include "scif_rma_list.h" - -/* - * scifdev_self: - * @dev: The remote SCIF Device - * - * Returns true if the SCIF Device passed is the self aka Loopback SCIF device. - */ -static inline int scifdev_self(struct scif_dev *dev) -{ - return dev->node == scif_info.nodeid; -} - -static inline bool scif_is_mgmt_node(void) -{ - return !scif_info.nodeid; -} - -/* - * scifdev_is_p2p: - * @dev: The remote SCIF Device - * - * Returns true if the SCIF Device is a MIC Peer to Peer SCIF device. - */ -static inline bool scifdev_is_p2p(struct scif_dev *dev) -{ - if (scif_is_mgmt_node()) - return false; - else - return dev != &scif_dev[SCIF_MGMT_NODE] && - !scifdev_self(dev); -} - -/* - * scifdev_alive: - * @scifdev: The remote SCIF Device - * - * Returns true if the remote SCIF Device is running or sleeping for - * this endpoint. - */ -static inline int _scifdev_alive(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev; - - rcu_read_lock(); - spdev = rcu_dereference(scifdev->spdev); - rcu_read_unlock(); - return !!spdev; -} - -#include "scif_epd.h" - -void __init scif_init_debugfs(void); -void scif_exit_debugfs(void); -int scif_setup_intr_wq(struct scif_dev *scifdev); -void scif_destroy_intr_wq(struct scif_dev *scifdev); -void scif_cleanup_scifdev(struct scif_dev *dev); -void scif_handle_remove_node(int node); -void scif_disconnect_node(u32 node_id, bool mgmt_initiated); -void scif_free_qp(struct scif_dev *dev); -void scif_misc_handler(struct work_struct *work); -void scif_stop(struct scif_dev *scifdev); -irqreturn_t scif_intr_handler(int irq, void *data); -#endif /* SCIF_MAIN_H */ diff --git a/drivers/misc/mic/scif/scif_map.h b/drivers/misc/mic/scif/scif_map.h deleted file mode 100644 index 96b760819bfc..000000000000 --- a/drivers/misc/mic/scif/scif_map.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef SCIF_MAP_H -#define SCIF_MAP_H - -#include "../bus/scif_bus.h" - -static __always_inline void * -scif_alloc_coherent(dma_addr_t *dma_handle, - struct scif_dev *scifdev, size_t size, - gfp_t gfp) -{ - void *va; - - if (scifdev_self(scifdev)) { - va = kmalloc(size, gfp); - if (va) - *dma_handle = virt_to_phys(va); - } else { - va = dma_alloc_coherent(&scifdev->sdev->dev, - size, dma_handle, gfp); - if (va && scifdev_is_p2p(scifdev)) - *dma_handle = *dma_handle + scifdev->base_addr; - } - return va; -} - -static __always_inline void -scif_free_coherent(void *va, dma_addr_t local, - struct scif_dev *scifdev, size_t size) -{ - if (scifdev_self(scifdev)) { - kfree(va); - } else { - if (scifdev_is_p2p(scifdev) && local > scifdev->base_addr) - local = local - scifdev->base_addr; - dma_free_coherent(&scifdev->sdev->dev, - size, va, local); - } -} - -static __always_inline int -scif_map_single(dma_addr_t *dma_handle, - void *local, struct scif_dev *scifdev, size_t size) -{ - int err = 0; - - if (scifdev_self(scifdev)) { - *dma_handle = virt_to_phys((local)); - } else { - *dma_handle = dma_map_single(&scifdev->sdev->dev, - local, size, DMA_BIDIRECTIONAL); - if (dma_mapping_error(&scifdev->sdev->dev, *dma_handle)) - err = -ENOMEM; - else if (scifdev_is_p2p(scifdev)) - *dma_handle = *dma_handle + scifdev->base_addr; - } - if (err) - *dma_handle = 0; - return err; -} - -static __always_inline void -scif_unmap_single(dma_addr_t local, struct scif_dev *scifdev, - size_t size) -{ - if (!scifdev_self(scifdev)) { - if (scifdev_is_p2p(scifdev)) - local = local - scifdev->base_addr; - dma_unmap_single(&scifdev->sdev->dev, local, - size, DMA_BIDIRECTIONAL); - } -} - -static __always_inline void * -scif_ioremap(dma_addr_t phys, size_t size, struct scif_dev *scifdev) -{ - void *out_virt; - struct scif_hw_dev *sdev = scifdev->sdev; - - if (scifdev_self(scifdev)) - out_virt = phys_to_virt(phys); - else - out_virt = (void __force *) - sdev->hw_ops->remap(sdev, phys, size); - return out_virt; -} - -static __always_inline void -scif_iounmap(void *virt, size_t len, struct scif_dev *scifdev) -{ - if (!scifdev_self(scifdev)) { - struct scif_hw_dev *sdev = scifdev->sdev; - - sdev->hw_ops->unmap(sdev, (void __force __iomem *)virt); - } -} - -static __always_inline int -scif_map_page(dma_addr_t *dma_handle, struct page *page, - struct scif_dev *scifdev) -{ - int err = 0; - - if (scifdev_self(scifdev)) { - *dma_handle = page_to_phys(page); - } else { - struct scif_hw_dev *sdev = scifdev->sdev; - *dma_handle = dma_map_page(&sdev->dev, - page, 0x0, PAGE_SIZE, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(&sdev->dev, *dma_handle)) - err = -ENOMEM; - else if (scifdev_is_p2p(scifdev)) - *dma_handle = *dma_handle + scifdev->base_addr; - } - if (err) - *dma_handle = 0; - return err; -} -#endif /* SCIF_MAP_H */ diff --git a/drivers/misc/mic/scif/scif_mmap.c b/drivers/misc/mic/scif/scif_mmap.c deleted file mode 100644 index a151d416f39c..000000000000 --- a/drivers/misc/mic/scif/scif_mmap.c +++ /dev/null @@ -1,690 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" - -/* - * struct scif_vma_info - Information about a remote memory mapping - * created via scif_mmap(..) - * @vma: VM area struct - * @list: link to list of active vmas - */ -struct scif_vma_info { - struct vm_area_struct *vma; - struct list_head list; -}; - -void scif_recv_munmap(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_rma_req req; - struct scif_window *window = NULL; - struct scif_window *recv_window = - (struct scif_window *)msg->payload[0]; - struct scif_endpt *ep; - - ep = (struct scif_endpt *)recv_window->ep; - req.out_window = &window; - req.offset = recv_window->offset; - req.prot = recv_window->prot; - req.nr_bytes = recv_window->nr_pages << PAGE_SHIFT; - req.type = SCIF_WINDOW_FULL; - req.head = &ep->rma_info.reg_list; - msg->payload[0] = ep->remote_ep; - - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - if (scif_query_window(&req)) { - dev_err(&scifdev->sdev->dev, - "%s %d -ENXIO\n", __func__, __LINE__); - msg->uop = SCIF_UNREGISTER_ACK; - goto error; - } - - scif_put_window(window, window->nr_pages); - - if (!window->ref_count) { - atomic_inc(&ep->rma_info.tw_refcount); - ep->rma_info.async_list_del = 1; - list_del_init(&window->list); - scif_free_window_offset(ep, window, window->offset); - } -error: - mutex_unlock(&ep->rma_info.rma_lock); - if (window && !window->ref_count) - scif_queue_for_cleanup(window, &scif_info.rma); -} - -/* - * Remove valid remote memory mappings created via scif_mmap(..) from the - * process address space since the remote node is lost - */ -static void __scif_zap_mmaps(struct scif_endpt *ep) -{ - struct list_head *item; - struct scif_vma_info *info; - struct vm_area_struct *vma; - unsigned long size; - - spin_lock(&ep->lock); - list_for_each(item, &ep->rma_info.vma_list) { - info = list_entry(item, struct scif_vma_info, list); - vma = info->vma; - size = vma->vm_end - vma->vm_start; - zap_vma_ptes(vma, vma->vm_start, size); - dev_dbg(scif_info.mdev.this_device, - "%s ep %p zap vma %p size 0x%lx\n", - __func__, ep, info->vma, size); - } - spin_unlock(&ep->lock); -} - -/* - * Traverse the list of endpoints for a particular remote node and - * zap valid remote memory mappings since the remote node is lost - */ -static void _scif_zap_mmaps(int node, struct list_head *head) -{ - struct scif_endpt *ep; - struct list_head *item; - - mutex_lock(&scif_info.connlock); - list_for_each(item, head) { - ep = list_entry(item, struct scif_endpt, list); - if (ep->remote_dev->node == node) - __scif_zap_mmaps(ep); - } - mutex_unlock(&scif_info.connlock); -} - -/* - * Wrapper for removing remote memory mappings for a particular node. This API - * is called by peer nodes as part of handling a lost node. - */ -void scif_zap_mmaps(int node) -{ - _scif_zap_mmaps(node, &scif_info.connected); - _scif_zap_mmaps(node, &scif_info.disconnected); -} - -/* - * This API is only called while handling a lost node: - * a) Remote node is dead. - * b) Remote memory mappings have been zapped - * So we can traverse the remote_reg_list without any locks. Since - * the window has not yet been unregistered we can drop the ref count - * and queue it to the cleanup thread. - */ -static void __scif_cleanup_rma_for_zombies(struct scif_endpt *ep) -{ - struct list_head *pos, *tmp; - struct scif_window *window; - - list_for_each_safe(pos, tmp, &ep->rma_info.remote_reg_list) { - window = list_entry(pos, struct scif_window, list); - if (window->ref_count) - scif_put_window(window, window->nr_pages); - else - dev_err(scif_info.mdev.this_device, - "%s %d unexpected\n", - __func__, __LINE__); - if (!window->ref_count) { - atomic_inc(&ep->rma_info.tw_refcount); - list_del_init(&window->list); - scif_queue_for_cleanup(window, &scif_info.rma); - } - } -} - -/* Cleanup remote registration lists for zombie endpoints */ -void scif_cleanup_rma_for_zombies(int node) -{ - struct scif_endpt *ep; - struct list_head *item; - - mutex_lock(&scif_info.eplock); - list_for_each(item, &scif_info.zombie) { - ep = list_entry(item, struct scif_endpt, list); - if (ep->remote_dev && ep->remote_dev->node == node) - __scif_cleanup_rma_for_zombies(ep); - } - mutex_unlock(&scif_info.eplock); - flush_work(&scif_info.misc_work); -} - -/* Insert the VMA into the per endpoint VMA list */ -static int scif_insert_vma(struct scif_endpt *ep, struct vm_area_struct *vma) -{ - struct scif_vma_info *info; - int err = 0; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - err = -ENOMEM; - goto done; - } - info->vma = vma; - spin_lock(&ep->lock); - list_add_tail(&info->list, &ep->rma_info.vma_list); - spin_unlock(&ep->lock); -done: - return err; -} - -/* Delete the VMA from the per endpoint VMA list */ -static void scif_delete_vma(struct scif_endpt *ep, struct vm_area_struct *vma) -{ - struct list_head *item; - struct scif_vma_info *info; - - spin_lock(&ep->lock); - list_for_each(item, &ep->rma_info.vma_list) { - info = list_entry(item, struct scif_vma_info, list); - if (info->vma == vma) { - list_del(&info->list); - kfree(info); - break; - } - } - spin_unlock(&ep->lock); -} - -static phys_addr_t scif_get_phys(phys_addr_t phys, struct scif_endpt *ep) -{ - struct scif_dev *scifdev = (struct scif_dev *)ep->remote_dev; - struct scif_hw_dev *sdev = scifdev->sdev; - phys_addr_t out_phys, apt_base = 0; - - /* - * If the DMA address is card relative then we need to add the - * aperture base for mmap to work correctly - */ - if (!scifdev_self(scifdev) && sdev->aper && sdev->card_rel_da) - apt_base = sdev->aper->pa; - out_phys = apt_base + phys; - return out_phys; -} - -int scif_get_pages(scif_epd_t epd, off_t offset, size_t len, - struct scif_range **pages) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_rma_req req; - struct scif_window *window = NULL; - int nr_pages, err, i; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI get_pinned_pages: ep %p offset 0x%lx len 0x%lx\n", - ep, offset, len); - err = scif_verify_epd(ep); - if (err) - return err; - - if (!len || (offset < 0) || - (offset + len < offset) || - (ALIGN(offset, PAGE_SIZE) != offset) || - (ALIGN(len, PAGE_SIZE) != len)) - return -EINVAL; - - nr_pages = len >> PAGE_SHIFT; - - req.out_window = &window; - req.offset = offset; - req.prot = 0; - req.nr_bytes = len; - req.type = SCIF_WINDOW_SINGLE; - req.head = &ep->rma_info.remote_reg_list; - - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - err = scif_query_window(&req); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error; - } - - /* Allocate scif_range */ - *pages = kzalloc(sizeof(**pages), GFP_KERNEL); - if (!*pages) { - err = -ENOMEM; - goto error; - } - - /* Allocate phys addr array */ - (*pages)->phys_addr = scif_zalloc(nr_pages * sizeof(dma_addr_t)); - if (!((*pages)->phys_addr)) { - err = -ENOMEM; - goto error; - } - - if (scif_is_mgmt_node() && !scifdev_self(ep->remote_dev)) { - /* Allocate virtual address array */ - ((*pages)->va = scif_zalloc(nr_pages * sizeof(void *))); - if (!(*pages)->va) { - err = -ENOMEM; - goto error; - } - } - /* Populate the values */ - (*pages)->cookie = window; - (*pages)->nr_pages = nr_pages; - (*pages)->prot_flags = window->prot; - - for (i = 0; i < nr_pages; i++) { - (*pages)->phys_addr[i] = - __scif_off_to_dma_addr(window, offset + - (i * PAGE_SIZE)); - (*pages)->phys_addr[i] = scif_get_phys((*pages)->phys_addr[i], - ep); - if (scif_is_mgmt_node() && !scifdev_self(ep->remote_dev)) - (*pages)->va[i] = - ep->remote_dev->sdev->aper->va + - (*pages)->phys_addr[i] - - ep->remote_dev->sdev->aper->pa; - } - - scif_get_window(window, nr_pages); -error: - mutex_unlock(&ep->rma_info.rma_lock); - if (err) { - if (*pages) { - scif_free((*pages)->phys_addr, - nr_pages * sizeof(dma_addr_t)); - scif_free((*pages)->va, - nr_pages * sizeof(void *)); - kfree(*pages); - *pages = NULL; - } - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - } - return err; -} -EXPORT_SYMBOL_GPL(scif_get_pages); - -int scif_put_pages(struct scif_range *pages) -{ - struct scif_endpt *ep; - struct scif_window *window; - struct scifmsg msg; - - if (!pages || !pages->cookie) - return -EINVAL; - - window = pages->cookie; - - if (!window || window->magic != SCIFEP_MAGIC) - return -EINVAL; - - ep = (struct scif_endpt *)window->ep; - /* - * If the state is SCIFEP_CONNECTED or SCIFEP_DISCONNECTED then the - * callee should be allowed to release references to the pages, - * else the endpoint was not connected in the first place, - * hence the ENOTCONN. - */ - if (ep->state != SCIFEP_CONNECTED && ep->state != SCIFEP_DISCONNECTED) - return -ENOTCONN; - - mutex_lock(&ep->rma_info.rma_lock); - - scif_put_window(window, pages->nr_pages); - - /* Initiate window destruction if ref count is zero */ - if (!window->ref_count) { - list_del(&window->list); - mutex_unlock(&ep->rma_info.rma_lock); - scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan); - /* Inform the peer about this window being destroyed. */ - msg.uop = SCIF_MUNMAP; - msg.src = ep->port; - msg.payload[0] = window->peer_window; - /* No error handling for notification messages */ - scif_nodeqp_send(ep->remote_dev, &msg); - /* Destroy this window from the peer's registered AS */ - scif_destroy_remote_window(window); - } else { - mutex_unlock(&ep->rma_info.rma_lock); - } - - scif_free(pages->phys_addr, pages->nr_pages * sizeof(dma_addr_t)); - scif_free(pages->va, pages->nr_pages * sizeof(void *)); - kfree(pages); - return 0; -} -EXPORT_SYMBOL_GPL(scif_put_pages); - -/* - * scif_rma_list_mmap: - * - * Traverse the remote registration list starting from start_window: - * 1) Create VtoP mappings via remap_pfn_range(..) - * 2) Once step 1) and 2) complete successfully then traverse the range of - * windows again and bump the reference count. - * RMA lock must be held. - */ -static int scif_rma_list_mmap(struct scif_window *start_window, s64 offset, - int nr_pages, struct vm_area_struct *vma) -{ - s64 end_offset, loop_offset = offset; - struct scif_window *window = start_window; - int loop_nr_pages, nr_pages_left = nr_pages; - struct scif_endpt *ep = (struct scif_endpt *)start_window->ep; - struct list_head *head = &ep->rma_info.remote_reg_list; - int i, err = 0; - dma_addr_t phys_addr; - struct scif_window_iter src_win_iter; - size_t contig_bytes = 0; - - might_sleep(); - list_for_each_entry_from(window, head, list) { - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - loop_nr_pages = min_t(int, - (end_offset - loop_offset) >> PAGE_SHIFT, - nr_pages_left); - scif_init_window_iter(window, &src_win_iter); - for (i = 0; i < loop_nr_pages; i++) { - phys_addr = scif_off_to_dma_addr(window, loop_offset, - &contig_bytes, - &src_win_iter); - phys_addr = scif_get_phys(phys_addr, ep); - err = remap_pfn_range(vma, - vma->vm_start + - loop_offset - offset, - phys_addr >> PAGE_SHIFT, - PAGE_SIZE, - vma->vm_page_prot); - if (err) - goto error; - loop_offset += PAGE_SIZE; - } - nr_pages_left -= loop_nr_pages; - if (!nr_pages_left) - break; - } - /* - * No more failures expected. Bump up the ref count for all - * the windows. Another traversal from start_window required - * for handling errors encountered across windows during - * remap_pfn_range(..). - */ - loop_offset = offset; - nr_pages_left = nr_pages; - window = start_window; - head = &ep->rma_info.remote_reg_list; - list_for_each_entry_from(window, head, list) { - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - loop_nr_pages = min_t(int, - (end_offset - loop_offset) >> PAGE_SHIFT, - nr_pages_left); - scif_get_window(window, loop_nr_pages); - nr_pages_left -= loop_nr_pages; - loop_offset += (loop_nr_pages << PAGE_SHIFT); - if (!nr_pages_left) - break; - } -error: - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - return err; -} - -/* - * scif_rma_list_munmap: - * - * Traverse the remote registration list starting from window: - * 1) Decrement ref count. - * 2) If the ref count drops to zero then send a SCIF_MUNMAP message to peer. - * RMA lock must be held. - */ -static void scif_rma_list_munmap(struct scif_window *start_window, - s64 offset, int nr_pages) -{ - struct scifmsg msg; - s64 loop_offset = offset, end_offset; - int loop_nr_pages, nr_pages_left = nr_pages; - struct scif_endpt *ep = (struct scif_endpt *)start_window->ep; - struct list_head *head = &ep->rma_info.remote_reg_list; - struct scif_window *window = start_window, *_window; - - msg.uop = SCIF_MUNMAP; - msg.src = ep->port; - loop_offset = offset; - nr_pages_left = nr_pages; - list_for_each_entry_safe_from(window, _window, head, list) { - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - loop_nr_pages = min_t(int, - (end_offset - loop_offset) >> PAGE_SHIFT, - nr_pages_left); - scif_put_window(window, loop_nr_pages); - if (!window->ref_count) { - struct scif_dev *rdev = ep->remote_dev; - - scif_drain_dma_intr(rdev->sdev, - ep->rma_info.dma_chan); - /* Inform the peer about this munmap */ - msg.payload[0] = window->peer_window; - /* No error handling for Notification messages. */ - scif_nodeqp_send(ep->remote_dev, &msg); - list_del(&window->list); - /* Destroy this window from the peer's registered AS */ - scif_destroy_remote_window(window); - } - nr_pages_left -= loop_nr_pages; - loop_offset += (loop_nr_pages << PAGE_SHIFT); - if (!nr_pages_left) - break; - } -} - -/* - * The private data field of each VMA used to mmap a remote window - * points to an instance of struct vma_pvt - */ -struct vma_pvt { - struct scif_endpt *ep; /* End point for remote window */ - s64 offset; /* offset within remote window */ - bool valid_offset; /* offset is valid only if the original - * mmap request was for a single page - * else the offset within the vma is - * the correct offset - */ - struct kref ref; -}; - -static void vma_pvt_release(struct kref *ref) -{ - struct vma_pvt *vmapvt = container_of(ref, struct vma_pvt, ref); - - kfree(vmapvt); -} - -/** - * scif_vma_open - VMA open driver callback - * @vma: VMM memory area. - * The open method is called by the kernel to allow the subsystem implementing - * the VMA to initialize the area. This method is invoked any time a new - * reference to the VMA is made (when a process forks, for example). - * The one exception happens when the VMA is first created by mmap; - * in this case, the driver's mmap method is called instead. - * This function is also invoked when an existing VMA is split by the kernel - * due to a call to munmap on a subset of the VMA resulting in two VMAs. - * The kernel invokes this function only on one of the two VMAs. - */ -static void scif_vma_open(struct vm_area_struct *vma) -{ - struct vma_pvt *vmapvt = vma->vm_private_data; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI vma open: vma_start 0x%lx vma_end 0x%lx\n", - vma->vm_start, vma->vm_end); - scif_insert_vma(vmapvt->ep, vma); - kref_get(&vmapvt->ref); -} - -/** - * scif_munmap - VMA close driver callback. - * @vma: VMM memory area. - * When an area is destroyed, the kernel calls its close operation. - * Note that there's no usage count associated with VMA's; the area - * is opened and closed exactly once by each process that uses it. - */ -static void scif_munmap(struct vm_area_struct *vma) -{ - struct scif_endpt *ep; - struct vma_pvt *vmapvt = vma->vm_private_data; - int nr_pages = vma_pages(vma); - s64 offset; - struct scif_rma_req req; - struct scif_window *window = NULL; - int err; - - might_sleep(); - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI munmap: vma_start 0x%lx vma_end 0x%lx\n", - vma->vm_start, vma->vm_end); - ep = vmapvt->ep; - offset = vmapvt->valid_offset ? vmapvt->offset : - (vma->vm_pgoff) << PAGE_SHIFT; - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI munmap: ep %p nr_pages 0x%x offset 0x%llx\n", - ep, nr_pages, offset); - req.out_window = &window; - req.offset = offset; - req.nr_bytes = vma->vm_end - vma->vm_start; - req.prot = vma->vm_flags & (VM_READ | VM_WRITE); - req.type = SCIF_WINDOW_PARTIAL; - req.head = &ep->rma_info.remote_reg_list; - - mutex_lock(&ep->rma_info.rma_lock); - - err = scif_query_window(&req); - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - else - scif_rma_list_munmap(window, offset, nr_pages); - - mutex_unlock(&ep->rma_info.rma_lock); - /* - * The kernel probably zeroes these out but we still want - * to clean up our own mess just in case. - */ - vma->vm_ops = NULL; - vma->vm_private_data = NULL; - kref_put(&vmapvt->ref, vma_pvt_release); - scif_delete_vma(ep, vma); -} - -static const struct vm_operations_struct scif_vm_ops = { - .open = scif_vma_open, - .close = scif_munmap, -}; - -/** - * scif_mmap - Map pages in virtual address space to a remote window. - * @vma: VMM memory area. - * @epd: endpoint descriptor - * - * Return: Upon successful completion, scif_mmap() returns zero - * else an apt error is returned as documented in scif.h - */ -int scif_mmap(struct vm_area_struct *vma, scif_epd_t epd) -{ - struct scif_rma_req req; - struct scif_window *window = NULL; - struct scif_endpt *ep = (struct scif_endpt *)epd; - s64 start_offset = vma->vm_pgoff << PAGE_SHIFT; - int nr_pages = vma_pages(vma); - int err; - struct vma_pvt *vmapvt; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI mmap: ep %p start_offset 0x%llx nr_pages 0x%x\n", - ep, start_offset, nr_pages); - err = scif_verify_epd(ep); - if (err) - return err; - - might_sleep(); - - err = scif_insert_vma(ep, vma); - if (err) - return err; - - vmapvt = kzalloc(sizeof(*vmapvt), GFP_KERNEL); - if (!vmapvt) { - scif_delete_vma(ep, vma); - return -ENOMEM; - } - - vmapvt->ep = ep; - kref_init(&vmapvt->ref); - - req.out_window = &window; - req.offset = start_offset; - req.nr_bytes = vma->vm_end - vma->vm_start; - req.prot = vma->vm_flags & (VM_READ | VM_WRITE); - req.type = SCIF_WINDOW_PARTIAL; - req.head = &ep->rma_info.remote_reg_list; - - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - err = scif_query_window(&req); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unlock; - } - - /* Default prot for loopback */ - if (!scifdev_self(ep->remote_dev)) - vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); - - /* - * VM_DONTCOPY - Do not copy this vma on fork - * VM_DONTEXPAND - Cannot expand with mremap() - * VM_RESERVED - Count as reserved_vm like IO - * VM_PFNMAP - Page-ranges managed without "struct page" - * VM_IO - Memory mapped I/O or similar - * - * We do not want to copy this VMA automatically on a fork(), - * expand this VMA due to mremap() or swap out these pages since - * the VMA is actually backed by physical pages in the remote - * node's physical memory and not via a struct page. - */ - vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP; - - if (!scifdev_self(ep->remote_dev)) - vma->vm_flags |= VM_IO | VM_PFNMAP; - - /* Map this range of windows */ - err = scif_rma_list_mmap(window, start_offset, nr_pages, vma); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unlock; - } - /* Set up the driver call back */ - vma->vm_ops = &scif_vm_ops; - vma->vm_private_data = vmapvt; -error_unlock: - mutex_unlock(&ep->rma_info.rma_lock); - if (err) { - kfree(vmapvt); - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - scif_delete_vma(ep, vma); - } - return err; -} diff --git a/drivers/misc/mic/scif/scif_nm.c b/drivers/misc/mic/scif/scif_nm.c deleted file mode 100644 index c4d9422082b7..000000000000 --- a/drivers/misc/mic/scif/scif_nm.c +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_peer_bus.h" - -#include "scif_main.h" -#include "scif_map.h" - -/** - * scif_invalidate_ep() - Set state for all connected endpoints - * to disconnected and wake up all send/recv waitqueues - * - * @node: Node to invalidate - */ -static void scif_invalidate_ep(int node) -{ - struct scif_endpt *ep; - struct list_head *pos, *tmpq; - - flush_work(&scif_info.conn_work); - mutex_lock(&scif_info.connlock); - list_for_each_safe(pos, tmpq, &scif_info.disconnected) { - ep = list_entry(pos, struct scif_endpt, list); - if (ep->remote_dev->node == node) { - scif_unmap_all_windows(ep); - spin_lock(&ep->lock); - scif_cleanup_ep_qp(ep); - spin_unlock(&ep->lock); - } - } - list_for_each_safe(pos, tmpq, &scif_info.connected) { - ep = list_entry(pos, struct scif_endpt, list); - if (ep->remote_dev->node == node) { - list_del(pos); - spin_lock(&ep->lock); - ep->state = SCIFEP_DISCONNECTED; - list_add_tail(&ep->list, &scif_info.disconnected); - scif_cleanup_ep_qp(ep); - wake_up_interruptible(&ep->sendwq); - wake_up_interruptible(&ep->recvwq); - spin_unlock(&ep->lock); - scif_unmap_all_windows(ep); - } - } - mutex_unlock(&scif_info.connlock); -} - -void scif_free_qp(struct scif_dev *scifdev) -{ - struct scif_qp *qp = scifdev->qpairs; - - if (!qp) - return; - scif_unmap_single(qp->local_buf, scifdev, qp->inbound_q.size); - kfree(qp->inbound_q.rb_base); - scif_unmap_single(qp->local_qp, scifdev, sizeof(struct scif_qp)); - kfree(scifdev->qpairs); - scifdev->qpairs = NULL; -} - -static void scif_cleanup_qp(struct scif_dev *dev) -{ - struct scif_qp *qp = &dev->qpairs[0]; - - if (!qp) - return; - scif_iounmap((void *)qp->remote_qp, sizeof(struct scif_qp), dev); - scif_iounmap((void *)qp->outbound_q.rb_base, - sizeof(struct scif_qp), dev); - qp->remote_qp = NULL; - qp->local_write = 0; - qp->inbound_q.current_write_offset = 0; - qp->inbound_q.current_read_offset = 0; - if (scifdev_is_p2p(dev)) - scif_free_qp(dev); -} - -void scif_send_acks(struct scif_dev *dev) -{ - struct scifmsg msg; - - if (dev->node_remove_ack_pending) { - msg.uop = SCIF_NODE_REMOVE_ACK; - msg.src.node = scif_info.nodeid; - msg.dst.node = SCIF_MGMT_NODE; - msg.payload[0] = dev->node; - scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg); - dev->node_remove_ack_pending = false; - } - if (dev->exit_ack_pending) { - msg.uop = SCIF_EXIT_ACK; - msg.src.node = scif_info.nodeid; - msg.dst.node = dev->node; - scif_nodeqp_send(dev, &msg); - dev->exit_ack_pending = false; - } -} - -/** - * scif_cleanup_scifdev - Uninitialize SCIF data structures for remote - * SCIF device. - * @dev: Remote SCIF device. - */ -void scif_cleanup_scifdev(struct scif_dev *dev) -{ - struct scif_hw_dev *sdev = dev->sdev; - - if (!dev->sdev) - return; - if (scifdev_is_p2p(dev)) { - if (dev->cookie) { - sdev->hw_ops->free_irq(sdev, dev->cookie, dev); - dev->cookie = NULL; - } - scif_destroy_intr_wq(dev); - } - flush_work(&scif_info.misc_work); - scif_destroy_p2p(dev); - scif_invalidate_ep(dev->node); - scif_zap_mmaps(dev->node); - scif_cleanup_rma_for_zombies(dev->node); - flush_work(&scif_info.misc_work); - scif_send_acks(dev); - if (!dev->node && scif_info.card_initiated_exit) { - /* - * Send an SCIF_EXIT message which is the last message from MIC - * to the Host and wait for a SCIF_EXIT_ACK - */ - scif_send_exit(dev); - scif_info.card_initiated_exit = false; - } - scif_cleanup_qp(dev); -} - -/** - * scif_remove_node - * - * @node: Node to remove - */ -void scif_handle_remove_node(int node) -{ - struct scif_dev *scifdev = &scif_dev[node]; - - if (scif_peer_unregister_device(scifdev)) - scif_send_acks(scifdev); -} - -static int scif_send_rmnode_msg(int node, int remove_node) -{ - struct scifmsg notif_msg; - struct scif_dev *dev = &scif_dev[node]; - - notif_msg.uop = SCIF_NODE_REMOVE; - notif_msg.src.node = scif_info.nodeid; - notif_msg.dst.node = node; - notif_msg.payload[0] = remove_node; - return scif_nodeqp_send(dev, ¬if_msg); -} - -/** - * scif_node_disconnect - * - * @node_id: source node id [in] - * @mgmt_initiated: Disconnection initiated from the mgmt node - * - * Disconnect a node from the scif network. - */ -void scif_disconnect_node(u32 node_id, bool mgmt_initiated) -{ - int ret; - int msg_cnt = 0; - u32 i = 0; - struct scif_dev *scifdev = &scif_dev[node_id]; - - if (!node_id) - return; - - atomic_set(&scifdev->disconn_rescnt, 0); - - /* Destroy p2p network */ - for (i = 1; i <= scif_info.maxid; i++) { - if (i == node_id) - continue; - ret = scif_send_rmnode_msg(i, node_id); - if (!ret) - msg_cnt++; - } - /* Wait for the remote nodes to respond with SCIF_NODE_REMOVE_ACK */ - ret = wait_event_timeout(scifdev->disconn_wq, - (atomic_read(&scifdev->disconn_rescnt) - == msg_cnt), SCIF_NODE_ALIVE_TIMEOUT); - /* Tell the card to clean up */ - if (mgmt_initiated && _scifdev_alive(scifdev)) - /* - * Send an SCIF_EXIT message which is the last message from Host - * to the MIC and wait for a SCIF_EXIT_ACK - */ - scif_send_exit(scifdev); - atomic_set(&scifdev->disconn_rescnt, 0); - /* Tell the mgmt node to clean up */ - ret = scif_send_rmnode_msg(SCIF_MGMT_NODE, node_id); - if (!ret) - /* Wait for mgmt node to respond with SCIF_NODE_REMOVE_ACK */ - wait_event_timeout(scifdev->disconn_wq, - (atomic_read(&scifdev->disconn_rescnt) == 1), - SCIF_NODE_ALIVE_TIMEOUT); -} - -void scif_get_node_info(void) -{ - struct scifmsg msg; - DECLARE_COMPLETION_ONSTACK(node_info); - - msg.uop = SCIF_GET_NODE_INFO; - msg.src.node = scif_info.nodeid; - msg.dst.node = SCIF_MGMT_NODE; - msg.payload[3] = (u64)&node_info; - - if ((scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], &msg))) - return; - - /* Wait for a response with SCIF_GET_NODE_INFO */ - wait_for_completion(&node_info); -} diff --git a/drivers/misc/mic/scif/scif_nodeqp.c b/drivers/misc/mic/scif/scif_nodeqp.c deleted file mode 100644 index 384ce08fa98a..000000000000 --- a/drivers/misc/mic/scif/scif_nodeqp.c +++ /dev/null @@ -1,1349 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "../bus/scif_bus.h" -#include "scif_peer_bus.h" -#include "scif_main.h" -#include "scif_nodeqp.h" -#include "scif_map.h" - -/* - ************************************************************************ - * SCIF node Queue Pair (QP) setup flow: - * - * 1) SCIF driver gets probed with a scif_hw_dev via the scif_hw_bus - * 2) scif_setup_qp(..) allocates the local qp and calls - * scif_setup_qp_connect(..) which allocates and maps the local - * buffer for the inbound QP - * 3) The local node updates the device page with the DMA address of the QP - * 4) A delayed work is scheduled (qp_dwork) which periodically reads if - * the peer node has updated its QP DMA address - * 5) Once a valid non zero address is found in the QP DMA address field - * in the device page, the local node maps the remote node's QP, - * updates its outbound QP and sends a SCIF_INIT message to the peer - * 6) The SCIF_INIT message is received by the peer node QP interrupt bottom - * half handler by calling scif_init(..) - * 7) scif_init(..) registers a new SCIF peer node by calling - * scif_peer_register_device(..) which signifies the addition of a new - * SCIF node - * 8) On the mgmt node, P2P network setup/teardown is initiated if all the - * remote nodes are online via scif_p2p_setup(..) - * 9) For P2P setup, the host maps the remote nodes' aperture and memory - * bars and sends a SCIF_NODE_ADD message to both nodes - * 10) As part of scif_nodeadd, both nodes set up their local inbound - * QPs and send a SCIF_NODE_ADD_ACK to the mgmt node - * 11) As part of scif_node_add_ack(..) the mgmt node forwards the - * SCIF_NODE_ADD_ACK to the remote nodes - * 12) As part of scif_node_add_ack(..) the remote nodes update their - * outbound QPs, make sure they can access memory on the remote node - * and then add a new SCIF peer node by calling - * scif_peer_register_device(..) which signifies the addition of a new - * SCIF node. - * 13) The SCIF network is now established across all nodes. - * - ************************************************************************ - * SCIF node QP teardown flow (initiated by non mgmt node): - * - * 1) SCIF driver gets a remove callback with a scif_hw_dev via the scif_hw_bus - * 2) The device page QP DMA address field is updated with 0x0 - * 3) A non mgmt node now cleans up all local data structures and sends a - * SCIF_EXIT message to the peer and waits for a SCIF_EXIT_ACK - * 4) As part of scif_exit(..) handling scif_disconnect_node(..) is called - * 5) scif_disconnect_node(..) sends a SCIF_NODE_REMOVE message to all the - * peers and waits for a SCIF_NODE_REMOVE_ACK - * 6) As part of scif_node_remove(..) a remote node unregisters the peer - * node from the SCIF network and sends a SCIF_NODE_REMOVE_ACK - * 7) When the mgmt node has received all the SCIF_NODE_REMOVE_ACKs - * it sends itself a node remove message whose handling cleans up local - * data structures and unregisters the peer node from the SCIF network - * 8) The mgmt node sends a SCIF_EXIT_ACK - * 9) Upon receipt of the SCIF_EXIT_ACK the node initiating the teardown - * completes the SCIF remove routine - * 10) The SCIF network is now torn down for the node initiating the - * teardown sequence - * - ************************************************************************ - * SCIF node QP teardown flow (initiated by mgmt node): - * - * 1) SCIF driver gets a remove callback with a scif_hw_dev via the scif_hw_bus - * 2) The device page QP DMA address field is updated with 0x0 - * 3) The mgmt node calls scif_disconnect_node(..) - * 4) scif_disconnect_node(..) sends a SCIF_NODE_REMOVE message to all the peers - * and waits for a SCIF_NODE_REMOVE_ACK - * 5) As part of scif_node_remove(..) a remote node unregisters the peer - * node from the SCIF network and sends a SCIF_NODE_REMOVE_ACK - * 6) When the mgmt node has received all the SCIF_NODE_REMOVE_ACKs - * it unregisters the peer node from the SCIF network - * 7) The mgmt node sends a SCIF_EXIT message and waits for a SCIF_EXIT_ACK. - * 8) A non mgmt node upon receipt of a SCIF_EXIT message calls scif_stop(..) - * which would clean up local data structures for all SCIF nodes and - * then send a SCIF_EXIT_ACK back to the mgmt node - * 9) Upon receipt of the SCIF_EXIT_ACK the the mgmt node sends itself a node - * remove message whose handling cleans up local data structures and - * destroys any P2P mappings. - * 10) The SCIF hardware device for which a remove callback was received is now - * disconnected from the SCIF network. - */ -/* - * Initializes "local" data structures for the QP. Allocates the QP - * ring buffer (rb) and initializes the "in bound" queue. - */ -int scif_setup_qp_connect(struct scif_qp *qp, dma_addr_t *qp_offset, - int local_size, struct scif_dev *scifdev) -{ - void *local_q = qp->inbound_q.rb_base; - int err = 0; - u32 tmp_rd = 0; - - spin_lock_init(&qp->send_lock); - spin_lock_init(&qp->recv_lock); - - /* Allocate rb only if not already allocated */ - if (!local_q) { - local_q = kzalloc(local_size, GFP_KERNEL); - if (!local_q) { - err = -ENOMEM; - return err; - } - } - - err = scif_map_single(&qp->local_buf, local_q, scifdev, local_size); - if (err) - goto kfree; - /* - * To setup the inbound_q, the buffer lives locally, the read pointer - * is remote and the write pointer is local. - */ - scif_rb_init(&qp->inbound_q, - &tmp_rd, - &qp->local_write, - local_q, get_count_order(local_size)); - /* - * The read pointer is NULL initially and it is unsafe to use the ring - * buffer til this changes! - */ - qp->inbound_q.read_ptr = NULL; - err = scif_map_single(qp_offset, qp, - scifdev, sizeof(struct scif_qp)); - if (err) - goto unmap; - qp->local_qp = *qp_offset; - return err; -unmap: - scif_unmap_single(qp->local_buf, scifdev, local_size); - qp->local_buf = 0; -kfree: - kfree(local_q); - return err; -} - -/* When the other side has already done it's allocation, this is called */ -int scif_setup_qp_accept(struct scif_qp *qp, dma_addr_t *qp_offset, - dma_addr_t phys, int local_size, - struct scif_dev *scifdev) -{ - void *local_q; - void *remote_q; - struct scif_qp *remote_qp; - int remote_size; - int err = 0; - - spin_lock_init(&qp->send_lock); - spin_lock_init(&qp->recv_lock); - /* Start by figuring out where we need to point */ - remote_qp = scif_ioremap(phys, sizeof(struct scif_qp), scifdev); - if (!remote_qp) - return -EIO; - qp->remote_qp = remote_qp; - if (qp->remote_qp->magic != SCIFEP_MAGIC) { - err = -EIO; - goto iounmap; - } - qp->remote_buf = remote_qp->local_buf; - remote_size = qp->remote_qp->inbound_q.size; - remote_q = scif_ioremap(qp->remote_buf, remote_size, scifdev); - if (!remote_q) { - err = -EIO; - goto iounmap; - } - qp->remote_qp->local_write = 0; - /* - * To setup the outbound_q, the buffer lives in remote memory, - * the read pointer is local, the write pointer is remote - */ - scif_rb_init(&qp->outbound_q, - &qp->local_read, - &qp->remote_qp->local_write, - remote_q, - get_count_order(remote_size)); - local_q = kzalloc(local_size, GFP_KERNEL); - if (!local_q) { - err = -ENOMEM; - goto iounmap_1; - } - err = scif_map_single(&qp->local_buf, local_q, scifdev, local_size); - if (err) - goto kfree; - qp->remote_qp->local_read = 0; - /* - * To setup the inbound_q, the buffer lives locally, the read pointer - * is remote and the write pointer is local - */ - scif_rb_init(&qp->inbound_q, - &qp->remote_qp->local_read, - &qp->local_write, - local_q, get_count_order(local_size)); - err = scif_map_single(qp_offset, qp, scifdev, - sizeof(struct scif_qp)); - if (err) - goto unmap; - qp->local_qp = *qp_offset; - return err; -unmap: - scif_unmap_single(qp->local_buf, scifdev, local_size); - qp->local_buf = 0; -kfree: - kfree(local_q); -iounmap_1: - scif_iounmap(remote_q, remote_size, scifdev); - qp->outbound_q.rb_base = NULL; -iounmap: - scif_iounmap(qp->remote_qp, sizeof(struct scif_qp), scifdev); - qp->remote_qp = NULL; - return err; -} - -int scif_setup_qp_connect_response(struct scif_dev *scifdev, - struct scif_qp *qp, u64 payload) -{ - int err = 0; - void *r_buf; - int remote_size; - phys_addr_t tmp_phys; - - qp->remote_qp = scif_ioremap(payload, sizeof(struct scif_qp), scifdev); - - if (!qp->remote_qp) { - err = -ENOMEM; - goto error; - } - - if (qp->remote_qp->magic != SCIFEP_MAGIC) { - dev_err(&scifdev->sdev->dev, - "SCIFEP_MAGIC mismatch between self %d remote %d\n", - scif_dev[scif_info.nodeid].node, scifdev->node); - err = -ENODEV; - goto error; - } - - tmp_phys = qp->remote_qp->local_buf; - remote_size = qp->remote_qp->inbound_q.size; - r_buf = scif_ioremap(tmp_phys, remote_size, scifdev); - - if (!r_buf) - return -EIO; - - qp->local_read = 0; - scif_rb_init(&qp->outbound_q, - &qp->local_read, - &qp->remote_qp->local_write, - r_buf, - get_count_order(remote_size)); - /* - * Because the node QP may already be processing an INIT message, set - * the read pointer so the cached read offset isn't lost - */ - qp->remote_qp->local_read = qp->inbound_q.current_read_offset; - /* - * resetup the inbound_q now that we know where the - * inbound_read really is. - */ - scif_rb_init(&qp->inbound_q, - &qp->remote_qp->local_read, - &qp->local_write, - qp->inbound_q.rb_base, - get_count_order(qp->inbound_q.size)); -error: - return err; -} - -static __always_inline void -scif_send_msg_intr(struct scif_dev *scifdev) -{ - struct scif_hw_dev *sdev = scifdev->sdev; - - if (scifdev_is_p2p(scifdev)) - sdev->hw_ops->send_p2p_intr(sdev, scifdev->rdb, &scifdev->mmio); - else - sdev->hw_ops->send_intr(sdev, scifdev->rdb); -} - -int scif_qp_response(phys_addr_t phys, struct scif_dev *scifdev) -{ - int err = 0; - struct scifmsg msg; - - err = scif_setup_qp_connect_response(scifdev, scifdev->qpairs, phys); - if (!err) { - /* - * Now that everything is setup and mapped, we're ready - * to tell the peer about our queue's location - */ - msg.uop = SCIF_INIT; - msg.dst.node = scifdev->node; - err = scif_nodeqp_send(scifdev, &msg); - } - return err; -} - -void scif_send_exit(struct scif_dev *scifdev) -{ - struct scifmsg msg; - int ret; - - scifdev->exit = OP_IN_PROGRESS; - msg.uop = SCIF_EXIT; - msg.src.node = scif_info.nodeid; - msg.dst.node = scifdev->node; - ret = scif_nodeqp_send(scifdev, &msg); - if (ret) - goto done; - /* Wait for a SCIF_EXIT_ACK message */ - wait_event_timeout(scif_info.exitwq, scifdev->exit == OP_COMPLETED, - SCIF_NODE_ALIVE_TIMEOUT); -done: - scifdev->exit = OP_IDLE; -} - -int scif_setup_qp(struct scif_dev *scifdev) -{ - int err = 0; - int local_size; - struct scif_qp *qp; - - local_size = SCIF_NODE_QP_SIZE; - - qp = kzalloc(sizeof(*qp), GFP_KERNEL); - if (!qp) { - err = -ENOMEM; - return err; - } - qp->magic = SCIFEP_MAGIC; - scifdev->qpairs = qp; - err = scif_setup_qp_connect(qp, &scifdev->qp_dma_addr, - local_size, scifdev); - if (err) - goto free_qp; - /* - * We're as setup as we can be. The inbound_q is setup, w/o a usable - * outbound q. When we get a message, the read_ptr will be updated, - * and we will pull the message. - */ - return err; -free_qp: - kfree(scifdev->qpairs); - scifdev->qpairs = NULL; - return err; -} - -static void scif_p2p_freesg(struct scatterlist *sg) -{ - kfree(sg); -} - -static struct scatterlist * -scif_p2p_setsg(phys_addr_t pa, int page_size, int page_cnt) -{ - struct scatterlist *sg; - struct page *page; - int i; - - sg = kmalloc_array(page_cnt, sizeof(struct scatterlist), GFP_KERNEL); - if (!sg) - return NULL; - sg_init_table(sg, page_cnt); - for (i = 0; i < page_cnt; i++) { - page = pfn_to_page(pa >> PAGE_SHIFT); - sg_set_page(&sg[i], page, page_size, 0); - pa += page_size; - } - return sg; -} - -/* Init p2p mappings required to access peerdev from scifdev */ -static struct scif_p2p_info * -scif_init_p2p_info(struct scif_dev *scifdev, struct scif_dev *peerdev) -{ - struct scif_p2p_info *p2p; - int num_mmio_pages, num_aper_pages, sg_page_shift, err, num_aper_chunks; - struct scif_hw_dev *psdev = peerdev->sdev; - struct scif_hw_dev *sdev = scifdev->sdev; - - num_mmio_pages = psdev->mmio->len >> PAGE_SHIFT; - num_aper_pages = psdev->aper->len >> PAGE_SHIFT; - - p2p = kzalloc(sizeof(*p2p), GFP_KERNEL); - if (!p2p) - return NULL; - p2p->ppi_sg[SCIF_PPI_MMIO] = scif_p2p_setsg(psdev->mmio->pa, - PAGE_SIZE, num_mmio_pages); - if (!p2p->ppi_sg[SCIF_PPI_MMIO]) - goto free_p2p; - p2p->sg_nentries[SCIF_PPI_MMIO] = num_mmio_pages; - sg_page_shift = get_order(min(psdev->aper->len, (u64)(1 << 30))); - num_aper_chunks = num_aper_pages >> (sg_page_shift - PAGE_SHIFT); - p2p->ppi_sg[SCIF_PPI_APER] = scif_p2p_setsg(psdev->aper->pa, - 1 << sg_page_shift, - num_aper_chunks); - p2p->sg_nentries[SCIF_PPI_APER] = num_aper_chunks; - err = dma_map_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO], - num_mmio_pages, PCI_DMA_BIDIRECTIONAL); - if (err != num_mmio_pages) - goto scif_p2p_free; - err = dma_map_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_APER], - num_aper_chunks, PCI_DMA_BIDIRECTIONAL); - if (err != num_aper_chunks) - goto dma_unmap; - p2p->ppi_da[SCIF_PPI_MMIO] = sg_dma_address(p2p->ppi_sg[SCIF_PPI_MMIO]); - p2p->ppi_da[SCIF_PPI_APER] = sg_dma_address(p2p->ppi_sg[SCIF_PPI_APER]); - p2p->ppi_len[SCIF_PPI_MMIO] = num_mmio_pages; - p2p->ppi_len[SCIF_PPI_APER] = num_aper_pages; - p2p->ppi_peer_id = peerdev->node; - return p2p; -dma_unmap: - dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO], - p2p->sg_nentries[SCIF_PPI_MMIO], DMA_BIDIRECTIONAL); -scif_p2p_free: - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]); -free_p2p: - kfree(p2p); - return NULL; -} - -/* Uninitialize and release resources from a p2p mapping */ -static void scif_deinit_p2p_info(struct scif_dev *scifdev, - struct scif_p2p_info *p2p) -{ - struct scif_hw_dev *sdev = scifdev->sdev; - - dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO], - p2p->sg_nentries[SCIF_PPI_MMIO], DMA_BIDIRECTIONAL); - dma_unmap_sg(&sdev->dev, p2p->ppi_sg[SCIF_PPI_APER], - p2p->sg_nentries[SCIF_PPI_APER], DMA_BIDIRECTIONAL); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]); - kfree(p2p); -} - -/** - * scif_node_connect: Respond to SCIF_NODE_CONNECT interrupt message - * @scifdev: SCIF device - * @dst: Destination node - * - * Connect the src and dst node by setting up the p2p connection - * between them. Management node here acts like a proxy. - */ -static void scif_node_connect(struct scif_dev *scifdev, int dst) -{ - struct scif_dev *dev_j = scifdev; - struct scif_dev *dev_i = NULL; - struct scif_p2p_info *p2p_ij = NULL; /* bus addr for j from i */ - struct scif_p2p_info *p2p_ji = NULL; /* bus addr for i from j */ - struct scif_p2p_info *p2p; - struct list_head *pos, *tmp; - struct scifmsg msg; - int err; - u64 tmppayload; - - if (dst < 1 || dst > scif_info.maxid) - return; - - dev_i = &scif_dev[dst]; - - if (!_scifdev_alive(dev_i)) - return; - /* - * If the p2p connection is already setup or in the process of setting - * up then just ignore this request. The requested node will get - * informed by SCIF_NODE_ADD_ACK or SCIF_NODE_ADD_NACK - */ - if (!list_empty(&dev_i->p2p)) { - list_for_each_safe(pos, tmp, &dev_i->p2p) { - p2p = list_entry(pos, struct scif_p2p_info, ppi_list); - if (p2p->ppi_peer_id == dev_j->node) - return; - } - } - p2p_ij = scif_init_p2p_info(dev_i, dev_j); - if (!p2p_ij) - return; - p2p_ji = scif_init_p2p_info(dev_j, dev_i); - if (!p2p_ji) { - scif_deinit_p2p_info(dev_i, p2p_ij); - return; - } - list_add_tail(&p2p_ij->ppi_list, &dev_i->p2p); - list_add_tail(&p2p_ji->ppi_list, &dev_j->p2p); - - /* - * Send a SCIF_NODE_ADD to dev_i, pass it its bus address - * as seen from dev_j - */ - msg.uop = SCIF_NODE_ADD; - msg.src.node = dev_j->node; - msg.dst.node = dev_i->node; - - msg.payload[0] = p2p_ji->ppi_da[SCIF_PPI_APER]; - msg.payload[1] = p2p_ij->ppi_da[SCIF_PPI_MMIO]; - msg.payload[2] = p2p_ij->ppi_da[SCIF_PPI_APER]; - msg.payload[3] = p2p_ij->ppi_len[SCIF_PPI_APER] << PAGE_SHIFT; - - err = scif_nodeqp_send(dev_i, &msg); - if (err) { - dev_err(&scifdev->sdev->dev, - "%s %d error %d\n", __func__, __LINE__, err); - return; - } - - /* Same as above but to dev_j */ - msg.uop = SCIF_NODE_ADD; - msg.src.node = dev_i->node; - msg.dst.node = dev_j->node; - - tmppayload = msg.payload[0]; - msg.payload[0] = msg.payload[2]; - msg.payload[2] = tmppayload; - msg.payload[1] = p2p_ji->ppi_da[SCIF_PPI_MMIO]; - msg.payload[3] = p2p_ji->ppi_len[SCIF_PPI_APER] << PAGE_SHIFT; - - scif_nodeqp_send(dev_j, &msg); -} - -static void scif_p2p_setup(void) -{ - int i, j; - - if (!scif_info.p2p_enable) - return; - - for (i = 1; i <= scif_info.maxid; i++) - if (!_scifdev_alive(&scif_dev[i])) - return; - - for (i = 1; i <= scif_info.maxid; i++) { - for (j = 1; j <= scif_info.maxid; j++) { - struct scif_dev *scifdev = &scif_dev[i]; - - if (i == j) - continue; - scif_node_connect(scifdev, j); - } - } -} - -static char *message_types[] = {"BAD", - "INIT", - "EXIT", - "SCIF_EXIT_ACK", - "SCIF_NODE_ADD", - "SCIF_NODE_ADD_ACK", - "SCIF_NODE_ADD_NACK", - "REMOVE_NODE", - "REMOVE_NODE_ACK", - "CNCT_REQ", - "CNCT_GNT", - "CNCT_GNTACK", - "CNCT_GNTNACK", - "CNCT_REJ", - "DISCNCT", - "DISCNT_ACK", - "CLIENT_SENT", - "CLIENT_RCVD", - "SCIF_GET_NODE_INFO", - "REGISTER", - "REGISTER_ACK", - "REGISTER_NACK", - "UNREGISTER", - "UNREGISTER_ACK", - "UNREGISTER_NACK", - "ALLOC_REQ", - "ALLOC_GNT", - "ALLOC_REJ", - "FREE_PHYS", - "FREE_VIRT", - "MUNMAP", - "MARK", - "MARK_ACK", - "MARK_NACK", - "WAIT", - "WAIT_ACK", - "WAIT_NACK", - "SIGNAL_LOCAL", - "SIGNAL_REMOTE", - "SIG_ACK", - "SIG_NACK"}; - -static void -scif_display_message(struct scif_dev *scifdev, struct scifmsg *msg, - const char *label) -{ - if (!scif_info.en_msg_log) - return; - if (msg->uop > SCIF_MAX_MSG) { - dev_err(&scifdev->sdev->dev, - "%s: unknown msg type %d\n", label, msg->uop); - return; - } - dev_info(&scifdev->sdev->dev, - "%s: msg type %s, src %d:%d, dest %d:%d payload 0x%llx:0x%llx:0x%llx:0x%llx\n", - label, message_types[msg->uop], msg->src.node, msg->src.port, - msg->dst.node, msg->dst.port, msg->payload[0], msg->payload[1], - msg->payload[2], msg->payload[3]); -} - -int _scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_qp *qp = scifdev->qpairs; - int err = -ENOMEM, loop_cnt = 0; - - scif_display_message(scifdev, msg, "Sent"); - if (!qp) { - err = -EINVAL; - goto error; - } - spin_lock(&qp->send_lock); - - while ((err = scif_rb_write(&qp->outbound_q, - msg, sizeof(struct scifmsg)))) { - mdelay(1); -#define SCIF_NODEQP_SEND_TO_MSEC (3 * 1000) - if (loop_cnt++ > (SCIF_NODEQP_SEND_TO_MSEC)) { - err = -ENODEV; - break; - } - } - if (!err) - scif_rb_commit(&qp->outbound_q); - spin_unlock(&qp->send_lock); - if (!err) { - if (scifdev_self(scifdev)) - /* - * For loopback we need to emulate an interrupt by - * queuing work for the queue handling real node - * Qp interrupts. - */ - queue_work(scifdev->intr_wq, &scifdev->intr_bh); - else - scif_send_msg_intr(scifdev); - } -error: - if (err) - dev_dbg(&scifdev->sdev->dev, - "%s %d error %d uop %d\n", - __func__, __LINE__, err, msg->uop); - return err; -} - -/** - * scif_nodeqp_send - Send a message on the node queue pair - * @scifdev: Scif Device. - * @msg: The message to be sent. - */ -int scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg) -{ - int err; - struct device *spdev = NULL; - - if (msg->uop > SCIF_EXIT_ACK) { - /* Don't send messages once the exit flow has begun */ - if (OP_IDLE != scifdev->exit) - return -ENODEV; - spdev = scif_get_peer_dev(scifdev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - return err; - } - } - err = _scif_nodeqp_send(scifdev, msg); - if (msg->uop > SCIF_EXIT_ACK) - scif_put_peer_dev(spdev); - return err; -} - -/* - * scif_misc_handler: - * - * Work queue handler for servicing miscellaneous SCIF tasks. - * Examples include: - * 1) Remote fence requests. - * 2) Destruction of temporary registered windows - * created during scif_vreadfrom()/scif_vwriteto(). - * 3) Cleanup of zombie endpoints. - */ -void scif_misc_handler(struct work_struct *work) -{ - scif_rma_handle_remote_fences(); - scif_rma_destroy_windows(); - scif_rma_destroy_tcw_invalid(); - scif_cleanup_zombie_epd(); -} - -/** - * scif_init() - Respond to SCIF_INIT interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - */ -static __always_inline void -scif_init(struct scif_dev *scifdev, struct scifmsg *msg) -{ - /* - * Allow the thread waiting for device page updates for the peer QP DMA - * address to complete initializing the inbound_q. - */ - flush_delayed_work(&scifdev->qp_dwork); - - scif_peer_register_device(scifdev); - - if (scif_is_mgmt_node()) { - mutex_lock(&scif_info.conflock); - scif_p2p_setup(); - mutex_unlock(&scif_info.conflock); - } -} - -/** - * scif_exit() - Respond to SCIF_EXIT interrupt message - * @scifdev: Remote SCIF device node - * @unused: Interrupt message (unused) - * - * This function stops the SCIF interface for the node which sent - * the SCIF_EXIT message and starts waiting for that node to - * resetup the queue pair again. - */ -static __always_inline void -scif_exit(struct scif_dev *scifdev, struct scifmsg *unused) -{ - scifdev->exit_ack_pending = true; - if (scif_is_mgmt_node()) - scif_disconnect_node(scifdev->node, false); - else - scif_stop(scifdev); - schedule_delayed_work(&scifdev->qp_dwork, - msecs_to_jiffies(1000)); -} - -/** - * scif_exitack() - Respond to SCIF_EXIT_ACK interrupt message - * @scifdev: Remote SCIF device node - * @unused: Interrupt message (unused) - * - */ -static __always_inline void -scif_exit_ack(struct scif_dev *scifdev, struct scifmsg *unused) -{ - scifdev->exit = OP_COMPLETED; - wake_up(&scif_info.exitwq); -} - -/** - * scif_node_add() - Respond to SCIF_NODE_ADD interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * When the mgmt node driver has finished initializing a MIC node queue pair it - * marks the node as online. It then looks for all currently online MIC cards - * and send a SCIF_NODE_ADD message to identify the ID of the new card for - * peer to peer initialization - * - * The local node allocates its incoming queue and sends its address in the - * SCIF_NODE_ADD_ACK message back to the mgmt node, the mgmt node "reflects" - * this message to the new node - */ -static __always_inline void -scif_node_add(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_dev *newdev; - dma_addr_t qp_offset; - int qp_connect; - struct scif_hw_dev *sdev; - - dev_dbg(&scifdev->sdev->dev, - "Scifdev %d:%d received NODE_ADD msg for node %d\n", - scifdev->node, msg->dst.node, msg->src.node); - dev_dbg(&scifdev->sdev->dev, - "Remote address for this node's aperture %llx\n", - msg->payload[0]); - newdev = &scif_dev[msg->src.node]; - newdev->node = msg->src.node; - newdev->sdev = scif_dev[SCIF_MGMT_NODE].sdev; - sdev = newdev->sdev; - - if (scif_setup_intr_wq(newdev)) { - dev_err(&scifdev->sdev->dev, - "failed to setup interrupts for %d\n", msg->src.node); - goto interrupt_setup_error; - } - newdev->mmio.va = ioremap(msg->payload[1], sdev->mmio->len); - if (!newdev->mmio.va) { - dev_err(&scifdev->sdev->dev, - "failed to map mmio for %d\n", msg->src.node); - goto mmio_map_error; - } - newdev->qpairs = kzalloc(sizeof(*newdev->qpairs), GFP_KERNEL); - if (!newdev->qpairs) - goto qp_alloc_error; - /* - * Set the base address of the remote node's memory since it gets - * added to qp_offset - */ - newdev->base_addr = msg->payload[0]; - - qp_connect = scif_setup_qp_connect(newdev->qpairs, &qp_offset, - SCIF_NODE_QP_SIZE, newdev); - if (qp_connect) { - dev_err(&scifdev->sdev->dev, - "failed to setup qp_connect %d\n", qp_connect); - goto qp_connect_error; - } - - newdev->db = sdev->hw_ops->next_db(sdev); - newdev->cookie = sdev->hw_ops->request_irq(sdev, scif_intr_handler, - "SCIF_INTR", newdev, - newdev->db); - if (IS_ERR(newdev->cookie)) - goto qp_connect_error; - newdev->qpairs->magic = SCIFEP_MAGIC; - newdev->qpairs->qp_state = SCIF_QP_OFFLINE; - - msg->uop = SCIF_NODE_ADD_ACK; - msg->dst.node = msg->src.node; - msg->src.node = scif_info.nodeid; - msg->payload[0] = qp_offset; - msg->payload[2] = newdev->db; - scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], msg); - return; -qp_connect_error: - kfree(newdev->qpairs); - newdev->qpairs = NULL; -qp_alloc_error: - iounmap(newdev->mmio.va); - newdev->mmio.va = NULL; -mmio_map_error: -interrupt_setup_error: - dev_err(&scifdev->sdev->dev, - "node add failed for node %d\n", msg->src.node); - msg->uop = SCIF_NODE_ADD_NACK; - msg->dst.node = msg->src.node; - msg->src.node = scif_info.nodeid; - scif_nodeqp_send(&scif_dev[SCIF_MGMT_NODE], msg); -} - -void scif_poll_qp_state(struct work_struct *work) -{ -#define SCIF_NODE_QP_RETRY 100 -#define SCIF_NODE_QP_TIMEOUT 100 - struct scif_dev *peerdev = container_of(work, struct scif_dev, - p2p_dwork.work); - struct scif_qp *qp = &peerdev->qpairs[0]; - - if (qp->qp_state != SCIF_QP_ONLINE || - qp->remote_qp->qp_state != SCIF_QP_ONLINE) { - if (peerdev->p2p_retry++ == SCIF_NODE_QP_RETRY) { - dev_err(&peerdev->sdev->dev, - "Warning: QP check timeout with state %d\n", - qp->qp_state); - goto timeout; - } - schedule_delayed_work(&peerdev->p2p_dwork, - msecs_to_jiffies(SCIF_NODE_QP_TIMEOUT)); - return; - } - return; -timeout: - dev_err(&peerdev->sdev->dev, - "%s %d remote node %d offline, state = 0x%x\n", - __func__, __LINE__, peerdev->node, qp->qp_state); - qp->remote_qp->qp_state = SCIF_QP_OFFLINE; - scif_peer_unregister_device(peerdev); - scif_cleanup_scifdev(peerdev); -} - -/** - * scif_node_add_ack() - Respond to SCIF_NODE_ADD_ACK interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * After a MIC node receives the SCIF_NODE_ADD_ACK message it send this - * message to the mgmt node to confirm the sequence is finished. - * - */ -static __always_inline void -scif_node_add_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_dev *peerdev; - struct scif_qp *qp; - struct scif_dev *dst_dev = &scif_dev[msg->dst.node]; - - dev_dbg(&scifdev->sdev->dev, - "Scifdev %d received SCIF_NODE_ADD_ACK msg src %d dst %d\n", - scifdev->node, msg->src.node, msg->dst.node); - dev_dbg(&scifdev->sdev->dev, - "payload %llx %llx %llx %llx\n", msg->payload[0], - msg->payload[1], msg->payload[2], msg->payload[3]); - if (scif_is_mgmt_node()) { - /* - * the lock serializes with scif_qp_response_ack. The mgmt node - * is forwarding the NODE_ADD_ACK message from src to dst we - * need to make sure that the dst has already received a - * NODE_ADD for src and setup its end of the qp to dst - */ - mutex_lock(&scif_info.conflock); - msg->payload[1] = scif_info.maxid; - scif_nodeqp_send(dst_dev, msg); - mutex_unlock(&scif_info.conflock); - return; - } - peerdev = &scif_dev[msg->src.node]; - peerdev->sdev = scif_dev[SCIF_MGMT_NODE].sdev; - peerdev->node = msg->src.node; - - qp = &peerdev->qpairs[0]; - - if ((scif_setup_qp_connect_response(peerdev, &peerdev->qpairs[0], - msg->payload[0]))) - goto local_error; - peerdev->rdb = msg->payload[2]; - qp->remote_qp->qp_state = SCIF_QP_ONLINE; - - scif_peer_register_device(peerdev); - - schedule_delayed_work(&peerdev->p2p_dwork, 0); - return; -local_error: - scif_cleanup_scifdev(peerdev); -} - -/** - * scif_node_add_nack: Respond to SCIF_NODE_ADD_NACK interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * SCIF_NODE_ADD failed, so inform the waiting wq. - */ -static __always_inline void -scif_node_add_nack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - if (scif_is_mgmt_node()) { - struct scif_dev *dst_dev = &scif_dev[msg->dst.node]; - - dev_dbg(&scifdev->sdev->dev, - "SCIF_NODE_ADD_NACK received from %d\n", scifdev->node); - scif_nodeqp_send(dst_dev, msg); - } -} - -/** - * scif_node_remove: Handle SCIF_NODE_REMOVE message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * Handle node removal. - */ -static __always_inline void -scif_node_remove(struct scif_dev *scifdev, struct scifmsg *msg) -{ - int node = msg->payload[0]; - struct scif_dev *scdev = &scif_dev[node]; - - scdev->node_remove_ack_pending = true; - scif_handle_remove_node(node); -} - -/** - * scif_node_remove_ack: Handle SCIF_NODE_REMOVE_ACK message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * The peer has acked a SCIF_NODE_REMOVE message. - */ -static __always_inline void -scif_node_remove_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_dev *sdev = &scif_dev[msg->payload[0]]; - - atomic_inc(&sdev->disconn_rescnt); - wake_up(&sdev->disconn_wq); -} - -/** - * scif_get_node_info: Respond to SCIF_GET_NODE_INFO interrupt message - * @scifdev: Remote SCIF device node - * @msg: Interrupt message - * - * Retrieve node info i.e maxid and total from the mgmt node. - */ -static __always_inline void -scif_get_node_info_resp(struct scif_dev *scifdev, struct scifmsg *msg) -{ - if (scif_is_mgmt_node()) { - swap(msg->dst.node, msg->src.node); - mutex_lock(&scif_info.conflock); - msg->payload[1] = scif_info.maxid; - msg->payload[2] = scif_info.total; - mutex_unlock(&scif_info.conflock); - scif_nodeqp_send(scifdev, msg); - } else { - struct completion *node_info = - (struct completion *)msg->payload[3]; - - mutex_lock(&scif_info.conflock); - scif_info.maxid = msg->payload[1]; - scif_info.total = msg->payload[2]; - complete_all(node_info); - mutex_unlock(&scif_info.conflock); - } -} - -static void -scif_msg_unknown(struct scif_dev *scifdev, struct scifmsg *msg) -{ - /* Bogus Node Qp Message? */ - dev_err(&scifdev->sdev->dev, - "Unknown message 0x%xn scifdev->node 0x%x\n", - msg->uop, scifdev->node); -} - -static void (*scif_intr_func[SCIF_MAX_MSG + 1]) - (struct scif_dev *, struct scifmsg *msg) = { - scif_msg_unknown, /* Error */ - scif_init, /* SCIF_INIT */ - scif_exit, /* SCIF_EXIT */ - scif_exit_ack, /* SCIF_EXIT_ACK */ - scif_node_add, /* SCIF_NODE_ADD */ - scif_node_add_ack, /* SCIF_NODE_ADD_ACK */ - scif_node_add_nack, /* SCIF_NODE_ADD_NACK */ - scif_node_remove, /* SCIF_NODE_REMOVE */ - scif_node_remove_ack, /* SCIF_NODE_REMOVE_ACK */ - scif_cnctreq, /* SCIF_CNCT_REQ */ - scif_cnctgnt, /* SCIF_CNCT_GNT */ - scif_cnctgnt_ack, /* SCIF_CNCT_GNTACK */ - scif_cnctgnt_nack, /* SCIF_CNCT_GNTNACK */ - scif_cnctrej, /* SCIF_CNCT_REJ */ - scif_discnct, /* SCIF_DISCNCT */ - scif_discnt_ack, /* SCIF_DISCNT_ACK */ - scif_clientsend, /* SCIF_CLIENT_SENT */ - scif_clientrcvd, /* SCIF_CLIENT_RCVD */ - scif_get_node_info_resp,/* SCIF_GET_NODE_INFO */ - scif_recv_reg, /* SCIF_REGISTER */ - scif_recv_reg_ack, /* SCIF_REGISTER_ACK */ - scif_recv_reg_nack, /* SCIF_REGISTER_NACK */ - scif_recv_unreg, /* SCIF_UNREGISTER */ - scif_recv_unreg_ack, /* SCIF_UNREGISTER_ACK */ - scif_recv_unreg_nack, /* SCIF_UNREGISTER_NACK */ - scif_alloc_req, /* SCIF_ALLOC_REQ */ - scif_alloc_gnt_rej, /* SCIF_ALLOC_GNT */ - scif_alloc_gnt_rej, /* SCIF_ALLOC_REJ */ - scif_free_virt, /* SCIF_FREE_VIRT */ - scif_recv_munmap, /* SCIF_MUNMAP */ - scif_recv_mark, /* SCIF_MARK */ - scif_recv_mark_resp, /* SCIF_MARK_ACK */ - scif_recv_mark_resp, /* SCIF_MARK_NACK */ - scif_recv_wait, /* SCIF_WAIT */ - scif_recv_wait_resp, /* SCIF_WAIT_ACK */ - scif_recv_wait_resp, /* SCIF_WAIT_NACK */ - scif_recv_sig_local, /* SCIF_SIG_LOCAL */ - scif_recv_sig_remote, /* SCIF_SIG_REMOTE */ - scif_recv_sig_resp, /* SCIF_SIG_ACK */ - scif_recv_sig_resp, /* SCIF_SIG_NACK */ -}; - -static int scif_max_msg_id = SCIF_MAX_MSG; -/** - * scif_nodeqp_msg_handler() - Common handler for node messages - * @scifdev: Remote device to respond to - * @qp: Remote memory pointer - * @msg: The message to be handled. - * - * This routine calls the appropriate routine to handle a Node Qp - * message receipt - */ -static void -scif_nodeqp_msg_handler(struct scif_dev *scifdev, - struct scif_qp *qp, struct scifmsg *msg) -{ - scif_display_message(scifdev, msg, "Rcvd"); - - if (msg->uop > (u32)scif_max_msg_id) { - /* Bogus Node Qp Message? */ - dev_err(&scifdev->sdev->dev, - "Unknown message 0x%xn scifdev->node 0x%x\n", - msg->uop, scifdev->node); - return; - } - - scif_intr_func[msg->uop](scifdev, msg); -} - -/** - * scif_nodeqp_intrhandler() - Interrupt handler for node messages - * @scifdev: Remote device to respond to - * @qp: Remote memory pointer - * - * This routine is triggered by the interrupt mechanism. It reads - * messages from the node queue RB and calls the Node QP Message handling - * routine. - */ -void scif_nodeqp_intrhandler(struct scif_dev *scifdev, struct scif_qp *qp) -{ - struct scifmsg msg; - int read_size; - - do { - read_size = scif_rb_get_next(&qp->inbound_q, &msg, sizeof(msg)); - if (!read_size) - break; - scif_nodeqp_msg_handler(scifdev, qp, &msg); - /* - * The node queue pair is unmapped so skip the read pointer - * update after receipt of a SCIF_EXIT_ACK - */ - if (SCIF_EXIT_ACK == msg.uop) - break; - scif_rb_update_read_ptr(&qp->inbound_q); - } while (1); -} - -/** - * scif_loopb_wq_handler - Loopback Workqueue Handler. - * @unused: loop back work (unused) - * - * This work queue routine is invoked by the loopback work queue handler. - * It grabs the recv lock, dequeues any available messages from the head - * of the loopback message list, calls the node QP message handler, - * waits for it to return, then frees up this message and dequeues more - * elements of the list if available. - */ -static void scif_loopb_wq_handler(struct work_struct *unused) -{ - struct scif_dev *scifdev = scif_info.loopb_dev; - struct scif_qp *qp = scifdev->qpairs; - struct scif_loopb_msg *msg; - - do { - msg = NULL; - spin_lock(&qp->recv_lock); - if (!list_empty(&scif_info.loopb_recv_q)) { - msg = list_first_entry(&scif_info.loopb_recv_q, - struct scif_loopb_msg, - list); - list_del(&msg->list); - } - spin_unlock(&qp->recv_lock); - - if (msg) { - scif_nodeqp_msg_handler(scifdev, qp, &msg->msg); - kfree(msg); - } - } while (msg); -} - -/** - * scif_loopb_msg_handler() - Workqueue handler for loopback messages. - * @scifdev: SCIF device - * @qp: Queue pair. - * - * This work queue routine is triggered when a loopback message is received. - * - * We need special handling for receiving Node Qp messages on a loopback SCIF - * device via two workqueues for receiving messages. - * - * The reason we need the extra workqueue which is not required with *normal* - * non-loopback SCIF devices is the potential classic deadlock described below: - * - * Thread A tries to send a message on a loopback SCIF device and blocks since - * there is no space in the RB while it has the send_lock held or another - * lock called lock X for example. - * - * Thread B: The Loopback Node QP message receive workqueue receives the message - * and tries to send a message (eg an ACK) to the loopback SCIF device. It tries - * to grab the send lock again or lock X and deadlocks with Thread A. The RB - * cannot be drained any further due to this classic deadlock. - * - * In order to avoid deadlocks as mentioned above we have an extra level of - * indirection achieved by having two workqueues. - * 1) The first workqueue whose handler is scif_loopb_msg_handler reads - * messages from the Node QP RB, adds them to a list and queues work for the - * second workqueue. - * - * 2) The second workqueue whose handler is scif_loopb_wq_handler dequeues - * messages from the list, handles them, frees up the memory and dequeues - * more elements from the list if possible. - */ -int -scif_loopb_msg_handler(struct scif_dev *scifdev, struct scif_qp *qp) -{ - int read_size; - struct scif_loopb_msg *msg; - - do { - msg = kmalloc(sizeof(*msg), GFP_KERNEL); - if (!msg) - return -ENOMEM; - read_size = scif_rb_get_next(&qp->inbound_q, &msg->msg, - sizeof(struct scifmsg)); - if (read_size != sizeof(struct scifmsg)) { - kfree(msg); - scif_rb_update_read_ptr(&qp->inbound_q); - break; - } - spin_lock(&qp->recv_lock); - list_add_tail(&msg->list, &scif_info.loopb_recv_q); - spin_unlock(&qp->recv_lock); - queue_work(scif_info.loopb_wq, &scif_info.loopb_work); - scif_rb_update_read_ptr(&qp->inbound_q); - } while (read_size == sizeof(struct scifmsg)); - return read_size; -} - -/** - * scif_setup_loopback_qp - One time setup work for Loopback Node Qp. - * @scifdev: SCIF device - * - * Sets up the required loopback workqueues, queue pairs and ring buffers - */ -int scif_setup_loopback_qp(struct scif_dev *scifdev) -{ - int err = 0; - void *local_q; - struct scif_qp *qp; - - err = scif_setup_intr_wq(scifdev); - if (err) - goto exit; - INIT_LIST_HEAD(&scif_info.loopb_recv_q); - snprintf(scif_info.loopb_wqname, sizeof(scif_info.loopb_wqname), - "SCIF LOOPB %d", scifdev->node); - scif_info.loopb_wq = - alloc_ordered_workqueue(scif_info.loopb_wqname, 0); - if (!scif_info.loopb_wq) { - err = -ENOMEM; - goto destroy_intr; - } - INIT_WORK(&scif_info.loopb_work, scif_loopb_wq_handler); - /* Allocate Self Qpair */ - scifdev->qpairs = kzalloc(sizeof(*scifdev->qpairs), GFP_KERNEL); - if (!scifdev->qpairs) { - err = -ENOMEM; - goto destroy_loopb_wq; - } - - qp = scifdev->qpairs; - qp->magic = SCIFEP_MAGIC; - spin_lock_init(&qp->send_lock); - spin_lock_init(&qp->recv_lock); - - local_q = kzalloc(SCIF_NODE_QP_SIZE, GFP_KERNEL); - if (!local_q) { - err = -ENOMEM; - goto free_qpairs; - } - /* - * For loopback the inbound_q and outbound_q are essentially the same - * since the Node sends a message on the loopback interface to the - * outbound_q which is then received on the inbound_q. - */ - scif_rb_init(&qp->outbound_q, - &qp->local_read, - &qp->local_write, - local_q, get_count_order(SCIF_NODE_QP_SIZE)); - - scif_rb_init(&qp->inbound_q, - &qp->local_read, - &qp->local_write, - local_q, get_count_order(SCIF_NODE_QP_SIZE)); - scif_info.nodeid = scifdev->node; - - scif_peer_register_device(scifdev); - - scif_info.loopb_dev = scifdev; - return err; -free_qpairs: - kfree(scifdev->qpairs); -destroy_loopb_wq: - destroy_workqueue(scif_info.loopb_wq); -destroy_intr: - scif_destroy_intr_wq(scifdev); -exit: - return err; -} - -/** - * scif_destroy_loopback_qp - One time uninit work for Loopback Node Qp - * @scifdev: SCIF device - * - * Destroys the workqueues and frees up the Ring Buffer and Queue Pair memory. - */ -int scif_destroy_loopback_qp(struct scif_dev *scifdev) -{ - scif_peer_unregister_device(scifdev); - destroy_workqueue(scif_info.loopb_wq); - scif_destroy_intr_wq(scifdev); - kfree(scifdev->qpairs->outbound_q.rb_base); - kfree(scifdev->qpairs); - scifdev->sdev = NULL; - scif_info.loopb_dev = NULL; - return 0; -} - -void scif_destroy_p2p(struct scif_dev *scifdev) -{ - struct scif_dev *peer_dev; - struct scif_p2p_info *p2p; - struct list_head *pos, *tmp; - int bd; - - mutex_lock(&scif_info.conflock); - /* Free P2P mappings in the given node for all its peer nodes */ - list_for_each_safe(pos, tmp, &scifdev->p2p) { - p2p = list_entry(pos, struct scif_p2p_info, ppi_list); - dma_unmap_sg(&scifdev->sdev->dev, p2p->ppi_sg[SCIF_PPI_MMIO], - p2p->sg_nentries[SCIF_PPI_MMIO], - DMA_BIDIRECTIONAL); - dma_unmap_sg(&scifdev->sdev->dev, p2p->ppi_sg[SCIF_PPI_APER], - p2p->sg_nentries[SCIF_PPI_APER], - DMA_BIDIRECTIONAL); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]); - list_del(pos); - kfree(p2p); - } - - /* Free P2P mapping created in the peer nodes for the given node */ - for (bd = SCIF_MGMT_NODE + 1; bd <= scif_info.maxid; bd++) { - peer_dev = &scif_dev[bd]; - list_for_each_safe(pos, tmp, &peer_dev->p2p) { - p2p = list_entry(pos, struct scif_p2p_info, ppi_list); - if (p2p->ppi_peer_id == scifdev->node) { - dma_unmap_sg(&peer_dev->sdev->dev, - p2p->ppi_sg[SCIF_PPI_MMIO], - p2p->sg_nentries[SCIF_PPI_MMIO], - DMA_BIDIRECTIONAL); - dma_unmap_sg(&peer_dev->sdev->dev, - p2p->ppi_sg[SCIF_PPI_APER], - p2p->sg_nentries[SCIF_PPI_APER], - DMA_BIDIRECTIONAL); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_MMIO]); - scif_p2p_freesg(p2p->ppi_sg[SCIF_PPI_APER]); - list_del(pos); - kfree(p2p); - } - } - } - mutex_unlock(&scif_info.conflock); -} diff --git a/drivers/misc/mic/scif/scif_nodeqp.h b/drivers/misc/mic/scif/scif_nodeqp.h deleted file mode 100644 index 95896273138e..000000000000 --- a/drivers/misc/mic/scif/scif_nodeqp.h +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Corporation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Intel SCIF driver. - * - */ -#ifndef SCIF_NODEQP -#define SCIF_NODEQP - -#include "scif_rb.h" -#include "scif_peer_bus.h" - -#define SCIF_INIT 1 /* First message sent to the peer node for discovery */ -#define SCIF_EXIT 2 /* Last message from the peer informing intent to exit */ -#define SCIF_EXIT_ACK 3 /* Response to SCIF_EXIT message */ -#define SCIF_NODE_ADD 4 /* Tell Online nodes a new node exits */ -#define SCIF_NODE_ADD_ACK 5 /* Confirm to mgmt node sequence is finished */ -#define SCIF_NODE_ADD_NACK 6 /* SCIF_NODE_ADD failed */ -#define SCIF_NODE_REMOVE 7 /* Request to deactivate a SCIF node */ -#define SCIF_NODE_REMOVE_ACK 8 /* Response to a SCIF_NODE_REMOVE message */ -#define SCIF_CNCT_REQ 9 /* Phys addr of Request connection to a port */ -#define SCIF_CNCT_GNT 10 /* Phys addr of new Grant connection request */ -#define SCIF_CNCT_GNTACK 11 /* Error type Reject a connection request */ -#define SCIF_CNCT_GNTNACK 12 /* Error type Reject a connection request */ -#define SCIF_CNCT_REJ 13 /* Error type Reject a connection request */ -#define SCIF_DISCNCT 14 /* Notify peer that connection is being terminated */ -#define SCIF_DISCNT_ACK 15 /* Notify peer that connection is being terminated */ -#define SCIF_CLIENT_SENT 16 /* Notify the peer that data has been written */ -#define SCIF_CLIENT_RCVD 17 /* Notify the peer that data has been read */ -#define SCIF_GET_NODE_INFO 18 /* Get current node mask from the mgmt node*/ -#define SCIF_REGISTER 19 /* Tell peer about a new registered window */ -#define SCIF_REGISTER_ACK 20 /* Notify peer about unregistration success */ -#define SCIF_REGISTER_NACK 21 /* Notify peer about registration success */ -#define SCIF_UNREGISTER 22 /* Tell peer about unregistering a window */ -#define SCIF_UNREGISTER_ACK 23 /* Notify peer about registration failure */ -#define SCIF_UNREGISTER_NACK 24 /* Notify peer about unregistration failure */ -#define SCIF_ALLOC_REQ 25 /* Request a mapped buffer */ -#define SCIF_ALLOC_GNT 26 /* Notify peer about allocation success */ -#define SCIF_ALLOC_REJ 27 /* Notify peer about allocation failure */ -#define SCIF_FREE_VIRT 28 /* Free previously allocated virtual memory */ -#define SCIF_MUNMAP 29 /* Acknowledgment for a SCIF_MMAP request */ -#define SCIF_MARK 30 /* SCIF Remote Fence Mark Request */ -#define SCIF_MARK_ACK 31 /* SCIF Remote Fence Mark Success */ -#define SCIF_MARK_NACK 32 /* SCIF Remote Fence Mark Failure */ -#define SCIF_WAIT 33 /* SCIF Remote Fence Wait Request */ -#define SCIF_WAIT_ACK 34 /* SCIF Remote Fence Wait Success */ -#define SCIF_WAIT_NACK 35 /* SCIF Remote Fence Wait Failure */ -#define SCIF_SIG_LOCAL 36 /* SCIF Remote Fence Local Signal Request */ -#define SCIF_SIG_REMOTE 37 /* SCIF Remote Fence Remote Signal Request */ -#define SCIF_SIG_ACK 38 /* SCIF Remote Fence Remote Signal Success */ -#define SCIF_SIG_NACK 39 /* SCIF Remote Fence Remote Signal Failure */ -#define SCIF_MAX_MSG SCIF_SIG_NACK - -/* - * struct scifmsg - Node QP message format - * - * @src: Source information - * @dst: Destination information - * @uop: The message opcode - * @payload: Unique payload format for each message - */ -struct scifmsg { - struct scif_port_id src; - struct scif_port_id dst; - u32 uop; - u64 payload[4]; -} __packed; - -/* - * struct scif_allocmsg - Used with SCIF_ALLOC_REQ to request - * the remote note to allocate memory - * - * phys_addr: Physical address of the buffer - * vaddr: Virtual address of the buffer - * size: Size of the buffer - * state: Current state - * allocwq: wait queue for status - */ -struct scif_allocmsg { - dma_addr_t phys_addr; - unsigned long vaddr; - size_t size; - enum scif_msg_state state; - wait_queue_head_t allocwq; -}; - -/* - * struct scif_qp - Node Queue Pair - * - * Interesting structure -- a little difficult because we can only - * write across the PCIe, so any r/w pointer we need to read is - * local. We only need to read the read pointer on the inbound_q - * and read the write pointer in the outbound_q - * - * @magic: Magic value to ensure the peer sees the QP correctly - * @outbound_q: The outbound ring buffer for sending messages - * @inbound_q: The inbound ring buffer for receiving messages - * @local_write: Local write index - * @local_read: Local read index - * @remote_qp: The remote queue pair - * @local_buf: DMA address of local ring buffer - * @local_qp: DMA address of the local queue pair data structure - * @remote_buf: DMA address of remote ring buffer - * @qp_state: QP state i.e. online or offline used for P2P - * @send_lock: synchronize access to outbound queue - * @recv_lock: Synchronize access to inbound queue - */ -struct scif_qp { - u64 magic; -#define SCIFEP_MAGIC 0x5c1f000000005c1fULL - struct scif_rb outbound_q; - struct scif_rb inbound_q; - - u32 local_write __aligned(64); - u32 local_read __aligned(64); - struct scif_qp *remote_qp; - dma_addr_t local_buf; - dma_addr_t local_qp; - dma_addr_t remote_buf; - u32 qp_state; -#define SCIF_QP_OFFLINE 0xdead -#define SCIF_QP_ONLINE 0xc0de - spinlock_t send_lock; - spinlock_t recv_lock; -}; - -/* - * struct scif_loopb_msg - An element in the loopback Node QP message list. - * - * @msg - The SCIF node QP message - * @list - link in the list of messages - */ -struct scif_loopb_msg { - struct scifmsg msg; - struct list_head list; -}; - -int scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg); -int _scif_nodeqp_send(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_nodeqp_intrhandler(struct scif_dev *scifdev, struct scif_qp *qp); -int scif_loopb_msg_handler(struct scif_dev *scifdev, struct scif_qp *qp); -int scif_setup_qp(struct scif_dev *scifdev); -int scif_qp_response(phys_addr_t phys, struct scif_dev *dev); -int scif_setup_qp_connect(struct scif_qp *qp, dma_addr_t *qp_offset, - int local_size, struct scif_dev *scifdev); -int scif_setup_qp_accept(struct scif_qp *qp, dma_addr_t *qp_offset, - dma_addr_t phys, int local_size, - struct scif_dev *scifdev); -int scif_setup_qp_connect_response(struct scif_dev *scifdev, - struct scif_qp *qp, u64 payload); -int scif_setup_loopback_qp(struct scif_dev *scifdev); -int scif_destroy_loopback_qp(struct scif_dev *scifdev); -void scif_poll_qp_state(struct work_struct *work); -void scif_destroy_p2p(struct scif_dev *scifdev); -void scif_send_exit(struct scif_dev *scifdev); -static inline struct device *scif_get_peer_dev(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev; - struct device *spdev_ret; - - rcu_read_lock(); - spdev = rcu_dereference(scifdev->spdev); - if (spdev) - spdev_ret = get_device(&spdev->dev); - else - spdev_ret = ERR_PTR(-ENODEV); - rcu_read_unlock(); - return spdev_ret; -} - -static inline void scif_put_peer_dev(struct device *dev) -{ - put_device(dev); -} -#endif /* SCIF_NODEQP */ diff --git a/drivers/misc/mic/scif/scif_peer_bus.c b/drivers/misc/mic/scif/scif_peer_bus.c deleted file mode 100644 index 6d608308bb60..000000000000 --- a/drivers/misc/mic/scif/scif_peer_bus.c +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" -#include "../bus/scif_bus.h" -#include "scif_peer_bus.h" - -static inline struct scif_peer_dev * -dev_to_scif_peer(struct device *dev) -{ - return container_of(dev, struct scif_peer_dev, dev); -} - -struct bus_type scif_peer_bus = { - .name = "scif_peer_bus", -}; - -static void scif_peer_release_dev(struct device *d) -{ - struct scif_peer_dev *sdev = dev_to_scif_peer(d); - struct scif_dev *scifdev = &scif_dev[sdev->dnode]; - - scif_cleanup_scifdev(scifdev); - kfree(sdev); -} - -static int scif_peer_initialize_device(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev; - int ret; - - spdev = kzalloc(sizeof(*spdev), GFP_KERNEL); - if (!spdev) { - ret = -ENOMEM; - goto err; - } - - spdev->dev.parent = scifdev->sdev->dev.parent; - spdev->dev.release = scif_peer_release_dev; - spdev->dnode = scifdev->node; - spdev->dev.bus = &scif_peer_bus; - dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode); - - device_initialize(&spdev->dev); - get_device(&spdev->dev); - rcu_assign_pointer(scifdev->spdev, spdev); - - mutex_lock(&scif_info.conflock); - scif_info.total++; - scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid); - mutex_unlock(&scif_info.conflock); - return 0; -err: - dev_err(&scifdev->sdev->dev, - "dnode %d: initialize_device rc %d\n", scifdev->node, ret); - return ret; -} - -static int scif_peer_add_device(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev); - char pool_name[16]; - int ret; - - ret = device_add(&spdev->dev); - put_device(&spdev->dev); - if (ret) { - dev_err(&scifdev->sdev->dev, - "dnode %d: peer device_add failed\n", scifdev->node); - goto put_spdev; - } - - scnprintf(pool_name, sizeof(pool_name), "scif-%d", spdev->dnode); - scifdev->signal_pool = dmam_pool_create(pool_name, &scifdev->sdev->dev, - sizeof(struct scif_status), 1, - 0); - if (!scifdev->signal_pool) { - dev_err(&scifdev->sdev->dev, - "dnode %d: dmam_pool_create failed\n", scifdev->node); - ret = -ENOMEM; - goto del_spdev; - } - dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode); - return 0; -del_spdev: - device_del(&spdev->dev); -put_spdev: - RCU_INIT_POINTER(scifdev->spdev, NULL); - synchronize_rcu(); - put_device(&spdev->dev); - - mutex_lock(&scif_info.conflock); - scif_info.total--; - mutex_unlock(&scif_info.conflock); - return ret; -} - -void scif_add_peer_device(struct work_struct *work) -{ - struct scif_dev *scifdev = container_of(work, struct scif_dev, - peer_add_work); - - scif_peer_add_device(scifdev); -} - -/* - * Peer device registration is split into a device_initialize and a device_add. - * The reason for doing this is as follows: First, peer device registration - * itself cannot be done in the message processing thread and must be delegated - * to another workqueue, otherwise if SCIF client probe, called during peer - * device registration, calls scif_connect(..), it will block the message - * processing thread causing a deadlock. Next, device_initialize is done in the - * "top-half" message processing thread and device_add in the "bottom-half" - * workqueue. If this is not done, SCIF_CNCT_REQ message processing executing - * concurrently with SCIF_INIT message processing is unable to get a reference - * on the peer device, thereby failing the connect request. - */ -void scif_peer_register_device(struct scif_dev *scifdev) -{ - int ret; - - mutex_lock(&scifdev->lock); - ret = scif_peer_initialize_device(scifdev); - if (ret) - goto exit; - schedule_work(&scifdev->peer_add_work); -exit: - mutex_unlock(&scifdev->lock); -} - -int scif_peer_unregister_device(struct scif_dev *scifdev) -{ - struct scif_peer_dev *spdev; - - mutex_lock(&scifdev->lock); - /* Flush work to ensure device register is complete */ - flush_work(&scifdev->peer_add_work); - - /* - * Continue holding scifdev->lock since theoretically unregister_device - * can be called simultaneously from multiple threads - */ - spdev = rcu_dereference(scifdev->spdev); - if (!spdev) { - mutex_unlock(&scifdev->lock); - return -ENODEV; - } - - RCU_INIT_POINTER(scifdev->spdev, NULL); - synchronize_rcu(); - mutex_unlock(&scifdev->lock); - - dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode); - device_unregister(&spdev->dev); - - mutex_lock(&scif_info.conflock); - scif_info.total--; - mutex_unlock(&scif_info.conflock); - return 0; -} - -int scif_peer_bus_init(void) -{ - return bus_register(&scif_peer_bus); -} - -void scif_peer_bus_exit(void) -{ - bus_unregister(&scif_peer_bus); -} diff --git a/drivers/misc/mic/scif/scif_peer_bus.h b/drivers/misc/mic/scif/scif_peer_bus.h deleted file mode 100644 index 2ea4c51c18c1..000000000000 --- a/drivers/misc/mic/scif/scif_peer_bus.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef _SCIF_PEER_BUS_H_ -#define _SCIF_PEER_BUS_H_ - -#include -#include -#include - -struct scif_dev; - -void scif_add_peer_device(struct work_struct *work); -void scif_peer_register_device(struct scif_dev *sdev); -int scif_peer_unregister_device(struct scif_dev *scifdev); -int scif_peer_bus_init(void); -void scif_peer_bus_exit(void); -#endif /* _SCIF_PEER_BUS_H */ diff --git a/drivers/misc/mic/scif/scif_ports.c b/drivers/misc/mic/scif/scif_ports.c deleted file mode 100644 index 4bdb5ef9a139..000000000000 --- a/drivers/misc/mic/scif/scif_ports.c +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include - -#include "scif_main.h" - -#define SCIF_PORT_COUNT 0x10000 /* Ports available */ - -struct idr scif_ports; - -/** - * struct scif_port - SCIF port information - * - * @ref_cnt: Reference count since there can be multiple endpoints - * created via scif_accept(..) simultaneously using a port. - */ -struct scif_port { - int ref_cnt; -}; - -/** - * __scif_get_port - Reserve a specified port # for SCIF and add it - * to the global list. - * @start: lowest port # to be reserved (inclusive). - * @end: highest port # to be reserved (exclusive). - * - * @return : Allocated SCIF port #, or -ENOSPC if port unavailable. - * On memory allocation failure, returns -ENOMEM. - */ -static int __scif_get_port(int start, int end) -{ - int id; - struct scif_port *port = kzalloc(sizeof(*port), GFP_ATOMIC); - - if (!port) - return -ENOMEM; - spin_lock(&scif_info.port_lock); - id = idr_alloc(&scif_ports, port, start, end, GFP_ATOMIC); - if (id >= 0) - port->ref_cnt++; - spin_unlock(&scif_info.port_lock); - return id; -} - -/** - * scif_rsrv_port - Reserve a specified port # for SCIF. - * @port : port # to be reserved. - * - * @return : Allocated SCIF port #, or -ENOSPC if port unavailable. - * On memory allocation failure, returns -ENOMEM. - */ -int scif_rsrv_port(u16 port) -{ - return __scif_get_port(port, port + 1); -} - -/** - * scif_get_new_port - Get and reserve any port # for SCIF in the range - * SCIF_PORT_RSVD + 1 to SCIF_PORT_COUNT - 1. - * - * @return : Allocated SCIF port #, or -ENOSPC if no ports available. - * On memory allocation failure, returns -ENOMEM. - */ -int scif_get_new_port(void) -{ - return __scif_get_port(SCIF_PORT_RSVD + 1, SCIF_PORT_COUNT); -} - -/** - * scif_get_port - Increment the reference count for a SCIF port - * @id : SCIF port - * - * @return : None - */ -void scif_get_port(u16 id) -{ - struct scif_port *port; - - if (!id) - return; - spin_lock(&scif_info.port_lock); - port = idr_find(&scif_ports, id); - if (port) - port->ref_cnt++; - spin_unlock(&scif_info.port_lock); -} - -/** - * scif_put_port - Release a reserved SCIF port - * @id : SCIF port to be released. - * - * @return : None - */ -void scif_put_port(u16 id) -{ - struct scif_port *port; - - if (!id) - return; - spin_lock(&scif_info.port_lock); - port = idr_find(&scif_ports, id); - if (port) { - port->ref_cnt--; - if (!port->ref_cnt) { - idr_remove(&scif_ports, id); - kfree(port); - } - } - spin_unlock(&scif_info.port_lock); -} diff --git a/drivers/misc/mic/scif/scif_rb.c b/drivers/misc/mic/scif/scif_rb.c deleted file mode 100644 index e425882ae06d..000000000000 --- a/drivers/misc/mic/scif/scif_rb.c +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel SCIF driver. - */ -#include -#include -#include -#include - -#include "scif_rb.h" - -#define scif_rb_ring_cnt(head, tail, size) CIRC_CNT(head, tail, size) -#define scif_rb_ring_space(head, tail, size) CIRC_SPACE(head, tail, size) - -/** - * scif_rb_init - Initializes the ring buffer - * @rb: ring buffer - * @read_ptr: A pointer to the read offset - * @write_ptr: A pointer to the write offset - * @rb_base: A pointer to the base of the ring buffer - * @size: The size of the ring buffer in powers of two - */ -void scif_rb_init(struct scif_rb *rb, u32 *read_ptr, u32 *write_ptr, - void *rb_base, u8 size) -{ - rb->rb_base = rb_base; - rb->size = (1 << size); - rb->read_ptr = read_ptr; - rb->write_ptr = write_ptr; - rb->current_read_offset = *read_ptr; - rb->current_write_offset = *write_ptr; -} - -/* Copies a message to the ring buffer -- handles the wrap around case */ -static void memcpy_torb(struct scif_rb *rb, void *header, - void *msg, u32 size) -{ - u32 size1, size2; - - if (header + size >= rb->rb_base + rb->size) { - /* Need to call two copies if it wraps around */ - size1 = (u32)(rb->rb_base + rb->size - header); - size2 = size - size1; - memcpy_toio((void __iomem __force *)header, msg, size1); - memcpy_toio((void __iomem __force *)rb->rb_base, - msg + size1, size2); - } else { - memcpy_toio((void __iomem __force *)header, msg, size); - } -} - -/* Copies a message from the ring buffer -- handles the wrap around case */ -static void memcpy_fromrb(struct scif_rb *rb, void *header, - void *msg, u32 size) -{ - u32 size1, size2; - - if (header + size >= rb->rb_base + rb->size) { - /* Need to call two copies if it wraps around */ - size1 = (u32)(rb->rb_base + rb->size - header); - size2 = size - size1; - memcpy_fromio(msg, (void __iomem __force *)header, size1); - memcpy_fromio(msg + size1, - (void __iomem __force *)rb->rb_base, size2); - } else { - memcpy_fromio(msg, (void __iomem __force *)header, size); - } -} - -/** - * scif_rb_space - Query space available for writing to the RB - * @rb: ring buffer - * - * Return: size available for writing to RB in bytes. - */ -u32 scif_rb_space(struct scif_rb *rb) -{ - rb->current_read_offset = *rb->read_ptr; - /* - * Update from the HW read pointer only once the peer has exposed the - * new empty slot. This barrier is paired with the memory barrier - * scif_rb_update_read_ptr() - */ - mb(); - return scif_rb_ring_space(rb->current_write_offset, - rb->current_read_offset, rb->size); -} - -/** - * scif_rb_write - Write a message to the RB - * @rb: ring buffer - * @msg: buffer to send the message. Must be at least size bytes long - * @size: the size (in bytes) to be copied to the RB - * - * This API does not block if there isn't enough space in the RB. - * Returns: 0 on success or -ENOMEM on failure - */ -int scif_rb_write(struct scif_rb *rb, void *msg, u32 size) -{ - void *header; - - if (scif_rb_space(rb) < size) - return -ENOMEM; - header = rb->rb_base + rb->current_write_offset; - memcpy_torb(rb, header, msg, size); - /* - * Wait until scif_rb_commit(). Update the local ring - * buffer data, not the shared data until commit. - */ - rb->current_write_offset = - (rb->current_write_offset + size) & (rb->size - 1); - return 0; -} - -/** - * scif_rb_commit - To submit the message to let the peer fetch it - * @rb: ring buffer - */ -void scif_rb_commit(struct scif_rb *rb) -{ - /* - * We must ensure ordering between the all the data committed - * previously before we expose the new message to the peer by - * updating the write_ptr. This write barrier is paired with - * the read barrier in scif_rb_count(..) - */ - wmb(); - WRITE_ONCE(*rb->write_ptr, rb->current_write_offset); -#ifdef CONFIG_INTEL_MIC_CARD - /* - * X100 Si bug: For the case where a Core is performing an EXT_WR - * followed by a Doorbell Write, the Core must perform two EXT_WR to the - * same address with the same data before it does the Doorbell Write. - * This way, if ordering is violated for the Interrupt Message, it will - * fall just behind the first Posted associated with the first EXT_WR. - */ - WRITE_ONCE(*rb->write_ptr, rb->current_write_offset); -#endif -} - -/** - * scif_rb_get - To get next message from the ring buffer - * @rb: ring buffer - * @size: Number of bytes to be read - * - * Return: NULL if no bytes to be read from the ring buffer, otherwise the - * pointer to the next byte - */ -static void *scif_rb_get(struct scif_rb *rb, u32 size) -{ - void *header = NULL; - - if (scif_rb_count(rb, size) >= size) - header = rb->rb_base + rb->current_read_offset; - return header; -} - -/* - * scif_rb_get_next - Read from ring buffer. - * @rb: ring buffer - * @msg: buffer to hold the message. Must be at least size bytes long - * @size: Number of bytes to be read - * - * Return: number of bytes read if available bytes are >= size, otherwise - * returns zero. - */ -u32 scif_rb_get_next(struct scif_rb *rb, void *msg, u32 size) -{ - void *header = NULL; - int read_size = 0; - - header = scif_rb_get(rb, size); - if (header) { - u32 next_cmd_offset = - (rb->current_read_offset + size) & (rb->size - 1); - - read_size = size; - rb->current_read_offset = next_cmd_offset; - memcpy_fromrb(rb, header, msg, size); - } - return read_size; -} - -/** - * scif_rb_update_read_ptr - * @rb: ring buffer - */ -void scif_rb_update_read_ptr(struct scif_rb *rb) -{ - u32 new_offset; - - new_offset = rb->current_read_offset; - /* - * We must ensure ordering between the all the data committed or read - * previously before we expose the empty slot to the peer by updating - * the read_ptr. This barrier is paired with the memory barrier in - * scif_rb_space(..) - */ - mb(); - WRITE_ONCE(*rb->read_ptr, new_offset); -#ifdef CONFIG_INTEL_MIC_CARD - /* - * X100 Si Bug: For the case where a Core is performing an EXT_WR - * followed by a Doorbell Write, the Core must perform two EXT_WR to the - * same address with the same data before it does the Doorbell Write. - * This way, if ordering is violated for the Interrupt Message, it will - * fall just behind the first Posted associated with the first EXT_WR. - */ - WRITE_ONCE(*rb->read_ptr, new_offset); -#endif -} - -/** - * scif_rb_count - * @rb: ring buffer - * @size: Number of bytes expected to be read - * - * Return: number of bytes that can be read from the RB - */ -u32 scif_rb_count(struct scif_rb *rb, u32 size) -{ - if (scif_rb_ring_cnt(rb->current_write_offset, - rb->current_read_offset, - rb->size) < size) { - rb->current_write_offset = *rb->write_ptr; - /* - * Update from the HW write pointer if empty only once the peer - * has exposed the new message. This read barrier is paired - * with the write barrier in scif_rb_commit(..) - */ - smp_rmb(); - } - return scif_rb_ring_cnt(rb->current_write_offset, - rb->current_read_offset, - rb->size); -} diff --git a/drivers/misc/mic/scif/scif_rb.h b/drivers/misc/mic/scif/scif_rb.h deleted file mode 100644 index 166dffe3093d..000000000000 --- a/drivers/misc/mic/scif/scif_rb.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Corporation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Intel SCIF driver. - */ -#ifndef SCIF_RB_H -#define SCIF_RB_H -/* - * This file describes a general purpose, byte based ring buffer. Writers to the - * ring buffer need to synchronize using a lock. The same is true for readers, - * although in practice, the ring buffer has a single reader. It is lockless - * between producer and consumer so it can handle being used across the PCIe - * bus. The ring buffer ensures that there are no reads across the PCIe bus for - * performance reasons. Two of these are used to form a single bidirectional - * queue-pair across PCIe. - */ -/* - * struct scif_rb - SCIF Ring Buffer - * - * @rb_base: The base of the memory used for storing RB messages - * @read_ptr: Pointer to the read offset - * @write_ptr: Pointer to the write offset - * @size: Size of the memory in rb_base - * @current_read_offset: Cached read offset for performance - * @current_write_offset: Cached write offset for performance - */ -struct scif_rb { - void *rb_base; - u32 *read_ptr; - u32 *write_ptr; - u32 size; - u32 current_read_offset; - u32 current_write_offset; -}; - -/* methods used by both */ -void scif_rb_init(struct scif_rb *rb, u32 *read_ptr, u32 *write_ptr, - void *rb_base, u8 size); -/* writer only methods */ -/* write a new command, then scif_rb_commit() */ -int scif_rb_write(struct scif_rb *rb, void *msg, u32 size); -/* after write(), then scif_rb_commit() */ -void scif_rb_commit(struct scif_rb *rb); -/* query space available for writing to a RB. */ -u32 scif_rb_space(struct scif_rb *rb); - -/* reader only methods */ -/* read a new message from the ring buffer of size bytes */ -u32 scif_rb_get_next(struct scif_rb *rb, void *msg, u32 size); -/* update the read pointer so that the space can be reused */ -void scif_rb_update_read_ptr(struct scif_rb *rb); -/* count the number of bytes that can be read */ -u32 scif_rb_count(struct scif_rb *rb, u32 size); -#endif diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c deleted file mode 100644 index 18fb9d8b8a4b..000000000000 --- a/drivers/misc/mic/scif/scif_rma.c +++ /dev/null @@ -1,1760 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#include -#include -#include -#include - -#include "scif_main.h" -#include "scif_map.h" - -/* Used to skip ulimit checks for registrations with SCIF_MAP_KERNEL flag */ -#define SCIF_MAP_ULIMIT 0x40 - -bool scif_ulimit_check = 1; - -/** - * scif_rma_ep_init: - * @ep: end point - * - * Initialize RMA per EP data structures. - */ -void scif_rma_ep_init(struct scif_endpt *ep) -{ - struct scif_endpt_rma_info *rma = &ep->rma_info; - - mutex_init(&rma->rma_lock); - init_iova_domain(&rma->iovad, PAGE_SIZE, SCIF_IOVA_START_PFN); - spin_lock_init(&rma->tc_lock); - mutex_init(&rma->mmn_lock); - INIT_LIST_HEAD(&rma->reg_list); - INIT_LIST_HEAD(&rma->remote_reg_list); - atomic_set(&rma->tw_refcount, 0); - atomic_set(&rma->tcw_refcount, 0); - atomic_set(&rma->tcw_total_pages, 0); - atomic_set(&rma->fence_refcount, 0); - - rma->async_list_del = 0; - rma->dma_chan = NULL; - INIT_LIST_HEAD(&rma->mmn_list); - INIT_LIST_HEAD(&rma->vma_list); - init_waitqueue_head(&rma->markwq); -} - -/** - * scif_rma_ep_can_uninit: - * @ep: end point - * - * Returns 1 if an endpoint can be uninitialized and 0 otherwise. - */ -int scif_rma_ep_can_uninit(struct scif_endpt *ep) -{ - int ret = 0; - - mutex_lock(&ep->rma_info.rma_lock); - /* Destroy RMA Info only if both lists are empty */ - if (list_empty(&ep->rma_info.reg_list) && - list_empty(&ep->rma_info.remote_reg_list) && - list_empty(&ep->rma_info.mmn_list) && - !atomic_read(&ep->rma_info.tw_refcount) && - !atomic_read(&ep->rma_info.tcw_refcount) && - !atomic_read(&ep->rma_info.fence_refcount)) - ret = 1; - mutex_unlock(&ep->rma_info.rma_lock); - return ret; -} - -/** - * scif_create_pinned_pages: - * @nr_pages: number of pages in window - * @prot: read/write protection - * - * Allocate and prepare a set of pinned pages. - */ -static struct scif_pinned_pages * -scif_create_pinned_pages(int nr_pages, int prot) -{ - struct scif_pinned_pages *pin; - - might_sleep(); - pin = scif_zalloc(sizeof(*pin)); - if (!pin) - goto error; - - pin->pages = scif_zalloc(nr_pages * sizeof(*pin->pages)); - if (!pin->pages) - goto error_free_pinned_pages; - - pin->prot = prot; - pin->magic = SCIFEP_MAGIC; - return pin; - -error_free_pinned_pages: - scif_free(pin, sizeof(*pin)); -error: - return NULL; -} - -/** - * scif_destroy_pinned_pages: - * @pin: A set of pinned pages. - * - * Deallocate resources for pinned pages. - */ -static int scif_destroy_pinned_pages(struct scif_pinned_pages *pin) -{ - int j; - int writeable = pin->prot & SCIF_PROT_WRITE; - int kernel = SCIF_MAP_KERNEL & pin->map_flags; - - if (kernel) { - for (j = 0; j < pin->nr_pages; j++) { - if (pin->pages[j] && !kernel) { - if (writeable) - set_page_dirty_lock(pin->pages[j]); - put_page(pin->pages[j]); - } - } - } else - unpin_user_pages_dirty_lock(pin->pages, pin->nr_pages, - writeable); - scif_free(pin->pages, - pin->nr_pages * sizeof(*pin->pages)); - scif_free(pin, sizeof(*pin)); - return 0; -} - -/* - * scif_create_window: - * @ep: end point - * @nr_pages: number of pages - * @offset: registration offset - * @temp: true if a temporary window is being created - * - * Allocate and prepare a self registration window. - */ -struct scif_window *scif_create_window(struct scif_endpt *ep, int nr_pages, - s64 offset, bool temp) -{ - struct scif_window *window; - - might_sleep(); - window = scif_zalloc(sizeof(*window)); - if (!window) - goto error; - - window->dma_addr = scif_zalloc(nr_pages * sizeof(*window->dma_addr)); - if (!window->dma_addr) - goto error_free_window; - - window->num_pages = scif_zalloc(nr_pages * sizeof(*window->num_pages)); - if (!window->num_pages) - goto error_free_window; - - window->offset = offset; - window->ep = (u64)ep; - window->magic = SCIFEP_MAGIC; - window->reg_state = OP_IDLE; - init_waitqueue_head(&window->regwq); - window->unreg_state = OP_IDLE; - init_waitqueue_head(&window->unregwq); - INIT_LIST_HEAD(&window->list); - window->type = SCIF_WINDOW_SELF; - window->temp = temp; - return window; - -error_free_window: - scif_free(window->dma_addr, - nr_pages * sizeof(*window->dma_addr)); - scif_free(window, sizeof(*window)); -error: - return NULL; -} - -/** - * scif_destroy_incomplete_window: - * @ep: end point - * @window: registration window - * - * Deallocate resources for self window. - */ -static void scif_destroy_incomplete_window(struct scif_endpt *ep, - struct scif_window *window) -{ - int err; - int nr_pages = window->nr_pages; - struct scif_allocmsg *alloc = &window->alloc_handle; - struct scifmsg msg; - -retry: - /* Wait for a SCIF_ALLOC_GNT/REJ message */ - err = wait_event_timeout(alloc->allocwq, - alloc->state != OP_IN_PROGRESS, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - - mutex_lock(&ep->rma_info.rma_lock); - if (alloc->state == OP_COMPLETED) { - msg.uop = SCIF_FREE_VIRT; - msg.src = ep->port; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = window->alloc_handle.vaddr; - msg.payload[2] = (u64)window; - msg.payload[3] = SCIF_REGISTER; - _scif_nodeqp_send(ep->remote_dev, &msg); - } - mutex_unlock(&ep->rma_info.rma_lock); - - scif_free_window_offset(ep, window, window->offset); - scif_free(window->dma_addr, nr_pages * sizeof(*window->dma_addr)); - scif_free(window->num_pages, nr_pages * sizeof(*window->num_pages)); - scif_free(window, sizeof(*window)); -} - -/** - * scif_unmap_window: - * @remote_dev: SCIF remote device - * @window: registration window - * - * Delete any DMA mappings created for a registered self window - */ -void scif_unmap_window(struct scif_dev *remote_dev, struct scif_window *window) -{ - int j; - - if (scif_is_iommu_enabled() && !scifdev_self(remote_dev)) { - if (window->st) { - dma_unmap_sg(&remote_dev->sdev->dev, - window->st->sgl, window->st->nents, - DMA_BIDIRECTIONAL); - sg_free_table(window->st); - kfree(window->st); - window->st = NULL; - } - } else { - for (j = 0; j < window->nr_contig_chunks; j++) { - if (window->dma_addr[j]) { - scif_unmap_single(window->dma_addr[j], - remote_dev, - window->num_pages[j] << - PAGE_SHIFT); - window->dma_addr[j] = 0x0; - } - } - } -} - -static inline struct mm_struct *__scif_acquire_mm(void) -{ - if (scif_ulimit_check) - return get_task_mm(current); - return NULL; -} - -static inline void __scif_release_mm(struct mm_struct *mm) -{ - if (mm) - mmput(mm); -} - -static inline int -__scif_dec_pinned_vm_lock(struct mm_struct *mm, - int nr_pages) -{ - if (!mm || !nr_pages || !scif_ulimit_check) - return 0; - - atomic64_sub(nr_pages, &mm->pinned_vm); - return 0; -} - -static inline int __scif_check_inc_pinned_vm(struct mm_struct *mm, - int nr_pages) -{ - unsigned long locked, lock_limit; - - if (!mm || !nr_pages || !scif_ulimit_check) - return 0; - - lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - locked = atomic64_add_return(nr_pages, &mm->pinned_vm); - - if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { - atomic64_sub(nr_pages, &mm->pinned_vm); - dev_err(scif_info.mdev.this_device, - "locked(%lu) > lock_limit(%lu)\n", - locked, lock_limit); - return -ENOMEM; - } - return 0; -} - -/** - * scif_destroy_window: - * @ep: end point - * @window: registration window - * - * Deallocate resources for self window. - */ -int scif_destroy_window(struct scif_endpt *ep, struct scif_window *window) -{ - int j; - struct scif_pinned_pages *pinned_pages = window->pinned_pages; - int nr_pages = window->nr_pages; - - might_sleep(); - if (!window->temp && window->mm) { - __scif_dec_pinned_vm_lock(window->mm, window->nr_pages); - __scif_release_mm(window->mm); - window->mm = NULL; - } - - scif_free_window_offset(ep, window, window->offset); - scif_unmap_window(ep->remote_dev, window); - /* - * Decrement references for this set of pinned pages from - * this window. - */ - j = atomic_sub_return(1, &pinned_pages->ref_count); - if (j < 0) - dev_err(scif_info.mdev.this_device, - "%s %d incorrect ref count %d\n", - __func__, __LINE__, j); - /* - * If the ref count for pinned_pages is zero then someone - * has already called scif_unpin_pages() for it and we should - * destroy the page cache. - */ - if (!j) - scif_destroy_pinned_pages(window->pinned_pages); - scif_free(window->dma_addr, nr_pages * sizeof(*window->dma_addr)); - scif_free(window->num_pages, nr_pages * sizeof(*window->num_pages)); - window->magic = 0; - scif_free(window, sizeof(*window)); - return 0; -} - -/** - * scif_create_remote_lookup: - * @remote_dev: SCIF remote device - * @window: remote window - * - * Allocate and prepare lookup entries for the remote - * end to copy over the physical addresses. - * Returns 0 on success and appropriate errno on failure. - */ -static int scif_create_remote_lookup(struct scif_dev *remote_dev, - struct scif_window *window) -{ - int i, j, err = 0; - int nr_pages = window->nr_pages; - bool vmalloc_dma_phys, vmalloc_num_pages; - - might_sleep(); - /* Map window */ - err = scif_map_single(&window->mapped_offset, - window, remote_dev, sizeof(*window)); - if (err) - goto error_window; - - /* Compute the number of lookup entries. 21 == 2MB Shift */ - window->nr_lookup = ALIGN(nr_pages * PAGE_SIZE, - ((2) * 1024 * 1024)) >> 21; - - window->dma_addr_lookup.lookup = - scif_alloc_coherent(&window->dma_addr_lookup.offset, - remote_dev, window->nr_lookup * - sizeof(*window->dma_addr_lookup.lookup), - GFP_KERNEL | __GFP_ZERO); - if (!window->dma_addr_lookup.lookup) { - err = -ENOMEM; - goto error_window; - } - - window->num_pages_lookup.lookup = - scif_alloc_coherent(&window->num_pages_lookup.offset, - remote_dev, window->nr_lookup * - sizeof(*window->num_pages_lookup.lookup), - GFP_KERNEL | __GFP_ZERO); - if (!window->num_pages_lookup.lookup) { - err = -ENOMEM; - goto error_window; - } - - vmalloc_dma_phys = is_vmalloc_addr(&window->dma_addr[0]); - vmalloc_num_pages = is_vmalloc_addr(&window->num_pages[0]); - - /* Now map each of the pages containing physical addresses */ - for (i = 0, j = 0; i < nr_pages; i += SCIF_NR_ADDR_IN_PAGE, j++) { - err = scif_map_page(&window->dma_addr_lookup.lookup[j], - vmalloc_dma_phys ? - vmalloc_to_page(&window->dma_addr[i]) : - virt_to_page(&window->dma_addr[i]), - remote_dev); - if (err) - goto error_window; - err = scif_map_page(&window->num_pages_lookup.lookup[j], - vmalloc_num_pages ? - vmalloc_to_page(&window->num_pages[i]) : - virt_to_page(&window->num_pages[i]), - remote_dev); - if (err) - goto error_window; - } - return 0; -error_window: - return err; -} - -/** - * scif_destroy_remote_lookup: - * @remote_dev: SCIF remote device - * @window: remote window - * - * Destroy lookup entries used for the remote - * end to copy over the physical addresses. - */ -static void scif_destroy_remote_lookup(struct scif_dev *remote_dev, - struct scif_window *window) -{ - int i, j; - - if (window->nr_lookup) { - struct scif_rma_lookup *lup = &window->dma_addr_lookup; - struct scif_rma_lookup *npup = &window->num_pages_lookup; - - for (i = 0, j = 0; i < window->nr_pages; - i += SCIF_NR_ADDR_IN_PAGE, j++) { - if (lup->lookup && lup->lookup[j]) - scif_unmap_single(lup->lookup[j], - remote_dev, - PAGE_SIZE); - if (npup->lookup && npup->lookup[j]) - scif_unmap_single(npup->lookup[j], - remote_dev, - PAGE_SIZE); - } - if (lup->lookup) - scif_free_coherent(lup->lookup, lup->offset, - remote_dev, window->nr_lookup * - sizeof(*lup->lookup)); - if (npup->lookup) - scif_free_coherent(npup->lookup, npup->offset, - remote_dev, window->nr_lookup * - sizeof(*npup->lookup)); - if (window->mapped_offset) - scif_unmap_single(window->mapped_offset, - remote_dev, sizeof(*window)); - window->nr_lookup = 0; - } -} - -/** - * scif_create_remote_window: - * @scifdev: SCIF device - * @nr_pages: number of pages in window - * - * Allocate and prepare a remote registration window. - */ -static struct scif_window * -scif_create_remote_window(struct scif_dev *scifdev, int nr_pages) -{ - struct scif_window *window; - - might_sleep(); - window = scif_zalloc(sizeof(*window)); - if (!window) - goto error_ret; - - window->magic = SCIFEP_MAGIC; - window->nr_pages = nr_pages; - - window->dma_addr = scif_zalloc(nr_pages * sizeof(*window->dma_addr)); - if (!window->dma_addr) - goto error_window; - - window->num_pages = scif_zalloc(nr_pages * - sizeof(*window->num_pages)); - if (!window->num_pages) - goto error_window; - - if (scif_create_remote_lookup(scifdev, window)) - goto error_window; - - window->type = SCIF_WINDOW_PEER; - window->unreg_state = OP_IDLE; - INIT_LIST_HEAD(&window->list); - return window; -error_window: - scif_destroy_remote_window(window); -error_ret: - return NULL; -} - -/** - * scif_destroy_remote_window: - * @window: remote registration window - * - * Deallocate resources for remote window. - */ -void -scif_destroy_remote_window(struct scif_window *window) -{ - scif_free(window->dma_addr, window->nr_pages * - sizeof(*window->dma_addr)); - scif_free(window->num_pages, window->nr_pages * - sizeof(*window->num_pages)); - window->magic = 0; - scif_free(window, sizeof(*window)); -} - -/** - * scif_iommu_map: create DMA mappings if the IOMMU is enabled - * @remote_dev: SCIF remote device - * @window: remote registration window - * - * Map the physical pages using dma_map_sg(..) and then detect the number - * of contiguous DMA mappings allocated - */ -static int scif_iommu_map(struct scif_dev *remote_dev, - struct scif_window *window) -{ - struct scatterlist *sg; - int i, err; - scif_pinned_pages_t pin = window->pinned_pages; - - window->st = kzalloc(sizeof(*window->st), GFP_KERNEL); - if (!window->st) - return -ENOMEM; - - err = sg_alloc_table(window->st, window->nr_pages, GFP_KERNEL); - if (err) - return err; - - for_each_sg(window->st->sgl, sg, window->st->nents, i) - sg_set_page(sg, pin->pages[i], PAGE_SIZE, 0x0); - - err = dma_map_sg(&remote_dev->sdev->dev, window->st->sgl, - window->st->nents, DMA_BIDIRECTIONAL); - if (!err) - return -ENOMEM; - /* Detect contiguous ranges of DMA mappings */ - sg = window->st->sgl; - for (i = 0; sg; i++) { - dma_addr_t last_da; - - window->dma_addr[i] = sg_dma_address(sg); - window->num_pages[i] = sg_dma_len(sg) >> PAGE_SHIFT; - last_da = sg_dma_address(sg) + sg_dma_len(sg); - while ((sg = sg_next(sg)) && sg_dma_address(sg) == last_da) { - window->num_pages[i] += - (sg_dma_len(sg) >> PAGE_SHIFT); - last_da = window->dma_addr[i] + - sg_dma_len(sg); - } - window->nr_contig_chunks++; - } - return 0; -} - -/** - * scif_map_window: - * @remote_dev: SCIF remote device - * @window: self registration window - * - * Map pages of a window into the aperture/PCI. - * Also determine addresses required for DMA. - */ -int -scif_map_window(struct scif_dev *remote_dev, struct scif_window *window) -{ - int i, j, k, err = 0, nr_contig_pages; - scif_pinned_pages_t pin; - phys_addr_t phys_prev, phys_curr; - - might_sleep(); - - pin = window->pinned_pages; - - if (intel_iommu_enabled && !scifdev_self(remote_dev)) - return scif_iommu_map(remote_dev, window); - - for (i = 0, j = 0; i < window->nr_pages; i += nr_contig_pages, j++) { - phys_prev = page_to_phys(pin->pages[i]); - nr_contig_pages = 1; - - /* Detect physically contiguous chunks */ - for (k = i + 1; k < window->nr_pages; k++) { - phys_curr = page_to_phys(pin->pages[k]); - if (phys_curr != (phys_prev + PAGE_SIZE)) - break; - phys_prev = phys_curr; - nr_contig_pages++; - } - window->num_pages[j] = nr_contig_pages; - window->nr_contig_chunks++; - if (scif_is_mgmt_node()) { - /* - * Management node has to deal with SMPT on X100 and - * hence the DMA mapping is required - */ - err = scif_map_single(&window->dma_addr[j], - phys_to_virt(page_to_phys( - pin->pages[i])), - remote_dev, - nr_contig_pages << PAGE_SHIFT); - if (err) - return err; - } else { - window->dma_addr[j] = page_to_phys(pin->pages[i]); - } - } - return err; -} - -/** - * scif_send_scif_unregister: - * @ep: end point - * @window: self registration window - * - * Send a SCIF_UNREGISTER message. - */ -static int scif_send_scif_unregister(struct scif_endpt *ep, - struct scif_window *window) -{ - struct scifmsg msg; - - msg.uop = SCIF_UNREGISTER; - msg.src = ep->port; - msg.payload[0] = window->alloc_handle.vaddr; - msg.payload[1] = (u64)window; - return scif_nodeqp_send(ep->remote_dev, &msg); -} - -/** - * scif_unregister_window: - * @window: self registration window - * - * Send an unregistration request and wait for a response. - */ -int scif_unregister_window(struct scif_window *window) -{ - int err = 0; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - bool send_msg = false; - - might_sleep(); - switch (window->unreg_state) { - case OP_IDLE: - { - window->unreg_state = OP_IN_PROGRESS; - send_msg = true; - } - fallthrough; - case OP_IN_PROGRESS: - { - scif_get_window(window, 1); - mutex_unlock(&ep->rma_info.rma_lock); - if (send_msg) { - err = scif_send_scif_unregister(ep, window); - if (err) { - window->unreg_state = OP_COMPLETED; - goto done; - } - } else { - /* Return ENXIO since unregistration is in progress */ - mutex_lock(&ep->rma_info.rma_lock); - return -ENXIO; - } -retry: - /* Wait for a SCIF_UNREGISTER_(N)ACK message */ - err = wait_event_timeout(window->unregwq, - window->unreg_state != OP_IN_PROGRESS, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - if (!err) { - err = -ENODEV; - window->unreg_state = OP_COMPLETED; - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", __func__, __LINE__, err); - } - if (err > 0) - err = 0; -done: - mutex_lock(&ep->rma_info.rma_lock); - scif_put_window(window, 1); - break; - } - case OP_FAILED: - { - if (!scifdev_alive(ep)) { - err = -ENODEV; - window->unreg_state = OP_COMPLETED; - } - break; - } - case OP_COMPLETED: - break; - default: - err = -ENODEV; - } - - if (window->unreg_state == OP_COMPLETED && window->ref_count) - scif_put_window(window, window->nr_pages); - - if (!window->ref_count) { - atomic_inc(&ep->rma_info.tw_refcount); - list_del_init(&window->list); - scif_free_window_offset(ep, window, window->offset); - mutex_unlock(&ep->rma_info.rma_lock); - if ((!!(window->pinned_pages->map_flags & SCIF_MAP_KERNEL)) && - scifdev_alive(ep)) { - scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan); - } else { - if (!__scif_dec_pinned_vm_lock(window->mm, - window->nr_pages)) { - __scif_release_mm(window->mm); - window->mm = NULL; - } - } - scif_queue_for_cleanup(window, &scif_info.rma); - mutex_lock(&ep->rma_info.rma_lock); - } - return err; -} - -/** - * scif_send_alloc_request: - * @ep: end point - * @window: self registration window - * - * Send a remote window allocation request - */ -static int scif_send_alloc_request(struct scif_endpt *ep, - struct scif_window *window) -{ - struct scifmsg msg; - struct scif_allocmsg *alloc = &window->alloc_handle; - - /* Set up the Alloc Handle */ - alloc->state = OP_IN_PROGRESS; - init_waitqueue_head(&alloc->allocwq); - - /* Send out an allocation request */ - msg.uop = SCIF_ALLOC_REQ; - msg.payload[1] = window->nr_pages; - msg.payload[2] = (u64)&window->alloc_handle; - return _scif_nodeqp_send(ep->remote_dev, &msg); -} - -/** - * scif_prep_remote_window: - * @ep: end point - * @window: self registration window - * - * Send a remote window allocation request, wait for an allocation response, - * and prepares the remote window by copying over the page lists - */ -static int scif_prep_remote_window(struct scif_endpt *ep, - struct scif_window *window) -{ - struct scifmsg msg; - struct scif_window *remote_window; - struct scif_allocmsg *alloc = &window->alloc_handle; - dma_addr_t *dma_phys_lookup, *tmp, *num_pages_lookup, *tmp1; - int i = 0, j = 0; - int nr_contig_chunks, loop_nr_contig_chunks; - int remaining_nr_contig_chunks, nr_lookup; - int err, map_err; - - map_err = scif_map_window(ep->remote_dev, window); - if (map_err) - dev_err(&ep->remote_dev->sdev->dev, - "%s %d map_err %d\n", __func__, __LINE__, map_err); - remaining_nr_contig_chunks = window->nr_contig_chunks; - nr_contig_chunks = window->nr_contig_chunks; -retry: - /* Wait for a SCIF_ALLOC_GNT/REJ message */ - err = wait_event_timeout(alloc->allocwq, - alloc->state != OP_IN_PROGRESS, - SCIF_NODE_ALIVE_TIMEOUT); - mutex_lock(&ep->rma_info.rma_lock); - /* Synchronize with the thread waking up allocwq */ - mutex_unlock(&ep->rma_info.rma_lock); - if (!err && scifdev_alive(ep)) - goto retry; - - if (!err) - err = -ENODEV; - - if (err > 0) - err = 0; - else - return err; - - /* Bail out. The remote end rejected this request */ - if (alloc->state == OP_FAILED) - return -ENOMEM; - - if (map_err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, map_err); - msg.uop = SCIF_FREE_VIRT; - msg.src = ep->port; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = window->alloc_handle.vaddr; - msg.payload[2] = (u64)window; - msg.payload[3] = SCIF_REGISTER; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) - err = _scif_nodeqp_send(ep->remote_dev, &msg); - else - err = -ENOTCONN; - spin_unlock(&ep->lock); - return err; - } - - remote_window = scif_ioremap(alloc->phys_addr, sizeof(*window), - ep->remote_dev); - - /* Compute the number of lookup entries. 21 == 2MB Shift */ - nr_lookup = ALIGN(nr_contig_chunks, SCIF_NR_ADDR_IN_PAGE) - >> ilog2(SCIF_NR_ADDR_IN_PAGE); - - dma_phys_lookup = - scif_ioremap(remote_window->dma_addr_lookup.offset, - nr_lookup * - sizeof(*remote_window->dma_addr_lookup.lookup), - ep->remote_dev); - num_pages_lookup = - scif_ioremap(remote_window->num_pages_lookup.offset, - nr_lookup * - sizeof(*remote_window->num_pages_lookup.lookup), - ep->remote_dev); - - while (remaining_nr_contig_chunks) { - loop_nr_contig_chunks = min_t(int, remaining_nr_contig_chunks, - (int)SCIF_NR_ADDR_IN_PAGE); - /* #1/2 - Copy physical addresses over to the remote side */ - - /* #2/2 - Copy DMA addresses (addresses that are fed into the - * DMA engine) We transfer bus addresses which are then - * converted into a MIC physical address on the remote - * side if it is a MIC, if the remote node is a mgmt node we - * transfer the MIC physical address - */ - tmp = scif_ioremap(dma_phys_lookup[j], - loop_nr_contig_chunks * - sizeof(*window->dma_addr), - ep->remote_dev); - tmp1 = scif_ioremap(num_pages_lookup[j], - loop_nr_contig_chunks * - sizeof(*window->num_pages), - ep->remote_dev); - if (scif_is_mgmt_node()) { - memcpy_toio((void __force __iomem *)tmp, - &window->dma_addr[i], loop_nr_contig_chunks - * sizeof(*window->dma_addr)); - memcpy_toio((void __force __iomem *)tmp1, - &window->num_pages[i], loop_nr_contig_chunks - * sizeof(*window->num_pages)); - } else { - if (scifdev_is_p2p(ep->remote_dev)) { - /* - * add remote node's base address for this node - * to convert it into a MIC address - */ - int m; - dma_addr_t dma_addr; - - for (m = 0; m < loop_nr_contig_chunks; m++) { - dma_addr = window->dma_addr[i + m] + - ep->remote_dev->base_addr; - writeq(dma_addr, - (void __force __iomem *)&tmp[m]); - } - memcpy_toio((void __force __iomem *)tmp1, - &window->num_pages[i], - loop_nr_contig_chunks - * sizeof(*window->num_pages)); - } else { - /* Mgmt node or loopback - transfer DMA - * addresses as is, this is the same as a - * MIC physical address (we use the dma_addr - * and not the phys_addr array since the - * phys_addr is only setup if there is a mmap() - * request from the mgmt node) - */ - memcpy_toio((void __force __iomem *)tmp, - &window->dma_addr[i], - loop_nr_contig_chunks * - sizeof(*window->dma_addr)); - memcpy_toio((void __force __iomem *)tmp1, - &window->num_pages[i], - loop_nr_contig_chunks * - sizeof(*window->num_pages)); - } - } - remaining_nr_contig_chunks -= loop_nr_contig_chunks; - i += loop_nr_contig_chunks; - j++; - scif_iounmap(tmp, loop_nr_contig_chunks * - sizeof(*window->dma_addr), ep->remote_dev); - scif_iounmap(tmp1, loop_nr_contig_chunks * - sizeof(*window->num_pages), ep->remote_dev); - } - - /* Prepare the remote window for the peer */ - remote_window->peer_window = (u64)window; - remote_window->offset = window->offset; - remote_window->prot = window->prot; - remote_window->nr_contig_chunks = nr_contig_chunks; - remote_window->ep = ep->remote_ep; - scif_iounmap(num_pages_lookup, - nr_lookup * - sizeof(*remote_window->num_pages_lookup.lookup), - ep->remote_dev); - scif_iounmap(dma_phys_lookup, - nr_lookup * - sizeof(*remote_window->dma_addr_lookup.lookup), - ep->remote_dev); - scif_iounmap(remote_window, sizeof(*remote_window), ep->remote_dev); - window->peer_window = alloc->vaddr; - return err; -} - -/** - * scif_send_scif_register: - * @ep: end point - * @window: self registration window - * - * Send a SCIF_REGISTER message if EP is connected and wait for a - * SCIF_REGISTER_(N)ACK message else send a SCIF_FREE_VIRT - * message so that the peer can free its remote window allocated earlier. - */ -static int scif_send_scif_register(struct scif_endpt *ep, - struct scif_window *window) -{ - int err = 0; - struct scifmsg msg; - - msg.src = ep->port; - msg.payload[0] = ep->remote_ep; - msg.payload[1] = window->alloc_handle.vaddr; - msg.payload[2] = (u64)window; - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) { - msg.uop = SCIF_REGISTER; - window->reg_state = OP_IN_PROGRESS; - err = _scif_nodeqp_send(ep->remote_dev, &msg); - spin_unlock(&ep->lock); - if (!err) { -retry: - /* Wait for a SCIF_REGISTER_(N)ACK message */ - err = wait_event_timeout(window->regwq, - window->reg_state != - OP_IN_PROGRESS, - SCIF_NODE_ALIVE_TIMEOUT); - if (!err && scifdev_alive(ep)) - goto retry; - err = !err ? -ENODEV : 0; - if (window->reg_state == OP_FAILED) - err = -ENOTCONN; - } - } else { - msg.uop = SCIF_FREE_VIRT; - msg.payload[3] = SCIF_REGISTER; - err = _scif_nodeqp_send(ep->remote_dev, &msg); - spin_unlock(&ep->lock); - if (!err) - err = -ENOTCONN; - } - return err; -} - -/** - * scif_get_window_offset: - * @ep: end point descriptor - * @flags: flags - * @offset: offset hint - * @num_pages: number of pages - * @out_offset: computed offset returned by reference. - * - * Compute/Claim a new offset for this EP. - */ -int scif_get_window_offset(struct scif_endpt *ep, int flags, s64 offset, - int num_pages, s64 *out_offset) -{ - s64 page_index; - struct iova *iova_ptr; - int err = 0; - - if (flags & SCIF_MAP_FIXED) { - page_index = SCIF_IOVA_PFN(offset); - iova_ptr = reserve_iova(&ep->rma_info.iovad, page_index, - page_index + num_pages - 1); - if (!iova_ptr) - err = -EADDRINUSE; - } else { - iova_ptr = alloc_iova(&ep->rma_info.iovad, num_pages, - SCIF_DMA_63BIT_PFN - 1, 0); - if (!iova_ptr) - err = -ENOMEM; - } - if (!err) - *out_offset = (iova_ptr->pfn_lo) << PAGE_SHIFT; - return err; -} - -/** - * scif_free_window_offset: - * @ep: end point descriptor - * @window: registration window - * @offset: Offset to be freed - * - * Free offset for this EP. The callee is supposed to grab - * the RMA mutex before calling this API. - */ -void scif_free_window_offset(struct scif_endpt *ep, - struct scif_window *window, s64 offset) -{ - if ((window && !window->offset_freed) || !window) { - free_iova(&ep->rma_info.iovad, offset >> PAGE_SHIFT); - if (window) - window->offset_freed = true; - } -} - -/** - * scif_alloc_req: Respond to SCIF_ALLOC_REQ interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side is requesting a memory allocation. - */ -void scif_alloc_req(struct scif_dev *scifdev, struct scifmsg *msg) -{ - int err; - struct scif_window *window = NULL; - int nr_pages = msg->payload[1]; - - window = scif_create_remote_window(scifdev, nr_pages); - if (!window) { - err = -ENOMEM; - goto error; - } - - /* The peer's allocation request is granted */ - msg->uop = SCIF_ALLOC_GNT; - msg->payload[0] = (u64)window; - msg->payload[1] = window->mapped_offset; - err = scif_nodeqp_send(scifdev, msg); - if (err) - scif_destroy_remote_window(window); - return; -error: - /* The peer's allocation request is rejected */ - dev_err(&scifdev->sdev->dev, - "%s %d error %d alloc_ptr %p nr_pages 0x%x\n", - __func__, __LINE__, err, window, nr_pages); - msg->uop = SCIF_ALLOC_REJ; - scif_nodeqp_send(scifdev, msg); -} - -/** - * scif_alloc_gnt_rej: Respond to SCIF_ALLOC_GNT/REJ interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remote side responded to a memory allocation. - */ -void scif_alloc_gnt_rej(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_allocmsg *handle = (struct scif_allocmsg *)msg->payload[2]; - struct scif_window *window = container_of(handle, struct scif_window, - alloc_handle); - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - handle->vaddr = msg->payload[0]; - handle->phys_addr = msg->payload[1]; - if (msg->uop == SCIF_ALLOC_GNT) - handle->state = OP_COMPLETED; - else - handle->state = OP_FAILED; - wake_up(&handle->allocwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -/** - * scif_free_virt: Respond to SCIF_FREE_VIRT interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Free up memory kmalloc'd earlier. - */ -void scif_free_virt(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = (struct scif_window *)msg->payload[1]; - - scif_destroy_remote_window(window); -} - -static void -scif_fixup_aper_base(struct scif_dev *dev, struct scif_window *window) -{ - int j; - struct scif_hw_dev *sdev = dev->sdev; - phys_addr_t apt_base = 0; - - /* - * Add the aperture base if the DMA address is not card relative - * since the DMA addresses need to be an offset into the bar - */ - if (!scifdev_self(dev) && window->type == SCIF_WINDOW_PEER && - sdev->aper && !sdev->card_rel_da) - apt_base = sdev->aper->pa; - else - return; - - for (j = 0; j < window->nr_contig_chunks; j++) { - if (window->num_pages[j]) - window->dma_addr[j] += apt_base; - else - break; - } -} - -/** - * scif_recv_reg: Respond to SCIF_REGISTER interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Update remote window list with a new registered window. - */ -void scif_recv_reg(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_endpt *ep = (struct scif_endpt *)msg->payload[0]; - struct scif_window *window = - (struct scif_window *)msg->payload[1]; - - mutex_lock(&ep->rma_info.rma_lock); - spin_lock(&ep->lock); - if (ep->state == SCIFEP_CONNECTED) { - msg->uop = SCIF_REGISTER_ACK; - scif_nodeqp_send(ep->remote_dev, msg); - scif_fixup_aper_base(ep->remote_dev, window); - /* No further failures expected. Insert new window */ - scif_insert_window(window, &ep->rma_info.remote_reg_list); - } else { - msg->uop = SCIF_REGISTER_NACK; - scif_nodeqp_send(ep->remote_dev, msg); - } - spin_unlock(&ep->lock); - mutex_unlock(&ep->rma_info.rma_lock); - /* free up any lookup resources now that page lists are transferred */ - scif_destroy_remote_lookup(ep->remote_dev, window); - /* - * We could not insert the window but we need to - * destroy the window. - */ - if (msg->uop == SCIF_REGISTER_NACK) - scif_destroy_remote_window(window); -} - -/** - * scif_recv_unreg: Respond to SCIF_UNREGISTER interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Remove window from remote registration list; - */ -void scif_recv_unreg(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_rma_req req; - struct scif_window *window = NULL; - struct scif_window *recv_window = - (struct scif_window *)msg->payload[0]; - struct scif_endpt *ep; - int del_window = 0; - - ep = (struct scif_endpt *)recv_window->ep; - req.out_window = &window; - req.offset = recv_window->offset; - req.prot = 0; - req.nr_bytes = recv_window->nr_pages << PAGE_SHIFT; - req.type = SCIF_WINDOW_FULL; - req.head = &ep->rma_info.remote_reg_list; - msg->payload[0] = ep->remote_ep; - - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - if (scif_query_window(&req)) { - dev_err(&scifdev->sdev->dev, - "%s %d -ENXIO\n", __func__, __LINE__); - msg->uop = SCIF_UNREGISTER_ACK; - goto error; - } - if (window) { - if (window->ref_count) - scif_put_window(window, window->nr_pages); - else - dev_err(&scifdev->sdev->dev, - "%s %d ref count should be +ve\n", - __func__, __LINE__); - window->unreg_state = OP_COMPLETED; - if (!window->ref_count) { - msg->uop = SCIF_UNREGISTER_ACK; - atomic_inc(&ep->rma_info.tw_refcount); - ep->rma_info.async_list_del = 1; - list_del_init(&window->list); - del_window = 1; - } else { - /* NACK! There are valid references to this window */ - msg->uop = SCIF_UNREGISTER_NACK; - } - } else { - /* The window did not make its way to the list at all. ACK */ - msg->uop = SCIF_UNREGISTER_ACK; - scif_destroy_remote_window(recv_window); - } -error: - mutex_unlock(&ep->rma_info.rma_lock); - if (del_window) - scif_drain_dma_intr(ep->remote_dev->sdev, - ep->rma_info.dma_chan); - scif_nodeqp_send(ep->remote_dev, msg); - if (del_window) - scif_queue_for_cleanup(window, &scif_info.rma); -} - -/** - * scif_recv_reg_ack: Respond to SCIF_REGISTER_ACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Wake up the window waiting to complete registration. - */ -void scif_recv_reg_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = - (struct scif_window *)msg->payload[2]; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - window->reg_state = OP_COMPLETED; - wake_up(&window->regwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -/** - * scif_recv_reg_nack: Respond to SCIF_REGISTER_NACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Wake up the window waiting to inform it that registration - * cannot be completed. - */ -void scif_recv_reg_nack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = - (struct scif_window *)msg->payload[2]; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - window->reg_state = OP_FAILED; - wake_up(&window->regwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -/** - * scif_recv_unreg_ack: Respond to SCIF_UNREGISTER_ACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Wake up the window waiting to complete unregistration. - */ -void scif_recv_unreg_ack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = - (struct scif_window *)msg->payload[1]; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - window->unreg_state = OP_COMPLETED; - wake_up(&window->unregwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -/** - * scif_recv_unreg_nack: Respond to SCIF_UNREGISTER_NACK interrupt message - * @scifdev: SCIF device - * @msg: Interrupt message - * - * Wake up the window waiting to inform it that unregistration - * cannot be completed immediately. - */ -void scif_recv_unreg_nack(struct scif_dev *scifdev, struct scifmsg *msg) -{ - struct scif_window *window = - (struct scif_window *)msg->payload[1]; - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - - mutex_lock(&ep->rma_info.rma_lock); - window->unreg_state = OP_FAILED; - wake_up(&window->unregwq); - mutex_unlock(&ep->rma_info.rma_lock); -} - -int __scif_pin_pages(void *addr, size_t len, int *out_prot, - int map_flags, scif_pinned_pages_t *pages) -{ - struct scif_pinned_pages *pinned_pages; - int nr_pages, err = 0, i; - bool vmalloc_addr = false; - bool try_upgrade = false; - int prot = *out_prot; - int ulimit = 0; - struct mm_struct *mm = NULL; - - /* Unsupported flags */ - if (map_flags & ~(SCIF_MAP_KERNEL | SCIF_MAP_ULIMIT)) - return -EINVAL; - ulimit = !!(map_flags & SCIF_MAP_ULIMIT); - - /* Unsupported protection requested */ - if (prot & ~(SCIF_PROT_READ | SCIF_PROT_WRITE)) - return -EINVAL; - - /* addr/len must be page aligned. len should be non zero */ - if (!len || - (ALIGN((u64)addr, PAGE_SIZE) != (u64)addr) || - (ALIGN((u64)len, PAGE_SIZE) != (u64)len)) - return -EINVAL; - - might_sleep(); - - nr_pages = len >> PAGE_SHIFT; - - /* Allocate a set of pinned pages */ - pinned_pages = scif_create_pinned_pages(nr_pages, prot); - if (!pinned_pages) - return -ENOMEM; - - if (map_flags & SCIF_MAP_KERNEL) { - if (is_vmalloc_addr(addr)) - vmalloc_addr = true; - - for (i = 0; i < nr_pages; i++) { - if (vmalloc_addr) - pinned_pages->pages[i] = - vmalloc_to_page(addr + (i * PAGE_SIZE)); - else - pinned_pages->pages[i] = - virt_to_page(addr + (i * PAGE_SIZE)); - } - pinned_pages->nr_pages = nr_pages; - pinned_pages->map_flags = SCIF_MAP_KERNEL; - } else { - /* - * SCIF supports registration caching. If a registration has - * been requested with read only permissions, then we try - * to pin the pages with RW permissions so that a subsequent - * transfer with RW permission can hit the cache instead of - * invalidating it. If the upgrade fails with RW then we - * revert back to R permission and retry - */ - if (prot == SCIF_PROT_READ) - try_upgrade = true; - prot |= SCIF_PROT_WRITE; -retry: - mm = current->mm; - if (ulimit) { - err = __scif_check_inc_pinned_vm(mm, nr_pages); - if (err) { - pinned_pages->nr_pages = 0; - goto error_unmap; - } - } - - pinned_pages->nr_pages = pin_user_pages_fast( - (u64)addr, - nr_pages, - (prot & SCIF_PROT_WRITE) ? FOLL_WRITE : 0, - pinned_pages->pages); - if (nr_pages != pinned_pages->nr_pages) { - if (pinned_pages->nr_pages < 0) - pinned_pages->nr_pages = 0; - if (try_upgrade) { - if (ulimit) - __scif_dec_pinned_vm_lock(mm, nr_pages); - /* Roll back any pinned pages */ - unpin_user_pages(pinned_pages->pages, - pinned_pages->nr_pages); - prot &= ~SCIF_PROT_WRITE; - try_upgrade = false; - goto retry; - } - } - pinned_pages->map_flags = 0; - } - - if (pinned_pages->nr_pages < nr_pages) { - err = -EFAULT; - goto dec_pinned; - } - - *out_prot = prot; - atomic_set(&pinned_pages->ref_count, 1); - *pages = pinned_pages; - return err; -dec_pinned: - if (ulimit) - __scif_dec_pinned_vm_lock(mm, nr_pages); - /* Something went wrong! Rollback */ -error_unmap: - scif_destroy_pinned_pages(pinned_pages); - *pages = NULL; - dev_dbg(scif_info.mdev.this_device, - "%s %d err %d len 0x%lx\n", __func__, __LINE__, err, len); - return err; -} - -int scif_pin_pages(void *addr, size_t len, int prot, - int map_flags, scif_pinned_pages_t *pages) -{ - return __scif_pin_pages(addr, len, &prot, map_flags, pages); -} -EXPORT_SYMBOL_GPL(scif_pin_pages); - -int scif_unpin_pages(scif_pinned_pages_t pinned_pages) -{ - int err = 0, ret; - - if (!pinned_pages || SCIFEP_MAGIC != pinned_pages->magic) - return -EINVAL; - - ret = atomic_sub_return(1, &pinned_pages->ref_count); - if (ret < 0) { - dev_err(scif_info.mdev.this_device, - "%s %d scif_unpin_pages called without pinning? rc %d\n", - __func__, __LINE__, ret); - return -EINVAL; - } - /* - * Destroy the window if the ref count for this set of pinned - * pages has dropped to zero. If it is positive then there is - * a valid registered window which is backed by these pages and - * it will be destroyed once all such windows are unregistered. - */ - if (!ret) - err = scif_destroy_pinned_pages(pinned_pages); - - return err; -} -EXPORT_SYMBOL_GPL(scif_unpin_pages); - -static inline void -scif_insert_local_window(struct scif_window *window, struct scif_endpt *ep) -{ - mutex_lock(&ep->rma_info.rma_lock); - scif_insert_window(window, &ep->rma_info.reg_list); - mutex_unlock(&ep->rma_info.rma_lock); -} - -off_t scif_register_pinned_pages(scif_epd_t epd, - scif_pinned_pages_t pinned_pages, - off_t offset, int map_flags) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - s64 computed_offset; - struct scif_window *window; - int err; - size_t len; - struct device *spdev; - - /* Unsupported flags */ - if (map_flags & ~SCIF_MAP_FIXED) - return -EINVAL; - - len = pinned_pages->nr_pages << PAGE_SHIFT; - - /* - * Offset is not page aligned/negative or offset+len - * wraps around with SCIF_MAP_FIXED. - */ - if ((map_flags & SCIF_MAP_FIXED) && - ((ALIGN(offset, PAGE_SIZE) != offset) || - (offset < 0) || - (len > LONG_MAX - offset))) - return -EINVAL; - - might_sleep(); - - err = scif_verify_epd(ep); - if (err) - return err; - /* - * It is an error to pass pinned_pages to scif_register_pinned_pages() - * after calling scif_unpin_pages(). - */ - if (!atomic_add_unless(&pinned_pages->ref_count, 1, 0)) - return -EINVAL; - - /* Compute the offset for this registration */ - err = scif_get_window_offset(ep, map_flags, offset, - len, &computed_offset); - if (err) { - atomic_sub(1, &pinned_pages->ref_count); - return err; - } - - /* Allocate and prepare self registration window */ - window = scif_create_window(ep, pinned_pages->nr_pages, - computed_offset, false); - if (!window) { - atomic_sub(1, &pinned_pages->ref_count); - scif_free_window_offset(ep, NULL, computed_offset); - return -ENOMEM; - } - - window->pinned_pages = pinned_pages; - window->nr_pages = pinned_pages->nr_pages; - window->prot = pinned_pages->prot; - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - scif_destroy_window(ep, window); - return err; - } - err = scif_send_alloc_request(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unmap; - } - - /* Prepare the remote registration window */ - err = scif_prep_remote_window(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unmap; - } - - /* Tell the peer about the new window */ - err = scif_send_scif_register(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error_unmap; - } - - scif_put_peer_dev(spdev); - /* No further failures expected. Insert new window */ - scif_insert_local_window(window, ep); - return computed_offset; -error_unmap: - scif_destroy_window(ep, window); - scif_put_peer_dev(spdev); - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_register_pinned_pages); - -off_t scif_register(scif_epd_t epd, void *addr, size_t len, off_t offset, - int prot, int map_flags) -{ - scif_pinned_pages_t pinned_pages; - off_t err; - struct scif_endpt *ep = (struct scif_endpt *)epd; - s64 computed_offset; - struct scif_window *window; - struct mm_struct *mm = NULL; - struct device *spdev; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI register: ep %p addr %p len 0x%lx offset 0x%lx prot 0x%x map_flags 0x%x\n", - epd, addr, len, offset, prot, map_flags); - /* Unsupported flags */ - if (map_flags & ~(SCIF_MAP_FIXED | SCIF_MAP_KERNEL)) - return -EINVAL; - - /* - * Offset is not page aligned/negative or offset+len - * wraps around with SCIF_MAP_FIXED. - */ - if ((map_flags & SCIF_MAP_FIXED) && - ((ALIGN(offset, PAGE_SIZE) != offset) || - (offset < 0) || - (len > LONG_MAX - offset))) - return -EINVAL; - - /* Unsupported protection requested */ - if (prot & ~(SCIF_PROT_READ | SCIF_PROT_WRITE)) - return -EINVAL; - - /* addr/len must be page aligned. len should be non zero */ - if (!len || (ALIGN((u64)addr, PAGE_SIZE) != (u64)addr) || - (ALIGN(len, PAGE_SIZE) != len)) - return -EINVAL; - - might_sleep(); - - err = scif_verify_epd(ep); - if (err) - return err; - - /* Compute the offset for this registration */ - err = scif_get_window_offset(ep, map_flags, offset, - len >> PAGE_SHIFT, &computed_offset); - if (err) - return err; - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - scif_free_window_offset(ep, NULL, computed_offset); - return err; - } - /* Allocate and prepare self registration window */ - window = scif_create_window(ep, len >> PAGE_SHIFT, - computed_offset, false); - if (!window) { - scif_free_window_offset(ep, NULL, computed_offset); - scif_put_peer_dev(spdev); - return -ENOMEM; - } - - window->nr_pages = len >> PAGE_SHIFT; - - err = scif_send_alloc_request(ep, window); - if (err) { - scif_destroy_incomplete_window(ep, window); - scif_put_peer_dev(spdev); - return err; - } - - if (!(map_flags & SCIF_MAP_KERNEL)) { - mm = __scif_acquire_mm(); - map_flags |= SCIF_MAP_ULIMIT; - } - /* Pin down the pages */ - err = __scif_pin_pages(addr, len, &prot, - map_flags & (SCIF_MAP_KERNEL | SCIF_MAP_ULIMIT), - &pinned_pages); - if (err) { - scif_destroy_incomplete_window(ep, window); - __scif_release_mm(mm); - goto error; - } - - window->pinned_pages = pinned_pages; - window->prot = pinned_pages->prot; - window->mm = mm; - - /* Prepare the remote registration window */ - err = scif_prep_remote_window(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %ld\n", __func__, __LINE__, err); - goto error_unmap; - } - - /* Tell the peer about the new window */ - err = scif_send_scif_register(ep, window); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %ld\n", __func__, __LINE__, err); - goto error_unmap; - } - - scif_put_peer_dev(spdev); - /* No further failures expected. Insert new window */ - scif_insert_local_window(window, ep); - dev_dbg(&ep->remote_dev->sdev->dev, - "SCIFAPI register: ep %p addr %p len 0x%lx computed_offset 0x%llx\n", - epd, addr, len, computed_offset); - return computed_offset; -error_unmap: - scif_destroy_window(ep, window); -error: - scif_put_peer_dev(spdev); - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %ld\n", __func__, __LINE__, err); - return err; -} -EXPORT_SYMBOL_GPL(scif_register); - -int -scif_unregister(scif_epd_t epd, off_t offset, size_t len) -{ - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct scif_window *window = NULL; - struct scif_rma_req req; - int nr_pages, err; - struct device *spdev; - - dev_dbg(scif_info.mdev.this_device, - "SCIFAPI unregister: ep %p offset 0x%lx len 0x%lx\n", - ep, offset, len); - /* len must be page aligned. len should be non zero */ - if (!len || - (ALIGN((u64)len, PAGE_SIZE) != (u64)len)) - return -EINVAL; - - /* Offset is not page aligned or offset+len wraps around */ - if ((ALIGN(offset, PAGE_SIZE) != offset) || - (offset < 0) || - (len > LONG_MAX - offset)) - return -EINVAL; - - err = scif_verify_epd(ep); - if (err) - return err; - - might_sleep(); - nr_pages = len >> PAGE_SHIFT; - - req.out_window = &window; - req.offset = offset; - req.prot = 0; - req.nr_bytes = len; - req.type = SCIF_WINDOW_FULL; - req.head = &ep->rma_info.reg_list; - - spdev = scif_get_peer_dev(ep->remote_dev); - if (IS_ERR(spdev)) { - err = PTR_ERR(spdev); - return err; - } - mutex_lock(&ep->rma_info.rma_lock); - /* Does a valid window exist? */ - err = scif_query_window(&req); - if (err) { - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); - goto error; - } - /* Unregister all the windows in this range */ - err = scif_rma_list_unregister(window, offset, nr_pages); - if (err) - dev_err(&ep->remote_dev->sdev->dev, - "%s %d err %d\n", __func__, __LINE__, err); -error: - mutex_unlock(&ep->rma_info.rma_lock); - scif_put_peer_dev(spdev); - return err; -} -EXPORT_SYMBOL_GPL(scif_unregister); diff --git a/drivers/misc/mic/scif/scif_rma.h b/drivers/misc/mic/scif/scif_rma.h deleted file mode 100644 index 964dd0fc3657..000000000000 --- a/drivers/misc/mic/scif/scif_rma.h +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2015 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Copyright(c) 2015 Intel Corporation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Intel SCIF driver. - * - */ -#ifndef SCIF_RMA_H -#define SCIF_RMA_H - -#include -#include - -#include "../bus/scif_bus.h" - -/* If this bit is set then the mark is a remote fence mark */ -#define SCIF_REMOTE_FENCE_BIT 31 -/* Magic value used to indicate a remote fence request */ -#define SCIF_REMOTE_FENCE BIT_ULL(SCIF_REMOTE_FENCE_BIT) - -#define SCIF_MAX_UNALIGNED_BUF_SIZE (1024 * 1024ULL) -#define SCIF_KMEM_UNALIGNED_BUF_SIZE (SCIF_MAX_UNALIGNED_BUF_SIZE + \ - (L1_CACHE_BYTES << 1)) - -#define SCIF_IOVA_START_PFN (1) -#define SCIF_IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) -#define SCIF_DMA_64BIT_PFN SCIF_IOVA_PFN(DMA_BIT_MASK(64)) -#define SCIF_DMA_63BIT_PFN SCIF_IOVA_PFN(DMA_BIT_MASK(63)) - -/* - * struct scif_endpt_rma_info - Per Endpoint Remote Memory Access Information - * - * @reg_list: List of registration windows for self - * @remote_reg_list: List of registration windows for peer - * @iovad: Offset generator - * @rma_lock: Synchronizes access to self/remote list and also protects the - * window from being destroyed while RMAs are in progress. - * @tc_lock: Synchronizes access to temporary cached windows list - * for SCIF Registration Caching. - * @mmn_lock: Synchronizes access to the list of MMU notifiers registered - * @tw_refcount: Keeps track of number of outstanding temporary registered - * windows created by scif_vreadfrom/scif_vwriteto which have - * not been destroyed. - * @tcw_refcount: Same as tw_refcount but for temporary cached windows - * @tcw_total_pages: Same as tcw_refcount but in terms of pages pinned - * @mmn_list: MMU notifier so that we can destroy the windows when required - * @fence_refcount: Keeps track of number of outstanding remote fence - * requests which have been received by the peer. - * @dma_chan: DMA channel used for all DMA transfers for this endpoint. - * @async_list_del: Detect asynchronous list entry deletion - * @vma_list: List of vmas with remote memory mappings - * @markwq: Wait queue used for scif_fence_mark/scif_fence_wait -*/ -struct scif_endpt_rma_info { - struct list_head reg_list; - struct list_head remote_reg_list; - struct iova_domain iovad; - struct mutex rma_lock; - spinlock_t tc_lock; - struct mutex mmn_lock; - atomic_t tw_refcount; - atomic_t tcw_refcount; - atomic_t tcw_total_pages; - struct list_head mmn_list; - atomic_t fence_refcount; - struct dma_chan *dma_chan; - int async_list_del; - struct list_head vma_list; - wait_queue_head_t markwq; -}; - -/* - * struct scif_fence_info - used for tracking fence requests - * - * @state: State of this transfer - * @wq: Fences wait on this queue - * @dma_mark: Used for storing the DMA mark - */ -struct scif_fence_info { - enum scif_msg_state state; - struct completion comp; - int dma_mark; -}; - -/* - * struct scif_remote_fence_info - used for tracking remote fence requests - * - * @msg: List of SCIF node QP fence messages - * @list: Link to list of remote fence requests - */ -struct scif_remote_fence_info { - struct scifmsg msg; - struct list_head list; -}; - -/* - * Specifies whether an RMA operation can span across partial windows, a single - * window or multiple contiguous windows. Mmaps can span across partial windows. - * Unregistration can span across complete windows. scif_get_pages() can span a - * single window. A window can also be of type self or peer. - */ -enum scif_window_type { - SCIF_WINDOW_PARTIAL, - SCIF_WINDOW_SINGLE, - SCIF_WINDOW_FULL, - SCIF_WINDOW_SELF, - SCIF_WINDOW_PEER -}; - -/* The number of physical addresses that can be stored in a PAGE. */ -#define SCIF_NR_ADDR_IN_PAGE (0x1000 >> 3) - -/* - * struct scif_rma_lookup - RMA lookup data structure for page list transfers - * - * Store an array of lookup offsets. Each offset in this array maps - * one 4K page containing 512 physical addresses i.e. 2MB. 512 such - * offsets in a 4K page will correspond to 1GB of registered address space. - - * @lookup: Array of offsets - * @offset: DMA offset of lookup array - */ -struct scif_rma_lookup { - dma_addr_t *lookup; - dma_addr_t offset; -}; - -/* - * struct scif_pinned_pages - A set of pinned pages obtained with - * scif_pin_pages() which could be part of multiple registered - * windows across different end points. - * - * @nr_pages: Number of pages which is defined as a s64 instead of an int - * to avoid sign extension with buffers >= 2GB - * @prot: read/write protections - * @map_flags: Flags specified during the pin operation - * @ref_count: Reference count bumped in terms of number of pages - * @magic: A magic value - * @pages: Array of pointers to struct pages populated with get_user_pages(..) - */ -struct scif_pinned_pages { - s64 nr_pages; - int prot; - int map_flags; - atomic_t ref_count; - u64 magic; - struct page **pages; -}; - -/* - * struct scif_status - Stores DMA status update information - * - * @src_dma_addr: Source buffer DMA address - * @val: src location for value to be written to the destination - * @ep: SCIF endpoint - */ -struct scif_status { - dma_addr_t src_dma_addr; - u64 val; - struct scif_endpt *ep; -}; - -/* - * struct scif_cb_arg - Stores the argument of the callback func - * - * @src_dma_addr: Source buffer DMA address - * @status: DMA status - * @ep: SCIF endpoint - */ -struct scif_cb_arg { - dma_addr_t src_dma_addr; - struct scif_status *status; - struct scif_endpt *ep; -}; - -/* - * struct scif_window - Registration Window for Self and Remote - * - * @nr_pages: Number of pages which is defined as a s64 instead of an int - * to avoid sign extension with buffers >= 2GB - * @nr_contig_chunks: Number of contiguous physical chunks - * @prot: read/write protections - * @ref_count: reference count in terms of number of pages - * @magic: Cookie to detect corruption - * @offset: registered offset - * @va_for_temp: va address that this window represents - * @dma_mark: Used to determine if all DMAs against the window are done - * @ep: Pointer to EP. Useful for passing EP around with messages to - avoid expensive list traversals. - * @list: link to list of windows for the endpoint - * @type: self or peer window - * @peer_window: Pointer to peer window. Useful for sending messages to peer - * without requiring an extra list traversal - * @unreg_state: unregistration state - * @offset_freed: True if the offset has been freed - * @temp: True for temporary windows created via scif_vreadfrom/scif_vwriteto - * @mm: memory descriptor for the task_struct which initiated the RMA - * @st: scatter gather table for DMA mappings with IOMMU enabled - * @pinned_pages: The set of pinned_pages backing this window - * @alloc_handle: Handle for sending ALLOC_REQ - * @regwq: Wait Queue for an registration (N)ACK - * @reg_state: Registration state - * @unregwq: Wait Queue for an unregistration (N)ACK - * @dma_addr_lookup: Lookup for physical addresses used for DMA - * @nr_lookup: Number of entries in lookup - * @mapped_offset: Offset used to map the window by the peer - * @dma_addr: Array of physical addresses used for Mgmt node & MIC initiated DMA - * @num_pages: Array specifying number of pages for each physical address - */ -struct scif_window { - s64 nr_pages; - int nr_contig_chunks; - int prot; - int ref_count; - u64 magic; - s64 offset; - unsigned long va_for_temp; - int dma_mark; - u64 ep; - struct list_head list; - enum scif_window_type type; - u64 peer_window; - enum scif_msg_state unreg_state; - bool offset_freed; - bool temp; - struct mm_struct *mm; - struct sg_table *st; - union { - struct { - struct scif_pinned_pages *pinned_pages; - struct scif_allocmsg alloc_handle; - wait_queue_head_t regwq; - enum scif_msg_state reg_state; - wait_queue_head_t unregwq; - }; - struct { - struct scif_rma_lookup dma_addr_lookup; - struct scif_rma_lookup num_pages_lookup; - int nr_lookup; - dma_addr_t mapped_offset; - }; - }; - dma_addr_t *dma_addr; - u64 *num_pages; -} __packed; - -/* - * scif_mmu_notif - SCIF mmu notifier information - * - * @mmu_notifier ep_mmu_notifier: MMU notifier operations - * @tc_reg_list: List of temp registration windows for self - * @mm: memory descriptor for the task_struct which initiated the RMA - * @ep: SCIF endpoint - * @list: link to list of MMU notifier information - */ -struct scif_mmu_notif { -#ifdef CONFIG_MMU_NOTIFIER - struct mmu_notifier ep_mmu_notifier; -#endif - struct list_head tc_reg_list; - struct mm_struct *mm; - struct scif_endpt *ep; - struct list_head list; -}; - -enum scif_rma_dir { - SCIF_LOCAL_TO_REMOTE, - SCIF_REMOTE_TO_LOCAL -}; - -extern struct kmem_cache *unaligned_cache; -/* Initialize RMA for this EP */ -void scif_rma_ep_init(struct scif_endpt *ep); -/* Check if epd can be uninitialized */ -int scif_rma_ep_can_uninit(struct scif_endpt *ep); -/* Obtain a new offset. Callee must grab RMA lock */ -int scif_get_window_offset(struct scif_endpt *ep, int flags, - s64 offset, int nr_pages, s64 *out_offset); -/* Free offset. Callee must grab RMA lock */ -void scif_free_window_offset(struct scif_endpt *ep, - struct scif_window *window, s64 offset); -/* Create self registration window */ -struct scif_window *scif_create_window(struct scif_endpt *ep, int nr_pages, - s64 offset, bool temp); -/* Destroy self registration window.*/ -int scif_destroy_window(struct scif_endpt *ep, struct scif_window *window); -void scif_unmap_window(struct scif_dev *remote_dev, struct scif_window *window); -/* Map pages of self window to Aperture/PCI */ -int scif_map_window(struct scif_dev *remote_dev, - struct scif_window *window); -/* Unregister a self window */ -int scif_unregister_window(struct scif_window *window); -/* Destroy remote registration window */ -void -scif_destroy_remote_window(struct scif_window *window); -/* remove valid remote memory mappings from process address space */ -void scif_zap_mmaps(int node); -/* Query if any applications have remote memory mappings */ -bool scif_rma_do_apps_have_mmaps(int node); -/* Cleanup remote registration lists for zombie endpoints */ -void scif_cleanup_rma_for_zombies(int node); -/* Reserve a DMA channel for a particular endpoint */ -int scif_reserve_dma_chan(struct scif_endpt *ep); -/* Setup a DMA mark for an endpoint */ -int _scif_fence_mark(scif_epd_t epd, int *mark); -int scif_prog_signal(scif_epd_t epd, off_t offset, u64 val, - enum scif_window_type type); -void scif_alloc_req(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_alloc_gnt_rej(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_free_virt(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_reg(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_unreg(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_reg_ack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_reg_nack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_unreg_ack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_unreg_nack(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_munmap(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_mark(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_mark_resp(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_wait(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_wait_resp(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_sig_local(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_sig_remote(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_recv_sig_resp(struct scif_dev *scifdev, struct scifmsg *msg); -void scif_mmu_notif_handler(struct work_struct *work); -void scif_rma_handle_remote_fences(void); -void scif_rma_destroy_windows(void); -void scif_rma_destroy_tcw_invalid(void); -int scif_drain_dma_intr(struct scif_hw_dev *sdev, struct dma_chan *chan); - -struct scif_window_iter { - s64 offset; - int index; -}; - -static inline void -scif_init_window_iter(struct scif_window *window, struct scif_window_iter *iter) -{ - iter->offset = window->offset; - iter->index = 0; -} - -dma_addr_t scif_off_to_dma_addr(struct scif_window *window, s64 off, - size_t *nr_bytes, - struct scif_window_iter *iter); -static inline -dma_addr_t __scif_off_to_dma_addr(struct scif_window *window, s64 off) -{ - return scif_off_to_dma_addr(window, off, NULL, NULL); -} - -static inline bool scif_unaligned(off_t src_offset, off_t dst_offset) -{ - src_offset = src_offset & (L1_CACHE_BYTES - 1); - dst_offset = dst_offset & (L1_CACHE_BYTES - 1); - return !(src_offset == dst_offset); -} - -/* - * scif_zalloc: - * @size: Size of the allocation request. - * - * Helper API which attempts to allocate zeroed pages via - * __get_free_pages(..) first and then falls back on - * vzalloc(..) if that fails. - */ -static inline void *scif_zalloc(size_t size) -{ - void *ret = NULL; - size_t align = ALIGN(size, PAGE_SIZE); - - if (align && get_order(align) < MAX_ORDER) - ret = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(align)); - return ret ? ret : vzalloc(align); -} - -/* - * scif_free: - * @addr: Address to be freed. - * @size: Size of the allocation. - * Helper API which frees memory allocated via scif_zalloc(). - */ -static inline void scif_free(void *addr, size_t size) -{ - size_t align = ALIGN(size, PAGE_SIZE); - - if (is_vmalloc_addr(addr)) - vfree(addr); - else - free_pages((unsigned long)addr, get_order(align)); -} - -static inline void scif_get_window(struct scif_window *window, int nr_pages) -{ - window->ref_count += nr_pages; -} - -static inline void scif_put_window(struct scif_window *window, int nr_pages) -{ - window->ref_count -= nr_pages; -} - -static inline void scif_set_window_ref(struct scif_window *window, int nr_pages) -{ - window->ref_count = nr_pages; -} - -static inline void -scif_queue_for_cleanup(struct scif_window *window, struct list_head *list) -{ - spin_lock(&scif_info.rmalock); - list_add_tail(&window->list, list); - spin_unlock(&scif_info.rmalock); - schedule_work(&scif_info.misc_work); -} - -static inline void __scif_rma_destroy_tcw_helper(struct scif_window *window) -{ - list_del_init(&window->list); - scif_queue_for_cleanup(window, &scif_info.rma_tc); -} - -static inline bool scif_is_iommu_enabled(void) -{ -#ifdef CONFIG_INTEL_IOMMU - return intel_iommu_enabled; -#else - return false; -#endif -} -#endif /* SCIF_RMA_H */ diff --git a/drivers/misc/mic/scif/scif_rma_list.c b/drivers/misc/mic/scif/scif_rma_list.c deleted file mode 100644 index ef923ba134c8..000000000000 --- a/drivers/misc/mic/scif/scif_rma_list.c +++ /dev/null @@ -1,282 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#include "scif_main.h" -#include -#include - -/* - * scif_insert_tcw: - * - * Insert a temp window to the temp registration list sorted by va_for_temp. - * RMA lock must be held. - */ -void scif_insert_tcw(struct scif_window *window, struct list_head *head) -{ - struct scif_window *curr = NULL; - struct scif_window *prev = list_entry(head, struct scif_window, list); - struct list_head *item; - - INIT_LIST_HEAD(&window->list); - /* Compare with tail and if the entry is new tail add it to the end */ - if (!list_empty(head)) { - curr = list_entry(head->prev, struct scif_window, list); - if (curr->va_for_temp < window->va_for_temp) { - list_add_tail(&window->list, head); - return; - } - } - list_for_each(item, head) { - curr = list_entry(item, struct scif_window, list); - if (curr->va_for_temp > window->va_for_temp) - break; - prev = curr; - } - list_add(&window->list, &prev->list); -} - -/* - * scif_insert_window: - * - * Insert a window to the self registration list sorted by offset. - * RMA lock must be held. - */ -void scif_insert_window(struct scif_window *window, struct list_head *head) -{ - struct scif_window *curr = NULL, *prev = NULL; - struct list_head *item; - - INIT_LIST_HEAD(&window->list); - list_for_each(item, head) { - curr = list_entry(item, struct scif_window, list); - if (curr->offset > window->offset) - break; - prev = curr; - } - if (!prev) - list_add(&window->list, head); - else - list_add(&window->list, &prev->list); - scif_set_window_ref(window, window->nr_pages); -} - -/* - * scif_query_tcw: - * - * Query the temp cached registration list of ep for an overlapping window - * in case of permission mismatch, destroy the previous window. if permissions - * match and overlap is partial, destroy the window but return the new range - * RMA lock must be held. - */ -int scif_query_tcw(struct scif_endpt *ep, struct scif_rma_req *req) -{ - struct list_head *item, *temp, *head = req->head; - struct scif_window *window; - u64 start_va_window, start_va_req = req->va_for_temp; - u64 end_va_window, end_va_req = start_va_req + req->nr_bytes; - - if (!req->nr_bytes) - return -EINVAL; - /* - * Avoid traversing the entire list to find out that there - * is no entry that matches - */ - if (!list_empty(head)) { - window = list_last_entry(head, struct scif_window, list); - end_va_window = window->va_for_temp + - (window->nr_pages << PAGE_SHIFT); - if (start_va_req > end_va_window) - return -ENXIO; - } - list_for_each_safe(item, temp, head) { - window = list_entry(item, struct scif_window, list); - start_va_window = window->va_for_temp; - end_va_window = window->va_for_temp + - (window->nr_pages << PAGE_SHIFT); - if (start_va_req < start_va_window && - end_va_req < start_va_window) - break; - if (start_va_req >= end_va_window) - continue; - if ((window->prot & req->prot) == req->prot) { - if (start_va_req >= start_va_window && - end_va_req <= end_va_window) { - *req->out_window = window; - return 0; - } - /* expand window */ - if (start_va_req < start_va_window) { - req->nr_bytes += - start_va_window - start_va_req; - req->va_for_temp = start_va_window; - } - if (end_va_req >= end_va_window) - req->nr_bytes += end_va_window - end_va_req; - } - /* Destroy the old window to create a new one */ - __scif_rma_destroy_tcw_helper(window); - break; - } - return -ENXIO; -} - -/* - * scif_query_window: - * - * Query the registration list and check if a valid contiguous - * range of windows exist. - * RMA lock must be held. - */ -int scif_query_window(struct scif_rma_req *req) -{ - struct list_head *item; - struct scif_window *window; - s64 end_offset, offset = req->offset; - u64 tmp_min, nr_bytes_left = req->nr_bytes; - - if (!req->nr_bytes) - return -EINVAL; - - list_for_each(item, req->head) { - window = list_entry(item, struct scif_window, list); - end_offset = window->offset + - (window->nr_pages << PAGE_SHIFT); - if (offset < window->offset) - /* Offset not found! */ - return -ENXIO; - if (offset >= end_offset) - continue; - /* Check read/write protections. */ - if ((window->prot & req->prot) != req->prot) - return -EPERM; - if (nr_bytes_left == req->nr_bytes) - /* Store the first window */ - *req->out_window = window; - tmp_min = min((u64)end_offset - offset, nr_bytes_left); - nr_bytes_left -= tmp_min; - offset += tmp_min; - /* - * Range requested encompasses - * multiple windows contiguously. - */ - if (!nr_bytes_left) { - /* Done for partial window */ - if (req->type == SCIF_WINDOW_PARTIAL || - req->type == SCIF_WINDOW_SINGLE) - return 0; - /* Extra logic for full windows */ - if (offset == end_offset) - /* Spanning multiple whole windows */ - return 0; - /* Not spanning multiple whole windows */ - return -ENXIO; - } - if (req->type == SCIF_WINDOW_SINGLE) - break; - } - dev_err(scif_info.mdev.this_device, - "%s %d ENXIO\n", __func__, __LINE__); - return -ENXIO; -} - -/* - * scif_rma_list_unregister: - * - * Traverse the self registration list starting from window: - * 1) Call scif_unregister_window(..) - * RMA lock must be held. - */ -int scif_rma_list_unregister(struct scif_window *window, - s64 offset, int nr_pages) -{ - struct scif_endpt *ep = (struct scif_endpt *)window->ep; - struct list_head *head = &ep->rma_info.reg_list; - s64 end_offset; - int err = 0; - int loop_nr_pages; - struct scif_window *_window; - - list_for_each_entry_safe_from(window, _window, head, list) { - end_offset = window->offset + (window->nr_pages << PAGE_SHIFT); - loop_nr_pages = min((int)((end_offset - offset) >> PAGE_SHIFT), - nr_pages); - err = scif_unregister_window(window); - if (err) - return err; - nr_pages -= loop_nr_pages; - offset += (loop_nr_pages << PAGE_SHIFT); - if (!nr_pages) - break; - } - return 0; -} - -/* - * scif_unmap_all_window: - * - * Traverse all the windows in the self registration list and: - * 1) Delete any DMA mappings created - */ -void scif_unmap_all_windows(scif_epd_t epd) -{ - struct list_head *item, *tmp; - struct scif_window *window; - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct list_head *head = &ep->rma_info.reg_list; - - mutex_lock(&ep->rma_info.rma_lock); - list_for_each_safe(item, tmp, head) { - window = list_entry(item, struct scif_window, list); - scif_unmap_window(ep->remote_dev, window); - } - mutex_unlock(&ep->rma_info.rma_lock); -} - -/* - * scif_unregister_all_window: - * - * Traverse all the windows in the self registration list and: - * 1) Call scif_unregister_window(..) - * RMA lock must be held. - */ -int scif_unregister_all_windows(scif_epd_t epd) -{ - struct list_head *item, *tmp; - struct scif_window *window; - struct scif_endpt *ep = (struct scif_endpt *)epd; - struct list_head *head = &ep->rma_info.reg_list; - int err = 0; - - mutex_lock(&ep->rma_info.rma_lock); -retry: - item = NULL; - tmp = NULL; - list_for_each_safe(item, tmp, head) { - window = list_entry(item, struct scif_window, list); - ep->rma_info.async_list_del = 0; - err = scif_unregister_window(window); - if (err) - dev_err(scif_info.mdev.this_device, - "%s %d err %d\n", - __func__, __LINE__, err); - /* - * Need to restart list traversal if there has been - * an asynchronous list entry deletion. - */ - if (READ_ONCE(ep->rma_info.async_list_del)) - goto retry; - } - mutex_unlock(&ep->rma_info.rma_lock); - if (!list_empty(&ep->rma_info.mmn_list)) { - spin_lock(&scif_info.rmalock); - list_add_tail(&ep->mmu_list, &scif_info.mmu_notif_cleanup); - spin_unlock(&scif_info.rmalock); - schedule_work(&scif_info.mmu_notif_work); - } - return err; -} diff --git a/drivers/misc/mic/scif/scif_rma_list.h b/drivers/misc/mic/scif/scif_rma_list.h deleted file mode 100644 index 0f8e0ed65614..000000000000 --- a/drivers/misc/mic/scif/scif_rma_list.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2015 Intel Corporation. - * - * Intel SCIF driver. - */ -#ifndef SCIF_RMA_LIST_H -#define SCIF_RMA_LIST_H - -/* - * struct scif_rma_req - Self Registration list RMA Request query - * - * @out_window - Returns the window if found - * @offset: Starting offset - * @nr_bytes: number of bytes - * @prot: protection requested i.e. read or write or both - * @type: Specify single, partial or multiple windows - * @head: Head of list on which to search - * @va_for_temp: VA for searching temporary cached windows - */ -struct scif_rma_req { - struct scif_window **out_window; - union { - s64 offset; - unsigned long va_for_temp; - }; - size_t nr_bytes; - int prot; - enum scif_window_type type; - struct list_head *head; -}; - -/* Insert */ -void scif_insert_window(struct scif_window *window, struct list_head *head); -void scif_insert_tcw(struct scif_window *window, - struct list_head *head); -/* Query */ -int scif_query_window(struct scif_rma_req *request); -int scif_query_tcw(struct scif_endpt *ep, struct scif_rma_req *request); -/* Called from close to unregister all self windows */ -int scif_unregister_all_windows(scif_epd_t epd); -void scif_unmap_all_windows(scif_epd_t epd); -/* Traverse list and unregister */ -int scif_rma_list_unregister(struct scif_window *window, s64 offset, - int nr_pages); -#endif /* SCIF_RMA_LIST_H */ diff --git a/drivers/misc/mic/vop/Makefile b/drivers/misc/mic/vop/Makefile deleted file mode 100644 index 51b9b0022786..000000000000 --- a/drivers/misc/mic/vop/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile - Intel MIC Linux driver. -# Copyright(c) 2016, Intel Corporation. -# -obj-$(CONFIG_VOP) := vop.o - -vop-objs += vop_main.o -vop-objs += vop_debugfs.o -vop-objs += vop_vringh.o diff --git a/drivers/misc/mic/vop/vop_debugfs.c b/drivers/misc/mic/vop/vop_debugfs.c deleted file mode 100644 index 9d4f175f4dd1..000000000000 --- a/drivers/misc/mic/vop/vop_debugfs.c +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio Over PCIe (VOP) driver. - */ -#include -#include - -#include "vop_main.h" - -static int vop_dp_show(struct seq_file *s, void *pos) -{ - struct mic_device_desc *d; - struct mic_device_ctrl *dc; - struct mic_vqconfig *vqconfig; - __u32 *features; - __u8 *config; - struct vop_info *vi = s->private; - struct vop_device *vpdev = vi->vpdev; - struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); - int j, k; - - seq_printf(s, "Bootparam: magic 0x%x\n", - bootparam->magic); - seq_printf(s, "Bootparam: h2c_config_db %d\n", - bootparam->h2c_config_db); - seq_printf(s, "Bootparam: node_id %d\n", - bootparam->node_id); - seq_printf(s, "Bootparam: c2h_scif_db %d\n", - bootparam->c2h_scif_db); - seq_printf(s, "Bootparam: h2c_scif_db %d\n", - bootparam->h2c_scif_db); - seq_printf(s, "Bootparam: scif_host_dma_addr 0x%llx\n", - bootparam->scif_host_dma_addr); - seq_printf(s, "Bootparam: scif_card_dma_addr 0x%llx\n", - bootparam->scif_card_dma_addr); - - for (j = sizeof(*bootparam); - j < MIC_DP_SIZE; j += mic_total_desc_size(d)) { - d = (void *)bootparam + j; - dc = (void *)d + mic_aligned_desc_size(d); - - /* end of list */ - if (d->type == 0) - break; - - if (d->type == -1) - continue; - - seq_printf(s, "Type %d ", d->type); - seq_printf(s, "Num VQ %d ", d->num_vq); - seq_printf(s, "Feature Len %d\n", d->feature_len); - seq_printf(s, "Config Len %d ", d->config_len); - seq_printf(s, "Shutdown Status %d\n", d->status); - - for (k = 0; k < d->num_vq; k++) { - vqconfig = mic_vq_config(d) + k; - seq_printf(s, "vqconfig[%d]: ", k); - seq_printf(s, "address 0x%llx ", - vqconfig->address); - seq_printf(s, "num %d ", vqconfig->num); - seq_printf(s, "used address 0x%llx\n", - vqconfig->used_address); - } - - features = (__u32 *)mic_vq_features(d); - seq_printf(s, "Features: Host 0x%x ", features[0]); - seq_printf(s, "Guest 0x%x\n", features[1]); - - config = mic_vq_configspace(d); - for (k = 0; k < d->config_len; k++) - seq_printf(s, "config[%d]=%d\n", k, config[k]); - - seq_puts(s, "Device control:\n"); - seq_printf(s, "Config Change %d ", dc->config_change); - seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); - seq_printf(s, "Guest Ack %d ", dc->guest_ack); - seq_printf(s, "Host ack %d\n", dc->host_ack); - seq_printf(s, "Used address updated %d ", - dc->used_address_updated); - seq_printf(s, "Vdev 0x%llx\n", dc->vdev); - seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); - seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); - } - schedule_work(&vi->hotplug_work); - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(vop_dp); - -static int vop_vdev_info_show(struct seq_file *s, void *unused) -{ - struct vop_info *vi = s->private; - struct list_head *pos, *tmp; - struct vop_vdev *vdev; - int i, j; - - mutex_lock(&vi->vop_mutex); - list_for_each_safe(pos, tmp, &vi->vdev_list) { - vdev = list_entry(pos, struct vop_vdev, list); - seq_printf(s, "VDEV type %d state %s in %ld out %ld in_dma %ld out_dma %ld\n", - vdev->virtio_id, - vop_vdevup(vdev) ? "UP" : "DOWN", - vdev->in_bytes, - vdev->out_bytes, - vdev->in_bytes_dma, - vdev->out_bytes_dma); - for (i = 0; i < MIC_MAX_VRINGS; i++) { - struct vring_desc *desc; - struct vring_avail *avail; - struct vring_used *used; - struct vop_vringh *vvr = &vdev->vvr[i]; - struct vringh *vrh = &vvr->vrh; - int num = vrh->vring.num; - - if (!num) - continue; - desc = vrh->vring.desc; - seq_printf(s, "vring i %d avail_idx %d", - i, vvr->vring.info->avail_idx & (num - 1)); - seq_printf(s, " vring i %d avail_idx %d\n", - i, vvr->vring.info->avail_idx); - seq_printf(s, "vrh i %d weak_barriers %d", - i, vrh->weak_barriers); - seq_printf(s, " last_avail_idx %d last_used_idx %d", - vrh->last_avail_idx, vrh->last_used_idx); - seq_printf(s, " completed %d\n", vrh->completed); - for (j = 0; j < num; j++) { - seq_printf(s, "desc[%d] addr 0x%llx len %d", - j, desc->addr, desc->len); - seq_printf(s, " flags 0x%x next %d\n", - desc->flags, desc->next); - desc++; - } - avail = vrh->vring.avail; - seq_printf(s, "avail flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, avail->flags), - vringh16_to_cpu(vrh, - avail->idx) & (num - 1)); - seq_printf(s, "avail flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, avail->flags), - vringh16_to_cpu(vrh, avail->idx)); - for (j = 0; j < num; j++) - seq_printf(s, "avail ring[%d] %d\n", - j, avail->ring[j]); - used = vrh->vring.used; - seq_printf(s, "used flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, used->flags), - vringh16_to_cpu(vrh, used->idx) & (num - 1)); - seq_printf(s, "used flags 0x%x idx %d\n", - vringh16_to_cpu(vrh, used->flags), - vringh16_to_cpu(vrh, used->idx)); - for (j = 0; j < num; j++) - seq_printf(s, "used ring[%d] id %d len %d\n", - j, vringh32_to_cpu(vrh, - used->ring[j].id), - vringh32_to_cpu(vrh, - used->ring[j].len)); - } - } - mutex_unlock(&vi->vop_mutex); - - return 0; -} - -DEFINE_SHOW_ATTRIBUTE(vop_vdev_info); - -void vop_init_debugfs(struct vop_info *vi) -{ - char name[16]; - - snprintf(name, sizeof(name), "%s%d", KBUILD_MODNAME, vi->vpdev->dnode); - vi->dbg = debugfs_create_dir(name, NULL); - debugfs_create_file("dp", 0444, vi->dbg, vi, &vop_dp_fops); - debugfs_create_file("vdev_info", 0444, vi->dbg, vi, &vop_vdev_info_fops); -} - -void vop_exit_debugfs(struct vop_info *vi) -{ - debugfs_remove_recursive(vi->dbg); -} diff --git a/drivers/misc/mic/vop/vop_main.c b/drivers/misc/mic/vop/vop_main.c deleted file mode 100644 index 714b94f42d38..000000000000 --- a/drivers/misc/mic/vop/vop_main.c +++ /dev/null @@ -1,784 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Adapted from: - * - * virtio for kvm on s390 - * - * Copyright IBM Corp. 2008 - * - * Author(s): Christian Borntraeger - * - * Intel Virtio Over PCIe (VOP) driver. - */ -#include -#include -#include -#include -#include - -#include "vop_main.h" - -#define VOP_MAX_VRINGS 4 - -/* - * _vop_vdev - Allocated per virtio device instance injected by the peer. - * - * @vdev: Virtio device - * @desc: Virtio device page descriptor - * @dc: Virtio device control - * @vpdev: VOP device which is the parent for this virtio device - * @vr: Buffer for accessing the VRING - * @used_virt: Virtual address of used ring - * @used: DMA address of used ring - * @used_size: Size of the used buffer - * @reset_done: Track whether VOP reset is complete - * @virtio_cookie: Cookie returned upon requesting a interrupt - * @c2h_vdev_db: The doorbell used by the guest to interrupt the host - * @h2c_vdev_db: The doorbell used by the host to interrupt the guest - * @dnode: The destination node - */ -struct _vop_vdev { - struct virtio_device vdev; - struct mic_device_desc __iomem *desc; - struct mic_device_ctrl __iomem *dc; - struct vop_device *vpdev; - void __iomem *vr[VOP_MAX_VRINGS]; - void *used_virt[VOP_MAX_VRINGS]; - dma_addr_t used[VOP_MAX_VRINGS]; - int used_size[VOP_MAX_VRINGS]; - struct completion reset_done; - struct mic_irq *virtio_cookie; - int c2h_vdev_db; - int h2c_vdev_db; - int dnode; -}; - -#define to_vopvdev(vd) container_of(vd, struct _vop_vdev, vdev) - -#define _vop_aligned_desc_size(d) __mic_align(_vop_desc_size(d), 8) - -/* Helper API to obtain the parent of the virtio device */ -static inline struct device *_vop_dev(struct _vop_vdev *vdev) -{ - return vdev->vdev.dev.parent; -} - -static inline unsigned _vop_desc_size(struct mic_device_desc __iomem *desc) -{ - return sizeof(*desc) - + ioread8(&desc->num_vq) * sizeof(struct mic_vqconfig) - + ioread8(&desc->feature_len) * 2 - + ioread8(&desc->config_len); -} - -static inline struct mic_vqconfig __iomem * -_vop_vq_config(struct mic_device_desc __iomem *desc) -{ - return (struct mic_vqconfig __iomem *)(desc + 1); -} - -static inline u8 __iomem * -_vop_vq_features(struct mic_device_desc __iomem *desc) -{ - return (u8 __iomem *)(_vop_vq_config(desc) + ioread8(&desc->num_vq)); -} - -static inline u8 __iomem * -_vop_vq_configspace(struct mic_device_desc __iomem *desc) -{ - return _vop_vq_features(desc) + ioread8(&desc->feature_len) * 2; -} - -static inline unsigned -_vop_total_desc_size(struct mic_device_desc __iomem *desc) -{ - return _vop_aligned_desc_size(desc) + sizeof(struct mic_device_ctrl); -} - -/* This gets the device's feature bits. */ -static u64 vop_get_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - u64 features = 0; - struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; - u8 __iomem *in_features = _vop_vq_features(desc); - int feature_len = ioread8(&desc->feature_len); - - bits = min_t(unsigned, feature_len, sizeof(vdev->features)) * 8; - for (i = 0; i < bits; i++) - if (ioread8(&in_features[i / 8]) & (BIT(i % 8))) - features |= BIT_ULL(i); - - return features; -} - -static void vop_transport_features(struct virtio_device *vdev) -{ - /* - * Packed ring isn't enabled on virtio_vop for now, - * because virtio_vop uses vring_new_virtqueue() which - * creates virtio rings on preallocated memory. - */ - __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED); - __virtio_set_bit(vdev, VIRTIO_F_ACCESS_PLATFORM); -} - -static int vop_finalize_features(struct virtio_device *vdev) -{ - unsigned int i, bits; - struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; - u8 feature_len = ioread8(&desc->feature_len); - /* Second half of bitmap is features we accept. */ - u8 __iomem *out_features = - _vop_vq_features(desc) + feature_len; - - /* Give virtio_ring a chance to accept features. */ - vring_transport_features(vdev); - - /* Give virtio_vop a chance to accept features. */ - vop_transport_features(vdev); - - memset_io(out_features, 0, feature_len); - bits = min_t(unsigned, feature_len, - sizeof(vdev->features)) * 8; - for (i = 0; i < bits; i++) { - if (__virtio_test_bit(vdev, i)) - iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), - &out_features[i / 8]); - } - return 0; -} - -/* - * Reading and writing elements in config space - */ -static void vop_get(struct virtio_device *vdev, unsigned int offset, - void *buf, unsigned len) -{ - struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; - - if (offset + len > ioread8(&desc->config_len)) - return; - memcpy_fromio(buf, _vop_vq_configspace(desc) + offset, len); -} - -static void vop_set(struct virtio_device *vdev, unsigned int offset, - const void *buf, unsigned len) -{ - struct mic_device_desc __iomem *desc = to_vopvdev(vdev)->desc; - - if (offset + len > ioread8(&desc->config_len)) - return; - memcpy_toio(_vop_vq_configspace(desc) + offset, buf, len); -} - -/* - * The operations to get and set the status word just access the status - * field of the device descriptor. set_status also interrupts the host - * to tell about status changes. - */ -static u8 vop_get_status(struct virtio_device *vdev) -{ - return ioread8(&to_vopvdev(vdev)->desc->status); -} - -static void vop_set_status(struct virtio_device *dev, u8 status) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct vop_device *vpdev = vdev->vpdev; - - if (!status) - return; - iowrite8(status, &vdev->desc->status); - vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); -} - -/* Inform host on a virtio device reset and wait for ack from host */ -static void vop_reset_inform_host(struct virtio_device *dev) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct mic_device_ctrl __iomem *dc = vdev->dc; - struct vop_device *vpdev = vdev->vpdev; - int retry; - - iowrite8(0, &dc->host_ack); - iowrite8(1, &dc->vdev_reset); - vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); - - /* Wait till host completes all card accesses and acks the reset */ - for (retry = 100; retry--;) { - if (ioread8(&dc->host_ack)) - break; - msleep(100); - } - - dev_dbg(_vop_dev(vdev), "%s: retry: %d\n", __func__, retry); - - /* Reset status to 0 in case we timed out */ - iowrite8(0, &vdev->desc->status); -} - -static void vop_reset(struct virtio_device *dev) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - - dev_dbg(_vop_dev(vdev), "%s: virtio id %d\n", - __func__, dev->id.device); - - vop_reset_inform_host(dev); - complete_all(&vdev->reset_done); -} - -/* - * The virtio_ring code calls this API when it wants to notify the Host. - */ -static bool vop_notify(struct virtqueue *vq) -{ - struct _vop_vdev *vdev = vq->priv; - struct vop_device *vpdev = vdev->vpdev; - - vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); - return true; -} - -static void vop_del_vq(struct virtqueue *vq, int n) -{ - struct _vop_vdev *vdev = to_vopvdev(vq->vdev); - struct vop_device *vpdev = vdev->vpdev; - - dma_unmap_single(&vpdev->dev, vdev->used[n], - vdev->used_size[n], DMA_BIDIRECTIONAL); - free_pages((unsigned long)vdev->used_virt[n], - get_order(vdev->used_size[n])); - vring_del_virtqueue(vq); - vpdev->hw_ops->unmap(vpdev, vdev->vr[n]); - vdev->vr[n] = NULL; -} - -static void vop_del_vqs(struct virtio_device *dev) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct virtqueue *vq, *n; - int idx = 0; - - dev_dbg(_vop_dev(vdev), "%s\n", __func__); - - list_for_each_entry_safe(vq, n, &dev->vqs, list) - vop_del_vq(vq, idx++); -} - -static struct virtqueue *vop_new_virtqueue(unsigned int index, - unsigned int num, - struct virtio_device *vdev, - bool context, - void *pages, - bool (*notify)(struct virtqueue *vq), - void (*callback)(struct virtqueue *vq), - const char *name, - void *used) -{ - bool weak_barriers = false; - struct vring vring; - - vring_init(&vring, num, pages, MIC_VIRTIO_RING_ALIGN); - vring.used = used; - - return __vring_new_virtqueue(index, vring, vdev, weak_barriers, context, - notify, callback, name); -} - -/* - * This routine will assign vring's allocated in host/io memory. Code in - * virtio_ring.c however continues to access this io memory as if it were local - * memory without io accessors. - */ -static struct virtqueue *vop_find_vq(struct virtio_device *dev, - unsigned index, - void (*callback)(struct virtqueue *vq), - const char *name, bool ctx) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct vop_device *vpdev = vdev->vpdev; - struct mic_vqconfig __iomem *vqconfig; - struct mic_vqconfig config; - struct virtqueue *vq; - void __iomem *va; - struct _mic_vring_info __iomem *info; - void *used; - int vr_size, _vr_size, err, magic; - u8 type = ioread8(&vdev->desc->type); - - if (index >= ioread8(&vdev->desc->num_vq)) - return ERR_PTR(-ENOENT); - - if (!name) - return ERR_PTR(-ENOENT); - - /* First assign the vring's allocated in host memory */ - vqconfig = _vop_vq_config(vdev->desc) + index; - memcpy_fromio(&config, vqconfig, sizeof(config)); - _vr_size = round_up(vring_size(le16_to_cpu(config.num), MIC_VIRTIO_RING_ALIGN), 4); - vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info)); - va = vpdev->hw_ops->remap(vpdev, le64_to_cpu(config.address), vr_size); - if (!va) - return ERR_PTR(-ENOMEM); - vdev->vr[index] = va; - memset_io(va, 0x0, _vr_size); - - info = va + _vr_size; - magic = ioread32(&info->magic); - - if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) { - err = -EIO; - goto unmap; - } - - vdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + - sizeof(struct vring_used_elem) * - le16_to_cpu(config.num)); - used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(vdev->used_size[index])); - vdev->used_virt[index] = used; - if (!used) { - err = -ENOMEM; - dev_err(_vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto unmap; - } - - vq = vop_new_virtqueue(index, le16_to_cpu(config.num), dev, ctx, - (void __force *)va, vop_notify, callback, - name, used); - if (!vq) { - err = -ENOMEM; - goto free_used; - } - - vdev->used[index] = dma_map_single(&vpdev->dev, used, - vdev->used_size[index], - DMA_BIDIRECTIONAL); - if (dma_mapping_error(&vpdev->dev, vdev->used[index])) { - err = -ENOMEM; - dev_err(_vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto del_vq; - } - writeq(vdev->used[index], &vqconfig->used_address); - - vq->priv = vdev; - return vq; -del_vq: - vring_del_virtqueue(vq); -free_used: - free_pages((unsigned long)used, - get_order(vdev->used_size[index])); -unmap: - vpdev->hw_ops->unmap(vpdev, vdev->vr[index]); - return ERR_PTR(err); -} - -static int vop_find_vqs(struct virtio_device *dev, unsigned nvqs, - struct virtqueue *vqs[], - vq_callback_t *callbacks[], - const char * const names[], const bool *ctx, - struct irq_affinity *desc) -{ - struct _vop_vdev *vdev = to_vopvdev(dev); - struct vop_device *vpdev = vdev->vpdev; - struct mic_device_ctrl __iomem *dc = vdev->dc; - int i, err, retry, queue_idx = 0; - - /* We must have this many virtqueues. */ - if (nvqs > ioread8(&vdev->desc->num_vq)) - return -ENOENT; - - for (i = 0; i < nvqs; ++i) { - if (!names[i]) { - vqs[i] = NULL; - continue; - } - - dev_dbg(_vop_dev(vdev), "%s: %d: %s\n", - __func__, i, names[i]); - vqs[i] = vop_find_vq(dev, queue_idx++, callbacks[i], names[i], - ctx ? ctx[i] : false); - if (IS_ERR(vqs[i])) { - err = PTR_ERR(vqs[i]); - goto error; - } - } - - iowrite8(1, &dc->used_address_updated); - /* - * Send an interrupt to the host to inform it that used - * rings have been re-assigned. - */ - vpdev->hw_ops->send_intr(vpdev, vdev->c2h_vdev_db); - for (retry = 100; --retry;) { - if (!ioread8(&dc->used_address_updated)) - break; - msleep(100); - } - - dev_dbg(_vop_dev(vdev), "%s: retry: %d\n", __func__, retry); - if (!retry) { - err = -ENODEV; - goto error; - } - - return 0; -error: - vop_del_vqs(dev); - return err; -} - -/* - * The config ops structure as defined by virtio config - */ -static const struct virtio_config_ops vop_vq_config_ops = { - .get_features = vop_get_features, - .finalize_features = vop_finalize_features, - .get = vop_get, - .set = vop_set, - .get_status = vop_get_status, - .set_status = vop_set_status, - .reset = vop_reset, - .find_vqs = vop_find_vqs, - .del_vqs = vop_del_vqs, -}; - -static irqreturn_t vop_virtio_intr_handler(int irq, void *data) -{ - struct _vop_vdev *vdev = data; - struct vop_device *vpdev = vdev->vpdev; - struct virtqueue *vq; - - vpdev->hw_ops->ack_interrupt(vpdev, vdev->h2c_vdev_db); - list_for_each_entry(vq, &vdev->vdev.vqs, list) - vring_interrupt(0, vq); - - return IRQ_HANDLED; -} - -static void vop_virtio_release_dev(struct device *_d) -{ - struct virtio_device *vdev = - container_of(_d, struct virtio_device, dev); - struct _vop_vdev *vop_vdev = - container_of(vdev, struct _vop_vdev, vdev); - - kfree(vop_vdev); -} - -/* - * adds a new device and register it with virtio - * appropriate drivers are loaded by the device model - */ -static int _vop_add_device(struct mic_device_desc __iomem *d, - unsigned int offset, struct vop_device *vpdev, - int dnode) -{ - struct _vop_vdev *vdev, *reg_dev = NULL; - int ret; - u8 type = ioread8(&d->type); - - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) - return -ENOMEM; - - vdev->vpdev = vpdev; - vdev->vdev.dev.parent = &vpdev->dev; - vdev->vdev.dev.release = vop_virtio_release_dev; - vdev->vdev.id.device = type; - vdev->vdev.config = &vop_vq_config_ops; - vdev->desc = d; - vdev->dc = (void __iomem *)d + _vop_aligned_desc_size(d); - vdev->dnode = dnode; - vdev->vdev.priv = (void *)(unsigned long)dnode; - init_completion(&vdev->reset_done); - - vdev->h2c_vdev_db = vpdev->hw_ops->next_db(vpdev); - vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, - vop_virtio_intr_handler, "virtio intr", - vdev, vdev->h2c_vdev_db); - if (IS_ERR(vdev->virtio_cookie)) { - ret = PTR_ERR(vdev->virtio_cookie); - goto kfree; - } - iowrite8((u8)vdev->h2c_vdev_db, &vdev->dc->h2c_vdev_db); - vdev->c2h_vdev_db = ioread8(&vdev->dc->c2h_vdev_db); - - ret = register_virtio_device(&vdev->vdev); - reg_dev = vdev; - if (ret) { - dev_err(_vop_dev(vdev), - "Failed to register vop device %u type %u\n", - offset, type); - goto free_irq; - } - writeq((unsigned long)vdev, &vdev->dc->vdev); - dev_dbg(_vop_dev(vdev), "%s: registered vop device %u type %u vdev %p\n", - __func__, offset, type, vdev); - - return 0; - -free_irq: - vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); -kfree: - if (reg_dev) - put_device(&vdev->vdev.dev); - else - kfree(vdev); - return ret; -} - -/* - * match for a vop device with a specific desc pointer - */ -static int vop_match_desc(struct device *dev, void *data) -{ - struct virtio_device *_dev = dev_to_virtio(dev); - struct _vop_vdev *vdev = to_vopvdev(_dev); - - return vdev->desc == (void __iomem *)data; -} - -static struct _vop_vdev *vop_dc_to_vdev(struct mic_device_ctrl __iomem *dc) -{ - return (struct _vop_vdev *)(unsigned long)readq(&dc->vdev); -} - -static void _vop_handle_config_change(struct mic_device_desc __iomem *d, - unsigned int offset, - struct vop_device *vpdev) -{ - struct mic_device_ctrl __iomem *dc - = (void __iomem *)d + _vop_aligned_desc_size(d); - struct _vop_vdev *vdev = vop_dc_to_vdev(dc); - - if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED) - return; - - dev_dbg(&vpdev->dev, "%s %d\n", __func__, __LINE__); - virtio_config_changed(&vdev->vdev); - iowrite8(1, &dc->guest_ack); -} - -/* - * removes a virtio device if a hot remove event has been - * requested by the host. - */ -static int _vop_remove_device(struct mic_device_desc __iomem *d, - unsigned int offset, struct vop_device *vpdev) -{ - struct mic_device_ctrl __iomem *dc - = (void __iomem *)d + _vop_aligned_desc_size(d); - struct _vop_vdev *vdev = vop_dc_to_vdev(dc); - u8 status; - int ret = -1; - - if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) { - struct device *dev = get_device(&vdev->vdev.dev); - - dev_dbg(&vpdev->dev, - "%s %d config_change %d type %d vdev %p\n", - __func__, __LINE__, - ioread8(&dc->config_change), ioread8(&d->type), vdev); - status = ioread8(&d->status); - reinit_completion(&vdev->reset_done); - unregister_virtio_device(&vdev->vdev); - vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); - iowrite8(-1, &dc->h2c_vdev_db); - if (status & VIRTIO_CONFIG_S_DRIVER_OK) - wait_for_completion(&vdev->reset_done); - put_device(dev); - iowrite8(1, &dc->guest_ack); - dev_dbg(&vpdev->dev, "%s %d guest_ack %d\n", - __func__, __LINE__, ioread8(&dc->guest_ack)); - iowrite8(-1, &d->type); - ret = 0; - } - return ret; -} - -#define REMOVE_DEVICES true - -static void _vop_scan_devices(void __iomem *dp, struct vop_device *vpdev, - bool remove, int dnode) -{ - s8 type; - unsigned int i; - struct mic_device_desc __iomem *d; - struct mic_device_ctrl __iomem *dc; - struct device *dev; - - for (i = sizeof(struct mic_bootparam); - i < MIC_DP_SIZE; i += _vop_total_desc_size(d)) { - d = dp + i; - dc = (void __iomem *)d + _vop_aligned_desc_size(d); - /* - * This read barrier is paired with the corresponding write - * barrier on the host which is inserted before adding or - * removing a virtio device descriptor, by updating the type. - */ - rmb(); - type = ioread8(&d->type); - - /* end of list */ - if (type == 0) - break; - - if (type == -1) - continue; - - /* device already exists */ - dev = device_find_child(&vpdev->dev, (void __force *)d, - vop_match_desc); - if (dev) { - if (remove) - iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, - &dc->config_change); - put_device(dev); - _vop_handle_config_change(d, i, vpdev); - _vop_remove_device(d, i, vpdev); - if (remove) { - iowrite8(0, &dc->config_change); - iowrite8(0, &dc->guest_ack); - } - continue; - } - - /* new device */ - dev_dbg(&vpdev->dev, "%s %d Adding new virtio device %p\n", - __func__, __LINE__, d); - if (!remove) - _vop_add_device(d, i, vpdev, dnode); - } -} - -static void vop_scan_devices(struct vop_info *vi, - struct vop_device *vpdev, bool remove) -{ - void __iomem *dp = vpdev->hw_ops->get_remote_dp(vpdev); - - if (!dp) - return; - mutex_lock(&vi->vop_mutex); - _vop_scan_devices(dp, vpdev, remove, vpdev->dnode); - mutex_unlock(&vi->vop_mutex); -} - -/* - * vop_hotplug_device tries to find changes in the device page. - */ -static void vop_hotplug_devices(struct work_struct *work) -{ - struct vop_info *vi = container_of(work, struct vop_info, - hotplug_work); - - vop_scan_devices(vi, vi->vpdev, !REMOVE_DEVICES); -} - -/* - * Interrupt handler for hot plug/config changes etc. - */ -static irqreturn_t vop_extint_handler(int irq, void *data) -{ - struct vop_info *vi = data; - struct mic_bootparam __iomem *bp; - struct vop_device *vpdev = vi->vpdev; - - bp = vpdev->hw_ops->get_remote_dp(vpdev); - dev_dbg(&vpdev->dev, "%s %d hotplug work\n", - __func__, __LINE__); - vpdev->hw_ops->ack_interrupt(vpdev, ioread8(&bp->h2c_config_db)); - schedule_work(&vi->hotplug_work); - return IRQ_HANDLED; -} - -static int vop_driver_probe(struct vop_device *vpdev) -{ - struct vop_info *vi; - int rc; - - vi = kzalloc(sizeof(*vi), GFP_KERNEL); - if (!vi) { - rc = -ENOMEM; - goto exit; - } - dev_set_drvdata(&vpdev->dev, vi); - vi->vpdev = vpdev; - - mutex_init(&vi->vop_mutex); - INIT_WORK(&vi->hotplug_work, vop_hotplug_devices); - if (vpdev->dnode) { - rc = vop_host_init(vi); - if (rc < 0) - goto free; - } else { - struct mic_bootparam __iomem *bootparam; - - vop_scan_devices(vi, vpdev, !REMOVE_DEVICES); - - vi->h2c_config_db = vpdev->hw_ops->next_db(vpdev); - vi->cookie = vpdev->hw_ops->request_irq(vpdev, - vop_extint_handler, - "virtio_config_intr", - vi, vi->h2c_config_db); - if (IS_ERR(vi->cookie)) { - rc = PTR_ERR(vi->cookie); - goto free; - } - bootparam = vpdev->hw_ops->get_remote_dp(vpdev); - iowrite8(vi->h2c_config_db, &bootparam->h2c_config_db); - } - vop_init_debugfs(vi); - return 0; -free: - kfree(vi); -exit: - return rc; -} - -static void vop_driver_remove(struct vop_device *vpdev) -{ - struct vop_info *vi = dev_get_drvdata(&vpdev->dev); - - if (vpdev->dnode) { - vop_host_uninit(vi); - } else { - struct mic_bootparam __iomem *bootparam = - vpdev->hw_ops->get_remote_dp(vpdev); - if (bootparam) - iowrite8(-1, &bootparam->h2c_config_db); - vpdev->hw_ops->free_irq(vpdev, vi->cookie, vi); - flush_work(&vi->hotplug_work); - vop_scan_devices(vi, vpdev, REMOVE_DEVICES); - } - vop_exit_debugfs(vi); - kfree(vi); -} - -static const struct vop_device_id id_table[] = { - { VOP_DEV_TRNSP, VOP_DEV_ANY_ID }, - { 0 }, -}; - -static struct vop_driver vop_driver = { - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = vop_driver_probe, - .remove = vop_driver_remove, -}; - -module_vop_driver(vop_driver); - -MODULE_DEVICE_TABLE(mbus, id_table); -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("Intel(R) Virtio Over PCIe (VOP) driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/vop/vop_main.h b/drivers/misc/mic/vop/vop_main.h deleted file mode 100644 index 2451d9218137..000000000000 --- a/drivers/misc/mic/vop/vop_main.h +++ /dev/null @@ -1,158 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio Over PCIe (VOP) driver. - */ -#ifndef _VOP_MAIN_H_ -#define _VOP_MAIN_H_ - -#include -#include -#include -#include - -#include -#include "../common/mic_dev.h" - -#include "../bus/vop_bus.h" - -/* - * Note on endianness. - * 1. Host can be both BE or LE - * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail - * rings and ioreadXX/iowriteXX to access used ring. - * 3. Device page exposed by host to guest contains LE values. Guest - * accesses these using ioreadXX/iowriteXX etc. This way in general we - * obey the virtio spec according to which guest works with native - * endianness and host is aware of guest endianness and does all - * required endianness conversion. - * 4. Data provided from user space to guest (in ADD_DEVICE and - * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be - * in guest endianness. - */ - -/* - * vop_info - Allocated per invocation of VOP probe - * - * @vpdev: VOP device - * @hotplug_work: Handle virtio device creation, deletion and configuration - * @cookie: Cookie received upon requesting a virtio configuration interrupt - * @h2c_config_db: The doorbell used by the peer to indicate a config change - * @vdev_list: List of "active" virtio devices injected in the peer node - * @vop_mutex: Synchronize access to the device page as well as serialize - * creation/deletion of virtio devices on the peer node - * @dp: Peer device page information - * @dbg: Debugfs entry - * @dma_ch: The DMA channel used by this transport for data transfers. - * @name: Name for this transport used in misc device creation. - * @miscdev: The misc device registered. - */ -struct vop_info { - struct vop_device *vpdev; - struct work_struct hotplug_work; - struct mic_irq *cookie; - int h2c_config_db; - struct list_head vdev_list; - struct mutex vop_mutex; - void __iomem *dp; - struct dentry *dbg; - struct dma_chan *dma_ch; - char name[16]; - struct miscdevice miscdev; -}; - -/** - * struct vop_vringh - Virtio ring host information. - * - * @vring: The VOP vring used for setting up user space mappings. - * @vrh: The host VRINGH used for accessing the card vrings. - * @riov: The VRINGH read kernel IOV. - * @wiov: The VRINGH write kernel IOV. - * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). - * @vr_mutex: Mutex for synchronizing access to the VRING. - * @buf: Temporary kernel buffer used to copy in/out data - * from/to the card via DMA. - * @buf_da: dma address of buf. - * @vdev: Back pointer to VOP virtio device for vringh_notify(..). - */ -struct vop_vringh { - struct mic_vring vring; - struct vringh vrh; - struct vringh_kiov riov; - struct vringh_kiov wiov; - u16 head; - struct mutex vr_mutex; - void *buf; - dma_addr_t buf_da; - struct vop_vdev *vdev; -}; - -/** - * struct vop_vdev - Host information for a card Virtio device. - * - * @virtio_id - Virtio device id. - * @waitq - Waitqueue to allow ring3 apps to poll. - * @vpdev - pointer to VOP bus device. - * @poll_wake - Used for waking up threads blocked in poll. - * @out_bytes - Debug stats for number of bytes copied from host to card. - * @in_bytes - Debug stats for number of bytes copied from card to host. - * @out_bytes_dma - Debug stats for number of bytes copied from host to card - * using DMA. - * @in_bytes_dma - Debug stats for number of bytes copied from card to host - * using DMA. - * @tx_len_unaligned - Debug stats for number of bytes copied to the card where - * the transfer length did not have the required DMA alignment. - * @tx_dst_unaligned - Debug stats for number of bytes copied where the - * destination address on the card did not have the required DMA alignment. - * @vvr - Store per VRING data structures. - * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. - * @dd - Virtio device descriptor. - * @dc - Virtio device control fields. - * @list - List of Virtio devices. - * @virtio_db - The doorbell used by the card to interrupt the host. - * @virtio_cookie - The cookie returned while requesting interrupts. - * @vi: Transport information. - * @vdev_mutex: Mutex synchronizing virtio device injection, - * removal and data transfers. - * @destroy: Track if a virtio device is being destroyed. - * @deleted: The virtio device has been deleted. - */ -struct vop_vdev { - int virtio_id; - wait_queue_head_t waitq; - struct vop_device *vpdev; - int poll_wake; - unsigned long out_bytes; - unsigned long in_bytes; - unsigned long out_bytes_dma; - unsigned long in_bytes_dma; - unsigned long tx_len_unaligned; - unsigned long tx_dst_unaligned; - unsigned long rx_dst_unaligned; - struct vop_vringh vvr[MIC_MAX_VRINGS]; - struct work_struct virtio_bh_work; - struct mic_device_desc *dd; - struct mic_device_ctrl *dc; - struct list_head list; - int virtio_db; - struct mic_irq *virtio_cookie; - struct vop_info *vi; - struct mutex vdev_mutex; - struct completion destroy; - bool deleted; -}; - -/* Helper API to check if a virtio device is running */ -static inline bool vop_vdevup(struct vop_vdev *vdev) -{ - return !!vdev->dd->status; -} - -void vop_init_debugfs(struct vop_info *vi); -void vop_exit_debugfs(struct vop_info *vi); -int vop_host_init(struct vop_info *vi); -void vop_host_uninit(struct vop_info *vi); -#endif diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c deleted file mode 100644 index 7014ffe88632..000000000000 --- a/drivers/misc/mic/vop/vop_vringh.c +++ /dev/null @@ -1,1166 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2016 Intel Corporation. - * - * Intel Virtio Over PCIe (VOP) driver. - */ -#include -#include -#include - -#include -#include "../common/mic_dev.h" - -#include -#include "vop_main.h" - -/* Helper API to obtain the VOP PCIe device */ -static inline struct device *vop_dev(struct vop_vdev *vdev) -{ - return vdev->vpdev->dev.parent; -} - -/* Helper API to check if a virtio device is initialized */ -static inline int vop_vdev_inited(struct vop_vdev *vdev) -{ - if (!vdev) - return -EINVAL; - /* Device has not been created yet */ - if (!vdev->dd || !vdev->dd->type) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, -EINVAL); - return -EINVAL; - } - /* Device has been removed/deleted */ - if (vdev->dd->type == -1) { - dev_dbg(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, -ENODEV); - return -ENODEV; - } - return 0; -} - -static void _vop_notify(struct vringh *vrh) -{ - struct vop_vringh *vvrh = container_of(vrh, struct vop_vringh, vrh); - struct vop_vdev *vdev = vvrh->vdev; - struct vop_device *vpdev = vdev->vpdev; - s8 db = vdev->dc->h2c_vdev_db; - - if (db != -1) - vpdev->hw_ops->send_intr(vpdev, db); -} - -static void vop_virtio_init_post(struct vop_vdev *vdev) -{ - struct mic_vqconfig *vqconfig = mic_vq_config(vdev->dd); - struct vop_device *vpdev = vdev->vpdev; - int i, used_size; - - for (i = 0; i < vdev->dd->num_vq; i++) { - used_size = PAGE_ALIGN(sizeof(u16) * 3 + - sizeof(struct vring_used_elem) * - le16_to_cpu(vqconfig->num)); - if (!le64_to_cpu(vqconfig[i].used_address)) { - dev_warn(vop_dev(vdev), "used_address zero??\n"); - continue; - } - vdev->vvr[i].vrh.vring.used = - (void __force *)vpdev->hw_ops->remap( - vpdev, - le64_to_cpu(vqconfig[i].used_address), - used_size); - } - - vdev->dc->used_address_updated = 0; - - dev_info(vop_dev(vdev), "%s: device type %d LINKUP\n", - __func__, vdev->virtio_id); -} - -static inline void vop_virtio_device_reset(struct vop_vdev *vdev) -{ - int i; - - dev_dbg(vop_dev(vdev), "%s: status %d device type %d RESET\n", - __func__, vdev->dd->status, vdev->virtio_id); - - for (i = 0; i < vdev->dd->num_vq; i++) - /* - * Avoid lockdep false positive. The + 1 is for the vop - * mutex which is held in the reset devices code path. - */ - mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1); - - /* 0 status means "reset" */ - vdev->dd->status = 0; - vdev->dc->vdev_reset = 0; - vdev->dc->host_ack = 1; - - for (i = 0; i < vdev->dd->num_vq; i++) { - struct vringh *vrh = &vdev->vvr[i].vrh; - - vdev->vvr[i].vring.info->avail_idx = 0; - vrh->completed = 0; - vrh->last_avail_idx = 0; - vrh->last_used_idx = 0; - } - - for (i = 0; i < vdev->dd->num_vq; i++) - mutex_unlock(&vdev->vvr[i].vr_mutex); -} - -static void vop_virtio_reset_devices(struct vop_info *vi) -{ - struct list_head *pos, *tmp; - struct vop_vdev *vdev; - - list_for_each_safe(pos, tmp, &vi->vdev_list) { - vdev = list_entry(pos, struct vop_vdev, list); - vop_virtio_device_reset(vdev); - vdev->poll_wake = 1; - wake_up(&vdev->waitq); - } -} - -static void vop_bh_handler(struct work_struct *work) -{ - struct vop_vdev *vdev = container_of(work, struct vop_vdev, - virtio_bh_work); - - if (vdev->dc->used_address_updated) - vop_virtio_init_post(vdev); - - if (vdev->dc->vdev_reset) - vop_virtio_device_reset(vdev); - - vdev->poll_wake = 1; - wake_up(&vdev->waitq); -} - -static irqreturn_t _vop_virtio_intr_handler(int irq, void *data) -{ - struct vop_vdev *vdev = data; - struct vop_device *vpdev = vdev->vpdev; - - vpdev->hw_ops->ack_interrupt(vpdev, vdev->virtio_db); - schedule_work(&vdev->virtio_bh_work); - return IRQ_HANDLED; -} - -static int vop_virtio_config_change(struct vop_vdev *vdev, void *argp) -{ - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - int ret = 0, retry, i; - struct vop_device *vpdev = vdev->vpdev; - struct vop_info *vi = dev_get_drvdata(&vpdev->dev); - struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); - s8 db = bootparam->h2c_config_db; - - mutex_lock(&vi->vop_mutex); - for (i = 0; i < vdev->dd->num_vq; i++) - mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1); - - if (db == -1 || vdev->dd->type == -1) { - ret = -EIO; - goto exit; - } - - memcpy(mic_vq_configspace(vdev->dd), argp, vdev->dd->config_len); - vdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; - vpdev->hw_ops->send_intr(vpdev, db); - - for (retry = 100; retry--;) { - ret = wait_event_timeout(wake, vdev->dc->guest_ack, - msecs_to_jiffies(100)); - if (ret) - break; - } - - dev_dbg(vop_dev(vdev), - "%s %d retry: %d\n", __func__, __LINE__, retry); - vdev->dc->config_change = 0; - vdev->dc->guest_ack = 0; -exit: - for (i = 0; i < vdev->dd->num_vq; i++) - mutex_unlock(&vdev->vvr[i].vr_mutex); - mutex_unlock(&vi->vop_mutex); - return ret; -} - -static int vop_copy_dp_entry(struct vop_vdev *vdev, - struct mic_device_desc *argp, __u8 *type, - struct mic_device_desc **devpage) -{ - struct vop_device *vpdev = vdev->vpdev; - struct mic_device_desc *devp; - struct mic_vqconfig *vqconfig; - int ret = 0, i; - bool slot_found = false; - - vqconfig = mic_vq_config(argp); - for (i = 0; i < argp->num_vq; i++) { - if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { - ret = -EINVAL; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - } - - /* Find the first free device page entry */ - for (i = sizeof(struct mic_bootparam); - i < MIC_DP_SIZE - mic_total_desc_size(argp); - i += mic_total_desc_size(devp)) { - devp = vpdev->hw_ops->get_dp(vpdev) + i; - if (devp->type == 0 || devp->type == -1) { - slot_found = true; - break; - } - } - if (!slot_found) { - ret = -EINVAL; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto exit; - } - /* - * Save off the type before doing the memcpy. Type will be set in the - * end after completing all initialization for the new device. - */ - *type = argp->type; - argp->type = 0; - memcpy(devp, argp, mic_desc_size(argp)); - - *devpage = devp; -exit: - return ret; -} - -static void vop_init_device_ctrl(struct vop_vdev *vdev, - struct mic_device_desc *devpage) -{ - struct mic_device_ctrl *dc; - - dc = (void *)devpage + mic_aligned_desc_size(devpage); - - dc->config_change = 0; - dc->guest_ack = 0; - dc->vdev_reset = 0; - dc->host_ack = 0; - dc->used_address_updated = 0; - dc->c2h_vdev_db = -1; - dc->h2c_vdev_db = -1; - vdev->dc = dc; -} - -static int vop_virtio_add_device(struct vop_vdev *vdev, - struct mic_device_desc *argp) -{ - struct vop_info *vi = vdev->vi; - struct vop_device *vpdev = vi->vpdev; - struct mic_device_desc *dd = NULL; - struct mic_vqconfig *vqconfig; - int vr_size, i, j, ret; - u8 type = 0; - s8 db = -1; - char irqname[16]; - struct mic_bootparam *bootparam; - u16 num; - dma_addr_t vr_addr; - - bootparam = vpdev->hw_ops->get_dp(vpdev); - init_waitqueue_head(&vdev->waitq); - INIT_LIST_HEAD(&vdev->list); - vdev->vpdev = vpdev; - - ret = vop_copy_dp_entry(vdev, argp, &type, &dd); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - return ret; - } - - vop_init_device_ctrl(vdev, dd); - - vdev->dd = dd; - vdev->virtio_id = type; - vqconfig = mic_vq_config(dd); - INIT_WORK(&vdev->virtio_bh_work, vop_bh_handler); - - for (i = 0; i < dd->num_vq; i++) { - struct vop_vringh *vvr = &vdev->vvr[i]; - struct mic_vring *vr = &vdev->vvr[i].vring; - - num = le16_to_cpu(vqconfig[i].num); - mutex_init(&vvr->vr_mutex); - vr_size = PAGE_ALIGN(round_up(vring_size(num, MIC_VIRTIO_RING_ALIGN), 4) + - sizeof(struct _mic_vring_info)); - vr->va = (void *) - __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(vr_size)); - if (!vr->va) { - ret = -ENOMEM; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vr->len = vr_size; - vr->info = vr->va + round_up(vring_size(num, MIC_VIRTIO_RING_ALIGN), 4); - vr->info->magic = cpu_to_le32(MIC_MAGIC + vdev->virtio_id + i); - vr_addr = dma_map_single(&vpdev->dev, vr->va, vr_size, - DMA_BIDIRECTIONAL); - if (dma_mapping_error(&vpdev->dev, vr_addr)) { - free_pages((unsigned long)vr->va, get_order(vr_size)); - ret = -ENOMEM; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vqconfig[i].address = cpu_to_le64(vr_addr); - - vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); - ret = vringh_init_kern(&vvr->vrh, - *(u32 *)mic_vq_features(vdev->dd), - num, false, vr->vr.desc, vr->vr.avail, - vr->vr.used); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - goto err; - } - vringh_kiov_init(&vvr->riov, NULL, 0); - vringh_kiov_init(&vvr->wiov, NULL, 0); - vvr->head = USHRT_MAX; - vvr->vdev = vdev; - vvr->vrh.notify = _vop_notify; - dev_dbg(&vpdev->dev, - "%s %d index %d va %p info %p vr_size 0x%x\n", - __func__, __LINE__, i, vr->va, vr->info, vr_size); - vvr->buf = (void *)__get_free_pages(GFP_KERNEL, - get_order(VOP_INT_DMA_BUF_SIZE)); - vvr->buf_da = dma_map_single(&vpdev->dev, - vvr->buf, VOP_INT_DMA_BUF_SIZE, - DMA_BIDIRECTIONAL); - } - - snprintf(irqname, sizeof(irqname), "vop%dvirtio%d", vpdev->index, - vdev->virtio_id); - vdev->virtio_db = vpdev->hw_ops->next_db(vpdev); - vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev, - _vop_virtio_intr_handler, irqname, vdev, - vdev->virtio_db); - if (IS_ERR(vdev->virtio_cookie)) { - ret = PTR_ERR(vdev->virtio_cookie); - dev_dbg(&vpdev->dev, "request irq failed\n"); - goto err; - } - - vdev->dc->c2h_vdev_db = vdev->virtio_db; - - /* - * Order the type update with previous stores. This write barrier - * is paired with the corresponding read barrier before the uncached - * system memory read of the type, on the card while scanning the - * device page. - */ - smp_wmb(); - dd->type = type; - argp->type = type; - - if (bootparam) { - db = bootparam->h2c_config_db; - if (db != -1) - vpdev->hw_ops->send_intr(vpdev, db); - } - dev_dbg(&vpdev->dev, "Added virtio id %d db %d\n", dd->type, db); - return 0; -err: - vqconfig = mic_vq_config(dd); - for (j = 0; j < i; j++) { - struct vop_vringh *vvr = &vdev->vvr[j]; - - dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[j].address), - vvr->vring.len, DMA_BIDIRECTIONAL); - free_pages((unsigned long)vvr->vring.va, - get_order(vvr->vring.len)); - } - return ret; -} - -static void vop_dev_remove(struct vop_info *pvi, struct mic_device_ctrl *devp, - struct vop_device *vpdev) -{ - struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); - s8 db; - int ret, retry; - DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); - - devp->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; - db = bootparam->h2c_config_db; - if (db != -1) - vpdev->hw_ops->send_intr(vpdev, db); - else - goto done; - for (retry = 15; retry--;) { - ret = wait_event_timeout(wake, devp->guest_ack, - msecs_to_jiffies(1000)); - if (ret) - break; - } -done: - devp->config_change = 0; - devp->guest_ack = 0; -} - -static void vop_virtio_del_device(struct vop_vdev *vdev) -{ - struct vop_info *vi = vdev->vi; - struct vop_device *vpdev = vdev->vpdev; - int i; - struct mic_vqconfig *vqconfig; - struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev); - - if (!bootparam) - goto skip_hot_remove; - vop_dev_remove(vi, vdev->dc, vpdev); -skip_hot_remove: - vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev); - flush_work(&vdev->virtio_bh_work); - vqconfig = mic_vq_config(vdev->dd); - for (i = 0; i < vdev->dd->num_vq; i++) { - struct vop_vringh *vvr = &vdev->vvr[i]; - - dma_unmap_single(&vpdev->dev, - vvr->buf_da, VOP_INT_DMA_BUF_SIZE, - DMA_BIDIRECTIONAL); - free_pages((unsigned long)vvr->buf, - get_order(VOP_INT_DMA_BUF_SIZE)); - vringh_kiov_cleanup(&vvr->riov); - vringh_kiov_cleanup(&vvr->wiov); - dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[i].address), - vvr->vring.len, DMA_BIDIRECTIONAL); - free_pages((unsigned long)vvr->vring.va, - get_order(vvr->vring.len)); - } - /* - * Order the type update with previous stores. This write barrier - * is paired with the corresponding read barrier before the uncached - * system memory read of the type, on the card while scanning the - * device page. - */ - smp_wmb(); - vdev->dd->type = -1; -} - -/* - * vop_sync_dma - Wrapper for synchronous DMAs. - * - * @dev - The address of the pointer to the device instance used - * for DMA registration. - * @dst - destination DMA address. - * @src - source DMA address. - * @len - size of the transfer. - * - * Return DMA_SUCCESS on success - */ -static int vop_sync_dma(struct vop_vdev *vdev, dma_addr_t dst, dma_addr_t src, - size_t len) -{ - int err = 0; - struct dma_device *ddev; - struct dma_async_tx_descriptor *tx; - struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev); - struct dma_chan *vop_ch = vi->dma_ch; - - if (!vop_ch) { - err = -EBUSY; - goto error; - } - ddev = vop_ch->device; - tx = ddev->device_prep_dma_memcpy(vop_ch, dst, src, len, - DMA_PREP_FENCE); - if (!tx) { - err = -ENOMEM; - goto error; - } else { - dma_cookie_t cookie; - - cookie = tx->tx_submit(tx); - if (dma_submit_error(cookie)) { - err = -ENOMEM; - goto error; - } - dma_async_issue_pending(vop_ch); - err = dma_sync_wait(vop_ch, cookie); - } -error: - if (err) - dev_err(&vi->vpdev->dev, "%s %d err %d\n", - __func__, __LINE__, err); - return err; -} - -#define VOP_USE_DMA true - -/* - * Initiates the copies across the PCIe bus from card memory to a user - * space buffer. When transfers are done using DMA, source/destination - * addresses and transfer length must follow the alignment requirements of - * the MIC DMA engine. - */ -static int vop_virtio_copy_to_user(struct vop_vdev *vdev, void __user *ubuf, - size_t len, u64 daddr, size_t dlen, - int vr_idx) -{ - struct vop_device *vpdev = vdev->vpdev; - void __iomem *dbuf = vpdev->hw_ops->remap(vpdev, daddr, len); - struct vop_vringh *vvr = &vdev->vvr[vr_idx]; - struct vop_info *vi = dev_get_drvdata(&vpdev->dev); - size_t dma_alignment; - bool x200; - size_t dma_offset, partlen; - int err; - - if (!VOP_USE_DMA || !vi->dma_ch) { - if (copy_to_user(ubuf, (void __force *)dbuf, len)) { - err = -EFAULT; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - vdev->in_bytes += len; - err = 0; - goto err; - } - - dma_alignment = 1 << vi->dma_ch->device->copy_align; - x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1); - - dma_offset = daddr - round_down(daddr, dma_alignment); - daddr -= dma_offset; - len += dma_offset; - /* - * X100 uses DMA addresses as seen by the card so adding - * the aperture base is not required for DMA. However x200 - * requires DMA addresses to be an offset into the bar so - * add the aperture base for x200. - */ - if (x200) - daddr += vpdev->aper->pa; - while (len) { - partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); - err = vop_sync_dma(vdev, vvr->buf_da, daddr, - ALIGN(partlen, dma_alignment)); - if (err) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - if (copy_to_user(ubuf, vvr->buf + dma_offset, - partlen - dma_offset)) { - err = -EFAULT; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - daddr += partlen; - ubuf += partlen; - dbuf += partlen; - vdev->in_bytes_dma += partlen; - vdev->in_bytes += partlen; - len -= partlen; - dma_offset = 0; - } - err = 0; -err: - vpdev->hw_ops->unmap(vpdev, dbuf); - dev_dbg(vop_dev(vdev), - "%s: ubuf %p dbuf %p len 0x%zx vr_idx 0x%x\n", - __func__, ubuf, dbuf, len, vr_idx); - return err; -} - -/* - * Initiates copies across the PCIe bus from a user space buffer to card - * memory. When transfers are done using DMA, source/destination addresses - * and transfer length must follow the alignment requirements of the MIC - * DMA engine. - */ -static int vop_virtio_copy_from_user(struct vop_vdev *vdev, void __user *ubuf, - size_t len, u64 daddr, size_t dlen, - int vr_idx) -{ - struct vop_device *vpdev = vdev->vpdev; - void __iomem *dbuf = vpdev->hw_ops->remap(vpdev, daddr, len); - struct vop_vringh *vvr = &vdev->vvr[vr_idx]; - struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev); - size_t dma_alignment; - bool x200; - size_t partlen; - bool dma = VOP_USE_DMA && vi->dma_ch; - int err = 0; - size_t offset = 0; - - if (dma) { - dma_alignment = 1 << vi->dma_ch->device->copy_align; - x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1); - - if (daddr & (dma_alignment - 1)) { - vdev->tx_dst_unaligned += len; - dma = false; - } else if (ALIGN(len, dma_alignment) > dlen) { - vdev->tx_len_unaligned += len; - dma = false; - } - } - - if (!dma) - goto memcpy; - - /* - * X100 uses DMA addresses as seen by the card so adding - * the aperture base is not required for DMA. However x200 - * requires DMA addresses to be an offset into the bar so - * add the aperture base for x200. - */ - if (x200) - daddr += vpdev->aper->pa; - while (len) { - partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); - - if (copy_from_user(vvr->buf, ubuf, partlen)) { - err = -EFAULT; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - err = vop_sync_dma(vdev, daddr, vvr->buf_da, - ALIGN(partlen, dma_alignment)); - if (err) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - daddr += partlen; - ubuf += partlen; - dbuf += partlen; - vdev->out_bytes_dma += partlen; - vdev->out_bytes += partlen; - len -= partlen; - } -memcpy: - /* - * We are copying to IO below and should ideally use something - * like copy_from_user_toio(..) if it existed. - */ - while (len) { - partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE); - - if (copy_from_user(vvr->buf, ubuf + offset, partlen)) { - err = -EFAULT; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - memcpy_toio(dbuf + offset, vvr->buf, partlen); - offset += partlen; - vdev->out_bytes += partlen; - len -= partlen; - } - err = 0; -err: - vpdev->hw_ops->unmap(vpdev, dbuf); - dev_dbg(vop_dev(vdev), - "%s: ubuf %p dbuf %p len 0x%zx vr_idx 0x%x\n", - __func__, ubuf, dbuf, len, vr_idx); - return err; -} - -#define MIC_VRINGH_READ true - -/* Determine the total number of bytes consumed in a VRINGH KIOV */ -static inline u32 vop_vringh_iov_consumed(struct vringh_kiov *iov) -{ - int i; - u32 total = iov->consumed; - - for (i = 0; i < iov->i; i++) - total += iov->iov[i].iov_len; - return total; -} - -/* - * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. - * This API is heavily based on the vringh_iov_xfer(..) implementation - * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) - * and vringh_iov_push_kern(..) directly is because there is no - * way to override the VRINGH xfer(..) routines as of v3.10. - */ -static int vop_vringh_copy(struct vop_vdev *vdev, struct vringh_kiov *iov, - void __user *ubuf, size_t len, bool read, int vr_idx, - size_t *out_len) -{ - int ret = 0; - size_t partlen, tot_len = 0; - - while (len && iov->i < iov->used) { - struct kvec *kiov = &iov->iov[iov->i]; - unsigned long daddr = (unsigned long)kiov->iov_base; - - partlen = min(kiov->iov_len, len); - if (read) - ret = vop_virtio_copy_to_user(vdev, ubuf, partlen, - daddr, - kiov->iov_len, - vr_idx); - else - ret = vop_virtio_copy_from_user(vdev, ubuf, partlen, - daddr, - kiov->iov_len, - vr_idx); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= partlen; - ubuf += partlen; - tot_len += partlen; - iov->consumed += partlen; - kiov->iov_len -= partlen; - kiov->iov_base += partlen; - if (!kiov->iov_len) { - /* Fix up old iov element then increment. */ - kiov->iov_len = iov->consumed; - kiov->iov_base -= iov->consumed; - - iov->consumed = 0; - iov->i++; - } - } - *out_len = tot_len; - return ret; -} - -/* - * Use the standard VRINGH infrastructure in the kernel to fetch new - * descriptors, initiate the copies and update the used ring. - */ -static int _vop_virtio_copy(struct vop_vdev *vdev, struct mic_copy_desc *copy) -{ - int ret = 0; - u32 iovcnt = copy->iovcnt; - struct iovec iov; - struct iovec __user *u_iov = copy->iov; - void __user *ubuf = NULL; - struct vop_vringh *vvr = &vdev->vvr[copy->vr_idx]; - struct vringh_kiov *riov = &vvr->riov; - struct vringh_kiov *wiov = &vvr->wiov; - struct vringh *vrh = &vvr->vrh; - u16 *head = &vvr->head; - struct mic_vring *vr = &vvr->vring; - size_t len = 0, out_len; - - copy->out_len = 0; - /* Fetch a new IOVEC if all previous elements have been processed */ - if (riov->i == riov->used && wiov->i == wiov->used) { - ret = vringh_getdesc_kern(vrh, riov, wiov, - head, GFP_KERNEL); - /* Check if there are available descriptors */ - if (ret <= 0) - return ret; - } - while (iovcnt) { - if (!len) { - /* Copy over a new iovec from user space. */ - ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); - if (ret) { - ret = -EINVAL; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len = iov.iov_len; - ubuf = iov.iov_base; - } - /* Issue all the read descriptors first */ - ret = vop_vringh_copy(vdev, riov, ubuf, len, - MIC_VRINGH_READ, copy->vr_idx, &out_len); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= out_len; - ubuf += out_len; - copy->out_len += out_len; - /* Issue the write descriptors next */ - ret = vop_vringh_copy(vdev, wiov, ubuf, len, - !MIC_VRINGH_READ, copy->vr_idx, &out_len); - if (ret) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, ret); - break; - } - len -= out_len; - ubuf += out_len; - copy->out_len += out_len; - if (!len) { - /* One user space iovec is now completed */ - iovcnt--; - u_iov++; - } - /* Exit loop if all elements in KIOVs have been processed. */ - if (riov->i == riov->used && wiov->i == wiov->used) - break; - } - /* - * Update the used ring if a descriptor was available and some data was - * copied in/out and the user asked for a used ring update. - */ - if (*head != USHRT_MAX && copy->out_len && copy->update_used) { - u32 total = 0; - - /* Determine the total data consumed */ - total += vop_vringh_iov_consumed(riov); - total += vop_vringh_iov_consumed(wiov); - vringh_complete_kern(vrh, *head, total); - *head = USHRT_MAX; - if (vringh_need_notify_kern(vrh) > 0) - vringh_notify(vrh); - vringh_kiov_cleanup(riov); - vringh_kiov_cleanup(wiov); - /* Update avail idx for user space */ - vr->info->avail_idx = vrh->last_avail_idx; - } - return ret; -} - -static inline int vop_verify_copy_args(struct vop_vdev *vdev, - struct mic_copy_desc *copy) -{ - if (!vdev || copy->vr_idx >= vdev->dd->num_vq) - return -EINVAL; - return 0; -} - -/* Copy a specified number of virtio descriptors in a chain */ -static int vop_virtio_copy_desc(struct vop_vdev *vdev, - struct mic_copy_desc *copy) -{ - int err; - struct vop_vringh *vvr; - - err = vop_verify_copy_args(vdev, copy); - if (err) - return err; - - vvr = &vdev->vvr[copy->vr_idx]; - mutex_lock(&vvr->vr_mutex); - if (!vop_vdevup(vdev)) { - err = -ENODEV; - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - goto err; - } - err = _vop_virtio_copy(vdev, copy); - if (err) { - dev_err(vop_dev(vdev), "%s %d err %d\n", - __func__, __LINE__, err); - } -err: - mutex_unlock(&vvr->vr_mutex); - return err; -} - -static int vop_open(struct inode *inode, struct file *f) -{ - struct vop_vdev *vdev; - struct vop_info *vi = container_of(f->private_data, - struct vop_info, miscdev); - - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) - return -ENOMEM; - vdev->vi = vi; - mutex_init(&vdev->vdev_mutex); - f->private_data = vdev; - init_completion(&vdev->destroy); - complete(&vdev->destroy); - return 0; -} - -static int vop_release(struct inode *inode, struct file *f) -{ - struct vop_vdev *vdev = f->private_data, *vdev_tmp; - struct vop_info *vi = vdev->vi; - struct list_head *pos, *tmp; - bool found = false; - - mutex_lock(&vdev->vdev_mutex); - if (vdev->deleted) - goto unlock; - mutex_lock(&vi->vop_mutex); - list_for_each_safe(pos, tmp, &vi->vdev_list) { - vdev_tmp = list_entry(pos, struct vop_vdev, list); - if (vdev == vdev_tmp) { - vop_virtio_del_device(vdev); - list_del(pos); - found = true; - break; - } - } - mutex_unlock(&vi->vop_mutex); -unlock: - mutex_unlock(&vdev->vdev_mutex); - if (!found) - wait_for_completion(&vdev->destroy); - f->private_data = NULL; - kfree(vdev); - return 0; -} - -static long vop_ioctl(struct file *f, unsigned int cmd, unsigned long arg) -{ - struct vop_vdev *vdev = f->private_data; - struct vop_info *vi = vdev->vi; - void __user *argp = (void __user *)arg; - int ret; - - switch (cmd) { - case MIC_VIRTIO_ADD_DEVICE: - { - struct mic_device_desc dd, *dd_config; - - if (copy_from_user(&dd, argp, sizeof(dd))) - return -EFAULT; - - if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || - dd.num_vq > MIC_MAX_VRINGS) - return -EINVAL; - - dd_config = memdup_user(argp, mic_desc_size(&dd)); - if (IS_ERR(dd_config)) - return PTR_ERR(dd_config); - - /* Ensure desc has not changed between the two reads */ - if (memcmp(&dd, dd_config, sizeof(dd))) { - ret = -EINVAL; - goto free_ret; - } - mutex_lock(&vdev->vdev_mutex); - mutex_lock(&vi->vop_mutex); - ret = vop_virtio_add_device(vdev, dd_config); - if (ret) - goto unlock_ret; - list_add_tail(&vdev->list, &vi->vdev_list); -unlock_ret: - mutex_unlock(&vi->vop_mutex); - mutex_unlock(&vdev->vdev_mutex); -free_ret: - kfree(dd_config); - return ret; - } - case MIC_VIRTIO_COPY_DESC: - { - struct mic_copy_desc copy; - - mutex_lock(&vdev->vdev_mutex); - ret = vop_vdev_inited(vdev); - if (ret) - goto _unlock_ret; - - if (copy_from_user(©, argp, sizeof(copy))) { - ret = -EFAULT; - goto _unlock_ret; - } - - ret = vop_virtio_copy_desc(vdev, ©); - if (ret < 0) - goto _unlock_ret; - if (copy_to_user( - &((struct mic_copy_desc __user *)argp)->out_len, - ©.out_len, sizeof(copy.out_len))) - ret = -EFAULT; -_unlock_ret: - mutex_unlock(&vdev->vdev_mutex); - return ret; - } - case MIC_VIRTIO_CONFIG_CHANGE: - { - void *buf; - - mutex_lock(&vdev->vdev_mutex); - ret = vop_vdev_inited(vdev); - if (ret) - goto __unlock_ret; - buf = memdup_user(argp, vdev->dd->config_len); - if (IS_ERR(buf)) { - ret = PTR_ERR(buf); - goto __unlock_ret; - } - ret = vop_virtio_config_change(vdev, buf); - kfree(buf); -__unlock_ret: - mutex_unlock(&vdev->vdev_mutex); - return ret; - } - default: - return -ENOIOCTLCMD; - }; - return 0; -} - -/* - * We return EPOLLIN | EPOLLOUT from poll when new buffers are enqueued, and - * not when previously enqueued buffers may be available. This means that - * in the card->host (TX) path, when userspace is unblocked by poll it - * must drain all available descriptors or it can stall. - */ -static __poll_t vop_poll(struct file *f, poll_table *wait) -{ - struct vop_vdev *vdev = f->private_data; - __poll_t mask = 0; - - mutex_lock(&vdev->vdev_mutex); - if (vop_vdev_inited(vdev)) { - mask = EPOLLERR; - goto done; - } - poll_wait(f, &vdev->waitq, wait); - if (vop_vdev_inited(vdev)) { - mask = EPOLLERR; - } else if (vdev->poll_wake) { - vdev->poll_wake = 0; - mask = EPOLLIN | EPOLLOUT; - } -done: - mutex_unlock(&vdev->vdev_mutex); - return mask; -} - -static inline int -vop_query_offset(struct vop_vdev *vdev, unsigned long offset, - unsigned long *size, unsigned long *pa) -{ - struct vop_device *vpdev = vdev->vpdev; - unsigned long start = MIC_DP_SIZE; - int i; - - /* - * MMAP interface is as follows: - * offset region - * 0x0 virtio device_page - * 0x1000 first vring - * 0x1000 + size of 1st vring second vring - * .... - */ - if (!offset) { - *pa = virt_to_phys(vpdev->hw_ops->get_dp(vpdev)); - *size = MIC_DP_SIZE; - return 0; - } - - for (i = 0; i < vdev->dd->num_vq; i++) { - struct vop_vringh *vvr = &vdev->vvr[i]; - - if (offset == start) { - *pa = virt_to_phys(vvr->vring.va); - *size = vvr->vring.len; - return 0; - } - start += vvr->vring.len; - } - return -1; -} - -/* - * Maps the device page and virtio rings to user space for readonly access. - */ -static int vop_mmap(struct file *f, struct vm_area_struct *vma) -{ - struct vop_vdev *vdev = f->private_data; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; - int i, err; - - err = vop_vdev_inited(vdev); - if (err) - goto ret; - if (vma->vm_flags & VM_WRITE) { - err = -EACCES; - goto ret; - } - while (size_rem) { - i = vop_query_offset(vdev, offset, &size, &pa); - if (i < 0) { - err = -EINVAL; - goto ret; - } - err = remap_pfn_range(vma, vma->vm_start + offset, - pa >> PAGE_SHIFT, size, - vma->vm_page_prot); - if (err) - goto ret; - size_rem -= size; - offset += size; - } -ret: - return err; -} - -static const struct file_operations vop_fops = { - .open = vop_open, - .release = vop_release, - .unlocked_ioctl = vop_ioctl, - .poll = vop_poll, - .mmap = vop_mmap, - .owner = THIS_MODULE, -}; - -int vop_host_init(struct vop_info *vi) -{ - int rc; - struct miscdevice *mdev; - struct vop_device *vpdev = vi->vpdev; - - INIT_LIST_HEAD(&vi->vdev_list); - vi->dma_ch = vpdev->dma_ch; - mdev = &vi->miscdev; - mdev->minor = MISC_DYNAMIC_MINOR; - snprintf(vi->name, sizeof(vi->name), "vop_virtio%d", vpdev->index); - mdev->name = vi->name; - mdev->fops = &vop_fops; - mdev->parent = &vpdev->dev; - - rc = misc_register(mdev); - if (rc) - dev_err(&vpdev->dev, "%s failed rc %d\n", __func__, rc); - return rc; -} - -void vop_host_uninit(struct vop_info *vi) -{ - struct list_head *pos, *tmp; - struct vop_vdev *vdev; - - mutex_lock(&vi->vop_mutex); - vop_virtio_reset_devices(vi); - list_for_each_safe(pos, tmp, &vi->vdev_list) { - vdev = list_entry(pos, struct vop_vdev, list); - list_del(pos); - reinit_completion(&vdev->destroy); - mutex_unlock(&vi->vop_mutex); - mutex_lock(&vdev->vdev_mutex); - vop_virtio_del_device(vdev); - vdev->deleted = true; - mutex_unlock(&vdev->vdev_mutex); - complete(&vdev->destroy); - mutex_lock(&vi->vop_mutex); - } - mutex_unlock(&vi->vop_mutex); - misc_deregister(&vi->miscdev); -} diff --git a/include/linux/mic_bus.h b/include/linux/mic_bus.h deleted file mode 100644 index e99c789424e0..000000000000 --- a/include/linux/mic_bus.h +++ /dev/null @@ -1,100 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2014 Intel Corporation. - * - * Intel MIC Bus driver. - * - * This implementation is very similar to the virtio bus driver - * implementation @ include/linux/virtio.h. - */ -#ifndef _MIC_BUS_H_ -#define _MIC_BUS_H_ -/* - * Everything a mbus driver needs to work with any particular mbus - * implementation. - */ -#include -#include - -struct mbus_device_id { - __u32 device; - __u32 vendor; -}; - -#define MBUS_DEV_DMA_HOST 2 -#define MBUS_DEV_DMA_MIC 3 -#define MBUS_DEV_ANY_ID 0xffffffff - -/** - * mbus_device - representation of a device using mbus - * @mmio_va: virtual address of mmio space - * @hw_ops: the hardware ops supported by this device. - * @id: the device type identification (used to match it with a driver). - * @dev: underlying device. - * be used to communicate with. - * @index: unique position on the mbus bus - */ -struct mbus_device { - void __iomem *mmio_va; - struct mbus_hw_ops *hw_ops; - struct mbus_device_id id; - struct device dev; - int index; -}; - -/** - * mbus_driver - operations for a mbus I/O driver - * @driver: underlying device driver (populate name and owner). - * @id_table: the ids serviced by this driver. - * @probe: the function to call when a device is found. Returns 0 or -errno. - * @remove: the function to call when a device is removed. - */ -struct mbus_driver { - struct device_driver driver; - const struct mbus_device_id *id_table; - int (*probe)(struct mbus_device *dev); - void (*scan)(struct mbus_device *dev); - void (*remove)(struct mbus_device *dev); -}; - -/** - * struct mic_irq - opaque pointer used as cookie - */ -struct mic_irq; - -/** - * mbus_hw_ops - Hardware operations for accessing a MIC device on the MIC bus. - */ -struct mbus_hw_ops { - struct mic_irq* (*request_threaded_irq)(struct mbus_device *mbdev, - irq_handler_t handler, - irq_handler_t thread_fn, - const char *name, void *data, - int intr_src); - void (*free_irq)(struct mbus_device *mbdev, - struct mic_irq *cookie, void *data); - void (*ack_interrupt)(struct mbus_device *mbdev, int num); -}; - -struct mbus_device * -mbus_register_device(struct device *pdev, int id, const struct dma_map_ops *dma_ops, - struct mbus_hw_ops *hw_ops, int index, - void __iomem *mmio_va); -void mbus_unregister_device(struct mbus_device *mbdev); - -int mbus_register_driver(struct mbus_driver *drv); -void mbus_unregister_driver(struct mbus_driver *drv); - -static inline struct mbus_device *dev_to_mbus(struct device *_dev) -{ - return container_of(_dev, struct mbus_device, dev); -} - -static inline struct mbus_driver *drv_to_mbus(struct device_driver *drv) -{ - return container_of(drv, struct mbus_driver, driver); -} - -#endif /* _MIC_BUS_H */ diff --git a/include/linux/scif.h b/include/linux/scif.h deleted file mode 100644 index 329e695b8fe5..000000000000 --- a/include/linux/scif.h +++ /dev/null @@ -1,1339 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2014 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * BSD LICENSE - * - * Copyright(c) 2014 Intel Corporation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Intel SCIF driver. - * - */ -#ifndef __SCIF_H__ -#define __SCIF_H__ - -#include -#include -#include -#include - -#define SCIF_ACCEPT_SYNC 1 -#define SCIF_SEND_BLOCK 1 -#define SCIF_RECV_BLOCK 1 - -enum { - SCIF_PROT_READ = (1 << 0), - SCIF_PROT_WRITE = (1 << 1) -}; - -enum { - SCIF_MAP_FIXED = 0x10, - SCIF_MAP_KERNEL = 0x20, -}; - -enum { - SCIF_FENCE_INIT_SELF = (1 << 0), - SCIF_FENCE_INIT_PEER = (1 << 1), - SCIF_SIGNAL_LOCAL = (1 << 4), - SCIF_SIGNAL_REMOTE = (1 << 5) -}; - -enum { - SCIF_RMA_USECPU = (1 << 0), - SCIF_RMA_USECACHE = (1 << 1), - SCIF_RMA_SYNC = (1 << 2), - SCIF_RMA_ORDERED = (1 << 3) -}; - -/* End of SCIF Admin Reserved Ports */ -#define SCIF_ADMIN_PORT_END 1024 - -/* End of SCIF Reserved Ports */ -#define SCIF_PORT_RSVD 1088 - -typedef struct scif_endpt *scif_epd_t; -typedef struct scif_pinned_pages *scif_pinned_pages_t; - -/** - * struct scif_range - SCIF registered range used in kernel mode - * @cookie: cookie used internally by SCIF - * @nr_pages: number of pages of PAGE_SIZE - * @prot_flags: R/W protection - * @phys_addr: Array of bus addresses - * @va: Array of kernel virtual addresses backed by the pages in the phys_addr - * array. The va is populated only when called on the host for a remote - * SCIF connection on MIC. This is required to support the use case of DMA - * between MIC and another device which is not a SCIF node e.g., an IB or - * ethernet NIC. - */ -struct scif_range { - void *cookie; - int nr_pages; - int prot_flags; - dma_addr_t *phys_addr; - void __iomem **va; -}; - -/** - * struct scif_pollepd - SCIF endpoint to be monitored via scif_poll - * @epd: SCIF endpoint - * @events: requested events - * @revents: returned events - */ -struct scif_pollepd { - scif_epd_t epd; - __poll_t events; - __poll_t revents; -}; - -/** - * scif_peer_dev - representation of a peer SCIF device - * - * Peer devices show up as PCIe devices for the mgmt node but not the cards. - * The mgmt node discovers all the cards on the PCIe bus and informs the other - * cards about their peers. Upon notification of a peer a node adds a peer - * device to the peer bus to maintain symmetry in the way devices are - * discovered across all nodes in the SCIF network. - * - * @dev: underlying device - * @dnode - The destination node which this device will communicate with. - */ -struct scif_peer_dev { - struct device dev; - u8 dnode; -}; - -/** - * scif_client - representation of a SCIF client - * @name: client name - * @probe - client method called when a peer device is registered - * @remove - client method called when a peer device is unregistered - * @si - subsys_interface used internally for implementing SCIF clients - */ -struct scif_client { - const char *name; - void (*probe)(struct scif_peer_dev *spdev); - void (*remove)(struct scif_peer_dev *spdev); - struct subsys_interface si; -}; - -#define SCIF_OPEN_FAILED ((scif_epd_t)-1) -#define SCIF_REGISTER_FAILED ((off_t)-1) -#define SCIF_MMAP_FAILED ((void *)-1) - -/** - * scif_open() - Create an endpoint - * - * Return: - * Upon successful completion, scif_open() returns an endpoint descriptor to - * be used in subsequent SCIF functions calls to refer to that endpoint; - * otherwise in user mode SCIF_OPEN_FAILED (that is ((scif_epd_t)-1)) is - * returned and errno is set to indicate the error; in kernel mode a NULL - * scif_epd_t is returned. - * - * Errors: - * ENOMEM - Insufficient kernel memory was available - */ -scif_epd_t scif_open(void); - -/** - * scif_bind() - Bind an endpoint to a port - * @epd: endpoint descriptor - * @pn: port number - * - * scif_bind() binds endpoint epd to port pn, where pn is a port number on the - * local node. If pn is zero, a port number greater than or equal to - * SCIF_PORT_RSVD is assigned and returned. Each endpoint may be bound to - * exactly one local port. Ports less than 1024 when requested can only be bound - * by system (or root) processes or by processes executed by privileged users. - * - * Return: - * Upon successful completion, scif_bind() returns the port number to which epd - * is bound; otherwise in user mode -1 is returned and errno is set to - * indicate the error; in kernel mode the negative of one of the following - * errors is returned. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * EINVAL - the endpoint or the port is already bound - * EISCONN - The endpoint is already connected - * ENOSPC - No port number available for assignment - * EACCES - The port requested is protected and the user is not the superuser - */ -int scif_bind(scif_epd_t epd, u16 pn); - -/** - * scif_listen() - Listen for connections on an endpoint - * @epd: endpoint descriptor - * @backlog: maximum pending connection requests - * - * scif_listen() marks the endpoint epd as a listening endpoint - that is, as - * an endpoint that will be used to accept incoming connection requests. Once - * so marked, the endpoint is said to be in the listening state and may not be - * used as the endpoint of a connection. - * - * The endpoint, epd, must have been bound to a port. - * - * The backlog argument defines the maximum length to which the queue of - * pending connections for epd may grow. If a connection request arrives when - * the queue is full, the client may receive an error with an indication that - * the connection was refused. - * - * Return: - * Upon successful completion, scif_listen() returns 0; otherwise in user mode - * -1 is returned and errno is set to indicate the error; in kernel mode the - * negative of one of the following errors is returned. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * EINVAL - the endpoint is not bound to a port - * EISCONN - The endpoint is already connected or listening - */ -int scif_listen(scif_epd_t epd, int backlog); - -/** - * scif_connect() - Initiate a connection on a port - * @epd: endpoint descriptor - * @dst: global id of port to which to connect - * - * The scif_connect() function requests the connection of endpoint epd to remote - * port dst. If the connection is successful, a peer endpoint, bound to dst, is - * created on node dst.node. On successful return, the connection is complete. - * - * If the endpoint epd has not already been bound to a port, scif_connect() - * will bind it to an unused local port. - * - * A connection is terminated when an endpoint of the connection is closed, - * either explicitly by scif_close(), or when a process that owns one of the - * endpoints of the connection is terminated. - * - * In user space, scif_connect() supports an asynchronous connection mode - * if the application has set the O_NONBLOCK flag on the endpoint via the - * fcntl() system call. Setting this flag will result in the calling process - * not to wait during scif_connect(). - * - * Return: - * Upon successful completion, scif_connect() returns the port ID to which the - * endpoint, epd, is bound; otherwise in user mode -1 is returned and errno is - * set to indicate the error; in kernel mode the negative of one of the - * following errors is returned. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNREFUSED - The destination was not listening for connections or refused - * the connection request - * EINVAL - dst.port is not a valid port ID - * EISCONN - The endpoint is already connected - * ENOMEM - No buffer space is available - * ENODEV - The destination node does not exist, or the node is lost or existed, - * but is not currently in the network since it may have crashed - * ENOSPC - No port number available for assignment - * EOPNOTSUPP - The endpoint is listening and cannot be connected - */ -int scif_connect(scif_epd_t epd, struct scif_port_id *dst); - -/** - * scif_accept() - Accept a connection on an endpoint - * @epd: endpoint descriptor - * @peer: global id of port to which connected - * @newepd: new connected endpoint descriptor - * @flags: flags - * - * The scif_accept() call extracts the first connection request from the queue - * of pending connections for the port on which epd is listening. scif_accept() - * creates a new endpoint, bound to the same port as epd, and allocates a new - * SCIF endpoint descriptor, returned in newepd, for the endpoint. The new - * endpoint is connected to the endpoint through which the connection was - * requested. epd is unaffected by this call, and remains in the listening - * state. - * - * On successful return, peer holds the global port identifier (node id and - * local port number) of the port which requested the connection. - * - * A connection is terminated when an endpoint of the connection is closed, - * either explicitly by scif_close(), or when a process that owns one of the - * endpoints of the connection is terminated. - * - * The number of connections that can (subsequently) be accepted on epd is only - * limited by system resources (memory). - * - * The flags argument is formed by OR'ing together zero or more of the - * following values. - * SCIF_ACCEPT_SYNC - block until a connection request is presented. If - * SCIF_ACCEPT_SYNC is not in flags, and no pending - * connections are present on the queue, scif_accept() - * fails with an EAGAIN error - * - * In user mode, the select() and poll() functions can be used to determine - * when there is a connection request. In kernel mode, the scif_poll() - * function may be used for this purpose. A readable event will be delivered - * when a connection is requested. - * - * Return: - * Upon successful completion, scif_accept() returns 0; otherwise in user mode - * -1 is returned and errno is set to indicate the error; in kernel mode the - * negative of one of the following errors is returned. - * - * Errors: - * EAGAIN - SCIF_ACCEPT_SYNC is not set and no connections are present to be - * accepted or SCIF_ACCEPT_SYNC is not set and remote node failed to complete - * its connection request - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * EINTR - Interrupted function - * EINVAL - epd is not a listening endpoint, or flags is invalid, or peer is - * NULL, or newepd is NULL - * ENODEV - The requesting node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOMEM - Not enough space - * ENOENT - Secondary part of epd registration failed - */ -int scif_accept(scif_epd_t epd, struct scif_port_id *peer, scif_epd_t - *newepd, int flags); - -/** - * scif_close() - Close an endpoint - * @epd: endpoint descriptor - * - * scif_close() closes an endpoint and performs necessary teardown of - * facilities associated with that endpoint. - * - * If epd is a listening endpoint then it will no longer accept connection - * requests on the port to which it is bound. Any pending connection requests - * are rejected. - * - * If epd is a connected endpoint, then its peer endpoint is also closed. RMAs - * which are in-process through epd or its peer endpoint will complete before - * scif_close() returns. Registered windows of the local and peer endpoints are - * released as if scif_unregister() was called against each window. - * - * Closing a SCIF endpoint does not affect local registered memory mapped by - * a SCIF endpoint on a remote node. The local memory remains mapped by the peer - * SCIF endpoint explicitly removed by calling munmap(..) by the peer. - * - * If the peer endpoint's receive queue is not empty at the time that epd is - * closed, then the peer endpoint can be passed as the endpoint parameter to - * scif_recv() until the receive queue is empty. - * - * epd is freed and may no longer be accessed. - * - * Return: - * Upon successful completion, scif_close() returns 0; otherwise in user mode - * -1 is returned and errno is set to indicate the error; in kernel mode the - * negative of one of the following errors is returned. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - */ -int scif_close(scif_epd_t epd); - -/** - * scif_send() - Send a message - * @epd: endpoint descriptor - * @msg: message buffer address - * @len: message length - * @flags: blocking mode flags - * - * scif_send() sends data to the peer of endpoint epd. Up to len bytes of data - * are copied from memory starting at address msg. On successful execution the - * return value of scif_send() is the number of bytes that were sent, and is - * zero if no bytes were sent because len was zero. scif_send() may be called - * only when the endpoint is in a connected state. - * - * If a scif_send() call is non-blocking, then it sends only those bytes which - * can be sent without waiting, up to a maximum of len bytes. - * - * If a scif_send() call is blocking, then it normally returns after sending - * all len bytes. If a blocking call is interrupted or the connection is - * reset, the call is considered successful if some bytes were sent or len is - * zero, otherwise the call is considered unsuccessful. - * - * In user mode, the select() and poll() functions can be used to determine - * when the send queue is not full. In kernel mode, the scif_poll() function - * may be used for this purpose. - * - * It is recommended that scif_send()/scif_recv() only be used for short - * control-type message communication between SCIF endpoints. The SCIF RMA - * APIs are expected to provide better performance for transfer sizes of - * 1024 bytes or longer for the current MIC hardware and software - * implementation. - * - * scif_send() will block until the entire message is sent if SCIF_SEND_BLOCK - * is passed as the flags argument. - * - * Return: - * Upon successful completion, scif_send() returns the number of bytes sent; - * otherwise in user mode -1 is returned and errno is set to indicate the - * error; in kernel mode the negative of one of the following errors is - * returned. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - flags is invalid, or len is negative - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOMEM - Not enough space - * ENOTCONN - The endpoint is not connected - */ -int scif_send(scif_epd_t epd, void *msg, int len, int flags); - -/** - * scif_recv() - Receive a message - * @epd: endpoint descriptor - * @msg: message buffer address - * @len: message buffer length - * @flags: blocking mode flags - * - * scif_recv() receives data from the peer of endpoint epd. Up to len bytes of - * data are copied to memory starting at address msg. On successful execution - * the return value of scif_recv() is the number of bytes that were received, - * and is zero if no bytes were received because len was zero. scif_recv() may - * be called only when the endpoint is in a connected state. - * - * If a scif_recv() call is non-blocking, then it receives only those bytes - * which can be received without waiting, up to a maximum of len bytes. - * - * If a scif_recv() call is blocking, then it normally returns after receiving - * all len bytes. If the blocking call was interrupted due to a disconnection, - * subsequent calls to scif_recv() will copy all bytes received upto the point - * of disconnection. - * - * In user mode, the select() and poll() functions can be used to determine - * when data is available to be received. In kernel mode, the scif_poll() - * function may be used for this purpose. - * - * It is recommended that scif_send()/scif_recv() only be used for short - * control-type message communication between SCIF endpoints. The SCIF RMA - * APIs are expected to provide better performance for transfer sizes of - * 1024 bytes or longer for the current MIC hardware and software - * implementation. - * - * scif_recv() will block until the entire message is received if - * SCIF_RECV_BLOCK is passed as the flags argument. - * - * Return: - * Upon successful completion, scif_recv() returns the number of bytes - * received; otherwise in user mode -1 is returned and errno is set to - * indicate the error; in kernel mode the negative of one of the following - * errors is returned. - * - * Errors: - * EAGAIN - The destination node is returning from a low power state - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - flags is invalid, or len is negative - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOMEM - Not enough space - * ENOTCONN - The endpoint is not connected - */ -int scif_recv(scif_epd_t epd, void *msg, int len, int flags); - -/** - * scif_register() - Mark a memory region for remote access. - * @epd: endpoint descriptor - * @addr: starting virtual address - * @len: length of range - * @offset: offset of window - * @prot_flags: read/write protection flags - * @map_flags: mapping flags - * - * The scif_register() function opens a window, a range of whole pages of the - * registered address space of the endpoint epd, starting at offset po and - * continuing for len bytes. The value of po, further described below, is a - * function of the parameters offset and len, and the value of map_flags. Each - * page of the window represents the physical memory page which backs the - * corresponding page of the range of virtual address pages starting at addr - * and continuing for len bytes. addr and len are constrained to be multiples - * of the page size. A successful scif_register() call returns po. - * - * When SCIF_MAP_FIXED is set in the map_flags argument, po will be offset - * exactly, and offset is constrained to be a multiple of the page size. The - * mapping established by scif_register() will not replace any existing - * registration; an error is returned if any page within the range [offset, - * offset + len - 1] intersects an existing window. - * - * When SCIF_MAP_FIXED is not set, the implementation uses offset in an - * implementation-defined manner to arrive at po. The po value so chosen will - * be an area of the registered address space that the implementation deems - * suitable for a mapping of len bytes. An offset value of 0 is interpreted as - * granting the implementation complete freedom in selecting po, subject to - * constraints described below. A non-zero value of offset is taken to be a - * suggestion of an offset near which the mapping should be placed. When the - * implementation selects a value for po, it does not replace any extant - * window. In all cases, po will be a multiple of the page size. - * - * The physical pages which are so represented by a window are available for - * access in calls to mmap(), scif_readfrom(), scif_writeto(), - * scif_vreadfrom(), and scif_vwriteto(). While a window is registered, the - * physical pages represented by the window will not be reused by the memory - * subsystem for any other purpose. Note that the same physical page may be - * represented by multiple windows. - * - * Subsequent operations which change the memory pages to which virtual - * addresses are mapped (such as mmap(), munmap()) have no effect on - * existing window. - * - * If the process will fork(), it is recommended that the registered - * virtual address range be marked with MADV_DONTFORK. Doing so will prevent - * problems due to copy-on-write semantics. - * - * The prot_flags argument is formed by OR'ing together one or more of the - * following values. - * SCIF_PROT_READ - allow read operations from the window - * SCIF_PROT_WRITE - allow write operations to the window - * - * Return: - * Upon successful completion, scif_register() returns the offset at which the - * mapping was placed (po); otherwise in user mode SCIF_REGISTER_FAILED (that - * is (off_t *)-1) is returned and errno is set to indicate the error; in - * kernel mode the negative of one of the following errors is returned. - * - * Errors: - * EADDRINUSE - SCIF_MAP_FIXED is set in map_flags, and pages in the range - * [offset, offset + len -1] are already registered - * EAGAIN - The mapping could not be performed due to lack of resources - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - map_flags is invalid, or prot_flags is invalid, or SCIF_MAP_FIXED is - * set in flags, and offset is not a multiple of the page size, or addr is not a - * multiple of the page size, or len is not a multiple of the page size, or is - * 0, or offset is negative - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOMEM - Not enough space - * ENOTCONN -The endpoint is not connected - */ -off_t scif_register(scif_epd_t epd, void *addr, size_t len, off_t offset, - int prot_flags, int map_flags); - -/** - * scif_unregister() - Mark a memory region for remote access. - * @epd: endpoint descriptor - * @offset: start of range to unregister - * @len: length of range to unregister - * - * The scif_unregister() function closes those previously registered windows - * which are entirely within the range [offset, offset + len - 1]. It is an - * error to specify a range which intersects only a subrange of a window. - * - * On a successful return, pages within the window may no longer be specified - * in calls to mmap(), scif_readfrom(), scif_writeto(), scif_vreadfrom(), - * scif_vwriteto(), scif_get_pages, and scif_fence_signal(). The window, - * however, continues to exist until all previous references against it are - * removed. A window is referenced if there is a mapping to it created by - * mmap(), or if scif_get_pages() was called against the window - * (and the pages have not been returned via scif_put_pages()). A window is - * also referenced while an RMA, in which some range of the window is a source - * or destination, is in progress. Finally a window is referenced while some - * offset in that window was specified to scif_fence_signal(), and the RMAs - * marked by that call to scif_fence_signal() have not completed. While a - * window is in this state, its registered address space pages are not - * available for use in a new registered window. - * - * When all such references to the window have been removed, its references to - * all the physical pages which it represents are removed. Similarly, the - * registered address space pages of the window become available for - * registration in a new window. - * - * Return: - * Upon successful completion, scif_unregister() returns 0; otherwise in user - * mode -1 is returned and errno is set to indicate the error; in kernel mode - * the negative of one of the following errors is returned. In the event of an - * error, no windows are unregistered. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - the range [offset, offset + len - 1] intersects a subrange of a - * window, or offset is negative - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENXIO - Offsets in the range [offset, offset + len - 1] are invalid for the - * registered address space of epd - */ -int scif_unregister(scif_epd_t epd, off_t offset, size_t len); - -/** - * scif_readfrom() - Copy from a remote address space - * @epd: endpoint descriptor - * @loffset: offset in local registered address space to - * which to copy - * @len: length of range to copy - * @roffset: offset in remote registered address space - * from which to copy - * @rma_flags: transfer mode flags - * - * scif_readfrom() copies len bytes from the remote registered address space of - * the peer of endpoint epd, starting at the offset roffset to the local - * registered address space of epd, starting at the offset loffset. - * - * Each of the specified ranges [loffset, loffset + len - 1] and [roffset, - * roffset + len - 1] must be within some registered window or windows of the - * local and remote nodes. A range may intersect multiple registered windows, - * but only if those windows are contiguous in the registered address space. - * - * If rma_flags includes SCIF_RMA_USECPU, then the data is copied using - * programmed read/writes. Otherwise the data is copied using DMA. If rma_- - * flags includes SCIF_RMA_SYNC, then scif_readfrom() will return after the - * transfer is complete. Otherwise, the transfer may be performed asynchron- - * ously. The order in which any two asynchronous RMA operations complete - * is non-deterministic. The synchronization functions, scif_fence_mark()/ - * scif_fence_wait() and scif_fence_signal(), can be used to synchronize to - * the completion of asynchronous RMA operations on the same endpoint. - * - * The DMA transfer of individual bytes is not guaranteed to complete in - * address order. If rma_flags includes SCIF_RMA_ORDERED, then the last - * cacheline or partial cacheline of the source range will become visible on - * the destination node after all other transferred data in the source - * range has become visible on the destination node. - * - * The optimal DMA performance will likely be realized if both - * loffset and roffset are cacheline aligned (are a multiple of 64). Lower - * performance will likely be realized if loffset and roffset are not - * cacheline aligned but are separated by some multiple of 64. The lowest level - * of performance is likely if loffset and roffset are not separated by a - * multiple of 64. - * - * The rma_flags argument is formed by ORing together zero or more of the - * following values. - * SCIF_RMA_USECPU - perform the transfer using the CPU, otherwise use the DMA - * engine. - * SCIF_RMA_SYNC - perform the transfer synchronously, returning after the - * transfer has completed. Passing this flag results in the - * current implementation busy waiting and consuming CPU cycles - * while the DMA transfer is in progress for best performance by - * avoiding the interrupt latency. - * SCIF_RMA_ORDERED - ensure that the last cacheline or partial cacheline of - * the source range becomes visible on the destination node - * after all other transferred data in the source range has - * become visible on the destination - * - * Return: - * Upon successful completion, scif_readfrom() returns 0; otherwise in user - * mode -1 is returned and errno is set to indicate the error; in kernel mode - * the negative of one of the following errors is returned. - * - * Errors: - * EACCES - Attempt to write to a read-only range - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - rma_flags is invalid - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENXIO - The range [loffset, loffset + len - 1] is invalid for the registered - * address space of epd, or, The range [roffset, roffset + len - 1] is invalid - * for the registered address space of the peer of epd, or loffset or roffset - * is negative - */ -int scif_readfrom(scif_epd_t epd, off_t loffset, size_t len, off_t - roffset, int rma_flags); - -/** - * scif_writeto() - Copy to a remote address space - * @epd: endpoint descriptor - * @loffset: offset in local registered address space - * from which to copy - * @len: length of range to copy - * @roffset: offset in remote registered address space to - * which to copy - * @rma_flags: transfer mode flags - * - * scif_writeto() copies len bytes from the local registered address space of - * epd, starting at the offset loffset to the remote registered address space - * of the peer of endpoint epd, starting at the offset roffset. - * - * Each of the specified ranges [loffset, loffset + len - 1] and [roffset, - * roffset + len - 1] must be within some registered window or windows of the - * local and remote nodes. A range may intersect multiple registered windows, - * but only if those windows are contiguous in the registered address space. - * - * If rma_flags includes SCIF_RMA_USECPU, then the data is copied using - * programmed read/writes. Otherwise the data is copied using DMA. If rma_- - * flags includes SCIF_RMA_SYNC, then scif_writeto() will return after the - * transfer is complete. Otherwise, the transfer may be performed asynchron- - * ously. The order in which any two asynchronous RMA operations complete - * is non-deterministic. The synchronization functions, scif_fence_mark()/ - * scif_fence_wait() and scif_fence_signal(), can be used to synchronize to - * the completion of asynchronous RMA operations on the same endpoint. - * - * The DMA transfer of individual bytes is not guaranteed to complete in - * address order. If rma_flags includes SCIF_RMA_ORDERED, then the last - * cacheline or partial cacheline of the source range will become visible on - * the destination node after all other transferred data in the source - * range has become visible on the destination node. - * - * The optimal DMA performance will likely be realized if both - * loffset and roffset are cacheline aligned (are a multiple of 64). Lower - * performance will likely be realized if loffset and roffset are not cacheline - * aligned but are separated by some multiple of 64. The lowest level of - * performance is likely if loffset and roffset are not separated by a multiple - * of 64. - * - * The rma_flags argument is formed by ORing together zero or more of the - * following values. - * SCIF_RMA_USECPU - perform the transfer using the CPU, otherwise use the DMA - * engine. - * SCIF_RMA_SYNC - perform the transfer synchronously, returning after the - * transfer has completed. Passing this flag results in the - * current implementation busy waiting and consuming CPU cycles - * while the DMA transfer is in progress for best performance by - * avoiding the interrupt latency. - * SCIF_RMA_ORDERED - ensure that the last cacheline or partial cacheline of - * the source range becomes visible on the destination node - * after all other transferred data in the source range has - * become visible on the destination - * - * Return: - * Upon successful completion, scif_readfrom() returns 0; otherwise in user - * mode -1 is returned and errno is set to indicate the error; in kernel mode - * the negative of one of the following errors is returned. - * - * Errors: - * EACCES - Attempt to write to a read-only range - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - rma_flags is invalid - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENXIO - The range [loffset, loffset + len - 1] is invalid for the registered - * address space of epd, or, The range [roffset , roffset + len -1] is invalid - * for the registered address space of the peer of epd, or loffset or roffset - * is negative - */ -int scif_writeto(scif_epd_t epd, off_t loffset, size_t len, off_t - roffset, int rma_flags); - -/** - * scif_vreadfrom() - Copy from a remote address space - * @epd: endpoint descriptor - * @addr: address to which to copy - * @len: length of range to copy - * @roffset: offset in remote registered address space - * from which to copy - * @rma_flags: transfer mode flags - * - * scif_vreadfrom() copies len bytes from the remote registered address - * space of the peer of endpoint epd, starting at the offset roffset, to local - * memory, starting at addr. - * - * The specified range [roffset, roffset + len - 1] must be within some - * registered window or windows of the remote nodes. The range may - * intersect multiple registered windows, but only if those windows are - * contiguous in the registered address space. - * - * If rma_flags includes SCIF_RMA_USECPU, then the data is copied using - * programmed read/writes. Otherwise the data is copied using DMA. If rma_- - * flags includes SCIF_RMA_SYNC, then scif_vreadfrom() will return after the - * transfer is complete. Otherwise, the transfer may be performed asynchron- - * ously. The order in which any two asynchronous RMA operations complete - * is non-deterministic. The synchronization functions, scif_fence_mark()/ - * scif_fence_wait() and scif_fence_signal(), can be used to synchronize to - * the completion of asynchronous RMA operations on the same endpoint. - * - * The DMA transfer of individual bytes is not guaranteed to complete in - * address order. If rma_flags includes SCIF_RMA_ORDERED, then the last - * cacheline or partial cacheline of the source range will become visible on - * the destination node after all other transferred data in the source - * range has become visible on the destination node. - * - * If rma_flags includes SCIF_RMA_USECACHE, then the physical pages which back - * the specified local memory range may be remain in a pinned state even after - * the specified transfer completes. This may reduce overhead if some or all of - * the same virtual address range is referenced in a subsequent call of - * scif_vreadfrom() or scif_vwriteto(). - * - * The optimal DMA performance will likely be realized if both - * addr and roffset are cacheline aligned (are a multiple of 64). Lower - * performance will likely be realized if addr and roffset are not - * cacheline aligned but are separated by some multiple of 64. The lowest level - * of performance is likely if addr and roffset are not separated by a - * multiple of 64. - * - * The rma_flags argument is formed by ORing together zero or more of the - * following values. - * SCIF_RMA_USECPU - perform the transfer using the CPU, otherwise use the DMA - * engine. - * SCIF_RMA_USECACHE - enable registration caching - * SCIF_RMA_SYNC - perform the transfer synchronously, returning after the - * transfer has completed. Passing this flag results in the - * current implementation busy waiting and consuming CPU cycles - * while the DMA transfer is in progress for best performance by - * avoiding the interrupt latency. - * SCIF_RMA_ORDERED - ensure that the last cacheline or partial cacheline of - * the source range becomes visible on the destination node - * after all other transferred data in the source range has - * become visible on the destination - * - * Return: - * Upon successful completion, scif_vreadfrom() returns 0; otherwise in user - * mode -1 is returned and errno is set to indicate the error; in kernel mode - * the negative of one of the following errors is returned. - * - * Errors: - * EACCES - Attempt to write to a read-only range - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - rma_flags is invalid - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENXIO - Offsets in the range [roffset, roffset + len - 1] are invalid for the - * registered address space of epd - */ -int scif_vreadfrom(scif_epd_t epd, void *addr, size_t len, off_t roffset, - int rma_flags); - -/** - * scif_vwriteto() - Copy to a remote address space - * @epd: endpoint descriptor - * @addr: address from which to copy - * @len: length of range to copy - * @roffset: offset in remote registered address space to - * which to copy - * @rma_flags: transfer mode flags - * - * scif_vwriteto() copies len bytes from the local memory, starting at addr, to - * the remote registered address space of the peer of endpoint epd, starting at - * the offset roffset. - * - * The specified range [roffset, roffset + len - 1] must be within some - * registered window or windows of the remote nodes. The range may intersect - * multiple registered windows, but only if those windows are contiguous in the - * registered address space. - * - * If rma_flags includes SCIF_RMA_USECPU, then the data is copied using - * programmed read/writes. Otherwise the data is copied using DMA. If rma_- - * flags includes SCIF_RMA_SYNC, then scif_vwriteto() will return after the - * transfer is complete. Otherwise, the transfer may be performed asynchron- - * ously. The order in which any two asynchronous RMA operations complete - * is non-deterministic. The synchronization functions, scif_fence_mark()/ - * scif_fence_wait() and scif_fence_signal(), can be used to synchronize to - * the completion of asynchronous RMA operations on the same endpoint. - * - * The DMA transfer of individual bytes is not guaranteed to complete in - * address order. If rma_flags includes SCIF_RMA_ORDERED, then the last - * cacheline or partial cacheline of the source range will become visible on - * the destination node after all other transferred data in the source - * range has become visible on the destination node. - * - * If rma_flags includes SCIF_RMA_USECACHE, then the physical pages which back - * the specified local memory range may be remain in a pinned state even after - * the specified transfer completes. This may reduce overhead if some or all of - * the same virtual address range is referenced in a subsequent call of - * scif_vreadfrom() or scif_vwriteto(). - * - * The optimal DMA performance will likely be realized if both - * addr and offset are cacheline aligned (are a multiple of 64). Lower - * performance will likely be realized if addr and offset are not cacheline - * aligned but are separated by some multiple of 64. The lowest level of - * performance is likely if addr and offset are not separated by a multiple of - * 64. - * - * The rma_flags argument is formed by ORing together zero or more of the - * following values. - * SCIF_RMA_USECPU - perform the transfer using the CPU, otherwise use the DMA - * engine. - * SCIF_RMA_USECACHE - allow registration caching - * SCIF_RMA_SYNC - perform the transfer synchronously, returning after the - * transfer has completed. Passing this flag results in the - * current implementation busy waiting and consuming CPU cycles - * while the DMA transfer is in progress for best performance by - * avoiding the interrupt latency. - * SCIF_RMA_ORDERED - ensure that the last cacheline or partial cacheline of - * the source range becomes visible on the destination node - * after all other transferred data in the source range has - * become visible on the destination - * - * Return: - * Upon successful completion, scif_vwriteto() returns 0; otherwise in user - * mode -1 is returned and errno is set to indicate the error; in kernel mode - * the negative of one of the following errors is returned. - * - * Errors: - * EACCES - Attempt to write to a read-only range - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - rma_flags is invalid - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENXIO - Offsets in the range [roffset, roffset + len - 1] are invalid for the - * registered address space of epd - */ -int scif_vwriteto(scif_epd_t epd, void *addr, size_t len, off_t roffset, - int rma_flags); - -/** - * scif_fence_mark() - Mark previously issued RMAs - * @epd: endpoint descriptor - * @flags: control flags - * @mark: marked value returned as output. - * - * scif_fence_mark() returns after marking the current set of all uncompleted - * RMAs initiated through the endpoint epd or the current set of all - * uncompleted RMAs initiated through the peer of endpoint epd. The RMAs are - * marked with a value returned at mark. The application may subsequently call - * scif_fence_wait(), passing the value returned at mark, to await completion - * of all RMAs so marked. - * - * The flags argument has exactly one of the following values. - * SCIF_FENCE_INIT_SELF - RMA operations initiated through endpoint - * epd are marked - * SCIF_FENCE_INIT_PEER - RMA operations initiated through the peer - * of endpoint epd are marked - * - * Return: - * Upon successful completion, scif_fence_mark() returns 0; otherwise in user - * mode -1 is returned and errno is set to indicate the error; in kernel mode - * the negative of one of the following errors is returned. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - flags is invalid - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENOMEM - Insufficient kernel memory was available - */ -int scif_fence_mark(scif_epd_t epd, int flags, int *mark); - -/** - * scif_fence_wait() - Wait for completion of marked RMAs - * @epd: endpoint descriptor - * @mark: mark request - * - * scif_fence_wait() returns after all RMAs marked with mark have completed. - * The value passed in mark must have been obtained in a previous call to - * scif_fence_mark(). - * - * Return: - * Upon successful completion, scif_fence_wait() returns 0; otherwise in user - * mode -1 is returned and errno is set to indicate the error; in kernel mode - * the negative of one of the following errors is returned. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENOMEM - Insufficient kernel memory was available - */ -int scif_fence_wait(scif_epd_t epd, int mark); - -/** - * scif_fence_signal() - Request a memory update on completion of RMAs - * @epd: endpoint descriptor - * @loff: local offset - * @lval: local value to write to loffset - * @roff: remote offset - * @rval: remote value to write to roffset - * @flags: flags - * - * scif_fence_signal() returns after marking the current set of all uncompleted - * RMAs initiated through the endpoint epd or marking the current set of all - * uncompleted RMAs initiated through the peer of endpoint epd. - * - * If flags includes SCIF_SIGNAL_LOCAL, then on completion of the RMAs in the - * marked set, lval is written to memory at the address corresponding to offset - * loff in the local registered address space of epd. loff must be within a - * registered window. If flags includes SCIF_SIGNAL_REMOTE, then on completion - * of the RMAs in the marked set, rval is written to memory at the address - * corresponding to offset roff in the remote registered address space of epd. - * roff must be within a remote registered window of the peer of epd. Note - * that any specified offset must be DWORD (4 byte / 32 bit) aligned. - * - * The flags argument is formed by OR'ing together the following. - * Exactly one of the following values. - * SCIF_FENCE_INIT_SELF - RMA operations initiated through endpoint - * epd are marked - * SCIF_FENCE_INIT_PEER - RMA operations initiated through the peer - * of endpoint epd are marked - * One or more of the following values. - * SCIF_SIGNAL_LOCAL - On completion of the marked set of RMAs, write lval to - * memory at the address corresponding to offset loff in the local - * registered address space of epd. - * SCIF_SIGNAL_REMOTE - On completion of the marked set of RMAs, write rval to - * memory at the address corresponding to offset roff in the remote - * registered address space of epd. - * - * Return: - * Upon successful completion, scif_fence_signal() returns 0; otherwise in - * user mode -1 is returned and errno is set to indicate the error; in kernel - * mode the negative of one of the following errors is returned. - * - * Errors: - * EBADF, ENOTTY - epd is not a valid endpoint descriptor - * ECONNRESET - Connection reset by peer - * EINVAL - flags is invalid, or loff or roff are not DWORD aligned - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENXIO - loff is invalid for the registered address of epd, or roff is invalid - * for the registered address space, of the peer of epd - */ -int scif_fence_signal(scif_epd_t epd, off_t loff, u64 lval, off_t roff, - u64 rval, int flags); - -/** - * scif_get_node_ids() - Return information about online nodes - * @nodes: array in which to return online node IDs - * @len: number of entries in the nodes array - * @self: address to place the node ID of the local node - * - * scif_get_node_ids() fills in the nodes array with up to len node IDs of the - * nodes in the SCIF network. If there is not enough space in nodes, as - * indicated by the len parameter, only len node IDs are returned in nodes. The - * return value of scif_get_node_ids() is the total number of nodes currently in - * the SCIF network. By checking the return value against the len parameter, - * the user may determine if enough space for nodes was allocated. - * - * The node ID of the local node is returned at self. - * - * Return: - * Upon successful completion, scif_get_node_ids() returns the actual number of - * online nodes in the SCIF network including 'self'; otherwise in user mode - * -1 is returned and errno is set to indicate the error; in kernel mode no - * errors are returned. - */ -int scif_get_node_ids(u16 *nodes, int len, u16 *self); - -/** - * scif_pin_pages() - Pin a set of pages - * @addr: Virtual address of range to pin - * @len: Length of range to pin - * @prot_flags: Page protection flags - * @map_flags: Page classification flags - * @pinned_pages: Handle to pinned pages - * - * scif_pin_pages() pins (locks in physical memory) the physical pages which - * back the range of virtual address pages starting at addr and continuing for - * len bytes. addr and len are constrained to be multiples of the page size. A - * successful scif_pin_pages() call returns a handle to pinned_pages which may - * be used in subsequent calls to scif_register_pinned_pages(). - * - * The pages will remain pinned as long as there is a reference against the - * scif_pinned_pages_t value returned by scif_pin_pages() and until - * scif_unpin_pages() is called, passing the scif_pinned_pages_t value. A - * reference is added to a scif_pinned_pages_t value each time a window is - * created by calling scif_register_pinned_pages() and passing the - * scif_pinned_pages_t value. A reference is removed from a - * scif_pinned_pages_t value each time such a window is deleted. - * - * Subsequent operations which change the memory pages to which virtual - * addresses are mapped (such as mmap(), munmap()) have no effect on the - * scif_pinned_pages_t value or windows created against it. - * - * If the process will fork(), it is recommended that the registered - * virtual address range be marked with MADV_DONTFORK. Doing so will prevent - * problems due to copy-on-write semantics. - * - * The prot_flags argument is formed by OR'ing together one or more of the - * following values. - * SCIF_PROT_READ - allow read operations against the pages - * SCIF_PROT_WRITE - allow write operations against the pages - * The map_flags argument can be set as SCIF_MAP_KERNEL to interpret addr as a - * kernel space address. By default, addr is interpreted as a user space - * address. - * - * Return: - * Upon successful completion, scif_pin_pages() returns 0; otherwise the - * negative of one of the following errors is returned. - * - * Errors: - * EINVAL - prot_flags is invalid, map_flags is invalid, or offset is negative - * ENOMEM - Not enough space - */ -int scif_pin_pages(void *addr, size_t len, int prot_flags, int map_flags, - scif_pinned_pages_t *pinned_pages); - -/** - * scif_unpin_pages() - Unpin a set of pages - * @pinned_pages: Handle to pinned pages to be unpinned - * - * scif_unpin_pages() prevents scif_register_pinned_pages() from registering new - * windows against pinned_pages. The physical pages represented by pinned_pages - * will remain pinned until all windows previously registered against - * pinned_pages are deleted (the window is scif_unregister()'d and all - * references to the window are removed (see scif_unregister()). - * - * pinned_pages must have been obtain from a previous call to scif_pin_pages(). - * After calling scif_unpin_pages(), it is an error to pass pinned_pages to - * scif_register_pinned_pages(). - * - * Return: - * Upon successful completion, scif_unpin_pages() returns 0; otherwise the - * negative of one of the following errors is returned. - * - * Errors: - * EINVAL - pinned_pages is not valid - */ -int scif_unpin_pages(scif_pinned_pages_t pinned_pages); - -/** - * scif_register_pinned_pages() - Mark a memory region for remote access. - * @epd: endpoint descriptor - * @pinned_pages: Handle to pinned pages - * @offset: Registered address space offset - * @map_flags: Flags which control where pages are mapped - * - * The scif_register_pinned_pages() function opens a window, a range of whole - * pages of the registered address space of the endpoint epd, starting at - * offset po. The value of po, further described below, is a function of the - * parameters offset and pinned_pages, and the value of map_flags. Each page of - * the window represents a corresponding physical memory page of the range - * represented by pinned_pages; the length of the window is the same as the - * length of range represented by pinned_pages. A successful - * scif_register_pinned_pages() call returns po as the return value. - * - * When SCIF_MAP_FIXED is set in the map_flags argument, po will be offset - * exactly, and offset is constrained to be a multiple of the page size. The - * mapping established by scif_register_pinned_pages() will not replace any - * existing registration; an error is returned if any page of the new window - * would intersect an existing window. - * - * When SCIF_MAP_FIXED is not set, the implementation uses offset in an - * implementation-defined manner to arrive at po. The po so chosen will be an - * area of the registered address space that the implementation deems suitable - * for a mapping of the required size. An offset value of 0 is interpreted as - * granting the implementation complete freedom in selecting po, subject to - * constraints described below. A non-zero value of offset is taken to be a - * suggestion of an offset near which the mapping should be placed. When the - * implementation selects a value for po, it does not replace any extant - * window. In all cases, po will be a multiple of the page size. - * - * The physical pages which are so represented by a window are available for - * access in calls to scif_get_pages(), scif_readfrom(), scif_writeto(), - * scif_vreadfrom(), and scif_vwriteto(). While a window is registered, the - * physical pages represented by the window will not be reused by the memory - * subsystem for any other purpose. Note that the same physical page may be - * represented by multiple windows. - * - * Windows created by scif_register_pinned_pages() are unregistered by - * scif_unregister(). - * - * The map_flags argument can be set to SCIF_MAP_FIXED which interprets a - * fixed offset. - * - * Return: - * Upon successful completion, scif_register_pinned_pages() returns the offset - * at which the mapping was placed (po); otherwise the negative of one of the - * following errors is returned. - * - * Errors: - * EADDRINUSE - SCIF_MAP_FIXED is set in map_flags and pages in the new window - * would intersect an existing window - * EAGAIN - The mapping could not be performed due to lack of resources - * ECONNRESET - Connection reset by peer - * EINVAL - map_flags is invalid, or SCIF_MAP_FIXED is set in map_flags, and - * offset is not a multiple of the page size, or offset is negative - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOMEM - Not enough space - * ENOTCONN - The endpoint is not connected - */ -off_t scif_register_pinned_pages(scif_epd_t epd, - scif_pinned_pages_t pinned_pages, - off_t offset, int map_flags); - -/** - * scif_get_pages() - Add references to remote registered pages - * @epd: endpoint descriptor - * @offset: remote registered offset - * @len: length of range of pages - * @pages: returned scif_range structure - * - * scif_get_pages() returns the addresses of the physical pages represented by - * those pages of the registered address space of the peer of epd, starting at - * offset and continuing for len bytes. offset and len are constrained to be - * multiples of the page size. - * - * All of the pages in the specified range [offset, offset + len - 1] must be - * within a single window of the registered address space of the peer of epd. - * - * The addresses are returned as a virtually contiguous array pointed to by the - * phys_addr component of the scif_range structure whose address is returned in - * pages. The nr_pages component of scif_range is the length of the array. The - * prot_flags component of scif_range holds the protection flag value passed - * when the pages were registered. - * - * Each physical page whose address is returned by scif_get_pages() remains - * available and will not be released for reuse until the scif_range structure - * is returned in a call to scif_put_pages(). The scif_range structure returned - * by scif_get_pages() must be unmodified. - * - * It is an error to call scif_close() on an endpoint on which a scif_range - * structure of that endpoint has not been returned to scif_put_pages(). - * - * Return: - * Upon successful completion, scif_get_pages() returns 0; otherwise the - * negative of one of the following errors is returned. - * Errors: - * ECONNRESET - Connection reset by peer. - * EINVAL - offset is not a multiple of the page size, or offset is negative, or - * len is not a multiple of the page size - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - * ENXIO - Offsets in the range [offset, offset + len - 1] are invalid - * for the registered address space of the peer epd - */ -int scif_get_pages(scif_epd_t epd, off_t offset, size_t len, - struct scif_range **pages); - -/** - * scif_put_pages() - Remove references from remote registered pages - * @pages: pages to be returned - * - * scif_put_pages() releases a scif_range structure previously obtained by - * calling scif_get_pages(). The physical pages represented by pages may - * be reused when the window which represented those pages is unregistered. - * Therefore, those pages must not be accessed after calling scif_put_pages(). - * - * Return: - * Upon successful completion, scif_put_pages() returns 0; otherwise the - * negative of one of the following errors is returned. - * Errors: - * EINVAL - pages does not point to a valid scif_range structure, or - * the scif_range structure pointed to by pages was already returned - * ENODEV - The remote node is lost or existed, but is not currently in the - * network since it may have crashed - * ENOTCONN - The endpoint is not connected - */ -int scif_put_pages(struct scif_range *pages); - -/** - * scif_poll() - Wait for some event on an endpoint - * @epds: Array of endpoint descriptors - * @nepds: Length of epds - * @timeout: Upper limit on time for which scif_poll() will block - * - * scif_poll() waits for one of a set of endpoints to become ready to perform - * an I/O operation. - * - * The epds argument specifies the endpoint descriptors to be examined and the - * events of interest for each endpoint descriptor. epds is a pointer to an - * array with one member for each open endpoint descriptor of interest. - * - * The number of items in the epds array is specified in nepds. The epd field - * of scif_pollepd is an endpoint descriptor of an open endpoint. The field - * events is a bitmask specifying the events which the application is - * interested in. The field revents is an output parameter, filled by the - * kernel with the events that actually occurred. The bits returned in revents - * can include any of those specified in events, or one of the values EPOLLERR, - * EPOLLHUP, or EPOLLNVAL. (These three bits are meaningless in the events - * field, and will be set in the revents field whenever the corresponding - * condition is true.) - * - * If none of the events requested (and no error) has occurred for any of the - * endpoint descriptors, then scif_poll() blocks until one of the events occurs. - * - * The timeout argument specifies an upper limit on the time for which - * scif_poll() will block, in milliseconds. Specifying a negative value in - * timeout means an infinite timeout. - * - * The following bits may be set in events and returned in revents. - * EPOLLIN - Data may be received without blocking. For a connected - * endpoint, this means that scif_recv() may be called without blocking. For a - * listening endpoint, this means that scif_accept() may be called without - * blocking. - * EPOLLOUT - Data may be sent without blocking. For a connected endpoint, this - * means that scif_send() may be called without blocking. EPOLLOUT may also be - * used to block waiting for a non-blocking connect to complete. This bit value - * has no meaning for a listening endpoint and is ignored if specified. - * - * The following bits are only returned in revents, and are ignored if set in - * events. - * EPOLLERR - An error occurred on the endpoint - * EPOLLHUP - The connection to the peer endpoint was disconnected - * EPOLLNVAL - The specified endpoint descriptor is invalid. - * - * Return: - * Upon successful completion, scif_poll() returns a non-negative value. A - * positive value indicates the total number of endpoint descriptors that have - * been selected (that is, endpoint descriptors for which the revents member is - * non-zero). A value of 0 indicates that the call timed out and no endpoint - * descriptors have been selected. Otherwise in user mode -1 is returned and - * errno is set to indicate the error; in kernel mode the negative of one of - * the following errors is returned. - * - * Errors: - * EINTR - A signal occurred before any requested event - * EINVAL - The nepds argument is greater than {OPEN_MAX} - * ENOMEM - There was no space to allocate file descriptor tables - */ -int scif_poll(struct scif_pollepd *epds, unsigned int nepds, long timeout); - -/** - * scif_client_register() - Register a SCIF client - * @client: client to be registered - * - * scif_client_register() registers a SCIF client. The probe() method - * of the client is called when SCIF peer devices come online and the - * remove() method is called when the peer devices disappear. - * - * Return: - * Upon successful completion, scif_client_register() returns a non-negative - * value. Otherwise the return value is the same as subsys_interface_register() - * in the kernel. - */ -int scif_client_register(struct scif_client *client); - -/** - * scif_client_unregister() - Unregister a SCIF client - * @client: client to be unregistered - * - * scif_client_unregister() unregisters a SCIF client. - * - * Return: - * None - */ -void scif_client_unregister(struct scif_client *client); - -#endif /* __SCIF_H__ */ diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h deleted file mode 100644 index 504e523f702c..000000000000 --- a/include/uapi/linux/mic_common.h +++ /dev/null @@ -1,235 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC driver. - * - */ -#ifndef __MIC_COMMON_H_ -#define __MIC_COMMON_H_ - -#include - -#define __mic_align(a, x) (((a) + (x) - 1) & ~((x) - 1)) - -/** - * struct mic_device_desc: Virtio device information shared between the - * virtio driver and userspace backend - * - * @type: Device type: console/network/disk etc. Type 0/-1 terminates. - * @num_vq: Number of virtqueues. - * @feature_len: Number of bytes of feature bits. Multiply by 2: one for - host features and one for guest acknowledgements. - * @config_len: Number of bytes of the config array after virtqueues. - * @status: A status byte, written by the Guest. - * @config: Start of the following variable length config. - */ -struct mic_device_desc { - __s8 type; - __u8 num_vq; - __u8 feature_len; - __u8 config_len; - __u8 status; - __le64 config[0]; -} __attribute__ ((aligned(8))); - -/** - * struct mic_device_ctrl: Per virtio device information in the device page - * used internally by the host and card side drivers. - * - * @vdev: Used for storing MIC vdev information by the guest. - * @config_change: Set to 1 by host when a config change is requested. - * @vdev_reset: Set to 1 by guest to indicate virtio device has been reset. - * @guest_ack: Set to 1 by guest to ack a command. - * @host_ack: Set to 1 by host to ack a command. - * @used_address_updated: Set to 1 by guest when the used address should be - * updated. - * @c2h_vdev_db: The doorbell number to be used by guest. Set by host. - * @h2c_vdev_db: The doorbell number to be used by host. Set by guest. - */ -struct mic_device_ctrl { - __le64 vdev; - __u8 config_change; - __u8 vdev_reset; - __u8 guest_ack; - __u8 host_ack; - __u8 used_address_updated; - __s8 c2h_vdev_db; - __s8 h2c_vdev_db; -} __attribute__ ((aligned(8))); - -/** - * struct mic_bootparam: Virtio device independent information in device page - * - * @magic: A magic value used by the card to ensure it can see the host - * @h2c_config_db: Host to Card Virtio config doorbell set by card - * @node_id: Unique id of the node - * @h2c_scif_db - Host to card SCIF doorbell set by card - * @c2h_scif_db - Card to host SCIF doorbell set by host - * @scif_host_dma_addr - SCIF host queue pair DMA address - * @scif_card_dma_addr - SCIF card queue pair DMA address - */ -struct mic_bootparam { - __le32 magic; - __s8 h2c_config_db; - __u8 node_id; - __u8 h2c_scif_db; - __u8 c2h_scif_db; - __u64 scif_host_dma_addr; - __u64 scif_card_dma_addr; -} __attribute__ ((aligned(8))); - -/** - * struct mic_device_page: High level representation of the device page - * - * @bootparam: The bootparam structure is used for sharing information and - * status updates between MIC host and card drivers. - * @desc: Array of MIC virtio device descriptors. - */ -struct mic_device_page { - struct mic_bootparam bootparam; - struct mic_device_desc desc[0]; -}; -/** - * struct mic_vqconfig: This is how we expect the device configuration field - * for a virtqueue to be laid out in config space. - * - * @address: Guest/MIC physical address of the virtio ring - * (avail and desc rings) - * @used_address: Guest/MIC physical address of the used ring - * @num: The number of entries in the virtio_ring - */ -struct mic_vqconfig { - __le64 address; - __le64 used_address; - __le16 num; -} __attribute__ ((aligned(8))); - -/* - * The alignment to use between consumer and producer parts of vring. - * This is pagesize for historical reasons. - */ -#define MIC_VIRTIO_RING_ALIGN 4096 - -#define MIC_MAX_VRINGS 4 -#define MIC_VRING_ENTRIES 128 - -/* - * Max vring entries (power of 2) to ensure desc and avail rings - * fit in a single page - */ -#define MIC_MAX_VRING_ENTRIES 128 - -/** - * Max size of the desc block in bytes: includes: - * - struct mic_device_desc - * - struct mic_vqconfig (num_vq of these) - * - host and guest features - * - virtio device config space - */ -#define MIC_MAX_DESC_BLK_SIZE 256 - -/** - * struct _mic_vring_info - Host vring info exposed to userspace backend - * for the avail index and magic for the card. - * - * @avail_idx: host avail idx - * @magic: A magic debug cookie. - */ -struct _mic_vring_info { - __u16 avail_idx; - __le32 magic; -}; - -/** - * struct mic_vring - Vring information. - * - * @vr: The virtio ring. - * @info: Host vring information exposed to the userspace backend for the - * avail index and magic for the card. - * @va: The va for the buffer allocated for vr and info. - * @len: The length of the buffer required for allocating vr and info. - */ -struct mic_vring { - struct vring vr; - struct _mic_vring_info *info; - void *va; - int len; -}; - -#define mic_aligned_desc_size(d) __mic_align(mic_desc_size(d), 8) - -#ifndef INTEL_MIC_CARD -static inline unsigned mic_desc_size(const struct mic_device_desc *desc) -{ - return sizeof(*desc) + desc->num_vq * sizeof(struct mic_vqconfig) - + desc->feature_len * 2 + desc->config_len; -} - -static inline struct mic_vqconfig * -mic_vq_config(const struct mic_device_desc *desc) -{ - return (struct mic_vqconfig *)(desc + 1); -} - -static inline __u8 *mic_vq_features(const struct mic_device_desc *desc) -{ - return (__u8 *)(mic_vq_config(desc) + desc->num_vq); -} - -static inline __u8 *mic_vq_configspace(const struct mic_device_desc *desc) -{ - return mic_vq_features(desc) + desc->feature_len * 2; -} -static inline unsigned mic_total_desc_size(struct mic_device_desc *desc) -{ - return mic_aligned_desc_size(desc) + sizeof(struct mic_device_ctrl); -} -#endif - -/* Device page size */ -#define MIC_DP_SIZE 4096 - -#define MIC_MAGIC 0xc0ffee00 - -/** - * enum mic_states - MIC states. - */ -enum mic_states { - MIC_READY = 0, - MIC_BOOTING, - MIC_ONLINE, - MIC_SHUTTING_DOWN, - MIC_RESETTING, - MIC_RESET_FAILED, - MIC_LAST -}; - -/** - * enum mic_status - MIC status reported by card after - * a host or card initiated shutdown or a card crash. - */ -enum mic_status { - MIC_NOP = 0, - MIC_CRASHED, - MIC_HALTED, - MIC_POWER_OFF, - MIC_RESTART, - MIC_STATUS_LAST -}; - -#endif diff --git a/include/uapi/linux/mic_ioctl.h b/include/uapi/linux/mic_ioctl.h deleted file mode 100644 index 687b9cd9d3e2..000000000000 --- a/include/uapi/linux/mic_ioctl.h +++ /dev/null @@ -1,77 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * - * Intel MIC Host driver. - * - */ -#ifndef _MIC_IOCTL_H_ -#define _MIC_IOCTL_H_ - -#include - -/* - * mic_copy - MIC virtio descriptor copy. - * - * @iov: An array of IOVEC structures containing user space buffers. - * @iovcnt: Number of IOVEC structures in iov. - * @vr_idx: The vring index. - * @update_used: A non zero value results in used index being updated. - * @out_len: The aggregate of the total length written to or read from - * the virtio device. - */ -struct mic_copy_desc { -#ifdef __KERNEL__ - struct iovec __user *iov; -#else - struct iovec *iov; -#endif - __u32 iovcnt; - __u8 vr_idx; - __u8 update_used; - __u32 out_len; -}; - -/* - * Add a new virtio device - * The (struct mic_device_desc *) pointer points to a device page entry - * for the virtio device consisting of: - * - struct mic_device_desc - * - struct mic_vqconfig (num_vq of these) - * - host and guest features - * - virtio device config space - * The total size referenced by the pointer should equal the size returned - * by desc_size() in mic_common.h - */ -#define MIC_VIRTIO_ADD_DEVICE _IOWR('s', 1, struct mic_device_desc *) - -/* - * Copy the number of entries in the iovec and update the used index - * if requested by the user. - */ -#define MIC_VIRTIO_COPY_DESC _IOWR('s', 2, struct mic_copy_desc *) - -/* - * Notify virtio device of a config change - * The (__u8 *) pointer points to config space values for the device - * as they should be written into the device page. The total size - * referenced by the pointer should equal the config_len field of struct - * mic_device_desc. - */ -#define MIC_VIRTIO_CONFIG_CHANGE _IOWR('s', 5, __u8 *) - -#endif diff --git a/samples/mic/mpssd/.gitignore b/samples/mic/mpssd/.gitignore deleted file mode 100644 index aa03f1eb37a0..000000000000 --- a/samples/mic/mpssd/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -mpssd diff --git a/samples/mic/mpssd/Makefile b/samples/mic/mpssd/Makefile deleted file mode 100644 index a7a6e0c70424..000000000000 --- a/samples/mic/mpssd/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -ifndef CROSS_COMPILE -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) - -ifeq ($(ARCH),x86) - -PROGS := mpssd -CC = $(CROSS_COMPILE)gcc -CFLAGS := -I../../../usr/include -I../../../tools/include - -ifdef DEBUG -CFLAGS += -DDEBUG=$(DEBUG) -endif - -all: $(PROGS) -mpssd: mpssd.c sysfs.c - $(CC) $(CFLAGS) mpssd.c sysfs.c -o mpssd -lpthread - -install: - install mpssd /usr/sbin/mpssd - install micctrl /usr/sbin/micctrl - -clean: - rm -fr $(PROGS) - -endif -endif diff --git a/samples/mic/mpssd/micctrl b/samples/mic/mpssd/micctrl deleted file mode 100755 index 030a60b04046..000000000000 --- a/samples/mic/mpssd/micctrl +++ /dev/null @@ -1,162 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: GPL-2.0-only -# Intel MIC Platform Software Stack (MPSS) -# -# Copyright(c) 2013 Intel Corporation. -# -# Intel MIC User Space Tools. -# -# micctrl - Controls MIC boot/start/stop. -# -# chkconfig: 2345 95 05 -# description: start MPSS stack processing. -# -### BEGIN INIT INFO -# Provides: micctrl -### END INIT INFO - -# Source function library. -. /etc/init.d/functions - -sysfs="/sys/class/mic" - -_status() -{ - f=$sysfs/$1 - echo -e $1 state: "`cat $f/state`" shutdown_status: "`cat $f/shutdown_status`" -} - -status() -{ - if [ "`echo $1 | head -c3`" == "mic" ]; then - _status $1 - return $? - fi - for f in $sysfs/* - do - _status `basename $f` - RETVAL=$? - [ $RETVAL -ne 0 ] && return $RETVAL - done - return 0 -} - -_reset() -{ - f=$sysfs/$1 - echo reset > $f/state -} - -reset() -{ - if [ "`echo $1 | head -c3`" == "mic" ]; then - _reset $1 - return $? - fi - for f in $sysfs/* - do - _reset `basename $f` - RETVAL=$? - [ $RETVAL -ne 0 ] && return $RETVAL - done - return 0 -} - -_boot() -{ - f=$sysfs/$1 - echo "linux" > $f/bootmode - echo "mic/uos.img" > $f/firmware - echo "mic/$1.image" > $f/ramdisk - echo "boot" > $f/state -} - -boot() -{ - if [ "`echo $1 | head -c3`" == "mic" ]; then - _boot $1 - return $? - fi - for f in $sysfs/* - do - _boot `basename $f` - RETVAL=$? - [ $RETVAL -ne 0 ] && return $RETVAL - done - return 0 -} - -_shutdown() -{ - f=$sysfs/$1 - echo shutdown > $f/state -} - -shutdown() -{ - if [ "`echo $1 | head -c3`" == "mic" ]; then - _shutdown $1 - return $? - fi - for f in $sysfs/* - do - _shutdown `basename $f` - RETVAL=$? - [ $RETVAL -ne 0 ] && return $RETVAL - done - return 0 -} - -_wait() -{ - f=$sysfs/$1 - while [ "`cat $f/state`" != "offline" -a "`cat $f/state`" != "online" ] - do - sleep 1 - echo -e "Waiting for $1 to go offline" - done -} - -wait() -{ - if [ "`echo $1 | head -c3`" == "mic" ]; then - _wait $1 - return $? - fi - # Wait for the cards to go offline - for f in $sysfs/* - do - _wait `basename $f` - RETVAL=$? - [ $RETVAL -ne 0 ] && return $RETVAL - done - return 0 -} - -if [ ! -d "$sysfs" ]; then - echo -e $"Module unloaded " - exit 3 -fi - -case $1 in - -s) - status $2 - ;; - -r) - reset $2 - ;; - -b) - boot $2 - ;; - -S) - shutdown $2 - ;; - -w) - wait $2 - ;; - *) - echo $"Usage: $0 {-s (status) |-r (reset) |-b (boot) |-S (shutdown) |-w (wait)}" - exit 2 -esac - -exit $? diff --git a/samples/mic/mpssd/mpss b/samples/mic/mpssd/mpss deleted file mode 100755 index 248ac7313c71..000000000000 --- a/samples/mic/mpssd/mpss +++ /dev/null @@ -1,189 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: GPL-2.0-only -# Intel MIC Platform Software Stack (MPSS) -# -# Copyright(c) 2013 Intel Corporation. -# -# Intel MIC User Space Tools. -# -# mpss Start mpssd. -# -# chkconfig: 2345 95 05 -# description: start MPSS stack processing. -# -### BEGIN INIT INFO -# Provides: mpss -# Required-Start: -# Required-Stop: -# Short-Description: MPSS stack control -# Description: MPSS stack control -### END INIT INFO - -# Source function library. -. /etc/init.d/functions - -exec=/usr/sbin/mpssd -sysfs="/sys/class/mic" -mic_modules="mic_host mic_x100_dma scif vop" - -start() -{ - [ -x $exec ] || exit 5 - - if [ "`ps -e | awk '{print $4}' | grep mpssd | head -1`" = "mpssd" ]; then - echo -e $"MPSSD already running! " - success - echo - return 0 - fi - - echo -e $"Starting MPSS Stack" - echo -e $"Loading MIC drivers:" $mic_modules - - modprobe -a $mic_modules - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - failure - echo - return $RETVAL - fi - - # Start the daemon - echo -n $"Starting MPSSD " - $exec - RETVAL=$? - if [ $RETVAL -ne 0 ]; then - failure - echo - return $RETVAL - fi - success - echo - - sleep 5 - - # Boot the cards - micctrl -b - - # Wait till ping works - for f in $sysfs/* - do - count=100 - ipaddr=`cat $f/cmdline` - ipaddr=${ipaddr#*address,} - ipaddr=`echo $ipaddr | cut -d, -f1 | cut -d\; -f1` - while [ $count -ge 0 ] - do - echo -e "Pinging "`basename $f`" " - ping -c 1 $ipaddr &> /dev/null - RETVAL=$? - if [ $RETVAL -eq 0 ]; then - success - break - fi - sleep 1 - count=`expr $count - 1` - done - [ $RETVAL -ne 0 ] && failure || success - echo - done - return $RETVAL -} - -stop() -{ - echo -e $"Shutting down MPSS Stack: " - - # Bail out if module is unloaded - if [ ! -d "$sysfs" ]; then - echo -n $"Module unloaded " - success - echo - return 0 - fi - - # Shut down the cards. - micctrl -S - - # Wait for the cards to go offline - for f in $sysfs/* - do - while [ "`cat $f/state`" != "ready" ] - do - sleep 1 - echo -e "Waiting for "`basename $f`" to become ready" - done - done - - # Display the status of the cards - micctrl -s - - # Kill MPSSD now - echo -n $"Killing MPSSD" - killall -9 mpssd 2>/dev/null - RETVAL=$? - [ $RETVAL -ne 0 ] && failure || success - echo - return $RETVAL -} - -restart() -{ - stop - sleep 5 - start -} - -status() -{ - micctrl -s - if [ "`ps -e | awk '{print $4}' | grep mpssd | head -n 1`" = "mpssd" ]; then - echo "mpssd is running" - else - echo "mpssd is stopped" - fi - return 0 -} - -unload() -{ - if [ ! -d "$sysfs" ]; then - echo -n $"No MIC_HOST Module: " - success - echo - return - fi - - stop - - sleep 5 - echo -n $"Removing MIC drivers:" $mic_modules - modprobe -r $mic_modules - RETVAL=$? - [ $RETVAL -ne 0 ] && failure || success - echo - return $RETVAL -} - -case $1 in - start) - start - ;; - stop) - stop - ;; - restart) - restart - ;; - status) - status - ;; - unload) - unload - ;; - *) - echo $"Usage: $0 {start|stop|restart|status|unload}" - exit 2 -esac - -exit $? diff --git a/samples/mic/mpssd/mpssd.c b/samples/mic/mpssd/mpssd.c deleted file mode 100644 index c03a05d498f0..000000000000 --- a/samples/mic/mpssd/mpssd.c +++ /dev/null @@ -1,1815 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC User Space Tools. - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mpssd.h" -#include -#include -#include - -static void *init_mic(void *arg); - -static FILE *logfp; -static struct mic_info mic_list; - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -#define min_t(type, x, y) ({ \ - type __min1 = (x); \ - type __min2 = (y); \ - __min1 < __min2 ? __min1 : __min2; }) - -/* align addr on a size boundary - adjust address up/down if needed */ -#define _ALIGN_DOWN(addr, size) ((addr)&(~((size)-1))) -#define _ALIGN_UP(addr, size) _ALIGN_DOWN(addr + size - 1, size) - -/* align addr on a size boundary - adjust address up if needed */ -#define _ALIGN(addr, size) _ALIGN_UP(addr, size) - -/* to align the pointer to the (next) page boundary */ -#define PAGE_ALIGN(addr) _ALIGN(addr, PAGE_SIZE) - -#define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) - -#define GSO_ENABLED 1 -#define MAX_GSO_SIZE (64 * 1024) -#define ETH_H_LEN 14 -#define MAX_NET_PKT_SIZE (_ALIGN_UP(MAX_GSO_SIZE + ETH_H_LEN, 64)) -#define MIC_DEVICE_PAGE_END 0x1000 - -#ifndef VIRTIO_NET_HDR_F_DATA_VALID -#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */ -#endif - -static struct { - struct mic_device_desc dd; - struct mic_vqconfig vqconfig[2]; - __u32 host_features, guest_acknowledgements; - struct virtio_console_config cons_config; -} virtcons_dev_page = { - .dd = { - .type = VIRTIO_ID_CONSOLE, - .num_vq = ARRAY_SIZE(virtcons_dev_page.vqconfig), - .feature_len = sizeof(virtcons_dev_page.host_features), - .config_len = sizeof(virtcons_dev_page.cons_config), - }, - .vqconfig[0] = { - .num = htole16(MIC_VRING_ENTRIES), - }, - .vqconfig[1] = { - .num = htole16(MIC_VRING_ENTRIES), - }, -}; - -static struct { - struct mic_device_desc dd; - struct mic_vqconfig vqconfig[2]; - __u32 host_features, guest_acknowledgements; - struct virtio_net_config net_config; -} virtnet_dev_page = { - .dd = { - .type = VIRTIO_ID_NET, - .num_vq = ARRAY_SIZE(virtnet_dev_page.vqconfig), - .feature_len = sizeof(virtnet_dev_page.host_features), - .config_len = sizeof(virtnet_dev_page.net_config), - }, - .vqconfig[0] = { - .num = htole16(MIC_VRING_ENTRIES), - }, - .vqconfig[1] = { - .num = htole16(MIC_VRING_ENTRIES), - }, -#if GSO_ENABLED - .host_features = htole32( - 1 << VIRTIO_NET_F_CSUM | - 1 << VIRTIO_NET_F_GSO | - 1 << VIRTIO_NET_F_GUEST_TSO4 | - 1 << VIRTIO_NET_F_GUEST_TSO6 | - 1 << VIRTIO_NET_F_GUEST_ECN), -#else - .host_features = 0, -#endif -}; - -static const char *mic_config_dir = "/etc/mpss"; -static const char *virtblk_backend = "VIRTBLK_BACKEND"; -static struct { - struct mic_device_desc dd; - struct mic_vqconfig vqconfig[1]; - __u32 host_features, guest_acknowledgements; - struct virtio_blk_config blk_config; -} virtblk_dev_page = { - .dd = { - .type = VIRTIO_ID_BLOCK, - .num_vq = ARRAY_SIZE(virtblk_dev_page.vqconfig), - .feature_len = sizeof(virtblk_dev_page.host_features), - .config_len = sizeof(virtblk_dev_page.blk_config), - }, - .vqconfig[0] = { - .num = htole16(MIC_VRING_ENTRIES), - }, - .host_features = - htole32(1<name, strerror(errno)); - return ret; - } - } - if (pid < 0) { - mpsslog("%s fork failed errno %s\n", - mic->name, strerror(errno)); - return ret; - } - - ret = waitpid(pid, NULL, 0); - if (ret < 0) { - mpsslog("%s waitpid failed errno %s\n", - mic->name, strerror(errno)); - return ret; - } - - snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id + 1); - - pid = fork(); - if (pid == 0) { - ifargv[0] = "ip"; - ifargv[1] = "addr"; - ifargv[2] = "add"; - ifargv[3] = ipaddr; - ifargv[4] = "dev"; - ifargv[5] = dev; - ifargv[6] = NULL; - mpsslog("Configuring %s ipaddr %s\n", dev, ipaddr); - ret = execvp("ip", ifargv); - if (ret < 0) { - mpsslog("%s execvp failed errno %s\n", - mic->name, strerror(errno)); - return ret; - } - } - if (pid < 0) { - mpsslog("%s fork failed errno %s\n", - mic->name, strerror(errno)); - return ret; - } - - ret = waitpid(pid, NULL, 0); - if (ret < 0) { - mpsslog("%s waitpid failed errno %s\n", - mic->name, strerror(errno)); - return ret; - } - mpsslog("MIC name %s %s %d DONE!\n", - mic->name, __func__, __LINE__); - return 0; -} - -static int tun_alloc(struct mic_info *mic, char *dev) -{ - struct ifreq ifr; - int fd, err; -#if GSO_ENABLED - unsigned offload; -#endif - fd = open("/dev/net/tun", O_RDWR); - if (fd < 0) { - mpsslog("Could not open /dev/net/tun %s\n", strerror(errno)); - goto done; - } - - memset(&ifr, 0, sizeof(ifr)); - - ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR; - if (*dev) - strncpy(ifr.ifr_name, dev, IFNAMSIZ); - - err = ioctl(fd, TUNSETIFF, (void *)&ifr); - if (err < 0) { - mpsslog("%s %s %d TUNSETIFF failed %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - close(fd); - return err; - } -#if GSO_ENABLED - offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_TSO_ECN; - - err = ioctl(fd, TUNSETOFFLOAD, offload); - if (err < 0) { - mpsslog("%s %s %d TUNSETOFFLOAD failed %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - close(fd); - return err; - } -#endif - strcpy(dev, ifr.ifr_name); - mpsslog("Created TAP %s\n", dev); -done: - return fd; -} - -#define NET_FD_VIRTIO_NET 0 -#define NET_FD_TUN 1 -#define MAX_NET_FD 2 - -static void set_dp(struct mic_info *mic, int type, void *dp) -{ - switch (type) { - case VIRTIO_ID_CONSOLE: - mic->mic_console.console_dp = dp; - return; - case VIRTIO_ID_NET: - mic->mic_net.net_dp = dp; - return; - case VIRTIO_ID_BLOCK: - mic->mic_virtblk.block_dp = dp; - return; - } - mpsslog("%s %s %d not found\n", mic->name, __func__, type); - assert(0); -} - -static void *get_dp(struct mic_info *mic, int type) -{ - switch (type) { - case VIRTIO_ID_CONSOLE: - return mic->mic_console.console_dp; - case VIRTIO_ID_NET: - return mic->mic_net.net_dp; - case VIRTIO_ID_BLOCK: - return mic->mic_virtblk.block_dp; - } - mpsslog("%s %s %d not found\n", mic->name, __func__, type); - assert(0); - return NULL; -} - -static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type) -{ - struct mic_device_desc *d; - int i; - void *dp = get_dp(mic, type); - - for (i = sizeof(struct mic_bootparam); i < PAGE_SIZE; - i += mic_total_desc_size(d)) { - d = dp + i; - - /* End of list */ - if (d->type == 0) - break; - - if (d->type == -1) - continue; - - mpsslog("%s %s d-> type %d d %p\n", - mic->name, __func__, d->type, d); - - if (d->type == (__u8)type) - return d; - } - mpsslog("%s %s %d not found\n", mic->name, __func__, type); - return NULL; -} - -/* See comments in vhost.c for explanation of next_desc() */ -static unsigned next_desc(struct vring_desc *desc) -{ - unsigned int next; - - if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) - return -1U; - next = le16toh(desc->next); - return next; -} - -/* Sum up all the IOVEC length */ -static ssize_t -sum_iovec_len(struct mic_copy_desc *copy) -{ - ssize_t sum = 0; - unsigned int i; - - for (i = 0; i < copy->iovcnt; i++) - sum += copy->iov[i].iov_len; - return sum; -} - -static inline void verify_out_len(struct mic_info *mic, - struct mic_copy_desc *copy) -{ - if (copy->out_len != sum_iovec_len(copy)) { - mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%zx\n", - mic->name, __func__, __LINE__, - copy->out_len, sum_iovec_len(copy)); - assert(copy->out_len == sum_iovec_len(copy)); - } -} - -/* Display an iovec */ -static void -disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy, - const char *s, int line) -{ - unsigned int i; - - for (i = 0; i < copy->iovcnt; i++) - mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%zx\n", - mic->name, s, line, i, - copy->iov[i].iov_base, copy->iov[i].iov_len); -} - -static inline __u16 read_avail_idx(struct mic_vring *vr) -{ - return READ_ONCE(vr->info->avail_idx); -} - -static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr, - struct mic_copy_desc *copy, ssize_t len) -{ - copy->vr_idx = tx ? 0 : 1; - copy->update_used = true; - if (type == VIRTIO_ID_NET) - copy->iov[1].iov_len = len - sizeof(struct virtio_net_hdr); - else - copy->iov[0].iov_len = len; -} - -/* Central API which triggers the copies */ -static int -mic_virtio_copy(struct mic_info *mic, int fd, - struct mic_vring *vr, struct mic_copy_desc *copy) -{ - int ret; - - ret = ioctl(fd, MIC_VIRTIO_COPY_DESC, copy); - if (ret) { - mpsslog("%s %s %d errno %s ret %d\n", - mic->name, __func__, __LINE__, - strerror(errno), ret); - } - return ret; -} - -static inline unsigned _vring_size(unsigned int num, unsigned long align) -{ - return _ALIGN_UP(((sizeof(struct vring_desc) * num + sizeof(__u16) * (3 + num) - + align - 1) & ~(align - 1)) - + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num, 4); -} - -/* - * This initialization routine requires at least one - * vring i.e. vr0. vr1 is optional. - */ -static void * -init_vr(struct mic_info *mic, int fd, int type, - struct mic_vring *vr0, struct mic_vring *vr1, int num_vq) -{ - int vr_size; - char *va; - - vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES, - MIC_VIRTIO_RING_ALIGN) + - sizeof(struct _mic_vring_info)); - va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq, - PROT_READ, MAP_SHARED, fd, 0); - if (MAP_FAILED == va) { - mpsslog("%s %s %d mmap failed errno %s\n", - mic->name, __func__, __LINE__, - strerror(errno)); - goto done; - } - set_dp(mic, type, va); - vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END]; - vr0->info = vr0->va + - _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); - vring_init(&vr0->vr, - MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN); - mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ", - __func__, mic->name, vr0->va, vr0->info, vr_size, - _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); - mpsslog("magic 0x%x expected 0x%x\n", - le32toh(vr0->info->magic), MIC_MAGIC + type); - assert(le32toh(vr0->info->magic) == MIC_MAGIC + type); - if (vr1) { - vr1->va = (struct mic_vring *) - &va[MIC_DEVICE_PAGE_END + vr_size]; - vr1->info = vr1->va + _vring_size(MIC_VRING_ENTRIES, - MIC_VIRTIO_RING_ALIGN); - vring_init(&vr1->vr, - MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN); - mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ", - __func__, mic->name, vr1->va, vr1->info, vr_size, - _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); - mpsslog("magic 0x%x expected 0x%x\n", - le32toh(vr1->info->magic), MIC_MAGIC + type + 1); - assert(le32toh(vr1->info->magic) == MIC_MAGIC + type + 1); - } -done: - return va; -} - -static int -wait_for_card_driver(struct mic_info *mic, int fd, int type) -{ - struct pollfd pollfd; - int err; - struct mic_device_desc *desc = get_device_desc(mic, type); - __u8 prev_status; - - if (!desc) - return -ENODEV; - prev_status = desc->status; - pollfd.fd = fd; - mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n", - mic->name, __func__, type, desc->status); - - while (1) { - pollfd.events = POLLIN; - pollfd.revents = 0; - err = poll(&pollfd, 1, -1); - if (err < 0) { - mpsslog("%s %s poll failed %s\n", - mic->name, __func__, strerror(errno)); - continue; - } - - if (pollfd.revents) { - if (desc->status != prev_status) { - mpsslog("%s %s Waiting... desc-> type %d " - "status 0x%x\n", - mic->name, __func__, type, - desc->status); - prev_status = desc->status; - } - if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) { - mpsslog("%s %s poll.revents %d\n", - mic->name, __func__, pollfd.revents); - mpsslog("%s %s desc-> type %d status 0x%x\n", - mic->name, __func__, type, - desc->status); - break; - } - } - } - return 0; -} - -/* Spin till we have some descriptors */ -static void -spin_for_descriptors(struct mic_info *mic, struct mic_vring *vr) -{ - __u16 avail_idx = read_avail_idx(vr); - - while (avail_idx == le16toh(READ_ONCE(vr->vr.avail->idx))) { -#ifdef DEBUG - mpsslog("%s %s waiting for desc avail %d info_avail %d\n", - mic->name, __func__, - le16toh(vr->vr.avail->idx), vr->info->avail_idx); -#endif - sched_yield(); - } -} - -static void * -virtio_net(void *arg) -{ - static __u8 vnet_hdr[2][sizeof(struct virtio_net_hdr)]; - static __u8 vnet_buf[2][MAX_NET_PKT_SIZE] __attribute__ ((aligned(64))); - struct iovec vnet_iov[2][2] = { - { { .iov_base = vnet_hdr[0], .iov_len = sizeof(vnet_hdr[0]) }, - { .iov_base = vnet_buf[0], .iov_len = sizeof(vnet_buf[0]) } }, - { { .iov_base = vnet_hdr[1], .iov_len = sizeof(vnet_hdr[1]) }, - { .iov_base = vnet_buf[1], .iov_len = sizeof(vnet_buf[1]) } }, - }; - struct iovec *iov0 = vnet_iov[0], *iov1 = vnet_iov[1]; - struct mic_info *mic = (struct mic_info *)arg; - char if_name[IFNAMSIZ]; - struct pollfd net_poll[MAX_NET_FD]; - struct mic_vring tx_vr, rx_vr; - struct mic_copy_desc copy; - struct mic_device_desc *desc; - int err; - - snprintf(if_name, IFNAMSIZ, "mic%d", mic->id); - mic->mic_net.tap_fd = tun_alloc(mic, if_name); - if (mic->mic_net.tap_fd < 0) - goto done; - - if (tap_configure(mic, if_name)) - goto done; - mpsslog("MIC name %s id %d\n", mic->name, mic->id); - - net_poll[NET_FD_VIRTIO_NET].fd = mic->mic_net.virtio_net_fd; - net_poll[NET_FD_VIRTIO_NET].events = POLLIN; - net_poll[NET_FD_TUN].fd = mic->mic_net.tap_fd; - net_poll[NET_FD_TUN].events = POLLIN; - - if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd, - VIRTIO_ID_NET, &tx_vr, &rx_vr, - virtnet_dev_page.dd.num_vq)) { - mpsslog("%s init_vr failed %s\n", - mic->name, strerror(errno)); - goto done; - } - - copy.iovcnt = 2; - desc = get_device_desc(mic, VIRTIO_ID_NET); - - while (1) { - ssize_t len; - - net_poll[NET_FD_VIRTIO_NET].revents = 0; - net_poll[NET_FD_TUN].revents = 0; - - /* Start polling for data from tap and virtio net */ - err = poll(net_poll, 2, -1); - if (err < 0) { - mpsslog("%s poll failed %s\n", - __func__, strerror(errno)); - continue; - } - if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - err = wait_for_card_driver(mic, - mic->mic_net.virtio_net_fd, - VIRTIO_ID_NET); - if (err) { - mpsslog("%s %s %d Exiting...\n", - mic->name, __func__, __LINE__); - break; - } - } - /* - * Check if there is data to be read from TUN and write to - * virtio net fd if there is. - */ - if (net_poll[NET_FD_TUN].revents & POLLIN) { - copy.iov = iov0; - len = readv(net_poll[NET_FD_TUN].fd, - copy.iov, copy.iovcnt); - if (len > 0) { - struct virtio_net_hdr *hdr - = (struct virtio_net_hdr *)vnet_hdr[0]; - - /* Disable checksums on the card since we are on - a reliable PCIe link */ - hdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID; -#ifdef DEBUG - mpsslog("%s %s %d hdr->flags 0x%x ", mic->name, - __func__, __LINE__, hdr->flags); - mpsslog("copy.out_len %d hdr->gso_type 0x%x\n", - copy.out_len, hdr->gso_type); -#endif -#ifdef DEBUG - disp_iovec(mic, ©, __func__, __LINE__); - mpsslog("%s %s %d read from tap 0x%lx\n", - mic->name, __func__, __LINE__, - len); -#endif - spin_for_descriptors(mic, &tx_vr); - txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, ©, - len); - - err = mic_virtio_copy(mic, - mic->mic_net.virtio_net_fd, &tx_vr, - ©); - if (err < 0) { - mpsslog("%s %s %d mic_virtio_copy %s\n", - mic->name, __func__, __LINE__, - strerror(errno)); - } - if (!err) - verify_out_len(mic, ©); -#ifdef DEBUG - disp_iovec(mic, ©, __func__, __LINE__); - mpsslog("%s %s %d wrote to net 0x%lx\n", - mic->name, __func__, __LINE__, - sum_iovec_len(©)); -#endif - /* Reinitialize IOV for next run */ - iov0[1].iov_len = MAX_NET_PKT_SIZE; - } else if (len < 0) { - disp_iovec(mic, ©, __func__, __LINE__); - mpsslog("%s %s %d read failed %s ", mic->name, - __func__, __LINE__, strerror(errno)); - mpsslog("cnt %d sum %zd\n", - copy.iovcnt, sum_iovec_len(©)); - } - } - - /* - * Check if there is data to be read from virtio net and - * write to TUN if there is. - */ - if (net_poll[NET_FD_VIRTIO_NET].revents & POLLIN) { - while (rx_vr.info->avail_idx != - le16toh(rx_vr.vr.avail->idx)) { - copy.iov = iov1; - txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, ©, - MAX_NET_PKT_SIZE - + sizeof(struct virtio_net_hdr)); - - err = mic_virtio_copy(mic, - mic->mic_net.virtio_net_fd, &rx_vr, - ©); - if (!err) { -#ifdef DEBUG - struct virtio_net_hdr *hdr - = (struct virtio_net_hdr *) - vnet_hdr[1]; - - mpsslog("%s %s %d hdr->flags 0x%x, ", - mic->name, __func__, __LINE__, - hdr->flags); - mpsslog("out_len %d gso_type 0x%x\n", - copy.out_len, - hdr->gso_type); -#endif - /* Set the correct output iov_len */ - iov1[1].iov_len = copy.out_len - - sizeof(struct virtio_net_hdr); - verify_out_len(mic, ©); -#ifdef DEBUG - disp_iovec(mic, ©, __func__, - __LINE__); - mpsslog("%s %s %d ", - mic->name, __func__, __LINE__); - mpsslog("read from net 0x%lx\n", - sum_iovec_len(©)); -#endif - len = writev(net_poll[NET_FD_TUN].fd, - copy.iov, copy.iovcnt); - if (len != sum_iovec_len(©)) { - mpsslog("Tun write failed %s ", - strerror(errno)); - mpsslog("len 0x%zx ", len); - mpsslog("read_len 0x%zx\n", - sum_iovec_len(©)); - } else { -#ifdef DEBUG - disp_iovec(mic, ©, __func__, - __LINE__); - mpsslog("%s %s %d ", - mic->name, __func__, - __LINE__); - mpsslog("wrote to tap 0x%lx\n", - len); -#endif - } - } else { - mpsslog("%s %s %d mic_virtio_copy %s\n", - mic->name, __func__, __LINE__, - strerror(errno)); - break; - } - } - } - if (net_poll[NET_FD_VIRTIO_NET].revents & POLLERR) - mpsslog("%s: %s: POLLERR\n", __func__, mic->name); - } -done: - pthread_exit(NULL); -} - -/* virtio_console */ -#define VIRTIO_CONSOLE_FD 0 -#define MONITOR_FD (VIRTIO_CONSOLE_FD + 1) -#define MAX_CONSOLE_FD (MONITOR_FD + 1) /* must be the last one + 1 */ -#define MAX_BUFFER_SIZE PAGE_SIZE - -static void * -virtio_console(void *arg) -{ - static __u8 vcons_buf[2][PAGE_SIZE]; - struct iovec vcons_iov[2] = { - { .iov_base = vcons_buf[0], .iov_len = sizeof(vcons_buf[0]) }, - { .iov_base = vcons_buf[1], .iov_len = sizeof(vcons_buf[1]) }, - }; - struct iovec *iov0 = &vcons_iov[0], *iov1 = &vcons_iov[1]; - struct mic_info *mic = (struct mic_info *)arg; - int err; - struct pollfd console_poll[MAX_CONSOLE_FD]; - int pty_fd; - char *pts_name; - ssize_t len; - struct mic_vring tx_vr, rx_vr; - struct mic_copy_desc copy; - struct mic_device_desc *desc; - - pty_fd = posix_openpt(O_RDWR); - if (pty_fd < 0) { - mpsslog("can't open a pseudoterminal master device: %s\n", - strerror(errno)); - goto _return; - } - pts_name = ptsname(pty_fd); - if (pts_name == NULL) { - mpsslog("can't get pts name\n"); - goto _close_pty; - } - printf("%s console message goes to %s\n", mic->name, pts_name); - mpsslog("%s console message goes to %s\n", mic->name, pts_name); - err = grantpt(pty_fd); - if (err < 0) { - mpsslog("can't grant access: %s %s\n", - pts_name, strerror(errno)); - goto _close_pty; - } - err = unlockpt(pty_fd); - if (err < 0) { - mpsslog("can't unlock a pseudoterminal: %s %s\n", - pts_name, strerror(errno)); - goto _close_pty; - } - console_poll[MONITOR_FD].fd = pty_fd; - console_poll[MONITOR_FD].events = POLLIN; - - console_poll[VIRTIO_CONSOLE_FD].fd = mic->mic_console.virtio_console_fd; - console_poll[VIRTIO_CONSOLE_FD].events = POLLIN; - - if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd, - VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr, - virtcons_dev_page.dd.num_vq)) { - mpsslog("%s init_vr failed %s\n", - mic->name, strerror(errno)); - goto _close_pty; - } - - copy.iovcnt = 1; - desc = get_device_desc(mic, VIRTIO_ID_CONSOLE); - - for (;;) { - console_poll[MONITOR_FD].revents = 0; - console_poll[VIRTIO_CONSOLE_FD].revents = 0; - err = poll(console_poll, MAX_CONSOLE_FD, -1); - if (err < 0) { - mpsslog("%s %d: poll failed: %s\n", __func__, __LINE__, - strerror(errno)); - continue; - } - if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) { - err = wait_for_card_driver(mic, - mic->mic_console.virtio_console_fd, - VIRTIO_ID_CONSOLE); - if (err) { - mpsslog("%s %s %d Exiting...\n", - mic->name, __func__, __LINE__); - break; - } - } - - if (console_poll[MONITOR_FD].revents & POLLIN) { - copy.iov = iov0; - len = readv(pty_fd, copy.iov, copy.iovcnt); - if (len > 0) { -#ifdef DEBUG - disp_iovec(mic, ©, __func__, __LINE__); - mpsslog("%s %s %d read from tap 0x%lx\n", - mic->name, __func__, __LINE__, - len); -#endif - spin_for_descriptors(mic, &tx_vr); - txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr, - ©, len); - - err = mic_virtio_copy(mic, - mic->mic_console.virtio_console_fd, - &tx_vr, ©); - if (err < 0) { - mpsslog("%s %s %d mic_virtio_copy %s\n", - mic->name, __func__, __LINE__, - strerror(errno)); - } - if (!err) - verify_out_len(mic, ©); -#ifdef DEBUG - disp_iovec(mic, ©, __func__, __LINE__); - mpsslog("%s %s %d wrote to net 0x%lx\n", - mic->name, __func__, __LINE__, - sum_iovec_len(©)); -#endif - /* Reinitialize IOV for next run */ - iov0->iov_len = PAGE_SIZE; - } else if (len < 0) { - disp_iovec(mic, ©, __func__, __LINE__); - mpsslog("%s %s %d read failed %s ", - mic->name, __func__, __LINE__, - strerror(errno)); - mpsslog("cnt %d sum %zd\n", - copy.iovcnt, sum_iovec_len(©)); - } - } - - if (console_poll[VIRTIO_CONSOLE_FD].revents & POLLIN) { - while (rx_vr.info->avail_idx != - le16toh(rx_vr.vr.avail->idx)) { - copy.iov = iov1; - txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr, - ©, PAGE_SIZE); - - err = mic_virtio_copy(mic, - mic->mic_console.virtio_console_fd, - &rx_vr, ©); - if (!err) { - /* Set the correct output iov_len */ - iov1->iov_len = copy.out_len; - verify_out_len(mic, ©); -#ifdef DEBUG - disp_iovec(mic, ©, __func__, - __LINE__); - mpsslog("%s %s %d ", - mic->name, __func__, __LINE__); - mpsslog("read from net 0x%lx\n", - sum_iovec_len(©)); -#endif - len = writev(pty_fd, - copy.iov, copy.iovcnt); - if (len != sum_iovec_len(©)) { - mpsslog("Tun write failed %s ", - strerror(errno)); - mpsslog("len 0x%zx ", len); - mpsslog("read_len 0x%zx\n", - sum_iovec_len(©)); - } else { -#ifdef DEBUG - disp_iovec(mic, ©, __func__, - __LINE__); - mpsslog("%s %s %d ", - mic->name, __func__, - __LINE__); - mpsslog("wrote to tap 0x%lx\n", - len); -#endif - } - } else { - mpsslog("%s %s %d mic_virtio_copy %s\n", - mic->name, __func__, __LINE__, - strerror(errno)); - break; - } - } - } - if (console_poll[NET_FD_VIRTIO_NET].revents & POLLERR) - mpsslog("%s: %s: POLLERR\n", __func__, mic->name); - } -_close_pty: - close(pty_fd); -_return: - pthread_exit(NULL); -} - -static void -add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd) -{ - char path[PATH_MAX]; - int fd, err; - - snprintf(path, PATH_MAX, "/dev/vop_virtio%d", mic->id); - fd = open(path, O_RDWR); - if (fd < 0) { - mpsslog("Could not open %s %s\n", path, strerror(errno)); - return; - } - - err = ioctl(fd, MIC_VIRTIO_ADD_DEVICE, dd); - if (err < 0) { - mpsslog("Could not add %d %s\n", dd->type, strerror(errno)); - close(fd); - return; - } - switch (dd->type) { - case VIRTIO_ID_NET: - mic->mic_net.virtio_net_fd = fd; - mpsslog("Added VIRTIO_ID_NET for %s\n", mic->name); - break; - case VIRTIO_ID_CONSOLE: - mic->mic_console.virtio_console_fd = fd; - mpsslog("Added VIRTIO_ID_CONSOLE for %s\n", mic->name); - break; - case VIRTIO_ID_BLOCK: - mic->mic_virtblk.virtio_block_fd = fd; - mpsslog("Added VIRTIO_ID_BLOCK for %s\n", mic->name); - break; - } -} - -static bool -set_backend_file(struct mic_info *mic) -{ - FILE *config; - char buff[PATH_MAX], *line, *evv, *p; - - snprintf(buff, PATH_MAX, "%s/mpssd%03d.conf", mic_config_dir, mic->id); - config = fopen(buff, "r"); - if (config == NULL) - return false; - do { /* look for "virtblk_backend=XXXX" */ - line = fgets(buff, PATH_MAX, config); - if (line == NULL) - break; - if (*line == '#') - continue; - p = strchr(line, '\n'); - if (p) - *p = '\0'; - } while (strncmp(line, virtblk_backend, strlen(virtblk_backend)) != 0); - fclose(config); - if (line == NULL) - return false; - evv = strchr(line, '='); - if (evv == NULL) - return false; - mic->mic_virtblk.backend_file = malloc(strlen(evv) + 1); - if (mic->mic_virtblk.backend_file == NULL) { - mpsslog("%s %d can't allocate memory\n", mic->name, mic->id); - return false; - } - strcpy(mic->mic_virtblk.backend_file, evv + 1); - return true; -} - -#define SECTOR_SIZE 512 -static bool -set_backend_size(struct mic_info *mic) -{ - mic->mic_virtblk.backend_size = lseek(mic->mic_virtblk.backend, 0, - SEEK_END); - if (mic->mic_virtblk.backend_size < 0) { - mpsslog("%s: can't seek: %s\n", - mic->name, mic->mic_virtblk.backend_file); - return false; - } - virtblk_dev_page.blk_config.capacity = - mic->mic_virtblk.backend_size / SECTOR_SIZE; - if ((mic->mic_virtblk.backend_size % SECTOR_SIZE) != 0) - virtblk_dev_page.blk_config.capacity++; - - virtblk_dev_page.blk_config.capacity = - htole64(virtblk_dev_page.blk_config.capacity); - - return true; -} - -static bool -open_backend(struct mic_info *mic) -{ - if (!set_backend_file(mic)) - goto _error_exit; - mic->mic_virtblk.backend = open(mic->mic_virtblk.backend_file, O_RDWR); - if (mic->mic_virtblk.backend < 0) { - mpsslog("%s: can't open: %s\n", mic->name, - mic->mic_virtblk.backend_file); - goto _error_free; - } - if (!set_backend_size(mic)) - goto _error_close; - mic->mic_virtblk.backend_addr = mmap(NULL, - mic->mic_virtblk.backend_size, - PROT_READ|PROT_WRITE, MAP_SHARED, - mic->mic_virtblk.backend, 0L); - if (mic->mic_virtblk.backend_addr == MAP_FAILED) { - mpsslog("%s: can't map: %s %s\n", - mic->name, mic->mic_virtblk.backend_file, - strerror(errno)); - goto _error_close; - } - return true; - - _error_close: - close(mic->mic_virtblk.backend); - _error_free: - free(mic->mic_virtblk.backend_file); - _error_exit: - return false; -} - -static void -close_backend(struct mic_info *mic) -{ - munmap(mic->mic_virtblk.backend_addr, mic->mic_virtblk.backend_size); - close(mic->mic_virtblk.backend); - free(mic->mic_virtblk.backend_file); -} - -static bool -start_virtblk(struct mic_info *mic, struct mic_vring *vring) -{ - if (((unsigned long)&virtblk_dev_page.blk_config % 8) != 0) { - mpsslog("%s: blk_config is not 8 byte aligned.\n", - mic->name); - return false; - } - add_virtio_device(mic, &virtblk_dev_page.dd); - if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd, - VIRTIO_ID_BLOCK, vring, NULL, - virtblk_dev_page.dd.num_vq)) { - mpsslog("%s init_vr failed %s\n", - mic->name, strerror(errno)); - return false; - } - return true; -} - -static void -stop_virtblk(struct mic_info *mic) -{ - int vr_size, ret; - - vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES, - MIC_VIRTIO_RING_ALIGN) + - sizeof(struct _mic_vring_info)); - ret = munmap(mic->mic_virtblk.block_dp, - MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq); - if (ret < 0) - mpsslog("%s munmap errno %d\n", mic->name, errno); - close(mic->mic_virtblk.virtio_block_fd); -} - -static __u8 -header_error_check(struct vring_desc *desc) -{ - if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) { - mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n", - __func__, __LINE__); - return -EIO; - } - if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) { - mpsslog("%s() %d: alone\n", - __func__, __LINE__); - return -EIO; - } - if (le16toh(desc->flags) & VRING_DESC_F_WRITE) { - mpsslog("%s() %d: not read\n", - __func__, __LINE__); - return -EIO; - } - return 0; -} - -static int -read_header(int fd, struct virtio_blk_outhdr *hdr, __u32 desc_idx) -{ - struct iovec iovec; - struct mic_copy_desc copy; - - iovec.iov_len = sizeof(*hdr); - iovec.iov_base = hdr; - copy.iov = &iovec; - copy.iovcnt = 1; - copy.vr_idx = 0; /* only one vring on virtio_block */ - copy.update_used = false; /* do not update used index */ - return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); -} - -static int -transfer_blocks(int fd, struct iovec *iovec, __u32 iovcnt) -{ - struct mic_copy_desc copy; - - copy.iov = iovec; - copy.iovcnt = iovcnt; - copy.vr_idx = 0; /* only one vring on virtio_block */ - copy.update_used = false; /* do not update used index */ - return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); -} - -static __u8 -status_error_check(struct vring_desc *desc) -{ - if (le32toh(desc->len) != sizeof(__u8)) { - mpsslog("%s() %d: length is not sizeof(status)\n", - __func__, __LINE__); - return -EIO; - } - return 0; -} - -static int -write_status(int fd, __u8 *status) -{ - struct iovec iovec; - struct mic_copy_desc copy; - - iovec.iov_base = status; - iovec.iov_len = sizeof(*status); - copy.iov = &iovec; - copy.iovcnt = 1; - copy.vr_idx = 0; /* only one vring on virtio_block */ - copy.update_used = true; /* Update used index */ - return ioctl(fd, MIC_VIRTIO_COPY_DESC, ©); -} - -#ifndef VIRTIO_BLK_T_GET_ID -#define VIRTIO_BLK_T_GET_ID 8 -#endif - -static void * -virtio_block(void *arg) -{ - struct mic_info *mic = (struct mic_info *)arg; - int ret; - struct pollfd block_poll; - struct mic_vring vring; - __u16 avail_idx; - __u32 desc_idx; - struct vring_desc *desc; - struct iovec *iovec, *piov; - __u8 status; - __u32 buffer_desc_idx; - struct virtio_blk_outhdr hdr; - void *fos; - - for (;;) { /* forever */ - if (!open_backend(mic)) { /* No virtblk */ - for (mic->mic_virtblk.signaled = 0; - !mic->mic_virtblk.signaled;) - sleep(1); - continue; - } - - /* backend file is specified. */ - if (!start_virtblk(mic, &vring)) - goto _close_backend; - iovec = malloc(sizeof(*iovec) * - le32toh(virtblk_dev_page.blk_config.seg_max)); - if (!iovec) { - mpsslog("%s: can't alloc iovec: %s\n", - mic->name, strerror(ENOMEM)); - goto _stop_virtblk; - } - - block_poll.fd = mic->mic_virtblk.virtio_block_fd; - block_poll.events = POLLIN; - for (mic->mic_virtblk.signaled = 0; - !mic->mic_virtblk.signaled;) { - block_poll.revents = 0; - /* timeout in 1 sec to see signaled */ - ret = poll(&block_poll, 1, 1000); - if (ret < 0) { - mpsslog("%s %d: poll failed: %s\n", - __func__, __LINE__, - strerror(errno)); - continue; - } - - if (!(block_poll.revents & POLLIN)) { -#ifdef DEBUG - mpsslog("%s %d: block_poll.revents=0x%x\n", - __func__, __LINE__, block_poll.revents); -#endif - continue; - } - - /* POLLIN */ - while (vring.info->avail_idx != - le16toh(vring.vr.avail->idx)) { - /* read header element */ - avail_idx = - vring.info->avail_idx & - (vring.vr.num - 1); - desc_idx = le16toh( - vring.vr.avail->ring[avail_idx]); - desc = &vring.vr.desc[desc_idx]; -#ifdef DEBUG - mpsslog("%s() %d: avail_idx=%d ", - __func__, __LINE__, - vring.info->avail_idx); - mpsslog("vring.vr.num=%d desc=%p\n", - vring.vr.num, desc); -#endif - status = header_error_check(desc); - ret = read_header( - mic->mic_virtblk.virtio_block_fd, - &hdr, desc_idx); - if (ret < 0) { - mpsslog("%s() %d %s: ret=%d %s\n", - __func__, __LINE__, - mic->name, ret, - strerror(errno)); - break; - } - /* buffer element */ - piov = iovec; - status = 0; - fos = mic->mic_virtblk.backend_addr + - (hdr.sector * SECTOR_SIZE); - buffer_desc_idx = next_desc(desc); - desc_idx = buffer_desc_idx; - for (desc = &vring.vr.desc[buffer_desc_idx]; - desc->flags & VRING_DESC_F_NEXT; - desc_idx = next_desc(desc), - desc = &vring.vr.desc[desc_idx]) { - piov->iov_len = desc->len; - piov->iov_base = fos; - piov++; - fos += desc->len; - } - /* Returning NULLs for VIRTIO_BLK_T_GET_ID. */ - if (hdr.type & ~(VIRTIO_BLK_T_OUT | - VIRTIO_BLK_T_GET_ID)) { - /* - VIRTIO_BLK_T_IN - does not do - anything. Probably for documenting. - VIRTIO_BLK_T_SCSI_CMD - for - virtio_scsi. - VIRTIO_BLK_T_FLUSH - turned off in - config space. - VIRTIO_BLK_T_BARRIER - defined but not - used in anywhere. - */ - mpsslog("%s() %d: type %x ", - __func__, __LINE__, - hdr.type); - mpsslog("is not supported\n"); - status = -ENOTSUP; - - } else { - ret = transfer_blocks( - mic->mic_virtblk.virtio_block_fd, - iovec, - piov - iovec); - if (ret < 0 && - status != 0) - status = ret; - } - /* write status and update used pointer */ - if (status != 0) - status = status_error_check(desc); - ret = write_status( - mic->mic_virtblk.virtio_block_fd, - &status); -#ifdef DEBUG - mpsslog("%s() %d: write status=%d on desc=%p\n", - __func__, __LINE__, - status, desc); -#endif - } - } - free(iovec); -_stop_virtblk: - stop_virtblk(mic); -_close_backend: - close_backend(mic); - } /* forever */ - - pthread_exit(NULL); -} - -static void -reset(struct mic_info *mic) -{ -#define RESET_TIMEOUT 120 - int i = RESET_TIMEOUT; - setsysfs(mic->name, "state", "reset"); - while (i) { - char *state; - state = readsysfs(mic->name, "state"); - if (!state) - goto retry; - mpsslog("%s: %s %d state %s\n", - mic->name, __func__, __LINE__, state); - - if (!strcmp(state, "ready")) { - free(state); - break; - } - free(state); -retry: - sleep(1); - i--; - } -} - -static int -get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status) -{ - if (!strcmp(shutdown_status, "nop")) - return MIC_NOP; - if (!strcmp(shutdown_status, "crashed")) - return MIC_CRASHED; - if (!strcmp(shutdown_status, "halted")) - return MIC_HALTED; - if (!strcmp(shutdown_status, "poweroff")) - return MIC_POWER_OFF; - if (!strcmp(shutdown_status, "restart")) - return MIC_RESTART; - mpsslog("%s: BUG invalid status %s\n", mic->name, shutdown_status); - /* Invalid state */ - assert(0); -}; - -static int get_mic_state(struct mic_info *mic) -{ - char *state = NULL; - enum mic_states mic_state; - - while (!state) { - state = readsysfs(mic->name, "state"); - sleep(1); - } - mpsslog("%s: %s %d state %s\n", - mic->name, __func__, __LINE__, state); - - if (!strcmp(state, "ready")) { - mic_state = MIC_READY; - } else if (!strcmp(state, "booting")) { - mic_state = MIC_BOOTING; - } else if (!strcmp(state, "online")) { - mic_state = MIC_ONLINE; - } else if (!strcmp(state, "shutting_down")) { - mic_state = MIC_SHUTTING_DOWN; - } else if (!strcmp(state, "reset_failed")) { - mic_state = MIC_RESET_FAILED; - } else if (!strcmp(state, "resetting")) { - mic_state = MIC_RESETTING; - } else { - mpsslog("%s: BUG invalid state %s\n", mic->name, state); - assert(0); - } - - free(state); - return mic_state; -}; - -static void mic_handle_shutdown(struct mic_info *mic) -{ -#define SHUTDOWN_TIMEOUT 60 - int i = SHUTDOWN_TIMEOUT; - char *shutdown_status; - while (i) { - shutdown_status = readsysfs(mic->name, "shutdown_status"); - if (!shutdown_status) { - sleep(1); - continue; - } - mpsslog("%s: %s %d shutdown_status %s\n", - mic->name, __func__, __LINE__, shutdown_status); - switch (get_mic_shutdown_status(mic, shutdown_status)) { - case MIC_RESTART: - mic->restart = 1; - case MIC_HALTED: - case MIC_POWER_OFF: - case MIC_CRASHED: - free(shutdown_status); - goto reset; - default: - break; - } - free(shutdown_status); - sleep(1); - i--; - } -reset: - if (!i) - mpsslog("%s: %s %d timing out waiting for shutdown_status %s\n", - mic->name, __func__, __LINE__, shutdown_status); - reset(mic); -} - -static int open_state_fd(struct mic_info *mic) -{ - char pathname[PATH_MAX]; - int fd; - - snprintf(pathname, PATH_MAX - 1, "%s/%s/%s", - MICSYSFSDIR, mic->name, "state"); - - fd = open(pathname, O_RDONLY); - if (fd < 0) - mpsslog("%s: opening file %s failed %s\n", - mic->name, pathname, strerror(errno)); - return fd; -} - -static int block_till_state_change(int fd, struct mic_info *mic) -{ - struct pollfd ufds[1]; - char value[PAGE_SIZE]; - int ret; - - ufds[0].fd = fd; - ufds[0].events = POLLERR | POLLPRI; - ret = poll(ufds, 1, -1); - if (ret < 0) { - mpsslog("%s: %s %d poll failed %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - return ret; - } - - ret = lseek(fd, 0, SEEK_SET); - if (ret < 0) { - mpsslog("%s: %s %d Failed to seek to 0: %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - return ret; - } - - ret = read(fd, value, sizeof(value)); - if (ret < 0) { - mpsslog("%s: %s %d Failed to read sysfs entry: %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - return ret; - } - - return 0; -} - -static void * -mic_config(void *arg) -{ - struct mic_info *mic = (struct mic_info *)arg; - int fd, ret, stat = 0; - - fd = open_state_fd(mic); - if (fd < 0) { - mpsslog("%s: %s %d open state fd failed %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - goto exit; - } - - do { - ret = block_till_state_change(fd, mic); - if (ret < 0) { - mpsslog("%s: %s %d block_till_state_change error %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - goto close_exit; - } - - switch (get_mic_state(mic)) { - case MIC_SHUTTING_DOWN: - mic_handle_shutdown(mic); - break; - case MIC_READY: - case MIC_RESET_FAILED: - ret = kill(mic->pid, SIGTERM); - mpsslog("%s: %s %d kill pid %d ret %d\n", - mic->name, __func__, __LINE__, - mic->pid, ret); - if (!ret) { - ret = waitpid(mic->pid, &stat, - WIFSIGNALED(stat)); - mpsslog("%s: %s %d waitpid ret %d pid %d\n", - mic->name, __func__, __LINE__, - ret, mic->pid); - } - if (mic->boot_on_resume) { - setsysfs(mic->name, "state", "boot"); - mic->boot_on_resume = 0; - } - goto close_exit; - default: - break; - } - } while (1); - -close_exit: - close(fd); -exit: - init_mic(mic); - pthread_exit(NULL); -} - -static void -set_cmdline(struct mic_info *mic) -{ - char buffer[PATH_MAX]; - int len; - - len = snprintf(buffer, PATH_MAX, - "clocksource=tsc highres=off nohz=off "); - len += snprintf(buffer + len, PATH_MAX - len, - "cpufreq_on;corec6_off;pc3_off;pc6_off "); - len += snprintf(buffer + len, PATH_MAX - len, - "ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0", - mic->id + 1); - - setsysfs(mic->name, "cmdline", buffer); - mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer); - snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id + 1); - mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer); -} - -static void -set_log_buf_info(struct mic_info *mic) -{ - int fd; - off_t len; - char system_map[] = "/lib/firmware/mic/System.map"; - char *map, *temp, log_buf[17] = {'\0'}; - - fd = open(system_map, O_RDONLY); - if (fd < 0) { - mpsslog("%s: Opening System.map failed: %d\n", - mic->name, errno); - return; - } - len = lseek(fd, 0, SEEK_END); - if (len < 0) { - mpsslog("%s: Reading System.map size failed: %d\n", - mic->name, errno); - close(fd); - return; - } - map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); - if (map == MAP_FAILED) { - mpsslog("%s: mmap of System.map failed: %d\n", - mic->name, errno); - close(fd); - return; - } - temp = strstr(map, "__log_buf"); - if (!temp) { - mpsslog("%s: __log_buf not found: %d\n", mic->name, errno); - munmap(map, len); - close(fd); - return; - } - strncpy(log_buf, temp - 19, 16); - setsysfs(mic->name, "log_buf_addr", log_buf); - mpsslog("%s: log_buf_addr: %s\n", mic->name, log_buf); - temp = strstr(map, "log_buf_len"); - if (!temp) { - mpsslog("%s: log_buf_len not found: %d\n", mic->name, errno); - munmap(map, len); - close(fd); - return; - } - strncpy(log_buf, temp - 19, 16); - setsysfs(mic->name, "log_buf_len", log_buf); - mpsslog("%s: log_buf_len: %s\n", mic->name, log_buf); - munmap(map, len); - close(fd); -} - -static void -change_virtblk_backend(int x, siginfo_t *siginfo, void *p) -{ - struct mic_info *mic; - - for (mic = mic_list.next; mic != NULL; mic = mic->next) - mic->mic_virtblk.signaled = 1/* true */; -} - -static void -set_mic_boot_params(struct mic_info *mic) -{ - set_log_buf_info(mic); - set_cmdline(mic); -} - -static void * -init_mic(void *arg) -{ - struct mic_info *mic = (struct mic_info *)arg; - struct sigaction ignore = { - .sa_flags = 0, - .sa_handler = SIG_IGN - }; - struct sigaction act = { - .sa_flags = SA_SIGINFO, - .sa_sigaction = change_virtblk_backend, - }; - char buffer[PATH_MAX]; - int err, fd; - - /* - * Currently, one virtio block device is supported for each MIC card - * at a time. Any user (or test) can send a SIGUSR1 to the MIC daemon. - * The signal informs the virtio block backend about a change in the - * configuration file which specifies the virtio backend file name on - * the host. Virtio block backend then re-reads the configuration file - * and switches to the new block device. This signalling mechanism may - * not be required once multiple virtio block devices are supported by - * the MIC daemon. - */ - sigaction(SIGUSR1, &ignore, NULL); -retry: - fd = open_state_fd(mic); - if (fd < 0) { - mpsslog("%s: %s %d open state fd failed %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - sleep(2); - goto retry; - } - - if (mic->restart) { - snprintf(buffer, PATH_MAX, "boot"); - setsysfs(mic->name, "state", buffer); - mpsslog("%s restarting mic %d\n", - mic->name, mic->restart); - mic->restart = 0; - } - - while (1) { - while (block_till_state_change(fd, mic)) { - mpsslog("%s: %s %d block_till_state_change error %s\n", - mic->name, __func__, __LINE__, strerror(errno)); - sleep(2); - continue; - } - - if (get_mic_state(mic) == MIC_BOOTING) - break; - } - - mic->pid = fork(); - switch (mic->pid) { - case 0: - add_virtio_device(mic, &virtcons_dev_page.dd); - add_virtio_device(mic, &virtnet_dev_page.dd); - err = pthread_create(&mic->mic_console.console_thread, NULL, - virtio_console, mic); - if (err) - mpsslog("%s virtcons pthread_create failed %s\n", - mic->name, strerror(err)); - err = pthread_create(&mic->mic_net.net_thread, NULL, - virtio_net, mic); - if (err) - mpsslog("%s virtnet pthread_create failed %s\n", - mic->name, strerror(err)); - err = pthread_create(&mic->mic_virtblk.block_thread, NULL, - virtio_block, mic); - if (err) - mpsslog("%s virtblk pthread_create failed %s\n", - mic->name, strerror(err)); - sigemptyset(&act.sa_mask); - err = sigaction(SIGUSR1, &act, NULL); - if (err) - mpsslog("%s sigaction SIGUSR1 failed %s\n", - mic->name, strerror(errno)); - while (1) - sleep(60); - case -1: - mpsslog("fork failed MIC name %s id %d errno %d\n", - mic->name, mic->id, errno); - break; - default: - err = pthread_create(&mic->config_thread, NULL, - mic_config, mic); - if (err) - mpsslog("%s mic_config pthread_create failed %s\n", - mic->name, strerror(err)); - } - - return NULL; -} - -static void -start_daemon(void) -{ - struct mic_info *mic; - int err; - - for (mic = mic_list.next; mic; mic = mic->next) { - set_mic_boot_params(mic); - err = pthread_create(&mic->init_thread, NULL, init_mic, mic); - if (err) - mpsslog("%s init_mic pthread_create failed %s\n", - mic->name, strerror(err)); - } - - while (1) - sleep(60); -} - -static int -init_mic_list(void) -{ - struct mic_info *mic = &mic_list; - struct dirent *file; - DIR *dp; - int cnt = 0; - - dp = opendir(MICSYSFSDIR); - if (!dp) - return 0; - - while ((file = readdir(dp)) != NULL) { - if (!strncmp(file->d_name, "mic", 3)) { - mic->next = calloc(1, sizeof(struct mic_info)); - if (mic->next) { - mic = mic->next; - mic->id = atoi(&file->d_name[3]); - mic->name = malloc(strlen(file->d_name) + 16); - if (mic->name) - strcpy(mic->name, file->d_name); - mpsslog("MIC name %s id %d\n", mic->name, - mic->id); - cnt++; - } - } - } - - closedir(dp); - return cnt; -} - -void -mpsslog(char *format, ...) -{ - va_list args; - char buffer[4096]; - char ts[52], *ts1; - time_t t; - - if (logfp == NULL) - return; - - va_start(args, format); - vsprintf(buffer, format, args); - va_end(args); - - time(&t); - ts1 = ctime_r(&t, ts); - ts1[strlen(ts1) - 1] = '\0'; - fprintf(logfp, "%s: %s", ts1, buffer); - - fflush(logfp); -} - -int -main(int argc, char *argv[]) -{ - int cnt; - pid_t pid; - - myname = argv[0]; - - logfp = fopen(LOGFILE_NAME, "a+"); - if (!logfp) { - fprintf(stderr, "cannot open logfile '%s'\n", LOGFILE_NAME); - exit(1); - } - pid = fork(); - switch (pid) { - case 0: - break; - case -1: - exit(2); - default: - exit(0); - } - - mpsslog("MIC Daemon start\n"); - - cnt = init_mic_list(); - if (cnt == 0) { - mpsslog("MIC module not loaded\n"); - exit(3); - } - mpsslog("MIC found %d devices\n", cnt); - - start_daemon(); - - exit(0); -} diff --git a/samples/mic/mpssd/mpssd.h b/samples/mic/mpssd/mpssd.h deleted file mode 100644 index 5f98bdafe653..000000000000 --- a/samples/mic/mpssd/mpssd.h +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC User Space Tools. - */ -#ifndef _MPSSD_H_ -#define _MPSSD_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MICSYSFSDIR "/sys/class/mic" -#define LOGFILE_NAME "/var/log/mpssd" -#define PAGE_SIZE 4096 - -struct mic_console_info { - pthread_t console_thread; - int virtio_console_fd; - void *console_dp; -}; - -struct mic_net_info { - pthread_t net_thread; - int virtio_net_fd; - int tap_fd; - void *net_dp; -}; - -struct mic_virtblk_info { - pthread_t block_thread; - int virtio_block_fd; - void *block_dp; - volatile sig_atomic_t signaled; - char *backend_file; - int backend; - void *backend_addr; - long backend_size; -}; - -struct mic_info { - int id; - char *name; - pthread_t config_thread; - pthread_t init_thread; - pid_t pid; - struct mic_console_info mic_console; - struct mic_net_info mic_net; - struct mic_virtblk_info mic_virtblk; - int restart; - int boot_on_resume; - struct mic_info *next; -}; - -__attribute__((format(printf, 1, 2))) -void mpsslog(char *format, ...); -char *readsysfs(char *dir, char *entry); -int setsysfs(char *dir, char *entry, char *value); -#endif diff --git a/samples/mic/mpssd/sysfs.c b/samples/mic/mpssd/sysfs.c deleted file mode 100644 index 3fb08eb7ed9d..000000000000 --- a/samples/mic/mpssd/sysfs.c +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 Intel Corporation. - * - * Intel MIC User Space Tools. - */ - -#include "mpssd.h" - -#define PAGE_SIZE 4096 - -char * -readsysfs(char *dir, char *entry) -{ - char filename[PATH_MAX]; - char value[PAGE_SIZE]; - char *string = NULL; - int fd; - int len; - - if (dir == NULL) - snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry); - else - snprintf(filename, PATH_MAX, - "%s/%s/%s", MICSYSFSDIR, dir, entry); - - fd = open(filename, O_RDONLY); - if (fd < 0) { - mpsslog("Failed to open sysfs entry '%s': %s\n", - filename, strerror(errno)); - return NULL; - } - - len = read(fd, value, sizeof(value)); - if (len < 0) { - mpsslog("Failed to read sysfs entry '%s': %s\n", - filename, strerror(errno)); - goto readsys_ret; - } - if (len == 0) - goto readsys_ret; - - value[len - 1] = '\0'; - - string = malloc(strlen(value) + 1); - if (string) - strcpy(string, value); - -readsys_ret: - close(fd); - return string; -} - -int -setsysfs(char *dir, char *entry, char *value) -{ - char filename[PATH_MAX]; - char *oldvalue; - int fd, ret = 0; - - if (dir == NULL) - snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry); - else - snprintf(filename, PATH_MAX, "%s/%s/%s", - MICSYSFSDIR, dir, entry); - - oldvalue = readsysfs(dir, entry); - - fd = open(filename, O_RDWR); - if (fd < 0) { - ret = errno; - mpsslog("Failed to open sysfs entry '%s': %s\n", - filename, strerror(errno)); - goto done; - } - - if (!oldvalue || strcmp(value, oldvalue)) { - if (write(fd, value, strlen(value)) < 0) { - ret = errno; - mpsslog("Failed to write new sysfs entry '%s': %s\n", - filename, strerror(errno)); - } - } - close(fd); -done: - if (oldvalue) - free(oldvalue); - return ret; -} -- cgit v1.2.3 From f27891abb7930d7a92bee2a47648a4a8708652d3 Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Wed, 28 Oct 2020 16:17:03 +0100 Subject: usb: typec: add missing MODULE_DEVICE_TABLE() to stusb160x When stusb160x driver is built as a module, no modalias information is available, and it prevents the module to be loaded by udev. Add MODULE_DEVICE_TABLE() to fix this issue. Fixes: da0cb6310094 ("usb: typec: add support for STUSB160x Type-C controller family") Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20201028151703.31195-1-amelie.delaunay@st.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/stusb160x.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index da7f1957bcb3..42076697e26c 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -633,6 +633,7 @@ static const struct of_device_id stusb160x_of_match[] = { { .compatible = "st,stusb1600", .data = &stusb1600_regmap_config}, {}, }; +MODULE_DEVICE_TABLE(of, stusb160x_of_match); static int stusb160x_probe(struct i2c_client *client) { -- cgit v1.2.3 From befa1c2d65c639a7aefa793d36d7229732f14642 Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Wed, 28 Oct 2020 17:33:09 +0100 Subject: usb: typec: stusb160x: fix signedness comparison issue with enum variables chip->port_type and chip->pwr_opmode are enums and when GCC considers them as unsigned, the conditions are never met. This patch takes advantage of the ret variable and fixes the following warnings: drivers/usb/typec/stusb160x.c:548 stusb160x_get_fw_caps() warn: unsigned 'chip->port_type' is never less than zero. drivers/usb/typec/stusb160x.c:570 stusb160x_get_fw_caps() warn: unsigned 'chip->pwr_opmode' is never less than zero. Fixes: da0cb6310094 ("usb: typec: add support for STUSB160x Type-C controller family") Reported-by: kernel test robot Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20201028163309.12878-1-amelie.delaunay@st.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/stusb160x.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index 42076697e26c..2a618f02f4f1 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -544,11 +544,10 @@ static int stusb160x_get_fw_caps(struct stusb160x *chip, */ ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); if (!ret) { - chip->port_type = typec_find_port_power_role(cap_str); - if ((int)chip->port_type < 0) { - ret = chip->port_type; + ret = typec_find_port_power_role(cap_str); + if (ret < 0) return ret; - } + chip->port_type = ret; } chip->capability.type = chip->port_type; @@ -565,16 +564,13 @@ static int stusb160x_get_fw_caps(struct stusb160x *chip, */ ret = fwnode_property_read_string(fwnode, "power-opmode", &cap_str); if (!ret) { - chip->pwr_opmode = typec_find_pwr_opmode(cap_str); + ret = typec_find_pwr_opmode(cap_str); /* Power delivery not yet supported */ - if ((int)chip->pwr_opmode < 0 || - chip->pwr_opmode == TYPEC_PWR_MODE_PD) { - ret = (int)chip->pwr_opmode < 0 ? chip->pwr_opmode : - -EINVAL; - dev_err(chip->dev, "bad power operation mode: %d\n", - chip->pwr_opmode); - return ret; + if (ret < 0 || ret == TYPEC_PWR_MODE_PD) { + dev_err(chip->dev, "bad power operation mode: %d\n", ret); + return -EINVAL; } + chip->pwr_opmode = ret; } return 0; -- cgit v1.2.3 From a75e2d604da8219d928be010bde37c3ab0ebd466 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 28 Oct 2020 22:31:22 +0200 Subject: xhci: Fix sizeof() mismatch An incorrect sizeof() is being used, sizeof(rhub->ports) is not correct, it should be sizeof(*rhub->ports). This bug did not cause any issues because it just so happens the sizes are the same. Fixes: bcaa9d5c5900 ("xhci: Create new structures to store xhci port information") Signed-off-by: Colin Ian King Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20201028203124.375344-2-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index fe405cd38dbc..138ba4528dd3 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2252,8 +2252,8 @@ static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, if (!rhub->num_ports) return; - rhub->ports = kcalloc_node(rhub->num_ports, sizeof(rhub->ports), flags, - dev_to_node(dev)); + rhub->ports = kcalloc_node(rhub->num_ports, sizeof(*rhub->ports), + flags, dev_to_node(dev)); for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { if (xhci->hw_ports[i].rhub != rhub || xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY) -- cgit v1.2.3 From 2a632815683d2d34df52b701a36fe5ac6654e719 Mon Sep 17 00:00:00 2001 From: Sandeep Singh Date: Wed, 28 Oct 2020 22:31:23 +0200 Subject: usb: xhci: Workaround for S3 issue on AMD SNPS 3.0 xHC On some platform of AMD, S3 fails with HCE and SRE errors. To fix this, need to disable a bit which is enable in sparse controller. Cc: stable@vger.kernel.org #v4.19+ Signed-off-by: Sanket Goswami Signed-off-by: Sandeep Singh Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20201028203124.375344-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 17 +++++++++++++++++ drivers/usb/host/xhci.h | 1 + 2 files changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index c26c06e5c88c..bf89172c43ca 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -23,6 +23,8 @@ #define SSIC_PORT_CFG2_OFFSET 0x30 #define PROG_DONE (1 << 30) #define SSIC_PORT_UNUSED (1 << 31) +#define SPARSE_DISABLE_BIT 17 +#define SPARSE_CNTL_ENABLE 0xC12C /* Device for a quirk */ #define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 @@ -161,6 +163,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) (pdev->device == 0x15e0 || pdev->device == 0x15e1)) xhci->quirks |= XHCI_SNPS_BROKEN_SUSPEND; + if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x15e5) + xhci->quirks |= XHCI_DISABLE_SPARSE; + if (pdev->vendor == PCI_VENDOR_ID_AMD) xhci->quirks |= XHCI_TRUST_TX_LENGTH; @@ -498,6 +503,15 @@ static void xhci_pme_quirk(struct usb_hcd *hcd) readl(reg); } +static void xhci_sparse_control_quirk(struct usb_hcd *hcd) +{ + u32 reg; + + reg = readl(hcd->regs + SPARSE_CNTL_ENABLE); + reg &= ~BIT(SPARSE_DISABLE_BIT); + writel(reg, hcd->regs + SPARSE_CNTL_ENABLE); +} + static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -517,6 +531,9 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) if (xhci->quirks & XHCI_SSIC_PORT_UNUSED) xhci_ssic_port_unused_quirk(hcd, true); + if (xhci->quirks & XHCI_DISABLE_SPARSE) + xhci_sparse_control_quirk(hcd); + ret = xhci_suspend(xhci, do_wakeup); if (ret && (xhci->quirks & XHCI_SSIC_PORT_UNUSED)) xhci_ssic_port_unused_quirk(hcd, false); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 8be88379c0fb..ebb359ebb261 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1877,6 +1877,7 @@ struct xhci_hcd { #define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35) #define XHCI_RENESAS_FW_QUIRK BIT_ULL(36) #define XHCI_SKIP_PHY_INIT BIT_ULL(37) +#define XHCI_DISABLE_SPARSE BIT_ULL(38) unsigned int num_active_eps; unsigned int limit_active_eps; -- cgit v1.2.3 From 712da5fc4a66fbb1f144742e1b808498e932009b Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Wed, 28 Oct 2020 22:31:24 +0200 Subject: xhci: Don't create stream debugfs files with spinlock held. Creating debugfs files while loding the spin_lock_irqsave(xhci->lock) creates a lock dependecy that could possibly deadlock. Lockdep warns: ===================================================== WARNING: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected 5.10.0-rc1pdx86+ #8 Not tainted ----------------------------------------------------- systemd-udevd/386 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire: ffffffffb1a94038 (pin_fs_lock){+.+.}-{2:2}, at: simple_pin_fs+0x22/0xa0 and this task is already holding: ffff9e7b87fbc430 (&xhci->lock){-.-.}-{2:2}, at: xhci_alloc_streams+0x5f9/0x810 which would create a new lock dependency: (&xhci->lock){-.-.}-{2:2} -> (pin_fs_lock){+.+.}-{2:2} Create the files a bit later after lock is released. Fixes: 673d74683627 ("usb: xhci: add debugfs support for ep with stream") CC: Li Jun Reported-by: Hans de Goede Reported-by: Mike Galbraith Tested-by: Hans de Goede Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20201028203124.375344-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 482fe8c5e3b4..d4a8d0efbbc4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3533,11 +3533,14 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg(xhci, "Slot %u ep ctx %u now has streams.\n", udev->slot_id, ep_index); vdev->eps[ep_index].ep_state |= EP_HAS_STREAMS; - xhci_debugfs_create_stream_files(xhci, vdev, ep_index); } xhci_free_command(xhci, config_cmd); spin_unlock_irqrestore(&xhci->lock, flags); + for (i = 0; i < num_eps; i++) { + ep_index = xhci_get_endpoint_index(&eps[i]->desc); + xhci_debugfs_create_stream_files(xhci, vdev, ep_index); + } /* Subtract 1 for stream 0, which drivers can't use */ return num_streams - 1; -- cgit v1.2.3 From 0f5cef2b2904b8d41d34ecb6c5d5bf2f00562b1b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 26 Oct 2020 17:01:54 +0100 Subject: coresight: add module license When built as a loadable module, coresight now causes a warning about missing license information. WARNING: modpost: missing MODULE_LICENSE() in drivers/hwtracing/coresight/coresight.o Fixes: 8e264c52e1da ("coresight: core: Allow the coresight core driver to be built as a module") Signed-off-by: Arnd Bergmann Cc: Mathieu Poirier Link: https://lore.kernel.org/r/20201026160205.3704789-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 6994c1309b2b..cc9e8025c533 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -1689,6 +1689,7 @@ static void __exit coresight_exit(void) module_init(coresight_init); module_exit(coresight_exit); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Pratik Patel "); MODULE_AUTHOR("Mathieu Poirier "); MODULE_DESCRIPTION("Arm CoreSight tracer driver"); -- cgit v1.2.3 From a3a0ded3ed38f4eafa86cef61799d50250da4dbe Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 8 Jul 2020 16:45:55 +0200 Subject: drm/vc4: hdmi: Add a name to the codec DAI component Since the components for a given device in ASoC are identified by their name, it makes sense to add one even though it's not strictly necessary. Signed-off-by: Maxime Ripard Reviewed-by: Dave Stevenson Link: https://patchwork.freedesktop.org/patch/msgid/20200708144555.718404-1-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 73ac52714e54..95779d50cca0 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1077,6 +1077,7 @@ static const struct snd_soc_dapm_route vc4_hdmi_audio_routes[] = { }; static const struct snd_soc_component_driver vc4_hdmi_audio_component_drv = { + .name = "vc4-hdmi-codec-dai-component", .controls = vc4_hdmi_audio_controls, .num_controls = ARRAY_SIZE(vc4_hdmi_audio_controls), .dapm_widgets = vc4_hdmi_audio_widgets, -- cgit v1.2.3 From 5066f42c7d3c7766c16ba6e73da514af04d43ff7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 28 Oct 2020 13:37:52 +0100 Subject: drm/vc4: Rework the structure conversion functions Most of the helpers to retrieve vc4 structures from the DRM base structures rely on the fact that the first member of the vc4 structure is the DRM one and just cast the pointers between them. However, this is pretty fragile especially since there's no check to make sure that the DRM structure is indeed at the offset 0 in the structure, so let's use container_of to make it more robust. Signed-off-by: Maxime Ripard Reviewed-by: Dave Stevenson Link: https://patchwork.freedesktop.org/patch/msgid/20201028123752.1733242-1-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_drv.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 90b911fd2a7f..66d4fb16db8f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -287,7 +287,7 @@ struct vc4_bo { static inline struct vc4_bo * to_vc4_bo(struct drm_gem_object *bo) { - return (struct vc4_bo *)bo; + return container_of(to_drm_gem_cma_obj(bo), struct vc4_bo, base); } struct vc4_fence { @@ -300,7 +300,7 @@ struct vc4_fence { static inline struct vc4_fence * to_vc4_fence(struct dma_fence *fence) { - return (struct vc4_fence *)fence; + return container_of(fence, struct vc4_fence, base); } struct vc4_seqno_cb { @@ -347,7 +347,7 @@ struct vc4_plane { static inline struct vc4_plane * to_vc4_plane(struct drm_plane *plane) { - return (struct vc4_plane *)plane; + return container_of(plane, struct vc4_plane, base); } enum vc4_scaling_mode { @@ -423,7 +423,7 @@ struct vc4_plane_state { static inline struct vc4_plane_state * to_vc4_plane_state(struct drm_plane_state *state) { - return (struct vc4_plane_state *)state; + return container_of(state, struct vc4_plane_state, base); } enum vc4_encoder_type { @@ -499,7 +499,7 @@ struct vc4_crtc { static inline struct vc4_crtc * to_vc4_crtc(struct drm_crtc *crtc) { - return (struct vc4_crtc *)crtc; + return container_of(crtc, struct vc4_crtc, base); } static inline const struct vc4_crtc_data * @@ -537,7 +537,7 @@ struct vc4_crtc_state { static inline struct vc4_crtc_state * to_vc4_crtc_state(struct drm_crtc_state *crtc_state) { - return (struct vc4_crtc_state *)crtc_state; + return container_of(crtc_state, struct vc4_crtc_state, base); } #define V3D_READ(offset) readl(vc4->v3d->regs + offset) -- cgit v1.2.3 From 5fca3f062879f8e5214c56f3e3e2be6727900f5d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 22 Oct 2020 08:55:03 +0800 Subject: usb: cdns3: gadget: suspicious implicit sign extension The code: trb->length = cpu_to_le32(TRB_BURST_LEN(priv_ep->trb_burst_size) | TRB_LEN(length)); TRB_BURST_LEN(priv_ep->trb_burst_size) may be overflow for int 32 if priv_ep->trb_burst_size is equal or larger than 0x80; Below is the Coverity warning: sign_extension: Suspicious implicit sign extension: priv_ep->trb_burst_size with type u8 (8 bits, unsigned) is promoted in priv_ep->trb_burst_size << 24 to type int (32 bits, signed), then sign-extended to type unsigned long (64 bits, unsigned). If priv_ep->trb_burst_size << 24 is greater than 0x7FFFFFFF, the upper bits of the result will all be 1. To fix it, it needs to add an explicit cast to unsigned int type for ((p) << 24). Reviewed-by: Jun Li Signed-off-by: Peter Chen --- drivers/usb/cdns3/gadget.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index 1ccecd237530..737377913788 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -1072,7 +1072,7 @@ struct cdns3_trb { #define TRB_TDL_SS_SIZE_GET(p) (((p) & GENMASK(23, 17)) >> 17) /* transfer_len bitmasks - bits 31:24 */ -#define TRB_BURST_LEN(p) (((p) << 24) & GENMASK(31, 24)) +#define TRB_BURST_LEN(p) ((unsigned int)((p) << 24) & GENMASK(31, 24)) #define TRB_BURST_LEN_GET(p) (((p) & GENMASK(31, 24)) >> 24) /* Data buffer pointer bitmasks*/ -- cgit v1.2.3 From a62f68f5ca53ab61cba2f0a410d0add7a6d54a52 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 23 Oct 2020 17:35:46 +0200 Subject: cpufreq: Introduce cpufreq_driver_test_flags() Add a helper function to test the flags of the cpufreq driver in use againt a given flags mask. In particular, this will be needed to test the CPUFREQ_NEED_UPDATE_LIMITS cpufreq driver flag in the schedutil governor. Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 12 ++++++++++++ include/linux/cpufreq.h | 1 + 2 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index ea58337fb65f..336b5e94cbc8 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1907,6 +1907,18 @@ void cpufreq_resume(void) } } +/** + * cpufreq_driver_test_flags - Test cpufreq driver's flags against given ones. + * @flags: Flags to test against the current cpufreq driver's flags. + * + * Assumes that the driver is there, so callers must ensure that this is the + * case. + */ +bool cpufreq_driver_test_flags(u16 flags) +{ + return !!(cpufreq_driver->flags & flags); +} + /** * cpufreq_get_current_driver - return current driver's name * diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 038ed83aab41..1eaa04f1bae6 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -433,6 +433,7 @@ struct cpufreq_driver { int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); +bool cpufreq_driver_test_flags(u16 flags); const char *cpufreq_get_current_driver(void); void *cpufreq_get_driver_data(void); -- cgit v1.2.3 From b40813ddcd6bf9f01d020804e4cb8febc480b9e4 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 28 Oct 2020 15:24:34 +0800 Subject: nbd: don't update block size after device is started Mounted NBD device can be resized, one use case is rbd-nbd. Fix the issue by setting up default block size, then not touch it in nbd_size_update() any more. This kind of usage is aligned with loop which has same use case too. Cc: stable@vger.kernel.org Fixes: c8a83a6b54d0 ("nbd: Use set_blocksize() to set device blocksize") Reported-by: lining Signed-off-by: Ming Lei Cc: Josef Bacik Cc: Jan Kara Tested-by: lining Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 0bed21c0c81b..c4f9ccf5cc2a 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -296,7 +296,7 @@ static void nbd_size_clear(struct nbd_device *nbd) } } -static void nbd_size_update(struct nbd_device *nbd) +static void nbd_size_update(struct nbd_device *nbd, bool start) { struct nbd_config *config = nbd->config; struct block_device *bdev = bdget_disk(nbd->disk, 0); @@ -313,7 +313,8 @@ static void nbd_size_update(struct nbd_device *nbd) if (bdev) { if (bdev->bd_disk) { bd_set_nr_sectors(bdev, nr_sectors); - set_blocksize(bdev, config->blksize); + if (start) + set_blocksize(bdev, config->blksize); } else set_bit(GD_NEED_PART_SCAN, &nbd->disk->state); bdput(bdev); @@ -328,7 +329,7 @@ static void nbd_size_set(struct nbd_device *nbd, loff_t blocksize, config->blksize = blocksize; config->bytesize = blocksize * nr_blocks; if (nbd->task_recv != NULL) - nbd_size_update(nbd); + nbd_size_update(nbd, false); } static void nbd_complete_rq(struct request *req) @@ -1308,7 +1309,7 @@ static int nbd_start_device(struct nbd_device *nbd) args->index = i; queue_work(nbd->recv_workq, &args->work); } - nbd_size_update(nbd); + nbd_size_update(nbd, true); return error; } -- cgit v1.2.3 From f9c9104288da543cd64f186f9e2fba389f415630 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 29 Oct 2020 20:04:59 +0900 Subject: null_blk: Fix zone reset all tracing In the cae of the REQ_OP_ZONE_RESET_ALL operation, the command sector is ignored and the operation is applied to all sequential zones. For these commands, tracing the effect of the command using the command sector to determine the target zone is thus incorrect. Fix null_zone_mgmt() zone condition tracing in the case of REQ_OP_ZONE_RESET_ALL to apply tracing to all sequential zones that are not already empty. Fixes: 766c3297d7e1 ("null_blk: add trace in null_blk_zoned.c") Signed-off-by: Damien Le Moal Cc: stable@vger.kernel.org Signed-off-by: Jens Axboe --- drivers/block/null_blk_zoned.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/block/null_blk_zoned.c b/drivers/block/null_blk_zoned.c index 98056c88926b..b637b16a5f54 100644 --- a/drivers/block/null_blk_zoned.c +++ b/drivers/block/null_blk_zoned.c @@ -475,9 +475,14 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, switch (op) { case REQ_OP_ZONE_RESET_ALL: - for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) - null_reset_zone(dev, &dev->zones[i]); - break; + for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) { + zone = &dev->zones[i]; + if (zone->cond != BLK_ZONE_COND_EMPTY) { + null_reset_zone(dev, zone); + trace_nullb_zone_op(cmd, i, zone->cond); + } + } + return BLK_STS_OK; case REQ_OP_ZONE_RESET: ret = null_reset_zone(dev, zone); break; -- cgit v1.2.3 From aa1c09cb65e2ed17cb8e652bc7ec84e0af1229eb Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 29 Oct 2020 20:05:00 +0900 Subject: null_blk: Fix locking in zoned mode When the zoned mode is enabled in null_blk, Serializing read, write and zone management operations for each zone is necessary to protect device level information for managing zone resources (zone open and closed counters) as well as each zone condition and write pointer position. Commit 35bc10b2eafb ("null_blk: synchronization fix for zoned device") introduced a spinlock to implement this serialization. However, when memory backing is also enabled, GFP_NOIO memory allocations are executed under the spinlock, resulting in might_sleep() warnings. Furthermore, the zone_lock spinlock is locked/unlocked using spin_lock_irq/spin_unlock_irq, similarly to the memory backing code with the nullb->lock spinlock. This nested use of irq locks wrecks the irq enabled/disabled state. Fix all this by introducing a bitmap for per-zone lock, with locking implemented using wait_on_bit_lock_io() and clear_and_wake_up_bit(). This locking mechanism allows keeping a zone locked while executing null_process_cmd(), serializing all operations to the zone while allowing to sleep during memory backing allocation with GFP_NOIO. Device level zone resource management information is protected using a spinlock which is not held while executing null_process_cmd(); Fixes: 35bc10b2eafb ("null_blk: synchronization fix for zoned device") Signed-off-by: Damien Le Moal Signed-off-by: Jens Axboe --- drivers/block/null_blk.h | 3 +- drivers/block/null_blk_zoned.c | 104 +++++++++++++++++++++++++++++++---------- 2 files changed, 82 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/block/null_blk.h b/drivers/block/null_blk.h index 3176b269b822..cfd00ad40355 100644 --- a/drivers/block/null_blk.h +++ b/drivers/block/null_blk.h @@ -47,7 +47,8 @@ struct nullb_device { unsigned int nr_zones_closed; struct blk_zone *zones; sector_t zone_size_sects; - spinlock_t zone_lock; + spinlock_t zone_dev_lock; + unsigned long *zone_locks; unsigned long size; /* device size in MB */ unsigned long completion_nsec; /* time in ns to complete a request */ diff --git a/drivers/block/null_blk_zoned.c b/drivers/block/null_blk_zoned.c index b637b16a5f54..8775acbb4f8f 100644 --- a/drivers/block/null_blk_zoned.c +++ b/drivers/block/null_blk_zoned.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include "null_blk.h" #define CREATE_TRACE_POINTS @@ -45,7 +46,13 @@ int null_init_zoned_dev(struct nullb_device *dev, struct request_queue *q) if (!dev->zones) return -ENOMEM; - spin_lock_init(&dev->zone_lock); + spin_lock_init(&dev->zone_dev_lock); + dev->zone_locks = bitmap_zalloc(dev->nr_zones, GFP_KERNEL); + if (!dev->zone_locks) { + kvfree(dev->zones); + return -ENOMEM; + } + if (dev->zone_nr_conv >= dev->nr_zones) { dev->zone_nr_conv = dev->nr_zones - 1; pr_info("changed the number of conventional zones to %u", @@ -124,15 +131,26 @@ int null_register_zoned_dev(struct nullb *nullb) void null_free_zoned_dev(struct nullb_device *dev) { + bitmap_free(dev->zone_locks); kvfree(dev->zones); } +static inline void null_lock_zone(struct nullb_device *dev, unsigned int zno) +{ + wait_on_bit_lock_io(dev->zone_locks, zno, TASK_UNINTERRUPTIBLE); +} + +static inline void null_unlock_zone(struct nullb_device *dev, unsigned int zno) +{ + clear_and_wake_up_bit(zno, dev->zone_locks); +} + int null_report_zones(struct gendisk *disk, sector_t sector, unsigned int nr_zones, report_zones_cb cb, void *data) { struct nullb *nullb = disk->private_data; struct nullb_device *dev = nullb->dev; - unsigned int first_zone, i; + unsigned int first_zone, i, zno; struct blk_zone zone; int error; @@ -143,17 +161,17 @@ int null_report_zones(struct gendisk *disk, sector_t sector, nr_zones = min(nr_zones, dev->nr_zones - first_zone); trace_nullb_report_zones(nullb, nr_zones); - for (i = 0; i < nr_zones; i++) { + zno = first_zone; + for (i = 0; i < nr_zones; i++, zno++) { /* * Stacked DM target drivers will remap the zone information by * modifying the zone information passed to the report callback. * So use a local copy to avoid corruption of the device zone * array. */ - spin_lock_irq(&dev->zone_lock); - memcpy(&zone, &dev->zones[first_zone + i], - sizeof(struct blk_zone)); - spin_unlock_irq(&dev->zone_lock); + null_lock_zone(dev, zno); + memcpy(&zone, &dev->zones[zno], sizeof(struct blk_zone)); + null_unlock_zone(dev, zno); error = cb(&zone, i, data); if (error) @@ -163,6 +181,10 @@ int null_report_zones(struct gendisk *disk, sector_t sector, return nr_zones; } +/* + * This is called in the case of memory backing from null_process_cmd() + * with the target zone already locked. + */ size_t null_zone_valid_read_len(struct nullb *nullb, sector_t sector, unsigned int len) { @@ -299,22 +321,27 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL) return null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors); + null_lock_zone(dev, zno); + spin_lock(&dev->zone_dev_lock); + switch (zone->cond) { case BLK_ZONE_COND_FULL: /* Cannot write to a full zone */ - return BLK_STS_IOERR; + ret = BLK_STS_IOERR; + goto unlock; case BLK_ZONE_COND_EMPTY: case BLK_ZONE_COND_CLOSED: ret = null_check_zone_resources(dev, zone); if (ret != BLK_STS_OK) - return ret; + goto unlock; break; case BLK_ZONE_COND_IMP_OPEN: case BLK_ZONE_COND_EXP_OPEN: break; default: /* Invalid zone condition */ - return BLK_STS_IOERR; + ret = BLK_STS_IOERR; + goto unlock; } /* @@ -330,11 +357,14 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, else cmd->rq->__sector = sector; } else if (sector != zone->wp) { - return BLK_STS_IOERR; + ret = BLK_STS_IOERR; + goto unlock; } - if (zone->wp + nr_sectors > zone->start + zone->capacity) - return BLK_STS_IOERR; + if (zone->wp + nr_sectors > zone->start + zone->capacity) { + ret = BLK_STS_IOERR; + goto unlock; + } if (zone->cond == BLK_ZONE_COND_CLOSED) { dev->nr_zones_closed--; @@ -345,9 +375,11 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, if (zone->cond != BLK_ZONE_COND_EXP_OPEN) zone->cond = BLK_ZONE_COND_IMP_OPEN; + spin_unlock(&dev->zone_dev_lock); ret = null_process_cmd(cmd, REQ_OP_WRITE, sector, nr_sectors); + spin_lock(&dev->zone_dev_lock); if (ret != BLK_STS_OK) - return ret; + goto unlock; zone->wp += nr_sectors; if (zone->wp == zone->start + zone->capacity) { @@ -357,7 +389,13 @@ static blk_status_t null_zone_write(struct nullb_cmd *cmd, sector_t sector, dev->nr_zones_imp_open--; zone->cond = BLK_ZONE_COND_FULL; } - return BLK_STS_OK; + ret = BLK_STS_OK; + +unlock: + spin_unlock(&dev->zone_dev_lock); + null_unlock_zone(dev, zno); + + return ret; } static blk_status_t null_open_zone(struct nullb_device *dev, struct blk_zone *zone) @@ -468,21 +506,33 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, sector_t sector) { struct nullb_device *dev = cmd->nq->dev; - unsigned int zone_no = null_zone_no(dev, sector); - struct blk_zone *zone = &dev->zones[zone_no]; - blk_status_t ret = BLK_STS_OK; + unsigned int zone_no; + struct blk_zone *zone; + blk_status_t ret; size_t i; - switch (op) { - case REQ_OP_ZONE_RESET_ALL: + if (op == REQ_OP_ZONE_RESET_ALL) { for (i = dev->zone_nr_conv; i < dev->nr_zones; i++) { + null_lock_zone(dev, i); zone = &dev->zones[i]; if (zone->cond != BLK_ZONE_COND_EMPTY) { + spin_lock(&dev->zone_dev_lock); null_reset_zone(dev, zone); + spin_unlock(&dev->zone_dev_lock); trace_nullb_zone_op(cmd, i, zone->cond); } + null_unlock_zone(dev, i); } return BLK_STS_OK; + } + + zone_no = null_zone_no(dev, sector); + zone = &dev->zones[zone_no]; + + null_lock_zone(dev, zone_no); + spin_lock(&dev->zone_dev_lock); + + switch (op) { case REQ_OP_ZONE_RESET: ret = null_reset_zone(dev, zone); break; @@ -496,22 +546,27 @@ static blk_status_t null_zone_mgmt(struct nullb_cmd *cmd, enum req_opf op, ret = null_finish_zone(dev, zone); break; default: - return BLK_STS_NOTSUPP; + ret = BLK_STS_NOTSUPP; + break; } + spin_unlock(&dev->zone_dev_lock); + if (ret == BLK_STS_OK) trace_nullb_zone_op(cmd, zone_no, zone->cond); + null_unlock_zone(dev, zone_no); + return ret; } blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_opf op, sector_t sector, sector_t nr_sectors) { - blk_status_t sts; struct nullb_device *dev = cmd->nq->dev; + unsigned int zno = null_zone_no(dev, sector); + blk_status_t sts; - spin_lock_irq(&dev->zone_lock); switch (op) { case REQ_OP_WRITE: sts = null_zone_write(cmd, sector, nr_sectors, false); @@ -527,9 +582,10 @@ blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_opf op, sts = null_zone_mgmt(cmd, op, sector); break; default: + null_lock_zone(dev, zno); sts = null_process_cmd(cmd, op, sector, nr_sectors); + null_unlock_zone(dev, zno); } - spin_unlock_irq(&dev->zone_lock); return sts; } -- cgit v1.2.3 From 7cb6e22ba011c2dc1e94cc0dc94d9ef199715ed9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 27 Oct 2020 19:11:30 +0200 Subject: xsysace: use platform_get_resource() and platform_get_irq_optional() Use platform_get_resource() to fetch the memory resource and platform_get_irq_optional() to get optional IRQ instead of open-coded variants. IRQ is not supposed to be changed at runtime, so there is no functional change in ace_fsm_yieldirq(). On the other hand we now take first resources instead of last ones to proceed. I can't imagine how broken should be firmware to have a garbage in the first resource slots. But if it the case, it needs to be documented. Signed-off-by: Andy Shevchenko Acked-by: Michal Simek Signed-off-by: Jens Axboe --- drivers/block/xsysace.c | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 8d581c7536fb..eb8ef65778c3 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -443,22 +443,27 @@ static void ace_fix_driveid(u16 *id) #define ACE_FSM_NUM_STATES 11 /* Set flag to exit FSM loop and reschedule tasklet */ -static inline void ace_fsm_yield(struct ace_device *ace) +static inline void ace_fsm_yieldpoll(struct ace_device *ace) { - dev_dbg(ace->dev, "ace_fsm_yield()\n"); tasklet_schedule(&ace->fsm_tasklet); ace->fsm_continue_flag = 0; } +static inline void ace_fsm_yield(struct ace_device *ace) +{ + dev_dbg(ace->dev, "%s()\n", __func__); + ace_fsm_yieldpoll(ace); +} + /* Set flag to exit FSM loop and wait for IRQ to reschedule tasklet */ static inline void ace_fsm_yieldirq(struct ace_device *ace) { dev_dbg(ace->dev, "ace_fsm_yieldirq()\n"); - if (!ace->irq) - /* No IRQ assigned, so need to poll */ - tasklet_schedule(&ace->fsm_tasklet); - ace->fsm_continue_flag = 0; + if (ace->irq > 0) + ace->fsm_continue_flag = 0; + else + ace_fsm_yieldpoll(ace); } static bool ace_has_next_request(struct request_queue *q) @@ -1053,12 +1058,12 @@ static int ace_setup(struct ace_device *ace) ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ); /* Now we can hook up the irq handler */ - if (ace->irq) { + if (ace->irq > 0) { rc = request_irq(ace->irq, ace_interrupt, 0, "systemace", ace); if (rc) { /* Failure - fall back to polled mode */ dev_err(ace->dev, "request_irq failed\n"); - ace->irq = 0; + ace->irq = rc; } } @@ -1110,7 +1115,7 @@ static void ace_teardown(struct ace_device *ace) tasklet_kill(&ace->fsm_tasklet); - if (ace->irq) + if (ace->irq > 0) free_irq(ace->irq, ace); iounmap(ace->baseaddr); @@ -1123,11 +1128,6 @@ static int ace_alloc(struct device *dev, int id, resource_size_t physaddr, int rc; dev_dbg(dev, "ace_alloc(%p)\n", dev); - if (!physaddr) { - rc = -ENODEV; - goto err_noreg; - } - /* Allocate and initialize the ace device structure */ ace = kzalloc(sizeof(struct ace_device), GFP_KERNEL); if (!ace) { @@ -1153,7 +1153,6 @@ err_setup: dev_set_drvdata(dev, NULL); kfree(ace); err_alloc: -err_noreg: dev_err(dev, "could not initialize device, err=%i\n", rc); return rc; } @@ -1176,10 +1175,11 @@ static void ace_free(struct device *dev) static int ace_probe(struct platform_device *dev) { - resource_size_t physaddr = 0; int bus_width = ACE_BUS_WIDTH_16; /* FIXME: should not be hard coded */ + resource_size_t physaddr; + struct resource *res; u32 id = dev->id; - int irq = 0; + int irq; int i; dev_dbg(&dev->dev, "ace_probe(%p)\n", dev); @@ -1190,12 +1190,15 @@ static int ace_probe(struct platform_device *dev) if (of_find_property(dev->dev.of_node, "8-bit", NULL)) bus_width = ACE_BUS_WIDTH_8; - for (i = 0; i < dev->num_resources; i++) { - if (dev->resource[i].flags & IORESOURCE_MEM) - physaddr = dev->resource[i].start; - if (dev->resource[i].flags & IORESOURCE_IRQ) - irq = dev->resource[i].start; - } + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + physaddr = res->start; + if (!physaddr) + return -ENODEV; + + irq = platform_get_irq_optional(dev, 0); /* Call the bus-independent setup code */ return ace_alloc(&dev->dev, id, physaddr, irq, bus_width); -- cgit v1.2.3 From 48ab6d5d1f096d6fac5b59f94af0aa394115a001 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 26 Oct 2020 16:27:55 +0100 Subject: dma-mapping: fix 32-bit overflow with CONFIG_ARM_LPAE=n On r8a7791/koelsch and shmobile_defconfig, PCIe probing fails with: rcar-pcie fe000000.pcie: Adjusted size 0x0 invalid rcar-pcie: probe of fe000000.pcie failed with error -22 of_dma_get_range() returns the following map: cpu_start 0x40000000 dma_start 0x40000000 size 0x080000000 offset 0 cpu_start 0x00000000 dma_start 0x00000000 size 0x100000000 offset 0 If CONFIG_ARM_LPAE=n, dma_addr_t is 32-bit. Hence when assigning r->dma_start + r->size to dma_end, this value will be truncated to 32-bit, yielding zero when processing the second table entry. Consequently, both dma_start and dma_end will be zero, leading to a zero size. Fix this by changing the dma_start and dma_end variables from dma_addr_t to u64. Fixes: e0d072782c734d27 ("dma-mapping: introduce DMA range map, supplanting dma_pfn_offset") Signed-off-by: Geert Uytterhoeven Signed-off-by: Christoph Hellwig --- drivers/of/device.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/of/device.c b/drivers/of/device.c index 655dee422563..3a469c79e6b0 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -93,7 +93,7 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, { const struct iommu_ops *iommu; const struct bus_dma_region *map = NULL; - dma_addr_t dma_start = 0; + u64 dma_start = 0; u64 mask, end, size = 0; bool coherent; int ret; @@ -109,10 +109,10 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, return ret == -ENODEV ? 0 : ret; } else { const struct bus_dma_region *r = map; - dma_addr_t dma_end = 0; + u64 dma_end = 0; /* Determine the overall bounds of all DMA regions */ - for (dma_start = ~(dma_addr_t)0; r->size; r++) { + for (dma_start = ~0ULL; r->size; r++) { /* Take lower and upper limits */ if (r->dma_start < dma_start) dma_start = r->dma_start; -- cgit v1.2.3 From 51467431200b91682b89d31317e35dcbca1469ce Mon Sep 17 00:00:00 2001 From: Masahiro Fujiwara Date: Tue, 27 Oct 2020 20:48:46 +0900 Subject: gtp: fix an use-before-init in gtp_newlink() *_pdp_find() from gtp_encap_recv() would trigger a crash when a peer sends GTP packets while creating new GTP device. RIP: 0010:gtp1_pdp_find.isra.0+0x68/0x90 [gtp] Call Trace: gtp_encap_recv+0xc2/0x2e0 [gtp] ? gtp1_pdp_find.isra.0+0x90/0x90 [gtp] udp_queue_rcv_one_skb+0x1fe/0x530 udp_queue_rcv_skb+0x40/0x1b0 udp_unicast_rcv_skb.isra.0+0x78/0x90 __udp4_lib_rcv+0x5af/0xc70 udp_rcv+0x1a/0x20 ip_protocol_deliver_rcu+0xc5/0x1b0 ip_local_deliver_finish+0x48/0x50 ip_local_deliver+0xe5/0xf0 ? ip_protocol_deliver_rcu+0x1b0/0x1b0 gtp_encap_enable() should be called after gtp_hastable_new() otherwise *_pdp_find() will access the uninitialized hash table. Fixes: 1e3a3abd8b28 ("gtp: make GTP sockets in gtp_newlink optional") Signed-off-by: Masahiro Fujiwara Link: https://lore.kernel.org/r/20201027114846.3924-1-fujiwara.masahiro@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/gtp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 030a1a5afe05..dc668ed280b9 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -657,10 +657,6 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, gtp = netdev_priv(dev); - err = gtp_encap_enable(gtp, data); - if (err < 0) - return err; - if (!data[IFLA_GTP_PDP_HASHSIZE]) { hashsize = 1024; } else { @@ -671,12 +667,16 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, err = gtp_hashtable_new(gtp, hashsize); if (err < 0) - goto out_encap; + return err; + + err = gtp_encap_enable(gtp, data); + if (err < 0) + goto out_hashtable; err = register_netdevice(dev); if (err < 0) { netdev_dbg(dev, "failed to register new netdev %d\n", err); - goto out_hashtable; + goto out_encap; } gn = net_generic(dev_net(dev), gtp_net_id); @@ -687,11 +687,11 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, return 0; +out_encap: + gtp_encap_disable(gtp); out_hashtable: kfree(gtp->addr_hash); kfree(gtp->tid_hash); -out_encap: - gtp_encap_disable(gtp); return err; } -- cgit v1.2.3 From 09a729b178be1cb20ffce620f7e5bfc04c4a13c0 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Wed, 21 Oct 2020 11:36:06 +0100 Subject: drm/i915/region: fix max size calculation We are incorrectly limiting the max allocation size as per the mm max_order, which is effectively the largest power-of-two that we can fit in the region size. However, it's normal to setup the region or allocator with a non-power-of-two size(for example 3G), which we should already handle correctly, except it seems for the early too-big-check. v2: make sure we also exercise the I915_BO_ALLOC_CONTIGUOUS path, which is quite different, since for that we are actually limited by the largest power-of-two that we can fit within the region size. (Chris) Fixes: b908be543e44 ("drm/i915: support creating LMEM objects") Signed-off-by: Matthew Auld Cc: Chris Wilson Cc: CQ Tang Reviewed-by: Chris Wilson Signed-off-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20201021103606.241395-1-matthew.auld@intel.com (cherry picked from commit 83ebef47f8ebe320d5c5673db82f9903a4f40a69) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/intel_memory_region.c | 2 +- .../gpu/drm/i915/selftests/intel_memory_region.c | 77 ++++++++++++++++++++++ drivers/gpu/drm/i915/selftests/mock_region.c | 2 +- 3 files changed, 79 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_memory_region.c b/drivers/gpu/drm/i915/intel_memory_region.c index 6b5e9d88646d..180e1078ef7c 100644 --- a/drivers/gpu/drm/i915/intel_memory_region.c +++ b/drivers/gpu/drm/i915/intel_memory_region.c @@ -87,7 +87,7 @@ __intel_memory_region_get_pages_buddy(struct intel_memory_region *mem, min_order = ilog2(size) - ilog2(mem->mm.chunk_size); } - if (size > BIT(mem->mm.max_order) * mem->mm.chunk_size) + if (size > mem->mm.size) return -E2BIG; n_pages = size >> ilog2(mem->mm.chunk_size); diff --git a/drivers/gpu/drm/i915/selftests/intel_memory_region.c b/drivers/gpu/drm/i915/selftests/intel_memory_region.c index 334b0648e253..0aeba8e3af28 100644 --- a/drivers/gpu/drm/i915/selftests/intel_memory_region.c +++ b/drivers/gpu/drm/i915/selftests/intel_memory_region.c @@ -261,6 +261,82 @@ err_close_objects: return err; } +static int igt_mock_splintered_region(void *arg) +{ + struct intel_memory_region *mem = arg; + struct drm_i915_private *i915 = mem->i915; + struct drm_i915_gem_object *obj; + unsigned int expected_order; + LIST_HEAD(objects); + u64 size; + int err = 0; + + /* + * Sanity check we can still allocate everything even if the + * mm.max_order != mm.size. i.e our starting address space size is not a + * power-of-two. + */ + + size = (SZ_4G - 1) & PAGE_MASK; + mem = mock_region_create(i915, 0, size, PAGE_SIZE, 0); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + if (mem->mm.size != size) { + pr_err("%s size mismatch(%llu != %llu)\n", + __func__, mem->mm.size, size); + err = -EINVAL; + goto out_put; + } + + expected_order = get_order(rounddown_pow_of_two(size)); + if (mem->mm.max_order != expected_order) { + pr_err("%s order mismatch(%u != %u)\n", + __func__, mem->mm.max_order, expected_order); + err = -EINVAL; + goto out_put; + } + + obj = igt_object_create(mem, &objects, size, 0); + if (IS_ERR(obj)) { + err = PTR_ERR(obj); + goto out_close; + } + + close_objects(mem, &objects); + + /* + * While we should be able allocate everything without any flag + * restrictions, if we consider I915_BO_ALLOC_CONTIGUOUS then we are + * actually limited to the largest power-of-two for the region size i.e + * max_order, due to the inner workings of the buddy allocator. So make + * sure that does indeed hold true. + */ + + obj = igt_object_create(mem, &objects, size, I915_BO_ALLOC_CONTIGUOUS); + if (!IS_ERR(obj)) { + pr_err("%s too large contiguous allocation was not rejected\n", + __func__); + err = -EINVAL; + goto out_close; + } + + obj = igt_object_create(mem, &objects, rounddown_pow_of_two(size), + I915_BO_ALLOC_CONTIGUOUS); + if (IS_ERR(obj)) { + pr_err("%s largest possible contiguous allocation failed\n", + __func__); + err = PTR_ERR(obj); + goto out_close; + } + +out_close: + close_objects(mem, &objects); +out_put: + intel_memory_region_put(mem); + return err; +} + static int igt_gpu_write_dw(struct intel_context *ce, struct i915_vma *vma, u32 dword, @@ -771,6 +847,7 @@ int intel_memory_region_mock_selftests(void) static const struct i915_subtest tests[] = { SUBTEST(igt_mock_fill), SUBTEST(igt_mock_contiguous), + SUBTEST(igt_mock_splintered_region), }; struct intel_memory_region *mem; struct drm_i915_private *i915; diff --git a/drivers/gpu/drm/i915/selftests/mock_region.c b/drivers/gpu/drm/i915/selftests/mock_region.c index 09660f5a0a4c..979d96f27c43 100644 --- a/drivers/gpu/drm/i915/selftests/mock_region.c +++ b/drivers/gpu/drm/i915/selftests/mock_region.c @@ -24,7 +24,7 @@ mock_object_create(struct intel_memory_region *mem, struct drm_i915_private *i915 = mem->i915; struct drm_i915_gem_object *obj; - if (size > BIT(mem->mm.max_order) * mem->mm.chunk_size) + if (size > mem->mm.size) return ERR_PTR(-E2BIG); obj = i915_gem_object_alloc(); -- cgit v1.2.3 From 5cbd7685b22823ebf432ec71eac1691b71c41037 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 21 Oct 2020 16:14:39 +0300 Subject: drm/i915: Restore ILK-M RPS support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restore RPS for ILK-M. We lost it when an extra HAS_RPS() check appeared in intel_rps_enable(). Unfortunaltey this just makes the performance worse on my ILK because intel_ips insists on limiting the GPU freq to the minimum. If we don't do the RPS init then intel_ips will not limit the frequency for whatever reason. Either it can't get at some required information and thus makes wrong decisions, or we mess up some weights/etc. and cause it to make the wrong decisions when RPS init has been done, or the entire thing is just wrong. Would require a bunch of reverse engineering to figure out what's going on. Cc: stable@vger.kernel.org Cc: Chris Wilson Fixes: 9c878557b1eb ("drm/i915/gt: Use the RPM config register to determine clk frequencies") Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20201021131443.25616-1-ville.syrjala@linux.intel.com Reviewed-by: Chris Wilson (cherry picked from commit 2bf06370bcfb0dea5655e9a5ad460c7f7dca7739) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/i915_pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index 366ddfc8df6b..fb5e30de78c2 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -389,6 +389,7 @@ static const struct intel_device_info ilk_m_info = { GEN5_FEATURES, PLATFORM(INTEL_IRONLAKE), .is_mobile = 1, + .has_rps = true, .display.has_fbc = 1, }; -- cgit v1.2.3 From 61334ed227a5852100115180f5535b1396ed5227 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 20 Oct 2020 22:43:29 +0300 Subject: drm/i915: Reject 90/270 degree rotated initial fbs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't currently handle the initial fb readout correctly for 90/270 degree rotated scanout. Reject it. Cc: stable@vger.kernel.org Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20201020194330.28568-1-ville.syrjala@linux.intel.com Reviewed-by: Chris Wilson (cherry picked from commit a40a8305a732f4ecc2186ac7ca132ba062ed770d) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_display.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 829b2a40a482..31337d2a2cde 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -10636,6 +10636,10 @@ skl_get_initial_plane_config(struct intel_crtc *crtc, val & PLANE_CTL_FLIP_HORIZONTAL) plane_config->rotation |= DRM_MODE_REFLECT_X; + /* 90/270 degree rotation would require extra work */ + if (drm_rotation_90_or_270(plane_config->rotation)) + goto error; + base = intel_de_read(dev_priv, PLANE_SURF(pipe, plane_id)) & 0xfffff000; plane_config->base = base; -- cgit v1.2.3 From 8fc3672a8ad3e782bac80e979bc2a2c10960cbe9 Mon Sep 17 00:00:00 2001 From: Lijun Pan Date: Tue, 27 Oct 2020 17:04:56 -0500 Subject: ibmvnic: fix ibmvnic_set_mac Jakub Kicinski brought up a concern in ibmvnic_set_mac(). ibmvnic_set_mac() does this: ether_addr_copy(adapter->mac_addr, addr->sa_data); if (adapter->state != VNIC_PROBED) rc = __ibmvnic_set_mac(netdev, addr->sa_data); So if state == VNIC_PROBED, the user can assign an invalid address to adapter->mac_addr, and ibmvnic_set_mac() will still return 0. The fix is to validate ethernet address at the beginning of ibmvnic_set_mac(), and move the ether_addr_copy to the case of "adapter->state != VNIC_PROBED". Fixes: c26eba03e407 ("ibmvnic: Update reset infrastructure to support tunable parameters") Signed-off-by: Lijun Pan Link: https://lore.kernel.org/r/20201027220456.71450-1-ljp@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/ibm/ibmvnic.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index 8148f796a807..af4dfbe28d56 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -1815,9 +1815,13 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p) int rc; rc = 0; - ether_addr_copy(adapter->mac_addr, addr->sa_data); - if (adapter->state != VNIC_PROBED) + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (adapter->state != VNIC_PROBED) { + ether_addr_copy(adapter->mac_addr, addr->sa_data); rc = __ibmvnic_set_mac(netdev, addr->sa_data); + } return rc; } -- cgit v1.2.3 From 2734a24e6e5d18522fbf599135c59b82ec9b2c9e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 29 Oct 2020 10:18:53 +0100 Subject: r8169: fix issue with forced threading in combination with shared interrupts As reported by Serge flag IRQF_NO_THREAD causes an error if the interrupt is actually shared and the other driver(s) don't have this flag set. This situation can occur if a PCI(e) legacy interrupt is used in combination with forced threading. There's no good way to deal with this properly, therefore we have to remove flag IRQF_NO_THREAD. For fixing the original forced threading issue switch to napi_schedule(). Fixes: 424a646e072a ("r8169: fix operation under forced interrupt threading") Link: https://www.spinics.net/lists/netdev/msg694960.html Reported-by: Serge Belyshev Signed-off-by: Heiner Kallweit Tested-by: Serge Belyshev Link: https://lore.kernel.org/r/b5b53bfe-35ac-3768-85bf-74d1290cf394@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/realtek/r8169_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 3b6ddc706e92..00f13805c6f7 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -4573,7 +4573,7 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) } rtl_irq_disable(tp); - napi_schedule_irqoff(&tp->napi); + napi_schedule(&tp->napi); out: rtl_ack_events(tp, status); @@ -4746,7 +4746,7 @@ static int rtl_open(struct net_device *dev) rtl_request_firmware(tp); retval = request_irq(pci_irq_vector(pdev, 0), rtl8169_interrupt, - IRQF_NO_THREAD | IRQF_SHARED, dev->name, tp); + IRQF_SHARED, dev->name, tp); if (retval < 0) goto err_release_fw_2; -- cgit v1.2.3 From 39a7661dcf655c8198fd5d72412f5030a8e58444 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Thu, 29 Oct 2020 10:45:59 -0600 Subject: coresight: Fix uninitialised pointer bug in etm_setup_aux() Commit [bb1860efc817] changed the sink handling code introducing an uninitialised pointer bug. This results in the default sink selection failing. Prior to commit: static void etm_setup_aux(...) struct coresight_device *sink; /* First get the selected sink from user space. */ if (event->attr.config2) { id = (u32)event->attr.config2; sink = coresight_get_sink_by_id(id); } else { sink = coresight_get_enabled_sink(true); } *sink always initialised - possibly to NULL which triggers the automatic sink selection. After commit: static void etm_setup_aux(...) struct coresight_device *sink; /* First get the selected sink from user space. */ if (event->attr.config2) { id = (u32)event->attr.config2; sink = coresight_get_sink_by_id(id); } *sink pointer uninitialised when not providing a sink on the perf command line. This breaks later checks to enable automatic sink selection. Fixes: bb1860efc817 ("coresight: etm: perf: Sink selection using sysfs is deprecated") Signed-off-by: Mike Leach Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20201029164559.1268531-3-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index c2c9b127d074..bdc34ca449f7 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -210,7 +210,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, u32 id; int cpu = event->cpu; cpumask_t *mask; - struct coresight_device *sink; + struct coresight_device *sink = NULL; struct etm_event_data *event_data = NULL; event_data = alloc_event_data(cpu); -- cgit v1.2.3 From 80624263fa289b3416f7ca309491f1b75e579477 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 29 Oct 2020 10:45:58 -0600 Subject: coresight: cti: Initialize dynamic sysfs attributes With LOCKDEP enabled, CTI driver triggers the following splat due to uninitialized lock class for dynamically allocated attribute objects. [ 5.372901] coresight etm0: CPU0: ETM v4.0 initialized [ 5.376694] coresight etm1: CPU1: ETM v4.0 initialized [ 5.380785] coresight etm2: CPU2: ETM v4.0 initialized [ 5.385851] coresight etm3: CPU3: ETM v4.0 initialized [ 5.389808] BUG: key ffff00000564a798 has not been registered! [ 5.392456] ------------[ cut here ]------------ [ 5.398195] DEBUG_LOCKS_WARN_ON(1) [ 5.398233] WARNING: CPU: 1 PID: 32 at kernel/locking/lockdep.c:4623 lockdep_init_map_waits+0x14c/0x260 [ 5.406149] Modules linked in: [ 5.415411] CPU: 1 PID: 32 Comm: kworker/1:1 Not tainted 5.9.0-12034-gbbe85027ce80 #51 [ 5.418553] Hardware name: Qualcomm Technologies, Inc. APQ 8016 SBC (DT) [ 5.426453] Workqueue: events amba_deferred_retry_func [ 5.433299] pstate: 40000005 (nZcv daif -PAN -UAO -TCO BTYPE=--) [ 5.438252] pc : lockdep_init_map_waits+0x14c/0x260 [ 5.444410] lr : lockdep_init_map_waits+0x14c/0x260 [ 5.449007] sp : ffff800012bbb720 ... [ 5.531561] Call trace: [ 5.536847] lockdep_init_map_waits+0x14c/0x260 [ 5.539027] __kernfs_create_file+0xa8/0x1c8 [ 5.543539] sysfs_add_file_mode_ns+0xd0/0x208 [ 5.548054] internal_create_group+0x118/0x3c8 [ 5.552307] internal_create_groups+0x58/0xb8 [ 5.556733] sysfs_create_groups+0x2c/0x38 [ 5.561160] device_add+0x2d8/0x768 [ 5.565148] device_register+0x28/0x38 [ 5.568537] coresight_register+0xf8/0x320 [ 5.572358] cti_probe+0x1b0/0x3f0 ... Fix this by initializing the attributes when they are allocated. Fixes: 3c5597e39812 ("coresight: cti: Add connection information to sysfs") Reported-by: Leo Yan Tested-by: Leo Yan Cc: Mike Leach Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Cc: stable Signed-off-by: Mathieu Poirier Link: https://lore.kernel.org/r/20201029164559.1268531-2-mathieu.poirier@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-cti-sysfs.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 392757f3a019..7ff7e7780bbf 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -1065,6 +1065,13 @@ static int cti_create_con_sysfs_attr(struct device *dev, } eattr->var = con; con->con_attrs[attr_idx] = &eattr->attr.attr; + /* + * Initialize the dynamically allocated attribute + * to avoid LOCKDEP splat. See include/linux/sysfs.h + * for more details. + */ + sysfs_attr_init(con->con_attrs[attr_idx]); + return 0; } -- cgit v1.2.3 From 3d696f42c7f4bfcc54bde545898d4b89a74f9cc0 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Thu, 29 Oct 2020 11:33:37 +0200 Subject: soc: ti: ti_sci_pm_domains: check for proper args count in xlate K2G devices still only use single parameter for power-domains property, so check for this properly in the driver. Without this, every peripheral fails to probe resulting in boot failure. Link: https://lore.kernel.org/r/20201029093337.21170-1-t-kristo@ti.com Fixes: efa5c01cd7ee ("soc: ti: ti_sci_pm_domains: switch to use multiple genpds instead of one") Reported-by: Nishanth Menon Signed-off-by: Tero Kristo Acked-by: Nishanth Menon Acked-by: Santosh Shilimkar Signed-off-by: Arnd Bergmann --- drivers/soc/ti/ti_sci_pm_domains.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c index af2126d2b2ff..8afb3f45d263 100644 --- a/drivers/soc/ti/ti_sci_pm_domains.c +++ b/drivers/soc/ti/ti_sci_pm_domains.c @@ -91,7 +91,7 @@ static struct generic_pm_domain *ti_sci_pd_xlate( struct genpd_onecell_data *genpd_data = data; unsigned int idx = genpdspec->args[0]; - if (genpdspec->args_count < 2) + if (genpdspec->args_count != 1 && genpdspec->args_count != 2) return ERR_PTR(-EINVAL); if (idx >= genpd_data->num_domains) { -- cgit v1.2.3 From bfe124d197f61af632b9f3f4c2c0579bd7848448 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 31 Aug 2020 08:25:42 -0500 Subject: enetc: Replace zero-length array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a regular need in the kernel to provide a way to declare having a dynamically sized set of trailing elements in a structure. Kernel code should always use “flexible array members”[1] for these cases. The older style of one-element or zero-length arrays should no longer be used[2]. [1] https://en.wikipedia.org/wiki/Flexible_array_member [2] https://www.kernel.org/doc/html/v5.9-rc1/process/deprecated.html#zero-length-and-one-element-arrays Signed-off-by: Gustavo A. R. Silva --- drivers/net/ethernet/freescale/enetc/enetc_qos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c index c81be32bcedf..827f74e86d34 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c @@ -402,7 +402,7 @@ struct enetc_psfp_gate { u32 num_entries; refcount_t refcount; struct hlist_node node; - struct action_gate_entry entries[0]; + struct action_gate_entry entries[]; }; /* Only enable the green color frame now -- cgit v1.2.3 From 8fdaabe1c9b3226172ba2e9e525627219be6d29a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 31 Aug 2020 08:25:42 -0500 Subject: scsi: target: tcmu: Replace zero-length array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a regular need in the kernel to provide a way to declare having a dynamically sized set of trailing elements in a structure. Kernel code should always use “flexible array members”[1] for these cases. The older style of one-element or zero-length arrays should no longer be used[2]. [1] https://en.wikipedia.org/wiki/Flexible_array_member [2] https://www.kernel.org/doc/html/v5.9-rc1/process/deprecated.html#zero-length-and-one-element-arrays Signed-off-by: Gustavo A. R. Silva --- drivers/target/target_core_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index ea84d08747df..590e6d072228 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -194,7 +194,7 @@ struct tcmu_tmr { uint8_t tmr_type; uint32_t tmr_cmd_cnt; - int16_t tmr_cmd_ids[0]; + int16_t tmr_cmd_ids[]; }; /* -- cgit v1.2.3 From cfa736f5a6f31ca8a05459b5720aac030247ad1b Mon Sep 17 00:00:00 2001 From: Ralph Campbell Date: Mon, 31 Aug 2020 13:31:11 -0700 Subject: drm/nouveau/nouveau: fix the start/end range for migration The user level OpenCL code shouldn't have to align start and end addresses to a page boundary. That is better handled in the nouveau driver. The npages field is also redundant since it can be computed from the start and end addresses. Signed-off-by: Ralph Campbell Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_svm.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_svm.c b/drivers/gpu/drm/nouveau/nouveau_svm.c index 2df1c0460559..4f69e4c3dafd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_svm.c +++ b/drivers/gpu/drm/nouveau/nouveau_svm.c @@ -105,11 +105,11 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, struct nouveau_cli *cli = nouveau_cli(file_priv); struct drm_nouveau_svm_bind *args = data; unsigned target, cmd, priority; - unsigned long addr, end, size; + unsigned long addr, end; struct mm_struct *mm; args->va_start &= PAGE_MASK; - args->va_end &= PAGE_MASK; + args->va_end = ALIGN(args->va_end, PAGE_SIZE); /* Sanity check arguments */ if (args->reserved0 || args->reserved1) @@ -118,8 +118,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, return -EINVAL; if (args->va_start >= args->va_end) return -EINVAL; - if (!args->npages) - return -EINVAL; cmd = args->header >> NOUVEAU_SVM_BIND_COMMAND_SHIFT; cmd &= NOUVEAU_SVM_BIND_COMMAND_MASK; @@ -151,12 +149,6 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, if (args->stride) return -EINVAL; - size = ((unsigned long)args->npages) << PAGE_SHIFT; - if ((args->va_start + size) <= args->va_start) - return -EINVAL; - if ((args->va_start + size) > args->va_end) - return -EINVAL; - /* * Ok we are ask to do something sane, for now we only support migrate * commands but we will add things like memory policy (what to do on @@ -171,7 +163,7 @@ nouveau_svmm_bind(struct drm_device *dev, void *data, return -EINVAL; } - for (addr = args->va_start, end = args->va_start + size; addr < end;) { + for (addr = args->va_start, end = args->va_end; addr < end;) { struct vm_area_struct *vma; unsigned long next; -- cgit v1.2.3 From 24d9422e26ea75118acf00172f83417c296f5b5f Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 4 Sep 2020 16:27:58 -0400 Subject: drm/nouveau/kms/nv50-: Program notifier offset before requesting disp caps Not entirely sure why this never came up when I originally tested this (maybe some BIOSes already have this setup?) but the ->caps_init vfunc appears to cause the display engine to throw an exception on driver init, at least on my ThinkPad P72: nouveau 0000:01:00.0: disp: chid 0 mthd 008c data 00000000 0000508c 0000102b This is magic nvidia speak for "You need to have the DMA notifier offset programmed before you can call NV507D_GET_CAPABILITIES." So, let's fix this by doing that, and also perform an update afterwards to prevent racing with the GPU when reading capabilities. v2: * Don't just program the DMA notifier offset, make sure to actually perform an update v3: * Don't call UPDATE() * Actually read the correct notifier fields, as apparently the CAPABILITIES_DONE field lives in a different location than the main NV_DISP_CORE_NOTIFIER_1 field. As well, 907d+ use a different CAPABILITIES_DONE field then pre-907d cards. v4: * Don't forget to check the return value of core507d_read_caps() v5: * Get rid of NV50_DISP_CAPS_NTFY[14], use NV50_DISP_CORE_NTFY * Disable notifier after calling GetCapabilities() Signed-off-by: Lyude Paul Fixes: 4a2cb4181b07 ("drm/nouveau/kms/nv50-: Probe SOR and PIOR caps for DP interlacing support") Cc: # v5.8+ Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/dispnv50/core.h | 2 ++ drivers/gpu/drm/nouveau/dispnv50/core507d.c | 41 ++++++++++++++++++++-- drivers/gpu/drm/nouveau/dispnv50/core907d.c | 36 ++++++++++++++++++- drivers/gpu/drm/nouveau/dispnv50/core917d.c | 2 +- .../gpu/drm/nouveau/include/nvhw/class/cl507d.h | 5 ++- .../gpu/drm/nouveau/include/nvhw/class/cl907d.h | 4 +++ 6 files changed, 85 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/dispnv50/core.h b/drivers/gpu/drm/nouveau/dispnv50/core.h index 498622c0c670..f75088186fba 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core.h +++ b/drivers/gpu/drm/nouveau/dispnv50/core.h @@ -44,6 +44,7 @@ int core507d_new_(const struct nv50_core_func *, struct nouveau_drm *, s32, struct nv50_core **); int core507d_init(struct nv50_core *); void core507d_ntfy_init(struct nouveau_bo *, u32); +int core507d_read_caps(struct nv50_disp *disp); int core507d_caps_init(struct nouveau_drm *, struct nv50_disp *); int core507d_ntfy_wait_done(struct nouveau_bo *, u32, struct nvif_device *); int core507d_update(struct nv50_core *, u32 *, bool); @@ -55,6 +56,7 @@ extern const struct nv50_outp_func pior507d; int core827d_new(struct nouveau_drm *, s32, struct nv50_core **); int core907d_new(struct nouveau_drm *, s32, struct nv50_core **); +int core907d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp); extern const struct nv50_outp_func dac907d; extern const struct nv50_outp_func sor907d; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core507d.c b/drivers/gpu/drm/nouveau/dispnv50/core507d.c index 248edf69e168..e6f16a7750f0 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core507d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core507d.c @@ -78,18 +78,55 @@ core507d_ntfy_init(struct nouveau_bo *bo, u32 offset) } int -core507d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp) +core507d_read_caps(struct nv50_disp *disp) { struct nvif_push *push = disp->core->chan.push; int ret; - if ((ret = PUSH_WAIT(push, 2))) + ret = PUSH_WAIT(push, 6); + if (ret) return ret; + PUSH_MTHD(push, NV507D, SET_NOTIFIER_CONTROL, + NVDEF(NV507D, SET_NOTIFIER_CONTROL, MODE, WRITE) | + NVVAL(NV507D, SET_NOTIFIER_CONTROL, OFFSET, NV50_DISP_CORE_NTFY >> 2) | + NVDEF(NV507D, SET_NOTIFIER_CONTROL, NOTIFY, ENABLE)); + PUSH_MTHD(push, NV507D, GET_CAPABILITIES, 0x00000000); + + PUSH_MTHD(push, NV507D, SET_NOTIFIER_CONTROL, + NVDEF(NV507D, SET_NOTIFIER_CONTROL, NOTIFY, DISABLE)); + return PUSH_KICK(push); } +int +core507d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp) +{ + struct nv50_core *core = disp->core; + struct nouveau_bo *bo = disp->sync; + s64 time; + int ret; + + NVBO_WR32(bo, NV50_DISP_CORE_NTFY, NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1, + NVDEF(NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1, DONE, FALSE)); + + ret = core507d_read_caps(disp); + if (ret < 0) + return ret; + + time = nvif_msec(core->chan.base.device, 2000ULL, + if (NVBO_TD32(bo, NV50_DISP_CORE_NTFY, + NV_DISP_CORE_NOTIFIER_1, CAPABILITIES_1, DONE, ==, TRUE)) + break; + usleep_range(1, 2); + ); + if (time < 0) + NV_ERROR(drm, "core caps notifier timeout\n"); + + return 0; +} + int core507d_init(struct nv50_core *core) { diff --git a/drivers/gpu/drm/nouveau/dispnv50/core907d.c b/drivers/gpu/drm/nouveau/dispnv50/core907d.c index b17c03529c78..8564d4dffaff 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core907d.c @@ -22,11 +22,45 @@ #include "core.h" #include "head.h" +#include +#include + +#include + +#include "nouveau_bo.h" + +int +core907d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp) +{ + struct nv50_core *core = disp->core; + struct nouveau_bo *bo = disp->sync; + s64 time; + int ret; + + NVBO_WR32(bo, NV50_DISP_CORE_NTFY, NV907D_CORE_NOTIFIER_3, CAPABILITIES_4, + NVDEF(NV907D_CORE_NOTIFIER_3, CAPABILITIES_4, DONE, FALSE)); + + ret = core507d_read_caps(disp); + if (ret < 0) + return ret; + + time = nvif_msec(core->chan.base.device, 2000ULL, + if (NVBO_TD32(bo, NV50_DISP_CORE_NTFY, + NV907D_CORE_NOTIFIER_3, CAPABILITIES_4, DONE, ==, TRUE)) + break; + usleep_range(1, 2); + ); + if (time < 0) + NV_ERROR(drm, "core caps notifier timeout\n"); + + return 0; +} + static const struct nv50_core_func core907d = { .init = core507d_init, .ntfy_init = core507d_ntfy_init, - .caps_init = core507d_caps_init, + .caps_init = core907d_caps_init, .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head907d, diff --git a/drivers/gpu/drm/nouveau/dispnv50/core917d.c b/drivers/gpu/drm/nouveau/dispnv50/core917d.c index 66846f372080..1cd3a2a35dfb 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core917d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core917d.c @@ -26,7 +26,7 @@ static const struct nv50_core_func core917d = { .init = core507d_init, .ntfy_init = core507d_ntfy_init, - .caps_init = core507d_caps_init, + .caps_init = core907d_caps_init, .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head917d, diff --git a/drivers/gpu/drm/nouveau/include/nvhw/class/cl507d.h b/drivers/gpu/drm/nouveau/include/nvhw/class/cl507d.h index 2e444bac701d..6a463f308b64 100644 --- a/drivers/gpu/drm/nouveau/include/nvhw/class/cl507d.h +++ b/drivers/gpu/drm/nouveau/include/nvhw/class/cl507d.h @@ -32,7 +32,10 @@ #define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_DONE_TRUE 0x00000001 #define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_R0 15:1 #define NV_DISP_CORE_NOTIFIER_1_COMPLETION_0_TIMESTAMP 29:16 - +#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1 0x00000001 +#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE 0:0 +#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE_FALSE 0x00000000 +#define NV_DISP_CORE_NOTIFIER_1_CAPABILITIES_1_DONE_TRUE 0x00000001 // class methods #define NV507D_UPDATE (0x00000080) diff --git a/drivers/gpu/drm/nouveau/include/nvhw/class/cl907d.h b/drivers/gpu/drm/nouveau/include/nvhw/class/cl907d.h index 34bc3eafac7d..79aff6ff3138 100644 --- a/drivers/gpu/drm/nouveau/include/nvhw/class/cl907d.h +++ b/drivers/gpu/drm/nouveau/include/nvhw/class/cl907d.h @@ -24,6 +24,10 @@ #ifndef _cl907d_h_ #define _cl907d_h_ +#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4 0x00000004 +#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE 0:0 +#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE_FALSE 0x00000000 +#define NV907D_CORE_NOTIFIER_3_CAPABILITIES_4_DONE_TRUE 0x00000001 #define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20 0x00000014 #define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20_SINGLE_LVDS18 0:0 #define NV907D_CORE_NOTIFIER_3_CAPABILITIES_CAP_SOR0_20_SINGLE_LVDS18_FALSE 0x00000000 -- cgit v1.2.3 From 925681454d7b557d404b5d28ef4469fac1b2e105 Mon Sep 17 00:00:00 2001 From: Karol Herbst Date: Wed, 7 Oct 2020 00:08:09 +0200 Subject: drm/nouveau/gem: fix "refcount_t: underflow; use-after-free" we can't use nouveau_bo_ref here as no ttm object was allocated and nouveau_bo_ref mainly deals with that. Simply deallocate the object. Signed-off-by: Karol Herbst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 89adadf4706b..549bc67feabb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -190,7 +190,8 @@ nouveau_gem_new(struct nouveau_cli *cli, u64 size, int align, uint32_t domain, * to the caller, instead of a normal nouveau_bo ttm reference. */ ret = drm_gem_object_init(drm->dev, &nvbo->bo.base, size); if (ret) { - nouveau_bo_ref(NULL, &nvbo); + drm_gem_object_release(&nvbo->bo.base); + kfree(nvbo); return ret; } -- cgit v1.2.3 From dcd292c172493067a72672b245a3dd1bcf7268dd Mon Sep 17 00:00:00 2001 From: Karol Herbst Date: Tue, 13 Oct 2020 14:01:26 +0200 Subject: drm/nouveau/device: fix changing endianess code to work on older GPUs With this we try to detect if the endianess switch works and assume LE if not. Suggested by Ben. Fixes: 51c05340e407 ("drm/nouveau/device: detect if changing endianness failed") Signed-off-by: Karol Herbst Cc: # v5.8+ Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nvkm/engine/device/base.c | 39 +++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index dcb70677d0ac..7851bec5f0e5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2924,17 +2924,34 @@ nvkm_device_del(struct nvkm_device **pdevice) } } +/* returns true if the GPU is in the CPU native byte order */ static inline bool nvkm_device_endianness(struct nvkm_device *device) { - u32 boot1 = nvkm_rd32(device, 0x000004) & 0x01000001; #ifdef __BIG_ENDIAN - if (!boot1) - return false; + const bool big_endian = true; #else - if (boot1) - return false; + const bool big_endian = false; #endif + + /* Read NV_PMC_BOOT_1, and assume non-functional endian switch if it + * doesn't contain the expected values. + */ + u32 pmc_boot_1 = nvkm_rd32(device, 0x000004); + if (pmc_boot_1 && pmc_boot_1 != 0x01000001) + return !big_endian; /* Assume GPU is LE in this case. */ + + /* 0 means LE and 0x01000001 means BE GPU. Condition is true when + * GPU/CPU endianness don't match. + */ + if (big_endian == !pmc_boot_1) { + nvkm_wr32(device, 0x000004, 0x01000001); + nvkm_rd32(device, 0x000000); + if (nvkm_rd32(device, 0x000004) != (big_endian ? 0x01000001 : 0x00000000)) + return !big_endian; /* Assume GPU is LE on any unexpected read-back. */ + } + + /* CPU/GPU endianness should (hopefully) match. */ return true; } @@ -2987,14 +3004,10 @@ nvkm_device_ctor(const struct nvkm_device_func *func, if (detect) { /* switch mmio to cpu's native endianness */ if (!nvkm_device_endianness(device)) { - nvkm_wr32(device, 0x000004, 0x01000001); - nvkm_rd32(device, 0x000000); - if (!nvkm_device_endianness(device)) { - nvdev_error(device, - "GPU not supported on big-endian\n"); - ret = -ENOSYS; - goto done; - } + nvdev_error(device, + "Couldn't switch GPU to CPUs endianess\n"); + ret = -ENOSYS; + goto done; } boot0 = nvkm_rd32(device, 0x000000); -- cgit v1.2.3 From 2d831155cf0607566e43d8465da33774b2dc7221 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Tue, 29 Sep 2020 18:31:31 -0400 Subject: drm/nouveau/kms/nv50-: Get rid of bogus nouveau_conn_mode_valid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ville also pointed out that I got a lot of the logic here wrong as well, whoops. While I don't think anyone's likely using 3D output with nouveau, the next patch will make nouveau_conn_mode_valid() make a lot less sense. So, let's just get rid of it and open-code it like before, while taking care to move the 3D frame packing calculations on the dot clock into the right place. Signed-off-by: Lyude Paul Fixes: d6a9efece724 ("drm/nouveau/kms/nv50-: Share DP SST mode_valid() handling with MST") Cc: Ville Syrjälä Cc: # v5.8+ Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_connector.c | 36 ++++++++--------------------- drivers/gpu/drm/nouveau/nouveau_dp.c | 16 +++++++++---- 2 files changed, 21 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 49dd0cbc332f..6f21f36719fc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1023,29 +1023,6 @@ get_tmds_link_bandwidth(struct drm_connector *connector) return 112000 * duallink_scale; } -enum drm_mode_status -nouveau_conn_mode_clock_valid(const struct drm_display_mode *mode, - const unsigned min_clock, - const unsigned max_clock, - unsigned int *clock_out) -{ - unsigned int clock = mode->clock; - - if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == - DRM_MODE_FLAG_3D_FRAME_PACKING) - clock *= 2; - - if (clock < min_clock) - return MODE_CLOCK_LOW; - if (clock > max_clock) - return MODE_CLOCK_HIGH; - - if (clock_out) - *clock_out = clock; - - return MODE_OK; -} - static enum drm_mode_status nouveau_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) @@ -1053,7 +1030,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct drm_encoder *encoder = to_drm_encoder(nv_encoder); - unsigned min_clock = 25000, max_clock = min_clock; + unsigned int min_clock = 25000, max_clock = min_clock, clock = mode->clock; switch (nv_encoder->dcb->type) { case DCB_OUTPUT_LVDS: @@ -1082,8 +1059,15 @@ nouveau_connector_mode_valid(struct drm_connector *connector, return MODE_BAD; } - return nouveau_conn_mode_clock_valid(mode, min_clock, max_clock, - NULL); + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + + if (clock < min_clock) + return MODE_CLOCK_LOW; + if (clock > max_clock) + return MODE_CLOCK_HIGH; + + return MODE_OK; } static struct drm_encoder * diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 7b640e05bd4c..93e3751ad7f1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -232,12 +232,14 @@ nv50_dp_mode_valid(struct drm_connector *connector, unsigned *out_clock) { const unsigned min_clock = 25000; - unsigned max_clock, ds_clock, clock; - enum drm_mode_status ret; + unsigned max_clock, ds_clock, clock = mode->clock; if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace) return MODE_NO_INTERLACE; + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + max_clock = outp->dp.link_nr * outp->dp.link_bw; ds_clock = drm_dp_downstream_max_dotclock(outp->dp.dpcd, outp->dp.downstream_ports); @@ -245,9 +247,13 @@ nv50_dp_mode_valid(struct drm_connector *connector, max_clock = min(max_clock, ds_clock); clock = mode->clock * (connector->display_info.bpc * 3) / 10; - ret = nouveau_conn_mode_clock_valid(mode, min_clock, max_clock, - &clock); + if (clock < min_clock) + return MODE_CLOCK_LOW; + if (clock > max_clock) + return MODE_CLOCK_HIGH; + if (out_clock) *out_clock = clock; - return ret; + + return MODE_OK; } -- cgit v1.2.3 From d7787cc04e0a1f2043264d1550465081096bd065 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Tue, 29 Sep 2020 18:31:32 -0400 Subject: drm/nouveau/kms/nv50-: Fix clock checking algorithm in nv50_dp_mode_valid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While I thought I had this correct (since it actually did reject modes like I expected during testing), Ville Syrjala from Intel pointed out that the logic here isn't correct. max_clock refers to the max data rate supported by the DP encoder. So, limiting it to the output of ds_clock (which refers to the maximum dotclock of the downstream DP device) doesn't make any sense. Additionally, since we're using the connector's bpc as the canonical BPC we should use this in mode_valid until we support dynamically setting the bpp based on bandwidth constraints. https://lists.freedesktop.org/archives/dri-devel/2020-September/280276.html For more info. So, let's rewrite this using Ville's advice. v2: * Ville pointed out I mixed up the dotclock and the link rate. So fix that... * ...and also rename all the variables in this function to be more appropriately labeled so I stop mixing them up. * Reuse the bpp from the connector for now until we have dynamic bpp selection. * Use use DIV_ROUND_UP for calculating the mode rate like i915 does, which we should also have been doing from the start Signed-off-by: Lyude Paul Fixes: 409d38139b42 ("drm/nouveau/kms/nv50-: Use downstream DP clock limits for mode validation") Cc: Ville Syrjälä Cc: Lyude Paul Cc: Ben Skeggs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 93e3751ad7f1..040ed88d362d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -231,8 +231,9 @@ nv50_dp_mode_valid(struct drm_connector *connector, const struct drm_display_mode *mode, unsigned *out_clock) { - const unsigned min_clock = 25000; - unsigned max_clock, ds_clock, clock = mode->clock; + const unsigned int min_clock = 25000; + unsigned int max_rate, mode_rate, ds_max_dotclock, clock = mode->clock; + const u8 bpp = connector->display_info.bpc * 3; if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace) return MODE_NO_INTERLACE; @@ -240,17 +241,17 @@ nv50_dp_mode_valid(struct drm_connector *connector, if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) clock *= 2; - max_clock = outp->dp.link_nr * outp->dp.link_bw; - ds_clock = drm_dp_downstream_max_dotclock(outp->dp.dpcd, - outp->dp.downstream_ports); - if (ds_clock) - max_clock = min(max_clock, ds_clock); + max_rate = outp->dp.link_nr * outp->dp.link_bw; + mode_rate = DIV_ROUND_UP(clock * bpp, 8); + if (mode_rate > max_rate) + return MODE_CLOCK_HIGH; + + ds_max_dotclock = drm_dp_downstream_max_dotclock(outp->dp.dpcd, outp->dp.downstream_ports); + if (ds_max_dotclock && clock > ds_max_dotclock) + return MODE_CLOCK_HIGH; - clock = mode->clock * (connector->display_info.bpc * 3) / 10; if (clock < min_clock) return MODE_CLOCK_LOW; - if (clock > max_clock) - return MODE_CLOCK_HIGH; if (out_clock) *out_clock = clock; -- cgit v1.2.3 From 52d3967704aea6cb316d419a33a5e1d56d33a3c1 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Thu, 22 Oct 2020 08:55:05 +0800 Subject: usb: cdns3: Fix on-chip memory overflow issue Patch fixes issue caused setting On-chip memory overflow bit in usb_sts register. The issue occurred because EP_CFG register was set twice before USB_STS.CFGSTS was set. Every write operation on EP_CFG.BUFFERING causes that controller increases internal counter holding the number of reserved on-chip buffers. First time this register was updated in function cdns3_ep_config before delegating SET_CONFIGURATION request to class driver and again it was updated when class wanted to enable endpoint. This patch fixes this issue by configuring endpoints enabled by class driver in cdns3_gadget_ep_enable and others just before status stage. Cc: stable@vger.kernel.org#v5.8+ Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Reported-and-tested-by: Peter Chen Signed-off-by: Pawel Laszczak Signed-off-by: Peter Chen --- drivers/usb/cdns3/ep0.c | 65 ++++++++++++++++------------- drivers/usb/cdns3/gadget.c | 102 +++++++++++++++++++++++++-------------------- drivers/usb/cdns3/gadget.h | 3 +- 3 files changed, 94 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c index 4761c852d9c4..d3121a32cc68 100644 --- a/drivers/usb/cdns3/ep0.c +++ b/drivers/usb/cdns3/ep0.c @@ -137,48 +137,36 @@ static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev, struct usb_ctrlrequest *ctrl_req) { enum usb_device_state device_state = priv_dev->gadget.state; - struct cdns3_endpoint *priv_ep; u32 config = le16_to_cpu(ctrl_req->wValue); int result = 0; - int i; switch (device_state) { case USB_STATE_ADDRESS: - /* Configure non-control EPs */ - for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { - priv_ep = priv_dev->eps[i]; - if (!priv_ep) - continue; - - if (priv_ep->flags & EP_CLAIMED) - cdns3_ep_config(priv_ep); - } - result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); - if (result) - return result; - - if (!config) { - cdns3_hw_reset_eps_config(priv_dev); - usb_gadget_set_state(&priv_dev->gadget, - USB_STATE_ADDRESS); - } + if (result || !config) + goto reset_config; break; case USB_STATE_CONFIGURED: result = cdns3_ep0_delegate_req(priv_dev, ctrl_req); + if (!config && !result) + goto reset_config; - if (!config && !result) { - cdns3_hw_reset_eps_config(priv_dev); - usb_gadget_set_state(&priv_dev->gadget, - USB_STATE_ADDRESS); - } break; default: - result = -EINVAL; + return -EINVAL; } + return 0; + +reset_config: + if (result != USB_GADGET_DELAYED_STATUS) + cdns3_hw_reset_eps_config(priv_dev); + + usb_gadget_set_state(&priv_dev->gadget, + USB_STATE_ADDRESS); + return result; } @@ -705,6 +693,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, unsigned long flags; int ret = 0; u8 zlp = 0; + int i; spin_lock_irqsave(&priv_dev->lock, flags); trace_cdns3_ep0_queue(priv_dev, request); @@ -720,6 +709,17 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep, u32 val; cdns3_select_ep(priv_dev, 0x00); + + /* + * Configure all non-control EPs which are not enabled by class driver + */ + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) { + priv_ep = priv_dev->eps[i]; + if (priv_ep && priv_ep->flags & EP_CLAIMED && + !(priv_ep->flags & EP_ENABLED)) + cdns3_ep_config(priv_ep, 0); + } + cdns3_set_hw_configuration(priv_dev); cdns3_ep0_complete_setup(priv_dev, 0, 1); /* wait until configuration set */ @@ -811,6 +811,7 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) struct cdns3_usb_regs __iomem *regs; struct cdns3_endpoint *priv_ep; u32 max_packet_size = 64; + u32 ep_cfg; regs = priv_dev->regs; @@ -842,8 +843,10 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) BIT(0) | BIT(16)); } - writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size), - ®s->ep_cfg); + ep_cfg = EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size); + + if (!(priv_ep->flags & EP_CONFIGURED)) + writel(ep_cfg, ®s->ep_cfg); writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en); @@ -851,8 +854,10 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev) /* init ep in */ cdns3_select_ep(priv_dev, USB_DIR_IN); - writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size), - ®s->ep_cfg); + if (!(priv_ep->flags & EP_CONFIGURED)) + writel(ep_cfg, ®s->ep_cfg); + + priv_ep->flags |= EP_CONFIGURED; writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en); diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 692acf7b9b14..36c8c1aede5e 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -296,6 +296,8 @@ static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep) */ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev) { + int i; + writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf); cdns3_allow_enable_l1(priv_dev, 0); @@ -304,6 +306,10 @@ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev) priv_dev->out_mem_is_allocated = 0; priv_dev->wait_for_setup = 0; priv_dev->using_streams = 0; + + for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) + if (priv_dev->eps[i]) + priv_dev->eps[i]->flags &= ~EP_CONFIGURED; } /** @@ -1977,27 +1983,6 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev, return 0; } -static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev, - struct cdns3_endpoint *priv_ep) -{ - if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER) - return; - - if (priv_dev->dev_ver >= DEV_VER_V3) { - u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0)); - - /* - * Stream capable endpoints are handled by using ep_tdl - * register. Other endpoints use TDL from TRB feature. - */ - cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb, mask); - } - - /* Enable Stream Bit TDL chk and SID chk */ - cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_STREAM_EN | - EP_CFG_TDL_CHK | EP_CFG_SID_CHK); -} - static void cdns3_configure_dmult(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep) { @@ -2035,8 +2020,9 @@ static void cdns3_configure_dmult(struct cdns3_device *priv_dev, /** * cdns3_ep_config Configure hardware endpoint * @priv_ep: extended endpoint object + * @enable: set EP_CFG_ENABLE bit in ep_cfg register. */ -void cdns3_ep_config(struct cdns3_endpoint *priv_ep) +int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable) { bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC); struct cdns3_device *priv_dev = priv_ep->cdns3_dev; @@ -2097,7 +2083,7 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) break; default: /* all other speed are not supported */ - return; + return -EINVAL; } if (max_packet_size == 1024) @@ -2107,11 +2093,33 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) else priv_ep->trb_burst_size = 16; - ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1, - !!priv_ep->dir); - if (ret) { - dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n"); - return; + /* onchip buffer is only allocated before configuration */ + if (!priv_dev->hw_configured_flag) { + ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1, + !!priv_ep->dir); + if (ret) { + dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n"); + return ret; + } + } + + if (enable) + ep_cfg |= EP_CFG_ENABLE; + + if (priv_ep->use_streams && priv_dev->gadget.speed >= USB_SPEED_SUPER) { + if (priv_dev->dev_ver >= DEV_VER_V3) { + u32 mask = BIT(priv_ep->num + (priv_ep->dir ? 16 : 0)); + + /* + * Stream capable endpoints are handled by using ep_tdl + * register. Other endpoints use TDL from TRB feature. + */ + cdns3_clear_register_bit(&priv_dev->regs->tdl_from_trb, + mask); + } + + /* Enable Stream Bit TDL chk and SID chk */ + ep_cfg |= EP_CFG_STREAM_EN | EP_CFG_TDL_CHK | EP_CFG_SID_CHK; } ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) | @@ -2121,9 +2129,12 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep) cdns3_select_ep(priv_dev, bEndpointAddress); writel(ep_cfg, &priv_dev->regs->ep_cfg); + priv_ep->flags |= EP_CONFIGURED; dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n", priv_ep->name, ep_cfg); + + return 0; } /* Find correct direction for HW endpoint according to description */ @@ -2264,7 +2275,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, u32 bEndpointAddress; unsigned long flags; int enable = 1; - int ret; + int ret = 0; int val; priv_ep = ep_to_cdns3_ep(ep); @@ -2303,6 +2314,17 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, bEndpointAddress = priv_ep->num | priv_ep->dir; cdns3_select_ep(priv_dev, bEndpointAddress); + /* + * For some versions of controller at some point during ISO OUT traffic + * DMA reads Transfer Ring for the EP which has never got doorbell. + * This issue was detected only on simulation, but to avoid this issue + * driver add protection against it. To fix it driver enable ISO OUT + * endpoint before setting DRBL. This special treatment of ISO OUT + * endpoints are recommended by controller specification. + */ + if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) + enable = 0; + if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { /* * Enable stream support (SS mode) related interrupts @@ -2313,13 +2335,17 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, EP_STS_EN_SIDERREN | EP_STS_EN_MD_EXITEN | EP_STS_EN_STREAMREN; priv_ep->use_streams = true; - cdns3_stream_ep_reconfig(priv_dev, priv_ep); + ret = cdns3_ep_config(priv_ep, enable); priv_dev->using_streams |= true; } + } else { + ret = cdns3_ep_config(priv_ep, enable); } - ret = cdns3_allocate_trb_pool(priv_ep); + if (ret) + goto exit; + ret = cdns3_allocate_trb_pool(priv_ep); if (ret) goto exit; @@ -2349,20 +2375,6 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, writel(reg, &priv_dev->regs->ep_sts_en); - /* - * For some versions of controller at some point during ISO OUT traffic - * DMA reads Transfer Ring for the EP which has never got doorbell. - * This issue was detected only on simulation, but to avoid this issue - * driver add protection against it. To fix it driver enable ISO OUT - * endpoint before setting DRBL. This special treatment of ISO OUT - * endpoints are recommended by controller specification. - */ - if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir) - enable = 0; - - if (enable) - cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE); - ep->desc = desc; priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING | EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN); diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h index 737377913788..21fa461c518e 100644 --- a/drivers/usb/cdns3/gadget.h +++ b/drivers/usb/cdns3/gadget.h @@ -1159,6 +1159,7 @@ struct cdns3_endpoint { #define EP_QUIRK_EXTRA_BUF_DET BIT(12) #define EP_QUIRK_EXTRA_BUF_EN BIT(13) #define EP_TDLCHK_EN BIT(15) +#define EP_CONFIGURED BIT(16) u32 flags; struct cdns3_request *descmis_req; @@ -1360,7 +1361,7 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, int cdns3_init_ep0(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep); void cdns3_ep0_config(struct cdns3_device *priv_dev); -void cdns3_ep_config(struct cdns3_endpoint *priv_ep); +int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable); void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir); int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev); -- cgit v1.2.3 From e11d2bf271589e70ea80a2ee3e116c40fcac62c2 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 29 Oct 2020 17:55:18 +0800 Subject: usb: cdns3: gadget: own the lock wrongly at the suspend routine When the system goes to suspend, if the controller is at device mode with cable connecting to host, the call stack is: cdns3_suspend-> cdns3_gadget_suspend -> cdns3_disconnect_gadget, after cdns3_disconnect_gadget is called, it owns lock wrongly, it causes the system being deadlock after resume due to at cdns3_device_thread_irq_handler, it tries to get the lock, but can't get it forever. To fix it, we delete the unlock-lock operations at cdns3_disconnect_gadget, and do it at the caller. Fixes: b1234e3b3b26 ("usb: cdns3: add runtime PM support") Acked-by: Pawel Laszczak Signed-off-by: Peter Chen --- drivers/usb/cdns3/gadget.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c index 36c8c1aede5e..66c1e6723eb1 100644 --- a/drivers/usb/cdns3/gadget.c +++ b/drivers/usb/cdns3/gadget.c @@ -1750,11 +1750,8 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep) static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev) { - if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) { - spin_unlock(&priv_dev->lock); + if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) priv_dev->gadget_driver->disconnect(&priv_dev->gadget); - spin_lock(&priv_dev->lock); - } } /** @@ -1765,6 +1762,7 @@ static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev) */ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, u32 usb_ists) +__must_hold(&priv_dev->lock) { int speed = 0; @@ -1789,7 +1787,9 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev, /* Disconnection detected */ if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) { + spin_unlock(&priv_dev->lock); cdns3_disconnect_gadget(priv_dev); + spin_lock(&priv_dev->lock); priv_dev->gadget.speed = USB_SPEED_UNKNOWN; usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); cdns3_hw_reset_eps_config(priv_dev); @@ -3275,10 +3275,13 @@ err0: } static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup) +__must_hold(&cdns->lock) { struct cdns3_device *priv_dev = cdns->gadget_dev; + spin_unlock(&cdns->lock); cdns3_disconnect_gadget(priv_dev); + spin_lock(&cdns->lock); priv_dev->gadget.speed = USB_SPEED_UNKNOWN; usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED); -- cgit v1.2.3 From 7922460e33c81f41e0d2421417228b32e6fdbe94 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Oct 2020 15:08:53 +0300 Subject: vhost_vdpa: Return -EFAULT if copy_from_user() fails The copy_to/from_user() functions return the number of bytes which we weren't able to copy but the ioctl should return -EFAULT if they fail. Fixes: a127c5bbb6a8 ("vhost-vdpa: fix backend feature ioctls") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20201023120853.GI282278@mwanda Signed-off-by: Michael S. Tsirkin Cc: stable@vger.kernel.org Acked-by: Jason Wang --- drivers/vhost/vdpa.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 846de69d9c01..37f461cff892 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -432,12 +432,11 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, void __user *argp = (void __user *)arg; u64 __user *featurep = argp; u64 features; - long r; + long r = 0; if (cmd == VHOST_SET_BACKEND_FEATURES) { - r = copy_from_user(&features, featurep, sizeof(features)); - if (r) - return r; + if (copy_from_user(&features, featurep, sizeof(features))) + return -EFAULT; if (features & ~VHOST_VDPA_BACKEND_FEATURES) return -EOPNOTSUPP; vhost_set_backend_features(&v->vdev, features); @@ -480,7 +479,8 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, break; case VHOST_GET_BACKEND_FEATURES: features = VHOST_VDPA_BACKEND_FEATURES; - r = copy_to_user(featurep, &features, sizeof(features)); + if (copy_to_user(featurep, &features, sizeof(features))) + r = -EFAULT; break; case VHOST_VDPA_GET_IOVA_RANGE: r = vhost_vdpa_get_iova_range(v, argp); -- cgit v1.2.3 From 7ba08e81cb4aec9724ab7674a5de49e7a341062c Mon Sep 17 00:00:00 2001 From: Jing Xiangfeng Date: Mon, 26 Oct 2020 15:06:37 +0800 Subject: vdpa/mlx5: Fix error return in map_direct_mr() Fix to return the variable "err" from the error handling case instead of "ret". Fixes: 94abbccdf291 ("vdpa/mlx5: Add shared memory registration code") Signed-off-by: Jing Xiangfeng Link: https://lore.kernel.org/r/20201026070637.164321-1-jingxiangfeng@huawei.com Signed-off-by: Michael S. Tsirkin Acked-by: Eli Cohen Cc: stable@vger.kernel.org Acked-by: Jason Wang --- drivers/vdpa/mlx5/core/mr.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index ef1c550f8266..4b6195666c58 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -239,7 +239,6 @@ static int map_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr u64 paend; struct scatterlist *sg; struct device *dma = mvdev->mdev->device; - int ret; for (map = vhost_iotlb_itree_first(iotlb, mr->start, mr->end - 1); map; map = vhost_iotlb_itree_next(map, start, mr->end - 1)) { @@ -277,8 +276,8 @@ static int map_direct_mr(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_direct_mr done: mr->log_size = log_entity_size; mr->nsg = nsg; - ret = dma_map_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0); - if (!ret) + err = dma_map_sg_attrs(dma, mr->sg_head.sgl, mr->nsg, DMA_BIDIRECTIONAL, 0); + if (!err) goto err_map; err = create_direct_mr(mvdev, mr); -- cgit v1.2.3 From 5e1a3149eec8675c2767cc465903f5e4829de5b0 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 29 Oct 2020 17:53:36 -0400 Subject: Revert "vhost-vdpa: fix page pinning leakage in error path" This reverts commit 7ed9e3d97c32d969caded2dfb6e67c1a2cc5a0b1. The patch creates a DoS risk since it can result in a high order memory allocation. Fixes: 7ed9e3d97c32d ("vhost-vdpa: fix page pinning leakage in error path") Cc: stable@vger.kernel.org Signed-off-by: Michael S. Tsirkin --- drivers/vhost/vdpa.c | 119 +++++++++++++++++++++------------------------------ 1 file changed, 48 insertions(+), 71 deletions(-) (limited to 'drivers') diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 37f461cff892..ec223da70a73 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -602,13 +602,11 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, struct vhost_dev *dev = &v->vdev; struct vhost_iotlb *iotlb = dev->iotlb; struct page **page_list; - struct vm_area_struct **vmas; + unsigned long list_size = PAGE_SIZE / sizeof(struct page *); unsigned int gup_flags = FOLL_LONGTERM; - unsigned long map_pfn, last_pfn = 0; - unsigned long npages, lock_limit; - unsigned long i, nmap = 0; + unsigned long npages, cur_base, map_pfn, last_pfn = 0; + unsigned long locked, lock_limit, pinned, i; u64 iova = msg->iova; - long pinned; int ret = 0; if (msg->iova < v->range.first || @@ -619,6 +617,10 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, msg->iova + msg->size - 1)) return -EEXIST; + page_list = (struct page **) __get_free_page(GFP_KERNEL); + if (!page_list) + return -ENOMEM; + if (msg->perm & VHOST_ACCESS_WO) gup_flags |= FOLL_WRITE; @@ -626,86 +628,61 @@ static int vhost_vdpa_process_iotlb_update(struct vhost_vdpa *v, if (!npages) return -EINVAL; - page_list = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); - vmas = kvmalloc_array(npages, sizeof(struct vm_area_struct *), - GFP_KERNEL); - if (!page_list || !vmas) { - ret = -ENOMEM; - goto free; - } - mmap_read_lock(dev->mm); + locked = atomic64_add_return(npages, &dev->mm->pinned_vm); lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; - if (npages + atomic64_read(&dev->mm->pinned_vm) > lock_limit) { - ret = -ENOMEM; - goto unlock; - } - pinned = pin_user_pages(msg->uaddr & PAGE_MASK, npages, gup_flags, - page_list, vmas); - if (npages != pinned) { - if (pinned < 0) { - ret = pinned; - } else { - unpin_user_pages(page_list, pinned); - ret = -ENOMEM; - } - goto unlock; + if (locked > lock_limit) { + ret = -ENOMEM; + goto out; } + cur_base = msg->uaddr & PAGE_MASK; iova &= PAGE_MASK; - map_pfn = page_to_pfn(page_list[0]); - - /* One more iteration to avoid extra vdpa_map() call out of loop. */ - for (i = 0; i <= npages; i++) { - unsigned long this_pfn; - u64 csize; - - /* The last chunk may have no valid PFN next to it */ - this_pfn = i < npages ? page_to_pfn(page_list[i]) : -1UL; - - if (last_pfn && (this_pfn == -1UL || - this_pfn != last_pfn + 1)) { - /* Pin a contiguous chunk of memory */ - csize = last_pfn - map_pfn + 1; - ret = vhost_vdpa_map(v, iova, csize << PAGE_SHIFT, - map_pfn << PAGE_SHIFT, - msg->perm); - if (ret) { - /* - * Unpin the rest chunks of memory on the - * flight with no corresponding vdpa_map() - * calls having been made yet. On the other - * hand, vdpa_unmap() in the failure path - * is in charge of accounting the number of - * pinned pages for its own. - * This asymmetrical pattern of accounting - * is for efficiency to pin all pages at - * once, while there is no other callsite - * of vdpa_map() than here above. - */ - unpin_user_pages(&page_list[nmap], - npages - nmap); - goto out; + + while (npages) { + pinned = min_t(unsigned long, npages, list_size); + ret = pin_user_pages(cur_base, pinned, + gup_flags, page_list, NULL); + if (ret != pinned) + goto out; + + if (!last_pfn) + map_pfn = page_to_pfn(page_list[0]); + + for (i = 0; i < ret; i++) { + unsigned long this_pfn = page_to_pfn(page_list[i]); + u64 csize; + + if (last_pfn && (this_pfn != last_pfn + 1)) { + /* Pin a contiguous chunk of memory */ + csize = (last_pfn - map_pfn + 1) << PAGE_SHIFT; + if (vhost_vdpa_map(v, iova, csize, + map_pfn << PAGE_SHIFT, + msg->perm)) + goto out; + map_pfn = this_pfn; + iova += csize; } - atomic64_add(csize, &dev->mm->pinned_vm); - nmap += csize; - iova += csize << PAGE_SHIFT; - map_pfn = this_pfn; + + last_pfn = this_pfn; } - last_pfn = this_pfn; + + cur_base += ret << PAGE_SHIFT; + npages -= ret; } - WARN_ON(nmap != npages); + /* Pin the rest chunk */ + ret = vhost_vdpa_map(v, iova, (last_pfn - map_pfn + 1) << PAGE_SHIFT, + map_pfn << PAGE_SHIFT, msg->perm); out: - if (ret) + if (ret) { vhost_vdpa_unmap(v, msg->iova, msg->size); -unlock: + atomic64_sub(npages, &dev->mm->pinned_vm); + } mmap_read_unlock(dev->mm); -free: - kvfree(vmas); - kvfree(page_list); + free_page((unsigned long)page_list); return ret; } -- cgit v1.2.3 From 1eca16b231570c8ae57fb91fdfbc48eb52c6a93b Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 27 Oct 2020 18:59:14 +0100 Subject: vdpa_sim: Fix DMA mask Since commit f959dcd6ddfd ("dma-direct: Fix potential NULL pointer dereference") an error is reported when we load vdpa_sim and virtio-vdpa: [ 129.351207] net eth0: Unexpected TXQ (0) queue failure: -12 It seems that dma_mask is not initialized. This patch initializes dma_mask() and calls dma_set_mask_and_coherent() to fix the problem. Full log: [ 128.548628] ------------[ cut here ]------------ [ 128.553268] WARNING: CPU: 23 PID: 1105 at kernel/dma/mapping.c:149 dma_map_page_attrs+0x14c/0x1d0 [ 128.562139] Modules linked in: virtio_net net_failover failover virtio_vdpa vdpa_sim vringh vhost_iotlb vdpa xt_CHECKSUM xt_MASQUERADE xt_conntrack ipt_REJECT nf_reject_ipv4 nft_compat nft_counter nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 nf_tables nfnetlink tun bridge stp llc iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi rfkill intel_rapl_msr intel_rapl_common isst_if_common sunrpc skx_edac nfit libnvdimm x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel ipmi_ssif kvm mgag200 i2c_algo_bit irqbypass drm_kms_helper crct10dif_pclmul crc32_pclmul syscopyarea ghash_clmulni_intel iTCO_wdt sysfillrect iTCO_vendor_support sysimgblt rapl fb_sys_fops dcdbas intel_cstate drm acpi_ipmi ipmi_si mei_me dell_smbios intel_uncore ipmi_devintf mei i2c_i801 dell_wmi_descriptor wmi_bmof pcspkr lpc_ich i2c_smbus ipmi_msghandler acpi_power_meter ip_tables xfs libcrc32c sd_mod t10_pi sg ahci libahci libata megaraid_sas tg3 crc32c_intel wmi dm_mirror dm_region_hash dm_log [ 128.562188] dm_mod [ 128.651334] CPU: 23 PID: 1105 Comm: NetworkManager Tainted: G S I 5.10.0-rc1+ #59 [ 128.659939] Hardware name: Dell Inc. PowerEdge R440/04JN2K, BIOS 2.8.1 06/30/2020 [ 128.667419] RIP: 0010:dma_map_page_attrs+0x14c/0x1d0 [ 128.672384] Code: 1c 25 28 00 00 00 0f 85 97 00 00 00 48 83 c4 10 5b 5d 41 5c 41 5d c3 4c 89 da eb d7 48 89 f2 48 2b 50 18 48 89 d0 eb 8d 0f 0b <0f> 0b 48 c7 c0 ff ff ff ff eb c3 48 89 d9 48 8b 40 40 e8 2d a0 aa [ 128.691131] RSP: 0018:ffffae0f0151f3c8 EFLAGS: 00010246 [ 128.696357] RAX: ffffffffc06b7400 RBX: 00000000000005fa RCX: 0000000000000000 [ 128.703488] RDX: 0000000000000040 RSI: ffffcee3c7861200 RDI: ffff9e2bc16cd000 [ 128.710620] RBP: 0000000000000000 R08: 0000000000000002 R09: 0000000000000000 [ 128.717754] R10: 0000000000000002 R11: 0000000000000000 R12: ffff9e472cb291f8 [ 128.724886] R13: ffff9e2bc14da780 R14: ffff9e472bc20000 R15: ffff9e2bc1b14940 [ 128.732020] FS: 00007f887bae23c0(0000) GS:ffff9e4ac01c0000(0000) knlGS:0000000000000000 [ 128.740105] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 128.745852] CR2: 0000562bc09de998 CR3: 00000003c156c006 CR4: 00000000007706e0 [ 128.752982] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 128.760114] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [ 128.767247] PKRU: 55555554 [ 128.769961] Call Trace: [ 128.772418] virtqueue_add+0x81e/0xb00 [ 128.776176] virtqueue_add_inbuf_ctx+0x26/0x30 [ 128.780625] try_fill_recv+0x3a2/0x6e0 [virtio_net] [ 128.785509] virtnet_open+0xf9/0x180 [virtio_net] [ 128.790217] __dev_open+0xe8/0x180 [ 128.793620] __dev_change_flags+0x1a7/0x210 [ 128.797808] dev_change_flags+0x21/0x60 [ 128.801646] do_setlink+0x328/0x10e0 [ 128.805227] ? __nla_validate_parse+0x121/0x180 [ 128.809757] ? __nla_parse+0x21/0x30 [ 128.813338] ? inet6_validate_link_af+0x5c/0xf0 [ 128.817871] ? cpumask_next+0x17/0x20 [ 128.821535] ? __snmp6_fill_stats64.isra.54+0x6b/0x110 [ 128.826676] ? __nla_validate_parse+0x47/0x180 [ 128.831120] __rtnl_newlink+0x541/0x8e0 [ 128.834962] ? __nla_reserve+0x38/0x50 [ 128.838713] ? security_sock_rcv_skb+0x2a/0x40 [ 128.843158] ? netlink_deliver_tap+0x2c/0x1e0 [ 128.847518] ? netlink_attachskb+0x1d8/0x220 [ 128.851793] ? skb_queue_tail+0x1b/0x50 [ 128.855641] ? fib6_clean_node+0x43/0x170 [ 128.859652] ? _cond_resched+0x15/0x30 [ 128.863406] ? kmem_cache_alloc_trace+0x3a3/0x420 [ 128.868110] rtnl_newlink+0x43/0x60 [ 128.871602] rtnetlink_rcv_msg+0x12c/0x380 [ 128.875701] ? rtnl_calcit.isra.39+0x110/0x110 [ 128.880147] netlink_rcv_skb+0x50/0x100 [ 128.883987] netlink_unicast+0x1a5/0x280 [ 128.887913] netlink_sendmsg+0x23d/0x470 [ 128.891839] sock_sendmsg+0x5b/0x60 [ 128.895331] ____sys_sendmsg+0x1ef/0x260 [ 128.899255] ? copy_msghdr_from_user+0x5c/0x90 [ 128.903702] ___sys_sendmsg+0x7c/0xc0 [ 128.907369] ? dev_forward_change+0x130/0x130 [ 128.911731] ? sysctl_head_finish.part.29+0x24/0x40 [ 128.916616] ? new_sync_write+0x11f/0x1b0 [ 128.920628] ? mntput_no_expire+0x47/0x240 [ 128.924727] __sys_sendmsg+0x57/0xa0 [ 128.928309] do_syscall_64+0x33/0x40 [ 128.931887] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 128.936937] RIP: 0033:0x7f88792e3857 [ 128.940518] Code: c3 66 90 41 54 41 89 d4 55 48 89 f5 53 89 fb 48 83 ec 10 e8 0b ed ff ff 44 89 e2 48 89 ee 89 df 41 89 c0 b8 2e 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 35 44 89 c7 48 89 44 24 08 e8 44 ed ff ff 48 [ 128.959263] RSP: 002b:00007ffdca60dea0 EFLAGS: 00000293 ORIG_RAX: 000000000000002e [ 128.966827] RAX: ffffffffffffffda RBX: 000000000000000c RCX: 00007f88792e3857 [ 128.973960] RDX: 0000000000000000 RSI: 00007ffdca60def0 RDI: 000000000000000c [ 128.981095] RBP: 00007ffdca60def0 R08: 0000000000000000 R09: 0000000000000000 [ 128.988224] R10: 0000000000000001 R11: 0000000000000293 R12: 0000000000000000 [ 128.995357] R13: 0000000000000000 R14: 00007ffdca60e0a8 R15: 00007ffdca60e09c [ 129.002492] CPU: 23 PID: 1105 Comm: NetworkManager Tainted: G S I 5.10.0-rc1+ #59 [ 129.011093] Hardware name: Dell Inc. PowerEdge R440/04JN2K, BIOS 2.8.1 06/30/2020 [ 129.018571] Call Trace: [ 129.021027] dump_stack+0x57/0x6a [ 129.024346] __warn.cold.14+0xe/0x3d [ 129.027925] ? dma_map_page_attrs+0x14c/0x1d0 [ 129.032283] report_bug+0xbd/0xf0 [ 129.035602] handle_bug+0x44/0x80 [ 129.038922] exc_invalid_op+0x13/0x60 [ 129.042589] asm_exc_invalid_op+0x12/0x20 [ 129.046602] RIP: 0010:dma_map_page_attrs+0x14c/0x1d0 [ 129.051566] Code: 1c 25 28 00 00 00 0f 85 97 00 00 00 48 83 c4 10 5b 5d 41 5c 41 5d c3 4c 89 da eb d7 48 89 f2 48 2b 50 18 48 89 d0 eb 8d 0f 0b <0f> 0b 48 c7 c0 ff ff ff ff eb c3 48 89 d9 48 8b 40 40 e8 2d a0 aa [ 129.070311] RSP: 0018:ffffae0f0151f3c8 EFLAGS: 00010246 [ 129.075536] RAX: ffffffffc06b7400 RBX: 00000000000005fa RCX: 0000000000000000 [ 129.082669] RDX: 0000000000000040 RSI: ffffcee3c7861200 RDI: ffff9e2bc16cd000 [ 129.089803] RBP: 0000000000000000 R08: 0000000000000002 R09: 0000000000000000 [ 129.096936] R10: 0000000000000002 R11: 0000000000000000 R12: ffff9e472cb291f8 [ 129.104068] R13: ffff9e2bc14da780 R14: ffff9e472bc20000 R15: ffff9e2bc1b14940 [ 129.111200] virtqueue_add+0x81e/0xb00 [ 129.114952] virtqueue_add_inbuf_ctx+0x26/0x30 [ 129.119399] try_fill_recv+0x3a2/0x6e0 [virtio_net] [ 129.124280] virtnet_open+0xf9/0x180 [virtio_net] [ 129.128984] __dev_open+0xe8/0x180 [ 129.132390] __dev_change_flags+0x1a7/0x210 [ 129.136575] dev_change_flags+0x21/0x60 [ 129.140415] do_setlink+0x328/0x10e0 [ 129.143994] ? __nla_validate_parse+0x121/0x180 [ 129.148528] ? __nla_parse+0x21/0x30 [ 129.152107] ? inet6_validate_link_af+0x5c/0xf0 [ 129.156639] ? cpumask_next+0x17/0x20 [ 129.160306] ? __snmp6_fill_stats64.isra.54+0x6b/0x110 [ 129.165443] ? __nla_validate_parse+0x47/0x180 [ 129.169890] __rtnl_newlink+0x541/0x8e0 [ 129.173731] ? __nla_reserve+0x38/0x50 [ 129.177483] ? security_sock_rcv_skb+0x2a/0x40 [ 129.181928] ? netlink_deliver_tap+0x2c/0x1e0 [ 129.186286] ? netlink_attachskb+0x1d8/0x220 [ 129.190560] ? skb_queue_tail+0x1b/0x50 [ 129.194401] ? fib6_clean_node+0x43/0x170 [ 129.198411] ? _cond_resched+0x15/0x30 [ 129.202163] ? kmem_cache_alloc_trace+0x3a3/0x420 [ 129.206869] rtnl_newlink+0x43/0x60 [ 129.210361] rtnetlink_rcv_msg+0x12c/0x380 [ 129.214462] ? rtnl_calcit.isra.39+0x110/0x110 [ 129.218908] netlink_rcv_skb+0x50/0x100 [ 129.222747] netlink_unicast+0x1a5/0x280 [ 129.226672] netlink_sendmsg+0x23d/0x470 [ 129.230599] sock_sendmsg+0x5b/0x60 [ 129.234090] ____sys_sendmsg+0x1ef/0x260 [ 129.238015] ? copy_msghdr_from_user+0x5c/0x90 [ 129.242461] ___sys_sendmsg+0x7c/0xc0 [ 129.246128] ? dev_forward_change+0x130/0x130 [ 129.250487] ? sysctl_head_finish.part.29+0x24/0x40 [ 129.255368] ? new_sync_write+0x11f/0x1b0 [ 129.259381] ? mntput_no_expire+0x47/0x240 [ 129.263478] __sys_sendmsg+0x57/0xa0 [ 129.267058] do_syscall_64+0x33/0x40 [ 129.270639] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 129.275689] RIP: 0033:0x7f88792e3857 [ 129.279268] Code: c3 66 90 41 54 41 89 d4 55 48 89 f5 53 89 fb 48 83 ec 10 e8 0b ed ff ff 44 89 e2 48 89 ee 89 df 41 89 c0 b8 2e 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 35 44 89 c7 48 89 44 24 08 e8 44 ed ff ff 48 [ 129.298015] RSP: 002b:00007ffdca60dea0 EFLAGS: 00000293 ORIG_RAX: 000000000000002e [ 129.305581] RAX: ffffffffffffffda RBX: 000000000000000c RCX: 00007f88792e3857 [ 129.312712] RDX: 0000000000000000 RSI: 00007ffdca60def0 RDI: 000000000000000c [ 129.319846] RBP: 00007ffdca60def0 R08: 0000000000000000 R09: 0000000000000000 [ 129.326978] R10: 0000000000000001 R11: 0000000000000293 R12: 0000000000000000 [ 129.334109] R13: 0000000000000000 R14: 00007ffdca60e0a8 R15: 00007ffdca60e09c [ 129.341249] ---[ end trace c551e8028fbaf59d ]--- [ 129.351207] net eth0: Unexpected TXQ (0) queue failure: -12 [ 129.360445] net eth0: Unexpected TXQ (0) queue failure: -12 [ 129.824428] net eth0: Unexpected TXQ (0) queue failure: -12 Fixes: 2c53d0f64c06 ("vdpasim: vDPA device simulator") Signed-off-by: Laurent Vivier Link: https://lore.kernel.org/r/20201027175914.689278-1-lvivier@redhat.com Signed-off-by: Michael S. Tsirkin Cc: stable@vger.kernel.org Acked-by: Jason Wang --- drivers/vdpa/vdpa_sim/vdpa_sim.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index ff6c9fd8d879..c76f7277cea4 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -361,7 +361,9 @@ static struct vdpasim *vdpasim_create(void) spin_lock_init(&vdpasim->iommu_lock); dev = &vdpasim->vdpa.dev; - dev->coherent_dma_mask = DMA_BIT_MASK(64); + dev->dma_mask = &dev->coherent_dma_mask; + if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) + goto err_iommu; set_dma_ops(dev, &vdpasim_dma_ops); vdpasim->iommu = vhost_iotlb_alloc(2048, 0); -- cgit v1.2.3 From e01afe36df1a9e42cacdb9c5b99512ec69a6f14b Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Fri, 23 Oct 2020 18:40:46 +0800 Subject: vdpa: handle irq bypass register failure case LKP considered variable 'ret' in vhost_vdpa_setup_vq_irq() as a unused variable, so suggest we remove it. Actually it stores return value of irq_bypass_register_producer(), but we did not check it, we should handle the failure case. This commit will print a message if irq bypass register producer fail, in this case, vqs still remain functional. Signed-off-by: Zhu Lingshan Reported-by: kernel test robot Link: https://lore.kernel.org/r/20201023104046.404794-1-lingshan.zhu@intel.com Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang --- drivers/vhost/vdpa.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index ec223da70a73..2754f3069738 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -104,6 +104,9 @@ static void vhost_vdpa_setup_vq_irq(struct vhost_vdpa *v, u16 qid) vq->call_ctx.producer.token = vq->call_ctx.ctx; vq->call_ctx.producer.irq = irq; ret = irq_bypass_register_producer(&vq->call_ctx.producer); + if (unlikely(ret)) + dev_info(&v->dev, "vq %u, irq bypass producer (token %p) registration fails, ret = %d\n", + qid, vq->call_ctx.producer.token, ret); } static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid) -- cgit v1.2.3 From 4a6a42db53aae049a8a64d4b273761bc80c46ebf Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 29 Oct 2020 13:20:49 +0100 Subject: vdpasim: fix MAC address configuration vdpa_sim generates a ramdom MAC address but it is never used by upper layers because the VIRTIO_NET_F_MAC bit is not set in the features list. Because of that, virtio-net always regenerates a random MAC address each time it is loaded whereas the address should only change on vdpa_sim load/unload. Fix that by adding VIRTIO_NET_F_MAC in the features list of vdpa_sim. Fixes: 2c53d0f64c06 ("vdpasim: vDPA device simulator") Cc: jasowang@redhat.com Signed-off-by: Laurent Vivier Link: https://lore.kernel.org/r/20201029122050.776445-2-lvivier@redhat.com Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang --- drivers/vdpa/vdpa_sim/vdpa_sim.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index c76f7277cea4..3192ce47e029 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -60,7 +60,8 @@ struct vdpasim_virtqueue { static u64 vdpasim_features = (1ULL << VIRTIO_F_ANY_LAYOUT) | (1ULL << VIRTIO_F_VERSION_1) | - (1ULL << VIRTIO_F_ACCESS_PLATFORM); + (1ULL << VIRTIO_F_ACCESS_PLATFORM) | + (1ULL << VIRTIO_NET_F_MAC); /* State of each vdpasim device */ struct vdpasim { -- cgit v1.2.3 From 0c86d774883fa17e7c81b0c8838b88d06c2c911e Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 29 Oct 2020 13:20:50 +0100 Subject: vdpasim: allow to assign a MAC address Add macaddr parameter to the module to set the MAC address to use Signed-off-by: Laurent Vivier Link: https://lore.kernel.org/r/20201029122050.776445-3-lvivier@redhat.com Signed-off-by: Michael S. Tsirkin Acked-by: Jason Wang --- drivers/vdpa/vdpa_sim/vdpa_sim.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index 3192ce47e029..9e9ead83c19b 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -38,6 +38,10 @@ static int batch_mapping = 1; module_param(batch_mapping, int, 0444); MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable"); +static char *macaddr; +module_param(macaddr, charp, 0); +MODULE_PARM_DESC(macaddr, "Ethernet MAC address"); + struct vdpasim_virtqueue { struct vringh vring; struct vringh_kiov iov; @@ -375,7 +379,15 @@ static struct vdpasim *vdpasim_create(void) if (!vdpasim->buffer) goto err_iommu; - eth_random_addr(vdpasim->config.mac); + if (macaddr) { + mac_pton(macaddr, vdpasim->config.mac); + if (!is_valid_ether_addr(vdpasim->config.mac)) { + ret = -EADDRNOTAVAIL; + goto err_iommu; + } + } else { + eth_random_addr(vdpasim->config.mac); + } vringh_set_iotlb(&vdpasim->vqs[0].vring, vdpasim->iommu); vringh_set_iotlb(&vdpasim->vqs[1].vring, vdpasim->iommu); -- cgit v1.2.3 From 4acbf5545d5acfeeac6d84e31cb2203ba19223ef Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Oct 2020 00:54:08 -0500 Subject: Bluetooth: btintel: Replace zero-length array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a regular need in the kernel to provide a way to declare having a dynamically sized set of trailing elements in a structure. Kernel code should always use “flexible array members”[1] for these cases. The older style of one-element or zero-length arrays should no longer be used[2]. [1] https://en.wikipedia.org/wiki/Flexible_array_member [2] https://www.kernel.org/doc/html/v5.9/process/deprecated.html#zero-length-and-one-element-arrays Signed-off-by: Gustavo A. R. Silva --- drivers/bluetooth/btintel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 09346ae308eb..78cc64b42b30 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -47,7 +47,7 @@ enum { struct intel_tlv { u8 type; u8 len; - u8 val[0]; + u8 val[]; } __packed; struct intel_version_tlv { -- cgit v1.2.3 From 691f4077d5604af4067ebbea4a27f05e760557b7 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Oct 2020 16:30:45 -0500 Subject: gve: Replace zero-length array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a regular need in the kernel to provide a way to declare having a dynamically sized set of trailing elements in a structure. Kernel code should always use “flexible array members”[1] for these cases. The older style of one-element or zero-length arrays should no longer be used[2]. Refactor the code according to the use of a flexible-array member in struct gve_stats_report, instead of a zero-length array, and use the struct_size() helper to calculate the size for the resource allocation. [1] https://en.wikipedia.org/wiki/Flexible_array_member [2] https://www.kernel.org/doc/html/v5.9/process/deprecated.html#zero-length-and-one-element-arrays Signed-off-by: Gustavo A. R. Silva --- drivers/net/ethernet/google/gve/gve_adminq.h | 2 +- drivers/net/ethernet/google/gve/gve_main.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/google/gve/gve_adminq.h b/drivers/net/ethernet/google/gve/gve_adminq.h index 281de8326bc5..015796a20118 100644 --- a/drivers/net/ethernet/google/gve/gve_adminq.h +++ b/drivers/net/ethernet/google/gve/gve_adminq.h @@ -198,7 +198,7 @@ static_assert(sizeof(struct stats) == 16); struct gve_stats_report { __be64 written_count; - struct stats stats[0]; + struct stats stats[]; }; static_assert(sizeof(struct gve_stats_report) == 8); diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 48a433154ce0..02e7d74779f4 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -116,9 +116,8 @@ static int gve_alloc_stats_report(struct gve_priv *priv) priv->tx_cfg.num_queues; rx_stats_num = (GVE_RX_STATS_REPORT_NUM + NIC_RX_STATS_REPORT_NUM) * priv->rx_cfg.num_queues; - priv->stats_report_len = sizeof(struct gve_stats_report) + - (tx_stats_num + rx_stats_num) * - sizeof(struct stats); + priv->stats_report_len = struct_size(priv->stats_report, stats, + tx_stats_num + rx_stats_num); priv->stats_report = dma_alloc_coherent(&priv->pdev->dev, priv->stats_report_len, &priv->stats_report_bus, GFP_KERNEL); -- cgit v1.2.3 From b21de80ac156044c731889a686135e6d0b7c2bcc Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Tue, 27 Oct 2020 01:15:49 -0500 Subject: mei: hw: Replace zero-length array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a regular need in the kernel to provide a way to declare having a dynamically sized set of trailing elements in a structure. Kernel code should always use “flexible array members”[1] for these cases. The older style of one-element or zero-length arrays should no longer be used[2]. [1] https://en.wikipedia.org/wiki/Flexible_array_member [2] https://www.kernel.org/doc/html/v5.9/process/deprecated.html#zero-length-and-one-element-arrays Signed-off-by: Gustavo A. R. Silva --- drivers/misc/mei/hw.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 8bac86c4d86b..df2fb9520dd8 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -224,7 +224,7 @@ struct mei_ext_hdr { u8 type; u8 length; u8 ext_payload[2]; - u8 hdr[0]; + u8 hdr[]; }; /** @@ -238,7 +238,7 @@ struct mei_ext_meta_hdr { u8 count; u8 size; u8 reserved[2]; - struct mei_ext_hdr hdrs[0]; + struct mei_ext_hdr hdrs[]; }; /* @@ -308,7 +308,7 @@ struct mei_msg_hdr { u32 dma_ring:1; u32 internal:1; u32 msg_complete:1; - u32 extension[0]; + u32 extension[]; } __packed; /* The length is up to 9 bits */ -- cgit v1.2.3 From e6ff10f24c587c1af705b898761e5df615fb0e1a Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 3 Nov 2020 11:00:21 +0100 Subject: regulator: Add support for DA9121 regulator Add support for the Dialog Semiconductor DA9121, a single-channel dual-phase buck converter controlled via I2C. Signed-off-by: Vincent Whitchurch Link: https://lore.kernel.org/r/20201103100021.19603-3-vincent.whitchurch@axis.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 12 ++++ drivers/regulator/Makefile | 1 + drivers/regulator/da9121-regulator.c | 108 +++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 drivers/regulator/da9121-regulator.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 020a00d6696b..005a6036dd38 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -303,6 +303,18 @@ config REGULATOR_DA9063 This driver can also be built as a module. If so, the module will be called da9063-regulator. +config REGULATOR_DA9121 + tristate "Dialog Semiconductor DA9121 regulator" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support for the Dialog Semiconductor DA9121. The + DA9210 is a dual-phase buck converter controlled through an I2C + interface. + + This driver can also be built as a module. If so, the module + will be called da9121-regulator. + config REGULATOR_DA9210 tristate "Dialog Semiconductor DA9210 regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 6ebae516258e..6096862a1d60 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o +obj-$(CONFIG_REGULATOR_DA9121) += da9121-regulator.o obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c new file mode 100644 index 000000000000..66bdfd1979c0 --- /dev/null +++ b/drivers/regulator/da9121-regulator.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2020 Axis Communications AB */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DA9121_BUCK_BUCK1_0 0x20 +#define DA9121_BUCK_BUCK1_0_CH1_EN BIT(0) + +#define DA9121_BUCK_BUCK1_5 0x25 +#define DA9121_BUCK_BUCK1_5_CH1_A_VOUT GENMASK(7, 0) + +#define DA9121_MIN_MV 300 +#define DA9121_MAX_MV 1900 +#define DA9121_STEP_MV 10 +#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV) +#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \ + + 1 + DA9121_MIN_SEL) + +static const struct regmap_config da9121_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static const struct regulator_ops da9121_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +static const struct regulator_desc da9121_reg = { + .name = "da9121", + .of_match = "buck1", + .owner = THIS_MODULE, + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .vsel_reg = DA9121_BUCK_BUCK1_5, + .vsel_mask = DA9121_BUCK_BUCK1_5_CH1_A_VOUT, + .enable_reg = DA9121_BUCK_BUCK1_0, + .enable_mask = DA9121_BUCK_BUCK1_0_CH1_EN, + /* Default value of BUCK_BUCK1_0.CH1_SRC_DVC_UP */ + .ramp_delay = 20000, + /* tBUCK_EN */ + .enable_time = 20, +}; + +static const struct of_device_id da9121_dt_ids[] = { + { .compatible = "dlg,da9121", }, + { } +}; +MODULE_DEVICE_TABLE(of, da9121_dt_ids); + +static int da9121_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct regulator_config config = {}; + struct regulator_dev *rdev; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &da9121_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + config.dev = &i2c->dev; + config.of_node = dev->of_node; + config.regmap = regmap; + + rdev = devm_regulator_register(&i2c->dev, &da9121_reg, &config); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, "Failed to register da9121 regulator\n"); + return PTR_ERR(rdev); + } + + return 0; +} + +static const struct i2c_device_id da9121_i2c_id[] = { + { "da9121", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, da9121_i2c_id); + +static struct i2c_driver da9121_regulator_driver = { + .driver = { + .name = "da9121", + .of_match_table = of_match_ptr(da9121_dt_ids), + }, + .probe = da9121_i2c_probe, + .id_table = da9121_i2c_id, +}; + +module_i2c_driver(da9121_regulator_driver); + +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From d2ad981151b3a812e961c8ee0ffd7e349b4027d6 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 10 Nov 2020 10:20:17 +0200 Subject: regulator: bd718x7: Support external connection to scale voltages Setups where regulator (especially the buck8) output voltage is scaled by adding external connection where some other regulator output is connected to feedback-pin (over suitable resistors) is getting popular amongst users of BD71837. This allows for example scaling down the buck8 voltages to suit lover GPU voltages for projects where buck8 is (ab)used to supply power for GPU. As a note - some setups do allow DVS for buck8. This do produce voltage spikes and the HW must be evaluated to be able to survive them. Thus this commit still keep the DVS disabled for non DVS bucks by default. Let's not help you burn your proto board. Allow describing this external connection from DT and scale the voltages accordingly. This is what the connection should look like: |------------| | buck 8 |-------+----->Vout | | | |------------| | | FB pin | | | +-------+--R2---+ | R1 | V FB-pull-up Here the buck output is sifted according to formula: Vout_o = Vo - (Vpu - Vo)*R2/R1 Linear_step = step_orig*(R1+R2)/R1 where: Vout_o is adjusted voltage output at vsel reg value 0 Vo is original voltage output at vsel reg value 0 Vpu is the pull-up voltage V FB-pull-up in the picture R1 and R2 are resistor values. Bring support for specifying the Vpu, R1 and R2 from device tree and scale voltages if they are given. Signed-off-by: Matti Vaittinen Link: https://lore.kernel.org/r/89b2be87074f307a8823f15f34e1f662023cbf36.1604994184.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 164 ++++++++++++++++++++++++++++++++-- 1 file changed, 157 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 0774467994fb..e6d5d98c3cea 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -1323,13 +1323,142 @@ static void mark_hw_controlled(struct device *dev, struct device_node *np, dev_warn(dev, "Bad regulator node\n"); } -static int get_hw_controlled_regulators(struct device *dev, - struct bd718xx_regulator_data *reg_data, - unsigned int num_reg_data, int *info) +/* + * Setups where regulator (especially the buck8) output voltage is scaled + * by adding external connection where some other regulator output is connected + * to feedback-pin (over suitable resistors) is getting popular amongst users + * of BD71837. (This allows for example scaling down the buck8 voltages to suit + * lover GPU voltages for projects where buck8 is (ab)used to supply power + * for GPU. Additionally some setups do allow DVS for buck8 but as this do + * produce voltage spikes the HW must be evaluated to be able to survive this + * - hence I keep the DVS disabled for non DVS bucks by default. I don't want + * to help you burn your proto board) + * + * So we allow describing this external connection from DT and scale the + * voltages accordingly. This is what the connection should look like: + * + * |------------| + * | buck 8 |-------+----->Vout + * | | | + * |------------| | + * | FB pin | + * | | + * +-------+--R2---+ + * | + * R1 + * | + * V FB-pull-up + * + * Here the buck output is sifted according to formula: + * + * Vout_o = Vo - (Vpu - Vo)*R2/R1 + * Linear_step = step_orig*(R1+R2)/R1 + * + * where: + * Vout_o is adjusted voltage output at vsel reg value 0 + * Vo is original voltage output at vsel reg value 0 + * Vpu is the pull-up voltage V FB-pull-up in the picture + * R1 and R2 are resistor values. + * + * As a real world example for buck8 and a specific GPU: + * VLDO = 1.6V (used as FB-pull-up) + * R1 = 1000ohms + * R2 = 150ohms + * VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V + * Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV + */ +static int setup_feedback_loop(struct device *dev, struct device_node *np, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int fb_uv) { + int i, r1, r2, ret; + + /* + * We do adjust the values in the global desc based on DT settings. + * This may not be best approach as it can cause problems if more than + * one PMIC is controlled from same processor. I don't see such use-case + * for BD718x7 now - so we spare some bits. + * + * If this will point out to be a problem - then we can allocate new + * bd718xx_regulator_data array at probe and just use the global + * array as a template where we copy initial values. Then we can + * use allocated descs for regultor registration and do IC specific + * modifications to this copy while leaving other PMICs untouched. But + * that means allocating new array for each PMIC - and currently I see + * no need for that. + */ + + for (i = 0; i < num_reg_data; i++) { + struct regulator_desc *desc = ®_data[i].desc; + int j; + + if (!of_node_name_eq(np, desc->of_match)) + continue; + + pr_info("Looking at node '%s'\n", desc->of_match); + + /* The feedback loop connection does not make sense for LDOs */ + if (desc->id >= BD718XX_LDO1) + return -EINVAL; + + ret = of_property_read_u32(np, "rohm,feedback-pull-up-r1-ohms", + &r1); + if (ret) + return ret; + + if (!r1) + return -EINVAL; + + ret = of_property_read_u32(np, "rohm,feedback-pull-up-r2-ohms", + &r2); + if (ret) + return ret; + + if (desc->n_linear_ranges && desc->linear_ranges) { + struct linear_range *new; + + new = devm_kzalloc(dev, desc->n_linear_ranges * + sizeof(struct linear_range), + GFP_KERNEL); + if (!new) + return -ENOMEM; + + for (j = 0; j < desc->n_linear_ranges; j++) { + int min = desc->linear_ranges[j].min; + int step = desc->linear_ranges[j].step; + + min -= (fb_uv - min)*r2/r1; + step = step * (r1 + r2); + step /= r1; + + new[j].min = min; + new[j].step = step; + + dev_dbg(dev, "%s: old range min %d, step %d\n", + desc->name, desc->linear_ranges[j].min, + desc->linear_ranges[j].step); + dev_dbg(dev, "new range min %d, step %d\n", min, + step); + } + desc->linear_ranges = new; + } + dev_dbg(dev, "regulator '%s' has FB pull-up configured\n", + desc->name); + + return 0; + } + + return -ENODEV; +} + +static int get_special_regulators(struct device *dev, + struct bd718xx_regulator_data *reg_data, + unsigned int num_reg_data, int *info) +{ + int ret; struct device_node *np; struct device_node *nproot = dev->of_node; - const char *prop = "rohm,no-regulator-enable-control"; + int uv; *info = 0; @@ -1338,13 +1467,32 @@ static int get_hw_controlled_regulators(struct device *dev, dev_err(dev, "failed to find regulators node\n"); return -ENODEV; } - for_each_child_of_node(nproot, np) - if (of_property_read_bool(np, prop)) + for_each_child_of_node(nproot, np) { + if (of_property_read_bool(np, "rohm,no-regulator-enable-control")) mark_hw_controlled(dev, np, reg_data, num_reg_data, info); + ret = of_property_read_u32(np, "rohm,fb-pull-up-microvolt", + &uv); + if (ret) { + if (ret == -EINVAL) + continue; + else + goto err_out; + } + + ret = setup_feedback_loop(dev, np, reg_data, num_reg_data, uv); + if (ret) + goto err_out; + } of_node_put(nproot); return 0; + +err_out: + of_node_put(np); + of_node_put(nproot); + + return ret; } static int bd718xx_probe(struct platform_device *pdev) @@ -1432,8 +1580,10 @@ static int bd718xx_probe(struct platform_device *pdev) * be affected by PMIC state machine - Eg. regulator is likely to stay * on even in SUSPEND */ - get_hw_controlled_regulators(pdev->dev.parent, reg_data, num_reg_data, + err = get_special_regulators(pdev->dev.parent, reg_data, num_reg_data, &omit_enable); + if (err) + return err; for (i = 0; i < num_reg_data; i++) { -- cgit v1.2.3 From 0917c9db23accb8690d8f1987ada36eb5b1a5ac9 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Fri, 13 Nov 2020 01:20:27 +0100 Subject: regulator: debug early supply resolving MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Help debugging the case when set_machine_constraints() needs to be repeated. Signed-off-by: Michał Mirosław Tested-by: Ahmad Fatoum # stpmic1 Link: https://lore.kernel.org/r/f9cba575580369e46661a9278ee6c6a8d8564c2a.1605226675.git.mirq-linux@rere.qmqm.pl Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2e1ea18221ef..2aa7def7cec6 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5299,6 +5299,8 @@ regulator_register(const struct regulator_desc *regulator_desc, /* FIXME: this currently triggers a chicken-and-egg problem * when creating -SUPPLY symlink in sysfs to a regulator * that is just being created */ + rdev_dbg(rdev, "will resolve supply early: %s\n", + rdev->supply_name); ret = regulator_resolve_supply(rdev); if (!ret) ret = set_machine_constraints(rdev, constraints); -- cgit v1.2.3 From 478f8089161e9a8f487ef3f560e59d1423b81c05 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:07 +0200 Subject: regulator: mcp16502: add linear_min_sel Selectors b/w zero and VDD_LOW_SEL are not valid. Use linear_min_sel. Fixes: 919261c03e7ca ("regulator: mcp16502: add regulator driver for MCP16502") Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-4-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 6d0ad74935b3..ab78f831f5bf 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -93,6 +93,7 @@ static unsigned int mcp16502_of_map_mode(unsigned int mode) .owner = THIS_MODULE, \ .n_voltages = MCP16502_VSEL + 1, \ .linear_ranges = _ranges, \ + .linear_min_sel = VDD_LOW_SEL, \ .n_linear_ranges = ARRAY_SIZE(_ranges), \ .of_match = of_match_ptr(_name), \ .of_map_mode = mcp16502_of_map_mode, \ -- cgit v1.2.3 From 3e5532a011b09861abc2da3aa518b9aafc250570 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:08 +0200 Subject: regulator: mcp16502: adapt for get/set on other registers MCP16502 have multiple registers for each regulator (as described in enum mcp16502_reg). Adapt the code to be able to get/set all these registers. This is necessary for the following commits. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-5-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index ab78f831f5bf..48eb64bc4018 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -54,13 +54,9 @@ * This function is useful for iterating over all regulators and accessing their * registers in a generic way or accessing a regulator device by its id. */ -#define MCP16502_BASE(i) (((i) + 1) << 4) +#define MCP16502_REG_BASE(i, r) ((((i) + 1) << 4) + MCP16502_REG_##r) #define MCP16502_STAT_BASE(i) ((i) + 5) -#define MCP16502_OFFSET_MODE_A 0 -#define MCP16502_OFFSET_MODE_LPM 1 -#define MCP16502_OFFSET_MODE_HIB 2 - #define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL #define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE #define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY @@ -75,6 +71,23 @@ #define MCP16502_MIN_REG 0x0 #define MCP16502_MAX_REG 0x65 +/** + * enum mcp16502_reg - MCP16502 regulators's registers + * @MCP16502_REG_A: active state register + * @MCP16502_REG_LPM: low power mode state register + * @MCP16502_REG_HIB: hibernate state register + * @MCP16502_REG_SEQ: startup sequence register + * @MCP16502_REG_CFG: configuration register + */ +enum mcp16502_reg { + MCP16502_REG_A, + MCP16502_REG_LPM, + MCP16502_REG_HIB, + MCP16502_REG_HPM, + MCP16502_REG_SEQ, + MCP16502_REG_CFG, +}; + static unsigned int mcp16502_of_map_mode(unsigned int mode) { if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) @@ -144,22 +157,20 @@ static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode) } /* - * mcp16502_get_reg() - get the PMIC's configuration register for opmode + * mcp16502_get_reg() - get the PMIC's state configuration register for opmode * * @rdev: the regulator whose register we are searching * @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate */ -static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode) +static int mcp16502_get_state_reg(struct regulator_dev *rdev, int opmode) { - int reg = MCP16502_BASE(rdev_get_id(rdev)); - switch (opmode) { case MCP16502_OPMODE_ACTIVE: - return reg + MCP16502_OFFSET_MODE_A; + return MCP16502_REG_BASE(rdev_get_id(rdev), A); case MCP16502_OPMODE_LPM: - return reg + MCP16502_OFFSET_MODE_LPM; + return MCP16502_REG_BASE(rdev_get_id(rdev), LPM); case MCP16502_OPMODE_HIB: - return reg + MCP16502_OFFSET_MODE_HIB; + return MCP16502_REG_BASE(rdev_get_id(rdev), HIB); default: return -EINVAL; } @@ -179,7 +190,7 @@ static unsigned int mcp16502_get_mode(struct regulator_dev *rdev) unsigned int val; int ret, reg; - reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE); + reg = mcp16502_get_state_reg(rdev, MCP16502_OPMODE_ACTIVE); if (reg < 0) return reg; @@ -210,7 +221,7 @@ static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode, int val; int reg; - reg = mcp16502_get_reg(rdev, op_mode); + reg = mcp16502_get_state_reg(rdev, op_mode); if (reg < 0) return reg; @@ -269,10 +280,10 @@ static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev) { switch (pm_suspend_target_state) { case PM_SUSPEND_STANDBY: - return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM); + return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_LPM); case PM_SUSPEND_ON: case PM_SUSPEND_MEM: - return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB); + return mcp16502_get_state_reg(rdev, MCP16502_OPMODE_HIB); default: dev_err(&rdev->dev, "invalid suspend target: %d\n", pm_suspend_target_state); -- cgit v1.2.3 From 322eb8666d2f50556e89d73b54cf2dad8703c4e0 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:09 +0200 Subject: regulator: mcp16502: add support for ramp delay MCP16502 have configurable ramp delay support (via DVSR bits in regulators' CFG register). Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-6-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 89 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index 48eb64bc4018..f81afeeddb19 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -22,8 +22,9 @@ #define VDD_LOW_SEL 0x0D #define VDD_HIGH_SEL 0x3F -#define MCP16502_FLT BIT(7) -#define MCP16502_ENS BIT(0) +#define MCP16502_FLT BIT(7) +#define MCP16502_DVSR GENMASK(3, 2) +#define MCP16502_ENS BIT(0) /* * The PMIC has four sets of registers corresponding to four power modes: @@ -88,6 +89,12 @@ enum mcp16502_reg { MCP16502_REG_CFG, }; +/* Ramp delay (uV/us) for buck1, ldo1, ldo2. */ +static const int mcp16502_ramp_b1l12[] = { 6250, 3125, 2083, 1563 }; + +/* Ramp delay (uV/us) for buck2, buck3, buck4. */ +static const int mcp16502_ramp_b234[] = { 3125, 1563, 1042, 781 }; + static unsigned int mcp16502_of_map_mode(unsigned int mode) { if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE) @@ -271,6 +278,80 @@ static int mcp16502_get_status(struct regulator_dev *rdev) return REGULATOR_STATUS_UNDEFINED; } +static int mcp16502_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_sel, + unsigned int new_sel) +{ + static const u8 us_ramp[] = { 8, 16, 24, 32 }; + int id = rdev_get_id(rdev); + unsigned int uV_delta, val; + int ret; + + ret = regmap_read(rdev->regmap, MCP16502_REG_BASE(id, CFG), &val); + if (ret) + return ret; + + val = (val & MCP16502_DVSR) >> 2; + uV_delta = abs(new_sel * rdev->desc->linear_ranges->step - + old_sel * rdev->desc->linear_ranges->step); + switch (id) { + case BUCK1: + case LDO1: + case LDO2: + ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val], + mcp16502_ramp_b1l12[val]); + break; + + case BUCK2: + case BUCK3: + case BUCK4: + ret = DIV_ROUND_CLOSEST(uV_delta * us_ramp[val], + mcp16502_ramp_b234[val]); + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int mcp16502_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + const int *ramp; + int id = rdev_get_id(rdev); + unsigned int i, size; + + switch (id) { + case BUCK1: + case LDO1: + case LDO2: + ramp = mcp16502_ramp_b1l12; + size = ARRAY_SIZE(mcp16502_ramp_b1l12); + break; + + case BUCK2: + case BUCK3: + case BUCK4: + ramp = mcp16502_ramp_b234; + size = ARRAY_SIZE(mcp16502_ramp_b234); + break; + + default: + return -EINVAL; + } + + for (i = 0; i < size; i++) { + if (ramp[i] == ramp_delay) + break; + } + if (i == size) + return -EINVAL; + + return regmap_update_bits(rdev->regmap, MCP16502_REG_BASE(id, CFG), + MCP16502_DVSR, (i << 2)); +} + #ifdef CONFIG_SUSPEND /* * mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC @@ -365,6 +446,8 @@ static const struct regulator_ops mcp16502_buck_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .get_status = mcp16502_get_status, + .set_voltage_time_sel = mcp16502_set_voltage_time_sel, + .set_ramp_delay = mcp16502_set_ramp_delay, .set_mode = mcp16502_set_mode, .get_mode = mcp16502_get_mode, @@ -389,6 +472,8 @@ static const struct regulator_ops mcp16502_ldo_ops = { .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, .get_status = mcp16502_get_status, + .set_voltage_time_sel = mcp16502_set_voltage_time_sel, + .set_ramp_delay = mcp16502_set_ramp_delay, #ifdef CONFIG_SUSPEND .set_suspend_voltage = mcp16502_set_suspend_voltage, -- cgit v1.2.3 From 842f44806efaddfae5ecff8f143c2607a4fa65d7 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:10 +0200 Subject: regulator: mcp16502: remove void documentation of struct mcp16502 struct mcp16502 has no members called rdev or rmap. Remove the documentation. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-7-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/mcp16502.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c index f81afeeddb19..74ad92dc664a 100644 --- a/drivers/regulator/mcp16502.c +++ b/drivers/regulator/mcp16502.c @@ -135,8 +135,6 @@ enum { /* * struct mcp16502 - PMIC representation - * @rdev: the regulators belonging to this chip - * @rmap: regmap to be used for I2C communication * @lpm: LPM GPIO descriptor */ struct mcp16502 { -- cgit v1.2.3 From bdcd1177578cd5556f7494da86d5038db8203a16 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 17:21:05 +0200 Subject: regulator: core: validate selector against linear_min_sel There are regulators who's min selector is not zero. Selectors loops (looping b/w zero and regulator::desc::n_voltages) might throw errors because invalid selectors are used (lower than regulator::desc::linear_min_sel). For this situations validate selectors against regulator::desc::linear_min_sel. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605280870-32432-2-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 9 +++++++-- drivers/regulator/helpers.c | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2e1ea18221ef..1d0d35f14f37 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2958,7 +2958,8 @@ static int _regulator_list_voltage(struct regulator_dev *rdev, return rdev->desc->fixed_uV; if (ops->list_voltage) { - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; if (lock) regulator_lock(rdev); @@ -3109,7 +3110,8 @@ int regulator_list_hardware_vsel(struct regulator *regulator, struct regulator_dev *rdev = regulator->rdev; const struct regulator_ops *ops = rdev->desc->ops; - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) return -EOPNOTSUPP; @@ -4032,6 +4034,9 @@ int regulator_set_voltage_time(struct regulator *regulator, for (i = 0; i < rdev->desc->n_voltages; i++) { /* We only look for exact voltage matches here */ + if (i < rdev->desc->linear_min_sel) + continue; + voltage = regulator_list_voltage(regulator, i); if (voltage < 0) return -EINVAL; diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index e4bb09bbd3fa..974f1a63993d 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -647,7 +647,8 @@ int regulator_list_voltage_table(struct regulator_dev *rdev, return -EINVAL; } - if (selector >= rdev->desc->n_voltages) + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) return -EINVAL; return rdev->desc->volt_table[selector]; -- cgit v1.2.3 From ab97800e088acf34d0014845ed93605dd5c1ea2a Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 13 Nov 2020 19:56:04 +0200 Subject: regulator: core: do not continue if selector match Do not continue if selector has already been located. Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1605290164-11556-1-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7de3df6bedbf..85bf278c9378 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4037,6 +4037,9 @@ int regulator_set_voltage_time(struct regulator *regulator, if (i < rdev->desc->linear_min_sel) continue; + if (old_sel >= 0 && new_sel >= 0) + break; + voltage = regulator_list_voltage(regulator, i); if (voltage < 0) return -EINVAL; -- cgit v1.2.3 From 2add5cacff3531e54c50b0832128299faa9f0563 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 19 Nov 2020 19:10:47 +0000 Subject: firmware: arm_scmi: Add voltage domain management protocol support SCMI v3.0 introduces voltage domain protocol which provides commands to: - Discover the voltage levels supported by a domain - Get the configuration and voltage level of a domain - Set the configuration and voltage level of a domain Let us add support for the same. Link: https://lore.kernel.org/r/20201119191051.46363-2-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/common.h | 1 + drivers/firmware/arm_scmi/driver.c | 2 + drivers/firmware/arm_scmi/voltage.c | 380 ++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 64 ++++++ 5 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/voltage.c (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index bc0d54f8e861..6a2ef63306d6 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -4,7 +4,7 @@ scmi-driver-y = driver.o notify.o scmi-transport-y = shmem.o scmi-transport-$(CONFIG_MAILBOX) += mailbox.o scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o -scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o +scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \ $(scmi-transport-y) obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 65063fa948d4..c0fb45e7c3e8 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -169,6 +169,7 @@ DECLARE_SCMI_REGISTER_UNREGISTER(perf); DECLARE_SCMI_REGISTER_UNREGISTER(power); DECLARE_SCMI_REGISTER_UNREGISTER(reset); DECLARE_SCMI_REGISTER_UNREGISTER(sensors); +DECLARE_SCMI_REGISTER_UNREGISTER(voltage); DECLARE_SCMI_REGISTER_UNREGISTER(system); #define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(id, name) \ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 3dfd8b6a0ebf..ada35e63feae 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -946,6 +946,7 @@ static int __init scmi_driver_init(void) scmi_power_register(); scmi_reset_register(); scmi_sensors_register(); + scmi_voltage_register(); scmi_system_register(); return platform_driver_register(&scmi_driver); @@ -961,6 +962,7 @@ static void __exit scmi_driver_exit(void) scmi_power_unregister(); scmi_reset_unregister(); scmi_sensors_unregister(); + scmi_voltage_unregister(); scmi_system_unregister(); platform_driver_unregister(&scmi_driver); diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c new file mode 100644 index 000000000000..e794e4349ae6 --- /dev/null +++ b/drivers/firmware/arm_scmi/voltage.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Voltage Protocol + * + * Copyright (C) 2020 ARM Ltd. + */ + +#include + +#include "common.h" + +#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0) +#define REMAINING_LEVELS_MASK GENMASK(31, 16) +#define RETURNED_LEVELS_MASK GENMASK(11, 0) + +enum scmi_voltage_protocol_cmd { + VOLTAGE_DOMAIN_ATTRIBUTES = 0x3, + VOLTAGE_DESCRIBE_LEVELS = 0x4, + VOLTAGE_CONFIG_SET = 0x5, + VOLTAGE_CONFIG_GET = 0x6, + VOLTAGE_LEVEL_SET = 0x7, + VOLTAGE_LEVEL_GET = 0x8, +}; + +#define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x)))) + +struct scmi_msg_resp_domain_attributes { + __le32 attr; + u8 name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_msg_cmd_describe_levels { + __le32 domain_id; + __le32 level_index; +}; + +struct scmi_msg_resp_describe_levels { + __le32 flags; +#define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f)))) +#define NUM_RETURNED_LEVELS(f) ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f)))) +#define SUPPORTS_SEGMENTED_LEVELS(f) ((f) & BIT(12)) + __le32 voltage[]; +}; + +struct scmi_msg_cmd_config_set { + __le32 domain_id; + __le32 config; +}; + +struct scmi_msg_cmd_level_set { + __le32 domain_id; + __le32 flags; + __le32 voltage_level; +}; + +struct voltage_info { + unsigned int version; + unsigned int num_domains; + struct scmi_voltage_info *domains; +}; + +static int scmi_protocol_attributes_get(const struct scmi_handle *handle, + struct voltage_info *vinfo) +{ + int ret; + struct scmi_xfer *t; + + ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES, + SCMI_PROTOCOL_VOLTAGE, 0, sizeof(__le32), &t); + if (ret) + return ret; + + ret = scmi_do_xfer(handle, t); + if (!ret) + vinfo->num_domains = + NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf)); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_init_voltage_levels(struct device *dev, + struct scmi_voltage_info *v, + u32 num_returned, u32 num_remaining, + bool segmented) +{ + u32 num_levels; + + num_levels = num_returned + num_remaining; + /* + * segmented levels entries are represented by a single triplet + * returned all in one go. + */ + if (!num_levels || + (segmented && (num_remaining || num_returned != 3))) { + dev_err(dev, + "Invalid level descriptor(%d/%d/%d) for voltage dom %d\n", + num_levels, num_returned, num_remaining, v->id); + return -EINVAL; + } + + v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL); + if (!v->levels_uv) + return -ENOMEM; + + v->num_levels = num_levels; + v->segmented = segmented; + + return 0; +} + +static int scmi_voltage_descriptors_get(const struct scmi_handle *handle, + struct voltage_info *vinfo) +{ + int ret, dom; + struct scmi_xfer *td, *tl; + struct device *dev = handle->dev; + struct scmi_msg_resp_domain_attributes *resp_dom; + struct scmi_msg_resp_describe_levels *resp_levels; + + ret = scmi_xfer_get_init(handle, VOLTAGE_DOMAIN_ATTRIBUTES, + SCMI_PROTOCOL_VOLTAGE, sizeof(__le32), + sizeof(*resp_dom), &td); + if (ret) + return ret; + resp_dom = td->rx.buf; + + ret = scmi_xfer_get_init(handle, VOLTAGE_DESCRIBE_LEVELS, + SCMI_PROTOCOL_VOLTAGE, sizeof(__le64), 0, &tl); + if (ret) + goto outd; + resp_levels = tl->rx.buf; + + for (dom = 0; dom < vinfo->num_domains; dom++) { + u32 desc_index = 0; + u16 num_returned = 0, num_remaining = 0; + struct scmi_msg_cmd_describe_levels *cmd; + struct scmi_voltage_info *v; + + /* Retrieve domain attributes at first ... */ + put_unaligned_le32(dom, td->tx.buf); + ret = scmi_do_xfer(handle, td); + /* Skip domain on comms error */ + if (ret) + continue; + + v = vinfo->domains + dom; + v->id = dom; + v->attributes = le32_to_cpu(resp_dom->attr); + strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE); + + cmd = tl->tx.buf; + /* ...then retrieve domain levels descriptions */ + do { + u32 flags; + int cnt; + + cmd->domain_id = cpu_to_le32(v->id); + cmd->level_index = desc_index; + ret = scmi_do_xfer(handle, tl); + if (ret) + break; + + flags = le32_to_cpu(resp_levels->flags); + num_returned = NUM_RETURNED_LEVELS(flags); + num_remaining = NUM_REMAINING_LEVELS(flags); + + /* Allocate space for num_levels if not already done */ + if (!v->num_levels) { + ret = scmi_init_voltage_levels(dev, v, + num_returned, + num_remaining, + SUPPORTS_SEGMENTED_LEVELS(flags)); + if (ret) + break; + } + + if (desc_index + num_returned > v->num_levels) { + dev_err(handle->dev, + "No. of voltage levels can't exceed %d\n", + v->num_levels); + ret = -EINVAL; + break; + } + + for (cnt = 0; cnt < num_returned; cnt++) { + s32 val; + + val = + (s32)le32_to_cpu(resp_levels->voltage[cnt]); + v->levels_uv[desc_index + cnt] = val; + if (val < 0) + v->negative_volts_allowed = true; + } + + desc_index += num_returned; + + scmi_reset_rx_to_maxsz(handle, tl); + /* check both to avoid infinite loop due to buggy fw */ + } while (num_returned && num_remaining); + + if (ret) { + v->num_levels = 0; + devm_kfree(dev, v->levels_uv); + } + + scmi_reset_rx_to_maxsz(handle, td); + } + + scmi_xfer_put(handle, tl); +outd: + scmi_xfer_put(handle, td); + + return ret; +} + +static int __scmi_voltage_get_u32(const struct scmi_handle *handle, + u8 cmd_id, u32 domain_id, u32 *value) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, cmd_id, + SCMI_PROTOCOL_VOLTAGE, + sizeof(__le32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(domain_id, t->tx.buf); + ret = scmi_do_xfer(handle, t); + if (!ret) + *value = get_unaligned_le32(t->rx.buf); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_config_set(const struct scmi_handle *handle, + u32 domain_id, u32 config) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + struct scmi_msg_cmd_config_set *cmd; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, VOLTAGE_CONFIG_SET, + SCMI_PROTOCOL_VOLTAGE, + sizeof(*cmd), 0, &t); + if (ret) + return ret; + + cmd = t->tx.buf; + cmd->domain_id = cpu_to_le32(domain_id); + cmd->config = cpu_to_le32(config & GENMASK(3, 0)); + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_config_get(const struct scmi_handle *handle, + u32 domain_id, u32 *config) +{ + return __scmi_voltage_get_u32(handle, VOLTAGE_CONFIG_GET, + domain_id, config); +} + +static int scmi_voltage_level_set(const struct scmi_handle *handle, + u32 domain_id, u32 flags, s32 volt_uV) +{ + int ret; + struct scmi_xfer *t; + struct voltage_info *vinfo = handle->voltage_priv; + struct scmi_msg_cmd_level_set *cmd; + + if (domain_id >= vinfo->num_domains) + return -EINVAL; + + ret = scmi_xfer_get_init(handle, VOLTAGE_LEVEL_SET, + SCMI_PROTOCOL_VOLTAGE, + sizeof(*cmd), 0, &t); + if (ret) + return ret; + + cmd = t->tx.buf; + cmd->domain_id = cpu_to_le32(domain_id); + cmd->flags = cpu_to_le32(flags); + cmd->voltage_level = cpu_to_le32(volt_uV); + + ret = scmi_do_xfer(handle, t); + + scmi_xfer_put(handle, t); + return ret; +} + +static int scmi_voltage_level_get(const struct scmi_handle *handle, + u32 domain_id, s32 *volt_uV) +{ + return __scmi_voltage_get_u32(handle, VOLTAGE_LEVEL_GET, + domain_id, (u32 *)volt_uV); +} + +static const struct scmi_voltage_info * __must_check +scmi_voltage_info_get(const struct scmi_handle *handle, u32 domain_id) +{ + struct voltage_info *vinfo = handle->voltage_priv; + + if (domain_id >= vinfo->num_domains || + !vinfo->domains[domain_id].num_levels) + return NULL; + + return vinfo->domains + domain_id; +} + +static int scmi_voltage_domains_num_get(const struct scmi_handle *handle) +{ + struct voltage_info *vinfo = handle->voltage_priv; + + return vinfo->num_domains; +} + +static struct scmi_voltage_ops voltage_ops = { + .num_domains_get = scmi_voltage_domains_num_get, + .info_get = scmi_voltage_info_get, + .config_set = scmi_voltage_config_set, + .config_get = scmi_voltage_config_get, + .level_set = scmi_voltage_level_set, + .level_get = scmi_voltage_level_get, +}; + +static int scmi_voltage_protocol_init(struct scmi_handle *handle) +{ + int ret; + u32 version; + struct voltage_info *vinfo; + + ret = scmi_version_get(handle, SCMI_PROTOCOL_VOLTAGE, &version); + if (ret) + return ret; + + dev_dbg(handle->dev, "Voltage Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + vinfo = devm_kzalloc(handle->dev, sizeof(*vinfo), GFP_KERNEL); + if (!vinfo) + return -ENOMEM; + vinfo->version = version; + + ret = scmi_protocol_attributes_get(handle, vinfo); + if (ret) + return ret; + + if (vinfo->num_domains) { + vinfo->domains = devm_kcalloc(handle->dev, vinfo->num_domains, + sizeof(*vinfo->domains), + GFP_KERNEL); + if (!vinfo->domains) + return -ENOMEM; + ret = scmi_voltage_descriptors_get(handle, vinfo); + if (ret) + return ret; + } else { + dev_warn(handle->dev, "No Voltage domains found.\n"); + } + + handle->voltage_ops = &voltage_ops; + handle->voltage_priv = vinfo; + + return 0; +} + +DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_VOLTAGE, voltage) diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 9cd312a1ff92..b11ca01e2393 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -209,6 +209,64 @@ struct scmi_reset_ops { int (*deassert)(const struct scmi_handle *handle, u32 domain); }; +/** + * struct scmi_voltage_info - describe one available SCMI Voltage Domain + * + * @id: the domain ID as advertised by the platform + * @segmented: defines the layout of the entries of array @levels_uv. + * - when True the entries are to be interpreted as triplets, + * each defining a segment representing a range of equally + * space voltages: , , + * - when False the entries simply represent a single discrete + * supported voltage level + * @negative_volts_allowed: True if any of the entries of @levels_uv represent + * a negative voltage. + * @attributes: represents Voltage Domain advertised attributes + * @name: name assigned to the Voltage Domain by platform + * @num_levels: number of total entries in @levels_uv. + * @levels_uv: array of entries describing the available voltage levels for + * this domain. + */ +struct scmi_voltage_info { + unsigned int id; + bool segmented; + bool negative_volts_allowed; + unsigned int attributes; + char name[SCMI_MAX_STR_SIZE]; + unsigned int num_levels; +#define SCMI_VOLTAGE_SEGMENT_LOW 0 +#define SCMI_VOLTAGE_SEGMENT_HIGH 1 +#define SCMI_VOLTAGE_SEGMENT_STEP 2 + int *levels_uv; +}; + +/** + * struct scmi_voltage_ops - represents the various operations provided + * by SCMI Voltage Protocol + * + * @num_domains_get: get the count of voltage domains provided by SCMI + * @info_get: get the information of the specified domain + * @config_set: set the config for the specified domain + * @config_get: get the config of the specified domain + * @level_set: set the voltage level for the specified domain + * @level_get: get the voltage level of the specified domain + */ +struct scmi_voltage_ops { + int (*num_domains_get)(const struct scmi_handle *handle); + const struct scmi_voltage_info __must_check *(*info_get) + (const struct scmi_handle *handle, u32 domain_id); + int (*config_set)(const struct scmi_handle *handle, u32 domain_id, + u32 config); +#define SCMI_VOLTAGE_ARCH_STATE_OFF 0x0 +#define SCMI_VOLTAGE_ARCH_STATE_ON 0x7 + int (*config_get)(const struct scmi_handle *handle, u32 domain_id, + u32 *config); + int (*level_set)(const struct scmi_handle *handle, u32 domain_id, + u32 flags, s32 volt_uV); + int (*level_get)(const struct scmi_handle *handle, u32 domain_id, + s32 *volt_uV); +}; + /** * struct scmi_notify_ops - represents notifications' operations provided by * SCMI core @@ -262,6 +320,7 @@ struct scmi_notify_ops { * @clk_ops: pointer to set of clock protocol operations * @sensor_ops: pointer to set of sensor protocol operations * @reset_ops: pointer to set of reset protocol operations + * @voltage_ops: pointer to set of voltage protocol operations * @notify_ops: pointer to set of notifications related operations * @perf_priv: pointer to private data structure specific to performance * protocol(for internal use only) @@ -273,6 +332,8 @@ struct scmi_notify_ops { * protocol(for internal use only) * @reset_priv: pointer to private data structure specific to reset * protocol(for internal use only) + * @voltage_priv: pointer to private data structure specific to voltage + * protocol(for internal use only) * @notify_priv: pointer to private data structure specific to notifications * (for internal use only) */ @@ -284,6 +345,7 @@ struct scmi_handle { const struct scmi_power_ops *power_ops; const struct scmi_sensor_ops *sensor_ops; const struct scmi_reset_ops *reset_ops; + const struct scmi_voltage_ops *voltage_ops; const struct scmi_notify_ops *notify_ops; /* for protocol internal use */ void *perf_priv; @@ -291,6 +353,7 @@ struct scmi_handle { void *power_priv; void *sensor_priv; void *reset_priv; + void *voltage_priv; void *notify_priv; void *system_priv; }; @@ -303,6 +366,7 @@ enum scmi_std_protocol { SCMI_PROTOCOL_CLOCK = 0x14, SCMI_PROTOCOL_SENSOR = 0x15, SCMI_PROTOCOL_RESET = 0x16, + SCMI_PROTOCOL_VOLTAGE = 0x17, }; enum scmi_system_events { -- cgit v1.2.3 From ec88381936954a146f260a21bf8466ca07e5c71e Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 19 Nov 2020 19:10:48 +0000 Subject: firmware: arm_scmi: Add support to enumerated SCMI voltage domain device Add SCMI voltage domain device name to the core list of supported protocol devices so that it can be enumerated if the firmware supports it. Link: https://lore.kernel.org/r/20201119191051.46363-3-cristian.marussi@arm.com Signed-off-by: Cristian Marussi Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index ada35e63feae..5392e1fc6b4e 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -743,6 +743,7 @@ static struct scmi_prot_devnames devnames[] = { { SCMI_PROTOCOL_CLOCK, { "clocks" },}, { SCMI_PROTOCOL_SENSOR, { "hwmon" },}, { SCMI_PROTOCOL_RESET, { "reset" },}, + { SCMI_PROTOCOL_VOLTAGE, { "regulator" },}, }; static inline void -- cgit v1.2.3 From e7095c35abfc5a5d566941a87434c0fd5ffb570f Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 19 Nov 2020 19:10:49 +0000 Subject: regulator: core: add of_match_full_name boolean flag During regulators registration, if .of_match and .regulators_node are defined as non-null strings in struct regulator_desc the core searches the DT subtree rooted at .regulators_node trying to match, at first, .of_match against the 'regulator-compatible' property and, then, falling back to use the name of the node itself to determine a good match. Property 'regulator-compatible', though, is now deprecated and falling back to match against the node name, works fine only as long as the involved nodes are named in an unique way across the searched subtree; if that's not the case, like when using @ style naming for properties indexed via 'reg' property (as advised by the standard), the above matching mechanism based on the simple common name will lead to multiple matches and the only viable alternative would be to properly define the now deprecated 'regulator-compatible' as the node full name, i.e. @. In order to address this case without using such deprecated binding, define a new boolean flag .of_match_full_name in struct regulator_desc to force the core to match against the node full-name instead of the plain name. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20201119191051.46363-4-cristian.marussi@arm.com Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 8 ++++++-- include/linux/regulator/driver.h | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 06c0b15fe4c0..564f928eb1db 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -413,8 +413,12 @@ device_node *regulator_of_get_init_node(struct device *dev, for_each_available_child_of_node(search, child) { name = of_get_property(child, "regulator-compatible", NULL); - if (!name) - name = child->name; + if (!name) { + if (!desc->of_match_full_name) + name = child->name; + else + name = child->full_name; + } if (!strcmp(desc->of_match, name)) { of_node_put(search); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 11cade73726c..d7c77ee370f3 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -223,6 +223,8 @@ enum regulator_type { * @name: Identifying name for the regulator. * @supply_name: Identifying the regulator supply * @of_match: Name used to identify regulator in DT. + * @of_match_full_name: A flag to indicate that the of_match string, if + * present, should be matched against the node full_name. * @regulators_node: Name of node containing regulator definitions in DT. * @of_parse_cb: Optional callback called only if of_match is present. * Will be called for each regulator parsed from DT, during @@ -314,6 +316,7 @@ struct regulator_desc { const char *name; const char *supply_name; const char *of_match; + bool of_match_full_name; const char *regulators_node; int (*of_parse_cb)(struct device_node *, const struct regulator_desc *, -- cgit v1.2.3 From b52b417ccac4fae5b1f2ec4f1d46eb91e4493dc5 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 20 Nov 2020 12:33:08 -0600 Subject: regulator: as3722: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a fallthrough pseudo-keyword instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/c0efb81064f71837f19408f65b52d155103ee514.1605896059.git.gustavoars@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/as3722-regulator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/as3722-regulator.c b/drivers/regulator/as3722-regulator.c index 33ca197860b3..7bebf9ce6271 100644 --- a/drivers/regulator/as3722-regulator.c +++ b/drivers/regulator/as3722-regulator.c @@ -455,7 +455,8 @@ static int as3722_sd_set_mode(struct regulator_dev *rdev, switch (mode) { case REGULATOR_MODE_FAST: val = as3722_reg_lookup[id].mode_mask; - case REGULATOR_MODE_NORMAL: /* fall down */ + fallthrough; + case REGULATOR_MODE_NORMAL: break; default: return -EINVAL; -- cgit v1.2.3 From 0fbeae70ee7ce98e18a47337cd1f205dd88589e9 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 23 Nov 2020 20:23:36 +0000 Subject: regulator: add SCMI driver Add a simple regulator based on SCMI Voltage Domain Protocol. Signed-off-by: Cristian Marussi ---- v6 --> v7 - add proper blank lines between semantic blocks - fix return value on error path of scmi_reg_is_enabled() - use generic Failure message on err path of info_get() - fix comment containing apostrophe v3 --> v4 - using of_match_full_name core regulator flag - avoid coccinelle falde complaints about pointer-sized allocations v2 --> v3 - remove multiple linear mappings support - removed duplicated voltage name printout - added a few comments - simplified return path in scmi_reg_set_voltage_sel() v1 --> v2 - removed duplicate regulator naming - removed redundant .get/set_voltage ops: only _sel variants implemented - removed condexpr on fail path to increase readability v0 --> v1 - fixed init_data constraint parsing - fixes for v5.8 (linear_range.h) - fixed commit message content and subject line format - factored out SCMI core specific changes to distinct patch - reworked Kconfig and Makefile to keep proper alphabetic order - fixed SPDX comment style - removed unneeded inline functions - reworked conditionals for legibility - fixed some return paths to properly report SCMI original errors codes - added some more descriptive error messages when fw returns invalid ranges - removed unneeded explicit devm_regulator_unregister from .remove() Link: https://lore.kernel.org/r/20201123202336.46701-4-cristian.marussi@arm.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/scmi-regulator.c | 417 +++++++++++++++++++++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 drivers/regulator/scmi-regulator.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 005a6036dd38..4c2073a20899 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -155,6 +155,15 @@ config REGULATOR_ARIZONA_MICSUPP and Wolfson Microelectronic Arizona codecs devices. +config REGULATOR_ARM_SCMI + tristate "SCMI based regulator driver" + depends on ARM_SCMI_PROTOCOL && OF + help + This adds the regulator driver support for ARM platforms using SCMI + protocol for device voltage management. + This driver uses SCMI Message Protocol driver to interact with the + firmware providing the device Voltage functionality. + config REGULATOR_AS3711 tristate "AS3711 PMIC" depends on MFD_AS3711 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 6096862a1d60..4e5a06f82de6 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o +obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c new file mode 100644 index 000000000000..0e8b3caa8146 --- /dev/null +++ b/drivers/regulator/scmi-regulator.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// System Control and Management Interface (SCMI) based regulator driver +// +// Copyright (C) 2020 ARM Ltd. +// +// Implements a regulator driver on top of the SCMI Voltage Protocol. +// +// The ARM SCMI Protocol aims in general to hide as much as possible all the +// underlying operational details while providing an abstracted interface for +// its users to operate upon: as a consequence the resulting operational +// capabilities and configurability of this regulator device are much more +// limited than the ones usually available on a standard physical regulator. +// +// The supported SCMI regulator ops are restricted to the bare minimum: +// +// - 'status_ops': enable/disable/is_enabled +// - 'voltage_ops': get_voltage_sel/set_voltage_sel +// list_voltage/map_voltage +// +// Each SCMI regulator instance is associated, through the means of a proper DT +// entry description, to a specific SCMI Voltage Domain. + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct scmi_regulator { + u32 id; + struct scmi_device *sdev; + struct regulator_dev *rdev; + struct device_node *of_node; + struct regulator_desc desc; + struct regulator_config conf; +}; + +struct scmi_regulator_info { + int num_doms; + struct scmi_regulator **sregv; +}; + +static int scmi_reg_enable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_ON); +} + +static int scmi_reg_disable(struct regulator_dev *rdev) +{ + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + return handle->voltage_ops->config_set(handle, sreg->id, + SCMI_VOLTAGE_ARCH_STATE_OFF); +} + +static int scmi_reg_is_enabled(struct regulator_dev *rdev) +{ + int ret; + u32 config; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->config_get(handle, sreg->id, + &config); + if (ret) { + dev_err(&sreg->sdev->dev, + "Error %d reading regulator %s status.\n", + ret, sreg->desc.name); + return ret; + } + + return config & SCMI_VOLTAGE_ARCH_STATE_ON; +} + +static int scmi_reg_get_voltage_sel(struct regulator_dev *rdev) +{ + int ret; + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + ret = handle->voltage_ops->level_get(handle, sreg->id, &volt_uV); + if (ret) + return ret; + + return sreg->desc.ops->map_voltage(rdev, volt_uV, volt_uV); +} + +static int scmi_reg_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + s32 volt_uV; + struct scmi_regulator *sreg = rdev_get_drvdata(rdev); + const struct scmi_handle *handle = sreg->sdev->handle; + + volt_uV = sreg->desc.ops->list_voltage(rdev, selector); + if (volt_uV <= 0) + return -EINVAL; + + return handle->voltage_ops->level_set(handle, sreg->id, 0x0, volt_uV); +} + +static const struct regulator_ops scmi_reg_fixed_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, +}; + +static const struct regulator_ops scmi_reg_linear_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, +}; + +static const struct regulator_ops scmi_reg_discrete_ops = { + .enable = scmi_reg_enable, + .disable = scmi_reg_disable, + .is_enabled = scmi_reg_is_enabled, + .get_voltage_sel = scmi_reg_get_voltage_sel, + .set_voltage_sel = scmi_reg_set_voltage_sel, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, +}; + +static int +scmi_config_linear_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + s32 delta_uV; + + /* + * Note that SCMI voltage domains describable by linear ranges + * (segments) {low, high, step} are guaranteed to come in one single + * triplet by the SCMI Voltage Domain protocol support itself. + */ + + delta_uV = (vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH] - + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]); + + /* Rule out buggy negative-intervals answers from fw */ + if (delta_uV < 0) { + dev_err(&sreg->sdev->dev, + "Invalid volt-range %d-%duV for domain %d\n", + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW], + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_HIGH], + sreg->id); + return -EINVAL; + } + + if (!delta_uV) { + /* Just one fixed voltage exposed by SCMI */ + sreg->desc.fixed_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.n_voltages = 1; + sreg->desc.ops = &scmi_reg_fixed_ops; + } else { + /* One simple linear mapping. */ + sreg->desc.min_uV = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_LOW]; + sreg->desc.uV_step = + vinfo->levels_uv[SCMI_VOLTAGE_SEGMENT_STEP]; + sreg->desc.linear_min_sel = 0; + sreg->desc.n_voltages = delta_uV / sreg->desc.uV_step; + sreg->desc.ops = &scmi_reg_linear_ops; + } + + return 0; +} + +static int +scmi_config_discrete_regulator_mappings(struct scmi_regulator *sreg, + const struct scmi_voltage_info *vinfo) +{ + /* Discrete non linear levels are mapped to volt_table */ + sreg->desc.n_voltages = vinfo->num_levels; + + if (sreg->desc.n_voltages > 1) { + sreg->desc.volt_table = (const unsigned int *)vinfo->levels_uv; + sreg->desc.ops = &scmi_reg_discrete_ops; + } else { + sreg->desc.fixed_uV = vinfo->levels_uv[0]; + sreg->desc.ops = &scmi_reg_fixed_ops; + } + + return 0; +} + +static int scmi_regulator_common_init(struct scmi_regulator *sreg) +{ + int ret; + const struct scmi_handle *handle = sreg->sdev->handle; + struct device *dev = &sreg->sdev->dev; + const struct scmi_voltage_info *vinfo; + + vinfo = handle->voltage_ops->info_get(handle, sreg->id); + if (!vinfo) { + dev_warn(dev, "Failure to get voltage domain %d\n", + sreg->id); + return -ENODEV; + } + + /* + * Regulator framework does not fully support negative voltages + * so we discard any voltage domain reported as supporting negative + * voltages: as a consequence each levels_uv entry is guaranteed to + * be non-negative from here on. + */ + if (vinfo->negative_volts_allowed) { + dev_warn(dev, "Negative voltages NOT supported...skip %s\n", + sreg->of_node->full_name); + return -EOPNOTSUPP; + } + + sreg->desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s", vinfo->name); + if (!sreg->desc.name) + return -ENOMEM; + + sreg->desc.id = sreg->id; + sreg->desc.type = REGULATOR_VOLTAGE; + sreg->desc.owner = THIS_MODULE; + sreg->desc.of_match_full_name = true; + sreg->desc.of_match = sreg->of_node->full_name; + sreg->desc.regulators_node = "regulators"; + if (vinfo->segmented) + ret = scmi_config_linear_regulator_mappings(sreg, vinfo); + else + ret = scmi_config_discrete_regulator_mappings(sreg, vinfo); + if (ret) + return ret; + + /* + * Using the scmi device here to have DT searched from Voltage + * protocol node down. + */ + sreg->conf.dev = dev; + + /* Store for later retrieval via rdev_get_drvdata() */ + sreg->conf.driver_data = sreg; + + return 0; +} + +static int process_scmi_regulator_of_node(struct scmi_device *sdev, + struct device_node *np, + struct scmi_regulator_info *rinfo) +{ + u32 dom, ret; + + ret = of_property_read_u32(np, "reg", &dom); + if (ret) + return ret; + + if (dom >= rinfo->num_doms) + return -ENODEV; + + if (rinfo->sregv[dom]) { + dev_err(&sdev->dev, + "SCMI Voltage Domain %d already in use. Skipping: %s\n", + dom, np->full_name); + return -EINVAL; + } + + rinfo->sregv[dom] = devm_kzalloc(&sdev->dev, + sizeof(struct scmi_regulator), + GFP_KERNEL); + if (!rinfo->sregv[dom]) + return -ENOMEM; + + rinfo->sregv[dom]->id = dom; + rinfo->sregv[dom]->sdev = sdev; + + /* get hold of good nodes */ + of_node_get(np); + rinfo->sregv[dom]->of_node = np; + + dev_dbg(&sdev->dev, + "Found SCMI Regulator entry -- OF node [%d] -> %s\n", + dom, np->full_name); + + return 0; +} + +static int scmi_regulator_probe(struct scmi_device *sdev) +{ + int d, ret, num_doms; + struct device_node *np, *child; + const struct scmi_handle *handle = sdev->handle; + struct scmi_regulator_info *rinfo; + + if (!handle || !handle->voltage_ops) + return -ENODEV; + + num_doms = handle->voltage_ops->num_domains_get(handle); + if (num_doms <= 0) { + if (!num_doms) { + dev_err(&sdev->dev, + "number of voltage domains invalid\n"); + num_doms = -EINVAL; + } else { + dev_err(&sdev->dev, + "failed to get voltage domains - err:%d\n", + num_doms); + } + + return num_doms; + } + + rinfo = devm_kzalloc(&sdev->dev, sizeof(*rinfo), GFP_KERNEL); + if (!rinfo) + return -ENOMEM; + + /* Allocate pointers array for all possible domains */ + rinfo->sregv = devm_kcalloc(&sdev->dev, num_doms, + sizeof(void *), GFP_KERNEL); + if (!rinfo->sregv) + return -ENOMEM; + + rinfo->num_doms = num_doms; + + /* + * Start collecting into rinfo->sregv possibly good SCMI Regulators as + * described by a well-formed DT entry and associated with an existing + * plausible SCMI Voltage Domain number, all belonging to this SCMI + * platform instance node (handle->dev->of_node). + */ + np = of_find_node_by_name(handle->dev->of_node, "regulators"); + for_each_child_of_node(np, child) { + ret = process_scmi_regulator_of_node(sdev, child, rinfo); + /* abort on any mem issue */ + if (ret == -ENOMEM) + return ret; + } + + /* + * Register a regulator for each valid regulator-DT-entry that we + * can successfully reach via SCMI and has a valid associated voltage + * domain. + */ + for (d = 0; d < num_doms; d++) { + struct scmi_regulator *sreg = rinfo->sregv[d]; + + /* Skip empty slots */ + if (!sreg) + continue; + + ret = scmi_regulator_common_init(sreg); + /* Skip invalid voltage domains */ + if (ret) + continue; + + sreg->rdev = devm_regulator_register(&sdev->dev, &sreg->desc, + &sreg->conf); + if (IS_ERR(sreg->rdev)) { + sreg->rdev = NULL; + continue; + } + + dev_info(&sdev->dev, + "Regulator %s registered for domain [%d]\n", + sreg->desc.name, sreg->id); + } + + dev_set_drvdata(&sdev->dev, rinfo); + + return 0; +} + +static void scmi_regulator_remove(struct scmi_device *sdev) +{ + int d; + struct scmi_regulator_info *rinfo; + + rinfo = dev_get_drvdata(&sdev->dev); + if (!rinfo) + return; + + for (d = 0; d < rinfo->num_doms; d++) { + if (!rinfo->sregv[d]) + continue; + of_node_put(rinfo->sregv[d]->of_node); + } +} + +static const struct scmi_device_id scmi_regulator_id_table[] = { + { SCMI_PROTOCOL_VOLTAGE, "regulator" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_regulator_id_table); + +static struct scmi_driver scmi_drv = { + .name = "scmi-regulator", + .probe = scmi_regulator_probe, + .remove = scmi_regulator_remove, + .id_table = scmi_regulator_id_table, +}; + +module_scmi_driver(scmi_drv); + +MODULE_AUTHOR("Cristian Marussi "); +MODULE_DESCRIPTION("ARM SCMI regulator driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 55cca73931c3a08eb74f5ad06e88304af7a292e0 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 25 Nov 2020 19:25:47 +0200 Subject: regulator: core: return zero for selectors lower than linear_min_sel Selectors lower than linear_min_sel should not be considered invalid. Thus return zero in case _regulator_list_voltage(), regulator_list_hardware_vsel() or regulator_list_voltage_table() receives such selectors as argument. Fixes: bdcd1177578c ("regulator: core: validate selector against linear_min_sel") Reported-by: Jon Hunter Signed-off-by: Claudiu Beznea Link: https://lore.kernel.org/r/1606325147-606-1-git-send-email-claudiu.beznea@microchip.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 10 ++++++---- drivers/regulator/helpers.c | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 85bf278c9378..2d5d5f3bac62 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2958,9 +2958,10 @@ static int _regulator_list_voltage(struct regulator_dev *rdev, return rdev->desc->fixed_uV; if (ops->list_voltage) { - if (selector >= rdev->desc->n_voltages || - selector < rdev->desc->linear_min_sel) + if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; if (lock) regulator_lock(rdev); ret = ops->list_voltage(rdev, selector); @@ -3110,9 +3111,10 @@ int regulator_list_hardware_vsel(struct regulator *regulator, struct regulator_dev *rdev = regulator->rdev; const struct regulator_ops *ops = rdev->desc->ops; - if (selector >= rdev->desc->n_voltages || - selector < rdev->desc->linear_min_sel) + if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; if (ops->set_voltage_sel != regulator_set_voltage_sel_regmap) return -EOPNOTSUPP; diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index 974f1a63993d..f42b394a0c46 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -647,9 +647,10 @@ int regulator_list_voltage_table(struct regulator_dev *rdev, return -EINVAL; } - if (selector >= rdev->desc->n_voltages || - selector < rdev->desc->linear_min_sel) + if (selector >= rdev->desc->n_voltages) return -EINVAL; + if (selector < rdev->desc->linear_min_sel) + return 0; return rdev->desc->volt_table[selector]; } -- cgit v1.2.3 From 36dd70ceb4d955e6cd3ecd18e78169141aaa23b7 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 26 Nov 2020 15:00:18 +0530 Subject: regulator: qcom-rpmh: Add support for SDX55 Add support from RPMH regulators found in SDX55 platform Signed-off-by: Vinod Koul Link: https://lore.kernel.org/r/20201126093018.1085594-2-vkoul@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index d488325499a9..e673d48b31a1 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -930,6 +930,33 @@ static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pmx55_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l16"), + {}, +}; + static int rpmh_regulator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1000,6 +1027,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm6150l-rpmh-regulators", .data = pm6150l_vreg_data, }, + { + .compatible = "qcom,pmx55-rpmh-regulators", + .data = pmx55_vreg_data, + }, {} }; MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table); -- cgit v1.2.3 From d3795d6321ecaa55d94dc24c3b1e3cce608aabd6 Mon Sep 17 00:00:00 2001 From: Jagan Teki Date: Mon, 30 Nov 2020 16:53:29 +0530 Subject: regulator: Add NXP PF8X00 regulator driver Add NXP PF8100/PF8121A/PF8200 regulator driver. PF8100/PF8121A/PF8200 is PMIC designed for highperformance consumer applications. It features seven high efficiency buck, four linear and one vsnvs regulators. Tested in Engicam i.Core MX8M Mini SOM platform boards. Signed-off-by: Troy Kisky Signed-off-by: Jagan Teki Link: https://lore.kernel.org/r/20201130112329.104614-2-jagan@amarulasolutions.com Signed-off-by: Mark Brown --- MAINTAINERS | 6 + drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/pf8x00-regulator.c | 496 +++++++++++++++++++++++++++++++++++ 4 files changed, 511 insertions(+) create mode 100644 drivers/regulator/pf8x00-regulator.c (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 9bff94560b42..131860ff307f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12559,6 +12559,12 @@ S: Maintained F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml F: drivers/gpu/drm/imx/dcss/ +NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER +M: Jagan Teki +S: Maintained +F: Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml +F: drivers/regulator/pf8x00-regulator.c + NXP PTN5150A CC LOGIC AND EXTCON DRIVER M: Krzysztof Kozlowski L: linux-kernel@vger.kernel.org diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 4c2073a20899..a497a3729091 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -812,6 +812,14 @@ config REGULATOR_PCF50633 Say Y here to support the voltage regulators and converters on PCF50633 +config REGULATOR_PF8X00 + tristate "NXP PF8100/PF8121A/PF8200 regulator driver" + depends on I2C && OF + select REGMAP_I2C + help + Say y here to support the regulators found on the NXP + PF8100/PF8121A/PF8200 PMIC. + config REGULATOR_PFUZE100 tristate "Freescale PFUZE100/200/3000/3001 regulator driver" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 4e5a06f82de6..680e539f6579 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o obj-$(CONFIG_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PCA9450) += pca9450-regulator.o +obj-$(CONFIG_REGULATOR_PF8X00) += pf8x00-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o diff --git a/drivers/regulator/pf8x00-regulator.c b/drivers/regulator/pf8x00-regulator.c new file mode 100644 index 000000000000..308c27fa6ea8 --- /dev/null +++ b/drivers/regulator/pf8x00-regulator.c @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 NXP + * Copyright (C) 2019 Boundary Devices + * Copyright (C) 2020 Amarula Solutions(India) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers */ +#define PF8X00_DEVICEID 0x00 +#define PF8X00_REVID 0x01 +#define PF8X00_EMREV 0x02 +#define PF8X00_PROGID 0x03 +#define PF8X00_IMS_INT 0x04 +#define PF8X00_IMS_THERM 0x07 +#define PF8X00_SW_MODE_INT 0x0a +#define PF8X00_SW_MODE_MASK 0x0b +#define PF8X00_IMS_SW_ILIM 0x12 +#define PF8X00_IMS_LDO_ILIM 0x15 +#define PF8X00_IMS_SW_UV 0x18 +#define PF8X00_IMS_SW_OV 0x1b +#define PF8X00_IMS_LDO_UV 0x1e +#define PF8X00_IMS_LDO_OV 0x21 +#define PF8X00_IMS_PWRON 0x24 +#define PF8X00_SYS_INT 0x27 +#define PF8X00_HARD_FAULT 0x29 +#define PF8X00_FSOB_FLAGS 0x2a +#define PF8X00_FSOB_SELECT 0x2b +#define PF8X00_ABIST_OV1 0x2c +#define PF8X00_ABIST_OV2 0x2d +#define PF8X00_ABIST_UV1 0x2e +#define PF8X00_ABIST_UV2 0x2f +#define PF8X00_TEST_FLAGS 0x30 +#define PF8X00_ABIST_RUN 0x31 +#define PF8X00_RANDOM_GEN 0x33 +#define PF8X00_RANDOM_CHK 0x34 +#define PF8X00_VMONEN1 0x35 +#define PF8X00_VMONEN2 0x36 +#define PF8X00_CTRL1 0x37 +#define PF8X00_CTRL2 0x38 +#define PF8X00_CTRL3 0x39 +#define PF8X00_PWRUP_CTRL 0x3a +#define PF8X00_RESETBMCU 0x3c +#define PF8X00_PGOOD 0x3d +#define PF8X00_PWRDN_DLY1 0x3e +#define PF8X00_PWRDN_DLY2 0x3f +#define PF8X00_FREQ_CTRL 0x40 +#define PF8X00_COINCELL_CTRL 0x41 +#define PF8X00_PWRON 0x42 +#define PF8X00_WD_CONFIG 0x43 +#define PF8X00_WD_CLEAR 0x44 +#define PF8X00_WD_EXPIRE 0x45 +#define PF8X00_WD_COUNTER 0x46 +#define PF8X00_FAULT_COUNTER 0x47 +#define PF8X00_FSAFE_COUNTER 0x48 +#define PF8X00_FAULT_TIMER 0x49 +#define PF8X00_AMUX 0x4a +#define PF8X00_SW1_CONFIG1 0x4d +#define PF8X00_LDO1_CONFIG1 0x85 +#define PF8X00_VSNVS_CONFIG1 0x9d +#define PF8X00_PAGE_SELECT 0x9f + +/* regulators */ +enum pf8x00_regulators { + PF8X00_LDO1, + PF8X00_LDO2, + PF8X00_LDO3, + PF8X00_LDO4, + PF8X00_BUCK1, + PF8X00_BUCK2, + PF8X00_BUCK3, + PF8X00_BUCK4, + PF8X00_BUCK5, + PF8X00_BUCK6, + PF8X00_BUCK7, + PF8X00_VSNVS, + + PF8X00_MAX_REGULATORS, +}; + +enum pf8x00_buck_states { + SW_CONFIG1, + SW_CONFIG2, + SW_PWRUP, + SW_MODE1, + SW_RUN_VOLT, + SW_STBY_VOLT, +}; +#define PF8X00_SW_BASE(i) (8 * (i - PF8X00_BUCK1) + PF8X00_SW1_CONFIG1) + +enum pf8x00_ldo_states { + LDO_CONFIG1, + LDO_CONFIG2, + LDO_PWRUP, + LDO_RUN_VOLT, + LDO_STBY_VOLT, +}; +#define PF8X00_LDO_BASE(i) (6 * (i - PF8X00_LDO1) + PF8X00_LDO1_CONFIG1) + +enum swxilim_bits { + SWXILIM_2100_MA, + SWXILIM_2600_MA, + SWXILIM_3000_MA, + SWXILIM_4500_MA, +}; +#define PF8X00_SWXILIM_SHIFT 3 +#define PF8X00_SWXILIM_MASK GENMASK(4, 3) +#define PF8X00_SWXPHASE_MASK GENMASK(2, 0) +#define PF8X00_SWXPHASE_DEFAULT 0 +#define PF8X00_SWXPHASE_SHIFT 7 + +enum pf8x00_devid { + PF8100 = 0x0, + PF8121A = BIT(1), + PF8200 = BIT(3), +}; +#define PF8X00_FAM BIT(6) +#define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4) +#define PF8X00_DEVICE_ID_MASK GENMASK(3, 0) + +struct pf8x00_regulator { + struct regulator_desc desc; + u8 ilim; + u8 phase_shift; +}; + +struct pf8x00_chip { + struct regmap *regmap; + struct device *dev; +}; + +static const struct regmap_config pf8x00_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PF8X00_PAGE_SELECT, + .cache_type = REGCACHE_RBTREE, +}; + +/* VLDOx output: 1.5V to 5.0V */ +static const int pf8x00_ldo_voltages[] = { + 1500000, 1600000, 1800000, 1850000, 2150000, 2500000, 2800000, 3000000, + 3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000, +}; + +#define SWV(i) (6250 * i + 400000) +#define SWV_LINE(i) SWV(i*8+0), SWV(i*8+1), SWV(i*8+2), SWV(i*8+3), \ + SWV(i*8+4), SWV(i*8+5), SWV(i*8+6), SWV(i*8+7) + +/* Output: 0.4V to 1.8V */ +static const int pf8x00_sw1_to_6_voltages[] = { + SWV_LINE(0), + SWV_LINE(1), + SWV_LINE(2), + SWV_LINE(3), + SWV_LINE(4), + SWV_LINE(5), + SWV_LINE(6), + SWV_LINE(7), + SWV_LINE(8), + SWV_LINE(9), + SWV_LINE(10), + SWV_LINE(11), + SWV_LINE(12), + SWV_LINE(13), + SWV_LINE(14), + SWV_LINE(15), + SWV_LINE(16), + SWV_LINE(17), + SWV_LINE(18), + SWV_LINE(19), + SWV_LINE(20), + SWV_LINE(21), + 1500000, 1800000, +}; + +/* Output: 1.0V to 4.1V */ +static const int pf8x00_sw7_voltages[] = { + 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1500000, 1600000, + 1800000, 1850000, 2000000, 2100000, 2150000, 2250000, 2300000, 2400000, + 2500000, 2800000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000, + 3500000, 3800000, 4000000, 4100000, 4100000, 4100000, 4100000, 4100000, +}; + +/* Output: 1.8V, 3.0V, or 3.3V */ +static const int pf8x00_vsnvs_voltages[] = { + 0, 1800000, 3000000, 3300000, +}; + +static struct pf8x00_regulator *desc_to_regulator(const struct regulator_desc *desc) +{ + return container_of(desc, struct pf8x00_regulator, desc); +} + +static void swxilim_select(const struct regulator_desc *desc, int ilim) +{ + struct pf8x00_regulator *data = desc_to_regulator(desc); + u8 ilim_sel; + + switch (ilim) { + case 2100: + ilim_sel = SWXILIM_2100_MA; + break; + case 2600: + ilim_sel = SWXILIM_2600_MA; + break; + case 3000: + ilim_sel = SWXILIM_3000_MA; + break; + case 4500: + ilim_sel = SWXILIM_4500_MA; + break; + default: + ilim_sel = SWXILIM_2100_MA; + break; + } + + data->ilim = ilim_sel; +} + +static int pf8x00_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct pf8x00_regulator *data = desc_to_regulator(desc); + struct pf8x00_chip *chip = config->driver_data; + int phase; + int val; + int ret; + + ret = of_property_read_u32(np, "nxp,ilim-ma", &val); + if (ret) + dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use 2100 mA\n", + desc->id - PF8X00_LDO4); + + swxilim_select(desc, val); + + ret = of_property_read_u32(np, "nxp,phase-shift", &val); + if (ret) { + dev_dbg(chip->dev, + "unspecified phase-shift for BUCK%d, use 0 degrees\n", + desc->id - PF8X00_LDO4); + val = PF8X00_SWXPHASE_DEFAULT; + } + + phase = val / 45; + if ((phase * 45) != val) { + dev_warn(config->dev, + "invalid phase_shift %d for BUCK%d, use 0 degrees\n", + (phase * 45), desc->id - PF8X00_LDO4); + phase = PF8X00_SWXPHASE_SHIFT; + } + + data->phase_shift = (phase >= 1) ? phase - 1 : PF8X00_SWXPHASE_SHIFT; + + return 0; +} + +static const struct regulator_ops pf8x00_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf8x00_buck_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static const struct regulator_ops pf8x00_vsnvs_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +#define PF8X00LDO(_id, _name, base, voltages) \ + [PF8X00_LDO ## _id] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_LDO ## _id, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base) + LDO_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .enable_reg = (base) + LDO_CONFIG2, \ + .enable_val = 0x2, \ + .disable_val = 0x0, \ + .enable_mask = 2, \ + }, \ + } + +#define PF8X00BUCK(_id, _name, base, voltages) \ + [PF8X00_BUCK ## _id] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .of_parse_cb = pf8x00_of_parse_cb, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_buck_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_BUCK ## _id, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base) + SW_RUN_VOLT, \ + .vsel_mask = 0xff, \ + .enable_reg = (base) + SW_MODE1, \ + .enable_val = 0x3, \ + .disable_val = 0x0, \ + .enable_mask = 0x3, \ + .enable_time = 500, \ + }, \ + } + +#define PF8X00VSNVS(_name, base, voltages) \ + [PF8X00_VSNVS] = { \ + .desc = { \ + .name = _name, \ + .of_match = _name, \ + .regulators_node = "regulators", \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pf8x00_vsnvs_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PF8X00_VSNVS, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base), \ + .vsel_mask = 0x3, \ + }, \ + } + +static struct pf8x00_regulator pf8x00_regulators_data[PF8X00_MAX_REGULATORS] = { + PF8X00LDO(1, "ldo1", PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages), + PF8X00LDO(2, "ldo2", PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages), + PF8X00LDO(3, "ldo3", PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages), + PF8X00LDO(4, "ldo4", PF8X00_LDO_BASE(PF8X00_LDO4), pf8x00_ldo_voltages), + PF8X00BUCK(1, "buck1", PF8X00_SW_BASE(PF8X00_BUCK1), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(2, "buck2", PF8X00_SW_BASE(PF8X00_BUCK2), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(3, "buck3", PF8X00_SW_BASE(PF8X00_BUCK3), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(4, "buck4", PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(5, "buck5", PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(6, "buck6", PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages), + PF8X00BUCK(7, "buck7", PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages), + PF8X00VSNVS("vsnvs", PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages), +}; + +static int pf8x00_identify(struct pf8x00_chip *chip) +{ + unsigned int value; + u8 dev_fam, dev_id; + const char *name = NULL; + int ret; + + ret = regmap_read(chip->regmap, PF8X00_DEVICEID, &value); + if (ret) { + dev_err(chip->dev, "failed to read chip family\n"); + return ret; + } + + dev_fam = value & PF8X00_DEVICE_FAM_MASK; + switch (dev_fam) { + case PF8X00_FAM: + break; + default: + dev_err(chip->dev, + "Chip 0x%x is not from PF8X00 family\n", dev_fam); + return ret; + } + + dev_id = value & PF8X00_DEVICE_ID_MASK; + switch (dev_id) { + case PF8100: + name = "PF8100"; + break; + case PF8121A: + name = "PF8121A"; + break; + case PF8200: + name = "PF8100"; + break; + default: + dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n", dev_id); + return -ENODEV; + } + + dev_info(chip->dev, "%s PMIC found.\n", name); + + return 0; +} + +static int pf8x00_i2c_probe(struct i2c_client *client) +{ + struct regulator_config config = { NULL, }; + struct pf8x00_chip *chip; + int id; + int ret; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + i2c_set_clientdata(client, chip); + chip->dev = &client->dev; + + chip->regmap = devm_regmap_init_i2c(client, &pf8x00_regmap_config); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(&client->dev, + "regmap allocation failed with err %d\n", ret); + return ret; + } + + ret = pf8x00_identify(chip); + if (ret) + return ret; + + for (id = 0; id < ARRAY_SIZE(pf8x00_regulators_data); id++) { + struct pf8x00_regulator *data = &pf8x00_regulators_data[id]; + struct regulator_dev *rdev; + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + rdev = devm_regulator_register(&client->dev, &data->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&client->dev, + "failed to register %s regulator\n", data->desc.name); + return PTR_ERR(rdev); + } + + if ((id >= PF8X00_BUCK1) && (id <= PF8X00_BUCK7)) { + u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2; + + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXPHASE_MASK, + data->phase_shift); + + regmap_update_bits(chip->regmap, reg, + PF8X00_SWXILIM_MASK, + data->ilim << PF8X00_SWXILIM_SHIFT); + } + } + + return 0; +} + +static const struct of_device_id pf8x00_dt_ids[] = { + { .compatible = "nxp,pf8x00",}, + { } +}; +MODULE_DEVICE_TABLE(of, pf8x00_dt_ids); + +static const struct i2c_device_id pf8x00_i2c_id[] = { + { "pf8x00", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id); + +static struct i2c_driver pf8x00_regulator_driver = { + .id_table = pf8x00_i2c_id, + .driver = { + .name = "pf8x00", + .of_match_table = pf8x00_dt_ids, + }, + .probe_new = pf8x00_i2c_probe, +}; +module_i2c_driver(pf8x00_regulator_driver); + +MODULE_AUTHOR("Jagan Teki "); +MODULE_AUTHOR("Troy Kisky "); +MODULE_DESCRIPTION("Regulator Driver for NXP's PF8100/PF8121A/PF8200 PMIC"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 86f162c91f274e0d8a0c440d7a991230f6ac7725 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:06 +0000 Subject: regulator: da9121: Add header file Add header file for Dialog Semiconductor DA9121 regulator and related devices, mostly autogenerated from the chip design databases, and update driver to replace local defines with those from header. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/3527d84448d1e6ddc0fcb883ae564880f75a6cb0.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 15 +- drivers/regulator/da9121-regulator.h | 291 +++++++++++++++++++++++++++++++++++ 2 files changed, 296 insertions(+), 10 deletions(-) create mode 100644 drivers/regulator/da9121-regulator.h (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 66bdfd1979c0..c11fe046345f 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -9,12 +9,7 @@ #include #include #include - -#define DA9121_BUCK_BUCK1_0 0x20 -#define DA9121_BUCK_BUCK1_0_CH1_EN BIT(0) - -#define DA9121_BUCK_BUCK1_5 0x25 -#define DA9121_BUCK_BUCK1_5_CH1_A_VOUT GENMASK(7, 0) +#include "da9121-regulator.h" #define DA9121_MIN_MV 300 #define DA9121_MAX_MV 1900 @@ -47,10 +42,10 @@ static const struct regulator_desc da9121_reg = { .min_uV = DA9121_MIN_MV * 1000, .uV_step = DA9121_STEP_MV * 1000, .linear_min_sel = DA9121_MIN_SEL, - .vsel_reg = DA9121_BUCK_BUCK1_5, - .vsel_mask = DA9121_BUCK_BUCK1_5_CH1_A_VOUT, - .enable_reg = DA9121_BUCK_BUCK1_0, - .enable_mask = DA9121_BUCK_BUCK1_0_CH1_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, /* Default value of BUCK_BUCK1_0.CH1_SRC_DVC_UP */ .ramp_delay = 20000, /* tBUCK_EN */ diff --git a/drivers/regulator/da9121-regulator.h b/drivers/regulator/da9121-regulator.h new file mode 100644 index 000000000000..3c34cb889ca8 --- /dev/null +++ b/drivers/regulator/da9121-regulator.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * DA9121 Single-channel dual-phase 10A buck converter + * DA9130 Single-channel dual-phase 10A buck converter (Automotive) + * DA9217 Single-channel dual-phase 6A buck converter + * DA9122 Dual-channel single-phase 5A buck converter + * DA9131 Dual-channel single-phase 5A buck converter (Automotive) + * DA9220 Dual-channel single-phase 3A buck converter + * DA9132 Dual-channel single-phase 3A buck converter (Automotive) + * + * Copyright (C) 2020 Dialog Semiconductor + * + * Authors: Steve Twiss, Dialog Semiconductor + * Adam Ward, Dialog Semiconductor + */ + +#ifndef __DA9121_REGISTERS_H__ +#define __DA9121_REGISTERS_H__ + +/* Values for: DA9121_REG_BUCK_BUCKx_4 registers, fields CHx_y_MODE + * DA9121_REG_BUCK_BUCKx_7 registers, fields CHx_RIPPLE_CANCEL + */ +#include + +enum da9121_variant { + DA9121_TYPE_DA9121_DA9130, + DA9121_TYPE_DA9220_DA9132, + DA9121_TYPE_DA9122_DA9131, + DA9121_TYPE_DA9217 +}; + +/* Minimum, maximum and default polling millisecond periods are provided + * here as an example. It is expected that any final implementation will + * include a modification of these settings to match the required + * application. + */ +#define DA9121_DEFAULT_POLLING_PERIOD_MS 3000 +#define DA9121_MAX_POLLING_PERIOD_MS 10000 +#define DA9121_MIN_POLLING_PERIOD_MS 1000 + +/* Registers */ + +#define DA9121_REG_SYS_STATUS_0 0x01 +#define DA9121_REG_SYS_STATUS_1 0x02 +#define DA9121_REG_SYS_STATUS_2 0x03 +#define DA9121_REG_SYS_EVENT_0 0x04 +#define DA9121_REG_SYS_EVENT_1 0x05 +#define DA9121_REG_SYS_EVENT_2 0x06 +#define DA9121_REG_SYS_MASK_0 0x07 +#define DA9121_REG_SYS_MASK_1 0x08 +#define DA9121_REG_SYS_MASK_2 0x09 +#define DA9121_REG_SYS_MASK_3 0x0A +#define DA9121_REG_SYS_CONFIG_0 0x0B +#define DA9121_REG_SYS_CONFIG_1 0x0C +#define DA9121_REG_SYS_CONFIG_2 0x0D +#define DA9121_REG_SYS_CONFIG_3 0x0E +#define DA9121_REG_SYS_GPIO0_0 0x10 +#define DA9121_REG_SYS_GPIO0_1 0x11 +#define DA9121_REG_SYS_GPIO1_0 0x12 +#define DA9121_REG_SYS_GPIO1_1 0x13 +#define DA9121_REG_SYS_GPIO2_0 0x14 +#define DA9121_REG_SYS_GPIO2_1 0x15 +#define DA9121_REG_BUCK_BUCK1_0 0x20 +#define DA9121_REG_BUCK_BUCK1_1 0x21 +#define DA9121_REG_BUCK_BUCK1_2 0x22 +#define DA9121_REG_BUCK_BUCK1_3 0x23 +#define DA9121_REG_BUCK_BUCK1_4 0x24 +#define DA9121_REG_BUCK_BUCK1_5 0x25 +#define DA9121_REG_BUCK_BUCK1_6 0x26 +#define DA9121_REG_BUCK_BUCK1_7 0x27 +#define DA9xxx_REG_BUCK_BUCK2_0 0x28 +#define DA9xxx_REG_BUCK_BUCK2_1 0x29 +#define DA9xxx_REG_BUCK_BUCK2_2 0x2A +#define DA9xxx_REG_BUCK_BUCK2_3 0x2B +#define DA9xxx_REG_BUCK_BUCK2_4 0x2C +#define DA9xxx_REG_BUCK_BUCK2_5 0x2D +#define DA9xxx_REG_BUCK_BUCK2_6 0x2E +#define DA9xxx_REG_BUCK_BUCK2_7 0x2F +#define DA9121_REG_OTP_DEVICE_ID 0x48 +#define DA9121_REG_OTP_VARIANT_ID 0x49 +#define DA9121_REG_OTP_CUSTOMER_ID 0x4A +#define DA9121_REG_OTP_CONFIG_ID 0x4B + +/* Register bits */ + +/* DA9121_REG_SYS_STATUS_0 */ + +#define DA9xxx_MASK_SYS_STATUS_0_SG BIT(2) +#define DA9121_MASK_SYS_STATUS_0_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_STATUS_0_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_STATUS_1 */ + +#define DA9xxx_MASK_SYS_STATUS_1_PG2 BIT(7) +#define DA9xxx_MASK_SYS_STATUS_1_OV2 BIT(6) +#define DA9xxx_MASK_SYS_STATUS_1_UV2 BIT(5) +#define DA9xxx_MASK_SYS_STATUS_1_OC2 BIT(4) +#define DA9121_MASK_SYS_STATUS_1_PG1 BIT(3) +#define DA9121_MASK_SYS_STATUS_1_OV1 BIT(2) +#define DA9121_MASK_SYS_STATUS_1_UV1 BIT(1) +#define DA9121_MASK_SYS_STATUS_1_OC1 BIT(0) + +/* DA9121_REG_SYS_STATUS_2 */ + +#define DA9121_MASK_SYS_STATUS_2_GPIO2 BIT(2) +#define DA9121_MASK_SYS_STATUS_2_GPIO1 BIT(1) +#define DA9121_MASK_SYS_STATUS_2_GPIO0 BIT(0) + +/* DA9121_REG_SYS_EVENT_0 */ + +#define DA9xxx_MASK_SYS_EVENT_0_E_SG BIT(2) +#define DA9121_MASK_SYS_EVENT_0_E_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_EVENT_0_E_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_EVENT_1 */ + +#define DA9xxx_MASK_SYS_EVENT_1_E_PG2 BIT(7) +#define DA9xxx_MASK_SYS_EVENT_1_E_OV2 BIT(6) +#define DA9xxx_MASK_SYS_EVENT_1_E_UV2 BIT(5) +#define DA9xxx_MASK_SYS_EVENT_1_E_OC2 BIT(4) +#define DA9121_MASK_SYS_EVENT_1_E_PG1 BIT(3) +#define DA9121_MASK_SYS_EVENT_1_E_OV1 BIT(2) +#define DA9121_MASK_SYS_EVENT_1_E_UV1 BIT(1) +#define DA9121_MASK_SYS_EVENT_1_E_OC1 BIT(0) + +/* DA9121_REG_SYS_EVENT_2 */ + +#define DA9121_MASK_SYS_EVENT_2_E_GPIO2 BIT(2) +#define DA9121_MASK_SYS_EVENT_2_E_GPIO1 BIT(1) +#define DA9121_MASK_SYS_EVENT_2_E_GPIO0 BIT(0) + +/* DA9121_REG_SYS_MASK_0 */ + +#define DA9xxx_MASK_SYS_MASK_0_M_SG BIT(2) +#define DA9121_MASK_SYS_MASK_0_M_TEMP_CRIT BIT(1) +#define DA9121_MASK_SYS_MASK_0_M_TEMP_WARN BIT(0) + +/* DA9121_REG_SYS_MASK_1 */ + +#define DA9xxx_MASK_SYS_MASK_1_M_PG2 BIT(7) +#define DA9xxx_MASK_SYS_MASK_1_M_OV2 BIT(6) +#define DA9xxx_MASK_SYS_MASK_1_M_UV2 BIT(5) +#define DA9xxx_MASK_SYS_MASK_1_M_OC2 BIT(4) +#define DA9121_MASK_SYS_MASK_1_M_PG1 BIT(3) +#define DA9121_MASK_SYS_MASK_1_M_OV1 BIT(2) +#define DA9121_MASK_SYS_MASK_1_M_UV1 BIT(1) +#define DA9121_MASK_SYS_MASK_1_M_OC1 BIT(0) + +/* DA9121_REG_SYS_MASK_2 */ + +#define DA9121_MASK_SYS_MASK_2_M_GPIO2 BIT(2) +#define DA9121_MASK_SYS_MASK_2_M_GPIO1 BIT(1) +#define DA9121_MASK_SYS_MASK_2_M_GPIO0 BIT(0) + +/* DA9122_REG_SYS_MASK_3 */ + +#define DA9121_MASK_SYS_MASK_3_M_VR_HOT BIT(3) +#define DA9xxx_MASK_SYS_MASK_3_M_SG_STAT BIT(2) +#define DA9xxx_MASK_SYS_MASK_3_M_PG2_STAT BIT(1) +#define DA9121_MASK_SYS_MASK_3_M_PG1_STAT BIT(0) + +/* DA9121_REG_SYS_CONFIG_0 */ + +#define DA9121_MASK_SYS_CONFIG_0_CH1_DIS_DLY 0xF0 +#define DA9121_MASK_SYS_CONFIG_0_CH1_EN_DLY 0x0F + +/* DA9xxx_REG_SYS_CONFIG_1 */ + +#define DA9xxx_MASK_SYS_CONFIG_1_CH2_DIS_DLY 0xF0 +#define DA9xxx_MASK_SYS_CONFIG_1_CH2_EN_DLY 0x0F + +/* DA9121_REG_SYS_CONFIG_2 */ + +#define DA9121_MASK_SYS_CONFIG_2_OC_LATCHOFF 0x60 +#define DA9121_MASK_SYS_CONFIG_2_OC_DVC_MASK BIT(4) +#define DA9121_MASK_SYS_CONFIG_2_PG_DVC_MASK 0x0C + +/* DA9121_REG_SYS_CONFIG_3 */ + +#define DA9121_MASK_SYS_CONFIG_3_OSC_TUNE 0X70 +#define DA9121_MASK_SYS_CONFIG_3_I2C_TIMEOUT BIT(1) + +/* DA9121_REG_SYS_GPIO0_0 */ + +#define DA9121_MASK_SYS_GPIO0_0_GPIO0_MODE 0X1E +#define DA9121_MASK_SYS_GPIO0_0_GPIO0_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO0_1 */ + +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_DEB 0x30 +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_POL BIT(2) +#define DA9121_MASK_SYS_GPIO0_1_GPIO0_TRIG 0x03 + +/* DA9121_REG_SYS_GPIO1_0 */ + +#define DA9121_MASK_SYS_GPIO1_0_GPIO1_MODE 0x1E +#define DA9121_MASK_SYS_GPIO1_0_GPIO1_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO1_1 */ + +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_DEB 0x30 +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_POL BIT(2) +#define DA9121_MASK_SYS_GPIO1_1_GPIO1_TRIG 0x03 + +/* DA9121_REG_SYS_GPIO2_0 */ + +#define DA9121_MASK_SYS_GPIO2_0_GPIO2_MODE 0x1E +#define DA9121_MASK_SYS_GPIO2_0_GPIO2_OBUF BIT(0) + +/* DA9121_REG_SYS_GPIO2_1 */ + +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_FALL BIT(7) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB_RISE BIT(6) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_DEB 0x30 +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_PUPD BIT(3) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_POL BIT(2) +#define DA9121_MASK_SYS_GPIO2_1_GPIO2_TRIG 0x03 + +/* DA9121_REG_BUCK_BUCK1_0 / DA9xxx_REG_BUCK_BUCK2_0 */ + +#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_DWN 0x70 +#define DA9121_MASK_BUCK_BUCKx_0_CHx_SR_DVC_UP 0x0E +#define DA9121_MASK_BUCK_BUCKx_0_CHx_EN BIT(0) + +/* DA9121_REG_BUCK_BUCK1_1 / DA9xxx_REG_BUCK_BUCK2_1 */ + +#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_SHDN 0x70 +#define DA9121_MASK_BUCK_BUCKx_1_CHx_SR_STARTUP 0x0E +#define DA9121_MASK_BUCK_BUCKx_1_CHx_PD_DIS BIT(0) + +/* DA9121_REG_BUCK_BUCK1_2 / DA9xxx_REG_BUCK_BUCK2_2 */ + +#define DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM 0x0F + +/* DA9121_REG_BUCK_BUCK1_3 / DA9xxx_REG_BUCK_BUCK2_3 */ + +#define DA9121_MASK_BUCK_BUCKx_3_CHx_VMAX 0xFF + +/* DA9121_REG_BUCK_BUCK1_4 / DA9xxx_REG_BUCK_BUCK2_4 */ + +#define DA9121_MASK_BUCK_BUCKx_4_CHx_VSEL BIT(4) +#define DA9121_MASK_BUCK_BUCKx_4_CHx_B_MODE 0x0C +#define DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE 0x03 + +/* DA9121_REG_BUCK_BUCK1_5 / DA9xxx_REG_BUCK_BUCK2_5 */ + +#define DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT 0xFF + +/* DA9121_REG_BUCK_BUCK1_6 / DA9xxx_REG_BUCK_BUCK2_6 */ + +#define DA9121_MASK_BUCK_BUCKx_6_CHx_B_VOUT 0xFF + +/* DA9121_REG_BUCK_BUCK1_7 / DA9xxx_REG_BUCK_BUCK2_7 */ + +#define DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL 0x03 + + +/* DA9121_REG_OTP_DEVICE_ID */ + +#define DA9121_MASK_OTP_DEVICE_ID_DEV_ID 0xFF + +#define DA9121_DEVICE_ID 0x05 + +/* DA9121_REG_OTP_VARIANT_ID */ + +#define DA9121_SHIFT_OTP_VARIANT_ID_MRC 4 +#define DA9121_MASK_OTP_VARIANT_ID_MRC 0xF0 +#define DA9121_SHIFT_OTP_VARIANT_ID_VRC 0 +#define DA9121_MASK_OTP_VARIANT_ID_VRC 0x0F + +#define DA9121_VARIANT_MRC_BASE 0x2 +#define DA9121_VARIANT_VRC 0x1 +#define DA9220_VARIANT_VRC 0x0 +#define DA9122_VARIANT_VRC 0x2 +#define DA9217_VARIANT_VRC 0x7 + +/* DA9121_REG_OTP_CUSTOMER_ID */ + +#define DA9121_MASK_OTP_CUSTOMER_ID_CUST_ID 0xFF + +/* DA9121_REG_OTP_CONFIG_ID */ + +#define DA9121_MASK_OTP_CONFIG_ID_CONFIG_REV 0xFF + +#endif /* __DA9121_REGISTERS_H__ */ -- cgit v1.2.3 From f3fbd5566f6a8cdb7c48ab29bd1096205b7fbcaf Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:07 +0000 Subject: regulator: da9121: Add device variants Add basic support for configuration to reference variants of this device, and track the selected variant within the driver. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/aaabd3063593e5172fa6b605884d475df64e6d65.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 46 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index c11fe046345f..5bebdb2a480d 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -11,6 +11,12 @@ #include #include "da9121-regulator.h" +/* Chip data */ +struct da9121 { + struct device *dev; + int variant_id; +}; + #define DA9121_MIN_MV 300 #define DA9121_MAX_MV 1900 #define DA9121_STEP_MV 10 @@ -53,19 +59,46 @@ static const struct regulator_desc da9121_reg = { }; static const struct of_device_id da9121_dt_ids[] = { - { .compatible = "dlg,da9121", }, + { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, + { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, + { .compatible = "dlg,da9217", .data = (void *) DA9121_TYPE_DA9217 }, + { .compatible = "dlg,da9122", .data = (void *) DA9121_TYPE_DA9122_DA9131 }, + { .compatible = "dlg,da9131", .data = (void *) DA9121_TYPE_DA9122_DA9131 }, + { .compatible = "dlg,da9220", .data = (void *) DA9121_TYPE_DA9220_DA9132 }, + { .compatible = "dlg,da9132", .data = (void *) DA9121_TYPE_DA9220_DA9132 }, { } }; MODULE_DEVICE_TABLE(of, da9121_dt_ids); +static inline int da9121_of_get_id(struct device *dev) +{ + const struct of_device_id *id = of_match_device(da9121_dt_ids, dev); + + if (!id) { + dev_err(dev, "%s: Failed\n", __func__); + return -EINVAL; + } + return (uintptr_t)id->data; +} + static int da9121_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct da9121 *chip; + int ret = 0; struct device *dev = &i2c->dev; struct regulator_config config = {}; struct regulator_dev *rdev; struct regmap *regmap; + chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto error; + } + + chip->variant_id = da9121_of_get_id(&i2c->dev); + regmap = devm_regmap_init_i2c(i2c, &da9121_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); @@ -80,11 +113,18 @@ static int da9121_i2c_probe(struct i2c_client *i2c, return PTR_ERR(rdev); } - return 0; +error: + return ret; } static const struct i2c_device_id da9121_i2c_id[] = { - { "da9121", 0 }, + {"da9121", DA9121_TYPE_DA9121_DA9130}, + {"da9130", DA9121_TYPE_DA9121_DA9130}, + {"da9217", DA9121_TYPE_DA9217}, + {"da9122", DA9121_TYPE_DA9122_DA9131}, + {"da9131", DA9121_TYPE_DA9122_DA9131}, + {"da9220", DA9121_TYPE_DA9220_DA9132}, + {"da9132", DA9121_TYPE_DA9220_DA9132}, {}, }; MODULE_DEVICE_TABLE(i2c, da9121_i2c_id); -- cgit v1.2.3 From c860476b9e3a420192b28e580cb749e024d032eb Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:08 +0000 Subject: regulator: da9121: Add device variant regmaps Add ability to probe device and validate configuration, then apply a regmap configuration for a single or dual buck device accordingly. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/068c6b8d5e1b4e221e899e4c914c429429a2ec7d.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 14 +- drivers/regulator/da9121-regulator.c | 244 ++++++++++++++++++++++++++++++++--- include/linux/regulator/da9121.h | 25 ++++ 3 files changed, 262 insertions(+), 21 deletions(-) create mode 100644 include/linux/regulator/da9121.h (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index a497a3729091..7faedb05b7a6 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -313,13 +313,21 @@ config REGULATOR_DA9063 will be called da9063-regulator. config REGULATOR_DA9121 - tristate "Dialog Semiconductor DA9121 regulator" + tristate "Dialog Semiconductor DA9121/DA9122/DA9220/DA9217/DA9130/DA9131/DA9132 regulator" depends on I2C && OF select REGMAP_I2C help Say y here to support for the Dialog Semiconductor DA9121. The - DA9210 is a dual-phase buck converter controlled through an I2C - interface. + DA9121 is a single channel dual-phase buck converter controlled + through an I2C interface. + + DA9121 Single-channel dual-phase 10A buck converter + DA9130 Single-channel dual-phase 10A buck converter (Automotive) + DA9217 Single-channel dual-phase 6A buck converter + DA9122 Dual-channel single-phase 5A buck converter + DA9131 Dual-channel single-phase 5A buck converter (Automotive) + DA9220 Dual-channel single-phase 3A buck converter + DA9132 Dual-channel single-phase 3A buck converter (Automotive) This driver can also be built as a module. If so, the module will be called da9121-regulator. diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 5bebdb2a480d..137b1dfbcfbb 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -1,5 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only -/* Copyright (C) 2020 Axis Communications AB */ +// +// DA9121 Single-channel dual-phase 10A buck converter +// +// Copyright (C) 2020 Axis Communications AB +// +// DA9130 Single-channel dual-phase 10A buck converter (Automotive) +// DA9217 Single-channel dual-phase 6A buck converter +// DA9122 Dual-channel single-phase 5A buck converter +// DA9131 Dual-channel single-phase 5A buck converter (Automotive) +// DA9220 Dual-channel single-phase 3A buck converter +// DA9132 Dual-channel single-phase 3A buck converter (Automotive) +// +// Copyright (C) 2020 Dialog Semiconductor #include #include @@ -9,26 +21,17 @@ #include #include #include +#include + #include "da9121-regulator.h" /* Chip data */ struct da9121 { struct device *dev; + struct regmap *regmap; int variant_id; }; -#define DA9121_MIN_MV 300 -#define DA9121_MAX_MV 1900 -#define DA9121_STEP_MV 10 -#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV) -#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \ - + 1 + DA9121_MIN_SEL) - -static const struct regmap_config da9121_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - static const struct regulator_ops da9121_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -38,6 +41,13 @@ static const struct regulator_ops da9121_buck_ops = { .list_voltage = regulator_list_voltage_linear, }; +#define DA9121_MIN_MV 300 +#define DA9121_MAX_MV 1900 +#define DA9121_STEP_MV 10 +#define DA9121_MIN_SEL (DA9121_MIN_MV / DA9121_STEP_MV) +#define DA9121_N_VOLTAGES (((DA9121_MAX_MV - DA9121_MIN_MV) / DA9121_STEP_MV) \ + + 1 + DA9121_MIN_SEL) + static const struct regulator_desc da9121_reg = { .name = "da9121", .of_match = "buck1", @@ -58,6 +68,205 @@ static const struct regulator_desc da9121_reg = { .enable_time = 20, }; +/* DA9121 chip register model */ +static const struct regmap_range da9121_1ch_readable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6), + regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID), +}; + +static const struct regmap_access_table da9121_1ch_readable_table = { + .yes_ranges = da9121_1ch_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_1ch_readable_ranges), +}; + +static const struct regmap_range da9121_2ch_readable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_7), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_7), + regmap_reg_range(DA9121_REG_OTP_DEVICE_ID, DA9121_REG_OTP_CONFIG_ID), +}; + +static const struct regmap_access_table da9121_2ch_readable_table = { + .yes_ranges = da9121_2ch_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_2ch_readable_ranges), +}; + +static const struct regmap_range da9121_1ch_writeable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_6), +}; + +static const struct regmap_access_table da9121_1ch_writeable_table = { + .yes_ranges = da9121_1ch_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_1ch_writeable_ranges), +}; + +static const struct regmap_range da9121_2ch_writeable_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_EVENT_0, DA9121_REG_SYS_MASK_3), + regmap_reg_range(DA9121_REG_SYS_CONFIG_2, DA9121_REG_SYS_CONFIG_3), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_2), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_4, DA9121_REG_BUCK_BUCK1_7), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_0, DA9xxx_REG_BUCK_BUCK2_2), + regmap_reg_range(DA9xxx_REG_BUCK_BUCK2_4, DA9xxx_REG_BUCK_BUCK2_7), +}; + +static const struct regmap_access_table da9121_2ch_writeable_table = { + .yes_ranges = da9121_2ch_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_2ch_writeable_ranges), +}; + + +static const struct regmap_range da9121_volatile_ranges[] = { + regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_EVENT_2), + regmap_reg_range(DA9121_REG_SYS_GPIO0_0, DA9121_REG_SYS_GPIO2_1), + regmap_reg_range(DA9121_REG_BUCK_BUCK1_0, DA9121_REG_BUCK_BUCK1_6), +}; + +static const struct regmap_access_table da9121_volatile_table = { + .yes_ranges = da9121_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9121_volatile_ranges), +}; + +/* DA9121 regmap config for 1 channel variants */ +static struct regmap_config da9121_1ch_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DA9121_REG_OTP_CONFIG_ID, + .rd_table = &da9121_1ch_readable_table, + .wr_table = &da9121_1ch_writeable_table, + .volatile_table = &da9121_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +/* DA9121 regmap config for 2 channel variants */ +static struct regmap_config da9121_2ch_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DA9121_REG_OTP_CONFIG_ID, + .rd_table = &da9121_2ch_readable_table, + .wr_table = &da9121_2ch_writeable_table, + .volatile_table = &da9121_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; + +static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) +{ + u32 device_id; + u8 chip_id = chip->variant_id; + u32 variant_id; + u8 variant_mrc, variant_vrc; + char *type; + const char *name; + bool config_match = false; + int ret = 0; + + ret = regmap_read(chip->regmap, DA9121_REG_OTP_DEVICE_ID, &device_id); + if (ret < 0) { + dev_err(chip->dev, "Cannot read device ID: %d\n", ret); + goto error; + } + + ret = regmap_read(chip->regmap, DA9121_REG_OTP_VARIANT_ID, &variant_id); + if (ret < 0) { + dev_err(chip->dev, "Cannot read variant ID: %d\n", ret); + goto error; + } + + if (device_id != DA9121_DEVICE_ID) { + dev_err(chip->dev, "Invalid device ID: 0x%02x\n", device_id); + ret = -ENODEV; + goto error; + } + + variant_vrc = variant_id & DA9121_MASK_OTP_VARIANT_ID_VRC; + + switch (variant_vrc) { + case DA9121_VARIANT_VRC: + type = "DA9121/DA9130"; + config_match = (chip_id == DA9121_TYPE_DA9121_DA9130); + break; + case DA9220_VARIANT_VRC: + type = "DA9220/DA9132"; + config_match = (chip_id == DA9121_TYPE_DA9220_DA9132); + break; + case DA9122_VARIANT_VRC: + type = "DA9122/DA9131"; + config_match = (chip_id == DA9121_TYPE_DA9122_DA9131); + break; + case DA9217_VARIANT_VRC: + type = "DA9217"; + config_match = (chip_id == DA9121_TYPE_DA9217); + break; + default: + type = "Unknown"; + break; + } + + dev_info(chip->dev, + "Device detected (device-ID: 0x%02X, var-ID: 0x%02X, %s)\n", + device_id, variant_id, type); + + if (!config_match) { + dev_err(chip->dev, "Device tree configuration '%s' does not match detected device.\n", name); + ret = -EINVAL; + goto error; + } + + variant_mrc = (variant_id & DA9121_MASK_OTP_VARIANT_ID_MRC) + >> DA9121_SHIFT_OTP_VARIANT_ID_MRC; + + if ((device_id == DA9121_DEVICE_ID) && + (variant_mrc < DA9121_VARIANT_MRC_BASE)) { + dev_err(chip->dev, + "Cannot support variant MRC: 0x%02X\n", variant_mrc); + ret = -EINVAL; + } +error: + return ret; +} + +static int da9121_assign_chip_model(struct i2c_client *i2c, + struct da9121 *chip) +{ + struct regmap_config *regmap; + int ret = 0; + + chip->dev = &i2c->dev; + + switch (chip->variant_id) { + case DA9121_TYPE_DA9121_DA9130: + fallthrough; + case DA9121_TYPE_DA9217: + regmap = &da9121_1ch_regmap_config; + break; + case DA9121_TYPE_DA9122_DA9131: + fallthrough; + case DA9121_TYPE_DA9220_DA9132: + regmap = &da9121_2ch_regmap_config; + break; + } + + chip->regmap = devm_regmap_init_i2c(i2c, regmap); + if (IS_ERR(chip->regmap)) { + ret = PTR_ERR(chip->regmap); + dev_err(chip->dev, "Failed to configure a register map: %d\n", + ret); + } + + ret = da9121_check_device_type(i2c, chip); + + return ret; +} + static const struct of_device_id da9121_dt_ids[] = { { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, @@ -89,7 +298,6 @@ static int da9121_i2c_probe(struct i2c_client *i2c, struct device *dev = &i2c->dev; struct regulator_config config = {}; struct regulator_dev *rdev; - struct regmap *regmap; chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); if (!chip) { @@ -99,13 +307,13 @@ static int da9121_i2c_probe(struct i2c_client *i2c, chip->variant_id = da9121_of_get_id(&i2c->dev); - regmap = devm_regmap_init_i2c(i2c, &da9121_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + ret = da9121_assign_chip_model(i2c, chip); + if (ret < 0) + goto error; config.dev = &i2c->dev; config.of_node = dev->of_node; - config.regmap = regmap; + config.regmap = chip->regmap; rdev = devm_regulator_register(&i2c->dev, &da9121_reg, &config); if (IS_ERR(rdev)) { diff --git a/include/linux/regulator/da9121.h b/include/linux/regulator/da9121.h new file mode 100644 index 000000000000..c31180d886cc --- /dev/null +++ b/include/linux/regulator/da9121.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * DA9121 Single-channel dual-phase 10A buck converter + * DA9130 Single-channel dual-phase 10A buck converter (Automotive) + * DA9217 Single-channel dual-phase 6A buck converter + * DA9122 Dual-channel single-phase 5A buck converter + * DA9131 Dual-channel single-phase 5A buck converter (Automotive) + * DA9220 Dual-channel single-phase 3A buck converter + * DA9132 Dual-channel single-phase 3A buck converter (Automotive) + * + * Copyright (C) 2020 Dialog Semiconductor + * + * Authors: Adam Ward, Dialog Semiconductor + */ + +#ifndef __LINUX_REGULATOR_DA9121_H +#define __LINUX_REGULATOR_DA9121_H + +enum { + DA9121_IDX_BUCK1, + DA9121_IDX_BUCK2, + DA9121_IDX_MAX +}; + +#endif -- cgit v1.2.3 From 91863239ce0366c801f1f128246f30ea80d7727b Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:09 +0000 Subject: regulator: da9121: Add device variant descriptors Descriptors for bucks in all variants, ready for of_regulator_match Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/8f7a11d59e5ac3ba1bfd448bcfc905efc99b7874.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 110 +++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 137b1dfbcfbb..a717e2b1da33 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -41,6 +41,11 @@ static const struct regulator_ops da9121_buck_ops = { .list_voltage = regulator_list_voltage_linear, }; +static struct of_regulator_match da9121_matches[] = { + [DA9121_IDX_BUCK1] = { .name = "buck1" }, + [DA9121_IDX_BUCK2] = { .name = "buck2" }, +}; + #define DA9121_MIN_MV 300 #define DA9121_MAX_MV 1900 #define DA9121_STEP_MV 10 @@ -49,9 +54,11 @@ static const struct regulator_ops da9121_buck_ops = { + 1 + DA9121_MIN_SEL) static const struct regulator_desc da9121_reg = { + .id = DA9121_IDX_BUCK1, .name = "da9121", .of_match = "buck1", .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -68,6 +75,105 @@ static const struct regulator_desc da9121_reg = { .enable_time = 20, }; +static const struct regulator_desc da9220_reg[2] = { + { + .id = DA9121_IDX_BUCK1, + .name = "DA9220/DA9132 BUCK1", + .of_match = "buck1", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + }, + { + .id = DA9121_IDX_BUCK2, + .name = "DA9220/DA9132 BUCK2", + .of_match = "buck2", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9xxx_REG_BUCK_BUCK2_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + } +}; + +static const struct regulator_desc da9122_reg[2] = { + { + .id = DA9121_IDX_BUCK1, + .name = "DA9122/DA9131 BUCK1", + .of_match = "buck1", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + }, + { + .id = DA9121_IDX_BUCK2, + .name = "DA9122/DA9131 BUCK2", + .of_match = "buck2", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9xxx_REG_BUCK_BUCK2_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9xxx_REG_BUCK_BUCK2_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, + } +}; + +static const struct regulator_desc da9217_reg = { + .id = DA9121_IDX_BUCK1, + .name = "DA9217 BUCK1", + .of_match = "buck1", + .owner = THIS_MODULE, + .regulators_node = of_match_ptr("regulators"), + .ops = &da9121_buck_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = DA9121_N_VOLTAGES, + .min_uV = DA9121_MIN_MV * 1000, + .uV_step = DA9121_STEP_MV * 1000, + .linear_min_sel = DA9121_MIN_SEL, + .enable_reg = DA9121_REG_BUCK_BUCK1_0, + .enable_mask = DA9121_MASK_BUCK_BUCKx_0_CHx_EN, + .vsel_reg = DA9121_REG_BUCK_BUCK1_5, + .vsel_mask = DA9121_MASK_BUCK_BUCKx_5_CHx_A_VOUT, +}; + +static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = { + [DA9121_TYPE_DA9121_DA9130] = { &da9121_reg, NULL }, + [DA9121_TYPE_DA9220_DA9132] = { &da9220_reg[0], &da9220_reg[1] }, + [DA9121_TYPE_DA9122_DA9131] = { &da9122_reg[0], &da9122_reg[1] }, + [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, +}; + /* DA9121 chip register model */ static const struct regmap_range da9121_1ch_readable_ranges[] = { regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), @@ -255,6 +361,10 @@ static int da9121_assign_chip_model(struct i2c_client *i2c, break; } + /* Set these up for of_regulator_match call which may want .of_map_modes */ + da9121_matches[0].desc = local_da9121_regulators[chip->variant_id][0]; + da9121_matches[1].desc = local_da9121_regulators[chip->variant_id][1]; + chip->regmap = devm_regmap_init_i2c(i2c, regmap); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); -- cgit v1.2.3 From 46c413d5bb239769e6f1de706adf422c807c7a5f Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:10 +0000 Subject: regulator: da9121: Add support for device variants via devicetree Add devicetree configuration and device variant parameters. Use the latter to enable the check and use of parameters specific to dual buck variants. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/5849ce60595aef1018bdde7dcfb54a7397597545.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 119 +++++++++++++++++++++++++++++++++++ include/linux/regulator/da9121.h | 11 ++++ 2 files changed, 130 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index a717e2b1da33..1f74371eebad 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -14,6 +14,7 @@ // Copyright (C) 2020 Dialog Semiconductor #include +#include #include #include #include @@ -28,10 +29,67 @@ /* Chip data */ struct da9121 { struct device *dev; + struct da9121_pdata *pdata; struct regmap *regmap; int variant_id; }; +/* Define ranges for different variants, enabling translation to/from + * registers. Maximums give scope to allow for transients. + */ +struct da9121_range { + int val_min; + int val_max; + int val_stp; + int reg_min; + int reg_max; +}; + +struct da9121_range da9121_10A_2phase_current = { + .val_min = 7000000, + .val_max = 20000000, + .val_stp = 1000000, + .reg_min = 1, + .reg_max = 14, +}; + +struct da9121_range da9121_6A_2phase_current = { + .val_min = 7000000, + .val_max = 12000000, + .val_stp = 1000000, + .reg_min = 1, + .reg_max = 6, +}; + +struct da9121_range da9121_5A_1phase_current = { + .val_min = 3500000, + .val_max = 10000000, + .val_stp = 500000, + .reg_min = 1, + .reg_max = 14, +}; + +struct da9121_range da9121_3A_1phase_current = { + .val_min = 3500000, + .val_max = 6000000, + .val_stp = 500000, + .reg_min = 1, + .reg_max = 6, +}; + +struct da9121_variant_info { + int num_bucks; + int num_phases; + struct da9121_range *current_range; +}; + +static const struct da9121_variant_info variant_parameters[] = { + { 1, 2, &da9121_10A_2phase_current }, //DA9121_TYPE_DA9121_DA9130 + { 2, 1, &da9121_3A_1phase_current }, //DA9121_TYPE_DA9220_DA9132 + { 2, 1, &da9121_5A_1phase_current }, //DA9121_TYPE_DA9122_DA9131 + { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217 +}; + static const struct regulator_ops da9121_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -46,6 +104,59 @@ static struct of_regulator_match da9121_matches[] = { [DA9121_IDX_BUCK2] = { .name = "buck2" }, }; +static int da9121_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct da9121 *chip = config->driver_data; + struct da9121_pdata *pdata; + struct gpio_desc *ena_gpiod; + + if (chip->pdata == NULL) { + pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } else { + pdata = chip->pdata; + } + + pdata->num_buck++; + + if (pdata->num_buck > variant_parameters[chip->variant_id].num_bucks) { + dev_err(chip->dev, "Error: excessive regulators for device\n"); + return -ENODEV; + } + + ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, + GPIOD_OUT_HIGH | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, + "da9121-enable"); + if (!IS_ERR(ena_gpiod)) + config->ena_gpiod = ena_gpiod; + + if (variant_parameters[chip->variant_id].num_bucks == 2) { + uint32_t ripple_cancel; + uint32_t ripple_reg; + int ret; + + if (of_property_read_u32(da9121_matches[pdata->num_buck].of_node, + "dlg,ripple-cancel", &ripple_cancel)) { + if (pdata->num_buck > 1) + ripple_reg = DA9xxx_REG_BUCK_BUCK2_7; + else + ripple_reg = DA9121_REG_BUCK_BUCK1_7; + + ret = regmap_update_bits(chip->regmap, ripple_reg, + DA9xxx_MASK_BUCK_BUCKx_7_CHx_RIPPLE_CANCEL, + ripple_cancel); + if (ret < 0) + dev_err(chip->dev, "Cannot set ripple mode, err: %d\n", ret); + } + } + + return 0; +} + #define DA9121_MIN_MV 300 #define DA9121_MAX_MV 1900 #define DA9121_STEP_MV 10 @@ -57,6 +168,7 @@ static const struct regulator_desc da9121_reg = { .id = DA9121_IDX_BUCK1, .name = "da9121", .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -80,6 +192,7 @@ static const struct regulator_desc da9220_reg[2] = { .id = DA9121_IDX_BUCK1, .name = "DA9220/DA9132 BUCK1", .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -97,6 +210,7 @@ static const struct regulator_desc da9220_reg[2] = { .id = DA9121_IDX_BUCK2, .name = "DA9220/DA9132 BUCK2", .of_match = "buck2", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -117,6 +231,7 @@ static const struct regulator_desc da9122_reg[2] = { .id = DA9121_IDX_BUCK1, .name = "DA9122/DA9131 BUCK1", .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -134,6 +249,7 @@ static const struct regulator_desc da9122_reg[2] = { .id = DA9121_IDX_BUCK2, .name = "DA9122/DA9131 BUCK2", .of_match = "buck2", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -153,6 +269,7 @@ static const struct regulator_desc da9217_reg = { .id = DA9121_IDX_BUCK1, .name = "DA9217 BUCK1", .of_match = "buck1", + .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), .ops = &da9121_buck_ops, @@ -415,6 +532,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, goto error; } + chip->pdata = i2c->dev.platform_data; chip->variant_id = da9121_of_get_id(&i2c->dev); ret = da9121_assign_chip_model(i2c, chip); @@ -422,6 +540,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, goto error; config.dev = &i2c->dev; + config.driver_data = chip; config.of_node = dev->of_node; config.regmap = chip->regmap; diff --git a/include/linux/regulator/da9121.h b/include/linux/regulator/da9121.h index c31180d886cc..62d9d257dc25 100644 --- a/include/linux/regulator/da9121.h +++ b/include/linux/regulator/da9121.h @@ -16,10 +16,21 @@ #ifndef __LINUX_REGULATOR_DA9121_H #define __LINUX_REGULATOR_DA9121_H +#include + +struct gpio_desc; + enum { DA9121_IDX_BUCK1, DA9121_IDX_BUCK2, DA9121_IDX_MAX }; +struct da9121_pdata { + int num_buck; + struct gpio_desc *gpiod_ren[DA9121_IDX_MAX]; + struct device_node *reg_node[DA9121_IDX_MAX]; + struct regulator_init_data *init_data[DA9121_IDX_MAX]; +}; + #endif -- cgit v1.2.3 From 9929900d1878565a90a70bf25baa7d3e4187ae99 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:11 +0000 Subject: regulator: da9121: Update registration to support multiple buck variants Add function which iterates the regulator descriptors for the confirmed variant ID and registers each buck. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/1fd53c3ab032ef3c8a92f80a2247381db1c09ced.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 44 +++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 1f74371eebad..6c82441dda57 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -31,6 +31,7 @@ struct da9121 { struct device *dev; struct da9121_pdata *pdata; struct regmap *regmap; + struct regulator_dev *rdev[DA9121_IDX_MAX]; int variant_id; }; @@ -291,6 +292,35 @@ static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, }; +static int da9121_set_regulator_config(struct da9121 *chip) +{ + struct regulator_config config = { }; + unsigned int max_matches = variant_parameters[chip->variant_id].num_bucks; + int ret = 0; + int i; + + for (i = 0; i < max_matches; i++) { + const struct regulator_desc *regl_desc = + local_da9121_regulators[chip->variant_id][i]; + + config.dev = chip->dev; + config.driver_data = chip; + config.regmap = chip->regmap; + + chip->rdev[i] = devm_regulator_register(chip->dev, + regl_desc, &config); + if (IS_ERR(chip->rdev[i])) { + dev_err(chip->dev, "Failed to register regulator %s, %d/%d\n", + regl_desc->name, (i+1), max_matches); + ret = PTR_ERR(chip->rdev[i]); + goto error; + } + } + +error: + return ret; +} + /* DA9121 chip register model */ static const struct regmap_range da9121_1ch_readable_ranges[] = { regmap_reg_range(DA9121_REG_SYS_STATUS_0, DA9121_REG_SYS_MASK_3), @@ -522,9 +552,6 @@ static int da9121_i2c_probe(struct i2c_client *i2c, { struct da9121 *chip; int ret = 0; - struct device *dev = &i2c->dev; - struct regulator_config config = {}; - struct regulator_dev *rdev; chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); if (!chip) { @@ -539,16 +566,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto error; - config.dev = &i2c->dev; - config.driver_data = chip; - config.of_node = dev->of_node; - config.regmap = chip->regmap; - - rdev = devm_regulator_register(&i2c->dev, &da9121_reg, &config); - if (IS_ERR(rdev)) { - dev_err(&i2c->dev, "Failed to register da9121 regulator\n"); - return PTR_ERR(rdev); - } + ret = da9121_set_regulator_config(chip); error: return ret; -- cgit v1.2.3 From 5c4b62af1d58fe3823dfb462ce553ceb9729c8fa Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:12 +0000 Subject: regulator: da9121: add current support This commit adds support for getting/setting current for all supported variants. Limits are adjusted per variant to match HW implementation. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/9aa80b909893dbe609730919ed595c6a8ac26606.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 113 +++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 6c82441dda57..a69acb26f748 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -91,6 +91,117 @@ static const struct da9121_variant_info variant_parameters[] = { { 1, 2, &da9121_6A_2phase_current }, //DA9121_TYPE_DA9217 }; +struct da9121_field { + unsigned int reg; + unsigned int msk; +}; + +static const struct da9121_field da9121_current_field[2] = { + { DA9121_REG_BUCK_BUCK1_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, + { DA9xxx_REG_BUCK_BUCK2_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, +}; + +static int da9121_get_current_limit(struct regulator_dev *rdev) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int val = 0; + int ret = 0; + + ret = regmap_read(chip->regmap, da9121_current_field[id].reg, &val); + if (ret < 0) { + dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret); + goto error; + } + + if (val < range->reg_min) { + ret = -EACCES; + goto error; + } + + if (val > range->reg_max) { + ret = -EINVAL; + goto error; + } + + return range->val_min + (range->val_stp * (val - range->reg_min)); +error: + return ret; +} + +static int da9121_ceiling_selector(struct regulator_dev *rdev, + int min, int max, + unsigned int *selector) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int level; + unsigned int i = 0; + unsigned int sel = 0; + int ret = 0; + + if (range->val_min > max || range->val_max < min) { + dev_err(chip->dev, + "Requested current out of regulator capability\n"); + ret = -EINVAL; + goto error; + } + + level = range->val_max; + for (i = range->reg_max; i >= range->reg_min; i--) { + if (level <= max) { + sel = i; + break; + } + level -= range->val_stp; + } + + if (level < min) { + dev_err(chip->dev, + "Best match falls below minimum requested current\n"); + ret = -EINVAL; + goto error; + } + + *selector = sel; +error: + return ret; +} + +static int da9121_set_current_limit(struct regulator_dev *rdev, + int min_ua, int max_ua) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct da9121_range *range = + variant_parameters[chip->variant_id].current_range; + unsigned int sel = 0; + int ret = 0; + + if (min_ua < range->val_min || + max_ua > range->val_max) { + ret = -EINVAL; + goto error; + } + + ret = da9121_ceiling_selector(rdev, min_ua, max_ua, &sel); + if (ret < 0) + goto error; + + ret = regmap_update_bits(chip->regmap, + da9121_current_field[id].reg, + da9121_current_field[id].msk, + (unsigned int)sel); + if (ret < 0) + dev_err(chip->dev, "Cannot update BUCK current limit, err: %d\n", ret); + +error: + return ret; +} + static const struct regulator_ops da9121_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -98,6 +209,8 @@ static const struct regulator_ops da9121_buck_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, + .get_current_limit = da9121_get_current_limit, + .set_current_limit = da9121_set_current_limit, }; static struct of_regulator_match da9121_matches[] = { -- cgit v1.2.3 From 65ac97042d4e0918b401b5f21e2810efa27be848 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:13 +0000 Subject: regulator: da9121: add mode support Adds get/set for mode, and mapping from REGULATOR_MODE_* to select PFM/PWM/Auto operation. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/7844c8f6facb6f7c0649381629cc75ccad14723d.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index a69acb26f748..8e50f55af45d 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -101,6 +101,11 @@ static const struct da9121_field da9121_current_field[2] = { { DA9xxx_REG_BUCK_BUCK2_2, DA9121_MASK_BUCK_BUCKx_2_CHx_ILIM }, }; +static const struct da9121_field da9121_mode_field[2] = { + { DA9121_REG_BUCK_BUCK1_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, + { DA9xxx_REG_BUCK_BUCK2_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, +}; + static int da9121_get_current_limit(struct regulator_dev *rdev) { struct da9121 *chip = rdev_get_drvdata(rdev); @@ -202,6 +207,67 @@ error: return ret; } +static unsigned int da9121_map_mode(unsigned int mode) +{ + switch (mode) { + case DA9121_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + case DA9121_BUCK_MODE_FORCE_PWM_SHEDDING: + return REGULATOR_MODE_NORMAL; + case DA9121_BUCK_MODE_AUTO: + return REGULATOR_MODE_IDLE; + case DA9121_BUCK_MODE_FORCE_PFM: + return REGULATOR_MODE_STANDBY; + default: + return -EINVAL; + } +} + +static int da9121_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = DA9121_BUCK_MODE_FORCE_PWM; + break; + case REGULATOR_MODE_NORMAL: + val = DA9121_BUCK_MODE_FORCE_PWM_SHEDDING; + break; + case REGULATOR_MODE_IDLE: + val = DA9121_BUCK_MODE_AUTO; + break; + case REGULATOR_MODE_STANDBY: + val = DA9121_BUCK_MODE_FORCE_PFM; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(chip->regmap, + da9121_mode_field[id].reg, + da9121_mode_field[id].msk, + val); +} + +static unsigned int da9121_buck_get_mode(struct regulator_dev *rdev) +{ + struct da9121 *chip = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + unsigned int val; + int ret = 0; + + ret = regmap_read(chip->regmap, da9121_mode_field[id].reg, &val); + if (ret < 0) { + dev_err(chip->dev, "Cannot read BUCK register: %d\n", ret); + return -EINVAL; + } + + return da9121_map_mode(val & da9121_mode_field[id].msk); +} + static const struct regulator_ops da9121_buck_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -211,6 +277,8 @@ static const struct regulator_ops da9121_buck_ops = { .list_voltage = regulator_list_voltage_linear, .get_current_limit = da9121_get_current_limit, .set_current_limit = da9121_set_current_limit, + .set_mode = da9121_buck_set_mode, + .get_mode = da9121_buck_get_mode, }; static struct of_regulator_match da9121_matches[] = { @@ -285,6 +353,7 @@ static const struct regulator_desc da9121_reg = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -309,6 +378,7 @@ static const struct regulator_desc da9220_reg[2] = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -327,6 +397,7 @@ static const struct regulator_desc da9220_reg[2] = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -348,6 +419,7 @@ static const struct regulator_desc da9122_reg[2] = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -366,6 +438,7 @@ static const struct regulator_desc da9122_reg[2] = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, @@ -386,6 +459,7 @@ static const struct regulator_desc da9217_reg = { .of_parse_cb = da9121_of_parse_cb, .owner = THIS_MODULE, .regulators_node = of_match_ptr("regulators"), + .of_map_mode = da9121_map_mode, .ops = &da9121_buck_ops, .type = REGULATOR_VOLTAGE, .n_voltages = DA9121_N_VOLTAGES, -- cgit v1.2.3 From 40bb5b02ff10f6b68925850e7dedaeec532abb88 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 30 Nov 2020 16:59:14 +0000 Subject: regulator: da9121: add interrupt support Adds interrupt handler for variants, and notifications for events; over temperature/voltage/current. Because the IRQs are triggered by persisting status, they must be masked and the status polled until clear, before the IRQ can be enabled again. Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/fe21796bbcbadff84a472a4cc581ae8fafc7f8f5.1606755367.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 286 +++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 8e50f55af45d..d9a8a4b8fb3b 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -23,15 +23,21 @@ #include #include #include +#include +#include #include "da9121-regulator.h" /* Chip data */ struct da9121 { struct device *dev; + struct delayed_work work; struct da9121_pdata *pdata; struct regmap *regmap; struct regulator_dev *rdev[DA9121_IDX_MAX]; + unsigned int persistent[2]; + unsigned int passive_delay; + int chip_irq; int variant_id; }; @@ -106,6 +112,59 @@ static const struct da9121_field da9121_mode_field[2] = { { DA9xxx_REG_BUCK_BUCK2_4, DA9121_MASK_BUCK_BUCKx_4_CHx_A_MODE }, }; +struct status_event_data { + int buck_id; /* 0=core, 1/2-buck */ + int reg_index; /* index for status/event/mask register selection */ + int status_bit; /* bit masks... */ + int event_bit; + int mask_bit; + unsigned long notification; /* Notification for status inception */ + char *warn; /* if NULL, notify - otherwise dev_warn this string */ +}; + +#define DA9121_STATUS(id, bank, name, notification, warning) \ + { id, bank, \ + DA9121_MASK_SYS_STATUS_##bank##_##name, \ + DA9121_MASK_SYS_EVENT_##bank##_E_##name, \ + DA9121_MASK_SYS_MASK_##bank##_M_##name, \ + notification, warning } + +/* For second buck related event bits that are specific to DA9122, DA9220 variants */ +#define DA9xxx_STATUS(id, bank, name, notification, warning) \ + { id, bank, \ + DA9xxx_MASK_SYS_STATUS_##bank##_##name, \ + DA9xxx_MASK_SYS_EVENT_##bank##_E_##name, \ + DA9xxx_MASK_SYS_MASK_##bank##_M_##name, \ + notification, warning } + +/* The status signals that may need servicing, depending on device variant. + * After assertion, they persist; so event is notified, the IRQ disabled, + * and status polled until clear again and IRQ is reenabled. + * + * SG/PG1/PG2 should be set when device first powers up and should never + * re-occur. When this driver starts, it is expected that these will have + * self-cleared for when the IRQs are enabled, so these should never be seen. + * If seen, the implication is that the device has reset. + * + * GPIO0/1/2 are not configured for use by default, so should not be seen. + */ +const struct status_event_data status_event_handling[] = { + DA9xxx_STATUS(0, 0, SG, 0, "Handled E_SG\n"), + DA9121_STATUS(0, 0, TEMP_CRIT, (REGULATOR_EVENT_OVER_TEMP|REGULATOR_EVENT_DISABLE), NULL), + DA9121_STATUS(0, 0, TEMP_WARN, REGULATOR_EVENT_OVER_TEMP, NULL), + DA9121_STATUS(1, 1, PG1, 0, "Handled E_PG1\n"), + DA9121_STATUS(1, 1, OV1, REGULATOR_EVENT_REGULATION_OUT, NULL), + DA9121_STATUS(1, 1, UV1, REGULATOR_EVENT_UNDER_VOLTAGE, NULL), + DA9121_STATUS(1, 1, OC1, REGULATOR_EVENT_OVER_CURRENT, NULL), + DA9xxx_STATUS(2, 1, PG2, 0, "Handled E_PG2\n"), + DA9xxx_STATUS(2, 1, OV2, REGULATOR_EVENT_REGULATION_OUT, NULL), + DA9xxx_STATUS(2, 1, UV2, REGULATOR_EVENT_UNDER_VOLTAGE, NULL), + DA9xxx_STATUS(2, 1, OC2, REGULATOR_EVENT_OVER_CURRENT, NULL), + DA9121_STATUS(0, 2, GPIO0, 0, "Handled E_GPIO0\n"), + DA9121_STATUS(0, 2, GPIO1, 0, "Handled E_GPIO1\n"), + DA9121_STATUS(0, 2, GPIO2, 0, "Handled E_GPIO2\n"), +}; + static int da9121_get_current_limit(struct regulator_dev *rdev) { struct da9121 *chip = rdev_get_drvdata(rdev); @@ -479,6 +538,157 @@ static const struct regulator_desc *local_da9121_regulators[][DA9121_IDX_MAX] = [DA9121_TYPE_DA9217] = { &da9217_reg, NULL }, }; +static void da9121_status_poll_on(struct work_struct *work) +{ + struct da9121 *chip = container_of(work, struct da9121, work.work); + int status[3] = {0}; + int clear[3] = {0}; + unsigned long delay; + int i; + int ret; + + ret = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_STATUS_0, status, 2); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read STATUS registers: %d\n", ret); + goto error; + } + + /* Possible events are tested to be within range for the variant, potentially + * masked by the IRQ handler (not just warned about), as having been masked, + * and the respective state cleared - then flagged to unmask for next IRQ. + */ + for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) { + const struct status_event_data *item = &status_event_handling[i]; + int reg_idx = item->reg_index; + bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks); + bool supported = (item->warn == NULL); + bool persisting = (chip->persistent[reg_idx] & item->event_bit); + bool now_cleared = !(status[reg_idx] & item->status_bit); + + if (relevant && supported && persisting && now_cleared) { + clear[reg_idx] |= item->mask_bit; + chip->persistent[reg_idx] &= ~item->event_bit; + } + } + + for (i = 0; i < 2; i++) { + if (clear[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = clear[i]; + + ret = regmap_update_bits(chip->regmap, reg, mbit, 0); + if (ret < 0) { + dev_err(chip->dev, + "Failed to unmask 0x%02x %d\n", + reg, ret); + goto error; + } + } + } + + if (chip->persistent[0] | chip->persistent[1]) { + delay = msecs_to_jiffies(chip->passive_delay); + queue_delayed_work(system_freezable_wq, &chip->work, delay); + } + +error: + return; +} + +static irqreturn_t da9121_irq_handler(int irq, void *data) +{ + struct da9121 *chip = data; + struct regulator_dev *rdev; + int event[3] = {0}; + int handled[3] = {0}; + int mask[3] = {0}; + int ret = IRQ_NONE; + int i; + int err; + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_EVENT_0, event, 3); + if (err < 0) { + dev_err(chip->dev, "Failed to read EVENT registers %d\n", err); + ret = IRQ_NONE; + goto error; + } + + err = regmap_bulk_read(chip->regmap, DA9121_REG_SYS_MASK_0, mask, 3); + if (err < 0) { + dev_err(chip->dev, + "Failed to read MASK registers: %d\n", ret); + ret = IRQ_NONE; + goto error; + } + + rdev = chip->rdev[DA9121_IDX_BUCK1]; + + /* Possible events are tested to be within range for the variant, currently + * enabled, and having triggered this IRQ. The event may then be notified, + * or a warning given for unexpected events - those from device POR, and + * currently unsupported GPIO configurations. + */ + for (i = 0; i < ARRAY_SIZE(status_event_handling); i++) { + const struct status_event_data *item = &status_event_handling[i]; + int reg_idx = item->reg_index; + bool relevant = (item->buck_id <= variant_parameters[chip->variant_id].num_bucks); + bool enabled = !(mask[reg_idx] & item->mask_bit); + bool active = (event[reg_idx] & item->event_bit); + bool notify = (item->warn == NULL); + + if (relevant && enabled && active) { + if (notify) { + chip->persistent[reg_idx] |= item->event_bit; + regulator_notifier_call_chain(rdev, item->notification, NULL); + } else { + dev_warn(chip->dev, item->warn); + handled[reg_idx] |= item->event_bit; + ret = IRQ_HANDLED; + } + } + } + + for (i = 0; i < 3; i++) { + if (event[i] != handled[i]) { + dev_warn(chip->dev, + "Unhandled event(s) in bank%d 0x%02x\n", i, + event[i] ^ handled[i]); + } + } + + /* Mask the interrupts for persistent events OV, OC, UV, WARN, CRIT */ + for (i = 0; i < 2; i++) { + if (handled[i]) { + unsigned int reg = DA9121_REG_SYS_MASK_0 + i; + unsigned int mbit = handled[i]; + + err = regmap_update_bits(chip->regmap, reg, mbit, mbit); + if (err < 0) { + dev_err(chip->dev, + "Failed to mask 0x%02x interrupt %d\n", + reg, err); + ret = IRQ_NONE; + goto error; + } + } + } + + /* clear the events */ + if (handled[0] | handled[1] | handled[2]) { + err = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_EVENT_0, handled, 3); + if (err < 0) { + dev_err(chip->dev, "Fail to write EVENTs %d\n", err); + ret = IRQ_NONE; + goto error; + } + } + + queue_delayed_work(system_freezable_wq, &chip->work, 0); +error: + return ret; +} + static int da9121_set_regulator_config(struct da9121 *chip) { struct regulator_config config = { }; @@ -711,6 +921,56 @@ static int da9121_assign_chip_model(struct i2c_client *i2c, return ret; } +static int da9121_config_irq(struct i2c_client *i2c, + struct da9121 *chip) +{ + unsigned int p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + const int mask_all[4] = { 0, 0, 0xFF, 0xFF }; + int ret = 0; + + chip->chip_irq = i2c->irq; + + if (chip->chip_irq != 0) { + if (!of_property_read_u32(chip->dev->of_node, + "dlg,irq-polling-delay-passive-ms", + &p_delay)) { + if (p_delay < DA9121_MIN_POLLING_PERIOD_MS || + p_delay > DA9121_MAX_POLLING_PERIOD_MS) { + dev_warn(chip->dev, + "Out-of-range polling period %d ms\n", + p_delay); + p_delay = DA9121_DEFAULT_POLLING_PERIOD_MS; + } + } + + chip->passive_delay = p_delay; + + ret = devm_request_threaded_irq(chip->dev, + chip->chip_irq, NULL, + da9121_irq_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, + "da9121", chip); + if (ret != 0) { + dev_err(chip->dev, "Failed IRQ request: %d\n", + chip->chip_irq); + goto error; + } + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", + ret); + goto error; + } + + INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on); + dev_info(chip->dev, "Interrupt polling period set at %d ms\n", + chip->passive_delay); + } +error: + return ret; +} + static const struct of_device_id da9121_dt_ids[] = { { .compatible = "dlg,da9121", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, { .compatible = "dlg,da9130", .data = (void *) DA9121_TYPE_DA9121_DA9130 }, @@ -738,6 +998,7 @@ static int da9121_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct da9121 *chip; + const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; int ret = 0; chip = devm_kzalloc(&i2c->dev, sizeof(struct da9121), GFP_KERNEL); @@ -753,12 +1014,36 @@ static int da9121_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto error; + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) { + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + goto error; + } + ret = da9121_set_regulator_config(chip); + if (ret < 0) + goto error; + + ret = da9121_config_irq(i2c, chip); error: return ret; } +static int da9121_i2c_remove(struct i2c_client *i2c) +{ + struct da9121 *chip = i2c_get_clientdata(i2c); + const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; + int ret = 0; + + cancel_delayed_work_sync(&chip->work); + + ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); + if (ret != 0) + dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); + return ret; +} + static const struct i2c_device_id da9121_i2c_id[] = { {"da9121", DA9121_TYPE_DA9121_DA9130}, {"da9130", DA9121_TYPE_DA9121_DA9130}, @@ -777,6 +1062,7 @@ static struct i2c_driver da9121_regulator_driver = { .of_match_table = of_match_ptr(da9121_dt_ids), }, .probe = da9121_i2c_probe, + .remove = da9121_i2c_remove, .id_table = da9121_i2c_id, }; -- cgit v1.2.3 From 5e191d2e05a4fe098632006bb3afa5e21c8789db Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Wed, 2 Dec 2020 11:32:47 +0000 Subject: regulator: da9121: Request IRQ directly and free in release function to avoid masking race Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/24b2d8b0c3536408369add034221b1141e58bbfc.1606908582.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index d9a8a4b8fb3b..3ead6a171d95 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -945,8 +945,7 @@ static int da9121_config_irq(struct i2c_client *i2c, chip->passive_delay = p_delay; - ret = devm_request_threaded_irq(chip->dev, - chip->chip_irq, NULL, + ret = request_threaded_irq(chip->chip_irq, NULL, da9121_irq_handler, IRQF_TRIGGER_LOW|IRQF_ONESHOT, "da9121", chip); @@ -960,7 +959,7 @@ static int da9121_config_irq(struct i2c_client *i2c, if (ret != 0) { dev_err(chip->dev, "Failed to set IRQ masks: %d\n", ret); - goto error; + goto regmap_error; } INIT_DELAYED_WORK(&chip->work, da9121_status_poll_on); @@ -969,6 +968,9 @@ static int da9121_config_irq(struct i2c_client *i2c, } error: return ret; +regmap_error: + free_irq(chip->chip_irq, chip); + return ret; } static const struct of_device_id da9121_dt_ids[] = { @@ -1036,6 +1038,7 @@ static int da9121_i2c_remove(struct i2c_client *i2c) const int mask_all[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; int ret = 0; + free_irq(chip->chip_irq, chip); cancel_delayed_work_sync(&chip->work); ret = regmap_bulk_write(chip->regmap, DA9121_REG_SYS_MASK_0, mask_all, 4); -- cgit v1.2.3 From c8dbf2f200de8c9cba3b332522fabad114cf9f53 Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Thu, 3 Dec 2020 19:26:35 +0800 Subject: regulator: da9121: Mark some symbols with static keyword Fix the following sparse warnings: drivers/regulator/da9121-regulator.c:55:21: warning: symbol 'da9121_10A_2phase_current' was not declared. Should it be static? drivers/regulator/da9121-regulator.c:63:21: warning: symbol 'da9121_6A_2phase_current' was not declared. Should it be static? drivers/regulator/da9121-regulator.c:71:21: warning: symbol 'da9121_5A_1phase_current' was not declared. Should it be static? drivers/regulator/da9121-regulator.c:79:21: warning: symbol 'da9121_3A_1phase_current' was not declared. Should it be static? drivers/regulator/da9121-regulator.c:151:32: warning: symbol 'status_event_handling' was not declared. Should it be static? Signed-off-by: Zou Wei Acked-by: Adam Ward Link: https://lore.kernel.org/r/1606994795-36182-1-git-send-email-zou_wei@huawei.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 3ead6a171d95..893512cee9e4 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -52,7 +52,7 @@ struct da9121_range { int reg_max; }; -struct da9121_range da9121_10A_2phase_current = { +static struct da9121_range da9121_10A_2phase_current = { .val_min = 7000000, .val_max = 20000000, .val_stp = 1000000, @@ -60,7 +60,7 @@ struct da9121_range da9121_10A_2phase_current = { .reg_max = 14, }; -struct da9121_range da9121_6A_2phase_current = { +static struct da9121_range da9121_6A_2phase_current = { .val_min = 7000000, .val_max = 12000000, .val_stp = 1000000, @@ -68,7 +68,7 @@ struct da9121_range da9121_6A_2phase_current = { .reg_max = 6, }; -struct da9121_range da9121_5A_1phase_current = { +static struct da9121_range da9121_5A_1phase_current = { .val_min = 3500000, .val_max = 10000000, .val_stp = 500000, @@ -76,7 +76,7 @@ struct da9121_range da9121_5A_1phase_current = { .reg_max = 14, }; -struct da9121_range da9121_3A_1phase_current = { +static struct da9121_range da9121_3A_1phase_current = { .val_min = 3500000, .val_max = 6000000, .val_stp = 500000, @@ -148,7 +148,7 @@ struct status_event_data { * * GPIO0/1/2 are not configured for use by default, so should not be seen. */ -const struct status_event_data status_event_handling[] = { +static const struct status_event_data status_event_handling[] = { DA9xxx_STATUS(0, 0, SG, 0, "Handled E_SG\n"), DA9121_STATUS(0, 0, TEMP_CRIT, (REGULATOR_EVENT_OVER_TEMP|REGULATOR_EVENT_DISABLE), NULL), DA9121_STATUS(0, 0, TEMP_WARN, REGULATOR_EVENT_OVER_TEMP, NULL), -- cgit v1.2.3 From b4b277760a2167ddb28a309b81363889efd5cc22 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 4 Dec 2020 17:52:22 +0100 Subject: regulator: da9121: include linux/gpio/consumer.h When CONFIG_GPIOLIB is disabled, the declarations from linux/gpio/consumer.h are not visible: drivers/regulator/da9121-regulator.c:371:14: error: implicit declaration of function 'fwnode_gpiod_get_index' [-Werror,-Wimplicit-function-declaration] ena_gpiod = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, ^ drivers/regulator/da9121-regulator.c:372:7: error: use of undeclared identifier 'GPIOD_OUT_HIGH' GPIOD_OUT_HIGH | ^ drivers/regulator/da9121-regulator.c:373:7: error: use of undeclared identifier 'GPIOD_FLAGS_BIT_NONEXCLUSIVE' GPIOD_FLAGS_BIT_NONEXCLUSIVE, Include this explicitly to help compile testing. Fixes: 46c413d5bb23 ("regulator: da9121: Add support for device variants via devicetree") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20201204165229.3754763-1-arnd@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 893512cee9e4..db1c2cc838bc 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From bebb2c6d5ca23d6b7556d39564212b619e068562 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 3 Dec 2020 12:42:44 +0530 Subject: regulator: qcom-rpmh: Add support for PM8350/PM8350c Add support from RPMH regulators found in PM8350 and PM8350c PMICs Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20201203071244.2652297-2-vkoul@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index e673d48b31a1..fe030ec4b7db 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -865,6 +865,60 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { {}, }; +static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_hfsmps510, "vdd-s10"), + RPMH_VREG("smps11", "smp%s11", &pmic5_hfsmps510, "vdd-s11"), + RPMH_VREG("smps12", "smp%s12", &pmic5_hfsmps510, "vdd-s12"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"), + {}, +}; + +static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {}, +}; + static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), @@ -1011,6 +1065,14 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm8150l-rpmh-regulators", .data = pm8150l_vreg_data, }, + { + .compatible = "qcom,pm8350-rpmh-regulators", + .data = pm8350_vreg_data, + }, + { + .compatible = "qcom,pm8350c-rpmh-regulators", + .data = pm8350c_vreg_data, + }, { .compatible = "qcom,pm8998-rpmh-regulators", .data = pm8998_vreg_data, -- cgit v1.2.3 From 416c29e9ce1347ba9a4ef7aeb4f30c8d9a3ada49 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 7 Dec 2020 17:15:15 +0000 Subject: regulator: da9121: Remove uninitialised string variable Erroneously left in when switched to using of_parse_cb() Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/c7a9e947a9582fe0150d860b5eab7e093cd832bb.1607361013.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index db1c2cc838bc..ed68259bd952 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -816,7 +816,6 @@ static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) u32 variant_id; u8 variant_mrc, variant_vrc; char *type; - const char *name; bool config_match = false; int ret = 0; @@ -867,7 +866,7 @@ static int da9121_check_device_type(struct i2c_client *i2c, struct da9121 *chip) device_id, variant_id, type); if (!config_match) { - dev_err(chip->dev, "Device tree configuration '%s' does not match detected device.\n", name); + dev_err(chip->dev, "Device tree configuration does not match detected device.\n"); ret = -EINVAL; goto error; } -- cgit v1.2.3 From 9536ce63705952be5214544e3b048c78f932e794 Mon Sep 17 00:00:00 2001 From: Adam Ward Date: Mon, 7 Dec 2020 17:15:16 +0000 Subject: regulator: da9121: Fix index used for DT property Signed-off-by: Adam Ward Link: https://lore.kernel.org/r/2cf324b68d37c4059c7995e8cab5fc9a608ea65d.1607361013.git.Adam.Ward.opensource@diasemi.com Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index ed68259bd952..9d5b02fb1657 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -381,7 +381,7 @@ static int da9121_of_parse_cb(struct device_node *np, uint32_t ripple_reg; int ret; - if (of_property_read_u32(da9121_matches[pdata->num_buck].of_node, + if (of_property_read_u32(da9121_matches[pdata->num_buck-1].of_node, "dlg,ripple-cancel", &ripple_cancel)) { if (pdata->num_buck > 1) ripple_reg = DA9xxx_REG_BUCK_BUCK2_7; -- cgit v1.2.3 From 8db06423e079b1f6c0657e5bebda0006acf75c3c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 7 Dec 2020 20:55:44 +0300 Subject: regulator: da9121: Potential Oops in da9121_assign_chip_model() There is a missing "return ret;" on this error path so we call "da9121_check_device_type(i2c, chip);" which will end up dereferencing "chip->regmap" and lead to an Oops. Fixes: c860476b9e3a ("regulator: da9121: Add device variant regmaps") Signed-off-by: Dan Carpenter Acked-by: Adam Ward Link: https://lore.kernel.org/r/X85soGKnWAjPUA7a@mwanda Signed-off-by: Mark Brown --- drivers/regulator/da9121-regulator.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index db1c2cc838bc..e4fc3a7cd5d8 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -915,6 +915,7 @@ static int da9121_assign_chip_model(struct i2c_client *i2c, ret = PTR_ERR(chip->regmap); dev_err(chip->dev, "Failed to configure a register map: %d\n", ret); + return ret; } ret = da9121_check_device_type(i2c, chip); -- cgit v1.2.3 From 0da6736ecd10b45e535b100acd58df2db4c099d8 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 10 Dec 2020 12:21:39 +0100 Subject: regulator: max14577: Add proper module aliases strings Add proper modalias structures to let this driver load automatically if compiled as module, because max14577 MFD driver creates MFD cells with such compatible strings. Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20201210112139.5370-1-m.szyprowski@samsung.com Signed-off-by: Mark Brown --- drivers/regulator/max14577-regulator.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/max14577-regulator.c b/drivers/regulator/max14577-regulator.c index e34face736f4..1d78b455cc48 100644 --- a/drivers/regulator/max14577-regulator.c +++ b/drivers/regulator/max14577-regulator.c @@ -269,3 +269,5 @@ module_exit(max14577_regulator_exit); MODULE_AUTHOR("Krzysztof Kozlowski "); MODULE_DESCRIPTION("Maxim 14577/77836 regulator driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:max14577-regulator"); +MODULE_ALIAS("platform:max77836-regulator"); -- cgit v1.2.3 From 6a6939d5f588b40db32b82ebcec20ee5189c8376 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 10 Dec 2020 18:27:48 -0300 Subject: regulator: pfuze100: Convert the driver to DT-only Since 5.10-rc1 i.MX is a devicetree-only platform, so simplify the code by removing the unused non-DT support. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201210212748.5849-1-festevam@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- drivers/regulator/pfuze100-regulator.c | 34 +--------------------------------- include/linux/regulator/pfuze100.h | 6 ------ 3 files changed, 2 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 7faedb05b7a6..cb9333d4c0fd 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -830,7 +830,7 @@ config REGULATOR_PF8X00 config REGULATOR_PFUZE100 tristate "Freescale PFUZE100/200/3000/3001 regulator driver" - depends on I2C + depends on I2C && OF select REGMAP_I2C help Say y here to support the regulators found on the Freescale diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index 7e8ba9246167..09bab0b45f90 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -105,15 +105,6 @@ static const int pfuze3000_sw2hi[] = { 2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000, }; -static const struct i2c_device_id pfuze_device_id[] = { - {.name = "pfuze100", .driver_data = PFUZE100}, - {.name = "pfuze200", .driver_data = PFUZE200}, - {.name = "pfuze3000", .driver_data = PFUZE3000}, - {.name = "pfuze3001", .driver_data = PFUZE3001}, - { } -}; -MODULE_DEVICE_TABLE(i2c, pfuze_device_id); - static const struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, @@ -440,7 +431,6 @@ static struct pfuze_regulator pfuze3001_regulators[] = { PFUZE100_VGEN_REG(PFUZE3001, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), }; -#ifdef CONFIG_OF /* PFUZE100 */ static struct of_regulator_match pfuze100_matches[] = { { .name = "sw1ab", }, @@ -578,22 +568,6 @@ static inline struct device_node *match_of_node(int index) { return pfuze_matches[index].of_node; } -#else -static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) -{ - return 0; -} - -static inline struct regulator_init_data *match_init_data(int index) -{ - return NULL; -} - -static inline struct device_node *match_of_node(int index) -{ - return NULL; -} -#endif static struct pfuze_chip *syspm_pfuze_chip; @@ -708,8 +682,6 @@ static int pfuze100_regulator_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pfuze_chip *pfuze_chip; - struct pfuze_regulator_platform_data *pdata = - dev_get_platdata(&client->dev); struct regulator_config config = { }; int i, ret; const struct of_device_id *match; @@ -802,10 +774,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, desc = &pfuze_chip->regulator_descs[i].desc; - if (pdata) - init_data = pdata->init_data[i]; - else - init_data = match_init_data(i); + init_data = match_init_data(i); /* SW2~SW4 high bit check and modify the voltage value table */ if (i >= sw_check_start && i <= sw_check_end) { @@ -876,7 +845,6 @@ static int pfuze100_regulator_remove(struct i2c_client *client) } static struct i2c_driver pfuze_driver = { - .id_table = pfuze_device_id, .driver = { .name = "pfuze100-regulator", .of_match_table = pfuze_dt_ids, diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h index d47e668d9ca8..c964fe8ab698 100644 --- a/include/linux/regulator/pfuze100.h +++ b/include/linux/regulator/pfuze100.h @@ -63,10 +63,4 @@ #define PFUZE3001_VLDO3 8 #define PFUZE3001_VLDO4 9 -struct regulator_init_data; - -struct pfuze_regulator_platform_data { - struct regulator_init_data *init_data[PFUZE100_MAX_REGULATOR]; -}; - #endif /* __LINUX_REG_PFUZE100_H */ -- cgit v1.2.3 From 2819569147cb6e79730f2907d3ab3dfe75fe8478 Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Fri, 11 Dec 2020 16:45:10 +0800 Subject: regulator: mc13892-regulator: convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Zheng Yongjun Link: https://lore.kernel.org/r/20201211084510.2264-1-zhengyongjun3@huawei.com Signed-off-by: Mark Brown --- drivers/regulator/mc13892-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index a731e826a037..5221f7a9df91 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -582,8 +582,8 @@ static int mc13892_regulator_probe(struct platform_device *pdev) /* update mc13892_vcam ops */ memcpy(&mc13892_vcam_ops, mc13892_regulators[MC13892_VCAM].desc.ops, sizeof(struct regulator_ops)); - mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode, - mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode, + mc13892_vcam_ops.set_mode = mc13892_vcam_set_mode; + mc13892_vcam_ops.get_mode = mc13892_vcam_get_mode; mc13892_regulators[MC13892_VCAM].desc.ops = &mc13892_vcam_ops; mc13xxx_data = mc13xxx_parse_regulators_dt(pdev, mc13892_regulators, -- cgit v1.2.3